Storage Access API: Make document.hasStorageAccess a function and always allow access...
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Dec 2017 19:41:01 +0000 (19:41 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Dec 2017 19:41:01 +0000 (19:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176944
<rdar://problem/34440658>

Reviewed by Brent Fulgham.

Source/WebCore:

Test: http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html

This change introduces document.hasStorageAccess() as a function which
returns a promise instead of being a property. Since cookie access can
be due to both a granted request and recent user interaction as first
party, the WebKit::WebResourceLoadStatisticsStore needs to be consulted.

* dom/Document.cpp:
(WebCore::Document::hasStorageAccess):
(WebCore::Document::requestStorageAccess):
    Removed check of the previous m_hasStorageAccess member.
    Same-origin check done earlier. This was a request/suggestion
    from Mozilla.
* dom/Document.h:
(WebCore::Document::hasStorageAccess const): Deleted.
    Now uses a promise.
* dom/Document.idl:
* page/ChromeClient.h:

Source/WebKit:

This change introduces document.hasStorageAccess() as a function which
returns a promise instead of being a property. Since cookie access can
be due to both a granted request and recent user interaction as first
party, the WebKit::WebResourceLoadStatisticsStore needs to be consulted.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::hasStorageAccess):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::hasStorageAccess):
(WebKit::WebResourceLoadStatisticsStore::requestStorageAccess):
    Now adds an entry for granted access. A bug found through testing.
    Switched from WTF::Function to WTF::CompletionHandler.
* UIProcess/WebResourceLoadStatisticsStore.h:
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::hasStorageAccess):
(WebKit::WebsiteDataStore::requestStorageAccess):
    Switched from WTF::Function to WTF::CompletionHandler.
* UIProcess/WebsiteData/WebsiteDataStore.h:
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::hasStorageAccess):
(WebKit::WebChromeClient::requestStorageAccess):
    Switched from WTF::Function to WTF::CompletionHandler.
* WebProcess/WebCoreSupport/WebChromeClient.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::hasStorageAccess):
(WebKit::WebPage::requestStorageAccess):
    Switched from WTF::Function to WTF::CompletionHandler.
* WebProcess/WebPage/WebPage.h:

LayoutTests:

These tests now use the new document.hasStorageAccess() function
instead of the previous document.hasStorageAccess property.
The added test is a break out of the negative case of an iframe
calling the API when no user gesture is processed.

* 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-non-sandboxed-iframe-expected.txt:
* 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-expected.txt:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-non-recent-user-interaction-expected.txt:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-non-recent-user-interaction.html:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-recent-user-interaction-expected.txt:
* http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt:
* 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-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-cross-origin-sandboxed-iframe-without-user-gesture-expected.txt: Added.
* http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html: Added.
* 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-top-frame-expected.txt:
* http/tests/storageAccess/request-storage-access-top-frame.html:
* http/tests/storageAccess/resources/request-storage-access-iframe.html:
* http/tests/storageAccess/resources/request-storage-access-without-user-gesture-iframe.html: Added.
* platform/mac-wk2/TestExpectations:
    Marked the new test case as [ Pass ]

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

46 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
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-non-sandboxed-iframe.html
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-non-recent-user-interaction-expected.txt
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-non-recent-user-interaction.html
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-recent-user-interaction-expected.txt
LayoutTests/http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt
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-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-cross-origin-sandboxed-iframe-without-user-gesture-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html [new file with mode: 0644]
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-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/http/tests/storageAccess/resources/request-storage-access-without-user-gesture-iframe.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Document.idl
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

index d12826b..566196f 100644 (file)
@@ -1,3 +1,46 @@
+2017-12-06  John Wilander  <wilander@apple.com>
+
+        Storage Access API: Make document.hasStorageAccess a function and always allow access for same-origin iframes
+        https://bugs.webkit.org/show_bug.cgi?id=176944
+        <rdar://problem/34440658>
+
+        Reviewed by Brent Fulgham.
+
+        These tests now use the new document.hasStorageAccess() function
+        instead of the previous document.hasStorageAccess property.
+        The added test is a break out of the negative case of an iframe
+        calling the API when no user gesture is processed.
+
+        * 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-non-sandboxed-iframe-expected.txt:
+        * 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-expected.txt:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-non-recent-user-interaction-expected.txt:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-non-recent-user-interaction.html:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-recent-user-interaction-expected.txt:
+        * http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-without-user-interaction-expected.txt:
+        * 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-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-cross-origin-sandboxed-iframe-without-user-gesture-expected.txt: Added.
+        * http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html: Added.
+        * 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-top-frame-expected.txt:
+        * http/tests/storageAccess/request-storage-access-top-frame.html:
+        * http/tests/storageAccess/resources/request-storage-access-iframe.html:
+        * http/tests/storageAccess/resources/request-storage-access-without-user-gesture-iframe.html: Added.
+        * platform/mac-wk2/TestExpectations:
+            Marked the new test case as [ Pass ]
+
 2017-12-06  Chris Dumez  <cdumez@apple.com>
 
         ServiceWorkers API should reject promises when calling objects inside detached frames
