[Service Workers] Implement "Notify Controller Change" algorithm
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 18 Nov 2017 03:42:17 +0000 (03:42 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 18 Nov 2017 03:42:17 +0000 (03:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179822

Reviewed by Youenn Fablet.

Source/WebCore:

Implement "Notify Controller Change" algorithm:
- https://w3c.github.io/ServiceWorker/#notify-controller-change

Use it to support step 7 of "Activate" algorithm:
- https://w3c.github.io/ServiceWorker/#activate

Test: http/tests/workers/service/controller-change.html

* workers/service/ServiceWorkerContainer.cpp:
(WebCore::ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent):
* workers/service/ServiceWorkerContainer.h:
* workers/service/server/SWClientConnection.cpp:
(WebCore::SWClientConnection::notifyClientsOfControllerChange):
* workers/service/server/SWClientConnection.h:
* workers/service/server/SWServer.h:
* workers/service/server/SWServerJobQueue.cpp:
(WebCore::SWServerJobQueue::activate):
* workers/service/server/SWServerRegistration.cpp:
(WebCore::SWServerRegistration::notifyClientsOfControllerChange):
* workers/service/server/SWServerRegistration.h:

Source/WebKit:

* Scripts/webkit/messages.py:
(class_template_headers):
* StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::notifyClientsOfControllerChange):
* StorageProcess/ServiceWorker/WebSWServerConnection.h:
* WebProcess/Storage/WebSWClientConnection.messages.in:

LayoutTests:

Add layout test coverage.

* http/tests/workers/service/controller-change-expected.txt: Added.
* http/tests/workers/service/controller-change.html: Added.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/workers/service/controller-change-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/workers/service/controller-change.html [new file with mode: 0644]
LayoutTests/http/tests/workers/service/resources/sw-test-pre.js
Source/WebCore/ChangeLog
Source/WebCore/workers/service/ServiceWorkerContainer.cpp
Source/WebCore/workers/service/ServiceWorkerContainer.h
Source/WebCore/workers/service/server/SWClientConnection.cpp
Source/WebCore/workers/service/server/SWClientConnection.h
Source/WebCore/workers/service/server/SWServer.h
Source/WebCore/workers/service/server/SWServerJobQueue.cpp
Source/WebCore/workers/service/server/SWServerRegistration.cpp
Source/WebCore/workers/service/server/SWServerRegistration.h
Source/WebKit/ChangeLog
Source/WebKit/Scripts/webkit/messages.py
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.cpp
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.h
Source/WebKit/WebProcess/Storage/WebSWClientConnection.messages.in

index b281442..cf04c31 100644 (file)
@@ -1,3 +1,15 @@
+2017-11-17  Chris Dumez  <cdumez@apple.com>
+
+        [Service Workers] Implement "Notify Controller Change" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=179822
+
+        Reviewed by Youenn Fablet.
+
+        Add layout test coverage.
+
+        * http/tests/workers/service/controller-change-expected.txt: Added.
+        * http/tests/workers/service/controller-change.html: Added.
+
 2017-11-17  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Fix grammar typo in tests
diff --git a/LayoutTests/http/tests/workers/service/controller-change-expected.txt b/LayoutTests/http/tests/workers/service/controller-change-expected.txt
new file mode 100644 (file)
index 0000000..bced9fe
--- /dev/null
@@ -0,0 +1,5 @@
+
+PASS: frame has a controller
+PASS: controllerchange event has been fired
+PASS: controller has been updated
+
diff --git a/LayoutTests/http/tests/workers/service/controller-change.html b/LayoutTests/http/tests/workers/service/controller-change.html
new file mode 100644 (file)
index 0000000..161b71d
--- /dev/null
@@ -0,0 +1,44 @@
+<html>
+<head>
+<script src="resources/sw-test-pre.js"></script>
+</head>
+<body>
+<script>
+let initialController = null;
+
+function listenForControllerChange(frame)
+{
+    frame.contentWindow.navigator.serviceWorker.addEventListener("controllerchange", function() {
+        log("PASS: controllerchange event has been fired");
+
+        let newController = frame.contentWindow.navigator.serviceWorker.controller;
+        if (newController != null && newController != initialController)
+             log("PASS: controller has been updated");
+        else
+             log("FAIL: controller has not been updated");
+
+        finishSWTest();
+    });
+}
+
+async function test() {
+    let scopeURL = "/";
+    let registration = await registerAndWaitForActive("resources/service-worker-fetch-worker.js", scopeURL);
+    let frame = await withFrame(scopeURL);
+    initialController = frame.contentWindow.navigator.serviceWorker.controller;
+    if (initialController === null) {
+        log("FAIL: frame does not have a controller");
+        finishSWTest();
+        return;
+    }
+
+    log("PASS: frame has a controller");
+    
+    listenForControllerChange(frame);
+    registration.update();
+}
+
+test();
+</script>
+</body>
+</html>
index ed2a9e7..bd60864 100644 (file)
@@ -39,29 +39,37 @@ function finishSWTest()
         testRunner.notifyDone();
 }
 
