Implement self.skipWaiting() inside service workers
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Dec 2017 20:32:57 +0000 (20:32 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Dec 2017 20:32:57 +0000 (20:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180329

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Rebaseline WPT tests that are now passing.

* web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https-expected.txt:
* web-platform-tests/service-workers/service-worker/claim-with-redirect.https-expected.txt:
* web-platform-tests/service-workers/service-worker/postmessage-from-waiting-serviceworker.https-expected.txt:
* web-platform-tests/service-workers/service-worker/skip-waiting-without-client.https-expected.txt:

Source/WebCore:

Implement self.skipWaiting() inside service workers:
- https://w3c.github.io/ServiceWorker/#service-worker-global-scope-skipwaiting

Also fixes a bug where tryActivate() was calling activate() even though the
registration's active worker was "in use":
- https://w3c.github.io/ServiceWorker/#try-activate-algorithm

No new tests, rebaselined existing tests.

* workers/service/ServiceWorkerGlobalScope.cpp:
(WebCore::ServiceWorkerGlobalScope::skipWaiting):
* workers/service/context/SWContextManager.h:
* workers/service/server/SWServerRegistration.cpp:
(WebCore::SWServerRegistration::tryActivate):
* workers/service/server/SWServerToContextConnection.cpp:
(WebCore::SWServerToContextConnection::skipWaiting):
* workers/service/server/SWServerToContextConnection.h:
* workers/service/server/SWServerWorker.cpp:
(WebCore::SWServerWorker::skipWaiting):
* workers/service/server/SWServerWorker.h:
(WebCore::SWServerWorker::isSkipWaitingFlagSet const):

Source/WebKit:

* StorageProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
(WebKit::WebSWServerToContextConnection::didFinishSkipWaiting):
* StorageProcess/ServiceWorker/WebSWServerToContextConnection.h:
* StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::skipWaiting):
(WebKit::WebSWContextManagerConnection::didFinishSkipWaiting):
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.messages.in:

LayoutTests:

* http/tests/workers/service/resources/service-worker-fetch-worker.js:
add self.skipWaiting() now that we properly wait when the registation's
active worker is in use. This is needed because this worker script is
used by http/tests/workers/service/controller-change.html. The
'controllerchange' event is only fired if an active worker is being
replaced while in use due to the waiting worker calling skipWaiting().

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/workers/service/resources/service-worker-fetch-worker.js
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/claim-with-redirect.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/postmessage-from-waiting-serviceworker.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/skip-waiting-without-client.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp
Source/WebCore/workers/service/context/SWContextManager.h
Source/WebCore/workers/service/server/SWServerRegistration.cpp
Source/WebCore/workers/service/server/SWServerToContextConnection.cpp
Source/WebCore/workers/service/server/SWServerToContextConnection.h
Source/WebCore/workers/service/server/SWServerWorker.cpp
Source/WebCore/workers/service/server/SWServerWorker.h
Source/WebKit/ChangeLog
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.cpp
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.h
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in

index 332451e..a15fa59 100644 (file)
@@ -1,3 +1,17 @@
+2017-12-03  Chris Dumez  <cdumez@apple.com>
+
+        Implement self.skipWaiting() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180329
+
+        Reviewed by Darin Adler.
+
+        * http/tests/workers/service/resources/service-worker-fetch-worker.js:
+        add self.skipWaiting() now that we properly wait when the registation's
+        active worker is in use. This is needed because this worker script is
+        used by http/tests/workers/service/controller-change.html. The
+        'controllerchange' event is only fired if an active worker is being
+        replaced while in use due to the waiting worker calling skipWaiting().
+
 2017-12-02  Chris Dumez  <cdumez@apple.com>
 
         Support container.register() / registration.unregister() /  inside service workers
index bc69186..fa6b4c5 100644 (file)
@@ -1,4 +1,7 @@
 var status = "no status";
+
+self.skipWaiting();
+
 self.addEventListener("fetch", (event) => {
     if (event.request.url.indexOf("status") !== -1) {
         event.respondWith(new Response(null, {status: 200, statusText: status}));
index 8953c65..e817630 100644 (file)
@@ -1,3 +1,17 @@
+2017-12-03  Chris Dumez  <cdumez@apple.com>
+
+        Implement self.skipWaiting() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180329
+
+        Reviewed by Darin Adler.
+
+        Rebaseline WPT tests that are now passing.
+
+        * web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https-expected.txt:
+        * web-platform-tests/service-workers/service-worker/claim-with-redirect.https-expected.txt:
+        * web-platform-tests/service-workers/service-worker/postmessage-from-waiting-serviceworker.https-expected.txt:
+        * web-platform-tests/service-workers/service-worker/skip-waiting-without-client.https-expected.txt:
+
 2017-12-02  Chris Dumez  <cdumez@apple.com>
 
         Support container.register() / registration.unregister() /  inside service workers
index 284e429..d445de1 100644 (file)
@@ -1,5 +1,4 @@
-CONSOLE MESSAGE: line 27: Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating 'r.update')
   
 
-FAIL Claim works after redirection to another origin assert_equals: expected (string) "updated" but got (undefined) undefined
+PASS Claim works after redirection to another origin 
 
index cfd10ad..998bdf4 100644 (file)
@@ -1,4 +1,3 @@
 
-
-FAIL Client.postMessage() from waiting serviceworker. assert_unreached: unexpected rejection: assert_equals: message event source should be correct expected null but got object "[object ServiceWorker]" Reached unreachable code
+PASS Client.postMessage() from waiting serviceworker. 
 
index 1d3f962..edc17fb 100644 (file)
@@ -1,3 +1,32 @@
+2017-12-03  Chris Dumez  <cdumez@apple.com>
+
+        Implement self.skipWaiting() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180329
+
+        Reviewed by Darin Adler.
+
+        Implement self.skipWaiting() inside service workers:
+        - https://w3c.github.io/ServiceWorker/#service-worker-global-scope-skipwaiting
+
+        Also fixes a bug where tryActivate() was calling activate() even though the
+        registration's active worker was "in use":
+        - https://w3c.github.io/ServiceWorker/#try-activate-algorithm
+
+        No new tests, rebaselined existing tests.
+
+        * workers/service/ServiceWorkerGlobalScope.cpp:
+        (WebCore::ServiceWorkerGlobalScope::skipWaiting):
+        * workers/service/context/SWContextManager.h:
+        * workers/service/server/SWServerRegistration.cpp:
+        (WebCore::SWServerRegistration::tryActivate):
+        * workers/service/server/SWServerToContextConnection.cpp:
+        (WebCore::SWServerToContextConnection::skipWaiting):
+        * workers/service/server/SWServerToContextConnection.h:
+        * workers/service/server/SWServerWorker.cpp:
+        (WebCore::SWServerWorker::skipWaiting):
+        * workers/service/server/SWServerWorker.h:
+        (WebCore::SWServerWorker::isSkipWaitingFlagSet const):
+
 2017-12-03  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GStreamer] GstPad leaked in WebKitTextCombiner
index 0f230c2..d7628de 100644 (file)
@@ -50,8 +50,15 @@ ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() = default;
 
 void ServiceWorkerGlobalScope::skipWaiting(Ref<DeferredPromise>&& promise)
 {
-    // FIXME: Implement this.
-    promise->reject(Exception { NotSupportedError, ASCIILiteral("self.skipWaiting() is not yet supported") });
+    callOnMainThread([this, protectedThis = makeRef(*this), threadIdentifier = thread().identifier(), promise = WTFMove(promise)]() mutable {
+        if (auto* connection = SWContextManager::singleton().connection()) {
+            connection->skipWaiting(threadIdentifier, [this, protectedThis = WTFMove(protectedThis), promise = WTFMove(promise)]() mutable {
+                thread().runLoop().postTask([promise = WTFMove(promise), protectedThis = WTFMove(protectedThis)](auto&) {
+                    promise->resolve();
+                });
+            });
+        }
+    });
 }
 
 EventTargetInterface ServiceWorkerGlobalScope::eventTargetInterface() const
index f0b10a0..ab5de70 100644 (file)
@@ -54,6 +54,7 @@ public:
         virtual void didFinishActivation(ServiceWorkerIdentifier) = 0;
         virtual void setServiceWorkerHasPendingEvents(ServiceWorkerIdentifier, bool) = 0;
         virtual void workerTerminated(ServiceWorkerIdentifier) = 0;
+        virtual void skipWaiting(ServiceWorkerIdentifier, WTF::Function<void()>&& callback) = 0;
 
         using FindClientByIdentifierCallback = WTF::CompletionHandler<void(ExceptionOr<std::optional<ServiceWorkerClientData>>&&)>;
         virtual void findClientByIdentifier(ServiceWorkerIdentifier, ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) = 0;
index 82749e4..6ca9264 100644 (file)
@@ -232,9 +232,8 @@ void SWServerRegistration::tryActivate()
     // Invoke Activate with registration if either of the following is true:
     // - registration's active worker is null.
     // - The result of running Service Worker Has No Pending Events with registration's active worker is true,
-    //   and no service worker client is using registration
-    // FIXME: Check for the skip waiting flag.
-    if (!activeWorker() || !activeWorker()->hasPendingEvents())
+    //   and no service worker client is using registration or registration's waiting worker's skip waiting flag is set.
+    if (!activeWorker() || (!activeWorker()->hasPendingEvents() && (!hasClientsUsingRegistration() || waitingWorker()->isSkipWaitingFlagSet())))
         activate();
 }
 
index f61d7d3..231b9b4 100644 (file)
@@ -123,6 +123,14 @@ void SWServerToContextConnection::matchAll(uint64_t requestIdentifier, ServiceWo
     }
 }
 
