Title: [292459] trunk
Revision
292459
Author
you...@apple.com
Date
2022-04-06 04:17:25 -0700 (Wed, 06 Apr 2022)

Log Message

Implement ServiceWorkerWindowClient.navigate
https://bugs.webkit.org/show_bug.cgi?id=238738

Reviewed by Chris Dumez.

LayoutTests/imported/w3c:

* web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt:

Source/WebCore:

Introduce a routine to navigate a Document.
Send FrameIdentifier as part of ServiceWorkerClientData.
Implement ServiceWorkerWindowClient::navigate by sending IPC to network process.

Test: http/wpt/service-workers/navigate-iframes-window-client.https.html

* dom/Document.cpp:
* dom/Document.h:
* loader/FrameLoader.cpp:
* workers/service/ServiceWorkerClientData.cpp:
* workers/service/ServiceWorkerClientData.h:
* workers/service/ServiceWorkerWindowClient.cpp:
* workers/service/context/SWContextManager.h:
* workers/service/server/SWServer.cpp:
* workers/service/server/SWServer.h:
* workers/service/server/SWServerWorker.cpp:
* workers/service/server/SWServerWorker.h:

Source/WebKit:

When receiving a request to navigate a client, send message to UIProcess.
UIProcess locates the WebFrameProxy which will send an IPC message to the WebProcess to do navigation.
We keep track of the navigation within WebFrameProxy.
In case of process swapping, we make sure to transfer the WebFrameProxy callback.
In case of policy decision to stop loads, we resolve the promise with an empty client.
A follow-up patch may reject the promise once the expected behavior will be clarified.

* NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
* NetworkProcess/ServiceWorker/WebSWServerConnection.h:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
* UIProcess/Network/NetworkProcessProxy.cpp:
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/Network/NetworkProcessProxy.messages.in:
* UIProcess/ProvisionalPageProxy.cpp:
* UIProcess/WebFrameProxy.cpp:
* UIProcess/WebFrameProxy.h:
* UIProcess/WebPageProxy.cpp:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
* WebProcess/WebPage/WebPage.cpp:
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:

LayoutTests:

* http/wpt/service-workers/navigate-iframes-window-client.https-expected.txt: Added.
* http/wpt/service-workers/navigate-iframes-window-client.https.html: Added.
* http/wpt/service-workers/navigate-window-client-worker.js: Added.
* platform/glib/TestExpectations:
* platform/glib/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt: Removed.
* platform/mac-wk2/TestExpectations:

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (292458 => 292459)


--- trunk/LayoutTests/ChangeLog	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/LayoutTests/ChangeLog	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1,3 +1,17 @@
+2022-04-06  Youenn Fablet  <you...@apple.com>
+
+        Implement ServiceWorkerWindowClient.navigate
+        https://bugs.webkit.org/show_bug.cgi?id=238738
+
+        Reviewed by Chris Dumez.
+
+        * http/wpt/service-workers/navigate-iframes-window-client.https-expected.txt: Added.
+        * http/wpt/service-workers/navigate-iframes-window-client.https.html: Added.
+        * http/wpt/service-workers/navigate-window-client-worker.js: Added.
+        * platform/glib/TestExpectations:
+        * platform/glib/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt: Removed.
+        * platform/mac-wk2/TestExpectations:
+
 2022-04-05  Ada Chan  <adac...@apple.com>
 
         [WebXR] Add a new enum type to represent session features

Added: trunk/LayoutTests/http/wpt/service-workers/navigate-iframes-window-client.https-expected.txt (0 => 292459)


--- trunk/LayoutTests/http/wpt/service-workers/navigate-iframes-window-client.https-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/service-workers/navigate-iframes-window-client.https-expected.txt	2022-04-06 11:17:25 UTC (rev 292459)
@@ -0,0 +1,8 @@
+
+
+PASS Setup activating worker
+PASS Navigate to same origin
+PASS Navigate to same URL
+PASS Navigate to same document fragment
+PASS Navigate to cross origin
+

Added: trunk/LayoutTests/http/wpt/service-workers/navigate-iframes-window-client.https.html (0 => 292459)


--- trunk/LayoutTests/http/wpt/service-workers/navigate-iframes-window-client.https.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/service-workers/navigate-iframes-window-client.https.html	2022-04-06 11:17:25 UTC (rev 292459)
@@ -0,0 +1,82 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</head>
+<body>
+<script>
+var url = "" URL("/WebKit/service-workers/resources/", location);
+url = ""
+var activeWorker;
+
+promise_test(async (test) => {
+    let registration = await navigator.serviceWorker.register("/WebKit/service-workers/navigate-window-client-worker.js", { scope : url });
+    if (!registration.installing) {
+        registration.unregister();
+        registration = await navigator.serviceWorker.register("/WebKit/service-workers/navigate-window-client-worker.js", { scope : url });
+    }
+    activeWorker = registration.installing;
+    await waitForState(activeWorker, "activating");
+    activeWorker = registration.active;
+}, "Setup activating worker");
+
+promise_test(async (test) => {
+    const iframe = await withIframe(url);
+
+    activeWorker.postMessage({navigate: url + "?test"});
+    const result = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+    assert_true(result.url.includes("?test"));
+
+    iframe.remove();
+}, "Navigate to same origin");
+
+promise_test(async (test) => {
+    const iframe = await withIframe(url);
+
+    activeWorker.postMessage("get-client");
+    const client1 = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+
+    activeWorker.postMessage({navigate: url});
+    const result = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+    assert_equals(result.url, url);
+
+    activeWorker.postMessage("get-client");
+    const client2 = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+
+    assert_not_equals(client1.id, client2.id);
+
+    iframe.remove();
+}, "Navigate to same URL");
+
+promise_test(async (test) => {
+    const iframe = await withIframe(url);
+
+    activeWorker.postMessage("get-client");
+    const client1 = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+
+    activeWorker.postMessage({navigate: url + "#test"});
+    const result = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+    assert_equals(result.url, url);
+
+    activeWorker.postMessage("get-client");
+    const client2 = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+
+    assert_equals(client1.id, client2.id);
+
+    iframe.remove();
+}, "Navigate to same document fragment");
+
+promise_test(async (test) => {
+    const iframe = await withIframe(url);
+
+    activeWorker.postMessage({navigate: "https://127.0.0.1:9443" });
+    const result = await new Promise(resolve => navigator.serviceWorker._onmessage_ = (event) => resolve(event.data));
+    assert_equals(result, "none");
+}, "Navigate to cross origin");
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/http/wpt/service-workers/navigate-window-client-worker.js (0 => 292459)


