[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_targets_ui.cc
blob868d8da867c5b80d16d9429d9243820dc4473459
1 // Copyright 2013 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 "chrome/browser/devtools/devtools_targets_ui.h"
7 #include "base/memory/weak_ptr.h"
8 #include "base/stl_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "base/version.h"
12 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
13 #include "chrome/browser/devtools/devtools_target_impl.h"
14 #include "chrome/browser/guest_view/guest_view_base.h"
15 #include "chrome/common/chrome_version_info.h"
16 #include "content/public/browser/browser_child_process_observer.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/child_process_data.h"
19 #include "content/public/browser/notification_observer.h"
20 #include "content/public/browser/notification_registrar.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/worker_service.h"
29 #include "content/public/browser/worker_service_observer.h"
30 #include "content/public/common/process_type.h"
31 #include "net/base/escape.h"
33 using content::BrowserThread;
34 using content::RenderFrameHost;
35 using content::WebContents;
37 namespace {
39 const char kTargetSourceField[] = "source";
40 const char kTargetSourceRenderer[] = "renderers";
41 const char kTargetSourceWorker[] = "workers";
42 const char kTargetSourceAdb[] = "adb";
44 const char kTargetIdField[] = "id";
45 const char kTargetTypeField[] = "type";
46 const char kAttachedField[] = "attached";
47 const char kUrlField[] = "url";
48 const char kNameField[] = "name";
49 const char kFaviconUrlField[] = "faviconUrl";
50 const char kDescriptionField[] = "description";
52 const char kGuestList[] = "guests";
54 const char kAdbModelField[] = "adbModel";
55 const char kAdbConnectedField[] = "adbConnected";
56 const char kAdbSerialField[] = "adbSerial";
57 const char kAdbBrowsersList[] = "browsers";
58 const char kAdbDeviceIdFormat[] = "device:%s";
60 const char kAdbBrowserNameField[] = "adbBrowserName";
61 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
62 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
63 const char kCompatibleVersion[] = "compatibleVersion";
64 const char kAdbPagesList[] = "pages";
66 const char kAdbScreenWidthField[] = "adbScreenWidth";
67 const char kAdbScreenHeightField[] = "adbScreenHeight";
68 const char kAdbAttachedForeignField[] = "adbAttachedForeign";
70 // CancelableTimer ------------------------------------------------------------
72 class CancelableTimer {
73 public:
74 CancelableTimer(base::Closure callback, base::TimeDelta delay)
75 : callback_(callback),
76 weak_factory_(this) {
77 base::MessageLoop::current()->PostDelayedTask(
78 FROM_HERE,
79 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
80 delay);
83 private:
84 void Fire() { callback_.Run(); }
86 base::Closure callback_;
87 base::WeakPtrFactory<CancelableTimer> weak_factory_;
90 // RenderViewHostTargetsUIHandler ---------------------------------------------
92 class RenderViewHostTargetsUIHandler
93 : public DevToolsTargetsUIHandler,
94 public content::NotificationObserver {
95 public:
96 explicit RenderViewHostTargetsUIHandler(Callback callback);
97 virtual ~RenderViewHostTargetsUIHandler();
98 private:
99 // content::NotificationObserver overrides.
100 virtual void Observe(int type,
101 const content::NotificationSource& source,
102 const content::NotificationDetails& details) OVERRIDE;
104 void UpdateTargets();
106 content::NotificationRegistrar notification_registrar_;
107 scoped_ptr<CancelableTimer> timer_;
110 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
111 Callback callback)
112 : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) {
113 notification_registrar_.Add(this,
114 content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
115 content::NotificationService::AllSources());
116 notification_registrar_.Add(this,
117 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
118 content::NotificationService::AllSources());
119 notification_registrar_.Add(this,
120 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
121 content::NotificationService::AllSources());
122 UpdateTargets();
125 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
126 notification_registrar_.RemoveAll();
129 void RenderViewHostTargetsUIHandler::Observe(
130 int type,
131 const content::NotificationSource& source,
132 const content::NotificationDetails& details) {
133 const int kUpdateDelay = 100;
134 timer_.reset(
135 new CancelableTimer(
136 base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets,
137 base::Unretained(this)),
138 base::TimeDelta::FromMilliseconds(kUpdateDelay)));
141 void RenderViewHostTargetsUIHandler::UpdateTargets() {
142 scoped_ptr<base::ListValue> list_value(new base::ListValue());
144 std::map<RenderFrameHost*, base::DictionaryValue*> rfh_to_descriptor;
145 std::vector<RenderFrameHost*> nested_frames;
147 DevToolsTargetImpl::List targets =
148 DevToolsTargetImpl::EnumerateRenderViewHostTargets();
150 STLDeleteValues(&targets_);
151 for (DevToolsTargetImpl::List::iterator it = targets.begin();
152 it != targets.end(); ++it) {
153 scoped_ptr<DevToolsTargetImpl> target(*it);
154 content::RenderViewHost* rvh = target->GetRenderViewHost();
155 if (!rvh)
156 continue;
158 DevToolsTargetImpl* target_ptr = target.get();
159 targets_[target_ptr->GetId()] = target.release();
160 base::DictionaryValue* descriptor = Serialize(*target_ptr);
162 // TODO (kaznacheev): GetMainFrame() call is a temporary hack.
163 // Revisit this when multiple OOP frames are supported.
164 RenderFrameHost* rfh = rvh->GetMainFrame();
165 rfh_to_descriptor[rfh] = descriptor;
166 if (rvh->GetProcess()->IsGuest() || rfh->IsCrossProcessSubframe()) {
167 nested_frames.push_back(rfh);
168 } else {
169 list_value->Append(descriptor);
173 // Add the list of nested targets to each of its owners.
174 for (std::vector<RenderFrameHost*>::iterator it(nested_frames.begin());
175 it != nested_frames.end(); ++it) {
176 RenderFrameHost* rfh = (*it);
177 RenderFrameHost* parent_rfh = NULL;
178 content::RenderViewHost* rvh = rfh->GetRenderViewHost();
179 WebContents* nested_web_contents = WebContents::FromRenderViewHost(rvh);
180 GuestViewBase* guest = GuestViewBase::FromWebContents(nested_web_contents);
181 if (guest) {
182 WebContents* embedder = guest->embedder_web_contents();
183 parent_rfh = embedder->GetRenderViewHost()->GetMainFrame();
184 } else {
185 parent_rfh = rfh->GetParent();
186 DCHECK(parent_rfh);
188 if (parent_rfh && rfh_to_descriptor.count(parent_rfh) > 0) {
189 base::DictionaryValue* parent = rfh_to_descriptor[parent_rfh];
190 base::ListValue* guests = NULL;
191 if (!parent->GetList(kGuestList, &guests)) {
192 guests = new base::ListValue();
193 parent->Set(kGuestList, guests);
195 guests->Append(rfh_to_descriptor[rfh]);
199 SendSerializedTargets(list_value.Pass());
202 // WorkerObserver -------------------------------------------------------------
204 class WorkerObserver
205 : public content::WorkerServiceObserver,
206 public base::RefCountedThreadSafe<WorkerObserver> {
207 public:
208 WorkerObserver() {}
210 void Start(DevToolsTargetImpl::Callback callback) {
211 DCHECK(callback_.is_null());
212 DCHECK(!callback.is_null());
213 callback_ = callback;
214 BrowserThread::PostTask(
215 BrowserThread::IO, FROM_HERE,
216 base::Bind(&WorkerObserver::StartOnIOThread, this));
219 void Stop() {
220 DCHECK(!callback_.is_null());
221 callback_ = DevToolsTargetImpl::Callback();
222 BrowserThread::PostTask(
223 BrowserThread::IO, FROM_HERE,
224 base::Bind(&WorkerObserver::StopOnIOThread, this));
227 void Enumerate() {
228 BrowserThread::PostTask(
229 BrowserThread::IO, FROM_HERE,
230 base::Bind(&WorkerObserver::EnumerateOnIOThread,
231 this));
234 private:
235 friend class base::RefCountedThreadSafe<WorkerObserver>;
236 virtual ~WorkerObserver() {}
238 // content::WorkerServiceObserver overrides:
239 virtual void WorkerCreated(
240 const GURL& url,
241 const base::string16& name,
242 int process_id,
243 int route_id) OVERRIDE {
244 EnumerateOnIOThread();
247 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
248 EnumerateOnIOThread();
251 void StartOnIOThread() {
252 content::WorkerService::GetInstance()->AddObserver(this);
253 EnumerateOnIOThread();
256 void StopOnIOThread() {
257 content::WorkerService::GetInstance()->RemoveObserver(this);
260 void EnumerateOnIOThread() {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262 DevToolsTargetImpl::EnumerateWorkerTargets(
263 base::Bind(&WorkerObserver::RespondOnUIThread, this));
266 void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
268 if (callback_.is_null())
269 return;
270 callback_.Run(targets);
273 DevToolsTargetImpl::Callback callback_;
276 // WorkerTargetsUIHandler -----------------------------------------------------
278 class WorkerTargetsUIHandler
279 : public DevToolsTargetsUIHandler,
280 public content::BrowserChildProcessObserver {
281 public:
282 explicit WorkerTargetsUIHandler(Callback callback);
283 virtual ~WorkerTargetsUIHandler();
285 private:
286 // content::BrowserChildProcessObserver overrides.
287 virtual void BrowserChildProcessHostConnected(
288 const content::ChildProcessData& data) OVERRIDE;
289 virtual void BrowserChildProcessHostDisconnected(
290 const content::ChildProcessData& data) OVERRIDE;
292 void UpdateTargets(const DevToolsTargetImpl::List& targets);
294 scoped_refptr<WorkerObserver> observer_;
297 WorkerTargetsUIHandler::WorkerTargetsUIHandler(Callback callback)
298 : DevToolsTargetsUIHandler(kTargetSourceWorker, callback),
299 observer_(new WorkerObserver()) {
300 observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets,
301 base::Unretained(this)));
302 BrowserChildProcessObserver::Add(this);
305 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
306 BrowserChildProcessObserver::Remove(this);
307 observer_->Stop();
310 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected(
311 const content::ChildProcessData& data) {
312 if (data.process_type == content::PROCESS_TYPE_WORKER)
313 observer_->Enumerate();
316 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected(
317 const content::ChildProcessData& data) {
318 if (data.process_type == content::PROCESS_TYPE_WORKER)
319 observer_->Enumerate();
322 void WorkerTargetsUIHandler::UpdateTargets(
323 const DevToolsTargetImpl::List& targets) {
324 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
325 scoped_ptr<base::ListValue> list_value(new base::ListValue());
326 STLDeleteValues(&targets_);
327 for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
328 it != targets.end(); ++it) {
329 DevToolsTargetImpl* target = *it;
330 list_value->Append(Serialize(*target));
331 targets_[target->GetId()] = target;
333 SendSerializedTargets(list_value.Pass());
336 // AdbTargetsUIHandler --------------------------------------------------------
338 class AdbTargetsUIHandler
339 : public DevToolsTargetsUIHandler,
340 public DevToolsAndroidBridge::DeviceListListener {
341 public:
342 AdbTargetsUIHandler(Callback callback, Profile* profile);
343 virtual ~AdbTargetsUIHandler();
345 virtual void Open(const std::string& browser_id,
346 const std::string& url,
347 const DevToolsTargetsUIHandler::TargetCallback&) OVERRIDE;
349 virtual scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
350 const std::string& browser_id) OVERRIDE;
352 private:
353 // DevToolsAndroidBridge::Listener overrides.
354 virtual void DeviceListChanged(
355 const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE;
357 Profile* profile_;
359 typedef std::map<std::string,
360 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
361 RemoteBrowsers remote_browsers_;
364 AdbTargetsUIHandler::AdbTargetsUIHandler(Callback callback, Profile* profile)
365 : DevToolsTargetsUIHandler(kTargetSourceAdb, callback),
366 profile_(profile) {
367 DevToolsAndroidBridge* android_bridge =
368 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
369 if (android_bridge)
370 android_bridge->AddDeviceListListener(this);
373 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
374 DevToolsAndroidBridge* android_bridge =
375 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
376 if (android_bridge)
377 android_bridge->RemoveDeviceListListener(this);
380 static void CallOnTarget(
381 const DevToolsTargetsUIHandler::TargetCallback& callback,
382 DevToolsAndroidBridge::RemotePage* page) {
383 scoped_ptr<DevToolsAndroidBridge::RemotePage> my_page(page);
384 callback.Run(my_page ? my_page->GetTarget() : NULL);
387 void AdbTargetsUIHandler::Open(
388 const std::string& browser_id,
389 const std::string& url,
390 const DevToolsTargetsUIHandler::TargetCallback& callback) {
391 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
392 if (it != remote_browsers_.end())
393 it->second->Open(url, base::Bind(&CallOnTarget, callback));
396 scoped_refptr<content::DevToolsAgentHost>
397 AdbTargetsUIHandler::GetBrowserAgentHost(
398 const std::string& browser_id) {
399 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
400 return it != remote_browsers_.end() ? it->second->GetAgentHost() : NULL;
403 void AdbTargetsUIHandler::DeviceListChanged(
404 const DevToolsAndroidBridge::RemoteDevices& devices) {
405 remote_browsers_.clear();
406 STLDeleteValues(&targets_);
408 scoped_ptr<base::ListValue> device_list(new base::ListValue());
409 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
410 devices.begin(); dit != devices.end(); ++dit) {
411 DevToolsAndroidBridge::RemoteDevice* device = dit->get();
412 base::DictionaryValue* device_data = new base::DictionaryValue();
413 device_data->SetString(kAdbModelField, device->model());
414 device_data->SetString(kAdbSerialField, device->serial());
415 device_data->SetBoolean(kAdbConnectedField, device->is_connected());
416 std::string device_id = base::StringPrintf(
417 kAdbDeviceIdFormat,
418 device->serial().c_str());
419 device_data->SetString(kTargetIdField, device_id);
420 base::ListValue* browser_list = new base::ListValue();
421 device_data->Set(kAdbBrowsersList, browser_list);
423 DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
424 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
425 browsers.begin(); bit != browsers.end(); ++bit) {
426 DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
427 base::DictionaryValue* browser_data = new base::DictionaryValue();
428 browser_data->SetString(kAdbBrowserNameField, browser->display_name());
429 browser_data->SetString(kAdbBrowserVersionField, browser->version());
430 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
431 browser->GetParsedVersion();
432 browser_data->SetInteger(
433 kAdbBrowserChromeVersionField,
434 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
435 std::string browser_id = base::StringPrintf(
436 "browser:%s:%s:%s:%s",
437 device->serial().c_str(), // Ensure uniqueness across devices.
438 browser->display_name().c_str(), // Sort by display name.
439 browser->version().c_str(), // Then by version.
440 browser->socket().c_str()); // Ensure uniqueness on the device.
441 browser_data->SetString(kTargetIdField, browser_id);
442 browser_data->SetString(kTargetSourceField, source_id());
444 base::Version remote_version;
445 if (browser->IsChrome()) {
446 remote_version = base::Version(browser->version());
447 } else {
448 // Try parse WebView version.
449 std::string version = browser->version();
450 size_t pos = version.find("Chrome/");
451 if (pos != std::string::npos) {
452 remote_version = base::Version(browser->version().substr(pos + 7));
455 chrome::VersionInfo version_info;
456 base::Version local_version(version_info.Version());
458 browser_data->SetBoolean(kCompatibleVersion,
459 (!remote_version.IsValid()) || (!local_version.IsValid()) ||
460 remote_version.components()[0] <= local_version.components()[0]);
462 base::ListValue* page_list = new base::ListValue();
463 remote_browsers_[browser_id] = browser;
464 browser_data->Set(kAdbPagesList, page_list);
465 std::vector<DevToolsAndroidBridge::RemotePage*> pages =
466 browser->CreatePages();
467 for (std::vector<DevToolsAndroidBridge::RemotePage*>::iterator it =
468 pages.begin(); it != pages.end(); ++it) {
469 DevToolsAndroidBridge::RemotePage* page = *it;
470 DevToolsTargetImpl* target = page->GetTarget();
471 base::DictionaryValue* target_data = Serialize(*target);
472 target_data->SetBoolean(
473 kAdbAttachedForeignField,
474 target->IsAttached() &&
475 !DevToolsAndroidBridge::HasDevToolsWindow(target->GetId()));
476 // Pass the screen size in the target object to make sure that
477 // the caching logic does not prevent the target item from updating
478 // when the screen size changes.
479 gfx::Size screen_size = device->screen_size();
480 target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
481 target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
482 targets_[target->GetId()] = target;
483 page_list->Append(target_data);
485 browser_list->Append(browser_data);
488 device_list->Append(device_data);
490 SendSerializedTargets(device_list.Pass());
493 } // namespace
495 // DevToolsTargetsUIHandler ---------------------------------------------------
497 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
498 const std::string& source_id,
499 Callback callback)
500 : source_id_(source_id),
501 callback_(callback) {
504 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
505 STLDeleteValues(&targets_);
508 // static
509 scoped_ptr<DevToolsTargetsUIHandler>
510 DevToolsTargetsUIHandler::CreateForRenderers(
511 DevToolsTargetsUIHandler::Callback callback) {
512 return scoped_ptr<DevToolsTargetsUIHandler>(
513 new RenderViewHostTargetsUIHandler(callback));
516 // static
517 scoped_ptr<DevToolsTargetsUIHandler>
518 DevToolsTargetsUIHandler::CreateForWorkers(
519 DevToolsTargetsUIHandler::Callback callback) {
520 return scoped_ptr<DevToolsTargetsUIHandler>(
521 new WorkerTargetsUIHandler(callback));
524 // static
525 scoped_ptr<DevToolsTargetsUIHandler>
526 DevToolsTargetsUIHandler::CreateForAdb(
527 DevToolsTargetsUIHandler::Callback callback, Profile* profile) {
528 return scoped_ptr<DevToolsTargetsUIHandler>(
529 new AdbTargetsUIHandler(callback, profile));
532 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
533 const std::string& target_id) {
534 TargetMap::iterator it = targets_.find(target_id);
535 if (it != targets_.end())
536 return it->second;
537 return NULL;
540 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
541 const std::string& url,
542 const TargetCallback& callback) {
543 callback.Run(NULL);
546 scoped_refptr<content::DevToolsAgentHost>
547 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
548 return NULL;
551 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
552 const DevToolsTargetImpl& target) {
553 base::DictionaryValue* target_data = new base::DictionaryValue();
554 target_data->SetString(kTargetSourceField, source_id_);
555 target_data->SetString(kTargetIdField, target.GetId());
556 target_data->SetString(kTargetTypeField, target.GetType());
557 target_data->SetBoolean(kAttachedField, target.IsAttached());
558 target_data->SetString(kUrlField, target.GetURL().spec());
559 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
560 target_data->SetString(kFaviconUrlField, target.GetFaviconURL().spec());
561 target_data->SetString(kDescriptionField, target.GetDescription());
562 return target_data;
565 void DevToolsTargetsUIHandler::SendSerializedTargets(
566 scoped_ptr<base::ListValue> list) {
567 callback_.Run(source_id_, list.Pass());
570 // PortForwardingStatusSerializer ---------------------------------------------
572 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
573 const Callback& callback, Profile* profile)
574 : callback_(callback),
575 profile_(profile) {
576 PortForwardingController* port_forwarding_controller =
577 PortForwardingController::Factory::GetForProfile(profile_);
578 if (port_forwarding_controller)
579 port_forwarding_controller->AddListener(this);
582 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
583 PortForwardingController* port_forwarding_controller =
584 PortForwardingController::Factory::GetForProfile(profile_);
585 if (port_forwarding_controller)
586 port_forwarding_controller->RemoveListener(this);
589 void PortForwardingStatusSerializer::PortStatusChanged(
590 const DevicesStatus& status) {
591 base::DictionaryValue result;
592 for (DevicesStatus::const_iterator sit = status.begin();
593 sit != status.end(); ++sit) {
594 base::DictionaryValue* device_status_dict = new base::DictionaryValue();
595 const PortStatusMap& device_status_map = sit->second;
596 for (PortStatusMap::const_iterator it = device_status_map.begin();
597 it != device_status_map.end(); ++it) {
598 device_status_dict->SetInteger(
599 base::StringPrintf("%d", it->first), it->second);
602 std::string device_id = base::StringPrintf(
603 kAdbDeviceIdFormat,
604 sit->first.c_str());
605 result.Set(device_id, device_status_dict);
607 callback_.Run(result);