Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_targets_ui.cc
blob069dba3d7830c49486f1129dca1cc7bbb19f9aa8
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/common/chrome_version_info.h"
15 #include "content/public/browser/browser_child_process_observer.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/child_process_data.h"
18 #include "content/public/browser/notification_observer.h"
19 #include "content/public/browser/notification_registrar.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_source.h"
22 #include "content/public/browser/notification_types.h"
23 #include "content/public/browser/render_frame_host.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/worker_service.h"
28 #include "content/public/browser/worker_service_observer.h"
29 #include "content/public/common/process_type.h"
30 #include "net/base/escape.h"
32 using content::BrowserThread;
33 using content::RenderFrameHost;
34 using content::WebContents;
36 namespace {
38 const char kTargetSourceField[] = "source";
39 const char kTargetSourceRenderer[] = "renderers";
40 const char kTargetSourceWorker[] = "workers";
41 const char kTargetSourceAdb[] = "adb";
43 const char kTargetIdField[] = "id";
44 const char kTargetTypeField[] = "type";
45 const char kAttachedField[] = "attached";
46 const char kUrlField[] = "url";
47 const char kNameField[] = "name";
48 const char kFaviconUrlField[] = "faviconUrl";
49 const char kDescriptionField[] = "description";
51 const char kGuestList[] = "guests";
53 const char kAdbModelField[] = "adbModel";
54 const char kAdbConnectedField[] = "adbConnected";
55 const char kAdbSerialField[] = "adbSerial";
56 const char kAdbBrowsersList[] = "browsers";
57 const char kAdbDeviceIdFormat[] = "device:%s";
59 const char kAdbBrowserNameField[] = "adbBrowserName";
60 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
61 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
62 const char kCompatibleVersion[] = "compatibleVersion";
63 const char kAdbPagesList[] = "pages";
65 const char kAdbScreenWidthField[] = "adbScreenWidth";
66 const char kAdbScreenHeightField[] = "adbScreenHeight";
67 const char kAdbAttachedForeignField[] = "adbAttachedForeign";
69 // CancelableTimer ------------------------------------------------------------
71 class CancelableTimer {
72 public:
73 CancelableTimer(base::Closure callback, base::TimeDelta delay)
74 : callback_(callback),
75 weak_factory_(this) {
76 base::MessageLoop::current()->PostDelayedTask(
77 FROM_HERE,
78 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
79 delay);
82 private:
83 void Fire() { callback_.Run(); }
85 base::Closure callback_;
86 base::WeakPtrFactory<CancelableTimer> weak_factory_;
89 // RenderViewHostTargetsUIHandler ---------------------------------------------
91 class RenderViewHostTargetsUIHandler
92 : public DevToolsTargetsUIHandler,
93 public content::NotificationObserver {
94 public:
95 explicit RenderViewHostTargetsUIHandler(Callback callback);
96 virtual ~RenderViewHostTargetsUIHandler();
97 private:
98 // content::NotificationObserver overrides.
99 virtual void Observe(int type,
100 const content::NotificationSource& source,
101 const content::NotificationDetails& details) OVERRIDE;
103 void UpdateTargets();
105 content::NotificationRegistrar notification_registrar_;
106 scoped_ptr<CancelableTimer> timer_;
109 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
110 Callback callback)
111 : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) {
112 notification_registrar_.Add(this,
113 content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
114 content::NotificationService::AllSources());
115 notification_registrar_.Add(this,
116 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
117 content::NotificationService::AllSources());
118 notification_registrar_.Add(this,
119 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
120 content::NotificationService::AllSources());
121 UpdateTargets();
124 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
125 notification_registrar_.RemoveAll();
128 void RenderViewHostTargetsUIHandler::Observe(
129 int type,
130 const content::NotificationSource& source,
131 const content::NotificationDetails& details) {
132 const int kUpdateDelay = 100;
133 timer_.reset(
134 new CancelableTimer(
135 base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets,
136 base::Unretained(this)),
137 base::TimeDelta::FromMilliseconds(kUpdateDelay)));
140 void RenderViewHostTargetsUIHandler::UpdateTargets() {
141 scoped_ptr<base::ListValue> list_value(new base::ListValue());
143 std::map<RenderFrameHost*, base::DictionaryValue*> rfh_to_descriptor;
144 std::vector<RenderFrameHost*> nested_frames;
146 DevToolsTargetImpl::List targets =
147 DevToolsTargetImpl::EnumerateRenderViewHostTargets();
149 STLDeleteValues(&targets_);
150 for (DevToolsTargetImpl::List::iterator it = targets.begin();
151 it != targets.end(); ++it) {
152 scoped_ptr<DevToolsTargetImpl> target(*it);
153 content::RenderViewHost* rvh = target->GetRenderViewHost();
154 if (!rvh)
155 continue;
157 DevToolsTargetImpl* target_ptr = target.get();
158 targets_[target_ptr->GetId()] = target.release();
159 base::DictionaryValue* descriptor = Serialize(*target_ptr);
161 // TODO (kaznacheev): GetMainFrame() call is a temporary hack.
162 // Revisit this when multiple OOP frames are supported.
163 RenderFrameHost* rfh = rvh->GetMainFrame();
164 rfh_to_descriptor[rfh] = descriptor;
165 if (rvh->GetProcess()->IsGuest() || rfh->IsCrossProcessSubframe()) {
166 nested_frames.push_back(rfh);
167 } else {
168 list_value->Append(descriptor);
172 // Add the list of nested targets to each of its owners.
173 for (std::vector<RenderFrameHost*>::iterator it(nested_frames.begin());
174 it != nested_frames.end(); ++it) {
175 RenderFrameHost* rfh = (*it);
176 RenderFrameHost* parent_rfh = NULL;
177 content::RenderViewHost* rvh = rfh->GetRenderViewHost();
178 if (rvh->GetProcess()->IsGuest()) {
179 WebContents* nested_web_contents = WebContents::FromRenderViewHost(rvh);
180 WebContents* embedder = nested_web_contents->GetEmbedderWebContents();
181 parent_rfh = embedder->GetRenderViewHost()->GetMainFrame();
182 } else {
183 parent_rfh = rfh->GetParent();
184 DCHECK(parent_rfh);
186 if (parent_rfh && rfh_to_descriptor.count(parent_rfh) > 0) {
187 base::DictionaryValue* parent = rfh_to_descriptor[parent_rfh];
188 base::ListValue* guests = NULL;
189 if (!parent->GetList(kGuestList, &guests)) {
190 guests = new base::ListValue();
191 parent->Set(kGuestList, guests);
193 guests->Append(rfh_to_descriptor[rfh]);
197 SendSerializedTargets(list_value.Pass());
200 // WorkerObserver -------------------------------------------------------------
202 class WorkerObserver
203 : public content::WorkerServiceObserver,
204 public base::RefCountedThreadSafe<WorkerObserver> {
205 public:
206 WorkerObserver() {}
208 void Start(DevToolsTargetImpl::Callback callback) {
209 DCHECK(callback_.is_null());
210 DCHECK(!callback.is_null());
211 callback_ = callback;
212 BrowserThread::PostTask(
213 BrowserThread::IO, FROM_HERE,
214 base::Bind(&WorkerObserver::StartOnIOThread, this));
217 void Stop() {
218 DCHECK(!callback_.is_null());
219 callback_ = DevToolsTargetImpl::Callback();
220 BrowserThread::PostTask(
221 BrowserThread::IO, FROM_HERE,
222 base::Bind(&WorkerObserver::StopOnIOThread, this));
225 void Enumerate() {
226 BrowserThread::PostTask(
227 BrowserThread::IO, FROM_HERE,
228 base::Bind(&WorkerObserver::EnumerateOnIOThread,
229 this));
232 private:
233 friend class base::RefCountedThreadSafe<WorkerObserver>;
234 virtual ~WorkerObserver() {}
236 // content::WorkerServiceObserver overrides:
237 virtual void WorkerCreated(
238 const GURL& url,
239 const base::string16& name,
240 int process_id,
241 int route_id) OVERRIDE {
242 EnumerateOnIOThread();
245 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
246 EnumerateOnIOThread();
249 void StartOnIOThread() {
250 content::WorkerService::GetInstance()->AddObserver(this);
251 EnumerateOnIOThread();
254 void StopOnIOThread() {
255 content::WorkerService::GetInstance()->RemoveObserver(this);
258 void EnumerateOnIOThread() {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
260 DevToolsTargetImpl::EnumerateWorkerTargets(
261 base::Bind(&WorkerObserver::RespondOnUIThread, this));
264 void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 if (callback_.is_null())
267 return;
268 callback_.Run(targets);
271 DevToolsTargetImpl::Callback callback_;
274 // WorkerTargetsUIHandler -----------------------------------------------------
276 class WorkerTargetsUIHandler
277 : public DevToolsTargetsUIHandler,
278 public content::BrowserChildProcessObserver {
279 public:
280 explicit WorkerTargetsUIHandler(Callback callback);
281 virtual ~WorkerTargetsUIHandler();
283 private:
284 // content::BrowserChildProcessObserver overrides.
285 virtual void BrowserChildProcessHostConnected(
286 const content::ChildProcessData& data) OVERRIDE;
287 virtual void BrowserChildProcessHostDisconnected(
288 const content::ChildProcessData& data) OVERRIDE;
290 void UpdateTargets(const DevToolsTargetImpl::List& targets);
292 scoped_refptr<WorkerObserver> observer_;
295 WorkerTargetsUIHandler::WorkerTargetsUIHandler(Callback callback)
296 : DevToolsTargetsUIHandler(kTargetSourceWorker, callback),
297 observer_(new WorkerObserver()) {
298 observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets,
299 base::Unretained(this)));
300 BrowserChildProcessObserver::Add(this);
303 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
304 BrowserChildProcessObserver::Remove(this);
305 observer_->Stop();
308 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected(
309 const content::ChildProcessData& data) {
310 if (data.process_type == content::PROCESS_TYPE_WORKER)
311 observer_->Enumerate();
314 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected(
315 const content::ChildProcessData& data) {
316 if (data.process_type == content::PROCESS_TYPE_WORKER)
317 observer_->Enumerate();
320 void WorkerTargetsUIHandler::UpdateTargets(
321 const DevToolsTargetImpl::List& targets) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 scoped_ptr<base::ListValue> list_value(new base::ListValue());
324 STLDeleteValues(&targets_);
325 for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
326 it != targets.end(); ++it) {
327 DevToolsTargetImpl* target = *it;
328 list_value->Append(Serialize(*target));
329 targets_[target->GetId()] = target;
331 SendSerializedTargets(list_value.Pass());
334 // AdbTargetsUIHandler --------------------------------------------------------
336 class AdbTargetsUIHandler
337 : public DevToolsTargetsUIHandler,
338 public DevToolsAndroidBridge::DeviceListListener {
339 public:
340 AdbTargetsUIHandler(Callback callback, Profile* profile);
341 virtual ~AdbTargetsUIHandler();
343 virtual void Open(const std::string& browser_id,
344 const std::string& url,
345 const DevToolsTargetsUIHandler::TargetCallback&) OVERRIDE;
347 private:
348 // DevToolsAndroidBridge::Listener overrides.
349 virtual void DeviceListChanged(
350 const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE;
352 Profile* profile_;
354 typedef std::map<std::string,
355 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
356 RemoteBrowsers remote_browsers_;
359 AdbTargetsUIHandler::AdbTargetsUIHandler(Callback callback, Profile* profile)
360 : DevToolsTargetsUIHandler(kTargetSourceAdb, callback),
361 profile_(profile) {
362 DevToolsAndroidBridge* android_bridge =
363 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
364 if (android_bridge)
365 android_bridge->AddDeviceListListener(this);
368 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
369 DevToolsAndroidBridge* android_bridge =
370 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
371 if (android_bridge)
372 android_bridge->RemoveDeviceListListener(this);
375 void AdbTargetsUIHandler::Open(
376 const std::string& browser_id,
377 const std::string& url,
378 const DevToolsTargetsUIHandler::TargetCallback& callback) {
379 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
380 if (it != remote_browsers_.end())
381 it->second->Open(url, callback);
384 void AdbTargetsUIHandler::DeviceListChanged(
385 const DevToolsAndroidBridge::RemoteDevices& devices) {
386 remote_browsers_.clear();
387 STLDeleteValues(&targets_);
389 scoped_ptr<base::ListValue> device_list(new base::ListValue());
390 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
391 devices.begin(); dit != devices.end(); ++dit) {
392 DevToolsAndroidBridge::RemoteDevice* device = dit->get();
393 base::DictionaryValue* device_data = new base::DictionaryValue();
394 device_data->SetString(kAdbModelField, device->model());
395 device_data->SetString(kAdbSerialField, device->serial());
396 device_data->SetBoolean(kAdbConnectedField, device->is_connected());
397 std::string device_id = base::StringPrintf(
398 kAdbDeviceIdFormat,
399 device->serial().c_str());
400 device_data->SetString(kTargetIdField, device_id);
401 base::ListValue* browser_list = new base::ListValue();
402 device_data->Set(kAdbBrowsersList, browser_list);
404 DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
405 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
406 browsers.begin(); bit != browsers.end(); ++bit) {
407 DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
408 base::DictionaryValue* browser_data = new base::DictionaryValue();
409 browser_data->SetString(kAdbBrowserNameField, browser->display_name());
410 browser_data->SetString(kAdbBrowserVersionField, browser->version());
411 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
412 browser->GetParsedVersion();
413 browser_data->SetInteger(
414 kAdbBrowserChromeVersionField,
415 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
416 std::string browser_id = base::StringPrintf(
417 "browser:%s:%s:%s:%s",
418 device->serial().c_str(), // Ensure uniqueness across devices.
419 browser->display_name().c_str(), // Sort by display name.
420 browser->version().c_str(), // Then by version.
421 browser->socket().c_str()); // Ensure uniqueness on the device.
422 browser_data->SetString(kTargetIdField, browser_id);
423 browser_data->SetString(kTargetSourceField, source_id());
425 base::Version remote_version;
426 if (browser->IsChrome()) {
427 remote_version = base::Version(browser->version());
428 } else {
429 // Try parse WebView version.
430 std::string version = browser->version();
431 size_t pos = version.find("Chrome/");
432 if (pos != std::string::npos) {
433 remote_version = base::Version(browser->version().substr(pos + 7));
436 chrome::VersionInfo version_info;
437 base::Version local_version(version_info.Version());
439 browser_data->SetBoolean(kCompatibleVersion,
440 (!remote_version.IsValid()) || (!local_version.IsValid()) ||
441 remote_version.components()[0] <= local_version.components()[0]);
443 base::ListValue* page_list = new base::ListValue();
444 remote_browsers_[browser_id] = browser;
445 browser_data->Set(kAdbPagesList, page_list);
446 DevToolsTargetImpl::List pages = browser->CreatePageTargets();
447 for (DevToolsTargetImpl::List::iterator it =
448 pages.begin(); it != pages.end(); ++it) {
449 DevToolsTargetImpl* target = *it;
450 base::DictionaryValue* target_data = Serialize(*target);
451 target_data->SetBoolean(
452 kAdbAttachedForeignField,
453 target->IsAttached() &&
454 !DevToolsAndroidBridge::HasDevToolsWindow(target->GetId()));
455 // Pass the screen size in the target object to make sure that
456 // the caching logic does not prevent the target item from updating
457 // when the screen size changes.
458 gfx::Size screen_size = device->screen_size();
459 target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
460 target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
461 targets_[target->GetId()] = target;
462 page_list->Append(target_data);
464 browser_list->Append(browser_data);
467 device_list->Append(device_data);
469 SendSerializedTargets(device_list.Pass());
472 } // namespace
474 // DevToolsTargetsUIHandler ---------------------------------------------------
476 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
477 const std::string& source_id,
478 Callback callback)
479 : source_id_(source_id),
480 callback_(callback) {
483 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
484 STLDeleteValues(&targets_);
487 // static
488 scoped_ptr<DevToolsTargetsUIHandler>
489 DevToolsTargetsUIHandler::CreateForRenderers(
490 DevToolsTargetsUIHandler::Callback callback) {
491 return scoped_ptr<DevToolsTargetsUIHandler>(
492 new RenderViewHostTargetsUIHandler(callback));
495 // static
496 scoped_ptr<DevToolsTargetsUIHandler>
497 DevToolsTargetsUIHandler::CreateForWorkers(
498 DevToolsTargetsUIHandler::Callback callback) {
499 return scoped_ptr<DevToolsTargetsUIHandler>(
500 new WorkerTargetsUIHandler(callback));
503 // static
504 scoped_ptr<DevToolsTargetsUIHandler>
505 DevToolsTargetsUIHandler::CreateForAdb(
506 DevToolsTargetsUIHandler::Callback callback, Profile* profile) {
507 return scoped_ptr<DevToolsTargetsUIHandler>(
508 new AdbTargetsUIHandler(callback, profile));
511 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
512 const std::string& target_id) {
513 TargetMap::iterator it = targets_.find(target_id);
514 if (it != targets_.end())
515 return it->second;
516 return NULL;
519 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
520 const std::string& url,
521 const TargetCallback& callback) {
522 callback.Run(NULL);
525 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
526 const DevToolsTargetImpl& target) {
527 base::DictionaryValue* target_data = new base::DictionaryValue();
528 target_data->SetString(kTargetSourceField, source_id_);
529 target_data->SetString(kTargetIdField, target.GetId());
530 target_data->SetString(kTargetTypeField, target.GetType());
531 target_data->SetBoolean(kAttachedField, target.IsAttached());
532 target_data->SetString(kUrlField, target.GetUrl().spec());
533 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
534 target_data->SetString(kFaviconUrlField, target.GetFaviconUrl().spec());
535 target_data->SetString(kDescriptionField, target.GetDescription());
536 return target_data;
539 void DevToolsTargetsUIHandler::SendSerializedTargets(
540 scoped_ptr<base::ListValue> list) {
541 callback_.Run(source_id_, list.Pass());
544 // PortForwardingStatusSerializer ---------------------------------------------
546 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
547 const Callback& callback, Profile* profile)
548 : callback_(callback),
549 profile_(profile) {
550 PortForwardingController* port_forwarding_controller =
551 PortForwardingController::Factory::GetForProfile(profile_);
552 if (port_forwarding_controller)
553 port_forwarding_controller->AddListener(this);
556 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
557 PortForwardingController* port_forwarding_controller =
558 PortForwardingController::Factory::GetForProfile(profile_);
559 if (port_forwarding_controller)
560 port_forwarding_controller->RemoveListener(this);
563 void PortForwardingStatusSerializer::PortStatusChanged(
564 const DevicesStatus& status) {
565 base::DictionaryValue result;
566 for (DevicesStatus::const_iterator sit = status.begin();
567 sit != status.end(); ++sit) {
568 base::DictionaryValue* device_status_dict = new base::DictionaryValue();
569 const PortStatusMap& device_status_map = sit->second;
570 for (PortStatusMap::const_iterator it = device_status_map.begin();
571 it != device_status_map.end(); ++it) {
572 device_status_dict->SetInteger(
573 base::StringPrintf("%d", it->first), it->second);
576 std::string device_id = base::StringPrintf(
577 kAdbDeviceIdFormat,
578 sit->first.c_str());
579 result.Set(device_id, device_status_dict);
581 callback_.Run(result);