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 "SWServerRegistration.h"
28
29#if ENABLE(SERVICE_WORKER)
30
31#include "SWServer.h"
32#include "SWServerWorker.h"
33#include "ServiceWorkerTypes.h"
34#include "ServiceWorkerUpdateViaCache.h"
35
36namespace WebCore {
37
38static ServiceWorkerRegistrationIdentifier generateServiceWorkerRegistrationIdentifier()
39{
40 return ServiceWorkerRegistrationIdentifier::generate();
41}
42
43SWServerRegistration::SWServerRegistration(SWServer& server, const ServiceWorkerRegistrationKey& key, ServiceWorkerUpdateViaCache updateViaCache, const URL& scopeURL, const URL& scriptURL)
44 : m_identifier(generateServiceWorkerRegistrationIdentifier())
45 , m_registrationKey(key)
46 , m_updateViaCache(updateViaCache)
47 , m_scopeURL(scopeURL)
48 , m_scriptURL(scriptURL)
49 , m_server(server)
50 , m_creationTime(MonotonicTime::now())
51{
52 m_scopeURL.removeFragmentIdentifier();
53}
54
55SWServerRegistration::~SWServerRegistration()
56{
57 ASSERT(!m_preInstallationWorker || !m_preInstallationWorker->isRunning());
58 ASSERT(!m_installingWorker || !m_installingWorker->isRunning());
59 ASSERT(!m_waitingWorker || !m_waitingWorker->isRunning());
60 ASSERT(!m_activeWorker || !m_activeWorker->isRunning());
61}
62
63SWServerWorker* SWServerRegistration::getNewestWorker()
64{
65 if (m_installingWorker)
66 return m_installingWorker.get();
67 if (m_waitingWorker)
68 return m_waitingWorker.get();
69
70 return m_activeWorker.get();
71}
72
73void SWServerRegistration::setPreInstallationWorker(SWServerWorker* worker)
74{
75 m_preInstallationWorker = worker;
76}
77
78void SWServerRegistration::updateRegistrationState(ServiceWorkerRegistrationState state, SWServerWorker* worker)
79{
80 LOG(ServiceWorker, "(%p) Updating registration state to %i with worker %p", this, (int)state, worker);
81
82 switch (state) {
83 case ServiceWorkerRegistrationState::Installing:
84 ASSERT(!m_installingWorker || !m_installingWorker->isRunning() || m_waitingWorker == m_installingWorker);
85 m_installingWorker = worker;
86 break;
87 case ServiceWorkerRegistrationState::Waiting:
88 ASSERT(!m_waitingWorker || !m_waitingWorker->isRunning() || m_activeWorker == m_waitingWorker);
89 m_waitingWorker = worker;
90 break;
91 case ServiceWorkerRegistrationState::Active:
92 ASSERT(!m_activeWorker || !m_activeWorker->isRunning());
93 m_activeWorker = worker;
94 break;
95 };
96
97 Optional<ServiceWorkerData> serviceWorkerData;
98 if (worker)
99 serviceWorkerData = worker->data();
100
101 forEachConnection([&](auto& connection) {
102 connection.updateRegistrationStateInClient(this->identifier(), state, serviceWorkerData);
103 });
104}
105
106void SWServerRegistration::updateWorkerState(SWServerWorker& worker, ServiceWorkerState state)
107{
108 LOG(ServiceWorker, "Updating worker %p state to %i (%p)", &worker, (int)state, this);
109
110 worker.setState(state);
111}
112
113void SWServerRegistration::setUpdateViaCache(ServiceWorkerUpdateViaCache updateViaCache)
114{
115 m_updateViaCache = updateViaCache;
116 forEachConnection([&](auto& connection) {
117 connection.setRegistrationUpdateViaCache(this->identifier(), updateViaCache);
118 });
119}
120
121void SWServerRegistration::setLastUpdateTime(WallTime time)
122{
123 m_lastUpdateTime = time;
124 forEachConnection([&](auto& connection) {
125 connection.setRegistrationLastUpdateTime(this->identifier(), time);
126 });
127}
128
129void SWServerRegistration::fireUpdateFoundEvent()
130{
131 forEachConnection([&](auto& connection) {
132 connection.fireUpdateFoundEvent(this->identifier());
133 });
134}
135
136void SWServerRegistration::forEachConnection(const WTF::Function<void(SWServer::Connection&)>& apply)
137{
138 for (auto connectionIdentifierWithClients : m_connectionsWithClientRegistrations.values()) {
139 if (auto* connection = m_server.connection(connectionIdentifierWithClients))
140 apply(*connection);
141 }
142}
143
144ServiceWorkerRegistrationData SWServerRegistration::data() const
145{
146 Optional<ServiceWorkerData> installingWorkerData;
147 if (m_installingWorker)
148 installingWorkerData = m_installingWorker->data();
149
150 Optional<ServiceWorkerData> waitingWorkerData;
151 if (m_waitingWorker)
152 waitingWorkerData = m_waitingWorker->data();
153
154 Optional<ServiceWorkerData> activeWorkerData;
155 if (m_activeWorker)
156 activeWorkerData = m_activeWorker->data();
157
158 return { m_registrationKey, identifier(), m_scopeURL, m_updateViaCache, m_lastUpdateTime, WTFMove(installingWorkerData), WTFMove(waitingWorkerData), WTFMove(activeWorkerData) };
159}
160
161void SWServerRegistration::addClientServiceWorkerRegistration(SWServerConnectionIdentifier connectionIdentifier)
162{
163 m_connectionsWithClientRegistrations.add(connectionIdentifier);
164}
165
166void SWServerRegistration::removeClientServiceWorkerRegistration(SWServerConnectionIdentifier connectionIdentifier)
167{
168 m_connectionsWithClientRegistrations.remove(connectionIdentifier);
169}
170
171void SWServerRegistration::addClientUsingRegistration(const ServiceWorkerClientIdentifier& clientIdentifier)
172{
173 auto addResult = m_clientsUsingRegistration.ensure(clientIdentifier.serverConnectionIdentifier, [] {
174 return HashSet<DocumentIdentifier> { };
175 }).iterator->value.add(clientIdentifier.contextIdentifier);
176 ASSERT_UNUSED(addResult, addResult.isNewEntry);
177}
178
179void SWServerRegistration::removeClientUsingRegistration(const ServiceWorkerClientIdentifier& clientIdentifier)
180{
181 auto iterator = m_clientsUsingRegistration.find(clientIdentifier.serverConnectionIdentifier);
182 ASSERT(iterator != m_clientsUsingRegistration.end());
183 if (iterator == m_clientsUsingRegistration.end())
184 return;
185
186 bool wasRemoved = iterator->value.remove(clientIdentifier.contextIdentifier);
187 ASSERT_UNUSED(wasRemoved, wasRemoved);
188
189 if (iterator->value.isEmpty())
190 m_clientsUsingRegistration.remove(iterator);
191
192 handleClientUnload();
193}
194
195// https://w3c.github.io/ServiceWorker/#notify-controller-change
196void SWServerRegistration::notifyClientsOfControllerChange()
197{
198 ASSERT(activeWorker());
199
200 for (auto& item : m_clientsUsingRegistration) {
201 if (auto* connection = m_server.connection(item.key))
202 connection->notifyClientsOfControllerChange(item.value, activeWorker()->data());
203 }
204}
205
206void SWServerRegistration::unregisterServerConnection(SWServerConnectionIdentifier serverConnectionIdentifier)
207{
208 m_connectionsWithClientRegistrations.removeAll(serverConnectionIdentifier);
209 m_clientsUsingRegistration.remove(serverConnectionIdentifier);
210}
211
212// https://w3c.github.io/ServiceWorker/#try-clear-registration-algorithm
213bool SWServerRegistration::tryClear()
214{
215 if (hasClientsUsingRegistration())
216 return false;
217
218 if (installingWorker() && installingWorker()->hasPendingEvents())
219 return false;
220 if (waitingWorker() && waitingWorker()->hasPendingEvents())
221 return false;
222 if (activeWorker() && activeWorker()->hasPendingEvents())
223 return false;
224
225 clear();
226 return true;
227}
228
229// https://w3c.github.io/ServiceWorker/#clear-registration
230void SWServerRegistration::clear()
231{
232 if (m_preInstallationWorker) {
233 ASSERT(m_preInstallationWorker->state() == ServiceWorkerState::Redundant);
234 m_preInstallationWorker->terminate();
235 m_preInstallationWorker = nullptr;
236 }
237
238 RefPtr<SWServerWorker> installingWorker = this->installingWorker();
239 if (installingWorker) {
240 installingWorker->terminate();
241 updateRegistrationState(ServiceWorkerRegistrationState::Installing, nullptr);
242 }
243 RefPtr<SWServerWorker> waitingWorker = this->waitingWorker();
244 if (waitingWorker) {
245 waitingWorker->terminate();
246 updateRegistrationState(ServiceWorkerRegistrationState::Waiting, nullptr);
247 }
248 RefPtr<SWServerWorker> activeWorker = this->activeWorker();
249 if (activeWorker) {
250 activeWorker->terminate();
251 updateRegistrationState(ServiceWorkerRegistrationState::Active, nullptr);
252 }
253
254 if (installingWorker)
255 updateWorkerState(*installingWorker, ServiceWorkerState::Redundant);
256 if (waitingWorker)
257 updateWorkerState(*waitingWorker, ServiceWorkerState::Redundant);
258 if (activeWorker)
259 updateWorkerState(*activeWorker, ServiceWorkerState::Redundant);
260
261 // Remove scope to registration map[scopeString].
262 m_server.removeRegistration(key());
263}
264
265// https://w3c.github.io/ServiceWorker/#try-activate-algorithm
266void SWServerRegistration::tryActivate()
267{
268 // If registration's waiting worker is null, return.
269 if (!waitingWorker())
270 return;
271 // If registration's active worker is not null and registration's active worker's state is activating, return.
272 if (activeWorker() && activeWorker()->state() == ServiceWorkerState::Activating)
273 return;
274
275 // Invoke Activate with registration if either of the following is true:
276 // - registration's active worker is null.
277 // - The result of running Service Worker Has No Pending Events with registration's active worker is true,
278 // and no service worker client is using registration or registration's waiting worker's skip waiting flag is set.
279 if (!activeWorker() || (!activeWorker()->hasPendingEvents() && (!hasClientsUsingRegistration() || waitingWorker()->isSkipWaitingFlagSet())))
280 activate();
281}
282
283// https://w3c.github.io/ServiceWorker/#activate
284void SWServerRegistration::activate()
285{
286 // If registration's waiting worker is null, abort these steps.
287 if (!waitingWorker())
288 return;
289
290 // If registration's active worker is not null, then:
291 if (auto* worker = activeWorker()) {
292 // Terminate registration's active worker.
293 worker->terminate();
294 // Run the Update Worker State algorithm passing registration's active worker and redundant as the arguments.
295 updateWorkerState(*worker, ServiceWorkerState::Redundant);
296 }
297 // Run the Update Registration State algorithm passing registration, "active" and registration's waiting worker as the arguments.
298 updateRegistrationState(ServiceWorkerRegistrationState::Active, waitingWorker());
299 // Run the Update Registration State algorithm passing registration, "waiting" and null as the arguments.
300 updateRegistrationState(ServiceWorkerRegistrationState::Waiting, nullptr);
301 // Run the Update Worker State algorithm passing registration's active worker and activating as the arguments.
302 updateWorkerState(*activeWorker(), ServiceWorkerState::Activating);
303 // FIXME: For each service worker client whose creation URL matches registration's scope url...
304
305 // The registration now has an active worker so we need to check if there are any ready promises that were waiting for this.
306 m_server.resolveRegistrationReadyRequests(*this);
307
308 // For each service worker client who is using registration:
309 // - Set client's active worker to registration's active worker.
310
311 // - Invoke Notify Controller Change algorithm with client as the argument.
312 notifyClientsOfControllerChange();
313
314 // FIXME: Invoke Run Service Worker algorithm with activeWorker as the argument.
315
316 // Queue a task to fire the activate event.
317 ASSERT(activeWorker());
318 m_server.fireActivateEvent(*activeWorker());
319}
320
321// https://w3c.github.io/ServiceWorker/#activate (post activate event steps).
322void SWServerRegistration::didFinishActivation(ServiceWorkerIdentifier serviceWorkerIdentifier)
323{
324 if (!activeWorker() || activeWorker()->identifier() != serviceWorkerIdentifier)
325 return;
326
327 // Run the Update Worker State algorithm passing registration's active worker and activated as the arguments.
328 updateWorkerState(*activeWorker(), ServiceWorkerState::Activated);
329}
330
331// https://w3c.github.io/ServiceWorker/#on-client-unload-algorithm
332void SWServerRegistration::handleClientUnload()
333{
334 if (hasClientsUsingRegistration())
335 return;
336 if (isUninstalling() && tryClear())
337 return;
338 tryActivate();
339}
340
341void SWServerRegistration::controlClient(ServiceWorkerClientIdentifier identifier)
342{
343 ASSERT(activeWorker());
344
345 addClientUsingRegistration(identifier);
346
347 HashSet<DocumentIdentifier> identifiers;
348 identifiers.add(identifier.contextIdentifier);
349 m_server.connection(identifier.serverConnectionIdentifier)->notifyClientsOfControllerChange(identifiers, activeWorker()->data());
350}
351
352void SWServerRegistration::setIsUninstalling(bool value)
353{
354 if (m_uninstalling == value)
355 return;
356
357 m_uninstalling = value;
358
359 if (!m_uninstalling && activeWorker()) {
360 // Registration with active worker has been resurrected, we need to check if any ready promises were waiting for this.
361 m_server.resolveRegistrationReadyRequests(*this);
362 }
363}
364
365} // namespace WebCore
366
367#endif // ENABLE(SERVICE_WORKER)
368