Support container.getRegistration() / getRegistrations() inside service workers
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Dec 2017 02:06:26 +0000 (02:06 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Dec 2017 02:06:26 +0000 (02:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180360

Reviewed by Youenn Fablet.

LayoutTests/imported/w3c:

* web-platform-tests/service-workers/service-worker/activation.https-expected.txt:
Rebaseline test with slightly different output.

* web-platform-tests/service-workers/service-worker/skip-waiting-without-using-registration.https-expected.txt:
* web-platform-tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html:
With my change, this test started running and passing a check before timing out. When investigating the time
out, I found out that this is caused by the test unregistering the worker while the test is still running
in the service worker, which causes the worker to terminate early. To address the issue, we no longer add
a cleanup step to unregister. The test now passes all checks.

Source/WebCore:

Support container.getRegistration() / getRegistrations() inside service workers
by making sure we hop to the right thread when needed.

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

* dom/ScriptExecutionContext.cpp:
(WebCore::ScriptExecutionContext::postTaskTo):
* dom/ScriptExecutionContext.h:
* workers/service/SWClientConnection.cpp:
(WebCore::SWClientConnection::jobRejectedInServer):
(WebCore::SWClientConnection::registrationJobResolvedInServer):
(WebCore::SWClientConnection::unregistrationJobResolvedInServer):
(WebCore::SWClientConnection::startScriptFetchForServer):
(WebCore::SWClientConnection::clearPendingJobs):
* workers/service/SWClientConnection.h:
* workers/service/ServiceWorkerContainer.cpp:
(WebCore::ServiceWorkerContainer::getRegistration):
(WebCore::ServiceWorkerContainer::didFinishGetRegistrationRequest):
(WebCore::ServiceWorkerContainer::getRegistrations):
(WebCore::ServiceWorkerContainer::didFinishGetRegistrationsRequest):
(WebCore::ServiceWorkerContainer::stop):
* workers/service/ServiceWorkerContainer.h:

Source/WebKit:

* WebProcess/Storage/WebSWClientConnection.cpp:
(WebKit::WebSWClientConnection::didMatchRegistration):
(WebKit::WebSWClientConnection::didGetRegistrations):
(WebKit::WebSWClientConnection::matchRegistration):
(WebKit::WebSWClientConnection::getRegistrations):

LayoutTests:

Add layout test coverage.

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

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration.html [new file with mode: 0644]
LayoutTests/http/tests/workers/service/resources/ServiceWorkerGlobalScope_getRegistration-worker.js [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/activation.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/skip-waiting-using-registration.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/skip-waiting-using-registration.https.html
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/skip-waiting-without-using-registration.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html
Source/WebCore/ChangeLog
Source/WebCore/dom/ScriptExecutionContext.cpp
Source/WebCore/dom/ScriptExecutionContext.h
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/WebKit/ChangeLog
Source/WebKit/WebProcess/Storage/WebSWClientConnection.cpp

index 7240fae..61cb175 100644 (file)
@@ -1,3 +1,16 @@
+2017-12-04  Chris Dumez  <cdumez@apple.com>
+
+        Support container.getRegistration() / getRegistrations() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180360
+
+        Reviewed by Youenn Fablet.
+
+        Add layout test coverage.
+
+        * http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration-expected.txt: Added.
+        * http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration.html: Added.
+        * http/tests/workers/service/resources/ServiceWorkerGlobalScope_getRegistration-worker.js: Added.
+
 2017-12-04  Nan Wang  <n_wang@apple.com>
 
         AX: AOM: Implement relation type properties
index 0aee6e7..03cfaa4 100644 (file)
@@ -161,7 +161,6 @@ imported/w3c/web-platform-tests/service-workers/service-worker/postmessage-msgpo
 webkit.org/b/179452 imported/w3c/web-platform-tests/service-workers/service-worker/register-same-scope-different-script-url.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/respond-with-body-accessed-response.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/sandboxed-iframe-fetch-event.https.html [ Skip ]
-imported/w3c/web-platform-tests/service-workers/service-worker/skip-waiting-using-registration.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/unregister-then-register-new-script.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/update-after-navigation-fetch-event.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
diff --git a/LayoutTests/http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration-expected.txt b/LayoutTests/http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration-expected.txt
new file mode 100644 (file)
index 0000000..985d19e
--- /dev/null
@@ -0,0 +1,8 @@
+* Tests that serviceWorker.getRegistration() / getRegistrations() inside service workers
+
+PASS: Registration succeeded
+PASS: getRegistration() returned the right registration
+PASS: getRegistrations() returned 2 registrations
+PASS: getRegistrations()[0] is the right registration
+PASS: getRegistrations()[1] is the right registration
+
diff --git a/LayoutTests/http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration.html b/LayoutTests/http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration.html
new file mode 100644 (file)
index 0000000..58059f5
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="resources/sw-test-pre.js"></script>
+<script>
+log("* Tests that serviceWorker.getRegistration() / getRegistrations() inside service workers");
+log("");
+
+navigator.serviceWorker.addEventListener("message", function(event) {
+    if (event.data === "DONE") {
+        finishSWTest();
+        return;
+    }
+    log(event.data);
+});
+
+navigator.serviceWorker.register("resources/ServiceWorkerGlobalScope_getRegistration-worker.js", { }).then(function(registration) {
+    registration.installing.postMessage("runTest");
+});
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/workers/service/resources/ServiceWorkerGlobalScope_getRegistration-worker.js b/LayoutTests/http/tests/workers/service/resources/ServiceWorkerGlobalScope_getRegistration-worker.js
new file mode 100644 (file)
index 0000000..a3e0c6d
--- /dev/null
@@ -0,0 +1,49 @@
+let client = null;
+
+function log(msg)
+{
+    client.postMessage(msg);
+}
+
+async function runTest()
+{
+    try {
+        let r = await navigator.serviceWorker.register("empty-worker.js", { scope: "/test" });
+        log("PASS: Registration succeeded");
+
+        let retrievedRegistration = await navigator.serviceWorker.getRegistration("/test");
+        if (r === retrievedRegistration)
+            log("PASS: getRegistration() returned the right registration");
+        else
+            log("FAIL: getRegistration() did not return the right registration");
+
+        let retrievedRegistrations = await navigator.serviceWorker.getRegistrations();
+        if (retrievedRegistrations.length === 2)
+            log("PASS: getRegistrations() returned 2 registrations");
+        else {
+            log("FAIL: getRegistrations() returned " + retrievedRegistrations.length + " registration(s)");
+            log("DONE");
+            return;
+        }
+
+        if (retrievedRegistrations[0] === self.registration)
+            log("PASS: getRegistrations()[0] is the right registration");
+        else
+            log("FAIL: getRegistrations()[0] is not the right registration");
+
+        if (retrievedRegistrations[1] === r)
+            log("PASS: getRegistrations()[1] is the right registration");
+        else
+            log("FAIL: getRegistrations()[1] is not the right registration");
+
+        log("DONE");
+    } catch (e) {
+        log("FAIL: " + e);
+        log("DONE");
+    }
+}
+
+self.addEventListener("message", (event) => {
+    client = event.source;
+    runTest();
+});
index 9b7342a..b88efb2 100644 (file)
@@ -1,3 +1,20 @@
+2017-12-04  Chris Dumez  <cdumez@apple.com>
+
+        Support container.getRegistration() / getRegistrations() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180360
+
+        Reviewed by Youenn Fablet.
+
+        * web-platform-tests/service-workers/service-worker/activation.https-expected.txt:
+        Rebaseline test with slightly different output.
+
+        * web-platform-tests/service-workers/service-worker/skip-waiting-without-using-registration.https-expected.txt:
+        * web-platform-tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html:
+        With my change, this test started running and passing a check before timing out. When investigating the time
+        out, I found out that this is caused by the test unregistering the worker while the test is still running
+        in the service worker, which causes the worker to terminate early. To address the issue, we no longer add
+        a cleanup step to unregister. The test now passes all checks.
+
 2017-12-03  Chris Dumez  <cdumez@apple.com>
 
         Re-sync Service Workers web-platform-tests from upstream
index a9c398a..c265b03 100644 (file)
@@ -50,9 +50,6 @@ promise_test(function(t) {
         })
       .then(function(registration) {
           sw_registration = registration;
-          t.add_cleanup(function() {
-              registration.unregister();
-            });
           return saw_controllerchanged;
         })
       .then(function() {
index 812adc2..0ea9d58 100644 (file)
@@ -1,3 +1,4 @@
 
-FAIL Test skipWaiting while a client is not being controlled promise_test: Unhandled rejection with value: object "TypeError: null is not an object (evaluating 'document.body.appendChild')"
+PASS Test skipWaiting while a client is not being controlled 
+PASS skipWaiting 
 
index dbe2bde..da1e334 100644 (file)
@@ -26,9 +26,6 @@ promise_test(function(t) {
         })
       .then(function(registration) {
           sw_registration = registration;
-          t.add_cleanup(function() {
-              registration.unregister();
-            });
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
index 49db782..a23caf2 100644 (file)
@@ -1,3 +1,33 @@
+2017-12-04  Chris Dumez  <cdumez@apple.com>
+
+        Support container.getRegistration() / getRegistrations() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180360
+
+        Reviewed by Youenn Fablet.
+
+        Support container.getRegistration() / getRegistrations() inside service workers
+        by making sure we hop to the right thread when needed.
+
+        Test: http/tests/workers/service/ServiceWorkerGlobalScope_getRegistration.html
+
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ScriptExecutionContext::postTaskTo):
+        * dom/ScriptExecutionContext.h:
+        * workers/service/SWClientConnection.cpp:
+        (WebCore::SWClientConnection::jobRejectedInServer):
+        (WebCore::SWClientConnection::registrationJobResolvedInServer):
+        (WebCore::SWClientConnection::unregistrationJobResolvedInServer):
+        (WebCore::SWClientConnection::startScriptFetchForServer):
+        (WebCore::SWClientConnection::clearPendingJobs):
+        * workers/service/SWClientConnection.h:
+        * workers/service/ServiceWorkerContainer.cpp:
+        (WebCore::ServiceWorkerContainer::getRegistration):
+        (WebCore::ServiceWorkerContainer::didFinishGetRegistrationRequest):
+        (WebCore::ServiceWorkerContainer::getRegistrations):
+        (WebCore::ServiceWorkerContainer::didFinishGetRegistrationsRequest):
+        (WebCore::ServiceWorkerContainer::stop):
+        * workers/service/ServiceWorkerContainer.h:
+
 2017-12-04  Simon Fraser  <simon.fraser@apple.com>
 
         Cleanup code that computes iframe content offsets in FrameView
index b6e85ea..362a782 100644 (file)
 #include "RejectedPromiseTracker.h"
 #include "ResourceRequest.h"
 #include "SWClientConnection.h"
+#include "SWContextManager.h"
 #include "ScriptState.h"
 #include "ServiceWorker.h"
+#include "ServiceWorkerGlobalScope.h"
 #include "ServiceWorkerProvider.h"
 #include "Settings.h"
 #include "WorkerGlobalScope.h"
@@ -582,6 +584,24 @@ ServiceWorkerContainer* ScriptExecutionContext::serviceWorkerContainer()
 
     return navigator ? &navigator->serviceWorker() : nullptr;
 }
+
+void ScriptExecutionContext::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void(ScriptExecutionContext&)>&& task)
+{
+    ASSERT(isMainThread());
+
+    switchOn(contextIdentifier, [&] (DocumentIdentifier identifier) {
+        auto* document = Document::allDocumentsMap().get(identifier);
+        if (!document)
+            return;
+        document->postTask([task = WTFMove(task)](auto& scope) {
+            task(scope);
+        });
+    }, [&](ServiceWorkerIdentifier identifier) {
+        SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](auto& scope) {
+            task(scope);
+        });
+    });
+}
 #endif
 
 } // namespace WebCore
index 18dd9c7..1c15772 100644 (file)
@@ -30,7 +30,7 @@
 #include "ActiveDOMObject.h"
 #include "DOMTimer.h"
 #include "SecurityContext.h"
-#include "ServiceWorkerIdentifier.h"
+#include "ServiceWorkerTypes.h"
 #include <heap/HandleTypes.h>
 #include <runtime/ConsoleTypes.h>
 #include <wtf/CrossThreadTask.h>
@@ -246,6 +246,8 @@ public:
     ServiceWorker* serviceWorker(ServiceWorkerIdentifier identifier) { return m_serviceWorkers.get(identifier); }
 
     ServiceWorkerContainer* serviceWorkerContainer();
+
+    WEBCORE_EXPORT static void postTaskTo(const DocumentOrWorkerIdentifier&, WTF::Function<void(ScriptExecutionContext&)>&&);
 #endif
 
 protected:
index b3f56c7..6fe98f5 100644 (file)
@@ -81,7 +81,7 @@ void SWClientConnection::jobRejectedInServer(const ServiceWorkerJobDataIdentifie
         return;
     }
 
-    postTaskTo(job->contextIdentifier(), [job, exceptionData = exceptionData.isolatedCopy()] {
+    ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, exceptionData = exceptionData.isolatedCopy()](ScriptExecutionContext&) {
         job->failedWithException(exceptionData.toException());
     });
 }
