Storage Access API: Web process should ask UI process for grant/deny
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Oct 2017 23:05:02 +0000 (23:05 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Oct 2017 23:05:02 +0000 (23:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176941
<rdar://problem/34440036>

Reviewed by Chris Dumez and Sam Weinig.

Source/WebCore:

Tests: http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html
       http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html
       http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html

This patch changes Document::requestStorageAccess() so that it
calls the ChromeClient instead of the ResourceLoadObserver to
request storage access on behalf of the iframe.
The patch also changes how the promise is used, from returning
a boolean to either resolving or rejecting to signal grant/deny
access respectively.

* dom/Document.cpp:
(WebCore::Document::requestStorageAccess):
* dom/Document.idl:
* loader/ResourceLoadObserver.cpp:
(WebCore::ResourceLoadObserver::registerStorageAccess): Deleted.
* loader/ResourceLoadObserver.h:
* page/ChromeClient.h:

Source/WebKit:

This patch adds infrastructure to let WebCore::Document call
WebKit::WebResourceLoadStatisticsStore and ask for storage
access on behalf of an iframe. The communication goes through
the ChromeClient since we might want to add a native prompt.
From the ChromeClient it continues to the WebPage which
messages the WebPageProxy. The WebPageProxy calls the
WebsiteDataStore which owns the WebResourceLoadStatisticsStore.
A uint64_t is used to match the right callback with the boolean
result.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::requestStorageAccess):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::requestStorageAccess):
* UIProcess/WebResourceLoadStatisticsStore.h:
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::requestStorageAccess):
* UIProcess/WebsiteData/WebsiteDataStore.h:
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::requestStorageAccess):
* WebProcess/WebCoreSupport/WebChromeClient.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::nextRequestStorageAccessContextId):
(WebKit::WebPage::requestStorageAccess):
(WebKit::WebPage::storageAccessResponse):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

LayoutTests:

The iframes now communicate the results back to the top frame through postMessage.
Also, denied access results in a rejected promise which is handled in these
changed test cases.

* http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe-expected.txt:
* http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe.html:
* http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe-expected.txt:
* http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe.html:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe-expected.txt: Removed.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe.html: Removed.
    Renamed to request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe-expected.txt: Added.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html: Added.
    See comment above on renaming.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-expected.txt:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction-expected.txt: Added.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html: Added.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt: Added.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html: Added.
    Two new test cases added for the new logic in WebKit::WebResourceLoadStatisticsStore.
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe.html:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe-expected.txt:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe.html:
* http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin-expected.txt:
* http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin.html:
* http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token-expected.txt:
* http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token.html:
* http/tests/storageAccess/request-storage-access-same-origin-iframe-expected.txt:
* http/tests/storageAccess/request-storage-access-same-origin-iframe.html:
* http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-expected.txt:
* http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token-expected.txt:
* http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token.html:
* http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe.html:
* http/tests/storageAccess/request-storage-access-top-frame-expected.txt:
* http/tests/storageAccess/request-storage-access-top-frame.html:
* http/tests/storageAccess/resources/request-storage-access-iframe.html:
* platform/mac-wk2/TestExpectations:
    Added the new tests as [ Pass ].

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

48 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe-expected.txt
LayoutTests/http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe.html
LayoutTests/http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe-expected.txt
LayoutTests/http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe.html
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe-expected.txt [moved from LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe-expected.txt with 76% similarity]
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html [moved from LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe.html with 67% similarity]
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-expected.txt
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html [new file with mode: 0644]
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html [new file with mode: 0644]
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe.html
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe-expected.txt
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe.html
LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin-expected.txt
LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin.html
LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token-expected.txt
LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token.html
LayoutTests/http/tests/storageAccess/request-storage-access-same-origin-iframe-expected.txt
LayoutTests/http/tests/storageAccess/request-storage-access-same-origin-iframe.html
LayoutTests/http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-expected.txt
LayoutTests/http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token-expected.txt
LayoutTests/http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token.html
LayoutTests/http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe.html
LayoutTests/http/tests/storageAccess/request-storage-access-top-frame-expected.txt
LayoutTests/http/tests/storageAccess/request-storage-access-top-frame.html
LayoutTests/http/tests/storageAccess/resources/request-storage-access-iframe.html
LayoutTests/platform/mac-wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.idl
Source/WebCore/loader/ResourceLoadObserver.cpp
Source/WebCore/loader/ResourceLoadObserver.h
Source/WebCore/page/ChromeClient.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.cpp
Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.h
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in

