Title: [207207] branches/safari-602-branch

Diff

Modified: branches/safari-602-branch/LayoutTests/ChangeLog (207206 => 207207)


--- branches/safari-602-branch/LayoutTests/ChangeLog	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/LayoutTests/ChangeLog	2016-10-12 08:41:40 UTC (rev 207207)
@@ -1,5 +1,23 @@
 2016-10-12  Matthew Hanson  <matthew_han...@apple.com>
 
+        Merge r205786. rdar://problem/28476956
+
+    2016-09-10  Chris Dumez  <cdu...@apple.com>
+
+            It is possible for Document::m_frame pointer to become stale
+            https://bugs.webkit.org/show_bug.cgi?id=161812
+            <rdar://problem/27745023>
+
+            Reviewed by Ryosuke Niwa.
+
+            Add layout test that crashes on both Mac and iOS due to using a stale
+            Document::m_frame pointer.
+
+            * fast/history/pagehide-remove-iframe-crash-expected.txt: Added.
+            * fast/history/pagehide-remove-iframe-crash.html: Added.
+
+2016-10-12  Matthew Hanson  <matthew_han...@apple.com>
+
         Merge r205190. rdar://problem/28545010
 
     2016-08-30  Youenn Fablet  <you...@apple.com>

Added: branches/safari-602-branch/LayoutTests/fast/history/pagehide-remove-iframe-crash-expected.txt (0 => 207207)


--- branches/safari-602-branch/LayoutTests/fast/history/pagehide-remove-iframe-crash-expected.txt	                        (rev 0)
+++ branches/safari-602-branch/LayoutTests/fast/history/pagehide-remove-iframe-crash-expected.txt	2016-10-12 08:41:40 UTC (rev 207207)
@@ -0,0 +1,13 @@
+Tests that we do not crash when deleting a subframe from the pagehide event handler.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+pageshow - not from cache
+pagehide - entering cache
+pageshow - from cache
+PASS Page did enter and was restored from the page cache
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: branches/safari-602-branch/LayoutTests/fast/history/pagehide-remove-iframe-crash.html (0 => 207207)


--- branches/safari-602-branch/LayoutTests/fast/history/pagehide-remove-iframe-crash.html	                        (rev 0)
+++ branches/safari-602-branch/LayoutTests/fast/history/pagehide-remove-iframe-crash.html	2016-10-12 08:41:40 UTC (rev 207207)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<iframe srcdoc="<body></body>"></iframe>
+<script>
+description("Tests that we do not crash when deleting a subframe from the pagehide event handler.");
+jsTestIsAsync = true;
+if (window.testRunner)
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+
+window.addEventListener("pageshow", function(event) {
+    debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");
+
+    if (event.persisted) {
+        testPassed("Page did enter and was restored from the page cache");
+        finishJSTest();
+    }
+}, false);
+
+window.addEventListener("pagehide", function(event) {
+    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache");
+    if (!event.persisted) {
+        testFailed("Page did not enter the page cache.");
+        finishJSTest();
+    }
+    // Remove subframe in pagehide event handler.
+    var frame = document.getElementsByTagName("iframe")[0];
+    subFrameDoc = frame.contentDocument;
+    document.body.removeChild(frame);
+    frame = null;
+    gc();
+}, false);
+
+_onload_ = function() {
+   var frame = document.getElementsByTagName("iframe")[0];
+   frame.addEventListener("touchstart", function() { });
+   frame.addEventListener("click", function() { });
+   frame.contentDocument.body.addEventListener("touchstart", function() { });
+   frame.contentDocument.body.addEventListener("click", function() { });
+
+   setTimeout(function() {
+       // Force a back navigation back to this page.
+       window.location.href = ""
+   }, 0);
+}
+</script>
+<script src=""
+</body>
+</html>

