Implement https://w3c.github.io/ServiceWorker/#clients-get
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Dec 2017 00:00:40 +0000 (00:00 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Dec 2017 00:00:40 +0000 (00:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180167

Patch by Youenn Fablet <youenn@apple.com> on 2017-12-01
Reviewed by Chris Dumez.

Source/WebCore:

Test: http/tests/workers/service/serviceworkerclients-get.https.html

Implement clients get by having service worker clients do the following:
- Go to main thread to query the SWClientConnection for getting the client.
- SWClientConnection requests it through IPC to StorageProcess SWServer.
- SWServer looks at its client map and returns client data based on the given identifier.
- SWClientConnection sends it back to the right clients for resolving the promise.

Identifier is parsed at service worker process level.

Made ServiceWorkerClients no longer an ActiveDOMObject since it is owned by ServiceWorkerGlobalScope
and is only exposed in service workers.

* workers/service/ServiceWorkerClientIdentifier.h:
(WebCore::ServiceWorkerClientIdentifier::fromString):
* workers/service/ServiceWorkerClients.cpp:
(WebCore::ServiceWorkerClients::ServiceWorkerClients):
(WebCore::ServiceWorkerClients::get):
* workers/service/ServiceWorkerClients.h:
(WebCore::ServiceWorkerClients::create):
* workers/service/context/SWContextManager.cpp:
(WebCore::SWContextManager::postTaskToServiceWorker):
* workers/service/context/SWContextManager.h:
* workers/service/server/SWServer.cpp:
(WebCore::SWServer::getClientFromId):
* workers/service/server/SWServer.h:
* workers/service/server/SWServerToContextConnection.cpp:
(WebCore::SWServerToContextConnection::findClientByIdentifier):
* workers/service/server/SWServerToContextConnection.h:
* workers/service/server/SWServerWorker.cpp:
(WebCore::SWServerWorker::origin const):
(WebCore::SWServerWorker::findClientByIdentifier):
* workers/service/server/SWServerWorker.h:

Source/WebKit:

Add IPC plumbery for clientFromId between ServiceWorker process and Storage process.

* StorageProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
(WebKit::WebSWServerToContextConnection::clientFromIdCompleted):
* StorageProcess/ServiceWorker/WebSWServerToContextConnection.h:
* StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::findClientByIdentifier):
(WebKit::WebSWContextManagerConnection::findClientByIdentifierCompleted):
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.messages.in:

Source/WTF:

* wtf/text/StringView.h:
(WTF::StringView::toUInt64Strict const):
* wtf/text/WTFString.h:

LayoutTests:

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

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

28 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/workers/service/resources/serviceworkerclients-get-worker.js [new file with mode: 0644]
LayoutTests/http/tests/workers/service/serviceworkerclients-get.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/workers/service/serviceworkerclients-get.https.html [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/text/StringView.h
Source/WTF/wtf/text/WTFString.h
Source/WebCore/ChangeLog
Source/WebCore/workers/service/ServiceWorkerClientIdentifier.h
Source/WebCore/workers/service/ServiceWorkerClients.cpp
Source/WebCore/workers/service/ServiceWorkerClients.h
Source/WebCore/workers/service/ServiceWorkerClients.idl
Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp
Source/WebCore/workers/service/context/SWContextManager.cpp
Source/WebCore/workers/service/context/SWContextManager.h
Source/WebCore/workers/service/server/SWServer.cpp
Source/WebCore/workers/service/server/SWServer.h
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 f134fd7..8b20918 100644 (file)
@@ -1,3 +1,14 @@
+2017-12-01  Youenn Fablet  <youenn@apple.com>
+
+        Implement https://w3c.github.io/ServiceWorker/#clients-get
+        https://bugs.webkit.org/show_bug.cgi?id=180167
+
+        Reviewed by Chris Dumez.
+
+        * http/tests/workers/service/resources/serviceworkerclients-get-worker.js: Added.
+        * http/tests/workers/service/serviceworkerclients-get.https-expected.txt: Added.
+        * http/tests/workers/service/serviceworkerclients-get.https.html: Added.
+
 2017-12-01  Ryan Haddad  <ryanhaddad@apple.com>
 
         Update TestExpectations for editing/input tests on iOS.
diff --git a/LayoutTests/http/tests/workers/service/resources/serviceworkerclients-get-worker.js b/LayoutTests/http/tests/workers/service/resources/serviceworkerclients-get-worker.js
new file mode 100644 (file)
index 0000000..ca3bba2
--- /dev/null
@@ -0,0 +1,41 @@
+var client = null;
+self.addEventListener("message", async (event) => {
+    try {
+        var client = event.source;
+
+        if (!(client instanceof WindowClient)) {
+            event.source.postMessage("FAIL: client source is not a WindowClient");
+            return;
+        }
+
+        var retrievedClient = await self.clients.get(client.id);
+        if (!retrievedClient) {
+            event.source.postMessage("FAIL: did not retrieve any client through self.clients.get");
+            return;
+        }
+
+        if (retrievedClient.id !== client.id) {
+            event.source.postMessage("FAIL: client id is different from retrieved client id through self.clients.get: " + client.id + " / " + retrievedClient.id);
+            return;
+        }
+
+        if (retrievedClient !== client) {
+            event.source.postMessage("FAIL: client is different from the one retrieved through self.clients.get");
+            return;
+        }
+
+        var badIds = [client.id + "0", "0-0", "0-1", "1-0", "0-", "-", "-0"];
+        for (id in badIds) {
+            retrievedClient = await self.clients.get(id);
+            if (!!retrievedClient) {
+                event.source.postMessage("FAIL: retrieved client with bad id " + id + " should be null");
+                return;
+            }
+        }
+
+        event.source.postMessage("PASS");
+    } catch (e) {
+        event.source.postMessage("FAIL: received exception " + e);
+    }
+});
+
diff --git a/LayoutTests/http/tests/workers/service/serviceworkerclients-get.https-expected.txt b/LayoutTests/http/tests/workers/service/serviceworkerclients-get.https-expected.txt
new file mode 100644 (file)
index 0000000..c7f366b
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing clients.get 
+
diff --git a/LayoutTests/http/tests/workers/service/serviceworkerclients-get.https.html b/LayoutTests/http/tests/workers/service/serviceworkerclients-get.https.html
new file mode 100644 (file)
index 0000000..9bbfba3
--- /dev/null
@@ -0,0 +1,27 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+
+promise_test(async (test) => {
+    var promise = new Promise((resolve) => {
+        navigator.serviceWorker.addEventListener("message", test.step_func((event) => {
+            assert_equals(event.data, "PASS");
+            resolve();
+        }));
+    });
+
+    var registration = await navigator.serviceWorker.register("resources/serviceworkerclients-get-worker.js", { scope : "/noone" });
+    var worker = registration.installing;
+    if (!worker)
+        worker = registration.active;
+    worker.postMessage("start");
+
+    await promise;
+}, "Testing clients.get");
+</script>
+</body>
+</html>
index c090fbe..7d28bef 100644 (file)
@@ -1,3 +1,14 @@
+2017-12-01  Youenn Fablet  <youenn@apple.com>
+
+        Implement https://w3c.github.io/ServiceWorker/#clients-get
+        https://bugs.webkit.org/show_bug.cgi?id=180167
+
+        Reviewed by Chris Dumez.
+
+        * wtf/text/StringView.h:
+        (WTF::StringView::toUInt64Strict const):
+        * wtf/text/WTFString.h:
+
 2017-12-01  Brian Burg  <bburg@apple.com>
 
         Web Inspector: move Inspector::Protocol::Array<T> to JSON namespace
index 48f78dd..c88118b 100644 (file)
@@ -29,6 +29,7 @@
 #include <limits.h>
 #include <unicode/utypes.h>
 #include <wtf/Forward.h>
+#include <wtf/Optional.h>
 #include <wtf/RetainPtr.h>
 #include <wtf/Vector.h>
 #include <wtf/text/CString.h>
@@ -146,6 +147,7 @@ public:
     int toInt() const;
     int toInt(bool& isValid) const;
     int toIntStrict(bool& isValid) const;
+    std::optional<uint64_t> toUInt64Strict() const;
     float toFloat(bool& isValid) const;
 
     static void invalidate(const StringImpl&);
@@ -523,6 +525,13 @@ inline int StringView::toIntStrict(bool& isValid) const
     return charactersToIntStrict(characters16(), m_length, &isValid);
 }
 
+inline std::optional<uint64_t> StringView::toUInt64Strict() const
+{
+    bool isValid;
+    uint64_t result = is8Bit() ? charactersToUInt64Strict(characters8(), m_length, &isValid) : charactersToUInt64Strict(characters16(), m_length, &isValid);
+    return isValid ? std::make_optional(result) : std::nullopt;
+}
+
 inline String StringView::toStringWithoutCopying() const
 {
     if (is8Bit())
index c8a4dd6..d2b99bf 100644 (file)
@@ -46,8 +46,8 @@ WTF_EXPORT_STRING_API unsigned charactersToUIntStrict(const LChar*, size_t, bool
 WTF_EXPORT_STRING_API unsigned charactersToUIntStrict(const UChar*, size_t, bool* ok = nullptr, int base = 10);
 int64_t charactersToInt64Strict(const LChar*, size_t, bool* ok = nullptr, int base = 10);
 int64_t charactersToInt64Strict(const UChar*, size_t, bool* ok = nullptr, int base = 10);
-uint64_t charactersToUInt64Strict(const LChar*, size_t, bool* ok = nullptr, int base = 10);
-uint64_t charactersToUInt64Strict(const UChar*, size_t, bool* ok = nullptr, int base = 10);
+WTF_EXPORT_STRING_API uint64_t charactersToUInt64Strict(const LChar*, size_t, bool* ok = nullptr, int base = 10);
+WTF_EXPORT_STRING_API uint64_t charactersToUInt64Strict(const UChar*, size_t, bool* ok = nullptr, int base = 10);
 intptr_t charactersToIntPtrStrict(const LChar*, size_t, bool* ok = nullptr, int base = 10);
 intptr_t charactersToIntPtrStrict(const UChar*, size_t, bool* ok = nullptr, int base = 10);
 
index 561a0ef..f130e69 100644 (file)
@@ -1,3 +1,44 @@
+2017-12-01  Youenn Fablet  <youenn@apple.com>
+
+        Implement https://w3c.github.io/ServiceWorker/#clients-get
+        https://bugs.webkit.org/show_bug.cgi?id=180167
+
+        Reviewed by Chris Dumez.
+
+        Test: http/tests/workers/service/serviceworkerclients-get.https.html
+
+        Implement clients get by having service worker clients do the following:
+        - Go to main thread to query the SWClientConnection for getting the client.
+        - SWClientConnection requests it through IPC to StorageProcess SWServer.
+        - SWServer looks at its client map and returns client data based on the given identifier.
+        - SWClientConnection sends it back to the right clients for resolving the promise.
+
+        Identifier is parsed at service worker process level.
+
+        Made ServiceWorkerClients no longer an ActiveDOMObject since it is owned by ServiceWorkerGlobalScope
+        and is only exposed in service workers.
+
+        * workers/service/ServiceWorkerClientIdentifier.h:
+        (WebCore::ServiceWorkerClientIdentifier::fromString):
+        * workers/service/ServiceWorkerClients.cpp:
+        (WebCore::ServiceWorkerClients::ServiceWorkerClients):
+        (WebCore::ServiceWorkerClients::get):
+        * workers/service/ServiceWorkerClients.h:
+        (WebCore::ServiceWorkerClients::create):
+        * workers/service/context/SWContextManager.cpp:
+        (WebCore::SWContextManager::postTaskToServiceWorker):
+        * workers/service/context/SWContextManager.h:
+        * workers/service/server/SWServer.cpp:
+        (WebCore::SWServer::getClientFromId):
+        * workers/service/server/SWServer.h:
+        * workers/service/server/SWServerToContextConnection.cpp:
+        (WebCore::SWServerToContextConnection::findClientByIdentifier):
+        * workers/service/server/SWServerToContextConnection.h:
+        * workers/service/server/SWServerWorker.cpp:
+        (WebCore::SWServerWorker::origin const):
+        (WebCore::SWServerWorker::findClientByIdentifier):
+        * workers/service/server/SWServerWorker.h:
+
 2017-12-01  Brian Burg  <bburg@apple.com>
 
         Web Inspector: move Inspector::Protocol::Array<T> to JSON namespace
index 62855d5..0d0e92e 100644 (file)
@@ -40,6 +40,7 @@ struct ServiceWorkerClientIdentifier {
     unsigned hash() const;
 
     String toString() const { return String::number(serverConnectionIdentifier.toUInt64()) + "-" +  String::number(contextIdentifier.toUInt64()); }
+    static std::optional<ServiceWorkerClientIdentifier> fromString(StringView);
 
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static std::optional<ServiceWorkerClientIdentifier> decode(Decoder&);
@@ -50,6 +51,23 @@ inline bool operator==(const ServiceWorkerClientIdentifier& a, const ServiceWork
     return a.serverConnectionIdentifier == b.serverConnectionIdentifier &&  a.contextIdentifier == b.contextIdentifier;
 }
 
+inline std::optional<ServiceWorkerClientIdentifier> ServiceWorkerClientIdentifier::fromString(StringView string)
+{
+    ServiceWorkerClientIdentifier clientIdentifier;
+
+    unsigned counter = 0;
+    for (auto item : string.split('-')) {
+        auto identifier = item.toUInt64Strict();
+        if (!identifier || !*identifier)
+            return std::nullopt;
+        if (!counter++)
+            clientIdentifier.serverConnectionIdentifier = makeObjectIdentifier<SWServerConnectionIdentifierType>(identifier.value());
+        else if (counter == 2)
+            clientIdentifier.contextIdentifier = makeObjectIdentifier<DocumentIdentifierType>(identifier.value());
+    }
+    return (counter == 2) ? std::make_optional(WTFMove(clientIdentifier)) : std::nullopt;
+}
+
 template<class Encoder>
 void ServiceWorkerClientIdentifier::encode(Encoder& encoder) const
 {
index a14557e..a4ebc7a 100644 (file)
 #include "ServiceWorkerClients.h"
 
 #include "JSDOMPromiseDeferred.h"
+#include "JSServiceWorkerWindowClient.h"
+#include "ServiceWorkerGlobalScope.h"
 
 namespace WebCore {
 
-ServiceWorkerClients::ServiceWorkerClients(ScriptExecutionContext& context)
-    : ActiveDOMObject(&context)
+static inline void didFinishGetRequest(ServiceWorkerGlobalScope& scope, DeferredPromise& promise, ServiceWorkerClientIdentifier identifier, ExceptionOr<std::optional<ServiceWorkerClientData>>&& clientData)
 {
-    suspendIfNeeded();
-}
+    if (clientData.hasException()) {
+        promise.reject(clientData.releaseException());
+        return;
+    }
+    auto data = clientData.releaseReturnValue();
+    if (!data) {
+        promise.resolve();
+        return;
+    }
 
-const char* ServiceWorkerClients::activeDOMObjectName() const
-{
-    return "ServiceWorkerClients";
+    promise.resolve<IDLInterface<ServiceWorkerClient>>(ServiceWorkerClient::getOrCreate(scope, identifier, WTFMove(data.value())));
 }
 
-bool ServiceWorkerClients::canSuspendForDocumentSuspension() const
+void ServiceWorkerClients::get(ScriptExecutionContext& context, const String& id, Ref<DeferredPromise>&& promise)
 {
-    return !hasPendingActivity();
-}
+    auto identifier = ServiceWorkerClientIdentifier::fromString(id);
+    if (!identifier) {
+        promise->resolve();
+        return;
+    }
+    auto clientIdentifier = identifier.value();
 
-void ServiceWorkerClients::get(const String& id, Ref<DeferredPromise>&& promise)
-{
-    UNUSED_PARAM(id);
-    promise->reject(Exception { NotSupportedError, ASCIILiteral("clients.get() is not yet supported") });
+    auto serviceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
+
+    auto promisePointer = promise.ptr();
+    m_pendingPromises.add(promisePointer, WTFMove(promise));
+
+    callOnMainThread([promisePointer, serviceWorkerIdentifier, clientIdentifier] () {
+        auto connection = SWContextManager::singleton().connection();
+        connection->findClientByIdentifier(serviceWorkerIdentifier, clientIdentifier, [promisePointer, serviceWorkerIdentifier, clientIdentifier] (auto&& clientData) {
+            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promisePointer, clientIdentifier, data = crossThreadCopy(clientData)] (auto& context) mutable {
+                if (auto promise = context.clients().m_pendingPromises.take(promisePointer))
+                    didFinishGetRequest(context, *promise, clientIdentifier, WTFMove(data));
+            });
+        });
+    });
 }
 
 void ServiceWorkerClients::matchAll(const ClientQueryOptions&, Ref<DeferredPromise>&& promise)
index 74bbb7f..9cac0f6 100644 (file)
 
 #if ENABLE(SERVICE_WORKER)
 
-#include "ActiveDOMObject.h"
 #include "ServiceWorkerClientType.h"
+#include "ServiceWorkerIdentifier.h"
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
 
 namespace WebCore {
 
 class DeferredPromise;
+class ScriptExecutionContext;
+struct ServiceWorkerClientData;
+struct ServiceWorkerClientIdentifier;
 
-class ServiceWorkerClients : public RefCounted<ServiceWorkerClients>, public ActiveDOMObject {
+class ServiceWorkerClients : public RefCounted<ServiceWorkerClients> {
 public:
-    static Ref<ServiceWorkerClients> create(ScriptExecutionContext& context)
+    static Ref<ServiceWorkerClients> create()
     {
-        return adoptRef(*new ServiceWorkerClients(context));
+        return adoptRef(*new ServiceWorkerClients);
     }
 
     struct ClientQueryOptions {
@@ -48,17 +51,15 @@ public:
         ServiceWorkerClientType type { ServiceWorkerClientType::Window };
     };
 
-    void get(const String& id, Ref<DeferredPromise>&&);
+    void get(ScriptExecutionContext&, const String& id, Ref<DeferredPromise>&&);
     void matchAll(const ClientQueryOptions&, Ref<DeferredPromise>&&);
     void openWindow(const String& url, Ref<DeferredPromise>&&);
     void claim(Ref<DeferredPromise>&&);
 
 private:
-    explicit ServiceWorkerClients(ScriptExecutionContext&);
+    ServiceWorkerClients() = default;
 
-    // ActiveDOMObject.
-    const char* activeDOMObjectName() const final;
-    bool canSuspendForDocumentSuspension() const final;
+    HashMap<DeferredPromise*, Ref<DeferredPromise>> m_pendingPromises;
 };
 
 } // namespace WebCore
index 65293a0..48a2082 100644 (file)
     EnabledAtRuntime=ServiceWorker,
     Exposed=ServiceWorker,
     GenerateIsReachable=Impl,
+    ImplementationLacksVTable,
     InterfaceName=Clients,
 ] interface ServiceWorkerClients {
     // The objects returned will be new instances every time
-    [NewObject] Promise<any> get(DOMString id);
+    [NewObject, CallWith=ScriptExecutionContext] Promise<any> get(DOMString id);
     [NewObject] Promise<sequence<Client>> matchAll(optional ClientQueryOptions options);
     [NewObject] Promise<WindowClient?> openWindow(USVString url);
     [NewObject] Promise<void> claim();
index 29f03ba..0f230c2 100644 (file)
@@ -42,7 +42,7 @@ ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(const ServiceWorkerContextDat
     : WorkerGlobalScope(url, identifier, userAgent, isOnline, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), timeOrigin, connectionProxy, socketProvider, sessionID)
     , m_contextData(crossThreadCopy(data))
     , m_registration(ServiceWorkerRegistration::getOrCreate(*this, navigator().serviceWorker(), WTFMove(m_contextData.registration)))
-    , m_clients(ServiceWorkerClients::create(*this))
+    , m_clients(ServiceWorkerClients::create())
 {
 }
 
index ad3a570..8c2761d 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(SERVICE_WORKER)
 #include "SWContextManager.h"
 #include "ServiceWorkerClientIdentifier.h"
+#include "ServiceWorkerGlobalScope.h"
 
 namespace WebCore {
 
@@ -124,6 +125,15 @@ void SWContextManager::forEachServiceWorkerThread(const WTF::Function<void(Servi
         apply(*workerThread);
 }
 
+void SWContextManager::postTaskToServiceWorker(ServiceWorkerIdentifier identifier, WTF::Function<void(ServiceWorkerGlobalScope&)>&& task)
+{
+    if (auto* serviceWorker = m_workerMap.get(identifier)) {
+        serviceWorker->thread().runLoop().postTask([task = WTFMove(task)] (auto& context) {
+            task(downcast<ServiceWorkerGlobalScope>(context));
+        });
+    }
+}
+
 } // namespace WebCore
 
 #endif
index 1713115..7b0e8a6 100644 (file)
 #include "ExceptionOr.h"
 #include "ServiceWorkerIdentifier.h"
 #include "ServiceWorkerThreadProxy.h"
+#include <wtf/CompletionHandler.h>
 #include <wtf/HashMap.h>
 
 namespace WebCore {
 
 class SerializedScriptValue;
 struct ServiceWorkerClientIdentifier;
+class ServiceWorkerGlobalScope;
 
 class SWContextManager {
 public:
@@ -51,6 +53,9 @@ public:
         virtual void didFinishActivation(ServiceWorkerIdentifier) = 0;
         virtual void setServiceWorkerHasPendingEvents(ServiceWorkerIdentifier, bool) = 0;
         virtual void workerTerminated(ServiceWorkerIdentifier) = 0;
+
+        using FindClientByIdentifierCallback = WTF::CompletionHandler<void(ExceptionOr<std::optional<ServiceWorkerClientData>>&&)>;
+        virtual void findClientByIdentifier(ServiceWorkerIdentifier, ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) = 0;
     };
 
     WEBCORE_EXPORT void setConnection(std::unique_ptr<Connection>&&);
@@ -65,6 +70,8 @@ public:
 
     void forEachServiceWorkerThread(const WTF::Function<void(ServiceWorkerThreadProxy&)>&);
 
+    WEBCORE_EXPORT void postTaskToServiceWorker(ServiceWorkerIdentifier, WTF::Function<void(ServiceWorkerGlobalScope&)>&&);
+
 private:
     SWContextManager() = default;
 
index d77453b..0c37e2d 100644 (file)
@@ -312,6 +312,20 @@ void SWServer::didFinishActivation(SWServerWorker& worker)
         SWServerJobQueue::didFinishActivation(*registration, worker.identifier());
 }
 
+std::optional<ServiceWorkerClientData> SWServer::findClientByIdentifier(const ClientOrigin& origin, ServiceWorkerClientIdentifier clientIdentifier)
+{
+    auto iterator = m_clients.find(origin);
+    if (iterator == m_clients.end())
+        return std::nullopt;
+
+    auto& clients = iterator->value;
+    auto position = clients.findMatching([&] (const auto& client) {
+        return clientIdentifier == client.identifier;
+    });
+
+    return (position != notFound) ? std::make_optional(clients[position].data) : std::nullopt;
+}
+
 void SWServer::didResolveRegistrationPromise(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey)
 {
     ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
index cab8631..8ba8974 100644 (file)
@@ -138,6 +138,7 @@ public:
     void didFinishInstall(const std::optional<ServiceWorkerJobDataIdentifier>&, SWServerWorker&, bool wasSuccessful);
     void didFinishActivation(SWServerWorker&);
     void workerContextTerminated(SWServerWorker&);
+    std::optional<ServiceWorkerClientData> findClientByIdentifier(const ClientOrigin&, ServiceWorkerClientIdentifier);
 
     WEBCORE_EXPORT void serverToContextConnectionCreated();
     
index be85b39..737c78b 100644 (file)
@@ -108,6 +108,12 @@ void SWServerToContextConnection::workerTerminated(ServiceWorkerIdentifier servi
         worker->contextTerminated();
 }
 
+void SWServerToContextConnection::findClientByIdentifier(uint64_t requestIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, ServiceWorkerClientIdentifier clientId)
+{
+    if (auto* worker = SWServerWorker::existingWorkerForIdentifier(serviceWorkerIdentifier))
+        globalServerToContextConnection()->findClientByIdentifierCompleted(requestIdentifier, worker->findClientByIdentifier(clientId), false);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 1a1a7f6..47364c9 100644 (file)
@@ -34,6 +34,8 @@
 namespace WebCore {
 
 class SWServer;
+struct ServiceWorkerClientData;
+struct ServiceWorkerClientIdentifier;
 struct ServiceWorkerContextData;
 struct ServiceWorkerJobDataIdentifier;
 
@@ -49,6 +51,7 @@ public:
     virtual void fireActivateEvent(ServiceWorkerIdentifier) = 0;
     virtual void terminateWorker(ServiceWorkerIdentifier) = 0;
     virtual void syncTerminateWorker(ServiceWorkerIdentifier) = 0;
+    virtual void findClientByIdentifierCompleted(uint64_t requestIdentifier, const std::optional<ServiceWorkerClientData>&, bool hasSecurityError) = 0;
 
     // Messages back from the SW host process
     WEBCORE_EXPORT void scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>&, ServiceWorkerIdentifier, const String& message);
@@ -57,6 +60,7 @@ public:
     WEBCORE_EXPORT void didFinishActivation(ServiceWorkerIdentifier);
     WEBCORE_EXPORT void setServiceWorkerHasPendingEvents(ServiceWorkerIdentifier, bool hasPendingEvents);
     WEBCORE_EXPORT void workerTerminated(ServiceWorkerIdentifier);
+    WEBCORE_EXPORT void findClientByIdentifier(uint64_t clientIdRequestIdentifier, ServiceWorkerIdentifier, ServiceWorkerClientIdentifier);
 
     static SWServerToContextConnection* connectionForIdentifier(SWServerToContextConnectionIdentifier);
 
index 558cf54..bcf96dc 100644 (file)
@@ -73,6 +73,14 @@ void SWServerWorker::terminate()
     m_server.terminateWorker(*this);
 }
 
+const ClientOrigin& SWServerWorker::origin() const
+{
+    if (!m_origin)
+        m_origin = ClientOrigin { m_registrationKey.topOrigin(), SecurityOriginData::fromSecurityOrigin(SecurityOrigin::create(m_data.scriptURL)) };
+
+    return *m_origin;
+}
+
 void SWServerWorker::scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, const String& message)
 {
     m_server.scriptContextFailedToStart(jobDataIdentifier, *this, message);
@@ -98,6 +106,11 @@ void SWServerWorker::contextTerminated()
     m_server.workerContextTerminated(*this);
 }
 
+std::optional<ServiceWorkerClientData> SWServerWorker::findClientByIdentifier(ServiceWorkerClientIdentifier clientId)
+{
+    return m_server.findClientByIdentifier(origin(), clientId);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 7f56d37..df51f8f 100644 (file)
 
 namespace WebCore {
 
+struct ClientOrigin;
 class SWServer;
 class SWServerRegistration;
-enum class WorkerType;
+struct ServiceWorkerClientData;
+struct ServiceWorkerClientIdentifier;
 struct ServiceWorkerContextData;
 struct ServiceWorkerJobDataIdentifier;
+enum class WorkerType;
 
 class SWServerWorker : public RefCounted<SWServerWorker> {
 public:
@@ -83,11 +86,13 @@ public:
     void didFinishInstall(const std::optional<ServiceWorkerJobDataIdentifier>&, bool wasSuccessful);
     void didFinishActivation();
     void contextTerminated();
+    std::optional<ServiceWorkerClientData> findClientByIdentifier(ServiceWorkerClientIdentifier);
 
     WEBCORE_EXPORT static SWServerWorker* existingWorkerForIdentifier(ServiceWorkerIdentifier);
 
     const ServiceWorkerData& data() const { return m_data; }
     ServiceWorkerContextData contextData() const;
+    const ClientOrigin& origin() const;
 
 private:
     SWServerWorker(SWServer&, SWServerRegistration&, SWServerToContextConnectionIdentifier, const URL&, const String& script, WorkerType, ServiceWorkerIdentifier);
@@ -99,6 +104,7 @@ private:
     String m_script;
     bool m_hasPendingEvents { false };
     State m_state { State::NotRunning };
+    mutable std::optional<ClientOrigin> m_origin;
 };
 
 } // namespace WebCore
index ce61665..c464833 100644 (file)
@@ -1,3 +1,22 @@
+2017-12-01  Youenn Fablet  <youenn@apple.com>
+
+        Implement https://w3c.github.io/ServiceWorker/#clients-get
+        https://bugs.webkit.org/show_bug.cgi?id=180167
+
+        Reviewed by Chris Dumez.
+
+        Add IPC plumbery for clientFromId between ServiceWorker process and Storage process.
+
+        * StorageProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+        (WebKit::WebSWServerToContextConnection::clientFromIdCompleted):
+        * StorageProcess/ServiceWorker/WebSWServerToContextConnection.h:
+        * StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::findClientByIdentifier):
+        (WebKit::WebSWContextManagerConnection::findClientByIdentifierCompleted):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.messages.in:
+
 2017-12-01  Brian Burg  <bburg@apple.com>
 
         Web Inspector: move Inspector::Protocol::Array<T> to JSON namespace
index e5dcee2..1bb1c88 100644 (file)
@@ -81,6 +81,11 @@ void WebSWServerToContextConnection::syncTerminateWorker(ServiceWorkerIdentifier
     sendSync(Messages::WebSWContextManagerConnection::SyncTerminateWorker(serviceWorkerIdentifier), Messages::WebSWContextManagerConnection::SyncTerminateWorker::Reply());
 }
 
+void WebSWServerToContextConnection::findClientByIdentifierCompleted(uint64_t requestIdentifier, const std::optional<ServiceWorkerClientData>& data, bool hasSecurityError)
+{
+    send(Messages::WebSWContextManagerConnection::FindClientByIdentifierCompleted { requestIdentifier, data, hasSecurityError });
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(SERVICE_WORKER)
index 6ca0c03..9172e3f 100644 (file)
@@ -60,6 +60,7 @@ private:
     void fireActivateEvent(WebCore::ServiceWorkerIdentifier) final;
     void terminateWorker(WebCore::ServiceWorkerIdentifier) final;
     void syncTerminateWorker(WebCore::ServiceWorkerIdentifier) final;
+    void findClientByIdentifierCompleted(uint64_t requestIdentifier, const std::optional<WebCore::ServiceWorkerClientData>&, bool hasSecurityError) final;
 
     Ref<IPC::Connection> m_ipcConnection;
     
index 8b23478..0ef0188 100644 (file)
@@ -31,6 +31,7 @@ messages -> WebSWServerToContextConnection {
     DidFinishActivation(WebCore::ServiceWorkerIdentifier identifier);
     SetServiceWorkerHasPendingEvents(WebCore::ServiceWorkerIdentifier identifier, bool hasPendingEvents);
     WorkerTerminated(WebCore::ServiceWorkerIdentifier identifier);
+    FindClientByIdentifier(uint64_t requestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientIdentifier clientIdentifier);
 }
 
 #endif // ENABLE(SERVICE_WORKER)
index 5862da5..fd656c5 100644 (file)
@@ -48,6 +48,7 @@
 #include <WebCore/PageConfiguration.h>
 #include <WebCore/RuntimeEnabledFeatures.h>
 #include <WebCore/SerializedScriptValue.h>
+#include <WebCore/ServiceWorkerClientData.h>
 #include <WebCore/ServiceWorkerClientIdentifier.h>
 #include <pal/SessionID.h>
 
@@ -201,6 +202,24 @@ void WebSWContextManagerConnection::workerTerminated(ServiceWorkerIdentifier ser
     m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::WorkerTerminated(serviceWorkerIdentifier), 0);
 }
 
+void WebSWContextManagerConnection::findClientByIdentifier(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, ServiceWorkerClientIdentifier clientIdentifier, FindClientByIdentifierCallback&& callback)
+{
+    auto requestIdentifier = ++m_previousRequestIdentifier;
+    m_findClientByIdentifierRequests.add(requestIdentifier, WTFMove(callback));
+    m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::FindClientByIdentifier { requestIdentifier, serviceWorkerIdentifier, clientIdentifier }, 0);
+}
+
+void WebSWContextManagerConnection::findClientByIdentifierCompleted(uint64_t requestIdentifier, std::optional<ServiceWorkerClientData>&& clientData, bool hasSecurityError)
+{
+    if (auto callback = m_findClientByIdentifierRequests.take(requestIdentifier)) {
+        if (hasSecurityError) {
+            callback(Exception { SecurityError });
+            return;
+        }
+        callback(WTFMove(clientData));
+    }
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index ec1b029..597da4e 100644 (file)
@@ -63,6 +63,7 @@ private:
     void didFinishActivation(WebCore::ServiceWorkerIdentifier) final;
     void setServiceWorkerHasPendingEvents(WebCore::ServiceWorkerIdentifier, bool) final;
     void workerTerminated(WebCore::ServiceWorkerIdentifier) final;
+    void findClientByIdentifier(WebCore::ServiceWorkerIdentifier, WebCore::ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) final;
 
     // IPC messages.
     void serviceWorkerStartedWithMessage(std::optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, const String& exceptionMessage) final;
@@ -73,10 +74,14 @@ private:
     void fireActivateEvent(WebCore::ServiceWorkerIdentifier);
     void terminateWorker(WebCore::ServiceWorkerIdentifier);
     void syncTerminateWorker(WebCore::ServiceWorkerIdentifier, Ref<Messages::WebSWContextManagerConnection::SyncTerminateWorker::DelayedReply>&&);
+    void findClientByIdentifierCompleted(uint64_t requestIdentifier, std::optional<WebCore::ServiceWorkerClientData>&&, bool hasSecurityError);
 
     Ref<IPC::Connection> m_connectionToStorageProcess;
     uint64_t m_pageID { 0 };
     uint64_t m_previousServiceWorkerID { 0 };
+
+    HashMap<uint64_t, FindClientByIdentifierCallback> m_findClientByIdentifierRequests;
+    uint64_t m_previousRequestIdentifier { 0 };
 };
 
 } // namespace WebKit
index c8ac7d7..7a1715e 100644 (file)
@@ -30,6 +30,7 @@ messages -> WebSWContextManagerConnection {
     FireActivateEvent(WebCore::ServiceWorkerIdentifier identifier)
     TerminateWorker(WebCore::ServiceWorkerIdentifier identifier)
     SyncTerminateWorker(WebCore::ServiceWorkerIdentifier identifier) -> () Delayed
+    FindClientByIdentifierCompleted(uint64_t clientIdRequestIdentifier, std::optional<WebCore::ServiceWorkerClientData> data, bool hasSecurityError)
 }
 
 #endif