1/*
2 * Copyright (C) 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CachedPage.h"
28
29#include "Document.h"
30#include "Element.h"
31#include "FocusController.h"
32#include "Frame.h"
33#include "FrameLoader.h"
34#include "FrameView.h"
35#include "HistoryController.h"
36#include "HistoryItem.h"
37#include "Node.h"
38#include "Page.h"
39#include "PageTransitionEvent.h"
40#include "ScriptDisallowedScope.h"
41#include "Settings.h"
42#include "VisitedLinkState.h"
43#include <wtf/RefCountedLeakCounter.h>
44#include <wtf/StdLibExtras.h>
45
46#if PLATFORM(IOS_FAMILY)
47#include "FrameSelection.h"
48#endif
49
50
51namespace WebCore {
52using namespace JSC;
53
54DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedPageCounter, ("CachedPage"));
55
56CachedPage::CachedPage(Page& page)
57 : m_page(page)
58 , m_expirationTime(MonotonicTime::now() + Seconds(page.settings().backForwardCacheExpirationInterval()))
59 , m_cachedMainFrame(std::make_unique<CachedFrame>(page.mainFrame()))
60{
61#ifndef NDEBUG
62 cachedPageCounter.increment();
63#endif
64}
65
66CachedPage::~CachedPage()
67{
68#ifndef NDEBUG
69 cachedPageCounter.decrement();
70#endif
71
72 if (m_cachedMainFrame)
73 m_cachedMainFrame->destroy();
74}
75
76static void firePageShowAndPopStateEvents(Page& page)
77{
78 // Dispatching JavaScript events can cause frame destruction.
79 auto& mainFrame = page.mainFrame();
80 Vector<Ref<Frame>> childFrames;
81 for (auto* child = mainFrame.tree().traverseNextInPostOrder(CanWrap::Yes); child; child = child->tree().traverseNextInPostOrder(CanWrap::No))
82 childFrames.append(*child);
83
84 for (auto& child : childFrames) {
85 if (!child->tree().isDescendantOf(&mainFrame))
86 continue;
87 auto* document = child->document();
88 if (!document)
89 continue;
90
91 // FIXME: Update Page Visibility state here.
92 // https://bugs.webkit.org/show_bug.cgi?id=116770
93 document->dispatchPageshowEvent(PageshowEventPersisted);
94
95 auto* historyItem = child->loader().history().currentItem();
96 if (historyItem && historyItem->stateObject())
97 document->dispatchPopstateEvent(historyItem->stateObject());
98 }
99}
100
101class CachedPageRestorationScope {
102public:
103 CachedPageRestorationScope(Page& page)
104 : m_page(page)
105 {
106 m_page.setIsRestoringCachedPage(true);
107 }
108
109 ~CachedPageRestorationScope()
110 {
111 m_page.setIsRestoringCachedPage(false);
112 }
113
114private:
115 Page& m_page;
116};
117
118void CachedPage::restore(Page& page)
119{
120 ASSERT(m_cachedMainFrame);
121 ASSERT(m_cachedMainFrame->view()->frame().isMainFrame());
122 ASSERT(!page.subframeCount());
123
124 CachedPageRestorationScope restorationScope(page);
125 m_cachedMainFrame->open();
126
127 // Restore the focus appearance for the focused element.
128 // FIXME: Right now we don't support pages w/ frames in the b/f cache. This may need to be tweaked when we add support for that.
129 Document* focusedDocument = page.focusController().focusedOrMainFrame().document();
130 if (Element* element = focusedDocument->focusedElement()) {
131#if PLATFORM(IOS_FAMILY)
132 // We don't want focused nodes changing scroll position when restoring from the cache
133 // as it can cause ugly jumps before we manage to restore the cached position.
134 page.mainFrame().selection().suppressScrolling();
135
136 bool hadProhibitsScrolling = false;
137 FrameView* frameView = page.mainFrame().view();
138 if (frameView) {
139 hadProhibitsScrolling = frameView->prohibitsScrolling();
140 frameView->setProhibitsScrolling(true);
141 }
142#endif
143 element->updateFocusAppearance(SelectionRestorationMode::Restore);
144#if PLATFORM(IOS_FAMILY)
145 if (frameView)
146 frameView->setProhibitsScrolling(hadProhibitsScrolling);
147 page.mainFrame().selection().restoreScrolling();
148#endif
149 }
150
151 if (m_needsDeviceOrPageScaleChanged)
152 page.mainFrame().deviceOrPageScaleFactorChanged();
153
154 page.setNeedsRecalcStyleInAllFrames();
155
156#if ENABLE(VIDEO_TRACK)
157 if (m_needsCaptionPreferencesChanged)
158 page.captionPreferencesChanged();
159#endif
160
161 if (m_needsUpdateContentsSize) {
162 if (FrameView* frameView = page.mainFrame().view())
163 frameView->updateContentsSize();
164 }
165
166 firePageShowAndPopStateEvents(page);
167
168 clear();
169}
170
171void CachedPage::clear()
172{
173 ASSERT(m_cachedMainFrame);
174 m_cachedMainFrame->clear();
175 m_cachedMainFrame = nullptr;
176#if ENABLE(VIDEO_TRACK)
177 m_needsCaptionPreferencesChanged = false;
178#endif
179 m_needsDeviceOrPageScaleChanged = false;
180 m_needsUpdateContentsSize = false;
181}
182
183bool CachedPage::hasExpired() const
184{
185 return MonotonicTime::now() > m_expirationTime;
186}
187
188} // namespace WebCore
189