Crash when navigating back to a page in PacheCache when one of its frames has been...
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Jan 2017 05:36:19 +0000 (05:36 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Jan 2017 05:36:19 +0000 (05:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167421
<rdar://problem/30188490>

Reviewed by Darin Adler.

Source/WebCore:

Disallow page caching of a page if:
1. The main window has an opener (i.e. it was opened via window.open)
2. It has ever used window.open()

This is because allowing page caching in this case would allow such
windows to script each other even after one of them entered Page
Cache. Allowing this is dangerous and easily causes crashes.

This is a short term workaround until we find a better solution to
the problem. One issue is this workaround is that navigating back
to a page that has an opener or used window.open() will not longer
get the page from PageCache. As a result, state may be lost upon
navigating back. However, we never guarantee that pages get page
cached, and Chrome does not have a PageCache.

Tests: fast/history/page-cache-after-window-open.html
       fast/history/page-cache-back-navigation-crash.html
       fast/history/page-cache-with-opener.html

* dom/Document.cpp:
(WebCore::Document::hasEverCalledWindowOpen):
(WebCore::Document::markHasCalledWindowOpen):
* dom/Document.h:
* history/PageCache.cpp:
(WebCore::canCachePage):
* page/DOMWindow.cpp:
(WebCore::DOMWindow::createWindow):
* page/DiagnosticLoggingKeys.cpp:
(WebCore::DiagnosticLoggingKeys::hasCalledWindowOpenKey):
(WebCore::DiagnosticLoggingKeys::hasOpenerKey):
* page/DiagnosticLoggingKeys.h:
* page/Page.cpp:
(WebCore::Page::openedByWindowOpen):
* page/Page.h:
* page/Settings.in:

Source/WebKit/mac:

Add a new setting allowing layout tests to enable PageCache in a window
that has an opener, for convenience.

* WebView/WebPreferenceKeysPrivate.h:
* WebView/WebPreferences.mm:
(+[WebPreferences initialize]):
(-[WebPreferences allowsPageCacheWithWindowOpener]):
(-[WebPreferences setAllowsPageCacheWithWindowOpener:]):
* WebView/WebPreferencesPrivate.h:
* WebView/WebView.mm:
(-[WebView _preferencesChanged:]):

Source/WebKit/win:

Add a new setting allowing layout tests to enable PageCache in a window
that has an opener, for convenience.

* WebPreferenceKeysPrivate.h:
* WebPreferences.cpp:
(WebPreferences::initializeDefaultSettings):
* WebPreferences.h:

Source/WebKit2:

Add a new setting allowing layout tests to enable PageCache in a window
that has an opener, for convenience.

* Shared/WebPreferencesDefinitions.h:
* UIProcess/API/C/WKPreferences.cpp:
(WKPreferencesSetAllowsPageCacheWithWindowOpener):
(WKPreferencesGetAllowsPageCacheWithWindowOpener):
* UIProcess/API/C/WKPreferencesRefPrivate.h:
* WebProcess/InjectedBundle/InjectedBundle.cpp:
(WebKit::InjectedBundle::overrideBoolPreferenceForTestRunner):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updatePreferences):

Tools:

Add a new setting allowing layout tests to enable PageCache in a window
that has an opener, for convenience.

* DumpRenderTree/mac/DumpRenderTree.mm:
(resetWebPreferencesToConsistentValues):
* DumpRenderTree/win/DumpRenderTree.cpp:
(resetWebPreferencesToConsistentValues):
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetPreferencesToConsistentValues):

LayoutTests:

* fast/history/page-cache-after-window-open-expected.txt: Added.
* fast/history/page-cache-after-window-open.html: Added.
* fast/history/page-cache-back-navigation-crash-expected.txt: Added.
* fast/history/page-cache-back-navigation-crash.html: Added.
* fast/history/page-cache-with-opener-expected.txt: Added.
* fast/history/page-cache-with-opener.html: Added.
* fast/history/resources/page-cache-window-with-iframe.html: Added.
* fast/history/resources/page-cache-window-with-opener.html: Added.
Add layout test coverage.

* editing/mac/input/unconfirmed-text-navigation-with-page-cache.html:
* fast/harness/page-cache-crash-on-data-urls.html:
* fast/harness/use-page-cache.html:
* fast/history/page-cache-after-window-open-expected.txt: Added.
* fast/history/page-cache-after-window-open.html: Added.
* fast/history/page-cache-with-opener-expected.txt: Added.
* fast/history/page-cache-with-opener.html: Added.
* fast/history/resources/page-cache-window-with-opener.html: Added.
* fast/loader/stateobjects/no-popstate-when-back-to-stateless-entry-with-page-cache.html:
* fast/loader/stateobjects/popstate-fires-with-page-cache.html:
* tiled-drawing/tiled-drawing-scroll-position-page-cache-restoration.html:
These tests relied on using window.open() to test PageCache for convenience. They now
need to override a setting in order to be allowed to do so.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@211254 268f45cc-cd09-0410-ab3c-d52691b4dbfc

