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