index 21b6ea4..3ee018e 100644 (file)
@@ -1,3 +1,50 @@
+2017-10-05  John Wilander  <wilander@apple.com>
+
+        Storage Access API: Web process should ask UI process for grant/deny
+        https://bugs.webkit.org/show_bug.cgi?id=176941
+        <rdar://problem/34440036>
+
+        Reviewed by Chris Dumez and Sam Weinig.
+
+        The iframes now communicate the results back to the top frame through postMessage.
+        Also, denied access results in a rejected promise which is handled in these
+        changed test cases.
+        * http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe-expected.txt:
+        * http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe.html:
+        * http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe-expected.txt:
+        * http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe.html:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe-expected.txt: Removed.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe.html: Removed.
+            Renamed to request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe-expected.txt: Added.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html: Added.
+            See comment above on renaming.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-expected.txt:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction-expected.txt: Added.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html: Added.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt: Added.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html: Added.
+            Two new test cases added for the new logic in WebKit::WebResourceLoadStatisticsStore.
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe.html:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe-expected.txt:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe.html:
+        * http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin-expected.txt:
+        * http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin.html:
+        * http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token-expected.txt:
+        * http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token.html:
+        * http/tests/storageAccess/request-storage-access-same-origin-iframe-expected.txt:
+        * http/tests/storageAccess/request-storage-access-same-origin-iframe.html:
+        * http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-expected.txt:
+        * http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token-expected.txt:
+        * http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token.html:
+        * http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe.html:
+        * http/tests/storageAccess/request-storage-access-top-frame-expected.txt:
+        * http/tests/storageAccess/request-storage-access-top-frame.html:
+        * http/tests/storageAccess/resources/request-storage-access-iframe.html:
+        * platform/mac-wk2/TestExpectations:
+            Added the new tests as [ Pass ].
+
 2017-10-05  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         RenderSVGRoot should check the renderers inside its visualOverflowRect for hit testing if the overflow is visible
index 9806948..70bbdcc 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that cross-origin iframe storage access is denied if the iframe is not sandboxed.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index c712533..1afb72a 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is not sandboxed.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 80b482c..db376be 100644 (file)
@@ -1,10 +1,10 @@
-CONFIRM: Do you want to use your  ID on ?
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
+CONFIRM: Do you want to use your localhost ID on 127.0.0.1?
 Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, but the user opts out.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 21e8d3f..7928bd4 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, but the user opts out.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that cross-origin iframe storage access is denied if the iframe is not sandboxed.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is not sandboxed.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS ") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data);
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
+                    else {
+                        testFailed("No eventSender.");
                         finishJSTest();
-                    },
+                    }
+                },
                 function () {
                     testFailed("Promise rejected.");
                     finishJSTest();
index 61854cc..5dce6e3 100644 (file)
@@ -1,10 +1,10 @@
-CONFIRM: Do you want to use your  ID on ?
-CONSOLE MESSAGE: line 64: PASS document.hasStorageAccess was granted.
+CONFIRM: Do you want to use your localhost ID on 127.0.0.1?
 Tests that cross-origin iframe storage access is granted if the iframe is sandboxed, has the allow token, and the user opts in.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was granted.
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction-expected.txt b/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction-expected.txt
new file mode 100644 (file)
index 0000000..3004fa3
--- /dev/null
@@ -0,0 +1,11 @@
+CONFIRM: Do you want to use your localhost ID on 127.0.0.1?
+Tests that cross-origin iframe storage access is granted if the iframe is sandboxed, has the allow token, the iframe origin is a prevalent resource, the iframe origin has had user interaction, and the user opts in.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.hasStorageAccess was granted.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html b/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html
new file mode 100644 (file)
index 0000000..2f1e3e3
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/js-test-resources/js-test.js"></script>
+    <script src="/js-test-resources/ui-helper.js"></script>
+    <script>
+        description("Tests that cross-origin iframe storage access is granted if the iframe is sandboxed, has the allow token, the iframe origin is a prevalent resource, the iframe origin has had user interaction, and the user opts in.");
+        jsTestIsAsync = true;
+
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data);
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
+        function activateElement(elementId) {
+            var element = document.getElementById(elementId);
+            var centerX = element.offsetLeft + element.offsetWidth / 2;
+            var centerY = element.offsetTop + element.offsetHeight / 2;
+            UIHelper.activateAt(centerX, centerY).then(
+                function () {
+                    if (window.eventSender)
+                        eventSender.keyDown("escape");
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
+                },
+                function () {
+                    testFailed("Promise rejected.");
+                    finishJSTest();
+                }
+            );
+        }
+
+        function runTest() {
+            activateElement("theIframe");
+        }
+
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+        testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+        if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+            testFailed("Host did not get set as prevalent resource.");
+        testRunner.setStatisticsHasHadUserInteraction(statisticsUrl, true);
+        if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+            testFailed("Host did not get logged for user interaction.");
+    </script>
+</head>
+<body>
+    <iframe sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin allow-modals" onload="runTest()" id="theIframe" src="http://localhost:8000/storageAccess/resources/request-storage-access-iframe.html#userShouldGrantAccess,userShouldBeConsulted,policyShouldGrantAccess,isNotSameOriginIframe"></iframe>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt b/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt
new file mode 100644 (file)
index 0000000..8ba898f
--- /dev/null
@@ -0,0 +1,11 @@
+CONFIRM: Do you want to use your localhost ID on 127.0.0.1?
+Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, the iframe origin is a prevalent resource, the iframe origin has not had user interaction, and the user opts in.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.hasStorageAccess was denied.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html b/LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html
new file mode 100644 (file)
index 0000000..0b1b1d5
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/js-test-resources/js-test.js"></script>
+    <script src="/js-test-resources/ui-helper.js"></script>
+    <script>
+        description("Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, the iframe origin is a prevalent resource, the iframe origin has not had user interaction, and the user opts in.");
+        jsTestIsAsync = true;
+
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data);
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
+        function activateElement(elementId) {
+            var element = document.getElementById(elementId);
+            var centerX = element.offsetLeft + element.offsetWidth / 2;
+            var centerY = element.offsetTop + element.offsetHeight / 2;
+            UIHelper.activateAt(centerX, centerY).then(
+                function () {
+                    if (window.eventSender)
+                        eventSender.keyDown("escape");
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
+                },
+                function () {
+                    testFailed("Promise rejected.");
+                    finishJSTest();
+                }
+            );
+        }
+
+        function runTest() {
+            activateElement("theIframe");
+        }
+
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+        testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+        if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+            testFailed("Host did not get set as prevalent resource.");
+    </script>
+</head>
+<body>
+    <iframe sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin allow-modals" onload="runTest()" id="theIframe" src="http://localhost:8000/storageAccess/resources/request-storage-access-iframe.html#userShouldGrantAccess,userShouldBeConsulted,policyShouldNotGrantAccess,isNotSameOriginIframe"></iframe>
+</body>
+</html>
\ No newline at end of file
index 389fddf..bd308c6 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is granted if the iframe is sandboxed, has the allow token, and the user opts in.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data);
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 34d20d9..1b6c055 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, but is nested.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 19572ef..8d1d80f 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, but is nested.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 8316563..2ac95da 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that cross-origin iframe storage access is denied if the iframe is sandboxed and has the unique origin because it lacks the allow-same-origin token.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 6682c7e..c0eaf9b 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is sandboxed and has the unique origin because it lacks the allow-same-origin token.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "null") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index a1dd572..bfec2f3 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that cross-origin iframe storage access is denied if the iframe is sandboxed and doesn't have the allow token.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 0909a69..3284d40 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is sandboxed and doesn't have the allow token.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://localhost:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 18396ed..1a649a4 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that same-origin iframe storage access is denied if the iframe is not sandboxed.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 2aac8b8..6b11289 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that same-origin iframe storage access is denied if the iframe is not sandboxed.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://127.0.0.1:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 764d412..fdcd106 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 64: PASS document.hasStorageAccess was granted.
 Tests that same-origin iframe storage access is granted if the iframe is sandboxed and has the allow token.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was granted.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index f257f5b..10ea8fc 100644 (file)
