Clearing all Website Data should remove service worker registrations on disk
[WebKit-https.git] / Source / WebCore / workers / service / server / SWServer.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "SWServer.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "ExceptionCode.h"
32 #include "ExceptionData.h"
33 #include "Logging.h"
34 #include "RegistrationStore.h"
35 #include "SWOriginStore.h"
36 #include "SWServerJobQueue.h"
37 #include "SWServerRegistration.h"
38 #include "SWServerToContextConnection.h"
39 #include "SWServerWorker.h"
40 #include "SecurityOrigin.h"
41 #include "ServiceWorkerClientType.h"
42 #include "ServiceWorkerContextData.h"
43 #include "ServiceWorkerFetchResult.h"
44 #include "ServiceWorkerJobData.h"
45 #include <wtf/CompletionHandler.h>
46 #include <wtf/NeverDestroyed.h>
47 #include <wtf/text/WTFString.h>
48
49 namespace WebCore {
50
51 static Seconds terminationDelay { 10_s };
52
53 SWServer::Connection::Connection(SWServer& server)
54     : m_server(server)
55     , m_identifier(generateObjectIdentifier<SWServerConnectionIdentifierType>())
56 {
57     m_server.registerConnection(*this);
58 }
59
60 SWServer::Connection::~Connection()
61 {
62     m_server.unregisterConnection(*this);
63 }
64
65 HashSet<SWServer*>& SWServer::allServers()
66 {
67     static NeverDestroyed<HashSet<SWServer*>> servers;
68     return servers;
69 }
70
71 SWServer::~SWServer()
72 {
73     RELEASE_ASSERT(m_connections.isEmpty());
74     RELEASE_ASSERT(m_registrations.isEmpty());
75     RELEASE_ASSERT(m_jobQueues.isEmpty());
76
77     ASSERT(m_taskQueue.isEmpty());
78     ASSERT(m_taskReplyQueue.isEmpty());
79
80     // For a SWServer to be cleanly shut down its thread must have finished and gone away.
81     // At this stage in development of the feature that actually never happens.
82     // But once it does start happening, this ASSERT will catch us doing it wrong.
83     Locker<Lock> locker(m_taskThreadLock);
84     ASSERT(!m_taskThread);
85     
86     allServers().remove(this);
87 }
88
89 SWServerWorker* SWServer::workerByID(ServiceWorkerIdentifier identifier) const
90 {
91     auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier);
92     ASSERT(!worker || &worker->server() == this);
93     return worker;
94 }
95
96 SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationKey& registrationKey)
97 {
98     return m_registrations.get(registrationKey);
99 }
100
101 void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data)
102 {
103     addRegistration(std::make_unique<SWServerRegistration>(*this, data.registration.key, data.registration.updateViaCache, data.registration.scopeURL, data.scriptURL));
104     tryInstallContextData(WTFMove(data));
105 }
106
107 void SWServer::addRegistration(std::unique_ptr<SWServerRegistration>&& registration)
108 {
109     auto key = registration->key();
110     auto* registrationPtr = registration.get();
111     auto addResult1 = m_registrations.add(key, WTFMove(registration));
112     ASSERT_UNUSED(addResult1, addResult1.isNewEntry);
113
114     auto addResult2 = m_registrationsByID.add(registrationPtr->identifier(), registrationPtr);
115     ASSERT_UNUSED(addResult2, addResult2.isNewEntry);
116
117     m_originStore->add(key.topOrigin().securityOrigin());
118 }
119
120 void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& key)
121 {
122     auto topOrigin = key.topOrigin().securityOrigin();
123     auto registration = m_registrations.take(key);
124     ASSERT(registration);
125     bool wasRemoved = m_registrationsByID.remove(registration->identifier());
126     ASSERT_UNUSED(wasRemoved, wasRemoved);
127
128     m_originStore->remove(topOrigin);
129     m_registrationStore.removeRegistration(*registration);
130 }
131
132 Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityOriginData& topOrigin, const URL& clientURL)
133 {
134     Vector<SWServerRegistration*> matchingRegistrations;
135     for (auto& item : m_registrations) {
136         if (!item.value->isUninstalling() && item.key.originIsMatching(topOrigin, clientURL))
137             matchingRegistrations.append(item.value.get());
138     }
139     // The specification mandates that registrations are returned in the insertion order.
140     std::sort(matchingRegistrations.begin(), matchingRegistrations.end(), [](auto& a, auto& b) {
141         return a->creationTime() < b->creationTime();
142     });
143     Vector<ServiceWorkerRegistrationData> matchingRegistrationDatas;
144     matchingRegistrationDatas.reserveInitialCapacity(matchingRegistrations.size());
145     for (auto* registration : matchingRegistrations)
146         matchingRegistrationDatas.uncheckedAppend(registration->data());
147     return matchingRegistrationDatas;
148 }
149
150 void SWServer::clearAll(WTF::CompletionHandler<void()>&& completionHandler)
151 {
152     m_jobQueues.clear();
153     while (!m_registrations.isEmpty())
154         m_registrations.begin()->value->clear();
155     ASSERT(m_registrationsByID.isEmpty());
156     m_originStore->clearAll();
157     m_registrationStore.clearAll(WTFMove(completionHandler));
158 }
159
160 void SWServer::clear(const SecurityOrigin& origin, WTF::CompletionHandler<void()>&& completionHandler)
161 {
162     m_jobQueues.removeIf([&](auto& keyAndValue) {
163         return keyAndValue.key.relatesToOrigin(origin);
164     });
165
166     Vector<SWServerRegistration*> registrationsToRemove;
167     for (auto& keyAndValue : m_registrations) {
168         if (keyAndValue.key.relatesToOrigin(origin))
169             registrationsToRemove.append(keyAndValue.value.get());
170     }
171
172     // Calling SWServerRegistration::clear() takes care of updating m_registrations, m_originStore and m_registrationStore.
173     for (auto* registration : registrationsToRemove)
174         registration->clear();
175
176     m_registrationStore.flushChanges(WTFMove(completionHandler));
177 }
178
179 void SWServer::Connection::scheduleJobInServer(const ServiceWorkerJobData& jobData)
180 {
181     LOG(ServiceWorker, "Scheduling ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
182     ASSERT(identifier() == jobData.connectionIdentifier());
183
184     m_server.scheduleJob(jobData);
185 }
186
187 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
188 {
189     m_server.scriptFetchFinished(*this, result);
190 }
191
192 void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key)
193 {
194     m_server.didResolveRegistrationPromise(*this, key);
195 }
196
197 void SWServer::Connection::addServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier)
198 {
199     m_server.addClientServiceWorkerRegistration(*this, identifier);
200 }
201
202 void SWServer::Connection::removeServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier)
203 {
204     m_server.removeClientServiceWorkerRegistration(*this, identifier);
205 }
206
207 void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifier)
208 {
209     if (auto* worker = m_server.workerByID(identifier))
210         m_server.syncTerminateWorker(*worker);
211 }
212
213     SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore, String&& registrationDatabaseDirectory, PAL::SessionID sessionID)
214     : m_originStore(WTFMove(originStore))
215     , m_registrationStore(*this, WTFMove(registrationDatabaseDirectory))
216     , m_sessionID(sessionID)
217 {
218     UNUSED_PARAM(registrationDatabaseDirectory);
219     allServers().add(this);
220     m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
221         taskThreadEntryPoint();
222     });
223 }
224
225 // https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
226 void SWServer::scheduleJob(const ServiceWorkerJobData& jobData)
227 {
228     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
229
230     // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
231     // If it is, stack it along with that job.
232
233     auto& jobQueue = *m_jobQueues.ensure(jobData.registrationKey(), [this, &jobData] {
234         return std::make_unique<SWServerJobQueue>(*this, jobData.registrationKey());
235     }).iterator->value;
236
237     jobQueue.enqueueJob(jobData);
238     if (jobQueue.size() == 1)
239         jobQueue.runNextJob();
240 }
241
242 void SWServer::rejectJob(const ServiceWorkerJobData& jobData, const ExceptionData& exceptionData)
243 {
244     LOG(ServiceWorker, "Rejected ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
245     auto* connection = m_connections.get(jobData.connectionIdentifier());
246     if (!connection)
247         return;
248
249     connection->rejectJobInClient(jobData.identifier(), exceptionData);
250 }
251
252 void SWServer::resolveRegistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
253 {
254     LOG(ServiceWorker, "Resolved ServiceWorker job %s in server with registration %s", jobData.identifier().loggingString().utf8().data(), registrationData.identifier.loggingString().utf8().data());
255     auto* connection = m_connections.get(jobData.connectionIdentifier());
256     if (!connection)
257         return;
258
259     connection->resolveRegistrationJobInClient(jobData.identifier(), registrationData, shouldNotifyWhenResolved);
260 }
261
262 void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
263 {
264     auto* connection = m_connections.get(jobData.connectionIdentifier());
265     if (!connection)
266         return;
267
268     connection->resolveUnregistrationJobInClient(jobData.identifier(), registrationKey, unregistrationResult);
269 }
270
271 void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData)
272 {
273     LOG(ServiceWorker, "Server issuing startScriptFetch for current job %s in client", jobData.identifier().loggingString().utf8().data());
274     auto* connection = m_connections.get(jobData.connectionIdentifier());
275     if (!connection)
276         return;
277
278     connection->startScriptFetchInClient(jobData.identifier());
279 }
280
281 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
282 {
283     LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %s in client", result.jobDataIdentifier.loggingString().utf8().data());
284
285     ASSERT(m_connections.contains(result.jobDataIdentifier.connectionIdentifier));
286
287     auto jobQueue = m_jobQueues.get(result.registrationKey);
288     if (!jobQueue)
289         return;
290
291     jobQueue->scriptFetchFinished(connection, result);
292 }
293
294 void SWServer::scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, const String& message)
295 {
296     if (!jobDataIdentifier)
297         return;
298
299     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
300         jobQueue->scriptContextFailedToStart(*jobDataIdentifier, worker.identifier(), message);
301 }
302
303 void SWServer::scriptContextStarted(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker)
304 {
305     if (!jobDataIdentifier)
306         return;
307
308     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
309         jobQueue->scriptContextStarted(*jobDataIdentifier, worker.identifier());
310 }
311
312 void SWServer::didFinishInstall(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
313 {
314     if (!jobDataIdentifier)
315         return;
316
317     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
318         jobQueue->didFinishInstall(*jobDataIdentifier, worker.identifier(), wasSuccessful);
319 }
320
321 void SWServer::didFinishActivation(SWServerWorker& worker)
322 {
323     if (auto* registration = getRegistration(worker.registrationKey()))
324         registration->didFinishActivation(worker.identifier());
325 }
326
327 // https://w3c.github.io/ServiceWorker/#clients-get
328 std::optional<ServiceWorkerClientData> SWServer::findClientByIdentifier(const ClientOrigin& origin, ServiceWorkerClientIdentifier clientIdentifier)
329 {
330     // FIXME: Support WindowClient additional properties.
331
332     auto iterator = m_clients.find(origin);
333     if (iterator == m_clients.end())
334         return std::nullopt;
335
336     auto& clients = iterator->value.clients;
337     auto position = clients.findMatching([&] (const auto& client) {
338         return clientIdentifier == client.identifier;
339     });
340
341     return (position != notFound) ? std::make_optional(clients[position].data) : std::nullopt;
342 }
343
344 // https://w3c.github.io/ServiceWorker/#clients-getall
345 void SWServer::matchAll(SWServerWorker& worker, const ServiceWorkerClientQueryOptions& options, ServiceWorkerClientsMatchAllCallback&& callback)
346 {
347     // FIXME: Support reserved client filtering.
348     // FIXME: Support WindowClient additional properties.
349
350     auto clients = m_clients.find(worker.origin())->value.clients;
351
352     if (!options.includeUncontrolled) {
353         clients.removeAllMatching([&] (const auto& client) {
354             return worker.identifier() != m_clientToControllingWorker.get(client.identifier);
355         });
356     }
357     if (options.type != ServiceWorkerClientType::All) {
358         clients.removeAllMatching([&] (const auto& client) {
359             return options.type != client.data.type;
360         });
361     }
362     callback(WTFMove(clients));
363 }
364
365 void SWServer::claim(SWServerWorker& worker)
366 {
367     auto& origin = worker.origin();
368     auto iterator = m_clients.find(origin);
369     if (iterator == m_clients.end())
370         return;
371
372     auto& clients = iterator->value.clients;
373     for (auto& client : clients) {
374         auto* registration = doRegistrationMatching(origin.topOrigin, client.data.url);
375         if (!(registration && registration->key() == worker.registrationKey()))
376             continue;
377
378         auto result = m_clientToControllingWorker.add(client.identifier, worker.identifier());
379         if (!result.isNewEntry) {
380             if (result.iterator->value == worker.identifier())
381                 continue;
382             if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(result.iterator->value))
383                 controllingRegistration->removeClientUsingRegistration(client.identifier);
384             result.iterator->value = worker.identifier();
385         }
386         registration->controlClient(client.identifier);
387     }
388 }
389
390 void SWServer::didResolveRegistrationPromise(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey)
391 {
392     ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
393
394     if (auto* jobQueue = m_jobQueues.get(registrationKey))
395         jobQueue->didResolveRegistrationPromise();
396 }
397
398 void SWServer::addClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
399 {
400     auto* registration = m_registrationsByID.get(identifier);
401     if (!registration) {
402         LOG_ERROR("Request to add client-side ServiceWorkerRegistration to non-existent server-side registration");
403         return;
404     }
405     
406     registration->addClientServiceWorkerRegistration(connection.identifier());
407 }
408
409 void SWServer::removeClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
410 {
411     auto* registration = m_registrationsByID.get(identifier);
412     if (!registration) {
413         LOG_ERROR("Request to remove client-side ServiceWorkerRegistration from non-existent server-side registration");
414         return;
415     }
416     
417     registration->removeClientServiceWorkerRegistration(connection.identifier());
418 }
419
420 void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, WorkerType type)
421 {
422     registration.setLastUpdateTime(WallTime::now());
423     tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, url, type, false });
424 }
425
426 void SWServer::tryInstallContextData(ServiceWorkerContextData&& data)
427 {
428     // Right now we only ever keep up to one connection to one SW context process.
429     // And it should always exist if we're trying to install context data.
430     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
431     if (!connection) {
432         m_pendingContextDatas.append(WTFMove(data));
433         return;
434     }
435     
436     installContextData(data);
437 }
438
439 void SWServer::serverToContextConnectionCreated()
440 {
441     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
442     ASSERT(connection);
443
444     auto pendingContextDatas = WTFMove(m_pendingContextDatas);
445     for (auto& data : pendingContextDatas)
446         installContextData(data);
447
448     auto serviceWorkerRunRequests = WTFMove(m_serviceWorkerRunRequests);
449     for (auto& item : serviceWorkerRunRequests) {
450         bool success = runServiceWorker(item.key);
451         for (auto& callback : item.value)
452             callback(success, *connection);
453     }
454 }
455
456 void SWServer::installContextData(const ServiceWorkerContextData& data)
457 {
458     if (!data.loadedFromDisk)
459         m_registrationStore.updateRegistration(data);
460
461     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
462     ASSERT(connection);
463
464     auto* registration = m_registrations.get(data.registration.key);
465     RELEASE_ASSERT(registration);
466
467     auto worker = SWServerWorker::create(*this, *registration, connection->identifier(), data.scriptURL, data.script, data.workerType, data.serviceWorkerIdentifier);
468
469     // We don't immediately launch all workers that were just read in from disk,
470     // as it is unlikely they will be needed immediately.
471     if (data.loadedFromDisk) {
472         registration->updateRegistrationState(ServiceWorkerRegistrationState::Active, worker.ptr());
473         return;
474     }
475
476     worker->setState(SWServerWorker::State::Running);
477     auto result = m_runningOrTerminatingWorkers.add(data.serviceWorkerIdentifier, WTFMove(worker));
478     ASSERT_UNUSED(result, result.isNewEntry);
479
480     connection->installServiceWorkerContext(data, m_sessionID);
481 }
482
483 void SWServer::runServiceWorkerIfNecessary(ServiceWorkerIdentifier identifier, RunServiceWorkerCallback&& callback)
484 {
485     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
486     if (auto* worker = m_runningOrTerminatingWorkers.get(identifier)) {
487         if (worker->isRunning()) {
488             ASSERT(connection);
489             callback(true, *connection);
490             return;
491         }
492     }
493
494     if (!connection) {
495         m_serviceWorkerRunRequests.ensure(identifier, [&] {
496             return Vector<RunServiceWorkerCallback> { };
497         }).iterator->value.append(WTFMove(callback));
498         return;
499     }
500
501     callback(runServiceWorker(identifier), *connection);
502 }
503
504 bool SWServer::runServiceWorker(ServiceWorkerIdentifier identifier)
505 {
506     auto* worker = workerByID(identifier);
507     if (!worker)
508         return false;
509
510     // If the registration for a working has been removed then the request to run
511     // the worker is moot.
512     if (!getRegistration(worker->registrationKey()))
513         return false;
514
515     auto addResult = m_runningOrTerminatingWorkers.add(identifier, *worker);
516     ASSERT_UNUSED(addResult, addResult.isNewEntry);
517
518     worker->setState(SWServerWorker::State::Running);
519
520     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
521     ASSERT(connection);
522     connection->installServiceWorkerContext(worker->contextData(), m_sessionID);
523
524     return true;
525 }
526
527 void SWServer::terminateWorker(SWServerWorker& worker)
528 {
529     terminateWorkerInternal(worker, Asynchronous);
530 }
531
532 void SWServer::syncTerminateWorker(SWServerWorker& worker)
533 {
534     terminateWorkerInternal(worker, Synchronous);
535 }
536
537 void SWServer::terminateWorkerInternal(SWServerWorker& worker, TerminationMode mode)
538 {
539     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
540     if (!connection) {
541         LOG_ERROR("Request to terminate a worker whose context connection does not exist");
542         return;
543     }
544
545     ASSERT(m_runningOrTerminatingWorkers.get(worker.identifier()) == &worker);
546     ASSERT(!worker.isTerminating());
547
548     worker.setState(SWServerWorker::State::Terminating);
549
550     switch (mode) {
551     case Asynchronous:
552         connection->terminateWorker(worker.identifier());
553         break;
554     case Synchronous:
555         connection->syncTerminateWorker(worker.identifier());
556         break;
557     };
558 }
559
560 void SWServer::markAllWorkersAsTerminated()
561 {
562     while (!m_runningOrTerminatingWorkers.isEmpty())
563         workerContextTerminated(m_runningOrTerminatingWorkers.begin()->value);
564 }
565
566 void SWServer::workerContextTerminated(SWServerWorker& worker)
567 {
568     worker.setState(SWServerWorker::State::NotRunning);
569
570     // At this point if no registrations are referencing the worker then it will be destroyed,
571     // removing itself from the m_workersByID map.
572     auto result = m_runningOrTerminatingWorkers.take(worker.identifier());
573     ASSERT_UNUSED(result, result && result->ptr() == &worker);
574 }
575
576 void SWServer::fireInstallEvent(SWServerWorker& worker)
577 {
578     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
579     if (!connection) {
580         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
581         return;
582     }
583
584     connection->fireInstallEvent(worker.identifier());
585 }
586
587 void SWServer::fireActivateEvent(SWServerWorker& worker)
588 {
589     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
590     if (!connection) {
591         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
592         return;
593     }
594
595     connection->fireActivateEvent(worker.identifier());
596 }
597
598 void SWServer::taskThreadEntryPoint()
599 {
600     ASSERT(!isMainThread());
601
602     while (!m_taskQueue.isKilled())
603         m_taskQueue.waitForMessage().performTask();
604
605     Locker<Lock> locker(m_taskThreadLock);
606     m_taskThread = nullptr;
607 }
608
609 void SWServer::postTask(CrossThreadTask&& task)
610 {
611     m_taskQueue.append(WTFMove(task));
612 }
613
614 void SWServer::postTaskReply(CrossThreadTask&& task)
615 {
616     m_taskReplyQueue.append(WTFMove(task));
617
618     Locker<Lock> locker(m_mainThreadReplyLock);
619     if (m_mainThreadReplyScheduled)
620         return;
621
622     m_mainThreadReplyScheduled = true;
623     callOnMainThread([this] {
624         handleTaskRepliesOnMainThread();
625     });
626 }
627
628 void SWServer::handleTaskRepliesOnMainThread()
629 {
630     {
631         Locker<Lock> locker(m_mainThreadReplyLock);
632         m_mainThreadReplyScheduled = false;
633     }
634
635     while (auto task = m_taskReplyQueue.tryGetMessage())
636         task->performTask();
637 }
638
639 void SWServer::registerConnection(Connection& connection)
640 {
641     auto result = m_connections.add(connection.identifier(), nullptr);
642     ASSERT(result.isNewEntry);
643     result.iterator->value = &connection;
644 }
645
646 void SWServer::unregisterConnection(Connection& connection)
647 {
648     ASSERT(m_connections.get(connection.identifier()) == &connection);
649     m_connections.remove(connection.identifier());
650
651     for (auto& registration : m_registrations.values())
652         registration->unregisterServerConnection(connection.identifier());
653 }
654
655 SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL)
656 {
657     SWServerRegistration* selectedRegistration = nullptr;
658     for (auto& registration : m_registrations.values()) {
659         if (!registration->key().isMatching(topOrigin, clientURL))
660             continue;
661         if (!selectedRegistration || selectedRegistration->key().scopeLength() < registration->key().scopeLength())
662             selectedRegistration = registration.get();
663     }
664
665     return (selectedRegistration && !selectedRegistration->isUninstalling()) ? selectedRegistration : nullptr;
666 }
667
668 void SWServer::setClientActiveWorker(ServiceWorkerClientIdentifier clientIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier)
669 {
670     m_clientToControllingWorker.set(clientIdentifier, serviceWorkerIdentifier);
671 }
672
673 SWServerRegistration* SWServer::registrationFromServiceWorkerIdentifier(ServiceWorkerIdentifier identifier)
674 {
675     auto iterator = m_runningOrTerminatingWorkers.find(identifier);
676     if (iterator == m_runningOrTerminatingWorkers.end())
677         return nullptr;
678
679     return m_registrations.get(iterator->value->registrationKey());
680 }
681
682 void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceWorkerClientIdentifier clientIdentifier, ServiceWorkerClientData&& data, const std::optional<ServiceWorkerIdentifier>& controllingServiceWorkerIdentifier)
683 {
684     auto& clientsData = m_clients.ensure(WTFMove(clientOrigin), [] {
685         return Clients { };
686     }).iterator->value;
687
688     clientsData.clients.append(ServiceWorkerClientInformation { clientIdentifier, WTFMove(data) });
689     if (clientsData.terminateServiceWorkersTimer)
690         clientsData.terminateServiceWorkersTimer = nullptr;
691
692     if (!controllingServiceWorkerIdentifier)
693         return;
694
695     if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(*controllingServiceWorkerIdentifier)) {
696         controllingRegistration->addClientUsingRegistration(clientIdentifier);
697         auto result = m_clientToControllingWorker.add(clientIdentifier, *controllingServiceWorkerIdentifier);
698         ASSERT_UNUSED(result, result.isNewEntry);
699     }
700 }
701
702 void SWServer::unregisterServiceWorkerClient(const ClientOrigin& clientOrigin, ServiceWorkerClientIdentifier clientIdentifier)
703 {
704     auto clientIterator = m_clients.find(clientOrigin);
705     ASSERT(clientIterator != m_clients.end());
706
707     auto& clients = clientIterator->value.clients;
708     clients.removeFirstMatching([&] (const auto& client) {
709         return clientIdentifier == client.identifier;
710     });
711     if (clients.isEmpty()) {
712         ASSERT(!clientIterator->value.terminateServiceWorkersTimer);
713         clientIterator->value.terminateServiceWorkersTimer = std::make_unique<Timer>([clientOrigin, this] {
714             for (auto& worker : m_runningOrTerminatingWorkers.values()) {
715                 if (worker->origin() == clientOrigin)
716                     terminateWorker(worker);
717             }
718             m_clients.remove(clientOrigin);
719         });
720         clientIterator->value.terminateServiceWorkersTimer->startOneShot(terminationDelay);
721     }
722
723     auto workerIterator = m_clientToControllingWorker.find(clientIdentifier);
724     if (workerIterator == m_clientToControllingWorker.end())
725         return;
726
727     if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(workerIterator->value))
728         controllingRegistration->removeClientUsingRegistration(clientIdentifier);
729
730     m_clientToControllingWorker.remove(workerIterator);
731 }
732
733 void SWServer::resolveRegistrationReadyRequests(SWServerRegistration& registration)
734 {
735     for (auto* connection : m_connections.values())
736         connection->resolveRegistrationReadyRequests(registration);
737 }
738
739 void SWServer::Connection::whenRegistrationReady(uint64_t registrationReadyRequestIdentifier, const SecurityOriginData& topOrigin, const URL& clientURL)
740 {
741     if (auto* registration = doRegistrationMatching(topOrigin, clientURL)) {
742         if (registration->activeWorker()) {
743             registrationReady(registrationReadyRequestIdentifier, registration->data());
744             return;
745         }
746     }
747     m_registrationReadyRequests.append({ topOrigin, clientURL, registrationReadyRequestIdentifier });
748 }
749
750 void SWServer::Connection::resolveRegistrationReadyRequests(SWServerRegistration& registration)
751 {
752     m_registrationReadyRequests.removeAllMatching([&](auto& request) {
753         if (!registration.key().isMatching(request.topOrigin, request.clientURL))
754             return false;
755
756         registrationReady(request.identifier, registration.data());
757         return true;
758     });
759 }
760
761 } // namespace WebCore
762
763 #endif // ENABLE(SERVICE_WORKER)