Support serviceWorkerRegistration.update() inside service workers
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Dec 2017 02:32:42 +0000 (02:32 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Dec 2017 02:32:42 +0000 (02:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180215

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Rebaseline WPT test that no longer times out. The test still fails even
though we support update(). The issue is that the second frame load
uses the new worker (because of the update()) instead of the old one.
The test seems to expect that the new service worker is still in
waiting state at the point the second frame is loaded. However, for us
it is already activated.

* web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https-expected.txt:

Source/WebCore:

Support serviceWorkerRegistration.update() inside service workers. The code paths
for job scheduling and resolution have been made thread safe by hopping to the right
thread when necessary. In particular, ServiceWorkerContainer always makes sure to to
a callOnMainThread() before calling methods on the SWClientConnection. Also, the
SWClientConnection relies on a new postTaskTo() method before calling methods on the
job.

Test: http/tests/workers/service/self_registration_update.html

* workers/service/SWClientConnection.cpp:
(WebCore::SWClientConnection::scheduleJob):
(WebCore::SWClientConnection::finishedFetchingScript):
(WebCore::SWClientConnection::failedFetchingScript):
(WebCore::SWClientConnection::jobRejectedInServer):
(WebCore::SWClientConnection::registrationJobResolvedInServer):
(WebCore::SWClientConnection::unregistrationJobResolvedInServer):
(WebCore::SWClientConnection::startScriptFetchForServer):
(WebCore::SWClientConnection::postMessageToServiceWorkerClient):
(WebCore::SWClientConnection::updateRegistrationState):
(WebCore::SWClientConnection::updateWorkerState):
(WebCore::SWClientConnection::fireUpdateFoundEvent):
(WebCore::SWClientConnection::notifyClientsOfControllerChange):
(WebCore::SWClientConnection::clearPendingJobs):
(WebCore::SWClientConnection::postTaskTo):
* workers/service/SWClientConnection.h:
* workers/service/ServiceWorkerContainer.cpp:
(WebCore::ServiceWorkerContainer::scheduleJob):
(WebCore::ServiceWorkerContainer::jobFailedWithException):
(WebCore::ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent):
(WebCore::ServiceWorkerContainer::jobResolvedWithRegistration):
(WebCore::ServiceWorkerContainer::jobResolvedWithUnregistrationResult):
(WebCore::ServiceWorkerContainer::startScriptFetchForJob):
(WebCore::ServiceWorkerContainer::jobFinishedLoadingScript):
(WebCore::ServiceWorkerContainer::jobFailedLoadingScript):
(WebCore::ServiceWorkerContainer::jobDidFinish):
(WebCore::ServiceWorkerContainer::addRegistration):
(WebCore::ServiceWorkerContainer::removeRegistration):
(WebCore::ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent):
(WebCore::ServiceWorkerContainer::contextIdentifier):
* workers/service/ServiceWorkerContainer.h:
* workers/service/ServiceWorkerJob.cpp:
(WebCore::ServiceWorkerJob::ServiceWorkerJob):
* workers/service/ServiceWorkerJob.h:
(WebCore::ServiceWorkerJob::contextIdentifier):
* workers/service/ServiceWorkerJobClient.h:
* workers/service/ServiceWorkerRegistration.cpp:
(WebCore::ServiceWorkerRegistration::update):
* workers/service/ServiceWorkerTypes.h:

LayoutTests:

Add layout test coverage and unskip WPT test that no longer times out.

* TestExpectations:
* http/tests/workers/service/resources/self_registration_update-worker.js: Added.
* http/tests/workers/service/self_registration_update-expected.txt: Added.
* http/tests/workers/service/self_registration_update.html: Added.

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

17 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/workers/service/resources/self_registration_update-worker.js [new file with mode: 0644]
LayoutTests/http/tests/workers/service/self_registration_update-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/workers/service/self_registration_update.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/workers/service/SWClientConnection.cpp
Source/WebCore/workers/service/SWClientConnection.h
Source/WebCore/workers/service/ServiceWorkerContainer.cpp
Source/WebCore/workers/service/ServiceWorkerContainer.h
Source/WebCore/workers/service/ServiceWorkerJob.cpp
Source/WebCore/workers/service/ServiceWorkerJob.h
Source/WebCore/workers/service/ServiceWorkerJobClient.h
Source/WebCore/workers/service/ServiceWorkerRegistration.cpp
Source/WebCore/workers/service/ServiceWorkerTypes.h

index 3e41b8b..1a12a69 100644 (file)
@@ -1,3 +1,17 @@
+2017-12-02  Chris Dumez  <cdumez@apple.com>
+
+        Support serviceWorkerRegistration.update() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180215
+
+        Reviewed by Darin Adler.
+
+        Add layout test coverage and unskip WPT test that no longer times out.
+
+        * TestExpectations:
+        * http/tests/workers/service/resources/self_registration_update-worker.js: Added.
+        * http/tests/workers/service/self_registration_update-expected.txt: Added.
+        * http/tests/workers/service/self_registration_update.html: Added.
+
 2017-12-02  Youenn Fablet  <youenn@apple.com>
 
         Implement https://w3c.github.io/ServiceWorker/#clients-getall
index 5dd3afc..6f9b33c 100644 (file)
@@ -147,7 +147,6 @@ imported/w3c/web-platform-tests/secure-contexts/shared-worker-secure-first.https
 imported/w3c/web-platform-tests/fetch/api/abort/general-serviceworker.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html [ Skip ]
-imported/w3c/web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/clients-matchall-on-evaluation.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/detached-context.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/extendable-event-waituntil.https.html [ Skip ]
diff --git a/LayoutTests/http/tests/workers/service/resources/self_registration_update-worker.js b/LayoutTests/http/tests/workers/service/resources/self_registration_update-worker.js
new file mode 100644 (file)
index 0000000..2b36acb
--- /dev/null
@@ -0,0 +1,4 @@
+self.addEventListener("message", (event) => {
+    self.registration.update();
+});
+
diff --git a/LayoutTests/http/tests/workers/service/self_registration_update-expected.txt b/LayoutTests/http/tests/workers/service/self_registration_update-expected.txt
new file mode 100644 (file)
index 0000000..f7e3f58
--- /dev/null
@@ -0,0 +1,7 @@
+* Add basic testing for ServiceWorkerGlobalScope.registration.update()
+
+PASS: registration was successfuly updated by the service worker
+PASS: service worker became redundant
+PASS: Old worker is still the registration's active worker
+PASS: New worker should be the registration's waiting worker
+
diff --git a/LayoutTests/http/tests/workers/service/self_registration_update.html b/LayoutTests/http/tests/workers/service/self_registration_update.html
new file mode 100644 (file)
index 0000000..3c646ae
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="resources/sw-test-pre.js"></script>
+<script>
+log("* Add basic testing for ServiceWorkerGlobalScope.registration.update()");
+log("");
+
+navigator.serviceWorker.register("resources/self_registration_update-worker.js", { }).then(function(_registration) {
+    registration = _registration;
+    worker = registration.installing;
+
+    waitForState(worker, "activated").then(function() {
+        registration.addEventListener("updatefound", function() {
+            log("PASS: registration was successfuly updated by the service worker");
+            waitForState(worker, "redundant").then(function() {
+                log("PASS: service worker became redundant");
+                if (registration.active === worker)
+                    log("PASS: Old worker is still the registration's active worker");
+                else
+                    log("FAIL: Old worker is still the registration's active worker");
+
+                if (!self.registration.waiting || self.registration.waiting === worker)
+                    log("FAIL: New worker should be the registration's waiting worker");
+                else
+                    log("PASS: New worker should be the registration's waiting worker");
+                finishSWTest();
+            });
+        });
+        worker.postMessage("update");
+    });
+});
+</script>
+</body>
+</html>
index e459a7c..b6059a2 100644 (file)
@@ -1,3 +1,19 @@
+2017-12-02  Chris Dumez  <cdumez@apple.com>
+
+        Support serviceWorkerRegistration.update() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180215
+
+        Reviewed by Darin Adler.
+
+        Rebaseline WPT test that no longer times out. The test still fails even
+        though we support update(). The issue is that the second frame load
+        uses the new worker (because of the update()) instead of the old one.
+        The test seems to expect that the new service worker is still in
+        waiting state at the point the second frame is loaded. However, for us
+        it is already activated.
+
+        * web-platform-tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https-expected.txt:
+
 2017-12-02  Youenn Fablet  <youenn@apple.com>
 
         Implement https://w3c.github.io/ServiceWorker/#clients-getall
index 861196d..5fc93fd 100644 (file)
@@ -1,6 +1,4 @@
 
 
-Harness Error (TIMEOUT), message = null
-
-TIMEOUT Update a registration on ServiceWorkerGlobalScope Test timed out
+FAIL Update a registration on ServiceWorkerGlobalScope assert_equals: events seen by the worker expected "updatefound,activate,fetch,message,updatefound,fetch" but got "updatefound,activate,fetch"
 
index 4cefcb9..13d4111 100644 (file)
@@ -1,3 +1,59 @@
+2017-12-02  Chris Dumez  <cdumez@apple.com>
+
+        Support serviceWorkerRegistration.update() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180215
+
+        Reviewed by Darin Adler.
+
+        Support serviceWorkerRegistration.update() inside service workers. The code paths
+        for job scheduling and resolution have been made thread safe by hopping to the right
+        thread when necessary. In particular, ServiceWorkerContainer always makes sure to to
+        a callOnMainThread() before calling methods on the SWClientConnection. Also, the
+        SWClientConnection relies on a new postTaskTo() method before calling methods on the
+        job.
+
+        Test: http/tests/workers/service/self_registration_update.html
+
+        * workers/service/SWClientConnection.cpp:
+        (WebCore::SWClientConnection::scheduleJob):
+        (WebCore::SWClientConnection::finishedFetchingScript):
+        (WebCore::SWClientConnection::failedFetchingScript):
+        (WebCore::SWClientConnection::jobRejectedInServer):
+        (WebCore::SWClientConnection::registrationJobResolvedInServer):
+        (WebCore::SWClientConnection::unregistrationJobResolvedInServer):
+        (WebCore::SWClientConnection::startScriptFetchForServer):
+        (WebCore::SWClientConnection::postMessageToServiceWorkerClient):
+        (WebCore::SWClientConnection::updateRegistrationState):
+        (WebCore::SWClientConnection::updateWorkerState):
+        (WebCore::SWClientConnection::fireUpdateFoundEvent):
+        (WebCore::SWClientConnection::notifyClientsOfControllerChange):
+        (WebCore::SWClientConnection::clearPendingJobs):
+        (WebCore::SWClientConnection::postTaskTo):
+        * workers/service/SWClientConnection.h:
+        * workers/service/ServiceWorkerContainer.cpp:
+        (WebCore::ServiceWorkerContainer::scheduleJob):
+        (WebCore::ServiceWorkerContainer::jobFailedWithException):
+        (WebCore::ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent):
+        (WebCore::ServiceWorkerContainer::jobResolvedWithRegistration):
+        (WebCore::ServiceWorkerContainer::jobResolvedWithUnregistrationResult):
+        (WebCore::ServiceWorkerContainer::startScriptFetchForJob):
+        (WebCore::ServiceWorkerContainer::jobFinishedLoadingScript):
+        (WebCore::ServiceWorkerContainer::jobFailedLoadingScript):
+        (WebCore::ServiceWorkerContainer::jobDidFinish):
+        (WebCore::ServiceWorkerContainer::addRegistration):
+        (WebCore::ServiceWorkerContainer::removeRegistration):
+        (WebCore::ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent):
+        (WebCore::ServiceWorkerContainer::contextIdentifier):
+        * workers/service/ServiceWorkerContainer.h:
+        * workers/service/ServiceWorkerJob.cpp:
+        (WebCore::ServiceWorkerJob::ServiceWorkerJob):
+        * workers/service/ServiceWorkerJob.h:
+        (WebCore::ServiceWorkerJob::contextIdentifier):
+        * workers/service/ServiceWorkerJobClient.h:
+        * workers/service/ServiceWorkerRegistration.cpp:
+        (WebCore::ServiceWorkerRegistration::update):
+        * workers/service/ServiceWorkerTypes.h:
+
 2017-12-02  Simon Fraser  <simon.fraser@apple.com>
 
         Make IOSurface::Locker and use it in ImageBufferDataCG
index ec50f65..b3f56c7 100644 (file)
@@ -47,6 +47,8 @@ SWClientConnection::~SWClientConnection() = default;
 
 void SWClientConnection::scheduleJob(ServiceWorkerJob& job)
 {
+    ASSERT(isMainThread());
+
     auto addResult = m_scheduledJobs.add(job.identifier(), &job);
     ASSERT_UNUSED(addResult, addResult.isNewEntry);
 
@@ -55,6 +57,7 @@ void SWClientConnection::scheduleJob(ServiceWorkerJob& job)
 
 void SWClientConnection::finishedFetchingScript(ServiceWorkerJob& job, const String& script)
 {
+    ASSERT(isMainThread());
     ASSERT(m_scheduledJobs.get(job.identifier()) == &job);
 
     finishFetchingScriptInServer({ job.data().identifier(), job.data().registrationKey(), script, { } });
@@ -62,6 +65,7 @@ void SWClientConnection::finishedFetchingScript(ServiceWorkerJob& job, const Str
 
 void SWClientConnection::failedFetchingScript(ServiceWorkerJob& job, const ResourceError& error)
 {
+    ASSERT(isMainThread());
     ASSERT(m_scheduledJobs.get(job.identifier()) == &job);
 
     finishFetchingScriptInServer({ job.data().identifier(), job.data().registrationKey(), { }, error });
@@ -69,40 +73,53 @@ void SWClientConnection::failedFetchingScript(ServiceWorkerJob& job, const Resou
 
 void SWClientConnection::jobRejectedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, const ExceptionData& exceptionData)
 {
+    ASSERT(isMainThread());
+
     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
     if (!job) {
         LOG_ERROR("Job %s rejected from server, but was not found", jobDataIdentifier.loggingString().utf8().data());
         return;
     }
 
-    job->failedWithException(exceptionData.toException());
+    postTaskTo(job->contextIdentifier(), [job, exceptionData = exceptionData.isolatedCopy()] {
+        job->failedWithException(exceptionData.toException());
+    });
 }
 
 void SWClientConnection::registrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, ServiceWorkerRegistrationData&& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
 {
+    ASSERT(isMainThread());
+
     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
     if (!job) {
         LOG_ERROR("Job %s resolved in server, but was not found", jobDataIdentifier.loggingString().utf8().data());
         return;
     }
 
-    auto key = registrationData.key;
-    job->resolvedWithRegistration(WTFMove(registrationData), shouldNotifyWhenResolved);
+    postTaskTo(job->contextIdentifier(), [job, registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved]() mutable {
+        job->resolvedWithRegistration(WTFMove(registrationData), shouldNotifyWhenResolved);
+    });
 }
 
 void SWClientConnection::unregistrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, bool unregistrationResult)
 {
+    ASSERT(isMainThread());
+
     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
     if (!job) {
         LOG_ERROR("Job %s resolved in server, but was not found", jobDataIdentifier.loggingString().utf8().data());
         return;
     }
 
-    job->resolvedWithUnregistrationResult(unregistrationResult);
+    postTaskTo(job->contextIdentifier(), [job, unregistrationResult] {
+        job->resolvedWithUnregistrationResult(unregistrationResult);
+    });
 }
 
 void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier)
 {
+    ASSERT(isMainThread());
+
     auto job = m_scheduledJobs.get(jobDataIdentifier.jobIdentifier);
     if (!job) {
         LOG_ERROR("Job %s instructed to start fetch from server, but job was not found", jobDataIdentifier.loggingString().utf8().data());
@@ -114,11 +131,15 @@ void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIde
         return;
     }
 
-    job->startScriptFetch();
+    postTaskTo(job->contextIdentifier(), [job] {
+        job->startScriptFetch();
+    });
 }
 
 void SWClientConnection::postMessageToServiceWorkerClient(DocumentIdentifier destinationContextIdentifier, Ref<SerializedScriptValue>&& message, ServiceWorkerData&& sourceData, const String& sourceOrigin)
 {
+    ASSERT(isMainThread());
+
     // FIXME: destinationContextIdentifier can only identify a Document at the moment.
     auto* destinationDocument = Document::allDocumentsMap().get(destinationContextIdentifier);
     if (!destinationDocument)
@@ -137,6 +158,8 @@ void SWClientConnection::postMessageToServiceWorkerClient(DocumentIdentifier des
 
 void SWClientConnection::updateRegistrationState(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
 {
+    ASSERT(isMainThread());
+
     SWContextManager::singleton().forEachServiceWorkerThread([identifier, state, &serviceWorkerData] (auto& workerThread) {
         workerThread.thread().runLoop().postTask([identifier, state, serviceWorkerData = crossThreadCopy(serviceWorkerData)](ScriptExecutionContext& context) mutable {
             if (auto* container = context.serviceWorkerContainer())
@@ -152,6 +175,8 @@ void SWClientConnection::updateRegistrationState(ServiceWorkerRegistrationIdenti
 
 void SWClientConnection::updateWorkerState(ServiceWorkerIdentifier identifier, ServiceWorkerState state)
 {
+    ASSERT(isMainThread());
+
     SWContextManager::singleton().forEachServiceWorkerThread([identifier, state] (auto& workerThread) {
         workerThread.thread().runLoop().postTask([identifier, state](ScriptExecutionContext& context) {
             if (auto* serviceWorker = context.serviceWorker(identifier))
@@ -167,6 +192,8 @@ void SWClientConnection::updateWorkerState(ServiceWorkerIdentifier identifier, S
 
 void SWClientConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
 {
+    ASSERT(isMainThread());
+
     SWContextManager::singleton().forEachServiceWorkerThread([identifier] (auto& workerThread) {
         workerThread.thread().runLoop().postTask([identifier](ScriptExecutionContext& context) {
             if (auto* container = context.serviceWorkerContainer())
@@ -182,6 +209,7 @@ void SWClientConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifie
 
 void SWClientConnection::notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, ServiceWorkerData&& newController)
 {
+    ASSERT(isMainThread());
     ASSERT(!contextIdentifiers.isEmpty());
 
     for (auto& clientIdentifier : contextIdentifiers) {
@@ -200,9 +228,32 @@ void SWClientConnection::notifyClientsOfControllerChange(const HashSet<DocumentI
 
 void SWClientConnection::clearPendingJobs()
 {
+    ASSERT(isMainThread());
+
     auto jobs = WTFMove(m_scheduledJobs);
-    for (auto& job : jobs.values())
-        job->failedWithException(Exception { TypeError, ASCIILiteral("Internal error") });
+    for (auto& job : jobs.values()) {
+        postTaskTo(job->contextIdentifier(), [job] {
+            job->failedWithException(Exception { TypeError, ASCIILiteral("Internal error") });
+        });
+    }
+}
+
+void SWClientConnection::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void()>&& task)
+{
+    ASSERT(isMainThread());
+
+    switchOn(contextIdentifier, [&](DocumentIdentifier identifier) {
+        auto* document = Document::allDocumentsMap().get(identifier);
+        if (!document)
+            return;
+        document->postTask([task = WTFMove(task)](ScriptExecutionContext&) {
+            task();
+        });
+    }, [&](ServiceWorkerIdentifier identifier) {
+        SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](ServiceWorkerGlobalScope&) {
+            task();
+        });
+    });
 }
 
 } // namespace WebCore
index 59b6bb6..ead9ee8 100644 (file)
@@ -95,6 +95,7 @@ protected:
     WEBCORE_EXPORT void notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, ServiceWorkerData&& newController);
 
     WEBCORE_EXPORT void clearPendingJobs();
+    WEBCORE_EXPORT void postTaskTo(const DocumentOrWorkerIdentifier&, WTF::Function<void()>&&);
 
 private:
     virtual void scheduleJobInServer(const ServiceWorkerJobData&) = 0;
index 27df9a0..4f8423a 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(SERVICE_WORKER)
 
+#include "Document.h"
 #include "Event.h"
 #include "EventNames.h"
 #include "Exception.h"
 #include "ScriptExecutionContext.h"
 #include "SecurityOrigin.h"
 #include "ServiceWorker.h"
+#include "ServiceWorkerGlobalScope.h"
 #include "ServiceWorkerJob.h"
 #include "ServiceWorkerJobData.h"
 #include "ServiceWorkerProvider.h"
+#include "ServiceWorkerThread.h"
 #include "URL.h"
 #include <wtf/RunLoop.h>
 #include <wtf/Scope.h>
@@ -196,15 +199,20 @@ void ServiceWorkerContainer::updateRegistration(const URL& scopeURL, const URL&
 
 void ServiceWorkerContainer::scheduleJob(Ref<ServiceWorkerJob>&& job)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     ASSERT(m_swConnection);
 
     setPendingActivity(this);
 
-    ServiceWorkerJob& rawJob = job.get();
-    auto result = m_jobMap.add(rawJob.identifier(), WTFMove(job));
+    auto result = m_jobMap.add(job->identifier(), job.copyRef());
     ASSERT_UNUSED(result, result.isNewEntry);
 
-    m_swConnection->scheduleJob(rawJob);
+    callOnMainThread([connection = m_swConnection, job = WTFMove(job)] {
+        connection->scheduleJob(job);
+    });
 }
 
 void ServiceWorkerContainer::getRegistration(const String& clientURL, Ref<DeferredPromise>&& promise)
@@ -293,6 +301,10 @@ void ServiceWorkerContainer::startMessages()
 
 void ServiceWorkerContainer::jobFailedWithException(ServiceWorkerJob& job, const Exception& exception)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     if (auto* context = scriptExecutionContext()) {
         context->postTask([job = makeRef(job), exception](ScriptExecutionContext&) {
             job->promise().reject(exception);
@@ -303,12 +315,20 @@ void ServiceWorkerContainer::jobFailedWithException(ServiceWorkerJob& job, const
 
 void ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     if (auto* registration = m_registrations.get(identifier))
         registration->scheduleTaskToFireUpdateFoundEvent();
 }
 
 void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job, ServiceWorkerRegistrationData&& data, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     auto guard = WTF::makeScopeExit([this, &job] {
         jobDidFinish(job);
     });
@@ -345,6 +365,10 @@ void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job,
 
 void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJob& job, bool unregistrationResult)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     auto guard = WTF::makeScopeExit([this, &job] {
         jobDidFinish(job);
     });
@@ -362,12 +386,18 @@ void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJo
 
 void ServiceWorkerContainer::startScriptFetchForJob(ServiceWorkerJob& job)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     LOG(ServiceWorker, "SeviceWorkerContainer %p starting script fetch for job %s", this, job.identifier().loggingString().utf8().data());
 
     auto* context = scriptExecutionContext();
     if (!context) {
         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithRegistration called but the container's ScriptExecutionContext is gone");
-        m_swConnection->failedFetchingScript(job, { errorDomainWebKitInternal, 0, job.data().scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
+        callOnMainThread([connection = m_swConnection, job = makeRef(job)] {
+            connection->failedFetchingScript(job, { errorDomainWebKitInternal, 0, job->data().scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
+        });
         jobDidFinish(job);
         return;
     }
@@ -377,23 +407,39 @@ void ServiceWorkerContainer::startScriptFetchForJob(ServiceWorkerJob& job)
 
 void ServiceWorkerContainer::jobFinishedLoadingScript(ServiceWorkerJob& job, const String& script)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     LOG(ServiceWorker, "SeviceWorkerContainer %p finished fetching script for job %s", this, job.identifier().loggingString().utf8().data());
 
-    m_swConnection->finishedFetchingScript(job, script);
+    callOnMainThread([connection = m_swConnection, job = makeRef(job), script = script.isolatedCopy()] {
+        connection->finishedFetchingScript(job, script);
+    });
 }
 
 void ServiceWorkerContainer::jobFailedLoadingScript(ServiceWorkerJob& job, const ResourceError& error, std::optional<Exception>&& exception)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     LOG(ServiceWorker, "SeviceWorkerContainer %p failed fetching script for job %s", this, job.identifier().loggingString().utf8().data());
 
     if (exception)
         job.promise().reject(*exception);
 
-    m_swConnection->failedFetchingScript(job, error);
+    callOnMainThread([connection = m_swConnection, job = makeRef(job), error = error.isolatedCopy()] {
+        connection->failedFetchingScript(job, error);
+    });
 }
 
 void ServiceWorkerContainer::jobDidFinish(ServiceWorkerJob& job)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     auto taken = m_jobMap.take(job.identifier());
     ASSERT_UNUSED(taken, !taken || taken->ptr() == &job);
 
@@ -429,18 +475,30 @@ SWClientConnection& ServiceWorkerContainer::ensureSWClientConnection()
 
 void ServiceWorkerContainer::addRegistration(ServiceWorkerRegistration& registration)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     ensureSWClientConnection().addServiceWorkerRegistrationInServer(registration.identifier());
     m_registrations.add(registration.identifier(), &registration);
 }
 
 void ServiceWorkerContainer::removeRegistration(ServiceWorkerRegistration& registration)
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     m_swConnection->removeServiceWorkerRegistrationInServer(registration.identifier());
     m_registrations.remove(registration.identifier());
 }
 
 void ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent()
 {
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
     if (m_isStopped)
         return;
 
@@ -458,6 +516,18 @@ void ServiceWorkerContainer::stop()
     removeAllEventListeners();
 }
 
+DocumentOrWorkerIdentifier ServiceWorkerContainer::contextIdentifier()
+{
+#ifndef NDEBUG
+    ASSERT(m_creationThread == currentThread());
+#endif
+
+    ASSERT(scriptExecutionContext());
+    if (is<ServiceWorkerGlobalScope>(*scriptExecutionContext()))
+        return downcast<ServiceWorkerGlobalScope>(*scriptExecutionContext()).thread().identifier();
+    return downcast<Document>(*scriptExecutionContext()).identifier();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 812af34..2d9a9f0 100644 (file)
@@ -96,6 +96,8 @@ private:
     void jobDidFinish(ServiceWorkerJob&);
 
     SWServerConnectionIdentifier connectionIdentifier() final;
+    DocumentOrWorkerIdentifier contextIdentifier() final;
+
     SWClientConnection& ensureSWClientConnection();
 
     const char* activeDOMObjectName() const final;
index 8336a78..f73f907 100644 (file)
@@ -41,6 +41,7 @@ ServiceWorkerJob::ServiceWorkerJob(ServiceWorkerJobClient& client, Ref<DeferredP
     : m_client(client)
     , m_jobData(WTFMove(jobData))
     , m_promise(WTFMove(promise))
+    , m_contextIdentifier(client.contextIdentifier())
 {
 }
 
index c8a970a..c30a4fb 100644 (file)
@@ -68,6 +68,8 @@ public:
 
     void fetchScriptWithContext(ScriptExecutionContext&);
 
+    const DocumentOrWorkerIdentifier& contextIdentifier() { return m_contextIdentifier; }
+
 private:
     ServiceWorkerJob(ServiceWorkerJobClient&, Ref<DeferredPromise>&&, ServiceWorkerJobData&&);
 
@@ -83,7 +85,7 @@ private:
 
     bool m_completed { false };
 
-    Ref<RunLoop> m_runLoop { RunLoop::current() };
+    DocumentOrWorkerIdentifier m_contextIdentifier;
     RefPtr<WorkerScriptLoader> m_scriptLoader;
     ResourceResponse m_lastResponse;
 
index 415a69c..f103b27 100644 (file)
@@ -41,6 +41,8 @@ class ServiceWorkerJobClient {
 public:
     virtual ~ServiceWorkerJobClient() = default;
 
+    virtual DocumentOrWorkerIdentifier contextIdentifier() = 0;
+
     virtual void jobFailedWithException(ServiceWorkerJob&, const Exception&) = 0;
     virtual void jobResolvedWithRegistration(ServiceWorkerJob&, ServiceWorkerRegistrationData&&, ShouldNotifyWhenResolved) = 0;
     virtual void jobResolvedWithUnregistrationResult(ServiceWorkerJob&, bool unregistrationResult) = 0;
index bf0b479..f2890ea 100644 (file)
@@ -121,18 +121,6 @@ void ServiceWorkerRegistration::update(Ref<DeferredPromise>&& promise)
         return;
     }
 
-    // FIXME: Add support in workers.
-    if (!is<Document>(*context)) {
-        promise->reject(Exception { NotSupportedError, ASCIILiteral("serviceWorkerRegistration.update() is not yet supported in workers") });
-        return;
-    }
-
-    auto* container = context->serviceWorkerContainer();
-    if (!container) {
-        promise->reject(Exception(InvalidStateError));
-        return;
-    }
-
     auto* newestWorker = getNewestWorker();
     if (!newestWorker) {
         promise->reject(Exception(InvalidStateError, ASCIILiteral("newestWorker is null")));
@@ -140,7 +128,7 @@ void ServiceWorkerRegistration::update(Ref<DeferredPromise>&& promise)
     }
 
     // FIXME: Support worker types.
-    container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, WTFMove(promise));
+    m_container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, WTFMove(promise));
 }
 
 void ServiceWorkerRegistration::unregister(Ref<DeferredPromise>&& promise)
index ddb305d..8d152d3 100644 (file)
 
 #if ENABLE(SERVICE_WORKER)
 
+#include "DocumentIdentifier.h"
+#include "ServiceWorkerIdentifier.h"
 #include <wtf/EnumTraits.h>
 #include <wtf/ObjectIdentifier.h>
+#include <wtf/Variant.h>
 
 namespace WebCore {
 
@@ -67,6 +70,8 @@ using SWServerToContextConnectionIdentifier = ObjectIdentifier<SWServerToContext
 enum SWServerConnectionIdentifierType { };
 using SWServerConnectionIdentifier = ObjectIdentifier<SWServerConnectionIdentifierType>;
 
+using DocumentOrWorkerIdentifier = Variant<DocumentIdentifier, ServiceWorkerIdentifier>;
+
 } // namespace WebCore
 
 namespace WTF {