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