Title: [291979] trunk
Revision
291979
Author
beid...@apple.com
Date
2022-03-28 11:14:19 -0700 (Mon, 28 Mar 2022)

Log Message

Support ServiceWorkerClients.openWindow.
<rdar://90616651> and https://bugs.webkit.org/show_bug.cgi?id=238400

Reviewed by Youenn Fablet.
Source/WebCore:

Test: http/tests/workers/service/openwindow-from-notification-click.html

* dom/Document.cpp:
(WebCore::Document::pageID const):

* page/ClientOrigin.h:
(WebCore::ClientOrigin::loggingString const):

* workers/service/ServiceWorkerClientData.cpp:
(WebCore::ServiceWorkerClientData::isolatedCopy const):
(WebCore::ServiceWorkerClientData::isolatedCopy):
(WebCore::ServiceWorkerClientData::from):

* workers/service/ServiceWorkerClientData.h:
(WebCore::ServiceWorkerClientData::encode const):
(WebCore::ServiceWorkerClientData::decode):

* workers/service/ServiceWorkerClients.cpp:
(WebCore::matchWindowWithPageIdentifier):
(WebCore::ServiceWorkerClients::openWindow):

* workers/service/context/SWContextManager.h:
* workers/service/server/SWServerToContextConnection.h:
* workers/service/server/SWServerWorker.h:

Source/WebKit:

This API asks the browser to asynchronously open a new tab to a URL then resolve
a promise with the new WindowClient representing that tab.

>From a WebCore/WebKit standpoint, implementing this was mostly straightforward.
1 - A plumbing exercise (thread hopping and IPC'ing the message and its reply around)
2 - Implmenting a new delegate method for the hosting app to create the requested WKWebView

The delegate method was interesting. Normally this is the type of thing that'd go to the
WKUIDelegate but that requires there to be a WKWebView, and service workers can be running
without any web views.

Fortunately we already had a WKWebsiteDataStore delegate SPI, and service workers *do* always
have an associated website data store they're running under.

Once the app gives the new web view back to WebKit, we record its PageIdentifier in the reply.

Once the reply makes its way all the way back to the ServiceWorker process and on the
ServiceWorker thread, we do a client match and cross check with that PageIdentifier to make
sure we're resolving the promise with the correct WindowClient.

If there's no matched clients, then the view is either already gone or it has navigated away
to a non-applicable URL.

Same if there are matched clients, but they don't match the specified PageIdentifier.

A straight forward layouttest completes the task.

* NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::controlClient):

* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
(WebKit::WebSWServerToContextConnection::openWindow):
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:

* UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
(-[WKWebsiteDataStore set_delegate:]):
* UIProcess/API/Cocoa/_WKWebsiteDataStoreDelegate.h:

* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::openWindowFromServiceWorker):
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/Network/NetworkProcessProxy.messages.in:

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didFailProvisionalLoadForFrameShared):
(WebKit::WebPageProxy::callLoadCompletionHandlersIfNecessary):
(WebKit::WebPageProxy::didFinishLoadForFrame):
(WebKit::WebPageProxy::didFailLoadForFrame):
(WebKit::WebPageProxy::resetState):
(WebKit::WebPageProxy::callServiceWorkerLaunchCompletionHandlerIfNecessary): Deleted.
* UIProcess/WebPageProxy.h:

* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::openWindowFromServiceWorker):
* UIProcess/WebsiteData/WebsiteDataStore.h:

* UIProcess/WebsiteData/WebsiteDataStoreClient.h:
(WebKit::WebsiteDataStoreClient::openWindowFromServiceWorker):

* WebProcess/Notifications/WebNotificationManager.cpp:
(WebKit::WebNotificationManager::show):
(WebKit::WebNotificationManager::didShowNotification):
(WebKit::WebNotificationManager::didClickNotification):

* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::openWindow):
* WebProcess/Storage/WebSWContextManagerConnection.h:

Source/WTF:

* wtf/HashTable.h:
(WTF::KeyTraits>::inlineLookup): Relax this value size requirement for now.
The required refactoring is best left as a seperate task with a seperate patch.

Tools:

Implement the new delegate to create the new view.

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::createOtherPage):
(WTR::TestController::createOtherPlatformWebView):
* WebKitTestRunner/TestController.h:

* WebKitTestRunner/cocoa/TestControllerCocoa.mm:
(WTR::TestController::platformCreateOtherPage):
* WebKitTestRunner/cocoa/TestWebsiteDataStoreDelegate.mm:
(-[TestWebsiteDataStoreDelegate websiteDataStore:openWindow:fromServiceWorkerOrigin:completionHandler:]):

LayoutTests:

WKTR knows how to openWindow() now.

So test:
- The success case
- Failure due to a disallowed URL
- Failure due to a lack of a related user gesture.

* http/tests/workers/service/openwindow-from-notification-click-expected.txt: Added.
* http/tests/workers/service/openwindow-from-notification-click.html: Added.
* http/tests/workers/service/resources/openwindow-client.html: Added.
* http/tests/workers/service/resources/shownotification-openwindow-worker.js: Added.
(async const):
(let.messageClients):
(clients.openWindow.string_appeared_here.then.async client):
(catch.async error):
(async event):
(async tryShow):
(async getNotes):
* platform/gtk/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (291978 => 291979)


--- trunk/LayoutTests/ChangeLog	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/LayoutTests/ChangeLog	2022-03-28 18:14:19 UTC (rev 291979)
@@ -1,3 +1,30 @@
+2022-03-28  Brady Eidson  <beid...@apple.com>
+
+        Support ServiceWorkerClients.openWindow.
+        <rdar://90616651> and https://bugs.webkit.org/show_bug.cgi?id=238400
+
+        Reviewed by Youenn Fablet.
+
+        WKTR knows how to openWindow() now.
+        
+        So test:
+        - The success case
+        - Failure due to a disallowed URL
+        - Failure due to a lack of a related user gesture.
+        
+        * http/tests/workers/service/openwindow-from-notification-click-expected.txt: Added.
+        * http/tests/workers/service/openwindow-from-notification-click.html: Added.
+        * http/tests/workers/service/resources/openwindow-client.html: Added.
+        * http/tests/workers/service/resources/shownotification-openwindow-worker.js: Added.
+        (async const):
+        (let.messageClients):
+        (clients.openWindow.string_appeared_here.then.async client):
+        (catch.async error):
+        (async event):
+        (async tryShow):
+        (async getNotes):
+        * platform/gtk/TestExpectations:
+
 2022-03-28  Ziran Sun  <z...@igalia.com>
 
         [Forms] Alias appearance <compat-auto> keywords to 'auto' for button

Added: trunk/LayoutTests/http/tests/workers/service/openwindow-from-notification-click-expected.txt (0 => 291979)


--- trunk/LayoutTests/http/tests/workers/service/openwindow-from-notification-click-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/workers/service/openwindow-from-notification-click-expected.txt	2022-03-28 18:14:19 UTC (rev 291979)
@@ -0,0 +1,15 @@
+This tests that a notificationclick handler can open a window
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Notification.permission is "granted"
+PASS gotClicked is false
+PASS gotClosed is false
+PASS gotClicked is true
+PASS gotClosed is false
+PASS gotAboutBlankFail is true
+PASS gotUserGestureFail is true
+PASS gotSuccessfulNullClient is true
+PASS New client has been observed
+

