Title: [290198] trunk
Revision
290198
Author
n...@apple.com
Date
2022-02-18 21:26:41 -0800 (Fri, 18 Feb 2022)

Log Message

Hook up PushManager to permission state
https://bugs.webkit.org/show_bug.cgi?id=236739

Reviewed by Brady Eidson.

Source/WebCore:

This implements PushManager.permissionState. It returns the same value as
Notification.permission through the same mechanism.

This also makes PushManager.subscribe request permission from the user to show notifications
if the permission is in the default state and these requirements are met:

1. The caller is a document with the same origin as a top level document. This means that
   cross-origin iframes cannot request push permissions. This matches Chrome's behavior.
2. The caller cannot be a service worker. This limitation is allowed for by the spec.

There also needs to be some code added which removes a push subscription if the user deletes
the subscription entirely in the UI, for instance when the UIProcess calls
WKNotificationManagerProviderDidRemoveNotificationPolicies. That will be handled in a
separate patch.

Covered by new layout tests.

* Modules/push-api/PushManager.cpp:
(WebCore::PushManager::subscribe):
(WebCore::PushManager::getSubscription):
(WebCore::PushManager::permissionState):

Source/WebKit:

Update comment explaining why we still have a permission state stub in webpushd.

* webpushd/WebPushDaemon.mm:
(WebPushD::Daemon::getPushPermissionState):

Tools:

Made the WebPushD API tests work now that subscribe checks for permission.

* TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm:

LayoutTests:

Added tests that make sure that PushManager.permissionState() and PushManager.subscribe()
respect the notification permission level for the current origin.

I made these regular tests rather than WPT tests because the WPT test harness calls
grantWebNotificationPermission at the top of its setup script (in testharnessreport.js), and
I didn't want to change that for this patch.

* http/tests/push-api/resources/push-api-test-pre.js: Added.
(finishPushAPITest):
(waitForState):
* http/tests/push-api/resources/subscribe-iframe-cross-origin.html: Added.
* http/tests/push-api/resources/subscribe-iframe-same-origin.html: Added.
* http/tests/push-api/resources/subscribe-tests.js: Added.
(async testServiceWorkerPermissionState):
(async testDocumentPermissionState):
(async testServiceWorkerSubscribe):
(async testDocumentSubscribe):
* http/tests/push-api/resources/subscribe-worker.js: Added.
(async event):
* http/tests/push-api/subscribe-default-permissions-expected.txt: Added.
* http/tests/push-api/subscribe-default-permissions-iframe-cross-origin-expected.txt: Added.
* http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html: Added.
* http/tests/push-api/subscribe-default-permissions-iframe-same-origin-expected.txt: Added.
* http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html: Added.
* http/tests/push-api/subscribe-default-permissions.html: Added.
* http/tests/push-api/subscribe-deny-permissions-expected.txt: Added.
* http/tests/push-api/subscribe-deny-permissions.html: Added.
* http/tests/push-api/subscribe-grant-permissions-expected.txt: Added.
* http/tests/push-api/subscribe-grant-permissions.html: Added.
* platform/gtk/TestExpectations:
* platform/mac-wk1/TestExpectations:
* platform/win/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (290197 => 290198)