--- trunk/LayoutTests/http/wpt/service-workers/navigate-window-client-worker.js	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/service-workers/navigate-window-client-worker.js	2022-04-06 11:17:25 UTC (rev 292459)
@@ -0,0 +1,25 @@
+_onmessage_ = async (event) => {
+    if (event.data ="" "get-client") {
+        const clients = await self.clients.matchAll();
+        if (!clients.length) {
+            event.source.postMessage("no client");
+            return;
+        }
+        event.source.postMessage({ id: clients[0].id, url: clients[0].url });
+        return;
+    }
+
+    if (event.data && event.data.navigate) {
+        const clients = await self.clients.matchAll();
+        if (!clients.length) {
+            event.source.postMessage("failed, no clients");
+            return;
+        }
+        await clients[0].navigate(event.data.navigate).then((client) => {
+            event.source.postMessage(client ? { id: client.id, url: client.url } : "none");
+        }, (e) => {
+            event.source.postMessage("failed");
+        });
+        return;
+    }
+};

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (292458 => 292459)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1,3 +1,12 @@
+2022-04-06  Youenn Fablet  <you...@apple.com>
+
+        Implement ServiceWorkerWindowClient.navigate
+        https://bugs.webkit.org/show_bug.cgi?id=238738
+
+        Reviewed by Chris Dumez.
+
+        * web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt:
+
 2022-04-05  Matt Woodrow  <mattwood...@apple.com>
 
         Support transitions/animations on grid-template-columns|rows

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt (292458 => 292459)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt	2022-04-06 11:17:25 UTC (rev 292459)
@@ -10,29 +10,15 @@
 PASS invalid url (file:///)
 PASS invalid url (about:blank)
 PASS navigate on a top-level window client
-FAIL normal worker side promise_test: Unhandled rejection with value: object "NotSupportedError: windowClient.navigate() is not yet supported"
-FAIL blank url worker side promise_test: Unhandled rejection with value: object "NotSupportedError: windowClient.navigate() is not yet supported"
-FAIL in scope but not controlled test on installing worker worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL in scope but not controlled test on active worker worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL out of scope worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL cross orgin url worker side promise_test: Unhandled rejection with value: object "NotSupportedError: windowClient.navigate() is not yet supported"
-FAIL invalid url (http://[example.com]) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL invalid url (view-source://example.com) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL invalid url (file:///) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL invalid url (about:blank) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
+PASS normal worker side
+PASS blank url worker side
+PASS in scope but not controlled test on installing worker worker side
+PASS in scope but not controlled test on active worker worker side
+PASS out of scope worker side
+PASS cross orgin url worker side
+PASS invalid url (http://[example.com]) worker side
+PASS invalid url (view-source://example.com) worker side
+FAIL invalid url (file:///) worker side assert_equals: expected (string) "TypeError" but got (object) null
+PASS invalid url (about:blank) worker side
 FAIL navigate on a top-level window client worker side assert_equals: expected "top-level" but got "auxiliary"
 

Modified: trunk/LayoutTests/platform/glib/TestExpectations (292458 => 292459)


--- trunk/LayoutTests/platform/glib/TestExpectations	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/LayoutTests/platform/glib/TestExpectations	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1194,6 +1194,9 @@
 webkit.org/b/201981 http/wpt/service-workers/server-trust-evaluation.https.html [ Failure ]
 webkit.org/b/201981 imported/w3c/web-platform-tests/service-workers/service-worker/fetch-canvas-tainting-image-cache.https.html [ Failure Pass ]
 
+webkit.org/b/238738 http/wpt/service-workers/navigate-iframes-window-client.https.html [ Failure ]
+webkit.org/b/238738238738 imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https.html [ Failure ]
+
 webkit.org/b/208369 http/tests/workers/service/basic-timeout.https.html [ Failure Timeout Pass ]
 
 webkit.org/b/215638 http/tests/websocket/tests/hybi/invalid-continuation.html [ Failure ]

Deleted: trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt (292458 => 292459)


--- trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https-expected.txt	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1,38 +0,0 @@
-
-PASS normal
-PASS blank url
-PASS in scope but not controlled test on installing worker
-PASS in scope but not controlled test on active worker
-PASS out of scope
-PASS cross orgin url
-PASS invalid url (http://[example.com])
-PASS invalid url (view-source://example.com)
-PASS invalid url (file:///)
-PASS invalid url (about:blank)
-PASS navigate on a top-level window client
-FAIL normal worker side promise_test: Unhandled rejection with value: object "NotSupportedError: windowClient.navigate() is not yet supported"
-FAIL blank url worker side promise_test: Unhandled rejection with value: object "NotSupportedError: windowClient.navigate() is not yet supported"
-FAIL in scope but not controlled test on installing worker worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL in scope but not controlled test on active worker worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL out of scope worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL cross orgin url worker side promise_test: Unhandled rejection with value: object "NotSupportedError: windowClient.navigate() is not yet supported"
-FAIL invalid url (http://[example.com]) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL invalid url (view-source://example.com) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL invalid url (file:///) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL invalid url (about:blank) worker side promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: windowClient.navigate() is not yet supported" ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL navigate on a top-level window client worker side assert_equals: expected "top-level" but got "auxiliary"
-

Modified: trunk/LayoutTests/platform/mac-wk2/TestExpectations (292458 => 292459)


--- trunk/LayoutTests/platform/mac-wk2/TestExpectations	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/LayoutTests/platform/mac-wk2/TestExpectations	2022-04-06 11:17:25 UTC (rev 292459)
@@ -924,7 +924,7 @@
 
 webkit.org/b/230412 imported/w3c/web-platform-tests/service-workers/service-worker/clients-matchall-client-types.https.html [ Pass Failure ]
 
-webkit.org/b/179352 imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https.html [ Pass Failure Slow ]
+webkit.org/b/179352 imported/w3c/web-platform-tests/service-workers/service-worker/windowclient-navigate.https.html [ Slow ]
 
 webkit.org/b/183164 fast/dom/Window/window-focus-self.html [ Pass Failure ]
 

Modified: trunk/Source/WebCore/ChangeLog (292458 => 292459)


--- trunk/Source/WebCore/ChangeLog	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/ChangeLog	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1,5 +1,30 @@
 2022-04-06  Youenn Fablet  <you...@apple.com>
 
+        Implement ServiceWorkerWindowClient.navigate
+        https://bugs.webkit.org/show_bug.cgi?id=238738
+
+        Reviewed by Chris Dumez.
+
+        Introduce a routine to navigate a Document.
+        Send FrameIdentifier as part of ServiceWorkerClientData.
+        Implement ServiceWorkerWindowClient::navigate by sending IPC to network process.
+
+        Test: http/wpt/service-workers/navigate-iframes-window-client.https.html
+
+        * dom/Document.cpp:
+        * dom/Document.h:
+        * loader/FrameLoader.cpp:
+        * workers/service/ServiceWorkerClientData.cpp:
+        * workers/service/ServiceWorkerClientData.h:
+        * workers/service/ServiceWorkerWindowClient.cpp:
+        * workers/service/context/SWContextManager.h:
+        * workers/service/server/SWServer.cpp:
+        * workers/service/server/SWServer.h:
+        * workers/service/server/SWServerWorker.cpp:
+        * workers/service/server/SWServerWorker.h:
+
+2022-04-06  Youenn Fablet  <you...@apple.com>
+
         ServiceWorkerClients.openWindow should not need to get all clients asynchronously to resolve its promise
         https://bugs.webkit.org/show_bug.cgi?id=238503
 

Modified: trunk/Source/WebCore/dom/Document.cpp (292458 => 292459)


--- trunk/Source/WebCore/dom/Document.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/dom/Document.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -8414,7 +8414,7 @@
 
 std::optional<FrameIdentifier> Document::frameID() const
 {
-    return m_frame->loader().frameID();
+    return m_frame ? m_frame->loader().frameID() : std::nullopt;
 }
 
 void Document::registerArticleElement(Element& article)
@@ -8780,6 +8780,24 @@
     auto controllingServiceWorkerRegistrationIdentifier = activeServiceWorker() ? std::make_optional<ServiceWorkerRegistrationIdentifier>(activeServiceWorker()->registrationIdentifier()) : std::nullopt;
     m_serviceWorkerConnection->registerServiceWorkerClient(topOrigin(), ServiceWorkerClientData::from(*this), controllingServiceWorkerRegistrationIdentifier, userAgent(url()));
 }
+
+void Document::navigateFromServiceWorker(const URL& url, CompletionHandler<void(bool)>&& callback)
+{
+    if (activeDOMObjectsAreSuspended() || activeDOMObjectsAreStopped()) {
+        callback(false);
+        return;
+    }
+    eventLoop().queueTask(TaskSource::DOMManipulation, [weakThis = WeakPtr { *this }, url, callback = WTFMove(callback)]() mutable {
+        auto* frame = weakThis ? weakThis->frame() : nullptr;
+        if (!frame) {
+            callback(false);
+            return;
+        }
+        frame->navigationScheduler().scheduleLocationChange(*weakThis, weakThis->securityOrigin(), url, frame->loader().outgoingReferrer(), LockHistory::Yes, LockBackForwardList::No, [callback = WTFMove(callback)]() mutable {
+            callback(true);
+        });
+    });
+}
 #endif
 
 String Document::signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String& challengeString, const URL& url)

Modified: trunk/Source/WebCore/dom/Document.h (292458 => 292459)


--- trunk/Source/WebCore/dom/Document.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/dom/Document.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1569,6 +1569,7 @@
 #if ENABLE(SERVICE_WORKER)
     void setServiceWorkerConnection(SWClientConnection*);
     void updateServiceWorkerClientData();
+    WEBCORE_EXPORT void navigateFromServiceWorker(const URL&, CompletionHandler<void(bool)>&&);
 #endif
 
 #if ENABLE(VIDEO)

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerClientData.cpp (292458 => 292459)


--- trunk/Source/WebCore/workers/service/ServiceWorkerClientData.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerClientData.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -59,12 +59,12 @@
 
 ServiceWorkerClientData ServiceWorkerClientData::isolatedCopy() const &
 {
-    return { identifier, type, frameType, url.isolatedCopy(), pageIdentifier, lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder, crossThreadCopy(ancestorOrigins) };
+    return { identifier, type, frameType, url.isolatedCopy(), pageIdentifier, frameIdentifier, lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder, crossThreadCopy(ancestorOrigins) };
 }
 
 ServiceWorkerClientData ServiceWorkerClientData::isolatedCopy() &&
 {
-    return { identifier, type, frameType, WTFMove(url).isolatedCopy(), pageIdentifier, lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder, crossThreadCopy(WTFMove(ancestorOrigins)) };
+    return { identifier, type, frameType, WTFMove(url).isolatedCopy(), pageIdentifier, frameIdentifier, lastNavigationWasAppInitiated, isVisible, isFocused, focusOrder, crossThreadCopy(WTFMove(ancestorOrigins)) };
 }
 
 ServiceWorkerClientData ServiceWorkerClientData::from(ScriptExecutionContext& context)
@@ -87,6 +87,7 @@
         toServiceWorkerClientFrameType(context),
         document.creationURL(),
         document.pageID(),
+        document.frameID(),
         lastNavigationWasAppInitiated,
         !document.hidden(),
         document.hasFocus(),

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h (292458 => 292459)


--- trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -27,6 +27,7 @@
 
 #if ENABLE(SERVICE_WORKER)
 
+#include "FrameIdentifier.h"
 #include "PageIdentifier.h"
 #include "ProcessQualified.h"
 #include "ScriptExecutionContextIdentifier.h"
@@ -49,6 +50,7 @@
     ServiceWorkerClientFrameType frameType;
     URL url;
     std::optional<PageIdentifier> pageIdentifier;
+    std::optional<FrameIdentifier> frameIdentifier;
     LastNavigationWasAppInitiated lastNavigationWasAppInitiated;
     bool isVisible { false };
     bool isFocused { false };
@@ -67,7 +69,7 @@
 template<class Encoder>
 void ServiceWorkerClientData::encode(Encoder& encoder) const
 {
-    encoder << identifier << type << frameType << url << pageIdentifier << lastNavigationWasAppInitiated << isVisible << isFocused << focusOrder << ancestorOrigins;
+    encoder << identifier << type << frameType << url << pageIdentifier << frameIdentifier << lastNavigationWasAppInitiated << isVisible << isFocused << focusOrder << ancestorOrigins;
 }
 
 template<class Decoder>
@@ -98,6 +100,11 @@
     if (!pageIdentifier)
         return std::nullopt;
 
+    std::optional<std::optional<FrameIdentifier>> frameIdentifier;
+    decoder >> frameIdentifier;
+    if (!frameIdentifier)
+        return std::nullopt;
+
     std::optional<LastNavigationWasAppInitiated> lastNavigationWasAppInitiated;
     decoder >> lastNavigationWasAppInitiated;
     if (!lastNavigationWasAppInitiated)
@@ -123,7 +130,7 @@
     if (!ancestorOrigins)
         return std::nullopt;
 
-    return { { WTFMove(*identifier), WTFMove(*type), WTFMove(*frameType), WTFMove(*url), WTFMove(*pageIdentifier), WTFMove(*lastNavigationWasAppInitiated), WTFMove(*isVisible), WTFMove(*isFocused), WTFMove(*focusOrder), WTFMove(*ancestorOrigins) } };
+    return { { WTFMove(*identifier), WTFMove(*type), WTFMove(*frameType), WTFMove(*url), WTFMove(*pageIdentifier), WTFMove(*frameIdentifier), WTFMove(*lastNavigationWasAppInitiated), WTFMove(*isVisible), WTFMove(*isFocused), WTFMove(*focusOrder), WTFMove(*ancestorOrigins) } };
 }
 
 using ServiceWorkerClientsMatchAllCallback = CompletionHandler<void(Vector<ServiceWorkerClientData>&&)>;

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerWindowClient.cpp (292458 => 292459)


--- trunk/Source/WebCore/workers/service/ServiceWorkerWindowClient.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerWindowClient.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -71,10 +71,48 @@
     });
 }
 
-void ServiceWorkerWindowClient::navigate(ScriptExecutionContext&, const String& url, Ref<DeferredPromise>&& promise)
+void ServiceWorkerWindowClient::navigate(ScriptExecutionContext& context, const String& urlString, Ref<DeferredPromise>&& promise)
 {
-    UNUSED_PARAM(url);
-    promise->reject(Exception { NotSupportedError, "windowClient.navigate() is not yet supported"_s });
+    auto url = ""
+
+    if (!url.isValid()) {
+        promise->reject(Exception { TypeError, makeString("URL string ", urlString, " cannot successfully be parsed") });
+        return;
+    }
+
+    if (url.protocolIsAbout()) {
+        promise->reject(Exception { TypeError, makeString("ServiceWorkerClients.navigate() cannot be called with URL ", url.string()) });
+        return;
+    }
+
+    // We implement step 4 (checking of client's active service worker) in network process as we cannot do it synchronously.
+    auto& serviceWorkerContext = downcast<ServiceWorkerGlobalScope>(context);
+    auto promiseIdentifier = serviceWorkerContext.clients().addPendingPromise(WTFMove(promise));
+    callOnMainThread([clientIdentifier = identifier(), promiseIdentifier, serviceWorkerIdentifier = serviceWorkerContext.thread().identifier(), url = "" mutable {
+        SWContextManager::singleton().connection()->navigate(clientIdentifier, serviceWorkerIdentifier, url, [promiseIdentifier, serviceWorkerIdentifier](auto result) mutable {
+            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promiseIdentifier, result = crossThreadCopy(WTFMove(result))](auto& serviceWorkerContext) mutable {
+                auto promise = serviceWorkerContext.clients().takePendingPromise(promiseIdentifier);
+                if (!promise)
+                    return;
+
+                if (result.hasException()) {
+                    promise->reject(result.releaseException());
+                    return;
+                }
+                auto clientData = result.releaseReturnValue();
+                if (!clientData) {
+                    promise->resolveWithJSValue(JSC::jsNull());
+                    return;
+                }
+#if ASSERT_ENABLED
+                auto originData = SecurityOriginData::fromURL(clientData->url);
+                ClientOrigin clientOrigin { originData, originData };
+#endif
+                ASSERT(serviceWorkerContext.clientOrigin() == clientOrigin);
+                promise->template resolve<IDLInterface<ServiceWorkerWindowClient>>(ServiceWorkerWindowClient::create(serviceWorkerContext, WTFMove(*clientData)));
+            });
+        });
+    });
 }
 
 } // namespace WebCore

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


--- trunk/Source/WebCore/workers/service/context/SWContextManager.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/context/SWContextManager.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -72,6 +72,9 @@
         using OpenWindowCallback = CompletionHandler<void(ExceptionOr<std::optional<ServiceWorkerClientData>>&&)>;
         virtual void openWindow(ServiceWorkerIdentifier, const URL&, OpenWindowCallback&&) = 0;
 
+        using NavigateCallback = CompletionHandler<void(ExceptionOr<std::optional<WebCore::ServiceWorkerClientData>>&&)>;
+        virtual void navigate(ScriptExecutionContextIdentifier, ServiceWorkerIdentifier, const URL&, NavigateCallback&&) = 0;
+
         virtual void didFailHeartBeatCheck(ServiceWorkerIdentifier) = 0;
         virtual void setAsInspected(ServiceWorkerIdentifier, bool) = 0;
 

Modified: trunk/Source/WebCore/workers/service/server/SWServer.cpp (292458 => 292459)


--- trunk/Source/WebCore/workers/service/server/SWServer.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/server/SWServer.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1091,6 +1091,14 @@
     m_clientToControllingRegistration.remove(registrationIterator);
 }
 
