Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / devtools / devtools_targets_ui.cc
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.
4
5 #include "chrome/browser/devtools/devtools_targets_ui.h"
6
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"
27
28 using content::BrowserThread;
29
30 namespace {
31
32 const char kTargetSourceField[]  = "source";
33 const char kTargetSourceRenderer[]  = "renderers";
34 const char kTargetSourceWorker[]  = "workers";
35 const char kTargetSourceAdb[]  = "adb";
36
37 const char kTargetIdField[]  = "id";
38 const char kTargetTypeField[]  = "type";
39 const char kAttachedField[]  = "attached";
40 const char kUrlField[]  = "url";
41 const char kNameField[]  = "name";
42 const char kFaviconUrlField[] = "faviconUrl";
43 const char kDescriptionField[] = "description";
44
45 const char kGuestList[] = "guests";
46
47 const char kAdbModelField[] = "adbModel";
48 const char kAdbConnectedField[] = "adbConnected";
49 const char kAdbSerialField[] = "adbSerial";
50 const char kAdbBrowsersList[] = "browsers";
51 const char kAdbDeviceIdFormat[] = "device:%s";
52
53 const char kAdbBrowserNameField[] = "adbBrowserName";
54 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
55 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
56 const char kCompatibleVersion[] = "compatibleVersion";
57 const char kAdbPagesList[] = "pages";
58
59 const char kAdbScreenWidthField[] = "adbScreenWidth";
60 const char kAdbScreenHeightField[] = "adbScreenHeight";
61 const char kAdbAttachedForeignField[]  = "adbAttachedForeign";
62
63 // CancelableTimer ------------------------------------------------------------
64
65 class CancelableTimer {
66  public:
67   CancelableTimer(base::Closure callback, base::TimeDelta delay)
68       : callback_(callback),
69         weak_factory_(this) {
70     base::MessageLoop::current()->PostDelayedTask(
71         FROM_HERE,
72         base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
73         delay);
74   }
75
76  private:
77   void Fire() { callback_.Run(); }
78
79   base::Closure callback_;
80   base::WeakPtrFactory<CancelableTimer> weak_factory_;
81 };
82
83 // RenderViewHostTargetsUIHandler ---------------------------------------------
84
85 class RenderViewHostTargetsUIHandler
86     : public DevToolsTargetsUIHandler,
87       public content::NotificationObserver {
88  public:
89   explicit RenderViewHostTargetsUIHandler(const Callback& callback);
90   virtual ~RenderViewHostTargetsUIHandler();
91
92  private:
93   // content::NotificationObserver overrides.
94   virtual void Observe(int type,
95                        const content::NotificationSource& source,
96                        const content::NotificationDetails& details) OVERRIDE;
97
98   void UpdateTargets();
99
100   content::NotificationRegistrar notification_registrar_;
101   scoped_ptr<CancelableTimer> timer_;
102 };
103
104 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
105     const Callback& callback)
106     : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) {
107   notification_registrar_.Add(this,
108                               content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
109                               content::NotificationService::AllSources());
110   notification_registrar_.Add(this,
111                               content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
112                               content::NotificationService::AllSources());
113   notification_registrar_.Add(this,
114                               content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
115                               content::NotificationService::AllSources());
116   UpdateTargets();
117 }
118
119 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
120   notification_registrar_.RemoveAll();
121 }
122
123 void RenderViewHostTargetsUIHandler::Observe(
124     int type,
125     const content::NotificationSource& source,
126     const content::NotificationDetails& details) {
127   const int kUpdateDelay = 100;
128   timer_.reset(
129       new CancelableTimer(
130           base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets,
131                      base::Unretained(this)),
132           base::TimeDelta::FromMilliseconds(kUpdateDelay)));
133 }
134
135 void RenderViewHostTargetsUIHandler::UpdateTargets() {
136   base::ListValue list_value;
137
138   std::map<std::string, base::DictionaryValue*> id_to_descriptor;
139
140   DevToolsTargetImpl::List targets =
141       DevToolsTargetImpl::EnumerateWebContentsTargets();
142
143   STLDeleteValues(&targets_);
144   for (DevToolsTargetImpl::List::iterator it = targets.begin();
145       it != targets.end(); ++it) {
146     DevToolsTargetImpl* target = *it;
147     targets_[target->GetId()] = target;
148     id_to_descriptor[target->GetId()] = Serialize(*target);
149   }
150
151   for (TargetMap::iterator it(targets_.begin()); it != targets_.end(); ++it) {
152     DevToolsTargetImpl* target = it->second;
153     base::DictionaryValue* descriptor = id_to_descriptor[target->GetId()];
154
155     std::string parent_id = target->GetParentId();
156     if (parent_id.empty() || id_to_descriptor.count(parent_id) == 0) {
157       list_value.Append(descriptor);
158     } else {
159       base::DictionaryValue* parent = id_to_descriptor[parent_id];
160       base::ListValue* guests = NULL;
161       if (!parent->GetList(kGuestList, &guests)) {
162         guests = new base::ListValue();
163         parent->Set(kGuestList, guests);
164       }
165       guests->Append(descriptor);
166     }
167   }
168
169   SendSerializedTargets(list_value);
170 }
171
172 // WorkerObserver -------------------------------------------------------------
173
174 class WorkerObserver
175     : public content::WorkerServiceObserver,
176       public base::RefCountedThreadSafe<WorkerObserver> {
177  public:
178   WorkerObserver() {}
179
180   void Start(DevToolsTargetImpl::Callback callback) {
181     DCHECK(callback_.is_null());
182     DCHECK(!callback.is_null());
183     callback_ = callback;
184     BrowserThread::PostTask(
185         BrowserThread::IO, FROM_HERE,
186         base::Bind(&WorkerObserver::StartOnIOThread, this));
187   }
188
189   void Stop() {
190     DCHECK(!callback_.is_null());
191     callback_ = DevToolsTargetImpl::Callback();
192     BrowserThread::PostTask(
193         BrowserThread::IO, FROM_HERE,
194         base::Bind(&WorkerObserver::StopOnIOThread, this));
195   }
196
197   void Enumerate() {
198     BrowserThread::PostTask(
199         BrowserThread::IO, FROM_HERE,
200         base::Bind(&WorkerObserver::EnumerateOnIOThread,
201                    this));
202   }
203
204  private:
205   friend class base::RefCountedThreadSafe<WorkerObserver>;
206   virtual ~WorkerObserver() {}
207
208   // content::WorkerServiceObserver overrides:
209   virtual void WorkerCreated(
210       const GURL& url,
211       const base::string16& name,
212       int process_id,
213       int route_id) OVERRIDE {
214     EnumerateOnIOThread();
215   }
216
217   virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
218     EnumerateOnIOThread();
219   }
220
221   void StartOnIOThread() {
222     content::WorkerService::GetInstance()->AddObserver(this);
223     EnumerateOnIOThread();
224   }
225
226   void StopOnIOThread() {
227     content::WorkerService::GetInstance()->RemoveObserver(this);
228   }
229
230   void EnumerateOnIOThread() {
231     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232     DevToolsTargetImpl::EnumerateWorkerTargets(
233         base::Bind(&WorkerObserver::RespondOnUIThread, this));
234   }
235
236   void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
237     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238     if (callback_.is_null())
239       return;
240     callback_.Run(targets);
241   }
242
243   DevToolsTargetImpl::Callback callback_;
244 };
245
246 // WorkerTargetsUIHandler -----------------------------------------------------
247
248 class WorkerTargetsUIHandler
249     : public DevToolsTargetsUIHandler {
250  public:
251   explicit WorkerTargetsUIHandler(const Callback& callback);
252   virtual ~WorkerTargetsUIHandler();
253
254  private:
255   void UpdateTargets(const DevToolsTargetImpl::List& targets);
256
257   scoped_refptr<WorkerObserver> observer_;
258 };
259
260 WorkerTargetsUIHandler::WorkerTargetsUIHandler(const Callback& callback)
261     : DevToolsTargetsUIHandler(kTargetSourceWorker, callback),
262       observer_(new WorkerObserver()) {
263   observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets,
264                               base::Unretained(this)));
265 }
266
267 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
268   observer_->Stop();
269 }
270
271 void WorkerTargetsUIHandler::UpdateTargets(
272     const DevToolsTargetImpl::List& targets) {
273   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274   base::ListValue list_value;
275   STLDeleteValues(&targets_);
276   for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
277       it != targets.end(); ++it) {
278     DevToolsTargetImpl* target = *it;
279     list_value.Append(Serialize(*target));
280     targets_[target->GetId()] = target;
281   }
282   SendSerializedTargets(list_value);
283 }
284
285 // AdbTargetsUIHandler --------------------------------------------------------
286
287 class AdbTargetsUIHandler
288     : public DevToolsTargetsUIHandler,
289       public DevToolsAndroidBridge::DeviceListListener {
290  public:
291   AdbTargetsUIHandler(const Callback& callback, Profile* profile);
292   virtual ~AdbTargetsUIHandler();
293
294   virtual void Open(const std::string& browser_id,
295                     const std::string& url,
296                     const DevToolsTargetsUIHandler::TargetCallback&) OVERRIDE;
297
298   virtual scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
299       const std::string& browser_id) OVERRIDE;
300
301  private:
302   // DevToolsAndroidBridge::Listener overrides.
303   virtual void DeviceListChanged(
304       const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE;
305
306   Profile* profile_;
307
308   typedef std::map<std::string,
309       scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
310   RemoteBrowsers remote_browsers_;
311 };
312
313 AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback& callback,
314                                          Profile* profile)
315     : DevToolsTargetsUIHandler(kTargetSourceAdb, callback),
316       profile_(profile) {
317   DevToolsAndroidBridge* android_bridge =
318       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
319   if (android_bridge)
320     android_bridge->AddDeviceListListener(this);
321 }
322
323 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
324   DevToolsAndroidBridge* android_bridge =
325       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
326   if (android_bridge)
327     android_bridge->RemoveDeviceListListener(this);
328 }
329
330 static void CallOnTarget(
331     const DevToolsTargetsUIHandler::TargetCallback& callback,
332     DevToolsAndroidBridge::RemotePage* page) {
333   scoped_ptr<DevToolsAndroidBridge::RemotePage> my_page(page);
334   callback.Run(my_page ? my_page->GetTarget() : NULL);
335 }
336
337 void AdbTargetsUIHandler::Open(
338     const std::string& browser_id,
339     const std::string& url,
340     const DevToolsTargetsUIHandler::TargetCallback& callback) {
341   RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
342   if (it !=  remote_browsers_.end())
343     it->second->Open(url, base::Bind(&CallOnTarget, callback));
344 }
345
346 scoped_refptr<content::DevToolsAgentHost>
347 AdbTargetsUIHandler::GetBrowserAgentHost(
348     const std::string& browser_id) {
349   RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
350   return it != remote_browsers_.end() ? it->second->GetAgentHost() : NULL;
351 }
352
353 void AdbTargetsUIHandler::DeviceListChanged(
354     const DevToolsAndroidBridge::RemoteDevices& devices) {
355   remote_browsers_.clear();
356   STLDeleteValues(&targets_);
357
358   base::ListValue device_list;
359   for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
360       devices.begin(); dit != devices.end(); ++dit) {
361     DevToolsAndroidBridge::RemoteDevice* device = dit->get();
362     base::DictionaryValue* device_data = new base::DictionaryValue();
363     device_data->SetString(kAdbModelField, device->model());
364     device_data->SetString(kAdbSerialField, device->serial());
365     device_data->SetBoolean(kAdbConnectedField, device->is_connected());
366     std::string device_id = base::StringPrintf(
367         kAdbDeviceIdFormat,
368         device->serial().c_str());
369     device_data->SetString(kTargetIdField, device_id);
370     base::ListValue* browser_list = new base::ListValue();
371     device_data->Set(kAdbBrowsersList, browser_list);
372
373     DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
374     for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
375         browsers.begin(); bit != browsers.end(); ++bit) {
376       DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
377       base::DictionaryValue* browser_data = new base::DictionaryValue();
378       browser_data->SetString(kAdbBrowserNameField, browser->display_name());
379       browser_data->SetString(kAdbBrowserVersionField, browser->version());
380       DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
381           browser->GetParsedVersion();
382       browser_data->SetInteger(
383           kAdbBrowserChromeVersionField,
384           browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
385       std::string browser_id = base::StringPrintf(
386           "browser:%s:%s:%s:%s",
387           device->serial().c_str(), // Ensure uniqueness across devices.
388           browser->display_name().c_str(),  // Sort by display name.
389           browser->version().c_str(),  // Then by version.
390           browser->socket().c_str());  // Ensure uniqueness on the device.
391       browser_data->SetString(kTargetIdField, browser_id);
392       browser_data->SetString(kTargetSourceField, source_id());
393
394       base::Version remote_version;
395       remote_version = base::Version(browser->version());
396
397       chrome::VersionInfo version_info;
398       base::Version local_version(version_info.Version());
399
400       browser_data->SetBoolean(kCompatibleVersion,
401           (!remote_version.IsValid()) || (!local_version.IsValid()) ||
402           remote_version.components()[0] <= local_version.components()[0]);
403
404       base::ListValue* page_list = new base::ListValue();
405       remote_browsers_[browser_id] = browser;
406             browser_data->Set(kAdbPagesList, page_list);
407       std::vector<DevToolsAndroidBridge::RemotePage*> pages =
408           browser->CreatePages();
409       for (std::vector<DevToolsAndroidBridge::RemotePage*>::iterator it =
410           pages.begin(); it != pages.end(); ++it) {
411         DevToolsAndroidBridge::RemotePage* page =  *it;
412         DevToolsTargetImpl* target = page->GetTarget();
413         base::DictionaryValue* target_data = Serialize(*target);
414         target_data->SetBoolean(
415             kAdbAttachedForeignField,
416             target->IsAttached() &&
417                 !DevToolsAndroidBridge::HasDevToolsWindow(target->GetId()));
418         // Pass the screen size in the target object to make sure that
419         // the caching logic does not prevent the target item from updating
420         // when the screen size changes.
421         gfx::Size screen_size = device->screen_size();
422         target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
423         target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
424         targets_[target->GetId()] = target;
425         page_list->Append(target_data);
426       }
427       browser_list->Append(browser_data);
428     }
429
430     device_list.Append(device_data);
431   }
432   SendSerializedTargets(device_list);
433 }
434
435 } // namespace
436
437 // DevToolsTargetsUIHandler ---------------------------------------------------
438
439 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
440     const std::string& source_id,
441     const Callback& callback)
442     : source_id_(source_id),
443       callback_(callback) {
444 }
445
446 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
447   STLDeleteValues(&targets_);
448 }
449
450 // static
451 scoped_ptr<DevToolsTargetsUIHandler>
452 DevToolsTargetsUIHandler::CreateForRenderers(
453     const DevToolsTargetsUIHandler::Callback& callback) {
454   return scoped_ptr<DevToolsTargetsUIHandler>(
455       new RenderViewHostTargetsUIHandler(callback));
456 }
457
458 // static
459 scoped_ptr<DevToolsTargetsUIHandler>
460 DevToolsTargetsUIHandler::CreateForWorkers(
461     const DevToolsTargetsUIHandler::Callback& callback) {
462   return scoped_ptr<DevToolsTargetsUIHandler>(
463       new WorkerTargetsUIHandler(callback));
464 }
465
466 // static
467 scoped_ptr<DevToolsTargetsUIHandler>
468 DevToolsTargetsUIHandler::CreateForAdb(
469     const DevToolsTargetsUIHandler::Callback& callback, Profile* profile) {
470   return scoped_ptr<DevToolsTargetsUIHandler>(
471       new AdbTargetsUIHandler(callback, profile));
472 }
473
474 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
475     const std::string& target_id) {
476   TargetMap::iterator it = targets_.find(target_id);
477   if (it != targets_.end())
478     return it->second;
479   return NULL;
480 }
481
482 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
483                                     const std::string& url,
484                                     const TargetCallback& callback) {
485   callback.Run(NULL);
486 }
487
488 scoped_refptr<content::DevToolsAgentHost>
489 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
490   return NULL;
491 }
492
493 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
494     const DevToolsTargetImpl& target) {
495   base::DictionaryValue* target_data = new base::DictionaryValue();
496   target_data->SetString(kTargetSourceField, source_id_);
497   target_data->SetString(kTargetIdField, target.GetId());
498   target_data->SetString(kTargetTypeField, target.GetType());
499   target_data->SetBoolean(kAttachedField, target.IsAttached());
500   target_data->SetString(kUrlField, target.GetURL().spec());
501   target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
502   target_data->SetString(kFaviconUrlField, target.GetFaviconURL().spec());
503   target_data->SetString(kDescriptionField, target.GetDescription());
504   return target_data;
505 }
506
507 void DevToolsTargetsUIHandler::SendSerializedTargets(
508     const base::ListValue& list) {
509   callback_.Run(source_id_, list);
510 }
511
512 // PortForwardingStatusSerializer ---------------------------------------------
513
514 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
515     const Callback& callback, Profile* profile)
516       : callback_(callback),
517         profile_(profile) {
518   PortForwardingController* port_forwarding_controller =
519       PortForwardingController::Factory::GetForProfile(profile_);
520   if (port_forwarding_controller)
521     port_forwarding_controller->AddListener(this);
522 }
523
524 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
525   PortForwardingController* port_forwarding_controller =
526       PortForwardingController::Factory::GetForProfile(profile_);
527   if (port_forwarding_controller)
528     port_forwarding_controller->RemoveListener(this);
529 }
530
531 void PortForwardingStatusSerializer::PortStatusChanged(
532     const DevicesStatus& status) {
533   base::DictionaryValue result;
534   for (DevicesStatus::const_iterator sit = status.begin();
535       sit != status.end(); ++sit) {
536     base::DictionaryValue* device_status_dict = new base::DictionaryValue();
537     const PortStatusMap& device_status_map = sit->second;
538     for (PortStatusMap::const_iterator it = device_status_map.begin();
539          it != device_status_map.end(); ++it) {
540       device_status_dict->SetInteger(
541           base::StringPrintf("%d", it->first), it->second);
542     }
543
544     std::string device_id = base::StringPrintf(
545         kAdbDeviceIdFormat,
546         sit->first.c_str());
547     result.Set(device_id, device_status_dict);
548   }
549   callback_.Run(result);
550 }