Window's properties such as 'location' should not become null when it loses its brows...
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Oct 2018 14:54:21 +0000 (14:54 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Oct 2018 14:54:21 +0000 (14:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190539

Reviewed by Alex Christensen.

LayoutTests/imported/w3c:

Rebaseline WPT test whose output has changed.

* web-platform-tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window-expected.txt:

Source/WebCore:

Window's properties such as 'location' should not become null when it loses its browsing context.
This Webkit behavior is not standard and does not match other browsers so this patch makes it so
that those properties persist.

Tests: http/tests/dom/cross-origin-detached-window-properties.html
       http/tests/dom/same-origin-detached-window-properties.html

* bindings/js/JSDOMBindingSecurity.cpp:
(WebCore::BindingSecurity::shouldAllowAccessToDOMWindow):
* bindings/js/JSDOMBindingSecurity.h:
* bindings/js/JSDOMWindowProperties.cpp:
(WebCore::jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter):
(WebCore::JSDOMWindowProperties::getOwnPropertySlot):
* bindings/js/JSLocationCustom.cpp:
(WebCore::getOwnPropertySlotCommon):
(WebCore::putCommon):
(WebCore::JSLocation::deleteProperty):
(WebCore::JSLocation::deletePropertyByIndex):
(WebCore::JSLocation::getOwnPropertyNames):
(WebCore::JSLocation::defineOwnProperty):
(WebCore::JSLocation::getPrototype):
(WebCore::JSLocation::toStringName):
* bindings/scripts/CodeGeneratorJS.pm:
(GenerateAttributeGetterBodyDefinition):
(GenerateAttributeSetterBodyDefinition):
(GenerateOperationBodyDefinition):
* bindings/scripts/test/JS/JSTestActiveDOMObject.cpp:
(WebCore::jsTestActiveDOMObjectExcitingAttrGetter):
(WebCore::jsTestActiveDOMObjectPrototypeFunctionExcitingFunctionBody):
* crypto/SubtleCrypto.cpp:
(WebCore::SubtleCrypto::SubtleCrypto):
* crypto/SubtleCrypto.h:
(WebCore::SubtleCrypto::create):
* dom/Document.cpp:
(WebCore::Document::~Document):
* page/Crypto.cpp:
(WebCore::Crypto::Crypto):
* page/Crypto.h:
(WebCore::Crypto::create):
* page/DOMWindow.cpp:
(WebCore::DOMWindow::~DOMWindow):
(WebCore::DOMWindow::frameDestroyed):
(WebCore::DOMWindow::screen):
(WebCore::DOMWindow::history):
(WebCore::DOMWindow::crypto):
(WebCore::DOMWindow::locationbar):
(WebCore::DOMWindow::menubar):
(WebCore::DOMWindow::personalbar):
(WebCore::DOMWindow::scrollbars):
(WebCore::DOMWindow::statusbar):
(WebCore::DOMWindow::toolbar):
(WebCore::DOMWindow::console const):
(WebCore::DOMWindow::applicationCache):
(WebCore::DOMWindow::navigator):
(WebCore::DOMWindow::performance const):
(WebCore::DOMWindow::location):
(WebCore::DOMWindow::visualViewport):
(WebCore::DOMWindow::styleMedia):
* page/DOMWindow.h:
* page/DOMWindow.idl:
* workers/WorkerGlobalScope.cpp:
(WebCore::WorkerGlobalScope::crypto):

LayoutTests:

* http/tests/dom/cross-origin-detached-window-properties-expected.txt: Added.
* http/tests/dom/cross-origin-detached-window-properties.html: Added.
* http/tests/dom/resources/post-message-to-parent-when-loaded.html: Added.
* http/tests/dom/same-origin-detached-window-properties-expected.txt: Added.
* http/tests/dom/same-origin-detached-window-properties.html: Added.
Add layout test coverage.

* fast/frames/detached-frame-property-expected.txt:
* fast/frames/detached-frame-property.html:
* http/tests/security/named-window-property-from-same-origin-inactive-document-expected.txt:
* http/tests/security/named-window-property-from-same-origin-inactive-document.html:
* http/tests/security/xss-DENIED-named-window-property-from-cross-origin-inactive-document-expected.txt:
* http/tests/security/xss-DENIED-named-window-property-from-cross-origin-inactive-document.html:
* http/tests/security/xss-DENIED-script-inject-into-inactive-window.html:
* http/tests/security/xss-DENIED-script-inject-into-inactive-window2-pson.html:
* http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html:
* http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html:
Update existing layout tests to reflect behavior change.

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

33 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/frames/detached-frame-property-expected.txt
LayoutTests/fast/frames/detached-frame-property.html
LayoutTests/http/tests/dom/cross-origin-detached-window-properties-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/dom/cross-origin-detached-window-properties.html [new file with mode: 0644]
LayoutTests/http/tests/dom/resources/post-message-to-parent-when-loaded.html [new file with mode: 0644]
LayoutTests/http/tests/dom/same-origin-detached-window-properties-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/dom/same-origin-detached-window-properties.html [new file with mode: 0644]
LayoutTests/http/tests/security/named-window-property-from-same-origin-inactive-document-expected.txt
LayoutTests/http/tests/security/named-window-property-from-same-origin-inactive-document.html
LayoutTests/http/tests/security/xss-DENIED-named-window-property-from-cross-origin-inactive-document-expected.txt
LayoutTests/http/tests/security/xss-DENIED-named-window-property-from-cross-origin-inactive-document.html
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window.html
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2-pson.html
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp
Source/WebCore/bindings/js/JSDOMBindingSecurity.h
Source/WebCore/bindings/js/JSDOMWindowProperties.cpp
Source/WebCore/bindings/js/JSLocationCustom.cpp
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/test/JS/JSTestActiveDOMObject.cpp
Source/WebCore/crypto/SubtleCrypto.cpp
Source/WebCore/crypto/SubtleCrypto.h
Source/WebCore/page/Crypto.cpp
Source/WebCore/page/Crypto.h
Source/WebCore/page/DOMWindow.cpp
Source/WebCore/page/DOMWindow.h
Source/WebCore/page/DOMWindow.idl
Source/WebCore/workers/WorkerGlobalScope.cpp

index 7caa4ed..0c72121 100644 (file)
@@ -1,3 +1,29 @@
+2018-10-15  Chris Dumez  <cdumez@apple.com>
+
+        Window's properties such as 'location' should not become null when it loses its browsing context
+        https://bugs.webkit.org/show_bug.cgi?id=190539
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/dom/cross-origin-detached-window-properties-expected.txt: Added.
+        * http/tests/dom/cross-origin-detached-window-properties.html: Added.
+        * http/tests/dom/resources/post-message-to-parent-when-loaded.html: Added.
+        * http/tests/dom/same-origin-detached-window-properties-expected.txt: Added.
+        * http/tests/dom/same-origin-detached-window-properties.html: Added.
+        Add layout test coverage.
+
+        * fast/frames/detached-frame-property-expected.txt:
+        * fast/frames/detached-frame-property.html:
+        * http/tests/security/named-window-property-from-same-origin-inactive-document-expected.txt:
+        * http/tests/security/named-window-property-from-same-origin-inactive-document.html:
+        * http/tests/security/xss-DENIED-named-window-property-from-cross-origin-inactive-document-expected.txt:
+        * http/tests/security/xss-DENIED-named-window-property-from-cross-origin-inactive-document.html:
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window.html:
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window2-pson.html:
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html:
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html:
+        Update existing layout tests to reflect behavior change.
+
 2018-10-15  Claudio Saavedra  <csaavedra@igalia.com>
 
         [GStreamer] MediaStream test failing since r236877
index b725799..02ded49 100644 (file)
@@ -5,6 +5,10 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS !!detachedWindow.postMessage is true
 PASS !!detachedWindow.close is true
+PASS !!detachedWindow.locationbar is true
+PASS !!detachedWindow.history is true
+PASS !!detachedWindow.screen is true
+PASS !!detachedWindow.location is true
 PASS detachedWindow.closed is true
 PASS detachedWindow.top is null
 PASS detachedWindow.opener is null
@@ -14,16 +18,12 @@ PASS detachedWindow.window is null
 PASS detachedWindow.frames is null
 PASS detachedWindow.self is null
 PASS !detachedWindow.navigator is true
-PASS !detachedWindow.locationbar is true
-PASS !detachedWindow.history is true
 PASS !detachedWindow.localStorage is true
 PASS !!detachedWindow.document is true
 PASS !!detachedWindow.XMLHttpRequest is true
 PASS !!detachedWindow.getComputedStyle is true
-PASS !detachedWindow.screen is true
 PASS detachedWindow.innerHeight is 0
 PASS detachedWindow.innerWidth is 0
-PASS !detachedWindow.location is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 2382bee..2530b50 100644 (file)
@@ -9,12 +9,15 @@ onload = function()
     // Chrome and Firefox agree with us.
     shouldBeTrue("!!detachedWindow.postMessage");
     shouldBeTrue("!!detachedWindow.close");
+    shouldBeTrue("!!detachedWindow.locationbar");
+    shouldBeTrue("!!detachedWindow.history");
+    shouldBeTrue("!!detachedWindow.screen");
+    shouldBeTrue("!!detachedWindow.location");
     shouldBeTrue("detachedWindow.closed");
     shouldBeNull("detachedWindow.top");
     shouldBeNull("detachedWindow.opener");
     shouldBeNull("detachedWindow.parent");
     shouldBeNull("detachedWindow.frameElement"); // Technically, Chrome returns undefined here, not null.
-
     // Chrome agrees with us but Firefox returns the detachedWindow.
     shouldBeNull("detachedWindow.window");
     shouldBeNull("detachedWindow.frames");
@@ -22,20 +25,14 @@ onload = function()
 
     // Chrome returns undefined but Firefox has a valid object.
     shouldBeTrue("!detachedWindow.navigator");
-    shouldBeTrue("!detachedWindow.locationbar");
-    shouldBeTrue("!detachedWindow.history");
     shouldBeTrue("!detachedWindow.localStorage");
     shouldBeTrue("!!detachedWindow.document");
     shouldBeTrue("!!detachedWindow.XMLHttpRequest");
     shouldBeTrue("!!detachedWindow.getComputedStyle");
 
     // Chrome returns undefined but Firefox throws an exception.
-    shouldBeTrue("!detachedWindow.screen");
     shouldBe("detachedWindow.innerHeight", "0");
     shouldBe("detachedWindow.innerWidth", "0");
-
-    // Both Chrome and Firefox disagree with us and return a valid object.
-    shouldBeTrue("!detachedWindow.location");
 }
 </script>
 <iframe src="about:blank"></iframe>
diff --git a/LayoutTests/http/tests/dom/cross-origin-detached-window-properties-expected.txt b/LayoutTests/http/tests/dom/cross-origin-detached-window-properties-expected.txt
new file mode 100644 (file)
index 0000000..cffc61f
--- /dev/null
@@ -0,0 +1,66 @@
+Validate the properties of a detached Window object.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+* Before GC
+PASS !!w.location is true
+PASS w.location.href threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.protocol threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.host threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.hostname threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.port threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.pathname threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.search threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.hash threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.origin threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.assign('') threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.replace('') did not throw exception.
+PASS w.location.reload('') threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.screen threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.history threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.crypto threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.locationbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.menubar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.personalbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.scrollbars threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.statusbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.toolbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.applicationCache threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.visualViewport threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.styleMedia threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.foo threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.foo threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+
+* After GC
+PASS !!w.location is true
+PASS w.location.href threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.protocol threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.host threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.hostname threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.port threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.pathname threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.search threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.hash threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.origin threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.assign('') threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.replace('') did not throw exception.
+PASS w.location.reload('') threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.screen threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.history threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.crypto threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.locationbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.menubar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.personalbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.scrollbars threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.statusbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.toolbar threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.applicationCache threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.visualViewport threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.styleMedia threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.foo threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS w.location.foo threw exception SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. Protocols, domains, and ports must match..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/dom/cross-origin-detached-window-properties.html b/LayoutTests/http/tests/dom/cross-origin-detached-window-properties.html
new file mode 100644 (file)
index 0000000..fd1a22a
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+description("Validate the properties of a detached Window object.");
+jsTestIsAsync = true;
+
+function validateWindow(_w)
+{
+    w = _w;
+
+    try {
+    shouldBeTrue("!!w.location");
+    if (w.location) {
+        shouldThrowErrorName("w.location.href", "SecurityError");
+        shouldThrowErrorName("w.location.protocol", "SecurityError");
+        shouldThrowErrorName("w.location.host", "SecurityError");
+        shouldThrowErrorName("w.location.hostname", "SecurityError");
+        shouldThrowErrorName("w.location.port", "SecurityError");
+        shouldThrowErrorName("w.location.pathname", "SecurityError");
+        shouldThrowErrorName("w.location.search", "SecurityError");
+        shouldThrowErrorName("w.location.hash", "SecurityError");
+        shouldThrowErrorName("w.location.origin", "SecurityError");
+        shouldThrowErrorName("w.location.assign('')", "SecurityError");
+        shouldNotThrow("w.location.replace('')");
+        shouldThrowErrorName("w.location.reload('')", "SecurityError");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    shouldThrowErrorName("w.screen", "SecurityError");
+    shouldThrowErrorName("w.history", "SecurityError");
+    shouldThrowErrorName("w.crypto", "SecurityError");
+
+    let bars = ['locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar', 'toolbar'];
+    for (let bar of bars) {
+        shouldThrowErrorName("w." + bar, "SecurityError");
+    }
+
+    shouldThrowErrorName("w.applicationCache", "SecurityError");
+    shouldThrowErrorName("w.visualViewport", "SecurityError");
+    shouldThrowErrorName("w.styleMedia", "SecurityError");
+
+    shouldThrowErrorName("w.foo", "SecurityError");
+    shouldThrowErrorName("w.location.foo", "SecurityError");
+}
+
+onmessage = function() {
+    let w = f.contentWindow;
+    f.remove();
+    f = null;
+
+    debug("* Before GC");
+    validateWindow(w);
+    gc();
+    setTimeout(() => {
+        gc();
+        debug("");
+        debug("* After GC");
+        validateWindow(w);
+        finishJSTest();
+    }, 0);
+}
+
+onload = function() {
+    f = document.createElement("iframe");
+    f.src = "http://localhost:8000/dom/resources/post-message-to-parent-when-loaded.html"; // Cross-origin.
+    document.body.appendChild(f);
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/dom/resources/post-message-to-parent-when-loaded.html b/LayoutTests/http/tests/dom/resources/post-message-to-parent-when-loaded.html
new file mode 100644 (file)
index 0000000..618c388
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+onload = () => {
+    parent.postMessage("loaded", "*");
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/dom/same-origin-detached-window-properties-expected.txt b/LayoutTests/http/tests/dom/same-origin-detached-window-properties-expected.txt
new file mode 100644 (file)
index 0000000..c838207
--- /dev/null
@@ -0,0 +1,134 @@
+Validate the properties of a detached Window object.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+* Before GC
+PASS !!w.location is true
+PASS w.location.href is ""
+PASS w.location.protocol is ""
+PASS w.location.host is ""
+PASS w.location.hostname is ""
+PASS w.location.port is ""
+PASS w.location.pathname is ""
+PASS w.location.search is ""
+PASS w.location.hash is ""
+PASS w.location.origin is ""
+PASS w.location.assign('') did not throw exception.
+PASS w.location.replace('') did not throw exception.
+PASS w.location.reload('') did not throw exception.
+PASS !!w.screen is true
+PASS w.screen.height is 0
+PASS w.screen.width is 0
+PASS w.screen.colorDepth is 0
+PASS w.screen.pixelDepth is 0
+PASS w.screen.availLeft is 0
+PASS w.screen.availTop is 0
+PASS w.screen.availHeight is 0
+PASS w.screen.availWidth is 0
+PASS !!w.history is true
+PASS w.history.length is 0
+PASS w.history.state is null
+PASS w.history.back() did not throw exception.
+PASS w.history.forward() did not throw exception.
+PASS w.history.go(-1) did not throw exception.
+PASS w.history.pushState({}, null) did not throw exception.
+PASS w.history.replaceState({}, null) did not throw exception.
+PASS !!w.crypto is true
+PASS !!w.locationbar is true
+PASS w.locationbar.visible is false
+PASS !!w.menubar is true
+PASS w.menubar.visible is false
+PASS !!w.personalbar is true
+PASS w.personalbar.visible is false
+PASS !!w.scrollbars is true
+PASS w.scrollbars.visible is false
+PASS !!w.statusbar is true
+PASS w.statusbar.visible is false
+PASS !!w.toolbar is true
+PASS w.toolbar.visible is false
+PASS !!w.applicationCache is true
+PASS w.applicationCache.status is ApplicationCache.UNCACHED
+PASS w.applicationCache.update() threw exception InvalidStateError: The object is in an invalid state..
+PASS w.applicationCache.swapCache() threw exception InvalidStateError: The object is in an invalid state..
+PASS w.applicationCache.abort() did not throw exception.
+PASS !!w.visualViewport is true
+PASS w.visualViewport.offsetLeft is 0
+PASS w.visualViewport.offsetTop is 0
+PASS w.visualViewport.pageLeft is 0
+PASS w.visualViewport.pageTop is 0
+PASS w.visualViewport.width is 0
+PASS w.visualViewport.height is 0
+PASS w.visualViewport.scale is 1
+PASS !!w.styleMedia is true
+PASS w.styleMedia.type is ""
+PASS w.styleMedia.matchMedium('foo') is false
+PASS w.foo is undefined.
+PASS w.location.foo is undefined.
+
+* After GC
+PASS !!w.location is true
+PASS w.location.href is ""
+PASS w.location.protocol is ""
+PASS w.location.host is ""
+PASS w.location.hostname is ""
+PASS w.location.port is ""
+PASS w.location.pathname is ""
+PASS w.location.search is ""
+PASS w.location.hash is ""
+PASS w.location.origin is ""
+PASS w.location.assign('') did not throw exception.
+PASS w.location.replace('') did not throw exception.
+PASS w.location.reload('') did not throw exception.
+PASS !!w.screen is true
+PASS w.screen.height is 0
+PASS w.screen.width is 0
+PASS w.screen.colorDepth is 0
+PASS w.screen.pixelDepth is 0
+PASS w.screen.availLeft is 0
+PASS w.screen.availTop is 0
+PASS w.screen.availHeight is 0
+PASS w.screen.availWidth is 0
+PASS !!w.history is true
+PASS w.history.length is 0
+PASS w.history.state is null
+PASS w.history.back() did not throw exception.
+PASS w.history.forward() did not throw exception.
+PASS w.history.go(-1) did not throw exception.
+PASS w.history.pushState({}, null) did not throw exception.
+PASS w.history.replaceState({}, null) did not throw exception.
+PASS !!w.crypto is true
+PASS !!w.locationbar is true
+PASS w.locationbar.visible is false
+PASS !!w.menubar is true
+PASS w.menubar.visible is false
+PASS !!w.personalbar is true
+PASS w.personalbar.visible is false
+PASS !!w.scrollbars is true
+PASS w.scrollbars.visible is false
+PASS !!w.statusbar is true
+PASS w.statusbar.visible is false
+PASS !!w.toolbar is true
+PASS w.toolbar.visible is false
+PASS !!w.applicationCache is true
+PASS w.applicationCache.status is ApplicationCache.UNCACHED
+PASS w.applicationCache.update() threw exception InvalidStateError: The object is in an invalid state..
+PASS w.applicationCache.swapCache() threw exception InvalidStateError: The object is in an invalid state..
+PASS w.applicationCache.abort() did not throw exception.
+PASS !!w.visualViewport is true
+PASS w.visualViewport.offsetLeft is 0
+PASS w.visualViewport.offsetTop is 0
+PASS w.visualViewport.pageLeft is 0
+PASS w.visualViewport.pageTop is 0
+PASS w.visualViewport.width is 0
+PASS w.visualViewport.height is 0
+PASS w.visualViewport.scale is 1
+PASS !!w.styleMedia is true
+PASS w.styleMedia.type is ""
+PASS w.styleMedia.matchMedium('foo') is false
+PASS w.foo is undefined.
+PASS w.location.foo is undefined.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/dom/same-origin-detached-window-properties.html b/LayoutTests/http/tests/dom/same-origin-detached-window-properties.html
new file mode 100644 (file)
index 0000000..68c1a2a
--- /dev/null
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+description("Validate the properties of a detached Window object.");
+jsTestIsAsync = true;
+
+function validateWindow(_w)
+{
+    w = _w;
+
+    try {
+    shouldBeTrue("!!w.location");
+    if (w.location) {
+        shouldBeEqualToString("w.location.href", "");
+        shouldBeEqualToString("w.location.protocol", "");
+        shouldBeEqualToString("w.location.host", "");
+        shouldBeEqualToString("w.location.hostname", "");
+        shouldBeEqualToString("w.location.port", "");
+        shouldBeEqualToString("w.location.pathname", "");
+        shouldBeEqualToString("w.location.search", "");
+        shouldBeEqualToString("w.location.hash", "");
+        shouldBeEqualToString("w.location.origin", "");
+        shouldNotThrow("w.location.assign('')");
+        shouldNotThrow("w.location.replace('')");
+        shouldNotThrow("w.location.reload('')");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    try {
+    shouldBeTrue("!!w.screen");
+    if (w.screen) {
+        shouldBe("w.screen.height", "0");
+        shouldBe("w.screen.width", "0");
+        shouldBe("w.screen.colorDepth", "0");
+        shouldBe("w.screen.pixelDepth", "0");
+        shouldBe("w.screen.availLeft", "0");
+        shouldBe("w.screen.availTop", "0");
+        shouldBe("w.screen.availHeight", "0");
+        shouldBe("w.screen.availWidth", "0");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    try {
+    shouldBeTrue("!!w.history");
+    if (w.history) {
+        shouldBe("w.history.length", "0");
+        shouldBeNull("w.history.state");
+        shouldNotThrow("w.history.back()");
+        shouldNotThrow("w.history.forward()");
+        shouldNotThrow("w.history.go(-1)");
+        shouldNotThrow("w.history.pushState({}, null)");
+        shouldNotThrow("w.history.replaceState({}, null)");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    try {
+    shouldBeTrue("!!w.crypto");
+    } catch (e) {
+        debug(e);
+    }
+
+    let bars = ['locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar', 'toolbar'];
+    for (let bar of bars) {
+        try {
+        shouldBeTrue("!!w." + bar);
+        if (w[bar]) {
+            shouldBeFalse("w." + bar + ".visible");
+        }
+        } catch (e) {
+            debug(e);
+        }
+    }
+
+    try {
+    shouldBeTrue("!!w.applicationCache");
+    if (w.applicationCache) {
+        shouldBe("w.applicationCache.status", "ApplicationCache.UNCACHED");
+        shouldThrowErrorName("w.applicationCache.update()", "InvalidStateError");
+        shouldThrowErrorName("w.applicationCache.swapCache()", "InvalidStateError");
+        shouldNotThrow("w.applicationCache.abort()");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    try {
+    shouldBeTrue("!!w.visualViewport");
+    if (w.visualViewport) {
+        shouldBe("w.visualViewport.offsetLeft", "0");
+        shouldBe("w.visualViewport.offsetTop", "0");
+        shouldBe("w.visualViewport.pageLeft", "0");
+        shouldBe("w.visualViewport.pageTop", "0");
+        shouldBe("w.visualViewport.width", "0");
+        shouldBe("w.visualViewport.height", "0");
+        shouldBe("w.visualViewport.scale", "1");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    try {
+    shouldBeTrue("!!w.styleMedia");
+    if (w.styleMedia) {
+        shouldBeEqualToString("w.styleMedia.type", "");
+        shouldBeFalse("w.styleMedia.matchMedium('foo')");
+    }
+    } catch (e) {
+        debug(e);
+    }
+
+    try {
+    shouldBeUndefined("w.foo");
+    shouldBeUndefined("w.location.foo");
+    } catch (e) {
+        debug(e);
+    }
+}
+
+onload = function() {
+    let f = document.createElement("iframe");
+    document.body.appendChild(f);
+    let w = f.contentWindow;
+    f.remove();
+    f = null;
+    debug("* Before GC");
+    validateWindow(w);
+    gc();
+    setTimeout(() => {
+        gc();
+        debug("");
+        debug("* After GC");
+        validateWindow(w);
+        finishJSTest();
+    }, 0);
+}
+</script>
+</body>
+</html>
index ab20b97..a476f56 100644 (file)
@@ -5,7 +5,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 Lookup named element whose name corresponds to an element in the initial about:blank document:
 PASS frame.contentDocument.getElementById('A') is not elementAInInactiveDocument
-PASS elementAInDetachedWindowFunction() threw exception ReferenceError: Can't find variable: A.
+PASS elementAInDetachedWindowFunction() is elementAInInactiveDocument
 
 Lookup named element whose name does not correspond to an element in the initial about:blank document:
 PASS elementBInDetachedWindowFunction() threw exception ReferenceError: Can't find variable: B.
index e366f6d..660a988 100644 (file)
@@ -24,7 +24,7 @@ frame.onload = function ()
 {
     debug("Lookup named element whose name corresponds to an element in the initial about:blank document:");
     shouldNotBe("frame.contentDocument.getElementById('A')", "elementAInInactiveDocument");
-    shouldThrowErrorName("elementAInDetachedWindowFunction()", "ReferenceError");
+    shouldBe("elementAInDetachedWindowFunction()", "elementAInInactiveDocument");
 
     debug("<br>Lookup named element whose name does not correspond to an element in the initial about:blank document:");
     shouldThrowErrorName("elementBInDetachedWindowFunction()", "ReferenceError");
index e4fa94d..2892c37 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 Lookup named element whose name corresponds to an element in the initial about:blank document:
-PASS elementAInDetachedWindowFunction() threw exception ReferenceError: Can't find variable: A.
+PASS elementAInDetachedWindowFunction() is elementAInInactiveDocument
 
 Lookup named element whose name does not correspond to an element in the initial about:blank document:
 PASS elementBInDetachedWindowFunction() threw exception ReferenceError: Can't find variable: B.
index 893aca7..7a73337 100644 (file)
@@ -23,7 +23,7 @@ var elementBInDetachedWindowFunction = frame.contentWindow.Function("return B;")
 frame.onload = function ()
 {
     debug("Lookup named element whose name corresponds to an element in the initial about:blank document:")
-    shouldThrowErrorName("elementAInDetachedWindowFunction()", 'ReferenceError');
+    shouldBe("elementAInDetachedWindowFunction()", 'elementAInInactiveDocument');
 
     debug("<br>Lookup named element whose name does not correspond to an element in the initial about:blank document:");
     shouldThrowErrorName("elementBInDetachedWindowFunction()", 'ReferenceError');
index 8627b65..2284b5b 100644 (file)
@@ -16,9 +16,7 @@ var intervalId;
 
 function checkDidLoadVictim()
 {
-    try {
-        _openedWindowDocument.location.href
-    } catch (e) {
+    if (_openedWindowDocument.location.href == "") {
         // Victim loaded; |_openedWindowDocument| is an inactive document.
         window.clearInterval(intervalId);
 
index 6a59c2c..7f0d59b 100644 (file)
@@ -41,9 +41,7 @@ if (document.location.search.indexOf("?actually-attack") !== -1) {
 
 function checkDidLoadVictim()
 {
-    try {
-        openerDocument.location.href
-    } catch (e) {
+    if (openerDocument.location.href == "") {
         // Victim loaded.
         window.clearInterval(intervalId);
 
index 2be35a0..47be18f 100644 (file)
@@ -41,9 +41,7 @@ if (document.location.search.indexOf("?actually-attack") !== -1) {
 
 function checkDidLoadVictim()
 {
-    try {
-        openerDocument.location.href
-    } catch (e) {
+    if (openerDocument.location.href == "") {
         // Victim loaded.
         window.clearInterval(intervalId);
 
index 39a8271..1f8d44e 100644 (file)
@@ -60,9 +60,7 @@ if (!window.location.search) {
 
 function checkDidLoadVictim()
 {
-    try {
-        secondOpenerDocument.location.href
-    } catch (e) {
+    if (secondOpenerDocument.location.href == "") {
         // Victim loaded.
         window.clearInterval(intervalId);
 
index e9ba0ed..f02001a 100644 (file)
@@ -1,3 +1,14 @@
+2018-10-15  Chris Dumez  <cdumez@apple.com>
+
+        Window's properties such as 'location' should not become null when it loses its browsing context
+        https://bugs.webkit.org/show_bug.cgi?id=190539
+
+        Reviewed by Alex Christensen.
+
+        Rebaseline WPT test whose output has changed.
+
+        * web-platform-tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window-expected.txt:
+
 2018-10-08  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
 
         [JSC] JSC should have "parseFunction" to optimize Function constructor
index cf60bad..c9a2936 100644 (file)
@@ -1,6 +1,6 @@
 
 PASS document.open() changes document's URL (fully active document) 
-FAIL document.open() does not change document's URL (active but not fully active document) null is not an object (evaluating 'childWin.location.href')
+FAIL document.open() does not change document's URL (active but not fully active document) assert_equals: expected "http://localhost:8800/common/blank.html" but got ""
 PASS document.open() does not change document's URL (non-active document with an associated Window object; frame is removed) 
 PASS document.open() does not change document's URL (non-active document with an associated Window object; navigated away) 
 PASS document.open() does not change document's URL (non-active document without an associated Window object) 
index 06f3802..e79bcae 100644 (file)
@@ -1,3 +1,73 @@
+2018-10-15  Chris Dumez  <cdumez@apple.com>
+
+        Window's properties such as 'location' should not become null when it loses its browsing context
+        https://bugs.webkit.org/show_bug.cgi?id=190539
+
+        Reviewed by Alex Christensen.
+
+        Window's properties such as 'location' should not become null when it loses its browsing context.
+        This Webkit behavior is not standard and does not match other browsers so this patch makes it so
+        that those properties persist.
+
+        Tests: http/tests/dom/cross-origin-detached-window-properties.html
+               http/tests/dom/same-origin-detached-window-properties.html
+
+        * bindings/js/JSDOMBindingSecurity.cpp:
+        (WebCore::BindingSecurity::shouldAllowAccessToDOMWindow):
+        * bindings/js/JSDOMBindingSecurity.h:
+        * bindings/js/JSDOMWindowProperties.cpp:
+        (WebCore::jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter):
+        (WebCore::JSDOMWindowProperties::getOwnPropertySlot):
+        * bindings/js/JSLocationCustom.cpp:
+        (WebCore::getOwnPropertySlotCommon):
+        (WebCore::putCommon):
+        (WebCore::JSLocation::deleteProperty):
+        (WebCore::JSLocation::deletePropertyByIndex):
+        (WebCore::JSLocation::getOwnPropertyNames):
+        (WebCore::JSLocation::defineOwnProperty):
+        (WebCore::JSLocation::getPrototype):
+        (WebCore::JSLocation::toStringName):
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateAttributeGetterBodyDefinition):
+        (GenerateAttributeSetterBodyDefinition):
+        (GenerateOperationBodyDefinition):
+        * bindings/scripts/test/JS/JSTestActiveDOMObject.cpp:
+        (WebCore::jsTestActiveDOMObjectExcitingAttrGetter):
+        (WebCore::jsTestActiveDOMObjectPrototypeFunctionExcitingFunctionBody):
+        * crypto/SubtleCrypto.cpp:
+        (WebCore::SubtleCrypto::SubtleCrypto):
+        * crypto/SubtleCrypto.h:
+        (WebCore::SubtleCrypto::create):
+        * dom/Document.cpp:
+        (WebCore::Document::~Document):
+        * page/Crypto.cpp:
+        (WebCore::Crypto::Crypto):
+        * page/Crypto.h:
+        (WebCore::Crypto::create):
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::~DOMWindow):
+        (WebCore::DOMWindow::frameDestroyed):
+        (WebCore::DOMWindow::screen):
+        (WebCore::DOMWindow::history):
+        (WebCore::DOMWindow::crypto):
+        (WebCore::DOMWindow::locationbar):
+        (WebCore::DOMWindow::menubar):
+        (WebCore::DOMWindow::personalbar):
+        (WebCore::DOMWindow::scrollbars):
+        (WebCore::DOMWindow::statusbar):
+        (WebCore::DOMWindow::toolbar):
+        (WebCore::DOMWindow::console const):
+        (WebCore::DOMWindow::applicationCache):
+        (WebCore::DOMWindow::navigator):
+        (WebCore::DOMWindow::performance const):
+        (WebCore::DOMWindow::location):
+        (WebCore::DOMWindow::visualViewport):
+        (WebCore::DOMWindow::styleMedia):
+        * page/DOMWindow.h:
+        * page/DOMWindow.idl:
+        * workers/WorkerGlobalScope.cpp:
+        (WebCore::WorkerGlobalScope::crypto):
+
 2018-10-15  Alex Christensen  <achristensen@webkit.org>
 
         Include EnumTraits.h less
index 70d74b2..786a09d 100644 (file)
@@ -77,6 +77,11 @@ bool BindingSecurity::shouldAllowAccessToFrame(ExecState& state, Frame& frame, S
     return false;
 }
 
+bool BindingSecurity::shouldAllowAccessToDOMWindow(ExecState& state, DOMWindow* globalObject, String& message)
+{
+    return globalObject && shouldAllowAccessToDOMWindow(state, *globalObject, message);
+}
+
 bool BindingSecurity::shouldAllowAccessToDOMWindow(ExecState& state, DOMWindow& globalObject, String& message)
 {
     if (BindingSecurity::shouldAllowAccessToDOMWindow(&state, globalObject, DoNotReportSecurityError))
@@ -90,6 +95,11 @@ bool BindingSecurity::shouldAllowAccessToDOMWindow(JSC::ExecState* state, DOMWin
     return canAccessDocument(state, target.document(), reportingOption);
 }
 
+bool BindingSecurity::shouldAllowAccessToDOMWindow(JSC::ExecState* state, DOMWindow* target, SecurityReportingOption reportingOption)
+{
+    return target && shouldAllowAccessToDOMWindow(state, *target, reportingOption);
+}
+
 bool BindingSecurity::shouldAllowAccessToFrame(JSC::ExecState* state, Frame* target, SecurityReportingOption reportingOption)
 {
     return target && canAccessDocument(state, target->document(), reportingOption);
index 2f33876..ebdb8a9 100644 (file)
@@ -49,6 +49,8 @@ template<typename T> ExceptionOr<T*> checkSecurityForNode(JSC::ExecState&, Excep
 
 bool shouldAllowAccessToDOMWindow(JSC::ExecState*, DOMWindow&, SecurityReportingOption = LogSecurityError);
 bool shouldAllowAccessToDOMWindow(JSC::ExecState&, DOMWindow&, String& message);
+bool shouldAllowAccessToDOMWindow(JSC::ExecState*, DOMWindow*, SecurityReportingOption = LogSecurityError);
+bool shouldAllowAccessToDOMWindow(JSC::ExecState&, DOMWindow*, String& message);
 bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*, SecurityReportingOption = LogSecurityError);
 bool shouldAllowAccessToFrame(JSC::ExecState&, Frame&, String& message);
 bool shouldAllowAccessToNode(JSC::ExecState&, Node*);
index e9eedc8..308ca0e 100644 (file)
@@ -41,26 +41,28 @@ using namespace JSC;
 
 const ClassInfo JSDOMWindowProperties::s_info = { "WindowProperties", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMWindowProperties) };
 
-static bool jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter(JSDOMWindowProperties* thisObject, Frame& frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+static bool jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter(JSDOMWindowProperties* thisObject, DOMWindow& window, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     // Check for child frames by name before built-in properties to match Mozilla. This does
     // not match IE, but some sites end up naming frames things that conflict with window
     // properties that are in Moz but not IE. Since we have some of these, we have to do it
     // the Moz way.
-    if (auto* scopedChild = frame.tree().scopedChild(propertyNameToAtomicString(propertyName))) {
-        slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, toJS(exec, scopedChild->document()->domWindow()));
-        return true;
+    if (auto* frame = window.frame()) {
+        if (auto* scopedChild = frame->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
+            slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, toJS(exec, scopedChild->document()->domWindow()));
+            return true;
+        }
     }
 
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, &frame, ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, window, ThrowSecurityError))
         return false;
 
     // FIXME: Search the whole frame hierarchy somewhere around here.
     // We need to test the correct priority order.
 
     // Allow shortcuts like 'Image1' instead of document.images.Image1
-    Document* document = frame.document();
-    if (is<HTMLDocument>(*document)) {
+    auto* document = window.document();
+    if (is<HTMLDocument>(document)) {
         auto& htmlDocument = downcast<HTMLDocument>(*document);
         auto* atomicPropertyName = propertyName.publicName();
         if (atomicPropertyName && htmlDocument.hasWindowNamedItem(*atomicPropertyName)) {
@@ -97,11 +99,7 @@ bool JSDOMWindowProperties::getOwnPropertySlot(JSObject* object, ExecState* stat
         return false;
 
     auto& window = jsWindow->wrapped();
-
-    if (auto* frame = window.frame())
-        return jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter(thisObject, *frame, state, propertyName, slot);
-
-    return false;
+    return jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter(thisObject, window, state, propertyName, slot);
 }
 
 bool JSDOMWindowProperties::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)
index d2869d6..11d22a7 100644 (file)
@@ -39,11 +39,7 @@ static bool getOwnPropertySlotCommon(JSLocation& thisObject, ExecState& state, P
     VM& vm = state.vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    Frame* frame = thisObject.wrapped().frame();
-    if (!frame) {
-        slot.setUndefined();
-        return true;
-    }
+    auto* window = thisObject.wrapped().window();
 
     // When accessing Location cross-domain, functions are always the native built-in ones.
     // See JSDOMWindow::getOwnPropertySlotDelegate for additional details.
@@ -51,7 +47,7 @@ static bool getOwnPropertySlotCommon(JSLocation& thisObject, ExecState& state, P
     // Our custom code is only needed to implement the Window cross-domain scheme, so if access is
     // allowed, return false so the normal lookup will take place.
     String message;
-    if (BindingSecurity::shouldAllowAccessToFrame(state, *frame, message))
+    if (BindingSecurity::shouldAllowAccessToDOMWindow(state, window, message))
         return false;
 
     // https://html.spec.whatwg.org/#crossorigingetownpropertyhelper-(-o,-p-)
@@ -101,10 +97,6 @@ bool JSLocation::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, u
 
 static bool putCommon(JSLocation& thisObject, ExecState& state, PropertyName propertyName)
 {
-    Frame* frame = thisObject.wrapped().frame();
-    if (!frame)
-        return true;
-
     VM& vm = state.vm();
     // Silently block access to toString and valueOf.
     if (propertyName == vm.propertyNames->toString || propertyName == vm.propertyNames->valueOf)
@@ -117,7 +109,7 @@ static bool putCommon(JSLocation& thisObject, ExecState& state, PropertyName pro
         return false;
 
     // Block access and throw if there is a security error.
-    if (!BindingSecurity::shouldAllowAccessToFrame(&state, frame, ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped().window(), ThrowSecurityError))
         return true;
 
     return false;
@@ -149,7 +141,7 @@ bool JSLocation::deleteProperty(JSCell* cell, ExecState* exec, PropertyName prop
 {
     JSLocation* thisObject = jsCast<JSLocation*>(cell);
     // Only allow deleting by frames in the same origin.
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped().window(), ThrowSecurityError))
         return false;
     return Base::deleteProperty(thisObject, exec, propertyName);
 }
@@ -158,7 +150,7 @@ bool JSLocation::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned p
 {
     JSLocation* thisObject = jsCast<JSLocation*>(cell);
     // Only allow deleting by frames in the same origin.
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped().window(), ThrowSecurityError))
         return false;
     return Base::deletePropertyByIndex(thisObject, exec, propertyName);
 }
@@ -166,7 +158,7 @@ bool JSLocation::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned p
 void JSLocation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
 {
     JSLocation* thisObject = jsCast<JSLocation*>(object);
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError)) {
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped().window(), DoNotReportSecurityError)) {
         if (mode.includeDontEnumProperties())
             addCrossOriginOwnPropertyNames<CrossOriginObject::Location>(*exec, propertyNames);
         return;
@@ -177,7 +169,7 @@ void JSLocation::getOwnPropertyNames(JSObject* object, ExecState* exec, Property
 bool JSLocation::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
 {
     JSLocation* thisObject = jsCast<JSLocation*>(object);
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped().window(), ThrowSecurityError))
         return false;
 
     VM& vm = exec->vm();
@@ -189,7 +181,7 @@ bool JSLocation::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNa
 JSValue JSLocation::getPrototype(JSObject* object, ExecState* exec)
 {
     JSLocation* thisObject = jsCast<JSLocation*>(object);
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped().window(), DoNotReportSecurityError))
         return jsNull();
 
     return Base::getPrototype(object, exec);
@@ -206,7 +198,7 @@ bool JSLocation::preventExtensions(JSObject*, ExecState* exec)
 String JSLocation::toStringName(const JSObject* object, ExecState* exec)
 {
     auto* thisObject = jsCast<const JSLocation*>(object);
-    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped().window(), DoNotReportSecurityError))
         return "Object"_s;
     return "Location"_s;
 }
index 2180cb5..48fce7b 100644 (file)
@@ -4777,7 +4777,7 @@ sub GenerateAttributeGetterBodyDefinition
         if ($interface->type->name eq "DOMWindow") {
             push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n");
         } else {
-            push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))\n");
+            push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped().window(), ThrowSecurityError))\n");
         }
         push(@$outputArray, "        return jsUndefined();\n");
     }
@@ -4905,7 +4905,7 @@ sub GenerateAttributeSetterBodyDefinition
         if ($interface->type->name eq "DOMWindow") {
             push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n");
         } else {
-            push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))\n");
+            push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped().window(), ThrowSecurityError))\n");
         }
         push(@$outputArray, "        return false;\n");
     }
@@ -5124,7 +5124,7 @@ sub GenerateOperationBodyDefinition
                 push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped(), ThrowSecurityError))\n");
                 push(@$outputArray, "        return JSValue::encode(jsUndefined());\n");
             } else {
-                push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToFrame(state, castedThis->wrapped().frame(), ThrowSecurityError))\n");
+                push(@$outputArray, "    if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped().window(), ThrowSecurityError))\n");
                 push(@$outputArray, "        return JSValue::encode(jsUndefined());\n");
             }
         }
index 62916d3..0f7154a 100644 (file)
@@ -200,7 +200,7 @@ static inline JSValue jsTestActiveDOMObjectExcitingAttrGetter(ExecState& state,
 {
     UNUSED_PARAM(throwScope);
     UNUSED_PARAM(state);
-    if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped().window(), ThrowSecurityError))
         return jsUndefined();
     auto& impl = thisObject.wrapped();
     JSValue result = toJS<IDLLong>(state, throwScope, impl.excitingAttr());
@@ -216,7 +216,7 @@ static inline JSC::EncodedJSValue jsTestActiveDOMObjectPrototypeFunctionExciting
 {
     UNUSED_PARAM(state);
     UNUSED_PARAM(throwScope);
-    if (!BindingSecurity::shouldAllowAccessToFrame(state, castedThis->wrapped().frame(), ThrowSecurityError))
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped().window(), ThrowSecurityError))
         return JSValue::encode(jsUndefined());
     auto& impl = castedThis->wrapped();
     if (UNLIKELY(state->argumentCount() < 1))
index 7af1e62..df2f1eb 100644 (file)
@@ -56,8 +56,8 @@
 namespace WebCore {
 using namespace JSC;
 
-SubtleCrypto::SubtleCrypto(ScriptExecutionContext& context)
-    : ContextDestructionObserver(&context)
+SubtleCrypto::SubtleCrypto(ScriptExecutionContext* context)
+    : ContextDestructionObserver(context)
     , m_workQueue(WorkQueue::create("com.apple.WebKit.CryptoQueue"))
 {
 }
index 04e603e..afd397b 100644 (file)
@@ -54,7 +54,7 @@ enum class CryptoKeyUsage;
 
 class SubtleCrypto : public ContextDestructionObserver, public RefCounted<SubtleCrypto>, public CanMakeWeakPtr<SubtleCrypto> {
 public:
-    static Ref<SubtleCrypto> create(ScriptExecutionContext& context) { return adoptRef(*new SubtleCrypto(context)); }
+    static Ref<SubtleCrypto> create(ScriptExecutionContext* context) { return adoptRef(*new SubtleCrypto(context)); }
     ~SubtleCrypto();
 
     using KeyFormat = CryptoKeyFormat;
@@ -76,7 +76,7 @@ public:
     void unwrapKey(JSC::ExecState&, KeyFormat, BufferSource&& wrappedKey, CryptoKey& unwrappingKey, AlgorithmIdentifier&& unwrapAlgorithm, AlgorithmIdentifier&& unwrappedKeyAlgorithm, bool extractable, Vector<CryptoKeyUsage>&&, Ref<DeferredPromise>&&);
 
 private:
-    explicit SubtleCrypto(ScriptExecutionContext&);
+    explicit SubtleCrypto(ScriptExecutionContext*);
 
     inline friend RefPtr<DeferredPromise> getPromise(DeferredPromise*, WeakPtr<SubtleCrypto>);
 
index 7a463bf..bc37cec 100644 (file)
@@ -42,8 +42,8 @@
 
 namespace WebCore {
 
-Crypto::Crypto(ScriptExecutionContext& context)
-    : ContextDestructionObserver(&context)
+Crypto::Crypto(ScriptExecutionContext* context)
+    : ContextDestructionObserver(context)
 #if ENABLE(SUBTLE_CRYPTO)
     , m_subtle(SubtleCrypto::create(context))
 #endif
index fb2470e..3652655 100644 (file)
@@ -42,7 +42,7 @@ class SubtleCrypto;
 
 class Crypto : public ContextDestructionObserver, public RefCounted<Crypto> {
 public:
-    static Ref<Crypto> create(ScriptExecutionContext& context) { return adoptRef(*new Crypto(context)); }
+    static Ref<Crypto> create(ScriptExecutionContext* context) { return adoptRef(*new Crypto(context)); }
     virtual ~Crypto();
 
     ExceptionOr<void> getRandomValues(JSC::ArrayBufferView&);
@@ -52,7 +52,7 @@ public:
 #endif
 
 private:
-    Crypto(ScriptExecutionContext&);
+    Crypto(ScriptExecutionContext*);
 
 #if ENABLE(SUBTLE_CRYPTO)
     Ref<SubtleCrypto> m_subtle;
index 0c3a254..3bab48d 100644 (file)
@@ -418,37 +418,11 @@ void DOMWindow::didSecureTransitionTo(Document& document)
 
 DOMWindow::~DOMWindow()
 {
-#ifndef NDEBUG
-    if (!m_suspendedForDocumentSuspension) {
-        ASSERT(!m_screen);
-        ASSERT(!m_history);
-        ASSERT(!m_crypto);
-        ASSERT(!m_locationbar);
-        ASSERT(!m_menubar);
-        ASSERT(!m_personalbar);
-        ASSERT(!m_scrollbars);
-        ASSERT(!m_statusbar);
-        ASSERT(!m_toolbar);
-        ASSERT(!m_navigator);
-        ASSERT(!m_performance);
-        ASSERT(!m_location);
-        ASSERT(!m_media);
-        ASSERT(!m_sessionStorage);
-        ASSERT(!m_localStorage);
-        ASSERT(!m_applicationCache);
-        ASSERT(!m_visualViewport);
-    }
-#endif
-
     if (m_suspendedForDocumentSuspension)
         willDestroyCachedFrame();
     else
         willDestroyDocumentInFrame();
 
-    // As the ASSERTs above indicate, this reset should only be necessary if this DOMWindow is suspended for the page cache.
-    // But we don't want to risk any of these objects hanging around after we've been destroyed.
-    resetDOMWindowProperties();
-
     removeAllUnloadEventListeners(this);
     removeAllBeforeUnloadEventListeners(this);
 
@@ -475,7 +449,6 @@ void DOMWindow::frameDestroyed()
     Ref<DOMWindow> protectedThis(*this);
 
     willDestroyDocumentInFrame();
-    resetDOMWindowProperties();
     JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(this);
 }
 
@@ -664,8 +637,6 @@ int DOMWindow::orientation() const
 
 Screen* DOMWindow::screen()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_screen)
         m_screen = Screen::create(*this);
     return m_screen.get();
@@ -673,8 +644,6 @@ Screen* DOMWindow::screen()
 
 History* DOMWindow::history()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_history)
         m_history = History::create(*this);
     return m_history.get();
@@ -682,18 +651,13 @@ History* DOMWindow::history()
 
 Crypto* DOMWindow::crypto() const
 {
-    // FIXME: Why is crypto not available when the window is not currently displayed in a frame?
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_crypto)
-        m_crypto = Crypto::create(*document());
+        m_crypto = Crypto::create(document());
     return m_crypto.get();
 }
 
 BarProp* DOMWindow::locationbar()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_locationbar)
         m_locationbar = BarProp::create(*this, BarProp::Locationbar);
     return m_locationbar.get();
@@ -701,8 +665,6 @@ BarProp* DOMWindow::locationbar()
 
 BarProp* DOMWindow::menubar()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_menubar)
         m_menubar = BarProp::create(*this, BarProp::Menubar);
     return m_menubar.get();
@@ -710,8 +672,6 @@ BarProp* DOMWindow::menubar()
 
 BarProp* DOMWindow::personalbar()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_personalbar)
         m_personalbar = BarProp::create(*this, BarProp::Personalbar);
     return m_personalbar.get();
@@ -719,8 +679,6 @@ BarProp* DOMWindow::personalbar()
 
 BarProp* DOMWindow::scrollbars()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_scrollbars)
         m_scrollbars = BarProp::create(*this, BarProp::Scrollbars);
     return m_scrollbars.get();
@@ -728,8 +686,6 @@ BarProp* DOMWindow::scrollbars()
 
 BarProp* DOMWindow::statusbar()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_statusbar)
         m_statusbar = BarProp::create(*this, BarProp::Statusbar);
     return m_statusbar.get();
@@ -737,8 +693,6 @@ BarProp* DOMWindow::statusbar()
 
 BarProp* DOMWindow::toolbar()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_toolbar)
         m_toolbar = BarProp::create(*this, BarProp::Toolbar);
     return m_toolbar.get();
@@ -746,6 +700,7 @@ BarProp* DOMWindow::toolbar()
 
 PageConsoleClient* DOMWindow::console() const
 {
+    // FIXME: This should not return nullptr when frameless.
     if (!isCurrentlyDisplayedInFrame())
         return nullptr;
     auto* frame = this->frame();
@@ -754,8 +709,6 @@ PageConsoleClient* DOMWindow::console() const
 
 DOMApplicationCache* DOMWindow::applicationCache()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_applicationCache)
         m_applicationCache = DOMApplicationCache::create(*this);
     return m_applicationCache.get();
@@ -763,6 +716,7 @@ DOMApplicationCache* DOMWindow::applicationCache()
 
 Navigator* DOMWindow::navigator()
 {
+    // FIXME: This should not return nullptr when frameless.
     if (!isCurrentlyDisplayedInFrame())
         return nullptr;
 
@@ -776,8 +730,10 @@ Navigator* DOMWindow::navigator()
 
 Performance* DOMWindow::performance() const
 {
+    // FIXME: This should not return nullptr when frameless.
     if (!isCurrentlyDisplayedInFrame())
         return nullptr;
+
     if (!m_performance) {
         MonotonicTime timeOrigin = document()->loader() ? document()->loader()->timing().referenceMonotonicTime() : MonotonicTime::now();
         m_performance = Performance::create(*document(), timeOrigin);
@@ -792,8 +748,6 @@ double DOMWindow::nowTimestamp() const
 
 Location* DOMWindow::location()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_location)
         m_location = Location::create(*this);
     return m_location.get();
@@ -801,9 +755,7 @@ Location* DOMWindow::location()
 
 VisualViewport* DOMWindow::visualViewport()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
-    if (!m_visualViewport && !m_suspendedForDocumentSuspension)
+    if (!m_visualViewport)
         m_visualViewport = VisualViewport::create(*this);
     return m_visualViewport.get();
 }
@@ -1493,8 +1445,6 @@ Document* DOMWindow::document() const
 
 RefPtr<StyleMedia> DOMWindow::styleMedia()
 {
-    if (!isCurrentlyDisplayedInFrame())
-        return nullptr;
     if (!m_media)
         m_media = StyleMedia::create(*this);
     return m_media;
index b94d154..65873a4 100644 (file)
@@ -334,6 +334,7 @@ public:
     void enableSuddenTermination();
     void disableSuddenTermination();
 
+    void willDestroyDocumentInFrame();
     void frameDestroyed();
 
 private:
@@ -351,7 +352,6 @@ private:
     bool isInsecureScriptAccess(DOMWindow& activeWindow, const String& urlString);
 
     void resetDOMWindowProperties();
-    void willDestroyDocumentInFrame();
 
     bool isSameSecurityOriginAsMainFrame() const;
 
index a5592a9..9001900 100644 (file)
@@ -54,7 +54,7 @@ typedef USVString CSSOMString;
     [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute WindowProxy self;
     [Unforgeable] readonly attribute Document document;
     attribute DOMString name;
-    [DoNotCheckSecurity, PutForwards=href, Unforgeable] readonly attribute Location? location; // FIXME: Should not be nullable.
+    [DoNotCheckSecurity, PutForwards=href, Unforgeable] readonly attribute Location location;
     readonly attribute History history;
     [EnabledAtRuntime=CustomElements, ImplementedAs=ensureCustomElementRegistry] readonly attribute CustomElementRegistry customElements;
     [Replaceable] readonly attribute BarProp locationbar;
index 2a26313..15c6701 100644 (file)
@@ -421,7 +421,7 @@ bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vecto
 Crypto& WorkerGlobalScope::crypto()
 {
     if (!m_crypto)
-        m_crypto = Crypto::create(*this);
+        m_crypto = Crypto::create(this);
     return *m_crypto;
 }