-async function interceptedFrame(workerURL, scopeURL)
+function withFrame(scopeURL)
 {
-    var registration = await navigator.serviceWorker.register(workerURL, { scope : scopeURL });
-    await new Promise(resolve => {
-        if (registration.active)
-            resolve();
-        worker = registration.installing;
-        if (worker.state === "activated")
-            resolve();
-        worker.addEventListener("statechange", () => {
-            if (worker.state === "activated")
-                resolve();
-        });
-    });
-
-    return await new Promise((resolve) => {
-        var frame = document.createElement('iframe');
+    return new Promise((resolve) => {
+        let frame = document.createElement('iframe');
         frame.src = scopeURL;
         frame.onload = function() { resolve(frame); };
         document.body.appendChild(frame);
     });
 }
 
+function registerAndWaitForActive(workerURL, scopeURL)
+{
+    return navigator.serviceWorker.register(workerURL, { scope : scopeURL }).then(function(registration) {
+        return new Promise(resolve => {
+            if (registration.active)
+                resolve(registration);
+            worker = registration.installing;
+            worker.addEventListener("statechange", () => {
+                if (worker.state === "activated")
+                    resolve(registration);
+            });
+        });
+    });
+}
+
+async function interceptedFrame(workerURL, scopeURL)
+{
+    await registerAndWaitForActive(workerURL, scopeURL);
+    return await withFrame(scopeURL);
+}
+
 function gc() {
     if (typeof GCController !== "undefined")
         GCController.collect();
index 5c7a39a..b27ea6e 100644 (file)
@@ -1,5 +1,33 @@
 2017-11-17  Chris Dumez  <cdumez@apple.com>
 
+        [Service Workers] Implement "Notify Controller Change" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=179822
+
+        Reviewed by Youenn Fablet.
+
+        Implement "Notify Controller Change" algorithm:
+        - https://w3c.github.io/ServiceWorker/#notify-controller-change
+
+        Use it to support step 7 of "Activate" algorithm:
+        - https://w3c.github.io/ServiceWorker/#activate
+
+        Test: http/tests/workers/service/controller-change.html
+
+        * workers/service/ServiceWorkerContainer.cpp:
+        (WebCore::ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent):
+        * workers/service/ServiceWorkerContainer.h:
+        * workers/service/server/SWClientConnection.cpp:
+        (WebCore::SWClientConnection::notifyClientsOfControllerChange):
+        * workers/service/server/SWClientConnection.h:
+        * workers/service/server/SWServer.h:
+        * workers/service/server/SWServerJobQueue.cpp:
+        (WebCore::SWServerJobQueue::activate):
+        * workers/service/server/SWServerRegistration.cpp:
+        (WebCore::SWServerRegistration::notifyClientsOfControllerChange):
+        * workers/service/server/SWServerRegistration.h:
+
+2017-11-17  Chris Dumez  <cdumez@apple.com>
+
         Unreviewed attempt to fix build after r225006.
 
         * platform/network/cf/NetworkStorageSessionCFNet.cpp:
index 0fd300f..bf97efe 100644 (file)
@@ -411,6 +411,19 @@ void ServiceWorkerContainer::removeRegistration(ServiceWorkerRegistration& regis
     m_registrations.remove(registration.identifier());
 }
 
+void ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent()
+{
+    if (m_isStopped)
+        return;
+
+    scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)](ScriptExecutionContext&) mutable {
+        if (m_isStopped)
+            return;
+
+        dispatchEvent(Event::create(eventNames().controllerchangeEvent, false, false));
+    });
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index b8d5876..62bd4db 100644 (file)
@@ -67,6 +67,7 @@ public:
     void getRegistration(const String& clientURL, Ref<DeferredPromise>&&);
     void scheduleTaskToUpdateRegistrationState(ServiceWorkerRegistrationIdentifier, ServiceWorkerRegistrationState, const std::optional<ServiceWorkerData>&);
     void scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier);