@@ -1,9 +1,9 @@
-CONSOLE MESSAGE: line 68: PASS document.hasStorageAccess was denied.
 Tests that same-origin iframe storage access is denied if the iframe is sandboxed but doesn't have the allow token.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS document.hasStorageAccess was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index d32b3d7..c8550b6 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that same-origin iframe storage access is denied if the iframe is sandboxed but doesn't have the allow token.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://127.0.0.1:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 4464af9..46674a2 100644 (file)
@@ -7,6 +7,19 @@
         description("Tests that same-origin iframe storage access is granted if the iframe is sandboxed and has the allow token.");
         jsTestIsAsync = true;
 
+        window.addEventListener("message", receiveMessage, false);
+
+        function receiveMessage(event) {
+            if (event.origin === "http://127.0.0.1:8000") {
+                if (event.data.indexOf("PASS") !== -1)
+                    testPassed(event.data.replace("PASS ", ""));
+                else
+                    testFailed(event.data.replace("FAIL ", ""));
+            } else
+                testFailed("Received a message from an unexpected origin: " + event.origin);
+            finishJSTest();
+        }
+
         function activateElement(elementId) {
             var element = document.getElementById(elementId);
             var centerX = element.offsetLeft + element.offsetWidth / 2;
                 function () {
                     if (window.eventSender)
                         eventSender.keyDown("escape");
-                    finishJSTest();
+                    else {
+                        testFailed("No eventSender.");
+                        finishJSTest();
+                    }
                 },
                 function () {
                     testFailed("Promise rejected.");
index 9664a80..13e1cc6 100644 (file)
@@ -3,9 +3,9 @@ Tests that top frame storage access is always granted in case the page requests
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS requestStorageAccessReturnValue is true
+PASS requestStorageAccessResolved is true
 PASS document.hasStorageAccess is true
-PASS requestStorageAccessReturnValue is true
+PASS requestStorageAccessResolved is true
 PASS document.hasStorageAccess is true
 PASS successfullyParsed is true
 
index f05e2d7..d22517a 100644 (file)
         var centerX = element.offsetLeft + element.offsetWidth / 2;
         var centerY = element.offsetTop + element.offsetHeight / 2;
         UIHelper.activateAt(centerX, centerY).then(
-            function() { },
-            function() {
+            function () {
+            },
+            function () {
                 testFailed("Promise rejected.");
                 finishJSTest();
             }
         );
     }
 
-    var requestStorageAccessReturnValue;
+    var requestStorageAccessResolved;
 
     function makeRequestWithoutUserGesture() {
-        document.requestStorageAccess();
         var promise = document.requestStorageAccess();
-        promise.then(function (hasAccess) {
-            requestStorageAccessReturnValue = hasAccess;
-            shouldBe("requestStorageAccessReturnValue", "true");
-            shouldBe("document.hasStorageAccess", "true");
-            activateElement("theButton");
-        });
+        promise.then(
+            function () {
+                requestStorageAccessResolved = true;
+                continueAfterRequestWithoutUserGesture();
+            },
+            function () {
+                requestStorageAccessResolved = false;
+                continueAfterRequestWithoutUserGesture();
+            }
+        );
+    }
+
+    function continueAfterRequestWithoutUserGesture() {
+        shouldBe("requestStorageAccessResolved", "true");
+        shouldBe("document.hasStorageAccess", "true");
+        activateElement("theButton");
     }
 
     function makeRequestWithUserGesture() {
-        document.requestStorageAccess();
         var promise = document.requestStorageAccess();
-        promise.then(function (hasAccess) {
-            requestStorageAccessReturnValue = hasAccess;
-            shouldBe("requestStorageAccessReturnValue", "true");
-            shouldBe("document.hasStorageAccess", "true");
-            finishJSTest();
-        });
+        promise.then(
+            function () {
+                requestStorageAccessResolved = true;
+                continueAfterRequestWithUserGesture();
+            },
+            function () {
+                requestStorageAccessResolved = false;
+                continueAfterRequestWithUserGesture();
+            }
+        );
+    }
+
+    function continueAfterRequestWithUserGesture() {
+        shouldBe("requestStorageAccessResolved", "true");
+        shouldBe("document.hasStorageAccess", "true");
+        finishJSTest();
     }
 
     function runTest() {
index 5040ef0..9f7b42b 100644 (file)
 
         function storageAccessShouldBeFalse() {
             if (document.hasStorageAccess)
-                console.log("FAIL document.hasStorageAccess was true when it was not supposed to.");
-            if (requestStorageAccessReturnValue)
-                console.log("FAIL requestStorageAccessReturnValue was true when it was not supposed to.");
+                top.postMessage("FAIL document.hasStorageAccess was true when it was not supposed to.", "http://127.0.0.1:8000");
+            if (requestStorageAccessResolved)
+                top.postMessage("FAIL requestStorageAccessResolved was true when it was not supposed to.", "http://127.0.0.1:8000");
         }
 
         function storageAccessShouldBeTrue() {
             if (!document.hasStorageAccess)
-                console.log("FAIL document.hasStorageAccess was false when it was not supposed to.");
-            if (!requestStorageAccessReturnValue)
-                console.log("FAIL requestStorageAccessReturnValue was false when it was not supposed to.");
+                top.postMessage("FAIL document.hasStorageAccess was false when it was not supposed to.", "http://127.0.0.1:8000");
+            if (!requestStorageAccessResolved)
+                top.postMessage("FAIL requestStorageAccessResolved was false when it was not supposed to.", "http://127.0.0.1:8000");
         }
 
-        var requestStorageAccessReturnValue;
+        var requestStorageAccessResolved;
 
         function makeRequestWithoutUserGesture() {
             storageAccessShouldBeFalse();
 
             var promise = document.requestStorageAccess();
-            promise.then(function (hasAccess) {
-                requestStorageAccessReturnValue = hasAccess;
-                continueAfterRequestWithoutUserGesture();
-            });
+            promise.then(
+                function () {
+                    requestStorageAccessResolved = true;
+                    continueAfterRequestWithoutUserGesture();
+                },
+                function () {
+                    requestStorageAccessResolved = false;
+                    continueAfterRequestWithoutUserGesture();
+                }
+            );
         }
 
         function continueAfterRequestWithoutUserGesture() {
                 storageAccessShouldBeFalse();
 
             var promise = document.requestStorageAccess();
-            promise.then(function (hasAccess) {
-                requestStorageAccessReturnValue = hasAccess;
-                continueAfterRequestWithUserGesture();
-            });
+            promise.then(
+                function () {
+                    requestStorageAccessResolved = true;
+                    continueAfterRequestWithUserGesture();
+                },
+                function () {
+                    requestStorageAccessResolved = false;
+                    continueAfterRequestWithUserGesture();
+                }
+            );
         }
 
         function continueAfterRequestWithUserGesture() {
-            if (requestStorageAccessReturnValue
+            if (requestStorageAccessResolved
                 && document.hasStorageAccess
                 && (userShouldGrantAccess || !userShouldBeConsulted)
                 && policyShouldGrantAccess)
-                console.log("PASS document.hasStorageAccess was granted.");
+                top.postMessage("PASS document.hasStorageAccess was granted.", "http://127.0.0.1:8000");
             else if (!document.hasStorageAccess
-                    && !requestStorageAccessReturnValue
+                    && !requestStorageAccessResolved
                     && ((!userShouldGrantAccess && userShouldBeConsulted) || !policyShouldGrantAccess))
-                console.log("PASS document.hasStorageAccess was denied.");
+                top.postMessage("PASS document.hasStorageAccess was denied.", "http://127.0.0.1:8000");
             else
-                console.log("FAIL document.hasStorageAccess was " +
+                top.postMessage("FAIL document.hasStorageAccess was " +
                     (document.hasStorageAccess ? "" : "not ") +
-                    "granted and requestStorageAccessReturnValue was " +
-                    (requestStorageAccessReturnValue ? "" : "not ") +
+                    "granted and requestStorageAccessResolved was " +
+                    (requestStorageAccessResolved ? "" : "not ") +
                     "granted but should " +
                     (userShouldGrantAccess && policyShouldGrantAccess ? "" : "not ") +
-                    "have been granted.");
+                    "have been granted.", "http://127.0.0.1:8000");
         }
     </script>
 </head>
index ca063af..1b6a52a 100644 (file)
@@ -761,7 +761,7 @@ http/tests/loading/resourceLoadStatistics/user-interaction-only-reported-once-wi
 http/tests/loading/resourceLoadStatistics/user-interaction-reported-after-website-data-removal.html [ Pass ]
 http/tests/storageAccess/request-and-deny-storage-access-cross-origin-iframe.html [ Pass ]
 http/tests/storageAccess/request-and-deny-storage-access-cross-origin-sandboxed-iframe.html [ Pass ]
-http/tests/storageAccess/request-and-grant-storage-access-cross-origin-iframe.html [ Pass ]
+http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html [ Pass ]
 http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe.html [ Pass ]
 http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-with-unique-origin.html [ Pass ]
 http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-allow-token.html [ Pass ]
@@ -769,6 +769,9 @@ http/tests/storageAccess/request-storage-access-same-origin-iframe.html [ Pass ]
 http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe-without-allow-token.html [ Pass ]
 http/tests/storageAccess/request-storage-access-same-origin-sandboxed-iframe.html [ Pass ]
 http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-nested-iframe.html [ Pass ]
+http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html [ Pass ]
+http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html [ Pass ]
+http/tests/storageAccess/request-storage-access-top-frame.html [ Pass ]
 
 webkit.org/b/173861 [ Release ] http/tests/webrtc/filtering-ice-candidate-same-origin-frame.html [ Pass Timeout ]
 webkit.org/b/173861 [ Release ] http/tests/webrtc/filtering-ice-candidate-cross-origin-frame.html [ Pass Timeout ]
index c371147..55382ae 100644 (file)
@@ -1,3 +1,30 @@
+2017-10-05  John Wilander  <wilander@apple.com>
+
+        Storage Access API: Web process should ask UI process for grant/deny
+        https://bugs.webkit.org/show_bug.cgi?id=176941
+        <rdar://problem/34440036>
+
+        Reviewed by Chris Dumez and Sam Weinig.
+
+        Tests: http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html
+               http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-user-interaction.html
+               http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction.html
+
+        This patch changes Document::requestStorageAccess() so that it
+        calls the ChromeClient instead of the ResourceLoadObserver to
+        request storage access on behalf of the iframe.
+        The patch also changes how the promise is used, from returning
+        a boolean to either resolving or rejecting to signal grant/deny
+        access respectively.
+
+        * dom/Document.cpp:
+        (WebCore::Document::requestStorageAccess):
+        * dom/Document.idl:
+        * loader/ResourceLoadObserver.cpp:
+        (WebCore::ResourceLoadObserver::registerStorageAccess): Deleted.
+        * loader/ResourceLoadObserver.h:
+        * page/ChromeClient.h:
+
 2017-10-05  Brian Burg  <bburg@apple.com>
 
         Web Inspector: EventLoop::cycle() should not send nil NSEvents
index 7e2bd85..bb62015 100644 (file)
@@ -7277,31 +7277,31 @@ void Document::requestStorageAccess(Ref<DeferredPromise>&& passedPromise)
     RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
     
     if (m_hasStorageAccess) {
-        promise->resolve<IDLBoolean>(true);
+        promise->resolve();
         return;
     }
     
     if (!m_frame || securityOrigin().isUnique()) {
-        promise->resolve<IDLBoolean>(false);
+        promise->reject();
         return;
     }
     
     if (m_frame->isMainFrame()) {
         m_hasStorageAccess = true;
-        promise->resolve<IDLBoolean>(true);
+        promise->resolve();
         return;
     }
     
     // There has to be a sandbox and it has to allow the storage access API to be called.
     if (sandboxFlags() == SandboxNone || isSandboxed(SandboxStorageAccessByUserActivation)) {
-        promise->resolve<IDLBoolean>(false);
+        promise->reject();
         return;
     }
 
     // The iframe has to be a direct child of the top document.
     auto& topDocument = this->topDocument();
     if (&topDocument != parentDocument()) {
-        promise->resolve<IDLBoolean>(false);
+        promise->reject();
         return;
     }
 
@@ -7309,33 +7309,41 @@ void Document::requestStorageAccess(Ref<DeferredPromise>&& passedPromise)
     auto& topSecurityOrigin = topDocument.securityOrigin();
     if (securityOrigin.equal(&topSecurityOrigin)) {
         m_hasStorageAccess = true;
-        promise->resolve<IDLBoolean>(true);
+        promise->resolve();
         return;
     }
     
     if (!UserGestureIndicator::processingUserGesture()) {
-        promise->resolve<IDLBoolean>(false);
+        promise->reject();
         return;
     }
     
-    auto partitionDomain = securityOrigin.domainForCachePartition();
-    auto topPartitionDomain = topSecurityOrigin.domainForCachePartition();
+    auto iframeHost = securityOrigin.host();
+    auto topHost = topSecurityOrigin.host();
     StringBuilder builder;
     builder.appendLiteral("Do you want to use your ");
-    builder.append(partitionDomain);
+    builder.append(iframeHost);
     builder.appendLiteral(" ID on ");
-    builder.append(topPartitionDomain);
+    builder.append(topHost);
     builder.appendLiteral("?");
     Page* page = this->page();
     // FIXME: Don't use runJavaScriptConfirm because it responds synchronously.
     if ((page && page->chrome().runJavaScriptConfirm(*m_frame, builder.toString())) || m_grantStorageAccessOverride) {
-        m_hasStorageAccess = true;
-        ResourceLoadObserver::shared().registerStorageAccess(partitionDomain, topPartitionDomain);
-        promise->resolve<IDLBoolean>(true);
+        page->chrome().client().requestStorageAccess(WTFMove(iframeHost), WTFMove(topHost), [documentReference = m_weakFactory.createWeakPtr(*this), promise] (bool wasGranted) {
+            Document* document = documentReference.get();
+            if (!document)
+                return;
+
+            if (wasGranted) {
+                document->m_hasStorageAccess = true;
+                promise->resolve();
+            } else
+                promise->reject();
+        });
         return;
     }
     
-    promise->resolve<IDLBoolean>(false);
+    promise->reject();
 }
 
 void Document::setConsoleMessageListener(RefPtr<StringCallback>&& listener)
index 86caa78..5bc5e34 100644 (file)
@@ -191,7 +191,7 @@ typedef (
 
     // Non standard, to bring up with standards working group.
     [EnabledBySetting=StorageAccessAPI] readonly attribute boolean hasStorageAccess;
-    [EnabledBySetting=StorageAccessAPI] Promise<boolean> requestStorageAccess();
+    [EnabledBySetting=StorageAccessAPI] Promise<void> requestStorageAccess();
 
     // Obsolete features from https://html.spec.whatwg.org/multipage/obsolete.html
 
index c320f03..5c228eb 100644 (file)
@@ -284,23 +284,6 @@ void ResourceLoadObserver::logUserInteractionWithReducedTimeResolution(const Doc
     notifyObserver();
 }
 
-void ResourceLoadObserver::registerStorageAccess(const String& subFrameTopPrivatelyControlledDomain, const String& topFrameTopPrivatelyControlledDomain)
-{
-    auto addResult = m_storageAccessMap.add(subFrameTopPrivatelyControlledDomain, HashSet<String> { });
-    if (!addResult.isNewEntry && addResult.iterator->value.contains(topFrameTopPrivatelyControlledDomain))
-        return;
-
-    if (addResult.isNewEntry)
-        addResult.iterator->value = HashSet<String> { topFrameTopPrivatelyControlledDomain };
-    else
-        m_storageAccessMap.get(subFrameTopPrivatelyControlledDomain).add(topFrameTopPrivatelyControlledDomain);
-
-    auto& statistics = ensureResourceStatisticsForPrimaryDomain(subFrameTopPrivatelyControlledDomain);
-    statistics.storageAccessUnderTopFrameOrigins.add(topFrameTopPrivatelyControlledDomain);
-    m_notificationTimer.stop();
-    notifyObserver();
-}
-
 ResourceLoadStatistics& ResourceLoadObserver::ensureResourceStatisticsForPrimaryDomain(const String& primaryDomain)
 {
     auto addResult = m_resourceStatisticsMap.ensure(primaryDomain, [&primaryDomain] {
index 5e2cab0..cf08e4b 100644 (file)
@@ -60,7 +60,6 @@ public:
     void logWebSocketLoading(const Frame*, const URL&);
     void logUserInteractionWithReducedTimeResolution(const Document&);
 
-    void registerStorageAccess(const String& subFrameTopPrivatelyControlledDomain, const String& topFrameTopPrivatelyControlledDomain);
     WEBCORE_EXPORT String statisticsForOrigin(const String&);
 
     WEBCORE_EXPORT void setNotificationCallback(WTF::Function<void (Vector<ResourceLoadStatistics>&&)>&&);
@@ -77,7 +76,6 @@ private:
     Vector<ResourceLoadStatistics> takeStatistics();
 
     HashMap<String, ResourceLoadStatistics> m_resourceStatisticsMap;
-    HashMap<String, HashSet<String>> m_storageAccessMap;
     HashMap<String, WTF::WallTime> m_lastReportedUserInteractionMap;
     WTF::Function<void (Vector<ResourceLoadStatistics>&&)> m_notificationCallback;
     Timer m_notificationTimer;
index 2b69ff9..306527b 100644 (file)
@@ -464,6 +464,8 @@ public:
     virtual void reportProcessCPUTime(Seconds, ActivityStateForCPUSampling) { }
     virtual RefPtr<Icon> createIconForFiles(const Vector<String>& /* filenames */) = 0;
 
+    virtual void requestStorageAccess(String&& /*subFrameHost*/, String&& /*topFrameHost*/, WTF::Function<void (bool)>&& callback) { callback(false); }
+
 protected:
     virtual ~ChromeClient() { }
 };
index 3483c98..5a3adbd 100644 (file)
@@ -1,3 +1,41 @@
+2017-10-05  John Wilander  <wilander@apple.com>
+
+        Storage Access API: Web process should ask UI process for grant/deny
+        https://bugs.webkit.org/show_bug.cgi?id=176941
+        <rdar://problem/34440036>
+
+        Reviewed by Chris Dumez and Sam Weinig.
+
+        This patch adds infrastructure to let WebCore::Document call
+        WebKit::WebResourceLoadStatisticsStore and ask for storage
+        access on behalf of an iframe. The communication goes through
+        the ChromeClient since we might want to add a native prompt.
+        From the ChromeClient it continues to the WebPage which
+        messages the WebPageProxy. The WebPageProxy calls the
+        WebsiteDataStore which owns the WebResourceLoadStatisticsStore.
+        A uint64_t is used to match the right callback with the boolean
+        result.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::requestStorageAccess):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::requestStorageAccess):
+        * UIProcess/WebResourceLoadStatisticsStore.h:
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::requestStorageAccess):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::requestStorageAccess):
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::nextRequestStorageAccessContextId):
+        (WebKit::WebPage::requestStorageAccess):
+        (WebKit::WebPage::storageAccessResponse):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2017-10-05  Chris Dumez  <cdumez@apple.com>
 
         Drop unused parameters for CookiesStrategy::cookiesEnabled()
index 1ef1392..b8461b8 100644 (file)
@@ -7074,4 +7074,11 @@ void WebPageProxy::stopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskId
     iterator->value->stopTask(*this, taskIdentifier);
 }
 