+std::optional<ServiceWorkerRegistrationIdentifier> SWServer::clientIdentifierToControllingRegistration(ScriptExecutionContextIdentifier clientIdentifier) const
+{
+    auto registrationIterator = m_clientToControllingRegistration.find(clientIdentifier);
+    if (registrationIterator == m_clientToControllingRegistration.end())
+        return { };
+    return registrationIterator->value;
+}
+
 SWServer::ShouldDelayRemoval SWServer::removeContextConnectionIfPossible(const RegistrableDomain& domain)
 {
     if (m_clientsByRegistrableDomain.contains(domain))

Modified: trunk/Source/WebCore/workers/service/server/SWServer.h (292458 => 292459)


--- trunk/Source/WebCore/workers/service/server/SWServer.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/server/SWServer.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -242,6 +242,9 @@
     enum class ShouldDelayRemoval : bool { No, Yes };
     ShouldDelayRemoval removeContextConnectionIfPossible(const RegistrableDomain&);
 
+    std::optional<ServiceWorkerRegistrationIdentifier> clientIdentifierToControllingRegistration(ScriptExecutionContextIdentifier) const;
+    WEBCORE_EXPORT void forEachClientForOrigin(const ClientOrigin&, const Function<void(ServiceWorkerClientData&)>&);
+
 private:
     void validateRegistrationDomain(WebCore::RegistrableDomain, ServiceWorkerJobType, CompletionHandler<void(bool)>&&);
 
@@ -261,7 +264,6 @@
     void installContextData(const ServiceWorkerContextData&);
 
     SWServerRegistration* registrationFromServiceWorkerIdentifier(ServiceWorkerIdentifier);
-    void forEachClientForOrigin(const ClientOrigin&, const Function<void(ServiceWorkerClientData&)>&);
 
     void performGetOriginsWithRegistrationsCallbacks();
 

Modified: trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp (292458 => 292459)


--- trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -419,6 +419,14 @@
     m_server->removeContextConnectionIfPossible(registrableDomain());
 }
 
