1 // Copyright 2019 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/background_sync/periodic_sync_manager.h"
6 
7 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
8 #include "third_party/blink/public/platform/platform.h"
9 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
10 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
11 #include "third_party/blink/renderer/bindings/modules/v8/v8_background_sync_options.h"
12 #include "third_party/blink/renderer/core/dom/dom_exception.h"
13 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
14 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
15 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
16 
17 namespace blink {
18 
PeriodicSyncManager(ServiceWorkerRegistration * registration,scoped_refptr<base::SequencedTaskRunner> task_runner)19 PeriodicSyncManager::PeriodicSyncManager(
20     ServiceWorkerRegistration* registration,
21     scoped_refptr<base::SequencedTaskRunner> task_runner)
22     : registration_(registration), task_runner_(std::move(task_runner)) {
23   DCHECK(registration_);
24 }
25 
registerPeriodicSync(ScriptState * script_state,const String & tag,const BackgroundSyncOptions * options,ExceptionState & exception_state)26 ScriptPromise PeriodicSyncManager::registerPeriodicSync(
27     ScriptState* script_state,
28     const String& tag,
29     const BackgroundSyncOptions* options,
30     ExceptionState& exception_state) {
31   if (!registration_->active()) {
32     exception_state.ThrowDOMException(
33         DOMExceptionCode::kInvalidStateError,
34         "Registration failed - no active Service Worker");
35     return ScriptPromise();
36   }
37 
38   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
39   ScriptPromise promise = resolver->Promise();
40 
41   mojom::blink::SyncRegistrationOptionsPtr sync_registration =
42       mojom::blink::SyncRegistrationOptions::New(tag, options->minInterval());
43 
44   GetBackgroundSyncServiceRemote()->Register(
45       std::move(sync_registration), registration_->RegistrationId(),
46       WTF::Bind(&PeriodicSyncManager::RegisterCallback, WrapPersistent(this),
47                 WrapPersistent(resolver)));
48 
49   return promise;
50 }
51 
getTags(ScriptState * script_state)52 ScriptPromise PeriodicSyncManager::getTags(ScriptState* script_state) {
53   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
54   ScriptPromise promise = resolver->Promise();
55 
56   // Creating a Periodic Background Sync registration requires an activated
57   // service worker, so if |registration_| has not been activated yet, we can
58   // skip the Mojo roundtrip.
59   if (!registration_->active()) {
60     return ScriptPromise::Cast(script_state,
61                                v8::Array::New(script_state->GetIsolate()));
62   }
63 
64   // TODO(crbug.com/932591): Optimize this to only get the tags from the browser
65   // process instead of the registrations themselves.
66   GetBackgroundSyncServiceRemote()->GetRegistrations(
67       registration_->RegistrationId(),
68       WTF::Bind(&PeriodicSyncManager::GetRegistrationsCallback,
69                 WrapPersistent(this), WrapPersistent(resolver)));
70 
71   return promise;
72 }
73 
unregister(ScriptState * script_state,const String & tag)74 ScriptPromise PeriodicSyncManager::unregister(ScriptState* script_state,
75                                               const String& tag) {
76   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
77   ScriptPromise promise = resolver->Promise();
78 
79   // Silently succeed if there's no active service worker registration.
80   if (!registration_->active()) {
81     resolver->Resolve();
82     return promise;
83   }
84 
85   GetBackgroundSyncServiceRemote()->Unregister(
86       registration_->RegistrationId(), tag,
87       WTF::Bind(&PeriodicSyncManager::UnregisterCallback, WrapPersistent(this),
88                 WrapPersistent(resolver)));
89   return promise;
90 }
91 
92 const mojo::Remote<mojom::blink::PeriodicBackgroundSyncService>&
GetBackgroundSyncServiceRemote()93 PeriodicSyncManager::GetBackgroundSyncServiceRemote() {
94   if (!background_sync_service_.is_bound()) {
95     Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
96         background_sync_service_.BindNewPipeAndPassReceiver());
97   }
98   return background_sync_service_;
99 }
100 
RegisterCallback(ScriptPromiseResolver * resolver,mojom::blink::BackgroundSyncError error,mojom::blink::SyncRegistrationOptionsPtr options)101 void PeriodicSyncManager::RegisterCallback(
102     ScriptPromiseResolver* resolver,
103     mojom::blink::BackgroundSyncError error,
104     mojom::blink::SyncRegistrationOptionsPtr options) {
105   switch (error) {
106     case mojom::blink::BackgroundSyncError::NONE:
107       resolver->Resolve();
108       break;
109     case mojom::blink::BackgroundSyncError::NOT_FOUND:
110       NOTREACHED();
111       break;
112     case mojom::blink::BackgroundSyncError::STORAGE:
113       resolver->Reject(MakeGarbageCollected<DOMException>(
114           DOMExceptionCode::kUnknownError, "Unknown error."));
115       break;
116     case mojom::blink::BackgroundSyncError::NOT_ALLOWED:
117       resolver->Reject(MakeGarbageCollected<DOMException>(
118           DOMExceptionCode::kInvalidAccessError,
119           "Attempted to register a sync event without a "
120           "window or registration tag too long."));
121       break;
122     case mojom::blink::BackgroundSyncError::PERMISSION_DENIED:
123       resolver->Reject(MakeGarbageCollected<DOMException>(
124           DOMExceptionCode::kNotAllowedError, "Permission denied."));
125       break;
126     case mojom::blink::BackgroundSyncError::NO_SERVICE_WORKER:
127       resolver->Reject(MakeGarbageCollected<DOMException>(
128           DOMExceptionCode::kInvalidStateError,
129           "Registration failed - no active Service Worker"));
130       break;
131   }
132 }
133 
GetRegistrationsCallback(ScriptPromiseResolver * resolver,mojom::blink::BackgroundSyncError error,WTF::Vector<mojom::blink::SyncRegistrationOptionsPtr> registrations)134 void PeriodicSyncManager::GetRegistrationsCallback(
135     ScriptPromiseResolver* resolver,
136     mojom::blink::BackgroundSyncError error,
137     WTF::Vector<mojom::blink::SyncRegistrationOptionsPtr> registrations) {
138   switch (error) {
139     case mojom::blink::BackgroundSyncError::NONE: {
140       Vector<String> tags;
141       for (const auto& registration : registrations)
142         tags.push_back(registration->tag);
143       resolver->Resolve(tags);
144       break;
145     }
146     case mojom::blink::BackgroundSyncError::NOT_FOUND:
147     case mojom::blink::BackgroundSyncError::NOT_ALLOWED:
148     case mojom::blink::BackgroundSyncError::PERMISSION_DENIED:
149       // These errors should never be returned from
150       // BackgroundSyncManager::GetPeriodicSyncRegistrations
151       NOTREACHED();
152       break;
153     case mojom::blink::BackgroundSyncError::STORAGE:
154       resolver->Reject(MakeGarbageCollected<DOMException>(
155           DOMExceptionCode::kUnknownError, "Unknown error."));
156       break;
157     case mojom::blink::BackgroundSyncError::NO_SERVICE_WORKER:
158       resolver->Reject(MakeGarbageCollected<DOMException>(
159           DOMExceptionCode::kUnknownError, "No service worker is active."));
160       break;
161   }
162 }
163 
UnregisterCallback(ScriptPromiseResolver * resolver,mojom::blink::BackgroundSyncError error)164 void PeriodicSyncManager::UnregisterCallback(
165     ScriptPromiseResolver* resolver,
166     mojom::blink::BackgroundSyncError error) {
167   switch (error) {
168     case mojom::blink::BackgroundSyncError::NONE:
169       resolver->Resolve();
170       break;
171     case mojom::blink::BackgroundSyncError::NO_SERVICE_WORKER:
172       resolver->Reject(MakeGarbageCollected<DOMException>(
173           DOMExceptionCode::kUnknownError, "No service worker is active."));
174       break;
175     case mojom::blink::BackgroundSyncError::STORAGE:
176       resolver->Reject(MakeGarbageCollected<DOMException>(
177           DOMExceptionCode::kUnknownError, "Unknown error."));
178       break;
179     case mojom::blink::BackgroundSyncError::NOT_FOUND:
180     case mojom::blink::BackgroundSyncError::NOT_ALLOWED:
181     case mojom::BackgroundSyncError::PERMISSION_DENIED:
182       NOTREACHED();
183       break;
184   }
185 }
186 
Trace(Visitor * visitor)187 void PeriodicSyncManager::Trace(Visitor* visitor) {
188   visitor->Trace(registration_);
189   ScriptWrappable::Trace(visitor);
190 }
191 
192 }  // namespace blink
193