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 "chrome/grit/generated_resources.h"
     14 #include "components/nacl/common/nacl_process_type.h"
     15 #include "content/public/browser/browser_child_process_host_iterator.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/child_process_data.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     case content::PROCESS_TYPE_UNKNOWN:
    197       NOTREACHED() << "Need localized name for child process type.";
    198   }
    199 
    200   return title;
    201 }
    202 
    203 ////////////////////////////////////////////////////////////////////////////////
    204 // ChildProcessResourceProvider class
    205 ////////////////////////////////////////////////////////////////////////////////
    206 
    207 ChildProcessResourceProvider::
    208     ChildProcessResourceProvider(TaskManager* task_manager)
    209     : task_manager_(task_manager),
    210       updating_(false) {
    211 }
    212 
    213 ChildProcessResourceProvider::~ChildProcessResourceProvider() {
    214 }
    215 
    216 Resource* ChildProcessResourceProvider::GetResource(
    217     int origin_pid,
    218     int child_id,
    219     int route_id) {
    220   PidResourceMap::iterator iter = pid_to_resources_.find(origin_pid);
    221   if (iter != pid_to_resources_.end())
    222     return iter->second;
    223   else
    224     return NULL;
    225 }
    226 
    227 void ChildProcessResourceProvider::StartUpdating() {
    228   DCHECK(!updating_);
    229   updating_ = true;
    230 
    231   // Get the existing child processes.
    232   BrowserThread::PostTask(
    233       BrowserThread::IO, FROM_HERE,
    234       base::Bind(
    235           &ChildProcessResourceProvider::RetrieveChildProcessData,
    236           this));
    237 
    238   BrowserChildProcessObserver::Add(this);
    239 }
    240 
    241 void ChildProcessResourceProvider::StopUpdating() {
    242   DCHECK(updating_);
    243   updating_ = false;
    244 
    245   // Delete all the resources.
    246   STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
    247 
    248   resources_.clear();
    249   pid_to_resources_.clear();
    250 
    251   BrowserChildProcessObserver::Remove(this);
    252 }
    253 
    254 void ChildProcessResourceProvider::BrowserChildProcessHostConnected(
    255     const content::ChildProcessData& data) {
    256   DCHECK(updating_);
    257 
    258   if (resources_.count(data.handle)) {
    259     // The case may happen that we have added a child_process_info as part of
    260     // the iteration performed during StartUpdating() call but the notification
    261     // that it has connected was not fired yet. So when the notification
    262     // happens, we already know about this plugin and just ignore it.
    263     return;
    264   }
    265   AddToTaskManager(data);
    266 }
    267 
    268 void ChildProcessResourceProvider::
    269     BrowserChildProcessHostDisconnected(
    270         const content::ChildProcessData& data) {
    271   DCHECK(updating_);
    272 
    273   ChildProcessMap::iterator iter = resources_.find(data.handle);
    274   if (iter == resources_.end()) {
    275     // ChildProcessData disconnection notifications are asynchronous, so we
    276     // might be notified for a plugin we don't know anything about (if it was
    277     // closed before the task manager was shown and destroyed after that).
    278     return;
    279   }
    280   // Remove the resource from the Task Manager.
    281   ChildProcessResource* resource = iter->second;
    282   task_manager_->RemoveResource(resource);
    283   // Remove it from the provider.
    284   resources_.erase(iter);
    285   // Remove it from our pid map.
    286   PidResourceMap::iterator pid_iter =
    287       pid_to_resources_.find(resource->process_id());
    288   DCHECK(pid_iter != pid_to_resources_.end());
    289   if (pid_iter != pid_to_resources_.end())
    290     pid_to_resources_.erase(pid_iter);
    291 
    292   // Finally, delete the resource.
    293   delete resource;
    294 }
    295 
    296 void ChildProcessResourceProvider::AddToTaskManager(
    297     const content::ChildProcessData& child_process_data) {
    298   ChildProcessResource* resource =
    299       new ChildProcessResource(
    300           child_process_data.process_type,
    301           child_process_data.name,
    302           child_process_data.handle,
    303           child_process_data.id);
    304   resources_[child_process_data.handle] = resource;
    305   pid_to_resources_[resource->process_id()] = resource;
    306   task_manager_->AddResource(resource);
    307 }
    308 
    309 // The ChildProcessData::Iterator has to be used from the IO thread.
    310 void ChildProcessResourceProvider::RetrieveChildProcessData() {
    311   std::vector<content::ChildProcessData> child_processes;
    312   for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
    313     // Only add processes which are already started, since we need their handle.
    314     if (iter.GetData().handle == base::kNullProcessHandle)
    315       continue;
    316     child_processes.push_back(iter.GetData());
    317   }
    318   // Now notify the UI thread that we have retrieved information about child
    319   // processes.
    320   BrowserThread::PostTask(
    321       BrowserThread::UI, FROM_HERE,
    322       base::Bind(
    323           &ChildProcessResourceProvider::ChildProcessDataRetreived,
    324           this, child_processes));
    325 }
    326 
    327 // This is called on the UI thread.
    328 void ChildProcessResourceProvider::ChildProcessDataRetreived(
    329     const std::vector<content::ChildProcessData>& child_processes) {
    330   for (size_t i = 0; i < child_processes.size(); ++i)
    331     AddToTaskManager(child_processes[i]);
    332 
    333   task_manager_->model()->NotifyDataReady();
    334 }
    335 
    336 }  // namespace task_manager
    337