1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
6
7 #include <utility>
8
9 #include "base/memory/ptr_util.h"
10 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
11 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
12 #include "third_party/blink/public/common/security_context/insecure_request_policy.h"
13 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom-blink.h"
14 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
15 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
16 #include "third_party/blink/renderer/bindings/modules/v8/v8_navigation_preload_state.h"
17 #include "third_party/blink/renderer/core/dom/dom_exception.h"
18 #include "third_party/blink/renderer/core/dom/events/event.h"
19 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
20 #include "third_party/blink/renderer/modules/event_target_modules.h"
21 #include "third_party/blink/renderer/modules/service_worker/service_worker_container.h"
22 #include "third_party/blink/renderer/modules/service_worker/service_worker_error.h"
23 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
24 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
25 #include "third_party/blink/renderer/platform/bindings/script_state.h"
26 #include "third_party/blink/renderer/platform/heap/heap.h"
27 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
28 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
29 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
30
31 namespace blink {
32
33 namespace {
34
DidUpdate(ScriptPromiseResolver * resolver,ServiceWorkerRegistration * registration,mojom::ServiceWorkerErrorType error,const String & error_msg)35 void DidUpdate(ScriptPromiseResolver* resolver,
36 ServiceWorkerRegistration* registration,
37 mojom::ServiceWorkerErrorType error,
38 const String& error_msg) {
39 if (!resolver->GetExecutionContext() ||
40 resolver->GetExecutionContext()->IsContextDestroyed()) {
41 return;
42 }
43
44 if (error != mojom::ServiceWorkerErrorType::kNone) {
45 DCHECK(!error_msg.IsNull());
46 ScriptState::Scope scope(resolver->GetScriptState());
47 resolver->Reject(ServiceWorkerErrorForUpdate::Take(
48 resolver, WebServiceWorkerError(error, error_msg)));
49 return;
50 }
51 resolver->Resolve(registration);
52 }
53
DidUnregister(ScriptPromiseResolver * resolver,mojom::ServiceWorkerErrorType error,const String & error_msg)54 void DidUnregister(ScriptPromiseResolver* resolver,
55 mojom::ServiceWorkerErrorType error,
56 const String& error_msg) {
57 if (!resolver->GetExecutionContext() ||
58 resolver->GetExecutionContext()->IsContextDestroyed()) {
59 return;
60 }
61
62 if (error != mojom::ServiceWorkerErrorType::kNone &&
63 error != mojom::ServiceWorkerErrorType::kNotFound) {
64 DCHECK(!error_msg.IsNull());
65 resolver->Reject(
66 ServiceWorkerError::GetException(resolver, error, error_msg));
67 return;
68 }
69 resolver->Resolve(error == mojom::ServiceWorkerErrorType::kNone);
70 }
71
DidEnableNavigationPreload(ScriptPromiseResolver * resolver,mojom::ServiceWorkerErrorType error,const String & error_msg)72 void DidEnableNavigationPreload(ScriptPromiseResolver* resolver,
73 mojom::ServiceWorkerErrorType error,
74 const String& error_msg) {
75 if (!resolver->GetExecutionContext() ||
76 resolver->GetExecutionContext()->IsContextDestroyed()) {
77 return;
78 }
79
80 if (error != mojom::ServiceWorkerErrorType::kNone) {
81 DCHECK(!error_msg.IsNull());
82 resolver->Reject(
83 ServiceWorkerError::GetException(resolver, error, error_msg));
84 return;
85 }
86 resolver->Resolve();
87 }
88
DidGetNavigationPreloadState(ScriptPromiseResolver * resolver,mojom::ServiceWorkerErrorType error,const String & error_msg,mojom::blink::NavigationPreloadStatePtr state)89 void DidGetNavigationPreloadState(
90 ScriptPromiseResolver* resolver,
91 mojom::ServiceWorkerErrorType error,
92 const String& error_msg,
93 mojom::blink::NavigationPreloadStatePtr state) {
94 if (!resolver->GetExecutionContext() ||
95 resolver->GetExecutionContext()->IsContextDestroyed()) {
96 return;
97 }
98
99 if (error != mojom::ServiceWorkerErrorType::kNone) {
100 DCHECK(!error_msg.IsNull());
101 resolver->Reject(
102 ServiceWorkerError::GetException(resolver, error, error_msg));
103 return;
104 }
105 NavigationPreloadState* dict = NavigationPreloadState::Create();
106 dict->setEnabled(state->enabled);
107 dict->setHeaderValue(state->header);
108 resolver->Resolve(dict);
109 }
110
DidSetNavigationPreloadHeader(ScriptPromiseResolver * resolver,mojom::ServiceWorkerErrorType error,const String & error_msg)111 void DidSetNavigationPreloadHeader(ScriptPromiseResolver* resolver,
112 mojom::ServiceWorkerErrorType error,
113 const String& error_msg) {
114 if (!resolver->GetExecutionContext() ||
115 resolver->GetExecutionContext()->IsContextDestroyed()) {
116 return;
117 }
118
119 if (error != mojom::ServiceWorkerErrorType::kNone) {
120 DCHECK(!error_msg.IsNull());
121 resolver->Reject(
122 ServiceWorkerError::GetException(resolver, error, error_msg));
123 return;
124 }
125 resolver->Resolve();
126 }
127
128 } // namespace
129
Take(ScriptPromiseResolver * resolver,WebServiceWorkerRegistrationObjectInfo info)130 ServiceWorkerRegistration* ServiceWorkerRegistration::Take(
131 ScriptPromiseResolver* resolver,
132 WebServiceWorkerRegistrationObjectInfo info) {
133 return ServiceWorkerContainer::From(
134 Document::From(resolver->GetExecutionContext()))
135 ->GetOrCreateServiceWorkerRegistration(std::move(info));
136 }
137
ServiceWorkerRegistration(ExecutionContext * execution_context,WebServiceWorkerRegistrationObjectInfo info)138 ServiceWorkerRegistration::ServiceWorkerRegistration(
139 ExecutionContext* execution_context,
140 WebServiceWorkerRegistrationObjectInfo info)
141 : ExecutionContextLifecycleObserver(execution_context),
142 registration_id_(info.registration_id),
143 scope_(std::move(info.scope)),
144 stopped_(false) {
145 DCHECK_NE(mojom::blink::kInvalidServiceWorkerRegistrationId,
146 registration_id_);
147 Attach(std::move(info));
148 }
149
ServiceWorkerRegistration(ExecutionContext * execution_context,mojom::blink::ServiceWorkerRegistrationObjectInfoPtr info)150 ServiceWorkerRegistration::ServiceWorkerRegistration(
151 ExecutionContext* execution_context,
152 mojom::blink::ServiceWorkerRegistrationObjectInfoPtr info)
153 : ExecutionContextLifecycleObserver(execution_context),
154 registration_id_(info->registration_id),
155 scope_(std::move(info->scope)),
156 stopped_(false) {
157 DCHECK_NE(mojom::blink::kInvalidServiceWorkerRegistrationId,
158 registration_id_);
159
160 host_.Bind(
161 std::move(info->host_remote),
162 GetExecutionContext()->GetTaskRunner(blink::TaskType::kInternalDefault));
163 // The host expects us to use |info.receiver| so bind to it.
164 receiver_.Bind(
165 std::move(info->receiver),
166 GetExecutionContext()->GetTaskRunner(blink::TaskType::kInternalDefault));
167
168 update_via_cache_ = info->update_via_cache;
169 installing_ =
170 ServiceWorker::From(GetExecutionContext(), std::move(info->installing));
171 waiting_ =
172 ServiceWorker::From(GetExecutionContext(), std::move(info->waiting));
173 active_ = ServiceWorker::From(GetExecutionContext(), std::move(info->active));
174 }
175
Attach(WebServiceWorkerRegistrationObjectInfo info)176 void ServiceWorkerRegistration::Attach(
177 WebServiceWorkerRegistrationObjectInfo info) {
178 DCHECK_EQ(registration_id_, info.registration_id);
179 DCHECK_EQ(scope_.GetString(), WTF::String(info.scope.GetString()));
180
181 // If |host_| is bound, it already points to the same object host as
182 // |info.host_remote|, so there is no need to bind again.
183 if (!host_) {
184 host_.Bind(mojo::PendingAssociatedRemote<
185 mojom::blink::ServiceWorkerRegistrationObjectHost>(
186 std::move(info.host_remote),
187 mojom::blink::ServiceWorkerRegistrationObjectHost::Version_),
188 GetExecutionContext()->GetTaskRunner(
189 blink::TaskType::kInternalDefault));
190 }
191 // The host expects us to use |info.receiver| so bind to it.
192 receiver_.reset();
193 receiver_.Bind(
194 mojo::PendingAssociatedReceiver<
195 mojom::blink::ServiceWorkerRegistrationObject>(
196 std::move(info.receiver)),
197 GetExecutionContext()->GetTaskRunner(blink::TaskType::kInternalDefault));
198
199 update_via_cache_ = info.update_via_cache;
200 installing_ =
201 ServiceWorker::From(GetExecutionContext(), std::move(info.installing));
202 waiting_ =
203 ServiceWorker::From(GetExecutionContext(), std::move(info.waiting));
204 active_ = ServiceWorker::From(GetExecutionContext(), std::move(info.active));
205 }
206
HasPendingActivity() const207 bool ServiceWorkerRegistration::HasPendingActivity() const {
208 return !stopped_;
209 }
210
InterfaceName() const211 const AtomicString& ServiceWorkerRegistration::InterfaceName() const {
212 return event_target_names::kServiceWorkerRegistration;
213 }
214
navigationPreload()215 NavigationPreloadManager* ServiceWorkerRegistration::navigationPreload() {
216 if (!navigation_preload_)
217 navigation_preload_ = MakeGarbageCollected<NavigationPreloadManager>(this);
218 return navigation_preload_;
219 }
220
scope() const221 String ServiceWorkerRegistration::scope() const {
222 return scope_.GetString();
223 }
224
updateViaCache() const225 String ServiceWorkerRegistration::updateViaCache() const {
226 switch (update_via_cache_) {
227 case mojom::ServiceWorkerUpdateViaCache::kImports:
228 return "imports";
229 case mojom::ServiceWorkerUpdateViaCache::kAll:
230 return "all";
231 case mojom::ServiceWorkerUpdateViaCache::kNone:
232 return "none";
233 }
234 NOTREACHED();
235 return "";
236 }
237
EnableNavigationPreload(bool enable,ScriptPromiseResolver * resolver)238 void ServiceWorkerRegistration::EnableNavigationPreload(
239 bool enable,
240 ScriptPromiseResolver* resolver) {
241 host_->EnableNavigationPreload(
242 enable, WTF::Bind(&DidEnableNavigationPreload, WrapPersistent(resolver)));
243 }
244
GetNavigationPreloadState(ScriptPromiseResolver * resolver)245 void ServiceWorkerRegistration::GetNavigationPreloadState(
246 ScriptPromiseResolver* resolver) {
247 host_->GetNavigationPreloadState(
248 WTF::Bind(&DidGetNavigationPreloadState, WrapPersistent(resolver)));
249 }
250
SetNavigationPreloadHeader(const String & value,ScriptPromiseResolver * resolver)251 void ServiceWorkerRegistration::SetNavigationPreloadHeader(
252 const String& value,
253 ScriptPromiseResolver* resolver) {
254 host_->SetNavigationPreloadHeader(
255 value,
256 WTF::Bind(&DidSetNavigationPreloadHeader, WrapPersistent(resolver)));
257 }
258
update(ScriptState * script_state,ExceptionState & exception_state)259 ScriptPromise ServiceWorkerRegistration::update(
260 ScriptState* script_state,
261 ExceptionState& exception_state) {
262 if (!GetExecutionContext()) {
263 exception_state.ThrowDOMException(
264 DOMExceptionCode::kInvalidStateError,
265 "Failed to update a ServiceWorkerRegistration: No associated provider "
266 "is available.");
267 return ScriptPromise();
268 }
269
270 // The fetcher is lazily loaded in a worker global scope.
271 auto* execution_context = ExecutionContext::From(script_state);
272 if (auto* global_scope = DynamicTo<WorkerGlobalScope>(execution_context))
273 global_scope->EnsureFetcher();
274
275 const FetchClientSettingsObject& settings_object =
276 execution_context->Fetcher()
277 ->GetProperties()
278 .GetFetchClientSettingsObject();
279 auto mojom_settings_object = mojom::blink::FetchClientSettingsObject::New(
280 settings_object.GetReferrerPolicy(),
281 KURL(settings_object.GetOutgoingReferrer()),
282 (settings_object.GetInsecureRequestsPolicy() &
283 mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) !=
284 mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone
285 ? blink::mojom::InsecureRequestsPolicy::kUpgrade
286 : blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade);
287
288 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
289 host_->Update(
290 std::move(mojom_settings_object),
291 WTF::Bind(&DidUpdate, WrapPersistent(resolver), WrapPersistent(this)));
292 return resolver->Promise();
293 }
294
unregister(ScriptState * script_state,ExceptionState & exception_state)295 ScriptPromise ServiceWorkerRegistration::unregister(
296 ScriptState* script_state,
297 ExceptionState& exception_state) {
298 if (!GetExecutionContext()) {
299 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
300 "Failed to unregister a "
301 "ServiceWorkerRegistration: No "
302 "associated provider is available.");
303 return ScriptPromise();
304 }
305 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
306 host_->Unregister(WTF::Bind(&DidUnregister, WrapPersistent(resolver)));
307 return resolver->Promise();
308 }
309
310 ServiceWorkerRegistration::~ServiceWorkerRegistration() = default;
311
Dispose()312 void ServiceWorkerRegistration::Dispose() {
313 host_.reset();
314 receiver_.reset();
315 }
316
Trace(Visitor * visitor)317 void ServiceWorkerRegistration::Trace(Visitor* visitor) {
318 visitor->Trace(installing_);
319 visitor->Trace(waiting_);
320 visitor->Trace(active_);
321 visitor->Trace(navigation_preload_);
322 EventTargetWithInlineData::Trace(visitor);
323 ExecutionContextLifecycleObserver::Trace(visitor);
324 Supplementable<ServiceWorkerRegistration>::Trace(visitor);
325 }
326
ContextDestroyed()327 void ServiceWorkerRegistration::ContextDestroyed() {
328 if (stopped_)
329 return;
330 stopped_ = true;
331 }
332
SetServiceWorkerObjects(mojom::blink::ChangedServiceWorkerObjectsMaskPtr changed_mask,mojom::blink::ServiceWorkerObjectInfoPtr installing,mojom::blink::ServiceWorkerObjectInfoPtr waiting,mojom::blink::ServiceWorkerObjectInfoPtr active)333 void ServiceWorkerRegistration::SetServiceWorkerObjects(
334 mojom::blink::ChangedServiceWorkerObjectsMaskPtr changed_mask,
335 mojom::blink::ServiceWorkerObjectInfoPtr installing,
336 mojom::blink::ServiceWorkerObjectInfoPtr waiting,
337 mojom::blink::ServiceWorkerObjectInfoPtr active) {
338 if (!GetExecutionContext())
339 return;
340
341 DCHECK(changed_mask->installing || !installing);
342 if (changed_mask->installing) {
343 installing_ =
344 ServiceWorker::From(GetExecutionContext(), std::move(installing));
345 }
346 DCHECK(changed_mask->waiting || !waiting);
347 if (changed_mask->waiting) {
348 waiting_ = ServiceWorker::From(GetExecutionContext(), std::move(waiting));
349 }
350 DCHECK(changed_mask->active || !active);
351 if (changed_mask->active) {
352 active_ = ServiceWorker::From(GetExecutionContext(), std::move(active));
353 }
354 }
355
SetUpdateViaCache(mojom::blink::ServiceWorkerUpdateViaCache update_via_cache)356 void ServiceWorkerRegistration::SetUpdateViaCache(
357 mojom::blink::ServiceWorkerUpdateViaCache update_via_cache) {
358 update_via_cache_ = update_via_cache;
359 }
360
UpdateFound()361 void ServiceWorkerRegistration::UpdateFound() {
362 DispatchEvent(*Event::Create(event_type_names::kUpdatefound));
363 }
364
365 } // namespace blink
366