46 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/mac/input/unconfirmed-text-navigation-with-page-cache.html
LayoutTests/fast/harness/page-cache-crash-on-data-urls.html
LayoutTests/fast/harness/use-page-cache.html
LayoutTests/fast/history/page-cache-after-window-open-expected.txt [new file with mode: 0644]
LayoutTests/fast/history/page-cache-after-window-open.html [new file with mode: 0644]
LayoutTests/fast/history/page-cache-back-navigation-crash-expected.txt [new file with mode: 0644]
LayoutTests/fast/history/page-cache-back-navigation-crash.html [new file with mode: 0644]
LayoutTests/fast/history/page-cache-with-opener-expected.txt [new file with mode: 0644]
LayoutTests/fast/history/page-cache-with-opener.html [new file with mode: 0644]
LayoutTests/fast/history/resources/page-cache-window-with-iframe.html [new file with mode: 0644]
LayoutTests/fast/history/resources/page-cache-window-with-opener.html [new file with mode: 0644]
LayoutTests/fast/loader/stateobjects/no-popstate-when-back-to-stateless-entry-with-page-cache.html
LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache.html
LayoutTests/tiled-drawing/tiled-drawing-scroll-position-page-cache-restoration.html
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/history/PageCache.cpp
Source/WebCore/page/DOMWindow.cpp
Source/WebCore/page/DiagnosticLoggingKeys.cpp
Source/WebCore/page/DiagnosticLoggingKeys.h
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/page/Settings.in
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebView/WebPreferenceKeysPrivate.h
Source/WebKit/mac/WebView/WebPreferences.mm
Source/WebKit/mac/WebView/WebPreferencesPrivate.h
Source/WebKit/mac/WebView/WebView.mm
Source/WebKit/win/ChangeLog
Source/WebKit/win/Interfaces/IWebPreferencesPrivate.idl
Source/WebKit/win/WebPreferenceKeysPrivate.h
Source/WebKit/win/WebPreferences.cpp
Source/WebKit/win/WebPreferences.h
Source/WebKit/win/WebView.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/WebPreferencesDefinitions.h
Source/WebKit2/UIProcess/API/C/WKPreferences.cpp
Source/WebKit2/UIProcess/API/C/WKPreferencesRefPrivate.h
Source/WebKit2/WebProcess/InjectedBundle/InjectedBundle.cpp
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Tools/ChangeLog
Tools/DumpRenderTree/mac/DumpRenderTree.mm
Tools/DumpRenderTree/win/DumpRenderTree.cpp
Tools/WebKitTestRunner/TestController.cpp

index b1499d5..1ce45a3 100644 (file)
@@ -1,3 +1,35 @@
+2017-01-26  Chris Dumez  <cdumez@apple.com>
+
+        Crash when navigating back to a page in PacheCache when one of its frames has been removed
+        https://bugs.webkit.org/show_bug.cgi?id=167421
+        <rdar://problem/30188490>
+
+        Reviewed by Darin Adler.
+
+        * fast/history/page-cache-after-window-open-expected.txt: Added.
+        * fast/history/page-cache-after-window-open.html: Added.
+        * fast/history/page-cache-back-navigation-crash-expected.txt: Added.
+        * fast/history/page-cache-back-navigation-crash.html: Added.
+        * fast/history/page-cache-with-opener-expected.txt: Added.
+        * fast/history/page-cache-with-opener.html: Added.
+        * fast/history/resources/page-cache-window-with-iframe.html: Added.
+        * fast/history/resources/page-cache-window-with-opener.html: Added.
+        Add layout test coverage.
+
+        * editing/mac/input/unconfirmed-text-navigation-with-page-cache.html:
+        * fast/harness/page-cache-crash-on-data-urls.html:
+        * fast/harness/use-page-cache.html:
+        * fast/history/page-cache-after-window-open-expected.txt: Added.
+        * fast/history/page-cache-after-window-open.html: Added.
+        * fast/history/page-cache-with-opener-expected.txt: Added.
+        * fast/history/page-cache-with-opener.html: Added.
+        * fast/history/resources/page-cache-window-with-opener.html: Added.
+        * fast/loader/stateobjects/no-popstate-when-back-to-stateless-entry-with-page-cache.html:
+        * fast/loader/stateobjects/popstate-fires-with-page-cache.html:
+        * tiled-drawing/tiled-drawing-scroll-position-page-cache-restoration.html:
+        These tests relied on using window.open() to test PageCache for convenience. They now
+        need to override a setting in order to be allowed to do so.
+
 2017-01-26  Youenn Fablet  <youennf@gmail.com>
 
         [WebRTC] Add a LibWebRTC mock for testing
index 4af33ab..ba4fef1 100644 (file)
@@ -10,6 +10,7 @@ jsTestIsAsync = true;
 if (window.testRunner) {
     testRunner.setCanOpenWindows();
     testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.overridePreference("WebKitAllowsPageCacheWithWindowOpenerKey", 1);
 }
 
 // Window we will be controlling.
index 737e4ca..87688c3 100644 (file)
@@ -22,6 +22,7 @@ function test()
         testRunner.waitUntilDone();
         testRunner.setCanOpenWindows();
         testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+        testRunner.overridePreference("WebKitAllowsPageCacheWithWindowOpenerKey", 1);
     }
     log("open page with data urls");
     window.open("resources/cached-page-with-data-urls.html");
index 2564b60..487127d 100644 (file)
@@ -28,6 +28,7 @@ function test()
         testRunner.waitUntilDone();
         testRunner.setCanOpenWindows();
         testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+        testRunner.overridePreference("WebKitAllowsPageCacheWithWindowOpenerKey", 1);
     }
     log("open page-1");
     window.open("resources/cached-page-1.html");