@@ -96,7 +96,7 @@ void SWClientConnection::registrationJobResolvedInServer(const ServiceWorkerJobD
         return;
     }
 
-    postTaskTo(job->contextIdentifier(), [job, registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved]() mutable {
+    ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved](ScriptExecutionContext&) mutable {
         job->resolvedWithRegistration(WTFMove(registrationData), shouldNotifyWhenResolved);
     });
 }
@@ -111,7 +111,7 @@ void SWClientConnection::unregistrationJobResolvedInServer(const ServiceWorkerJo
         return;
     }
 
-    postTaskTo(job->contextIdentifier(), [job, unregistrationResult] {
+    ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, unregistrationResult](ScriptExecutionContext&) {
         job->resolvedWithUnregistrationResult(unregistrationResult);
     });
 }
@@ -131,7 +131,7 @@ void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIde
         return;
     }
 
-    postTaskTo(job->contextIdentifier(), [job] {
+    ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job](ScriptExecutionContext&) {
         job->startScriptFetch();
     });
 }
@@ -232,30 +232,12 @@ void SWClientConnection::clearPendingJobs()
 
     auto jobs = WTFMove(m_scheduledJobs);
     for (auto& job : jobs.values()) {
-        postTaskTo(job->contextIdentifier(), [job] {
+        ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job](ScriptExecutionContext&) {
             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
 
 #endif // ENABLE(SERVICE_WORKER)
index d411ebf..3177f29 100644 (file)
@@ -98,7 +98,6 @@ 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 56ecbc4..85daf2e 100644 (file)
@@ -218,32 +218,47 @@ void ServiceWorkerContainer::getRegistration(const String& clientURL, Ref<Deferr
         return;
     }
 
-    // FIXME: Add support in workers.
-    if (!is<Document>(*context)) {
-        promise->reject(Exception { NotSupportedError, ASCIILiteral("serviceWorker.getRegistration() is not yet supported in workers") });
-        return;
-    }
-
     URL parsedURL = context->completeURL(clientURL);
     if (!protocolHostAndPortAreEqual(parsedURL, context->url())) {
         promise->reject(Exception { SecurityError, ASCIILiteral("Origin of clientURL is not client's origin") });
         return;
     }
 
-    return ensureSWClientConnection().matchRegistration(context->topOrigin(), parsedURL, [promise = WTFMove(promise), protectingThis = makePendingActivity(*this), this] (auto&& result) mutable {
-        if (m_isStopped)
-            return;
+    uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
+    auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
+    m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
 
-        if (!result) {
-            promise->resolve();
-            return;
-        }
-
-        auto registration = ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(result.value()));
-        promise->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
+    auto contextIdentifier = this->contextIdentifier();
+    callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context->topOrigin().isolatedCopy(), parsedURL = parsedURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
+        connection->matchRegistration(topOrigin, parsedURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& result) mutable {
+            ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, result = crossThreadCopy(result)](ScriptExecutionContext&) mutable {
+                didFinishGetRegistrationRequest(pendingPromiseIdentifier, WTFMove(result));
+            });
+        });
     });
 }
 
