Title: [227280] trunk
Revision
227280
Author
[email protected]
Date
2018-01-21 19:39:36 -0800 (Sun, 21 Jan 2018)

Log Message

Release assertion in canExecuteScript when executing scripts during page cache restore
https://bugs.webkit.org/show_bug.cgi?id=181902

Reviewed by Antti Koivisto.

Source/WebCore:

The crash was caused by an erroneous instantiation of ScriptDisallowedScope::InMainThread in CachedPage::restore.
It can execute arbitrary scripts since CachedFrame::open can update style, layout, and evaluate media queries.

This is fine because there is no way to put this page back into a page cache until the load is commited via
FrameLoader::commitProvisionalLoad is invoked later which only happens after CachedPage::restore had exited.

Also added a release assert to make sure this condition holds.

Tests: fast/history/page-cache-execute-script-during-restore.html
       fast/history/page-cache-navigate-during-restore.html

* history/CachedPage.cpp:
(WebCore::CachedPageRestorationScope::CachedPageRestorationScope): Added.
(WebCore::CachedPageRestorationScope::~CachedPageRestorationScope): Added.
(WebCore::CachedPage::restore): Don't instantiate ScriptDisallowedScope::InMainThread. Set isRestoringCachedPage
on the cached pate to release-assert that there won't be any attempt to put this very page back into the cache.
* history/PageCache.cpp:
(WebCore::canCachePage): Added a release assert to make sure the page which is in the process of being restored
from the page cache is not put into the page cache.
* page/Page.h:
(WebCore::Page::setIsRestoringCachedPage): Added.
(WebCore::Page::isRestoringCachedPage const): Added.

LayoutTests:

Added regression tests for the release assertion and navigating while a document is being restored from the page cache.
WebKit should not hit any assertions in either situations.

* fast/history/page-cache-execute-script-during-restore-expected.txt: Added.
* fast/history/page-cache-execute-script-during-restore.html: Added.
* fast/history/page-cache-navigate-during-restore-expected.txt: Added.
* fast/history/page-cache-navigate-during-restore.html: Added.
* fast/history/resources/navigate-back-with-finish-test-stage.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (227279 => 227280)


--- trunk/LayoutTests/ChangeLog	2018-01-22 03:30:35 UTC (rev 227279)
+++ trunk/LayoutTests/ChangeLog	2018-01-22 03:39:36 UTC (rev 227280)
@@ -1,3 +1,19 @@
+2018-01-19  Ryosuke Niwa  <[email protected]>
+
+        Release assertion in canExecuteScript when executing scripts during page cache restore
+        https://bugs.webkit.org/show_bug.cgi?id=181902
+
+        Reviewed by Antti Koivisto.
+
+        Added regression tests for the release assertion and navigating while a document is being restored from the page cache.
+        WebKit should not hit any assertions in either situations.
+
+        * fast/history/page-cache-execute-script-during-restore-expected.txt: Added.
+        * fast/history/page-cache-execute-script-during-restore.html: Added.
+        * fast/history/page-cache-navigate-during-restore-expected.txt: Added.
+        * fast/history/page-cache-navigate-during-restore.html: Added.
+        * fast/history/resources/navigate-back-with-finish-test-stage.html: Added.
+
 2018-01-21  Jer Noble  <[email protected]>
 
         REGRESSION (macOS 10.13.2): imported/w3c/web-platform-tests/media-source/mediasource-* LayoutTests failing

Added: trunk/LayoutTests/fast/history/page-cache-execute-script-during-restore-expected.txt (0 => 227280)


--- trunk/LayoutTests/fast/history/page-cache-execute-script-during-restore-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/history/page-cache-execute-script-during-restore-expected.txt	2018-01-22 03:39:36 UTC (rev 227280)
@@ -0,0 +1,3 @@
+This tests executing a script while being restored from a page cache. WebKit should not hit a release assertion.
+
+PASS

Added: trunk/LayoutTests/fast/history/page-cache-execute-script-during-restore.html (0 => 227280)