Added: trunk/LayoutTests/http/tests/workers/service/openwindow-from-notification-click.html (0 => 291979)


--- trunk/LayoutTests/http/tests/workers/service/openwindow-from-notification-click.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/workers/service/openwindow-from-notification-click.html	2022-03-28 18:14:19 UTC (rev 291979)
@@ -0,0 +1,91 @@
+<head>
+<script src=""
+<script src=""
+<script>
+
+function failTheTest(msg)
+{
+    testFailed(msg);
+    if (testRunner)
+        testRunner.notifyDone();
+}
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.grantWebNotificationPermission(testURL);
+    setTimeout("failTheTest('timed out')", 15000);
+}
+
+description("This tests that a notificationclick handler can open a window");
+
+shouldBeEqualToString("Notification.permission", "granted");
+
+async function registerServiceWorker() {
+    var registration = await navigator.serviceWorker.register('resources/shownotification-openwindow-worker.js');
+    
+    if (!registration)
+        return testFailed("No registration");
+    
+    if (registration.active) {
+        registration.active.postMessage("Start");
+        return;
+    }
+
+    var installingWorker = registration.installing;
+    if (!installingWorker)
+        failTheTest("No active *or* installing worker");
+
+    installingWorker.addEventListener("statechange", () => {
+        if (installingWorker.state === "activated") {
+            installingWorker.postMessage("tryshow");
+        }
+    });
+}
+
+var gotClicked = false;
+var gotClosed = false;
+var gotNewClient = false;
+var gotAboutBlankFail = false;
+var gotUserGestureFail = false;
+var gotSuccessfulNullClient = false;
+
+navigator.serviceWorker.addEventListener('message', event => {
+    if (event.data == "showFailed") {
+        failTheTest("Unexpectedly received the failed-to-show message");
+    } else if (event.data == "shown") {
+        if (testRunner)
+            testRunner.simulateWebNotificationClickForServiceWorkerNotifications();
+    } else if (event.data == "clicked") {
+        shouldBeFalse("gotClicked");
+        shouldBeFalse("gotClosed");
+        gotClicked = true;
+    } else if (event.data == "closed") {
+        shouldBeTrue("gotClicked")
+        shouldBeFalse("gotClosed")
+        gotClosed = true;
+    } else if (event.data == "gotNewClient") {
+        gotNewClient = true;
+    } else if (event.data == "gotAboutBlankFail") {
+        gotAboutBlankFail = true;
+    } else if (event.data == "gotUserGestureFail") {
+        gotUserGestureFail = true;
+    } else if (event.data = "" {
+        gotSuccessfulNullClient = true;
+    } else {
+        testFailed("Message received: " + event.data);
+    }
+    
+    if (gotNewClient) {
+        shouldBeTrue("gotAboutBlankFail");
+        shouldBeTrue("gotUserGestureFail");
+        shouldBeTrue("gotSuccessfulNullClient");
+        testPassed("New client has been observed");
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+});
+
+</script>
+</head>
+<body _onload_="registerServiceWorker()">
+</body>

Added: trunk/LayoutTests/http/tests/workers/service/resources/openwindow-client.html (0 => 291979)


--- trunk/LayoutTests/http/tests/workers/service/resources/openwindow-client.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/workers/service/resources/openwindow-client.html	2022-03-28 18:14:19 UTC (rev 291979)
@@ -0,0 +1,16 @@
+<head>
+<script>
+    
+navigator.serviceWorker.addEventListener('message', event => {
+    if (event.data != "echo")
+        return;
+
+    navigator.serviceWorker.getRegistration().then(function(registration) {
+        registration.active.postMessage("echo back");
+    });
+});
+
+</script>
+</head>
+<body>
+</body>

Added: trunk/LayoutTests/http/tests/workers/service/resources/shownotification-openwindow-worker.js (0 => 291979)


--- trunk/LayoutTests/http/tests/workers/service/resources/shownotification-openwindow-worker.js	                        (rev 0)
+++ trunk/LayoutTests/http/tests/workers/service/resources/shownotification-openwindow-worker.js	2022-03-28 18:14:19 UTC (rev 291979)
@@ -0,0 +1,108 @@
+
+let messageClients = async function(msg) { 
+    const allClients = await clients.matchAll({
+        includeUncontrolled: true
+    });
+
+    for (const client of allClients)
+        client.postMessage(msg);
+}
+
+// Try to open a new window now.
+// It should fail due to lack of user gesture
+clients.openWindow('http://127.0.0.1:8000/workers/service/resources/openwindow-client.html').then(async function(client) {
+    await messageClients("Opening a window client without a user gesture SUCCEEDED. It should've failed.");
+}).catch(async function(error) {
+    await messageClients("gotUserGestureFail");
+});
+
+self.addEventListener('notificationclick', async function(event) {
+    await messageClients("clicked");
+    event.notification.close();
+
+    // Should fail because about:blank is not an allowable URL
+    clients.openWindow('about:blank').then(async function(client) {
+        await messageClients("Opening a window client to about:blank SUCCEEDED. It should've failed.");
+    }).catch(async function(error) {
+        await messageClients("gotAboutBlankFail");
+    });
+
+    // This successfully loads the openwindow-client.html resource, but the origin is different, therefore the serviceworker
+    // should NOT get a WindowClient
+    clients.openWindow('http://localhost:8000/workers/service/resources/openwindow-client.html').then(async function(client) {
+        if (client == null)
+            await messageClients("gotSuccessfulNullClient")
+        else
+            await messageClients("Got a WindowClient when we were *not* expecting to");
+    }).catch(async function(error) {
+        await messageClients("Expected a resolved promise with a null window client. Promise rejected instead");
+    });
+
+    clients.openWindow('http://127.0.0.1:8000/workers/service/resources/openwindow-client.html').then(function(client) {
+        client.postMessage("echo");
+    });
+});
+
+self.addEventListener('notificationclose', async function(event) {
+    await messageClients("closed");
+});
+
+async function tryShow(message)
+{
+    var title, body, tag;
+    var components = message.split('|');
+
+    if (components.length == 1) {
+        title = "This is a notification";        
+    } else {
+        title = components[1];
+        body = components[2];
+        tag = components[3];
+    }
+    
+    try {
+        await registration.showNotification(title, {
+            body: body,
+            tag: tag
+        });
+    } catch(error) {
+        await messageClients("showFailed");
+        return;
+    }
+    
+    await messageClients("shown");
+}
+
+var seenNotes = new Set();
+
+async function getNotes(message)
+{
+    var tag = undefined;
+    var components = message.split('|');
+    if (components.length == 2)
+        tag = components[1];
+
+    var notifications = await registration.getNotifications({ tag: tag });
+    var reply = "gotnotes|There are " + notifications.length + " notifications|";
+
+    for (notification of notifications) {
+        if (seenNotes.has(notification))
+            messageClients("Saw the same notifcation twice through getNotifications(), this should not happen");
+        seenNotes.add(notification)
+        
+        reply += "Title: " + notification.title + "|";
+        reply += "Body: " + notification.body + "|";
+        reply += "Tag: " + notification.tag + "|";
+    }
+    await messageClients(reply);
+}
+
+self.addEventListener('message', async function(event) {
+    var messageName = event.data.split('|')[0];
+    if (messageName == "tryshow")
+        await tryShow(event.data);
+    if (messageName == "getnotes")
+        await getNotes(event.data);
+    if (messageName == "echo back")
+        await messageClients("gotNewClient");
+});

Modified: trunk/LayoutTests/platform/gtk/TestExpectations (291978 => 291979)


--- trunk/LayoutTests/platform/gtk/TestExpectations	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/LayoutTests/platform/gtk/TestExpectations	2022-03-28 18:14:19 UTC (rev 291979)
@@ -717,6 +717,7 @@
 webkit.org/b/175419 imported/w3c/web-platform-tests/service-workers/cache-storage/serviceworker/cache-storage-match.https.html [ Skip ]
 webkit.org/b/175419 imported/w3c/web-platform-tests/service-workers/cache-storage/serviceworker/credentials.https.html [ Skip ]
 webkit.org/b/175419 [ Release ] http/tests/workers/service/postmessage-after-terminate.https.html [ Pass Failure Timeout ]
+webkit.org/b/175419 http/tests/workers/service/openwindow-from-notification-click.html [ Skip ]
 
 # FIXME: This can't be the right bug number.
 webkit.org/b/175419 imported/w3c/web-platform-tests/fetch/api/abort/serviceworker-intercepted.https.html [ Failure ]

Modified: trunk/Source/WTF/ChangeLog (291978 => 291979)


--- trunk/Source/WTF/ChangeLog	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WTF/ChangeLog	2022-03-28 18:14:19 UTC (rev 291979)
@@ -1,3 +1,14 @@
+2022-03-28  Brady Eidson  <beid...@apple.com>
+
+        Support ServiceWorkerClients.openWindow.
+        <rdar://90616651> and https://bugs.webkit.org/show_bug.cgi?id=238400
+
+        Reviewed by Youenn Fablet.
+
+        * wtf/HashTable.h:
+        (WTF::KeyTraits>::inlineLookup): Relax this value size requirement for now.
+        The required refactoring is best left as a seperate task with a seperate patch.
+
 2022-03-28  Chris Dumez  <cdu...@apple.com>
 
         Use StringView for Yarr / RegularExpression parsing

Modified: trunk/Source/WTF/wtf/HashTable.h (291978 => 291979)


--- trunk/Source/WTF/wtf/HashTable.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WTF/wtf/HashTable.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -681,7 +681,7 @@
     template<typename HashTranslator, typename T>
     ALWAYS_INLINE auto HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::inlineLookup(const T& key) -> ValueType*
     {
-        static_assert(sizeof(Value) <= 128, "Your HashTable types are too big to efficiently move when rehashing.  Consider using UniqueRef instead");
+        static_assert(sizeof(Value) <= 150, "Your HashTable types are too big to efficiently move when rehashing.  Consider using UniqueRef instead");
         checkKey<HashTranslator>(key);
 
         unsigned k = 0;

Modified: trunk/Source/WebCore/ChangeLog (291978 => 291979)


--- trunk/Source/WebCore/ChangeLog	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/ChangeLog	2022-03-28 18:14:19 UTC (rev 291979)
@@ -1,3 +1,35 @@
+2022-03-28  Brady Eidson  <beid...@apple.com>
+
+        Support ServiceWorkerClients.openWindow.
+        <rdar://90616651> and https://bugs.webkit.org/show_bug.cgi?id=238400
+
+        Reviewed by Youenn Fablet.
+
+        Test: http/tests/workers/service/openwindow-from-notification-click.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::pageID const):
+        
+        * page/ClientOrigin.h:
+        (WebCore::ClientOrigin::loggingString const):
+        
+        * workers/service/ServiceWorkerClientData.cpp:
+        (WebCore::ServiceWorkerClientData::isolatedCopy const):
+        (WebCore::ServiceWorkerClientData::isolatedCopy):
+        (WebCore::ServiceWorkerClientData::from):
+        
+        * workers/service/ServiceWorkerClientData.h:
+        (WebCore::ServiceWorkerClientData::encode const):
+        (WebCore::ServiceWorkerClientData::decode):
+        
+        * workers/service/ServiceWorkerClients.cpp:
+        (WebCore::matchWindowWithPageIdentifier):
+        (WebCore::ServiceWorkerClients::openWindow):
+        
+        * workers/service/context/SWContextManager.h:
+        * workers/service/server/SWServerToContextConnection.h:
+        * workers/service/server/SWServerWorker.h:
+
 2022-03-28  Ian Anderson  <i...@apple.com>
 
         Some Apple internal clients fail to build due to redeclared AppKit types in WebKitLegacy

Modified: trunk/Source/WebCore/dom/Document.cpp (291978 => 291979)


--- trunk/Source/WebCore/dom/Document.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/dom/Document.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -8408,7 +8408,7 @@
     
 std::optional<PageIdentifier> Document::pageID() const
 {
-    return m_frame->loader().pageID();
+    return m_frame ? m_frame->loader().pageID() : std::nullopt;
 }
 
 std::optional<FrameIdentifier> Document::frameID() const

Modified: trunk/Source/WebCore/page/ClientOrigin.h (291978 => 291979)


--- trunk/Source/WebCore/page/ClientOrigin.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/page/ClientOrigin.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -50,6 +50,8 @@
 
     SecurityOriginData topOrigin;
     SecurityOriginData clientOrigin;
+
+    String loggingString() const { return makeString(topOrigin.toString(), "-", clientOrigin.toString()); }
 };
 
 inline void add(Hasher& hasher, const ClientOrigin& origin)

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerClientData.cpp (291978 => 291979)


--- trunk/Source/WebCore/workers/service/ServiceWorkerClientData.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerClientData.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -58,12 +58,12 @@
 
 ServiceWorkerClientData ServiceWorkerClientData::isolatedCopy() const &
 {
-    return { identifier, type, frameType, url.isolatedCopy(), lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder };
+    return { identifier, type, frameType, url.isolatedCopy(), pageIdentifier, lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder };
 }
 
 ServiceWorkerClientData ServiceWorkerClientData::isolatedCopy() &&
 {
-    return { identifier, type, frameType, WTFMove(url).isolatedCopy(), lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder };
+    return { identifier, type, frameType, WTFMove(url).isolatedCopy(), pageIdentifier, lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder };
 }
 
 ServiceWorkerClientData ServiceWorkerClientData::from(ScriptExecutionContext& context)
@@ -78,7 +78,9 @@
         context.identifier(),
         isDocument ? ServiceWorkerClientType::Window : ServiceWorkerClientType::Worker,
         toServiceWorkerClientFrameType(context),
-        document.creationURL(), lastNavigationWasAppInitiated,
+        document.creationURL(),
+        document.pageID(),
+        lastNavigationWasAppInitiated,
         !document.hidden(),
         document.hasFocus(),
         0

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h (291978 => 291979)


--- trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -27,6 +27,7 @@
 
 #if ENABLE(SERVICE_WORKER)
 
+#include "PageIdentifier.h"
 #include "ProcessQualified.h"
 #include "ScriptExecutionContextIdentifier.h"
 #include "ServiceWorkerClientType.h"
@@ -45,6 +46,7 @@
     ServiceWorkerClientType type;
     ServiceWorkerClientFrameType frameType;
     URL url;
+    std::optional<PageIdentifier> pageIdentifier;
     LastNavigationWasAppInitiated lastNavigationWasAppInitiated;
     bool isVisible { false };
     bool isFocused { false };
@@ -62,7 +64,7 @@
 template<class Encoder>
 void ServiceWorkerClientData::encode(Encoder& encoder) const
 {
-    encoder << identifier << type << frameType << url << lastNavigationWasAppInitiated << isVisible << isFocused << focusOrder;
+    encoder << identifier << type << frameType << url << pageIdentifier << lastNavigationWasAppInitiated << isVisible << isFocused << focusOrder;
 }
 
 template<class Decoder>
@@ -88,6 +90,11 @@
     if (!url)
         return std::nullopt;
 
+    std::optional<std::optional<PageIdentifier>> pageIdentifier;
+    decoder >> pageIdentifier;
+    if (!pageIdentifier)
+        return std::nullopt;
+
     std::optional<LastNavigationWasAppInitiated> lastNavigationWasAppInitiated;
     decoder >> lastNavigationWasAppInitiated;
     if (!lastNavigationWasAppInitiated)
@@ -108,7 +115,7 @@
     if (!focusOrder)
         return std::nullopt;
 
-    return { { WTFMove(*identifier), WTFMove(*type), WTFMove(*frameType), WTFMove(*url), WTFMove(*lastNavigationWasAppInitiated), WTFMove(*isVisible), WTFMove(*isFocused), WTFMove(*focusOrder) } };
+    return { { WTFMove(*identifier), WTFMove(*type), WTFMove(*frameType), WTFMove(*url), WTFMove(*pageIdentifier), WTFMove(*lastNavigationWasAppInitiated), WTFMove(*isVisible), WTFMove(*isFocused), WTFMove(*focusOrder) } };
 }
 
 using ServiceWorkerClientsMatchAllCallback = CompletionHandler<void(Vector<ServiceWorkerClientData>&&)>;

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerClients.cpp (291978 => 291979)