index 70bbdcc..cad9b6e 100644 (file)
@@ -3,7 +3,7 @@ Tests that cross-origin iframe storage access is denied if the iframe is not san
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was denied.
+PASS Storage access was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 1afb72a..c9fc3d9 100644 (file)
@@ -7,8 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is not sandboxed.");
         jsTestIsAsync = true;
 
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
         window.addEventListener("message", receiveMessage, false);
 
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
         function receiveMessage(event) {
             if (event.origin === "http://localhost:8000") {
                 if (event.data.indexOf("PASS") !== -1)
@@ -17,6 +28,7 @@
                     testFailed(event.data.replace("FAIL ", ""));
             } else
                 testFailed("Received a message from an unexpected origin: " + event.origin);
+            setEnableFeature(false);
             finishJSTest();
         }
 
                         eventSender.keyDown("escape");
                     else {
                         testFailed("No eventSender.");
+                        setEnableFeature(false);
                         finishJSTest();
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
+                    setEnableFeature(false);
                     finishJSTest();
                 }
             );
         }
 
         function runTest() {
+            setEnableFeature(true);
+
+            testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+            if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+                testFailed("Host did not get set as prevalent resource.");
+            testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+            if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+                testFailed("Host did not get logged for user interaction.");
+            testRunner.statisticsUpdateCookiePartitioning();
+
             activateElement("theIframe");
         }
     </script>
index db376be..e3d9803 100644 (file)
@@ -4,7 +4,7 @@ Tests that cross-origin iframe storage access is denied if the iframe is sandbox
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was denied.
+PASS Storage access was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 7928bd4..54d0165 100644 (file)
@@ -7,8 +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;
 
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
         window.addEventListener("message", receiveMessage, false);
 
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
         function receiveMessage(event) {
             if (event.origin === "http://localhost:8000") {
                 if (event.data.indexOf("PASS") !== -1)
@@ -18,6 +29,7 @@
             } else
                 testFailed("Received a message from an unexpected origin: " + event.origin);
             finishJSTest();
+            setEnableFeature(false);
         }
 
         function activateElement(elementId) {
                     else {
                         testFailed("No eventSender.");
                         finishJSTest();
+                        setEnableFeature(false);
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
                     finishJSTest();
+                    setEnableFeature(false);
                 }
             );
         }
 
         function runTest() {
-            setTimeout('activateElement("theIframe")', 500);
+            setEnableFeature(true);
+
+            testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+            if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+                testFailed("Host did not get set as prevalent resource.");
+            testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+            if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+                testFailed("Host did not get logged for user interaction.");
+            testRunner.statisticsUpdateCookiePartitioning();
+
+            activateElement("theIframe");
         }
     </script>
 </head>
index 70bbdcc..cad9b6e 100644 (file)
@@ -3,7 +3,7 @@ Tests that cross-origin iframe storage access is denied if the iframe is not san
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was denied.
+PASS Storage access was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 290bfe2..106d8e8 100644 (file)
@@ -7,8 +7,19 @@
         description("Tests that cross-origin iframe storage access is denied if the iframe is not sandboxed.");
         jsTestIsAsync = true;
 
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
         window.addEventListener("message", receiveMessage, false);
 
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
         function receiveMessage(event) {
             if (event.origin === "http://localhost:8000") {
                 if (event.data.indexOf("PASS ") !== -1)
@@ -18,6 +29,7 @@
             } else
                 testFailed("Received a message from an unexpected origin: " + event.origin);
             finishJSTest();
+            setEnableFeature(false);
         }
 
         function activateElement(elementId) {
                     else {
                         testFailed("No eventSender.");
                         finishJSTest();
+                        setEnableFeature(false);
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
                     finishJSTest();
+                    setEnableFeature(false);
                 }
             );
         }
 
         function runTest() {
+            setEnableFeature(true);
+
+            testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+            if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+                testFailed("Host did not get set as prevalent resource.");
+            testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+            if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+                testFailed("Host did not get logged for user interaction.");
+            testRunner.statisticsUpdateCookiePartitioning();
+
             activateElement("theIframe");
         }
     </script>
index 5dce6e3..7526bf1 100644 (file)
@@ -4,7 +4,7 @@ Tests that cross-origin iframe storage access is granted if the iframe is sandbo
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was granted.
+PASS Storage access was granted.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 1373d3d..346dca2 100644 (file)
                         eventSender.keyDown("escape");
                     else {
                         testFailed("No eventSender.");
+                        setEnableFeature(false);
                         finishJSTest();
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
+                    setEnableFeature(false);
                     finishJSTest();
                 }
             );
index 1b6c055..969187e 100644 (file)
@@ -3,7 +3,7 @@ Tests that cross-origin iframe storage access is denied if the iframe is sandbox
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was denied.
+PASS Storage access was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 8d1d80f..3bff7a4 100644 (file)
@@ -7,8 +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;
 
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
         window.addEventListener("message", receiveMessage, false);
 
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
         function receiveMessage(event) {
             if (event.origin === "http://localhost:8000") {
                 if (event.data.indexOf("PASS") !== -1)
@@ -17,6 +28,7 @@
                     testFailed(event.data.replace("FAIL ", ""));
             } else
                 testFailed("Received a message from an unexpected origin: " + event.origin);
+            setEnableFeature(false);
             finishJSTest();
         }
 
                         eventSender.keyDown("escape");
                     else {
                         testFailed("No eventSender.");
+                        setEnableFeature(false);
                         finishJSTest();
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
+                    setEnableFeature(false);
                     finishJSTest();
                 }
             );
         }
 
         function runTest() {
-            setTimeout('activateElement("theIframe")', 500);
+            setEnableFeature(true);
+
+            testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+            if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+                testFailed("Host did not get set as prevalent resource.");
+            testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+            if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+                testFailed("Host did not get logged for user interaction.");
+            testRunner.statisticsUpdateCookiePartitioning();
+
+            activateElement("theIframe");
         }
     </script>
 </head>
