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