Close service worker database on network process suspension
[WebKit-https.git] / Source / WebCore / workers / service / server / SWServer.cpp
index 86ab2fb..2a283f4 100644 (file)
@@ -52,14 +52,8 @@ static Seconds terminationDelay { 10_s };
 
 SWServer::Connection::Connection(SWServer& server)
     : m_server(server)
-    , m_identifier(generateObjectIdentifier<SWServerConnectionIdentifierType>())
+    , m_identifier(SWServerConnectionIdentifier::generate())
 {
-    m_server.registerConnection(*this);
-}
-
-SWServer::Connection::~Connection()
-{
-    m_server.unregisterConnection(*this);
 }
 
 HashSet<SWServer*>& SWServer::allServers()
@@ -70,19 +64,11 @@ HashSet<SWServer*>& SWServer::allServers()
 
 SWServer::~SWServer()
 {
-    RELEASE_ASSERT(m_connections.isEmpty());
-    RELEASE_ASSERT(m_registrations.isEmpty());
-    RELEASE_ASSERT(m_jobQueues.isEmpty());
+    // 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();
 
-    ASSERT(m_taskQueue.isEmpty());
-    ASSERT(m_taskReplyQueue.isEmpty());
-
-    // 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);
-    
     allServers().remove(this);
 }
 
@@ -93,12 +79,26 @@ SWServerWorker* SWServer::workerByID(ServiceWorkerIdentifier identifier) const
     return worker;
 }
 
-std::optional<ServiceWorkerClientData> SWServer::serviceWorkerClientByID(const ServiceWorkerClientIdentifier& clientIdentifier) const
+Optional<ServiceWorkerClientData> SWServer::serviceWorkerClientWithOriginByID(const ClientOrigin& clientOrigin, const ServiceWorkerClientIdentifier& clientIdentifier) const
+{
+    auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
+    if (iterator == m_clientIdentifiersPerOrigin.end())
+        return WTF::nullopt;
+
+    if (!iterator->value.identifiers.contains(clientIdentifier))
+        return WTF::nullopt;
+
+    auto clientIterator = m_clientsById.find(clientIdentifier);
+    ASSERT(clientIterator != m_clientsById.end());
+    return clientIterator->value;
+}
+
+String SWServer::serviceWorkerClientUserAgent(const ClientOrigin& clientOrigin) const
 {
-    auto iterator = m_clientsById.find(clientIdentifier);
-    if (iterator == m_clientsById.end())
-        return std::nullopt;
-    return iterator->value;
+    auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
+    if (iterator == m_clientIdentifiersPerOrigin.end())
+        return String();
+    return iterator->value.userAgent;
 }
 
 SWServerWorker* SWServer::activeWorkerFromRegistrationID(ServiceWorkerRegistrationIdentifier identifier)
@@ -117,6 +117,11 @@ void SWServer::registrationStoreImportComplete()
     ASSERT(!m_importCompleted);
     m_importCompleted = true;
     m_originStore->importComplete();
+
+    auto clearCallbacks = WTFMove(m_clearCompletionCallbacks);
+    for (auto& callback : clearCallbacks)
+        callback();
+
     performGetOriginsWithRegistrationsCallbacks();
 }
 
@@ -133,8 +138,12 @@ void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data)
 
     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));
-    tryInstallContextData(WTFMove(data));
+
+    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)
@@ -147,19 +156,20 @@ void SWServer::addRegistration(std::unique_ptr<SWServerRegistration>&& registrat
     auto addResult2 = m_registrationsByID.add(registrationPtr->identifier(), registrationPtr);
     ASSERT_UNUSED(addResult2, addResult2.isNewEntry);
 
-    m_originStore->add(key.topOrigin().securityOrigin());
+    m_originStore->add(key.topOrigin());
 }
 
 void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& key)
 {
-    auto topOrigin = key.topOrigin().securityOrigin();
+    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);
-    m_registrationStore.removeRegistration(*registration);
+    if (m_registrationStore)
+        m_registrationStore->removeRegistration(*registration);
 }
 
 Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityOriginData& topOrigin, const URL& clientURL)
@@ -180,51 +190,75 @@ Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityO
     return matchingRegistrationDatas;
 }
 