+    void scheduleTaskToFireControllerChangeEvent();
 
     using RegistrationsPromise = DOMPromiseDeferred<IDLSequence<IDLInterface<ServiceWorkerRegistration>>>;
     void getRegistrations(RegistrationsPromise&&);
index b295964..1a57427 100644 (file)
@@ -165,6 +165,24 @@ void SWClientConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifie
     });
 }
 
+void SWClientConnection::notifyClientsOfControllerChange(const HashSet<uint64_t>& scriptExecutionContexts, ServiceWorkerData&& newController)
+{
+    ASSERT(!scriptExecutionContexts.isEmpty());
+
+    for (auto& clientIdentifier : scriptExecutionContexts) {
+        // FIXME: Support worker contexts.
+        auto* client = Document::allDocumentsMap().get(clientIdentifier);
+        if (!client)
+            continue;
+
+        ASSERT(client->activeServiceWorker());
+        ASSERT(client->activeServiceWorker()->identifier() != newController.identifier);
+        client->setActiveServiceWorker(ServiceWorker::getOrCreate(*client, ServiceWorkerData { newController }));
+        if (auto* container = client->serviceWorkerContainer())
+            container->scheduleTaskToFireControllerChangeEvent();
+    }
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 73432a2..4a2329e 100644 (file)
@@ -86,6 +86,7 @@ protected:
     WEBCORE_EXPORT void updateRegistrationState(ServiceWorkerRegistrationIdentifier, ServiceWorkerRegistrationState, const std::optional<ServiceWorkerData>&);
     WEBCORE_EXPORT void updateWorkerState(ServiceWorkerIdentifier, ServiceWorkerState);
     WEBCORE_EXPORT void fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier);
+    WEBCORE_EXPORT void notifyClientsOfControllerChange(const HashSet<uint64_t>& scriptExecutionContexts, ServiceWorkerData&& newController);
 
 private:
     virtual void scheduleJobInServer(const ServiceWorkerJobData&) = 0;
index fc3d714..81be012 100644 (file)
@@ -73,6 +73,7 @@ public:
         virtual void updateRegistrationStateInClient(ServiceWorkerRegistrationIdentifier, ServiceWorkerRegistrationState, const std::optional<ServiceWorkerData>&) = 0;
         virtual void updateWorkerStateInClient(ServiceWorkerIdentifier, ServiceWorkerState) = 0;
         virtual void fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier) = 0;
+        virtual void notifyClientsOfControllerChange(const HashSet<uint64_t>& scriptExecutionContexts, const ServiceWorkerData& newController) = 0;
 
     protected:
         WEBCORE_EXPORT explicit Connection(SWServer&);