index 2ac95da..1accaf3 100644 (file)
@@ -3,7 +3,7 @@ Tests that cross-origin iframe storage access is denied if the iframe is sandbox
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was denied.
+PASS Storage access was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index bfec2f3..f562379 100644 (file)
@@ -3,7 +3,7 @@ Tests that cross-origin iframe storage access is denied if the iframe is sandbox
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was denied.
+PASS Storage access was denied.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3284d40..aeadcaf 100644 (file)
@@ -7,8 +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;
 
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
         window.addEventListener("message", receiveMessage, false);
 
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
         function receiveMessage(event) {
             if (event.origin === "http://localhost:8000") {
                 if (event.data.indexOf("PASS") !== -1)
@@ -17,6 +28,7 @@
                     testFailed(event.data.replace("FAIL ", ""));
             } else
                 testFailed("Received a message from an unexpected origin: " + event.origin);
+            setEnableFeature(false);
             finishJSTest();
         }
 
                         eventSender.keyDown("escape");
                     else {
                         testFailed("No eventSender.");
+                        setEnableFeature(false);
                         finishJSTest();
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
+                    setEnableFeature(false);
                     finishJSTest();
                 }
             );
         }
 
         function runTest() {
+            setEnableFeature(true);
+
+            testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+            if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+                testFailed("Host did not get set as prevalent resource.");
+            testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+            if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+                testFailed("Host did not get logged for user interaction.");
+            testRunner.statisticsUpdateCookiePartitioning();
+
             activateElement("theIframe");
         }
     </script>
diff --git a/LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture-expected.txt b/LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture-expected.txt
new file mode 100644 (file)
index 0000000..36c524a
--- /dev/null
@@ -0,0 +1,10 @@
+Tests that cross-origin iframe storage access is denied if the iframe is sandboxed, has the allow token, but calls the API without a user gesture being processed.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Storage access was denied.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html b/LayoutTests/http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html
new file mode 100644 (file)
index 0000000..6640f79
--- /dev/null
@@ -0,0 +1,50 @@
+<!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, but calls the API without a user gesture being processed.");
+        jsTestIsAsync = true;
+
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
+        window.addEventListener("message", receiveMessage, false);
+
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
+        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);
+            setEnableFeature(false);
+            finishJSTest();
+        }
+
+        setEnableFeature(true);
+
+        testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+        if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+            testFailed("Host did not get set as prevalent resource.");
+        testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+        if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+            testFailed("Host did not get logged for user interaction.");
+        testRunner.statisticsUpdateCookiePartitioning();
+
+    </script>
+</head>
+<body>
+<iframe sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin allow-modals" id="theIframe" src="http://localhost:8000/storageAccess/resources/request-storage-access-without-user-gesture-iframe.html#userShouldGrantAccess,userShouldNotBeConsulted,policyShouldNotGrantAccess,isNotSameOriginIframe"></iframe>
+</body>
+</html>
\ No newline at end of file
index 1a649a4..e99d7de 100644 (file)
@@ -1,9 +1,9 @@
-Tests that same-origin iframe storage access is denied if the iframe is not sandboxed.
+Tests that same-origin iframe storage access is granted 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 Storage access was granted.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 6b11289..2daedd5 100644 (file)
@@ -4,7 +4,7 @@
     <script src="/js-test-resources/js-test.js"></script>
     <script src="/js-test-resources/ui-helper.js"></script>
     <script>
-        description("Tests that same-origin iframe storage access is denied if the iframe is not sandboxed.");
+        description("Tests that same-origin iframe storage access is granted if the iframe is not sandboxed.");
         jsTestIsAsync = true;
 
         window.addEventListener("message", receiveMessage, false);
@@ -46,6 +46,6 @@
     </script>
 </head>
 <body>
-    <iframe onload="runTest()" id="theIframe" src="http://127.0.0.1:8000/storageAccess/resources/request-storage-access-iframe.html#userShouldDenyAccess,userShouldNotBeConsulted,policyShouldDenyAccess,isSameOriginIframe"></iframe>
+    <iframe onload="runTest()" id="theIframe" src="http://127.0.0.1:8000/storageAccess/resources/request-storage-access-iframe.html#userShouldDenyAccess,userShouldNotBeConsulted,policyShouldGrantAccess,isSameOriginIframe"></iframe>
 </body>
 </html>
\ No newline at end of file
index fdcd106..e981389 100644 (file)
@@ -3,7 +3,7 @@ Tests that same-origin iframe storage access is granted if the iframe is sandbox
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS document.hasStorageAccess was granted.
+PASS Storage access was granted.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 10ea8fc..e1f67df 100644 (file)
@@ -1,9 +1,9 @@
-Tests that same-origin iframe storage access is denied if the iframe is sandboxed but doesn't have the allow token.
+Tests that same-origin iframe storage access is granted 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 Storage access was granted.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index c8550b6..bf149ac 100644 (file)
@@ -4,11 +4,22 @@
     <script src="/js-test-resources/js-test.js"></script>
     <script src="/js-test-resources/ui-helper.js"></script>
     <script>
-        description("Tests that same-origin iframe storage access is denied if the iframe is sandboxed but doesn't have the allow token.");
+        description("Tests that same-origin iframe storage access is granted if the iframe is sandboxed but doesn't have the allow token.");
         jsTestIsAsync = true;
 
+        const hostUnderTest = "localhost:8000";
+        const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
         window.addEventListener("message", receiveMessage, false);
 