+void WebPageProxy::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t webProcessContextId)
+{
+    m_websiteDataStore->requestStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), [this, webProcessContextId] (bool wasGranted) {
+        m_process->send(Messages::WebPage::StorageAccessResponse(wasGranted, webProcessContextId), m_pageID);
+    });
+}
+
 } // namespace WebKit
index 7058c8e..59d07c3 100644 (file)
@@ -1209,6 +1209,8 @@ public:
 #endif
     void editorStateChanged(const EditorState&);
 
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t webProcessContextId);
+
 private:
     WebPageProxy(PageClient&, WebProcessProxy&, uint64_t pageID, Ref<API::PageConfiguration>&&);
     void platformInitialize();
index ce5dba2..ec0bc9f 100644 (file)
@@ -500,4 +500,6 @@ messages -> WebPageProxy {
 
     StartURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier, WebCore::ResourceRequest request)
     StopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier)
+
+    RequestStorageAccess(String subFrameHost, String topFrameHost, uint64_t contextID)
 }
index 2189500..4fbd436 100644 (file)
@@ -244,6 +244,17 @@ void WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated(Vector<WebCor
     processStatisticsAndDataRecords();
 }
 
+void WebResourceLoadStatisticsStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+{
+    ASSERT(subFrameHost != topFrameHost);
+    ASSERT(RunLoop::isMain());
+
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), subFramePrimaryDomain = isolatedPrimaryDomain(subFrameHost), callback = WTFMove(callback)] () {
+        auto& statistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
+        callback(!statistic.isPrevalentResource || statistic.hadUserInteraction);
+    });
+}
+    
 void WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData()
 {
     ASSERT(!RunLoop::isMain());
index 21e1809..af50435 100644 (file)
@@ -78,6 +78,7 @@ public:
     void setShouldSubmitTelemetry(bool value) { m_parameters.shouldSubmitTelemetry = value; }
 
     void resourceLoadStatisticsUpdated(Vector<WebCore::ResourceLoadStatistics>&& origins);
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback);
 
     void processWillOpenConnection(WebProcessProxy&, IPC::Connection&);
     void processDidCloseConnection(WebProcessProxy&, IPC::Connection&);
