Close service worker database on network process suspension
[WebKit-https.git] / Source / WebCore / workers / service / server / SWServer.cpp
index cc422d2..2a283f4 100644 (file)
 #include "ExceptionCode.h"
 #include "ExceptionData.h"
 #include "Logging.h"
+#include "RegistrationStore.h"
+#include "SWOriginStore.h"
 #include "SWServerJobQueue.h"
 #include "SWServerRegistration.h"
+#include "SWServerToContextConnection.h"
 #include "SWServerWorker.h"
 #include "SecurityOrigin.h"
+#include "ServiceWorkerClientType.h"
 #include "ServiceWorkerContextData.h"
 #include "ServiceWorkerFetchResult.h"
 #include "ServiceWorkerJobData.h"
+#include <wtf/CompletionHandler.h>
+#include <wtf/NeverDestroyed.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
-static ServiceWorkerIdentifier generateServiceWorkerIdentifier()
+static Seconds terminationDelay { 10_s };
+
+SWServer::Connection::Connection(SWServer& server)
+    : m_server(server)
+    , m_identifier(SWServerConnectionIdentifier::generate())
 {
-    static uint64_t identifier = 0;
-    return makeObjectIdentifier<ServiceWorkerIdentifierType>(++identifier);
 }
 
-SWServer::Connection::Connection(SWServer& server, uint64_t identifier)
-    : Identified(identifier)
-    , m_server(server)
+HashSet<SWServer*>& SWServer::allServers()
 {
-    m_server.registerConnection(*this);
+    static NeverDestroyed<HashSet<SWServer*>> servers;
+    return servers;
 }
 
-SWServer::Connection::~Connection()
+SWServer::~SWServer()
 {
-    m_server.unregisterConnection(*this);
+    // Destroy the remaining connections before the SWServer gets destroyed since they have a raw pointer
+    // to the server and since they try to unregister clients from the server in their destructor.
+    auto connections = WTFMove(m_connections);
+    connections.clear();
+
+    allServers().remove(this);
 }
 
-SWServer::~SWServer()
+SWServerWorker* SWServer::workerByID(ServiceWorkerIdentifier identifier) const
+{
+    auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier);
+    ASSERT(!worker || &worker->server() == this);
+    return worker;
+}
+
+Optional<ServiceWorkerClientData> SWServer::serviceWorkerClientWithOriginByID(const ClientOrigin& clientOrigin, const ServiceWorkerClientIdentifier& clientIdentifier) const
 {
-    RELEASE_ASSERT(m_connections.isEmpty());
-    RELEASE_ASSERT(m_registrations.isEmpty());
-    RELEASE_ASSERT(m_jobQueues.isEmpty());
+    auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
+    if (iterator == m_clientIdentifiersPerOrigin.end())
+        return WTF::nullopt;
 
-    ASSERT(m_taskQueue.isEmpty());
-    ASSERT(m_taskReplyQueue.isEmpty());
+    if (!iterator->value.identifiers.contains(clientIdentifier))
+        return WTF::nullopt;
 
-    // For a SWServer to be cleanly shut down its thread must have finished and gone away.
-    // At this stage in development of the feature that actually never happens.
-    // But once it does start happening, this ASSERT will catch us doing it wrong.
-    Locker<Lock> locker(m_taskThreadLock);
-    ASSERT(!m_taskThread);
+    auto clientIterator = m_clientsById.find(clientIdentifier);
+    ASSERT(clientIterator != m_clientsById.end());
+    return clientIterator->value;
+}
+
+String SWServer::serviceWorkerClientUserAgent(const ClientOrigin& clientOrigin) const
+{
+    auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
+    if (iterator == m_clientIdentifiersPerOrigin.end())
+        return String();
+    return iterator->value.userAgent;
+}
+
+SWServerWorker* SWServer::activeWorkerFromRegistrationID(ServiceWorkerRegistrationIdentifier identifier)
+{
+    auto* registration = m_registrationsByID.get(identifier);
+    return registration ? registration->activeWorker() : nullptr;
 }
 
 SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationKey& registrationKey)
@@ -81,81 +112,197 @@ SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationK
     return m_registrations.get(registrationKey);
 }
 
+void SWServer::registrationStoreImportComplete()
+{
+    ASSERT(!m_importCompleted);
+    m_importCompleted = true;
+    m_originStore->importComplete();
+
+    auto clearCallbacks = WTFMove(m_clearCompletionCallbacks);
+    for (auto& callback : clearCallbacks)
+        callback();
+
+    performGetOriginsWithRegistrationsCallbacks();
+}
+
+void SWServer::registrationStoreDatabaseFailedToOpen()
+{
+    if (!m_importCompleted)
+        registrationStoreImportComplete();
+}
+
+void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data)
+{
+    // Pages should not have been able to make a new registration to this key while the import was still taking place.
+    ASSERT(!m_registrations.contains(data.registration.key));
+
+    auto registration = std::make_unique<SWServerRegistration>(*this, data.registration.key, data.registration.updateViaCache, data.registration.scopeURL, data.scriptURL);
+    registration->setLastUpdateTime(data.registration.lastUpdateTime);
+    auto registrationPtr = registration.get();
+    addRegistration(WTFMove(registration));
+
+    auto worker = SWServerWorker::create(*this, *registrationPtr, data.scriptURL, data.script, data.contentSecurityPolicy, WTFMove(data.referrerPolicy), data.workerType, data.serviceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript> { data.scriptResourceMap });
+    registrationPtr->updateRegistrationState(ServiceWorkerRegistrationState::Active, worker.ptr());
+    worker->setState(ServiceWorkerState::Activated);
+}
+
 void SWServer::addRegistration(std::unique_ptr<SWServerRegistration>&& registration)
 {
     auto key = registration->key();
-    m_registrations.add(key, WTFMove(registration));
+    auto* registrationPtr = registration.get();
+    auto addResult1 = m_registrations.add(key, WTFMove(registration));
+    ASSERT_UNUSED(addResult1, addResult1.isNewEntry);
+
+    auto addResult2 = m_registrationsByID.add(registrationPtr->identifier(), registrationPtr);
+    ASSERT_UNUSED(addResult2, addResult2.isNewEntry);
+
+    m_originStore->add(key.topOrigin());
 }
 
