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/location.h"
11 #include "base/metrics/histogram.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/values.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/extensions/api/processes/processes_api_constants.h"
19 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_tab_util.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/task_manager/resource_provider.h"
24 #include "chrome/browser/task_manager/task_manager.h"
25 #include "content/public/browser/browser_context.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/notification_source.h"
29 #include "content/public/browser/notification_types.h"
30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/render_widget_host.h"
33 #include "content/public/browser/render_widget_host_iterator.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/common/result_codes.h"
36 #include "extensions/browser/event_router.h"
37 #include "extensions/browser/extension_function_registry.h"
38 #include "extensions/browser/extension_function_util.h"
39 #include "extensions/common/error_utils.h"
41 namespace extensions
{
43 namespace keys
= processes_api_constants
;
44 namespace errors
= processes_api_constants
;
48 #if defined(ENABLE_TASK_MANAGER)
50 base::DictionaryValue
* CreateCacheData(
51 const blink::WebCache::ResourceTypeStat
& stat
) {
53 base::DictionaryValue
* cache
= new base::DictionaryValue();
54 cache
->SetDouble(keys::kCacheSize
, static_cast<double>(stat
.size
));
55 cache
->SetDouble(keys::kCacheLiveSize
, static_cast<double>(stat
.liveSize
));
59 void SetProcessType(base::DictionaryValue
* result
,
60 TaskManagerModel
* model
,
62 // Determine process type.
63 std::string type
= keys::kProcessTypeOther
;
64 task_manager::Resource::Type resource_type
= model
->GetResourceType(index
);
65 switch (resource_type
) {
66 case task_manager::Resource::BROWSER
:
67 type
= keys::kProcessTypeBrowser
;
69 case task_manager::Resource::RENDERER
:
70 type
= keys::kProcessTypeRenderer
;
72 case task_manager::Resource::EXTENSION
:
73 type
= keys::kProcessTypeExtension
;
75 case task_manager::Resource::NOTIFICATION
:
76 type
= keys::kProcessTypeNotification
;
78 case task_manager::Resource::PLUGIN
:
79 type
= keys::kProcessTypePlugin
;
81 case task_manager::Resource::WORKER
:
82 type
= keys::kProcessTypeWorker
;
84 case task_manager::Resource::NACL
:
85 type
= keys::kProcessTypeNacl
;
87 case task_manager::Resource::UTILITY
:
88 type
= keys::kProcessTypeUtility
;
90 case task_manager::Resource::GPU
:
91 type
= keys::kProcessTypeGPU
;
93 case task_manager::Resource::ZYGOTE
:
94 case task_manager::Resource::SANDBOX_HELPER
:
95 case task_manager::Resource::UNKNOWN
:
96 type
= keys::kProcessTypeOther
;
99 NOTREACHED() << "Unknown resource type.";
101 result
->SetString(keys::kTypeKey
, type
);
104 base::ListValue
* GetTabsForProcess(int process_id
) {
105 base::ListValue
* tabs_list
= new base::ListValue();
107 // The tabs list only makes sense for render processes, so if we don't find
108 // one, just return the empty list.
109 content::RenderProcessHost
* rph
=
110 content::RenderProcessHost::FromID(process_id
);
115 // We need to loop through all the RVHs to ensure we collect the set of all
116 // tabs using this renderer process.
117 scoped_ptr
<content::RenderWidgetHostIterator
> widgets(
118 content::RenderWidgetHost::GetRenderWidgetHosts());
119 while (content::RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
120 if (widget
->GetProcess()->GetID() != process_id
)
122 if (!widget
->IsRenderView())
125 content::RenderViewHost
* host
= content::RenderViewHost::From(widget
);
126 content::WebContents
* contents
=
127 content::WebContents::FromRenderViewHost(host
);
129 tab_id
= ExtensionTabUtil::GetTabId(contents
);
131 tabs_list
->Append(new base::FundamentalValue(tab_id
));
138 // This function creates a Process object to be returned to the extensions
139 // using these APIs. For memory details, which are not added by this function,
140 // the callers need to use AddMemoryDetails.
141 base::DictionaryValue
* CreateProcessFromModel(int process_id
,
142 TaskManagerModel
* model
,
144 bool include_optional
) {
145 base::DictionaryValue
* result
= new base::DictionaryValue();
148 result
->SetInteger(keys::kIdKey
, process_id
);
149 result
->SetInteger(keys::kOsProcessIdKey
, model
->GetProcessId(index
));
150 SetProcessType(result
, model
, index
);
151 result
->SetString(keys::kTitleKey
, model
->GetResourceTitle(index
));
152 result
->SetString(keys::kProfileKey
,
153 model
->GetResourceProfileName(index
));
154 result
->SetInteger(keys::kNaClDebugPortKey
,
155 model
->GetNaClDebugStubPort(index
));
157 result
->Set(keys::kTabsListKey
, GetTabsForProcess(process_id
));
159 // If we don't need to include the optional properties, just return now.
160 if (!include_optional
)
163 result
->SetDouble(keys::kCpuKey
, model
->GetCPUUsage(index
));
165 if (model
->GetV8Memory(index
, &mem
))
166 result
->SetDouble(keys::kJsMemoryAllocatedKey
,
167 static_cast<double>(mem
));
169 if (model
->GetV8MemoryUsed(index
, &mem
))
170 result
->SetDouble(keys::kJsMemoryUsedKey
,
171 static_cast<double>(mem
));
173 if (model
->GetSqliteMemoryUsedBytes(index
, &mem
))
174 result
->SetDouble(keys::kSqliteMemoryKey
,
175 static_cast<double>(mem
));
177 blink::WebCache::ResourceTypeStats cache_stats
;
178 if (model
->GetWebCoreCacheStats(index
, &cache_stats
)) {
179 result
->Set(keys::kImageCacheKey
,
180 CreateCacheData(cache_stats
.images
));
181 result
->Set(keys::kScriptCacheKey
,
182 CreateCacheData(cache_stats
.scripts
));
183 result
->Set(keys::kCssCacheKey
,
184 CreateCacheData(cache_stats
.cssStyleSheets
));
187 // Network is reported by the TaskManager per resource (tab), not per
188 // process, therefore we need to iterate through the group of resources
189 // and aggregate the data.
191 int length
= model
->GetGroupRangeForResource(index
).second
;
192 for (int i
= 0; i
< length
; ++i
)
193 net
+= model
->GetNetworkUsage(index
+ i
);
194 result
->SetDouble(keys::kNetworkKey
, static_cast<double>(net
));
199 // Since memory details are expensive to gather, we don't do it by default.
200 // This function is a helper to add memory details data to an existing
201 // Process object representation.
202 void AddMemoryDetails(base::DictionaryValue
* result
,
203 TaskManagerModel
* model
,
206 int64 pr_mem
= model
->GetPrivateMemory(index
, &mem
) ?
207 static_cast<int64
>(mem
) : -1;
208 result
->SetDouble(keys::kPrivateMemoryKey
, static_cast<double>(pr_mem
));
211 #endif // defined(ENABLE_TASK_MANAGER)
215 ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext
* context
)
216 : browser_context_(context
), listeners_(0), task_manager_listening_(false) {
217 #if defined(ENABLE_TASK_MANAGER)
218 model_
= TaskManager::GetInstance()->model();
219 model_
->AddObserver(this);
221 registrar_
.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
,
222 content::NotificationService::AllSources());
223 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
224 content::NotificationService::AllSources());
225 #endif // defined(ENABLE_TASK_MANAGER)
228 ProcessesEventRouter::~ProcessesEventRouter() {
229 #if defined(ENABLE_TASK_MANAGER)
230 registrar_
.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
,
231 content::NotificationService::AllSources());
232 registrar_
.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
233 content::NotificationService::AllSources());
235 if (task_manager_listening_
)
236 model_
->StopListening();
238 model_
->RemoveObserver(this);
239 #endif // defined(ENABLE_TASK_MANAGER)
242 void ProcessesEventRouter::ListenerAdded() {
243 #if defined(ENABLE_TASK_MANAGER)
244 // The task manager has its own ref count to balance other callers of
245 // StartUpdating/StopUpdating.
246 model_
->StartUpdating();
247 #endif // defined(ENABLE_TASK_MANAGER)
251 void ProcessesEventRouter::ListenerRemoved() {
252 DCHECK_GT(listeners_
, 0);
254 #if defined(ENABLE_TASK_MANAGER)
255 // The task manager has its own ref count to balance other callers of
256 // StartUpdating/StopUpdating.
257 model_
->StopUpdating();
258 #endif // defined(ENABLE_TASK_MANAGER)
261 void ProcessesEventRouter::StartTaskManagerListening() {
262 #if defined(ENABLE_TASK_MANAGER)
263 if (!task_manager_listening_
) {
264 model_
->StartListening();
265 task_manager_listening_
= true;
267 #endif // defined(ENABLE_TASK_MANAGER)
270 void ProcessesEventRouter::Observe(
272 const content::NotificationSource
& source
,
273 const content::NotificationDetails
& details
) {
276 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG
:
278 content::Source
<content::RenderWidgetHost
>(source
).ptr());
280 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
:
282 content::Source
<content::RenderProcessHost
>(source
).ptr(),
283 content::Details
<content::RenderProcessHost::RendererClosedDetails
>(
287 NOTREACHED() << "Unexpected observe of type " << type
;
292 void ProcessesEventRouter::OnItemsAdded(int start
, int length
) {
293 #if defined(ENABLE_TASK_MANAGER)
294 DCHECK_EQ(length
, 1);
297 std::string
event(keys::kOnCreated
);
298 if (!HasEventListeners(event
))
301 // If the item being added is not the first one in the group, find the base
302 // index and use it for retrieving the process data.
303 if (!model_
->IsResourceFirstInGroup(start
)) {
304 index
= model_
->GetGroupIndexForResource(start
);
307 scoped_ptr
<base::ListValue
> args(new base::ListValue());
308 base::DictionaryValue
* process
= CreateProcessFromModel(
309 model_
->GetUniqueChildProcessId(index
), model_
, index
, false);
310 DCHECK(process
!= NULL
);
315 args
->Append(process
);
317 DispatchEvent(events::PROCESSES_ON_CREATED
, keys::kOnCreated
, args
.Pass());
318 #endif // defined(ENABLE_TASK_MANAGER)
321 void ProcessesEventRouter::OnItemsChanged(int start
, int length
) {
322 #if defined(ENABLE_TASK_MANAGER)
323 // If we don't have any listeners, return immediately.
330 // We need to know which type of onUpdated events to fire and whether to
331 // collect memory or not.
332 std::string
updated_event(keys::kOnUpdated
);
333 std::string
updated_event_memory(keys::kOnUpdatedWithMemory
);
334 bool updated
= HasEventListeners(updated_event
);
335 bool updated_memory
= HasEventListeners(updated_event_memory
);
337 DCHECK(updated
|| updated_memory
);
339 IDMap
<base::DictionaryValue
> processes_map
;
340 for (int i
= start
; i
< start
+ length
; i
++) {
341 if (model_
->IsResourceFirstInGroup(i
)) {
342 int id
= model_
->GetUniqueChildProcessId(i
);
343 base::DictionaryValue
* process
= CreateProcessFromModel(id
, model_
, i
,
345 processes_map
.AddWithID(process
, i
);
350 std::string
idkey(keys::kIdKey
);
351 base::DictionaryValue
* processes
= new base::DictionaryValue();
354 IDMap
<base::DictionaryValue
>::iterator
it(&processes_map
);
355 for (; !it
.IsAtEnd(); it
.Advance()) {
356 if (!it
.GetCurrentValue()->GetInteger(idkey
, &id
))
359 // Store each process indexed by the string version of its id.
360 processes
->Set(base::IntToString(id
), it
.GetCurrentValue());
363 scoped_ptr
<base::ListValue
> args(new base::ListValue());
364 args
->Append(processes
);
365 DispatchEvent(events::PROCESSES_ON_UPDATED
, keys::kOnUpdated
, args
.Pass());
368 if (updated_memory
) {
369 IDMap
<base::DictionaryValue
>::iterator
it(&processes_map
);
370 for (; !it
.IsAtEnd(); it
.Advance()) {
371 if (!it
.GetCurrentValue()->GetInteger(idkey
, &id
))
374 AddMemoryDetails(it
.GetCurrentValue(), model_
, it
.GetCurrentKey());
376 // Store each process indexed by the string version of its id if we didn't
377 // already insert it as part of the onUpdated processing above.
379 processes
->Set(base::IntToString(id
), it
.GetCurrentValue());
382 scoped_ptr
<base::ListValue
> args(new base::ListValue());
383 args
->Append(processes
);
384 DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY
,
385 keys::kOnUpdatedWithMemory
, args
.Pass());
387 #endif // defined(ENABLE_TASK_MANAGER)
390 void ProcessesEventRouter::OnItemsToBeRemoved(int start
, int length
) {
391 #if defined(ENABLE_TASK_MANAGER)
392 DCHECK_EQ(length
, 1);
394 // Process exit for renderer processes has the data about exit code and
395 // termination status, therefore we will rely on notifications and not on
396 // the Task Manager data. We do use the rest of this method for non-renderer
398 if (model_
->GetResourceType(start
) == task_manager::Resource::RENDERER
)
401 // The callback function parameters.
402 scoped_ptr
<base::ListValue
> args(new base::ListValue());
404 // First arg: The id of the process that was closed.
405 args
->Append(new base::FundamentalValue(
406 model_
->GetUniqueChildProcessId(start
)));
408 // Second arg: The exit type for the process.
409 args
->Append(new base::FundamentalValue(0));
411 // Third arg: The exit code for the process.
412 args
->Append(new base::FundamentalValue(0));
414 DispatchEvent(events::PROCESSES_ON_EXITED
, keys::kOnExited
, args
.Pass());
415 #endif // defined(ENABLE_TASK_MANAGER)
418 void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost
* widget
) {
419 #if defined(ENABLE_TASK_MANAGER)
420 std::string
event(keys::kOnUnresponsive
);
421 if (!HasEventListeners(event
))
424 base::DictionaryValue
* process
= NULL
;
425 int count
= model_
->ResourceCount();
426 int id
= widget
->GetProcess()->GetID();
428 for (int i
= 0; i
< count
; ++i
) {
429 if (model_
->IsResourceFirstInGroup(i
)) {
430 if (id
== model_
->GetUniqueChildProcessId(i
)) {
431 process
= CreateProcessFromModel(id
, model_
, i
, false);
440 scoped_ptr
<base::ListValue
> args(new base::ListValue());
441 args
->Append(process
);
443 DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE
, keys::kOnUnresponsive
,
445 #endif // defined(ENABLE_TASK_MANAGER)
448 void ProcessesEventRouter::ProcessClosedEvent(
449 content::RenderProcessHost
* rph
,
450 content::RenderProcessHost::RendererClosedDetails
* details
) {
451 #if defined(ENABLE_TASK_MANAGER)
452 // The callback function parameters.
453 scoped_ptr
<base::ListValue
> args(new base::ListValue());
455 // First arg: The id of the process that was closed.
456 args
->Append(new base::FundamentalValue(rph
->GetID()));
458 // Second arg: The exit type for the process.
459 args
->Append(new base::FundamentalValue(details
->status
));
461 // Third arg: The exit code for the process.
462 args
->Append(new base::FundamentalValue(details
->exit_code
));
464 DispatchEvent(events::PROCESSES_ON_EXITED
, keys::kOnExited
, args
.Pass());
465 #endif // defined(ENABLE_TASK_MANAGER)
468 void ProcessesEventRouter::DispatchEvent(
469 events::HistogramValue histogram_value
,
470 const std::string
& event_name
,
471 scoped_ptr
<base::ListValue
> event_args
) {
472 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
474 scoped_ptr
<Event
> event(
475 new Event(histogram_value
, 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
<GetProcessIdForTabFunction
>();
494 registry
->RegisterFunction
<TerminateFunction
>();
495 registry
->RegisterFunction
<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::RunAsync() {
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::ThreadTaskRunnerHandle::Get()->PostTask(
558 base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab
, this));
560 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
561 base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab
, this));
563 ProcessesAPI::Get(GetProfile())
564 ->processes_event_router()
565 ->StartTaskManagerListening();
570 error_
= errors::kExtensionNotSupported
;
572 #endif // defined(ENABLE_TASK_MANAGER)
575 void GetProcessIdForTabFunction::GetProcessIdForTab() {
576 content::WebContents
* contents
= NULL
;
578 if (!ExtensionTabUtil::GetTabById(tab_id_
,
585 error_
= ErrorUtils::FormatErrorMessage(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 RunAsync.
599 TerminateFunction::TerminateFunction() : process_id_(-1) {
602 bool TerminateFunction::RunAsync() {
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::ThreadTaskRunnerHandle::Get()->PostTask(
618 FROM_HERE
, base::Bind(&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 base::Process process
=
649 base::Process::DeprecatedGetProcessFromHandle(model
->GetProcess(i
));
650 killed
= process
.Terminate(content::RESULT_CODE_KILLED
, true);
651 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
658 error_
= ErrorUtils::FormatErrorMessage(errors::kProcessNotFound
,
659 base::IntToString(process_id_
));
662 SetResult(new base::FundamentalValue(killed
));
666 // Balance the AddRef in the RunAsync.
669 error_
= errors::kExtensionNotSupported
;
671 #endif // defined(ENABLE_TASK_MANAGER)
674 GetProcessInfoFunction::GetProcessInfoFunction()
675 #if defined(ENABLE_TASK_MANAGER)
681 GetProcessInfoFunction::~GetProcessInfoFunction() {
684 bool GetProcessInfoFunction::RunAsync() {
685 #if defined(ENABLE_TASK_MANAGER)
686 base::Value
* processes
= NULL
;
688 EXTENSION_FUNCTION_VALIDATE(args_
->Get(0, &processes
));
689 EXTENSION_FUNCTION_VALIDATE(args_
->GetBoolean(1, &memory_
));
690 EXTENSION_FUNCTION_VALIDATE(ReadOneOrMoreIntegers(processes
, &process_ids_
));
692 // Add a reference, which is balanced in GatherProcessInfo to keep the object
693 // around and allow for the callback to be invoked.
696 // If the task manager is already listening, just post a task to execute
697 // which will invoke the callback once we have returned from this function.
698 // Otherwise, wait for the notification that the task manager is done with
699 // the data gathering.
700 if (ProcessesAPI::Get(GetProfile())
701 ->processes_event_router()
702 ->is_task_manager_listening()) {
703 base::ThreadTaskRunnerHandle::Get()->PostTask(
705 base::Bind(&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 RunAsync.
768 #endif // defined(ENABLE_TASK_MANAGER)
771 } // namespace extensions