index 0267e43..8242a0f 100644 (file)
@@ -230,7 +230,12 @@ void SWServerJobQueue::activate(SWServer& server, SWServerRegistration& registra
     // Run the Update Worker State algorithm passing registration's active worker and activating as the arguments.
     registration.updateWorkerState(*registration.activeWorker(), ServiceWorkerState::Activating);
     // FIXME: For each service worker client client whose creation URL matches registration's scope url...
-    // FIXME: For each service worker client client who is using registration...
+
+    // For each service worker client client who is using registration:
+    // - Set client's active worker to registration's active worker.
+    // - Invoke Notify Controller Change algorithm with client as the argument.
+    registration.notifyClientsOfControllerChange();
+
     // FIXME: Invoke Run Service Worker algorithm with activeWorker as the argument.
 
     // Queue a task to fire the activate event.
index 6230ab2..89056d4 100644 (file)
@@ -163,6 +163,17 @@ void SWServerRegistration::removeClientUsingRegistration(const ServiceWorkerClie
         m_clientsUsingRegistration.remove(iterator);
 }
 
+// https://w3c.github.io/ServiceWorker/#notify-controller-change
+void SWServerRegistration::notifyClientsOfControllerChange()
+{
+    ASSERT(activeWorker());
+
+    for (auto& item : m_clientsUsingRegistration) {
+        if (auto* connection = m_server.getConnection(item.key))
+            connection->notifyClientsOfControllerChange(item.value, activeWorker()->data());
+    }
+}
+
 void SWServerRegistration::unregisterServerConnection(SWServerConnectionIdentifier serverConnectionIdentifier)
 {
     m_connectionsWithClientRegistrations.removeAll(serverConnectionIdentifier);
index 4fc3c9d..1b8b505 100644 (file)
@@ -78,6 +78,8 @@ public:
     void removeClientUsingRegistration(const ServiceWorkerClientIdentifier&);
     void unregisterServerConnection(SWServerConnectionIdentifier);
 
+    void notifyClientsOfControllerChange();
+
 private:
     void forEachConnection(const WTF::Function<void(SWServer::Connection&)>&);
 
index 4f4bb73..7663ed3 100644 (file)
@@ -1,5 +1,19 @@
 2017-11-17  Chris Dumez  <cdumez@apple.com>
 
+        [Service Workers] Implement "Notify Controller Change" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=179822
+
+        Reviewed by Youenn Fablet.
+
+        * Scripts/webkit/messages.py:
+        (class_template_headers):
+        * StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::notifyClientsOfControllerChange):
+        * StorageProcess/ServiceWorker/WebSWServerConnection.h:
+        * WebProcess/Storage/WebSWClientConnection.messages.in:
+
+2017-11-17  Chris Dumez  <cdumez@apple.com>
+
         Use a strongly typed identifier for SWServer::Connection
         https://bugs.webkit.org/show_bug.cgi?id=179848
 
index af6e332..b33686b 100644 (file)
@@ -300,6 +300,7 @@ def class_template_headers(template_string):
     class_template_types = {
         'WebCore::RectEdges': {'headers': ['<WebCore/RectEdges.h>'], 'argument_coder_headers': ['"ArgumentCoders.h"']},
         'HashMap': {'headers': ['<wtf/HashMap.h>'], 'argument_coder_headers': ['"ArgumentCoders.h"']},
+        'HashSet': {'headers': ['<wtf/HashSet.h>'], 'argument_coder_headers': ['"ArgumentCoders.h"']},
         'std::optional': {'headers': ['<wtf/Optional.h>'], 'argument_coder_headers': ['"ArgumentCoders.h"']},
         'OptionSet': {'headers': ['<wtf/OptionSet.h>'], 'argument_coder_headers': ['"ArgumentCoders.h"']},
         'Vector': {'headers': ['<wtf/Vector.h>'], 'argument_coder_headers': ['"ArgumentCoders.h"']},
index d819b99..0072236 100644 (file)
@@ -102,6 +102,11 @@ void WebSWServerConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdenti
     send(Messages::WebSWClientConnection::FireUpdateFoundEvent(identifier));
 }
 
+void WebSWServerConnection::notifyClientsOfControllerChange(const HashSet<uint64_t>& scriptExecutionContexts, const ServiceWorkerData& newController)
+{
+    send(Messages::WebSWClientConnection::NotifyClientsOfControllerChange(scriptExecutionContexts, newController));
+}
+
 void WebSWServerConnection::updateWorkerStateInClient(ServiceWorkerIdentifier worker, ServiceWorkerState state)
 {
     send(Messages::WebSWClientConnection::UpdateWorkerState(worker, state));
index 22df0a7..98e92ce 100644 (file)
@@ -73,6 +73,7 @@ private:
     void updateRegistrationStateInClient(WebCore::ServiceWorkerRegistrationIdentifier, WebCore::ServiceWorkerRegistrationState, const std::optional<WebCore::ServiceWorkerData>&) final;
     void updateWorkerStateInClient(WebCore::ServiceWorkerIdentifier, WebCore::ServiceWorkerState) final;
     void fireUpdateFoundEvent(WebCore::ServiceWorkerRegistrationIdentifier) final;
+    void notifyClientsOfControllerChange(const HashSet<uint64_t>& scriptExecutionContexts, const WebCore::ServiceWorkerData& newController);
 
     void startFetch(uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier>, const WebCore::ResourceRequest&, const WebCore::FetchOptions&, const IPC::FormDataReference&);
 
index efcd65b..965a2f0 100644 (file)
@@ -31,6 +31,7 @@ messages -> WebSWClientConnection {
     UpdateRegistrationState(WebCore::ServiceWorkerRegistrationIdentifier identifier, enum WebCore::ServiceWorkerRegistrationState state, std::optional<WebCore::ServiceWorkerData> serviceWorkerIdentifier)
     UpdateWorkerState(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, enum WebCore::ServiceWorkerState state)
     FireUpdateFoundEvent(WebCore::ServiceWorkerRegistrationIdentifier identifier)
+    NotifyClientsOfControllerChange(HashSet<uint64_t> scriptExecutionContexts, struct WebCore::ServiceWorkerData newController)
 
     InitializeSWOriginTableAsEmpty()
     SetSWOriginTableSharedMemory(WebKit::SharedMemory::Handle handle)