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 "CachedPage.h" 28 29#include "AnimationController.h" 30#include "CachedFramePlatformData.h" 31#include "DOMWindow.h" 32#include "Document.h" 33#include "DocumentLoader.h" 34#include "EventNames.h" 35#include "ExceptionCode.h" 36#include "FocusController.h" 37#include "FrameLoader.h" 38#include "FrameLoaderClient.h" 39#include "FrameView.h" 40#include "HistoryController.h" 41#include "HistoryItem.h" 42#include "Logging.h" 43#include "MainFrame.h" 44#include "Page.h" 45#include "PageCache.h" 46#include "PageTransitionEvent.h" 47#include "SVGDocumentExtensions.h" 48#include "ScriptController.h" 49#include "SerializedScriptValue.h" 50#include <wtf/RefCountedLeakCounter.h> 51#include <wtf/text/CString.h> 52 53#if ENABLE(TOUCH_EVENTS) 54#include "Chrome.h" 55#include "ChromeClient.h" 56#endif 57 58namespace WebCore { 59 60DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedFrameCounter, ("CachedFrame")); 61 62CachedFrameBase::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 , m_isComposited(frame.view()->hasCompositedContent()) 69{ 70} 71 72CachedFrameBase::~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 81void CachedFrameBase::restore() 82{ 83 ASSERT(m_document->view() == m_view); 84 85 if (m_isMainFrame) 86 m_view->setParentVisible(true); 87 88 Frame& frame = m_view->frame(); 89 m_cachedFrameScriptData->restore(frame); 90 91 if (m_document->svgExtensions()) 92 m_document->accessSVGExtensions()->unpauseAnimations(); 93 94 frame.animation().resumeAnimationsForDocument(m_document.get()); 95 m_document->resumeActiveDOMObjects(ActiveDOMObject::DocumentWillBecomeInactive); 96 m_document->resumeScriptedAnimationControllerCallbacks(); 97 98 // It is necessary to update any platform script objects after restoring the 99 // cached page. 100 frame.script().updatePlatformScriptObjects(); 101 102 if (m_isComposited) 103 frame.view()->restoreBackingStores(); 104 105 frame.loader().client().didRestoreFromPageCache(); 106 107 // Reconstruct the FrameTree. And open the child CachedFrames in their respective FrameLoaders. 108 for (unsigned i = 0; i < m_childFrames.size(); ++i) { 109 frame.tree().appendChild(&m_childFrames[i]->view()->frame()); 110 m_childFrames[i]->open(); 111 } 112 113#if PLATFORM(IOS) 114 if (m_isMainFrame) { 115 frame.loader().client().didRestoreFrameHierarchyForCachedFrame(); 116 117 if (DOMWindow* domWindow = m_document->domWindow()) { 118 // FIXME: Add SCROLL_LISTENER to the list of event types on Document, and use m_document->hasListenerType(). See <rdar://problem/9615482>. 119 if (domWindow->scrollEventListenerCount() && frame.page()) 120 frame.page()->chrome().client().setNeedsScrollNotifications(&frame, true); 121 } 122 } 123#endif 124 125 // FIXME: update Page Visibility state here. 126 // https://bugs.webkit.org/show_bug.cgi?id=116770 127 m_document->enqueuePageshowEvent(PageshowEventPersisted); 128 129 HistoryItem* historyItem = frame.loader().history().currentItem(); 130 m_document->enqueuePopstateEvent(historyItem && historyItem->stateObject() ? historyItem->stateObject() : SerializedScriptValue::nullValue()); 131 132#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) 133 if (m_document->hasTouchEventHandlers()) 134 m_document->page()->chrome().client().needTouchEvents(true); 135#endif 136 137 m_document->documentDidResumeFromPageCache(); 138} 139 140CachedFrame::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 150 if (frame.page()->focusController().focusedFrame() == &frame) 151 frame.page()->focusController().setFocusedFrame(&frame.mainFrame()); 152 153 // Custom scrollbar renderers will get reattached when the document comes out of the page cache 154 m_view->detachCustomScrollbars(); 155 156 m_document->setInPageCache(true); 157 frame.loader().stopLoading(UnloadEventPolicyUnloadAndPageHide); 158 159 // Create the CachedFrames for all Frames in the FrameTree. 160 for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) 161 m_childFrames.append(std::make_unique<CachedFrame>(*child)); 162 163 // Active DOM objects must be suspended before we cache the frame script data, 164 // but after we've fired the pagehide event, in case that creates more objects. 165 // Suspending must also happen after we've recursed over child frames, in case 166 // those create more objects. 167 m_document->documentWillSuspendForPageCache(); 168 m_document->suspendScriptedAnimationControllerCallbacks(); 169 m_document->suspendActiveDOMObjects(ActiveDOMObject::DocumentWillBecomeInactive); 170 m_cachedFrameScriptData = std::make_unique<ScriptCachedFrameData>(frame); 171 172 m_document->domWindow()->suspendForPageCache(); 173 174 frame.loader().client().savePlatformDataToCachedFrame(this); 175 176 if (m_isComposited && pageCache()->shouldClearBackingStores()) 177 frame.view()->clearBackingStores(); 178 179 // documentWillSuspendForPageCache() can set up a layout timer on the FrameView, so clear timers after that. 180 frame.clearTimers(); 181 182 // Deconstruct the FrameTree, to restore it later. 183 // We do this for two reasons: 184 // 1 - We reuse the main frame, so when it navigates to a new page load it needs to start with a blank FrameTree. 185 // 2 - It's much easier to destroy a CachedFrame while it resides in the PageCache if it is disconnected from its parent. 186 for (unsigned i = 0; i < m_childFrames.size(); ++i) 187 frame.tree().removeChild(&m_childFrames[i]->view()->frame()); 188 189 if (!m_isMainFrame) 190 frame.page()->decrementSubframeCount(); 191 192 frame.loader().client().didSaveToPageCache(); 193 194#ifndef NDEBUG 195 if (m_isMainFrame) 196 LOG(PageCache, "Finished creating CachedFrame for main frame url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get()); 197 else 198 LOG(PageCache, "Finished creating CachedFrame for child frame with url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get()); 199#endif 200 201#if PLATFORM(IOS) 202 if (m_isMainFrame) { 203 if (DOMWindow* domWindow = m_document->domWindow()) { 204 if (domWindow->scrollEventListenerCount() && frame.page()) 205 frame.page()->chrome().client().setNeedsScrollNotifications(&frame, false); 206 } 207 } 208#endif 209} 210 211void CachedFrame::open() 212{ 213 ASSERT(m_view); 214 if (!m_isMainFrame) 215 m_view->frame().page()->incrementSubframeCount(); 216 217 m_view->frame().loader().open(*this); 218} 219 220void CachedFrame::clear() 221{ 222 if (!m_document) 223 return; 224 225 // clear() should only be called for Frames representing documents that are no longer in the page cache. 226 // This means the CachedFrame has been: 227 // 1 - Successfully restore()'d by going back/forward. 228 // 2 - destroy()'ed because the PageCache is pruning or the WebView was closed. 229 ASSERT(!m_document->inPageCache()); 230 ASSERT(m_view); 231 ASSERT(!m_document->frame() || m_document->frame() == &m_view->frame()); 232 233 for (int i = m_childFrames.size() - 1; i >= 0; --i) 234 m_childFrames[i]->clear(); 235 236 m_document = nullptr; 237 m_view = nullptr; 238 m_url = URL(); 239 240 m_cachedFramePlatformData = nullptr; 241 m_cachedFrameScriptData = nullptr; 242} 243 244void CachedFrame::destroy() 245{ 246 if (!m_document) 247 return; 248 249 // Only CachedFrames that are still in the PageCache should be destroyed in this manner 250 ASSERT(m_document->inPageCache()); 251 ASSERT(m_view); 252 ASSERT(m_document->frame() == &m_view->frame()); 253 254 m_document->domWindow()->willDestroyCachedFrame(); 255 256 if (!m_isMainFrame) { 257 m_view->frame().detachFromPage(); 258 m_view->frame().loader().detachViewsAndDocumentLoader(); 259 } 260 261 for (int i = m_childFrames.size() - 1; i >= 0; --i) 262 m_childFrames[i]->destroy(); 263 264 if (m_cachedFramePlatformData) 265 m_cachedFramePlatformData->clear(); 266 267 Frame::clearTimers(m_view.get(), m_document.get()); 268 269 // FIXME: Why do we need to call removeAllEventListeners here? When the document is in page cache, this method won't work 270 // fully anyway, because the document won't be able to access its DOMWindow object (due to being frameless). 271 m_document->removeAllEventListeners(); 272 273 m_document->setInPageCache(false); 274 m_document->prepareForDestruction(); 275 276 clear(); 277} 278 279void CachedFrame::setCachedFramePlatformData(std::unique_ptr<CachedFramePlatformData> data) 280{ 281 m_cachedFramePlatformData = WTF::move(data); 282} 283 284CachedFramePlatformData* CachedFrame::cachedFramePlatformData() 285{ 286 return m_cachedFramePlatformData.get(); 287} 288 289int CachedFrame::descendantFrameCount() const 290{ 291 int count = m_childFrames.size(); 292 for (size_t i = 0; i < m_childFrames.size(); ++i) 293 count += m_childFrames[i]->descendantFrameCount(); 294 295 return count; 296} 297 298} // namespace WebCore 299