-void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& registrationKey)
+void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& key)
 {
-    m_registrations.remove(registrationKey);
+    auto topOrigin = key.topOrigin();
+    auto registration = m_registrations.take(key);
+    ASSERT(registration);
+    bool wasRemoved = m_registrationsByID.remove(registration->identifier());
+    ASSERT_UNUSED(wasRemoved, wasRemoved);
+
+    m_originStore->remove(topOrigin);
+    if (m_registrationStore)
+        m_registrationStore->removeRegistration(*registration);
 }
 
-void SWServer::clear()
+Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityOriginData& topOrigin, const URL& clientURL)
 {
-    m_jobQueues.clear();
-    m_registrations.clear();
-    // FIXME: We should probably ask service workers to terminate.
+    Vector<SWServerRegistration*> matchingRegistrations;
+    for (auto& item : m_registrations) {
+        if (!item.value->isUninstalling() && item.key.originIsMatching(topOrigin, clientURL))
+            matchingRegistrations.append(item.value.get());
+    }
+    // The specification mandates that registrations are returned in the insertion order.
+    std::sort(matchingRegistrations.begin(), matchingRegistrations.end(), [](auto& a, auto& b) {
+        return a->creationTime() < b->creationTime();
+    });
+    Vector<ServiceWorkerRegistrationData> matchingRegistrationDatas;
+    matchingRegistrationDatas.reserveInitialCapacity(matchingRegistrations.size());
+    for (auto* registration : matchingRegistrations)
+        matchingRegistrationDatas.uncheckedAppend(registration->data());
+    return matchingRegistrationDatas;
 }
 
-void SWServer::Connection::scheduleJobInServer(const ServiceWorkerJobData& jobData)
+void SWServer::clearAll(CompletionHandler<void()>&& completionHandler)
 {
-    LOG(ServiceWorker, "Scheduling ServiceWorker job %" PRIu64 "-%" PRIu64 " in server", jobData.connectionIdentifier(), jobData.identifier());
-    ASSERT(identifier() == jobData.connectionIdentifier());
+    if (!m_importCompleted) {
+        m_clearCompletionCallbacks.append([this, completionHandler = WTFMove(completionHandler)] () mutable {
+            ASSERT(m_importCompleted);
+            clearAll(WTFMove(completionHandler));
+        });
+        return;
+    }
 
-    m_server.scheduleJob(jobData);
+    m_jobQueues.clear();
+    while (!m_registrations.isEmpty())
+        m_registrations.begin()->value->clear();
+    ASSERT(m_registrationsByID.isEmpty());
+    m_pendingContextDatas.clear();
+    m_originStore->clearAll();
+    if (m_registrationStore)
+        m_registrationStore->clearAll(WTFMove(completionHandler));
 }
 
-void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
+void SWServer::startSuspension(CompletionHandler<void()>&& completionHandler)
 {
-    m_server.scriptFetchFinished(*this, result);
+    if (m_registrationStore)
+        m_registrationStore->startSuspension(WTFMove(completionHandler));
 }
 
-void SWServer::Connection::didFinishInstall(const ServiceWorkerRegistrationKey& key, ServiceWorkerIdentifier serviceWorkerIdentifier, bool wasSuccessful)
+void SWServer::endSuspension()
 {
-    m_server.didFinishInstall(*this, key, serviceWorkerIdentifier, wasSuccessful);
+    if (m_registrationStore)
+        m_registrationStore->endSuspension();
 }
 
-void SWServer::Connection::didFinishActivation(const ServiceWorkerRegistrationKey& key, ServiceWorkerIdentifier serviceWorkerIdentifier)
+void SWServer::clear(const SecurityOriginData& securityOrigin, CompletionHandler<void()>&& completionHandler)
 {
-    m_server.didFinishActivation(*this, key, serviceWorkerIdentifier);
+    if (!m_importCompleted) {
+        m_clearCompletionCallbacks.append([this, securityOrigin, completionHandler = WTFMove(completionHandler)] () mutable {
+            ASSERT(m_importCompleted);
+            clear(securityOrigin, WTFMove(completionHandler));
+        });
+        return;
+    }
+
+    m_jobQueues.removeIf([&](auto& keyAndValue) {
+        return keyAndValue.key.relatesToOrigin(securityOrigin);
+    });
+
+    Vector<SWServerRegistration*> registrationsToRemove;
+    for (auto& keyAndValue : m_registrations) {
+        if (keyAndValue.key.relatesToOrigin(securityOrigin))
+            registrationsToRemove.append(keyAndValue.value.get());
+    }
+
+    for (auto& contextDatas : m_pendingContextDatas.values()) {
+        contextDatas.removeAllMatching([&](auto& contextData) {
+            return contextData.registration.key.relatesToOrigin(securityOrigin);
+        });
+    }
+
+    if (registrationsToRemove.isEmpty()) {
+        completionHandler();
+        return;
+    }
+
+    // Calling SWServerRegistration::clear() takes care of updating m_registrations, m_originStore and m_registrationStore.
+    for (auto* registration : registrationsToRemove)
+        registration->clear();
+
+    if (m_registrationStore)
+        m_registrationStore->flushChanges(WTFMove(completionHandler));
 }
 
