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