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 23using content::BrowserChildProcessHostIterator; 24using content::BrowserThread; 25using content::WebContents; 26 27namespace task_manager { 28 29class 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 71gfx::ImageSkia* ChildProcessResource::default_icon_ = NULL; 72 73ChildProcessResource::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 93ChildProcessResource::~ChildProcessResource() { 94} 95 96// Resource methods: 97base::string16 ChildProcessResource::GetTitle() const { 98 if (title_.empty()) 99 title_ = GetLocalizedTitle(); 100 101 return title_; 102} 103 104base::string16 ChildProcessResource::GetProfileName() const { 105 return base::string16(); 106} 107 108gfx::ImageSkia ChildProcessResource::GetIcon() const { 109 return *default_icon_; 110} 111 112base::ProcessHandle ChildProcessResource::GetProcess() const { 113 return handle_; 114} 115 116int ChildProcessResource::GetUniqueChildProcessId() const { 117 return unique_process_id_; 118} 119 120Resource::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 144bool ChildProcessResource::SupportNetworkUsage() const { 145 return network_usage_support_; 146} 147 148void ChildProcessResource::SetSupportNetworkUsage() { 149 network_usage_support_ = true; 150} 151 152base::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 207ChildProcessResourceProvider:: 208 ChildProcessResourceProvider(TaskManager* task_manager) 209 : task_manager_(task_manager), 210 updating_(false) { 211} 212 213ChildProcessResourceProvider::~ChildProcessResourceProvider() { 214} 215 216Resource* 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 227void 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 241void 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 254void 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 268void 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 296void 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. 310void 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. 328void 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