--- trunk/Source/WebCore/workers/service/ServiceWorkerClients.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerClients.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -90,12 +90,78 @@
     });
 }
 
-void ServiceWorkerClients::openWindow(ScriptExecutionContext&, const String& url, Ref<DeferredPromise>&& promise)
+static void matchWindowWithPageIdentifier(ServiceWorkerIdentifier serviceWorkerIdentifier, PageIdentifier pageIdentifier, CompletionHandler<void(ServiceWorkerGlobalScope&, std::optional<ServiceWorkerClientData>)>&& callback)
 {
-    UNUSED_PARAM(url);
-    promise->reject(Exception { NotSupportedError, "clients.openWindow() is not yet supported"_s });
+    callOnMainThread([serviceWorkerIdentifier, pageIdentifier, callback = WTFMove(callback)] () mutable {
+        auto connection = SWContextManager::singleton().connection();
+        auto options = ServiceWorkerClientQueryOptions { true, ServiceWorkerClientType::Window };
+        connection->matchAll(serviceWorkerIdentifier, options, [serviceWorkerIdentifier, pageIdentifier, callback = WTFMove(callback)] (Vector<ServiceWorkerClientData>&& clientsData) mutable {
+            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [pageIdentifier, callback = WTFMove(callback), clientsData = crossThreadCopy(WTFMove(clientsData))] (auto& scope) mutable {
+                for (auto& data : clientsData) {
+                    if (data.pageIdentifier == pageIdentifier) {
+                        callback(scope, data);
+                        return;
+                    }
+                }
+
+                callback(scope, std::nullopt);
+            });
+        });
+    });
 }
 
