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