-void SWServer::clearAll(WTF::CompletionHandler<void()>&& completionHandler)
+void SWServer::clearAll(CompletionHandler<void()>&& completionHandler)
 {
+    if (!m_importCompleted) {
+        m_clearCompletionCallbacks.append([this, completionHandler = WTFMove(completionHandler)] () mutable {
+            ASSERT(m_importCompleted);
+            clearAll(WTFMove(completionHandler));
+        });
+        return;
+    }
+
     m_jobQueues.clear();
     while (!m_registrations.isEmpty())
         m_registrations.begin()->value->clear();
     ASSERT(m_registrationsByID.isEmpty());
     m_pendingContextDatas.clear();
-    m_pendingJobs.clear();
     m_originStore->clearAll();
-    m_registrationStore.clearAll(WTFMove(completionHandler));
+    if (m_registrationStore)
+        m_registrationStore->clearAll(WTFMove(completionHandler));
+}
+
+void SWServer::startSuspension(CompletionHandler<void()>&& completionHandler)
+{
+    if (m_registrationStore)
+        m_registrationStore->startSuspension(WTFMove(completionHandler));
+}
+
+void SWServer::endSuspension()
+{
+    if (m_registrationStore)
+        m_registrationStore->endSuspension();
 }
 
-void SWServer::clear(const SecurityOrigin& origin, WTF::CompletionHandler<void()>&& completionHandler)
+void SWServer::clear(const SecurityOriginData& securityOrigin, CompletionHandler<void()>&& completionHandler)
 {
+    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(origin);
+        return keyAndValue.key.relatesToOrigin(securityOrigin);
     });
 
     Vector<SWServerRegistration*> registrationsToRemove;
     for (auto& keyAndValue : m_registrations) {
-        if (keyAndValue.key.relatesToOrigin(origin))
+        if (keyAndValue.key.relatesToOrigin(securityOrigin))
             registrationsToRemove.append(keyAndValue.value.get());
     }
 
-    m_pendingContextDatas.removeAllMatching([&](auto& contextData) {
-        return contextData.registration.key.relatesToOrigin(origin);
-    });
+    for (auto& contextDatas : m_pendingContextDatas.values()) {
+        contextDatas.removeAllMatching([&](auto& contextData) {
+            return contextData.registration.key.relatesToOrigin(securityOrigin);
+        });
+    }
 
-    m_pendingJobs.removeAllMatching([&](auto& job) {
-        return job.registrationKey().relatesToOrigin(origin);
-    });
+    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();
 
-    m_registrationStore.flushChanges(WTFMove(completionHandler));
-}
-
-void SWServer::Connection::scheduleJobInServer(ServiceWorkerJobData&& jobData)
-{
-    LOG(ServiceWorker, "Scheduling ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
-    ASSERT(identifier() == jobData.connectionIdentifier());
-
-    m_server.scheduleJob(WTFMove(jobData));
+    if (m_registrationStore)
+        m_registrationStore->flushChanges(WTFMove(completionHandler));
 }
 
 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
@@ -255,14 +289,16 @@ void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifie
 
 SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore, String&& registrationDatabaseDirectory, PAL::SessionID sessionID)
     : m_originStore(WTFMove(originStore))
-    , m_registrationStore(*this, WTFMove(registrationDatabaseDirectory))
     , m_sessionID(sessionID)
 {
+    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);
-    m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
-        taskThreadEntryPoint();
-    });
 }
 
 // https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
@@ -270,11 +306,6 @@ void SWServer::scheduleJob(ServiceWorkerJobData&& jobData)
 {
     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
 
-    if (!SWServerToContextConnection::globalServerToContextConnection()) {
-        m_pendingJobs.append(WTFMove(jobData));
-        return;
-    }
-
     // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
     // If it is, stack it along with that job.
 
@@ -320,10 +351,9 @@ void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData, FetchOption
 {
     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().jobIdentifier, jobData.registrationKey(), cachePolicy);
+    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)
@@ -339,27 +369,45 @@ void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFe
     jobQueue->scriptFetchFinished(connection, result);
 }
 
-void SWServer::scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, const String& message)
+void SWServer::scriptContextFailedToStart(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, const String& 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());
 