+void SWServerToContextConnection::skipWaiting(ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t callbackID)
+{
+    if (auto* worker = SWServerWorker::existingWorkerForIdentifier(serviceWorkerIdentifier))
+        worker->skipWaiting();
+
+    didFinishSkipWaiting(callbackID);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 7be4131..1be87f5 100644 (file)
@@ -55,6 +55,7 @@ public:
     virtual void syncTerminateWorker(ServiceWorkerIdentifier) = 0;
     virtual void findClientByIdentifierCompleted(uint64_t requestIdentifier, const std::optional<ServiceWorkerClientData>&, bool hasSecurityError) = 0;
     virtual void matchAllCompleted(uint64_t requestIdentifier, const Vector<ServiceWorkerClientInformation>&) = 0;
+    virtual void didFinishSkipWaiting(uint64_t callbackID) = 0;
 
     // Messages back from the SW host process
     WEBCORE_EXPORT void scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>&, ServiceWorkerIdentifier, const String& message);
@@ -62,6 +63,7 @@ public:
     WEBCORE_EXPORT void didFinishInstall(const std::optional<ServiceWorkerJobDataIdentifier>&, ServiceWorkerIdentifier, bool wasSuccessful);
     WEBCORE_EXPORT void didFinishActivation(ServiceWorkerIdentifier);
     WEBCORE_EXPORT void setServiceWorkerHasPendingEvents(ServiceWorkerIdentifier, bool hasPendingEvents);