+void ServiceWorkerClients::openWindow(ScriptExecutionContext& context, const String& urlString, Ref<DeferredPromise>&& promise)
+{
+    LOG(ServiceWorker, "WebProcess %i service worker calling openWindow to URL %s", getpid(), urlString.utf8().data());
+
+    auto serviceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
+    auto url = ""
+
+    if (context.settingsValues().serviceWorkersUserGestureEnabled && !downcast<ServiceWorkerGlobalScope>(context).isProcessingUserGesture()) {
+        promise->reject(Exception { InvalidAccessError, "ServiceWorkerClients.openWindow() requires a user gesture"_s });
+        return;
+    }
+
+    if (!url.isValid()) {
+        promise->reject(Exception { TypeError, makeString("URL string ", urlString, " cannot successfully be parsed"_s) });
+        return;
+    }
+
+    if (url.protocolIsAbout()) {
+        promise->reject(Exception { TypeError, makeString("ServiceWorkerClients.openWindow() cannot be called with URL "_s, url.string()) });
+        return;
+    }
+
+    callOnMainThread([promiseIdentifier = addPendingPromise(WTFMove(promise)), serviceWorkerIdentifier, url = "" () mutable {
+        auto connection = SWContextManager::singleton().connection();
+        connection->openWindow(serviceWorkerIdentifier, url.string(), [promiseIdentifier, serviceWorkerIdentifier] (std::optional<PageIdentifier>&& pageIdentifier) mutable {
+            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promiseIdentifier, pageIdentifier = WTFMove(pageIdentifier), serviceWorkerIdentifier] (ServiceWorkerGlobalScope& scope) mutable {
+                LOG(ServiceWorker, "WebProcess %i finished ServiceWorkerClients::openWindow call. Resulting page identifier is %s", getpid(), pageIdentifier ? pageIdentifier->loggingString().utf8().data() : "<NONE>");
+
+                if (!pageIdentifier) {
+                    if (auto promise = scope.clients().takePendingPromise(promiseIdentifier))
+                        promise->resolveWithJSValue(JSC::jsNull());
+                    return;
+                }
+
+                matchWindowWithPageIdentifier(serviceWorkerIdentifier, *pageIdentifier, [promiseIdentifier] (auto& scope, std::optional<ServiceWorkerClientData> clientData) mutable {
+                    auto promise = scope.clients().takePendingPromise(promiseIdentifier);
+                    if (!promise)
+                        return;
+
+                    if (!clientData) {
+                        promise->resolveWithJSValue(JSC::jsNull());
+                        return;
+                    }
+
+                    auto client = ServiceWorkerClient::create(scope, WTFMove(*clientData));
+                    promise->template resolve<IDLInterface<ServiceWorkerClient>>(WTFMove(client));
+                });
+            });
+        });
+    });
+}
+
 void ServiceWorkerClients::claim(ScriptExecutionContext& context, Ref<DeferredPromise>&& promise)
 {
     auto serviceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();

Modified: trunk/Source/WebCore/workers/service/context/SWContextManager.h (291978 => 291979)


--- trunk/Source/WebCore/workers/service/context/SWContextManager.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/workers/service/context/SWContextManager.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -67,8 +67,8 @@
         virtual void findClientByVisibleIdentifier(ServiceWorkerIdentifier, const String&, FindClientByIdentifierCallback&&) = 0;
         virtual void matchAll(ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&, ServiceWorkerClientsMatchAllCallback&&) = 0;
         virtual void claim(ServiceWorkerIdentifier, CompletionHandler<void(ExceptionOr<void>&&)>&&) = 0;
-
         virtual void focus(ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&) = 0;
+        virtual void openWindow(ServiceWorkerIdentifier, const String& url, CompletionHandler<void(std::optional<PageIdentifier>&&)>&&) = 0;
 
         virtual void didFailHeartBeatCheck(ServiceWorkerIdentifier) = 0;
         virtual void setAsInspected(ServiceWorkerIdentifier, bool) = 0;

Modified: trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h (291978 => 291979)


--- trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -82,6 +82,8 @@
     WEBCORE_EXPORT void setAsInspected(ServiceWorkerIdentifier, bool);
     WEBCORE_EXPORT void findClientByVisibleIdentifier(ServiceWorkerIdentifier, const String& clientIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
 
+    virtual void openWindow(ServiceWorkerIdentifier, const String& urlString, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&&) = 0;
+
     const RegistrableDomain& registrableDomain() const { return m_registrableDomain; }
     std::optional<ScriptExecutionContextIdentifier> serviceWorkerPageIdentifier() const { return m_serviceWorkerPageIdentifier; }
 

Modified: trunk/Source/WebCore/workers/service/server/SWServerWorker.h (291978 => 291979)


--- trunk/Source/WebCore/workers/service/server/SWServerWorker.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebCore/workers/service/server/SWServerWorker.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -117,7 +117,7 @@
     const ServiceWorkerData& data() const { return m_data; }
     ServiceWorkerContextData contextData() const;
 
-    const ClientOrigin& origin() const;
+    WEBCORE_EXPORT const ClientOrigin& origin() const;
     const RegistrableDomain& registrableDomain() const { return m_registrableDomain; }
     WEBCORE_EXPORT std::optional<ScriptExecutionContextIdentifier> serviceWorkerPageIdentifier() const;
 

Modified: trunk/Source/WebKit/ChangeLog (291978 => 291979)


--- trunk/Source/WebKit/ChangeLog	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/ChangeLog	2022-03-28 18:14:19 UTC (rev 291979)
@@ -1,3 +1,79 @@
+2022-03-28  Brady Eidson  <beid...@apple.com>
+
+        Support ServiceWorkerClients.openWindow.
+        <rdar://90616651> and https://bugs.webkit.org/show_bug.cgi?id=238400
+
+        Reviewed by Youenn Fablet.
+        
+        This API asks the browser to asynchronously open a new tab to a URL then resolve
+        a promise with the new WindowClient representing that tab.
+            
+        From a WebCore/WebKit standpoint, implementing this was mostly straightforward.
+        1 - A plumbing exercise (thread hopping and IPC'ing the message and its reply around)
+        2 - Implmenting a new delegate method for the hosting app to create the requested WKWebView
+        
+        The delegate method was interesting. Normally this is the type of thing that'd go to the
+        WKUIDelegate but that requires there to be a WKWebView, and service workers can be running
+        without any web views.
+        
+        Fortunately we already had a WKWebsiteDataStore delegate SPI, and service workers *do* always
+        have an associated website data store they're running under.
+        
+        Once the app gives the new web view back to WebKit, we record its PageIdentifier in the reply.
+        
+        Once the reply makes its way all the way back to the ServiceWorker process and on the 
+        ServiceWorker thread, we do a client match and cross check with that PageIdentifier to make
+        sure we're resolving the promise with the correct WindowClient.
+        
+        If there's no matched clients, then the view is either already gone or it has navigated away
+        to a non-applicable URL.
+        
+        Same if there are matched clients, but they don't match the specified PageIdentifier.
+
+        A straight forward layouttest completes the task.
+        
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::controlClient):
+        
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+        (WebKit::WebSWServerToContextConnection::openWindow):
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
+        
+        * UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
+        (-[WKWebsiteDataStore set_delegate:]):
+        * UIProcess/API/Cocoa/_WKWebsiteDataStoreDelegate.h:
+        
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::openWindowFromServiceWorker):
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/Network/NetworkProcessProxy.messages.in:
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::didFailProvisionalLoadForFrameShared):
+        (WebKit::WebPageProxy::callLoadCompletionHandlersIfNecessary):
+        (WebKit::WebPageProxy::didFinishLoadForFrame):
+        (WebKit::WebPageProxy::didFailLoadForFrame):
+        (WebKit::WebPageProxy::resetState):
+        (WebKit::WebPageProxy::callServiceWorkerLaunchCompletionHandlerIfNecessary): Deleted.
+        * UIProcess/WebPageProxy.h:
+
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::openWindowFromServiceWorker):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+
+        * UIProcess/WebsiteData/WebsiteDataStoreClient.h:
+        (WebKit::WebsiteDataStoreClient::openWindowFromServiceWorker):
+
+        * WebProcess/Notifications/WebNotificationManager.cpp:
+        (WebKit::WebNotificationManager::show):
+        (WebKit::WebNotificationManager::didShowNotification):
+        (WebKit::WebNotificationManager::didClickNotification):
+
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::openWindow):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+
 2022-03-28  Youenn Fablet  <you...@apple.com>
 
         Share more code between RemoteRealtimeAudioSource and RemoteRealtimeVideoSource

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp (291978 => 291979)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -175,7 +175,7 @@
         unregisterServiceWorkerClient(clientIdentifier);
     });
 