diff --git a/LayoutTests/fast/history/page-cache-after-window-open-expected.txt b/LayoutTests/fast/history/page-cache-after-window-open-expected.txt
new file mode 100644 (file)
index 0000000..bd596fa
--- /dev/null
@@ -0,0 +1,11 @@
+Tests that a page that has called window.open() does not go into the page cache.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+pageshow - not from cache
+PASS Page was not restored from page cache
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/history/page-cache-after-window-open.html b/LayoutTests/fast/history/page-cache-after-window-open.html
new file mode 100644 (file)
index 0000000..dc10019
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description('Tests that a page that has called window.open() does not go into the page cache.');
+window.jsTestIsAsync = true;
+
+if (window.testRunner) {
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.setCanOpenWindows();
+}
+
+window.addEventListener("pageshow", function(event) {
+    debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");
+
+    if (!window.sessionStorage.page_cache_after_window_open_test_started)
+        return;
+
+    delete window.sessionStorage.page_cache_after_window_open_test_started;
+
+    if (event.persisted)
+        testFailed("Page did enter and was restored from the page cache");
+    else
+        testPassed("Page was not restored from page cache");
+
+    finishJSTest();
+}, false);
+
+window.addEventListener("pagehide", function(event) {
+    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache");
+    if (event.persisted) {
+        testFailed("Page entered the page cache.");
+        finishJSTest();
+    }
+}, false);
+
+window.addEventListener('load', function() {
+    newWindow = open("about:blank", "one");
+    otherWindowDocument = newWindow.document;
+
+    setTimeout(function() {
+        // Force a back navigation back to this page.
+        window.sessionStorage.page_cache_after_window_open_test_started = true;
+        window.location.href = "resources/page-cache-helper.html";
+    }, 0);
+}, false);
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/history/page-cache-back-navigation-crash-expected.txt b/LayoutTests/fast/history/page-cache-back-navigation-crash-expected.txt
new file mode 100644 (file)
index 0000000..36db406
--- /dev/null
@@ -0,0 +1,9 @@
+Tests that removing a frame in a detached document after navigation does not cause a crash on navigating back
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/history/page-cache-back-navigation-crash.html b/LayoutTests/fast/history/page-cache-back-navigation-crash.html
new file mode 100644 (file)
index 0000000..7a59841
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description('Tests that removing a frame in a detached document after navigation does not cause a crash on navigating back');
+window.jsTestIsAsync = true;
+
+if (window.testRunner) {
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.setCanOpenWindows();
+}
+
+function removeFrameInChild()
+{
+    setTimeout(function() {
+        testFrame.remove();
+    }, 0);
+}
+
+window.addEventListener('load', function() {
+    newWindow = open("resources/page-cache-window-with-iframe.html", "one");
+    newWindow.onload = function () {
+        newWindow.onload = null;
+        otherWindowDocument = newWindow.document;
+        testFrame = otherWindowDocument.getElementsByTagName("iframe")[0];
+    }
+}, false);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/history/page-cache-with-opener-expected.txt b/LayoutTests/fast/history/page-cache-with-opener-expected.txt
new file mode 100644 (file)
index 0000000..379b189
--- /dev/null
@@ -0,0 +1,10 @@
+Tests that a page that has a window opener does not go into the page cache.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Page was not restored from page cache
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/history/page-cache-with-opener.html b/LayoutTests/fast/history/page-cache-with-opener.html
new file mode 100644 (file)
index 0000000..dbb3af1
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description('Tests that a page that has a window opener does not go into the page cache.');
+window.jsTestIsAsync = true;
+
+if (window.testRunner) {
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.setCanOpenWindows();
+}
+
+window.addEventListener('load', function() {
+    newWindow = open("resources/page-cache-window-with-opener.html", "one");
+    otherWindowDocument = newWindow.document;
+}, false);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/history/resources/page-cache-window-with-iframe.html b/LayoutTests/fast/history/resources/page-cache-window-with-iframe.html
new file mode 100644 (file)
index 0000000..c330e4e
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../../resources/js-test-pre.js"></script>
+<iframe src="about:blank"></iframe>
+<script>
+if (window.testRunner)
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+
+window.addEventListener("pageshow", function(event) {
+    debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");
+
+    if (!window.sessionStorage.page_cache_window_iframe_removed_test_started)
+        return;
+
+    delete window.sessionStorage.page_cache_window_iframe_removed_test_started;
+
+    setTimeout(function() {
+        window.opener.finishJSTest();
+    }, 0);
+}, false);
+
+window.addEventListener("pagehide", function(event) {
+    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache");
+}, false);
+
+window.addEventListener('load', function() {
+    setTimeout(function() {
+         window.opener.removeFrameInChild();
+        // Force a back navigation back to this page.
+        window.sessionStorage.page_cache_window_iframe_removed_test_started = true;
+        window.location.href = "page-cache-helper.html";
+    }, 0);
+}, false);
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/history/resources/page-cache-window-with-opener.html b/LayoutTests/fast/history/resources/page-cache-window-with-opener.html
new file mode 100644 (file)
index 0000000..652779a
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../../resources/js-test-pre.js"></script>
+<script>
+if (window.testRunner)
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+
+window.addEventListener("pageshow", function(event) {
+    debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");
+
+    if (!window.sessionStorage.page_cache_window_opener_test_started)
+        return;
+
+    delete window.sessionStorage.page_cache_window_opener_test_started;
+
+    if (event.persisted)
+        window.opener.testFailed("Page did enter and was restored from the page cache");
+    else
+        window.opener.testPassed("Page was not restored from page cache");
+
+    window.opener.finishJSTest();
+}, false);
+
+window.addEventListener("pagehide", function(event) {
+    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache");
+    if (event.persisted) {
+        window.opener.testFailed("Page entered the page cache.");
+        window.opener.finishJSTest();
+    }
+}, false);
+
+window.addEventListener('load', function() {
+    setTimeout(function() {
+        // Force a back navigation back to this page.
+        window.sessionStorage.page_cache_window_opener_test_started = true;
+        window.location.href = "page-cache-helper.html";
+    }, 0);
+}, false);
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
index 84d0e10..3888e15 100644 (file)
@@ -14,6 +14,7 @@ onload = function()
     if (window.testRunner) {
         testRunner.setCanOpenWindows();
         testRunner.overridePreference('WebKitUsesPageCachePreferenceKey', 1);
+        testRunner.overridePreference("WebKitAllowsPageCacheWithWindowOpenerKey", 1);
     }
     testWindow = window.open('resources/no-popstate-when-back-to-stateless-entry-1.html');
     if (!testWindow)