-void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key)
+void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
 {
-    m_server.didResolveRegistrationPromise(*this, key);
+    m_server.scriptFetchFinished(*this, result);
 }
 
-void SWServer::Connection::addServiceWorkerRegistrationInServer(const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
+void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key)
 {
-    m_server.addClientServiceWorkerRegistration(*this, key, identifier);
+    m_server.didResolveRegistrationPromise(*this, key);
 }
 
-void SWServer::Connection::removeServiceWorkerRegistrationInServer(const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
+void SWServer::Connection::addServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier)
 {
-    m_server.removeClientServiceWorkerRegistration(*this, key, identifier);
+    m_server.addClientServiceWorkerRegistration(*this, identifier);
 }
 
-void SWServer::Connection::scriptContextFailedToStart(const ServiceWorkerRegistrationKey& registrationKey, ServiceWorkerIdentifier identifier, const String& message)
+void SWServer::Connection::removeServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier)
 {
-    m_server.scriptContextFailedToStart(*this, registrationKey, identifier, message);
+    m_server.removeClientServiceWorkerRegistration(*this, identifier);
 }
 
-void SWServer::Connection::scriptContextStarted(const ServiceWorkerRegistrationKey& registrationKey, ServiceWorkerIdentifier identifier)
+void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifier)
 {
-    m_server.scriptContextStarted(*this, registrationKey, identifier);
+    if (auto* worker = m_server.workerByID(identifier))
+        m_server.syncTerminateWorker(*worker);
 }
 
-SWServer::SWServer()
+SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore, String&& registrationDatabaseDirectory, PAL::SessionID sessionID)
+    : m_originStore(WTFMove(originStore))
+    , m_sessionID(sessionID)
 {
-    m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
-        taskThreadEntryPoint();
-    });
+    ASSERT(!registrationDatabaseDirectory.isEmpty() || m_sessionID.isEphemeral());
+    if (!m_sessionID.isEphemeral())
+        m_registrationStore = std::make_unique<RegistrationStore>(*this, WTFMove(registrationDatabaseDirectory));
+    else
+        registrationStoreImportComplete();
+
+    UNUSED_PARAM(registrationDatabaseDirectory);
+    allServers().add(this);
 }
 
 // https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
-void SWServer::scheduleJob(const ServiceWorkerJobData& jobData)
+void SWServer::scheduleJob(ServiceWorkerJobData&& jobData)
 {
     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
 
@@ -173,22 +320,22 @@ void SWServer::scheduleJob(const ServiceWorkerJobData& jobData)
 
 void SWServer::rejectJob(const ServiceWorkerJobData& jobData, const ExceptionData& exceptionData)
 {
-    LOG(ServiceWorker, "Rejected ServiceWorker job %" PRIu64 "-%" PRIu64 " in server", jobData.connectionIdentifier(), jobData.identifier());
+    LOG(ServiceWorker, "Rejected ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
     auto* connection = m_connections.get(jobData.connectionIdentifier());
     if (!connection)
         return;
 
-    connection->rejectJobInClient(jobData.identifier(), exceptionData);
+    connection->rejectJobInClient(jobData.identifier().jobIdentifier, exceptionData);
 }
 
 void SWServer::resolveRegistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
 {
-    LOG(ServiceWorker, "Resolved ServiceWorker job %" PRIu64 "-%" PRIu64 " in server with registration %s", jobData.connectionIdentifier(), jobData.identifier(), registrationData.identifier.loggingString().utf8().data());
+    LOG(ServiceWorker, "Resolved ServiceWorker job %s in server with registration %s", jobData.identifier().loggingString().utf8().data(), registrationData.identifier.loggingString().utf8().data());
     auto* connection = m_connections.get(jobData.connectionIdentifier());
     if (!connection)
         return;
 
-    connection->resolveRegistrationJobInClient(jobData.identifier(), registrationData, shouldNotifyWhenResolved);
+    connection->resolveRegistrationJobInClient(jobData.identifier().jobIdentifier, registrationData, shouldNotifyWhenResolved);
 }
 
 void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
@@ -197,24 +344,23 @@ void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, con
     if (!connection)
         return;
 
-    connection->resolveUnregistrationJobInClient(jobData.identifier(), registrationKey, unregistrationResult);
+    connection->resolveUnregistrationJobInClient(jobData.identifier().jobIdentifier, registrationKey, unregistrationResult);
 }
 
-void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData)
+void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData, FetchOptions::Cache cachePolicy)
 {
-    LOG(ServiceWorker, "Server issuing startScriptFetch for current job %" PRIu64 "-%" PRIu64 " in client", jobData.connectionIdentifier(), jobData.identifier());
+    LOG(ServiceWorker, "Server issuing startScriptFetch for current job %s in client", jobData.identifier().loggingString().utf8().data());
     auto* connection = m_connections.get(jobData.connectionIdentifier());
-    if (!connection)
-        return;
-
-    connection->startScriptFetchInClient(jobData.identifier());
+    ASSERT_WITH_MESSAGE(connection, "If the connection was lost, this job should have been cancelled");
+    if (connection)
+        connection->startScriptFetchInClient(jobData.identifier().jobIdentifier, jobData.registrationKey(), cachePolicy);
 }
 
 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
 {
-    LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %" PRIu64 "-%" PRIu64 " in client", result.connectionIdentifier, result.jobIdentifier);
+    LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %s in client", result.jobDataIdentifier.loggingString().utf8().data());
 
-    ASSERT(m_connections.contains(result.connectionIdentifier));
+    ASSERT(m_connections.contains(result.jobDataIdentifier.connectionIdentifier));
 
     auto jobQueue = m_jobQueues.get(result.registrationKey);
     if (!jobQueue)
@@ -223,153 +369,371 @@ void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFe
     jobQueue->scriptFetchFinished(connection, result);
 }
 