-    if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
-        jobQueue->scriptContextFailedToStart(*jobDataIdentifier, worker.identifier(), message);
+    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(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker)
+void SWServer::scriptContextStarted(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker)
 {
     if (!jobDataIdentifier)
         return;
 
-    if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
-        jobQueue->scriptContextStarted(*jobDataIdentifier, worker.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(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
+void SWServer::terminatePreinstallationWorker(SWServerWorker& worker)
+{
+    worker.terminate();
+    auto* registration = getRegistration(worker.registrationKey());
+    if (registration && registration->preInstallationWorker() == &worker)
+        registration->setPreInstallationWorker(nullptr);
+}
+
+void SWServer::didFinishInstall(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
 {
     if (!jobDataIdentifier)
         return;
@@ -377,8 +425,13 @@ void SWServer::didFinishActivation(SWServerWorker& worker)
 {
     RELEASE_LOG(ServiceWorker, "%p - SWServer::didFinishActivation: Finished activation for service worker %llu", this, worker.identifier().toUInt64());
 
-    if (auto* registration = getRegistration(worker.registrationKey()))
-        registration->didFinishActivation(worker.identifier());
+    auto* registration = getRegistration(worker.registrationKey());
+    if (!registration)
+        return;
+
+    if (m_registrationStore)
+        m_registrationStore->updateRegistration(worker.contextData());
+    registration->didFinishActivation(worker.identifier());
 }
 
 // https://w3c.github.io/ServiceWorker/#clients-getall
@@ -389,8 +442,13 @@ void SWServer::matchAll(SWServerWorker& worker, const ServiceWorkerClientQueryOp
 
     Vector<ServiceWorkerClientData> matchingClients;
     forEachClientForOrigin(worker.origin(), [&](auto& clientData) {
-        if (!options.includeUncontrolled && worker.identifier() != m_clientToControllingWorker.get(clientData.identifier))
-            return;
+        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);
@@ -415,17 +473,18 @@ void SWServer::claim(SWServerWorker& worker)
 {
     auto& origin = worker.origin();
     forEachClientForOrigin(origin, [&](auto& clientData) {
-        auto* registration = doRegistrationMatching(origin.topOrigin, clientData.url);
+        auto* registration = this->doRegistrationMatching(origin.topOrigin, clientData.url);
         if (!(registration && registration->key() == worker.registrationKey()))
             return;
 
-        auto result = m_clientToControllingWorker.add(clientData.identifier, worker.identifier());
+        auto result = m_clientToControllingRegistration.add(clientData.identifier, registration->identifier());
         if (!result.isNewEntry) {
-            if (result.iterator->value == worker.identifier())
+            auto previousIdentifier = result.iterator->value;
+            if (previousIdentifier == registration->identifier())
                 return;
-            if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(result.iterator->value))
+            result.iterator->value = registration->identifier();
+            if (auto* controllingRegistration = m_registrationsByID.get(previousIdentifier))
                 controllingRegistration->removeClientUsingRegistration(clientData.identifier);
-            result.iterator->value = worker.identifier();
         }
         registration->controlClient(clientData.identifier);
     });
@@ -452,103 +511,98 @@ void SWServer::addClientServiceWorkerRegistration(Connection& connection, Servic
 
 void SWServer::removeClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
 {
-    auto* registration = m_registrationsByID.get(identifier);
-    if (!registration) {
-        LOG_ERROR("Request to remove client-side ServiceWorkerRegistration from non-existent server-side registration");
-        return;
-    }
-    
-    registration->removeClientServiceWorkerRegistration(connection.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, WorkerType type)
+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)
 {
-    registration.setLastUpdateTime(WallTime::now());
-    tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, contentSecurityPolicy, url, type, false });
+    tryInstallContextData({ jobDataIdentifier, registration.data(), ServiceWorkerIdentifier::generate(), script, contentSecurityPolicy, referrerPolicy, url, type, sessionID(), false, WTFMove(scriptResourceMap) });
 }
 
 void SWServer::tryInstallContextData(ServiceWorkerContextData&& data)
 {
-    // Right now we only ever keep up to one connection to one SW context process.
-    // And it should always exist if we're trying to install context data.
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
+    RegistrableDomain registrableDomain(data.scriptURL);
+    auto* connection = SWServerToContextConnection::connectionForRegistrableDomain(registrableDomain);
     if (!connection) {
-        m_pendingContextDatas.append(WTFMove(data));
+        m_pendingContextDatas.ensure(WTFMove(registrableDomain), [] {
+            return Vector<ServiceWorkerContextData> { };
+        }).iterator->value.append(WTFMove(data));
         return;
     }
     
     installContextData(data);
 }
 
-void SWServer::serverToContextConnectionCreated()
+void SWServer::serverToContextConnectionCreated(SWServerToContextConnection& contextConnection)
 {
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    ASSERT(connection);
-
-    auto pendingContextDatas = WTFMove(m_pendingContextDatas);
+    auto pendingContextDatas = m_pendingContextDatas.take(contextConnection.registrableDomain());
     for (auto& data : pendingContextDatas)
         installContextData(data);
 
-    auto serviceWorkerRunRequests = WTFMove(m_serviceWorkerRunRequests);
+    auto serviceWorkerRunRequests = m_serviceWorkerRunRequests.take(contextConnection.registrableDomain());
     for (auto& item : serviceWorkerRunRequests) {
         bool success = runServiceWorker(item.key);
         for (auto& callback : item.value)
-            callback(success, *connection);
+            callback(success ? &contextConnection : nullptr);
     }
-
-    auto pendingJobs = WTFMove(m_pendingJobs);
-    for (auto& jobData : pendingJobs)
-        scheduleJob(WTFMove(jobData));
 }
 
 void SWServer::installContextData(const ServiceWorkerContextData& data)
 {
-    if (!data.loadedFromDisk)
-        m_registrationStore.updateRegistration(data);
+    ASSERT_WITH_MESSAGE(!data.loadedFromDisk, "Workers we just read from disk should only be launched as needed");
 
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    ASSERT(connection);
+    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, connection->identifier(), data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier);
+    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 });
 
-    // We don't immediately launch all workers that were just read in from disk,
-    // as it is unlikely they will be needed immediately.
-    if (data.loadedFromDisk) {
-        registration->updateRegistrationState(ServiceWorkerRegistrationState::Active, worker.ptr());
-        worker->setState(ServiceWorkerState::Activated);
-        return;
-    }
+    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);
+    connection->installServiceWorkerContext(data, m_sessionID, userAgent);
 }
 
 void SWServer::runServiceWorkerIfNecessary(ServiceWorkerIdentifier identifier, RunServiceWorkerCallback&& callback)
 {
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    if (auto* worker = m_runningOrTerminatingWorkers.get(identifier)) {
-        if (worker->isRunning()) {
-            ASSERT(connection);
-            callback(true, *connection);
-            return;
-        }
+    auto* worker = workerByID(identifier);
+    if (!worker) {
+        callback(nullptr);
+        return;
     }
 
-    if (!connection) {
-        m_serviceWorkerRunRequests.ensure(identifier, [&] {
+    auto* contextConnection = worker->contextConnection();
+    if (worker->isRunning()) {
+        ASSERT(contextConnection);
+        callback(contextConnection);
+        return;
+    }
+
+    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;
     }
 
-    callback(runServiceWorker(identifier), *connection);
+    bool success = runServiceWorker(identifier);
+    callback(success ? contextConnection : nullptr);
 }
 
 bool SWServer::runServiceWorker(ServiceWorkerIdentifier identifier)
@@ -567,14 +621,10 @@ bool SWServer::runServiceWorker(ServiceWorkerIdentifier identifier)
 
     worker->setState(SWServerWorker::State::Running);
 
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    ASSERT(connection);
-
-    // When re-running a service worker after a context process crash, the connection identifier may have changed
-    // so we update it here.
-    worker->setContextConnectionIdentifier(connection->identifier());
+    auto* contextConnection = worker->contextConnection();
+    ASSERT(contextConnection);
 
-    connection->installServiceWorkerContext(worker->contextData(), m_sessionID);
+    contextConnection->installServiceWorkerContext(worker->contextData(), m_sessionID, worker->userAgent());
 
     return true;
 }
@@ -598,9 +648,9 @@ void SWServer::terminateWorkerInternal(SWServerWorker& worker, TerminationMode m
 
     worker.setState(SWServerWorker::State::Terminating);
 
-    auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
-    ASSERT(connection);
-    if (!connection) {
+    auto* contextConnection = worker.contextConnection();
+    ASSERT(contextConnection);
+    if (!contextConnection) {
         LOG_ERROR("Request to terminate a worker whose context connection does not exist");
         workerContextTerminated(worker);
         return;
@@ -608,24 +658,32 @@ void SWServer::terminateWorkerInternal(SWServerWorker& worker, TerminationMode m
 
     switch (mode) {
     case Asynchronous:
-        connection->terminateWorker(worker.identifier());
+        contextConnection->terminateWorker(worker.identifier());
         break;
     case Synchronous:
-        connection->syncTerminateWorker(worker.identifier());
+        contextConnection->syncTerminateWorker(worker.identifier());
         break;
     };
 }
 
-void SWServer::markAllWorkersAsTerminated()
+void SWServer::markAllWorkersForRegistrableDomainAsTerminated(const RegistrableDomain& registrableDomain)
 {
-    while (!m_runningOrTerminatingWorkers.isEmpty())
-        workerContextTerminated(m_runningOrTerminatingWorkers.begin()->value);
+    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::workerContextTerminated(SWServerWorker& worker)
 {
     worker.setState(SWServerWorker::State::NotRunning);
 
+    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());
@@ -634,81 +692,43 @@ void SWServer::workerContextTerminated(SWServerWorker& worker)
 
 void SWServer::fireInstallEvent(SWServerWorker& worker)
 {
-    auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
-    if (!connection) {
+    auto* contextConnection = worker.contextConnection();
+    if (!contextConnection) {
         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
         return;
     }
 
-    connection->fireInstallEvent(worker.identifier());
+    contextConnection->fireInstallEvent(worker.identifier());
 }
 
 void SWServer::fireActivateEvent(SWServerWorker& worker)
 {
-    auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
-    if (!connection) {
+    auto* contextConnection = worker.contextConnection();
+    if (!contextConnection) {
         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
         return;
     }
 
-    connection->fireActivateEvent(worker.identifier());
-}
-
-void SWServer::taskThreadEntryPoint()
-{
-    ASSERT(!isMainThread());
-
-    while (!m_taskQueue.isKilled())
-        m_taskQueue.waitForMessage().performTask();
-
-    Locker<Lock> locker(m_taskThreadLock);
-    m_taskThread = nullptr;
+    contextConnection->fireActivateEvent(worker.identifier());
 }
 
-void SWServer::postTask(CrossThreadTask&& task)
+void SWServer::addConnection(std::unique_ptr<Connection>&& connection)
 {
-    m_taskQueue.append(WTFMove(task));
+    auto identifier = connection->identifier();
+    ASSERT(!m_connections.contains(identifier));
+    m_connections.add(identifier, WTFMove(connection));
 }
 
-void SWServer::postTaskReply(CrossThreadTask&& task)
+void SWServer::removeConnection(SWServerConnectionIdentifier connectionIdentifier)
 {
-    m_taskReplyQueue.append(WTFMove(task));
-
-    Locker<Lock> locker(m_mainThreadReplyLock);
-    if (m_mainThreadReplyScheduled)
-        return;
-
-    m_mainThreadReplyScheduled = true;
-    callOnMainThread([this] {
-        handleTaskRepliesOnMainThread();
-    });
-}
-
-void SWServer::handleTaskRepliesOnMainThread()
-{
-    {
-        Locker<Lock> locker(m_mainThreadReplyLock);
-        m_mainThreadReplyScheduled = false;
-    }
-
-    while (auto task = m_taskReplyQueue.tryGetMessage())
-        task->performTask();
-}
-
-void SWServer::registerConnection(Connection& connection)
-{
-    auto result = m_connections.add(connection.identifier(), nullptr);
-    ASSERT(result.isNewEntry);
-    result.iterator->value = &connection;
-}
-
-void SWServer::unregisterConnection(Connection& connection)
-{
-    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(connection.identifier());
+        registration->unregisterServerConnection(connectionIdentifier);
+
+    for (auto& jobQueue : m_jobQueues.values())
+        jobQueue->cancelJobsFromConnection(connectionIdentifier);
 }
 
 SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL)
@@ -724,11 +744,6 @@ SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData&
     return (selectedRegistration && !selectedRegistration->isUninstalling()) ? selectedRegistration : nullptr;
 }
 
-void SWServer::setClientActiveWorker(ServiceWorkerClientIdentifier clientIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier)
-{
-    m_clientToControllingWorker.set(clientIdentifier, serviceWorkerIdentifier);
-}
-
 SWServerRegistration* SWServer::registrationFromServiceWorkerIdentifier(ServiceWorkerIdentifier identifier)
 {
     auto iterator = m_runningOrTerminatingWorkers.find(identifier);
@@ -738,30 +753,46 @@ SWServerRegistration* SWServer::registrationFromServiceWorkerIdentifier(ServiceW
     return m_registrations.get(iterator->value->registrationKey());
 }
 
-void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceWorkerClientData&& data, const std::optional<ServiceWorkerIdentifier>& controllingServiceWorkerIdentifier)
+void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceWorkerClientData&& data, const Optional<ServiceWorkerRegistrationIdentifier>& controllingServiceWorkerRegistrationIdentifier, String&& userAgent)
 {
     auto clientIdentifier = data.identifier;
-    auto addResult = m_clientsById.add(clientIdentifier, WTFMove(data));
-    ASSERT_UNUSED(addResult, addResult.isNewEntry);
 
-    auto& clientIdentifiersForOrigin = m_clientIdentifiersPerOrigin.ensure(WTFMove(clientOrigin), [] {
+    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;
 
-    if (!controllingServiceWorkerIdentifier)
+    m_clientsByRegistrableDomain.ensure(clientOrigin.clientRegistrableDomain(), [] {
+        return HashSet<ServiceWorkerClientIdentifier> { };
+    }).iterator->value.add(clientIdentifier);
+
+    if (!controllingServiceWorkerRegistrationIdentifier)
         return;
 
-    if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(*controllingServiceWorkerIdentifier)) {
-        controllingRegistration->addClientUsingRegistration(clientIdentifier);
-        auto result = m_clientToControllingWorker.add(clientIdentifier, *controllingServiceWorkerIdentifier);
-        ASSERT_UNUSED(result, result.isNewEntry);
-    }
+    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);
 
@@ -772,31 +803,53 @@ void SWServer::unregisterServiceWorkerClient(const ClientOrigin& clientOrigin, S
     clientIdentifiers.removeFirstMatching([&] (const auto& identifier) {
         return clientIdentifier == identifier;
     });
+
     if (clientIdentifiers.isEmpty()) {
         ASSERT(!iterator->value.terminateServiceWorkersTimer);
-        iterator->value.terminateServiceWorkersTimer = std::make_unique<Timer>([clientOrigin, this] {
+        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)
-                    terminateWorker(worker);
+                    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(terminationDelay);
+        iterator->value.terminateServiceWorkersTimer->startOneShot(m_shouldDisableServiceWorkerProcessTerminationDelay ? 0_s : terminationDelay);
     }
 
-    auto workerIterator = m_clientToControllingWorker.find(clientIdentifier);
-    if (workerIterator == m_clientToControllingWorker.end())
+    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* controllingRegistration = registrationFromServiceWorkerIdentifier(workerIterator->value))
-        controllingRegistration->removeClientUsingRegistration(clientIdentifier);
+    if (auto* registration = m_registrationsByID.get(registrationIterator->value))
+        registration->removeClientUsingRegistration(clientIdentifier);
+
+    m_clientToControllingRegistration.remove(registrationIterator);
+}
 
-    m_clientToControllingWorker.remove(workerIterator);
+bool SWServer::needsServerToContextConnectionForRegistrableDomain(const RegistrableDomain& registrableDomain) const
+{
+    return m_clientsByRegistrableDomain.contains(registrableDomain);
 }
 
 void SWServer::resolveRegistrationReadyRequests(SWServerRegistration& registration)
 {
-    for (auto* connection : m_connections.values())
+    for (auto& connection : m_connections.values())
         connection->resolveRegistrationReadyRequests(registration);
 }
 
@@ -817,12 +870,12 @@ void SWServer::Connection::resolveRegistrationReadyRequests(SWServerRegistration
         if (!registration.key().isMatching(request.topOrigin, request.clientURL))
             return false;
 
-        registrationReady(request.identifier, registration.data());
+        this->registrationReady(request.identifier, registration.data());
         return true;
     });
 }
 
-void SWServer::getOriginsWithRegistrations(WTF::Function<void(const HashSet<SecurityOriginData>&)> callback)
+void SWServer::getOriginsWithRegistrations(Function<void(const HashSet<SecurityOriginData>&)>&& callback)
 {
     m_getOriginsWithRegistrationsCallbacks.append(WTFMove(callback));
 
@@ -841,7 +894,7 @@ void SWServer::performGetOriginsWithRegistrationsCallbacks()
     HashSet<SecurityOriginData> originsWithRegistrations;
     for (auto& key : m_registrations.keys()) {
         originsWithRegistrations.add(key.topOrigin());
-        originsWithRegistrations.add(SecurityOriginData { key.scope().protocol().toString(), key.scope().host(), key.scope().port() });
+        originsWithRegistrations.add(SecurityOriginData { key.scope().protocol().toString(), key.scope().host().toString(), key.scope().port() });
     }
 
     auto callbacks = WTFMove(m_getOriginsWithRegistrationsCallbacks);