@@ -62,4 +63,4 @@ function timeOut()
 var jsTestIsAsync = true;
 </script>
 <script src="../../../resources/js-test-post.js"></script>
-</html>
\ No newline at end of file
+</html>
index 61df09b..ccb0a2d 100644 (file)
@@ -14,6 +14,7 @@ onload = function()
     if (window.testRunner) {
         testRunner.setCanOpenWindows();
         testRunner.overridePreference('WebKitUsesPageCachePreferenceKey', 1);
+        testRunner.overridePreference("WebKitAllowsPageCacheWithWindowOpenerKey", 1);
     }
     testWindow = window.open('resources/popstate-fires-with-page-cache-1.html');
     if (!testWindow)
index 6a2851f..ce22332 100644 (file)
@@ -7,6 +7,7 @@
             testRunner.waitUntilDone();
             testRunner.setCanOpenWindows();
             testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+            testRunner.overridePreference("WebKitAllowsPageCacheWithWindowOpenerKey", 1);
         }
 
         window.finishedTest = function (layerTree)
index 437c39b..1b2fbce 100644 (file)
@@ -1,3 +1,47 @@
+2017-01-26  Chris Dumez  <cdumez@apple.com>
+
+        Crash when navigating back to a page in PacheCache when one of its frames has been removed
+        https://bugs.webkit.org/show_bug.cgi?id=167421
+        <rdar://problem/30188490>
+
+        Reviewed by Darin Adler.
+
+        Disallow page caching of a page if:
+        1. The main window has an opener (i.e. it was opened via window.open)
+        2. It has ever used window.open()
+
+        This is because allowing page caching in this case would allow such
+        windows to script each other even after one of them entered Page
+        Cache. Allowing this is dangerous and easily causes crashes.
+
+        This is a short term workaround until we find a better solution to
+        the problem. One issue is this workaround is that navigating back
+        to a page that has an opener or used window.open() will not longer
+        get the page from PageCache. As a result, state may be lost upon
+        navigating back. However, we never guarantee that pages get page
+        cached, and Chrome does not have a PageCache.
+
+        Tests: fast/history/page-cache-after-window-open.html
+               fast/history/page-cache-back-navigation-crash.html
+               fast/history/page-cache-with-opener.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::hasEverCalledWindowOpen):
+        (WebCore::Document::markHasCalledWindowOpen):
+        * dom/Document.h:
+        * history/PageCache.cpp:
+        (WebCore::canCachePage):
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::createWindow):
+        * page/DiagnosticLoggingKeys.cpp:
+        (WebCore::DiagnosticLoggingKeys::hasCalledWindowOpenKey):
+        (WebCore::DiagnosticLoggingKeys::hasOpenerKey):
+        * page/DiagnosticLoggingKeys.h:
+        * page/Page.cpp:
+        (WebCore::Page::openedByWindowOpen):
+        * page/Page.h:
+        * page/Settings.in:
+
 2017-01-26  Youenn Fablet  <youennf@gmail.com>
 
         [WebRTC] Add a LibWebRTC mock for testing
index 7725bda..d0aa406 100644 (file)
@@ -835,6 +835,23 @@ bool Document::hasManifest() const
     return documentElement() && documentElement()->hasTagName(htmlTag) && documentElement()->hasAttributeWithoutSynchronization(manifestAttr);
 }
 
