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