+        function setEnableFeature(enable) {
+            if (!enable)
+                testRunner.statisticsResetToConsistentState();
+            internals.setResourceLoadStatisticsEnabled(enable);
+            testRunner.setCookieStoragePartitioningEnabled(enable);
+            testRunner.setStorageAccessAPIEnabled(enable);
+        }
+
         function receiveMessage(event) {
             if (event.origin === "http://127.0.0.1:8000") {
                 if (event.data.indexOf("PASS") !== -1)
@@ -17,6 +28,7 @@
                     testFailed(event.data.replace("FAIL ", ""));
             } else
                 testFailed("Received a message from an unexpected origin: " + event.origin);
+            setEnableFeature(false);
             finishJSTest();
         }
 
                         eventSender.keyDown("escape");
                     else {
                         testFailed("No eventSender.");
+                        setEnableFeature(false);
                         finishJSTest();
                     }
                 },
                 function () {
                     testFailed("Promise rejected.");
+                    setEnableFeature(false);
                     finishJSTest();
                 }
             );
         }
 
         function runTest() {
+            setEnableFeature(true);
+
+            testRunner.setStatisticsPrevalentResource(statisticsUrl, true);
+            if (!testRunner.isStatisticsPrevalentResource(statisticsUrl))
+                testFailed("Host did not get set as prevalent resource.");
+            testRunner.setStatisticsHasHadNonRecentUserInteraction(statisticsUrl);
+            if (!testRunner.isStatisticsHasHadUserInteraction(statisticsUrl))
+                testFailed("Host did not get logged for user interaction.");
+            testRunner.statisticsUpdateCookiePartitioning();
+
             activateElement("theIframe");
         }
     </script>
 </head>
 <body>
-    <iframe sandbox="allow-scripts allow-same-origin allow-modals" onload="runTest()" id="theIframe" src="http://127.0.0.1:8000/storageAccess/resources/request-storage-access-iframe.html#userShouldDenyAccess,userShouldNotBeConsulted,policyShouldDenyAccess,isSameOriginIframe"></iframe>
+    <iframe sandbox="allow-scripts allow-same-origin allow-modals" onload="runTest()" id="theIframe" src="http://127.0.0.1:8000/storageAccess/resources/request-storage-access-iframe.html#userShouldDenyAccess,userShouldNotBeConsulted,policyShouldGrantAccess,isSameOriginIframe"></iframe>
 </body>
 </html>
\ No newline at end of file
index 13e1cc6..f25a591 100644 (file)
@@ -4,9 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS requestStorageAccessResolved is true
-PASS document.hasStorageAccess is true
-PASS requestStorageAccessResolved is true
-PASS document.hasStorageAccess is true
+PASS hasStorageAccess is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index d22517a..d35557c 100644 (file)
@@ -7,6 +7,14 @@
     description("Tests that top frame storage access is always granted in case the page requests it.");
     jsTestIsAsync = true;
 
+    function setEnableFeature(enable) {
+        if (!enable)
+            testRunner.statisticsResetToConsistentState();
+        internals.setResourceLoadStatisticsEnabled(enable);
+        testRunner.setCookieStoragePartitioningEnabled(enable);
+        testRunner.setStorageAccessAPIEnabled(enable);
+    }
+
     function activateElement(elementId) {
         var element = document.getElementById(elementId);
         var centerX = element.offsetLeft + element.offsetWidth / 2;
             },
             function () {
                 testFailed("Promise rejected.");
+                setEnableFeature(false);
                 finishJSTest();
             }
         );
     }
 
+    var hasStorageAccess;
     var requestStorageAccessResolved;
 