+void ServiceWorkerContainer::didFinishGetRegistrationRequest(uint64_t pendingPromiseIdentifier, std::optional<ServiceWorkerRegistrationData>&& result)
+{
+#ifndef NDEBUG
+    ASSERT(m_creationThread.ptr() == &Thread::current());
+#endif
+
+    auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
+    if (!pendingPromise)
+        return;
+
+    ASSERT(!m_isStopped);
+
+    if (!result) {
+        pendingPromise->promise->resolve();
+        return;
+    }
+
+    auto registration = ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(result.value()));
+    pendingPromise->promise->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
+}
+
 void ServiceWorkerContainer::scheduleTaskToUpdateRegistrationState(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
 {
     auto* context = scriptExecutionContext();
@@ -260,33 +275,46 @@ void ServiceWorkerContainer::scheduleTaskToUpdateRegistrationState(ServiceWorker
     });
 }
 
-void ServiceWorkerContainer::getRegistrations(RegistrationsPromise&& promise)
+void ServiceWorkerContainer::getRegistrations(Ref<DeferredPromise>&& promise)
 {
     auto* context = scriptExecutionContext();
     if (!context) {
-        promise.reject(Exception { InvalidStateError });
+        promise->reject(Exception { InvalidStateError });
         return;
     }
 
-    // FIXME: Add support in workers.
-    if (!is<Document>(*context)) {
-        promise.reject(Exception { NotSupportedError, ASCIILiteral("serviceWorker.getRegistrations() is not yet supported in workers") });
-        return;
-    }
+    uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
+    auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
+    m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
 
-    return ensureSWClientConnection().getRegistrations(context->topOrigin(), context->url(), [this, pendingActivity = makePendingActivity(*this), promise = WTFMove(promise)] (auto&& registrationDatas) mutable {
-        if (m_isStopped)
-            return;
+    auto contextIdentifier = this->contextIdentifier();
+    auto contextURL = context->url();
+    callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context->topOrigin().isolatedCopy(), contextURL = contextURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
+        connection->getRegistrations(topOrigin, contextURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& registrationDatas) mutable {
+            ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, registrationDatas = crossThreadCopy(registrationDatas)](ScriptExecutionContext&) mutable {
+                didFinishGetRegistrationsRequest(pendingPromiseIdentifier, WTFMove(registrationDatas));
+            });
+        });
+    });
+}
 
