Use a strongly typed identifier for SWServer::Connection
[WebKit-https.git] / Source / WebCore / workers / service / server / SWServer.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 "SWServer.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "ExceptionCode.h"
32 #include "ExceptionData.h"
33 #include "Logging.h"
34 #include "SWOriginStore.h"
35 #include "SWServerJobQueue.h"
36 #include "SWServerRegistration.h"
37 #include "SWServerToContextConnection.h"
38 #include "SWServerWorker.h"
39 #include "SecurityOrigin.h"
40 #include "ServiceWorkerContextData.h"
41 #include "ServiceWorkerFetchResult.h"
42 #include "ServiceWorkerJobData.h"
43 #include <wtf/NeverDestroyed.h>
44 #include <wtf/text/WTFString.h>
45
46 namespace WebCore {
47
48 static ServiceWorkerIdentifier generateServiceWorkerIdentifier()
49 {
50     return generateObjectIdentifier<ServiceWorkerIdentifierType>();
51 }
52
53 SWServer::Connection::Connection(SWServer& server)
54     : m_server(server)
55     , m_identifier(generateObjectIdentifier<SWServerConnectionIdentifierType>())
56 {
57     m_server.registerConnection(*this);
58 }
59
60 SWServer::Connection::~Connection()
61 {
62     m_server.unregisterConnection(*this);
63 }
64
65 HashSet<SWServer*>& SWServer::allServers()
66 {
67     static NeverDestroyed<HashSet<SWServer*>> servers;
68     return servers;
69 }
70
71 SWServer::~SWServer()
72 {
73     RELEASE_ASSERT(m_connections.isEmpty());
74     RELEASE_ASSERT(m_registrations.isEmpty());
75     RELEASE_ASSERT(m_jobQueues.isEmpty());
76
77     ASSERT(m_taskQueue.isEmpty());
78     ASSERT(m_taskReplyQueue.isEmpty());
79
80     // For a SWServer to be cleanly shut down its thread must have finished and gone away.
81     // At this stage in development of the feature that actually never happens.
82     // But once it does start happening, this ASSERT will catch us doing it wrong.
83     Locker<Lock> locker(m_taskThreadLock);
84     ASSERT(!m_taskThread);
85     
86     allServers().remove(this);
87 }
88
89 SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationKey& registrationKey)
90 {
91     return m_registrations.get(registrationKey);
92 }
93
94 void SWServer::addRegistration(std::unique_ptr<SWServerRegistration>&& registration)
95 {
96     auto key = registration->key();
97     auto result = m_registrations.add(key, WTFMove(registration));
98     ASSERT_UNUSED(result, result.isNewEntry);
99
100     m_originStore->add(key.topOrigin().securityOrigin());
101 }
102
103 void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& key)
104 {
105     auto topOrigin = key.topOrigin().securityOrigin();
106     auto result = m_registrations.remove(key);
107     ASSERT_UNUSED(result, result);
108
109     m_originStore->remove(topOrigin);
110 }
111
112 Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityOriginData& topOrigin, const URL& clientURL)
113 {
114     Vector<SWServerRegistration*> matchingRegistrations;
115     for (auto& item : m_registrations) {
116         if (!item.value->isUninstalling() && item.key.originIsMatching(topOrigin, clientURL))
117             matchingRegistrations.append(item.value.get());
118     }
119     // The specification mandates that registrations are returned in the insertion order.
120     std::sort(matchingRegistrations.begin(), matchingRegistrations.end(), [](auto& a, auto& b) {
121         return a->creationTime() < b->creationTime();
122     });
123     Vector<ServiceWorkerRegistrationData> matchingRegistrationDatas;
124     matchingRegistrationDatas.reserveInitialCapacity(matchingRegistrations.size());
125     for (auto* registration : matchingRegistrations)
126         matchingRegistrationDatas.uncheckedAppend(registration->data());
127     return matchingRegistrationDatas;
128 }
129
130 void SWServer::clearAll()
131 {
132     m_jobQueues.clear();
133     while (!m_registrations.isEmpty())
134         SWServerJobQueue::clearRegistration(*this, *m_registrations.begin()->value);
135     m_originStore->clearAll();
136 }
137
138 void SWServer::clear(const SecurityOrigin& origin)
139 {
140     m_originStore->clear(origin);
141
142     // FIXME: We should clear entries in m_registrations, m_jobQueues and m_workersByID.
143 }
144
145 void SWServer::Connection::scheduleJobInServer(const ServiceWorkerJobData& jobData)
146 {
147     LOG(ServiceWorker, "Scheduling ServiceWorker job %s-%" PRIu64 " in server", jobData.connectionIdentifier().loggingString().utf8().data(), jobData.identifier());
148     ASSERT(identifier() == jobData.connectionIdentifier());
149
150     m_server.scheduleJob(jobData);
151 }
152
153 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
154 {
155     m_server.scriptFetchFinished(*this, result);
156 }
157
158 void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key)
159 {
160     m_server.didResolveRegistrationPromise(*this, key);
161 }
162
163 void SWServer::Connection::addServiceWorkerRegistrationInServer(const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
164 {
165     m_server.addClientServiceWorkerRegistration(*this, key, identifier);
166 }
167
168 void SWServer::Connection::removeServiceWorkerRegistrationInServer(const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
169 {
170     m_server.removeClientServiceWorkerRegistration(*this, key, identifier);
171 }
172
173 void SWServer::Connection::serviceWorkerStartedControllingClient(ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t scriptExecutionContextIdentifier)
174 {
175     m_server.serviceWorkerStartedControllingClient(*this, serviceWorkerIdentifier, scriptExecutionContextIdentifier);
176 }
177
178 void SWServer::Connection::serviceWorkerStoppedControllingClient(ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t scriptExecutionContextIdentifier)
179 {
180     m_server.serviceWorkerStoppedControllingClient(*this, serviceWorkerIdentifier, scriptExecutionContextIdentifier);
181 }
182
183 SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore)
184     : m_originStore(WTFMove(originStore))
185 {
186     allServers().add(this);
187     m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
188         taskThreadEntryPoint();
189     });
190 }
191
192 // https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
193 void SWServer::scheduleJob(const ServiceWorkerJobData& jobData)
194 {
195     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
196
197     // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
198     // If it is, stack it along with that job.
199
200     auto& jobQueue = *m_jobQueues.ensure(jobData.registrationKey(), [this, &jobData] {
201         return std::make_unique<SWServerJobQueue>(*this, jobData.registrationKey());
202     }).iterator->value;
203
204     jobQueue.enqueueJob(jobData);
205     if (jobQueue.size() == 1)
206         jobQueue.runNextJob();
207 }
208
209 void SWServer::rejectJob(const ServiceWorkerJobData& jobData, const ExceptionData& exceptionData)
210 {
211     LOG(ServiceWorker, "Rejected ServiceWorker job %s-%" PRIu64 " in server", jobData.connectionIdentifier().loggingString().utf8().data(), jobData.identifier());
212     auto* connection = m_connections.get(jobData.connectionIdentifier());
213     if (!connection)
214         return;
215
216     connection->rejectJobInClient(jobData.identifier(), exceptionData);
217 }
218
219 void SWServer::resolveRegistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
220 {
221     LOG(ServiceWorker, "Resolved ServiceWorker job %s-%" PRIu64 " in server with registration %s", jobData.connectionIdentifier().loggingString().utf8().data(), jobData.identifier(), registrationData.identifier.loggingString().utf8().data());
222     auto* connection = m_connections.get(jobData.connectionIdentifier());
223     if (!connection)
224         return;
225
226     connection->resolveRegistrationJobInClient(jobData.identifier(), registrationData, shouldNotifyWhenResolved);
227 }
228
229 void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
230 {
231     auto* connection = m_connections.get(jobData.connectionIdentifier());
232     if (!connection)
233         return;
234
235     connection->resolveUnregistrationJobInClient(jobData.identifier(), registrationKey, unregistrationResult);
236 }
237
238 void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData)
239 {
240     LOG(ServiceWorker, "Server issuing startScriptFetch for current job %s-%" PRIu64 " in client", jobData.connectionIdentifier().loggingString().utf8().data(), jobData.identifier());
241     auto* connection = m_connections.get(jobData.connectionIdentifier());
242     if (!connection)
243         return;
244
245     connection->startScriptFetchInClient(jobData.identifier());
246 }
247
248 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
249 {
250     LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %s-%" PRIu64 " in client", result.connectionIdentifier.loggingString().utf8().data(), result.jobIdentifier);
251
252     ASSERT(m_connections.contains(result.connectionIdentifier));
253
254     auto jobQueue = m_jobQueues.get(result.registrationKey);
255     if (!jobQueue)
256         return;
257
258     jobQueue->scriptFetchFinished(connection, result);
259 }
260
261 void SWServer::scriptContextFailedToStart(SWServerWorker& worker, const String& message)
262 {
263     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
264         jobQueue->scriptContextFailedToStart(worker.identifier(), message);
265 }
266
267 void SWServer::scriptContextStarted(SWServerWorker& worker)
268 {
269     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
270         jobQueue->scriptContextStarted(worker.identifier());
271 }
272
273 void SWServer::didFinishInstall(SWServerWorker& worker, bool wasSuccessful)
274 {
275     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
276         jobQueue->didFinishInstall(worker.identifier(), wasSuccessful);
277 }
278
279 void SWServer::didFinishActivation(SWServerWorker& worker)
280 {
281     if (auto* registration = getRegistration(worker.registrationKey()))
282         SWServerJobQueue::didFinishActivation(*registration, worker.identifier());
283 }
284
285 void SWServer::workerContextTerminated(SWServerWorker& worker)
286 {
287     auto result = m_workersByID.remove(worker.identifier());
288     ASSERT_UNUSED(result, result);
289 }
290
291 void SWServer::didResolveRegistrationPromise(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey)
292 {
293     ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
294
295     if (auto* jobQueue = m_jobQueues.get(registrationKey))
296         jobQueue->didResolveRegistrationPromise();
297 }
298
299 void SWServer::addClientServiceWorkerRegistration(Connection& connection, const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
300 {
301     auto* registration = m_registrations.get(key);
302     if (!registration) {
303         LOG_ERROR("Request to add client-side ServiceWorkerRegistration to non-existent server-side registration");
304         return;
305     }
306
307     if (registration->identifier() != identifier)
308         return;
309     
310     registration->addClientServiceWorkerRegistration(connection.identifier());
311 }
312
313 void SWServer::removeClientServiceWorkerRegistration(Connection& connection, const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
314 {
315     auto* registration = m_registrations.get(key);
316     if (!registration) {
317         LOG_ERROR("Request to remove client-side ServiceWorkerRegistration from non-existent server-side registration");
318         return;
319     }
320
321     if (registration->identifier() != identifier)
322         return;
323     
324     registration->removeClientServiceWorkerRegistration(connection.identifier());
325 }
326
327 void SWServer::serviceWorkerStartedControllingClient(Connection& connection, ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t scriptExecutionContextIdentifier)
328 {
329     auto* serviceWorker = m_workersByID.get(serviceWorkerIdentifier);
330     if (!serviceWorker)
331         return;
332
333     auto* registration = m_registrations.get(serviceWorker->registrationKey());
334     if (!registration)
335         return;
336
337     registration->addClientUsingRegistration({ connection.identifier(), scriptExecutionContextIdentifier });
338 }
339
340 void SWServer::serviceWorkerStoppedControllingClient(Connection& connection, ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t scriptExecutionContextIdentifier)
341 {
342     auto* serviceWorker = m_workersByID.get(serviceWorkerIdentifier);
343     if (!serviceWorker)
344         return;
345
346     auto* registration = m_registrations.get(serviceWorker->registrationKey());
347     if (!registration)
348         return;
349
350     registration->removeClientUsingRegistration({ connection.identifier(), scriptExecutionContextIdentifier });
351 }
352
353 void SWServer::updateWorker(Connection&, const ServiceWorkerRegistrationKey& registrationKey, const URL& url, const String& script, WorkerType type)
354 {
355     auto serviceWorkerIdentifier = generateServiceWorkerIdentifier();
356
357     ServiceWorkerContextData data = { registrationKey, serviceWorkerIdentifier, script, url, type };
358
359     // Right now we only ever keep up to one connection to one SW context process.
360     // And it should always exist if we're calling updateWorker
361     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
362     if (!connection) {
363         m_pendingContextDatas.append(WTFMove(data));
364         return;
365     }
366     
367     installContextData(data);
368 }
369
370 void SWServer::serverToContextConnectionCreated()
371 {
372     ASSERT(SWServerToContextConnection::globalServerToContextConnection());
373     for (auto& data : m_pendingContextDatas)
374         installContextData(data);
375     
376     m_pendingContextDatas.clear();
377 }
378
379 void SWServer::installContextData(const ServiceWorkerContextData& data)
380 {
381     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
382     ASSERT(connection);
383
384     auto result = m_workersByID.add(data.serviceWorkerIdentifier, SWServerWorker::create(*this, data.registrationKey, connection->identifier(), data.scriptURL, data.script, data.workerType, data.serviceWorkerIdentifier));
385     ASSERT_UNUSED(result, result.isNewEntry);
386
387     connection->installServiceWorkerContext(data);
388 }
389
390
391 void SWServer::terminateWorker(SWServerWorker& worker)
392 {
393     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
394     if (connection)
395         connection->terminateWorker(worker.identifier());
396     else
397         LOG_ERROR("Request to terminate a worker whose context connection does not exist");
398 }
399
400 void SWServer::fireInstallEvent(SWServerWorker& worker)
401 {
402     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
403     if (!connection) {
404         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
405         return;
406     }
407
408     connection->fireInstallEvent(worker.identifier());
409 }
410
411 void SWServer::fireActivateEvent(SWServerWorker& worker)
412 {
413     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
414     if (!connection) {
415         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
416         return;
417     }
418
419     connection->fireActivateEvent(worker.identifier());
420 }
421
422 void SWServer::taskThreadEntryPoint()
423 {
424     ASSERT(!isMainThread());
425
426     while (!m_taskQueue.isKilled())
427         m_taskQueue.waitForMessage().performTask();
428
429     Locker<Lock> locker(m_taskThreadLock);
430     m_taskThread = nullptr;
431 }
432
433 void SWServer::postTask(CrossThreadTask&& task)
434 {
435     m_taskQueue.append(WTFMove(task));
436 }
437
438 void SWServer::postTaskReply(CrossThreadTask&& task)
439 {
440     m_taskReplyQueue.append(WTFMove(task));
441
442     Locker<Lock> locker(m_mainThreadReplyLock);
443     if (m_mainThreadReplyScheduled)
444         return;
445
446     m_mainThreadReplyScheduled = true;
447     callOnMainThread([this] {
448         handleTaskRepliesOnMainThread();
449     });
450 }
451
452 void SWServer::handleTaskRepliesOnMainThread()
453 {
454     {
455         Locker<Lock> locker(m_mainThreadReplyLock);
456         m_mainThreadReplyScheduled = false;
457     }
458
459     while (auto task = m_taskReplyQueue.tryGetMessage())
460         task->performTask();
461 }
462
463 void SWServer::registerConnection(Connection& connection)
464 {
465     auto result = m_connections.add(connection.identifier(), nullptr);
466     ASSERT(result.isNewEntry);
467     result.iterator->value = &connection;
468 }
469
470 void SWServer::unregisterConnection(Connection& connection)
471 {
472     ASSERT(m_connections.get(connection.identifier()) == &connection);
473     m_connections.remove(connection.identifier());
474
475     for (auto& registration : m_registrations.values())
476         registration->unregisterServerConnection(connection.identifier());
477 }
478
479 const SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL) const
480 {
481     const SWServerRegistration* selectedRegistration = nullptr;
482     for (auto& registration : m_registrations.values()) {
483         if (!registration->key().isMatching(topOrigin, clientURL))
484             continue;
485         if (!selectedRegistration || selectedRegistration->key().scopeLength() < registration->key().scopeLength())
486             selectedRegistration = registration.get();
487     }
488
489     return (selectedRegistration && !selectedRegistration->isUninstalling()) ? selectedRegistration : nullptr;
490 }
491
492 } // namespace WebCore
493
494 #endif // ENABLE(SERVICE_WORKER)