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