-        Vector<Ref<ServiceWorkerRegistration>> registrations;
-        registrations.reserveInitialCapacity(registrationDatas.size());
-        for (auto& registrationData : registrationDatas) {
-            auto registration = ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(registrationData));
-            registrations.uncheckedAppend(WTFMove(registration));
-        }
+void ServiceWorkerContainer::didFinishGetRegistrationsRequest(uint64_t pendingPromiseIdentifier, Vector<ServiceWorkerRegistrationData>&& registrationDatas)
+{
+#ifndef NDEBUG
+    ASSERT(m_creationThread.ptr() == &Thread::current());
+#endif
+
+    auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
+    if (!pendingPromise)
+        return;
 
-        promise.resolve(WTFMove(registrations));
+    ASSERT(!m_isStopped);
+
+    auto registrations = WTF::map(WTFMove(registrationDatas), [&] (auto&& registrationData) {
+        return ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(registrationData));
     });
+
+    pendingPromise->promise->resolve<IDLSequence<IDLInterface<ServiceWorkerRegistration>>>(WTFMove(registrations));
 }
 
 void ServiceWorkerContainer::startMessages()
@@ -508,6 +536,7 @@ void ServiceWorkerContainer::stop()
 {
     m_isStopped = true;
     removeAllEventListeners();
+    m_pendingPromises.clear();
 }
 
 DocumentOrWorkerIdentifier ServiceWorkerContainer::contextIdentifier()
