Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_targets_ui.cc
blob5ffdb2d74615a0a2e910f1f023d81ac223e2039a
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 "chrome/browser/devtools/devtools_adb_bridge.h"
12 #include "chrome/browser/devtools/devtools_target_impl.h"
13 #include "chrome/browser/devtools/port_forwarding_controller.h"
14 #include "content/public/browser/browser_child_process_observer.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/notification_observer.h"
18 #include "content/public/browser/notification_registrar.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_source.h"
21 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/worker_service.h"
26 #include "content/public/browser/worker_service_observer.h"
27 #include "content/public/common/process_type.h"
28 #include "net/base/escape.h"
30 using content::BrowserThread;
31 using content::WebContents;
33 namespace {
35 const char kTargetSourceField[] = "source";
36 const char kTargetSourceRenderer[] = "renderers";
37 const char kTargetSourceWorker[] = "workers";
38 const char kTargetSourceAdb[] = "adb";
40 const char kTargetIdField[] = "id";
41 const char kTargetTypeField[] = "type";
42 const char kAttachedField[] = "attached";
43 const char kUrlField[] = "url";
44 const char kNameField[] = "name";
45 const char kFaviconUrlField[] = "faviconUrl";
46 const char kDescriptionField[] = "description";
48 const char kGuestList[] = "guests";
50 const char kAdbModelField[] = "adbModel";
51 const char kAdbConnectedField[] = "adbConnected";
52 const char kAdbSerialField[] = "adbSerial";
53 const char kAdbPortStatus[] = "adbPortStatus";
54 const char kAdbBrowsersList[] = "browsers";
56 const char kAdbBrowserNameField[] = "adbBrowserName";
57 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
58 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
59 const char kAdbPagesList[] = "pages";
61 const char kAdbScreenWidthField[] = "adbScreenWidth";
62 const char kAdbScreenHeightField[] = "adbScreenHeight";
63 const char kAdbAttachedForeignField[] = "adbAttachedForeign";
65 // CancelableTimer ------------------------------------------------------------
67 class CancelableTimer {
68 public:
69 CancelableTimer(base::Closure callback, base::TimeDelta delay)
70 : callback_(callback),
71 weak_factory_(this) {
72 base::MessageLoop::current()->PostDelayedTask(
73 FROM_HERE,
74 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
75 delay);
78 private:
79 void Fire() { callback_.Run(); }
81 base::Closure callback_;
82 base::WeakPtrFactory<CancelableTimer> weak_factory_;
85 // RenderViewHostTargetsUIHandler ---------------------------------------------
87 class RenderViewHostTargetsUIHandler
88 : public DevToolsTargetsUIHandler,
89 public content::NotificationObserver {
90 public:
91 explicit RenderViewHostTargetsUIHandler(Callback callback);
92 virtual ~RenderViewHostTargetsUIHandler();
93 private:
94 // content::NotificationObserver overrides.
95 virtual void Observe(int type,
96 const content::NotificationSource& source,
97 const content::NotificationDetails& details) OVERRIDE;
99 void UpdateTargets();
101 content::NotificationRegistrar notification_registrar_;
102 scoped_ptr<CancelableTimer> timer_;
105 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
106 Callback callback)
107 : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) {
108 notification_registrar_.Add(this,
109 content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
110 content::NotificationService::AllSources());
111 notification_registrar_.Add(this,
112 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
113 content::NotificationService::AllSources());
114 notification_registrar_.Add(this,
115 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
116 content::NotificationService::AllSources());
117 UpdateTargets();
120 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
121 notification_registrar_.RemoveAll();
124 void RenderViewHostTargetsUIHandler::Observe(
125 int type,
126 const content::NotificationSource& source,
127 const content::NotificationDetails& details) {
128 const int kUpdateDelay = 100;
129 timer_.reset(
130 new CancelableTimer(
131 base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets,
132 base::Unretained(this)),
133 base::TimeDelta::FromMilliseconds(kUpdateDelay)));
136 void RenderViewHostTargetsUIHandler::UpdateTargets() {
137 scoped_ptr<base::ListValue> list_value(new base::ListValue());
139 std::map<WebContents*, base::DictionaryValue*> web_contents_to_descriptor_;
140 std::vector<DevToolsTargetImpl*> guest_targets;
142 DevToolsTargetImpl::List targets =
143 DevToolsTargetImpl::EnumerateRenderViewHostTargets();
145 STLDeleteValues(&targets_);
146 for (DevToolsTargetImpl::List::iterator it = targets.begin();
147 it != targets.end(); ++it) {
148 scoped_ptr<DevToolsTargetImpl> target(*it);
149 content::RenderViewHost* rvh = target->GetRenderViewHost();
150 if (!rvh)
151 continue;
152 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
153 if (!web_contents)
154 continue;
156 DevToolsTargetImpl* target_ptr = target.get();
157 targets_[target_ptr->GetId()] = target.release();
158 if (rvh->GetProcess()->IsGuest()) {
159 guest_targets.push_back(target_ptr);
160 } else {
161 base::DictionaryValue* descriptor = Serialize(*target_ptr);
162 list_value->Append(descriptor);
163 web_contents_to_descriptor_[web_contents] = descriptor;
167 // Add the list of guest-views to each of its embedders.
168 for (std::vector<DevToolsTargetImpl*>::iterator it(guest_targets.begin());
169 it != guest_targets.end(); ++it) {
170 DevToolsTargetImpl* guest = (*it);
171 WebContents* guest_web_contents =
172 WebContents::FromRenderViewHost(guest->GetRenderViewHost());
173 WebContents* embedder = guest_web_contents->GetEmbedderWebContents();
174 if (embedder && web_contents_to_descriptor_.count(embedder) > 0) {
175 base::DictionaryValue* parent = web_contents_to_descriptor_[embedder];
176 base::ListValue* guests = NULL;
177 if (!parent->GetList(kGuestList, &guests)) {
178 guests = new base::ListValue();
179 parent->Set(kGuestList, guests);
181 guests->Append(Serialize(*guest));
185 SendSerializedTargets(list_value.Pass());
188 // WorkerObserver -------------------------------------------------------------
190 class WorkerObserver
191 : public content::WorkerServiceObserver,
192 public base::RefCountedThreadSafe<WorkerObserver> {
193 public:
194 WorkerObserver() {}
196 void Start(DevToolsTargetImpl::Callback callback) {
197 DCHECK(callback_.is_null());
198 DCHECK(!callback.is_null());
199 callback_ = callback;
200 BrowserThread::PostTask(
201 BrowserThread::IO, FROM_HERE,
202 base::Bind(&WorkerObserver::StartOnIOThread, this));
205 void Stop() {
206 DCHECK(!callback_.is_null());
207 callback_ = DevToolsTargetImpl::Callback();
208 BrowserThread::PostTask(
209 BrowserThread::IO, FROM_HERE,
210 base::Bind(&WorkerObserver::StopOnIOThread, this));
213 void Enumerate() {
214 BrowserThread::PostTask(
215 BrowserThread::IO, FROM_HERE,
216 base::Bind(&WorkerObserver::EnumerateOnIOThread,
217 this));
220 private:
221 friend class base::RefCountedThreadSafe<WorkerObserver>;
222 virtual ~WorkerObserver() {}
224 // content::WorkerServiceObserver overrides:
225 virtual void WorkerCreated(
226 const GURL& url,
227 const base::string16& name,
228 int process_id,
229 int route_id) OVERRIDE {
230 EnumerateOnIOThread();
233 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
234 EnumerateOnIOThread();
237 void StartOnIOThread() {
238 content::WorkerService::GetInstance()->AddObserver(this);
239 EnumerateOnIOThread();
242 void StopOnIOThread() {
243 content::WorkerService::GetInstance()->RemoveObserver(this);
246 void EnumerateOnIOThread() {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
248 DevToolsTargetImpl::EnumerateWorkerTargets(
249 base::Bind(&WorkerObserver::RespondOnUIThread, this));
252 void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
254 if (callback_.is_null())
255 return;
256 callback_.Run(targets);
259 DevToolsTargetImpl::Callback callback_;
262 // WorkerTargetsUIHandler -----------------------------------------------------
264 class WorkerTargetsUIHandler
265 : public DevToolsTargetsUIHandler,
266 public content::BrowserChildProcessObserver {
267 public:
268 explicit WorkerTargetsUIHandler(Callback callback);
269 virtual ~WorkerTargetsUIHandler();
271 private:
272 // content::BrowserChildProcessObserver overrides.
273 virtual void BrowserChildProcessHostConnected(
274 const content::ChildProcessData& data) OVERRIDE;
275 virtual void BrowserChildProcessHostDisconnected(
276 const content::ChildProcessData& data) OVERRIDE;
278 void UpdateTargets(const DevToolsTargetImpl::List& targets);
280 scoped_refptr<WorkerObserver> observer_;
283 WorkerTargetsUIHandler::WorkerTargetsUIHandler(Callback callback)
284 : DevToolsTargetsUIHandler(kTargetSourceWorker, callback),
285 observer_(new WorkerObserver()) {
286 observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets,
287 base::Unretained(this)));
288 BrowserChildProcessObserver::Add(this);
291 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
292 BrowserChildProcessObserver::Remove(this);
293 observer_->Stop();
296 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected(
297 const content::ChildProcessData& data) {
298 if (data.process_type == content::PROCESS_TYPE_WORKER)
299 observer_->Enumerate();
302 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected(
303 const content::ChildProcessData& data) {
304 if (data.process_type == content::PROCESS_TYPE_WORKER)
305 observer_->Enumerate();
308 void WorkerTargetsUIHandler::UpdateTargets(
309 const DevToolsTargetImpl::List& targets) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 scoped_ptr<base::ListValue> list_value(new base::ListValue());
312 STLDeleteValues(&targets_);
313 for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
314 it != targets.end(); ++it) {
315 DevToolsTargetImpl* target = *it;
316 list_value->Append(Serialize(*target));
317 targets_[target->GetId()] = target;
319 SendSerializedTargets(list_value.Pass());
322 // AdbTargetsUIHandler --------------------------------------------------------
324 class AdbTargetsUIHandler
325 : public DevToolsRemoteTargetsUIHandler,
326 public DevToolsAdbBridge::Listener {
327 public:
328 AdbTargetsUIHandler(Callback callback, Profile* profile);
329 virtual ~AdbTargetsUIHandler();
331 virtual void Open(const std::string& browser_id,
332 const std::string& url) OVERRIDE;
334 private:
335 // DevToolsAdbBridge::Listener overrides.
336 virtual void RemoteDevicesChanged(
337 DevToolsAdbBridge::RemoteDevices* devices) OVERRIDE;
339 Profile* profile_;
341 typedef std::map<std::string,
342 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > RemoteBrowsers;
343 RemoteBrowsers remote_browsers_;
346 AdbTargetsUIHandler::AdbTargetsUIHandler(Callback callback, Profile* profile)
347 : DevToolsRemoteTargetsUIHandler(kTargetSourceAdb, callback),
348 profile_(profile) {
349 DevToolsAdbBridge* adb_bridge =
350 DevToolsAdbBridge::Factory::GetForProfile(profile_);
351 if (adb_bridge)
352 adb_bridge->AddListener(this);
355 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
356 DevToolsAdbBridge* adb_bridge =
357 DevToolsAdbBridge::Factory::GetForProfile(profile_);
358 if (adb_bridge)
359 adb_bridge->RemoveListener(this);
362 void AdbTargetsUIHandler::Open(const std::string& browser_id,
363 const std::string& url) {
364 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
365 if (it != remote_browsers_.end())
366 it->second->Open(url);
369 void AdbTargetsUIHandler::RemoteDevicesChanged(
370 DevToolsAdbBridge::RemoteDevices* devices) {
371 PortForwardingController* port_forwarding_controller =
372 PortForwardingController::Factory::GetForProfile(profile_);
373 PortForwardingController::DevicesStatus port_forwarding_status;
374 if (port_forwarding_controller)
375 port_forwarding_status =
376 port_forwarding_controller->UpdateDeviceList(*devices);
378 remote_browsers_.clear();
379 STLDeleteValues(&targets_);
381 scoped_ptr<base::ListValue> device_list(new base::ListValue());
382 for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
383 dit != devices->end(); ++dit) {
384 DevToolsAdbBridge::RemoteDevice* device = dit->get();
385 base::DictionaryValue* device_data = new base::DictionaryValue();
386 device_data->SetString(kAdbModelField, device->GetModel());
387 device_data->SetString(kAdbSerialField, device->GetSerial());
388 device_data->SetBoolean(kAdbConnectedField, device->IsConnected());
389 std::string device_id = base::StringPrintf(
390 "device:%s",
391 device->GetSerial().c_str());
392 device_data->SetString(kTargetIdField, device_id);
393 base::ListValue* browser_list = new base::ListValue();
394 device_data->Set(kAdbBrowsersList, browser_list);
396 DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers();
397 for (DevToolsAdbBridge::RemoteBrowsers::iterator bit =
398 browsers.begin(); bit != browsers.end(); ++bit) {
399 DevToolsAdbBridge::RemoteBrowser* browser = bit->get();
400 base::DictionaryValue* browser_data = new base::DictionaryValue();
401 browser_data->SetString(kAdbBrowserNameField, browser->display_name());
402 browser_data->SetString(kAdbBrowserVersionField, browser->version());
403 DevToolsAdbBridge::RemoteBrowser::ParsedVersion parsed =
404 browser->GetParsedVersion();
405 browser_data->SetInteger(
406 kAdbBrowserChromeVersionField,
407 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
408 std::string browser_id = base::StringPrintf(
409 "browser:%s:%s:%s:%s",
410 device->GetSerial().c_str(), // Ensure uniqueness across devices.
411 browser->display_name().c_str(), // Sort by display name.
412 browser->version().c_str(), // Then by version.
413 browser->socket().c_str()); // Ensure uniqueness on the device.
414 browser_data->SetString(kTargetIdField, browser_id);
415 browser_data->SetString(kTargetSourceField, source_id());
416 remote_browsers_[browser_id] = browser;
417 base::ListValue* page_list = new base::ListValue();
418 browser_data->Set(kAdbPagesList, page_list);
420 DevToolsTargetImpl::List pages = browser->CreatePageTargets();
421 for (DevToolsTargetImpl::List::iterator it =
422 pages.begin(); it != pages.end(); ++it) {
423 DevToolsTargetImpl* target = *it;
424 base::DictionaryValue* target_data = Serialize(*target);
425 target_data->SetBoolean(
426 kAdbAttachedForeignField,
427 target->IsAttached() &&
428 !DevToolsAdbBridge::HasDevToolsWindow(target->GetId()));
429 // Pass the screen size in the target object to make sure that
430 // the caching logic does not prevent the target item from updating
431 // when the screen size changes.
432 gfx::Size screen_size = device->screen_size();
433 target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
434 target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
435 targets_[target->GetId()] = target;
436 page_list->Append(target_data);
438 browser_list->Append(browser_data);
441 if (port_forwarding_controller) {
442 PortForwardingController::DevicesStatus::iterator sit =
443 port_forwarding_status.find(device->GetSerial());
444 if (sit != port_forwarding_status.end()) {
445 base::DictionaryValue* port_status_dict = new base::DictionaryValue();
446 typedef PortForwardingController::PortStatusMap StatusMap;
447 const StatusMap& port_status = sit->second;
448 for (StatusMap::const_iterator it = port_status.begin();
449 it != port_status.end(); ++it) {
450 port_status_dict->SetInteger(
451 base::StringPrintf("%d", it->first), it->second);
453 device_data->Set(kAdbPortStatus, port_status_dict);
457 device_list->Append(device_data);
459 SendSerializedTargets(device_list.Pass());
462 } // namespace
464 // DevToolsTargetsUIHandler ---------------------------------------------------
466 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
467 const std::string& source_id,
468 Callback callback)
469 : source_id_(source_id),
470 callback_(callback) {
473 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
474 STLDeleteValues(&targets_);
477 // static
478 scoped_ptr<DevToolsTargetsUIHandler>
479 DevToolsTargetsUIHandler::CreateForRenderers(
480 DevToolsTargetsUIHandler::Callback callback) {
481 return scoped_ptr<DevToolsTargetsUIHandler>(
482 new RenderViewHostTargetsUIHandler(callback));
485 // static
486 scoped_ptr<DevToolsTargetsUIHandler>
487 DevToolsTargetsUIHandler::CreateForWorkers(
488 DevToolsTargetsUIHandler::Callback callback) {
489 return scoped_ptr<DevToolsTargetsUIHandler>(
490 new WorkerTargetsUIHandler(callback));
493 void DevToolsTargetsUIHandler::Inspect(const std::string& target_id,
494 Profile* profile) {
495 TargetMap::iterator it = targets_.find(target_id);
496 if (it != targets_.end())
497 it->second->Inspect(profile);
500 void DevToolsTargetsUIHandler::Activate(const std::string& target_id) {
501 TargetMap::iterator it = targets_.find(target_id);
502 if (it != targets_.end())
503 it->second->Activate();
506 void DevToolsTargetsUIHandler::Close(const std::string& target_id) {
507 TargetMap::iterator it = targets_.find(target_id);
508 if (it != targets_.end())
509 it->second->Close();
512 void DevToolsTargetsUIHandler::Reload(const std::string& target_id) {
513 TargetMap::iterator it = targets_.find(target_id);
514 if (it != targets_.end())
515 it->second->Reload();
518 base::DictionaryValue*
519 DevToolsTargetsUIHandler::Serialize(
520 const DevToolsTargetImpl& target) {
521 base::DictionaryValue* target_data = new base::DictionaryValue();
522 target_data->SetString(kTargetSourceField, source_id_);
523 target_data->SetString(kTargetIdField, target.GetId());
524 target_data->SetString(kTargetTypeField, target.GetType());
525 target_data->SetBoolean(kAttachedField, target.IsAttached());
526 target_data->SetString(kUrlField, target.GetUrl().spec());
527 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
528 target_data->SetString(kFaviconUrlField, target.GetFaviconUrl().spec());
529 target_data->SetString(kDescriptionField, target.GetDescription());
530 return target_data;
533 void DevToolsTargetsUIHandler::SendSerializedTargets(
534 scoped_ptr<base::ListValue> list) {
535 callback_.Run(source_id_, list.Pass());
538 // DevToolsRemoteTargetsUIHandler ---------------------------------------------
540 DevToolsRemoteTargetsUIHandler::DevToolsRemoteTargetsUIHandler(
541 const std::string& source_id,
542 Callback callback)
543 : DevToolsTargetsUIHandler(source_id, callback) {
546 // static
547 scoped_ptr<DevToolsRemoteTargetsUIHandler>
548 DevToolsRemoteTargetsUIHandler::CreateForAdb(
549 DevToolsTargetsUIHandler::Callback callback, Profile* profile) {
550 return scoped_ptr<DevToolsRemoteTargetsUIHandler>(
551 new AdbTargetsUIHandler(callback, profile));