[DevTools] Remove remote browser version restriction in chrome://inspect
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_targets_ui.cc
blobe84a602096440eaf4566f24d999713ed9523fd7e
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/worker_service.h"
24 #include "content/public/browser/worker_service_observer.h"
25 #include "content/public/common/process_type.h"
26 #include "net/base/escape.h"
28 using content::BrowserThread;
30 namespace {
32 const char kTargetSourceField[] = "source";
33 const char kTargetSourceLocal[] = "local";
34 const char kTargetSourceRemote[] = "remote";
36 const char kTargetIdField[] = "id";
37 const char kTargetTypeField[] = "type";
38 const char kAttachedField[] = "attached";
39 const char kUrlField[] = "url";
40 const char kNameField[] = "name";
41 const char kFaviconUrlField[] = "faviconUrl";
42 const char kDescriptionField[] = "description";
44 const char kGuestList[] = "guests";
46 const char kAdbModelField[] = "adbModel";
47 const char kAdbConnectedField[] = "adbConnected";
48 const char kAdbSerialField[] = "adbSerial";
49 const char kAdbBrowsersList[] = "browsers";
50 const char kAdbDeviceIdFormat[] = "device:%s";
52 const char kAdbBrowserNameField[] = "adbBrowserName";
53 const char kAdbBrowserUserField[] = "adbBrowserUser";
54 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
55 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
56 const char kAdbPagesList[] = "pages";
58 const char kAdbScreenWidthField[] = "adbScreenWidth";
59 const char kAdbScreenHeightField[] = "adbScreenHeight";
60 const char kAdbAttachedForeignField[] = "adbAttachedForeign";
62 const char kPortForwardingPorts[] = "ports";
63 const char kPortForwardingBrowserId[] = "browserId";
65 std::string SerializeBrowserId(
66 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser) {
67 return base::StringPrintf(
68 "browser:%s:%s:%s:%s",
69 browser->serial().c_str(), // Ensure uniqueness across devices.
70 browser->display_name().c_str(), // Sort by display name.
71 browser->version().c_str(), // Then by version.
72 browser->socket().c_str()); // Ensure uniqueness on the device.
75 // CancelableTimer ------------------------------------------------------------
77 class CancelableTimer {
78 public:
79 CancelableTimer(base::Closure callback, base::TimeDelta delay)
80 : callback_(callback),
81 weak_factory_(this) {
82 base::MessageLoop::current()->PostDelayedTask(
83 FROM_HERE,
84 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
85 delay);
88 private:
89 void Fire() { callback_.Run(); }
91 base::Closure callback_;
92 base::WeakPtrFactory<CancelableTimer> weak_factory_;
95 // WorkerObserver -------------------------------------------------------------
97 class WorkerObserver
98 : public content::WorkerServiceObserver,
99 public base::RefCountedThreadSafe<WorkerObserver> {
100 public:
101 WorkerObserver() {}
103 void Start(base::Closure callback) {
104 DCHECK(callback_.is_null());
105 DCHECK(!callback.is_null());
106 callback_ = callback;
107 BrowserThread::PostTask(
108 BrowserThread::IO, FROM_HERE,
109 base::Bind(&WorkerObserver::StartOnIOThread, this));
112 void Stop() {
113 DCHECK(!callback_.is_null());
114 callback_ = base::Closure();
115 BrowserThread::PostTask(
116 BrowserThread::IO, FROM_HERE,
117 base::Bind(&WorkerObserver::StopOnIOThread, this));
120 private:
121 friend class base::RefCountedThreadSafe<WorkerObserver>;
122 ~WorkerObserver() override {}
124 // content::WorkerServiceObserver overrides:
125 void WorkerCreated(const GURL& url,
126 const base::string16& name,
127 int process_id,
128 int route_id) override {
129 NotifyOnIOThread();
132 void WorkerDestroyed(int process_id, int route_id) override {
133 NotifyOnIOThread();
136 void StartOnIOThread() {
137 content::WorkerService::GetInstance()->AddObserver(this);
140 void StopOnIOThread() {
141 content::WorkerService::GetInstance()->RemoveObserver(this);
144 void NotifyOnIOThread() {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
146 BrowserThread::PostTask(
147 BrowserThread::UI, FROM_HERE,
148 base::Bind(&WorkerObserver::NotifyOnUIThread, this));
151 void NotifyOnUIThread() {
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153 if (callback_.is_null())
154 return;
155 callback_.Run();
158 // Accessed on UI thread.
159 base::Closure callback_;
162 // LocalTargetsUIHandler ---------------------------------------------
164 class LocalTargetsUIHandler
165 : public DevToolsTargetsUIHandler,
166 public content::NotificationObserver {
167 public:
168 explicit LocalTargetsUIHandler(const Callback& callback);
169 ~LocalTargetsUIHandler() override;
171 // DevToolsTargetsUIHandler overrides.
172 void ForceUpdate() override;
174 private:
175 // content::NotificationObserver overrides.
176 void Observe(int type,
177 const content::NotificationSource& source,
178 const content::NotificationDetails& details) override;
180 void ScheduleUpdate();
181 void UpdateTargets();
182 void SendTargets(const DevToolsTargetImpl::List& targets);
184 content::NotificationRegistrar notification_registrar_;
185 scoped_ptr<CancelableTimer> timer_;
186 scoped_refptr<WorkerObserver> observer_;
187 base::WeakPtrFactory<LocalTargetsUIHandler> weak_factory_;
190 LocalTargetsUIHandler::LocalTargetsUIHandler(
191 const Callback& callback)
192 : DevToolsTargetsUIHandler(kTargetSourceLocal, callback),
193 observer_(new WorkerObserver()),
194 weak_factory_(this) {
195 notification_registrar_.Add(this,
196 content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
197 content::NotificationService::AllSources());
198 notification_registrar_.Add(this,
199 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
200 content::NotificationService::AllSources());
201 notification_registrar_.Add(this,
202 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
203 content::NotificationService::AllSources());
204 observer_->Start(base::Bind(&LocalTargetsUIHandler::ScheduleUpdate,
205 base::Unretained(this)));
206 UpdateTargets();
209 LocalTargetsUIHandler::~LocalTargetsUIHandler() {
210 notification_registrar_.RemoveAll();
211 observer_->Stop();
214 void LocalTargetsUIHandler::Observe(
215 int type,
216 const content::NotificationSource& source,
217 const content::NotificationDetails& details) {
218 ScheduleUpdate();
221 void LocalTargetsUIHandler::ForceUpdate() {
222 ScheduleUpdate();
225 void LocalTargetsUIHandler::ScheduleUpdate() {
226 const int kUpdateDelay = 100;
227 timer_.reset(
228 new CancelableTimer(
229 base::Bind(&LocalTargetsUIHandler::UpdateTargets,
230 base::Unretained(this)),
231 base::TimeDelta::FromMilliseconds(kUpdateDelay)));
234 void LocalTargetsUIHandler::UpdateTargets() {
235 DevToolsTargetImpl::EnumerateAllTargets(base::Bind(
236 &LocalTargetsUIHandler::SendTargets,
237 weak_factory_.GetWeakPtr()));
240 void LocalTargetsUIHandler::SendTargets(
241 const DevToolsTargetImpl::List& targets) {
242 base::ListValue list_value;
243 std::map<std::string, base::DictionaryValue*> id_to_descriptor;
245 STLDeleteValues(&targets_);
246 for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
247 it != targets.end(); ++it) {
248 DevToolsTargetImpl* target = *it;
249 targets_[target->GetId()] = target;
250 id_to_descriptor[target->GetId()] = Serialize(*target);
253 for (TargetMap::iterator it(targets_.begin()); it != targets_.end(); ++it) {
254 DevToolsTargetImpl* target = it->second;
255 base::DictionaryValue* descriptor = id_to_descriptor[target->GetId()];
256 std::string parent_id = target->GetParentId();
257 if (parent_id.empty() || id_to_descriptor.count(parent_id) == 0) {
258 list_value.Append(descriptor);
259 } else {
260 base::DictionaryValue* parent = id_to_descriptor[parent_id];
261 base::ListValue* guests = NULL;
262 if (!parent->GetList(kGuestList, &guests)) {
263 guests = new base::ListValue();
264 parent->Set(kGuestList, guests);
266 guests->Append(descriptor);
270 SendSerializedTargets(list_value);
273 // AdbTargetsUIHandler --------------------------------------------------------
275 class AdbTargetsUIHandler
276 : public DevToolsTargetsUIHandler,
277 public DevToolsAndroidBridge::DeviceListListener {
278 public:
279 AdbTargetsUIHandler(const Callback& callback, Profile* profile);
280 ~AdbTargetsUIHandler() override;
282 void Open(const std::string& browser_id,
283 const std::string& url,
284 const DevToolsTargetsUIHandler::TargetCallback&) override;
286 scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
287 const std::string& browser_id) override;
289 private:
290 // DevToolsAndroidBridge::Listener overrides.
291 void DeviceListChanged(
292 const DevToolsAndroidBridge::RemoteDevices& devices) override;
294 DevToolsAndroidBridge* GetAndroidBridge();
296 Profile* const profile_;
297 DevToolsAndroidBridge* const android_bridge_;
299 typedef std::map<std::string,
300 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
301 RemoteBrowsers remote_browsers_;
304 AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback& callback,
305 Profile* profile)
306 : DevToolsTargetsUIHandler(kTargetSourceRemote, callback),
307 profile_(profile),
308 android_bridge_(
309 DevToolsAndroidBridge::Factory::GetForProfile(profile_)) {
310 DCHECK(android_bridge_);
311 android_bridge_->AddDeviceListListener(this);
314 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
315 android_bridge_->RemoveDeviceListListener(this);
318 static void CallOnTarget(
319 const DevToolsTargetsUIHandler::TargetCallback& callback,
320 DevToolsAndroidBridge* bridge,
321 scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
322 callback.Run(bridge && page.get() ? bridge->CreatePageTarget(page) : nullptr);
325 void AdbTargetsUIHandler::Open(
326 const std::string& browser_id,
327 const std::string& url,
328 const DevToolsTargetsUIHandler::TargetCallback& callback) {
329 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
330 if (it == remote_browsers_.end())
331 return;
333 android_bridge_->OpenRemotePage(
334 it->second,
335 url,
336 base::Bind(&CallOnTarget, callback, android_bridge_));
339 scoped_refptr<content::DevToolsAgentHost>
340 AdbTargetsUIHandler::GetBrowserAgentHost(
341 const std::string& browser_id) {
342 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
343 if (it == remote_browsers_.end())
344 return NULL;
346 return android_bridge_->GetBrowserAgentHost(it->second);
349 void AdbTargetsUIHandler::DeviceListChanged(
350 const DevToolsAndroidBridge::RemoteDevices& devices) {
351 remote_browsers_.clear();
352 STLDeleteValues(&targets_);
354 base::ListValue device_list;
355 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
356 devices.begin(); dit != devices.end(); ++dit) {
357 DevToolsAndroidBridge::RemoteDevice* device = dit->get();
358 base::DictionaryValue* device_data = new base::DictionaryValue();
359 device_data->SetString(kAdbModelField, device->model());
360 device_data->SetString(kAdbSerialField, device->serial());
361 device_data->SetBoolean(kAdbConnectedField, device->is_connected());
362 std::string device_id = base::StringPrintf(
363 kAdbDeviceIdFormat,
364 device->serial().c_str());
365 device_data->SetString(kTargetIdField, device_id);
366 base::ListValue* browser_list = new base::ListValue();
367 device_data->Set(kAdbBrowsersList, browser_list);
369 DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
370 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
371 browsers.begin(); bit != browsers.end(); ++bit) {
372 DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
373 base::DictionaryValue* browser_data = new base::DictionaryValue();
374 browser_data->SetString(kAdbBrowserNameField, browser->display_name());
375 browser_data->SetString(kAdbBrowserUserField, browser->user());
376 browser_data->SetString(kAdbBrowserVersionField, browser->version());
377 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
378 browser->GetParsedVersion();
379 browser_data->SetInteger(
380 kAdbBrowserChromeVersionField,
381 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
382 std::string browser_id = SerializeBrowserId(browser);
383 browser_data->SetString(kTargetIdField, browser_id);
384 browser_data->SetString(kTargetSourceField, source_id());
386 base::ListValue* page_list = new base::ListValue();
387 remote_browsers_[browser_id] = browser;
388 browser_data->Set(kAdbPagesList, page_list);
389 for (const auto& page : browser->pages()) {
390 DevToolsTargetImpl* target = android_bridge_->CreatePageTarget(page);
391 base::DictionaryValue* target_data = Serialize(*target);
392 target_data->SetBoolean(
393 kAdbAttachedForeignField,
394 target->IsAttached() &&
395 !android_bridge_->HasDevToolsWindow(target->GetId()));
396 // Pass the screen size in the target object to make sure that
397 // the caching logic does not prevent the target item from updating
398 // when the screen size changes.
399 gfx::Size screen_size = device->screen_size();
400 target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
401 target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
402 targets_[target->GetId()] = target;
403 page_list->Append(target_data);
405 browser_list->Append(browser_data);
408 device_list.Append(device_data);
410 SendSerializedTargets(device_list);
413 } // namespace
415 // DevToolsTargetsUIHandler ---------------------------------------------------
417 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
418 const std::string& source_id,
419 const Callback& callback)
420 : source_id_(source_id),
421 callback_(callback) {
424 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
425 STLDeleteValues(&targets_);
428 // static
429 scoped_ptr<DevToolsTargetsUIHandler>
430 DevToolsTargetsUIHandler::CreateForLocal(
431 const DevToolsTargetsUIHandler::Callback& callback) {
432 return scoped_ptr<DevToolsTargetsUIHandler>(
433 new LocalTargetsUIHandler(callback));
436 // static
437 scoped_ptr<DevToolsTargetsUIHandler>
438 DevToolsTargetsUIHandler::CreateForAdb(
439 const DevToolsTargetsUIHandler::Callback& callback, Profile* profile) {
440 return scoped_ptr<DevToolsTargetsUIHandler>(
441 new AdbTargetsUIHandler(callback, profile));
444 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
445 const std::string& target_id) {
446 TargetMap::iterator it = targets_.find(target_id);
447 if (it != targets_.end())
448 return it->second;
449 return NULL;
452 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
453 const std::string& url,
454 const TargetCallback& callback) {
455 callback.Run(NULL);
458 scoped_refptr<content::DevToolsAgentHost>
459 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
460 return NULL;
463 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
464 const DevToolsTargetImpl& target) {
465 base::DictionaryValue* target_data = new base::DictionaryValue();
466 target_data->SetString(kTargetSourceField, source_id_);
467 target_data->SetString(kTargetIdField, target.GetId());
468 target_data->SetString(kTargetTypeField, target.GetType());
469 target_data->SetBoolean(kAttachedField, target.IsAttached());
470 target_data->SetString(kUrlField, target.GetURL().spec());
471 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
472 target_data->SetString(kFaviconUrlField, target.GetFaviconURL().spec());
473 target_data->SetString(kDescriptionField, target.GetDescription());
474 return target_data;
477 void DevToolsTargetsUIHandler::SendSerializedTargets(
478 const base::ListValue& list) {
479 callback_.Run(source_id_, list);
482 void DevToolsTargetsUIHandler::ForceUpdate() {
485 // PortForwardingStatusSerializer ---------------------------------------------
487 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
488 const Callback& callback, Profile* profile)
489 : callback_(callback),
490 profile_(profile) {
491 DevToolsAndroidBridge* android_bridge =
492 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
493 if (android_bridge)
494 android_bridge->AddPortForwardingListener(this);
497 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
498 DevToolsAndroidBridge* android_bridge =
499 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
500 if (android_bridge)
501 android_bridge->RemovePortForwardingListener(this);
504 void PortForwardingStatusSerializer::PortStatusChanged(
505 const ForwardingStatus& status) {
506 base::DictionaryValue result;
507 for (ForwardingStatus::const_iterator sit = status.begin();
508 sit != status.end(); ++sit) {
509 base::DictionaryValue* port_status_dict = new base::DictionaryValue();
510 const PortStatusMap& port_status_map = sit->second;
511 for (PortStatusMap::const_iterator it = port_status_map.begin();
512 it != port_status_map.end(); ++it) {
513 port_status_dict->SetInteger(
514 base::StringPrintf("%d", it->first), it->second);
517 base::DictionaryValue* device_status_dict = new base::DictionaryValue();
518 device_status_dict->Set(kPortForwardingPorts, port_status_dict);
519 device_status_dict->SetString(kPortForwardingBrowserId,
520 SerializeBrowserId(sit->first));
522 std::string device_id = base::StringPrintf(
523 kAdbDeviceIdFormat,
524 sit->first->serial().c_str());
525 result.Set(device_id, device_status_dict);
527 callback_.Run(result);