Home | History | Annotate | Download | only in task_manager
      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/task_manager/child_process_resource_provider.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/i18n/rtl.h"
     10 #include "base/strings/string16.h"
     11 #include "chrome/browser/task_manager/resource_provider.h"
     12 #include "chrome/browser/task_manager/task_manager.h"
     13 #include "components/nacl/common/nacl_process_type.h"
     14 #include "content/public/browser/browser_child_process_host_iterator.h"
     15 #include "content/public/browser/browser_thread.h"
     16 #include "content/public/browser/child_process_data.h"
     17 #include "grit/generated_resources.h"
     18 #include "grit/theme_resources.h"
     19 #include "ui/base/l10n/l10n_util.h"
     20 #include "ui/base/resource/resource_bundle.h"
     21 #include "ui/gfx/image/image_skia.h"
     22 
     23 using content::BrowserChildProcessHostIterator;
     24 using content::BrowserThread;
     25 using content::WebContents;
     26 
     27 namespace task_manager {
     28 
     29 class ChildProcessResource : public Resource {
     30  public:
     31   ChildProcessResource(int process_type,
     32                        const base::string16& name,
     33                        base::ProcessHandle handle,
     34                        int unique_process_id);
     35   virtual ~ChildProcessResource();
     36 
     37   // Resource methods:
     38   virtual base::string16 GetTitle() const OVERRIDE;
     39   virtual base::string16 GetProfileName() const OVERRIDE;
     40   virtual gfx::ImageSkia GetIcon() const OVERRIDE;
     41   virtual base::ProcessHandle GetProcess() const OVERRIDE;
     42   virtual int GetUniqueChildProcessId() const OVERRIDE;
     43   virtual Type GetType() const OVERRIDE;
     44   virtual bool SupportNetworkUsage() const OVERRIDE;
     45   virtual void SetSupportNetworkUsage() OVERRIDE;
     46 
     47   // Returns the pid of the child process.
     48   int process_id() const { return pid_; }
     49 
     50  private:
     51   // Returns a localized title for the child process.  For example, a plugin
     52   // process would be "Plug-in: Flash" when name is "Flash".
     53   base::string16 GetLocalizedTitle() const;
     54 
     55   int process_type_;
     56   base::string16 name_;
     57   base::ProcessHandle handle_;
     58   int pid_;
     59   int unique_process_id_;
     60   mutable base::string16 title_;
     61   bool network_usage_support_;
     62 
     63   // The icon painted for the child processs.
     64   // TODO(jcampan): we should have plugin specific icons for well-known
     65   // plugins.
     66   static gfx::ImageSkia* default_icon_;
     67 
     68   DISALLOW_COPY_AND_ASSIGN(ChildProcessResource);
     69 };
     70 
     71 gfx::ImageSkia* ChildProcessResource::default_icon_ = NULL;
     72 
     73 ChildProcessResource::ChildProcessResource(
     74     int process_type,
     75     const base::string16& name,
     76     base::ProcessHandle handle,
     77     int unique_process_id)
     78     : process_type_(process_type),
     79       name_(name),
     80       handle_(handle),
     81       unique_process_id_(unique_process_id),
     82       network_usage_support_(false) {
     83   // We cache the process id because it's not cheap to calculate, and it won't
     84   // be available when we get the plugin disconnected notification.
     85   pid_ = base::GetProcId(handle);
     86   if (!default_icon_) {
     87     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     88     default_icon_ = rb.GetImageSkiaNamed(IDR_PLUGINS_FAVICON);
     89     // TODO(jabdelmalek): use different icon for web workers.
     90   }
     91 }
     92 
     93 ChildProcessResource::~ChildProcessResource() {
     94 }
     95 
     96 // Resource methods:
     97 base::string16 ChildProcessResource::GetTitle() const {
     98   if (title_.empty())
     99     title_ = GetLocalizedTitle();
    100 
    101   return title_;
    102 }
    103 
    104 base::string16 ChildProcessResource::GetProfileName() const {
    105   return base::string16();
    106 }
    107 
    108 gfx::ImageSkia ChildProcessResource::GetIcon() const {
    109   return *default_icon_;
    110 }
    111 
    112 base::ProcessHandle ChildProcessResource::GetProcess() const {
    113   return handle_;
    114 }
    115 
    116 int ChildProcessResource::GetUniqueChildProcessId() const {
    117   return unique_process_id_;
    118 }
    119 
    120 Resource::Type ChildProcessResource::GetType() const {
    121   // Translate types to Resource::Type, since ChildProcessData's type
    122   // is not available for all TaskManager resources.
    123   switch (process_type_) {
    124     case content::PROCESS_TYPE_PLUGIN:
    125     case content::PROCESS_TYPE_PPAPI_PLUGIN:
    126     case content::PROCESS_TYPE_PPAPI_BROKER:
    127       return Resource::PLUGIN;
    128     case content::PROCESS_TYPE_UTILITY:
    129       return Resource::UTILITY;
    130     case content::PROCESS_TYPE_ZYGOTE:
    131       return Resource::ZYGOTE;
    132     case content::PROCESS_TYPE_SANDBOX_HELPER:
    133       return Resource::SANDBOX_HELPER;
    134     case content::PROCESS_TYPE_GPU:
    135       return Resource::GPU;
    136     case PROCESS_TYPE_NACL_LOADER:
    137     case PROCESS_TYPE_NACL_BROKER:
    138       return Resource::NACL;
    139     default:
    140       return Resource::UNKNOWN;
    141   }
    142 }
    143 
    144 bool ChildProcessResource::SupportNetworkUsage() const {
    145   return network_usage_support_;
    146 }
    147 
    148 void ChildProcessResource::SetSupportNetworkUsage() {
    149   network_usage_support_ = true;
    150 }
    151 
    152 base::string16 ChildProcessResource::GetLocalizedTitle() const {
    153   base::string16 title = name_;
    154   if (title.empty()) {
    155     switch (process_type_) {
    156       case content::PROCESS_TYPE_PLUGIN:
    157       case content::PROCESS_TYPE_PPAPI_PLUGIN:
    158       case content::PROCESS_TYPE_PPAPI_BROKER:
    159         title = l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME);
    160         break;
    161       default:
    162         // Nothing to do for non-plugin processes.
    163         break;
    164     }
    165   }
    166 
    167   // Explicitly mark name as LTR if there is no strong RTL character,
    168   // to avoid the wrong concatenation result similar to "!Yahoo Mail: the
    169   // best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew
    170   // or Arabic word for "plugin".
    171   base::i18n::AdjustStringForLocaleDirection(&title);
    172 
    173   switch (process_type_) {
    174     case content::PROCESS_TYPE_UTILITY:
    175       return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX);
    176     case content::PROCESS_TYPE_GPU:
    177       return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX);
    178     case content::PROCESS_TYPE_PLUGIN:
    179     case content::PROCESS_TYPE_PPAPI_PLUGIN:
    180       return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_PREFIX, title);
    181     case content::PROCESS_TYPE_PPAPI_BROKER:
    182       return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_BROKER_PREFIX,
    183                                         title);
    184     case PROCESS_TYPE_NACL_BROKER:
    185       return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX);
    186     case PROCESS_TYPE_NACL_LOADER:
    187       return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX, title);
    188     // These types don't need display names or get them from elsewhere.
    189     case content::PROCESS_TYPE_BROWSER:
    190     case content::PROCESS_TYPE_RENDERER:
    191     case content::PROCESS_TYPE_ZYGOTE:
    192     case content::PROCESS_TYPE_SANDBOX_HELPER:
    193     case content::PROCESS_TYPE_MAX:
    194       NOTREACHED();
    195       break;
    196 
    197     case content::PROCESS_TYPE_WORKER:
    198       NOTREACHED() << "Workers are not handled by this provider.";
    199       break;
    200     case content::PROCESS_TYPE_UNKNOWN:
    201       NOTREACHED() << "Need localized name for child process type.";
    202   }
    203 
    204   return title;
    205 }
    206 
    207 ////////////////////////////////////////////////////////////////////////////////
    208 // ChildProcessResourceProvider class
    209 ////////////////////////////////////////////////////////////////////////////////
    210 
    211 ChildProcessResourceProvider::
    212     ChildProcessResourceProvider(TaskManager* task_manager)
    213     : task_manager_(task_manager),
    214       updating_(false) {
    215 }
    216 
    217 ChildProcessResourceProvider::~ChildProcessResourceProvider() {
    218 }
    219 
    220 Resource* ChildProcessResourceProvider::GetResource(
    221     int origin_pid,
    222     int child_id,
    223     int route_id) {
    224   PidResourceMap::iterator iter = pid_to_resources_.find(origin_pid);
    225   if (iter != pid_to_resources_.end())
    226     return iter->second;
    227   else
    228     return NULL;
    229 }
    230 
    231 void ChildProcessResourceProvider::StartUpdating() {
    232   DCHECK(!updating_);
    233   updating_ = true;
    234 
    235   // Get the existing child processes.
    236   BrowserThread::PostTask(
    237       BrowserThread::IO, FROM_HERE,
    238       base::Bind(
    239           &ChildProcessResourceProvider::RetrieveChildProcessData,
    240           this));
    241 
    242   BrowserChildProcessObserver::Add(this);
    243 }
    244 
    245 void ChildProcessResourceProvider::StopUpdating() {
    246   DCHECK(updating_);
    247   updating_ = false;
    248 
    249   // Delete all the resources.
    250   STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
    251 
    252   resources_.clear();
    253   pid_to_resources_.clear();
    254 
    255   BrowserChildProcessObserver::Remove(this);
    256 }
    257 
    258 void ChildProcessResourceProvider::BrowserChildProcessHostConnected(
    259     const content::ChildProcessData& data) {
    260   DCHECK(updating_);
    261 
    262   // Workers are handled by WorkerResourceProvider.
    263   if (data.process_type == content::PROCESS_TYPE_WORKER)
    264     return;
    265   if (resources_.count(data.handle)) {
    266     // The case may happen that we have added a child_process_info as part of
    267     // the iteration performed during StartUpdating() call but the notification
    268     // that it has connected was not fired yet. So when the notification
    269     // happens, we already know about this plugin and just ignore it.
    270     return;
    271   }
    272   AddToTaskManager(data);
    273 }
    274 
    275 void ChildProcessResourceProvider::
    276     BrowserChildProcessHostDisconnected(
    277         const content::ChildProcessData& data) {
    278   DCHECK(updating_);
    279 
    280   if (data.process_type == content::PROCESS_TYPE_WORKER)
    281     return;
    282   ChildProcessMap::iterator iter = resources_.find(data.handle);
    283   if (iter == resources_.end()) {
    284     // ChildProcessData disconnection notifications are asynchronous, so we
    285     // might be notified for a plugin we don't know anything about (if it was
    286     // closed before the task manager was shown and destroyed after that).
    287     return;
    288   }
    289   // Remove the resource from the Task Manager.
    290   ChildProcessResource* resource = iter->second;
    291   task_manager_->RemoveResource(resource);
    292   // Remove it from the provider.
    293   resources_.erase(iter);
    294   // Remove it from our pid map.
    295   PidResourceMap::iterator pid_iter =
    296       pid_to_resources_.find(resource->process_id());
    297   DCHECK(pid_iter != pid_to_resources_.end());
    298   if (pid_iter != pid_to_resources_.end())
    299     pid_to_resources_.erase(pid_iter);
    300 
    301   // Finally, delete the resource.
    302   delete resource;
    303 }
    304 
    305 void ChildProcessResourceProvider::AddToTaskManager(
    306     const content::ChildProcessData& child_process_data) {
    307   ChildProcessResource* resource =
    308       new ChildProcessResource(
    309           child_process_data.process_type,
    310           child_process_data.name,
    311           child_process_data.handle,
    312           child_process_data.id);
    313   resources_[child_process_data.handle] = resource;
    314   pid_to_resources_[resource->process_id()] = resource;
    315   task_manager_->AddResource(resource);
    316 }
    317 
    318 // The ChildProcessData::Iterator has to be used from the IO thread.
    319 void ChildProcessResourceProvider::RetrieveChildProcessData() {
    320   std::vector<content::ChildProcessData> child_processes;
    321   for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
    322     // Only add processes which are already started, since we need their handle.
    323     if (iter.GetData().handle == base::kNullProcessHandle)
    324       continue;
    325     if (iter.GetData().process_type == content::PROCESS_TYPE_WORKER)
    326       continue;
    327     child_processes.push_back(iter.GetData());
    328   }
    329   // Now notify the UI thread that we have retrieved information about child
    330   // processes.
    331   BrowserThread::PostTask(
    332       BrowserThread::UI, FROM_HERE,
    333       base::Bind(
    334           &ChildProcessResourceProvider::ChildProcessDataRetreived,
    335           this, child_processes));
    336 }
    337 
    338 // This is called on the UI thread.
    339 void ChildProcessResourceProvider::ChildProcessDataRetreived(
    340     const std::vector<content::ChildProcessData>& child_processes) {
    341   for (size_t i = 0; i < child_processes.size(); ++i)
    342     AddToTaskManager(child_processes[i]);
    343 
    344   task_manager_->model()->NotifyDataReady();
    345 }
    346 
    347 }  // namespace task_manager
    348