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