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(SWServerConnectionIdentifier::generate()) |
56 | { |
57 | } |
58 | |
59 | HashSet<SWServer*>& SWServer::allServers() |
60 | { |
61 | static NeverDestroyed<HashSet<SWServer*>> servers; |
62 | return servers; |
63 | } |
64 | |
65 | SWServer::~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 | |
83 | SWServerWorker* SWServer::workerByID(ServiceWorkerIdentifier identifier) const |
84 | { |
85 | auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier); |
86 | ASSERT(!worker || &worker->server() == this); |
87 | return worker; |
88 | } |
89 | |
90 | Optional<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 | |
104 | String 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 | |
112 | SWServerWorker* SWServer::activeWorkerFromRegistrationID(ServiceWorkerRegistrationIdentifier identifier) |
113 | { |
114 | auto* registration = m_registrationsByID.get(identifier); |
115 | return registration ? registration->activeWorker() : nullptr; |
116 | } |
117 | |
118 | SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationKey& registrationKey) |
119 | { |
120 | return m_registrations.get(registrationKey); |
121 | } |
122 | |
123 | void 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 | |
136 | void SWServer::registrationStoreDatabaseFailedToOpen() |
137 | { |
138 | if (!m_importCompleted) |
139 | registrationStoreImportComplete(); |
140 | } |
141 | |
142 | void 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 | |
157 | void 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 | |
170 | void 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 | |
183 | Vector<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 | |
201 | void 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 | |
221 | void SWServer::startSuspension(CompletionHandler<void()>&& completionHandler) |
222 | { |
223 | if (m_registrationStore) |
224 | m_registrationStore->startSuspension(WTFMove(completionHandler)); |
225 | } |
226 | |
227 | void SWServer::endSuspension() |
228 | { |
229 | if (m_registrationStore) |
230 | m_registrationStore->endSuspension(); |
231 | } |
232 | |
233 | void 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 | |
272 | void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result) |
273 | { |
274 | m_server.scriptFetchFinished(*this, result); |
275 | } |
276 | |
277 | void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key) |
278 | { |
279 | m_server.didResolveRegistrationPromise(*this, key); |
280 | } |
281 | |
282 | void SWServer::Connection::addServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier) |
283 | { |
284 | m_server.addClientServiceWorkerRegistration(*this, identifier); |
285 | } |
286 | |
287 | void SWServer::Connection::removeServiceWorkerRegistrationInServer(ServiceWorkerRegistrationIdentifier identifier) |
288 | { |
289 | m_server.removeClientServiceWorkerRegistration(*this, identifier); |
290 | } |
291 | |
292 | void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifier) |
293 | { |
294 | if (auto* worker = m_server.workerByID(identifier)) |
295 | m_server.syncTerminateWorker(*worker); |
296 | } |
297 | |
298 | SWServer::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 |
313 | void 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 | |
329 | void 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 | |
339 | void 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 | |
349 | void 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 | |
358 | void 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 | |
367 | void 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 | |
380 | void 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 | |
396 | void 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 | |
410 | void 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 | |
418 | void 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 | |
432 | void 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 |
446 | void 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 | |
467 | void 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 | |
480 | void 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 | |
501 | void 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 | |
509 | void 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 | |
520 | void SWServer::removeClientServiceWorkerRegistration(Connection& connection, ServiceWorkerRegistrationIdentifier identifier) |
521 | { |
522 | if (auto* registration = m_registrationsByID.get(identifier)) |
523 | registration->removeClientServiceWorkerRegistration(connection.identifier()); |
524 | } |
525 | |
526 | void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, const String& referrerPolicy, WorkerType type, HashMap<URL, ServiceWorkerContextData::ImportedScript>&& scriptResourceMap) |
527 | { |
528 | tryInstallContextData({ jobDataIdentifier, registration.data(), ServiceWorkerIdentifier::generate(), script, contentSecurityPolicy, referrerPolicy, url, type, sessionID(), false, WTFMove(scriptResourceMap) }); |
529 | } |
530 | |
531 | void 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 | |
545 | void 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 | |
562 | void 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 | |
590 | void 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 | |
619 | bool 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 | |
643 | void SWServer::terminateWorker(SWServerWorker& worker) |
644 | { |
645 | terminateWorkerInternal(worker, Asynchronous); |
646 | } |
647 | |
648 | void SWServer::syncTerminateWorker(SWServerWorker& worker) |
649 | { |
650 | terminateWorkerInternal(worker, Synchronous); |
651 | } |
652 | |
653 | void 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 | |
680 | void 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 | |
691 | void 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 | |
704 | void 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 | |
715 | void 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 | |
726 | void 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 | |
733 | void 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 | |
745 | SWServerRegistration* 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 | |
758 | SWServerRegistration* 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 | |
767 | void 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 | |
803 | void 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 | |
856 | bool SWServer::needsServerToContextConnectionForRegistrableDomain(const RegistrableDomain& registrableDomain) const |
857 | { |
858 | return m_clientsByRegistrableDomain.contains(registrableDomain); |
859 | } |
860 | |
861 | void SWServer::resolveRegistrationReadyRequests(SWServerRegistration& registration) |
862 | { |
863 | for (auto& connection : m_connections.values()) |
864 | connection->resolveRegistrationReadyRequests(registration); |
865 | } |
866 | |
867 | void 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 | |
878 | void 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 | |
889 | void SWServer::getOriginsWithRegistrations(Function<void(const HashSet<SecurityOriginData>&)>&& callback) |
890 | { |
891 | m_getOriginsWithRegistrationsCallbacks.append(WTFMove(callback)); |
892 | |
893 | if (m_importCompleted) |
894 | performGetOriginsWithRegistrationsCallbacks(); |
895 | } |
896 | |
897 | void 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 | |