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
;
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
));
57 void SetProcessType(base::DictionaryValue
* result
,
58 TaskManagerModel
* model
,
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
;
67 case task_manager::Resource::RENDERER
:
68 type
= keys::kProcessTypeRenderer
;
70 case task_manager::Resource::EXTENSION
:
71 type
= keys::kProcessTypeExtension
;
73 case task_manager::Resource::NOTIFICATION
:
74 type
= keys::kProcessTypeNotification
;
76 case task_manager::Resource::PLUGIN
:
77 type
= keys::kProcessTypePlugin
;
79 case task_manager::Resource::WORKER
:
80 type
= keys::kProcessTypeWorker
;
82 case task_manager::Resource::NACL
:
83 type
= keys::kProcessTypeNacl
;
85 case task_manager::Resource::UTILITY
:
86 type
= keys::kProcessTypeUtility
;
88 case task_manager::Resource::GPU
:
89 type
= keys::kProcessTypeGPU
;
91 case task_manager::Resource::ZYGOTE
:
92 case task_manager::Resource::SANDBOX_HELPER
:
93 case task_manager::Resource::UNKNOWN
:
94 type
= keys::kProcessTypeOther
;
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
);
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
)
120 if (!widget
->IsRenderView())
123 content::RenderViewHost
* host
= content::RenderViewHost::From(widget
);
124 content::WebContents
* contents
=
125 content::WebContents::FromRenderViewHost(host
);
127 tab_id
= ExtensionTabUtil::GetTabId(contents
);
129 tabs_list
->Append(new base::FundamentalValue(tab_id
));
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
,
142 bool include_optional
) {
143 base::DictionaryValue
* result
= new base::DictionaryValue();
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
)
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 and FPS are reported by the TaskManager per resource (tab), not
186 // per process, therefore we need to iterate through the group of resources
187 // and aggregate the data.
188 float fps
= 0, tmp
= 0;
190 int length
= model
->GetGroupRangeForResource(index
).second
;
191 for (int i
= 0; i
< length
; ++i
) {
192 net
+= model
->GetNetworkUsage(index
+ i
);
193 if (model
->GetFPS(index
+ i
, &tmp
))
196 result
->SetDouble(keys::kFPSKey
, static_cast<double>(fps
));
197 result
->SetDouble(keys::kNetworkKey
, static_cast<double>(net
));
202 // Since memory details are expensive to gather, we don't do it by default.
203 // This function is a helper to add memory details data to an existing
204 // Process object representation.
205 void AddMemoryDetails(base::DictionaryValue
* result
,
206 TaskManagerModel
* model
,
209 int64 pr_mem
= model
->GetPrivateMemory(index
, &mem
) ?
210 static_cast<int64
>(mem
) : -1;
211 result
->SetDouble(keys::kPrivateMemoryKey
, static_cast<double>(pr_mem
));
214 #endif // defined(ENABLE_TASK_MANAGER)
218 ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext
* context
)
219 : browser_context_(context
), listeners_(0), task_manager_listening_(false) {
220 #if defined(ENABLE_TASK_MANAGER)
221 model_
= TaskManager::GetInstance()->model();
222 model_
->AddObserver(this);
224 registrar_
.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
,
225 content::NotificationService::AllSources());
226 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
227 content::NotificationService::AllSources());
228 #endif // defined(ENABLE_TASK_MANAGER)
231 ProcessesEventRouter::~ProcessesEventRouter() {
232 #if defined(ENABLE_TASK_MANAGER)
233 registrar_
.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
,
234 content::NotificationService::AllSources());
235 registrar_
.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
236 content::NotificationService::AllSources());
238 if (task_manager_listening_
)
239 model_
->StopListening();
241 model_
->RemoveObserver(this);
242 #endif // defined(ENABLE_TASK_MANAGER)
245 void ProcessesEventRouter::ListenerAdded() {
246 #if defined(ENABLE_TASK_MANAGER)
247 // The task manager has its own ref count to balance other callers of
248 // StartUpdating/StopUpdating.
249 model_
->StartUpdating();
250 #endif // defined(ENABLE_TASK_MANAGER)
254 void ProcessesEventRouter::ListenerRemoved() {
255 DCHECK_GT(listeners_
, 0);
257 #if defined(ENABLE_TASK_MANAGER)
258 // The task manager has its own ref count to balance other callers of
259 // StartUpdating/StopUpdating.
260 model_
->StopUpdating();
261 #endif // defined(ENABLE_TASK_MANAGER)
264 void ProcessesEventRouter::StartTaskManagerListening() {
265 #if defined(ENABLE_TASK_MANAGER)
266 if (!task_manager_listening_
) {
267 model_
->StartListening();
268 task_manager_listening_
= true;
270 #endif // defined(ENABLE_TASK_MANAGER)
273 void ProcessesEventRouter::Observe(
275 const content::NotificationSource
& source
,
276 const content::NotificationDetails
& details
) {
279 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
:
281 content::Source
<content::RenderWidgetHost
>(source
).ptr());
283 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
:
285 content::Source
<content::RenderProcessHost
>(source
).ptr(),
286 content::Details
<content::RenderProcessHost::RendererClosedDetails
>(
290 NOTREACHED() << "Unexpected observe of type " << type
;
295 void ProcessesEventRouter::OnItemsAdded(int start
, int length
) {
296 #if defined(ENABLE_TASK_MANAGER)
297 DCHECK_EQ(length
, 1);
300 std::string
event(keys::kOnCreated
);
301 if (!HasEventListeners(event
))
304 // If the item being added is not the first one in the group, find the base
305 // index and use it for retrieving the process data.
306 if (!model_
->IsResourceFirstInGroup(start
)) {
307 index
= model_
->GetGroupIndexForResource(start
);
310 scoped_ptr
<base::ListValue
> args(new base::ListValue());
311 base::DictionaryValue
* process
= CreateProcessFromModel(
312 model_
->GetUniqueChildProcessId(index
), model_
, index
, false);
313 DCHECK(process
!= NULL
);
318 args
->Append(process
);
320 DispatchEvent(keys::kOnCreated
, args
.Pass());
321 #endif // defined(ENABLE_TASK_MANAGER)
324 void ProcessesEventRouter::OnItemsChanged(int start
, int length
) {
325 #if defined(ENABLE_TASK_MANAGER)
326 // If we don't have any listeners, return immediately.
333 // We need to know which type of onUpdated events to fire and whether to
334 // collect memory or not.
335 std::string
updated_event(keys::kOnUpdated
);
336 std::string
updated_event_memory(keys::kOnUpdatedWithMemory
);
337 bool updated
= HasEventListeners(updated_event
);
338 bool updated_memory
= HasEventListeners(updated_event_memory
);
340 DCHECK(updated
|| updated_memory
);
342 IDMap
<base::DictionaryValue
> processes_map
;
343 for (int i
= start
; i
< start
+ length
; i
++) {
344 if (model_
->IsResourceFirstInGroup(i
)) {
345 int id
= model_
->GetUniqueChildProcessId(i
);
346 base::DictionaryValue
* process
= CreateProcessFromModel(id
, model_
, i
,
348 processes_map
.AddWithID(process
, i
);
353 std::string
idkey(keys::kIdKey
);
354 base::DictionaryValue
* processes
= new base::DictionaryValue();
357 IDMap
<base::DictionaryValue
>::iterator
it(&processes_map
);
358 for (; !it
.IsAtEnd(); it
.Advance()) {
359 if (!it
.GetCurrentValue()->GetInteger(idkey
, &id
))
362 // Store each process indexed by the string version of its id.
363 processes
->Set(base::IntToString(id
), it
.GetCurrentValue());
366 scoped_ptr
<base::ListValue
> args(new base::ListValue());
367 args
->Append(processes
);
368 DispatchEvent(keys::kOnUpdated
, args
.Pass());
371 if (updated_memory
) {
372 IDMap
<base::DictionaryValue
>::iterator
it(&processes_map
);
373 for (; !it
.IsAtEnd(); it
.Advance()) {
374 if (!it
.GetCurrentValue()->GetInteger(idkey
, &id
))
377 AddMemoryDetails(it
.GetCurrentValue(), model_
, it
.GetCurrentKey());
379 // Store each process indexed by the string version of its id if we didn't
380 // already insert it as part of the onUpdated processing above.
382 processes
->Set(base::IntToString(id
), it
.GetCurrentValue());
385 scoped_ptr
<base::ListValue
> args(new base::ListValue());
386 args
->Append(processes
);
387 DispatchEvent(keys::kOnUpdatedWithMemory
, args
.Pass());
389 #endif // defined(ENABLE_TASK_MANAGER)
392 void ProcessesEventRouter::OnItemsToBeRemoved(int start
, int length
) {
393 #if defined(ENABLE_TASK_MANAGER)
394 DCHECK_EQ(length
, 1);
396 // Process exit for renderer processes has the data about exit code and
397 // termination status, therefore we will rely on notifications and not on
398 // the Task Manager data. We do use the rest of this method for non-renderer
400 if (model_
->GetResourceType(start
) == task_manager::Resource::RENDERER
)
403 // The callback function parameters.
404 scoped_ptr
<base::ListValue
> args(new base::ListValue());
406 // First arg: The id of the process that was closed.
407 args
->Append(new base::FundamentalValue(
408 model_
->GetUniqueChildProcessId(start
)));
410 // Second arg: The exit type for the process.
411 args
->Append(new base::FundamentalValue(0));
413 // Third arg: The exit code for the process.
414 args
->Append(new base::FundamentalValue(0));
416 DispatchEvent(keys::kOnExited
, args
.Pass());
417 #endif // defined(ENABLE_TASK_MANAGER)
420 void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost
* widget
) {
421 #if defined(ENABLE_TASK_MANAGER)
422 std::string
event(keys::kOnUnresponsive
);
423 if (!HasEventListeners(event
))
426 base::DictionaryValue
* process
= NULL
;
427 int count
= model_
->ResourceCount();
428 int id
= widget
->GetProcess()->GetID();
430 for (int i
= 0; i
< count
; ++i
) {
431 if (model_
->IsResourceFirstInGroup(i
)) {
432 if (id
== model_
->GetUniqueChildProcessId(i
)) {
433 process
= CreateProcessFromModel(id
, model_
, i
, false);
442 scoped_ptr
<base::ListValue
> args(new base::ListValue());
443 args
->Append(process
);
445 DispatchEvent(keys::kOnUnresponsive
, args
.Pass());
446 #endif // defined(ENABLE_TASK_MANAGER)
449 void ProcessesEventRouter::ProcessClosedEvent(
450 content::RenderProcessHost
* rph
,
451 content::RenderProcessHost::RendererClosedDetails
* details
) {
452 #if defined(ENABLE_TASK_MANAGER)
453 // The callback function parameters.
454 scoped_ptr
<base::ListValue
> args(new base::ListValue());
456 // First arg: The id of the process that was closed.
457 args
->Append(new base::FundamentalValue(rph
->GetID()));
459 // Second arg: The exit type for the process.
460 args
->Append(new base::FundamentalValue(details
->status
));
462 // Third arg: The exit code for the process.
463 args
->Append(new base::FundamentalValue(details
->exit_code
));
465 DispatchEvent(keys::kOnExited
, args
.Pass());
466 #endif // defined(ENABLE_TASK_MANAGER)
469 void ProcessesEventRouter::DispatchEvent(
470 const std::string
& event_name
,
471 scoped_ptr
<base::ListValue
> event_args
) {
472 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
474 scoped_ptr
<extensions::Event
> event(new extensions::Event(
475 event_name
, event_args
.Pass()));
476 event_router
->BroadcastEvent(event
.Pass());
480 bool ProcessesEventRouter::HasEventListeners(const std::string
& event_name
) {
481 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
482 return event_router
&& event_router
->HasEventListener(event_name
);
485 ProcessesAPI::ProcessesAPI(content::BrowserContext
* context
)
486 : browser_context_(context
) {
487 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
488 event_router
->RegisterObserver(this, processes_api_constants::kOnUpdated
);
489 event_router
->RegisterObserver(this,
490 processes_api_constants::kOnUpdatedWithMemory
);
491 ExtensionFunctionRegistry
* registry
=
492 ExtensionFunctionRegistry::GetInstance();
493 registry
->RegisterFunction
<extensions::GetProcessIdForTabFunction
>();
494 registry
->RegisterFunction
<extensions::TerminateFunction
>();
495 registry
->RegisterFunction
<extensions::GetProcessInfoFunction
>();
498 ProcessesAPI::~ProcessesAPI() {
501 void ProcessesAPI::Shutdown() {
502 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
505 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<ProcessesAPI
> >
506 g_factory
= LAZY_INSTANCE_INITIALIZER
;
509 BrowserContextKeyedAPIFactory
<ProcessesAPI
>*
510 ProcessesAPI::GetFactoryInstance() {
511 return g_factory
.Pointer();
515 ProcessesAPI
* ProcessesAPI::Get(content::BrowserContext
* context
) {
516 return BrowserContextKeyedAPIFactory
<ProcessesAPI
>::Get(context
);
519 ProcessesEventRouter
* ProcessesAPI::processes_event_router() {
520 if (!processes_event_router_
)
521 processes_event_router_
.reset(new ProcessesEventRouter(browser_context_
));
522 return processes_event_router_
.get();
525 void ProcessesAPI::OnListenerAdded(const EventListenerInfo
& details
) {
526 // We lazily tell the TaskManager to start updating when listeners to the
527 // processes.onUpdated or processes.onUpdatedWithMemory events arrive.
528 processes_event_router()->ListenerAdded();
531 void ProcessesAPI::OnListenerRemoved(const EventListenerInfo
& details
) {
532 // If a processes.onUpdated or processes.onUpdatedWithMemory event listener
533 // is removed (or a process with one exits), then we let the extension API
534 // know that it has one fewer listener.
535 processes_event_router()->ListenerRemoved();
538 GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) {
541 bool GetProcessIdForTabFunction::RunImpl() {
542 #if defined(ENABLE_TASK_MANAGER)
543 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &tab_id_
));
545 // Add a reference, which is balanced in GetProcessIdForTab to keep the object
546 // around and allow for the callback to be invoked.
549 // If the task manager is already listening, just post a task to execute
550 // which will invoke the callback once we have returned from this function.
551 // Otherwise, wait for the notification that the task manager is done with
552 // the data gathering.
553 if (ProcessesAPI::Get(GetProfile())
554 ->processes_event_router()
555 ->is_task_manager_listening()) {
556 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(
557 &GetProcessIdForTabFunction::GetProcessIdForTab
, this));
559 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
560 base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab
, this));
562 ProcessesAPI::Get(GetProfile())
563 ->processes_event_router()
564 ->StartTaskManagerListening();
569 error_
= errors::kExtensionNotSupported
;
571 #endif // defined(ENABLE_TASK_MANAGER)
574 void GetProcessIdForTabFunction::GetProcessIdForTab() {
575 content::WebContents
* contents
= NULL
;
577 if (!ExtensionTabUtil::GetTabById(tab_id_
,
584 error_
= ErrorUtils::FormatErrorMessage(
585 extensions::tabs_constants::kTabNotFoundError
,
586 base::IntToString(tab_id_
));
587 SetResult(new base::FundamentalValue(-1));
590 int process_id
= contents
->GetRenderProcessHost()->GetID();
591 SetResult(new base::FundamentalValue(process_id
));
595 // Balance the AddRef in the RunImpl.
599 TerminateFunction::TerminateFunction() : process_id_(-1) {
602 bool TerminateFunction::RunImpl() {
603 #if defined(ENABLE_TASK_MANAGER)
604 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &process_id_
));
606 // Add a reference, which is balanced in TerminateProcess to keep the object
607 // around and allow for the callback to be invoked.
610 // If the task manager is already listening, just post a task to execute
611 // which will invoke the callback once we have returned from this function.
612 // Otherwise, wait for the notification that the task manager is done with
613 // the data gathering.
614 if (ProcessesAPI::Get(GetProfile())
615 ->processes_event_router()
616 ->is_task_manager_listening()) {
617 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(
618 &TerminateFunction::TerminateProcess
, this));
620 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
621 base::Bind(&TerminateFunction::TerminateProcess
, this));
623 ProcessesAPI::Get(GetProfile())
624 ->processes_event_router()
625 ->StartTaskManagerListening();
630 error_
= errors::kExtensionNotSupported
;
632 #endif // defined(ENABLE_TASK_MANAGER)
636 void TerminateFunction::TerminateProcess() {
637 #if defined(ENABLE_TASK_MANAGER)
638 TaskManagerModel
* model
= TaskManager::GetInstance()->model();
640 int count
= model
->ResourceCount();
644 for (int i
= 0; i
< count
; ++i
) {
645 if (model
->IsResourceFirstInGroup(i
)) {
646 if (process_id_
== model
->GetUniqueChildProcessId(i
)) {
648 killed
= base::KillProcess(model
->GetProcess(i
),
649 content::RESULT_CODE_KILLED
, true);
650 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
657 error_
= ErrorUtils::FormatErrorMessage(errors::kProcessNotFound
,
658 base::IntToString(process_id_
));
661 SetResult(new base::FundamentalValue(killed
));
665 // Balance the AddRef in the RunImpl.
668 error_
= errors::kExtensionNotSupported
;
670 #endif // defined(ENABLE_TASK_MANAGER)
673 GetProcessInfoFunction::GetProcessInfoFunction()
674 #if defined(ENABLE_TASK_MANAGER)
680 GetProcessInfoFunction::~GetProcessInfoFunction() {
683 bool GetProcessInfoFunction::RunImpl() {
684 #if defined(ENABLE_TASK_MANAGER)
685 base::Value
* processes
= NULL
;
687 EXTENSION_FUNCTION_VALIDATE(args_
->Get(0, &processes
));
688 EXTENSION_FUNCTION_VALIDATE(args_
->GetBoolean(1, &memory_
));
690 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
691 processes
, &process_ids_
));
693 // Add a reference, which is balanced in GatherProcessInfo to keep the object
694 // around and allow for the callback to be invoked.
697 // If the task manager is already listening, just post a task to execute
698 // which will invoke the callback once we have returned from this function.
699 // Otherwise, wait for the notification that the task manager is done with
700 // the data gathering.
701 if (ProcessesAPI::Get(GetProfile())
702 ->processes_event_router()
703 ->is_task_manager_listening()) {
704 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(
705 &GetProcessInfoFunction::GatherProcessInfo
, this));
707 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
708 base::Bind(&GetProcessInfoFunction::GatherProcessInfo
, this));
710 ProcessesAPI::Get(GetProfile())
711 ->processes_event_router()
712 ->StartTaskManagerListening();
717 error_
= errors::kExtensionNotSupported
;
719 #endif // defined(ENABLE_TASK_MANAGER)
722 void GetProcessInfoFunction::GatherProcessInfo() {
723 #if defined(ENABLE_TASK_MANAGER)
724 TaskManagerModel
* model
= TaskManager::GetInstance()->model();
725 base::DictionaryValue
* processes
= new base::DictionaryValue();
727 // If there are no process IDs specified, it means we need to return all of
728 // the ones we know of.
729 if (process_ids_
.size() == 0) {
730 int resources
= model
->ResourceCount();
731 for (int i
= 0; i
< resources
; ++i
) {
732 if (model
->IsResourceFirstInGroup(i
)) {
733 int id
= model
->GetUniqueChildProcessId(i
);
734 base::DictionaryValue
* d
= CreateProcessFromModel(id
, model
, i
, false);
736 AddMemoryDetails(d
, model
, i
);
737 processes
->Set(base::IntToString(id
), d
);
741 int resources
= model
->ResourceCount();
742 for (int i
= 0; i
< resources
; ++i
) {
743 if (model
->IsResourceFirstInGroup(i
)) {
744 int id
= model
->GetUniqueChildProcessId(i
);
745 std::vector
<int>::iterator proc_id
= std::find(process_ids_
.begin(),
746 process_ids_
.end(), id
);
747 if (proc_id
!= process_ids_
.end()) {
748 base::DictionaryValue
* d
=
749 CreateProcessFromModel(id
, model
, i
, false);
751 AddMemoryDetails(d
, model
, i
);
752 processes
->Set(base::IntToString(id
), d
);
754 process_ids_
.erase(proc_id
);
755 if (process_ids_
.size() == 0)
760 DCHECK_EQ(process_ids_
.size(), 0U);
763 SetResult(processes
);
766 // Balance the AddRef in the RunImpl.
768 #endif // defined(ENABLE_TASK_MANAGER)
771 } // namespace extensions