+    WEBCORE_EXPORT void skipWaiting(ServiceWorkerIdentifier, uint64_t callbackID);
     WEBCORE_EXPORT void workerTerminated(ServiceWorkerIdentifier);
     WEBCORE_EXPORT void findClientByIdentifier(uint64_t clientIdRequestIdentifier, ServiceWorkerIdentifier, ServiceWorkerClientIdentifier);
     WEBCORE_EXPORT void matchAll(uint64_t requestIdentifier, ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&);
index 5bdc062..8076714 100644 (file)
@@ -116,6 +116,15 @@ void SWServerWorker::matchAll(const ServiceWorkerClientQueryOptions& options, Se
     return m_server.matchAll(*this, options, WTFMove(callback));
 }
 
+void SWServerWorker::skipWaiting()
+{
+    m_isSkipWaitingFlagSet = true;
+
+    auto* registration = m_server.getRegistration(m_registrationKey);
+    ASSERT(registration);
+    registration->tryActivate();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index e397c11..3c5208e 100644 (file)
@@ -91,6 +91,9 @@ public:
     std::optional<ServiceWorkerClientData> findClientByIdentifier(ServiceWorkerClientIdentifier);
     void matchAll(const ServiceWorkerClientQueryOptions&, ServiceWorkerClientsMatchAllCallback&&);
 
+    void skipWaiting();
+    bool isSkipWaitingFlagSet() const { return m_isSkipWaitingFlagSet; }
+
     WEBCORE_EXPORT static SWServerWorker* existingWorkerForIdentifier(ServiceWorkerIdentifier);
 
     const ServiceWorkerData& data() const { return m_data; }
@@ -108,6 +111,7 @@ private:
     bool m_hasPendingEvents { false };
     State m_state { State::NotRunning };
     mutable std::optional<ClientOrigin> m_origin;
+    bool m_isSkipWaitingFlagSet { false };
 };
 
 } // namespace WebCore