index ea12e8c..14163f9 100644 (file)
@@ -68,8 +68,7 @@ public:
     void scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier);
     void scheduleTaskToFireControllerChangeEvent();
 
-    using RegistrationsPromise = DOMPromiseDeferred<IDLSequence<IDLInterface<ServiceWorkerRegistration>>>;
-    void getRegistrations(RegistrationsPromise&&);
+    void getRegistrations(Ref<DeferredPromise>&&);
 
     ServiceWorkerRegistration* registration(ServiceWorkerRegistrationIdentifier identifier) const { return m_registrations.get(identifier); }
 
@@ -95,6 +94,9 @@ private:
 
     void jobDidFinish(ServiceWorkerJob&);
 
+    void didFinishGetRegistrationRequest(uint64_t requestIdentifier, std::optional<ServiceWorkerRegistrationData>&&);
+    void didFinishGetRegistrationsRequest(uint64_t requestIdentifier, Vector<ServiceWorkerRegistrationData>&&);
+
     SWServerConnectionIdentifier connectionIdentifier() final;
     DocumentOrWorkerIdentifier contextIdentifier() final;
 
@@ -121,6 +123,19 @@ private:
 #ifndef NDEBUG
     Ref<Thread> m_creationThread { Thread::current() };
 #endif