-void SWServer::scriptContextFailedToStart(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, ServiceWorkerIdentifier identifier, const String& message)
+void SWServer::scriptContextFailedToStart(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, const String& message)
 {
-    ASSERT(m_connections.contains(connection.identifier()));
-    
-    if (auto* jobQueue = m_jobQueues.get(registrationKey))
-        jobQueue->scriptContextFailedToStart(connection, identifier, message);
+    if (!jobDataIdentifier)
+        return;
+
+    RELEASE_LOG_ERROR(ServiceWorker, "%p - SWServer::scriptContextFailedToStart: Failed to start SW for job %s, error: %s", this, jobDataIdentifier->loggingString().utf8().data(), message.utf8().data());
+
+    auto* jobQueue = m_jobQueues.get(worker.registrationKey());
+    if (!jobQueue || !jobQueue->isCurrentlyProcessingJob(*jobDataIdentifier)) {
+        // The job which started this worker has been canceled, terminate this worker.
+        terminatePreinstallationWorker(worker);
+        return;
+    }
+    jobQueue->scriptContextFailedToStart(*jobDataIdentifier, worker.identifier(), message);
 }
 
-void SWServer::scriptContextStarted(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, ServiceWorkerIdentifier identifier)
+void SWServer::scriptContextStarted(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker)
 {
-    ASSERT(m_connections.contains(connection.identifier()));
+    if (!jobDataIdentifier)
+        return;
 
-    if (auto* jobQueue = m_jobQueues.get(registrationKey))
-        jobQueue->scriptContextStarted(connection, identifier);
+    auto* jobQueue = m_jobQueues.get(worker.registrationKey());
+    if (!jobQueue || !jobQueue->isCurrentlyProcessingJob(*jobDataIdentifier)) {
+        // The job which started this worker has been canceled, terminate this worker.
+        terminatePreinstallationWorker(worker);
+        return;
+    }
+    jobQueue->scriptContextStarted(*jobDataIdentifier, worker.identifier());
 }
 
-void SWServer::didFinishInstall(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, ServiceWorkerIdentifier serviceWorkerIdentifier, bool wasSuccessful)
+void SWServer::terminatePreinstallationWorker(SWServerWorker& worker)
 {
-    ASSERT(m_connections.contains(connection.identifier()));
+    worker.terminate();
+    auto* registration = getRegistration(worker.registrationKey());
+    if (registration && registration->preInstallationWorker() == &worker)
+        registration->setPreInstallationWorker(nullptr);
+}
 
