Close service worker database on network process suspension
[WebKit-https.git] / Source / WebCore / workers / service / server / SWServer.cpp
index 33a33ac..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,10 +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();
+
     allServers().remove(this);
 }
 
@@ -84,20 +79,28 @@ SWServerWorker* SWServer::workerByID(ServiceWorkerIdentifier identifier) const
     return worker;
 }
 
-std::optional<ServiceWorkerClientData> SWServer::serviceWorkerClientWithOriginByID(const ClientOrigin& clientOrigin, 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 std::nullopt;
+        return WTF::nullopt;
 
     if (!iterator->value.identifiers.contains(clientIdentifier))
-        return std::nullopt;
+        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_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);
@@ -138,8 +141,7 @@ void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data)
     auto registrationPtr = registration.get();
     addRegistration(WTFMove(registration));
 
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    auto worker = SWServerWorker::create(*this, *registrationPtr, connection ? connection->identifier() : SWServerToContextConnectionIdentifier(), data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier);
+    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);
 }
@@ -154,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)
@@ -203,32 +206,47 @@ void SWServer::clearAll(CompletionHandler<void()>&& completionHandler)
     ASSERT(m_registrationsByID.isEmpty());
     m_pendingContextDatas.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::clear(const SecurityOrigin& origin, CompletionHandler<void()>&& completionHandler)
+void SWServer::endSuspension()
+{
+    if (m_registrationStore)
+        m_registrationStore->endSuspension();
+}
+
+void SWServer::clear(const SecurityOriginData& securityOrigin, CompletionHandler<void()>&& completionHandler)
 {
     if (!m_importCompleted) {
-        m_clearCompletionCallbacks.append([this, origin = makeRef(origin), completionHandler = WTFMove(completionHandler)] () mutable {
+        m_clearCompletionCallbacks.append([this, securityOrigin, completionHandler = WTFMove(completionHandler)] () mutable {
             ASSERT(m_importCompleted);
-            clear(origin, WTFMove(completionHandler));
+            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);
+        });
+    }
 
     if (registrationsToRemove.isEmpty()) {
         completionHandler();
@@ -239,7 +257,8 @@ void SWServer::clear(const SecurityOrigin& origin, CompletionHandler<void()>&& c
     for (auto* registration : registrationsToRemove)
         registration->clear();
 
-    m_registrationStore.flushChanges(WTFMove(completionHandler));
+    if (m_registrationStore)
+        m_registrationStore->flushChanges(WTFMove(completionHandler));
 }
 
 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
@@ -270,9 +289,14 @@ 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);
 }
@@ -345,7 +369,7 @@ 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;
@@ -361,7 +385,7 @@ void SWServer::scriptContextFailedToStart(const std::optional<ServiceWorkerJobDa
     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;
@@ -383,7 +407,7 @@ void SWServer::terminatePreinstallationWorker(SWServerWorker& worker)
         registration->setPreInstallationWorker(nullptr);
 }
 
-void SWServer::didFinishInstall(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
+void SWServer::didFinishInstall(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
 {
     if (!jobDataIdentifier)
         return;
@@ -405,7 +429,8 @@ void SWServer::didFinishActivation(SWServerWorker& worker)
     if (!registration)
         return;
 
-    m_registrationStore.updateRegistration(worker.contextData());
+    if (m_registrationStore)
+        m_registrationStore->updateRegistration(worker.contextData());
     registration->didFinishActivation(worker.identifier());
 }
 
@@ -490,38 +515,36 @@ void SWServer::removeClientServiceWorkerRegistration(Connection& connection, Ser
         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)
 {
-    tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, contentSecurityPolicy, url, type, sessionID(), 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);
     }
 }
 
@@ -536,41 +559,50 @@ void SWServer::installContextData(const ServiceWorkerContextData& data)
             return;
     }
 
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    ASSERT(connection);
-
     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 });