--- trunk/LayoutTests/ChangeLog	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/LayoutTests/ChangeLog	2022-02-19 05:26:41 UTC (rev 290198)
@@ -1,3 +1,43 @@
+2022-02-18  Ben Nham  <n...@apple.com>
+
+        Hook up PushManager to permission state
+        https://bugs.webkit.org/show_bug.cgi?id=236739
+
+        Reviewed by Brady Eidson.
+
+        Added tests that make sure that PushManager.permissionState() and PushManager.subscribe()
+        respect the notification permission level for the current origin.
+
+        I made these regular tests rather than WPT tests because the WPT test harness calls
+        grantWebNotificationPermission at the top of its setup script (in testharnessreport.js), and
+        I didn't want to change that for this patch.
+
+        * http/tests/push-api/resources/push-api-test-pre.js: Added.
+        (finishPushAPITest):
+        (waitForState):
+        * http/tests/push-api/resources/subscribe-iframe-cross-origin.html: Added.
+        * http/tests/push-api/resources/subscribe-iframe-same-origin.html: Added.
+        * http/tests/push-api/resources/subscribe-tests.js: Added.
+        (async testServiceWorkerPermissionState):
+        (async testDocumentPermissionState):
+        (async testServiceWorkerSubscribe):
+        (async testDocumentSubscribe):
+        * http/tests/push-api/resources/subscribe-worker.js: Added.
+        (async event):
+        * http/tests/push-api/subscribe-default-permissions-expected.txt: Added.
+        * http/tests/push-api/subscribe-default-permissions-iframe-cross-origin-expected.txt: Added.
+        * http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html: Added.
+        * http/tests/push-api/subscribe-default-permissions-iframe-same-origin-expected.txt: Added.
+        * http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html: Added.
+        * http/tests/push-api/subscribe-default-permissions.html: Added.
+        * http/tests/push-api/subscribe-deny-permissions-expected.txt: Added.
+        * http/tests/push-api/subscribe-deny-permissions.html: Added.
+        * http/tests/push-api/subscribe-grant-permissions-expected.txt: Added.
+        * http/tests/push-api/subscribe-grant-permissions.html: Added.
+        * platform/gtk/TestExpectations:
+        * platform/mac-wk1/TestExpectations:
+        * platform/win/TestExpectations:
+
 2022-02-18  Ryan Haddad  <ryanhad...@apple.com>
 
         http/wpt/push-api/onpush-disabled.html fails (incremental build issue?)

