Support container.register() / registration.unregister() / inside 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 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 void ServiceWorkerRegistration::update(Ref<DeferredPromise>&& promise)
116 {
117     auto* context = scriptExecutionContext();
118     if (!context) {
119         promise->reject(Exception(InvalidStateError));
120         return;
121     }
122
123     auto* newestWorker = getNewestWorker();
124     if (!newestWorker) {
125         promise->reject(Exception(InvalidStateError, ASCIILiteral("newestWorker is null")));
126         return;
127     }
128
129     // FIXME: Support worker types.
130     m_container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, WTFMove(promise));
131 }
132
133 void ServiceWorkerRegistration::unregister(Ref<DeferredPromise>&& promise)
134 {
135     auto* context = scriptExecutionContext();
136     if (!context) {
137         promise->reject(Exception(InvalidStateError));
138         return;
139     }
140
141     m_container->removeRegistration(m_registrationData.scopeURL, WTFMove(promise));
142 }
143
144 void ServiceWorkerRegistration::updateStateFromServer(ServiceWorkerRegistrationState state, RefPtr<ServiceWorker>&& serviceWorker)
145 {
146     switch (state) {
147     case ServiceWorkerRegistrationState::Installing:
148         m_installingWorker = WTFMove(serviceWorker);
149         break;
150     case ServiceWorkerRegistrationState::Waiting:
151         m_waitingWorker = WTFMove(serviceWorker);
152         break;
153     case ServiceWorkerRegistrationState::Active:
154         m_activeWorker = WTFMove(serviceWorker);
155         break;
156     }
157     updatePendingActivityForEventDispatch();
158 }
159
160 void ServiceWorkerRegistration::scheduleTaskToFireUpdateFoundEvent()
161 {
162     if (m_isStopped)
163         return;
164
165     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)](ScriptExecutionContext&) {
166         if (m_isStopped)
167             return;
168
169         ASSERT(m_pendingActivityForEventDispatch);
170         dispatchEvent(Event::create(eventNames().updatefoundEvent, false, false));
171     });
172 }
173
174 EventTargetInterface ServiceWorkerRegistration::eventTargetInterface() const
175 {
176     return ServiceWorkerRegistrationEventTargetInterfaceType;
177 }
178
179 ScriptExecutionContext* ServiceWorkerRegistration::scriptExecutionContext() const
180 {
181     return ActiveDOMObject::scriptExecutionContext();
182 }
183
184 const char* ServiceWorkerRegistration::activeDOMObjectName() const
185 {
186     return "ServiceWorkerRegistration";
187 }
188
189 bool ServiceWorkerRegistration::canSuspendForDocumentSuspension() const
190 {
191     // FIXME: We should do better as this prevents a page from entering PageCache when there is a service worker registration.
192     return !hasPendingActivity();
193 }
194
195 void ServiceWorkerRegistration::stop()
196 {
197     m_isStopped = true;
198     removeAllEventListeners();
199     updatePendingActivityForEventDispatch();
200 }
201
202 void ServiceWorkerRegistration::updatePendingActivityForEventDispatch()
203 {
204     // If a registration has no ServiceWorker, then it has been cleared on server-side.
205     if (m_isStopped || !getNewestWorker()) {
206         m_pendingActivityForEventDispatch = nullptr;
207         return;
208     }
209     if (m_pendingActivityForEventDispatch)
210         return;
211     m_pendingActivityForEventDispatch = makePendingActivity(*this);
212 }
213
214 } // namespace WebCore
215
216 #endif // ENABLE(SERVICE_WORKER)