-    function makeRequestWithoutUserGesture() {
-        var promise = document.requestStorageAccess();
-        promise.then(
-            function () {
-                requestStorageAccessResolved = true;
-                continueAfterRequestWithoutUserGesture();
-            },
-            function () {
-                requestStorageAccessResolved = false;
-                continueAfterRequestWithoutUserGesture();
-            }
-        );
-    }
-
-    function continueAfterRequestWithoutUserGesture() {
-        shouldBe("requestStorageAccessResolved", "true");
-        shouldBe("document.hasStorageAccess", "true");
-        activateElement("theButton");
-    }
-
     function makeRequestWithUserGesture() {
         var promise = document.requestStorageAccess();
         promise.then(
     }
 
     function continueAfterRequestWithUserGesture() {
-        shouldBe("requestStorageAccessResolved", "true");
-        shouldBe("document.hasStorageAccess", "true");
-        finishJSTest();
+        var promise = document.hasStorageAccess();
+        promise.then(
+            function (hasAccess) {
+                hasStorageAccess = hasAccess;
+                shouldBe("requestStorageAccessResolved", "true");
+                shouldBe("hasStorageAccess", "true");
+                setEnableFeature(false);
+                finishJSTest();
+            },
+            function (reason) {
+                testFailed("document.hasStorageAccess() was rejected. Reason: " + reason);
+                setEnableFeature(false);
+                finishJSTest();
+            }
+        );
     }
 
     function runTest() {
-        makeRequestWithoutUserGesture();
+        activateElement("theButton");
     }
 </script>
 <button id="theButton" onclick="makeRequestWithUserGesture()">Request Access</button>
index 9f7b42b..5245141 100644 (file)
@@ -5,56 +5,13 @@
         const userShouldGrantAccess = hashArguments[0] === "userShouldGrantAccess";
         const userShouldBeConsulted = hashArguments[1] === "userShouldBeConsulted";
         const policyShouldGrantAccess = hashArguments[2] === "policyShouldGrantAccess";
-        const isSameOriginIframe = hashArguments[3] === "isSameOriginIframe";
 
         if (internals && userShouldGrantAccess)
                 internals.setUserGrantsStorageAccess(true);
 
-        function storageAccessShouldBeFalse() {
-            if (document.hasStorageAccess)
-                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)
-                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 requestStorageAccessResolved;
 
-        function makeRequestWithoutUserGesture() {
-            storageAccessShouldBeFalse();
-
-            var promise = document.requestStorageAccess();
-            promise.then(
-                function () {
-                    requestStorageAccessResolved = true;
-                    continueAfterRequestWithoutUserGesture();
-                },
-                function () {
-                    requestStorageAccessResolved = false;
-                    continueAfterRequestWithoutUserGesture();
-                }
-            );
-        }
-
-        function continueAfterRequestWithoutUserGesture() {
-            if (isSameOriginIframe && policyShouldGrantAccess)
-                storageAccessShouldBeTrue();
-            else
-                storageAccessShouldBeFalse();
-        }
-
         function makeRequestWithUserGesture() {
-            if (isSameOriginIframe && policyShouldGrantAccess)
-                storageAccessShouldBeTrue();
-            else
-                storageAccessShouldBeFalse();
-
             var promise = document.requestStorageAccess();
             promise.then(
                 function () {
         }
 
         function continueAfterRequestWithUserGesture() {
-            if (requestStorageAccessResolved
-                && document.hasStorageAccess
-                && (userShouldGrantAccess || !userShouldBeConsulted)
-                && policyShouldGrantAccess)
-                top.postMessage("PASS document.hasStorageAccess was granted.", "http://127.0.0.1:8000");
-            else if (!document.hasStorageAccess
-                    && !requestStorageAccessResolved
-                    && ((!userShouldGrantAccess && userShouldBeConsulted) || !policyShouldGrantAccess))
-                top.postMessage("PASS document.hasStorageAccess was denied.", "http://127.0.0.1:8000");
-            else
-                top.postMessage("FAIL document.hasStorageAccess was " +
-                    (document.hasStorageAccess ? "" : "not ") +
-                    "granted and requestStorageAccessResolved was " +
-                    (requestStorageAccessResolved ? "" : "not ") +
-                    "granted but should " +
-                    (userShouldGrantAccess && policyShouldGrantAccess ? "" : "not ") +
-                    "have been granted.", "http://127.0.0.1:8000");
+            var promise = document.hasStorageAccess();
+            promise.then(
+                function (hasAccess) {
+                    if (requestStorageAccessResolved
+                        && hasAccess
+                        && (userShouldGrantAccess || !userShouldBeConsulted)
+                        && policyShouldGrantAccess)
+                        top.postMessage("PASS Storage access was granted.", "http://127.0.0.1:8000");
+                    else if (!hasAccess
+                        && !requestStorageAccessResolved
+                        && ((!userShouldGrantAccess && userShouldBeConsulted) || !policyShouldGrantAccess))
+                        top.postMessage("PASS Storage access was denied.", "http://127.0.0.1:8000");
+                    else
+                        top.postMessage("FAIL Storage access was " +
+                            (hasAccess ? "" : "not ") +
+                            "granted and requestStorageAccessResolved was " +
+                            (requestStorageAccessResolved ? "" : "not ") +
+                            "granted but should " +
+                            (userShouldGrantAccess && policyShouldGrantAccess ? "" : "not ") +
+                            "have been granted.", "http://127.0.0.1:8000");
+                },
+                function (reason) {
+                    top.postMessage("FAIL document.hasStorageAccess() was rejected. Reason: " + reason, "http://127.0.0.1:8000");
+                }
+            );
         }
     </script>
 </head>
-<body onload="makeRequestWithoutUserGesture()" onclick="makeRequestWithUserGesture()">
+<body onclick="makeRequestWithUserGesture()">
 </body>
 </html>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/storageAccess/resources/request-storage-access-without-user-gesture-iframe.html b/LayoutTests/http/tests/storageAccess/resources/request-storage-access-without-user-gesture-iframe.html
new file mode 100644 (file)
index 0000000..64fd4a4
--- /dev/null
@@ -0,0 +1,59 @@
+<html>
+<head>
+    <script>
+        const hashArguments = document.location.hash.substring(1).split(",");
+        const userShouldGrantAccess = hashArguments[0] === "userShouldGrantAccess";
+        const userShouldBeConsulted = hashArguments[1] === "userShouldBeConsulted";
+        const policyShouldGrantAccess = hashArguments[2] === "policyShouldGrantAccess";
+
+        if (internals && userShouldGrantAccess)
+                internals.setUserGrantsStorageAccess(true);
+
+        var requestStorageAccessResolved;
+
+        function makeRequestWithoutUserGesture() {
+            var promise = document.requestStorageAccess();
+            promise.then(
+                function () {
+                    requestStorageAccessResolved = true;
+                    continueAfterRequestWithoutUserGesture();
+                },
+                function () {
+                    requestStorageAccessResolved = false;
+                    continueAfterRequestWithoutUserGesture();
+                }
+            );
+        }
+
+        function continueAfterRequestWithoutUserGesture() {
+            var promise = document.hasStorageAccess();
+            promise.then(
+                function (hasAccess) {
+                    if (requestStorageAccessResolved
+                        && hasAccess
+                        && (userShouldGrantAccess || !userShouldBeConsulted)
+                        && policyShouldGrantAccess)
+                        top.postMessage("PASS Storage access was granted.", "http://127.0.0.1:8000");
+                    else if (!hasAccess
+                        && !requestStorageAccessResolved
+                        && ((!userShouldGrantAccess && userShouldBeConsulted) || !policyShouldGrantAccess))
+                        top.postMessage("PASS Storage access was denied.", "http://127.0.0.1:8000");
+                    else
+                        top.postMessage("FAIL Storage access was " +
+                            (hasAccess ? "" : "not ") +
+                            "granted and requestStorageAccessResolved was " +
+                            (requestStorageAccessResolved ? "" : "not ") +
+                            "granted but should " +
+                            (userShouldGrantAccess && policyShouldGrantAccess ? "" : "not ") +
+                            "have been granted.", "http://127.0.0.1:8000");
+                },
+                function (reason) {
+                    top.postMessage("FAIL document.hasStorageAccess() was rejected. Reason: " + reason, "http://127.0.0.1:8000");
+                }
+            );
+        }
+    </script>
+</head>
+<body onload="makeRequestWithoutUserGesture()">
+</body>
+</html>
\ No newline at end of file
index c4d2e66..43142a0 100644 (file)
@@ -759,6 +759,7 @@ http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed
 [ HighSierra+ ] http/tests/storageAccess/request-and-grant-storage-access-cross-origin-sandboxed-iframe-from-prevalent-domain-with-non-recent-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 ]
+http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.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 6ed4dd6..cde2251 100644 (file)
@@ -1,3 +1,30 @@
+2017-12-06  John Wilander  <wilander@apple.com>
+
+        Storage Access API: Make document.hasStorageAccess a function and always allow access for same-origin iframes
+        https://bugs.webkit.org/show_bug.cgi?id=176944
+        <rdar://problem/34440658>
+
+        Reviewed by Brent Fulgham.
+
+        Test: http/tests/storageAccess/request-storage-access-cross-origin-sandboxed-iframe-without-user-gesture.html
+
+        This change introduces document.hasStorageAccess() as a function which
+        returns a promise instead of being a property. Since cookie access can
+        be due to both a granted request and recent user interaction as first
+        party, the WebKit::WebResourceLoadStatisticsStore needs to be consulted.
+
+        * dom/Document.cpp:
+        (WebCore::Document::hasStorageAccess):
+        (WebCore::Document::requestStorageAccess):
+            Removed check of the previous m_hasStorageAccess member.
+            Same-origin check done earlier. This was a request/suggestion
+            from Mozilla.
+        * dom/Document.h:
+        (WebCore::Document::hasStorageAccess const): Deleted.
+            Now uses a promise.
+        * dom/Document.idl:
+        * page/ChromeClient.h:
+
 2017-12-06  Youenn Fablet  <youenn@apple.com>
 
         REGRESSION (r225537): Crash in WebCore::SWServerWorker::setHasPendingEvents(bool) + 68
index f350dba..dbe34d8 100644 (file)
@@ -7397,24 +7397,65 @@ Logger& Document::logger()
     return *m_logger;
 }
 
-void Document::requestStorageAccess(Ref<DeferredPromise>&& passedPromise)
+void Document::hasStorageAccess(Ref<DeferredPromise>&& passedPromise)
 {
     ASSERT(settings().storageAccessAPIEnabled());
-    
+
     RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
+
+    if (!m_frame || securityOrigin().isUnique()) {
+        promise->resolve<IDLBoolean>(false);
+        return;
+    }
     
-    if (m_hasStorageAccess) {
-        promise->resolve();
+    if (m_frame->isMainFrame()) {
+        promise->resolve<IDLBoolean>(true);
         return;
     }
     
+    auto& securityOrigin = this->securityOrigin();
+    auto& topSecurityOrigin = topDocument().securityOrigin();
+    if (securityOrigin.equal(&topSecurityOrigin)) {
+        promise->resolve<IDLBoolean>(true);
+        return;
+    }
+
+    if (Page* page = this->page()) {
+        auto iframeHost = securityOrigin.host();
+        auto topHost = topSecurityOrigin.host();
+        page->chrome().client().hasStorageAccess(WTFMove(iframeHost), WTFMove(topHost), [documentReference = m_weakFactory.createWeakPtr(*this), promise] (bool hasAccess) {
+            Document* document = documentReference.get();
+            if (!document)
+                return;
+            
+            promise->resolve<IDLBoolean>(hasAccess);
+        });
+        return;
+    }
+
+    promise->reject();
+}
+
+void Document::requestStorageAccess(Ref<DeferredPromise>&& passedPromise)
+{
+    ASSERT(settings().storageAccessAPIEnabled());
+    
+    RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
+    
     if (!m_frame || securityOrigin().isUnique()) {
         promise->reject();
         return;
     }
     
     if (m_frame->isMainFrame()) {
-        m_hasStorageAccess = true;
+        promise->resolve();
+        return;
+    }
+    
+    auto& topDocument = this->topDocument();
+    auto& topSecurityOrigin = topDocument.securityOrigin();
+    auto& securityOrigin = this->securityOrigin();
+    if (securityOrigin.equal(&topSecurityOrigin)) {
         promise->resolve();
         return;
     }
@@ -7426,20 +7467,11 @@ void Document::requestStorageAccess(Ref<DeferredPromise>&& passedPromise)
     }
 
     // The iframe has to be a direct child of the top document.
-    auto& topDocument = this->topDocument();
     if (&topDocument != parentDocument()) {
         promise->reject();
         return;
     }
 
-    auto& securityOrigin = this->securityOrigin();
-    auto& topSecurityOrigin = topDocument.securityOrigin();
-    if (securityOrigin.equal(&topSecurityOrigin)) {
-        m_hasStorageAccess = true;
-        promise->resolve();
-        return;
-    }
-    
     if (!UserGestureIndicator::processingUserGesture()) {
         promise->reject();
         return;
@@ -7461,10 +7493,9 @@ void Document::requestStorageAccess(Ref<DeferredPromise>&& passedPromise)
             if (!document)
                 return;
 
-            if (wasGranted) {
-                document->m_hasStorageAccess = true;
+            if (wasGranted)
                 promise->resolve();
-            else
+            else
                 promise->reject();
         });
         return;
index 1b80c81..92e6fea 100644 (file)
@@ -1373,7 +1373,7 @@ public:
 
     PAL::Logger& logger();
 
-    bool hasStorageAccess() const { return m_hasStorageAccess; };
+    void hasStorageAccess(Ref<DeferredPromise>&& passedPromise);
     void requestStorageAccess(Ref<DeferredPromise>&& passedPromise);
     void setUserGrantsStorageAccessOverride(bool value) { m_grantStorageAccessOverride = value; }
 
@@ -1860,7 +1860,6 @@ private:
 
     static bool hasEverCreatedAnAXObjectCache;
 
-    bool m_hasStorageAccess { false };
     bool m_grantStorageAccessOverride { false };
 
     RefPtr<DocumentTimeline> m_timeline;
index e79c707..b4bafb3 100644 (file)
@@ -191,7 +191,7 @@ typedef (
     RenderingContext? getCSSCanvasContext(DOMString contextId, DOMString name, long width, long height);
 
     // Non standard, to bring up with standards working group.
-    [EnabledBySetting=StorageAccessAPI] readonly attribute boolean hasStorageAccess;
+    [EnabledBySetting=StorageAccessAPI] Promise<bool> hasStorageAccess();
     [EnabledBySetting=StorageAccessAPI] Promise<void> requestStorageAccess();
 
     // Obsolete features from https://html.spec.whatwg.org/multipage/obsolete.html
index 03cabdd..3c09693 100644 (file)
@@ -42,6 +42,7 @@
 #include "SearchPopupMenu.h"
 #include "WebCoreKeyboardUIMode.h"
 #include <runtime/ConsoleTypes.h>
+#include <wtf/CompletionHandler.h>
 #include <wtf/Forward.h>
 #include <wtf/Seconds.h>
 
@@ -466,7 +467,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); }
+    virtual void hasStorageAccess(String&& /*subFrameHost*/, String&& /*topFrameHost*/, WTF::CompletionHandler<void (bool)>&& callback) { callback(false); }
+    virtual void requestStorageAccess(String&& /*subFrameHost*/, String&& /*topFrameHost*/, WTF::CompletionHandler<void (bool)>&& callback) { callback(false); }
 
     virtual void didInsertMenuElement(HTMLMenuElement&) { }
     virtual void didRemoveMenuElement(HTMLMenuElement&) { }
index eb9ae46..4e71dba 100644 (file)
@@ -1,3 +1,42 @@
+2017-12-06  John Wilander  <wilander@apple.com>
+
+        Storage Access API: Make document.hasStorageAccess a function and always allow access for same-origin iframes
+        https://bugs.webkit.org/show_bug.cgi?id=176944
+        <rdar://problem/34440658>
+
+        Reviewed by Brent Fulgham.
+
+        This change introduces document.hasStorageAccess() as a function which
+        returns a promise instead of being a property. Since cookie access can
+        be due to both a granted request and recent user interaction as first
+        party, the WebKit::WebResourceLoadStatisticsStore needs to be consulted.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::hasStorageAccess):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::hasStorageAccess):
+        (WebKit::WebResourceLoadStatisticsStore::requestStorageAccess):
+            Now adds an entry for granted access. A bug found through testing.
+            Switched from WTF::Function to WTF::CompletionHandler.
+        * UIProcess/WebResourceLoadStatisticsStore.h:
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::hasStorageAccess):
+        (WebKit::WebsiteDataStore::requestStorageAccess):
+            Switched from WTF::Function to WTF::CompletionHandler.
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::hasStorageAccess):
+        (WebKit::WebChromeClient::requestStorageAccess):
+            Switched from WTF::Function to WTF::CompletionHandler.
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::hasStorageAccess):
+        (WebKit::WebPage::requestStorageAccess):
+            Switched from WTF::Function to WTF::CompletionHandler.
+        * WebProcess/WebPage/WebPage.h:
+
 2017-12-06  Youenn Fablet  <youenn@apple.com>
 
         CacheStorageEngineConnection should protect its IPC Connection when doing asynchronous tasks