+bool SWServerWorker::isClientActiveServiceWorker(ScriptExecutionContextIdentifier clientIdentifier) const
+{
+    if (!m_server)
+        return false;
+    auto registrationIdentifier = m_server->clientIdentifierToControllingRegistration(clientIdentifier);
+    return registrationIdentifier == m_data.registrationIdentifier;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)

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


--- trunk/Source/WebCore/workers/service/server/SWServerWorker.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebCore/workers/service/server/SWServerWorker.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -141,6 +141,8 @@
 
     bool shouldContinue() const { return !!m_functionalEventCounter || m_isInspected; }
 
+    WEBCORE_EXPORT bool isClientActiveServiceWorker(ScriptExecutionContextIdentifier) const;
+
 private:
     SWServerWorker(SWServer&, SWServerRegistration&, const URL&, const ScriptBuffer&, const CertificateInfo&, const ContentSecurityPolicyResponseHeaders&, const CrossOriginEmbedderPolicy&, String&& referrerPolicy, WorkerType, ServiceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&&);
 

Modified: trunk/Source/WebKit/ChangeLog (292458 => 292459)


--- trunk/Source/WebKit/ChangeLog	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/ChangeLog	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1,3 +1,36 @@
+2022-04-06  Youenn Fablet  <you...@apple.com>
+
+        Implement ServiceWorkerWindowClient.navigate
+        https://bugs.webkit.org/show_bug.cgi?id=238738
+
+        Reviewed by Chris Dumez.
+
+        When receiving a request to navigate a client, send message to UIProcess.
+        UIProcess locates the WebFrameProxy which will send an IPC message to the WebProcess to do navigation.
+        We keep track of the navigation within WebFrameProxy.
+        In case of process swapping, we make sure to transfer the WebFrameProxy callback.
+        In case of policy decision to stop loads, we resolve the promise with an empty client.
+        A follow-up patch may reject the promise once the expected behavior will be clarified.
+
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.h:
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/Network/NetworkProcessProxy.messages.in:
+        * UIProcess/ProvisionalPageProxy.cpp:
+        * UIProcess/WebFrameProxy.cpp:
+        * UIProcess/WebFrameProxy.h:
+        * UIProcess/WebPageProxy.cpp:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        * WebProcess/WebPage/WebPage.cpp:
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2022-04-06  Zan Dobersek  <zdober...@igalia.com>
 
         [Unix] Adopt UnixFileDescriptor in IPC::Attachment

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp (292458 => 292459)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -164,11 +164,12 @@
     send(Messages::WebSWClientConnection::UpdateWorkerState(worker, state));
 }
 
