Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / devtools / protocol / service_worker_handler.cc
blob887300c619550cdef6dc1b6547cc74a5f5028a45
1 // Copyright 2015 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.
5 #include "content/browser/devtools/protocol/service_worker_handler.h"
7 #include "base/bind.h"
8 #include "base/containers/scoped_ptr_hash_map.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
12 #include "content/browser/devtools/service_worker_devtools_manager.h"
13 #include "content/browser/frame_host/frame_tree.h"
14 #include "content/browser/frame_host/frame_tree_node.h"
15 #include "content/browser/frame_host/render_frame_host_impl.h"
16 #include "content/browser/service_worker/service_worker_context_watcher.h"
17 #include "content/browser/service_worker/service_worker_context_wrapper.h"
18 #include "content/browser/service_worker/service_worker_version.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/devtools_agent_host.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/common/push_messaging_status.h"
26 #include "url/gurl.h"
28 // Windows headers will redefine SendMessage.
29 #ifdef SendMessage
30 #undef SendMessage
31 #endif
33 namespace content {
34 namespace devtools {
35 namespace service_worker {
37 using Response = DevToolsProtocolClient::Response;
39 namespace {
41 void ResultNoOp(bool success) {
43 void StatusNoOp(ServiceWorkerStatusCode status) {
45 void PushDeliveryNoOp(PushDeliveryStatus status) {
48 const std::string GetVersionRunningStatusString(
49 content::ServiceWorkerVersion::RunningStatus running_status) {
50 switch (running_status) {
51 case content::ServiceWorkerVersion::STOPPED:
52 return kServiceWorkerVersionRunningStatusStopped;
53 case content::ServiceWorkerVersion::STARTING:
54 return kServiceWorkerVersionRunningStatusStarting;
55 case content::ServiceWorkerVersion::RUNNING:
56 return kServiceWorkerVersionRunningStatusRunning;
57 case content::ServiceWorkerVersion::STOPPING:
58 return kServiceWorkerVersionRunningStatusStopping;
60 return "";
63 const std::string GetVersionStatusString(
64 content::ServiceWorkerVersion::Status status) {
65 switch (status) {
66 case content::ServiceWorkerVersion::NEW:
67 return kServiceWorkerVersionStatusNew;
68 case content::ServiceWorkerVersion::INSTALLING:
69 return kServiceWorkerVersionStatusInstalling;
70 case content::ServiceWorkerVersion::INSTALLED:
71 return kServiceWorkerVersionStatusInstalled;
72 case content::ServiceWorkerVersion::ACTIVATING:
73 return kServiceWorkerVersionStatusActivating;
74 case content::ServiceWorkerVersion::ACTIVATED:
75 return kServiceWorkerVersionStatusActivated;
76 case content::ServiceWorkerVersion::REDUNDANT:
77 return kServiceWorkerVersionStatusRedundant;
79 return "";
82 scoped_refptr<ServiceWorkerVersion> CreateVersionDictionaryValue(
83 const ServiceWorkerVersionInfo& version_info) {
84 scoped_refptr<ServiceWorkerVersion> version(
85 ServiceWorkerVersion::Create()
86 ->set_version_id(base::Int64ToString(version_info.version_id))
87 ->set_registration_id(
88 base::Int64ToString(version_info.registration_id))
89 ->set_script_url(version_info.script_url.spec())
90 ->set_running_status(
91 GetVersionRunningStatusString(version_info.running_status))
92 ->set_status(GetVersionStatusString(version_info.status))
93 ->set_script_last_modified(
94 version_info.script_last_modified.ToDoubleT())
95 ->set_script_response_time(
96 version_info.script_response_time.ToDoubleT()));
97 return version;
100 scoped_refptr<ServiceWorkerRegistration> CreateRegistrationDictionaryValue(
101 const ServiceWorkerRegistrationInfo& registration_info) {
102 scoped_refptr<ServiceWorkerRegistration> registration(
103 ServiceWorkerRegistration::Create()
104 ->set_registration_id(
105 base::Int64ToString(registration_info.registration_id))
106 ->set_scope_url(registration_info.pattern.spec())
107 ->set_is_deleted(registration_info.delete_flag ==
108 ServiceWorkerRegistrationInfo::IS_DELETED));
109 return registration;
112 scoped_refptr<ServiceWorkerDevToolsAgentHost> GetMatchingServiceWorker(
113 const ServiceWorkerDevToolsAgentHost::List& agent_hosts,
114 const GURL& url) {
115 scoped_refptr<ServiceWorkerDevToolsAgentHost> best_host;
116 std::string best_scope;
117 for (auto host : agent_hosts) {
118 if (host->GetURL().host() != url.host())
119 continue;
120 std::string path = host->GetURL().path();
121 std::string file = host->GetURL().ExtractFileName();
122 std::string scope = path.substr(0, path.length() - file.length());
123 if (scope.length() > best_scope.length()) {
124 best_host = host;
125 best_scope = scope;
128 return best_host;
131 ServiceWorkerDevToolsAgentHost::Map GetMatchingServiceWorkers(
132 const std::set<GURL>& urls) {
133 ServiceWorkerDevToolsAgentHost::List agent_hosts;
134 ServiceWorkerDevToolsManager::GetInstance()->
135 AddAllAgentHosts(&agent_hosts);
136 ServiceWorkerDevToolsAgentHost::Map result;
137 for (const GURL& url : urls) {
138 scoped_refptr<ServiceWorkerDevToolsAgentHost> host =
139 GetMatchingServiceWorker(agent_hosts, url);
140 if (host)
141 result[host->GetId()] = host;
143 return result;
146 bool CollectURLs(std::set<GURL>* urls, FrameTreeNode* tree_node) {
147 urls->insert(tree_node->current_url());
148 return false;
151 void StopServiceWorkerOnIO(scoped_refptr<ServiceWorkerContextWrapper> context,
152 int64 version_id) {
153 if (content::ServiceWorkerVersion* version =
154 context->GetLiveVersion(version_id)) {
155 version->StopWorker(base::Bind(&StatusNoOp));
159 void GetDevToolsRouteInfoOnIO(
160 scoped_refptr<ServiceWorkerContextWrapper> context,
161 int64 version_id,
162 const base::Callback<void(int, int)>& callback) {
163 if (content::ServiceWorkerVersion* version =
164 context->GetLiveVersion(version_id)) {
165 BrowserThread::PostTask(
166 BrowserThread::UI, FROM_HERE,
167 base::Bind(
168 callback, version->embedded_worker()->process_id(),
169 version->embedded_worker()->worker_devtools_agent_route_id()));
173 Response CreateContextErrorResponse() {
174 return Response::InternalError("Could not connect to the context");
177 Response CreateInvalidVersionIdErrorResponse() {
178 return Response::InternalError("Invalid version ID");
181 } // namespace
183 ServiceWorkerHandler::ServiceWorkerHandler()
184 : enabled_(false), weak_factory_(this) {
187 ServiceWorkerHandler::~ServiceWorkerHandler() {
188 Disable();
191 void ServiceWorkerHandler::SetRenderFrameHost(
192 RenderFrameHostImpl* render_frame_host) {
193 render_frame_host_ = render_frame_host;
194 // Do not call UpdateHosts yet, wait for load to commit.
195 if (!render_frame_host) {
196 context_ = nullptr;
197 return;
199 StoragePartition* partition = BrowserContext::GetStoragePartition(
200 render_frame_host->GetProcess()->GetBrowserContext(),
201 render_frame_host->GetSiteInstance());
202 DCHECK(partition);
203 context_ = static_cast<ServiceWorkerContextWrapper*>(
204 partition->GetServiceWorkerContext());
207 void ServiceWorkerHandler::SetClient(scoped_ptr<Client> client) {
208 client_.swap(client);
211 void ServiceWorkerHandler::UpdateHosts() {
212 if (!enabled_)
213 return;
215 urls_.clear();
216 if (render_frame_host_) {
217 render_frame_host_->frame_tree_node()->frame_tree()->ForEach(
218 base::Bind(&CollectURLs, &urls_));
221 ServiceWorkerDevToolsAgentHost::Map old_hosts = attached_hosts_;
222 ServiceWorkerDevToolsAgentHost::Map new_hosts =
223 GetMatchingServiceWorkers(urls_);
225 for (auto pair : old_hosts) {
226 if (new_hosts.find(pair.first) == new_hosts.end())
227 ReportWorkerTerminated(pair.second.get());
230 for (auto pair : new_hosts) {
231 if (old_hosts.find(pair.first) == old_hosts.end())
232 ReportWorkerCreated(pair.second.get());
236 void ServiceWorkerHandler::Detached() {
237 Disable();
240 Response ServiceWorkerHandler::Enable() {
241 if (enabled_)
242 return Response::OK();
243 if (!context_)
244 return Response::InternalError("Could not connect to the context");
245 enabled_ = true;
247 ServiceWorkerDevToolsManager::GetInstance()->AddObserver(this);
249 client_->DebugOnStartUpdated(
250 DebugOnStartUpdatedParams::Create()->set_debug_on_start(
251 ServiceWorkerDevToolsManager::GetInstance()
252 ->debug_service_worker_on_start()));
254 context_watcher_ = new ServiceWorkerContextWatcher(
255 context_, base::Bind(&ServiceWorkerHandler::OnWorkerRegistrationUpdated,
256 weak_factory_.GetWeakPtr()),
257 base::Bind(&ServiceWorkerHandler::OnWorkerVersionUpdated,
258 weak_factory_.GetWeakPtr()),
259 base::Bind(&ServiceWorkerHandler::OnErrorReported,
260 weak_factory_.GetWeakPtr()));
261 context_watcher_->Start();
263 UpdateHosts();
264 return Response::OK();
267 Response ServiceWorkerHandler::Disable() {
268 if (!enabled_)
269 return Response::OK();
270 enabled_ = false;
272 ServiceWorkerDevToolsManager::GetInstance()->RemoveObserver(this);
273 for (const auto& pair : attached_hosts_)
274 pair.second->DetachClient();
275 attached_hosts_.clear();
276 DCHECK(context_watcher_);
277 context_watcher_->Stop();
278 context_watcher_ = nullptr;
279 return Response::OK();
282 Response ServiceWorkerHandler::SendMessage(
283 const std::string& worker_id,
284 const std::string& message) {
285 auto it = attached_hosts_.find(worker_id);
286 if (it == attached_hosts_.end())
287 return Response::InternalError("Not connected to the worker");
288 it->second->DispatchProtocolMessage(message);
289 return Response::OK();
292 Response ServiceWorkerHandler::Stop(
293 const std::string& worker_id) {
294 auto it = attached_hosts_.find(worker_id);
295 if (it == attached_hosts_.end())
296 return Response::InternalError("Not connected to the worker");
297 it->second->UnregisterWorker();
298 return Response::OK();
301 Response ServiceWorkerHandler::Unregister(const std::string& scope_url) {
302 if (!enabled_)
303 return Response::OK();
304 if (!context_)
305 return CreateContextErrorResponse();
306 context_->UnregisterServiceWorker(GURL(scope_url), base::Bind(&ResultNoOp));
307 return Response::OK();
310 Response ServiceWorkerHandler::StartWorker(const std::string& scope_url) {
311 if (!enabled_)
312 return Response::OK();
313 if (!context_)
314 return CreateContextErrorResponse();
315 context_->StartServiceWorker(GURL(scope_url), base::Bind(&StatusNoOp));
316 return Response::OK();
319 Response ServiceWorkerHandler::StopWorker(const std::string& version_id) {
320 if (!enabled_)
321 return Response::OK();
322 if (!context_)
323 return CreateContextErrorResponse();
324 int64 id = 0;
325 if (!base::StringToInt64(version_id, &id))
326 return CreateInvalidVersionIdErrorResponse();
327 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
328 base::Bind(&StopServiceWorkerOnIO, context_, id));
329 return Response::OK();
332 Response ServiceWorkerHandler::UpdateRegistration(
333 const std::string& scope_url) {
334 if (!enabled_)
335 return Response::OK();
336 if (!context_)
337 return CreateContextErrorResponse();
338 context_->UpdateRegistration(GURL(scope_url));
339 return Response::OK();
342 Response ServiceWorkerHandler::InspectWorker(const std::string& version_id) {
343 if (!enabled_)
344 return Response::OK();
345 if (!context_)
346 return CreateContextErrorResponse();
348 int64 id = 0;
349 if (!base::StringToInt64(version_id, &id))
350 return CreateInvalidVersionIdErrorResponse();
351 BrowserThread::PostTask(
352 BrowserThread::IO, FROM_HERE,
353 base::Bind(&GetDevToolsRouteInfoOnIO, context_, id,
354 base::Bind(&ServiceWorkerHandler::OpenNewDevToolsWindow,
355 weak_factory_.GetWeakPtr())));
356 return Response::OK();
359 Response ServiceWorkerHandler::SkipWaiting(const std::string& version_id) {
360 if (!enabled_)
361 return Response::OK();
362 if (!context_)
363 return CreateContextErrorResponse();
365 int64 id = 0;
366 if (!base::StringToInt64(version_id, &id))
367 return CreateInvalidVersionIdErrorResponse();
368 context_->SimulateSkipWaiting(id);
369 return Response::OK();
372 Response ServiceWorkerHandler::SetDebugOnStart(bool debug_on_start) {
373 ServiceWorkerDevToolsManager::GetInstance()
374 ->set_debug_service_worker_on_start(debug_on_start);
375 return Response::OK();
378 Response ServiceWorkerHandler::DeliverPushMessage(
379 const std::string& origin,
380 const std::string& registration_id,
381 const std::string& data) {
382 if (!enabled_)
383 return Response::OK();
384 if (!render_frame_host_)
385 return CreateContextErrorResponse();
386 int64 id = 0;
387 if (!base::StringToInt64(registration_id, &id))
388 return CreateInvalidVersionIdErrorResponse();
389 BrowserContext::DeliverPushMessage(
390 render_frame_host_->GetProcess()->GetBrowserContext(), GURL(origin), id,
391 data, base::Bind(&PushDeliveryNoOp));
392 return Response::OK();
395 void ServiceWorkerHandler::OpenNewDevToolsWindow(int process_id,
396 int devtools_agent_route_id) {
397 scoped_refptr<DevToolsAgentHostImpl> agent_host(
398 ServiceWorkerDevToolsManager::GetInstance()
399 ->GetDevToolsAgentHostForWorker(process_id, devtools_agent_route_id));
400 if (!agent_host.get())
401 return;
402 agent_host->Inspect(render_frame_host_->GetProcess()->GetBrowserContext());
405 void ServiceWorkerHandler::OnWorkerRegistrationUpdated(
406 const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
407 std::vector<scoped_refptr<ServiceWorkerRegistration>> registration_values;
408 for (const auto& registration : registrations) {
409 registration_values.push_back(
410 CreateRegistrationDictionaryValue(registration));
412 client_->WorkerRegistrationUpdated(
413 WorkerRegistrationUpdatedParams::Create()->set_registrations(
414 registration_values));
417 void ServiceWorkerHandler::OnWorkerVersionUpdated(
418 const std::vector<ServiceWorkerVersionInfo>& versions) {
419 std::vector<scoped_refptr<ServiceWorkerVersion>> version_values;
420 for (const auto& version : versions) {
421 version_values.push_back(CreateVersionDictionaryValue(version));
423 client_->WorkerVersionUpdated(
424 WorkerVersionUpdatedParams::Create()->set_versions(version_values));
427 void ServiceWorkerHandler::OnErrorReported(
428 int64 registration_id,
429 int64 version_id,
430 const ServiceWorkerContextObserver::ErrorInfo& info) {
431 client_->WorkerErrorReported(
432 WorkerErrorReportedParams::Create()->set_error_message(
433 ServiceWorkerErrorMessage::Create()
434 ->set_error_message(base::UTF16ToUTF8(info.error_message))
435 ->set_registration_id(base::Int64ToString(registration_id))
436 ->set_version_id(base::Int64ToString(version_id))
437 ->set_source_url(info.source_url.spec())
438 ->set_line_number(info.line_number)
439 ->set_column_number(info.column_number)));
442 void ServiceWorkerHandler::DispatchProtocolMessage(
443 DevToolsAgentHost* host,
444 const std::string& message) {
446 auto it = attached_hosts_.find(host->GetId());
447 if (it == attached_hosts_.end())
448 return; // Already disconnected.
450 client_->DispatchMessage(
451 DispatchMessageParams::Create()->
452 set_worker_id(host->GetId())->
453 set_message(message));
456 void ServiceWorkerHandler::AgentHostClosed(
457 DevToolsAgentHost* host,
458 bool replaced_with_another_client) {
459 client_->WorkerTerminated(WorkerTerminatedParams::Create()->
460 set_worker_id(host->GetId()));
461 attached_hosts_.erase(host->GetId());
464 void ServiceWorkerHandler::WorkerCreated(
465 ServiceWorkerDevToolsAgentHost* host) {
466 auto hosts = GetMatchingServiceWorkers(urls_);
467 if (hosts.find(host->GetId()) != hosts.end() && !host->IsAttached() &&
468 !host->IsPausedForDebugOnStart())
469 host->PauseForDebugOnStart();
472 void ServiceWorkerHandler::WorkerReadyForInspection(
473 ServiceWorkerDevToolsAgentHost* host) {
474 if (ServiceWorkerDevToolsManager::GetInstance()
475 ->debug_service_worker_on_start()) {
476 // When debug_service_worker_on_start is true, a new DevTools window will
477 // be opend in ServiceWorkerDevToolsManager::WorkerReadyForInspection.
478 return;
480 UpdateHosts();
483 void ServiceWorkerHandler::WorkerDestroyed(
484 ServiceWorkerDevToolsAgentHost* host) {
485 UpdateHosts();
488 void ServiceWorkerHandler::DebugOnStartUpdated(bool debug_on_start) {
489 client_->DebugOnStartUpdated(
490 DebugOnStartUpdatedParams::Create()->set_debug_on_start(debug_on_start));
493 void ServiceWorkerHandler::ReportWorkerCreated(
494 ServiceWorkerDevToolsAgentHost* host) {
495 if (host->IsAttached())
496 return;
497 attached_hosts_[host->GetId()] = host;
498 host->AttachClient(this);
499 client_->WorkerCreated(WorkerCreatedParams::Create()->
500 set_worker_id(host->GetId())->
501 set_url(host->GetURL().spec()));
504 void ServiceWorkerHandler::ReportWorkerTerminated(
505 ServiceWorkerDevToolsAgentHost* host) {
506 auto it = attached_hosts_.find(host->GetId());
507 if (it == attached_hosts_.end())
508 return;
509 host->DetachClient();
510 client_->WorkerTerminated(WorkerTerminatedParams::Create()->
511 set_worker_id(host->GetId()));
512 attached_hosts_.erase(it);
515 } // namespace service_worker
516 } // namespace devtools
517 } // namespace content