-    if (auto* jobQueue = m_jobQueues.get(registrationKey))
-        jobQueue->didFinishInstall(connection, serviceWorkerIdentifier, wasSuccessful);
+void SWServer::didFinishInstall(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
+{
+    if (!jobDataIdentifier)
+        return;
+
+    if (wasSuccessful)
+        RELEASE_LOG(ServiceWorker, "%p - SWServer::didFinishInstall: Successfuly finished SW install for job %s", this, jobDataIdentifier->loggingString().utf8().data());
+    else
+        RELEASE_LOG_ERROR(ServiceWorker, "%p - SWServer::didFinishInstall: Failed SW install for job %s", this, jobDataIdentifier->loggingString().utf8().data());
+
+    if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
+        jobQueue->didFinishInstall(*jobDataIdentifier, worker.identifier(), wasSuccessful);
 }
 
-void SWServer::didFinishActivation(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, ServiceWorkerIdentifier serviceWorkerIdentifier)
+void SWServer::didFinishActivation(SWServerWorker& worker)
 {
-    ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
+    RELEASE_LOG(ServiceWorker, "%p - SWServer::didFinishActivation: Finished activation for service worker %llu", this, worker.identifier().toUInt64());
+
+    auto* registration = getRegistration(worker.registrationKey());
+    if (!registration)
+        return;
 
-    if (auto* registration = getRegistration(registrationKey))
-        SWServerJobQueue::didFinishActivation(*registration, serviceWorkerIdentifier);
+    if (m_registrationStore)
+        m_registrationStore->updateRegistration(worker.contextData());
+    registration->didFinishActivation(worker.identifier());
+}
+
+// https://w3c.github.io/ServiceWorker/#clients-getall
+void SWServer::matchAll(SWServerWorker& worker, const ServiceWorkerClientQueryOptions& options, ServiceWorkerClientsMatchAllCallback&& callback)
+{
+    // FIXME: Support reserved client filtering.
+    // FIXME: Support WindowClient additional properties.
+
+    Vector<ServiceWorkerClientData> matchingClients;
+    forEachClientForOrigin(worker.origin(), [&](auto& clientData) {
+        if (!options.includeUncontrolled) {
+            auto registrationIdentifier = m_clientToControllingRegistration.get(clientData.identifier);
+            if (worker.data().registrationIdentifier != registrationIdentifier)
+                return;
+            if (&worker != this->activeWorkerFromRegistrationID(registrationIdentifier))
+                return;
+        }
+        if (options.type != ServiceWorkerClientType::All && options.type != clientData.type)
+            return;
+        matchingClients.append(clientData);
+    });
+    callback(WTFMove(matchingClients));
+}
+
+void SWServer::forEachClientForOrigin(const ClientOrigin& origin, const WTF::Function<void(ServiceWorkerClientData&)>& apply)
+{
+    auto iterator = m_clientIdentifiersPerOrigin.find(origin);
+    if (iterator == m_clientIdentifiersPerOrigin.end())
+        return;
+
+    for (auto& clientIdentifier : iterator->value.identifiers) {
+        auto clientIterator = m_clientsById.find(clientIdentifier);
+        ASSERT(clientIterator != m_clientsById.end());
+        apply(clientIterator->value);
+    }
+}
+
+void SWServer::claim(SWServerWorker& worker)
+{
+    auto& origin = worker.origin();
+    forEachClientForOrigin(origin, [&](auto& clientData) {
+        auto* registration = this->doRegistrationMatching(origin.topOrigin, clientData.url);
+        if (!(registration && registration->key() == worker.registrationKey()))
+            return;
+
+        auto result = m_clientToControllingRegistration.add(clientData.identifier, registration->identifier());
+        if (!result.isNewEntry) {
+            auto previousIdentifier = result.iterator->value;
+            if (previousIdentifier == registration->identifier())
+                return;
+            result.iterator->value = registration->identifier();
+            if (auto* controllingRegistration = m_registrationsByID.get(previousIdentifier))
+                controllingRegistration->removeClientUsingRegistration(clientData.identifier);
+        }
+        registration->controlClient(clientData.identifier);
+    });
 }
 
 void SWServer::didResolveRegistrationPromise(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey)
 {
-    ASSERT(m_connections.contains(connection.identifier()));
+    ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
 
     if (auto* jobQueue = m_jobQueues.get(registrationKey))
-        jobQueue->didResolveRegistrationPromise(connection);
+        jobQueue->didResolveRegistrationPromise();
 }
 
-void SWServer::addClientServiceWorkerRegistration(Connection& connection, const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
+void SWServer::addClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
 {
-    auto* registration = m_registrations.get(key);
+    auto* registration = m_registrationsByID.get(identifier);
     if (!registration) {
         LOG_ERROR("Request to add client-side ServiceWorkerRegistration to non-existent server-side registration");
         return;
     }
+    
+    registration->addClientServiceWorkerRegistration(connection.identifier());
+}
+
+void SWServer::removeClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
+{
+    if (auto* registration = m_registrationsByID.get(identifier))
+        registration->removeClientServiceWorkerRegistration(connection.identifier());
+}
+
+void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, const String& referrerPolicy, WorkerType type, HashMap<URL, ServiceWorkerContextData::ImportedScript>&& scriptResourceMap)
+{
+    tryInstallContextData({ jobDataIdentifier, registration.data(), ServiceWorkerIdentifier::generate(), script, contentSecurityPolicy, referrerPolicy, url, type, sessionID(), false, WTFMove(scriptResourceMap) });
+}
 
-    if (registration->identifier() != identifier)
+void SWServer::tryInstallContextData(ServiceWorkerContextData&& data)
+{
+    RegistrableDomain registrableDomain(data.scriptURL);
+    auto* connection = SWServerToContextConnection::connectionForRegistrableDomain(registrableDomain);
+    if (!connection) {
+        m_pendingContextDatas.ensure(WTFMove(registrableDomain), [] {
+            return Vector<ServiceWorkerContextData> { };
+        }).iterator->value.append(WTFMove(data));
         return;
+    }
     
-    registration->addClientServiceWorkerRegistration(connection.identifier());
+    installContextData(data);
 }
 
-void SWServer::removeClientServiceWorkerRegistration(Connection& connection, const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
+void SWServer::serverToContextConnectionCreated(SWServerToContextConnection& contextConnection)
 {
-    auto* registration = m_registrations.get(key);
-    if (!registration) {
-        LOG_ERROR("Request to remove client-side ServiceWorkerRegistration from non-existent server-side registration");
+    auto pendingContextDatas = m_pendingContextDatas.take(contextConnection.registrableDomain());
+    for (auto& data : pendingContextDatas)
+        installContextData(data);
+
+    auto serviceWorkerRunRequests = m_serviceWorkerRunRequests.take(contextConnection.registrableDomain());
+    for (auto& item : serviceWorkerRunRequests) {
+        bool success = runServiceWorker(item.key);
+        for (auto& callback : item.value)
+            callback(success ? &contextConnection : nullptr);
+    }
+}
+
+void SWServer::installContextData(const ServiceWorkerContextData& data)
+{
+    ASSERT_WITH_MESSAGE(!data.loadedFromDisk, "Workers we just read from disk should only be launched as needed");
+
+    if (data.jobDataIdentifier) {
+        // Abort if the job that scheduled this has been cancelled.
+        auto* jobQueue = m_jobQueues.get(data.registration.key);
+        if (!jobQueue || !jobQueue->isCurrentlyProcessingJob(*data.jobDataIdentifier))
+            return;
+    }
+
+    auto* registration = m_registrations.get(data.registration.key);
+    RELEASE_ASSERT(registration);
+
+    auto worker = SWServerWorker::create(*this, *registration, data.scriptURL, data.script, data.contentSecurityPolicy, String { data.referrerPolicy }, data.workerType, data.serviceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript> { data.scriptResourceMap });
+
+    auto* connection = worker->contextConnection();
+    ASSERT(connection);
+
+    registration->setPreInstallationWorker(worker.ptr());
+    worker->setState(SWServerWorker::State::Running);
+    auto userAgent = worker->userAgent();
+    auto result = m_runningOrTerminatingWorkers.add(data.serviceWorkerIdentifier, WTFMove(worker));
+    ASSERT_UNUSED(result, result.isNewEntry);
+
+    connection->installServiceWorkerContext(data, m_sessionID, userAgent);
+}
+
+void SWServer::runServiceWorkerIfNecessary(ServiceWorkerIdentifier identifier, RunServiceWorkerCallback&& callback)
+{
+    auto* worker = workerByID(identifier);
+    if (!worker) {
+        callback(nullptr);
         return;
     }
 
-    if (registration->identifier() != identifier)
+    auto* contextConnection = worker->contextConnection();
+    if (worker->isRunning()) {
+        ASSERT(contextConnection);
+        callback(contextConnection);
         return;
-    
-    registration->removeClientServiceWorkerRegistration(connection.identifier());
+    }
+
+    if (!contextConnection) {
+        auto& serviceWorkerRunRequestsForOrigin = m_serviceWorkerRunRequests.ensure(worker->registrableDomain(), [] {
+            return HashMap<ServiceWorkerIdentifier, Vector<RunServiceWorkerCallback>> { };
+        }).iterator->value;
+        serviceWorkerRunRequestsForOrigin.ensure(identifier, [&] {
+            return Vector<RunServiceWorkerCallback> { };
+        }).iterator->value.append(WTFMove(callback));
+        return;
+    }
+
+    bool success = runServiceWorker(identifier);
+    callback(success ? contextConnection : nullptr);
 }
 
-Ref<SWServerWorker> SWServer::updateWorker(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, const URL& url, const String& script, WorkerType type)
+bool SWServer::runServiceWorker(ServiceWorkerIdentifier identifier)
 {
-    auto serviceWorkerIdentifier = generateServiceWorkerIdentifier();
+    auto* worker = workerByID(identifier);
+    if (!worker)
+        return false;
 
-    auto result = m_workersByID.add(serviceWorkerIdentifier, SWServerWorker::create(registrationKey, url, script, type, serviceWorkerIdentifier));
-    ASSERT(result.isNewEntry);
+    // If the registration for a working has been removed then the request to run
+    // the worker is moot.
+    if (!getRegistration(worker->registrationKey()))
+        return false;
 
-    connection.installServiceWorkerContext({ registrationKey, serviceWorkerIdentifier, script, url });
-    
-    return result.iterator->value.get();
+    auto addResult = m_runningOrTerminatingWorkers.add(identifier, *worker);
+    ASSERT_UNUSED(addResult, addResult.isNewEntry || worker->isTerminating());
+
+    worker->setState(SWServerWorker::State::Running);
+
+    auto* contextConnection = worker->contextConnection();
+    ASSERT(contextConnection);
+
+    contextConnection->installServiceWorkerContext(worker->contextData(), m_sessionID, worker->userAgent());
+
+    return true;
 }
 
-void SWServer::fireInstallEvent(Connection& connection, ServiceWorkerIdentifier serviceWorkerIdentifier)
+void SWServer::terminateWorker(SWServerWorker& worker)
 {
-    connection.fireInstallEvent(serviceWorkerIdentifier);
+    terminateWorkerInternal(worker, Asynchronous);
 }
 
-void SWServer::fireActivateEvent(Connection& connection, ServiceWorkerIdentifier serviceWorkerIdentifier)
+void SWServer::syncTerminateWorker(SWServerWorker& worker)
 {
-    connection.fireActivateEvent(serviceWorkerIdentifier);
+    terminateWorkerInternal(worker, Synchronous);
 }
 
-void SWServer::taskThreadEntryPoint()
+void SWServer::terminateWorkerInternal(SWServerWorker& worker, TerminationMode mode)
 {
-    ASSERT(!isMainThread());
+    ASSERT(m_runningOrTerminatingWorkers.get(worker.identifier()) == &worker);
+    ASSERT(worker.isRunning());
 
-    while (!m_taskQueue.isKilled())
-        m_taskQueue.waitForMessage().performTask();
+    RELEASE_LOG(ServiceWorker, "%p - SWServer::terminateWorkerInternal: Terminating service worker %llu", this, worker.identifier().toUInt64());
 
-    Locker<Lock> locker(m_taskThreadLock);
-    m_taskThread = nullptr;
+    worker.setState(SWServerWorker::State::Terminating);
+
+    auto* contextConnection = worker.contextConnection();
+    ASSERT(contextConnection);
+    if (!contextConnection) {
+        LOG_ERROR("Request to terminate a worker whose context connection does not exist");
+        workerContextTerminated(worker);
+        return;
+    }
+
+    switch (mode) {
+    case Asynchronous:
+        contextConnection->terminateWorker(worker.identifier());
+        break;
+    case Synchronous:
+        contextConnection->syncTerminateWorker(worker.identifier());
+        break;
+    };
 }
 
-void SWServer::postTask(CrossThreadTask&& task)
+void SWServer::markAllWorkersForRegistrableDomainAsTerminated(const RegistrableDomain& registrableDomain)
 {
-    m_taskQueue.append(WTFMove(task));
+    Vector<SWServerWorker*> terminatedWorkers;
+    for (auto& worker : m_runningOrTerminatingWorkers.values()) {
+        if (worker->registrableDomain() == registrableDomain)
+            terminatedWorkers.append(worker.ptr());
+    }
+    for (auto& terminatedWorker : terminatedWorkers)
+        workerContextTerminated(*terminatedWorker);
 }
 
-void SWServer::postTaskReply(CrossThreadTask&& task)
+void SWServer::workerContextTerminated(SWServerWorker& worker)
 {
-    m_taskReplyQueue.append(WTFMove(task));
+    worker.setState(SWServerWorker::State::NotRunning);
 
-    Locker<Lock> locker(m_mainThreadReplyLock);
-    if (m_mainThreadReplyScheduled)
+    if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
+        jobQueue->cancelJobsFromServiceWorker(worker.identifier());
+
+    // At this point if no registrations are referencing the worker then it will be destroyed,
+    // removing itself from the m_workersByID map.
+    auto result = m_runningOrTerminatingWorkers.take(worker.identifier());
+    ASSERT_UNUSED(result, result && result->ptr() == &worker);
+}
+
+void SWServer::fireInstallEvent(SWServerWorker& worker)
+{
+    auto* contextConnection = worker.contextConnection();
+    if (!contextConnection) {
+        LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
         return;
+    }
 
-    m_mainThreadReplyScheduled = true;
-    callOnMainThread([this] {
-        handleTaskRepliesOnMainThread();
-    });
+    contextConnection->fireInstallEvent(worker.identifier());
 }
 
-void SWServer::handleTaskRepliesOnMainThread()
+void SWServer::fireActivateEvent(SWServerWorker& worker)
 {
-    {
-        Locker<Lock> locker(m_mainThreadReplyLock);
-        m_mainThreadReplyScheduled = false;
+    auto* contextConnection = worker.contextConnection();
+    if (!contextConnection) {
+        LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
+        return;
     }
 
-    while (auto task = m_taskReplyQueue.tryGetMessage())
-        task->performTask();
+    contextConnection->fireActivateEvent(worker.identifier());
 }
 
-void SWServer::registerConnection(Connection& connection)
+void SWServer::addConnection(std::unique_ptr<Connection>&& connection)
 {
-    auto result = m_connections.add(connection.identifier(), nullptr);
-    ASSERT(result.isNewEntry);
-    result.iterator->value = &connection;
+    auto identifier = connection->identifier();
+    ASSERT(!m_connections.contains(identifier));
+    m_connections.add(identifier, WTFMove(connection));
 }
 
-void SWServer::unregisterConnection(Connection& connection)
+void SWServer::removeConnection(SWServerConnectionIdentifier connectionIdentifier)
 {
-    ASSERT(m_connections.get(connection.identifier()) == &connection);
-    m_connections.remove(connection.identifier());
+    ASSERT(m_connections.contains(connectionIdentifier));
+    m_connections.remove(connectionIdentifier);
+
+    for (auto& registration : m_registrations.values())
+        registration->unregisterServerConnection(connectionIdentifier);
+
+    for (auto& jobQueue : m_jobQueues.values())
+        jobQueue->cancelJobsFromConnection(connectionIdentifier);
 }
 
-const SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL) const
+SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL)
 {
-    const SWServerRegistration* selectedRegistration = nullptr;
+    SWServerRegistration* selectedRegistration = nullptr;
     for (auto& registration : m_registrations.values()) {
         if (!registration->key().isMatching(topOrigin, clientURL))
             continue;
@@ -380,6 +744,164 @@ const SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOrigi
     return (selectedRegistration && !selectedRegistration->isUninstalling()) ? selectedRegistration : nullptr;
 }
 
+SWServerRegistration* SWServer::registrationFromServiceWorkerIdentifier(ServiceWorkerIdentifier identifier)
+{
+    auto iterator = m_runningOrTerminatingWorkers.find(identifier);
+    if (iterator == m_runningOrTerminatingWorkers.end())
+        return nullptr;
+
+    return m_registrations.get(iterator->value->registrationKey());
+}
+
+void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceWorkerClientData&& data, const Optional<ServiceWorkerRegistrationIdentifier>& controllingServiceWorkerRegistrationIdentifier, String&& userAgent)
+{
+    auto clientIdentifier = data.identifier;
+
+    ASSERT(!m_clientsById.contains(clientIdentifier));
+    m_clientsById.add(clientIdentifier, WTFMove(data));
+
+    auto& clientIdentifiersForOrigin = m_clientIdentifiersPerOrigin.ensure(clientOrigin, [] {
+        return Clients { };
+    }).iterator->value;
+
+    ASSERT(!clientIdentifiersForOrigin.identifiers.contains(clientIdentifier));
+    clientIdentifiersForOrigin.identifiers.append(clientIdentifier);
+
+    if (!clientIdentifiersForOrigin.userAgent.isNull() && clientIdentifiersForOrigin.userAgent != userAgent)
+        RELEASE_LOG_ERROR(ServiceWorker, "%p - SWServer::registerServiceWorkerClient: Service worker has clients using different user agents", this);
+    clientIdentifiersForOrigin.userAgent = WTFMove(userAgent);
+
+    clientIdentifiersForOrigin.terminateServiceWorkersTimer = nullptr;
+
+    m_clientsByRegistrableDomain.ensure(clientOrigin.clientRegistrableDomain(), [] {
+        return HashSet<ServiceWorkerClientIdentifier> { };
+    }).iterator->value.add(clientIdentifier);
+
+    if (!controllingServiceWorkerRegistrationIdentifier)
+        return;
+
+    auto* controllingRegistration = m_registrationsByID.get(*controllingServiceWorkerRegistrationIdentifier);
+    if (!controllingRegistration || !controllingRegistration->activeWorker())
+        return;
+
+    controllingRegistration->addClientUsingRegistration(clientIdentifier);
+    ASSERT(!m_clientToControllingRegistration.contains(clientIdentifier));
+    m_clientToControllingRegistration.add(clientIdentifier, *controllingServiceWorkerRegistrationIdentifier);
+}
+
+void SWServer::unregisterServiceWorkerClient(const ClientOrigin& clientOrigin, ServiceWorkerClientIdentifier clientIdentifier)
+{
+    auto clientRegistrableDomain = clientOrigin.clientRegistrableDomain();
+
+    bool wasRemoved = m_clientsById.remove(clientIdentifier);
+    ASSERT_UNUSED(wasRemoved, wasRemoved);
+
+    auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
+    ASSERT(iterator != m_clientIdentifiersPerOrigin.end());
+
+    auto& clientIdentifiers = iterator->value.identifiers;
+    clientIdentifiers.removeFirstMatching([&] (const auto& identifier) {
+        return clientIdentifier == identifier;
+    });
+
+    if (clientIdentifiers.isEmpty()) {
+        ASSERT(!iterator->value.terminateServiceWorkersTimer);
+        iterator->value.terminateServiceWorkersTimer = std::make_unique<Timer>([clientOrigin, clientRegistrableDomain, this] {
+            Vector<SWServerWorker*> workersToTerminate;
+            for (auto& worker : m_runningOrTerminatingWorkers.values()) {
+                if (worker->isRunning() && worker->origin() == clientOrigin)
+                    workersToTerminate.append(worker.ptr());
+            }
+            for (auto* worker : workersToTerminate)
+                terminateWorker(*worker);
+
+            if (!m_clientsByRegistrableDomain.contains(clientRegistrableDomain)) {
+                if (auto* connection = SWServerToContextConnection::connectionForRegistrableDomain(clientRegistrableDomain))
+                    connection->connectionMayNoLongerBeNeeded();
+            }
+
+            m_clientIdentifiersPerOrigin.remove(clientOrigin);
+        });
+        iterator->value.terminateServiceWorkersTimer->startOneShot(m_shouldDisableServiceWorkerProcessTerminationDelay ? 0_s : terminationDelay);
+    }
+
+    auto clientsByRegistrableDomainIterator = m_clientsByRegistrableDomain.find(clientRegistrableDomain);
+    ASSERT(clientsByRegistrableDomainIterator != m_clientsByRegistrableDomain.end());
+    auto& clientsForRegistrableDomain = clientsByRegistrableDomainIterator->value;
+    clientsForRegistrableDomain.remove(clientIdentifier);
+    if (clientsForRegistrableDomain.isEmpty())
+        m_clientsByRegistrableDomain.remove(clientsByRegistrableDomainIterator);
+
+    auto registrationIterator = m_clientToControllingRegistration.find(clientIdentifier);
+    if (registrationIterator == m_clientToControllingRegistration.end())
+        return;
+
+    if (auto* registration = m_registrationsByID.get(registrationIterator->value))
+        registration->removeClientUsingRegistration(clientIdentifier);
+
+    m_clientToControllingRegistration.remove(registrationIterator);
+}
+
+bool SWServer::needsServerToContextConnectionForRegistrableDomain(const RegistrableDomain& registrableDomain) const
+{
+    return m_clientsByRegistrableDomain.contains(registrableDomain);
+}
+
+void SWServer::resolveRegistrationReadyRequests(SWServerRegistration& registration)
+{
+    for (auto& connection : m_connections.values())
+        connection->resolveRegistrationReadyRequests(registration);
+}
+
+void SWServer::Connection::whenRegistrationReady(uint64_t registrationReadyRequestIdentifier, const SecurityOriginData& topOrigin, const URL& clientURL)
+{
+    if (auto* registration = doRegistrationMatching(topOrigin, clientURL)) {
+        if (registration->activeWorker()) {
+            registrationReady(registrationReadyRequestIdentifier, registration->data());
+            return;
+        }
+    }
+    m_registrationReadyRequests.append({ topOrigin, clientURL, registrationReadyRequestIdentifier });
+}
+
+void SWServer::Connection::resolveRegistrationReadyRequests(SWServerRegistration& registration)
+{
+    m_registrationReadyRequests.removeAllMatching([&](auto& request) {
+        if (!registration.key().isMatching(request.topOrigin, request.clientURL))
+            return false;
+
+        this->registrationReady(request.identifier, registration.data());
+        return true;
+    });
+}
+
+void SWServer::getOriginsWithRegistrations(Function<void(const HashSet<SecurityOriginData>&)>&& callback)
+{
+    m_getOriginsWithRegistrationsCallbacks.append(WTFMove(callback));
+
+    if (m_importCompleted)
+        performGetOriginsWithRegistrationsCallbacks();
+}
+
+void SWServer::performGetOriginsWithRegistrationsCallbacks()
+{
+    ASSERT(isMainThread());
+    ASSERT(m_importCompleted);
+
+    if (m_getOriginsWithRegistrationsCallbacks.isEmpty())
+        return;
+
+    HashSet<SecurityOriginData> originsWithRegistrations;
+    for (auto& key : m_registrations.keys()) {
+        originsWithRegistrations.add(key.topOrigin());
+        originsWithRegistrations.add(SecurityOriginData { key.scope().protocol().toString(), key.scope().host().toString(), key.scope().port() });
+    }
+
+    auto callbacks = WTFMove(m_getOriginsWithRegistrationsCallbacks);
+    for (auto& callback : callbacks)
+        callback(originsWithRegistrations);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)