-void WebSWServerConnection::controlClient(const Vector<RefPtr<SecurityOrigin>>& frameAncestorOrigins, ScriptExecutionContextIdentifier clientIdentifier, SWServerRegistration& registration, const ResourceRequest& request)
+void WebSWServerConnection::controlClient(const NetworkResourceLoadParameters& parameters, SWServerRegistration& registration, const ResourceRequest& request)
 {
+    auto clientIdentifier = *parameters.options.clientIdentifier;
     // As per step 12 of https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm, the active service worker should be controlling the document.
-    // We register a temporary service worker client using the identifier provided by DocumentLoader and notify DocumentLoader about it.
-    // If notification is successful, DocumentLoader will unregister the temporary service worker client just after the document is created and registered as a client.
+    // We register the service worker client using the identifier provided by DocumentLoader and notify DocumentLoader about it.
+    // If notification is successful, DocumentLoader is responsible to unregister the service worker client as needed.
     sendWithAsyncReply(Messages::WebSWClientConnection::SetDocumentIsControlled { clientIdentifier, registration.data() }, [weakThis = WeakPtr { *this }, this, clientIdentifier](bool isSuccess) {
         if (!weakThis || isSuccess)
             return;
@@ -175,8 +176,8 @@
         unregisterServiceWorkerClient(clientIdentifier);
     });
 
-    auto ancestorOrigins = map(frameAncestorOrigins, [](auto& origin) { return origin->toString(); });
-    ServiceWorkerClientData data { clientIdentifier, ServiceWorkerClientType::Window, ServiceWorkerClientFrameType::None, request.url(), { }, request.isAppInitiated() ? WebCore::LastNavigationWasAppInitiated::Yes : WebCore::LastNavigationWasAppInitiated::No, false, false, 0, WTFMove(ancestorOrigins) };
+    auto ancestorOrigins = map(parameters.frameAncestorOrigins, [](auto& origin) { return origin->toString(); });
+    ServiceWorkerClientData data { clientIdentifier, ServiceWorkerClientType::Window, ServiceWorkerClientFrameType::None, request.url(), parameters.webPageID, parameters.webFrameID, request.isAppInitiated() ? WebCore::LastNavigationWasAppInitiated::Yes : WebCore::LastNavigationWasAppInitiated::No, false, false, 0, WTFMove(ancestorOrigins) };
     registerServiceWorkerClient(SecurityOriginData { registration.key().topOrigin() }, WTFMove(data), registration.identifier(), request.httpUserAgent());
 }
 
@@ -201,7 +202,7 @@
             return nullptr;
 
         serviceWorkerRegistrationIdentifier = registration->identifier();
-        controlClient(loader.parameters().frameAncestorOrigins, *loader.parameters().options.clientIdentifier, *registration, request);
+        controlClient(loader.parameters(), *registration, request);
         loader.setResultingClientIdentifier(loader.parameters().options.clientIdentifier->toString());
     } else {
         if (!loader.parameters().serviceWorkerRegistrationIdentifier)

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.h (292458 => 292459)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -61,6 +61,7 @@
 namespace WebKit {
 
 class NetworkProcess;
+class NetworkResourceLoadParameters;
 class NetworkResourceLoader;
 class ServiceWorkerFetchTask;
 
@@ -83,8 +84,6 @@
     std::unique_ptr<ServiceWorkerFetchTask> createFetchTask(NetworkResourceLoader&, const WebCore::ResourceRequest&);
     void fetchTaskTimedOut(WebCore::ServiceWorkerIdentifier);
 
-    void focusServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
-
 private:
     // Implement SWServer::Connection (Messages to the client WebProcess)
     void rejectJobInClient(WebCore::ServiceWorkerJobIdentifier, const WebCore::ExceptionData&) final;
@@ -97,6 +96,7 @@
     void setRegistrationLastUpdateTime(WebCore::ServiceWorkerRegistrationIdentifier, WallTime) final;
     void setRegistrationUpdateViaCache(WebCore::ServiceWorkerRegistrationIdentifier, WebCore::ServiceWorkerUpdateViaCache) final;
     void notifyClientsOfControllerChange(const HashSet<WebCore::ScriptExecutionContextIdentifier>& contextIdentifiers, const WebCore::ServiceWorkerData& newController);
+    void focusServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&) final;
 
     void scheduleJobInServer(WebCore::ServiceWorkerJobData&&);
 
@@ -129,7 +129,7 @@
     void getPushPermissionState(WebCore::ServiceWorkerRegistrationIdentifier, CompletionHandler<void(Expected<uint8_t, WebCore::ExceptionData>&&)>&&);
 
     void postMessageToServiceWorker(WebCore::ServiceWorkerIdentifier destination, WebCore::MessageWithMessagePorts&&, const WebCore::ServiceWorkerOrClientIdentifier& source);
-    void controlClient(const Vector<RefPtr<WebCore::SecurityOrigin>>&, WebCore::ScriptExecutionContextIdentifier, WebCore::SWServerRegistration&, const WebCore::ResourceRequest&);
+    void controlClient(const NetworkResourceLoadParameters&, WebCore::SWServerRegistration&, const WebCore::ResourceRequest&);
 
     using ExceptionOrVoidCallback = CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>;
     void enableNavigationPreload(WebCore::ServiceWorkerRegistrationIdentifier, ExceptionOrVoidCallback&&);

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp (292458 => 292459)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -273,6 +273,46 @@
     connection->focusServiceWorkerClient(clientIdentifier, WTFMove(callback));
 }
 
+void WebSWServerToContextConnection::navigate(ScriptExecutionContextIdentifier clientIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, const URL& url, CompletionHandler<void(Expected<std::optional<ServiceWorkerClientData>, ExceptionData>&&)>&& callback)
+{
+    auto* worker = SWServerWorker::existingWorkerForIdentifier(serviceWorkerIdentifier);
+    if (!worker) {
+        callback(makeUnexpected(ExceptionData { TypeError, "no service worker"_s }));
+        return;
+    }
+
+    if (!worker->isClientActiveServiceWorker(clientIdentifier)) {
+        callback(makeUnexpected(ExceptionData { TypeError, "service worker is not the client active service worker"_s }));
+        return;
+    }
+
+    auto data = ""
+    if (!data || !data->pageIdentifier || !data->frameIdentifier) {
+        callback(makeUnexpected(ExceptionData { TypeError, "cannot navigate service worker client"_s }));
+        return;
+    }
+
+    auto frameIdentifier = *data->frameIdentifier;
+    m_connection.networkProcess().parentProcessConnection()->sendWithAsyncReply(Messages::NetworkProcessProxy::NavigateServiceWorkerClient { frameIdentifier, clientIdentifier, url }, [weakThis = WeakPtr { *this }, frameIdentifier, url, clientOrigin = worker->origin(), callback = WTFMove(callback)](auto pageIdentifier) mutable {
+        if (!weakThis || !weakThis->server()) {
+            callback(makeUnexpected(ExceptionData { TypeError, "service worker is gone"_s }));
+            return;
+        }
+
+        if (!pageIdentifier) {
+            callback(makeUnexpected(ExceptionData { TypeError, "navigate failed"_s }));
+            return;
+        }
+
+        std::optional<ServiceWorkerClientData> clientData;
+        weakThis->server()->forEachClientForOrigin(clientOrigin, [pageIdentifier, frameIdentifier, url, &clientData](auto& data) {
+            if (!clientData && data.pageIdentifier && *data.pageIdentifier == *pageIdentifier && data.frameIdentifier && *data.frameIdentifier == frameIdentifier && equalIgnoringFragmentIdentifier(data.url, url))
+                clientData = data;
+        });
+        callback(WTFMove(clientData));
+    }, 0);
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(SERVICE_WORKER)

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h (292458 => 292459)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -100,6 +100,7 @@
     void fireNotificationEvent(WebCore::ServiceWorkerIdentifier, const WebCore::NotificationData&, WebCore::NotificationEventType, CompletionHandler<void(bool)>&&) final;
     void close() final;
     void focus(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
+    void navigate(WebCore::ScriptExecutionContextIdentifier, WebCore::ServiceWorkerIdentifier, const URL&, CompletionHandler<void(Expected<std::optional<WebCore::ServiceWorkerClientData>, WebCore::ExceptionData>&&)>&&);
 
     void connectionIsNoLongerNeeded() final;
     void terminateDueToUnresponsiveness() final;

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


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in	2022-04-06 11:17:25 UTC (rev 292459)
@@ -36,6 +36,7 @@
     MatchAll(uint64_t matchAllRequestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientQueryOptions options);
     Claim(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier) -> (std::optional<WebCore::ExceptionData> result)
     Focus(WebCore::ScriptExecutionContextIdentifier serviceWorkerClientIdentifier) -> (std::optional<WebCore::ServiceWorkerClientData> result)
+    Navigate(WebCore::ScriptExecutionContextIdentifier clientIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, URL url) -> (Expected<std::optional<WebCore::ServiceWorkerClientData>, WebCore::ExceptionData> result)
     SetScriptResource(WebCore::ServiceWorkerIdentifier identifier, URL scriptURL, WebCore::ServiceWorkerContextData::ImportedScript script)
     PostMessageToServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier destination, struct WebCore::MessageWithMessagePorts message, WebCore::ServiceWorkerIdentifier source, String sourceOrigin)
     DidFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier identifier)

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (292458 => 292459)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1805,6 +1805,17 @@
     callback(std::nullopt);
 }
 
