Revert 285173 "Removed InProcessBrowserTest::CleanUpOnMainThread()"
[chromium-blink-merge.git] / chrome / browser / extensions / api / processes / processes_api.cc
blob56a7cb7d97a8cf81d77d7db6454b165e6a5775f6
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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/processes/processes_api.h"
7 #include "base/callback.h"
8 #include "base/json/json_writer.h"
9 #include "base/lazy_instance.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/api/processes/processes_api_constants.h"
17 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/extension_tab_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/task_manager/resource_provider.h"
22 #include "chrome/browser/task_manager/task_manager.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/render_widget_host.h"
31 #include "content/public/browser/render_widget_host_iterator.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/common/result_codes.h"
34 #include "extensions/browser/event_router.h"
35 #include "extensions/browser/extension_function_registry.h"
36 #include "extensions/browser/extension_function_util.h"
37 #include "extensions/common/error_utils.h"
39 namespace extensions {
41 namespace keys = processes_api_constants;
42 namespace errors = processes_api_constants;
44 namespace {
46 #if defined(ENABLE_TASK_MANAGER)
48 base::DictionaryValue* CreateCacheData(
49 const blink::WebCache::ResourceTypeStat& stat) {
51 base::DictionaryValue* cache = new base::DictionaryValue();
52 cache->SetDouble(keys::kCacheSize, static_cast<double>(stat.size));
53 cache->SetDouble(keys::kCacheLiveSize, static_cast<double>(stat.liveSize));
54 return cache;
57 void SetProcessType(base::DictionaryValue* result,
58 TaskManagerModel* model,
59 int index) {
60 // Determine process type.
61 std::string type = keys::kProcessTypeOther;
62 task_manager::Resource::Type resource_type = model->GetResourceType(index);
63 switch (resource_type) {
64 case task_manager::Resource::BROWSER:
65 type = keys::kProcessTypeBrowser;
66 break;
67 case task_manager::Resource::RENDERER:
68 type = keys::kProcessTypeRenderer;
69 break;
70 case task_manager::Resource::EXTENSION:
71 type = keys::kProcessTypeExtension;
72 break;
73 case task_manager::Resource::NOTIFICATION:
74 type = keys::kProcessTypeNotification;
75 break;
76 case task_manager::Resource::PLUGIN:
77 type = keys::kProcessTypePlugin;
78 break;
79 case task_manager::Resource::WORKER:
80 type = keys::kProcessTypeWorker;
81 break;
82 case task_manager::Resource::NACL:
83 type = keys::kProcessTypeNacl;
84 break;
85 case task_manager::Resource::UTILITY:
86 type = keys::kProcessTypeUtility;
87 break;
88 case task_manager::Resource::GPU:
89 type = keys::kProcessTypeGPU;
90 break;
91 case task_manager::Resource::ZYGOTE:
92 case task_manager::Resource::SANDBOX_HELPER:
93 case task_manager::Resource::UNKNOWN:
94 type = keys::kProcessTypeOther;
95 break;
96 default:
97 NOTREACHED() << "Unknown resource type.";
99 result->SetString(keys::kTypeKey, type);
102 base::ListValue* GetTabsForProcess(int process_id) {
103 base::ListValue* tabs_list = new base::ListValue();
105 // The tabs list only makes sense for render processes, so if we don't find
106 // one, just return the empty list.
107 content::RenderProcessHost* rph =
108 content::RenderProcessHost::FromID(process_id);
109 if (rph == NULL)
110 return tabs_list;
112 int tab_id = -1;
113 // We need to loop through all the RVHs to ensure we collect the set of all
114 // tabs using this renderer process.
115 scoped_ptr<content::RenderWidgetHostIterator> widgets(
116 content::RenderWidgetHost::GetRenderWidgetHosts());
117 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
118 if (widget->GetProcess()->GetID() != process_id)
119 continue;
120 if (!widget->IsRenderView())
121 continue;
123 content::RenderViewHost* host = content::RenderViewHost::From(widget);
124 content::WebContents* contents =
125 content::WebContents::FromRenderViewHost(host);
126 if (contents) {
127 tab_id = ExtensionTabUtil::GetTabId(contents);
128 if (tab_id != -1)
129 tabs_list->Append(new base::FundamentalValue(tab_id));
133 return tabs_list;
136 // This function creates a Process object to be returned to the extensions
137 // using these APIs. For memory details, which are not added by this function,
138 // the callers need to use AddMemoryDetails.
139 base::DictionaryValue* CreateProcessFromModel(int process_id,
140 TaskManagerModel* model,
141 int index,
142 bool include_optional) {
143 base::DictionaryValue* result = new base::DictionaryValue();
144 size_t mem;
146 result->SetInteger(keys::kIdKey, process_id);
147 result->SetInteger(keys::kOsProcessIdKey, model->GetProcessId(index));
148 SetProcessType(result, model, index);
149 result->SetString(keys::kTitleKey, model->GetResourceTitle(index));
150 result->SetString(keys::kProfileKey,
151 model->GetResourceProfileName(index));
152 result->SetInteger(keys::kNaClDebugPortKey,
153 model->GetNaClDebugStubPort(index));
155 result->Set(keys::kTabsListKey, GetTabsForProcess(process_id));
157 // If we don't need to include the optional properties, just return now.
158 if (!include_optional)
159 return result;
161 result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index));
163 if (model->GetV8Memory(index, &mem))
164 result->SetDouble(keys::kJsMemoryAllocatedKey,
165 static_cast<double>(mem));
167 if (model->GetV8MemoryUsed(index, &mem))
168 result->SetDouble(keys::kJsMemoryUsedKey,
169 static_cast<double>(mem));
171 if (model->GetSqliteMemoryUsedBytes(index, &mem))
172 result->SetDouble(keys::kSqliteMemoryKey,
173 static_cast<double>(mem));
175 blink::WebCache::ResourceTypeStats cache_stats;
176 if (model->GetWebCoreCacheStats(index, &cache_stats)) {
177 result->Set(keys::kImageCacheKey,
178 CreateCacheData(cache_stats.images));
179 result->Set(keys::kScriptCacheKey,
180 CreateCacheData(cache_stats.scripts));
181 result->Set(keys::kCssCacheKey,
182 CreateCacheData(cache_stats.cssStyleSheets));
185 // Network is reported by the TaskManager per resource (tab), not per
186 // process, therefore we need to iterate through the group of resources
187 // and aggregate the data.
188 int64 net = 0;
189 int length = model->GetGroupRangeForResource(index).second;
190 for (int i = 0; i < length; ++i)
191 net += model->GetNetworkUsage(index + i);
192 result->SetDouble(keys::kNetworkKey, static_cast<double>(net));
194 return result;
197 // Since memory details are expensive to gather, we don't do it by default.
198 // This function is a helper to add memory details data to an existing
199 // Process object representation.
200 void AddMemoryDetails(base::DictionaryValue* result,
201 TaskManagerModel* model,
202 int index) {
203 size_t mem;
204 int64 pr_mem = model->GetPrivateMemory(index, &mem) ?
205 static_cast<int64>(mem) : -1;
206 result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem));
209 #endif // defined(ENABLE_TASK_MANAGER)
211 } // namespace
213 ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
214 : browser_context_(context), listeners_(0), task_manager_listening_(false) {
215 #if defined(ENABLE_TASK_MANAGER)
216 model_ = TaskManager::GetInstance()->model();
217 model_->AddObserver(this);
219 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
220 content::NotificationService::AllSources());
221 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
222 content::NotificationService::AllSources());
223 #endif // defined(ENABLE_TASK_MANAGER)
226 ProcessesEventRouter::~ProcessesEventRouter() {
227 #if defined(ENABLE_TASK_MANAGER)
228 registrar_.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
229 content::NotificationService::AllSources());
230 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
231 content::NotificationService::AllSources());
233 if (task_manager_listening_)
234 model_->StopListening();
236 model_->RemoveObserver(this);
237 #endif // defined(ENABLE_TASK_MANAGER)
240 void ProcessesEventRouter::ListenerAdded() {
241 #if defined(ENABLE_TASK_MANAGER)
242 // The task manager has its own ref count to balance other callers of
243 // StartUpdating/StopUpdating.
244 model_->StartUpdating();
245 #endif // defined(ENABLE_TASK_MANAGER)
246 ++listeners_;
249 void ProcessesEventRouter::ListenerRemoved() {
250 DCHECK_GT(listeners_, 0);
251 --listeners_;
252 #if defined(ENABLE_TASK_MANAGER)
253 // The task manager has its own ref count to balance other callers of
254 // StartUpdating/StopUpdating.
255 model_->StopUpdating();
256 #endif // defined(ENABLE_TASK_MANAGER)
259 void ProcessesEventRouter::StartTaskManagerListening() {
260 #if defined(ENABLE_TASK_MANAGER)
261 if (!task_manager_listening_) {
262 model_->StartListening();
263 task_manager_listening_ = true;
265 #endif // defined(ENABLE_TASK_MANAGER)
268 void ProcessesEventRouter::Observe(
269 int type,
270 const content::NotificationSource& source,
271 const content::NotificationDetails& details) {
273 switch (type) {
274 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
275 ProcessHangEvent(
276 content::Source<content::RenderWidgetHost>(source).ptr());
277 break;
278 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
279 ProcessClosedEvent(
280 content::Source<content::RenderProcessHost>(source).ptr(),
281 content::Details<content::RenderProcessHost::RendererClosedDetails>(
282 details).ptr());
283 break;
284 default:
285 NOTREACHED() << "Unexpected observe of type " << type;
287 return;
290 void ProcessesEventRouter::OnItemsAdded(int start, int length) {
291 #if defined(ENABLE_TASK_MANAGER)
292 DCHECK_EQ(length, 1);
293 int index = start;
295 std::string event(keys::kOnCreated);
296 if (!HasEventListeners(event))
297 return;
299 // If the item being added is not the first one in the group, find the base
300 // index and use it for retrieving the process data.
301 if (!model_->IsResourceFirstInGroup(start)) {
302 index = model_->GetGroupIndexForResource(start);
305 scoped_ptr<base::ListValue> args(new base::ListValue());
306 base::DictionaryValue* process = CreateProcessFromModel(
307 model_->GetUniqueChildProcessId(index), model_, index, false);
308 DCHECK(process != NULL);
310 if (process == NULL)
311 return;
313 args->Append(process);
315 DispatchEvent(keys::kOnCreated, args.Pass());
316 #endif // defined(ENABLE_TASK_MANAGER)
319 void ProcessesEventRouter::OnItemsChanged(int start, int length) {
320 #if defined(ENABLE_TASK_MANAGER)
321 // If we don't have any listeners, return immediately.
322 if (listeners_ == 0)
323 return;
325 if (!model_)
326 return;
328 // We need to know which type of onUpdated events to fire and whether to
329 // collect memory or not.
330 std::string updated_event(keys::kOnUpdated);
331 std::string updated_event_memory(keys::kOnUpdatedWithMemory);
332 bool updated = HasEventListeners(updated_event);
333 bool updated_memory = HasEventListeners(updated_event_memory);
335 DCHECK(updated || updated_memory);
337 IDMap<base::DictionaryValue> processes_map;
338 for (int i = start; i < start + length; i++) {
339 if (model_->IsResourceFirstInGroup(i)) {
340 int id = model_->GetUniqueChildProcessId(i);
341 base::DictionaryValue* process = CreateProcessFromModel(id, model_, i,
342 true);
343 processes_map.AddWithID(process, i);
347 int id;
348 std::string idkey(keys::kIdKey);
349 base::DictionaryValue* processes = new base::DictionaryValue();
351 if (updated) {
352 IDMap<base::DictionaryValue>::iterator it(&processes_map);
353 for (; !it.IsAtEnd(); it.Advance()) {
354 if (!it.GetCurrentValue()->GetInteger(idkey, &id))
355 continue;
357 // Store each process indexed by the string version of its id.
358 processes->Set(base::IntToString(id), it.GetCurrentValue());
361 scoped_ptr<base::ListValue> args(new base::ListValue());
362 args->Append(processes);
363 DispatchEvent(keys::kOnUpdated, args.Pass());
366 if (updated_memory) {
367 IDMap<base::DictionaryValue>::iterator it(&processes_map);
368 for (; !it.IsAtEnd(); it.Advance()) {
369 if (!it.GetCurrentValue()->GetInteger(idkey, &id))
370 continue;
372 AddMemoryDetails(it.GetCurrentValue(), model_, it.GetCurrentKey());
374 // Store each process indexed by the string version of its id if we didn't
375 // already insert it as part of the onUpdated processing above.
376 if (!updated)
377 processes->Set(base::IntToString(id), it.GetCurrentValue());
380 scoped_ptr<base::ListValue> args(new base::ListValue());
381 args->Append(processes);
382 DispatchEvent(keys::kOnUpdatedWithMemory, args.Pass());
384 #endif // defined(ENABLE_TASK_MANAGER)
387 void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
388 #if defined(ENABLE_TASK_MANAGER)
389 DCHECK_EQ(length, 1);
391 // Process exit for renderer processes has the data about exit code and
392 // termination status, therefore we will rely on notifications and not on
393 // the Task Manager data. We do use the rest of this method for non-renderer
394 // processes.
395 if (model_->GetResourceType(start) == task_manager::Resource::RENDERER)
396 return;
398 // The callback function parameters.
399 scoped_ptr<base::ListValue> args(new base::ListValue());
401 // First arg: The id of the process that was closed.
402 args->Append(new base::FundamentalValue(
403 model_->GetUniqueChildProcessId(start)));
405 // Second arg: The exit type for the process.
406 args->Append(new base::FundamentalValue(0));
408 // Third arg: The exit code for the process.
409 args->Append(new base::FundamentalValue(0));
411 DispatchEvent(keys::kOnExited, args.Pass());
412 #endif // defined(ENABLE_TASK_MANAGER)
415 void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) {
416 #if defined(ENABLE_TASK_MANAGER)
417 std::string event(keys::kOnUnresponsive);
418 if (!HasEventListeners(event))
419 return;
421 base::DictionaryValue* process = NULL;
422 int count = model_->ResourceCount();
423 int id = widget->GetProcess()->GetID();
425 for (int i = 0; i < count; ++i) {
426 if (model_->IsResourceFirstInGroup(i)) {
427 if (id == model_->GetUniqueChildProcessId(i)) {
428 process = CreateProcessFromModel(id, model_, i, false);
429 break;
434 if (process == NULL)
435 return;
437 scoped_ptr<base::ListValue> args(new base::ListValue());
438 args->Append(process);
440 DispatchEvent(keys::kOnUnresponsive, args.Pass());
441 #endif // defined(ENABLE_TASK_MANAGER)
444 void ProcessesEventRouter::ProcessClosedEvent(
445 content::RenderProcessHost* rph,
446 content::RenderProcessHost::RendererClosedDetails* details) {
447 #if defined(ENABLE_TASK_MANAGER)
448 // The callback function parameters.
449 scoped_ptr<base::ListValue> args(new base::ListValue());
451 // First arg: The id of the process that was closed.
452 args->Append(new base::FundamentalValue(rph->GetID()));
454 // Second arg: The exit type for the process.
455 args->Append(new base::FundamentalValue(details->status));
457 // Third arg: The exit code for the process.
458 args->Append(new base::FundamentalValue(details->exit_code));
460 DispatchEvent(keys::kOnExited, args.Pass());
461 #endif // defined(ENABLE_TASK_MANAGER)
464 void ProcessesEventRouter::DispatchEvent(
465 const std::string& event_name,
466 scoped_ptr<base::ListValue> event_args) {
467 EventRouter* event_router = EventRouter::Get(browser_context_);
468 if (event_router) {
469 scoped_ptr<extensions::Event> event(new extensions::Event(
470 event_name, event_args.Pass()));
471 event_router->BroadcastEvent(event.Pass());
475 bool ProcessesEventRouter::HasEventListeners(const std::string& event_name) {
476 EventRouter* event_router = EventRouter::Get(browser_context_);
477 return event_router && event_router->HasEventListener(event_name);
480 ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
481 : browser_context_(context) {
482 EventRouter* event_router = EventRouter::Get(browser_context_);
483 event_router->RegisterObserver(this, processes_api_constants::kOnUpdated);
484 event_router->RegisterObserver(this,
485 processes_api_constants::kOnUpdatedWithMemory);
486 ExtensionFunctionRegistry* registry =
487 ExtensionFunctionRegistry::GetInstance();
488 registry->RegisterFunction<extensions::GetProcessIdForTabFunction>();
489 registry->RegisterFunction<extensions::TerminateFunction>();
490 registry->RegisterFunction<extensions::GetProcessInfoFunction>();
493 ProcessesAPI::~ProcessesAPI() {
496 void ProcessesAPI::Shutdown() {
497 EventRouter::Get(browser_context_)->UnregisterObserver(this);
500 static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
501 g_factory = LAZY_INSTANCE_INITIALIZER;
503 // static
504 BrowserContextKeyedAPIFactory<ProcessesAPI>*
505 ProcessesAPI::GetFactoryInstance() {
506 return g_factory.Pointer();
509 // static
510 ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
511 return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
514 ProcessesEventRouter* ProcessesAPI::processes_event_router() {
515 if (!processes_event_router_)
516 processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
517 return processes_event_router_.get();
520 void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
521 // We lazily tell the TaskManager to start updating when listeners to the
522 // processes.onUpdated or processes.onUpdatedWithMemory events arrive.
523 processes_event_router()->ListenerAdded();
526 void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
527 // If a processes.onUpdated or processes.onUpdatedWithMemory event listener
528 // is removed (or a process with one exits), then we let the extension API
529 // know that it has one fewer listener.
530 processes_event_router()->ListenerRemoved();
533 GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) {
536 bool GetProcessIdForTabFunction::RunAsync() {
537 #if defined(ENABLE_TASK_MANAGER)
538 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_));
540 // Add a reference, which is balanced in GetProcessIdForTab to keep the object
541 // around and allow for the callback to be invoked.
542 AddRef();
544 // If the task manager is already listening, just post a task to execute
545 // which will invoke the callback once we have returned from this function.
546 // Otherwise, wait for the notification that the task manager is done with
547 // the data gathering.
548 if (ProcessesAPI::Get(GetProfile())
549 ->processes_event_router()
550 ->is_task_manager_listening()) {
551 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
552 &GetProcessIdForTabFunction::GetProcessIdForTab, this));
553 } else {
554 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
555 base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab, this));
557 ProcessesAPI::Get(GetProfile())
558 ->processes_event_router()
559 ->StartTaskManagerListening();
562 return true;
563 #else
564 error_ = errors::kExtensionNotSupported;
565 return false;
566 #endif // defined(ENABLE_TASK_MANAGER)
569 void GetProcessIdForTabFunction::GetProcessIdForTab() {
570 content::WebContents* contents = NULL;
571 int tab_index = -1;
572 if (!ExtensionTabUtil::GetTabById(tab_id_,
573 GetProfile(),
574 include_incognito(),
575 NULL,
576 NULL,
577 &contents,
578 &tab_index)) {
579 error_ = ErrorUtils::FormatErrorMessage(
580 extensions::tabs_constants::kTabNotFoundError,
581 base::IntToString(tab_id_));
582 SetResult(new base::FundamentalValue(-1));
583 SendResponse(false);
584 } else {
585 int process_id = contents->GetRenderProcessHost()->GetID();
586 SetResult(new base::FundamentalValue(process_id));
587 SendResponse(true);
590 // Balance the AddRef in the RunAsync.
591 Release();
594 TerminateFunction::TerminateFunction() : process_id_(-1) {
597 bool TerminateFunction::RunAsync() {
598 #if defined(ENABLE_TASK_MANAGER)
599 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_));
601 // Add a reference, which is balanced in TerminateProcess to keep the object
602 // around and allow for the callback to be invoked.
603 AddRef();
605 // If the task manager is already listening, just post a task to execute
606 // which will invoke the callback once we have returned from this function.
607 // Otherwise, wait for the notification that the task manager is done with
608 // the data gathering.
609 if (ProcessesAPI::Get(GetProfile())
610 ->processes_event_router()
611 ->is_task_manager_listening()) {
612 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
613 &TerminateFunction::TerminateProcess, this));
614 } else {
615 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
616 base::Bind(&TerminateFunction::TerminateProcess, this));
618 ProcessesAPI::Get(GetProfile())
619 ->processes_event_router()
620 ->StartTaskManagerListening();
623 return true;
624 #else
625 error_ = errors::kExtensionNotSupported;
626 return false;
627 #endif // defined(ENABLE_TASK_MANAGER)
631 void TerminateFunction::TerminateProcess() {
632 #if defined(ENABLE_TASK_MANAGER)
633 TaskManagerModel* model = TaskManager::GetInstance()->model();
635 int count = model->ResourceCount();
636 bool killed = false;
637 bool found = false;
639 for (int i = 0; i < count; ++i) {
640 if (model->IsResourceFirstInGroup(i)) {
641 if (process_id_ == model->GetUniqueChildProcessId(i)) {
642 found = true;
643 killed = base::KillProcess(model->GetProcess(i),
644 content::RESULT_CODE_KILLED, true);
645 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
646 break;
651 if (!found) {
652 error_ = ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
653 base::IntToString(process_id_));
654 SendResponse(false);
655 } else {
656 SetResult(new base::FundamentalValue(killed));
657 SendResponse(true);
660 // Balance the AddRef in the RunAsync.
661 Release();
662 #else
663 error_ = errors::kExtensionNotSupported;
664 SendResponse(false);
665 #endif // defined(ENABLE_TASK_MANAGER)
668 GetProcessInfoFunction::GetProcessInfoFunction()
669 #if defined(ENABLE_TASK_MANAGER)
670 : memory_(false)
671 #endif
675 GetProcessInfoFunction::~GetProcessInfoFunction() {
678 bool GetProcessInfoFunction::RunAsync() {
679 #if defined(ENABLE_TASK_MANAGER)
680 base::Value* processes = NULL;
682 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &processes));
683 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory_));
685 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
686 processes, &process_ids_));
688 // Add a reference, which is balanced in GatherProcessInfo to keep the object
689 // around and allow for the callback to be invoked.
690 AddRef();
692 // If the task manager is already listening, just post a task to execute
693 // which will invoke the callback once we have returned from this function.
694 // Otherwise, wait for the notification that the task manager is done with
695 // the data gathering.
696 if (ProcessesAPI::Get(GetProfile())
697 ->processes_event_router()
698 ->is_task_manager_listening()) {
699 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
700 &GetProcessInfoFunction::GatherProcessInfo, this));
701 } else {
702 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
703 base::Bind(&GetProcessInfoFunction::GatherProcessInfo, this));
705 ProcessesAPI::Get(GetProfile())
706 ->processes_event_router()
707 ->StartTaskManagerListening();
709 return true;
711 #else
712 error_ = errors::kExtensionNotSupported;
713 return false;
714 #endif // defined(ENABLE_TASK_MANAGER)
717 void GetProcessInfoFunction::GatherProcessInfo() {
718 #if defined(ENABLE_TASK_MANAGER)
719 TaskManagerModel* model = TaskManager::GetInstance()->model();
720 base::DictionaryValue* processes = new base::DictionaryValue();
722 // If there are no process IDs specified, it means we need to return all of
723 // the ones we know of.
724 if (process_ids_.size() == 0) {
725 int resources = model->ResourceCount();
726 for (int i = 0; i < resources; ++i) {
727 if (model->IsResourceFirstInGroup(i)) {
728 int id = model->GetUniqueChildProcessId(i);
729 base::DictionaryValue* d = CreateProcessFromModel(id, model, i, false);
730 if (memory_)
731 AddMemoryDetails(d, model, i);
732 processes->Set(base::IntToString(id), d);
735 } else {
736 int resources = model->ResourceCount();
737 for (int i = 0; i < resources; ++i) {
738 if (model->IsResourceFirstInGroup(i)) {
739 int id = model->GetUniqueChildProcessId(i);
740 std::vector<int>::iterator proc_id = std::find(process_ids_.begin(),
741 process_ids_.end(), id);
742 if (proc_id != process_ids_.end()) {
743 base::DictionaryValue* d =
744 CreateProcessFromModel(id, model, i, false);
745 if (memory_)
746 AddMemoryDetails(d, model, i);
747 processes->Set(base::IntToString(id), d);
749 process_ids_.erase(proc_id);
750 if (process_ids_.size() == 0)
751 break;
755 DCHECK_EQ(process_ids_.size(), 0U);
758 SetResult(processes);
759 SendResponse(true);
761 // Balance the AddRef in the RunAsync.
762 Release();
763 #endif // defined(ENABLE_TASK_MANAGER)
766 } // namespace extensions