index 6437502..7cbf5ec 100644 (file)
@@ -1,3 +1,20 @@
+2017-12-03  Chris Dumez  <cdumez@apple.com>
+
+        Implement self.skipWaiting() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180329
+
+        Reviewed by Darin Adler.
+
+        * StorageProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+        (WebKit::WebSWServerToContextConnection::didFinishSkipWaiting):
+        * StorageProcess/ServiceWorker/WebSWServerToContextConnection.h:
+        * StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::skipWaiting):
+        (WebKit::WebSWContextManagerConnection::didFinishSkipWaiting):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.messages.in:
+
 2017-12-02  Youenn Fablet  <youenn@apple.com>
 
         Implement https://w3c.github.io/ServiceWorker/#clients-getall
index b6d6eb4..a5a9bc9 100644 (file)
@@ -91,6 +91,11 @@ void WebSWServerToContextConnection::matchAllCompleted(uint64_t requestIdentifie
     send(Messages::WebSWContextManagerConnection::MatchAllCompleted { requestIdentifier, clientsData });
 }
 
+void WebSWServerToContextConnection::didFinishSkipWaiting(uint64_t callbackID)
+{
+    send(Messages::WebSWContextManagerConnection::DidFinishSkipWaiting { callbackID });
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(SERVICE_WORKER)
index 365f197..c640256 100644 (file)
@@ -62,6 +62,7 @@ private:
     void syncTerminateWorker(WebCore::ServiceWorkerIdentifier) final;
     void findClientByIdentifierCompleted(uint64_t requestIdentifier, const std::optional<WebCore::ServiceWorkerClientData>&, bool hasSecurityError) final;
     void matchAllCompleted(uint64_t requestIdentifier, const Vector<WebCore::ServiceWorkerClientInformation>&) final;
+    void didFinishSkipWaiting(uint64_t callbackID) final;
 
     Ref<IPC::Connection> m_ipcConnection;
     
index 0660b6d..101d3d4 100644 (file)
@@ -30,6 +30,7 @@ messages -> WebSWServerToContextConnection {
     DidFinishInstall(std::optional<WebCore::ServiceWorkerJobDataIdentifier> jobDataIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, bool wasSuccessful);
     DidFinishActivation(WebCore::ServiceWorkerIdentifier identifier);
     SetServiceWorkerHasPendingEvents(WebCore::ServiceWorkerIdentifier identifier, bool hasPendingEvents);
+    SkipWaiting(WebCore::ServiceWorkerIdentifier identifier, uint64_t callbackID)
     WorkerTerminated(WebCore::ServiceWorkerIdentifier identifier);
     FindClientByIdentifier(uint64_t requestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientIdentifier clientIdentifier);
     MatchAll(uint64_t matchAllRequestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientQueryOptions options);
index a5e307e..a560276 100644 (file)
@@ -197,6 +197,13 @@ void WebSWContextManagerConnection::setServiceWorkerHasPendingEvents(ServiceWork
     m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::SetServiceWorkerHasPendingEvents(serviceWorkerIdentifier, hasPendingEvents), 0);
 }
 
+void WebSWContextManagerConnection::skipWaiting(ServiceWorkerIdentifier serviceWorkerIdentifier, WTF::Function<void()>&& callback)
+{
+    auto callbackID = ++m_previousRequestIdentifier;
+    m_skipWaitingRequests.add(callbackID, WTFMove(callback));
+    m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::SkipWaiting(serviceWorkerIdentifier, callbackID), 0);
+}
+
 void WebSWContextManagerConnection::workerTerminated(ServiceWorkerIdentifier serviceWorkerIdentifier)
 {
     m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::WorkerTerminated(serviceWorkerIdentifier), 0);
