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