Changeset 205786 in webkit
- Timestamp:
- Sep 10, 2016 9:06:05 AM (8 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r205785 r205786 1 2016-09-10 Chris Dumez <cdumez@apple.com> 2 3 It is possible for Document::m_frame pointer to become stale 4 https://bugs.webkit.org/show_bug.cgi?id=161812 5 <rdar://problem/27745023> 6 7 Reviewed by Ryosuke Niwa. 8 9 Add layout test that crashes on both Mac and iOS due to using a stale 10 Document::m_frame pointer. 11 12 * fast/history/pagehide-remove-iframe-crash-expected.txt: Added. 13 * fast/history/pagehide-remove-iframe-crash.html: Added. 14 1 15 2016-09-10 Gyuyoung Kim <gyuyoung.kim@webkit.org> 2 16 -
trunk/Source/WebCore/ChangeLog
r205784 r205786 1 2016-09-10 Chris Dumez <cdumez@apple.com> 2 3 It is possible for Document::m_frame pointer to become stale 4 https://bugs.webkit.org/show_bug.cgi?id=161812 5 <rdar://problem/27745023> 6 7 Reviewed by Ryosuke Niwa. 8 9 Document::m_frame is supposed to get cleared by Document::prepareForDestruction(). 10 The Frame destructor calls Frame::setView(nullptr) which is supposed to call the 11 prepareForDestruction() on the Frame's associated document. However, 12 Frame::setView(nullptr) was calling prepareForDestruction() only if 13 Document::inPageCache() returned true. This is because, we allow Documents to 14 stay alive in the PageCache even though they don't have a frame. 15 16 The issue is that Document::m_inPageCache flag was set to true right before 17 firing the pagehide event, so technically before really entering PageCache. 18 Therefore, we can run into problems if a Frame gets destroyed by a pagehide 19 EventHandler because ~Frame() will not call Document::prepareForDestruction() 20 due to Document::m_inPageCache being true. After the frame is destroyed, 21 Document::m_frame becomes stale and any action on the document will likely 22 lead to crashes (such as the one in the layout test and the radar which 23 happens when trying to unregister event listeners from the document). 24 25 The solution adopted in this patch is to replace the m_inPageCache boolean 26 with a m_pageCacheState enumeration that has 3 states: 27 - NotInPageCache 28 - AboutToEnterPageCache 29 - InPageCache 30 31 Frame::setView() / Frame::setDocument() were then updated to call 32 Document::prepareForDestruction() on the associated document whenever 33 the document's pageCacheState is not InPageCache. This means that we 34 will now call Document::prepareForDestruction() when the document is 35 being detached from its frame while firing the pagehide event. 36 37 Note that I tried to keep this patch minimal. Therefore, I kept 38 the Document::inPageCache() getter for now. I plan to switch all its 39 calls sites to the new Document::pageCacheState() getter in a follow-up 40 patch so that we can finally drop the confusing Document::inPageCache(). 41 42 Test: fast/history/pagehide-remove-iframe-crash.html 43 44 * dom/Document.cpp: 45 (WebCore::Document::Document): 46 (WebCore::Document::~Document): 47 (WebCore::Document::createRenderTree): 48 (WebCore::Document::destroyRenderTree): 49 (WebCore::Document::setFocusedElement): 50 (WebCore::Document::setPageCacheState): 51 (WebCore::Document::topDocument): 52 * dom/Document.h: 53 (WebCore::Document::pageCacheState): 54 (WebCore::Document::inPageCache): 55 * history/CachedFrame.cpp: 56 (WebCore::CachedFrame::destroy): 57 * history/PageCache.cpp: 58 (WebCore::setPageCacheState): 59 (WebCore::PageCache::addIfCacheable): 60 * loader/FrameLoader.cpp: 61 (WebCore::FrameLoader::stopAllLoaders): 62 (WebCore::FrameLoader::open): 63 * loader/HistoryController.cpp: 64 (WebCore::HistoryController::invalidateCurrentItemCachedPage): 65 * page/Frame.cpp: 66 (WebCore::Frame::setView): 67 1 68 2016-09-10 Wenson Hsieh <wenson_hsieh@apple.com> 2 69 -
trunk/Source/WebCore/dom/Document.cpp
r205468 r205786 494 494 #endif 495 495 , m_createRenderers(true) 496 , m_inPageCache(false)497 496 , m_accessKeyMapValid(false) 498 497 , m_documentClasses(documentClasses) … … 603 602 604 603 ASSERT(!renderView()); 605 ASSERT( !m_inPageCache);604 ASSERT(m_pageCacheState != InPageCache); 606 605 ASSERT(m_ranges.isEmpty()); 607 606 ASSERT(!m_parentTreeScope); … … 2238 2237 { 2239 2238 ASSERT(!renderView()); 2240 ASSERT( !m_inPageCache);2239 ASSERT(m_pageCacheState != InPageCache); 2241 2240 ASSERT(!m_axObjectCache || this != &topDocument()); 2242 2241 … … 2302 2301 { 2303 2302 ASSERT(hasLivingRenderTree()); 2304 ASSERT( !m_inPageCache);2303 ASSERT(m_pageCacheState != InPageCache); 2305 2304 2306 2305 TemporaryChange<bool> change(m_renderTreeBeingDestroyed, true); … … 3788 3787 return true; 3789 3788 3790 if ( m_inPageCache)3789 if (inPageCache()) 3791 3790 return false; 3792 3791 … … 4716 4715 } 4717 4716 4718 void Document::set InPageCache(bool flag)4719 { 4720 if (m_ inPageCache == flag)4721 return; 4722 4723 m_ inPageCache = flag;4717 void Document::setPageCacheState(PageCacheState state) 4718 { 4719 if (m_pageCacheState == state) 4720 return; 4721 4722 m_pageCacheState = state; 4724 4723 4725 4724 FrameView* v = view(); 4726 4725 Page* page = this->page(); 4727 4726 4728 if (flag) { 4727 switch (state) { 4728 case InPageCache: 4729 4729 if (v) { 4730 4730 // FIXME: There is some scrolling related work that needs to happen whenever a page goes into the … … 4746 4746 4747 4747 clearSharedObjectPool(); 4748 } else { 4748 break; 4749 case NotInPageCache: 4749 4750 if (childNeedsStyleRecalc()) 4750 4751 scheduleStyleRecalc(); 4752 break; 4753 case AboutToEnterPageCache: 4754 break; 4751 4755 } 4752 4756 } … … 5067 5071 // FIXME: This special-casing avoids incorrectly determined top documents during the process 5068 5072 // of AXObjectCache teardown or notification posting for cached or being-destroyed documents. 5069 if (! m_inPageCache&& !m_renderTreeBeingDestroyed) {5073 if (!inPageCache() && !m_renderTreeBeingDestroyed) { 5070 5074 if (!m_frame) 5071 5075 return const_cast<Document&>(*this); -
trunk/Source/WebCore/dom/Document.h
r205468 r205786 1002 1002 void finishedParsing(); 1003 1003 1004 bool inPageCache() const { return m_inPageCache; } 1005 void setInPageCache(bool flag); 1004 enum PageCacheState { NotInPageCache, AboutToEnterPageCache, InPageCache }; 1005 1006 PageCacheState pageCacheState() const { return m_pageCacheState; } 1007 void setPageCacheState(PageCacheState); 1008 1009 // FIXME: Update callers to use pageCacheState() instead. 1010 bool inPageCache() const { return m_pageCacheState != NotInPageCache; } 1006 1011 1007 1012 // Elements can register themselves for the "suspend()" and … … 1594 1599 1595 1600 bool m_createRenderers; 1596 bool m_inPageCache;1601 PageCacheState m_pageCacheState { NotInPageCache }; 1597 1602 1598 1603 HashSet<Element*> m_documentSuspensionCallbackElements; -
trunk/Source/WebCore/history/CachedFrame.cpp
r201310 r205786 265 265 m_document->removeAllEventListeners(); 266 266 267 m_document->set InPageCache(false);267 m_document->setPageCacheState(Document::NotInPageCache); 268 268 m_document->prepareForDestruction(); 269 269 -
trunk/Source/WebCore/history/PageCache.cpp
r201655 r205786 374 374 } 375 375 376 static void set InPageCache(Page& page, bool isInPageCache)376 static void setPageCacheState(Page& page, Document::PageCacheState pageCacheState) 377 377 { 378 378 for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { 379 379 if (auto* document = frame->document()) 380 document->set InPageCache(isInPageCache);380 document->setPageCacheState(pageCacheState); 381 381 } 382 382 } … … 408 408 return; 409 409 410 // Make sure all the documents know they are being added to the PageCache. 411 setInPageCache(*page, true); 410 setPageCacheState(*page, Document::AboutToEnterPageCache); 412 411 413 412 // Focus the main frame, defocusing a focused subframe (if we have one). We do this here, … … 422 421 // could have altered the page in a way that could prevent caching. 423 422 if (!canCache(*page)) { 424 setInPageCache(*page, false); 425 return; 426 } 423 setPageCacheState(*page, Document::NotInPageCache); 424 return; 425 } 426 427 setPageCacheState(*page, Document::InPageCache); 427 428 428 429 // Make sure we no longer fire any JS events past this point. -
trunk/Source/WebCore/loader/FrameLoader.cpp
r205411 r205786 1597 1597 void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItemPolicy) 1598 1598 { 1599 ASSERT(!m_frame.document() || !m_frame.document()->inPageCache());1599 ASSERT(!m_frame.document() || m_frame.document()->pageCacheState() != Document::InPageCache); 1600 1600 if (m_pageDismissalEventBeingDispatched != PageDismissalType::None) 1601 1601 return; … … 2095 2095 clear(document, true, true, cachedFrame.isMainFrame()); 2096 2096 2097 document->set InPageCache(false);2097 document->setPageCacheState(Document::NotInPageCache); 2098 2098 2099 2099 m_needsClear = true; -
trunk/Source/WebCore/loader/HistoryController.cpp
r200770 r205786 271 271 ASSERT(cachedPage->document() == m_frame.document()); 272 272 if (cachedPage->document() == m_frame.document()) { 273 cachedPage->document()->set InPageCache(false);273 cachedPage->document()->setPageCacheState(Document::NotInPageCache); 274 274 cachedPage->clear(); 275 275 } -
trunk/Source/WebCore/page/Frame.cpp
r205462 r205786 247 247 // notified. If we wait until the view is destroyed, then things won't be hooked up enough for 248 248 // these calls to work. 249 if (!view && m_doc && !m_doc->inPageCache())249 if (!view && m_doc && m_doc->pageCacheState() != Document::InPageCache) 250 250 m_doc->prepareForDestruction(); 251 251 … … 269 269 ASSERT(!newDocument || newDocument->frame() == this); 270 270 271 if (m_doc && !m_doc->inPageCache())271 if (m_doc && m_doc->pageCacheState() != Document::InPageCache) 272 272 m_doc->prepareForDestruction(); 273 273
Note: See TracChangeset
for help on using the changeset viewer.