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