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