Add release logging to help debug issues related to service workers
[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         auto* context = scriptExecutionContext();
90         if (!context)
91             return *m_readyPromise;
92
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)
98                         return;
99
100                     auto registration = ServiceWorkerRegistration::getOrCreate(*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());
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());
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());
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 = WTFMove(jobData)] {
246         connection->scheduleJob(contextIdentifier, jobData);
247     });
248 }
249
250 void ServiceWorkerContainer::getRegistration(const String& clientURL, Ref<DeferredPromise>&& promise)
251 {
252     if (m_isStopped) {
253         promise->reject(Exception { InvalidStateError });
254         return;
255     }
256
257     ASSERT(scriptExecutionContext());
258     auto& context = *scriptExecutionContext();
259
260     URL parsedURL = context.completeURL(clientURL);
261     if (!protocolHostAndPortAreEqual(parsedURL, context.url())) {
262         promise->reject(Exception { SecurityError, ASCIILiteral("Origin of clientURL is not client's origin") });
263         return;
264     }
265
266     uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
267     auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
268     m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
269
270     auto contextIdentifier = this->contextIdentifier();
271     callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context.topOrigin().isolatedCopy(), parsedURL = parsedURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
272         connection->matchRegistration(topOrigin, parsedURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& result) mutable {
273             ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, result = crossThreadCopy(result)](ScriptExecutionContext&) mutable {
274                 didFinishGetRegistrationRequest(pendingPromiseIdentifier, WTFMove(result));
275             });
276         });
277     });
278 }
279
280 void ServiceWorkerContainer::didFinishGetRegistrationRequest(uint64_t pendingPromiseIdentifier, std::optional<ServiceWorkerRegistrationData>&& result)
281 {
282 #ifndef NDEBUG
283     ASSERT(m_creationThread.ptr() == &Thread::current());
284 #endif
285
286     auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
287     if (!pendingPromise)
288         return;
289
290     ASSERT(!m_isStopped);
291
292     if (!result) {
293         pendingPromise->promise->resolve();
294         return;
295     }
296
297     auto registration = ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(result.value()));
298     pendingPromise->promise->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
299 }
300
301 void ServiceWorkerContainer::scheduleTaskToUpdateRegistrationState(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
302 {
303     auto* context = scriptExecutionContext();
304     if (!context)
305         return;
306
307     RefPtr<ServiceWorker> serviceWorker;
308     if (serviceWorkerData)
309         serviceWorker = ServiceWorker::getOrCreate(*context, ServiceWorkerData { *serviceWorkerData });
310
311     context->postTask([this, protectedThis = makeRef(*this), identifier, state, serviceWorker = WTFMove(serviceWorker)](ScriptExecutionContext&) mutable {
312         if (auto* registration = m_registrations.get(identifier))
313             registration->updateStateFromServer(state, WTFMove(serviceWorker));
314     });
315 }
316
317 void ServiceWorkerContainer::getRegistrations(Ref<DeferredPromise>&& promise)
318 {
319     if (m_isStopped) {
320         promise->reject(Exception { InvalidStateError });
321         return;
322     }
323
324     ASSERT(scriptExecutionContext());
325     auto& context = *scriptExecutionContext();
326
327     uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
328     auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
329     m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
330
331     auto contextIdentifier = this->contextIdentifier();
332     auto contextURL = context.url();
333     callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context.topOrigin().isolatedCopy(), contextURL = contextURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
334         connection->getRegistrations(topOrigin, contextURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& registrationDatas) mutable {
335             ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, registrationDatas = crossThreadCopy(registrationDatas)](ScriptExecutionContext&) mutable {
336                 didFinishGetRegistrationsRequest(pendingPromiseIdentifier, WTFMove(registrationDatas));
337             });
338         });
339     });
340 }
341
342 void ServiceWorkerContainer::didFinishGetRegistrationsRequest(uint64_t pendingPromiseIdentifier, Vector<ServiceWorkerRegistrationData>&& registrationDatas)
343 {
344 #ifndef NDEBUG
345     ASSERT(m_creationThread.ptr() == &Thread::current());
346 #endif
347
348     auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
349     if (!pendingPromise)
350         return;
351
352     ASSERT(!m_isStopped);
353
354     auto registrations = WTF::map(WTFMove(registrationDatas), [&] (auto&& registrationData) {
355         return ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(registrationData));
356     });
357
358     pendingPromise->promise->resolve<IDLSequence<IDLInterface<ServiceWorkerRegistration>>>(WTFMove(registrations));
359 }
360
361 void ServiceWorkerContainer::startMessages()
362 {
363 }
364
365 void ServiceWorkerContainer::jobFailedWithException(ServiceWorkerJob& job, const Exception& exception)
366 {
367 #ifndef NDEBUG
368     ASSERT(m_creationThread.ptr() == &Thread::current());
369 #endif
370
371     ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
372
373     auto guard = WTF::makeScopeExit([this, &job] {
374         jobDidFinish(job);
375     });
376
377     CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("jobFailedWithException: Job %llu failed with error %s", job.identifier().toUInt64(), exception.message().utf8().data());
378
379     if (!job.promise())
380         return;
381
382     if (auto* context = scriptExecutionContext()) {
383         context->postTask([job = makeRef(job), exception](ScriptExecutionContext&) {
384             job->promise()->reject(exception);
385         });
386     }
387 }
388
389 void ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
390 {
391 #ifndef NDEBUG
392     ASSERT(m_creationThread.ptr() == &Thread::current());
393 #endif
394
395     if (auto* registration = m_registrations.get(identifier))
396         registration->scheduleTaskToFireUpdateFoundEvent();
397 }
398
399 void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job, ServiceWorkerRegistrationData&& data, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
400 {
401 #ifndef NDEBUG
402     ASSERT(m_creationThread.ptr() == &Thread::current());
403 #endif
404     ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
405
406     auto guard = WTF::makeScopeExit([this, &job] {
407         jobDidFinish(job);
408     });
409
410     if (job.data().type == ServiceWorkerJobType::Register)
411         CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithRegistration: Registration job %llu succeeded", job.identifier().toUInt64());
412     else {
413         ASSERT(job.data().type == ServiceWorkerJobType::Update);
414         CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithRegistration: Update job %llu succeeded", job.identifier().toUInt64());
415     }
416
417     WTF::Function<void()> notifyWhenResolvedIfNeeded = [] { };
418     if (shouldNotifyWhenResolved == ShouldNotifyWhenResolved::Yes) {
419         notifyWhenResolvedIfNeeded = [connection = m_swConnection, registrationKey = data.key.isolatedCopy()]() mutable {
420             callOnMainThread([connection = WTFMove(connection), registrationKey = WTFMove(registrationKey)] {
421                 connection->didResolveRegistrationPromise(registrationKey);
422             });
423         };
424     }
425
426     if (isStopped()) {
427         notifyWhenResolvedIfNeeded();
428         return;
429     }
430
431     if (!job.promise()) {
432         notifyWhenResolvedIfNeeded();
433         return;
434     }
435
436     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this), job = makeRef(job), data = WTFMove(data), notifyWhenResolvedIfNeeded = WTFMove(notifyWhenResolvedIfNeeded)](ScriptExecutionContext& context) mutable {
437         if (isStopped()) {
438             notifyWhenResolvedIfNeeded();
439             return;
440         }
441
442         auto registration = ServiceWorkerRegistration::getOrCreate(context, *this, WTFMove(data));
443
444         CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithRegistration: Resolving promise for job %llu. Registration ID: %llu", job->identifier().toUInt64(), registration->identifier().toUInt64());
445
446         job->promise()->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
447
448         notifyWhenResolvedIfNeeded();
449     });
450 }
451
452 void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJob& job, bool unregistrationResult)
453 {
454 #ifndef NDEBUG
455     ASSERT(m_creationThread.ptr() == &Thread::current());
456 #endif
457
458     ASSERT(job.promise());
459
460     auto guard = WTF::makeScopeExit([this, &job] {
461         jobDidFinish(job);
462     });
463
464     CONTAINER_RELEASE_LOG_IF_ALLOWED("jobResolvedWithUnregistrationResult: Unregister job %llu finished. Success? %d", job.identifier().toUInt64(), unregistrationResult);
465
466     auto* context = scriptExecutionContext();
467     if (!context) {
468         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithUnregistrationResult called but the containers ScriptExecutionContext is gone");
469         return;
470     }
471
472     context->postTask([job = makeRef(job), unregistrationResult](ScriptExecutionContext&) mutable {
473         job->promise()->resolve<IDLBoolean>(unregistrationResult);
474     });
475 }
476
477 void ServiceWorkerContainer::startScriptFetchForJob(ServiceWorkerJob& job, FetchOptions::Cache cachePolicy)
478 {
479 #ifndef NDEBUG
480     ASSERT(m_creationThread.ptr() == &Thread::current());
481 #endif
482
483     CONTAINER_RELEASE_LOG_IF_ALLOWED("startScriptFetchForJob: Starting script fetch for job %llu", job.identifier().toUInt64());
484
485     auto* context = scriptExecutionContext();
486     if (!context) {
487         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithRegistration called but the container's ScriptExecutionContext is gone");
488         callOnMainThread([connection = m_swConnection, jobIdentifier = job.identifier(), registrationKey = job.data().registrationKey(), scriptURL = job.data().scriptURL.isolatedCopy()] {
489             connection->failedFetchingScript(jobIdentifier, registrationKey, { errorDomainWebKitInternal, 0, scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
490         });
491         jobDidFinish(job);
492         return;
493     }
494
495     job.fetchScriptWithContext(*context, cachePolicy);
496 }
497
498 void ServiceWorkerContainer::jobFinishedLoadingScript(ServiceWorkerJob& job, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy)
499 {
500 #ifndef NDEBUG
501     ASSERT(m_creationThread.ptr() == &Thread::current());
502 #endif
503
504     CONTAINER_RELEASE_LOG_IF_ALLOWED("jobFinishedLoadingScript: Successfuly finished fetching script for job %llu", job.identifier().toUInt64());
505
506     callOnMainThread([connection = m_swConnection, jobDataIdentifier = job.data().identifier(), registrationKey = job.data().registrationKey(), script = script.isolatedCopy(), contentSecurityPolicy = contentSecurityPolicy.isolatedCopy()] {
507         connection->finishFetchingScriptInServer({ jobDataIdentifier, registrationKey, script, contentSecurityPolicy, { } });
508     });
509 }
510
511 void ServiceWorkerContainer::jobFailedLoadingScript(ServiceWorkerJob& job, const ResourceError& error, std::optional<Exception>&& exception)
512 {
513 #ifndef NDEBUG
514     ASSERT(m_creationThread.ptr() == &Thread::current());
515 #endif
516     ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
517
518     CONTAINER_RELEASE_LOG_ERROR_IF_ALLOWED("jobFinishedLoadingScript: Failed to fetch script for job %llu, error: %s", job.identifier().toUInt64(), error.localizedDescription().utf8().data());
519
520     if (exception && job.promise())
521         job.promise()->reject(*exception);
522
523     callOnMainThread([connection = m_swConnection, jobIdentifier = job.identifier(), registrationKey = job.data().registrationKey(), error = error.isolatedCopy()] {
524         connection->failedFetchingScript(jobIdentifier, registrationKey, error);
525     });
526 }
527
528 void ServiceWorkerContainer::jobDidFinish(ServiceWorkerJob& job)
529 {
530 #ifndef NDEBUG
531     ASSERT(m_creationThread.ptr() == &Thread::current());
532 #endif
533
534     auto taken = m_jobMap.take(job.identifier());
535     ASSERT_UNUSED(taken, !taken || taken->ptr() == &job);
536
537     unsetPendingActivity(this);
538 }
539
540 SWServerConnectionIdentifier ServiceWorkerContainer::connectionIdentifier()
541 {
542     ASSERT(m_swConnection);
543     return m_swConnection->serverConnectionIdentifier();
544 }
545
546 const char* ServiceWorkerContainer::activeDOMObjectName() const
547 {
548     return "ServiceWorkerContainer";
549 }
550
551 bool ServiceWorkerContainer::canSuspendForDocumentSuspension() const
552 {
553     return !hasPendingActivity();
554 }
555
556 SWClientConnection& ServiceWorkerContainer::ensureSWClientConnection()
557 {
558     if (!m_swConnection) {
559         ASSERT(scriptExecutionContext());
560         callOnMainThreadAndWait([this, sessionID = scriptExecutionContext()->sessionID()]() {
561             m_swConnection = &ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
562         });
563     }
564     return *m_swConnection;
565 }
566
567 void ServiceWorkerContainer::addRegistration(ServiceWorkerRegistration& registration)
568 {
569 #ifndef NDEBUG
570     ASSERT(m_creationThread.ptr() == &Thread::current());
571 #endif
572
573     ensureSWClientConnection().addServiceWorkerRegistrationInServer(registration.identifier());
574     m_registrations.add(registration.identifier(), &registration);
575 }
576
577 void ServiceWorkerContainer::removeRegistration(ServiceWorkerRegistration& registration)
578 {
579 #ifndef NDEBUG
580     ASSERT(m_creationThread.ptr() == &Thread::current());
581 #endif
582
583     m_swConnection->removeServiceWorkerRegistrationInServer(registration.identifier());
584     m_registrations.remove(registration.identifier());
585 }
586
587 void ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent()
588 {
589 #ifndef NDEBUG
590     ASSERT(m_creationThread.ptr() == &Thread::current());
591 #endif
592
593     if (m_isStopped)
594         return;
595
596     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)](ScriptExecutionContext&) mutable {
597         if (m_isStopped)
598             return;
599
600         dispatchEvent(Event::create(eventNames().controllerchangeEvent, false, false));
601     });
602 }
603
604 void ServiceWorkerContainer::stop()
605 {
606     m_isStopped = true;
607     removeAllEventListeners();
608     m_pendingPromises.clear();
609     for (auto& job : m_jobMap.values())
610         job->cancelPendingLoad();
611 }
612
613 DocumentOrWorkerIdentifier ServiceWorkerContainer::contextIdentifier()
614 {
615 #ifndef NDEBUG
616     ASSERT(m_creationThread.ptr() == &Thread::current());
617 #endif
618
619     ASSERT(scriptExecutionContext());
620     if (is<ServiceWorkerGlobalScope>(*scriptExecutionContext()))
621         return downcast<ServiceWorkerGlobalScope>(*scriptExecutionContext()).thread().identifier();
622     return downcast<Document>(*scriptExecutionContext()).identifier();
623 }
624
625 bool ServiceWorkerContainer::isAlwaysOnLoggingAllowed() const
626 {
627     auto* context = scriptExecutionContext();
628     if (!context)
629         return false;
630
631     if (is<Document>(*context))
632         return downcast<Document>(*context).sessionID().isAlwaysOnLoggingAllowed();
633
634     // FIXME: No logging inside service workers for now.
635     return false;
636 }
637
638 } // namespace WebCore
639
640 #endif // ENABLE(SERVICE_WORKER)