Added: trunk/LayoutTests/http/tests/push-api/resources/push-api-test-pre.js (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/resources/push-api-test-pre.js	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/resources/push-api-test-pre.js	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,49 @@
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+function finishPushAPITest()
+{
+    if (typeof window !== 'undefined' && window.parent !== window) {
+        window.parent.postMessage('finishPushAPITest', '*');
+        return;
+    }
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function waitForState(worker, state)
+{
+    if (!worker || worker.state == undefined)
+        return Promise.reject(new Error('wait_for_state must be passed a ServiceWorker'));
+
+    if (worker.state === state)
+        return Promise.resolve(state);
+
+    return new Promise(function(resolve) {
+      worker.addEventListener('statechange', function() {
+          if (worker.state === state)
+            resolve(state);
+        });
+    });
+}
+
+function log(msg)
+{
+    if (typeof window !== 'undefined' && window.parent !== window) {
+        window.parent.postMessage(msg, '*');
+        return;
+    }
+
+    let console = document.getElementById("console");
+    if (!console) {
+        console = document.createElement("div");
+        console.id = "console";
+        document.body.appendChild(console);
+    }
+    let span = document.createElement("span");
+    span.innerHTML = msg + "<br>";
+    console.appendChild(span);
+}

Added: trunk/LayoutTests/http/tests/push-api/resources/subscribe-iframe-cross-origin.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/resources/subscribe-iframe-cross-origin.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/resources/subscribe-iframe-cross-origin.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,18 @@
+<script src=""
+<script src=""
+<script>
+navigator.serviceWorker.register("subscribe-worker.js", { }).then(async (registration) => {
+    try {
+        await waitForState(registration.installing, "activated");
+        await testServiceWorkerPermissionState(registration, 'prompt');
+        await testDocumentPermissionState(registration, 'prompt');
+        await testServiceWorkerSubscribe(registration, 'NotAllowedError');
+        await testDocumentSubscribe(registration, 'NotAllowedError');
+    } catch (e) {
+        log(`FAIL: unexpected exception ${e}`);
+    } finally {
+        await registration.unregister();
+        finishPushAPITest();
+    }
+});
+</script>

Added: trunk/LayoutTests/http/tests/push-api/resources/subscribe-iframe-same-origin.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/resources/subscribe-iframe-same-origin.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/resources/subscribe-iframe-same-origin.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,18 @@
+<script src=""
+<script src=""
+<script>
+navigator.serviceWorker.register("subscribe-worker.js", { }).then(async (registration) => {
+    try {
+        await waitForState(registration.installing, "activated");
+        await testServiceWorkerPermissionState(registration, 'prompt');
+        await testDocumentPermissionState(registration, 'prompt');
+        await testServiceWorkerSubscribe(registration, 'NotAllowedError');
+        await testDocumentSubscribe(registration);
+    } catch (e) {
+        log(`FAIL: unexpected exception ${e}`);
+    } finally {
+        await registration.unregister();
+        finishPushAPITest();
+    }
+});
+</script>

Added: trunk/LayoutTests/http/tests/push-api/resources/subscribe-tests.js (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/resources/subscribe-tests.js	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/resources/subscribe-tests.js	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,71 @@
+const VALID_SERVER_KEY = "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs";
+
+async function testServiceWorkerPermissionState(registration, expected)
+{
+    let promise = new Promise(resolve => navigator.serviceWorker._onmessage_ = resolve);
+    registration.active.postMessage(['permissionState']);
+    let event = await promise;
+    let result = event.data;
+
+    if (result == expected)
+        log(`PASS: service worker permissionState was ${expected}`);
+    else
+        log(`FAIL: service worker permissionState should be ${expected}, but was ${result}`);
+}
+
+async function testDocumentPermissionState(registration, expected)
+{
+    let state = await registration.pushManager.permissionState();
+
+    if (state == expected)
+        log(`PASS: document permissionState was ${expected}`);
+    else
+        log(`FAIL: document permissionState should be ${expected}, but was ${state}`);
+}
+
+async function testServiceWorkerSubscribe(registration, domExceptionName)
+{
+    let expected = domExceptionName ? `error: ${domExceptionName}` : "successful";
+
+    let promise = new Promise(resolve => { navigator.serviceWorker._onmessage_ = resolve; });
+    registration.active.postMessage(['subscribe', VALID_SERVER_KEY]);
+    let event = await promise;
+    let result = event.data;
+
+    if (result == expected)
+        log(`PASS: service worker subscribe was ${expected}`);
+    else
+        log(`FAIL: service worker subscribe should be ${expected}, but was ${result}`);
+}
+
+async function testDocumentSubscribe(registration, domExceptionName)
+{
+    let expected = domExceptionName ? `error: ${domExceptionName}` : "successful";
+    let result = null;
+
+    let subscription = null;
+    try {
+        // With default permissions, PushManager should request permission from TestController.
+        // TestController always calls WKNotificationPermissionRequestAllow so this should succeed.
+        subscription = await registration.pushManager.subscribe({
+            userVisibleOnly: true,
+            applicationServerKey: VALID_SERVER_KEY
+        });
+        result = 'successful';
+    } catch (e) {
+        // Layout tests aren't connected to webpushd, so subscribe is successful if it gets to the
+        // point where we attempt to communicate with webpushd (an AbortError).
+        if (e.name == 'AbortError')
+            result = 'successful';
+        else
+            result = 'error: ' + (e ? e.name : null);
+    }
+
+    if (subscription)
+        await subscription.unsubscribe();
+
+    if (result == expected)
+        log(`PASS: document subscribe was ${expected}`);
+    else
+        log(`FAIL: document subscribe should be ${expected}, but was ${result}`);
+}
\ No newline at end of file

Added: trunk/LayoutTests/http/tests/push-api/resources/subscribe-worker.js (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/resources/subscribe-worker.js	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/resources/subscribe-worker.js	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,43 @@
+const VALID_SERVER_KEY = "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs";
+
+self.addEventListener('message', async (event) => {
+    let [op, ...args] = event.data;
+
+    if (op == 'permissionState') {
+        try {
+            let state = await self.registration.pushManager.permissionState();
+            event.source.postMessage(state);
+        } catch (e) {
+            event.source.postMessage("error: " + e);
+        }
+    } else if (op == 'subscribe') {
+        let subscription = null;
+        let result = null;
+
+        try {
+            subscription = await self.registration.pushManager.subscribe({
+                userVisibleOnly: true,
+                applicationServerKey: args[0]
+            });
+            if (subscription)
+                result = "successful";
+            else
+                result = "error: null subscription";
+        } catch (e) {
+            if (!e)
+                result = "error: null exception";
+            else if (e.name == 'AbortError')
+                // Layout tests currently aren't connected to webpushd. So if we fail with an AbortError
+                // when trying to connect to webpushd, we count that as a successful subscription for
+                // testing purposes.
+                result = "successful";
+            else
+                result = "error: " + e.name;
+        }
+
+        if (subscription)
+            await subscription.unsubscribe();
+
+        event.source.postMessage(result);
+    }
+});

Added: trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-expected.txt (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-expected.txt	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,5 @@
+PASS: service worker permissionState was prompt
+PASS: document permissionState was prompt
+PASS: service worker subscribe was error: NotAllowedError
+PASS: document subscribe was successful
+

Added: trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-cross-origin-expected.txt (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-cross-origin-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-cross-origin-expected.txt	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,6 @@
+
+PASS: service worker permissionState was prompt
+PASS: document permissionState was prompt
+PASS: service worker subscribe was error: NotAllowedError
+PASS: document subscribe was error: NotAllowedError
+

Added: trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+window._onmessage_ = function(event) {
+    if (event.data == 'finishPushAPITest')
+        finishPushAPITest();
+    else
+        log(event.data);
+};
+</script>
+<iframe src=""
+</iframe>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-same-origin-expected.txt (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-same-origin-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-same-origin-expected.txt	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,6 @@
+
+PASS: service worker permissionState was prompt
+PASS: document permissionState was prompt
+PASS: service worker subscribe was error: NotAllowedError
+PASS: document subscribe was successful
+

Added: trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+window._onmessage_ = function(event) {
+    if (event.data == 'finishPushAPITest')
+        finishPushAPITest();
+    else
+        log(event.data);
+};
+</script>
+<iframe src=""
+</iframe>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-default-permissions.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,24 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script>
+navigator.serviceWorker.register("resources/subscribe-worker.js", { }).then(async (registration) => {
+    try {
+        await waitForState(registration.installing, "activated");
+        await testServiceWorkerPermissionState(registration, 'prompt');
+        await testDocumentPermissionState(registration, 'prompt');
+        await testServiceWorkerSubscribe(registration, 'NotAllowedError');
+        await testDocumentSubscribe(registration);
+    } catch (e) {
+        log(`FAIL: unexpected exception ${e}`);
+    } finally {
+        await registration.unregister();
+        finishPushAPITest();
+    }
+});
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/push-api/subscribe-deny-permissions-expected.txt (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-deny-permissions-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-deny-permissions-expected.txt	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,5 @@
+PASS: service worker permissionState was denied
+PASS: document permissionState was denied
+PASS: service worker subscribe was error: NotAllowedError
+PASS: document subscribe was error: NotAllowedError
+

Added: trunk/LayoutTests/http/tests/push-api/subscribe-deny-permissions.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-deny-permissions.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-deny-permissions.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,27 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script>
+if (window.testRunner)
+    testRunner.denyWebNotificationPermission(window.origin);
+
+navigator.serviceWorker.register("resources/subscribe-worker.js", { }).then(async (registration) => {
+    try {
+        await waitForState(registration.installing, "activated");
+        await testServiceWorkerPermissionState(registration, 'denied');
+        await testDocumentPermissionState(registration, 'denied');
+        await testServiceWorkerSubscribe(registration, 'NotAllowedError');
+        await testDocumentSubscribe(registration, 'NotAllowedError');
+    } catch (e) {
+        log(`FAIL: unexpected exception ${e}`);
+    } finally {
+        await registration.unregister();
+        finishPushAPITest();
+    }
+});
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/push-api/subscribe-grant-permissions-expected.txt (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-grant-permissions-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-grant-permissions-expected.txt	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,5 @@
+PASS: service worker permissionState was granted
+PASS: document permissionState was granted
+PASS: service worker subscribe was successful
+PASS: document subscribe was successful
+

Added: trunk/LayoutTests/http/tests/push-api/subscribe-grant-permissions.html (0 => 290198)


--- trunk/LayoutTests/http/tests/push-api/subscribe-grant-permissions.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/push-api/subscribe-grant-permissions.html	2022-02-19 05:26:41 UTC (rev 290198)
@@ -0,0 +1,27 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script>
+if (window.testRunner)
+    testRunner.grantWebNotificationPermission(window.origin);
+
+navigator.serviceWorker.register("resources/subscribe-worker.js", { }).then(async (registration) => {
+    try {
+        await waitForState(registration.installing, "activated");
+        await testServiceWorkerPermissionState(registration, 'granted');
+        await testDocumentPermissionState(registration, 'granted');
+        await testServiceWorkerSubscribe(registration);
+        await testDocumentSubscribe(registration);
+    } catch (e) {
+        log(`FAIL: unexpected exception ${e}`);
+    } finally {
+        await registration.unregister();
+        finishPushAPITest();
+    }
+});
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/platform/gtk/TestExpectations (290197 => 290198)


--- trunk/LayoutTests/platform/gtk/TestExpectations	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/LayoutTests/platform/gtk/TestExpectations	2022-02-19 05:26:41 UTC (rev 290198)
@@ -1875,4 +1875,11 @@
 imported/w3c/web-platform-tests/css/filter-effects/svg-empty-container-with-filter-content-added.html [ ImageOnlyFailure ]
 imported/w3c/web-platform-tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect.tentative.html [ ImageOnlyFailure ]
 [ Release ] imported/w3c/web-platform-tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
\ No newline at end of file
+imported/w3c/web-platform-tests/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
+
+# Push subscription tests fail without platform-specific PushCrypto implementations.
+http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html [ Failure ]
+http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html [ Failure ]
+http/tests/push-api/subscribe-default-permissions.html [ Failure ]
+http/tests/push-api/subscribe-deny-permissions.html [ Failure ]
+http/tests/push-api/subscribe-grant-permissions.html [ Failure ]

Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (290197 => 290198)


--- trunk/LayoutTests/platform/mac-wk1/TestExpectations	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations	2022-02-19 05:26:41 UTC (rev 290198)
@@ -1772,4 +1772,11 @@
 [ BigSur+ arm64 ] editing/execCommand/insert-newline-in-quoted-content-crash.html [ Skip ]
 [ BigSur+ arm64 ] editing/execCommand/paste-as-quotation-disconnected-paragraph-ancestor-crash.html [ Skip ]
 
-webkit.org/b/236530 fast/text/otsvg-canvas.html [ ImageOnlyFailure ]
\ No newline at end of file
+webkit.org/b/236530 fast/text/otsvg-canvas.html [ ImageOnlyFailure ]
+
+# Push subscription tests don't work without service worker support.
+http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html [ Skip ]
+http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html [ Skip ]
+http/tests/push-api/subscribe-default-permissions.html [ Skip ]
+http/tests/push-api/subscribe-deny-permissions.html [ Skip ]
+http/tests/push-api/subscribe-grant-permissions.html [ Skip ]

Modified: trunk/LayoutTests/platform/win/TestExpectations (290197 => 290198)


--- trunk/LayoutTests/platform/win/TestExpectations	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/LayoutTests/platform/win/TestExpectations	2022-02-19 05:26:41 UTC (rev 290198)
@@ -4995,3 +4995,10 @@
 http/tests/security/contentSecurityPolicy/userAgentShadowDOM/default-src-object-data-url-blocked.html [ Failure ]
 http/tests/security/contentSecurityPolicy/userAgentShadowDOM/default-src-object-data-url-blocked2.html [ Failure ]
 http/tests/security/contentSecurityPolicy/userAgentShadowDOM/default-src-object-data-url-blocked3.html [ Failure ]
+
+# Push subscription tests fail without platform-specific PushCrypto implementations.
+http/tests/push-api/subscribe-default-permissions-iframe-cross-origin.html [ Failure ]
+http/tests/push-api/subscribe-default-permissions-iframe-same-origin.html [ Failure ]
+http/tests/push-api/subscribe-default-permissions.html [ Failure ]
+http/tests/push-api/subscribe-deny-permissions.html [ Failure ]
+http/tests/push-api/subscribe-grant-permissions.html [ Failure ]

Modified: trunk/Source/WebCore/ChangeLog (290197 => 290198)


--- trunk/Source/WebCore/ChangeLog	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/Source/WebCore/ChangeLog	2022-02-19 05:26:41 UTC (rev 290198)
@@ -1,3 +1,32 @@
+2022-02-18  Ben Nham  <n...@apple.com>
+
+        Hook up PushManager to permission state
+        https://bugs.webkit.org/show_bug.cgi?id=236739
+
+        Reviewed by Brady Eidson.
+
+        This implements PushManager.permissionState. It returns the same value as
+        Notification.permission through the same mechanism.
+
+        This also makes PushManager.subscribe request permission from the user to show notifications
+        if the permission is in the default state and these requirements are met:
+
+        1. The caller is a document with the same origin as a top level document. This means that
+           cross-origin iframes cannot request push permissions. This matches Chrome's behavior.
+        2. The caller cannot be a service worker. This limitation is allowed for by the spec.
+
+        There also needs to be some code added which removes a push subscription if the user deletes
+        the subscription entirely in the UI, for instance when the UIProcess calls
+        WKNotificationManagerProviderDidRemoveNotificationPolicies. That will be handled in a
+        separate patch.
+
+        Covered by new layout tests.
+
+        * Modules/push-api/PushManager.cpp:
+        (WebCore::PushManager::subscribe):
+        (WebCore::PushManager::getSubscription):
+        (WebCore::PushManager::permissionState):
+
 2022-02-18  Tim Nguyen  <n...@apple.com>
 
         Stop propagating inertness through iframes in Node::deprecatedIsInert()

Modified: trunk/Source/WebCore/Modules/push-api/PushManager.cpp (290197 => 290198)


--- trunk/Source/WebCore/Modules/push-api/PushManager.cpp	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/Source/WebCore/Modules/push-api/PushManager.cpp	2022-02-19 05:26:41 UTC (rev 290198)
@@ -28,10 +28,12 @@
 
 #if ENABLE(SERVICE_WORKER)
 
+#include "DocumentInlines.h"
 #include "EventLoop.h"
 #include "Exception.h"
 #include "JSPushPermissionState.h"
 #include "JSPushSubscription.h"
+#include "NotificationClient.h"
 #include "PushCrypto.h"
 #include "ScriptExecutionContext.h"
 #include "ServiceWorkerRegistration.h"
@@ -65,11 +67,11 @@
     m_serviceWorkerRegistration.deref();
 }
 
-void PushManager::subscribe(ScriptExecutionContext& scriptExecutionContext, std::optional<PushSubscriptionOptionsInit>&& options, DOMPromiseDeferred<IDLInterface<PushSubscription>>&& promise)
+void PushManager::subscribe(ScriptExecutionContext& context, std::optional<PushSubscriptionOptionsInit>&& options, DOMPromiseDeferred<IDLInterface<PushSubscription>>&& promise)
 {
-    RELEASE_ASSERT(scriptExecutionContext.isSecureContext());
+    RELEASE_ASSERT(context.isSecureContext());
     
-    scriptExecutionContext.eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, options = WTFMove(options), promise = WTFMove(promise)]() mutable {
+    context.eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, context = Ref { context }, options = WTFMove(options), promise = WTFMove(promise)]() mutable {
         if (!options || !options->userVisibleOnly) {
             promise.reject(Exception { NotAllowedError, "Subscribing for push requires userVisibleOnly to be true"_s });
             return;
@@ -111,22 +113,68 @@
             return;
         }
 
+        auto client = context->notificationClient();
+        auto permission = client ? client->checkPermission(context.ptr()) : NotificationPermission::Denied;
+
+        if (permission == NotificationPermission::Denied) {
+            promise.reject(Exception { NotAllowedError, "User denied push permission"_s });
+            return;
+        }
+
+        if (permission == NotificationPermission::Default && !context->isDocument()) {
+            promise.reject(Exception { NotAllowedError, "User denied push permission"_s });
+            return;
+        }
+
+        if (permission == NotificationPermission::Default) {
+            RELEASE_ASSERT(client);
+            RELEASE_ASSERT(context->isDocument());
+
+            if (!downcast<Document>(context.get()).isSameOriginAsTopDocument()) {
+                promise.reject(Exception { NotAllowedError, "Cannot request permission from cross-origin iframe"_s });
+                return;
+            }
+
+            client->requestPermission(context, [this, protectedThis = WTFMove(protectedThis), keyData = keyDataResult.releaseReturnValue(), promise = WTFMove(promise)](auto permission) mutable {
+                if (permission != NotificationPermission::Granted) {
+                    promise.reject(Exception { NotAllowedError, "User denied push permission"_s });
+                    return;
+                }
+
+                m_serviceWorkerRegistration.subscribeToPushService(WTFMove(keyData), WTFMove(promise));
+            });
+            return;
+        }
+
+        RELEASE_ASSERT(permission == NotificationPermission::Granted);
         m_serviceWorkerRegistration.subscribeToPushService(keyDataResult.releaseReturnValue(), WTFMove(promise));
     });
 }
 
-void PushManager::getSubscription(ScriptExecutionContext& scriptExecutionContext, DOMPromiseDeferred<IDLNullable<IDLInterface<PushSubscription>>>&& promise)
+void PushManager::getSubscription(ScriptExecutionContext& context, DOMPromiseDeferred<IDLNullable<IDLInterface<PushSubscription>>>&& promise)
 {
-    scriptExecutionContext.eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, promise = WTFMove(promise)]() mutable {
+    context.eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, promise = WTFMove(promise)]() mutable {
         m_serviceWorkerRegistration.getPushSubscription(WTFMove(promise));
     });
 }
 
-void PushManager::permissionState(ScriptExecutionContext& scriptExecutionContext, std::optional<PushSubscriptionOptionsInit>&& options, DOMPromiseDeferred<IDLEnumeration<PushPermissionState>>&& promise)
+void PushManager::permissionState(ScriptExecutionContext& context, std::optional<PushSubscriptionOptionsInit>&&, DOMPromiseDeferred<IDLEnumeration<PushPermissionState>>&& promise)
 {
-    UNUSED_PARAM(options);
-    scriptExecutionContext.eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, promise = WTFMove(promise)]() mutable {
-        m_serviceWorkerRegistration.getPushPermissionState(WTFMove(promise));
+    context.eventLoop().queueTask(TaskSource::Networking, [context = Ref { context }, promise = WTFMove(promise)]() mutable {
+        auto client = context->notificationClient();
+        auto permission = client ? client->checkPermission(context.ptr()) : NotificationPermission::Denied;
+
+        switch (permission) {
+        case NotificationPermission::Default:
+            promise.resolve(PushPermissionState::Prompt);
+            break;
+        case NotificationPermission::Granted:
+            promise.resolve(PushPermissionState::Granted);
+            break;
+        default:
+            promise.resolve(PushPermissionState::Denied);
+            break;
+        }
     });
 }
 

Modified: trunk/Source/WebKit/ChangeLog (290197 => 290198)


--- trunk/Source/WebKit/ChangeLog	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/Source/WebKit/ChangeLog	2022-02-19 05:26:41 UTC (rev 290198)
@@ -1,3 +1,15 @@
+2022-02-18  Ben Nham  <n...@apple.com>
+
+        Hook up PushManager to permission state
+        https://bugs.webkit.org/show_bug.cgi?id=236739
+
+        Reviewed by Brady Eidson.
+
+        Update comment explaining why we still have a permission state stub in webpushd.
+
+        * webpushd/WebPushDaemon.mm:
+        (WebPushD::Daemon::getPushPermissionState):
+
 2022-02-18  Sihui Liu  <sihui_...@apple.com>
 
         Add assertion that no two network sessions share the same storage path

Modified: trunk/Source/WebKit/webpushd/WebPushDaemon.mm (290197 => 290198)


--- trunk/Source/WebKit/webpushd/WebPushDaemon.mm	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/Source/WebKit/webpushd/WebPushDaemon.mm	2022-02-19 05:26:41 UTC (rev 290198)
@@ -622,12 +622,10 @@
 
 void Daemon::getPushPermissionState(ClientConnection* connection, const URL& scopeURL, CompletionHandler<void(const Expected<uint8_t, WebCore::ExceptionData>&)>&& replySender)
 {
-    // FIXME
-#if HAVE(APPLE_PUSH_SERVICE_URL_TOKEN_SUPPORT)
-    replySender(static_cast<uint8_t>(WebCore::PushPermissionState::Granted));
-#else
+    // FIXME: This doesn't actually get called right now, since the permission is currently checked
+    // in WebProcess. However, we've left this stub in for now because there is a chance that we
+    // will move the permission check into webpushd when supporting other platforms.
     replySender(static_cast<uint8_t>(WebCore::PushPermissionState::Denied));
-#endif
 }
 
 ClientConnection* Daemon::toClientConnection(xpc_connection_t connection)

Modified: trunk/Tools/ChangeLog (290197 => 290198)


--- trunk/Tools/ChangeLog	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/Tools/ChangeLog	2022-02-19 05:26:41 UTC (rev 290198)
@@ -1,3 +1,14 @@
+2022-02-18  Ben Nham  <n...@apple.com>
+
+        Hook up PushManager to permission state
+        https://bugs.webkit.org/show_bug.cgi?id=236739
+
+        Reviewed by Brady Eidson.
+
+        Made the WebPushD API tests work now that subscribe checks for permission.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm:
+
 2022-02-18  Sihui Liu  <sihui_...@apple.com>
 
         Add assertion that no two network session share the same storage path

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm (290197 => 290198)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm	2022-02-19 04:27:39 UTC (rev 290197)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm	2022-02-19 05:26:41 UTC (rev 290198)
@@ -426,6 +426,10 @@
 
         m_notificationMessageHandler = adoptNS([[NotificationScriptMessageHandler alloc] init]);
         [[m_configuration userContentController] addScriptMessageHandler:m_notificationMessageHandler.get() name:@"note"];
+
+        m_webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:m_configuration.get()]);
+        m_uiDelegate = adoptNS([[NotificationPermissionDelegate alloc] init]);
+        [m_webView setUIDelegate:m_uiDelegate.get()];
     }
 
     void loadRequest(const char* htmlSource, const char* serviceWorkerScriptSource)
@@ -440,7 +444,6 @@
             { "/sw.js", { { { "Content-Type", "application/_javascript_" } }, serviceWorkerScriptSource } }
         }, TestWebKitAPI::HTTPServer::Protocol::Http));
 
-        m_webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:m_configuration.get()]);
         [m_webView loadRequest:m_server->request()];
     }
 
@@ -456,6 +459,7 @@
     RetainPtr<NotificationScriptMessageHandler> m_notificationMessageHandler;
     std::unique_ptr<TestWebKitAPI::HTTPServer> m_server;
     RetainPtr<WKWebView> m_webView;
+    RetainPtr<id<WKUIDelegatePrivate>> m_uiDelegate;
 };
 
 class WebPushDInjectedPushTest : public WebPushDTest {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to