index bbb8a27..cd20355 100644 (file)
@@ -7142,6 +7142,13 @@ void WebPageProxy::stopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskId
     iterator->value->stopTask(*this, taskIdentifier);
 }
 
+void WebPageProxy::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t webProcessContextId)
+{
+    m_websiteDataStore->hasStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), [this, webProcessContextId] (bool hasAccess) {
+        m_process->send(Messages::WebPage::StorageAccessResponse(hasAccess, webProcessContextId), m_pageID);
+    });
+}
+
 void WebPageProxy::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t webProcessContextId)
 {
     m_websiteDataStore->requestStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), [this, webProcessContextId] (bool wasGranted) {
index 293889f..16b5aeb 100644 (file)
@@ -82,6 +82,7 @@
 #include <WebCore/URL.h>
 #include <WebCore/UserInterfaceLayoutDirection.h>
 #include <memory>
+#include <wtf/CompletionHandler.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/MonotonicTime.h>
@@ -1233,6 +1234,7 @@ public:
     void touchBarMenuItemDataRemoved(const TouchBarMenuItemData&);
 #endif
 
+    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t webProcessContextId);
     void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t webProcessContextId);
 
 #if ENABLE(ATTACHMENT_ELEMENT)
index 1efa5fc..1268ce4 100644 (file)
@@ -505,6 +505,7 @@ messages -> WebPageProxy {
     StartURLSchemeTask(struct WebKit::URLSchemeTaskParameters parameters)
     StopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier)
 