-    ServiceWorkerClientData data { clientIdentifier, ServiceWorkerClientType::Window, ServiceWorkerClientFrameType::None, request.url(), request.isAppInitiated() ? WebCore::LastNavigationWasAppInitiated::Yes : WebCore::LastNavigationWasAppInitiated::No };
+    ServiceWorkerClientData data { clientIdentifier, ServiceWorkerClientType::Window, ServiceWorkerClientFrameType::None, request.url(), { }, request.isAppInitiated() ? WebCore::LastNavigationWasAppInitiated::Yes : WebCore::LastNavigationWasAppInitiated::No };
     registerServiceWorkerClient(SecurityOriginData { registration.key().topOrigin() }, WTFMove(data), registration.identifier(), request.httpUserAgent());
 }
 

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp (291978 => 291979)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -157,6 +157,27 @@
     m_connection.networkProcess().parentProcessConnection()->send(Messages::NetworkProcessProxy::TerminateUnresponsiveServiceWorkerProcesses { webProcessIdentifier() }, 0);
 }
 
+void WebSWServerToContextConnection::openWindow(WebCore::ServiceWorkerIdentifier identifier, const String& urlString, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&& callback)
+{
+    auto* server = this->server();
+    if (!server) {
+        callback(std::nullopt);
+        return;
+    }
+
+    auto* worker = server->workerByID(identifier);
+    if (!worker) {
+        callback(std::nullopt);
+        return;
+    }
+
+    auto innerCallback = [callback = WTFMove(callback)](std::optional<WebCore::PageIdentifier>&& pageIdentifier) mutable {
+        callback(WTFMove(pageIdentifier));
+    };
+
+    m_connection.networkProcess().parentProcessConnection()->sendWithAsyncReply(Messages::NetworkProcessProxy::OpenWindowFromServiceWorker { server->sessionID(), urlString, worker->origin().clientOrigin }, WTFMove(innerCallback));
+}
+
 void WebSWServerToContextConnection::matchAllCompleted(uint64_t requestIdentifier, const Vector<ServiceWorkerClientData>& clientsData)
 {
     send(Messages::WebSWContextManagerConnection::MatchAllCompleted { requestIdentifier, clientsData });

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h (291978 => 291979)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -102,6 +102,7 @@
 
     void connectionIsNoLongerNeeded() final;
     void terminateDueToUnresponsiveness() final;
+    void openWindow(WebCore::ServiceWorkerIdentifier, const String& urlString, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&&) final;
 
     void connectionClosed();
 

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in (291978 => 291979)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in	2022-03-28 18:14:19 UTC (rev 291979)
@@ -40,6 +40,8 @@
     PostMessageToServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier destination, struct WebCore::MessageWithMessagePorts message, WebCore::ServiceWorkerIdentifier source, String sourceOrigin)
     DidFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier identifier)
     SetAsInspected(WebCore::ServiceWorkerIdentifier identifier, bool isInspected)