--- trunk/LayoutTests/fast/history/page-cache-execute-script-during-restore.html	                        (rev 0)
+++ trunk/LayoutTests/fast/history/page-cache-execute-script-during-restore.html	2018-01-22 03:39:36 UTC (rev 227280)
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>This tests executing a script while being restored from a page cache. WebKit should not hit a release assertion.</p>
+<div id="result">
+<input id="input">
+<button _onclick_="runTest()">Start test</button>
+</div>
+<script>
+
+if (window.testRunner) {
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.clearBackForwardList();
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+window.addEventListener("pageshow", (event) => {
+    if (event.persisted)
+        return;
+    if (window.testRunner)
+        setTimeout(runTest, 0);
+});
+
+function runTest()
+{
+    input.setAttribute('autofocus', '');
+    document.getElementById('input').addEventListener('focus', finish);
+    location.href = '';
+}
+
+function finish()
+{
+    document.getElementById('result').textContent = 'PASS';
+    setTimeout(() => {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, 0);
+}
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/history/page-cache-navigate-during-restore-expected.txt (0 => 227280)


--- trunk/LayoutTests/fast/history/page-cache-navigate-during-restore-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/history/page-cache-navigate-during-restore-expected.txt	2018-01-22 03:39:36 UTC (rev 227280)
@@ -0,0 +1,3 @@
+This tests navigation while being restored from a page cache. WebKit should not hit any debug assertions.
+
+PASS

Added: trunk/LayoutTests/fast/history/page-cache-navigate-during-restore.html (0 => 227280)


--- trunk/LayoutTests/fast/history/page-cache-navigate-during-restore.html	                        (rev 0)
+++ trunk/LayoutTests/fast/history/page-cache-navigate-during-restore.html	2018-01-22 03:39:36 UTC (rev 227280)
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>This tests navigation while being restored from a page cache. WebKit should not hit any debug assertions.</p>
+<div id="result">
+<input id="input">
+<button _onclick_="delete sessionStorage.testStage; runTest()">Start test</button>
+</div>
+<script>
+
+if (window.testRunner) {
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.clearBackForwardList();
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+    internals.clearPageCache();
+}
+sessionStorage.testStage = 'initial';
+let persisted = false;
+
+window.addEventListener("pageshow", () => {
+    switch (sessionStorage.testStage) {
+    case 'initial':
+        if (window.testRunner)
+            setTimeout(runTest, 0);
+        break;
+    case 'navigate':
+        location.href = '';
+        break;
+    case 'finish':
+        document.getElementById('result').textContent = persisted ? 'PASS' : 'FAIL - Not put into the page cache';
+        delete sessionStorage.testStage;
+        if (window.testRunner)
+            testRunner.notifyDone();
+        break;
+    }
+});
+
+function runTest()
+{
+    sessionStorage.testStage = 'navigate';
+    input.setAttribute('autofocus', '');
+    document.getElementById('input').addEventListener('focus', navigate);
+    persisted = true;
+    location.href = '';
+}
+
+function navigate()
+{
+    if (sessionStorage.testStage != 'navigate')
+        return;
+    location.href = '';
+    document.getElementById('result').textContent = 'FAIL';
+}
+
+function finish() {
+    document.getElementById('result').textContent = 'PASS';
+    setTimeout(() => {
+        delete sessionStorage.testStage;
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, 0);
+}
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/history/resources/navigate-back-with-finish-test-stage.html (0 => 227280)


--- trunk/LayoutTests/fast/history/resources/navigate-back-with-finish-test-stage.html	                        (rev 0)
+++ trunk/LayoutTests/fast/history/resources/navigate-back-with-finish-test-stage.html	2018-01-22 03:39:36 UTC (rev 227280)
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+window._onload_ = () => {
+    sessionStorage.testStage = 'finish';
+    setTimeout(() => history.back(), 0);
+}
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (227279 => 227280)


--- trunk/Source/WebCore/ChangeLog	2018-01-22 03:30:35 UTC (rev 227279)
+++ trunk/Source/WebCore/ChangeLog	2018-01-22 03:39:36 UTC (rev 227280)
@@ -1,3 +1,33 @@
+2018-01-19  Ryosuke Niwa  <[email protected]>
+
+        Release assertion in canExecuteScript when executing scripts during page cache restore
+        https://bugs.webkit.org/show_bug.cgi?id=181902
+
+        Reviewed by Antti Koivisto.
+
+        The crash was caused by an erroneous instantiation of ScriptDisallowedScope::InMainThread in CachedPage::restore.
+        It can execute arbitrary scripts since CachedFrame::open can update style, layout, and evaluate media queries.
+
+        This is fine because there is no way to put this page back into a page cache until the load is commited via
+        FrameLoader::commitProvisionalLoad is invoked later which only happens after CachedPage::restore had exited.
+
+        Also added a release assert to make sure this condition holds.
+
+        Tests: fast/history/page-cache-execute-script-during-restore.html
+               fast/history/page-cache-navigate-during-restore.html
+
+        * history/CachedPage.cpp:
+        (WebCore::CachedPageRestorationScope::CachedPageRestorationScope): Added.
+        (WebCore::CachedPageRestorationScope::~CachedPageRestorationScope): Added.
+        (WebCore::CachedPage::restore): Don't instantiate ScriptDisallowedScope::InMainThread. Set isRestoringCachedPage
+        on the cached pate to release-assert that there won't be any attempt to put this very page back into the cache.
+        * history/PageCache.cpp:
+        (WebCore::canCachePage): Added a release assert to make sure the page which is in the process of being restored
+        from the page cache is not put into the page cache.
+        * page/Page.h:
+        (WebCore::Page::setIsRestoringCachedPage): Added.
+        (WebCore::Page::isRestoringCachedPage const): Added.
+
 2018-01-21  Eric Carlson  <[email protected]>
 
         Resign NowPlaying status when no media element is eligible

Modified: trunk/Source/WebCore/history/CachedPage.cpp (227279 => 227280)


--- trunk/Source/WebCore/history/CachedPage.cpp	2018-01-22 03:30:35 UTC (rev 227279)
+++ trunk/Source/WebCore/history/CachedPage.cpp	2018-01-22 03:39:36 UTC (rev 227280)
@@ -99,6 +99,23 @@
     }
 }
 
+class CachedPageRestorationScope {
+public:
+    CachedPageRestorationScope(Page& page)
+        : m_page(page)
+    {
+        m_page.setIsRestoringCachedPage(true);
+    }
+
+    ~CachedPageRestorationScope()
+    {
+        m_page.setIsRestoringCachedPage(false);
+    }
+
+private:
+    Page& m_page;
+};
+
 void CachedPage::restore(Page& page)
 {
     ASSERT(m_cachedMainFrame);
@@ -105,14 +122,9 @@
     ASSERT(m_cachedMainFrame->view()->frame().isMainFrame());
     ASSERT(!page.subframeCount());
 
-    {
-        // Do not dispatch DOM events as their _javascript_ listeners could cause the page to be put
-        // into the page cache before we have finished restoring it from the page cache.
-        ScriptDisallowedScope::InMainThread scriptDisallowedScope;
+    CachedPageRestorationScope restorationScope(page);
+    m_cachedMainFrame->open();
 
-        m_cachedMainFrame->open();
-    }
-    
     // Restore the focus appearance for the focused element.
     // FIXME: Right now we don't support pages w/ frames in the b/f cache.  This may need to be tweaked when we add support for that.
     Document* focusedDocument = page.focusController().focusedOrMainFrame().document();

Modified: trunk/Source/WebCore/history/PageCache.cpp (227279 => 227280)


--- trunk/Source/WebCore/history/PageCache.cpp	2018-01-22 03:30:35 UTC (rev 227279)
+++ trunk/Source/WebCore/history/PageCache.cpp	2018-01-22 03:39:36 UTC (rev 227280)
@@ -189,12 +189,14 @@
 
 static bool canCachePage(Page& page)
 {
+    RELEASE_ASSERT(!page.isRestoringCachedPage());
+
     unsigned indentLevel = 0;
     PCLOG("--------\n Determining if page can be cached:");
 
     DiagnosticLoggingClient& diagnosticLoggingClient = page.diagnosticLoggingClient();
     bool isCacheable = canCacheFrame(page.mainFrame(), diagnosticLoggingClient, indentLevel + 1);
-    
+
     if (!page.settings().usesPageCache() || page.isResourceCachingDisabled()) {
         PCLOG("   -Page settings says b/f cache disabled");
         logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isDisabledKey());

Modified: trunk/Source/WebCore/page/Page.h (227279 => 227280)


--- trunk/Source/WebCore/page/Page.h	2018-01-22 03:30:35 UTC (rev 227279)
+++ trunk/Source/WebCore/page/Page.h	2018-01-22 03:39:36 UTC (rev 227280)
@@ -374,6 +374,9 @@
     void setIsClosing() { m_isClosing = true; }
     bool isClosing() const { return m_isClosing; }
 
+    void setIsRestoringCachedPage(bool value) { m_isRestoringCachedPage = value; }
+    bool isRestoringCachedPage() const { return m_isRestoringCachedPage; }
+
     void addActivityStateChangeObserver(ActivityStateChangeObserver&);
     void removeActivityStateChangeObserver(ActivityStateChangeObserver&);
 
@@ -787,6 +790,7 @@
     PAL::SessionID m_sessionID;
 
     bool m_isClosing { false };
+    bool m_isRestoringCachedPage { false };
 
     MediaProducer::MediaStateFlags m_mediaState { MediaProducer::IsNotPlaying };
     
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to