+    HasStorageAccess(String subFrameHost, String topFrameHost, uint64_t contextID)
     RequestStorageAccess(String subFrameHost, String topFrameHost, uint64_t contextID)
 
 #if ENABLE(ATTACHMENT_ELEMENT)
index 417a0af..8313fbf 100644 (file)
@@ -245,7 +245,30 @@ void WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated(Vector<WebCor
     processStatisticsAndDataRecords();
 }
 
-void WebResourceLoadStatisticsStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+void WebResourceLoadStatisticsStore::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
+{
+    ASSERT(subFrameHost != topFrameHost);
+    ASSERT(RunLoop::isMain());
+    
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), subFramePrimaryDomain = isolatedPrimaryDomain(subFrameHost), topFramePrimaryDomain = isolatedPrimaryDomain(topFrameHost), callback = WTFMove(callback)] () mutable {
+        
+        auto& topFrameStatistic = ensureResourceStatisticsForPrimaryDomain(topFramePrimaryDomain);
+        if (topFrameStatistic.storageAccessUnderTopFrameOrigins.contains(subFramePrimaryDomain)) {
+            callback(true);
+            return;
+        }
+        
+        auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
+        if (shouldBlockCookies(subFrameStatistic)) {
+            callback(false);
+            return;
+        }
+        
+        callback(!shouldPartitionCookies(subFrameStatistic));
+    });
+}
+
+void WebResourceLoadStatisticsStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
 {
     ASSERT(subFrameHost != topFrameHost);
     ASSERT(RunLoop::isMain());
@@ -270,6 +293,7 @@ void WebResourceLoadStatisticsStore::requestStorageAccess(String&& subFrameHost,
         }
         
         m_updateStorageAccessForPrevalentDomainsHandler(subFramePrimaryDomain, topFramePrimaryDomain, true, WTFMove(callback));
+        topFrameStatistic.storageAccessUnderTopFrameOrigins.add(subFramePrimaryDomain);
     });
 }
     