+bool Document::hasEverCalledWindowOpen() const
+{
+    auto& topDocument = this->topDocument();
+    if (&topDocument == this)
+        return m_hasEverCalledWindowOpen;
+    return topDocument.hasEverCalledWindowOpen();
+}
+
+void Document::markHasCalledWindowOpen()
+{
+    auto& topDocument = this->topDocument();
+    if (&topDocument == this)
+        m_hasEverCalledWindowOpen = true;
+    else
+        topDocument.markHasCalledWindowOpen();
+}
+
 DocumentType* Document::doctype() const
 {
     for (Node* node = firstChild(); node; node = node->nextSibling()) {
index 28c0154..f0cade4 100644 (file)
@@ -367,6 +367,9 @@ public:
     WEBCORE_EXPORT bool hasFocus() const;
 
     bool hasManifest() const;
+
+    bool hasEverCalledWindowOpen() const;
+    void markHasCalledWindowOpen();
     
     WEBCORE_EXPORT ExceptionOr<Ref<Element>> createElementForBindings(const AtomicString& tagName);
     WEBCORE_EXPORT Ref<DocumentFragment> createDocumentFragment();
@@ -1469,6 +1472,7 @@ private:
     bool m_bParsing;
 
     Timer m_styleRecalcTimer;
+    bool m_hasEverCalledWindowOpen { false };
     bool m_pendingStyleRecalcShouldForce;
     bool m_inStyleRecalc;
     bool m_closeAfterStyleRecalc;
index 3ca6b07..becae26 100644 (file)
@@ -191,6 +191,19 @@ static bool canCachePage(Page& page)
 
     DiagnosticLoggingClient& diagnosticLoggingClient = page.diagnosticLoggingClient();
     bool isCacheable = canCacheFrame(page.mainFrame(), diagnosticLoggingClient, indentLevel + 1);
+
+    if (page.openedByWindowOpen() && !page.settings().allowsPageCacheWithWindowOpener()) {
+        PCLOG("   -Page has been opened via window.open()");
+        logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasOpenerKey());
+        isCacheable = false;
+    }
+
+    auto* topDocument = page.mainFrame().document();
+    if (topDocument && topDocument->hasEverCalledWindowOpen()) {
+        PCLOG("   -Page has called window.open()");
+        logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasCalledWindowOpenKey());
+        isCacheable = false;
+    }
     
     if (!page.settings().usesPageCache() || page.isResourceCachingDisabled()) {
         PCLOG("   -Page settings says b/f cache disabled");
index 095e5dd..8616e09 100644 (file)
@@ -2195,6 +2195,8 @@ RefPtr<Frame> DOMWindow::createWindow(const String& urlString, const AtomicStrin
 
     newFrame->loader().setOpener(&openerFrame);
     newFrame->page()->setOpenedByDOM();
+    if (auto* openerDocument = openerFrame.document())
+        openerDocument->markHasCalledWindowOpen();
 
     if (newFrame->document()->domWindow()->isInsecureScriptAccess(activeWindow, completedURL))
         return newFrame;
index 7baa7a2..25efddd 100644 (file)
@@ -663,6 +663,16 @@ String DiagnosticLoggingKeys::fontKey()
     return ASCIILiteral("font");
 }
 
+String DiagnosticLoggingKeys::hasCalledWindowOpenKey()
+{
+    return ASCIILiteral("hasCalledWindowOpen");
+}
+
+String DiagnosticLoggingKeys::hasOpenerKey()
+{
+    return ASCIILiteral("hasOpener");
+}
+
 String DiagnosticLoggingKeys::prunedDueToMemoryPressureKey()
 {
     return ASCIILiteral("pruned.memoryPressure");
index cbd08fc..53d0da4 100644 (file)
@@ -55,6 +55,8 @@ public:
     WEBCORE_EXPORT static String entryWronglyNotWarmedUpKey();
     static String expiredKey();
     static String fontKey();
+    static String hasCalledWindowOpenKey();
+    static String hasOpenerKey();
     static String hasPluginsKey();
     static String httpsNoStoreKey();
     static String imageKey();
index b316d66..e60c871 100644 (file)
@@ -467,6 +467,17 @@ void Page::setOpenedByDOM()
     m_openedByDOM = true;
 }
 
+bool Page::openedByWindowOpen() const
+{
+    auto* document = m_mainFrame->document();
+    if (!document)
+        return false;
+    auto* window = document->domWindow();
+    if (!window)
+        return false;
+    return window->opener();
+}
+
 void Page::goToItem(HistoryItem& item, FrameLoadType type)
 {
     // stopAllLoaders may end up running onload handlers, which could cause further history traversals that may lead to the passed in HistoryItem
index 440d1ab..c9ca095 100644 (file)
@@ -179,6 +179,8 @@ public:
     bool openedByDOM() const;
     void setOpenedByDOM();
 
+    bool openedByWindowOpen() const;
+
     WEBCORE_EXPORT void goToItem(HistoryItem&, FrameLoadType);
 
     WEBCORE_EXPORT void setGroupName(const String&);
index 35a9281..770088e 100644 (file)
@@ -285,3 +285,5 @@ animatedImageAsyncDecodingEnabled initial=true
 shouldSuppressKeyboardInputDuringProvisionalNavigation initial=false
 
 langAttributeAwareFormControlUIEnabled initial=false
+
+allowsPageCacheWithWindowOpener initial=false
index da6938c..fbfdf03 100644 (file)
@@ -1,3 +1,23 @@
+2017-01-26  Chris Dumez  <cdumez@apple.com>
+
+        Crash when navigating back to a page in PacheCache when one of its frames has been removed
+        https://bugs.webkit.org/show_bug.cgi?id=167421
+        <rdar://problem/30188490>
+
+        Reviewed by Darin Adler.
+
+        Add a new setting allowing layout tests to enable PageCache in a window
+        that has an opener, for convenience.
+
+        * WebView/WebPreferenceKeysPrivate.h:
+        * WebView/WebPreferences.mm:
+        (+[WebPreferences initialize]):
+        (-[WebPreferences allowsPageCacheWithWindowOpener]):
+        (-[WebPreferences setAllowsPageCacheWithWindowOpener:]):
+        * WebView/WebPreferencesPrivate.h:
+        * WebView/WebView.mm:
+        (-[WebView _preferencesChanged:]):
+
 2017-01-26  Keith Miller  <keith_miller@apple.com>
 
         classInfo should take a VM so it is not materialized from the object on each call
index 228c931..c35de82 100644 (file)
@@ -83,6 +83,7 @@
 #define WebAutomaticSpellingCorrectionEnabled @"WebAutomaticSpellingCorrectionEnabled"
 #define WebKitDOMPasteAllowedPreferenceKey @"WebKitDOMPasteAllowedPreferenceKey"
 #define WebKitUsesPageCachePreferenceKey @"WebKitUsesPageCachePreferenceKey"
+#define WebKitAllowsPageCacheWithWindowOpenerKey @"WebKitAllowsPageCacheWithWindowOpenerKey"
 #define WebKitPageCacheSupportsPluginsPreferenceKey @"WebKitPageCacheSupportsPluginsPreferenceKey"
 #define WebKitFTPDirectoryTemplatePath @"WebKitFTPDirectoryTemplatePath"
 #define WebKitForceFTPDirectoryListings @"WebKitForceFTPDirectoryListings"
index 55a28ad..430693b 100644 (file)
@@ -478,6 +478,7 @@ public:
         [NSNumber numberWithBool:NO],   WebKitDOMPasteAllowedPreferenceKey,
 #endif
         [NSNumber numberWithBool:YES],  WebKitUsesPageCachePreferenceKey,
+        [NSNumber numberWithBool:NO],   WebKitAllowsPageCacheWithWindowOpenerKey,
         [NSNumber numberWithInt:cacheModelForMainBundle()], WebKitCacheModelPreferenceKey,
         [NSNumber numberWithBool:YES],  WebKitPageCacheSupportsPluginsPreferenceKey,
         [NSNumber numberWithBool:NO],   WebKitDeveloperExtrasEnabledPreferenceKey,
@@ -2445,6 +2446,16 @@ static NSString *classIBCreatorID = nil;
 
 }
 
+- (BOOL)allowsPageCacheWithWindowOpener
+{
+    return [self _boolValueForKey:WebKitAllowsPageCacheWithWindowOpenerKey];
+}
+
+- (void)setAllowsPageCacheWithWindowOpener:(BOOL)flag
+{
+    [self _setBoolValue:flag forKey:WebKitAllowsPageCacheWithWindowOpenerKey];
+}
+
 #if PLATFORM(IOS)
 - (void)_invalidateCachedPreferences
 {
index d3c82bd..6f94ce4 100644 (file)
@@ -298,6 +298,9 @@ extern NSString *WebPreferencesCacheModelChangedInternalNotification;
 - (NSString *)pictographFontFamily;
 - (void)setPictographFontFamily:(NSString *)family;
 
+- (BOOL)allowsPageCacheWithWindowOpener;
+- (void)setAllowsPageCacheWithWindowOpener:(BOOL)flag;
+
 - (BOOL)pageCacheSupportsPlugins;
 - (void)setPageCacheSupportsPlugins:(BOOL)flag;
 
index 99fefa6..b062f36 100644 (file)
@@ -2670,6 +2670,7 @@ static bool needsSelfRetainWhileLoadingQuirk()
     settings.setTextDirectionSubmenuInclusionBehavior(core([preferences textDirectionSubmenuInclusionBehavior]));
     settings.setDOMPasteAllowed([preferences isDOMPasteAllowed]);
     settings.setUsesPageCache([self usesPageCache]);
+    settings.setAllowsPageCacheWithWindowOpener([preferences allowsPageCacheWithWindowOpener]);
     settings.setPageCacheSupportsPlugins([preferences pageCacheSupportsPlugins]);
     settings.setBackForwardCacheExpirationInterval([preferences _backForwardCacheExpirationInterval]);
 
index 5b7416a..2016415 100644 (file)
@@ -1,3 +1,19 @@
+2017-01-26  Chris Dumez  <cdumez@apple.com>
+
+        Crash when navigating back to a page in PacheCache when one of its frames has been removed
+        https://bugs.webkit.org/show_bug.cgi?id=167421
+        <rdar://problem/30188490>
+
+        Reviewed by Darin Adler.
+
+        Add a new setting allowing layout tests to enable PageCache in a window
+        that has an opener, for convenience.
+
+        * WebPreferenceKeysPrivate.h:
+        * WebPreferences.cpp:
+        (WebPreferences::initializeDefaultSettings):
+        * WebPreferences.h:
+
 2017-01-26  Keith Miller  <keith_miller@apple.com>
 
         classInfo should take a VM so it is not materialized from the object on each call
index 8fd1d92..834d5a9 100644 (file)
@@ -91,6 +91,9 @@ interface IWebPreferencesPrivate : IUnknown
     HRESULT experimentalNotificationsEnabled([out, retval] BOOL *enabled);
     HRESULT setExperimentalNotificationsEnabled([in] BOOL enabled);
 
+    HRESULT allowsPageCacheWithWindowOpener([out, retval] BOOL* usesPageCache);
+    HRESULT setAllowsPageCacheWithWindowOpener([in] BOOL usesPageCache);
+
     HRESULT setShouldUseHighResolutionTimers([in] BOOL useHighResolutionTimers);
     HRESULT shouldUseHighResolutionTimers([out, retval] BOOL* useHighResolutionTimers);
 
index 58bb881..0513fad 100644 (file)
@@ -68,6 +68,7 @@
 #define WebKitIconDatabaseLocationKey "WebKitIconDatabaseLocation"
 #define WebKitIconDatabaseEnabledPreferenceKey "WebKitIconDatabaseEnabled"
 #define WebKitUsesPageCachePreferenceKey "WebKitUsesPageCachePreferenceKey"
+#define WebKitAllowsPageCacheWithWindowOpenerKey "WebKitAllowsPageCacheWithWindowOpenerKey"
 #define WebKitCacheModelPreferenceKey "WebKitCacheModelPreferenceKey"
 #define WebKitLocalStorageDatabasePathPreferenceKey "WebKitLocalStorageDatabasePath"
 #define WebKitHyperlinkAuditingEnabledPreferenceKey "WebKitHyperlinkAuditingEnabled"
index e653128..a3c872b 100644 (file)
@@ -262,6 +262,7 @@ void WebPreferences::initializeDefaultSettings()
     CFDictionaryAddValue(defaults, CFSTR(WebGrammarCheckingEnabledPreferenceKey), kCFBooleanFalse);
     CFDictionaryAddValue(defaults, CFSTR(AllowContinuousSpellCheckingPreferenceKey), kCFBooleanTrue);
     CFDictionaryAddValue(defaults, CFSTR(WebKitUsesPageCachePreferenceKey), kCFBooleanTrue);
+    CFDictionaryAddValue(defaults, CFSTR(WebKitAllowsPageCacheWithWindowOpenerKey), kCFBooleanFalse);
     CFDictionaryAddValue(defaults, CFSTR(WebKitLocalStorageDatabasePathPreferenceKey), CFSTR(""));
 
     RetainPtr<CFStringRef> cacheModelRef = adoptCF(CFStringCreateWithFormat(0, 0, CFSTR("%d"), WebCacheModelDocumentViewer));
@@ -1606,6 +1607,20 @@ HRESULT WebPreferences::experimentalNotificationsEnabled(_Out_ BOOL* enabled)
     return S_OK;
 }
 
+HRESULT WebPreferences::setAllowsPageCacheWithWindowOpener(BOOL value)
+{
+    setBoolValue(WebKitAllowsPageCacheWithWindowOpenerKey, value);
+    return S_OK;
+}
+
+HRESULT WebPreferences::allowsPageCacheWithWindowOpener(_Out_ BOOL* enabled)
+{
+    if (!enabled)
+        return E_POINTER;
+    *enabled = boolValueForKey(WebKitAllowsPageCacheWithWindowOpenerKey);
+    return S_OK;
+}
+
 HRESULT WebPreferences::setZoomsTextOnly(BOOL zoomsTextOnly)
 {
     setBoolValue(WebKitZoomsTextOnlyPreferenceKey, zoomsTextOnly);
index d0940af..929e9ad 100644 (file)
@@ -159,6 +159,8 @@ public:
     virtual HRESULT STDMETHODCALLTYPE setLocalStorageDatabasePath(_In_ BSTR);
     virtual HRESULT STDMETHODCALLTYPE experimentalNotificationsEnabled(_Out_ BOOL*);
     virtual HRESULT STDMETHODCALLTYPE setExperimentalNotificationsEnabled(BOOL);
+    virtual HRESULT STDMETHODCALLTYPE allowsPageCacheWithWindowOpener(_Out_ BOOL*);
+    virtual HRESULT STDMETHODCALLTYPE setAllowsPageCacheWithWindowOpener(BOOL);
 
     // These two methods are no-ops, and only retained to keep
     // the Interface consistent. DO NOT USE THEM.
index 6781173..ec042a7 100644 (file)
@@ -5404,6 +5404,11 @@ HRESULT WebView::notifyPreferencesChanged(IWebNotification* notification)
         return hr;
     settings.setExperimentalNotificationsEnabled(enabled);
 
+    hr = prefsPrivate->allowsPageCacheWithWindowOpener(&enabled);
+    if (FAILED(hr))
+        return hr;
+    settings.setAllowsPageCacheWithWindowOpener(enabled);
+
     hr = prefsPrivate->isWebSecurityEnabled(&enabled);
     if (FAILED(hr))
         return hr;
index 75ca0ff..52befe8 100644 (file)
@@ -1,3 +1,24 @@
+2017-01-26  Chris Dumez  <cdumez@apple.com>
+
+        Crash when navigating back to a page in PacheCache when one of its frames has been removed
+        https://bugs.webkit.org/show_bug.cgi?id=167421
+        <rdar://problem/30188490>
+
+        Reviewed by Darin Adler.
+
+        Add a new setting allowing layout tests to enable PageCache in a window
+        that has an opener, for convenience.
+
+        * Shared/WebPreferencesDefinitions.h:
+        * UIProcess/API/C/WKPreferences.cpp:
+        (WKPreferencesSetAllowsPageCacheWithWindowOpener):
+        (WKPreferencesGetAllowsPageCacheWithWindowOpener):
+        * UIProcess/API/C/WKPreferencesRefPrivate.h:
+        * WebProcess/InjectedBundle/InjectedBundle.cpp:
+        (WebKit::InjectedBundle::overrideBoolPreferenceForTestRunner):
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::updatePreferences):
+
 2017-01-26  Keith Miller  <keith_miller@apple.com>
 
         classInfo should take a VM so it is not materialized from the object on each call
index e0a6482..283b231 100644 (file)
     macro(WebArchiveDebugModeEnabled, webArchiveDebugModeEnabled, Bool, bool, false, "", "") \
     macro(LocalFileContentSniffingEnabled, localFileContentSniffingEnabled, Bool, bool, false, "", "") \
     macro(UsesPageCache, usesPageCache, Bool, bool, true, "", "") \
+    macro(AllowsPageCacheWithWindowOpener, allowsPageCacheWithWindowOpener, Bool, bool, false, "", "") \
     macro(PageCacheSupportsPlugins, pageCacheSupportsPlugins, Bool, bool, true, "", "") \
     macro(AuthorAndUserStylesEnabled, authorAndUserStylesEnabled, Bool, bool, true, "", "") \
     macro(PaginateDuringLayoutEnabled, paginateDuringLayoutEnabled, Bool, bool, false, "", "") \
index ed3a6ea..2c8beb5 100644 (file)
@@ -623,6 +623,16 @@ bool WKPreferencesGetPageCacheEnabled(WKPreferencesRef preferencesRef)
     return toImpl(preferencesRef)->usesPageCache();
 }
 