+void NetworkProcessProxy::navigateServiceWorkerClient(WebCore::FrameIdentifier frameIdentifier, WebCore::ScriptExecutionContextIdentifier documentIdentifier, const URL& url, CompletionHandler<void(std::optional<WebCore::PageIdentifier>)>&& callback)
+{
+    auto* process = WebProcessProxy::processForIdentifier(documentIdentifier.processIdentifier());
+    auto* frame = process ? process->webFrame(frameIdentifier) : nullptr;
+    if (!frame) {
+        callback({ });
+        return;
+    }
+    frame->navigateServiceWorkerClient(documentIdentifier, url, WTFMove(callback));
+}
+
 void NetworkProcessProxy::applicationDidEnterBackground()
 {
     send(Messages::NetworkProcess::ApplicationDidEnterBackground(), 0);

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h (292458 => 292459)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -304,6 +304,8 @@
 
     void openWindowFromServiceWorker(PAL::SessionID, const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(std::optional<WebCore::PageIdentifier>&&)>&&);
 
+    void navigateServiceWorkerClient(WebCore::FrameIdentifier, WebCore::ScriptExecutionContextIdentifier, const URL&, CompletionHandler<void(std::optional<WebCore::PageIdentifier>)>&&);
+
 private:
     explicit NetworkProcessProxy();
 

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


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in	2022-04-06 11:17:25 UTC (rev 292459)
@@ -93,6 +93,7 @@
     DataTaskDidCompleteWithError(WebKit::DataTaskIdentifier identifier, WebCore::ResourceError error)
 
     OpenWindowFromServiceWorker(PAL::SessionID sessionID, String urlString, struct WebCore::SecurityOriginData serviceWorkerOrigin) -> (std::optional<WebCore::PageIdentifier> newPage)
+    NavigateServiceWorkerClient(WebCore::FrameIdentifier frameIdentifier, WebCore::ScriptExecutionContextIdentifier documentIdentifier, URL url) -> (std::optional<WebCore::PageIdentifier> page)
 }
 
 }

Modified: trunk/Source/WebKit/UIProcess/ProvisionalPageProxy.cpp (292458 => 292459)


--- trunk/Source/WebKit/UIProcess/ProvisionalPageProxy.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/UIProcess/ProvisionalPageProxy.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -238,8 +238,10 @@
 
     // Restore the main frame's committed URL as some clients may rely on it until the next load is committed.
     RefPtr previousMainFrame = m_page.mainFrame();
-    if (previousMainFrame)
+    if (previousMainFrame) {
         m_mainFrame->frameLoadState().setURL(previousMainFrame->url());
+        previousMainFrame->transferNavigationCallbackToFrame(*m_mainFrame);
+    }
 
     // Normally, notification of a server redirect comes from the WebContent process.
     // If we are process swapping in response to a server redirect then that notification will not come from the new WebContent process.

Modified: trunk/Source/WebKit/UIProcess/WebFrameProxy.cpp (292458 => 292459)


--- trunk/Source/WebKit/UIProcess/WebFrameProxy.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/UIProcess/WebFrameProxy.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -58,6 +58,9 @@
 #if PLATFORM(GTK)
     WebPasteboardProxy::singleton().didDestroyFrame(this);
 #endif
+
+    if (m_navigateCallback)
+        m_navigateCallback({ });
 }
 
 void WebFrameProxy::webProcessWillShutDown()
@@ -68,6 +71,9 @@
         m_activeListener->ignore();
         m_activeListener = nullptr;
     }
+
+    if (m_navigateCallback)
+        m_navigateCallback({ });
 }
 
 bool WebFrameProxy::isMainFrame() const
@@ -78,6 +84,38 @@
     return this == m_page->mainFrame() || (m_page->provisionalPageProxy() && this == m_page->provisionalPageProxy()->mainFrame());
 }
 
+std::optional<PageIdentifier> WebFrameProxy::pageIdentifier() const
+{
+    if (!m_page)
+        return { };
+    return m_page->webPageID();
+}
+
+void WebFrameProxy::navigateServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier documentIdentifier, const URL& url, CompletionHandler<void(std::optional<PageIdentifier>)>&& callback)
+{
+    if (!m_page) {
+        callback({ });
+        return;
+    }
+
+    m_page->sendWithAsyncReply(Messages::WebPage::NavigateServiceWorkerClient { documentIdentifier, url }, [this, protectedThis = Ref { *this }, url, callback = WTFMove(callback)](bool result) mutable {
+        if (!result) {
+            callback({ });
+            return;
+        }
+
+        if (!m_activeListener) {
+            callback(pageIdentifier());
+            return;
+        }
+
+        if (m_navigateCallback)
+            m_navigateCallback({ });
+
+        m_navigateCallback = WTFMove(callback);
+    });
+}
+
 void WebFrameProxy::loadURL(const URL& url, const String& referrer)
 {
     if (!m_page)
@@ -95,7 +133,7 @@
     m_page->send(Messages::WebPage::LoadDataInFrame(data, MIMEType, encodingName, baseURL, m_frameID));
 }
 
-void WebFrameProxy::stopLoading() const
+void WebFrameProxy::stopLoading()
 {
     if (!m_page)
         return;
@@ -104,6 +142,9 @@
         return;
 
     m_page->send(Messages::WebPage::StopLoadingFrame(m_frameID));
+
+    if (m_navigateCallback)
+        m_navigateCallback({ });
 }
     
 bool WebFrameProxy::canProvideSource() const
@@ -160,6 +201,9 @@
 void WebFrameProxy::didFailProvisionalLoad()
 {
     m_frameLoadState.didFailProvisionalLoad();
+
+    if (m_navigateCallback)
+        m_navigateCallback({ });
 }
 
 void WebFrameProxy::didCommitLoad(const String& contentType, WebCertificateInfo& certificateInfo, bool containsPluginDocument)