index 3f0a6df..5f05b05 100644 (file)
@@ -29,6 +29,7 @@
 #include "ResourceLoadStatisticsClassifier.h"
 #include "ResourceLoadStatisticsPersistentStorage.h"
 #include "WebsiteDataType.h"
+#include <wtf/CompletionHandler.h>
 #include <wtf/MonotonicTime.h>
 #include <wtf/RunLoop.h>
 #include <wtf/Vector.h>
@@ -80,7 +81,8 @@ public:
 
     void resourceLoadStatisticsUpdated(Vector<WebCore::ResourceLoadStatistics>&& origins);
 
-    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback);
+    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback);
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback);
     void requestStorageAccessCallback(bool wasGranted, uint64_t contextId);
 
     void processWillOpenConnection(WebProcessProxy&, IPC::Connection&);
index cfecc7e..e6c9152 100644 (file)
@@ -1401,7 +1401,17 @@ void WebsiteDataStore::removePendingCookie(const WebCore::Cookie& cookie)
     m_pendingCookies.remove(cookie);
 }
 
-void WebsiteDataStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+void WebsiteDataStore::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
+{
+    if (!resourceLoadStatisticsEnabled()) {
+        callback(false);
+        return;
+    }
+    
+    m_resourceLoadStatistics->hasStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), WTFMove(callback));
+}
+    
+void WebsiteDataStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
 {
     if (!resourceLoadStatisticsEnabled()) {
         callback(false);
index a13f4cb..0b29046 100644 (file)
@@ -144,7 +144,8 @@ public:
 
     void enableResourceLoadStatisticsAndSetTestingCallback(Function<void (const String&)>&& callback);
 
-    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback);
+    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback);
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback);
     
     void setBoundInterfaceIdentifier(String&& identifier) { m_boundInterfaceIdentifier = WTFMove(identifier); }
     const String& boundInterfaceIdentifier() { return m_boundInterfaceIdentifier; }
index cc72257..23234d9 100644 (file)
@@ -1256,7 +1256,12 @@ void WebChromeClient::didInvalidateDocumentMarkerRects()
     m_page.findController().didInvalidateDocumentMarkerRects();
 }
 
-void WebChromeClient::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+void WebChromeClient::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
+{
+    m_page.hasStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), WTFMove(callback));
+}
+
+void WebChromeClient::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
 {
     m_page.requestStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), WTFMove(callback));
 }
index 4628d0e..67cea6f 100644 (file)
@@ -349,7 +349,8 @@ private:
 
     void didInvalidateDocumentMarkerRects() final;
 
-    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&&) final;
+    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&&) final;
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&&) final;
 
     String m_cachedToolTip;
     mutable RefPtr<WebFrame> m_cachedFrameSetLargestFrame;
index 30229ba..519fd52 100644 (file)
@@ -5806,7 +5806,18 @@ static uint64_t nextRequestStorageAccessContextId()
     return ++nextContextId;
 }
 
-void WebPage::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback)
+void WebPage::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
+{
+    auto contextId = nextRequestStorageAccessContextId();
+    auto addResult = m_storageAccessResponseCallbackMap.add(contextId, WTFMove(callback));
+    ASSERT(addResult.isNewEntry);
+    if (addResult.iterator->value)
+        send(Messages::WebPageProxy::HasStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), contextId));
+    else
+        callback(false);
+}
+    
+void WebPage::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback)
 {
     auto contextId = nextRequestStorageAccessContextId();
     auto addResult = m_storageAccessResponseCallbackMap.add(contextId, WTFMove(callback));
index 3505c8a..f265038 100644 (file)
@@ -1020,7 +1020,8 @@ public:
     void sendPartialEditorStateAndSchedulePostLayoutUpdate();
     void flushPendingEditorStateUpdate();
 
-    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::Function<void (bool)>&& callback);
+    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback);
+    void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, WTF::CompletionHandler<void (bool)>&& callback);
     void storageAccessResponse(bool wasGranted, uint64_t contextId);
 
 #if ENABLE(ATTACHMENT_ELEMENT)