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_window_client.h"
6 
7 #include "base/memory/scoped_refptr.h"
8 #include "third_party/blink/public/platform/web_string.h"
9 #include "third_party/blink/renderer/bindings/core/v8/callback_promise_adapter.h"
10 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
11 #include "third_party/blink/renderer/core/dom/dom_exception.h"
12 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
13 #include "third_party/blink/renderer/core/messaging/message_port.h"
14 #include "third_party/blink/renderer/core/page/page_hidden_state.h"
15 #include "third_party/blink/renderer/core/workers/worker_location.h"
16 #include "third_party/blink/renderer/modules/service_worker/service_worker_error.h"
17 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
18 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
19 #include "third_party/blink/renderer/platform/heap/heap.h"
20 
21 namespace blink {
22 
23 namespace {
24 
DidFocus(ScriptPromiseResolver * resolver,mojom::blink::ServiceWorkerClientInfoPtr client)25 void DidFocus(ScriptPromiseResolver* resolver,
26               mojom::blink::ServiceWorkerClientInfoPtr client) {
27   if (!resolver->GetExecutionContext() ||
28       resolver->GetExecutionContext()->IsContextDestroyed()) {
29     return;
30   }
31 
32   if (!client) {
33     resolver->Reject(ServiceWorkerError::GetException(
34         resolver, mojom::blink::ServiceWorkerErrorType::kNotFound,
35         "The client was not found."));
36     return;
37   }
38   resolver->Resolve(MakeGarbageCollected<ServiceWorkerWindowClient>(*client));
39 }
40 
DidNavigateOrOpenWindow(ScriptPromiseResolver * resolver,bool success,mojom::blink::ServiceWorkerClientInfoPtr info,const String & error_msg)41 void DidNavigateOrOpenWindow(ScriptPromiseResolver* resolver,
42                              bool success,
43                              mojom::blink::ServiceWorkerClientInfoPtr info,
44                              const String& error_msg) {
45   if (!resolver->GetExecutionContext() ||
46       resolver->GetExecutionContext()->IsContextDestroyed()) {
47     return;
48   }
49 
50   if (!success) {
51     DCHECK(!info);
52     DCHECK(!error_msg.IsNull());
53     ScriptState::Scope scope(resolver->GetScriptState());
54     resolver->Reject(V8ThrowException::CreateTypeError(
55         resolver->GetScriptState()->GetIsolate(), error_msg));
56     return;
57   }
58   ServiceWorkerWindowClient* window_client = nullptr;
59   // Even if the open/navigation succeeded, |info| may be null if information of
60   // the opened/navigated window could not be obtained (this can happen for a
61   // cross-origin window, or if the browser process could not get the
62   // information in time before the window was closed).
63   if (info)
64     window_client = MakeGarbageCollected<ServiceWorkerWindowClient>(*info);
65   resolver->Resolve(window_client);
66 }
67 
68 }  // namespace
69 
70 // static
71 ServiceWorkerWindowClient::ResolveWindowClientCallback
CreateResolveWindowClientCallback(ScriptPromiseResolver * resolver)72 ServiceWorkerWindowClient::CreateResolveWindowClientCallback(
73     ScriptPromiseResolver* resolver) {
74   return WTF::Bind(&DidNavigateOrOpenWindow, WrapPersistent(resolver));
75 }
76 
ServiceWorkerWindowClient(const mojom::blink::ServiceWorkerClientInfo & info)77 ServiceWorkerWindowClient::ServiceWorkerWindowClient(
78     const mojom::blink::ServiceWorkerClientInfo& info)
79     : ServiceWorkerClient(info),
80       page_hidden_(info.page_hidden),
81       is_focused_(info.is_focused) {
82   DCHECK_EQ(mojom::blink::ServiceWorkerClientType::kWindow, info.client_type);
83 }
84 
85 ServiceWorkerWindowClient::~ServiceWorkerWindowClient() = default;
86 
visibilityState() const87 String ServiceWorkerWindowClient::visibilityState() const {
88   return PageHiddenStateString(page_hidden_);
89 }
90 
focus(ScriptState * script_state)91 ScriptPromise ServiceWorkerWindowClient::focus(ScriptState* script_state) {
92   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
93   ScriptPromise promise = resolver->Promise();
94   ServiceWorkerGlobalScope* global_scope =
95       To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
96 
97   if (!global_scope->IsWindowInteractionAllowed()) {
98     resolver->Reject(MakeGarbageCollected<DOMException>(
99         DOMExceptionCode::kInvalidAccessError,
100         "Not allowed to focus a window."));
101     return promise;
102   }
103   global_scope->ConsumeWindowInteraction();
104 
105   global_scope->GetServiceWorkerHost()->FocusClient(
106       Uuid(), WTF::Bind(&DidFocus, WrapPersistent(resolver)));
107   return promise;
108 }
109 
navigate(ScriptState * script_state,const String & url)110 ScriptPromise ServiceWorkerWindowClient::navigate(ScriptState* script_state,
111                                                   const String& url) {
112   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
113   ScriptPromise promise = resolver->Promise();
114   ServiceWorkerGlobalScope* global_scope =
115       To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
116 
117   KURL parsed_url = KURL(global_scope->location()->Url(), url);
118   if (!parsed_url.IsValid() || parsed_url.ProtocolIsAbout()) {
119     resolver->Reject(V8ThrowException::CreateTypeError(
120         script_state->GetIsolate(), "'" + url + "' is not a valid URL."));
121     return promise;
122   }
123   if (!global_scope->GetSecurityOrigin()->CanDisplay(parsed_url)) {
124     resolver->Reject(V8ThrowException::CreateTypeError(
125         script_state->GetIsolate(),
126         "'" + parsed_url.ElidedString() + "' cannot navigate."));
127     return promise;
128   }
129 
130   global_scope->GetServiceWorkerHost()->NavigateClient(
131       Uuid(), parsed_url, CreateResolveWindowClientCallback(resolver));
132   return promise;
133 }
134 
Trace(Visitor * visitor)135 void ServiceWorkerWindowClient::Trace(Visitor* visitor) {
136   ServiceWorkerClient::Trace(visitor);
137 }
138 
139 }  // namespace blink
140