Add support for unregistering a service worker
[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 "SWServerRegistration.h"
35 #include "SWServerWorker.h"
36 #include "ServiceWorkerContextData.h"
37 #include "ServiceWorkerFetchResult.h"
38 #include "ServiceWorkerJobData.h"
39 #include <wtf/UUID.h>
40 #include <wtf/text/WTFString.h>
41
42 namespace WebCore {
43
44 SWServer::Connection::Connection(SWServer& server, uint64_t identifier)
45     : Identified(identifier)
46     , m_server(server)
47 {
48     m_server.registerConnection(*this);
49 }
50
51 SWServer::Connection::~Connection()
52 {
53     m_server.unregisterConnection(*this);
54 }
55
56 SWServer::~SWServer()
57 {
58     RELEASE_ASSERT(m_connections.isEmpty());
59     RELEASE_ASSERT(m_registrations.isEmpty());
60
61     ASSERT(m_taskQueue.isEmpty());
62     ASSERT(m_taskReplyQueue.isEmpty());
63
64     // For a SWServer to be cleanly shut down its thread must have finished and gone away.
65     // At this stage in development of the feature that actually never happens.
66     // But once it does start happening, this ASSERT will catch us doing it wrong.
67     Locker<Lock> locker(m_taskThreadLock);
68     ASSERT(!m_taskThread);
69 }
70
71 void SWServer::Connection::scheduleJobInServer(const ServiceWorkerJobData& jobData)
72 {
73     LOG(ServiceWorker, "Scheduling ServiceWorker job %" PRIu64 "-%" PRIu64 " in server", jobData.connectionIdentifier(), jobData.identifier());
74     ASSERT(identifier() == jobData.connectionIdentifier());
75
76     m_server.scheduleJob(jobData);
77 }
78
79 void SWServer::Connection::finishFetchingScriptInServer(const ServiceWorkerFetchResult& result)
80 {
81     m_server.scriptFetchFinished(*this, result);
82 }
83
84 void SWServer::Connection::scriptContextFailedToStart(const ServiceWorkerRegistrationKey& registrationKey, const String& workerID, const String& message)
85 {
86     m_server.scriptContextFailedToStart(*this, registrationKey, workerID, message);
87 }
88
89 void SWServer::Connection::scriptContextStarted(const ServiceWorkerRegistrationKey& registrationKey, uint64_t identifier, const String& workerID)
90 {
91     m_server.scriptContextStarted(*this, registrationKey, identifier, workerID);
92 }
93
94 SWServer::SWServer()
95 {
96     m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
97         taskThreadEntryPoint();
98     });
99 }
100
101 void SWServer::scheduleJob(const ServiceWorkerJobData& jobData)
102 {
103     ASSERT(m_connections.contains(jobData.connectionIdentifier()));
104
105     auto result = m_registrations.add(jobData.registrationKey(), nullptr);
106     if (result.isNewEntry)
107         result.iterator->value = std::make_unique<SWServerRegistration>(*this, jobData.registrationKey());
108
109     ASSERT(result.iterator->value);
110
111     result.iterator->value->enqueueJob(jobData);
112 }
113
114 void SWServer::rejectJob(const ServiceWorkerJobData& jobData, const ExceptionData& exceptionData)
115 {
116     LOG(ServiceWorker, "Rejected ServiceWorker job %" PRIu64 "-%" PRIu64 " in server", jobData.connectionIdentifier(), jobData.identifier());
117     auto* connection = m_connections.get(jobData.connectionIdentifier());
118     if (!connection)
119         return;
120
121     connection->rejectJobInClient(jobData.identifier(), exceptionData);
122 }
123
124 void SWServer::resolveRegistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationData& registrationData)
125 {
126     LOG(ServiceWorker, "Resolved ServiceWorker job %" PRIu64 "-%" PRIu64 " in server with registration %" PRIu64, jobData.connectionIdentifier(), jobData.identifier(), registrationData.identifier);
127     auto* connection = m_connections.get(jobData.connectionIdentifier());
128     if (!connection)
129         return;
130
131     connection->resolveRegistrationJobInClient(jobData.identifier(), registrationData);
132 }
133
134 void SWServer::resolveUnregistrationJob(const ServiceWorkerJobData& jobData, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
135 {
136     auto* connection = m_connections.get(jobData.connectionIdentifier());
137     if (!connection)
138         return;
139
140     connection->resolveUnregistrationJobInClient(jobData.identifier(), registrationKey, unregistrationResult);
141 }
142
143 void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData)
144 {
145     LOG(ServiceWorker, "Server issuing startScriptFetch for current job %" PRIu64 "-%" PRIu64 " in client", jobData.connectionIdentifier(), jobData.identifier());
146     auto* connection = m_connections.get(jobData.connectionIdentifier());
147     if (!connection)
148         return;
149
150     connection->startScriptFetchInClient(jobData.identifier());
151 }
152
153 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
154 {
155     LOG(ServiceWorker, "Server handling scriptFetchFinished for current job %" PRIu64 "-%" PRIu64 " in client", result.connectionIdentifier, result.jobIdentifier);
156
157     ASSERT(m_connections.contains(result.connectionIdentifier));
158
159     auto registration = m_registrations.get(result.registrationKey);
160     if (!registration)
161         return;
162
163     registration->scriptFetchFinished(connection, result);
164 }
165
166 void SWServer::scriptContextFailedToStart(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, const String& workerID, const String& message)
167 {
168     ASSERT(m_connections.contains(connection.identifier()));
169     
170     if (auto* registration = m_registrations.get(registrationKey))
171         registration->scriptContextFailedToStart(connection, workerID, message);
172 }
173
174 void SWServer::scriptContextStarted(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, uint64_t identifier, const String& workerID)
175 {
176     ASSERT(m_connections.contains(connection.identifier()));
177
178     if (auto* registration = m_registrations.get(registrationKey))
179         registration->scriptContextStarted(connection, identifier, workerID);
180 }
181
182 Ref<SWServerWorker> SWServer::createWorker(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, const URL& url, const String& script, WorkerType type)
183 {
184     String workerID = createCanonicalUUIDString();
185     
186     auto result = m_workersByID.add(workerID, SWServerWorker::create(registrationKey, url, script, type, workerID));
187     ASSERT(result.isNewEntry);
188     
189     connection.startServiceWorkerContext({ registrationKey, workerID, script, url });
190     
191     return result.iterator->value.get();
192 }
193
194 void SWServer::taskThreadEntryPoint()
195 {
196     ASSERT(!isMainThread());
197
198     while (!m_taskQueue.isKilled())
199         m_taskQueue.waitForMessage().performTask();
200
201     Locker<Lock> locker(m_taskThreadLock);
202     m_taskThread = nullptr;
203 }
204
205 void SWServer::postTask(CrossThreadTask&& task)
206 {
207     m_taskQueue.append(WTFMove(task));
208 }
209
210 void SWServer::postTaskReply(CrossThreadTask&& task)
211 {
212     m_taskReplyQueue.append(WTFMove(task));
213
214     Locker<Lock> locker(m_mainThreadReplyLock);
215     if (m_mainThreadReplyScheduled)
216         return;
217
218     m_mainThreadReplyScheduled = true;
219     callOnMainThread([this] {
220         handleTaskRepliesOnMainThread();
221     });
222 }
223
224 void SWServer::handleTaskRepliesOnMainThread()
225 {
226     {
227         Locker<Lock> locker(m_mainThreadReplyLock);
228         m_mainThreadReplyScheduled = false;
229     }
230
231     while (auto task = m_taskReplyQueue.tryGetMessage())
232         task->performTask();
233 }
234
235 void SWServer::registerConnection(Connection& connection)
236 {
237     auto result = m_connections.add(connection.identifier(), nullptr);
238     ASSERT(result.isNewEntry);
239     result.iterator->value = &connection;
240 }
241
242 void SWServer::unregisterConnection(Connection& connection)
243 {
244     ASSERT(m_connections.get(connection.identifier()) == &connection);
245     m_connections.remove(connection.identifier());
246 }
247
248 } // namespace WebCore
249
250 #endif // ENABLE(SERVICE_WORKER)