+
+    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)
@@ -589,14 +621,10 @@ bool SWServer::runServiceWorker(ServiceWorkerIdentifier identifier)
 
     worker->setState(SWServerWorker::State::Running);
 
-    auto* connection = SWServerToContextConnection::globalServerToContextConnection();
-    ASSERT(connection);
+    auto* contextConnection = worker->contextConnection();
+    ASSERT(contextConnection);
 
-    // 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());
-
-    connection->installServiceWorkerContext(worker->contextData(), m_sessionID);
+    contextConnection->installServiceWorkerContext(worker->contextData(), m_sessionID, worker->userAgent());
 
     return true;
 }
@@ -620,17 +648,9 @@ void SWServer::terminateWorkerInternal(SWServerWorker& worker, TerminationMode m
 
     worker.setState(SWServerWorker::State::Terminating);
 
-    auto contextConnectionIdentifier = worker.contextConnectionIdentifier();
-    ASSERT(contextConnectionIdentifier);
-    if (!contextConnectionIdentifier) {
-        LOG_ERROR("Request to terminate a worker whose contextConnectionIdentifier is invalid");
-        workerContextTerminated(worker);
-        return;
-    }
-
-    auto* connection = SWServerToContextConnection::connectionForIdentifier(*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;
@@ -638,24 +658,28 @@ 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);
-    worker.setContextConnectionIdentifier(std::nullopt);
 
     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
         jobQueue->cancelJobsFromServiceWorker(worker.identifier());
@@ -668,54 +692,43 @@ void SWServer::workerContextTerminated(SWServerWorker& worker)
 
 void SWServer::fireInstallEvent(SWServerWorker& worker)
 {
-    auto contextConnectionIdentifier = worker.contextConnectionIdentifier();
-    if (!contextConnectionIdentifier) {
-        LOG_ERROR("Request to fire install event on a worker whose contextConnectionIdentifier is invalid");
-        return;
-    }
-    auto* connection = SWServerToContextConnection::connectionForIdentifier(*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 contextConnectionIdentifier = worker.contextConnectionIdentifier();
-    if (!contextConnectionIdentifier) {
-        LOG_ERROR("Request to fire install event on a worker whose contextConnectionIdentifier is invalid");
-        return;
-    }
-
-    auto* connection = SWServerToContextConnection::connectionForIdentifier(*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());
+    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(connection.identifier());
+        registration->unregisterServerConnection(connectionIdentifier);
 
     for (auto& jobQueue : m_jobQueues.values())
-        jobQueue->cancelJobsFromConnection(connection.identifier());
+        jobQueue->cancelJobsFromConnection(connectionIdentifier);
 }
 
 SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL)
@@ -740,18 +753,30 @@ SWServerRegistration* SWServer::registrationFromServiceWorkerIdentifier(ServiceW
     return m_registrations.get(iterator->value->registrationKey());
 }
 
-void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceWorkerClientData&& data, const std::optional<ServiceWorkerRegistrationIdentifier>& controllingServiceWorkerRegistrationIdentifier)
+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;
 
+    m_clientsByRegistrableDomain.ensure(clientOrigin.clientRegistrableDomain(), [] {
+        return HashSet<ServiceWorkerClientIdentifier> { };
+    }).iterator->value.add(clientIdentifier);
+
     if (!controllingServiceWorkerRegistrationIdentifier)
         return;
 
@@ -760,12 +785,14 @@ void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceW
         return;
 
     controllingRegistration->addClientUsingRegistration(clientIdentifier);
-    auto result = m_clientToControllingRegistration.add(clientIdentifier, *controllingServiceWorkerRegistrationIdentifier);
-    ASSERT_UNUSED(result, result.isNewEntry);
+    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);
 
@@ -776,18 +803,35 @@ 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 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;
@@ -798,9 +842,14 @@ void SWServer::unregisterServiceWorkerClient(const ClientOrigin& clientOrigin, S
     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())
+    for (auto& connection : m_connections.values())
         connection->resolveRegistrationReadyRequests(registration);
 }
 
@@ -845,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);