[Presentation API, Android] Implement basic messaging
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_targets_ui.cc
blobced9c166332962971b7f06d4dbfc2ec0b93e3367
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/location.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "base/version.h"
16 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
17 #include "chrome/browser/devtools/devtools_target_impl.h"
18 #include "content/public/browser/browser_child_process_observer.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/browser/notification_observer.h"
22 #include "content/public/browser/notification_registrar.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/worker_service.h"
27 #include "content/public/browser/worker_service_observer.h"
28 #include "content/public/common/process_type.h"
29 #include "net/base/escape.h"
31 using content::BrowserThread;
33 namespace {
35 const char kTargetSourceField[] = "source";
36 const char kTargetSourceLocal[] = "local";
37 const char kTargetSourceRemote[] = "remote";
39 const char kTargetIdField[] = "id";
40 const char kTargetTypeField[] = "type";
41 const char kAttachedField[] = "attached";
42 const char kUrlField[] = "url";
43 const char kNameField[] = "name";
44 const char kFaviconUrlField[] = "faviconUrl";
45 const char kDescriptionField[] = "description";
47 const char kGuestList[] = "guests";
49 const char kAdbModelField[] = "adbModel";
50 const char kAdbConnectedField[] = "adbConnected";
51 const char kAdbSerialField[] = "adbSerial";
52 const char kAdbBrowsersList[] = "browsers";
53 const char kAdbDeviceIdFormat[] = "device:%s";
55 const char kAdbBrowserNameField[] = "adbBrowserName";
56 const char kAdbBrowserUserField[] = "adbBrowserUser";
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 const char kPortForwardingPorts[] = "ports";
66 const char kPortForwardingBrowserId[] = "browserId";
68 // CancelableTimer ------------------------------------------------------------
70 class CancelableTimer {
71 public:
72 CancelableTimer(base::Closure callback, base::TimeDelta delay)
73 : callback_(callback),
74 weak_factory_(this) {
75 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
76 FROM_HERE,
77 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()), delay);
80 private:
81 void Fire() { callback_.Run(); }
83 base::Closure callback_;
84 base::WeakPtrFactory<CancelableTimer> weak_factory_;
87 // WorkerObserver -------------------------------------------------------------
89 class WorkerObserver
90 : public content::WorkerServiceObserver,
91 public base::RefCountedThreadSafe<WorkerObserver> {
92 public:
93 WorkerObserver() {}
95 void Start(base::Closure callback) {
96 DCHECK(callback_.is_null());
97 DCHECK(!callback.is_null());
98 callback_ = callback;
99 BrowserThread::PostTask(
100 BrowserThread::IO, FROM_HERE,
101 base::Bind(&WorkerObserver::StartOnIOThread, this));
104 void Stop() {
105 DCHECK(!callback_.is_null());
106 callback_ = base::Closure();
107 BrowserThread::PostTask(
108 BrowserThread::IO, FROM_HERE,
109 base::Bind(&WorkerObserver::StopOnIOThread, this));
112 private:
113 friend class base::RefCountedThreadSafe<WorkerObserver>;
114 ~WorkerObserver() override {}
116 // content::WorkerServiceObserver overrides:
117 void WorkerCreated(const GURL& url,
118 const base::string16& name,
119 int process_id,
120 int route_id) override {
121 NotifyOnIOThread();
124 void WorkerDestroyed(int process_id, int route_id) override {
125 NotifyOnIOThread();
128 void StartOnIOThread() {
129 content::WorkerService::GetInstance()->AddObserver(this);
132 void StopOnIOThread() {
133 content::WorkerService::GetInstance()->RemoveObserver(this);
136 void NotifyOnIOThread() {
137 DCHECK_CURRENTLY_ON(BrowserThread::IO);
138 BrowserThread::PostTask(
139 BrowserThread::UI, FROM_HERE,
140 base::Bind(&WorkerObserver::NotifyOnUIThread, this));
143 void NotifyOnUIThread() {
144 DCHECK_CURRENTLY_ON(BrowserThread::UI);
145 if (callback_.is_null())
146 return;
147 callback_.Run();
150 // Accessed on UI thread.
151 base::Closure callback_;
154 // LocalTargetsUIHandler ---------------------------------------------
156 class LocalTargetsUIHandler
157 : public DevToolsTargetsUIHandler,
158 public content::NotificationObserver {
159 public:
160 explicit LocalTargetsUIHandler(const Callback& callback);
161 ~LocalTargetsUIHandler() override;
163 // DevToolsTargetsUIHandler overrides.
164 void ForceUpdate() override;
166 private:
167 // content::NotificationObserver overrides.
168 void Observe(int type,
169 const content::NotificationSource& source,
170 const content::NotificationDetails& details) override;
172 void ScheduleUpdate();
173 void UpdateTargets();
174 void SendTargets(const std::vector<DevToolsTargetImpl*>& targets);
176 content::NotificationRegistrar notification_registrar_;
177 scoped_ptr<CancelableTimer> timer_;
178 scoped_refptr<WorkerObserver> observer_;
179 base::WeakPtrFactory<LocalTargetsUIHandler> weak_factory_;
182 LocalTargetsUIHandler::LocalTargetsUIHandler(
183 const Callback& callback)
184 : DevToolsTargetsUIHandler(kTargetSourceLocal, callback),
185 observer_(new WorkerObserver()),
186 weak_factory_(this) {
187 notification_registrar_.Add(this,
188 content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
189 content::NotificationService::AllSources());
190 notification_registrar_.Add(this,
191 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
192 content::NotificationService::AllSources());
193 notification_registrar_.Add(this,
194 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
195 content::NotificationService::AllSources());
196 observer_->Start(base::Bind(&LocalTargetsUIHandler::ScheduleUpdate,
197 base::Unretained(this)));
198 UpdateTargets();
201 LocalTargetsUIHandler::~LocalTargetsUIHandler() {
202 notification_registrar_.RemoveAll();
203 observer_->Stop();
206 void LocalTargetsUIHandler::Observe(
207 int type,
208 const content::NotificationSource& source,
209 const content::NotificationDetails& details) {
210 ScheduleUpdate();
213 void LocalTargetsUIHandler::ForceUpdate() {
214 ScheduleUpdate();
217 void LocalTargetsUIHandler::ScheduleUpdate() {
218 const int kUpdateDelay = 100;
219 timer_.reset(
220 new CancelableTimer(
221 base::Bind(&LocalTargetsUIHandler::UpdateTargets,
222 base::Unretained(this)),
223 base::TimeDelta::FromMilliseconds(kUpdateDelay)));
226 void LocalTargetsUIHandler::UpdateTargets() {
227 SendTargets(DevToolsTargetImpl::EnumerateAll());
230 void LocalTargetsUIHandler::SendTargets(
231 const std::vector<DevToolsTargetImpl*>& targets) {
232 base::ListValue list_value;
233 std::map<std::string, base::DictionaryValue*> id_to_descriptor;
235 STLDeleteValues(&targets_);
236 for (DevToolsTargetImpl* target : targets) {
237 targets_[target->GetId()] = target;
238 id_to_descriptor[target->GetId()] = Serialize(*target);
241 for (TargetMap::iterator it(targets_.begin()); it != targets_.end(); ++it) {
242 DevToolsTargetImpl* target = it->second;
243 base::DictionaryValue* descriptor = id_to_descriptor[target->GetId()];
244 std::string parent_id = target->GetParentId();
245 if (parent_id.empty() || id_to_descriptor.count(parent_id) == 0) {
246 list_value.Append(descriptor);
247 } else {
248 base::DictionaryValue* parent = id_to_descriptor[parent_id];
249 base::ListValue* guests = NULL;
250 if (!parent->GetList(kGuestList, &guests)) {
251 guests = new base::ListValue();
252 parent->Set(kGuestList, guests);
254 guests->Append(descriptor);
258 SendSerializedTargets(list_value);
261 // AdbTargetsUIHandler --------------------------------------------------------
263 class AdbTargetsUIHandler
264 : public DevToolsTargetsUIHandler,
265 public DevToolsAndroidBridge::DeviceListListener {
266 public:
267 AdbTargetsUIHandler(const Callback& callback, Profile* profile);
268 ~AdbTargetsUIHandler() override;
270 void Open(const std::string& browser_id, const std::string& url) override;
272 scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
273 const std::string& browser_id) override;
275 private:
276 // DevToolsAndroidBridge::Listener overrides.
277 void DeviceListChanged(
278 const DevToolsAndroidBridge::RemoteDevices& devices) override;
280 DevToolsAndroidBridge* GetAndroidBridge();
282 Profile* const profile_;
283 DevToolsAndroidBridge* const android_bridge_;
285 typedef std::map<std::string,
286 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
287 RemoteBrowsers remote_browsers_;
290 AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback& callback,
291 Profile* profile)
292 : DevToolsTargetsUIHandler(kTargetSourceRemote, callback),
293 profile_(profile),
294 android_bridge_(
295 DevToolsAndroidBridge::Factory::GetForProfile(profile_)) {
296 DCHECK(android_bridge_);
297 android_bridge_->AddDeviceListListener(this);
300 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
301 android_bridge_->RemoveDeviceListListener(this);
304 void AdbTargetsUIHandler::Open(const std::string& browser_id,
305 const std::string& url) {
306 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
307 if (it != remote_browsers_.end())
308 android_bridge_->OpenRemotePage(it->second, url);
311 scoped_refptr<content::DevToolsAgentHost>
312 AdbTargetsUIHandler::GetBrowserAgentHost(
313 const std::string& browser_id) {
314 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
315 if (it == remote_browsers_.end())
316 return NULL;
318 return android_bridge_->GetBrowserAgentHost(it->second);
321 void AdbTargetsUIHandler::DeviceListChanged(
322 const DevToolsAndroidBridge::RemoteDevices& devices) {
323 remote_browsers_.clear();
324 STLDeleteValues(&targets_);
326 base::ListValue device_list;
327 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
328 devices.begin(); dit != devices.end(); ++dit) {
329 DevToolsAndroidBridge::RemoteDevice* device = dit->get();
330 base::DictionaryValue* device_data = new base::DictionaryValue();
331 device_data->SetString(kAdbModelField, device->model());
332 device_data->SetString(kAdbSerialField, device->serial());
333 device_data->SetBoolean(kAdbConnectedField, device->is_connected());
334 std::string device_id = base::StringPrintf(
335 kAdbDeviceIdFormat,
336 device->serial().c_str());
337 device_data->SetString(kTargetIdField, device_id);
338 base::ListValue* browser_list = new base::ListValue();
339 device_data->Set(kAdbBrowsersList, browser_list);
341 DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
342 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
343 browsers.begin(); bit != browsers.end(); ++bit) {
344 DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
345 base::DictionaryValue* browser_data = new base::DictionaryValue();
346 browser_data->SetString(kAdbBrowserNameField, browser->display_name());
347 browser_data->SetString(kAdbBrowserUserField, browser->user());
348 browser_data->SetString(kAdbBrowserVersionField, browser->version());
349 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
350 browser->GetParsedVersion();
351 browser_data->SetInteger(
352 kAdbBrowserChromeVersionField,
353 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
354 std::string browser_id = browser->GetId();
355 browser_data->SetString(kTargetIdField, browser_id);
356 browser_data->SetString(kTargetSourceField, source_id());
358 base::ListValue* page_list = new base::ListValue();
359 remote_browsers_[browser_id] = browser;
360 browser_data->Set(kAdbPagesList, page_list);
361 for (const auto& page : browser->pages()) {
362 DevToolsTargetImpl* target = android_bridge_->CreatePageTarget(page);
363 base::DictionaryValue* target_data = Serialize(*target);
364 target_data->SetBoolean(
365 kAdbAttachedForeignField,
366 target->IsAttached() &&
367 !android_bridge_->HasDevToolsWindow(target->GetId()));
368 // Pass the screen size in the target object to make sure that
369 // the caching logic does not prevent the target item from updating
370 // when the screen size changes.
371 gfx::Size screen_size = device->screen_size();
372 target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
373 target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
374 targets_[target->GetId()] = target;
375 page_list->Append(target_data);
377 browser_list->Append(browser_data);
380 device_list.Append(device_data);
382 SendSerializedTargets(device_list);
385 } // namespace
387 // DevToolsTargetsUIHandler ---------------------------------------------------
389 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
390 const std::string& source_id,
391 const Callback& callback)
392 : source_id_(source_id),
393 callback_(callback) {
396 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
397 STLDeleteValues(&targets_);
400 // static
401 scoped_ptr<DevToolsTargetsUIHandler>
402 DevToolsTargetsUIHandler::CreateForLocal(
403 const DevToolsTargetsUIHandler::Callback& callback) {
404 return scoped_ptr<DevToolsTargetsUIHandler>(
405 new LocalTargetsUIHandler(callback));
408 // static
409 scoped_ptr<DevToolsTargetsUIHandler>
410 DevToolsTargetsUIHandler::CreateForAdb(
411 const DevToolsTargetsUIHandler::Callback& callback, Profile* profile) {
412 return scoped_ptr<DevToolsTargetsUIHandler>(
413 new AdbTargetsUIHandler(callback, profile));
416 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
417 const std::string& target_id) {
418 TargetMap::iterator it = targets_.find(target_id);
419 if (it != targets_.end())
420 return it->second;
421 return NULL;
424 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
425 const std::string& url) {
428 scoped_refptr<content::DevToolsAgentHost>
429 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
430 return NULL;
433 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
434 const DevToolsTargetImpl& target) {
435 base::DictionaryValue* target_data = new base::DictionaryValue();
436 target_data->SetString(kTargetSourceField, source_id_);
437 target_data->SetString(kTargetIdField, target.GetId());
438 target_data->SetString(kTargetTypeField, target.GetType());
439 target_data->SetBoolean(kAttachedField, target.IsAttached());
440 target_data->SetString(kUrlField, target.GetURL().spec());
441 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
442 target_data->SetString(kFaviconUrlField, target.GetFaviconURL().spec());
443 target_data->SetString(kDescriptionField, target.GetDescription());
444 return target_data;
447 void DevToolsTargetsUIHandler::SendSerializedTargets(
448 const base::ListValue& list) {
449 callback_.Run(source_id_, list);
452 void DevToolsTargetsUIHandler::ForceUpdate() {
455 // PortForwardingStatusSerializer ---------------------------------------------
457 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
458 const Callback& callback, Profile* profile)
459 : callback_(callback),
460 profile_(profile) {
461 DevToolsAndroidBridge* android_bridge =
462 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
463 if (android_bridge)
464 android_bridge->AddPortForwardingListener(this);
467 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
468 DevToolsAndroidBridge* android_bridge =
469 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
470 if (android_bridge)
471 android_bridge->RemovePortForwardingListener(this);
474 void PortForwardingStatusSerializer::PortStatusChanged(
475 const ForwardingStatus& status) {
476 base::DictionaryValue result;
477 for (ForwardingStatus::const_iterator sit = status.begin();
478 sit != status.end(); ++sit) {
479 base::DictionaryValue* port_status_dict = new base::DictionaryValue();
480 const PortStatusMap& port_status_map = sit->second;
481 for (PortStatusMap::const_iterator it = port_status_map.begin();
482 it != port_status_map.end(); ++it) {
483 port_status_dict->SetInteger(base::IntToString(it->first), it->second);
486 base::DictionaryValue* device_status_dict = new base::DictionaryValue();
487 device_status_dict->Set(kPortForwardingPorts, port_status_dict);
488 device_status_dict->SetString(kPortForwardingBrowserId,
489 sit->first->GetId());
491 std::string device_id = base::StringPrintf(
492 kAdbDeviceIdFormat,
493 sit->first->serial().c_str());
494 result.Set(device_id, device_status_dict);
496 callback_.Run(result);