Home | History | Annotate | Download | only in task_manager
      1 // Copyright (c) 2012 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/task_manager/worker_resource_provider.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/strings/string16.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/devtools/devtools_window.h"
     13 #include "chrome/browser/profiles/profile_manager.h"
     14 #include "chrome/browser/task_manager/resource_provider.h"
     15 #include "chrome/browser/task_manager/task_manager.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/child_process_data.h"
     18 #include "content/public/browser/devtools_agent_host.h"
     19 #include "content/public/browser/worker_service.h"
     20 #include "content/public/common/process_type.h"
     21 #include "grit/generated_resources.h"
     22 #include "grit/theme_resources.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "ui/base/resource/resource_bundle.h"
     25 #include "ui/gfx/image/image_skia.h"
     26 
     27 using content::BrowserThread;
     28 using content::DevToolsAgentHost;
     29 using content::WorkerService;
     30 
     31 namespace task_manager {
     32 
     33 // Objects of this class are created on the IO thread and then passed to the UI
     34 // thread where they are passed to the task manager. All methods must be called
     35 // only on the UI thread. Destructor may be called on any thread.
     36 class SharedWorkerResource : public Resource {
     37  public:
     38   SharedWorkerResource(const GURL& url,
     39                        const string16& name,
     40                        int process_id,
     41                        int routing_id,
     42                        base::ProcessHandle process_handle);
     43   virtual ~SharedWorkerResource();
     44 
     45   bool Matches(int process_id, int routing_id) const;
     46 
     47   void UpdateProcessHandle(base::ProcessHandle handle);
     48   base::ProcessHandle handle() const { return handle_; }
     49   int process_id() const { return process_id_; }
     50 
     51  private:
     52   // Resource methods:
     53   virtual string16 GetTitle() const OVERRIDE;
     54   virtual string16 GetProfileName() const OVERRIDE;
     55   virtual gfx::ImageSkia GetIcon() const OVERRIDE;
     56   virtual base::ProcessHandle GetProcess() const OVERRIDE;
     57   virtual int GetUniqueChildProcessId() const OVERRIDE;
     58   virtual Type GetType() const OVERRIDE;
     59   virtual bool CanInspect() const OVERRIDE;
     60   virtual void Inspect() const OVERRIDE;
     61 
     62   virtual bool SupportNetworkUsage() const OVERRIDE;
     63   virtual void SetSupportNetworkUsage() OVERRIDE;
     64 
     65   int process_id_;
     66   int routing_id_;
     67   string16 title_;
     68   base::ProcessHandle handle_;
     69 
     70   static gfx::ImageSkia* default_icon_;
     71 
     72   DISALLOW_COPY_AND_ASSIGN(SharedWorkerResource);
     73 };
     74 
     75 gfx::ImageSkia* SharedWorkerResource::default_icon_ = NULL;
     76 
     77 SharedWorkerResource::SharedWorkerResource(
     78     const GURL& url,
     79     const string16& name,
     80     int process_id,
     81     int routing_id,
     82     base::ProcessHandle process_handle)
     83     : process_id_(process_id),
     84       routing_id_(routing_id),
     85       handle_(process_handle) {
     86   title_ = UTF8ToUTF16(url.spec());
     87   if (!name.empty())
     88     title_ += ASCIIToUTF16(" (") + name + ASCIIToUTF16(")");
     89 }
     90 
     91 SharedWorkerResource::~SharedWorkerResource() {
     92 }
     93 
     94 bool SharedWorkerResource::Matches(int process_id,
     95                                    int routing_id) const {
     96   return process_id_ == process_id && routing_id_ == routing_id;
     97 }
     98 
     99 void SharedWorkerResource::UpdateProcessHandle(base::ProcessHandle handle) {
    100   handle_ = handle;
    101 }
    102 
    103 string16 SharedWorkerResource::GetTitle() const {
    104   return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_WORKER_PREFIX, title_);
    105 }
    106 
    107 string16 SharedWorkerResource::GetProfileName() const {
    108   return string16();
    109 }
    110 
    111 gfx::ImageSkia SharedWorkerResource::GetIcon() const {
    112   if (!default_icon_) {
    113     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    114     default_icon_ = rb.GetImageSkiaNamed(IDR_PLUGINS_FAVICON);
    115     // TODO(jabdelmalek): use different icon for web workers.
    116   }
    117   return *default_icon_;
    118 }
    119 
    120 base::ProcessHandle SharedWorkerResource::GetProcess() const {
    121   return handle_;
    122 }
    123 
    124 int SharedWorkerResource::GetUniqueChildProcessId() const {
    125   return process_id_;
    126 }
    127 
    128 Resource::Type SharedWorkerResource::GetType() const {
    129   return WORKER;
    130 }
    131 
    132 bool SharedWorkerResource::CanInspect() const {
    133   return true;
    134 }
    135 
    136 void SharedWorkerResource::Inspect() const {
    137   // TODO(yurys): would be better to get profile from one of the tabs connected
    138   // to the worker.
    139   Profile* profile = ProfileManager::GetLastUsedProfile();
    140   if (!profile)
    141     return;
    142   scoped_refptr<DevToolsAgentHost> agent_host(
    143       DevToolsAgentHost::GetForWorker(process_id_, routing_id_));
    144   DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get());
    145 }
    146 
    147 bool SharedWorkerResource::SupportNetworkUsage() const {
    148   return false;
    149 }
    150 
    151 void SharedWorkerResource::SetSupportNetworkUsage() {
    152 }
    153 
    154 
    155 // This class is needed to ensure that all resources in WorkerResourceList are
    156 // deleted if corresponding task is posted to but not executed on the UI
    157 // thread.
    158 class WorkerResourceProvider::WorkerResourceListHolder {
    159  public:
    160   WorkerResourceListHolder() {
    161   }
    162 
    163   ~WorkerResourceListHolder() {
    164     STLDeleteElements(&resources_);
    165   }
    166 
    167   WorkerResourceList* resources() {
    168     return &resources_;
    169   }
    170 
    171  private:
    172   WorkerResourceList resources_;
    173 };
    174 
    175 
    176 WorkerResourceProvider::
    177     WorkerResourceProvider(TaskManager* task_manager)
    178     : updating_(false),
    179       task_manager_(task_manager) {
    180 }
    181 
    182 WorkerResourceProvider::~WorkerResourceProvider() {
    183   DeleteAllResources();
    184 }
    185 
    186 Resource* WorkerResourceProvider::GetResource(
    187     int origin_pid,
    188     int render_process_host_id,
    189     int routing_id) {
    190   return NULL;
    191 }
    192 
    193 void WorkerResourceProvider::StartUpdating() {
    194   DCHECK(!updating_);
    195   updating_ = true;
    196   // Get existing workers.
    197   BrowserThread::PostTask(
    198       BrowserThread::IO, FROM_HERE, base::Bind(
    199           &WorkerResourceProvider::StartObservingWorkers,
    200           this));
    201 
    202   BrowserChildProcessObserver::Add(this);
    203 }
    204 
    205 void WorkerResourceProvider::StopUpdating() {
    206   DCHECK(updating_);
    207   updating_ = false;
    208   launching_workers_.clear();
    209   DeleteAllResources();
    210   BrowserThread::PostTask(
    211       BrowserThread::IO, FROM_HERE, base::Bind(
    212           &WorkerResourceProvider::StopObservingWorkers,
    213           this));
    214 
    215   BrowserChildProcessObserver::Remove(this);
    216 }
    217 
    218 void WorkerResourceProvider::BrowserChildProcessHostConnected(
    219     const content::ChildProcessData& data) {
    220   DCHECK(updating_);
    221 
    222   if (data.process_type != content::PROCESS_TYPE_WORKER)
    223     return;
    224 
    225   ProcessIdToWorkerResources::iterator it(launching_workers_.find(data.id));
    226   if (it == launching_workers_.end())
    227     return;
    228   WorkerResourceList& resources = it->second;
    229   for (WorkerResourceList::iterator r = resources.begin();
    230        r != resources.end(); ++r) {
    231     (*r)->UpdateProcessHandle(data.handle);
    232     task_manager_->AddResource(*r);
    233   }
    234   launching_workers_.erase(it);
    235 }
    236 
    237 void WorkerResourceProvider::BrowserChildProcessHostDisconnected(
    238     const content::ChildProcessData& data) {
    239   DCHECK(updating_);
    240 
    241   if (data.process_type != content::PROCESS_TYPE_WORKER)
    242     return;
    243 
    244   // Worker process may be destroyed before WorkerMsg_TerminateWorkerContex
    245   // message is handled and WorkerDestroyed is fired. In this case we won't
    246   // get WorkerDestroyed notification and have to clear resources for such
    247   // workers here when the worker process has been destroyed.
    248   for (WorkerResourceList::iterator it = resources_.begin();
    249        it != resources_.end();) {
    250     if ((*it)->process_id() == data.id) {
    251       task_manager_->RemoveResource(*it);
    252       delete *it;
    253       it = resources_.erase(it);
    254     } else {
    255       ++it;
    256     }
    257   }
    258   DCHECK(!ContainsKey(launching_workers_, data.id));
    259 }
    260 
    261 void WorkerResourceProvider::WorkerCreated(
    262     const GURL& url,
    263     const string16& name,
    264     int process_id,
    265     int route_id) {
    266   SharedWorkerResource* resource = new SharedWorkerResource(
    267       url, name, process_id, route_id, base::kNullProcessHandle);
    268   BrowserThread::PostTask(
    269       BrowserThread::UI, FROM_HERE,
    270       base::Bind(&WorkerResourceProvider::NotifyWorkerCreated,
    271                  this, base::Owned(new WorkerResourceHolder(resource))));
    272 }
    273 
    274 void WorkerResourceProvider::WorkerDestroyed(int process_id, int route_id) {
    275   BrowserThread::PostTask(
    276       BrowserThread::UI, FROM_HERE, base::Bind(
    277           &WorkerResourceProvider::NotifyWorkerDestroyed,
    278           this, process_id, route_id));
    279 }
    280 
    281 void WorkerResourceProvider::NotifyWorkerCreated(
    282     WorkerResourceHolder* resource_holder) {
    283   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    284   if (!updating_)
    285     return;
    286   AddResource(resource_holder->release());
    287 }
    288 
    289 void WorkerResourceProvider::NotifyWorkerDestroyed(
    290     int process_id, int routing_id) {
    291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    292   if (!updating_)
    293     return;
    294   for (WorkerResourceList::iterator it = resources_.begin();
    295        it !=resources_.end(); ++it) {
    296     if ((*it)->Matches(process_id, routing_id)) {
    297       task_manager_->RemoveResource(*it);
    298       delete *it;
    299       resources_.erase(it);
    300       return;
    301     }
    302   }
    303 }
    304 
    305 void WorkerResourceProvider::StartObservingWorkers() {
    306   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    307 
    308   scoped_ptr<WorkerResourceListHolder> holder(new WorkerResourceListHolder);
    309   std::vector<WorkerService::WorkerInfo> worker_info =
    310       WorkerService::GetInstance()->GetWorkers();
    311 
    312   for (size_t i = 0; i < worker_info.size(); ++i) {
    313     holder->resources()->push_back(new SharedWorkerResource(
    314         worker_info[i].url, worker_info[i].name, worker_info[i].process_id,
    315         worker_info[i].route_id, worker_info[i].handle));
    316   }
    317 
    318   BrowserThread::PostTask(
    319       BrowserThread::UI, FROM_HERE,
    320       base::Bind(
    321           &WorkerResourceProvider::AddWorkerResourceList,
    322           this, base::Owned(holder.release())));
    323 
    324   WorkerService::GetInstance()->AddObserver(this);
    325 }
    326 
    327 void WorkerResourceProvider::StopObservingWorkers() {
    328   WorkerService::GetInstance()->RemoveObserver(this);
    329 }
    330 
    331 void WorkerResourceProvider::AddWorkerResourceList(
    332     WorkerResourceListHolder* resource_list_holder) {
    333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    334   if (!updating_)
    335     return;
    336   WorkerResourceList* resources = resource_list_holder->resources();
    337   for (WorkerResourceList::iterator it = resources->begin();
    338        it !=resources->end(); ++it) {
    339     AddResource(*it);
    340   }
    341   resources->clear();
    342 }
    343 
    344 void WorkerResourceProvider::AddResource(SharedWorkerResource* resource) {
    345   DCHECK(updating_);
    346   resources_.push_back(resource);
    347   if (resource->handle() == base::kNullProcessHandle) {
    348     int process_id = resource->process_id();
    349     launching_workers_[process_id].push_back(resource);
    350   } else {
    351     task_manager_->AddResource(resource);
    352   }
    353 }
    354 
    355 void WorkerResourceProvider::DeleteAllResources() {
    356   STLDeleteElements(&resources_);
    357 }
    358 
    359 }  // namespace task_manager
    360