199be417dddc07ba1e870b75a61d72b40f0e6229
[WebKit-https.git] / Source / WebCore / workers / service / ServiceWorkerContainer.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 "ServiceWorkerContainer.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "Document.h"
32 #include "Event.h"
33 #include "EventNames.h"
34 #include "Exception.h"
35 #include "IDLTypes.h"
36 #include "JSDOMPromiseDeferred.h"
37 #include "JSServiceWorkerRegistration.h"
38 #include "Logging.h"
39 #include "NavigatorBase.h"
40 #include "ResourceError.h"
41 #include "SchemeRegistry.h"
42 #include "ScriptExecutionContext.h"
43 #include "SecurityOrigin.h"
44 #include "ServiceWorker.h"
45 #include "ServiceWorkerFetchResult.h"
46 #include "ServiceWorkerGlobalScope.h"
47 #include "ServiceWorkerJob.h"
48 #include "ServiceWorkerJobData.h"
49 #include "ServiceWorkerProvider.h"
50 #include "ServiceWorkerThread.h"
51 #include "URL.h"
52 #include <wtf/RunLoop.h>
53 #include <wtf/Scope.h>
54
55 #define CONTAINER_RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - ServiceWorkerContainer::" fmt, this, ##__VA_ARGS__)
56 #define CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - ServiceWorkerContainer::" fmt, this, ##__VA_ARGS__)
57
58 namespace WebCore {
59
60 ServiceWorkerContainer::ServiceWorkerContainer(ScriptExecutionContext& context, NavigatorBase& navigator)
61     : ActiveDOMObject(&context)
62     , m_navigator(navigator)
63 {
64     suspendIfNeeded();
65 }
66
67 ServiceWorkerContainer::~ServiceWorkerContainer()
68 {
69 #ifndef NDEBUG
70     ASSERT(m_creationThread.ptr() == &Thread::current());
71 #endif
72 }
73
74 void ServiceWorkerContainer::refEventTarget()
75 {
76     m_navigator.ref();
77 }
78
79 void ServiceWorkerContainer::derefEventTarget()
80 {
81     m_navigator.deref();
82 }
83
84 auto ServiceWorkerContainer::ready() -> ReadyPromise&
85 {
86     if (!m_readyPromise) {
87         m_readyPromise = std::make_unique<ReadyPromise>();
88
89         if (m_isStopped || !scriptExecutionContext()->sessionID().isValid())
90             return *m_readyPromise;
91
92         auto& context = *scriptExecutionContext();
93         auto contextIdentifier = this->contextIdentifier();
94         callOnMainThread([this, connection = makeRef(ensureSWClientConnection()), topOrigin = context.topOrigin().isolatedCopy(), clientURL = context.url().isolatedCopy(), contextIdentifier]() mutable {
95             connection->whenRegistrationReady(topOrigin, clientURL, [this, contextIdentifier](auto&& registrationData) {
96                 ScriptExecutionContext::postTaskTo(contextIdentifier, [this, registrationData = crossThreadCopy(registrationData)](auto&) mutable {
97                     if (m_isStopped || !this->scriptExecutionContext()->sessionID().isValid())
98                         return;
99
100                     auto registration = ServiceWorkerRegistration::getOrCreate(*this->scriptExecutionContext(), *this, WTFMove(registrationData));
101                     m_readyPromise->resolve(WTFMove(registration));
102                 });
103             });
104         });
105     }
106     return *m_readyPromise;
107 }
108
109 ServiceWorker* ServiceWorkerContainer::controller() const
110 {
111     auto* context = scriptExecutionContext();
112     ASSERT_WITH_MESSAGE(!context || is<Document>(*context) || !context->activeServiceWorker(), "Only documents can have a controller at the moment.");
113     return context ? context->activeServiceWorker() : nullptr;
114 }
115
116 void ServiceWorkerContainer::addRegistration(const String& relativeScriptURL, const RegistrationOptions& options, Ref<DeferredPromise>&& promise)
117 {
118     auto* context = scriptExecutionContext();
119     if (m_isStopped || !context->sessionID().isValid()) {
120         promise->reject(Exception(InvalidStateError));
121         return;
122     }
123
124     if (relativeScriptURL.isEmpty()) {
125         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() cannot be called with an empty script URL") });
126         return;
127     }
128
129     ServiceWorkerJobData jobData(ensureSWClientConnection().serverConnectionIdentifier(), contextIdentifier());
130
131     jobData.scriptURL = context->completeURL(relativeScriptURL);
132     if (!jobData.scriptURL.isValid()) {
133         CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("addRegistration: Invalid scriptURL");
134         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a valid relative script URL") });
135         return;
136     }
137
138     if (!SchemeRegistry::canServiceWorkersHandleURLScheme(jobData.scriptURL.protocol().toStringWithoutCopying())) {
139         CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("addRegistration: Invalid scriptURL scheme is not HTTP or HTTPS");
140         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a script URL whose protocol is either HTTP or HTTPS") });
141         return;
142     }
143
144     String path = jobData.scriptURL.path();
145     if (path.containsIgnoringASCIICase("%2f") || path.containsIgnoringASCIICase("%5c")) {
146         CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("addRegistration: scriptURL contains invalid character");
147         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a script URL whose path does not contain '%2f' or '%5c'") });
148         return;
149     }
150
151     if (!options.scope.isEmpty())
152         jobData.scopeURL = context->completeURL(options.scope);
153     else
154         jobData.scopeURL = URL(jobData.scriptURL, "./");
155
156     if (!jobData.scopeURL.isNull() && !SchemeRegistry::canServiceWorkersHandleURLScheme(jobData.scopeURL.protocol().toStringWithoutCopying())) {
157         CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("addRegistration: scopeURL scheme is not HTTP or HTTPS");
158         promise->reject(Exception { TypeError, ASCIILiteral("Scope URL provided to serviceWorker.register() must be either HTTP or HTTPS") });
159         return;
160     }
161
162     path = jobData.scopeURL.path();
163     if (path.containsIgnoringASCIICase("%2f") || path.containsIgnoringASCIICase("%5c")) {
164         CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("addRegistration: scopeURL contains invalid character");
165         promise->reject(Exception { TypeError, ASCIILiteral("Scope URL provided to serviceWorker.register() cannot have a path that contains '%2f' or '%5c'") });
166         return;
167     }
168
169     CONTAINER_RELEASE_LOG_IF_ALLOWED("addRegistration: Registering service worker. Job ID: %llu", jobData.identifier().jobIdentifier.toUInt64());
170
171     jobData.clientCreationURL = context->url();
172     jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context->topOrigin());
173     jobData.type = ServiceWorkerJobType::Register;
174     jobData.registrationOptions = options;
175
176     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
177 }
178
179 void ServiceWorkerContainer::removeRegistration(const URL& scopeURL, Ref<DeferredPromise>&& promise)
180 {
181     auto* context = scriptExecutionContext();
182     if (!context || !context->sessionID().isValid()) {
183         ASSERT_NOT_REACHED();
184         promise->reject(Exception(InvalidStateError));
185         return;
186     }
187
188     if (!m_swConnection) {
189         ASSERT_NOT_REACHED();
190         promise->reject(Exception(InvalidStateError));
191         return;
192     }
193
194     ServiceWorkerJobData jobData(m_swConnection->serverConnectionIdentifier(), contextIdentifier());
195     jobData.clientCreationURL = context->url();
196     jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context->topOrigin());
197     jobData.type = ServiceWorkerJobType::Unregister;
198     jobData.scopeURL = scopeURL;
199
200     CONTAINER_RELEASE_LOG_IF_ALLOWED("removeRegistration: Unregistering service worker. Job ID: %llu", jobData.identifier().jobIdentifier.toUInt64());
201
202     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
203 }
204
205 void ServiceWorkerContainer::updateRegistration(const URL& scopeURL, const URL& scriptURL, WorkerType, RefPtr<DeferredPromise>&& promise)
206 {
207     ASSERT(!m_isStopped);
208
209     auto& context = *scriptExecutionContext();
210     ASSERT(context.sessionID().isValid());
211
212     if (!m_swConnection) {
213         ASSERT_NOT_REACHED();
214         if (promise)
215             promise->reject(Exception(InvalidStateError));
216         return;
217     }
218
219     ServiceWorkerJobData jobData(m_swConnection->serverConnectionIdentifier(), contextIdentifier());
220     jobData.clientCreationURL = context.url();
221     jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context.topOrigin());
222     jobData.type = ServiceWorkerJobType::Update;
223     jobData.scopeURL = scopeURL;
224     jobData.scriptURL = scriptURL;
225
226     CONTAINER_RELEASE_LOG_IF_ALLOWED("removeRegistration: Updating service worker. Job ID: %llu", jobData.identifier().jobIdentifier.toUInt64());
227
228     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
229 }
230
231 void ServiceWorkerContainer::scheduleJob(Ref<ServiceWorkerJob>&& job)
232 {
233 #ifndef NDEBUG
234     ASSERT(m_creationThread.ptr() == &Thread::current());
235 #endif
236
237     ASSERT(m_swConnection);
238
239     setPendingActivity(this);
240
241     auto& jobData = job->data();
242     auto result = m_jobMap.add(job->identifier(), WTFMove(job));
243     ASSERT_UNUSED(result, result.isNewEntry);
244
245     callOnMainThread([connection = m_swConnection, contextIdentifier = this->contextIdentifier(), jobData = jobData.isolatedCopy()] {
246         connection->scheduleJob(contextIdentifier, jobData);
247     });
248 }
249
250 void ServiceWorkerContainer::getRegistration(const String& clientURL, Ref<DeferredPromise>&& promise)
251 {
252     auto* context = scriptExecutionContext();
253     if (m_isStopped || !context->sessionID().isValid()) {
254         promise->reject(Exception { InvalidStateError });
255         return;
256     }
257
258     URL parsedURL = context->completeURL(clientURL);
259     if (!protocolHostAndPortAreEqual(parsedURL, context->url())) {
260         promise->reject(Exception { SecurityError, ASCIILiteral("Origin of clientURL is not client's origin") });
261         return;
262     }
263
264     uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
265     auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
266     m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
267
268     auto contextIdentifier = this->contextIdentifier();
269     callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context->topOrigin().isolatedCopy(), parsedURL = parsedURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
270         connection->matchRegistration(topOrigin, parsedURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& result) mutable {
271             ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, result = crossThreadCopy(result)](ScriptExecutionContext&) mutable {
272                 this->didFinishGetRegistrationRequest(pendingPromiseIdentifier, WTFMove(result));
273             });
274         });
275     });
276 }
277
278 void ServiceWorkerContainer::didFinishGetRegistrationRequest(uint64_t pendingPromiseIdentifier, std::optional<ServiceWorkerRegistrationData>&& result)
279 {
280 #ifndef NDEBUG
281     ASSERT(m_creationThread.ptr() == &Thread::current());
282 #endif
283
284     auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
285     if (!pendingPromise)
286         return;
287
288     if (m_isStopped || !scriptExecutionContext()->sessionID().isValid()) {
289         pendingPromise->promise->reject(Exception { InvalidStateError });
290         return;
291     }
292
293     if (!result) {
294         pendingPromise->promise->resolve();
295         return;
296     }
297
298     auto registration = ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(result.value()));
299     pendingPromise->promise->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
300 }
301
302 void ServiceWorkerContainer::scheduleTaskToUpdateRegistrationState(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
303 {
304     auto* context = scriptExecutionContext();
305     if (!context)
306         return;
307
308     RefPtr<ServiceWorker> serviceWorker;
309     if (serviceWorkerData)
310         serviceWorker = ServiceWorker::getOrCreate(*context, ServiceWorkerData { *serviceWorkerData });
311
312     context->postTask([this, protectedThis = makeRef(*this), identifier, state, serviceWorker = WTFMove(serviceWorker)](ScriptExecutionContext&) mutable {
313         if (auto* registration = m_registrations.get(identifier))
314             registration->updateStateFromServer(state, WTFMove(serviceWorker));
315     });
316 }
317
318 void ServiceWorkerContainer::getRegistrations(Ref<DeferredPromise>&& promise)
319 {
320     auto* context = scriptExecutionContext();
321     if (m_isStopped || !context->sessionID().isValid()) {
322         promise->reject(Exception { InvalidStateError });
323         return;
324     }
325
326     uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
327     auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
328     m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
329
330     auto contextIdentifier = this->contextIdentifier();
331     auto contextURL = context->url();
332     callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context->topOrigin().isolatedCopy(), contextURL = contextURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
333         connection->getRegistrations(topOrigin, contextURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& registrationDatas) mutable {
334             ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, registrationDatas = crossThreadCopy(registrationDatas)](ScriptExecutionContext&) mutable {
335                 this->didFinishGetRegistrationsRequest(pendingPromiseIdentifier, WTFMove(registrationDatas));
336             });
337         });
338     });
339 }
340
341 void ServiceWorkerContainer::didFinishGetRegistrationsRequest(uint64_t pendingPromiseIdentifier, Vector<ServiceWorkerRegistrationData>&& registrationDatas)
342 {
343 #ifndef NDEBUG
344     ASSERT(m_creationThread.ptr() == &Thread::current());
345 #endif
346
347     auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
348     if (!pendingPromise)
349         return;
350
351     if (m_isStopped || !scriptExecutionContext()->sessionID().isValid()) {
352         pendingPromise->promise->reject(Exception { InvalidStateError });
353         return;
354     }
355
356     auto registrations = WTF::map(WTFMove(registrationDatas), [&] (auto&& registrationData) {
357         return ServiceWorkerRegistration::getOrCreate(*this->scriptExecutionContext(), *this, WTFMove(registrationData));
358     });
359
360     pendingPromise->promise->resolve<IDLSequence<IDLInterface<ServiceWorkerRegistration>>>(WTFMove(registrations));
361 }
362
363 void ServiceWorkerContainer::startMessages()
364 {
365 }
366
367 void ServiceWorkerContainer::jobFailedWithException(ServiceWorkerJob& job, const Exception& exception)
368 {
369 #ifndef NDEBUG
370     ASSERT(m_creationThread.ptr() == &Thread::current());
371 #endif
372
373     ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
374
375     auto guard = WTF::makeScopeExit([this, &job] {
376         jobDidFinish(job);
377     });
378
379     CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("jobFailedWithException: Job %llu failed with error %s", job.identifier().toUInt64(), exception.message().utf8().data());
380
381     if (!job.promise())
382         return;
383
384     if (auto* context = scriptExecutionContext()) {
385         context->postTask([job = makeRef(job), exception](ScriptExecutionContext&) {
386             job->promise()->reject(exception);
387         });
388     }
389 }
390
391 void ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
392 {
393 #ifndef NDEBUG
394     ASSERT(m_creationThread.ptr() == &Thread::current());
395 #endif
396
397     if (auto* registration = m_registrations.get(identifier))
398         registration->scheduleTaskToFireUpdateFoundEvent();
399 }
400
401 void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job, ServiceWorkerRegistrationData&& data, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
402 {
403 #ifndef NDEBUG
404     ASSERT(m_creationThread.ptr() == &Thread::current());
405 #endif
406     ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
407
408     auto guard = WTF::makeScopeExit([this, &job] {
409         jobDidFinish(job);
410     });
411
412     if (job.data().type == ServiceWorkerJobType::Register)
413         CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithRegistration: Registration job %llu succeeded", job.identifier().toUInt64());
414     else {
415         ASSERT(job.data().type == ServiceWorkerJobType::Update);
416         CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithRegistration: Update job %llu succeeded", job.identifier().toUInt64());
417     }
418
419     WTF::Function<void()> notifyWhenResolvedIfNeeded = [] { };
420     if (shouldNotifyWhenResolved == ShouldNotifyWhenResolved::Yes) {
421         notifyWhenResolvedIfNeeded = [connection = m_swConnection, registrationKey = data.key.isolatedCopy()]() mutable {
422             callOnMainThread([connection = WTFMove(connection), registrationKey = WTFMove(registrationKey)] {
423                 connection->didResolveRegistrationPromise(registrationKey);
424             });
425         };
426     }
427
428     if (isStopped()) {
429         notifyWhenResolvedIfNeeded();
430         return;
431     }
432
433     if (!job.promise()) {
434         notifyWhenResolvedIfNeeded();
435         return;
436     }
437
438     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this), job = makeRef(job), data = WTFMove(data), notifyWhenResolvedIfNeeded = WTFMove(notifyWhenResolvedIfNeeded)](ScriptExecutionContext& context) mutable {
439         if (isStopped() || !context.sessionID().isValid()) {
440             notifyWhenResolvedIfNeeded();
441             return;
442         }
443
444         auto registration = ServiceWorkerRegistration::getOrCreate(context, *this, WTFMove(data));
445
446         CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithRegistration: Resolving promise for job %llu. Registration ID: %llu", job->identifier().toUInt64(), registration->identifier().toUInt64());
447
448         job->promise()->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
449
450         notifyWhenResolvedIfNeeded();
451     });
452 }
453
454 void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJob& job, bool unregistrationResult)
455 {
456 #ifndef NDEBUG
457     ASSERT(m_creationThread.ptr() == &Thread::current());
458 #endif
459
460     ASSERT(job.promise());
461
462     auto guard = WTF::makeScopeExit([this, &job] {
463         jobDidFinish(job);
464     });
465
466     CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithUnregistrationResult: Unregister job %llu finished. Success? %d", job.identifier().toUInt64(), unregistrationResult);
467
468     auto* context = scriptExecutionContext();
469     if (!context) {
470         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithUnregistrationResult called but the containers ScriptExecutionContext is gone");
471         return;
472     }
473
474     context->postTask([job = makeRef(job), unregistrationResult](ScriptExecutionContext&) mutable {
475         job->promise()->resolve<IDLBoolean>(unregistrationResult);
476     });
477 }
478
479 void ServiceWorkerContainer::startScriptFetchForJob(ServiceWorkerJob& job, FetchOptions::Cache cachePolicy)
480 {
481 #ifndef NDEBUG
482     ASSERT(m_creationThread.ptr() == &Thread::current());
483 #endif
484
485     CONTAINER_RELEASE_LOG_IF_ALLOWED("startScriptFetchForJob: Starting script fetch for job %llu", job.identifier().toUInt64());
486
487     auto* context = scriptExecutionContext();
488     if (!context) {
489         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithRegistration called but the container's ScriptExecutionContext is gone");
490         callOnMainThread([connection = m_swConnection, jobIdentifier = job.identifier(), registrationKey = job.data().registrationKey(), scriptURL = job.data().scriptURL.isolatedCopy()] {
491             connection->failedFetchingScript(jobIdentifier, registrationKey, { errorDomainWebKitInternal, 0, scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
492         });
493         jobDidFinish(job);
494         return;
495     }
496
497     job.fetchScriptWithContext(*context, cachePolicy);
498 }
499
500 void ServiceWorkerContainer::jobFinishedLoadingScript(ServiceWorkerJob& job, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy)
501 {
502 #ifndef NDEBUG
503     ASSERT(m_creationThread.ptr() == &Thread::current());
504 #endif
505
506     CONTAINER_RELEASE_LOG_IF_ALLOWED("jobFinishedLoadingScript: Successfuly finished fetching script for job %llu", job.identifier().toUInt64());
507
508     callOnMainThread([connection = m_swConnection, jobDataIdentifier = job.data().identifier(), registrationKey = job.data().registrationKey(), script = script.isolatedCopy(), contentSecurityPolicy = contentSecurityPolicy.isolatedCopy()] {
509         connection->finishFetchingScriptInServer({ jobDataIdentifier, registrationKey, script, contentSecurityPolicy, { } });
510     });
511 }
512
513 void ServiceWorkerContainer::jobFailedLoadingScript(ServiceWorkerJob& job, const ResourceError& error, std::optional<Exception>&& exception)
514 {
515 #ifndef NDEBUG
516     ASSERT(m_creationThread.ptr() == &Thread::current());
517 #endif
518     ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
519
520     CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("jobFinishedLoadingScript: Failed to fetch script for job %llu, error: %s", job.identifier().toUInt64(), error.localizedDescription().utf8().data());
521
522     if (exception && job.promise())
523         job.promise()->reject(*exception);
524
525     callOnMainThread([connection = m_swConnection, jobIdentifier = job.identifier(), registrationKey = job.data().registrationKey(), error = error.isolatedCopy()] {
526         connection->failedFetchingScript(jobIdentifier, registrationKey, error);
527     });
528 }
529
530 void ServiceWorkerContainer::jobDidFinish(ServiceWorkerJob& job)
531 {
532 #ifndef NDEBUG
533     ASSERT(m_creationThread.ptr() == &Thread::current());
534 #endif
535
536     auto taken = m_jobMap.take(job.identifier());
537     ASSERT_UNUSED(taken, !taken || taken->ptr() == &job);
538
539     unsetPendingActivity(this);
540 }
541
542 SWServerConnectionIdentifier ServiceWorkerContainer::connectionIdentifier()
543 {
544     ASSERT(m_swConnection);
545     return m_swConnection->serverConnectionIdentifier();
546 }
547
548 const char* ServiceWorkerContainer::activeDOMObjectName() const
549 {
550     return "ServiceWorkerContainer";
551 }
552
553 bool ServiceWorkerContainer::canSuspendForDocumentSuspension() const
554 {
555     return !hasPendingActivity();
556 }
557
558 SWClientConnection& ServiceWorkerContainer::ensureSWClientConnection()
559 {
560     ASSERT(scriptExecutionContext());
561     ASSERT(scriptExecutionContext()->sessionID().isValid());
562     if (!m_swConnection) {
563         ASSERT(scriptExecutionContext());
564         callOnMainThreadAndWait([this, sessionID = scriptExecutionContext()->sessionID()]() {
565             m_swConnection = &ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
566         });
567     }
568     return *m_swConnection;
569 }
570
571 void ServiceWorkerContainer::addRegistration(ServiceWorkerRegistration& registration)
572 {
573 #ifndef NDEBUG
574     ASSERT(m_creationThread.ptr() == &Thread::current());
575 #endif
576
577     ensureSWClientConnection().addServiceWorkerRegistrationInServer(registration.identifier());
578     m_registrations.add(registration.identifier(), &registration);
579 }
580
581 void ServiceWorkerContainer::removeRegistration(ServiceWorkerRegistration& registration)
582 {
583 #ifndef NDEBUG
584     ASSERT(m_creationThread.ptr() == &Thread::current());
585 #endif
586
587     m_swConnection->removeServiceWorkerRegistrationInServer(registration.identifier());
588     m_registrations.remove(registration.identifier());
589 }
590
591 void ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent()
592 {
593 #ifndef NDEBUG
594     ASSERT(m_creationThread.ptr() == &Thread::current());
595 #endif
596
597     if (m_isStopped)
598         return;
599
600     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)](ScriptExecutionContext&) mutable {
601         if (m_isStopped)
602             return;
603
604         dispatchEvent(Event::create(eventNames().controllerchangeEvent, false, false));
605     });
606 }
607
608 void ServiceWorkerContainer::stop()
609 {
610     m_isStopped = true;
611     removeAllEventListeners();
612     m_pendingPromises.clear();
613     for (auto& job : m_jobMap.values())
614         job->cancelPendingLoad();
615 }
616
617 DocumentOrWorkerIdentifier ServiceWorkerContainer::contextIdentifier()
618 {
619 #ifndef NDEBUG
620     ASSERT(m_creationThread.ptr() == &Thread::current());
621 #endif
622
623     ASSERT(scriptExecutionContext());
624     if (is<ServiceWorkerGlobalScope>(*scriptExecutionContext()))
625         return downcast<ServiceWorkerGlobalScope>(*scriptExecutionContext()).thread().identifier();
626     return downcast<Document>(*scriptExecutionContext()).identifier();
627 }
628
629 bool ServiceWorkerContainer::isAlwaysOnLoggingAllowed() const
630 {
631     auto* context = scriptExecutionContext();
632     if (!context)
633         return false;
634
635     if (is<Document>(*context))
636         return downcast<Document>(*context).sessionID().isAlwaysOnLoggingAllowed();
637
638     // FIXME: No logging inside service workers for now.
639     return false;
640 }
641
642 } // namespace WebCore
643
644 #endif // ENABLE(SERVICE_WORKER)