Cleanup RuntimeEnabledFeatures includes
[WebKit.git] / Source / WebCore / history / CachedFrame.cpp
1 /*
2  * Copyright (C) 2009 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 "CachedFrame.h"
28
29 #include "CSSAnimationController.h"
30 #include "CachedFramePlatformData.h"
31 #include "CachedPage.h"
32 #include "CustomHeaderFields.h"
33 #include "DOMWindow.h"
34 #include "Document.h"
35 #include "DocumentLoader.h"
36 #include "DocumentTimeline.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameLoaderClient.h"
40 #include "FrameView.h"
41 #include "Logging.h"
42 #include "NavigationDisabler.h"
43 #include "Page.h"
44 #include "PageCache.h"
45 #include "RenderWidget.h"
46 #include "SVGDocumentExtensions.h"
47 #include "ScriptController.h"
48 #include "SerializedScriptValue.h"
49 #include "StyleTreeResolver.h"
50 #include <wtf/RefCountedLeakCounter.h>
51 #include <wtf/text/CString.h>
52
53 #if PLATFORM(IOS_FAMILY) || ENABLE(TOUCH_EVENTS)
54 #include "Chrome.h"
55 #include "ChromeClient.h"
56 #endif
57
58 namespace WebCore {
59
60 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedFrameCounter, ("CachedFrame"));
61
62 CachedFrameBase::CachedFrameBase(Frame& frame)
63     : m_document(frame.document())
64     , m_documentLoader(frame.loader().documentLoader())
65     , m_view(frame.view())
66     , m_url(frame.document()->url())
67     , m_isMainFrame(!frame.tree().parent())
68 {
69 }
70
71 CachedFrameBase::~CachedFrameBase()
72 {
73 #ifndef NDEBUG
74     cachedFrameCounter.decrement();
75 #endif
76     // CachedFrames should always have had destroy() called by their parent CachedPage
77     ASSERT(!m_document);
78 }
79
80 void CachedFrameBase::pruneDetachedChildFrames()
81 {
82     for (size_t i = m_childFrames.size(); i;) {
83         --i;
84         if (m_childFrames[i]->view()->frame().page())
85             continue;
86         m_childFrames[i]->destroy();
87         m_childFrames.remove(i);
88     }
89 }
90
91 void CachedFrameBase::restore()
92 {
93     ASSERT(m_document->view() == m_view);
94
95     if (m_isMainFrame)
96         m_view->setParentVisible(true);
97
98     auto frame = makeRef(m_view->frame());
99     {
100         Style::PostResolutionCallbackDisabler disabler(*m_document);
101         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
102         NavigationDisabler disableNavigation { nullptr }; // Disable navigation globally.
103
104         m_cachedFrameScriptData->restore(frame.get());
105
106         if (m_document->svgExtensions())
107             m_document->accessSVGExtensions().unpauseAnimations();
108
109         m_document->resume(ReasonForSuspension::PageCache);
110
111         // It is necessary to update any platform script objects after restoring the
112         // cached page.
113         frame->script().updatePlatformScriptObjects();
114
115         frame->loader().client().didRestoreFromPageCache();
116
117         pruneDetachedChildFrames();
118
119         // Reconstruct the FrameTree. And open the child CachedFrames in their respective FrameLoaders.
120         for (auto& childFrame : m_childFrames) {
121             ASSERT(childFrame->view()->frame().page());
122             frame->tree().appendChild(childFrame->view()->frame());
123             childFrame->open();
124             ASSERT_WITH_SECURITY_IMPLICATION(m_document == frame->document());
125         }
126     }
127
128 #if PLATFORM(IOS_FAMILY)
129     if (m_isMainFrame) {
130         frame->loader().client().didRestoreFrameHierarchyForCachedFrame();
131
132         if (DOMWindow* domWindow = m_document->domWindow()) {
133             // FIXME: Add SCROLL_LISTENER to the list of event types on Document, and use m_document->hasListenerType(). See <rdar://problem/9615482>.
134             // FIXME: Can use Document::hasListenerType() now.
135             if (domWindow->scrollEventListenerCount() && frame->page())
136                 frame->page()->chrome().client().setNeedsScrollNotifications(frame, true);
137         }
138     }
139 #endif
140
141     frame->view()->didRestoreFromPageCache();
142 }
143
144 CachedFrame::CachedFrame(Frame& frame)
145     : CachedFrameBase(frame)
146 {
147 #ifndef NDEBUG
148     cachedFrameCounter.increment();
149 #endif
150     ASSERT(m_document);
151     ASSERT(m_documentLoader);
152     ASSERT(m_view);
153     ASSERT(m_document->pageCacheState() == Document::InPageCache);
154
155     RELEASE_ASSERT(m_document->domWindow());
156     RELEASE_ASSERT(m_document->frame());
157     RELEASE_ASSERT(m_document->domWindow()->frame());
158
159     // FIXME: We have evidence that constructing CachedFrames for descendant frames may detach the document from its frame (rdar://problem/49877867).
160     // This sets the flag to help find the guilty code.
161     m_document->setMayBeDetachedFromFrame(false);
162
163     // Create the CachedFrames for all Frames in the FrameTree.
164     for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling())
165         m_childFrames.append(makeUnique<CachedFrame>(*child));
166
167     RELEASE_ASSERT(m_document->domWindow());
168     RELEASE_ASSERT(m_document->frame());
169     RELEASE_ASSERT(m_document->domWindow()->frame());
170
171     // Active DOM objects must be suspended before we cache the frame script data.
172     m_document->suspend(ReasonForSuspension::PageCache);
173
174     m_cachedFrameScriptData = makeUnique<ScriptCachedFrameData>(frame);
175
176     m_document->domWindow()->suspendForPageCache();
177
178     // Clear FrameView to reset flags such as 'firstVisuallyNonEmptyLayoutCallbackPending' so that the
179     // 'DidFirstVisuallyNonEmptyLayout' callback gets called against when restoring from PageCache.
180     m_view->resetLayoutMilestones();
181
182     frame.loader().client().savePlatformDataToCachedFrame(this);
183
184     // documentWillSuspendForPageCache() can set up a layout timer on the FrameView, so clear timers after that.
185     frame.clearTimers();
186
187     // Deconstruct the FrameTree, to restore it later.
188     // We do this for two reasons:
189     // 1 - We reuse the main frame, so when it navigates to a new page load it needs to start with a blank FrameTree.
190     // 2 - It's much easier to destroy a CachedFrame while it resides in the PageCache if it is disconnected from its parent.
191     for (unsigned i = 0; i < m_childFrames.size(); ++i)
192         frame.tree().removeChild(m_childFrames[i]->view()->frame());
193
194     if (!m_isMainFrame)
195         frame.page()->decrementSubframeCount();
196
197     frame.loader().client().didSaveToPageCache();
198
199 #ifndef NDEBUG
200     if (m_isMainFrame)
201         LOG(PageCache, "Finished creating CachedFrame for main frame url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get());
202     else
203         LOG(PageCache, "Finished creating CachedFrame for child frame with url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get());
204 #endif
205
206 #if PLATFORM(IOS_FAMILY)
207     if (m_isMainFrame) {
208         if (DOMWindow* domWindow = m_document->domWindow()) {
209             if (domWindow->scrollEventListenerCount() && frame.page())
210                 frame.page()->chrome().client().setNeedsScrollNotifications(frame, false);
211         }
212     }
213 #endif
214
215     m_document->setMayBeDetachedFromFrame(true);
216     m_document->detachFromCachedFrame(*this);
217
218     ASSERT_WITH_SECURITY_IMPLICATION(!m_documentLoader->isLoading());
219 }
220
221 void CachedFrame::open()
222 {
223     ASSERT(m_view);
224     ASSERT(m_document);
225     if (!m_isMainFrame)
226         m_view->frame().page()->incrementSubframeCount();
227
228     m_view->frame().loader().open(*this);
229 }
230
231 void CachedFrame::clear()
232 {
233     if (!m_document)
234         return;
235
236     // clear() should only be called for Frames representing documents that are no longer in the page cache.
237     // This means the CachedFrame has been:
238     // 1 - Successfully restore()'d by going back/forward.
239     // 2 - destroy()'ed because the PageCache is pruning or the WebView was closed.
240     ASSERT(m_document->pageCacheState() == Document::NotInPageCache);
241     ASSERT(m_view);
242     ASSERT(!m_document->frame() || m_document->frame() == &m_view->frame());
243
244     for (int i = m_childFrames.size() - 1; i >= 0; --i)
245         m_childFrames[i]->clear();
246
247     m_document = nullptr;
248     m_view = nullptr;
249     m_url = URL();
250
251     m_cachedFramePlatformData = nullptr;
252     m_cachedFrameScriptData = nullptr;
253 }
254
255 void CachedFrame::destroy()
256 {
257     if (!m_document)
258         return;
259     
260     // Only CachedFrames that are still in the PageCache should be destroyed in this manner
261     ASSERT(m_document->pageCacheState() == Document::InPageCache);
262     ASSERT(m_view);
263     ASSERT(!m_document->frame());
264
265     m_document->domWindow()->willDestroyCachedFrame();
266
267     if (!m_isMainFrame && m_view->frame().page()) {
268         m_view->frame().loader().detachViewsAndDocumentLoader();
269         m_view->frame().detachFromPage();
270     }
271     
272     for (int i = m_childFrames.size() - 1; i >= 0; --i)
273         m_childFrames[i]->destroy();
274
275     if (m_cachedFramePlatformData)
276         m_cachedFramePlatformData->clear();
277
278     Frame::clearTimers(m_view.get(), m_document.get());
279
280     m_view->frame().animation().detachFromDocument(m_document.get());
281
282     // FIXME: Why do we need to call removeAllEventListeners here? When the document is in page cache, this method won't work
283     // fully anyway, because the document won't be able to access its DOMWindow object (due to being frameless).
284     m_document->removeAllEventListeners();
285
286     m_document->setPageCacheState(Document::NotInPageCache);
287     m_document->prepareForDestruction();
288
289     clear();
290 }
291
292 void CachedFrame::setCachedFramePlatformData(std::unique_ptr<CachedFramePlatformData> data)
293 {
294     m_cachedFramePlatformData = WTFMove(data);
295 }
296
297 CachedFramePlatformData* CachedFrame::cachedFramePlatformData()
298 {
299     return m_cachedFramePlatformData.get();
300 }
301
302 void CachedFrame::setHasInsecureContent(HasInsecureContent hasInsecureContent)
303 {
304     m_hasInsecureContent = hasInsecureContent;
305 }
306
307 int CachedFrame::descendantFrameCount() const
308 {
309     int count = m_childFrames.size();
310     for (size_t i = 0; i < m_childFrames.size(); ++i)
311         count += m_childFrames[i]->descendantFrameCount();
312     
313     return count;
314 }
315
316 } // namespace WebCore