OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_processes_api.h" | 5 #include "chrome/browser/extensions/extension_processes_api.h" |
6 | 6 |
7 #include "base/callback.h" | 7 #include "base/callback.h" |
8 #include "base/json/json_writer.h" | 8 #include "base/json/json_writer.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/metrics/histogram.h" |
10 #include "base/string_number_conversions.h" | 11 #include "base/string_number_conversions.h" |
11 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
12 #include "base/values.h" | 13 #include "base/values.h" |
13 | 14 |
14 #include "chrome/browser/extensions/extension_event_router.h" | 15 #include "chrome/browser/extensions/extension_event_router.h" |
| 16 #include "chrome/browser/extensions/extension_function_util.h" |
15 #include "chrome/browser/extensions/extension_processes_api_constants.h" | 17 #include "chrome/browser/extensions/extension_processes_api_constants.h" |
| 18 #include "chrome/browser/extensions/extension_service.h" |
16 #include "chrome/browser/extensions/extension_tab_util.h" | 19 #include "chrome/browser/extensions/extension_tab_util.h" |
17 #include "chrome/browser/extensions/extension_tabs_module_constants.h" | 20 #include "chrome/browser/extensions/extension_tabs_module_constants.h" |
18 #include "chrome/browser/profiles/profile.h" | 21 #include "chrome/browser/profiles/profile.h" |
19 #include "chrome/browser/task_manager/task_manager.h" | 22 #include "chrome/browser/task_manager/task_manager.h" |
20 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 23 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 24 #include "chrome/common/chrome_notification_types.h" |
21 #include "chrome/common/extensions/extension_error_utils.h" | 25 #include "chrome/common/extensions/extension_error_utils.h" |
| 26 #include "content/public/browser/browser_thread.h" |
| 27 #include "content/public/browser/notification_details.h" |
| 28 #include "content/public/browser/notification_service.h" |
| 29 #include "content/public/browser/notification_source.h" |
22 #include "content/public/browser/notification_types.h" | 30 #include "content/public/browser/notification_types.h" |
23 #include "content/public/browser/render_process_host.h" | 31 #include "content/public/browser/render_process_host.h" |
| 32 #include "content/public/browser/render_view_host.h" |
| 33 #include "content/public/browser/render_view_host_delegate.h" |
| 34 #include "content/public/browser/render_widget_host.h" |
24 #include "content/public/browser/web_contents.h" | 35 #include "content/public/browser/web_contents.h" |
| 36 #include "content/public/common/result_codes.h" |
25 | 37 |
26 namespace keys = extension_processes_api_constants; | 38 namespace keys = extension_processes_api_constants; |
27 | 39 namespace errors = extension_processes_api_constants; |
28 DictionaryValue* CreateProcessValue(int process_id, | 40 |
29 const std::string& type, | 41 namespace { |
30 double cpu, | 42 |
31 int64 net, | 43 #if defined(ENABLE_TASK_MANAGER) |
32 int64 pr_mem, | 44 |
33 int64 sh_mem) { | 45 DictionaryValue* CreateCacheData( |
| 46 const WebKit::WebCache::ResourceTypeStat& stat) { |
| 47 |
| 48 DictionaryValue* cache = new DictionaryValue(); |
| 49 cache->SetDouble(keys::kCacheSize, static_cast<double>(stat.size)); |
| 50 cache->SetDouble(keys::kCacheLiveSize, static_cast<double>(stat.liveSize)); |
| 51 return cache; |
| 52 } |
| 53 |
| 54 void SetProcessType(DictionaryValue* result, |
| 55 TaskManagerModel* model, |
| 56 int index) { |
| 57 // Determine process type. |
| 58 std::string type = keys::kProcessTypeOther; |
| 59 TaskManager::Resource::Type resource_type = model->GetResourceType(index); |
| 60 switch (resource_type) { |
| 61 case TaskManager::Resource::BROWSER: |
| 62 type = keys::kProcessTypeBrowser; |
| 63 break; |
| 64 case TaskManager::Resource::RENDERER: |
| 65 type = keys::kProcessTypeRenderer; |
| 66 break; |
| 67 case TaskManager::Resource::EXTENSION: |
| 68 type = keys::kProcessTypeExtension; |
| 69 break; |
| 70 case TaskManager::Resource::NOTIFICATION: |
| 71 type = keys::kProcessTypeNotification; |
| 72 break; |
| 73 case TaskManager::Resource::PLUGIN: |
| 74 type = keys::kProcessTypePlugin; |
| 75 break; |
| 76 case TaskManager::Resource::WORKER: |
| 77 type = keys::kProcessTypeWorker; |
| 78 break; |
| 79 case TaskManager::Resource::NACL: |
| 80 type = keys::kProcessTypeNacl; |
| 81 break; |
| 82 case TaskManager::Resource::UTILITY: |
| 83 type = keys::kProcessTypeUtility; |
| 84 break; |
| 85 case TaskManager::Resource::GPU: |
| 86 type = keys::kProcessTypeGPU; |
| 87 break; |
| 88 case TaskManager::Resource::PROFILE_IMPORT: |
| 89 case TaskManager::Resource::ZYGOTE: |
| 90 case TaskManager::Resource::SANDBOX_HELPER: |
| 91 case TaskManager::Resource::UNKNOWN: |
| 92 type = keys::kProcessTypeOther; |
| 93 break; |
| 94 default: |
| 95 NOTREACHED() << "Unknown resource type."; |
| 96 } |
| 97 result->SetString(keys::kTypeKey, type); |
| 98 } |
| 99 |
| 100 ListValue* GetTabsForProcess(int process_id) { |
| 101 ListValue* tabs_list = new ListValue(); |
| 102 |
| 103 // The tabs list only makes sense for render processes, so if we don't find |
| 104 // one, just return the empty list. |
| 105 content::RenderProcessHost* rph = |
| 106 content::RenderProcessHost::FromID(process_id); |
| 107 if (rph == NULL) |
| 108 return tabs_list; |
| 109 |
| 110 int tab_id = -1; |
| 111 // We need to loop through all the RVHs to ensure we collect the set of all |
| 112 // tabs using this renderer process. |
| 113 content::RenderProcessHost::RenderWidgetHostsIterator iter( |
| 114 rph->GetRenderWidgetHostsIterator()); |
| 115 for (; !iter.IsAtEnd(); iter.Advance()) { |
| 116 const content::RenderWidgetHost* widget = iter.GetCurrentValue(); |
| 117 DCHECK(widget); |
| 118 if (!widget || !widget->IsRenderView()) |
| 119 continue; |
| 120 |
| 121 content::RenderViewHost* host = content::RenderViewHost::From( |
| 122 const_cast<content::RenderWidgetHost*>(widget)); |
| 123 content::RenderViewHostDelegate* host_delegate = host->GetDelegate(); |
| 124 content::WebContents* contents = host_delegate->GetAsWebContents(); |
| 125 if (contents != NULL) { |
| 126 tab_id = ExtensionTabUtil::GetTabId(contents); |
| 127 if (tab_id != -1) |
| 128 tabs_list->Append(Value::CreateIntegerValue(tab_id)); |
| 129 } |
| 130 } |
| 131 |
| 132 return tabs_list; |
| 133 } |
| 134 |
| 135 // This function creates a Process object to be returned to the extensions |
| 136 // using these APIs. For memory details, which are not added by this function, |
| 137 // the callers need to use AddMemoryDetails. |
| 138 DictionaryValue* CreateProcessFromModel(int process_id, |
| 139 TaskManagerModel* model, |
| 140 int index, |
| 141 bool include_optional) { |
34 DictionaryValue* result = new DictionaryValue(); | 142 DictionaryValue* result = new DictionaryValue(); |
| 143 size_t mem; |
| 144 |
35 result->SetInteger(keys::kIdKey, process_id); | 145 result->SetInteger(keys::kIdKey, process_id); |
36 result->SetString(keys::kTypeKey, type); | 146 result->SetInteger(keys::kOsProcessIdKey, model->GetProcessId(index)); |
37 result->SetDouble(keys::kCpuKey, cpu); | 147 SetProcessType(result, model, index); |
| 148 result->SetString(keys::kProfileKey, |
| 149 model->GetResourceProfileName(index)); |
| 150 |
| 151 result->Set(keys::kTabsListKey, GetTabsForProcess(process_id)); |
| 152 |
| 153 // If we don't need to include the optional properties, just return now. |
| 154 if (!include_optional) |
| 155 return result; |
| 156 |
| 157 result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index)); |
| 158 |
| 159 if (model->GetV8Memory(index, &mem)) |
| 160 result->SetDouble(keys::kJsMemoryAllocatedKey, |
| 161 static_cast<double>(mem)); |
| 162 |
| 163 if (model->GetV8MemoryUsed(index, &mem)) |
| 164 result->SetDouble(keys::kJsMemoryUsedKey, |
| 165 static_cast<double>(mem)); |
| 166 |
| 167 if (model->GetSqliteMemoryUsedBytes(index, &mem)) |
| 168 result->SetDouble(keys::kSqliteMemoryKey, |
| 169 static_cast<double>(mem)); |
| 170 |
| 171 WebKit::WebCache::ResourceTypeStats cache_stats; |
| 172 if (model->GetWebCoreCacheStats(index, &cache_stats)) { |
| 173 result->Set(keys::kImageCacheKey, |
| 174 CreateCacheData(cache_stats.images)); |
| 175 result->Set(keys::kScriptCacheKey, |
| 176 CreateCacheData(cache_stats.scripts)); |
| 177 result->Set(keys::kCssCacheKey, |
| 178 CreateCacheData(cache_stats.cssStyleSheets)); |
| 179 } |
| 180 |
| 181 // Network and FPS are reported by the TaskManager per resource (tab), not |
| 182 // per process, therefore we need to iterate through the group of resources |
| 183 // and aggregate the data. |
| 184 float fps = 0, tmp = 0; |
| 185 int64 net = 0; |
| 186 int length = model->GetGroupRangeForResource(index).second; |
| 187 for (int i = 0; i < length; ++i) { |
| 188 net += model->GetNetworkUsage(index + i); |
| 189 if (model->GetFPS(index + i, &tmp)) |
| 190 fps += tmp; |
| 191 } |
| 192 result->SetDouble(keys::kFPSKey, static_cast<double>(fps)); |
38 result->SetDouble(keys::kNetworkKey, static_cast<double>(net)); | 193 result->SetDouble(keys::kNetworkKey, static_cast<double>(net)); |
| 194 |
| 195 return result; |
| 196 } |
| 197 |
| 198 // Since memory details are expensive to gather, we don't do it by default. |
| 199 // This function is a helper to add memory details data to an existing |
| 200 // Process object representation. |
| 201 void AddMemoryDetails(DictionaryValue* result, |
| 202 TaskManagerModel* model, |
| 203 int index) { |
| 204 size_t mem; |
| 205 int64 pr_mem = model->GetPrivateMemory(index, &mem) ? |
| 206 static_cast<int64>(mem) : -1; |
39 result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem)); | 207 result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem)); |
40 result->SetDouble(keys::kSharedMemoryKey, static_cast<double>(sh_mem)); | 208 } |
41 return result; | 209 |
42 } | 210 #endif // defined(ENABLE_TASK_MANAGER) |
| 211 |
| 212 } // local namespace |
43 | 213 |
44 ExtensionProcessesEventRouter* ExtensionProcessesEventRouter::GetInstance() { | 214 ExtensionProcessesEventRouter* ExtensionProcessesEventRouter::GetInstance() { |
45 return Singleton<ExtensionProcessesEventRouter>::get(); | 215 return Singleton<ExtensionProcessesEventRouter>::get(); |
46 } | 216 } |
47 | 217 |
48 ExtensionProcessesEventRouter::ExtensionProcessesEventRouter() { | 218 ExtensionProcessesEventRouter::ExtensionProcessesEventRouter() |
| 219 : listeners_(0), |
| 220 task_manager_listening_(false) { |
49 #if defined(ENABLE_TASK_MANAGER) | 221 #if defined(ENABLE_TASK_MANAGER) |
50 model_ = TaskManager::GetInstance()->model(); | 222 model_ = TaskManager::GetInstance()->model(); |
51 model_->AddObserver(this); | 223 model_->AddObserver(this); |
| 224 |
| 225 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_HANG, |
| 226 content::NotificationService::AllSources()); |
| 227 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 228 content::NotificationService::AllSources()); |
52 #endif // defined(ENABLE_TASK_MANAGER) | 229 #endif // defined(ENABLE_TASK_MANAGER) |
53 } | 230 } |
54 | 231 |
55 ExtensionProcessesEventRouter::~ExtensionProcessesEventRouter() { | 232 ExtensionProcessesEventRouter::~ExtensionProcessesEventRouter() { |
56 #if defined(ENABLE_TASK_MANAGER) | 233 #if defined(ENABLE_TASK_MANAGER) |
| 234 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_HANG, |
| 235 content::NotificationService::AllSources()); |
| 236 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 237 content::NotificationService::AllSources()); |
| 238 |
| 239 if (task_manager_listening_) |
| 240 model_->StopListening(); |
| 241 |
57 model_->RemoveObserver(this); | 242 model_->RemoveObserver(this); |
58 #endif // defined(ENABLE_TASK_MANAGER) | 243 #endif // defined(ENABLE_TASK_MANAGER) |
59 } | 244 } |
60 | 245 |
61 void ExtensionProcessesEventRouter::ObserveProfile(Profile* profile) { | 246 void ExtensionProcessesEventRouter::ObserveProfile(Profile* profile) { |
62 profiles_.insert(profile); | 247 profiles_.insert(profile); |
63 } | 248 } |
64 | 249 |
65 void ExtensionProcessesEventRouter::ListenerAdded() { | 250 void ExtensionProcessesEventRouter::ListenerAdded() { |
66 #if defined(ENABLE_TASK_MANAGER) | 251 #if defined(ENABLE_TASK_MANAGER) |
| 252 // The task manager has its own ref count to balance other callers of |
| 253 // StartUpdating/StopUpdating. |
67 model_->StartUpdating(); | 254 model_->StartUpdating(); |
68 #endif // defined(ENABLE_TASK_MANAGER) | 255 #endif // defined(ENABLE_TASK_MANAGER) |
| 256 ++listeners_; |
69 } | 257 } |
70 | 258 |
71 void ExtensionProcessesEventRouter::ListenerRemoved() { | 259 void ExtensionProcessesEventRouter::ListenerRemoved() { |
72 #if defined(ENABLE_TASK_MANAGER) | 260 DCHECK(listeners_ > 0); |
| 261 --listeners_; |
| 262 #if defined(ENABLE_TASK_MANAGER) |
| 263 // The task manager has its own ref count to balance other callers of |
| 264 // StartUpdating/StopUpdating. |
73 model_->StopUpdating(); | 265 model_->StopUpdating(); |
74 #endif // defined(ENABLE_TASK_MANAGER) | 266 #endif // defined(ENABLE_TASK_MANAGER) |
75 } | 267 } |
76 | 268 |
| 269 void ExtensionProcessesEventRouter::StartTaskManagerListening() { |
| 270 #if defined(ENABLE_TASK_MANAGER) |
| 271 if (!task_manager_listening_) { |
| 272 model_->StartListening(); |
| 273 task_manager_listening_ = true; |
| 274 } |
| 275 #endif // defined(ENABLE_TASK_MANAGER) |
| 276 } |
| 277 |
| 278 void ExtensionProcessesEventRouter::Observe( |
| 279 int type, |
| 280 const content::NotificationSource& source, |
| 281 const content::NotificationDetails& details) { |
| 282 |
| 283 switch (type) { |
| 284 case content::NOTIFICATION_RENDERER_PROCESS_HANG: |
| 285 ProcessHangEvent( |
| 286 content::Source<content::RenderWidgetHost>(source).ptr()); |
| 287 break; |
| 288 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: |
| 289 ProcessClosedEvent( |
| 290 content::Source<content::RenderProcessHost>(source).ptr(), |
| 291 content::Details<content::RenderProcessHost::RendererClosedDetails>( |
| 292 details).ptr()); |
| 293 break; |
| 294 default: |
| 295 NOTREACHED() << "Unexpected observe of type " << type; |
| 296 } |
| 297 return; |
| 298 } |
| 299 |
| 300 void ExtensionProcessesEventRouter::OnItemsAdded(int start, int length) { |
| 301 #if defined(ENABLE_TASK_MANAGER) |
| 302 DCHECK_EQ(length, 1); |
| 303 int index = start; |
| 304 |
| 305 std::string event(keys::kOnCreated); |
| 306 if (!HasEventListeners(event)) |
| 307 return; |
| 308 |
| 309 // If the item being added is not the first one in the group, find the base |
| 310 // index and use it for retrieving the process data. |
| 311 if (!model_->IsResourceFirstInGroup(start)) { |
| 312 index = model_->GetGroupIndexForResource(start); |
| 313 } |
| 314 |
| 315 ListValue args; |
| 316 DictionaryValue* process = CreateProcessFromModel( |
| 317 model_->GetUniqueChildProcessId(index), model_, index, false); |
| 318 DCHECK(process != NULL); |
| 319 |
| 320 if (process == NULL) |
| 321 return; |
| 322 |
| 323 args.Append(process); |
| 324 |
| 325 std::string json_args; |
| 326 base::JSONWriter::Write(&args, &json_args); |
| 327 NotifyProfiles(keys::kOnCreated, json_args); |
| 328 #endif // defined(ENABLE_TASK_MANAGER) |
| 329 } |
| 330 |
77 void ExtensionProcessesEventRouter::OnItemsChanged(int start, int length) { | 331 void ExtensionProcessesEventRouter::OnItemsChanged(int start, int length) { |
78 #if defined(ENABLE_TASK_MANAGER) | 332 #if defined(ENABLE_TASK_MANAGER) |
79 if (model_) { | 333 // If we don't have any listeners, return immediately. |
| 334 if (listeners_ == 0) |
| 335 return; |
| 336 |
| 337 if (!model_) |
| 338 return; |
| 339 |
| 340 // We need to know which type of onUpdated events to fire and whether to |
| 341 // collect memory or not. |
| 342 std::string updated_event(keys::kOnUpdated); |
| 343 std::string updated_event_memory(keys::kOnUpdatedWithMemory); |
| 344 bool updated = HasEventListeners(updated_event); |
| 345 bool updated_memory = HasEventListeners(updated_event_memory); |
| 346 |
| 347 DCHECK(updated || updated_memory); |
| 348 |
| 349 IDMap<DictionaryValue> processes_map; |
| 350 for (int i = start; i < start + length; i++) { |
| 351 if (model_->IsResourceFirstInGroup(i)) { |
| 352 int id = model_->GetUniqueChildProcessId(i); |
| 353 DictionaryValue* process = CreateProcessFromModel(id, model_, i, true); |
| 354 processes_map.AddWithID(process, i); |
| 355 } |
| 356 } |
| 357 |
| 358 int id; |
| 359 std::string idkey(keys::kIdKey); |
| 360 DictionaryValue* processes = new DictionaryValue(); |
| 361 |
| 362 if (updated) { |
| 363 IDMap<DictionaryValue>::iterator it(&processes_map); |
| 364 for (; !it.IsAtEnd(); it.Advance()) { |
| 365 if (!it.GetCurrentValue()->GetInteger(idkey, &id)) |
| 366 continue; |
| 367 |
| 368 // Store each process indexed by the string version of its id. |
| 369 processes->Set(base::IntToString(id), it.GetCurrentValue()); |
| 370 } |
| 371 |
80 ListValue args; | 372 ListValue args; |
81 DictionaryValue* processes = new DictionaryValue(); | |
82 for (int i = start; i < start + length; i++) { | |
83 if (model_->IsResourceFirstInGroup(i)) { | |
84 int id = model_->GetProcessId(i); | |
85 | |
86 // Determine process type | |
87 std::string type = keys::kProcessTypeOther; | |
88 TaskManager::Resource::Type resource_type = model_->GetResourceType(i); | |
89 switch (resource_type) { | |
90 case TaskManager::Resource::BROWSER: | |
91 type = keys::kProcessTypeBrowser; | |
92 break; | |
93 case TaskManager::Resource::RENDERER: | |
94 type = keys::kProcessTypeRenderer; | |
95 break; | |
96 case TaskManager::Resource::EXTENSION: | |
97 type = keys::kProcessTypeExtension; | |
98 break; | |
99 case TaskManager::Resource::NOTIFICATION: | |
100 type = keys::kProcessTypeNotification; | |
101 break; | |
102 case TaskManager::Resource::PLUGIN: | |
103 type = keys::kProcessTypePlugin; | |
104 break; | |
105 case TaskManager::Resource::WORKER: | |
106 type = keys::kProcessTypeWorker; | |
107 break; | |
108 case TaskManager::Resource::NACL: | |
109 type = keys::kProcessTypeNacl; | |
110 break; | |
111 case TaskManager::Resource::UTILITY: | |
112 type = keys::kProcessTypeUtility; | |
113 break; | |
114 case TaskManager::Resource::GPU: | |
115 type = keys::kProcessTypeGPU; | |
116 break; | |
117 case TaskManager::Resource::PROFILE_IMPORT: | |
118 case TaskManager::Resource::ZYGOTE: | |
119 case TaskManager::Resource::SANDBOX_HELPER: | |
120 case TaskManager::Resource::UNKNOWN: | |
121 type = keys::kProcessTypeOther; | |
122 break; | |
123 default: | |
124 NOTREACHED() << "Unknown resource type."; | |
125 } | |
126 | |
127 // Get process metrics as numbers | |
128 double cpu = model_->GetCPUUsage(i); | |
129 | |
130 // TODO(creis): Network is actually reported per-resource (tab), | |
131 // not per-process. We should aggregate it here. | |
132 int64 net = model_->GetNetworkUsage(i); | |
133 size_t mem; | |
134 int64 pr_mem = model_->GetPrivateMemory(i, &mem) ? | |
135 static_cast<int64>(mem) : -1; | |
136 int64 sh_mem = model_->GetSharedMemory(i, &mem) ? | |
137 static_cast<int64>(mem) : -1; | |
138 | |
139 // Store each process indexed by the string version of its id | |
140 processes->Set(base::IntToString(id), | |
141 CreateProcessValue(id, type, cpu, net, pr_mem, sh_mem)); | |
142 } | |
143 } | |
144 args.Append(processes); | 373 args.Append(processes); |
145 | |
146 std::string json_args; | 374 std::string json_args; |
147 base::JSONWriter::Write(&args, &json_args); | 375 base::JSONWriter::Write(&args, &json_args); |
148 | 376 NotifyProfiles(keys::kOnUpdated, json_args); |
149 // Notify each profile that is interested. | 377 } |
150 for (ProfileSet::iterator it = profiles_.begin(); | 378 |
151 it != profiles_.end(); it++) { | 379 if (updated_memory) { |
152 Profile* profile = *it; | 380 IDMap<DictionaryValue>::iterator it(&processes_map); |
153 DispatchEvent(profile, keys::kOnUpdated, json_args); | 381 for (; !it.IsAtEnd(); it.Advance()) { |
154 } | 382 if (!it.GetCurrentValue()->GetInteger(idkey, &id)) |
155 } | 383 continue; |
156 #endif // defined(ENABLE_TASK_MANAGER) | 384 |
157 } | 385 AddMemoryDetails(it.GetCurrentValue(), model_, it.GetCurrentKey()); |
158 | 386 |
159 void ExtensionProcessesEventRouter::DispatchEvent(Profile* profile, | 387 // Store each process indexed by the string version of its id if we didn't |
| 388 // already insert it as part of the onUpdated processing above. |
| 389 if (!updated) |
| 390 processes->Set(base::IntToString(id), it.GetCurrentValue()); |
| 391 } |
| 392 |
| 393 ListValue args; |
| 394 args.Append(processes); |
| 395 std::string json_args; |
| 396 base::JSONWriter::Write(&args, &json_args); |
| 397 NotifyProfiles(keys::kOnUpdatedWithMemory, json_args); |
| 398 } |
| 399 #endif // defined(ENABLE_TASK_MANAGER) |
| 400 } |
| 401 |
| 402 void ExtensionProcessesEventRouter::OnItemsToBeRemoved(int start, int length) { |
| 403 #if defined(ENABLE_TASK_MANAGER) |
| 404 DCHECK(length == 1); |
| 405 |
| 406 // Process exit for renderer processes has the data about exit code and |
| 407 // termination status, therefore we will rely on notifications and not on |
| 408 // the Task Manager data. We do use the rest of this method for non-renderer |
| 409 // processes. |
| 410 if (model_->GetResourceType(start) == TaskManager::Resource::RENDERER) |
| 411 return; |
| 412 |
| 413 // The callback function parameters. |
| 414 ListValue args; |
| 415 |
| 416 // First arg: The id of the process that was closed. |
| 417 args.Append(Value::CreateIntegerValue( |
| 418 model_->GetUniqueChildProcessId(start))); |
| 419 |
| 420 // Second arg: The exit type for the process. |
| 421 args.Append(Value::CreateIntegerValue(0)); |
| 422 |
| 423 // Third arg: The exit code for the process. |
| 424 args.Append(Value::CreateIntegerValue(0)); |
| 425 |
| 426 std::string json_args; |
| 427 base::JSONWriter::Write(&args, &json_args); |
| 428 NotifyProfiles(keys::kOnExited, json_args); |
| 429 #endif // defined(ENABLE_TASK_MANAGER) |
| 430 } |
| 431 |
| 432 void ExtensionProcessesEventRouter::ProcessHangEvent( |
| 433 content::RenderWidgetHost* widget) { |
| 434 #if defined(ENABLE_TASK_MANAGER) |
| 435 std::string event(keys::kOnUnresponsive); |
| 436 if (!HasEventListeners(event)) |
| 437 return; |
| 438 |
| 439 DictionaryValue* process = NULL; |
| 440 int count = model_->ResourceCount(); |
| 441 int id = widget->GetProcess()->GetID(); |
| 442 |
| 443 for (int i = 0; i < count; ++i) { |
| 444 if (model_->IsResourceFirstInGroup(i)) { |
| 445 if (id == model_->GetUniqueChildProcessId(i)) { |
| 446 process = CreateProcessFromModel(id, model_, i, false); |
| 447 break; |
| 448 } |
| 449 } |
| 450 } |
| 451 |
| 452 DCHECK(process); |
| 453 if (process == NULL) |
| 454 return; |
| 455 |
| 456 ListValue args; |
| 457 args.Append(process); |
| 458 |
| 459 std::string json_args; |
| 460 base::JSONWriter::Write(&args, &json_args); |
| 461 NotifyProfiles(keys::kOnUnresponsive, json_args); |
| 462 #endif // defined(ENABLE_TASK_MANAGER) |
| 463 } |
| 464 |
| 465 void ExtensionProcessesEventRouter::ProcessClosedEvent( |
| 466 content::RenderProcessHost* rph, |
| 467 content::RenderProcessHost::RendererClosedDetails* details) { |
| 468 #if defined(ENABLE_TASK_MANAGER) |
| 469 // The callback function parameters. |
| 470 ListValue args; |
| 471 |
| 472 // First arg: The id of the process that was closed. |
| 473 args.Append(Value::CreateIntegerValue(rph->GetID())); |
| 474 |
| 475 // Second arg: The exit type for the process. |
| 476 args.Append(Value::CreateIntegerValue(details->status)); |
| 477 |
| 478 // Third arg: The exit code for the process. |
| 479 args.Append(Value::CreateIntegerValue(details->exit_code)); |
| 480 |
| 481 std::string json_args; |
| 482 base::JSONWriter::Write(&args, &json_args); |
| 483 NotifyProfiles(keys::kOnExited, json_args); |
| 484 #endif // defined(ENABLE_TASK_MANAGER) |
| 485 } |
| 486 |
| 487 void ExtensionProcessesEventRouter::DispatchEvent( |
| 488 Profile* profile, |
160 const char* event_name, | 489 const char* event_name, |
161 const std::string& json_args) { | 490 const std::string& json_args) { |
162 if (profile && profile->GetExtensionEventRouter()) { | 491 if (profile && profile->GetExtensionEventRouter()) { |
163 profile->GetExtensionEventRouter()->DispatchEventToRenderers( | 492 profile->GetExtensionEventRouter()->DispatchEventToRenderers( |
164 event_name, json_args, NULL, GURL()); | 493 event_name, json_args, NULL, GURL()); |
165 } | 494 } |
166 } | 495 } |
167 | 496 |
| 497 void ExtensionProcessesEventRouter::NotifyProfiles(const char* event_name, |
| 498 std::string json_args) { |
| 499 for (ProfileSet::iterator it = profiles_.begin(); |
| 500 it != profiles_.end(); it++) { |
| 501 Profile* profile = *it; |
| 502 DispatchEvent(profile, event_name, json_args); |
| 503 } |
| 504 } |
| 505 |
| 506 // In order to determine whether there are any listeners for the event of |
| 507 // interest, we need to ask each profile whether it has one registered. |
| 508 // We only need to look for the profiles that have registered with the |
| 509 // this extension API. |
| 510 bool ExtensionProcessesEventRouter::HasEventListeners(std::string& event_name) { |
| 511 for (ProfileSet::iterator it = profiles_.begin(); |
| 512 it != profiles_.end(); it++) { |
| 513 Profile* profile = *it; |
| 514 ExtensionEventRouter* router = profile->GetExtensionEventRouter(); |
| 515 if (!router) |
| 516 continue; |
| 517 |
| 518 if (router->HasEventListener(event_name)) |
| 519 return true; |
| 520 } |
| 521 return false; |
| 522 } |
| 523 |
168 bool GetProcessIdForTabFunction::RunImpl() { | 524 bool GetProcessIdForTabFunction::RunImpl() { |
169 int tab_id; | 525 #if defined(ENABLE_TASK_MANAGER) |
170 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | 526 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_)); |
171 | 527 |
| 528 // Add a reference, which is balanced in GetProcessIdForTab to keep the object |
| 529 // around and allow for the callback to be invoked. |
| 530 AddRef(); |
| 531 |
| 532 // If the task manager is already listening, just post a task to execute |
| 533 // which will invoke the callback once we have returned from this function. |
| 534 // Otherwise, wait for the notification that the task manager is done with |
| 535 // the data gathering. |
| 536 if (ExtensionProcessesEventRouter::GetInstance()-> |
| 537 is_task_manager_listening()) { |
| 538 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 539 &GetProcessIdForTabFunction::GetProcessIdForTab, this)); |
| 540 } else { |
| 541 registrar_.Add(this, |
| 542 chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY, |
| 543 content::NotificationService::AllSources()); |
| 544 ExtensionProcessesEventRouter::GetInstance()-> |
| 545 StartTaskManagerListening(); |
| 546 } |
| 547 |
| 548 return true; |
| 549 #else |
| 550 error_ = ExtensionErrorUtils::FormatErrorMessage( |
| 551 errors::kExtensionNotSupported); |
| 552 return false; |
| 553 #endif // defined(ENABLE_TASK_MANAGER) |
| 554 } |
| 555 |
| 556 void GetProcessIdForTabFunction::Observe( |
| 557 int type, |
| 558 const content::NotificationSource& source, |
| 559 const content::NotificationDetails& details) { |
| 560 DCHECK_EQ(type, chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY); |
| 561 registrar_.RemoveAll(); |
| 562 GetProcessIdForTab(); |
| 563 } |
| 564 |
| 565 void GetProcessIdForTabFunction::GetProcessIdForTab() { |
172 TabContentsWrapper* contents = NULL; | 566 TabContentsWrapper* contents = NULL; |
173 int tab_index = -1; | 567 int tab_index = -1; |
174 if (!ExtensionTabUtil::GetTabById(tab_id, profile(), include_incognito(), | 568 if (!ExtensionTabUtil::GetTabById(tab_id_, profile(), include_incognito(), |
175 NULL, NULL, &contents, &tab_index)) { | 569 NULL, NULL, &contents, &tab_index)) { |
176 error_ = ExtensionErrorUtils::FormatErrorMessage( | 570 error_ = ExtensionErrorUtils::FormatErrorMessage( |
177 extension_tabs_module_constants::kTabNotFoundError, | 571 extension_tabs_module_constants::kTabNotFoundError, |
178 base::IntToString(tab_id)); | 572 base::IntToString(tab_id_)); |
179 return false; | 573 result_.reset(Value::CreateIntegerValue(-1)); |
180 } | 574 SendResponse(false); |
181 | 575 } else { |
182 // Return the process ID of the tab as an integer. | 576 int process_id = contents->web_contents()->GetRenderProcessHost()->GetID(); |
183 int id = base::GetProcId(contents->web_contents()-> | 577 result_.reset(Value::CreateIntegerValue(process_id)); |
184 GetRenderProcessHost()->GetHandle()); | 578 SendResponse(true); |
185 result_.reset(Value::CreateIntegerValue(id)); | 579 } |
| 580 |
| 581 // Balance the AddRef in the RunImpl. |
| 582 Release(); |
| 583 } |
| 584 |
| 585 bool TerminateFunction::RunImpl() { |
| 586 #if defined(ENABLE_TASK_MANAGER) |
| 587 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_)); |
| 588 |
| 589 // Add a reference, which is balanced in TerminateProcess to keep the object |
| 590 // around and allow for the callback to be invoked. |
| 591 AddRef(); |
| 592 |
| 593 // If the task manager is already listening, just post a task to execute |
| 594 // which will invoke the callback once we have returned from this function. |
| 595 // Otherwise, wait for the notification that the task manager is done with |
| 596 // the data gathering. |
| 597 if (ExtensionProcessesEventRouter::GetInstance()-> |
| 598 is_task_manager_listening()) { |
| 599 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 600 &TerminateFunction::TerminateProcess, this)); |
| 601 } else { |
| 602 registrar_.Add(this, |
| 603 chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY, |
| 604 content::NotificationService::AllSources()); |
| 605 ExtensionProcessesEventRouter::GetInstance()-> |
| 606 StartTaskManagerListening(); |
| 607 } |
| 608 |
186 return true; | 609 return true; |
187 } | 610 #else |
| 611 error_ = ExtensionErrorUtils::FormatErrorMessage( |
| 612 errors::kExtensionNotSupported); |
| 613 return false; |
| 614 #endif // defined(ENABLE_TASK_MANAGER) |
| 615 } |
| 616 |
| 617 void TerminateFunction::Observe( |
| 618 int type, |
| 619 const content::NotificationSource& source, |
| 620 const content::NotificationDetails& details) { |
| 621 DCHECK_EQ(type, chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY); |
| 622 registrar_.RemoveAll(); |
| 623 TerminateProcess(); |
| 624 } |
| 625 |
| 626 void TerminateFunction::TerminateProcess() { |
| 627 TaskManagerModel* model = TaskManager::GetInstance()->model(); |
| 628 |
| 629 int count = model->ResourceCount(); |
| 630 bool killed = false; |
| 631 bool found = false; |
| 632 |
| 633 for (int i = 0; i < count; ++i) { |
| 634 if (model->IsResourceFirstInGroup(i)) { |
| 635 if (process_id_ == model->GetUniqueChildProcessId(i)) { |
| 636 found = true; |
| 637 killed = base::KillProcess(model->GetProcess(i), |
| 638 content::RESULT_CODE_KILLED, true); |
| 639 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1); |
| 640 break; |
| 641 } |
| 642 } |
| 643 } |
| 644 |
| 645 if (!found) { |
| 646 error_ = ExtensionErrorUtils::FormatErrorMessage(errors::kProcessNotFound, |
| 647 base::IntToString(process_id_)); |
| 648 SendResponse(false); |
| 649 } else { |
| 650 result_.reset(Value::CreateBooleanValue(killed)); |
| 651 SendResponse(true); |
| 652 } |
| 653 |
| 654 // Balance the AddRef in the RunImpl. |
| 655 Release(); |
| 656 } |
| 657 |
| 658 GetProcessInfoFunction::GetProcessInfoFunction() { |
| 659 } |
| 660 |
| 661 GetProcessInfoFunction::~GetProcessInfoFunction() { |
| 662 } |
| 663 |
| 664 bool GetProcessInfoFunction::RunImpl() { |
| 665 #if defined(ENABLE_TASK_MANAGER) |
| 666 Value* processes = NULL; |
| 667 |
| 668 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &processes)); |
| 669 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory_)); |
| 670 |
| 671 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers( |
| 672 processes, &process_ids_)); |
| 673 |
| 674 // Add a reference, which is balanced in GatherProcessInfo to keep the object |
| 675 // around and allow for the callback to be invoked. |
| 676 AddRef(); |
| 677 |
| 678 // If the task manager is already listening, just post a task to execute |
| 679 // which will invoke the callback once we have returned from this function. |
| 680 // Otherwise, wait for the notification that the task manager is done with |
| 681 // the data gathering. |
| 682 if (ExtensionProcessesEventRouter::GetInstance()-> |
| 683 is_task_manager_listening()) { |
| 684 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 685 &GetProcessInfoFunction::GatherProcessInfo, this)); |
| 686 } else { |
| 687 registrar_.Add(this, |
| 688 chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY, |
| 689 content::NotificationService::AllSources()); |
| 690 ExtensionProcessesEventRouter::GetInstance()-> |
| 691 StartTaskManagerListening(); |
| 692 } |
| 693 return true; |
| 694 |
| 695 #else |
| 696 error_ = ExtensionErrorUtils::FormatErrorMessage( |
| 697 errors::kExtensionNotSupported); |
| 698 return false; |
| 699 #endif // defined(ENABLE_TASK_MANAGER) |
| 700 } |
| 701 |
| 702 void GetProcessInfoFunction::Observe( |
| 703 int type, |
| 704 const content::NotificationSource& source, |
| 705 const content::NotificationDetails& details) { |
| 706 DCHECK_EQ(type, chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY); |
| 707 registrar_.RemoveAll(); |
| 708 GatherProcessInfo(); |
| 709 } |
| 710 |
| 711 void GetProcessInfoFunction::GatherProcessInfo() { |
| 712 TaskManagerModel* model = TaskManager::GetInstance()->model(); |
| 713 DictionaryValue* processes = new DictionaryValue(); |
| 714 |
| 715 // If there are no process IDs specified, it means we need to return all of |
| 716 // the ones we know of. |
| 717 if (process_ids_.size() == 0) { |
| 718 int resources = model->ResourceCount(); |
| 719 for (int i = 0; i < resources; ++i) { |
| 720 if (model->IsResourceFirstInGroup(i)) { |
| 721 int id = model->GetUniqueChildProcessId(i); |
| 722 DictionaryValue* d = CreateProcessFromModel(id, model, i, false); |
| 723 if (memory_) |
| 724 AddMemoryDetails(d, model, i); |
| 725 processes->Set(base::IntToString(id), d); |
| 726 } |
| 727 } |
| 728 } else { |
| 729 int resources = model->ResourceCount(); |
| 730 for (int i = 0; i < resources; ++i) { |
| 731 if (model->IsResourceFirstInGroup(i)) { |
| 732 int id = model->GetUniqueChildProcessId(i); |
| 733 std::vector<int>::iterator proc_id = std::find(process_ids_.begin(), |
| 734 process_ids_.end(), id); |
| 735 if (proc_id != process_ids_.end()) { |
| 736 DictionaryValue* d = CreateProcessFromModel(id, model, i, false); |
| 737 if (memory_) |
| 738 AddMemoryDetails(d, model, i); |
| 739 processes->Set(base::IntToString(id), d); |
| 740 |
| 741 process_ids_.erase(proc_id); |
| 742 if (process_ids_.size() == 0) |
| 743 break; |
| 744 } |
| 745 } |
| 746 } |
| 747 DCHECK(process_ids_.size() == 0); |
| 748 } |
| 749 |
| 750 result_.reset(processes); |
| 751 SendResponse(true); |
| 752 |
| 753 // Balance the AddRef in the RunImpl. |
| 754 Release(); |
| 755 } |
OLD | NEW |