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