Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / devtools / protocol / service_worker_handler.cc
blob5970c245e54f8640f4974bbe8e796926b8c46772
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/browser/web_contents.h"
26 #include "content/public/common/push_messaging_status.h"
27 #include "url/gurl.h"
29 // Windows headers will redefine SendMessage.
30 #ifdef SendMessage
31 #undef SendMessage
32 #endif
34 namespace content {
35 namespace devtools {
36 namespace service_worker {
38 using Response = DevToolsProtocolClient::Response;
40 namespace {
42 void ResultNoOp(bool success) {
44 void StatusNoOp(ServiceWorkerStatusCode status) {
46 void PushDeliveryNoOp(PushDeliveryStatus status) {
49 const std::string GetVersionRunningStatusString(
50 content::ServiceWorkerVersion::RunningStatus running_status) {
51 switch (running_status) {
52 case content::ServiceWorkerVersion::STOPPED:
53 return kServiceWorkerVersionRunningStatusStopped;
54 case content::ServiceWorkerVersion::STARTING:
55 return kServiceWorkerVersionRunningStatusStarting;
56 case content::ServiceWorkerVersion::RUNNING:
57 return kServiceWorkerVersionRunningStatusRunning;
58 case content::ServiceWorkerVersion::STOPPING:
59 return kServiceWorkerVersionRunningStatusStopping;
61 return std::string();
64 const std::string GetVersionStatusString(
65 content::ServiceWorkerVersion::Status status) {
66 switch (status) {
67 case content::ServiceWorkerVersion::NEW:
68 return kServiceWorkerVersionStatusNew;
69 case content::ServiceWorkerVersion::INSTALLING:
70 return kServiceWorkerVersionStatusInstalling;
71 case content::ServiceWorkerVersion::INSTALLED:
72 return kServiceWorkerVersionStatusInstalled;
73 case content::ServiceWorkerVersion::ACTIVATING:
74 return kServiceWorkerVersionStatusActivating;
75 case content::ServiceWorkerVersion::ACTIVATED:
76 return kServiceWorkerVersionStatusActivated;
77 case content::ServiceWorkerVersion::REDUNDANT:
78 return kServiceWorkerVersionStatusRedundant;
80 return std::string();
83 scoped_refptr<ServiceWorkerVersion> CreateVersionDictionaryValue(
84 const ServiceWorkerVersionInfo& version_info) {
85 std::vector<std::string> clients;
86 for (const auto& client : version_info.clients) {
87 if (client.second.type == SERVICE_WORKER_PROVIDER_FOR_WINDOW) {
88 RenderFrameHostImpl* render_frame_host = RenderFrameHostImpl::FromID(
89 client.second.process_id, client.second.route_id);
90 WebContents* web_contents =
91 WebContents::FromRenderFrameHost(render_frame_host);
92 // There is a possibility that the frame is already deleted because of the
93 // thread hopping.
94 if (!web_contents)
95 continue;
96 scoped_refptr<DevToolsAgentHost> agent_host(
97 DevToolsAgentHost::GetOrCreateFor(web_contents));
98 if (agent_host)
99 clients.push_back(agent_host->GetId());
100 } else if (client.second.type ==
101 SERVICE_WORKER_PROVIDER_FOR_SHARED_WORKER) {
102 scoped_refptr<DevToolsAgentHost> agent_host(
103 DevToolsAgentHost::GetForWorker(client.second.process_id,
104 client.second.route_id));
105 if (agent_host)
106 clients.push_back(agent_host->GetId());
109 scoped_refptr<ServiceWorkerVersion> version(
110 ServiceWorkerVersion::Create()
111 ->set_version_id(base::Int64ToString(version_info.version_id))
112 ->set_registration_id(
113 base::Int64ToString(version_info.registration_id))
114 ->set_script_url(version_info.script_url.spec())
115 ->set_running_status(
116 GetVersionRunningStatusString(version_info.running_status))
117 ->set_status(GetVersionStatusString(version_info.status))
118 ->set_script_last_modified(
119 version_info.script_last_modified.ToDoubleT())
120 ->set_script_response_time(
121 version_info.script_response_time.ToDoubleT())
122 ->set_controlled_clients(clients));
123 return version;
126 scoped_refptr<ServiceWorkerRegistration> CreateRegistrationDictionaryValue(
127 const ServiceWorkerRegistrationInfo& registration_info) {
128 scoped_refptr<ServiceWorkerRegistration> registration(
129 ServiceWorkerRegistration::Create()
130 ->set_registration_id(
131 base::Int64ToString(registration_info.registration_id))
132 ->set_scope_url(registration_info.pattern.spec())
133 ->set_is_deleted(registration_info.delete_flag ==
134 ServiceWorkerRegistrationInfo::IS_DELETED));
135 return registration;
138 scoped_refptr<ServiceWorkerDevToolsAgentHost> GetMatchingServiceWorker(
139 const ServiceWorkerDevToolsAgentHost::List& agent_hosts,
140 const GURL& url) {
141 scoped_refptr<ServiceWorkerDevToolsAgentHost> best_host;
142 std::string best_scope;
143 for (auto host : agent_hosts) {
144 if (host->GetURL().host() != url.host())
145 continue;
146 std::string path = host->GetURL().path();
147 std::string file = host->GetURL().ExtractFileName();
148 std::string scope = path.substr(0, path.length() - file.length());
149 if (scope.length() > best_scope.length()) {
150 best_host = host;
151 best_scope = scope;
154 return best_host;
157 ServiceWorkerDevToolsAgentHost::Map GetMatchingServiceWorkers(
158 BrowserContext* browser_context,
159 const std::set<GURL>& urls) {
160 ServiceWorkerDevToolsAgentHost::Map result;
161 if (!browser_context)
162 return result;
163 ServiceWorkerDevToolsAgentHost::List agent_hosts;
164 ServiceWorkerDevToolsManager::GetInstance()
165 ->AddAllAgentHostsForBrowserContext(browser_context, &agent_hosts);
166 for (const GURL& url : urls) {
167 scoped_refptr<ServiceWorkerDevToolsAgentHost> host =
168 GetMatchingServiceWorker(agent_hosts, url);
169 if (host)
170 result[host->GetId()] = host;
172 return result;
175 bool CollectURLs(std::set<GURL>* urls, FrameTreeNode* tree_node) {
176 urls->insert(tree_node->current_url());
177 return false;
180 void StopServiceWorkerOnIO(scoped_refptr<ServiceWorkerContextWrapper> context,
181 int64 version_id) {
182 if (content::ServiceWorkerVersion* version =
183 context->GetLiveVersion(version_id)) {
184 version->StopWorker(base::Bind(&StatusNoOp));
188 void GetDevToolsRouteInfoOnIO(
189 scoped_refptr<ServiceWorkerContextWrapper> context,
190 int64 version_id,
191 const base::Callback<void(int, int)>& callback) {
192 if (content::ServiceWorkerVersion* version =
193 context->GetLiveVersion(version_id)) {
194 BrowserThread::PostTask(
195 BrowserThread::UI, FROM_HERE,
196 base::Bind(
197 callback, version->embedded_worker()->process_id(),
198 version->embedded_worker()->worker_devtools_agent_route_id()));
202 Response CreateContextErrorResponse() {
203 return Response::InternalError("Could not connect to the context");
206 Response CreateInvalidVersionIdErrorResponse() {
207 return Response::InternalError("Invalid version ID");
210 const std::string GetDevToolsAgentHostTypeString(
211 content::DevToolsAgentHost::Type type) {
212 switch (type) {
213 case DevToolsAgentHost::TYPE_WEB_CONTENTS:
214 return "web_contents";
215 case DevToolsAgentHost::TYPE_FRAME:
216 return "frame";
217 case DevToolsAgentHost::TYPE_SHARED_WORKER:
218 return "shared_worker";
219 case DevToolsAgentHost::TYPE_SERVICE_WORKER:
220 return "service_worker";
221 case DevToolsAgentHost::TYPE_EXTERNAL:
222 return "external";
223 case DevToolsAgentHost::TYPE_BROWSER:
224 return "browser";
226 NOTREACHED() << type;
227 return std::string();
230 } // namespace
232 ServiceWorkerHandler::ServiceWorkerHandler()
233 : enabled_(false), render_frame_host_(nullptr), weak_factory_(this) {
236 ServiceWorkerHandler::~ServiceWorkerHandler() {
237 Disable();
240 void ServiceWorkerHandler::SetRenderFrameHost(
241 RenderFrameHostImpl* render_frame_host) {
242 render_frame_host_ = render_frame_host;
243 // Do not call UpdateHosts yet, wait for load to commit.
244 if (!render_frame_host) {
245 context_ = nullptr;
246 return;
248 StoragePartition* partition = BrowserContext::GetStoragePartition(
249 render_frame_host->GetProcess()->GetBrowserContext(),
250 render_frame_host->GetSiteInstance());
251 DCHECK(partition);
252 context_ = static_cast<ServiceWorkerContextWrapper*>(
253 partition->GetServiceWorkerContext());
256 void ServiceWorkerHandler::SetClient(scoped_ptr<Client> client) {
257 client_.swap(client);
260 void ServiceWorkerHandler::UpdateHosts() {
261 if (!enabled_)
262 return;
264 urls_.clear();
265 BrowserContext* browser_context = nullptr;
266 if (render_frame_host_) {
267 render_frame_host_->frame_tree_node()->frame_tree()->ForEach(
268 base::Bind(&CollectURLs, &urls_));
269 browser_context = render_frame_host_->GetProcess()->GetBrowserContext();
272 ServiceWorkerDevToolsAgentHost::Map old_hosts = attached_hosts_;
273 ServiceWorkerDevToolsAgentHost::Map new_hosts =
274 GetMatchingServiceWorkers(browser_context, urls_);
276 for (auto pair : old_hosts) {
277 if (new_hosts.find(pair.first) == new_hosts.end())
278 ReportWorkerTerminated(pair.second.get());
281 for (auto pair : new_hosts) {
282 if (old_hosts.find(pair.first) == old_hosts.end())
283 ReportWorkerCreated(pair.second.get());
287 void ServiceWorkerHandler::Detached() {
288 Disable();
291 Response ServiceWorkerHandler::Enable() {
292 if (enabled_)
293 return Response::OK();
294 if (!context_)
295 return Response::InternalError("Could not connect to the context");
296 enabled_ = true;
298 ServiceWorkerDevToolsManager::GetInstance()->AddObserver(this);
300 client_->DebugOnStartUpdated(
301 DebugOnStartUpdatedParams::Create()->set_debug_on_start(
302 ServiceWorkerDevToolsManager::GetInstance()
303 ->debug_service_worker_on_start()));
305 context_watcher_ = new ServiceWorkerContextWatcher(
306 context_, base::Bind(&ServiceWorkerHandler::OnWorkerRegistrationUpdated,
307 weak_factory_.GetWeakPtr()),
308 base::Bind(&ServiceWorkerHandler::OnWorkerVersionUpdated,
309 weak_factory_.GetWeakPtr()),
310 base::Bind(&ServiceWorkerHandler::OnErrorReported,
311 weak_factory_.GetWeakPtr()));
312 context_watcher_->Start();
314 UpdateHosts();
315 return Response::OK();
318 Response ServiceWorkerHandler::Disable() {
319 if (!enabled_)
320 return Response::OK();
321 enabled_ = false;
323 ServiceWorkerDevToolsManager::GetInstance()->RemoveObserver(this);
324 for (const auto& pair : attached_hosts_)
325 pair.second->DetachClient();
326 attached_hosts_.clear();
327 DCHECK(context_watcher_);
328 context_watcher_->Stop();
329 context_watcher_ = nullptr;
330 return Response::OK();
333 Response ServiceWorkerHandler::SendMessage(
334 const std::string& worker_id,
335 const std::string& message) {
336 auto it = attached_hosts_.find(worker_id);
337 if (it == attached_hosts_.end())
338 return Response::InternalError("Not connected to the worker");
339 it->second->DispatchProtocolMessage(message);
340 return Response::OK();
343 Response ServiceWorkerHandler::Stop(
344 const std::string& worker_id) {
345 auto it = attached_hosts_.find(worker_id);
346 if (it == attached_hosts_.end())
347 return Response::InternalError("Not connected to the worker");
348 it->second->UnregisterWorker();
349 return Response::OK();
352 Response ServiceWorkerHandler::Unregister(const std::string& scope_url) {
353 if (!enabled_)
354 return Response::OK();
355 if (!context_)
356 return CreateContextErrorResponse();
357 context_->UnregisterServiceWorker(GURL(scope_url), base::Bind(&ResultNoOp));
358 return Response::OK();
361 Response ServiceWorkerHandler::StartWorker(const std::string& scope_url) {
362 if (!enabled_)
363 return Response::OK();
364 if (!context_)
365 return CreateContextErrorResponse();
366 context_->StartServiceWorker(GURL(scope_url), base::Bind(&StatusNoOp));
367 return Response::OK();
370 Response ServiceWorkerHandler::StopWorker(const std::string& version_id) {
371 if (!enabled_)
372 return Response::OK();
373 if (!context_)
374 return CreateContextErrorResponse();
375 int64 id = 0;
376 if (!base::StringToInt64(version_id, &id))
377 return CreateInvalidVersionIdErrorResponse();
378 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
379 base::Bind(&StopServiceWorkerOnIO, context_, id));
380 return Response::OK();
383 Response ServiceWorkerHandler::UpdateRegistration(
384 const std::string& scope_url) {
385 if (!enabled_)
386 return Response::OK();
387 if (!context_)
388 return CreateContextErrorResponse();
389 context_->UpdateRegistration(GURL(scope_url));
390 return Response::OK();
393 Response ServiceWorkerHandler::InspectWorker(const std::string& version_id) {
394 if (!enabled_)
395 return Response::OK();
396 if (!context_)
397 return CreateContextErrorResponse();
399 int64 id = 0;
400 if (!base::StringToInt64(version_id, &id))
401 return CreateInvalidVersionIdErrorResponse();
402 BrowserThread::PostTask(
403 BrowserThread::IO, FROM_HERE,
404 base::Bind(&GetDevToolsRouteInfoOnIO, context_, id,
405 base::Bind(&ServiceWorkerHandler::OpenNewDevToolsWindow,
406 weak_factory_.GetWeakPtr())));
407 return Response::OK();
410 Response ServiceWorkerHandler::SkipWaiting(const std::string& version_id) {
411 if (!enabled_)
412 return Response::OK();
413 if (!context_)
414 return CreateContextErrorResponse();
416 int64 id = 0;
417 if (!base::StringToInt64(version_id, &id))
418 return CreateInvalidVersionIdErrorResponse();
419 context_->SimulateSkipWaiting(id);
420 return Response::OK();
423 Response ServiceWorkerHandler::SetDebugOnStart(bool debug_on_start) {
424 ServiceWorkerDevToolsManager::GetInstance()
425 ->set_debug_service_worker_on_start(debug_on_start);
426 return Response::OK();
429 Response ServiceWorkerHandler::DeliverPushMessage(
430 const std::string& origin,
431 const std::string& registration_id,
432 const std::string& data) {
433 if (!enabled_)
434 return Response::OK();
435 if (!render_frame_host_)
436 return CreateContextErrorResponse();
437 int64 id = 0;
438 if (!base::StringToInt64(registration_id, &id))
439 return CreateInvalidVersionIdErrorResponse();
440 BrowserContext::DeliverPushMessage(
441 render_frame_host_->GetProcess()->GetBrowserContext(), GURL(origin), id,
442 data, base::Bind(&PushDeliveryNoOp));
443 return Response::OK();
446 Response ServiceWorkerHandler::GetTargetInfo(
447 const std::string& target_id,
448 scoped_refptr<TargetInfo>* target_info) {
449 scoped_refptr<DevToolsAgentHost> agent_host(
450 DevToolsAgentHost::GetForId(target_id));
451 if (!agent_host)
452 return Response::InvalidParams("targetId");
453 *target_info =
454 TargetInfo::Create()
455 ->set_id(agent_host->GetId())
456 ->set_type(GetDevToolsAgentHostTypeString(agent_host->GetType()))
457 ->set_title(agent_host->GetTitle())
458 ->set_url(agent_host->GetURL().spec());
459 return Response::OK();
462 Response ServiceWorkerHandler::ActivateTarget(const std::string& target_id) {
463 scoped_refptr<DevToolsAgentHost> agent_host(
464 DevToolsAgentHost::GetForId(target_id));
465 if (!agent_host)
466 return Response::InvalidParams("targetId");
467 agent_host->Activate();
468 return Response::OK();
471 void ServiceWorkerHandler::OpenNewDevToolsWindow(int process_id,
472 int devtools_agent_route_id) {
473 scoped_refptr<DevToolsAgentHostImpl> agent_host(
474 ServiceWorkerDevToolsManager::GetInstance()
475 ->GetDevToolsAgentHostForWorker(process_id, devtools_agent_route_id));
476 if (!agent_host.get())
477 return;
478 agent_host->Inspect(render_frame_host_->GetProcess()->GetBrowserContext());
481 void ServiceWorkerHandler::OnWorkerRegistrationUpdated(
482 const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
483 std::vector<scoped_refptr<ServiceWorkerRegistration>> registration_values;
484 for (const auto& registration : registrations) {
485 registration_values.push_back(
486 CreateRegistrationDictionaryValue(registration));
488 client_->WorkerRegistrationUpdated(
489 WorkerRegistrationUpdatedParams::Create()->set_registrations(
490 registration_values));
493 void ServiceWorkerHandler::OnWorkerVersionUpdated(
494 const std::vector<ServiceWorkerVersionInfo>& versions) {
495 std::vector<scoped_refptr<ServiceWorkerVersion>> version_values;
496 for (const auto& version : versions) {
497 version_values.push_back(CreateVersionDictionaryValue(version));
499 client_->WorkerVersionUpdated(
500 WorkerVersionUpdatedParams::Create()->set_versions(version_values));
503 void ServiceWorkerHandler::OnErrorReported(
504 int64 registration_id,
505 int64 version_id,
506 const ServiceWorkerContextObserver::ErrorInfo& info) {
507 client_->WorkerErrorReported(
508 WorkerErrorReportedParams::Create()->set_error_message(
509 ServiceWorkerErrorMessage::Create()
510 ->set_error_message(base::UTF16ToUTF8(info.error_message))
511 ->set_registration_id(base::Int64ToString(registration_id))
512 ->set_version_id(base::Int64ToString(version_id))
513 ->set_source_url(info.source_url.spec())
514 ->set_line_number(info.line_number)
515 ->set_column_number(info.column_number)));
518 void ServiceWorkerHandler::DispatchProtocolMessage(
519 DevToolsAgentHost* host,
520 const std::string& message) {
522 auto it = attached_hosts_.find(host->GetId());
523 if (it == attached_hosts_.end())
524 return; // Already disconnected.
526 client_->DispatchMessage(
527 DispatchMessageParams::Create()->
528 set_worker_id(host->GetId())->
529 set_message(message));
532 void ServiceWorkerHandler::AgentHostClosed(
533 DevToolsAgentHost* host,
534 bool replaced_with_another_client) {
535 client_->WorkerTerminated(WorkerTerminatedParams::Create()->
536 set_worker_id(host->GetId()));
537 attached_hosts_.erase(host->GetId());
540 void ServiceWorkerHandler::WorkerCreated(
541 ServiceWorkerDevToolsAgentHost* host) {
542 BrowserContext* browser_context = nullptr;
543 if (render_frame_host_)
544 browser_context = render_frame_host_->GetProcess()->GetBrowserContext();
546 auto hosts = GetMatchingServiceWorkers(browser_context, urls_);
547 if (hosts.find(host->GetId()) != hosts.end() && !host->IsAttached() &&
548 !host->IsPausedForDebugOnStart())
549 host->PauseForDebugOnStart();
552 void ServiceWorkerHandler::WorkerReadyForInspection(
553 ServiceWorkerDevToolsAgentHost* host) {
554 if (ServiceWorkerDevToolsManager::GetInstance()
555 ->debug_service_worker_on_start()) {
556 // When debug_service_worker_on_start is true, a new DevTools window will
557 // be opend in ServiceWorkerDevToolsManager::WorkerReadyForInspection.
558 return;
560 UpdateHosts();
563 void ServiceWorkerHandler::WorkerDestroyed(
564 ServiceWorkerDevToolsAgentHost* host) {
565 UpdateHosts();
568 void ServiceWorkerHandler::DebugOnStartUpdated(bool debug_on_start) {
569 client_->DebugOnStartUpdated(
570 DebugOnStartUpdatedParams::Create()->set_debug_on_start(debug_on_start));
573 void ServiceWorkerHandler::ReportWorkerCreated(
574 ServiceWorkerDevToolsAgentHost* host) {
575 if (host->IsAttached())
576 return;
577 attached_hosts_[host->GetId()] = host;
578 host->AttachClient(this);
579 client_->WorkerCreated(WorkerCreatedParams::Create()->
580 set_worker_id(host->GetId())->
581 set_url(host->GetURL().spec()));
584 void ServiceWorkerHandler::ReportWorkerTerminated(
585 ServiceWorkerDevToolsAgentHost* host) {
586 auto it = attached_hosts_.find(host->GetId());
587 if (it == attached_hosts_.end())
588 return;
589 host->DetachClient();
590 client_->WorkerTerminated(WorkerTerminatedParams::Create()->
591 set_worker_id(host->GetId()));
592 attached_hosts_.erase(it);
595 } // namespace service_worker
596 } // namespace devtools
597 } // namespace content