Add release logging to help debug issues related to service workers
[WebKit-https.git] / Source / WebCore / workers / service / ServiceWorkerRegistration.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 "ServiceWorkerRegistration.h"
28
29 #if ENABLE(SERVICE_WORKER)
30 #include "DOMWindow.h"
31 #include "Document.h"
32 #include "Event.h"
33 #include "EventNames.h"
34 #include "Logging.h"
35 #include "ServiceWorker.h"
36 #include "ServiceWorkerContainer.h"
37 #include "ServiceWorkerTypes.h"
38 #include "WorkerGlobalScope.h"
39
40 #define REGISTRATION_RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_container->isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - ServiceWorkerRegistration::" fmt, this, ##__VA_ARGS__)
41 #define REGISTRATION_RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(m_container->isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - ServiceWorkerRegistration::" fmt, this, ##__VA_ARGS__)
42
43 namespace WebCore {
44
45 Ref<ServiceWorkerRegistration> ServiceWorkerRegistration::getOrCreate(ScriptExecutionContext& context, Ref<ServiceWorkerContainer>&& container, ServiceWorkerRegistrationData&& data)
46 {
47     if (auto* registration = container->registration(data.identifier)) {
48         ASSERT(!registration->m_isStopped);
49         return *registration;
50     }
51
52     return adoptRef(*new ServiceWorkerRegistration(context, WTFMove(container), WTFMove(data)));
53 }
54
55 ServiceWorkerRegistration::ServiceWorkerRegistration(ScriptExecutionContext& context, Ref<ServiceWorkerContainer>&& container, ServiceWorkerRegistrationData&& registrationData)
56     : ActiveDOMObject(&context)
57     , m_registrationData(WTFMove(registrationData))
58     , m_container(WTFMove(container))
59 {
60     LOG(ServiceWorker, "Creating registration %p for registration key %s", this, m_registrationData.key.loggingString().utf8().data());
61     suspendIfNeeded();
62
63     if (m_registrationData.installingWorker)
64         m_installingWorker = ServiceWorker::getOrCreate(context, WTFMove(*m_registrationData.installingWorker));
65     if (m_registrationData.waitingWorker)
66         m_waitingWorker = ServiceWorker::getOrCreate(context, WTFMove(*m_registrationData.waitingWorker));
67     if (m_registrationData.activeWorker)
68         m_activeWorker = ServiceWorker::getOrCreate(context, WTFMove(*m_registrationData.activeWorker));
69
70     REGISTRATION_RELEASE_LOG_IF_ALLOWED("ServiceWorkerRegistration: ID %llu, installing: %llu, waiting: %llu, active: %llu", identifier().toUInt64(), m_installingWorker ? m_installingWorker->identifier().toUInt64() : 0, m_waitingWorker ? m_waitingWorker->identifier().toUInt64() : 0, m_activeWorker ? m_activeWorker->identifier().toUInt64() : 0);
71
72     m_container->addRegistration(*this);
73
74     relaxAdoptionRequirement();
75     updatePendingActivityForEventDispatch();
76 }
77
78 ServiceWorkerRegistration::~ServiceWorkerRegistration()
79 {
80     LOG(ServiceWorker, "Deleting registration %p for registration key %s", this, m_registrationData.key.loggingString().utf8().data());
81
82     m_container->removeRegistration(*this);
83 }
84
85 ServiceWorker* ServiceWorkerRegistration::installing()
86 {
87     return m_installingWorker.get();
88 }
89
90 ServiceWorker* ServiceWorkerRegistration::waiting()
91 {
92     return m_waitingWorker.get();
93 }
94
95 ServiceWorker* ServiceWorkerRegistration::active()
96 {
97     return m_activeWorker.get();
98 }
99
100 ServiceWorker* ServiceWorkerRegistration::getNewestWorker()
101 {
102     if (m_installingWorker)
103         return m_installingWorker.get();
104     if (m_waitingWorker)
105         return m_waitingWorker.get();
106
107     return m_activeWorker.get();
108 }
109
110 const String& ServiceWorkerRegistration::scope() const
111 {
112     return m_registrationData.scopeURL;
113 }
114
115 ServiceWorkerUpdateViaCache ServiceWorkerRegistration::updateViaCache() const
116 {
117     return m_registrationData.updateViaCache;
118 }
119
120 WallTime ServiceWorkerRegistration::lastUpdateTime() const
121 {
122     return m_registrationData.lastUpdateTime;
123 }
124
125 void ServiceWorkerRegistration::setLastUpdateTime(WallTime lastUpdateTime)
126 {
127     m_registrationData.lastUpdateTime = lastUpdateTime;
128 }
129
130 void ServiceWorkerRegistration::setUpdateViaCache(ServiceWorkerUpdateViaCache updateViaCache)
131 {
132     m_registrationData.updateViaCache = updateViaCache;
133 }
134
135 void ServiceWorkerRegistration::update(Ref<DeferredPromise>&& promise)
136 {
137     if (m_isStopped) {
138         promise->reject(Exception(InvalidStateError));
139         return;
140     }
141
142     auto* newestWorker = getNewestWorker();
143     if (!newestWorker) {
144         promise->reject(Exception(InvalidStateError, ASCIILiteral("newestWorker is null")));
145         return;
146     }
147
148     // FIXME: Support worker types.
149     m_container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, WTFMove(promise));
150 }
151
152 void ServiceWorkerRegistration::softUpdate()
153 {
154     if (m_isStopped)
155         return;
156
157     auto* newestWorker = getNewestWorker();
158     if (!newestWorker)
159         return;
160
161     // FIXME: Support worker types.
162     m_container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, nullptr);
163 }
164
165 void ServiceWorkerRegistration::unregister(Ref<DeferredPromise>&& promise)
166 {
167     if (m_isStopped) {
168         promise->reject(Exception(InvalidStateError));
169         return;
170     }
171
172     m_container->removeRegistration(m_registrationData.scopeURL, WTFMove(promise));
173 }
174
175 void ServiceWorkerRegistration::updateStateFromServer(ServiceWorkerRegistrationState state, RefPtr<ServiceWorker>&& serviceWorker)
176 {
177     switch (state) {
178     case ServiceWorkerRegistrationState::Installing:
179         REGISTRATION_RELEASE_LOG_IF_ALLOWED("updateStateFromServer: Setting registration %llu installing worker to %llu", identifier().toUInt64(), serviceWorker ? serviceWorker->identifier().toUInt64() : 0);
180         m_installingWorker = WTFMove(serviceWorker);
181         break;
182     case ServiceWorkerRegistrationState::Waiting:
183         REGISTRATION_RELEASE_LOG_IF_ALLOWED("updateStateFromServer: Setting registration %llu waiting worker to %llu", identifier().toUInt64(), serviceWorker ? serviceWorker->identifier().toUInt64() : 0);
184         m_waitingWorker = WTFMove(serviceWorker);
185         break;
186     case ServiceWorkerRegistrationState::Active:
187         REGISTRATION_RELEASE_LOG_IF_ALLOWED("updateStateFromServer: Setting registration %llu active worker to %llu", identifier().toUInt64(), serviceWorker ? serviceWorker->identifier().toUInt64() : 0);
188         m_activeWorker = WTFMove(serviceWorker);
189         break;
190     }
191     updatePendingActivityForEventDispatch();
192 }
193
194 void ServiceWorkerRegistration::scheduleTaskToFireUpdateFoundEvent()
195 {
196     if (m_isStopped)
197         return;
198
199     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)](ScriptExecutionContext&) {
200         if (m_isStopped)
201             return;
202
203         REGISTRATION_RELEASE_LOG_IF_ALLOWED("scheduleTaskToFireUpdateFoundEvent: Firing updatefound event for registration %llu", identifier().toUInt64());
204
205         ASSERT(m_pendingActivityForEventDispatch);
206         dispatchEvent(Event::create(eventNames().updatefoundEvent, false, false));
207     });
208 }
209
210 EventTargetInterface ServiceWorkerRegistration::eventTargetInterface() const
211 {
212     return ServiceWorkerRegistrationEventTargetInterfaceType;
213 }
214
215 ScriptExecutionContext* ServiceWorkerRegistration::scriptExecutionContext() const
216 {
217     return ActiveDOMObject::scriptExecutionContext();
218 }
219
220 const char* ServiceWorkerRegistration::activeDOMObjectName() const
221 {
222     return "ServiceWorkerRegistration";
223 }
224
225 bool ServiceWorkerRegistration::canSuspendForDocumentSuspension() const
226 {
227     // FIXME: We should do better as this prevents a page from entering PageCache when there is a service worker registration.
228     return !hasPendingActivity();
229 }
230
231 void ServiceWorkerRegistration::stop()
232 {
233     m_isStopped = true;
234     removeAllEventListeners();
235     updatePendingActivityForEventDispatch();
236 }
237
238 void ServiceWorkerRegistration::updatePendingActivityForEventDispatch()
239 {
240     // If a registration has no ServiceWorker, then it has been cleared on server-side.
241     if (m_isStopped || !getNewestWorker()) {
242         m_pendingActivityForEventDispatch = nullptr;
243         return;
244     }
245     if (m_pendingActivityForEventDispatch)
246         return;
247     m_pendingActivityForEventDispatch = makePendingActivity(*this);
248 }
249
250 } // namespace WebCore
251
252 #endif // ENABLE(SERVICE_WORKER)