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