Support serviceWorkerRegistration.update() inside service workers
[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(ServiceWorkerJob& job)
49 {
50     ASSERT(isMainThread());
51
52     auto addResult = m_scheduledJobs.add(job.identifier(), &job);
53     ASSERT_UNUSED(addResult, addResult.isNewEntry);
54
55     scheduleJobInServer(job.data());
56 }
57
58 void SWClientConnection::finishedFetchingScript(ServiceWorkerJob& job, const String& script)
59 {
60     ASSERT(isMainThread());
61     ASSERT(m_scheduledJobs.get(job.identifier()) == &job);
62
63     finishFetchingScriptInServer({ job.data().identifier(), job.data().registrationKey(), script, { } });
64 }
65
66 void SWClientConnection::failedFetchingScript(ServiceWorkerJob& job, const ResourceError& error)
67 {
68     ASSERT(isMainThread());
69     ASSERT(m_scheduledJobs.get(job.identifier()) == &job);
70
71     finishFetchingScriptInServer({ job.data().identifier(), job.data().registrationKey(), { }, error });
72 }
73
74 void SWClientConnection::jobRejectedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, const ExceptionData& exceptionData)
75 {
76     ASSERT(isMainThread());
77
78     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
79     if (!job) {
80         LOG_ERROR("Job %s rejected from server, but was not found", jobDataIdentifier.loggingString().utf8().data());
81         return;
82     }
83
84     postTaskTo(job->contextIdentifier(), [job, exceptionData = exceptionData.isolatedCopy()] {
85         job->failedWithException(exceptionData.toException());
86     });
87 }
88
89 void SWClientConnection::registrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, ServiceWorkerRegistrationData&& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
90 {
91     ASSERT(isMainThread());
92
93     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
94     if (!job) {
95         LOG_ERROR("Job %s resolved in server, but was not found", jobDataIdentifier.loggingString().utf8().data());
96         return;
97     }
98
99     postTaskTo(job->contextIdentifier(), [job, registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved]() mutable {
100         job->resolvedWithRegistration(WTFMove(registrationData), shouldNotifyWhenResolved);
101     });
102 }
103
104 void SWClientConnection::unregistrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, bool unregistrationResult)
105 {
106     ASSERT(isMainThread());
107
108     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
109     if (!job) {
110         LOG_ERROR("Job %s resolved in server, but was not found", jobDataIdentifier.loggingString().utf8().data());
111         return;
112     }
113
114     postTaskTo(job->contextIdentifier(), [job, unregistrationResult] {
115         job->resolvedWithUnregistrationResult(unregistrationResult);
116     });
117 }
118
119 void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier)
120 {
121     ASSERT(isMainThread());
122
123     auto job = m_scheduledJobs.get(jobDataIdentifier.jobIdentifier);
124     if (!job) {
125         LOG_ERROR("Job %s instructed to start fetch from server, but job was not found", jobDataIdentifier.loggingString().utf8().data());
126
127         // FIXME: Should message back to the server here to signal failure to fetch,
128         // but we currently need the registration key to do so, and don't have it here.
129         // In the future we'll refactor to have a global, cross-process job identifier that can be used to overcome this.
130
131         return;
132     }
133
134     postTaskTo(job->contextIdentifier(), [job] {
135         job->startScriptFetch();
136     });
137 }
138
139 void SWClientConnection::postMessageToServiceWorkerClient(DocumentIdentifier destinationContextIdentifier, Ref<SerializedScriptValue>&& message, ServiceWorkerData&& sourceData, const String& sourceOrigin)
140 {
141     ASSERT(isMainThread());
142
143     // FIXME: destinationContextIdentifier can only identify a Document at the moment.
144     auto* destinationDocument = Document::allDocumentsMap().get(destinationContextIdentifier);
145     if (!destinationDocument)
146         return;
147
148     auto* container = destinationDocument->serviceWorkerContainer();
149     if (!container)
150         return;
151
152     MessageEventSource source = RefPtr<ServiceWorker> { ServiceWorker::getOrCreate(*destinationDocument, WTFMove(sourceData)) };
153
154     // FIXME: We should pass in ports.
155     auto messageEvent = MessageEvent::create({ }, WTFMove(message), sourceOrigin, { }, WTFMove(source));
156     container->dispatchEvent(messageEvent);
157 }
158
159 void SWClientConnection::updateRegistrationState(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
160 {
161     ASSERT(isMainThread());
162
163     SWContextManager::singleton().forEachServiceWorkerThread([identifier, state, &serviceWorkerData] (auto& workerThread) {
164         workerThread.thread().runLoop().postTask([identifier, state, serviceWorkerData = crossThreadCopy(serviceWorkerData)](ScriptExecutionContext& context) mutable {
165             if (auto* container = context.serviceWorkerContainer())
166                 container->scheduleTaskToUpdateRegistrationState(identifier, state, WTFMove(serviceWorkerData));
167         });
168     });
169
170     for (auto* document : Document::allDocuments()) {
171         if (auto* container = document->serviceWorkerContainer())
172             container->scheduleTaskToUpdateRegistrationState(identifier, state, serviceWorkerData);
173     }
174 }
175
176 void SWClientConnection::updateWorkerState(ServiceWorkerIdentifier identifier, ServiceWorkerState state)
177 {
178     ASSERT(isMainThread());
179
180     SWContextManager::singleton().forEachServiceWorkerThread([identifier, state] (auto& workerThread) {
181         workerThread.thread().runLoop().postTask([identifier, state](ScriptExecutionContext& context) {
182             if (auto* serviceWorker = context.serviceWorker(identifier))
183                 serviceWorker->scheduleTaskToUpdateState(state);
184         });
185     });
186
187     for (auto* document : Document::allDocuments()) {
188         if (auto* serviceWorker = document->serviceWorker(identifier))
189             serviceWorker->scheduleTaskToUpdateState(state);
190     }
191 }
192
193 void SWClientConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
194 {
195     ASSERT(isMainThread());
196
197     SWContextManager::singleton().forEachServiceWorkerThread([identifier] (auto& workerThread) {
198         workerThread.thread().runLoop().postTask([identifier](ScriptExecutionContext& context) {
199             if (auto* container = context.serviceWorkerContainer())
200                 container->scheduleTaskToFireUpdateFoundEvent(identifier);
201         });
202     });
203
204     for (auto* document : Document::allDocuments()) {
205         if (auto* container = document->serviceWorkerContainer())
206             container->scheduleTaskToFireUpdateFoundEvent(identifier);
207     }
208 }
209
210 void SWClientConnection::notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, ServiceWorkerData&& newController)
211 {
212     ASSERT(isMainThread());
213     ASSERT(!contextIdentifiers.isEmpty());
214
215     for (auto& clientIdentifier : contextIdentifiers) {
216         // FIXME: Support worker contexts.
217         auto* client = Document::allDocumentsMap().get(clientIdentifier);
218         if (!client)
219             continue;
220
221         ASSERT(client->activeServiceWorker());
222         ASSERT(client->activeServiceWorker()->identifier() != newController.identifier);
223         client->setActiveServiceWorker(ServiceWorker::getOrCreate(*client, ServiceWorkerData { newController }));
224         if (auto* container = client->serviceWorkerContainer())
225             container->scheduleTaskToFireControllerChangeEvent();
226     }
227 }
228
229 void SWClientConnection::clearPendingJobs()
230 {
231     ASSERT(isMainThread());
232
233     auto jobs = WTFMove(m_scheduledJobs);
234     for (auto& job : jobs.values()) {
235         postTaskTo(job->contextIdentifier(), [job] {
236             job->failedWithException(Exception { TypeError, ASCIILiteral("Internal error") });
237         });
238     }
239 }
240
241 void SWClientConnection::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void()>&& task)
242 {
243     ASSERT(isMainThread());
244
245     switchOn(contextIdentifier, [&](DocumentIdentifier identifier) {
246         auto* document = Document::allDocumentsMap().get(identifier);
247         if (!document)
248             return;
249         document->postTask([task = WTFMove(task)](ScriptExecutionContext&) {
250             task();
251         });
252     }, [&](ServiceWorkerIdentifier identifier) {
253         SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](ServiceWorkerGlobalScope&) {
254             task();
255         });
256     });
257 }
258
259 } // namespace WebCore
260
261 #endif // ENABLE(SERVICE_WORKER)