Add release logging to help debug issues related to service workers
[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 std::optional<ServiceWorkerClientData> SWServer::serviceWorkerClientByID(const ServiceWorkerClientIdentifier& clientIdentifier) const
97 {
98     auto iterator = m_clientsById.find(clientIdentifier);
99     if (iterator == m_clientsById.end())
100         return std::nullopt;
101     return iterator->value;
102 }
103
104 SWServerWorker* SWServer::activeWorkerFromRegistrationID(ServiceWorkerRegistrationIdentifier identifier)
105 {
106     auto* registration = m_registrationsByID.get(identifier);
107     return registration ? registration->activeWorker() : nullptr;
108 }
109
110 SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationKey& registrationKey)
111 {
112     return m_registrations.get(registrationKey);
113 }
114
115 void SWServer::registrationStoreImportComplete()
116 {
117     ASSERT(!m_importCompleted);
118     m_importCompleted = true;
119     m_originStore->importComplete();
120     performGetOriginsWithRegistrationsCallbacks();
121 }
122
123 void SWServer::registrationStoreDatabaseFailedToOpen()
124 {
125     if (!m_importCompleted)
126         registrationStoreImportComplete();
127 }
128
129 void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data)
130 {
131     // Pages should not have been able to make a new registration to this key while the import was still taking place.
132     ASSERT(!m_registrations.contains(data.registration.key));
133
134     auto registration = std::make_unique<SWServerRegistration>(*this, data.registration.key, data.registration.updateViaCache, data.registration.scopeURL, data.scriptURL);
135     registration->setLastUpdateTime(data.registration.lastUpdateTime);
136     addRegistration(WTFMove(registration));
137     tryInstallContextData(WTFMove(data));
138 }
139
140 void SWServer::addRegistration(std::unique_ptr<SWServerRegistration>&& registration)
141 {
142     auto key = registration->key();
143     auto* registrationPtr = registration.get();
144     auto addResult1 = m_registrations.add(key, WTFMove(registration));
145     ASSERT_UNUSED(addResult1, addResult1.isNewEntry);
146
147     auto addResult2 = m_registrationsByID.add(registrationPtr->identifier(), registrationPtr);
148     ASSERT_UNUSED(addResult2, addResult2.isNewEntry);
149
150     m_originStore->add(key.topOrigin().securityOrigin());
151 }
152
153 void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& key)
154 {
155     auto topOrigin = key.topOrigin().securityOrigin();
156     auto registration = m_registrations.take(key);
157     ASSERT(registration);
158     bool wasRemoved = m_registrationsByID.remove(registration->identifier());
159     ASSERT_UNUSED(wasRemoved, wasRemoved);
160
161     m_originStore->remove(topOrigin);
162     m_registrationStore.removeRegistration(*registration);
163 }
164
165 Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityOriginData& topOrigin, const URL& clientURL)
166 {
167     Vector<SWServerRegistration*> matchingRegistrations;
168     for (auto& item : m_registrations) {
169         if (!item.value->isUninstalling() && item.key.originIsMatching(topOrigin, clientURL))
170             matchingRegistrations.append(item.value.get());
171     }
172     // The specification mandates that registrations are returned in the insertion order.
173     std::sort(matchingRegistrations.begin(), matchingRegistrations.end(), [](auto& a, auto& b) {
174         return a->creationTime() < b->creationTime();
175     });
176     Vector<ServiceWorkerRegistrationData> matchingRegistrationDatas;
177     matchingRegistrationDatas.reserveInitialCapacity(matchingRegistrations.size());
178     for (auto* registration : matchingRegistrations)
179         matchingRegistrationDatas.uncheckedAppend(registration->data());
180     return matchingRegistrationDatas;
181 }
182
183 void SWServer::clearAll(WTF::CompletionHandler<void()>&& completionHandler)
184 {
185     m_jobQueues.clear();
186     while (!m_registrations.isEmpty())
187         m_registrations.begin()->value->clear();
188     ASSERT(m_registrationsByID.isEmpty());
189     m_originStore->clearAll();
190     m_registrationStore.clearAll(WTFMove(completionHandler));
191 }
192
193 void SWServer::clear(const SecurityOrigin& origin, WTF::CompletionHandler<void()>&& completionHandler)
194 {
195     m_jobQueues.removeIf([&](auto& keyAndValue) {
196         return keyAndValue.key.relatesToOrigin(origin);
197     });
198
199     Vector<SWServerRegistration*> registrationsToRemove;
200     for (auto& keyAndValue : m_registrations) {
201         if (keyAndValue.key.relatesToOrigin(origin))
202             registrationsToRemove.append(keyAndValue.value.get());
203     }
204
205     // Calling SWServerRegistration::clear() takes care of updating m_registrations, m_originStore and m_registrationStore.
206     for (auto* registration : registrationsToRemove)
207         registration->clear();
208
209     m_registrationStore.flushChanges(WTFMove(completionHandler));
210 }
211
212 void SWServer::Connection::scheduleJobInServer(ServiceWorkerJobData&& jobData)
213 {
214     LOG(ServiceWorker, "Scheduling ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
215     ASSERT(identifier() == jobData.connectionIdentifier());
216
217     m_server.scheduleJob(WTFMove(jobData));
218 }
219
220 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
221 {
222     m_server.scriptFetchFinished(*this, result);
223 }
224
225 void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key)
226 {
227     m_server.didResolveRegistrationPromise(*this, key);
228 }
229
230 void SWServer::Connection::addServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier)
231 {
232     m_server.addClientServiceWorkerRegistration(*this, identifier);
233 }
234
235 void SWServer::Connection::removeServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier)
236 {
237     m_server.removeClientServiceWorkerRegistration(*this, identifier);
238 }
239
240 void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifier)
241 {
242     if (auto* worker = m_server.workerByID(identifier))
243         m_server.syncTerminateWorker(*worker);
244 }
245
246 SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore, String&& registrationDatabaseDirectory, PAL::SessionID sessionID)
247     : m_originStore(WTFMove(originStore))
248     , m_registrationStore(*this, WTFMove(registrationDatabaseDirectory))
249     , m_sessionID(sessionID)
250 {
251     UNUSED_PARAM(registrationDatabaseDirectory);
252     allServers().add(this);
253     m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
254         taskThreadEntryPoint();
255     });
256 }
257
258 // https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
259 void SWServer::scheduleJob(ServiceWorkerJobData&& jobData)
260 {
261     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
262
263     if (!SWServerToContextConnection::globalServerToContextConnection()) {
264         m_pendingJobs.append(WTFMove(jobData));
265         return;
266     }
267
268     // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
269     // If it is, stack it along with that job.
270
271     auto& jobQueue = *m_jobQueues.ensure(jobData.registrationKey(), [this, &jobData] {
272         return std::make_unique<SWServerJobQueue>(*this, jobData.registrationKey());
273     }).iterator->value;
274
275     jobQueue.enqueueJob(jobData);
276     if (jobQueue.size() == 1)
277         jobQueue.runNextJob();
278 }
279
280 void SWServer::rejectJob(const ServiceWorkerJobData& jobData, const ExceptionData& exceptionData)
281 {
282     LOG(ServiceWorker, "Rejected ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
283     auto* connection = m_connections.get(jobData.connectionIdentifier());
284     if (!connection)
285         return;
286
287     connection->rejectJobInClient(jobData.identifier().jobIdentifier, exceptionData);
288 }
289
290 void SWServer::resolveRegistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
291 {
292     LOG(ServiceWorker, "Resolved ServiceWorker job %s in server with registration %s", jobData.identifier().loggingString().utf8().data(), registrationData.identifier.loggingString().utf8().data());
293     auto* connection = m_connections.get(jobData.connectionIdentifier());
294     if (!connection)
295         return;
296
297     connection->resolveRegistrationJobInClient(jobData.identifier().jobIdentifier, registrationData, shouldNotifyWhenResolved);
298 }
299
300 void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
301 {
302     auto* connection = m_connections.get(jobData.connectionIdentifier());
303     if (!connection)
304         return;
305
306     connection->resolveUnregistrationJobInClient(jobData.identifier().jobIdentifier, registrationKey, unregistrationResult);
307 }
308
309 void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData, FetchOptions::Cache cachePolicy)
310 {
311     LOG(ServiceWorker, "Server issuing startScriptFetch for current job %s in client", jobData.identifier().loggingString().utf8().data());
312     auto* connection = m_connections.get(jobData.connectionIdentifier());
313     if (!connection)
314         return;
315
316     connection->startScriptFetchInClient(jobData.identifier().jobIdentifier, jobData.registrationKey(), cachePolicy);
317 }
318
319 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
320 {
321     LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %s in client", result.jobDataIdentifier.loggingString().utf8().data());
322
323     ASSERT(m_connections.contains(result.jobDataIdentifier.connectionIdentifier));
324
325     auto jobQueue = m_jobQueues.get(result.registrationKey);
326     if (!jobQueue)
327         return;
328
329     jobQueue->scriptFetchFinished(connection, result);
330 }
331
332 void SWServer::scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, const String& message)
333 {
334     if (!jobDataIdentifier)
335         return;
336
337     RELEASE_LOG_ERROR(ServiceWorker, "%p - SWServer::scriptContextFailedToStart: Failed to start SW for job %s, error: %s", this, jobDataIdentifier->loggingString().utf8().data(), message.utf8().data());
338
339     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
340         jobQueue->scriptContextFailedToStart(*jobDataIdentifier, worker.identifier(), message);
341 }
342
343 void SWServer::scriptContextStarted(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker)
344 {
345     if (!jobDataIdentifier)
346         return;
347
348     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
349         jobQueue->scriptContextStarted(*jobDataIdentifier, worker.identifier());
350 }
351
352 void SWServer::didFinishInstall(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, SWServerWorker& worker, bool wasSuccessful)
353 {
354     if (!jobDataIdentifier)
355         return;
356
357     if (wasSuccessful)
358         RELEASE_LOG(ServiceWorker, "%p - SWServer::didFinishInstall: Successfuly finished SW install for job %s", this, jobDataIdentifier->loggingString().utf8().data());
359     else
360         RELEASE_LOG_ERROR(ServiceWorker, "%p - SWServer::didFinishInstall: Failed SW install for job %s", this, jobDataIdentifier->loggingString().utf8().data());
361
362     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
363         jobQueue->didFinishInstall(*jobDataIdentifier, worker.identifier(), wasSuccessful);
364 }
365
366 void SWServer::didFinishActivation(SWServerWorker& worker)
367 {
368     RELEASE_LOG(ServiceWorker, "%p - SWServer::didFinishActivation: Finished activation for service worker %llu", this, worker.identifier().toUInt64());
369
370     if (auto* registration = getRegistration(worker.registrationKey()))
371         registration->didFinishActivation(worker.identifier());
372 }
373
374 // https://w3c.github.io/ServiceWorker/#clients-getall
375 void SWServer::matchAll(SWServerWorker& worker, const ServiceWorkerClientQueryOptions& options, ServiceWorkerClientsMatchAllCallback&& callback)
376 {
377     // FIXME: Support reserved client filtering.
378     // FIXME: Support WindowClient additional properties.
379
380     Vector<ServiceWorkerClientData> matchingClients;
381     forEachClientForOrigin(worker.origin(), [&](auto& clientData) {
382         if (!options.includeUncontrolled && worker.identifier() != m_clientToControllingWorker.get(clientData.identifier))
383             return;
384         if (options.type != ServiceWorkerClientType::All && options.type != clientData.type)
385             return;
386         matchingClients.append(clientData);
387     });
388     callback(WTFMove(matchingClients));
389 }
390
391 void SWServer::forEachClientForOrigin(const ClientOrigin& origin, const WTF::Function<void(ServiceWorkerClientData&)>& apply)
392 {
393     auto iterator = m_clientIdentifiersPerOrigin.find(origin);
394     if (iterator == m_clientIdentifiersPerOrigin.end())
395         return;
396
397     for (auto& clientIdentifier : iterator->value.identifiers) {
398         auto clientIterator = m_clientsById.find(clientIdentifier);
399         ASSERT(clientIterator != m_clientsById.end());
400         apply(clientIterator->value);
401     }
402 }
403
404 void SWServer::claim(SWServerWorker& worker)
405 {
406     auto& origin = worker.origin();
407     forEachClientForOrigin(origin, [&](auto& clientData) {
408         auto* registration = doRegistrationMatching(origin.topOrigin, clientData.url);
409         if (!(registration && registration->key() == worker.registrationKey()))
410             return;
411
412         auto result = m_clientToControllingWorker.add(clientData.identifier, worker.identifier());
413         if (!result.isNewEntry) {
414             if (result.iterator->value == worker.identifier())
415                 return;
416             if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(result.iterator->value))
417                 controllingRegistration->removeClientUsingRegistration(clientData.identifier);
418             result.iterator->value = worker.identifier();
419         }
420         registration->controlClient(clientData.identifier);
421     });
422 }
423
424 void SWServer::didResolveRegistrationPromise(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey)
425 {
426     ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
427
428     if (auto* jobQueue = m_jobQueues.get(registrationKey))
429         jobQueue->didResolveRegistrationPromise();
430 }
431
432 void SWServer::addClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
433 {
434     auto* registration = m_registrationsByID.get(identifier);
435     if (!registration) {
436         LOG_ERROR("Request to add client-side ServiceWorkerRegistration to non-existent server-side registration");
437         return;
438     }
439     
440     registration->addClientServiceWorkerRegistration(connection.identifier());
441 }
442
443 void SWServer::removeClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier)
444 {
445     auto* registration = m_registrationsByID.get(identifier);
446     if (!registration) {
447         LOG_ERROR("Request to remove client-side ServiceWorkerRegistration from non-existent server-side registration");
448         return;
449     }
450     
451     registration->removeClientServiceWorkerRegistration(connection.identifier());
452 }
453
454 void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, WorkerType type)
455 {
456     registration.setLastUpdateTime(WallTime::now());
457     tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, contentSecurityPolicy, url, type, false });
458 }
459
460 void SWServer::tryInstallContextData(ServiceWorkerContextData&& data)
461 {
462     // Right now we only ever keep up to one connection to one SW context process.
463     // And it should always exist if we're trying to install context data.
464     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
465     if (!connection) {
466         m_pendingContextDatas.append(WTFMove(data));
467         return;
468     }
469     
470     installContextData(data);
471 }
472
473 void SWServer::serverToContextConnectionCreated()
474 {
475     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
476     ASSERT(connection);
477
478     auto pendingContextDatas = WTFMove(m_pendingContextDatas);
479     for (auto& data : pendingContextDatas)
480         installContextData(data);
481
482     auto serviceWorkerRunRequests = WTFMove(m_serviceWorkerRunRequests);
483     for (auto& item : serviceWorkerRunRequests) {
484         bool success = runServiceWorker(item.key);
485         for (auto& callback : item.value)
486             callback(success, *connection);
487     }
488
489     auto pendingJobs = WTFMove(m_pendingJobs);
490     for (auto& jobData : pendingJobs)
491         scheduleJob(WTFMove(jobData));
492 }
493
494 void SWServer::installContextData(const ServiceWorkerContextData& data)
495 {
496     if (!data.loadedFromDisk)
497         m_registrationStore.updateRegistration(data);
498
499     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
500     ASSERT(connection);
501
502     auto* registration = m_registrations.get(data.registration.key);
503     RELEASE_ASSERT(registration);
504
505     auto worker = SWServerWorker::create(*this, *registration, connection->identifier(), data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier);
506
507     // We don't immediately launch all workers that were just read in from disk,
508     // as it is unlikely they will be needed immediately.
509     if (data.loadedFromDisk) {
510         registration->updateRegistrationState(ServiceWorkerRegistrationState::Active, worker.ptr());
511         worker->setState(ServiceWorkerState::Activated);
512         return;
513     }
514
515     registration->setPreInstallationWorker(worker.ptr());
516     worker->setState(SWServerWorker::State::Running);
517     auto result = m_runningOrTerminatingWorkers.add(data.serviceWorkerIdentifier, WTFMove(worker));
518     ASSERT_UNUSED(result, result.isNewEntry);
519
520     connection->installServiceWorkerContext(data, m_sessionID);
521 }
522
523 void SWServer::runServiceWorkerIfNecessary(ServiceWorkerIdentifier identifier, RunServiceWorkerCallback&& callback)
524 {
525     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
526     if (auto* worker = m_runningOrTerminatingWorkers.get(identifier)) {
527         if (worker->isRunning()) {
528             ASSERT(connection);
529             callback(true, *connection);
530             return;
531         }
532     }
533
534     if (!connection) {
535         m_serviceWorkerRunRequests.ensure(identifier, [&] {
536             return Vector<RunServiceWorkerCallback> { };
537         }).iterator->value.append(WTFMove(callback));
538         return;
539     }
540
541     callback(runServiceWorker(identifier), *connection);
542 }
543
544 bool SWServer::runServiceWorker(ServiceWorkerIdentifier identifier)
545 {
546     auto* worker = workerByID(identifier);
547     if (!worker)
548         return false;
549
550     // If the registration for a working has been removed then the request to run
551     // the worker is moot.
552     if (!getRegistration(worker->registrationKey()))
553         return false;
554
555     auto addResult = m_runningOrTerminatingWorkers.add(identifier, *worker);
556     ASSERT_UNUSED(addResult, addResult.isNewEntry || worker->isTerminating());
557
558     worker->setState(SWServerWorker::State::Running);
559
560     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
561     ASSERT(connection);
562
563     // When re-running a service worker after a context process crash, the connection identifier may have changed
564     // so we update it here.
565     worker->setContextConnectionIdentifier(connection->identifier());
566
567     connection->installServiceWorkerContext(worker->contextData(), m_sessionID);
568
569     return true;
570 }
571
572 void SWServer::terminateWorker(SWServerWorker& worker)
573 {
574     terminateWorkerInternal(worker, Asynchronous);
575 }
576
577 void SWServer::syncTerminateWorker(SWServerWorker& worker)
578 {
579     terminateWorkerInternal(worker, Synchronous);
580 }
581
582 void SWServer::terminateWorkerInternal(SWServerWorker& worker, TerminationMode mode)
583 {
584     ASSERT(m_runningOrTerminatingWorkers.get(worker.identifier()) == &worker);
585     ASSERT(worker.isRunning());
586
587     RELEASE_LOG(ServiceWorker, "%p - SWServer::terminateWorkerInternal: Terminating service worker %llu", this, worker.identifier().toUInt64());
588
589     worker.setState(SWServerWorker::State::Terminating);
590
591     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
592     ASSERT(connection);
593     if (!connection) {
594         LOG_ERROR("Request to terminate a worker whose context connection does not exist");
595         workerContextTerminated(worker);
596         return;
597     }
598
599     switch (mode) {
600     case Asynchronous:
601         connection->terminateWorker(worker.identifier());
602         break;
603     case Synchronous:
604         connection->syncTerminateWorker(worker.identifier());
605         break;
606     };
607 }
608
609 void SWServer::markAllWorkersAsTerminated()
610 {
611     while (!m_runningOrTerminatingWorkers.isEmpty())
612         workerContextTerminated(m_runningOrTerminatingWorkers.begin()->value);
613 }
614
615 void SWServer::workerContextTerminated(SWServerWorker& worker)
616 {
617     worker.setState(SWServerWorker::State::NotRunning);
618
619     // At this point if no registrations are referencing the worker then it will be destroyed,
620     // removing itself from the m_workersByID map.
621     auto result = m_runningOrTerminatingWorkers.take(worker.identifier());
622     ASSERT_UNUSED(result, result && result->ptr() == &worker);
623 }
624
625 void SWServer::fireInstallEvent(SWServerWorker& worker)
626 {
627     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
628     if (!connection) {
629         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
630         return;
631     }
632
633     connection->fireInstallEvent(worker.identifier());
634 }
635
636 void SWServer::fireActivateEvent(SWServerWorker& worker)
637 {
638     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
639     if (!connection) {
640         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
641         return;
642     }
643
644     connection->fireActivateEvent(worker.identifier());
645 }
646
647 void SWServer::taskThreadEntryPoint()
648 {
649     ASSERT(!isMainThread());
650
651     while (!m_taskQueue.isKilled())
652         m_taskQueue.waitForMessage().performTask();
653
654     Locker<Lock> locker(m_taskThreadLock);
655     m_taskThread = nullptr;
656 }
657
658 void SWServer::postTask(CrossThreadTask&& task)
659 {
660     m_taskQueue.append(WTFMove(task));
661 }
662
663 void SWServer::postTaskReply(CrossThreadTask&& task)
664 {
665     m_taskReplyQueue.append(WTFMove(task));
666
667     Locker<Lock> locker(m_mainThreadReplyLock);
668     if (m_mainThreadReplyScheduled)
669         return;
670
671     m_mainThreadReplyScheduled = true;
672     callOnMainThread([this] {
673         handleTaskRepliesOnMainThread();
674     });
675 }
676
677 void SWServer::handleTaskRepliesOnMainThread()
678 {
679     {
680         Locker<Lock> locker(m_mainThreadReplyLock);
681         m_mainThreadReplyScheduled = false;
682     }
683
684     while (auto task = m_taskReplyQueue.tryGetMessage())
685         task->performTask();
686 }
687
688 void SWServer::registerConnection(Connection& connection)
689 {
690     auto result = m_connections.add(connection.identifier(), nullptr);
691     ASSERT(result.isNewEntry);
692     result.iterator->value = &connection;
693 }
694
695 void SWServer::unregisterConnection(Connection& connection)
696 {
697     ASSERT(m_connections.get(connection.identifier()) == &connection);
698     m_connections.remove(connection.identifier());
699
700     for (auto& registration : m_registrations.values())
701         registration->unregisterServerConnection(connection.identifier());
702 }
703
704 SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL)
705 {
706     SWServerRegistration* selectedRegistration = nullptr;
707     for (auto& registration : m_registrations.values()) {
708         if (!registration->key().isMatching(topOrigin, clientURL))
709             continue;
710         if (!selectedRegistration || selectedRegistration->key().scopeLength() < registration->key().scopeLength())
711             selectedRegistration = registration.get();
712     }
713
714     return (selectedRegistration && !selectedRegistration->isUninstalling()) ? selectedRegistration : nullptr;
715 }
716
717 void SWServer::setClientActiveWorker(ServiceWorkerClientIdentifier clientIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier)
718 {
719     m_clientToControllingWorker.set(clientIdentifier, serviceWorkerIdentifier);
720 }
721
722 SWServerRegistration* SWServer::registrationFromServiceWorkerIdentifier(ServiceWorkerIdentifier identifier)
723 {
724     auto iterator = m_runningOrTerminatingWorkers.find(identifier);
725     if (iterator == m_runningOrTerminatingWorkers.end())
726         return nullptr;
727
728     return m_registrations.get(iterator->value->registrationKey());
729 }
730
731 void SWServer::registerServiceWorkerClient(ClientOrigin&& clientOrigin, ServiceWorkerClientData&& data, const std::optional<ServiceWorkerIdentifier>& controllingServiceWorkerIdentifier)
732 {
733     auto clientIdentifier = data.identifier;
734     auto addResult = m_clientsById.add(clientIdentifier, WTFMove(data));
735     ASSERT_UNUSED(addResult, addResult.isNewEntry);
736
737     auto& clientIdentifiersForOrigin = m_clientIdentifiersPerOrigin.ensure(WTFMove(clientOrigin), [] {
738         return Clients { };
739     }).iterator->value;
740     clientIdentifiersForOrigin.identifiers.append(clientIdentifier);
741     clientIdentifiersForOrigin.terminateServiceWorkersTimer = nullptr;
742
743     if (!controllingServiceWorkerIdentifier)
744         return;
745
746     if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(*controllingServiceWorkerIdentifier)) {
747         controllingRegistration->addClientUsingRegistration(clientIdentifier);
748         auto result = m_clientToControllingWorker.add(clientIdentifier, *controllingServiceWorkerIdentifier);
749         ASSERT_UNUSED(result, result.isNewEntry);
750     }
751 }
752
753 void SWServer::unregisterServiceWorkerClient(const ClientOrigin& clientOrigin, ServiceWorkerClientIdentifier clientIdentifier)
754 {
755     bool wasRemoved = m_clientsById.remove(clientIdentifier);
756     ASSERT_UNUSED(wasRemoved, wasRemoved);
757
758     auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
759     ASSERT(iterator != m_clientIdentifiersPerOrigin.end());
760
761     auto& clientIdentifiers = iterator->value.identifiers;
762     clientIdentifiers.removeFirstMatching([&] (const auto& identifier) {
763         return clientIdentifier == identifier;
764     });
765     if (clientIdentifiers.isEmpty()) {
766         ASSERT(!iterator->value.terminateServiceWorkersTimer);
767         iterator->value.terminateServiceWorkersTimer = std::make_unique<Timer>([clientOrigin, this] {
768             for (auto& worker : m_runningOrTerminatingWorkers.values()) {
769                 if (worker->isRunning() && worker->origin() == clientOrigin)
770                     terminateWorker(worker);
771             }
772             m_clientIdentifiersPerOrigin.remove(clientOrigin);
773         });
774         iterator->value.terminateServiceWorkersTimer->startOneShot(terminationDelay);
775     }
776
777     auto workerIterator = m_clientToControllingWorker.find(clientIdentifier);
778     if (workerIterator == m_clientToControllingWorker.end())
779         return;
780
781     if (auto* controllingRegistration = registrationFromServiceWorkerIdentifier(workerIterator->value))
782         controllingRegistration->removeClientUsingRegistration(clientIdentifier);
783
784     m_clientToControllingWorker.remove(workerIterator);
785 }
786
787 void SWServer::resolveRegistrationReadyRequests(SWServerRegistration& registration)
788 {
789     for (auto* connection : m_connections.values())
790         connection->resolveRegistrationReadyRequests(registration);
791 }
792
793 void SWServer::Connection::whenRegistrationReady(uint64_t registrationReadyRequestIdentifier, const SecurityOriginData& topOrigin, const URL& clientURL)
794 {
795     if (auto* registration = doRegistrationMatching(topOrigin, clientURL)) {
796         if (registration->activeWorker()) {
797             registrationReady(registrationReadyRequestIdentifier, registration->data());
798             return;
799         }
800     }
801     m_registrationReadyRequests.append({ topOrigin, clientURL, registrationReadyRequestIdentifier });
802 }
803
804 void SWServer::Connection::resolveRegistrationReadyRequests(SWServerRegistration& registration)
805 {
806     m_registrationReadyRequests.removeAllMatching([&](auto& request) {
807         if (!registration.key().isMatching(request.topOrigin, request.clientURL))
808             return false;
809
810         registrationReady(request.identifier, registration.data());
811         return true;
812     });
813 }
814
815 void SWServer::getOriginsWithRegistrations(WTF::Function<void(const HashSet<SecurityOriginData>&)> callback)
816 {
817     m_getOriginsWithRegistrationsCallbacks.append(WTFMove(callback));
818
819     if (m_importCompleted)
820         performGetOriginsWithRegistrationsCallbacks();
821 }
822
823 void SWServer::performGetOriginsWithRegistrationsCallbacks()
824 {
825     ASSERT(isMainThread());
826     ASSERT(m_importCompleted);
827
828     if (m_getOriginsWithRegistrationsCallbacks.isEmpty())
829         return;
830
831     HashSet<SecurityOriginData> originsWithRegistrations;
832     for (auto& key : m_registrations.keys()) {
833         originsWithRegistrations.add(key.topOrigin());
834         originsWithRegistrations.add(SecurityOriginData { key.scope().protocol().toString(), key.scope().host(), key.scope().port() });
835     }
836
837     auto callbacks = WTFMove(m_getOriginsWithRegistrationsCallbacks);
838     for (auto& callback : callbacks)
839         callback(originsWithRegistrations);
840 }
841
842 } // namespace WebCore
843
844 #endif // ENABLE(SERVICE_WORKER)