@@ -175,11 +219,17 @@
 void WebFrameProxy::didFinishLoad()
 {
     m_frameLoadState.didFinishLoad();
+
+    if (m_navigateCallback)
+        m_navigateCallback(pageIdentifier());
 }
 
 void WebFrameProxy::didFailLoad()
 {
     m_frameLoadState.didFailLoad();
+
+    if (m_navigateCallback)
+        m_navigateCallback({ });
 }
 
 void WebFrameProxy::didSameDocumentNavigation(const URL& url)
@@ -197,6 +247,9 @@
     if (m_activeListener)
         m_activeListener->ignore();
     m_activeListener = WebFramePolicyListenerProxy::create([this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] (PolicyAction action, API::WebsitePolicies* policies, ProcessSwapRequestedByClient processSwapRequestedByClient, RefPtr<SafeBrowsingWarning>&& safeBrowsingWarning, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain) mutable {
+        if (action != PolicyAction::Use && m_navigateCallback)
+            m_navigateCallback(pageIdentifier());
+
         completionHandler(action, policies, processSwapRequestedByClient, WTFMove(safeBrowsingWarning), isNavigatingToAppBoundDomain);
         m_activeListener = nullptr;
     }, expectSafeBrowsingResult, expectAppBoundDomainResult);

Modified: trunk/Source/WebKit/UIProcess/WebFrameProxy.h (292458 => 292459)


--- trunk/Source/WebKit/UIProcess/WebFrameProxy.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/UIProcess/WebFrameProxy.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -77,10 +77,12 @@
 
     FrameLoadState& frameLoadState() { return m_frameLoadState; }
 
+    void navigateServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier, const URL&, CompletionHandler<void(std::optional<WebCore::PageIdentifier>)>&&);
+
     void loadURL(const URL&, const String& referrer = String());
     // Sub frames only. For main frames, use WebPageProxy::loadData.
     void loadData(const IPC::DataReference&, const String& MIMEType, const String& encodingName, const URL& baseURL);
-    void stopLoading() const;
+    void stopLoading();
 
     const URL& url() const { return m_frameLoadState.url(); }
     const URL& provisionalURL() const { return m_frameLoadState.provisionalURL(); }
@@ -128,9 +130,13 @@
     void collapseSelection();
 #endif
 
+    void transferNavigationCallbackToFrame(WebFrameProxy& frame) { frame.m_navigateCallback = WTFMove(m_navigateCallback); }
+
 private:
     WebFrameProxy(WebPageProxy&, WebCore::FrameIdentifier);
 
+    std::optional<WebCore::PageIdentifier> pageIdentifier() const;
+
     WeakPtr<WebPageProxy> m_page;
 
     FrameLoadState m_frameLoadState;
@@ -144,6 +150,7 @@
 #if ENABLE(CONTENT_FILTERING)
     WebCore::ContentFilterUnblockHandler m_contentFilterUnblockHandler;
 #endif
+    CompletionHandler<void(std::optional<WebCore::PageIdentifier>)> m_navigateCallback;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp (292458 => 292459)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -355,6 +355,17 @@
     });
 }
 
+void WebSWContextManagerConnection::navigate(ScriptExecutionContextIdentifier clientIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, const URL& url, NavigateCallback&& callback)
+{
+    m_connectionToNetworkProcess->sendWithAsyncReply(Messages::WebSWServerToContextConnection::Navigate { clientIdentifier, serviceWorkerIdentifier, url }, [callback = WTFMove(callback)](auto&& result) mutable {
+        if (!result.has_value()) {
+            callback(WTFMove(result).error().toException());
+            return;
+        }
+        callback(WTFMove(result).value());
+    });
+}
+
 void WebSWContextManagerConnection::focus(ScriptExecutionContextIdentifier clientIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&& callback)
 {
     m_connectionToNetworkProcess->sendWithAsyncReply(Messages::WebSWServerToContextConnection::Focus { clientIdentifier }, WTFMove(callback));

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h (292458 => 292459)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -82,6 +82,7 @@
     void matchAll(WebCore::ServiceWorkerIdentifier, const WebCore::ServiceWorkerClientQueryOptions&, WebCore::ServiceWorkerClientsMatchAllCallback&&) final;
     void claim(WebCore::ServiceWorkerIdentifier, CompletionHandler<void(WebCore::ExceptionOr<void>&&)>&&) final;
     void focus(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&) final;
+    void navigate(WebCore::ScriptExecutionContextIdentifier, WebCore::ServiceWorkerIdentifier, const URL&, NavigateCallback&&) final;
     void skipWaiting(WebCore::ServiceWorkerIdentifier, CompletionHandler<void()>&&) final;
     void setScriptResource(WebCore::ServiceWorkerIdentifier, const URL&, const WebCore::ServiceWorkerContextData::ImportedScript&) final;
     bool isThrottleable() const final;

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (292458 => 292459)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2022-04-06 11:17:25 UTC (rev 292459)
@@ -6439,6 +6439,22 @@
     completionHandler(WebCore::interactionRegions(*m_page, rectInContentCoordinates));
 }
 
+void WebPage::navigateServiceWorkerClient(ScriptExecutionContextIdentifier documentIdentifier, const URL& url, CompletionHandler<void(bool)>&& callback)
+{
+#if ENABLE(SERVICE_WORKER)
+    RefPtr document = Document::allDocumentsMap().get(documentIdentifier);
+    if (!document) {
+        callback(false);
+        return;
+    }
+    document->navigateFromServiceWorker(url, WTFMove(callback));
+#else
+    UNUSED_PARAM(documentIdentifier);
+    UNUSED_PARAM(url);
+    callback(false);
+#endif
+}
+
 void WebPage::setAlwaysShowsHorizontalScroller(bool alwaysShowsHorizontalScroller)
 {
     if (alwaysShowsHorizontalScroller == m_alwaysShowsHorizontalScroller)

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (292458 => 292459)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2022-04-06 11:17:25 UTC (rev 292459)
@@ -945,6 +945,7 @@
     void didEndUserTriggeredSelectionChanges();
 
     void interactionRegions(WebCore::FloatRect rectInContentCoordinates, CompletionHandler<void(Vector<WebCore::InteractionRegion>)>&&);
+    void navigateServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier, const URL&, CompletionHandler<void(bool)>&&);
 
 #if PLATFORM(COCOA)
     void platformInitializeAccessibility();

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (292458 => 292459)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2022-04-06 11:17:25 UTC (rev 292459)
@@ -683,4 +683,6 @@
     ScrollToRect(WebCore::FloatRect targetRect, WebCore::FloatPoint origin)
 
     InteractionRegions(WebCore::FloatRect rectInContentCoordinates) -> (Vector<WebCore::InteractionRegion> regions)
+
+    NavigateServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier documentIdentifier, URL url) -> (bool result)
 }

Modified: trunk/Tools/ChangeLog (292458 => 292459)


--- trunk/Tools/ChangeLog	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Tools/ChangeLog	2022-04-06 11:17:25 UTC (rev 292459)
@@ -1,5 +1,14 @@
 2022-04-06  Youenn Fablet  <you...@apple.com>
 
