Massive "Server-process-to-context-process" connection overhaul.
[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, uint64_t identifier)
54     : Identified(identifier)
55     , m_server(server)
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 void SWServer::clearAll()
113 {
114     m_jobQueues.clear();
115     m_registrations.clear();
116     m_originStore->clearAll();
117     // FIXME: We should probably ask service workers to terminate.
118 }
119
120 void SWServer::clear(const SecurityOrigin& origin)
121 {
122     m_originStore->clear(origin);
123
124     // FIXME: We should clear entries in m_registrations, m_jobQueues and m_workersByID.
125 }
126
127 void SWServer::Connection::scheduleJobInServer(const ServiceWorkerJobData& jobData)
128 {
129     LOG(ServiceWorker, "Scheduling ServiceWorker job %" PRIu64 "-%" PRIu64 " in server", jobData.connectionIdentifier(), jobData.identifier());
130     ASSERT(identifier() == jobData.connectionIdentifier());
131
132     m_server.scheduleJob(jobData);
133 }
134
135 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
136 {
137     m_server.scriptFetchFinished(*this, result);
138 }
139
140 void SWServer::Connection::didResolveRegistrationPromise(const ServiceWorkerRegistrationKey& key)
141 {
142     m_server.didResolveRegistrationPromise(*this, key);
143 }
144
145 void SWServer::Connection::addServiceWorkerRegistrationInServer(const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
146 {
147     m_server.addClientServiceWorkerRegistration(*this, key, identifier);
148 }
149
150 void SWServer::Connection::removeServiceWorkerRegistrationInServer(const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
151 {
152     m_server.removeClientServiceWorkerRegistration(*this, key, identifier);
153 }
154
155 SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore)
156     : m_originStore(WTFMove(originStore))
157 {
158     allServers().add(this);
159     m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
160         taskThreadEntryPoint();
161     });
162 }
163
164 // https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
165 void SWServer::scheduleJob(const ServiceWorkerJobData& jobData)
166 {
167     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
168
169     // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
170     // If it is, stack it along with that job.
171
172     auto& jobQueue = *m_jobQueues.ensure(jobData.registrationKey(), [this, &jobData] {
173         return std::make_unique<SWServerJobQueue>(*this, jobData.registrationKey());
174     }).iterator->value;
175
176     jobQueue.enqueueJob(jobData);
177     if (jobQueue.size() == 1)
178         jobQueue.runNextJob();
179 }
180
181 void SWServer::rejectJob(const ServiceWorkerJobData& jobData, const ExceptionData& exceptionData)
182 {
183     LOG(ServiceWorker, "Rejected ServiceWorker job %" PRIu64 "-%" PRIu64 " in server", jobData.connectionIdentifier(), jobData.identifier());
184     auto* connection = m_connections.get(jobData.connectionIdentifier());
185     if (!connection)
186         return;
187
188     connection->rejectJobInClient(jobData.identifier(), exceptionData);
189 }
190
191 void SWServer::resolveRegistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
192 {
193     LOG(ServiceWorker, "Resolved ServiceWorker job %" PRIu64 "-%" PRIu64 " in server with registration %s", jobData.connectionIdentifier(), jobData.identifier(), registrationData.identifier.loggingString().utf8().data());
194     auto* connection = m_connections.get(jobData.connectionIdentifier());
195     if (!connection)
196         return;
197
198     connection->resolveRegistrationJobInClient(jobData.identifier(), registrationData, shouldNotifyWhenResolved);
199 }
200
201 void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
202 {
203     auto* connection = m_connections.get(jobData.connectionIdentifier());
204     if (!connection)
205         return;
206
207     connection->resolveUnregistrationJobInClient(jobData.identifier(), registrationKey, unregistrationResult);
208 }
209
210 void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData)
211 {
212     LOG(ServiceWorker, "Server issuing startScriptFetch for current job %" PRIu64 "-%" PRIu64 " in client", jobData.connectionIdentifier(), jobData.identifier());
213     auto* connection = m_connections.get(jobData.connectionIdentifier());
214     if (!connection)
215         return;
216
217     connection->startScriptFetchInClient(jobData.identifier());
218 }
219
220 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
221 {
222     LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %" PRIu64 "-%" PRIu64 " in client", result.connectionIdentifier, result.jobIdentifier);
223
224     ASSERT(m_connections.contains(result.connectionIdentifier));
225
226     auto jobQueue = m_jobQueues.get(result.registrationKey);
227     if (!jobQueue)
228         return;
229
230     jobQueue->scriptFetchFinished(connection, result);
231 }
232
233 void SWServer::scriptContextFailedToStart(SWServerWorker& worker, const String& message)
234 {
235     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
236         jobQueue->scriptContextFailedToStart(worker.identifier(), message);
237 }
238
239 void SWServer::scriptContextStarted(SWServerWorker& worker)
240 {
241     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
242         jobQueue->scriptContextStarted(worker.identifier());
243 }
244
245 void SWServer::didFinishInstall(SWServerWorker& worker, bool wasSuccessful)
246 {
247     if (auto* jobQueue = m_jobQueues.get(worker.registrationKey()))
248         jobQueue->didFinishInstall(worker.identifier(), wasSuccessful);
249 }
250
251 void SWServer::didFinishActivation(SWServerWorker& worker)
252 {
253     if (auto* registration = getRegistration(worker.registrationKey()))
254         SWServerJobQueue::didFinishActivation(*registration, worker.identifier());
255 }
256
257 void SWServer::didResolveRegistrationPromise(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey)
258 {
259     ASSERT_UNUSED(connection, m_connections.contains(connection.identifier()));
260
261     if (auto* jobQueue = m_jobQueues.get(registrationKey))
262         jobQueue->didResolveRegistrationPromise();
263 }
264
265 void SWServer::addClientServiceWorkerRegistration(Connection& connection, const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
266 {
267     auto* registration = m_registrations.get(key);
268     if (!registration) {
269         LOG_ERROR("Request to add client-side ServiceWorkerRegistration to non-existent server-side registration");
270         return;
271     }
272
273     if (registration->identifier() != identifier)
274         return;
275     
276     registration->addClientServiceWorkerRegistration(connection.identifier());
277 }
278
279 void SWServer::removeClientServiceWorkerRegistration(Connection& connection, const ServiceWorkerRegistrationKey& key, ServiceWorkerRegistrationIdentifier identifier)
280 {
281     auto* registration = m_registrations.get(key);
282     if (!registration) {
283         LOG_ERROR("Request to remove client-side ServiceWorkerRegistration from non-existent server-side registration");
284         return;
285     }
286
287     if (registration->identifier() != identifier)
288         return;
289     
290     registration->removeClientServiceWorkerRegistration(connection.identifier());
291 }
292
293 void SWServer::updateWorker(Connection&, const ServiceWorkerRegistrationKey& registrationKey, const URL& url, const String& script, WorkerType type)
294 {
295     auto serviceWorkerIdentifier = generateServiceWorkerIdentifier();
296
297     ServiceWorkerContextData data = { registrationKey, serviceWorkerIdentifier, script, url, type };
298
299     // Right now we only ever keep up to one connection to one SW context process.
300     // And it should always exist if we're calling updateWorker
301     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
302     if (!connection) {
303         m_pendingContextDatas.append(WTFMove(data));
304         return;
305     }
306     
307     installContextData(data);
308 }
309
310 void SWServer::serverToContextConnectionCreated()
311 {
312     ASSERT(SWServerToContextConnection::globalServerToContextConnection());
313     for (auto& data : m_pendingContextDatas)
314         installContextData(data);
315     
316     m_pendingContextDatas.clear();
317 }
318
319 void SWServer::installContextData(const ServiceWorkerContextData& data)
320 {
321     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
322     ASSERT(connection);
323
324     auto result = m_workersByID.add(data.serviceWorkerIdentifier, SWServerWorker::create(*this, data.registrationKey, connection->identifier(), data.scriptURL, data.script, data.workerType, data.serviceWorkerIdentifier));
325     ASSERT_UNUSED(result, result.isNewEntry);
326
327     connection->installServiceWorkerContext(data);
328 }
329
330 void SWServer::fireInstallEvent(SWServerWorker& worker)
331 {
332 //    auto* worker = m_workersByID.get(serviceWorkerIdentifier);
333 //    if (!worker) {
334 //        LOG_ERROR("Request to fire install event on a worker that cannot be found in the server");
335 //        return;
336 //    }
337
338     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
339     if (!connection) {
340         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
341         return;
342     }
343
344     connection->fireInstallEvent(worker.identifier());
345 }
346
347 void SWServer::fireActivateEvent(SWServerWorker& worker)
348 {
349 //    auto* worker = m_workersByID.get(serviceWorkerIdentifier);
350 //    if (!worker) {
351 //        LOG_ERROR("Request to fire install event on a worker that cannot be found in the server");
352 //        return;
353 //    }
354
355     auto* connection = SWServerToContextConnection::connectionForIdentifier(worker.contextConnectionIdentifier());
356     if (!connection) {
357         LOG_ERROR("Request to fire install event on a worker whose context connection does not exist");
358         return;
359     }
360
361     connection->fireActivateEvent(worker.identifier());
362 }
363
364 void SWServer::taskThreadEntryPoint()
365 {
366     ASSERT(!isMainThread());
367
368     while (!m_taskQueue.isKilled())
369         m_taskQueue.waitForMessage().performTask();
370
371     Locker<Lock> locker(m_taskThreadLock);
372     m_taskThread = nullptr;
373 }
374
375 void SWServer::postTask(CrossThreadTask&& task)
376 {
377     m_taskQueue.append(WTFMove(task));
378 }
379
380 void SWServer::postTaskReply(CrossThreadTask&& task)
381 {
382     m_taskReplyQueue.append(WTFMove(task));
383
384     Locker<Lock> locker(m_mainThreadReplyLock);
385     if (m_mainThreadReplyScheduled)
386         return;
387
388     m_mainThreadReplyScheduled = true;
389     callOnMainThread([this] {
390         handleTaskRepliesOnMainThread();
391     });
392 }
393
394 void SWServer::handleTaskRepliesOnMainThread()
395 {
396     {
397         Locker<Lock> locker(m_mainThreadReplyLock);
398         m_mainThreadReplyScheduled = false;
399     }
400
401     while (auto task = m_taskReplyQueue.tryGetMessage())
402         task->performTask();
403 }
404
405 void SWServer::registerConnection(Connection& connection)
406 {
407     auto result = m_connections.add(connection.identifier(), nullptr);
408     ASSERT(result.isNewEntry);
409     result.iterator->value = &connection;
410 }
411
412 void SWServer::unregisterConnection(Connection& connection)
413 {
414     ASSERT(m_connections.get(connection.identifier()) == &connection);
415     m_connections.remove(connection.identifier());
416 }
417
418 const SWServerRegistration* SWServer::doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL) const
419 {
420     const SWServerRegistration* selectedRegistration = nullptr;
421     for (auto& registration : m_registrations.values()) {
422         if (!registration->key().isMatching(topOrigin, clientURL))
423             continue;
424         if (!selectedRegistration || selectedRegistration->key().scopeLength() < registration->key().scopeLength())
425             selectedRegistration = registration.get();
426     }
427
428     return (selectedRegistration && !selectedRegistration->isUninstalling()) ? selectedRegistration : nullptr;
429 }
430
431 } // namespace WebCore
432
433 #endif // ENABLE(SERVICE_WORKER)