ServiceWorkers API should reject promises when calling objects inside detached frames
[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 (m_isStopped || !context->sessionID().isValid()) {
115         promise->reject(Exception(InvalidStateError));
116         return;
117     }
118
119     if (relativeScriptURL.isEmpty()) {
120         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() cannot be called with an empty script URL") });
121         return;
122     }
123
124     ServiceWorkerJobData jobData(ensureSWClientConnection().serverConnectionIdentifier());
125
126     jobData.scriptURL = context->completeURL(relativeScriptURL);
127     if (!jobData.scriptURL.isValid()) {
128         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a valid relative script URL") });
129         return;
130     }
131
132     // FIXME: The spec disallows scripts outside of HTTP(S), but we'll likely support app custom URL schemes in WebKit.
133     if (!jobData.scriptURL.protocolIsInHTTPFamily()) {
134         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a script URL whose protocol is either HTTP or HTTPS") });
135         return;
136     }
137
138     String path = jobData.scriptURL.path();
139     if (path.containsIgnoringASCIICase("%2f") || path.containsIgnoringASCIICase("%5c")) {
140         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a script URL whose path does not contain '%2f' or '%5c'") });
141         return;
142     }
143
144     String scope = options.scope.isEmpty() ? ASCIILiteral("./") : options.scope;
145     if (!scope.isEmpty())
146         jobData.scopeURL = context->completeURL(scope);
147
148     if (!jobData.scopeURL.isNull() && !jobData.scopeURL.protocolIsInHTTPFamily()) {
149         promise->reject(Exception { TypeError, ASCIILiteral("Scope URL provided to serviceWorker.register() must be either HTTP or HTTPS") });
150         return;
151     }
152
153     path = jobData.scopeURL.path();
154     if (path.containsIgnoringASCIICase("%2f") || path.containsIgnoringASCIICase("%5c")) {
155         promise->reject(Exception { TypeError, ASCIILiteral("Scope URL provided to serviceWorker.register() cannot have a path that contains '%2f' or '%5c'") });
156         return;
157     }
158
159     jobData.clientCreationURL = context->url();
160     jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context->topOrigin());
161     jobData.type = ServiceWorkerJobType::Register;
162     jobData.registrationOptions = options;
163
164     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
165 }
166
167 void ServiceWorkerContainer::removeRegistration(const URL& scopeURL, Ref<DeferredPromise>&& promise)
168 {
169     auto* context = scriptExecutionContext();
170     if (!context || !context->sessionID().isValid()) {
171         ASSERT_NOT_REACHED();
172         promise->reject(Exception(InvalidStateError));
173         return;
174     }
175
176     if (!m_swConnection) {
177         ASSERT_NOT_REACHED();
178         promise->reject(Exception(InvalidStateError));
179         return;
180     }
181
182     ServiceWorkerJobData jobData(m_swConnection->serverConnectionIdentifier());
183     jobData.clientCreationURL = context->url();
184     jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context->topOrigin());
185     jobData.type = ServiceWorkerJobType::Unregister;
186     jobData.scopeURL = scopeURL;
187
188     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
189 }
190
191 void ServiceWorkerContainer::updateRegistration(const URL& scopeURL, const URL& scriptURL, WorkerType, Ref<DeferredPromise>&& promise)
192 {
193     auto* context = scriptExecutionContext();
194     if (!context || !context->sessionID().isValid()) {
195         ASSERT_NOT_REACHED();
196         promise->reject(Exception(InvalidStateError));
197         return;
198     }
199
200     if (!m_swConnection) {
201         ASSERT_NOT_REACHED();
202         promise->reject(Exception(InvalidStateError));
203         return;
204     }
205
206     ServiceWorkerJobData jobData(m_swConnection->serverConnectionIdentifier());
207     jobData.clientCreationURL = context->url();
208     jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context->topOrigin());
209     jobData.type = ServiceWorkerJobType::Update;
210     jobData.scopeURL = scopeURL;
211     jobData.scriptURL = scriptURL;
212
213     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
214 }
215
216 void ServiceWorkerContainer::scheduleJob(Ref<ServiceWorkerJob>&& job)
217 {
218 #ifndef NDEBUG
219     ASSERT(m_creationThread.ptr() == &Thread::current());
220 #endif
221
222     ASSERT(m_swConnection);
223
224     setPendingActivity(this);
225
226     auto result = m_jobMap.add(job->identifier(), job.copyRef());
227     ASSERT_UNUSED(result, result.isNewEntry);
228
229     callOnMainThread([connection = m_swConnection, job = WTFMove(job)] {
230         connection->scheduleJob(job);
231     });
232 }
233
234 void ServiceWorkerContainer::getRegistration(const String& clientURL, Ref<DeferredPromise>&& promise)
235 {
236     if (m_isStopped) {
237         promise->reject(Exception { InvalidStateError });
238         return;
239     }
240
241     ASSERT(scriptExecutionContext());
242     auto& context = *scriptExecutionContext();
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     if (m_isStopped) {
304         promise->reject(Exception { InvalidStateError });
305         return;
306     }
307
308     ASSERT(scriptExecutionContext());
309     auto& context = *scriptExecutionContext();
310
311     uint64_t pendingPromiseIdentifier = ++m_lastPendingPromiseIdentifier;
312     auto pendingPromise = std::make_unique<PendingPromise>(WTFMove(promise), makePendingActivity(*this));
313     m_pendingPromises.add(pendingPromiseIdentifier, WTFMove(pendingPromise));
314
315     auto contextIdentifier = this->contextIdentifier();
316     auto contextURL = context.url();
317     callOnMainThread([connection = makeRef(ensureSWClientConnection()), this, topOrigin = context.topOrigin().isolatedCopy(), contextURL = contextURL.isolatedCopy(), contextIdentifier, pendingPromiseIdentifier]() mutable {
318         connection->getRegistrations(topOrigin, contextURL, [this, contextIdentifier, pendingPromiseIdentifier] (auto&& registrationDatas) mutable {
319             ScriptExecutionContext::postTaskTo(contextIdentifier, [this, pendingPromiseIdentifier, registrationDatas = crossThreadCopy(registrationDatas)](ScriptExecutionContext&) mutable {
320                 didFinishGetRegistrationsRequest(pendingPromiseIdentifier, WTFMove(registrationDatas));
321             });
322         });
323     });
324 }
325
326 void ServiceWorkerContainer::didFinishGetRegistrationsRequest(uint64_t pendingPromiseIdentifier, Vector<ServiceWorkerRegistrationData>&& registrationDatas)
327 {
328 #ifndef NDEBUG
329     ASSERT(m_creationThread.ptr() == &Thread::current());
330 #endif
331
332     auto pendingPromise = m_pendingPromises.take(pendingPromiseIdentifier);
333     if (!pendingPromise)
334         return;
335
336     ASSERT(!m_isStopped);
337
338     auto registrations = WTF::map(WTFMove(registrationDatas), [&] (auto&& registrationData) {
339         return ServiceWorkerRegistration::getOrCreate(*scriptExecutionContext(), *this, WTFMove(registrationData));
340     });
341
342     pendingPromise->promise->resolve<IDLSequence<IDLInterface<ServiceWorkerRegistration>>>(WTFMove(registrations));
343 }
344
345 void ServiceWorkerContainer::startMessages()
346 {
347 }
348
349 void ServiceWorkerContainer::jobFailedWithException(ServiceWorkerJob& job, const Exception& exception)
350 {
351 #ifndef NDEBUG
352     ASSERT(m_creationThread.ptr() == &Thread::current());
353 #endif
354
355     if (auto* context = scriptExecutionContext()) {
356         context->postTask([job = makeRef(job), exception](ScriptExecutionContext&) {
357             job->promise().reject(exception);
358         });
359     }
360     jobDidFinish(job);
361 }
362
363 void ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
364 {
365 #ifndef NDEBUG
366     ASSERT(m_creationThread.ptr() == &Thread::current());
367 #endif
368
369     if (auto* registration = m_registrations.get(identifier))
370         registration->scheduleTaskToFireUpdateFoundEvent();
371 }
372
373 void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job, ServiceWorkerRegistrationData&& data, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
374 {
375 #ifndef NDEBUG
376     ASSERT(m_creationThread.ptr() == &Thread::current());
377 #endif
378
379     auto guard = WTF::makeScopeExit([this, &job] {
380         jobDidFinish(job);
381     });
382
383     WTF::Function<void()> notifyWhenResolvedIfNeeded = [] { };
384     if (shouldNotifyWhenResolved == ShouldNotifyWhenResolved::Yes) {
385         notifyWhenResolvedIfNeeded = [connection = m_swConnection, registrationKey = data.key.isolatedCopy()]() mutable {
386             callOnMainThread([connection = WTFMove(connection), registrationKey = WTFMove(registrationKey)] {
387                 connection->didResolveRegistrationPromise(registrationKey);
388             });
389         };
390     }
391
392     if (isStopped()) {
393         notifyWhenResolvedIfNeeded();
394         return;
395     }
396
397     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this), job = makeRef(job), data = WTFMove(data), notifyWhenResolvedIfNeeded = WTFMove(notifyWhenResolvedIfNeeded)](ScriptExecutionContext& context) mutable {
398         if (isStopped()) {
399             notifyWhenResolvedIfNeeded();
400             return;
401         }
402
403         auto registration = ServiceWorkerRegistration::getOrCreate(context, *this, WTFMove(data));
404
405         LOG(ServiceWorker, "Container %p resolved job with registration %p", this, registration.ptr());
406
407         job->promise().resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
408
409         notifyWhenResolvedIfNeeded();
410     });
411 }
412
413 void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJob& job, bool unregistrationResult)
414 {
415 #ifndef NDEBUG
416     ASSERT(m_creationThread.ptr() == &Thread::current());
417 #endif
418
419     auto guard = WTF::makeScopeExit([this, &job] {
420         jobDidFinish(job);
421     });
422
423     auto* context = scriptExecutionContext();
424     if (!context) {
425         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithUnregistrationResult called but the containers ScriptExecutionContext is gone");
426         return;
427     }
428
429     context->postTask([job = makeRef(job), unregistrationResult](ScriptExecutionContext&) mutable {
430         job->promise().resolve<IDLBoolean>(unregistrationResult);
431     });
432 }
433
434 void ServiceWorkerContainer::startScriptFetchForJob(ServiceWorkerJob& job)
435 {
436 #ifndef NDEBUG
437     ASSERT(m_creationThread.ptr() == &Thread::current());
438 #endif
439
440     LOG(ServiceWorker, "SeviceWorkerContainer %p starting script fetch for job %s", this, job.identifier().loggingString().utf8().data());
441
442     auto* context = scriptExecutionContext();
443     if (!context) {
444         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithRegistration called but the container's ScriptExecutionContext is gone");
445         callOnMainThread([connection = m_swConnection, job = makeRef(job)] {
446             connection->failedFetchingScript(job, { errorDomainWebKitInternal, 0, job->data().scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
447         });
448         jobDidFinish(job);
449         return;
450     }
451
452     job.fetchScriptWithContext(*context);
453 }
454
455 void ServiceWorkerContainer::jobFinishedLoadingScript(ServiceWorkerJob& job, const String& script)
456 {
457 #ifndef NDEBUG
458     ASSERT(m_creationThread.ptr() == &Thread::current());
459 #endif
460
461     LOG(ServiceWorker, "SeviceWorkerContainer %p finished fetching script for job %s", this, job.identifier().loggingString().utf8().data());
462
463     callOnMainThread([connection = m_swConnection, job = makeRef(job), script = script.isolatedCopy()] {
464         connection->finishedFetchingScript(job, script);
465     });
466 }
467
468 void ServiceWorkerContainer::jobFailedLoadingScript(ServiceWorkerJob& job, const ResourceError& error, std::optional<Exception>&& exception)
469 {
470 #ifndef NDEBUG
471     ASSERT(m_creationThread.ptr() == &Thread::current());
472 #endif
473
474     LOG(ServiceWorker, "SeviceWorkerContainer %p failed fetching script for job %s", this, job.identifier().loggingString().utf8().data());
475
476     if (exception)
477         job.promise().reject(*exception);
478
479     callOnMainThread([connection = m_swConnection, job = makeRef(job), error = error.isolatedCopy()] {
480         connection->failedFetchingScript(job, error);
481     });
482 }
483
484 void ServiceWorkerContainer::jobDidFinish(ServiceWorkerJob& job)
485 {
486 #ifndef NDEBUG
487     ASSERT(m_creationThread.ptr() == &Thread::current());
488 #endif
489
490     auto taken = m_jobMap.take(job.identifier());
491     ASSERT_UNUSED(taken, !taken || taken->ptr() == &job);
492
493     unsetPendingActivity(this);
494 }
495
496 SWServerConnectionIdentifier ServiceWorkerContainer::connectionIdentifier()
497 {
498     ASSERT(m_swConnection);
499     return m_swConnection->serverConnectionIdentifier();
500 }
501
502 const char* ServiceWorkerContainer::activeDOMObjectName() const
503 {
504     return "ServiceWorkerContainer";
505 }
506
507 bool ServiceWorkerContainer::canSuspendForDocumentSuspension() const
508 {
509     return !hasPendingActivity();
510 }
511
512 SWClientConnection& ServiceWorkerContainer::ensureSWClientConnection()
513 {
514     if (!m_swConnection) {
515         ASSERT(scriptExecutionContext());
516         callOnMainThreadAndWait([this, sessionID = scriptExecutionContext()->sessionID()]() {
517             m_swConnection = &ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
518         });
519     }
520     return *m_swConnection;
521 }
522
523 void ServiceWorkerContainer::addRegistration(ServiceWorkerRegistration& registration)
524 {
525 #ifndef NDEBUG
526     ASSERT(m_creationThread.ptr() == &Thread::current());
527 #endif
528
529     ensureSWClientConnection().addServiceWorkerRegistrationInServer(registration.identifier());
530     m_registrations.add(registration.identifier(), &registration);
531 }
532
533 void ServiceWorkerContainer::removeRegistration(ServiceWorkerRegistration& registration)
534 {
535 #ifndef NDEBUG
536     ASSERT(m_creationThread.ptr() == &Thread::current());
537 #endif
538
539     m_swConnection->removeServiceWorkerRegistrationInServer(registration.identifier());
540     m_registrations.remove(registration.identifier());
541 }
542
543 void ServiceWorkerContainer::scheduleTaskToFireControllerChangeEvent()
544 {
545 #ifndef NDEBUG
546     ASSERT(m_creationThread.ptr() == &Thread::current());
547 #endif
548
549     if (m_isStopped)
550         return;
551
552     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)](ScriptExecutionContext&) mutable {
553         if (m_isStopped)
554             return;
555
556         dispatchEvent(Event::create(eventNames().controllerchangeEvent, false, false));
557     });
558 }
559
560 void ServiceWorkerContainer::stop()
561 {
562     m_isStopped = true;
563     removeAllEventListeners();
564     m_pendingPromises.clear();
565 }
566
567 DocumentOrWorkerIdentifier ServiceWorkerContainer::contextIdentifier()
568 {
569 #ifndef NDEBUG
570     ASSERT(m_creationThread.ptr() == &Thread::current());
571 #endif
572
573     ASSERT(scriptExecutionContext());
574     if (is<ServiceWorkerGlobalScope>(*scriptExecutionContext()))
575         return downcast<ServiceWorkerGlobalScope>(*scriptExecutionContext()).thread().identifier();
576     return downcast<Document>(*scriptExecutionContext()).identifier();
577 }
578
579 } // namespace WebCore
580
581 #endif // ENABLE(SERVICE_WORKER)