+
+    struct PendingPromise {
+        PendingPromise(Ref<DeferredPromise>&& promise, Ref<PendingActivity<ServiceWorkerContainer>>&& pendingActivity)
+            : promise(WTFMove(promise))
+            , pendingActivity(WTFMove(pendingActivity))
+        { }
+
+        Ref<DeferredPromise> promise;
+        Ref<PendingActivity<ServiceWorkerContainer>> pendingActivity;
+    };
+
+    uint64_t m_lastPendingPromiseIdentifier { 0 };
+    HashMap<uint64_t, std::unique_ptr<PendingPromise>> m_pendingPromises;
 };
 
 } // namespace WebCore
index bc27400..9cfbcf7 100644 (file)
@@ -1,3 +1,16 @@
+2017-12-04  Chris Dumez  <cdumez@apple.com>
+
+        Support container.getRegistration() / getRegistrations() inside service workers
+        https://bugs.webkit.org/show_bug.cgi?id=180360
+
+        Reviewed by Youenn Fablet.
+
+        * WebProcess/Storage/WebSWClientConnection.cpp:
+        (WebKit::WebSWClientConnection::didMatchRegistration):
+        (WebKit::WebSWClientConnection::didGetRegistrations):
+        (WebKit::WebSWClientConnection::matchRegistration):
+        (WebKit::WebSWClientConnection::getRegistrations):
+
 2017-12-04  Brady Eidson  <beidson@apple.com>
 
         Followup to:
index 12cdd0a..2bb1b18 100644 (file)
@@ -137,18 +137,24 @@ void WebSWClientConnection::initializeSWOriginTableAsEmpty()
 
 void WebSWClientConnection::didMatchRegistration(uint64_t matchingRequest, std::optional<ServiceWorkerRegistrationData>&& result)
 {
+    ASSERT(isMainThread());
+
     if (auto completionHandler = m_ongoingMatchRegistrationTasks.take(matchingRequest))
         completionHandler(WTFMove(result));
 }
 
 void WebSWClientConnection::didGetRegistrations(uint64_t matchingRequest, Vector<ServiceWorkerRegistrationData>&& registrations)
 {
+    ASSERT(isMainThread());
+
     if (auto completionHandler = m_ongoingGetRegistrationsTasks.take(matchingRequest))
         completionHandler(WTFMove(registrations));
 }
 
 void WebSWClientConnection::matchRegistration(const SecurityOrigin& topOrigin, const URL& clientURL, RegistrationCallback&& callback)
 {
+    ASSERT(isMainThread());
+
     if (!mayHaveServiceWorkerRegisteredForOrigin(topOrigin)) {
         callback(std::nullopt);
         return;
@@ -161,6 +167,8 @@ void WebSWClientConnection::matchRegistration(const SecurityOrigin& topOrigin, c
 
 void WebSWClientConnection::getRegistrations(const SecurityOrigin& topOrigin, const URL& clientURL, GetRegistrationsCallback&& callback)
 {
+    ASSERT(isMainThread());
+
     if (!mayHaveServiceWorkerRegisteredForOrigin(topOrigin)) {
         callback({ });
         return;