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