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