@@ -233,6 +240,12 @@ void WebSWContextManagerConnection::matchAllCompleted(uint64_t requestIdentifier
         callback(WTFMove(clientsData));
 }
 
+void WebSWContextManagerConnection::didFinishSkipWaiting(uint64_t callbackID)
+{
+    if (auto callback = m_skipWaitingRequests.take(callbackID))
+        callback();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 8e57622..15a4b27 100644 (file)
@@ -66,6 +66,7 @@ private:
     void workerTerminated(WebCore::ServiceWorkerIdentifier) final;
     void findClientByIdentifier(WebCore::ServiceWorkerIdentifier, WebCore::ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) final;
     void matchAll(WebCore::ServiceWorkerIdentifier, const WebCore::ServiceWorkerClientQueryOptions&, WebCore::ServiceWorkerClientsMatchAllCallback&&) final;
+    void skipWaiting(WebCore::ServiceWorkerIdentifier, WTF::Function<void()>&& callback) final;
 
     // IPC messages.
     void serviceWorkerStartedWithMessage(std::optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, const String& exceptionMessage) final;
@@ -78,6 +79,7 @@ private:
     void syncTerminateWorker(WebCore::ServiceWorkerIdentifier, Ref<Messages::WebSWContextManagerConnection::SyncTerminateWorker::DelayedReply>&&);
     void findClientByIdentifierCompleted(uint64_t requestIdentifier, std::optional<WebCore::ServiceWorkerClientData>&&, bool hasSecurityError);
     void matchAllCompleted(uint64_t matchAllRequestIdentifier, Vector<WebCore::ServiceWorkerClientInformation>&&);
+    void didFinishSkipWaiting(uint64_t callbackID);
 
     Ref<IPC::Connection> m_connectionToStorageProcess;
     uint64_t m_pageID { 0 };
@@ -85,6 +87,7 @@ private:
 
     HashMap<uint64_t, FindClientByIdentifierCallback> m_findClientByIdentifierRequests;
     HashMap<uint64_t, WebCore::ServiceWorkerClientsMatchAllCallback> m_matchAllRequests;
+    HashMap<uint64_t, WTF::Function<void()>> m_skipWaitingRequests;
     uint64_t m_previousRequestIdentifier { 0 };
 };
 
index c21a509..a1d6511 100644 (file)
@@ -32,6 +32,7 @@ messages -> WebSWContextManagerConnection {
     SyncTerminateWorker(WebCore::ServiceWorkerIdentifier identifier) -> () Delayed
     FindClientByIdentifierCompleted(uint64_t clientIdRequestIdentifier, std::optional<WebCore::ServiceWorkerClientData> data, bool hasSecurityError)
     MatchAllCompleted(uint64_t matchAllRequestIdentifier, Vector<WebCore::ServiceWorkerClientInformation> clientsData)
+    DidFinishSkipWaiting(uint64_t callbackID)
 }
 
 #endif