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