[WTF] Add user-defined literal for ASCIILiteral
[WebKit-https.git] / Source / WebCore / workers / service / SWClientConnection.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 "SWClientConnection.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "Document.h"
32 #include "ExceptionData.h"
33 #include "MessageEvent.h"
34 #include "Microtasks.h"
35 #include "SWContextManager.h"
36 #include "ServiceWorkerContainer.h"
37 #include "ServiceWorkerFetchResult.h"
38 #include "ServiceWorkerJobData.h"
39 #include "ServiceWorkerRegistration.h"
40 #include <wtf/CrossThreadCopier.h>
41
42 namespace WebCore {
43
44 SWClientConnection::SWClientConnection() = default;
45
46 SWClientConnection::~SWClientConnection() = default;
47
48 void SWClientConnection::scheduleJob(DocumentOrWorkerIdentifier contextIdentifier, const ServiceWorkerJobData& jobData)
49 {
50     ASSERT(isMainThread());
51
52     auto addResult = m_scheduledJobSources.add(jobData.identifier().jobIdentifier, contextIdentifier);
53     ASSERT_UNUSED(addResult, addResult.isNewEntry);
54
55     scheduleJobInServer(jobData);
56 }
57
58 void SWClientConnection::failedFetchingScript(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, const ResourceError& error)
59 {
60     ASSERT(isMainThread());
61
62     finishFetchingScriptInServer({ { serverConnectionIdentifier(), jobIdentifier }, registrationKey, { }, { }, error });
63 }
64
65 bool SWClientConnection::postTaskForJob(ServiceWorkerJobIdentifier jobIdentifier, IsJobComplete isJobComplete, WTF::Function<void(ServiceWorkerJob&)>&& task)
66 {
67     ASSERT(isMainThread());
68
69     auto iterator = m_scheduledJobSources.find(jobIdentifier);
70     if (iterator == m_scheduledJobSources.end()) {
71         LOG_ERROR("Job %s was not found", jobIdentifier.loggingString().utf8().data());
72         return false;
73     }
74     auto isPosted = ScriptExecutionContext::postTaskTo(iterator->value, [jobIdentifier, task = WTFMove(task)] (ScriptExecutionContext& context) mutable {
75         if (auto* container = context.serviceWorkerContainer()) {
76             if (auto* job = container->job(jobIdentifier))
77                 task(*job);
78         }
79     });
80     if (isJobComplete == IsJobComplete::Yes)
81         m_scheduledJobSources.remove(iterator);
82     return isPosted;
83 }
84
85 void SWClientConnection::jobRejectedInServer(ServiceWorkerJobIdentifier jobIdentifier, const ExceptionData& exceptionData)
86 {
87     postTaskForJob(jobIdentifier, IsJobComplete::Yes, [exceptionData = exceptionData.isolatedCopy()] (auto& job) {
88         job.failedWithException(exceptionData.toException());
89     });
90 }
91
92 void SWClientConnection::registrationJobResolvedInServer(ServiceWorkerJobIdentifier jobIdentifier, ServiceWorkerRegistrationData&& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
93 {
94     bool isPosted = postTaskForJob(jobIdentifier, IsJobComplete::Yes, [registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved] (auto& job) mutable {
95         job.resolvedWithRegistration(WTFMove(registrationData), shouldNotifyWhenResolved);
96     });
97
98     if (!isPosted && shouldNotifyWhenResolved == ShouldNotifyWhenResolved::Yes)
99         didResolveRegistrationPromise(registrationData.key);
100 }
101
102 void SWClientConnection::unregistrationJobResolvedInServer(ServiceWorkerJobIdentifier jobIdentifier, bool unregistrationResult)
103 {
104     postTaskForJob(jobIdentifier, IsJobComplete::Yes, [unregistrationResult] (auto& job) {
105         job.resolvedWithUnregistrationResult(unregistrationResult);
106     });
107 }
108
109 void SWClientConnection::startScriptFetchForServer(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, FetchOptions::Cache cachePolicy)
110 {
111     bool isPosted = postTaskForJob(jobIdentifier, IsJobComplete::No, [cachePolicy] (auto& job) {
112         job.startScriptFetch(cachePolicy);
113     });
114     if (!isPosted)
115         failedFetchingScript(jobIdentifier, registrationKey, ResourceError { errorDomainWebKitInternal, 0, { }, makeString("Failed to fetch script for service worker with scope ", registrationKey.scope().string()) });
116 }
117
118
119 void SWClientConnection::postMessageToServiceWorkerClient(DocumentIdentifier destinationContextIdentifier, MessageWithMessagePorts&& message, ServiceWorkerData&& sourceData, const String& sourceOrigin)
120 {
121     ASSERT(isMainThread());
122
123     // FIXME: destinationContextIdentifier can only identify a Document at the moment.
124     auto* destinationDocument = Document::allDocumentsMap().get(destinationContextIdentifier);
125     if (!destinationDocument)
126         return;
127
128     auto* container = destinationDocument->serviceWorkerContainer();
129     if (!container)
130         return;
131
132     MessageEventSource source = RefPtr<ServiceWorker> { ServiceWorker::getOrCreate(*destinationDocument, WTFMove(sourceData)) };
133
134     auto messageEvent = MessageEvent::create(MessagePort::entanglePorts(*destinationDocument, WTFMove(message.transferredPorts)), message.message.releaseNonNull(), sourceOrigin, { }, WTFMove(source));
135     container->dispatchEvent(messageEvent);
136 }
137
138 void SWClientConnection::updateRegistrationState(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
139 {
140     ASSERT(isMainThread());
141
142     SWContextManager::singleton().forEachServiceWorkerThread([identifier, state, &serviceWorkerData] (auto& workerThread) {
143         workerThread.thread().runLoop().postTask([identifier, state, serviceWorkerData = crossThreadCopy(serviceWorkerData)](ScriptExecutionContext& context) mutable {
144             if (auto* container = context.serviceWorkerContainer())
145                 container->scheduleTaskToUpdateRegistrationState(identifier, state, WTFMove(serviceWorkerData));
146         });
147     });
148
149     for (auto* document : Document::allDocuments()) {
150         if (auto* container = document->serviceWorkerContainer())
151             container->scheduleTaskToUpdateRegistrationState(identifier, state, serviceWorkerData);
152     }
153 }
154
155 void SWClientConnection::updateWorkerState(ServiceWorkerIdentifier identifier, ServiceWorkerState state)
156 {
157     ASSERT(isMainThread());
158
159     SWContextManager::singleton().forEachServiceWorkerThread([identifier, state] (auto& workerThread) {
160         workerThread.thread().runLoop().postTask([identifier, state](ScriptExecutionContext& context) {
161             if (auto* serviceWorker = context.serviceWorker(identifier))
162                 serviceWorker->scheduleTaskToUpdateState(state);
163         });
164     });
165
166     for (auto* document : Document::allDocuments()) {
167         if (auto* serviceWorker = document->serviceWorker(identifier))
168             serviceWorker->scheduleTaskToUpdateState(state);
169     }
170 }
171
172 void SWClientConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
173 {
174     ASSERT(isMainThread());
175
176     SWContextManager::singleton().forEachServiceWorkerThread([identifier] (auto& workerThread) {
177         workerThread.thread().runLoop().postTask([identifier](ScriptExecutionContext& context) {
178             if (auto* container = context.serviceWorkerContainer())
179                 container->scheduleTaskToFireUpdateFoundEvent(identifier);
180         });
181     });
182
183     for (auto* document : Document::allDocuments()) {
184         if (auto* container = document->serviceWorkerContainer())
185             container->scheduleTaskToFireUpdateFoundEvent(identifier);
186     }
187 }
188
189 void SWClientConnection::setRegistrationLastUpdateTime(ServiceWorkerRegistrationIdentifier identifier, WallTime lastUpdateTime)
190 {
191     ASSERT(isMainThread());
192
193     SWContextManager::singleton().forEachServiceWorkerThread([identifier, lastUpdateTime] (auto& workerThread) {
194         workerThread.thread().runLoop().postTask([identifier, lastUpdateTime](ScriptExecutionContext& context) {
195             if (auto* container = context.serviceWorkerContainer()) {
196                 if (auto* registration = container->registration(identifier))
197                     registration->setLastUpdateTime(lastUpdateTime);
198             }
199         });
200     });
201
202     for (auto* document : Document::allDocuments()) {
203         if (auto* container = document->serviceWorkerContainer()) {
204             if (auto* registration = container->registration(identifier))
205                 registration->setLastUpdateTime(lastUpdateTime);
206         }
207     }
208 }
209
210 void SWClientConnection::setRegistrationUpdateViaCache(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerUpdateViaCache updateViaCache)
211 {
212     ASSERT(isMainThread());
213
214     SWContextManager::singleton().forEachServiceWorkerThread([identifier, updateViaCache] (auto& workerThread) {
215         workerThread.thread().runLoop().postTask([identifier, updateViaCache](ScriptExecutionContext& context) {
216             if (auto* container = context.serviceWorkerContainer()) {
217                 if (auto* registration = container->registration(identifier))
218                     registration->setUpdateViaCache(updateViaCache);
219             }
220         });
221     });
222
223     for (auto* document : Document::allDocuments()) {
224         if (auto* container = document->serviceWorkerContainer()) {
225             if (auto* registration = container->registration(identifier))
226                 registration->setUpdateViaCache(updateViaCache);
227         }
228     }
229 }
230
231 void SWClientConnection::notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, ServiceWorkerData&& newController)
232 {
233     ASSERT(isMainThread());
234     ASSERT(!contextIdentifiers.isEmpty());
235
236     for (auto& clientIdentifier : contextIdentifiers) {
237         // FIXME: Support worker contexts.
238         auto* client = Document::allDocumentsMap().get(clientIdentifier);
239         if (!client)
240             continue;
241
242         ASSERT(!client->activeServiceWorker() || client->activeServiceWorker()->identifier() != newController.identifier);
243         client->setActiveServiceWorker(ServiceWorker::getOrCreate(*client, ServiceWorkerData { newController }));
244         if (auto* container = client->serviceWorkerContainer())
245             container->scheduleTaskToFireControllerChangeEvent();
246     }
247 }
248
249 void SWClientConnection::clearPendingJobs()
250 {
251     ASSERT(isMainThread());
252
253     auto jobSources = WTFMove(m_scheduledJobSources);
254     for (auto& keyValue : jobSources) {
255         ScriptExecutionContext::postTaskTo(keyValue.value, [identifier = keyValue.key] (auto& context) {
256             if (auto* container = context.serviceWorkerContainer()) {
257                 if (auto* job = container->job(identifier))
258                     job->failedWithException(Exception { TypeError, "Internal error"_s });
259             }
260         });
261     }
262 }
263
264 } // namespace WebCore
265
266 #endif // ENABLE(SERVICE_WORKER)