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