+void WKPreferencesSetAllowsPageCacheWithWindowOpener(WKPreferencesRef preferencesRef, bool enabled)
+{
+    toImpl(preferencesRef)->setAllowsPageCacheWithWindowOpener(enabled);
+}
+
+bool WKPreferencesGetAllowsPageCacheWithWindowOpener(WKPreferencesRef preferencesRef)
+{
+    return toImpl(preferencesRef)->allowsPageCacheWithWindowOpener();
+}
+
 void WKPreferencesSetPageCacheSupportsPlugins(WKPreferencesRef preferencesRef, bool pageCacheSupportsPlugins)
 {
     toImpl(preferencesRef)->setPageCacheSupportsPlugins(pageCacheSupportsPlugins);
index c04e458..f8264bb 100644 (file)
@@ -143,6 +143,10 @@ WK_EXPORT bool WKPreferencesGetLocalFileContentSniffingEnabled(WKPreferencesRef
 WK_EXPORT void WKPreferencesSetPageCacheEnabled(WKPreferencesRef preferences, bool enabled);
 WK_EXPORT bool WKPreferencesGetPageCacheEnabled(WKPreferencesRef preferences);
 
+// Defaults to false.
+WK_EXPORT void WKPreferencesSetAllowsPageCacheWithWindowOpener(WKPreferencesRef preferences, bool enabled);
+WK_EXPORT bool WKPreferencesGetAllowsPageCacheWithWindowOpener(WKPreferencesRef preferences);
+
 // Defaults to true.
 WK_EXPORT void WKPreferencesSetPageCacheSupportsPlugins(WKPreferencesRef preferences, bool pageCacheSupportsPlugins);
 WK_EXPORT bool WKPreferencesGetPageCacheSupportsPlugins(WKPreferencesRef preferences);
index eb034aa..7bdd10a 100644 (file)
@@ -246,6 +246,7 @@ void InjectedBundle::overrideBoolPreferenceForTestRunner(WebPageGroupProxy* page
     macro(WebKitPageCacheSupportsPluginsPreferenceKey, PageCacheSupportsPlugins, pageCacheSupportsPlugins) \
     macro(WebKitPluginsEnabled, PluginsEnabled, pluginsEnabled) \
     macro(WebKitUsesPageCachePreferenceKey, UsesPageCache, usesPageCache) \
+    macro(WebKitAllowsPageCacheWithWindowOpenerKey, AllowsPageCacheWithWindowOpener, allowsPageCacheWithWindowOpener) \
     macro(WebKitWebAudioEnabled, WebAudioEnabled, webAudioEnabled) \
     macro(WebKitWebGLEnabled, WebGLEnabled, webGLEnabled) \
     macro(WebKitXSSAuditorEnabled, XSSAuditorEnabled, xssAuditorEnabled) \
index 86d341b..f97f567 100644 (file)
@@ -2995,6 +2995,7 @@ void WebPage::updatePreferences(const WebPreferencesStore& store)
 #endif
     settings.setLocalFileContentSniffingEnabled(store.getBoolValueForKey(WebPreferencesKey::localFileContentSniffingEnabledKey()));
     settings.setUsesPageCache(store.getBoolValueForKey(WebPreferencesKey::usesPageCacheKey()));
+    settings.setAllowsPageCacheWithWindowOpener(store.getBoolValueForKey(WebPreferencesKey::allowsPageCacheWithWindowOpenerKey()));
     settings.setPageCacheSupportsPlugins(store.getBoolValueForKey(WebPreferencesKey::pageCacheSupportsPluginsKey()));
     settings.setAuthorAndUserStylesEnabled(store.getBoolValueForKey(WebPreferencesKey::authorAndUserStylesEnabledKey()));
     settings.setPaginateDuringLayoutEnabled(store.getBoolValueForKey(WebPreferencesKey::paginateDuringLayoutEnabledKey()));
index e028512..3f07e4c 100644 (file)
@@ -1,3 +1,21 @@
+2017-01-26  Chris Dumez  <cdumez@apple.com>
+
+        Crash when navigating back to a page in PacheCache when one of its frames has been removed
+        https://bugs.webkit.org/show_bug.cgi?id=167421
+        <rdar://problem/30188490>
+
+        Reviewed by Darin Adler.
+
+        Add a new setting allowing layout tests to enable PageCache in a window
+        that has an opener, for convenience.
+
+        * DumpRenderTree/mac/DumpRenderTree.mm:
+        (resetWebPreferencesToConsistentValues):
+        * DumpRenderTree/win/DumpRenderTree.cpp:
+        (resetWebPreferencesToConsistentValues):
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::resetPreferencesToConsistentValues):
+
 2017-01-26  Keith Miller  <keith_miller@apple.com>
 
         classInfo should take a VM so it is not materialized from the object on each call
index 61a95d9..0458387 100644 (file)
@@ -980,6 +980,7 @@ static void resetWebPreferencesToConsistentValues()
     // The back/forward cache is causing problems due to layouts during transition from one page to another.
     // So, turn it off for now, but we might want to turn it back on some day.
     [preferences setUsesPageCache:NO];
+    [preferences setAllowsPageCacheWithWindowOpener:NO];
     [preferences setAcceleratedCompositingEnabled:YES];
 #if USE(CA)
     [preferences setCanvasUsesAcceleratedDrawing:YES];
index dbfb7f6..a0bfb0a 100644 (file)
@@ -834,6 +834,7 @@ static void resetWebPreferencesToConsistentValues(IWebPreferences* preferences)
     preferences->setPlugInsEnabled(TRUE);
     preferences->setTextAreasAreResizable(TRUE);
     preferences->setUsesPageCache(FALSE);
+    prefsPrivate->setAllowsPageCacheWithWindowOpener(FALSE);
 
     preferences->setPrivateBrowsingEnabled(FALSE);
     prefsPrivate->setAuthorAndUserStylesEnabled(TRUE);
index 3af2197..e92f96e 100644 (file)
@@ -655,6 +655,7 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio
     WKPreferencesSetFullScreenEnabled(preferences, true);
 #endif
     WKPreferencesSetPageCacheEnabled(preferences, false);
+    WKPreferencesSetAllowsPageCacheWithWindowOpener(preferences, false);
     WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
     WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
     WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);