index fc8e854..a766b41 100644 (file)
@@ -1367,6 +1367,16 @@ void WebsiteDataStore::removePendingCookie(const WebCore::Cookie& cookie)
     m_pendingCookies.remove(cookie);
 }
 
+void WebsiteDataStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+{
+    if (!resourceLoadStatisticsEnabled()) {
+        callback(false);
+        return;
+    }
+
+    m_resourceLoadStatistics->requestStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), WTFMove(callback));
+}
+
 #if !PLATFORM(COCOA)
 WebsiteDataStoreParameters WebsiteDataStore::parameters()
 {
index bd35206..1478b27 100644 (file)
@@ -132,6 +132,8 @@ public:
 
     void enableResourceLoadStatisticsAndSetTestingCallback(Function<void (const String&)>&& callback);
 
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback);
+
 private:
     explicit WebsiteDataStore(PAL::SessionID);
     explicit WebsiteDataStore(Configuration, PAL::SessionID);
index 295fbb1..4a6a3a1 100644 (file)
@@ -1223,4 +1223,9 @@ void WebChromeClient::didInvalidateDocumentMarkerRects()
     m_page.findController().didInvalidateDocumentMarkerRects();
 }
 
+void WebChromeClient::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+{
+    m_page.requestStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), WTFMove(callback));
+}
+
 } // namespace WebKit
