ServiceWorker registration should store any script fetched through importScripts
[WebKit-https.git] / Source / WebCore / workers / service / ServiceWorkerGlobalScope.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 "ServiceWorkerGlobalScope.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "ExtendableEvent.h"
32 #include "SWContextManager.h"
33 #include "ServiceWorkerClient.h"
34 #include "ServiceWorkerClients.h"
35 #include "ServiceWorkerThread.h"
36 #include "ServiceWorkerWindowClient.h"
37 #include "WorkerNavigator.h"
38
39 namespace WebCore {
40
41 Ref<ServiceWorkerGlobalScope> ServiceWorkerGlobalScope::create(const ServiceWorkerContextData& data, const URL& url, Ref<SecurityOrigin>&& origin, const String& identifier, const String& userAgent, bool isOnline, ServiceWorkerThread& thread, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, bool shouldBypassMainWorldContentSecurityPolicy, Ref<SecurityOrigin>&& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, PAL::SessionID sessionID)
42 {
43     auto scope = adoptRef(*new ServiceWorkerGlobalScope { data, url, WTFMove(origin), identifier, userAgent, isOnline, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), timeOrigin, connectionProxy, socketProvider, sessionID });
44     scope->applyContentSecurityPolicyResponseHeaders(contentSecurityPolicy);
45     return scope;
46 }
47
48 ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(const ServiceWorkerContextData& data, const URL& url, Ref<SecurityOrigin>&& origin, const String& identifier, const String& userAgent, bool isOnline, ServiceWorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, Ref<SecurityOrigin>&& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, PAL::SessionID sessionID)
49     : WorkerGlobalScope(url, WTFMove(origin), identifier, userAgent, isOnline, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), timeOrigin, connectionProxy, socketProvider, sessionID)
50     , m_contextData(crossThreadCopy(data))
51     , m_registration(ServiceWorkerRegistration::getOrCreate(*this, navigator().serviceWorker(), WTFMove(m_contextData.registration)))
52     , m_clients(ServiceWorkerClients::create())
53 {
54 }
55
56 ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() = default;
57
58 void ServiceWorkerGlobalScope::skipWaiting(Ref<DeferredPromise>&& promise)
59 {
60     uint64_t requestIdentifier = ++m_lastRequestIdentifier;
61     m_pendingSkipWaitingPromises.add(requestIdentifier, WTFMove(promise));
62
63     callOnMainThread([workerThread = makeRef(thread()), requestIdentifier]() mutable {
64         if (auto* connection = SWContextManager::singleton().connection()) {
65             connection->skipWaiting(workerThread->identifier(), [workerThread = WTFMove(workerThread), requestIdentifier] {
66                 workerThread->runLoop().postTask([requestIdentifier](auto& context) {
67                     auto& scope = downcast<ServiceWorkerGlobalScope>(context);
68                     if (auto promise = scope.m_pendingSkipWaitingPromises.take(requestIdentifier))
69                         promise->resolve();
70                 });
71             });
72         }
73     });
74 }
75
76 EventTargetInterface ServiceWorkerGlobalScope::eventTargetInterface() const
77 {
78     return ServiceWorkerGlobalScopeEventTargetInterfaceType;
79 }
80
81 ServiceWorkerThread& ServiceWorkerGlobalScope::thread()
82 {
83     return static_cast<ServiceWorkerThread&>(WorkerGlobalScope::thread());
84 }
85
86 ServiceWorkerClient* ServiceWorkerGlobalScope::serviceWorkerClient(ServiceWorkerClientIdentifier identifier)
87 {
88     return m_clientMap.get(identifier);
89 }
90
91 void ServiceWorkerGlobalScope::addServiceWorkerClient(ServiceWorkerClient& client)
92 {
93     auto result = m_clientMap.add(client.identifier(), &client);
94     ASSERT_UNUSED(result, result.isNewEntry);
95 }
96
97 void ServiceWorkerGlobalScope::removeServiceWorkerClient(ServiceWorkerClient& client)
98 {
99     auto isRemoved = m_clientMap.remove(client.identifier());
100     ASSERT_UNUSED(isRemoved, isRemoved);
101 }
102
103 // https://w3c.github.io/ServiceWorker/#update-service-worker-extended-events-set-algorithm
104 void ServiceWorkerGlobalScope::updateExtendedEventsSet(ExtendableEvent* newEvent)
105 {
106     ASSERT(!isMainThread());
107     ASSERT(!newEvent || !newEvent->isBeingDispatched());
108     bool hadPendingEvents = hasPendingEvents();
109     m_extendedEvents.removeAllMatching([](auto& event) {
110         return !event->pendingPromiseCount();
111     });
112
113     if (newEvent && newEvent->pendingPromiseCount()) {
114         m_extendedEvents.append(*newEvent);
115         newEvent->whenAllExtendLifetimePromisesAreSettled([this](auto&&) {
116             this->updateExtendedEventsSet();
117         });
118         // Clear out the event's target as it is the WorkerGlobalScope and we do not want to keep it
119         // alive unnecessarily.
120         newEvent->setTarget(nullptr);
121     }
122
123     bool hasPendingEvents = this->hasPendingEvents();
124     if (hasPendingEvents == hadPendingEvents)
125         return;
126
127     callOnMainThread([threadIdentifier = thread().identifier(), hasPendingEvents] {
128         if (auto* connection = SWContextManager::singleton().connection())
129             connection->setServiceWorkerHasPendingEvents(threadIdentifier, hasPendingEvents);
130     });
131 }
132
133 const ServiceWorkerContextData::ImportedScript* ServiceWorkerGlobalScope::scriptResource(const URL& url) const
134 {
135     auto iterator = m_contextData.scriptResourceMap.find(url);
136     return iterator == m_contextData.scriptResourceMap.end() ? nullptr : &iterator->value;
137 }
138
139 void ServiceWorkerGlobalScope::setScriptResource(const URL& url, ServiceWorkerContextData::ImportedScript&& script)
140 {
141     callOnMainThread([threadIdentifier = thread().identifier(), url = url.isolatedCopy(), script = script.isolatedCopy()] {
142         if (auto* connection = SWContextManager::singleton().connection())
143             connection->setScriptResource(threadIdentifier, url, script);
144     });
145
146     m_contextData.scriptResourceMap.set(url, WTFMove(script));
147 }
148
149 } // namespace WebCore
150
151 #endif // ENABLE(SERVICE_WORKER)