+
+    OpenWindow(WebCore::ServiceWorkerIdentifier identifier, String urlString) -> (std::optional<WebCore::PageIdentifier> newClientData)
 }
 
 #endif // ENABLE(SERVICE_WORKER)

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm	2022-03-28 18:14:19 UTC (rev 291979)
@@ -48,6 +48,7 @@
 #import "_WKWebsiteDataStoreDelegate.h"
 #import <WebCore/Credential.h>
 #import <WebCore/RegistrationDatabase.h>
+#import <WebCore/ServiceWorkerClientData.h>
 #import <WebCore/WebCoreObjCExtras.h>
 #import <wtf/BlockPtr.h>
 #import <wtf/URL.h>
@@ -56,10 +57,12 @@
 
 class WebsiteDataStoreClient final : public WebKit::WebsiteDataStoreClient {
 public:
-    explicit WebsiteDataStoreClient(id <_WKWebsiteDataStoreDelegate> delegate)
-        : m_delegate(delegate)
+    explicit WebsiteDataStoreClient(WKWebsiteDataStore *dataStore, id<_WKWebsiteDataStoreDelegate> delegate)
+        : m_dataStore(dataStore)
+        , m_delegate(delegate)
         , m_hasRequestStorageSpaceSelector([m_delegate.get() respondsToSelector:@selector(requestStorageSpace: frameOrigin: quota: currentSize: spaceRequired: decisionHandler:)])
         , m_hasAuthenticationChallengeSelector([m_delegate.get() respondsToSelector:@selector(didReceiveAuthenticationChallenge: completionHandler:)])
+        , m_hasOpenWindowSelector([m_delegate.get() respondsToSelector:@selector(websiteDataStore:openWindow:fromServiceWorkerOrigin:completionHandler:)])
     {
     }
 
@@ -104,9 +107,32 @@
         [m_delegate.getAutoreleased() didReceiveAuthenticationChallenge:nsURLChallenge completionHandler:completionHandler.get()];
     }
 
+    void openWindowFromServiceWorker(const String& url, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(WebKit::WebPageProxy*)>&& callback)
+    {
+        if (!m_hasOpenWindowSelector || !m_delegate || !m_dataStore) {
+            callback({ });
+            return;
+        }
+
+        auto checker = WebKit::CompletionHandlerCallChecker::create(m_delegate.getAutoreleased(), @selector(websiteDataStore:openWindow:fromServiceWorkerOrigin:completionHandler:));
+        auto completionHandler = makeBlockPtr([callback = WTFMove(callback), checker = WTFMove(checker)](WKWebView *newWebView) mutable {
+            if (checker->completionHandlerHasBeenCalled())
+                return;
+            checker->didCallCompletionHandler();
+
+            callback(newWebView._page.get());
+        });
+
+        auto nsURL = (NSURL *)URL { url };
+        auto apiOrigin = API::SecurityOrigin::create(serviceWorkerOrigin);
+        [m_delegate.getAutoreleased() websiteDataStore:m_dataStore.getAutoreleased() openWindow:nsURL fromServiceWorkerOrigin:wrapper(apiOrigin.get()) completionHandler:completionHandler.get()];
+    }
+
+    WeakObjCPtr<WKWebsiteDataStore> m_dataStore;
     WeakObjCPtr<id <_WKWebsiteDataStoreDelegate> > m_delegate;
     bool m_hasRequestStorageSpaceSelector { false };
     bool m_hasAuthenticationChallengeSelector { false };
+    bool m_hasOpenWindowSelector { false };
 };
 
 @implementation WKWebsiteDataStore
@@ -652,7 +678,7 @@
 - (void)set_delegate:(id <_WKWebsiteDataStoreDelegate>)delegate
 {
     _delegate = delegate;
-    _websiteDataStore->setClient(makeUniqueRef<WebsiteDataStoreClient>(delegate));
+    _websiteDataStore->setClient(makeUniqueRef<WebsiteDataStoreClient>(self, delegate));
 }
 
 - (_WKWebsiteDataStoreConfiguration *)_configuration

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreDelegate.h (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreDelegate.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreDelegate.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -26,6 +26,10 @@
 #import <Foundation/Foundation.h>
 #import <WebKit/WKFoundation.h>
 
+@class WKSecurityOrigin;
+@class WKWebsiteDataStore;
+@class WKWebView;
+
 WK_API_AVAILABLE(macos(10.15), ios(13.0))
 @protocol _WKWebsiteDataStoreDelegate <NSObject>
 
@@ -32,5 +36,5 @@
 @optional
 - (void)requestStorageSpace:(NSURL *)mainFrameURL frameOrigin:(NSURL *)frameURL quota:(NSUInteger)quota currentSize:(NSUInteger)currentSize spaceRequired:(NSUInteger)spaceRequired decisionHandler:(void (^)(unsigned long long quota))decisionHandler;
 - (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
-
+- (void)websiteDataStore:(WKWebsiteDataStore *)dataStore openWindow:(NSURL *)url fromServiceWorkerOrigin:(WKSecurityOrigin *)serviceWorkerOrigin completionHandler:(void (^)(WKWebView *newWebView))completionHandler;
 @end

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -1798,6 +1798,16 @@
     send(Messages::NetworkProcess::TerminateRemoteWorkerContextConnectionWhenPossible(workerType, sessionID, registrableDomain, processIdentifier), 0);
 }
 
+void NetworkProcessProxy::openWindowFromServiceWorker(PAL::SessionID sessionID, const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&& callback)
+{
+    if (auto* store = websiteDataStoreFromSessionID(sessionID)) {
+        store->openWindowFromServiceWorker(urlString, serviceWorkerOrigin, WTFMove(callback));
+        return;
+    }
+
+    callback(std::nullopt);
+}
+
 } // namespace WebKit
 
 #undef MESSAGE_CHECK

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -299,6 +299,8 @@
 
     void terminateRemoteWorkerContextConnectionWhenPossible(RemoteWorkerType, PAL::SessionID, const WebCore::RegistrableDomain&, WebCore::ProcessIdentifier);
 
+    void openWindowFromServiceWorker(PAL::SessionID, const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&&);
+
 private:
     explicit NetworkProcessProxy();
 

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in	2022-03-28 18:14:19 UTC (rev 291979)
@@ -91,6 +91,8 @@
     DataTaskDidReceiveResponse(WebKit::DataTaskIdentifier identifier, WebCore::ResourceResponse response) -> (bool allowed)
     DataTaskDidReceiveData(WebKit::DataTaskIdentifier identifier, IPC::DataReference data)
     DataTaskDidCompleteWithError(WebKit::DataTaskIdentifier identifier, WebCore::ResourceError error)
+
+    OpenWindowFromServiceWorker(PAL::SessionID sessionID, String urlString, struct WebCore::SecurityOriginData serviceWorkerOrigin) -> (std::optional<WebCore::PageIdentifier> newPage)
 }
 
 }

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -4870,7 +4870,7 @@
         if (navigation)
             navigation->setClientNavigationActivity(nullptr);
 
