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 "content/browser/devtools/service_worker_devtools_agent_host.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "content/browser/devtools/devtools_renderer_channel.h"
12 #include "content/browser/devtools/devtools_session.h"
13 #include "content/browser/devtools/protocol/fetch_handler.h"
14 #include "content/browser/devtools/protocol/inspector_handler.h"
15 #include "content/browser/devtools/protocol/io_handler.h"
16 #include "content/browser/devtools/protocol/network_handler.h"
17 #include "content/browser/devtools/protocol/protocol.h"
18 #include "content/browser/devtools/protocol/schema_handler.h"
19 #include "content/browser/devtools/protocol/target_handler.h"
20 #include "content/browser/devtools/service_worker_devtools_manager.h"
21 #include "content/browser/service_worker/service_worker_context_wrapper.h"
22 #include "content/browser/service_worker/service_worker_version.h"
23 #include "content/browser/url_loader_factory_params_helper.h"
24 #include "content/public/browser/browser_task_traits.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "mojo/public/cpp/bindings/pending_remote.h"
28 #include "net/cookies/site_for_cookies.h"
29 #include "services/network/public/mojom/network_context.mojom-forward.h"
30
31 namespace content {
32
33 namespace {
34
TerminateServiceWorkerOnCoreThread(scoped_refptr<ServiceWorkerContextWrapper> context,int64_t version_id)35 void TerminateServiceWorkerOnCoreThread(
36 scoped_refptr<ServiceWorkerContextWrapper> context,
37 int64_t version_id) {
38 if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
39 version->StopWorker(base::DoNothing());
40 }
41
SetDevToolsAttachedOnCoreThread(scoped_refptr<ServiceWorkerContextWrapper> context,int64_t version_id,bool attached)42 void SetDevToolsAttachedOnCoreThread(
43 scoped_refptr<ServiceWorkerContextWrapper> context,
44 int64_t version_id,
45 bool attached) {
46 if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
47 version->SetDevToolsAttached(attached);
48 }
49
UpdateLoaderFactoriesOnCoreThread(scoped_refptr<ServiceWorkerContextWrapper> context,int64_t version_id,std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresource_bundle)50 void UpdateLoaderFactoriesOnCoreThread(
51 scoped_refptr<ServiceWorkerContextWrapper> context,
52 int64_t version_id,
53 std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,
54 std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresource_bundle) {
55 auto* version = context->GetLiveVersion(version_id);
56 if (!version)
57 return;
58 version->embedded_worker()->UpdateLoaderFactories(
59 std::move(script_bundle), std::move(subresource_bundle));
60 }
61
62 } // namespace
63
ServiceWorkerDevToolsAgentHost(int worker_process_id,int worker_route_id,scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,int64_t version_id,const GURL & url,const GURL & scope,bool is_installed_version,base::Optional<network::CrossOriginEmbedderPolicy> cross_origin_embedder_policy,mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter,const base::UnguessableToken & devtools_worker_token)64 ServiceWorkerDevToolsAgentHost::ServiceWorkerDevToolsAgentHost(
65 int worker_process_id,
66 int worker_route_id,
67 scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
68 int64_t version_id,
69 const GURL& url,
70 const GURL& scope,
71 bool is_installed_version,
72 base::Optional<network::CrossOriginEmbedderPolicy>
73 cross_origin_embedder_policy,
74 mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
75 coep_reporter,
76 const base::UnguessableToken& devtools_worker_token)
77 : DevToolsAgentHostImpl(devtools_worker_token.ToString()),
78 state_(WORKER_NOT_READY),
79 devtools_worker_token_(devtools_worker_token),
80 worker_process_id_(worker_process_id),
81 worker_route_id_(worker_route_id),
82 context_wrapper_(context_wrapper),
83 version_id_(version_id),
84 url_(url),
85 scope_(scope),
86 version_installed_time_(is_installed_version ? base::Time::Now()
87 : base::Time()),
88 cross_origin_embedder_policy_(std::move(cross_origin_embedder_policy)),
89 coep_reporter_(std::move(coep_reporter)) {
90 NotifyCreated();
91 }
92
GetBrowserContext()93 BrowserContext* ServiceWorkerDevToolsAgentHost::GetBrowserContext() {
94 return context_wrapper_->browser_context();
95 }
96
GetType()97 std::string ServiceWorkerDevToolsAgentHost::GetType() {
98 return kTypeServiceWorker;
99 }
100
GetTitle()101 std::string ServiceWorkerDevToolsAgentHost::GetTitle() {
102 return "Service Worker " + url_.spec();
103 }
104
GetURL()105 GURL ServiceWorkerDevToolsAgentHost::GetURL() {
106 return url_;
107 }
108
Activate()109 bool ServiceWorkerDevToolsAgentHost::Activate() {
110 return false;
111 }
112
Reload()113 void ServiceWorkerDevToolsAgentHost::Reload() {
114 }
115
Close()116 bool ServiceWorkerDevToolsAgentHost::Close() {
117 RunOrPostTaskOnThread(FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
118 base::BindOnce(&TerminateServiceWorkerOnCoreThread,
119 context_wrapper_, version_id_));
120 return true;
121 }
122
WorkerVersionInstalled()123 void ServiceWorkerDevToolsAgentHost::WorkerVersionInstalled() {
124 version_installed_time_ = base::Time::Now();
125 }
126
WorkerVersionDoomed()127 void ServiceWorkerDevToolsAgentHost::WorkerVersionDoomed() {
128 version_doomed_time_ = base::Time::Now();
129 }
130
~ServiceWorkerDevToolsAgentHost()131 ServiceWorkerDevToolsAgentHost::~ServiceWorkerDevToolsAgentHost() {
132 ServiceWorkerDevToolsManager::GetInstance()->AgentHostDestroyed(this);
133 }
134
AttachSession(DevToolsSession * session,bool acquire_wake_lock)135 bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session,
136 bool acquire_wake_lock) {
137 session->AddHandler(std::make_unique<protocol::IOHandler>(GetIOContext()));
138 session->AddHandler(std::make_unique<protocol::InspectorHandler>());
139 session->AddHandler(std::make_unique<protocol::NetworkHandler>(
140 GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing()));
141 session->AddHandler(std::make_unique<protocol::FetchHandler>(
142 GetIOContext(),
143 base::BindRepeating(
144 &ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories,
145 base::Unretained(this))));
146 session->AddHandler(std::make_unique<protocol::SchemaHandler>());
147 session->AddHandler(std::make_unique<protocol::TargetHandler>(
148 protocol::TargetHandler::AccessMode::kAutoAttachOnly, GetId(),
149 GetRendererChannel(), session->GetRootSession()));
150 if (state_ == WORKER_READY && sessions().empty())
151 UpdateIsAttached(true);
152 return true;
153 }
154
DetachSession(DevToolsSession * session)155 void ServiceWorkerDevToolsAgentHost::DetachSession(DevToolsSession* session) {
156 // Destroying session automatically detaches in renderer.
157 if (state_ == WORKER_READY && sessions().empty())
158 UpdateIsAttached(false);
159 }
160
WorkerReadyForInspection(mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver)161 void ServiceWorkerDevToolsAgentHost::WorkerReadyForInspection(
162 mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
163 mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver) {
164 DCHECK_EQ(WORKER_NOT_READY, state_);
165 state_ = WORKER_READY;
166 GetRendererChannel()->SetRenderer(
167 std::move(agent_remote), std::move(host_receiver), worker_process_id_);
168 for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
169 inspector->TargetReloadedAfterCrash();
170 if (!sessions().empty())
171 UpdateIsAttached(true);
172 }
173
UpdateCrossOriginEmbedderPolicy(network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter)174 void ServiceWorkerDevToolsAgentHost::UpdateCrossOriginEmbedderPolicy(
175 network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
176 mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
177 coep_reporter) {
178 cross_origin_embedder_policy_ = std::move(cross_origin_embedder_policy);
179 coep_reporter_.Bind(std::move(coep_reporter));
180 }
181
WorkerRestarted(int worker_process_id,int worker_route_id)182 void ServiceWorkerDevToolsAgentHost::WorkerRestarted(int worker_process_id,
183 int worker_route_id) {
184 DCHECK_EQ(WORKER_TERMINATED, state_);
185 state_ = WORKER_NOT_READY;
186 worker_process_id_ = worker_process_id;
187 worker_route_id_ = worker_route_id;
188 }
189
WorkerStopped()190 void ServiceWorkerDevToolsAgentHost::WorkerStopped() {
191 DCHECK_NE(WORKER_TERMINATED, state_);
192 state_ = WORKER_TERMINATED;
193 for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
194 inspector->TargetCrashed();
195 GetRendererChannel()->SetRenderer(mojo::NullRemote(), mojo::NullReceiver(),
196 ChildProcessHost::kInvalidUniqueID);
197 if (!sessions().empty())
198 UpdateIsAttached(false);
199 }
200
UpdateIsAttached(bool attached)201 void ServiceWorkerDevToolsAgentHost::UpdateIsAttached(bool attached) {
202 RunOrPostTaskOnThread(
203 FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
204 base::BindOnce(&SetDevToolsAttachedOnCoreThread, context_wrapper_,
205 version_id_, attached));
206 }
207
UpdateLoaderFactories(base::OnceClosure callback)208 void ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories(
209 base::OnceClosure callback) {
210 RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id_);
211 if (!rph) {
212 std::move(callback).Run();
213 return;
214 }
215 const url::Origin origin = url::Origin::Create(url_);
216
217 mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
218 coep_reporter_for_script_loader;
219 mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
220 coep_reporter_for_subresource_loader;
221 if (coep_reporter_) {
222 coep_reporter_->Clone(
223 coep_reporter_for_script_loader.InitWithNewPipeAndPassReceiver());
224 coep_reporter_->Clone(
225 coep_reporter_for_subresource_loader.InitWithNewPipeAndPassReceiver());
226 }
227 // Use the default CrossOriginEmbedderPolicy if
228 // |cross_origin_embedder_policy_| is nullopt. It's acceptable because the
229 // factory bundles are updated with correct COEP value before any subresource
230 // requests in that case.
231 auto script_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
232 rph, worker_route_id_, origin,
233 cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
234 : network::CrossOriginEmbedderPolicy(),
235 std::move(coep_reporter_for_script_loader),
236 ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
237 auto subresource_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
238 rph, worker_route_id_, origin,
239 cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
240 : network::CrossOriginEmbedderPolicy(),
241 std::move(coep_reporter_for_subresource_loader),
242 ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
243
244 if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
245 UpdateLoaderFactoriesOnCoreThread(context_wrapper_, version_id_,
246 std::move(script_bundle),
247 std::move(subresource_bundle));
248 std::move(callback).Run();
249 } else {
250 GetIOThreadTaskRunner({})->PostTaskAndReply(
251 FROM_HERE,
252 base::BindOnce(&UpdateLoaderFactoriesOnCoreThread, context_wrapper_,
253 version_id_, std::move(script_bundle),
254 std::move(subresource_bundle)),
255 std::move(callback));
256 }
257 }
258
259 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo
CreateNetworkFactoryParamsForDevTools()260 ServiceWorkerDevToolsAgentHost::CreateNetworkFactoryParamsForDevTools() {
261 RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id_);
262 const url::Origin origin = url::Origin::Create(url_);
263 auto factory = URLLoaderFactoryParamsHelper::CreateForWorker(
264 rph, origin,
265 net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
266 origin, origin,
267 net::SiteForCookies::FromOrigin(origin)),
268 /*coep_reporter=*/mojo::NullRemote());
269 return {url::Origin::Create(GetURL()), net::SiteForCookies::FromUrl(GetURL()),
270 std::move(factory)};
271 }
272
GetProcessHost()273 RenderProcessHost* ServiceWorkerDevToolsAgentHost::GetProcessHost() {
274 return RenderProcessHost::FromID(worker_process_id_);
275 }
276
277 } // namespace content
278