Home | History | Annotate | Download | only in devtools
      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 "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"
     29 
     30 using content::BrowserThread;
     31 using content::WebContents;
     32 
     33 namespace {
     34 
     35 const char kTargetSourceField[]  = "source";
     36 const char kTargetSourceRenderer[]  = "renderers";
     37 const char kTargetSourceWorker[]  = "workers";
     38 const char kTargetSourceAdb[]  = "adb";
     39 
     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";
     47 
     48 const char kGuestList[] = "guests";
     49 
     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";
     55 
     56 const char kAdbBrowserNameField[] = "adbBrowserName";
     57 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
     58 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
     59 const char kAdbPagesList[] = "pages";
     60 
     61 const char kAdbScreenWidthField[] = "adbScreenWidth";
     62 const char kAdbScreenHeightField[] = "adbScreenHeight";
     63 const char kAdbAttachedForeignField[]  = "adbAttachedForeign";
     64 
     65 // CancelableTimer ------------------------------------------------------------
     66 
     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);
     76   };
     77 
     78  private:
     79   void Fire() { callback_.Run(); }
     80 
     81   base::Closure callback_;
     82   base::WeakPtrFactory<CancelableTimer> weak_factory_;
     83 };
     84 
     85 // RenderViewHostTargetsUIHandler ---------------------------------------------
     86 
     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;
     98 
     99   void UpdateTargets();
    100 
    101   content::NotificationRegistrar notification_registrar_;
    102   scoped_ptr<CancelableTimer> timer_;
    103 };
    104 
    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();
    118 }
    119 
    120 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
    121   notification_registrar_.RemoveAll();
    122 }
    123 
    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)));
    134 }
    135 
    136 void RenderViewHostTargetsUIHandler::UpdateTargets() {
    137   scoped_ptr<ListValue> list_value(new ListValue());
    138 
    139   std::map<WebContents*, DictionaryValue*> web_contents_to_descriptor_;
    140   std::vector<DevToolsTargetImpl*> guest_targets;
    141 
    142   DevToolsTargetImpl::List targets =
    143       DevToolsTargetImpl::EnumerateRenderViewHostTargets();
    144 
    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;
    155 
    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       DictionaryValue* descriptor = Serialize(*target_ptr);
    162       list_value->Append(descriptor);
    163       web_contents_to_descriptor_[web_contents] = descriptor;
    164     }
    165   }
    166 
    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       DictionaryValue* parent = web_contents_to_descriptor_[embedder];
    176       ListValue* guests = NULL;
    177       if (!parent->GetList(kGuestList, &guests)) {
    178         guests = new ListValue();
    179         parent->Set(kGuestList, guests);
    180       }
    181       guests->Append(Serialize(*guest));
    182     }
    183   }
    184 
    185   SendSerializedTargets(list_value.Pass());
    186 }
    187 
    188 // WorkerObserver -------------------------------------------------------------
    189 
    190 class WorkerObserver
    191     : public content::WorkerServiceObserver,
    192       public base::RefCountedThreadSafe<WorkerObserver> {
    193  public:
    194   WorkerObserver() {}
    195 
    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));
    203   }
    204 
    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));
    211   }
    212 
    213   void Enumerate() {
    214     BrowserThread::PostTask(
    215         BrowserThread::IO, FROM_HERE,
    216         base::Bind(&WorkerObserver::EnumerateOnIOThread,
    217                    this));
    218   }
    219 
    220  private:
    221   friend class base::RefCountedThreadSafe<WorkerObserver>;
    222   virtual ~WorkerObserver() {}
    223 
    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();
    231   }
    232 
    233   virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
    234     EnumerateOnIOThread();
    235   }
    236 
    237   void StartOnIOThread() {
    238     content::WorkerService::GetInstance()->AddObserver(this);
    239     EnumerateOnIOThread();
    240   }
    241 
    242   void StopOnIOThread() {
    243     content::WorkerService::GetInstance()->RemoveObserver(this);
    244   }
    245 
    246   void EnumerateOnIOThread() {
    247     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    248     DevToolsTargetImpl::EnumerateWorkerTargets(
    249         base::Bind(&WorkerObserver::RespondOnUIThread, this));
    250   }
    251 
    252   void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
    253     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    254     if (callback_.is_null())
    255       return;
    256     callback_.Run(targets);
    257   }
    258 
    259   DevToolsTargetImpl::Callback callback_;
    260 };
    261 
    262 // WorkerTargetsUIHandler -----------------------------------------------------
    263 
    264 class WorkerTargetsUIHandler
    265     : public DevToolsTargetsUIHandler,
    266       public content::BrowserChildProcessObserver {
    267  public:
    268   explicit WorkerTargetsUIHandler(Callback callback);
    269   virtual ~WorkerTargetsUIHandler();
    270 
    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;
    277 
    278   void UpdateTargets(const DevToolsTargetImpl::List& targets);
    279 
    280   scoped_refptr<WorkerObserver> observer_;
    281 };
    282 
    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);
    289 }
    290 
    291 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
    292   BrowserChildProcessObserver::Remove(this);
    293   observer_->Stop();
    294 }
    295 
    296 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected(
    297     const content::ChildProcessData& data) {
    298   if (data.process_type == content::PROCESS_TYPE_WORKER)
    299     observer_->Enumerate();
    300 }
    301 
    302 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected(
    303     const content::ChildProcessData& data) {
    304   if (data.process_type == content::PROCESS_TYPE_WORKER)
    305     observer_->Enumerate();
    306 }
    307 
    308 void WorkerTargetsUIHandler::UpdateTargets(
    309     const DevToolsTargetImpl::List& targets) {
    310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    311   scoped_ptr<ListValue> list_value(new 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;
    318   }
    319   SendSerializedTargets(list_value.Pass());
    320 }
    321 
    322 // AdbTargetsUIHandler --------------------------------------------------------
    323 
    324 class AdbTargetsUIHandler
    325     : public DevToolsRemoteTargetsUIHandler,
    326       public DevToolsAdbBridge::Listener {
    327  public:
    328   AdbTargetsUIHandler(Callback callback, Profile* profile);
    329   virtual ~AdbTargetsUIHandler();
    330 
    331   virtual void Open(const std::string& browser_id,
    332                     const std::string& url) OVERRIDE;
    333 
    334  private:
    335   // DevToolsAdbBridge::Listener overrides.
    336   virtual void RemoteDevicesChanged(
    337       DevToolsAdbBridge::RemoteDevices* devices) OVERRIDE;
    338 
    339   Profile* profile_;
    340 
    341   typedef std::map<std::string,
    342       scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > RemoteBrowsers;
    343   RemoteBrowsers remote_browsers_;
    344 };
    345 
    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);
    353 }
    354 
    355 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
    356   DevToolsAdbBridge* adb_bridge =
    357       DevToolsAdbBridge::Factory::GetForProfile(profile_);
    358   if (adb_bridge)
    359     adb_bridge->RemoveListener(this);
    360 }
    361 
    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);
    367 }
    368 
    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);
    377 
    378   remote_browsers_.clear();
    379   STLDeleteValues(&targets_);
    380 
    381   scoped_ptr<ListValue> device_list(new ListValue());
    382   for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
    383        dit != devices->end(); ++dit) {
    384     DevToolsAdbBridge::RemoteDevice* device = dit->get();
    385     DictionaryValue* device_data = new 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     ListValue* browser_list = new ListValue();
    394     device_data->Set(kAdbBrowsersList, browser_list);
    395 
    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       DictionaryValue* browser_data = new 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       ListValue* page_list = new ListValue();
    418       browser_data->Set(kAdbPagesList, page_list);
    419 
    420       DevToolsTargetImpl::List pages = browser->CreatePageTargets();
    421       for (DevToolsTargetImpl::List::iterator it =
    422           pages.begin(); it != pages.end(); ++it) {
    423         DevToolsTargetImpl* target =  *it;
    424         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);
    437       }
    438       browser_list->Append(browser_data);
    439     }
    440 
    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         DictionaryValue* port_status_dict = new 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);
    452         }
    453         device_data->Set(kAdbPortStatus, port_status_dict);
    454       }
    455     }
    456 
    457     device_list->Append(device_data);
    458   }
    459   SendSerializedTargets(device_list.Pass());
    460 }
    461 
    462 } // namespace
    463 
    464 // DevToolsTargetsUIHandler ---------------------------------------------------
    465 
    466 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
    467     const std::string& source_id,
    468     Callback callback)
    469     : source_id_(source_id),
    470       callback_(callback) {
    471 }
    472 
    473 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
    474   STLDeleteValues(&targets_);
    475 }
    476 
    477 // static
    478 scoped_ptr<DevToolsTargetsUIHandler>
    479 DevToolsTargetsUIHandler::CreateForRenderers(
    480     DevToolsTargetsUIHandler::Callback callback) {
    481   return scoped_ptr<DevToolsTargetsUIHandler>(
    482       new RenderViewHostTargetsUIHandler(callback));
    483 }
    484 
    485 // static
    486 scoped_ptr<DevToolsTargetsUIHandler>
    487 DevToolsTargetsUIHandler::CreateForWorkers(
    488     DevToolsTargetsUIHandler::Callback callback) {
    489   return scoped_ptr<DevToolsTargetsUIHandler>(
    490       new WorkerTargetsUIHandler(callback));
    491 }
    492 
    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);
    498 }
    499 
    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();
    504 }
    505 
    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();
    510 }
    511 
    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();
    516 }
    517 
    518 base::DictionaryValue*
    519 DevToolsTargetsUIHandler::Serialize(
    520     const DevToolsTargetImpl& target) {
    521   DictionaryValue* target_data = new 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;
    531 }
    532 
    533 void DevToolsTargetsUIHandler::SendSerializedTargets(
    534     scoped_ptr<ListValue> list) {
    535   callback_.Run(source_id_, list.Pass());
    536 }
    537 
    538 // DevToolsRemoteTargetsUIHandler ---------------------------------------------
    539 
    540 DevToolsRemoteTargetsUIHandler::DevToolsRemoteTargetsUIHandler(
    541     const std::string& source_id,
    542     Callback callback)
    543     : DevToolsTargetsUIHandler(source_id, callback) {
    544 }
    545 
    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));
    552 }
    553