index d95e8ca..691aed7 100644 (file)
@@ -342,6 +342,8 @@ private:
 
     void didInvalidateDocumentMarkerRects() final;
 
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&&);
+
     String m_cachedToolTip;
     mutable RefPtr<WebFrame> m_cachedFrameSetLargestFrame;
     mutable bool m_cachedMainFrameHasHorizontalScrollbar { false };
index fd5cf35..8d576ea 100644 (file)
@@ -6041,4 +6041,28 @@ void WebPage::urlSchemeTaskDidComplete(uint64_t handlerIdentifier, uint64_t task
     handler->taskDidComplete(taskIdentifier, error);
 }
 
+static uint64_t nextRequestStorageAccessContextId()
+{
+    static uint64_t nextContextId = 0;
+    return ++nextContextId;
+}
+
+void WebPage::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+{
+    auto contextId = nextRequestStorageAccessContextId();
+    auto addResult = m_storageAccessResponseCallbackMap.add(contextId, WTFMove(callback));
+    ASSERT(addResult.isNewEntry);
+    if (addResult.iterator->value)
+        send(Messages::WebPageProxy::RequestStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), contextId));
+    else
+        callback(false);
+}
+
+void WebPage::storageAccessResponse(bool wasGranted, uint64_t contextId)
+{
+    auto callback = m_storageAccessResponseCallbackMap.take(contextId);
+    ASSERT(callback);
+    callback(wasGranted);
+}
+
 } // namespace WebKit
