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