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