index ebe2c0a..643074a 100644 (file)
@@ -994,6 +994,9 @@ public:
     void sendPartialEditorStateAndSchedulePostLayoutUpdate();
     void flushPendingEditorStateUpdate();
 
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback);
+    void storageAccessResponse(bool wasGranted, uint64_t contextId);
+
 private:
     WebPage(uint64_t pageID, WebPageCreationParameters&&);
 
@@ -1588,6 +1591,8 @@ private:
 
     HashMap<String, RefPtr<WebURLSchemeHandlerProxy>> m_schemeToURLSchemeHandlerProxyMap;
     HashMap<uint64_t, WebURLSchemeHandlerProxy*> m_identifierToURLSchemeHandlerProxyMap;
+
+    HashMap<uint64_t, WTF::Function<void (bool granted)>> m_storageAccessResponseCallbackMap;
 };
 
 } // namespace WebKit
index 54c2abe..7f8d053 100644 (file)
@@ -482,4 +482,6 @@ messages -> WebPage LegacyReceiver {
     URLSchemeTaskDidReceiveResponse(uint64_t handlerIdentifier, uint64_t taskIdentifier, WebCore::ResourceResponse response)
     URLSchemeTaskDidReceiveData(uint64_t handlerIdentifier, uint64_t taskIdentifier, IPC::DataReference data)
     URLSchemeTaskDidComplete(uint64_t handlerIdentifier, uint64_t taskIdentifier, WebCore::ResourceError error)
+
+    StorageAccessResponse(bool wasGranted, uint64_t contextId)
 }