Modified: branches/safari-602-branch/Source/WebCore/ChangeLog (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/ChangeLog	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/ChangeLog	2016-10-12 08:41:40 UTC (rev 207207)
@@ -1,5 +1,76 @@
 2016-10-12  Matthew Hanson  <matthew_han...@apple.com>
 
+        Merge r205786. rdar://problem/28476956
+
+    2016-09-10  Chris Dumez  <cdu...@apple.com>
+
+            It is possible for Document::m_frame pointer to become stale
+            https://bugs.webkit.org/show_bug.cgi?id=161812
+            <rdar://problem/27745023>
+
+            Reviewed by Ryosuke Niwa.
+
+            Document::m_frame is supposed to get cleared by Document::prepareForDestruction().
+            The Frame destructor calls Frame::setView(nullptr) which is supposed to call the
+            prepareForDestruction() on the Frame's associated document. However,
+            Frame::setView(nullptr) was calling prepareForDestruction() only if
+            Document::inPageCache() returned true. This is because, we allow Documents to
+            stay alive in the PageCache even though they don't have a frame.
+
+            The issue is that Document::m_inPageCache flag was set to true right before
+            firing the pagehide event, so technically before really entering PageCache.
+            Therefore, we can run into problems if a Frame gets destroyed by a pagehide
+            EventHandler because ~Frame() will not call Document::prepareForDestruction()
+            due to Document::m_inPageCache being true. After the frame is destroyed,
+            Document::m_frame becomes stale and any action on the document will likely
+            lead to crashes (such as the one in the layout test and the radar which
+            happens when trying to unregister event listeners from the document).
+
+            The solution adopted in this patch is to replace the m_inPageCache boolean
+            with a m_pageCacheState enumeration that has 3 states:
+            - NotInPageCache
+            - AboutToEnterPageCache
+            - InPageCache
+
+            Frame::setView() / Frame::setDocument() were then updated to call
+            Document::prepareForDestruction() on the associated document whenever
+            the document's pageCacheState is not InPageCache. This means that we
+            will now call Document::prepareForDestruction() when the document is
+            being detached from its frame while firing the pagehide event.
+
+            Note that I tried to keep this patch minimal. Therefore, I kept
+            the Document::inPageCache() getter for now. I plan to switch all its
+            calls sites to the new Document::pageCacheState() getter in a follow-up
+            patch so that we can finally drop the confusing Document::inPageCache().
+
+            Test: fast/history/pagehide-remove-iframe-crash.html
+
+            * dom/Document.cpp:
+            (WebCore::Document::Document):
+            (WebCore::Document::~Document):
+            (WebCore::Document::createRenderTree):
+            (WebCore::Document::destroyRenderTree):
+            (WebCore::Document::setFocusedElement):
+            (WebCore::Document::setPageCacheState):
+            (WebCore::Document::topDocument):
+            * dom/Document.h:
+            (WebCore::Document::pageCacheState):
+            (WebCore::Document::inPageCache):
+            * history/CachedFrame.cpp:
+            (WebCore::CachedFrame::destroy):
+            * history/PageCache.cpp:
+            (WebCore::setPageCacheState):
+            (WebCore::PageCache::addIfCacheable):
+            * loader/FrameLoader.cpp:
+            (WebCore::FrameLoader::stopAllLoaders):
+            (WebCore::FrameLoader::open):
+            * loader/HistoryController.cpp:
+            (WebCore::HistoryController::invalidateCurrentItemCachedPage):
+            * page/Frame.cpp:
+            (WebCore::Frame::setView):
+
+2016-10-12  Matthew Hanson  <matthew_han...@apple.com>
+
         Merge r205197. rdar://problem/28481424
 
     2016-08-30  Brent Fulgham  <bfulg...@apple.com>

Modified: branches/safari-602-branch/Source/WebCore/dom/Document.cpp (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/dom/Document.cpp	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/dom/Document.cpp	2016-10-12 08:41:40 UTC (rev 207207)
@@ -492,7 +492,6 @@
     , m_annotatedRegionsDirty(false)
 #endif
     , m_createRenderers(true)
-    , m_inPageCache(false)
     , m_accessKeyMapValid(false)
     , m_documentClasses(documentClasses)
     , m_isSynthesized(constructionFlags & Synthesized)
@@ -601,7 +600,7 @@
     allDocuments().remove(this);
 
     ASSERT(!renderView());
-    ASSERT(!m_inPageCache);
+    ASSERT(m_pageCacheState != InPageCache);
     ASSERT(m_ranges.isEmpty());
     ASSERT(!m_parentTreeScope);
     ASSERT(!m_disabledFieldsetElementsCount);
@@ -2240,7 +2239,7 @@
 void Document::createRenderTree()
 {
     ASSERT(!renderView());
-    ASSERT(!m_inPageCache);
+    ASSERT(m_pageCacheState != InPageCache);
     ASSERT(!m_axObjectCache || this != &topDocument());
 
     if (m_isNonRenderedPlaceholder)
@@ -2304,7 +2303,7 @@
 void Document::destroyRenderTree()
 {
     ASSERT(hasLivingRenderTree());
-    ASSERT(!m_inPageCache);
+    ASSERT(m_pageCacheState != InPageCache);
 
     TemporaryChange<bool> change(m_renderTreeBeingDestroyed, true);
 
@@ -3791,7 +3790,7 @@
     if (m_focusedElement == newFocusedElement)
         return true;
 
-    if (m_inPageCache)
+    if (inPageCache())
         return false;
 
     bool focusChangeBlocked = false;
@@ -4710,17 +4709,18 @@
     return completeURL(url, m_baseURL);
 }
 
-void Document::setInPageCache(bool flag)
+void Document::setPageCacheState(PageCacheState state)
 {
-    if (m_inPageCache == flag)
+    if (m_pageCacheState == state)
         return;
 
-    m_inPageCache = flag;
+    m_pageCacheState = state;
 
     FrameView* v = view();
     Page* page = this->page();
 
-    if (flag) {
+    switch (state) {
+    case InPageCache:
         if (v) {
             // FIXME: There is some scrolling related work that needs to happen whenever a page goes into the
             // page cache and similar work that needs to occur when it comes out. This is where we do the work
@@ -4740,9 +4740,13 @@
         m_styleRecalcTimer.stop();
 
         clearSharedObjectPool();
-    } else {
+        break;
+    case NotInPageCache:
         if (childNeedsStyleRecalc())
             scheduleStyleRecalc();
+        break;
+    case AboutToEnterPageCache:
+        break;
     }
 }
 
@@ -5044,7 +5048,7 @@
 {
     // FIXME: This special-casing avoids incorrectly determined top documents during the process
     // of AXObjectCache teardown or notification posting for cached or being-destroyed documents.
-    if (!m_inPageCache && !m_renderTreeBeingDestroyed) {
+    if (!inPageCache() && !m_renderTreeBeingDestroyed) {
         if (!m_frame)
             return const_cast<Document&>(*this);
         // This should always be non-null.

Modified: branches/safari-602-branch/Source/WebCore/dom/Document.h (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/dom/Document.h	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/dom/Document.h	2016-10-12 08:41:40 UTC (rev 207207)
@@ -1001,9 +1001,14 @@
 
     void finishedParsing();
 
-    bool inPageCache() const { return m_inPageCache; }
-    void setInPageCache(bool flag);
+    enum PageCacheState { NotInPageCache, AboutToEnterPageCache, InPageCache };
 
+    PageCacheState pageCacheState() const { return m_pageCacheState; }
+    void setPageCacheState(PageCacheState);
+
+    // FIXME: Update callers to use pageCacheState() instead.
+    bool inPageCache() const { return m_pageCacheState != NotInPageCache; }
+
     // Elements can register themselves for the "suspend()" and
     // "resume()" callbacks
     void registerForDocumentSuspensionCallbacks(Element*);
@@ -1599,7 +1604,7 @@
     HashMap<String, RefPtr<HTMLCanvasElement>> m_cssCanvasElements;
 
     bool m_createRenderers;
-    bool m_inPageCache;
+    PageCacheState m_pageCacheState { NotInPageCache };
 
     HashSet<Element*> m_documentSuspensionCallbackElements;
     HashSet<Element*> m_mediaVolumeCallbackElements;

Modified: branches/safari-602-branch/Source/WebCore/history/CachedFrame.cpp (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/history/CachedFrame.cpp	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/history/CachedFrame.cpp	2016-10-12 08:41:40 UTC (rev 207207)
@@ -264,7 +264,7 @@
     // fully anyway, because the document won't be able to access its DOMWindow object (due to being frameless).
     m_document->removeAllEventListeners();
 
-    m_document->setInPageCache(false);
+    m_document->setPageCacheState(Document::NotInPageCache);
     m_document->prepareForDestruction();
 
     clear();

Modified: branches/safari-602-branch/Source/WebCore/history/PageCache.cpp (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/history/PageCache.cpp	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/history/PageCache.cpp	2016-10-12 08:41:40 UTC (rev 207207)
@@ -373,11 +373,11 @@
     return emptyString();
 }
 
-static void setInPageCache(Page& page, bool isInPageCache)
+static void setPageCacheState(Page& page, Document::PageCacheState pageCacheState)
 {
     for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
         if (auto* document = frame->document())
-            document->setInPageCache(isInPageCache);
+            document->setPageCacheState(pageCacheState);
     }
 }
 
@@ -407,8 +407,7 @@
     if (!page || !canCache(*page))
         return;
 
-    // Make sure all the documents know they are being added to the PageCache.
-    setInPageCache(*page, true);
+    setPageCacheState(*page, Document::AboutToEnterPageCache);
 
     // Focus the main frame, defocusing a focused subframe (if we have one). We do this here,
     // before the page enters the page cache, while we still can dispatch DOM blur/focus events.
@@ -421,10 +420,12 @@
     // Check that the page is still page-cacheable after firing the pagehide event. The JS event handlers
     // could have altered the page in a way that could prevent caching.
     if (!canCache(*page)) {
-        setInPageCache(*page, false);
+        setPageCacheState(*page, Document::NotInPageCache);
         return;
     }
 
+    setPageCacheState(*page, Document::InPageCache);
+
     // Make sure we no longer fire any JS events past this point.
     NoEventDispatchAssertion assertNoEventDispatch;
 

Modified: branches/safari-602-branch/Source/WebCore/loader/FrameLoader.cpp (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/loader/FrameLoader.cpp	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/loader/FrameLoader.cpp	2016-10-12 08:41:40 UTC (rev 207207)
@@ -1600,7 +1600,7 @@
 
 void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItemPolicy)
 {
-    ASSERT(!m_frame.document() || !m_frame.document()->inPageCache());
+    ASSERT(!m_frame.document() || m_frame.document()->pageCacheState() != Document::InPageCache);
     if (m_pageDismissalEventBeingDispatched != PageDismissalType::None)
         return;
 
@@ -2100,7 +2100,7 @@
 
     clear(document, true, true, cachedFrame.isMainFrame());
 
-    document->setInPageCache(false);
+    document->setPageCacheState(Document::NotInPageCache);
 
     m_needsClear = true;
     m_isComplete = false;

Modified: branches/safari-602-branch/Source/WebCore/loader/HistoryController.cpp (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/loader/HistoryController.cpp	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/loader/HistoryController.cpp	2016-10-12 08:41:40 UTC (rev 207207)
@@ -270,7 +270,7 @@
     
     ASSERT(cachedPage->document() == m_frame.document());
     if (cachedPage->document() == m_frame.document()) {
-        cachedPage->document()->setInPageCache(false);
+        cachedPage->document()->setPageCacheState(Document::NotInPageCache);
         cachedPage->clear();
     }
 }

Modified: branches/safari-602-branch/Source/WebCore/page/Frame.cpp (207206 => 207207)


--- branches/safari-602-branch/Source/WebCore/page/Frame.cpp	2016-10-12 08:41:35 UTC (rev 207206)
+++ branches/safari-602-branch/Source/WebCore/page/Frame.cpp	2016-10-12 08:41:40 UTC (rev 207207)
@@ -245,7 +245,7 @@
     // Prepare for destruction now, so any unload event handlers get run and the DOMWindow is
     // notified. If we wait until the view is destroyed, then things won't be hooked up enough for
     // these calls to work.
-    if (!view && m_doc && !m_doc->inPageCache())
+    if (!view && m_doc && m_doc->pageCacheState() != Document::InPageCache)
         m_doc->prepareForDestruction();
     
     if (m_view)
@@ -267,7 +267,7 @@
 {
     ASSERT(!newDocument || newDocument->frame() == this);
 
-    if (m_doc && !m_doc->inPageCache())
+    if (m_doc && m_doc->pageCacheState() != Document::InPageCache)
         m_doc->prepareForDestruction();
 
     m_doc = newDocument.copyRef();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to