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