-        callServiceWorkerLaunchCompletionHandlerIfNecessary();
+        callLoadCompletionHandlersIfNecessary(false);
     }
 
     frame.didFailProvisionalLoad();
@@ -4905,12 +4905,15 @@
 }
 #endif
 
-void WebPageProxy::callServiceWorkerLaunchCompletionHandlerIfNecessary()
+void WebPageProxy::callLoadCompletionHandlersIfNecessary(bool success)
 {
 #if ENABLE(SERVICE_WORKER)
-    if (m_isServiceWorkerPage && m_serviceWorkerLaunchCompletionHandler)
+    if (m_isServiceWorkerPage && m_serviceWorkerLaunchCompletionHandler && !success)
         m_serviceWorkerLaunchCompletionHandler(false);
 #endif
+
+    if (m_serviceWorkerOpenWindowCompletionCallback)
+        m_serviceWorkerOpenWindowCompletionCallback(success);
 }
 
 #if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
@@ -5134,6 +5137,8 @@
         resetRecentCrashCountSoon();
 
         notifyProcessPoolToPrewarm();
+
+        callLoadCompletionHandlersIfNecessary(true);
     }
 
     m_isLoadingAlternateHTMLStringForFailingProvisionalLoad = false;
@@ -5182,7 +5187,7 @@
         if (navigation)
             navigation->setClientNavigationActivity(nullptr);
 
-        callServiceWorkerLaunchCompletionHandlerIfNecessary();
+        callLoadCompletionHandlersIfNecessary(false);
     }
 }
 
@@ -8018,7 +8023,7 @@
 #endif
 
     if (resetStateReason != ResetStateReason::NavigationSwap)
-        callServiceWorkerLaunchCompletionHandlerIfNecessary();
+        callLoadCompletionHandlersIfNecessary(false);
 
     if (m_openPanelResultListener) {
         m_openPanelResultListener->invalidate();

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -2081,6 +2081,14 @@
 
     void interactableRegionsInRootViewCoordinates(WebCore::FloatRect, CompletionHandler<void(Vector<WebCore::FloatRect>)>&&);
 
+#if ENABLE(SERVICE_WORKER)
+    void setServiceWorkerOpenWindowCompletionCallback(CompletionHandler<void(bool)>&& completionCallback)
+    {
+        ASSERT(!m_serviceWorkerOpenWindowCompletionCallback);
+        m_serviceWorkerOpenWindowCompletionCallback = WTFMove(completionCallback);
+    }
+#endif
+
 private:
     WebPageProxy(PageClient&, WebProcessProxy&, Ref<API::PageConfiguration>&&);
     void platformInitialize();
@@ -2607,7 +2615,7 @@
 #if ENABLE(SERVICE_WORKER)
     void didFinishServiceWorkerPageRegistration(bool success);
 #endif
-    void callServiceWorkerLaunchCompletionHandlerIfNecessary();
+    void callLoadCompletionHandlersIfNecessary(bool success);
 
 #if PLATFORM(IOS_FAMILY)
     static bool isInHardwareKeyboardMode();
@@ -3119,6 +3127,7 @@
 #if ENABLE(SERVICE_WORKER)
     bool m_isServiceWorkerPage { false };
     CompletionHandler<void(bool)> m_serviceWorkerLaunchCompletionHandler;
+    CompletionHandler<void(bool)> m_serviceWorkerOpenWindowCompletionCallback;
 #endif
 
     RunLoop::Timer<WebPageProxy> m_tryCloseTimeoutTimer;

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -2087,4 +2087,34 @@
     WebNotificationManagerProxy::sharedServiceWorkerManager().didDestroyNotification(nullptr, notificationID);
 }
 
+void WebsiteDataStore::openWindowFromServiceWorker(const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(std::optional<WebCore::PageIdentifier>)>&& callback)
+{
+    auto innerCallback = [callback = WTFMove(callback)] (WebPageProxy* newPage) mutable {
+        if (!newPage) {
+            callback(std::nullopt);
+            return;
+        }
+
+        if (!newPage->pageLoadState().isLoading()) {
+            RELEASE_LOG(Loading, "The WKWebView provided in response to a ServiceWorker openWindow request was not in the loading state");
+            callback(std::nullopt);
+            return;
+        }
+
+        auto innerCallback = [pageID = newPage->identifier(), callback = WTFMove(callback)] (bool success) mutable {
+            auto* newPage = WebProcessProxy::webPage(pageID);
+            if (!newPage || !success) {
+                callback(std::nullopt);
+                return;
+            }
+
+            callback(newPage->webPageID());
+        };
+
+        newPage->setServiceWorkerOpenWindowCompletionCallback(WTFMove(innerCallback));
+    };
+
+    m_client->openWindowFromServiceWorker(urlString, serviceWorkerOrigin, WTFMove(innerCallback));
 }
+
+}

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -379,6 +379,8 @@
     void clearServiceWorkerNotification(const UUID& notificationID);
     void didDestroyServiceWorkerNotification(const UUID& notificationID);
 
