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