+        Implement ServiceWorkerWindowClient.navigate
+        https://bugs.webkit.org/show_bug.cgi?id=238738
+
+        Reviewed by Chris Dumez.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:
+
+2022-04-06  Youenn Fablet  <you...@apple.com>
+
         ServiceWorkerClients.openWindow should not need to get all clients asynchronously to resolve its promise
         https://bugs.webkit.org/show_bug.cgi?id=238503
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm (292458 => 292459)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm	2022-04-06 11:16:26 UTC (rev 292458)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm	2022-04-06 11:17:25 UTC (rev 292459)
@@ -3047,3 +3047,172 @@
     [webView evaluateJavaScript:@"openWindowClient()" completionHandler: nil];
     TestWebKitAPI::Util::run(&done);
 }
+
+static constexpr auto ServiceWorkerWindowClientNavigateMain =
+"<div>test page</div>"
+"<script>"
+"let worker;"
+"async function registerServiceWorker() {"
+"    try {"
+"        const registration = await navigator.serviceWorker.register('/sw.js');"
+"        if (registration.active) {"
+"            worker = registration.active;"
+"            alert('already active');"
+"            return;"
+"        }"
+"        worker = registration.installing;"
+"        worker.addEventListener('statechange', () => {"
+"            if (worker.state == 'activated')"
+"                alert('successfully registered');"
+"        });"
+"    } catch(e) {"
+"        alert('Exception: ' + e);"
+"    }"
+"}"
+"window._onload_ = registerServiceWorker;"
+""
+"function navigateOtherClientToURL(url) {"
+"    worker.postMessage({navigateOtherClientToURL: url});"
+"    navigator.serviceWorker._onmessage_ = (event) => {"
+"        alert(event.data);"
+"    };"
+"}"
+"</script>"_s;
+
+static constexpr auto ServiceWorkerWindowClientNavigateJS =
+"self.addEventListener('message', async (event) => {"
+"   if (event.data && event.data.navigateOtherClientToURL) {"
+"       let otherClient;"
+"       let currentClients = await self.clients.matchAll();"
+"       for (let client of currentClients) {"
+"           if (client.id !== event.source.id)"
+"               otherClient = client;"
+"       }"
+"       if (!otherClient) {"
+"           event.source.postMessage('failed, no other client, client number = ' + currentClients.length);"
+"           return;"
+"       }"
+"       await otherClient.navigate(event.data.navigateOtherClientToURL).then((client) => {"
+"           event.source.postMessage(client ? 'client' : 'none');"
+"       }, (e) => {"
+"           event.source.postMessage('failed');"
+"       });"
+"       return;"
+"   }"
+"});"_s;
+
+
+@interface ServiceWorkerPSONNavigationDelegate : NSObject <WKNavigationDelegatePrivate> {
+    @public void (^decidePolicyForNavigationAction)(WKNavigationAction *, void (^)(WKNavigationActionPolicy));
+    @public void (^didStartProvisionalNavigationHandler)();
+    @public void (^didCommitNavigationHandler)();
+}
+@end
+
+@implementation ServiceWorkerPSONNavigationDelegate
+
+- (instancetype) init
+{
+    self = [super init];
+    return self;
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
+{
+    decisionHandler(WKNavigationActionPolicyAllow);
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
+{
+    decisionHandler(WKNavigationResponsePolicyAllow);
+}
+
+@end
+
+TEST(ServiceWorker, WindowClientNavigate)
+{
+    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
+
+    // Start with a clean slate data store
+    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    for (_WKExperimentalFeature *feature in [WKPreferences _experimentalFeatures]) {
+        if ([feature.key isEqualToString:@"CrossOriginOpenerPolicyEnabled"])
+            [[configuration preferences] _setEnabled:YES forExperimentalFeature:feature];
+        else if ([feature.key isEqualToString:@"CrossOriginEmbedderPolicyEnabled"])
+            [[configuration preferences] _setEnabled:YES forExperimentalFeature:feature];
+    }
+
+    auto webView1 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
+    auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
+
+    TestWebKitAPI::HTTPServer server({
+        { "/"_s, { ServiceWorkerWindowClientNavigateMain } },
+        { "/?test"_s, { ServiceWorkerWindowClientNavigateMain } },
+        { "/?swap"_s, { {{ "Content-Type"_s, "application/html"_s }, { "Cross-Origin-Opener-Policy"_s, "same-origin"_s } }, ServiceWorkerWindowClientNavigateMain } },
+        { "/sw.js"_s, { {{ "Content-Type"_s, "application/_javascript_"_s }}, ServiceWorkerWindowClientNavigateJS } }
+    }, TestWebKitAPI::HTTPServer::Protocol::Http, nullptr, nullptr, 8091);
+
+    [webView1 loadRequest:server.request()];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "successfully registered");
+    [webView2 loadRequest:server.request()];
+    EXPECT_WK_STREQ([webView2 _test_waitForAlert], "already active");
+
+    auto navigationDelegate = adoptNS([[ServiceWorkerPSONNavigationDelegate alloc] init]);
+    [webView2 setNavigationDelegate:navigationDelegate.get()];
+
+    auto *baseURL = [[server.request() URL] absoluteString];
+
+    [webView1 evaluateJavaScript:[NSString stringWithFormat:@"navigateOtherClientToURL('%@')", baseURL] completionHandler: nil];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "client");
+
+    [webView1 evaluateJavaScript:[NSString stringWithFormat:@"navigateOtherClientToURL('%@#test')", baseURL] completionHandler: nil];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "client");
+
+    [webView1 evaluateJavaScript:[NSString stringWithFormat:@"navigateOtherClientToURL('%@?test')", baseURL] completionHandler: nil];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "client");
+
+    [webView1 evaluateJavaScript:[NSString stringWithFormat:@"navigateOtherClientToURL('%@?swap')", baseURL] completionHandler: nil];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "client");
+}
+
+TEST(ServiceWorker, WindowClientNavigateCrossOrigin)
+{
+    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
+
+    // Start with a clean slate data store
+    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    auto webView1 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
+    auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
+
+    TestWebKitAPI::HTTPServer server1({
+        { "/"_s, { ServiceWorkerWindowClientNavigateMain } },
+        { "/?test"_s, { ServiceWorkerWindowClientNavigateMain } },
+        { "/?swap"_s, { {{ "Content-Type"_s, "application/html"_s }, { "Cross-Origin-Opener-Policy"_s, "same-origin"_s } }, ServiceWorkerWindowClientNavigateMain } },
+        { "/sw.js"_s, { {{ "Content-Type"_s, "application/_javascript_"_s }}, ServiceWorkerWindowClientNavigateJS } }
+    }, TestWebKitAPI::HTTPServer::Protocol::Http, nullptr, nullptr, 8091);
+
+    TestWebKitAPI::HTTPServer server2({
+        { "/"_s, { ServiceWorkerWindowClientNavigateMain } },
+        { "/sw.js"_s, { {{ "Content-Type"_s, "application/_javascript_"_s }}, ServiceWorkerWindowClientNavigateJS } }
+    }, TestWebKitAPI::HTTPServer::Protocol::Http, nullptr, nullptr, 9091);
+
+    [webView1 loadRequest:server1.request()];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "successfully registered");
+    [webView2 loadRequest:server1.request()];
+    EXPECT_WK_STREQ([webView2 _test_waitForAlert], "already active");
+    [webView1 evaluateJavaScript:[NSString stringWithFormat:@"navigateOtherClientToURL('%@')", [[server2.request() URL] absoluteString]] completionHandler: nil];
+    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "none");
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to