+    void openWindowFromServiceWorker(const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(std::optional<WebCore::PageIdentifier>)>&&);
+
 private:
     enum class ForceReinitialization : bool { No, Yes };
 #if ENABLE(APP_BOUND_DOMAINS)

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h (291978 => 291979)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreClient.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -36,6 +36,8 @@
 
 namespace WebKit {
 
+class WebPageProxy;
+
 class WebsiteDataStoreClient {
     WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -50,6 +52,11 @@
     {
         challenge->listener().completeChallenge(AuthenticationChallengeDisposition::PerformDefaultHandling);
     }
+
+    virtual void openWindowFromServiceWorker(const String&, const WebCore::SecurityOriginData&, CompletionHandler<void(WebPageProxy*)>&& completionHandler)
+    {
+        completionHandler(nullptr);
+    }
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/Notifications/WebNotificationManager.cpp (291978 => 291979)


--- trunk/Source/WebKit/WebProcess/Notifications/WebNotificationManager.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/WebProcess/Notifications/WebNotificationManager.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -150,6 +150,8 @@
 
 bool WebNotificationManager::show(Notification& notification, WebPage* page)
 {
+    LOG(Notifications, "WebProcess %i going to show notification %s", getpid(), notification.identifier().toString().utf8().data());
+
     ASSERT(isMainRunLoop());
 #if ENABLE(NOTIFICATIONS)
     if (page && !page->corePage()->settings().notificationsEnabled())
@@ -208,6 +210,8 @@
 {
     ASSERT(isMainRunLoop());
 
+    LOG(Notifications, "WebProcess %i DID SHOW notification %s", getpid(), notificationID.toString().utf8().data());
+
 #if ENABLE(NOTIFICATIONS)
     RefPtr<Notification> notification = m_notificationIDMap.get(notificationID);
     if (!notification)
@@ -223,11 +227,15 @@
 {
     ASSERT(isMainRunLoop());
 
+    LOG(Notifications, "WebProcess %i DID CLICK notification %s", getpid(), notificationID.toString().utf8().data());
+
 #if ENABLE(NOTIFICATIONS)
     RefPtr<Notification> notification = m_notificationIDMap.get(notificationID);
     if (!notification)
         return;
 
+    LOG(Notifications, "WebProcess %i handling click event for notification %s", getpid(), notificationID.toString().utf8().data());
+
     // Indicate that this event is being dispatched in reaction to a user's interaction with a platform notification.
     UserGestureIndicator indicator(ProcessingUserGesture);
     notification->dispatchClickEvent();

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp (291978 => 291979)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -332,6 +332,11 @@
         callback(WTFMove(clientsData));
 }
 
+void WebSWContextManagerConnection::openWindow(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, const String& url, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&& callback)
+{
+    m_connectionToNetworkProcess->sendWithAsyncReply(Messages::WebSWServerToContextConnection::OpenWindow { serviceWorkerIdentifier, url }, WTFMove(callback));
+}
+
 void WebSWContextManagerConnection::claim(ServiceWorkerIdentifier serviceWorkerIdentifier, CompletionHandler<void(ExceptionOr<void>&&)>&& callback)
 {
     m_connectionToNetworkProcess->sendWithAsyncReply(Messages::WebSWServerToContextConnection::Claim { serviceWorkerIdentifier }, [callback = WTFMove(callback)](auto&& result) mutable {

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h (291978 => 291979)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -87,6 +87,7 @@
     bool isThrottleable() const final;
     void didFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier) final;
     void setAsInspected(WebCore::ServiceWorkerIdentifier, bool) final;
+    void openWindow(WebCore::ServiceWorkerIdentifier, const String& url, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&&) final;
 
     // IPC messages.
     void serviceWorkerStarted(std::optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, bool doesHandleFetch) final;

Modified: trunk/Tools/ChangeLog (291978 => 291979)


--- trunk/Tools/ChangeLog	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Tools/ChangeLog	2022-03-28 18:14:19 UTC (rev 291979)
@@ -1,3 +1,22 @@
+2022-03-28  Brady Eidson  <beid...@apple.com>
+
+        Support ServiceWorkerClients.openWindow.
+        <rdar://90616651> and https://bugs.webkit.org/show_bug.cgi?id=238400
+
+        Reviewed by Youenn Fablet.
+
+        Implement the new delegate to create the new view.
+        
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::createOtherPage):
+        (WTR::TestController::createOtherPlatformWebView):
+        * WebKitTestRunner/TestController.h:
+        
+        * WebKitTestRunner/cocoa/TestControllerCocoa.mm:
+        (WTR::TestController::platformCreateOtherPage):
+        * WebKitTestRunner/cocoa/TestWebsiteDataStoreDelegate.mm:
+        (-[TestWebsiteDataStoreDelegate websiteDataStore:openWindow:fromServiceWorkerOrigin:completionHandler:]):
+
 2022-03-28  Noam Rosenthal  <n...@webkit.org>
 
         Issues with WPT exporter

Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (291978 => 291979)


--- trunk/Tools/WebKitTestRunner/TestController.cpp	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp	2022-03-28 18:14:19 UTC (rev 291979)
@@ -381,6 +381,17 @@
 
 WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures)
 {
+    auto* platformWebView = createOtherPlatformWebView(parentView, configuration, navigationAction, windowFeatures);
+    if (!platformWebView)
+        return nullptr;
+
+    auto* page = platformWebView->page();
+    WKRetain(page);
+    return page;
+}
+
+PlatformWebView* TestController::createOtherPlatformWebView(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef, WKWindowFeaturesRef)
+{
     m_currentInvocation->willCreateNewPage();
 
     // The test called testRunner.preventPopupWindows() to prevent opening new windows.
@@ -389,7 +400,8 @@
 
     m_createdOtherPage = true;
 
-    auto view = platformCreateOtherPage(parentView, configuration, parentView->options());
+    auto options = parentView ? parentView->options() : m_mainWebView->options();
+    auto view = platformCreateOtherPage(parentView, configuration, options);
     WKPageRef newPage = view->page();
 
     view->resizeTo(800, 600);
@@ -509,9 +521,9 @@
 
     TestController::singleton().updateWindowScaleForTest(view.ptr(), *TestController::singleton().m_currentInvocation);
 
+    PlatformWebView* viewToReturn = view.ptr();
     m_auxiliaryWebViews.append(WTFMove(view));
-    WKRetain(newPage);
-    return newPage;
+    return viewToReturn;
 }
 
 const char* TestController::libraryPathForTesting()

Modified: trunk/Tools/WebKitTestRunner/TestController.h (291978 => 291979)


--- trunk/Tools/WebKitTestRunner/TestController.h	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Tools/WebKitTestRunner/TestController.h	2022-03-28 18:14:19 UTC (rev 291979)
@@ -378,6 +378,8 @@
     bool denyNotificationPermission(WKStringRef origin);
     bool denyNotificationPermissionOnPrompt(WKStringRef origin);
 
+    PlatformWebView* createOtherPlatformWebView(PlatformWebView* parentView, WKPageConfigurationRef, WKNavigationActionRef, WKWindowFeaturesRef);
+
 private:
     WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(const TestOptions&);
     WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration(const TestOptions&) const;

Modified: trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm (291978 => 291979)


--- trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm	2022-03-28 18:14:19 UTC (rev 291979)
@@ -214,7 +214,8 @@
 UniqueRef<PlatformWebView> TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef, const TestOptions& options)
 {
     auto newConfiguration = adoptNS([globalWebViewConfiguration() copy]);
-    [newConfiguration _setRelatedWebView:static_cast<WKWebView*>(parentView->platformView())];
+    if (parentView)
+        [newConfiguration _setRelatedWebView:static_cast<WKWebView*>(parentView->platformView())];
     if ([newConfiguration _relatedWebView])
         [newConfiguration setWebsiteDataStore:[newConfiguration _relatedWebView].configuration.websiteDataStore];
     auto view = makeUniqueRef<PlatformWebView>(newConfiguration.get(), options);

Modified: trunk/Tools/WebKitTestRunner/cocoa/TestWebsiteDataStoreDelegate.mm (291978 => 291979)


--- trunk/Tools/WebKitTestRunner/cocoa/TestWebsiteDataStoreDelegate.mm	2022-03-28 17:58:11 UTC (rev 291978)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestWebsiteDataStoreDelegate.mm	2022-03-28 18:14:19 UTC (rev 291979)
@@ -26,6 +26,12 @@
 #import "config.h"
 #import "TestWebsiteDataStoreDelegate.h"
 
+#import "PlatformWebView.h"
+#import "TestController.h"
+#import "TestRunnerWKWebView.h"
+#import <WebKit/WKWebView.h>
+#import <wtf/UniqueRef.h>
+
 @implementation TestWebsiteDataStoreDelegate { }
 - (instancetype)init
 {
@@ -61,4 +67,15 @@
     _shouldAllowAnySSLCertificate = shouldAllowAnySSLCertificate;
 }
 
+- (void)websiteDataStore:(WKWebsiteDataStore *)dataStore openWindow:(NSURL *)url fromServiceWorkerOrigin:(WKSecurityOrigin *)serviceWorkerOrigin completionHandler:(void (^)(WKWebView *newWebView))completionHandler
+{
+    auto* newView = WTR::TestController::singleton().createOtherPlatformWebView(nullptr, nullptr, nullptr, nullptr);
+    WKWebView *webView = newView->platformView();
+
+    ASSERT(webView.configuration.websiteDataStore == dataStore);
+
+    [webView loadRequest:[NSURLRequest requestWithURL:url]];
+    completionHandler(webView);
+}
+
 @end
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to