Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / processes / processes_api.cc
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.
4
5 #include "chrome/browser/extensions/api/processes/processes_api.h"
6
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_function_registry.h"
19 #include "chrome/browser/extensions/extension_function_util.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/notification_details.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/render_widget_host.h"
32 #include "content/public/browser/render_widget_host_iterator.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/common/result_codes.h"
35 #include "extensions/browser/event_router.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/common/error_utils.h"
38
39 namespace extensions {
40
41 namespace keys = processes_api_constants;
42 namespace errors = processes_api_constants;
43
44 namespace {
45
46 #if defined(ENABLE_TASK_MANAGER)
47
48 base::DictionaryValue* CreateCacheData(
49     const blink::WebCache::ResourceTypeStat& stat) {
50
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;
55 }
56
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.";
98   }
99   result->SetString(keys::kTypeKey, type);
100 }
101
102 base::ListValue* GetTabsForProcess(int process_id) {
103   base::ListValue* tabs_list = new base::ListValue();
104
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;
111
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;
122
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));
130     }
131   }
132
133   return tabs_list;
134 }
135
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;
145
146   result->SetInteger(keys::kIdKey, process_id);
147   result->SetInteger(keys::kOsProcessIdKey, model->GetProcessId(index));
148   SetProcessType(result, model, index);
149   result->SetString(keys::kProfileKey,
150       model->GetResourceProfileName(index));
151
152   result->Set(keys::kTabsListKey, GetTabsForProcess(process_id));
153
154   // If we don't need to include the optional properties, just return now.
155   if (!include_optional)
156     return result;
157
158   result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index));
159
160   if (model->GetV8Memory(index, &mem))
161     result->SetDouble(keys::kJsMemoryAllocatedKey,
162         static_cast<double>(mem));
163
164   if (model->GetV8MemoryUsed(index, &mem))
165     result->SetDouble(keys::kJsMemoryUsedKey,
166         static_cast<double>(mem));
167
168   if (model->GetSqliteMemoryUsedBytes(index, &mem))
169     result->SetDouble(keys::kSqliteMemoryKey,
170         static_cast<double>(mem));
171
172   blink::WebCache::ResourceTypeStats cache_stats;
173   if (model->GetWebCoreCacheStats(index, &cache_stats)) {
174     result->Set(keys::kImageCacheKey,
175                 CreateCacheData(cache_stats.images));
176     result->Set(keys::kScriptCacheKey,
177                 CreateCacheData(cache_stats.scripts));
178     result->Set(keys::kCssCacheKey,
179                 CreateCacheData(cache_stats.cssStyleSheets));
180   }
181
182   // Network and FPS are reported by the TaskManager per resource (tab), not
183   // per process, therefore we need to iterate through the group of resources
184   // and aggregate the data.
185   float fps = 0, tmp = 0;
186   int64 net = 0;
187   int length = model->GetGroupRangeForResource(index).second;
188   for (int i = 0; i < length; ++i) {
189     net += model->GetNetworkUsage(index + i);
190     if (model->GetFPS(index + i, &tmp))
191       fps += tmp;
192   }
193   result->SetDouble(keys::kFPSKey, static_cast<double>(fps));
194   result->SetDouble(keys::kNetworkKey, static_cast<double>(net));
195
196   return result;
197 }
198
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,
204                       int index) {
205   size_t mem;
206   int64 pr_mem = model->GetPrivateMemory(index, &mem) ?
207       static_cast<int64>(mem) : -1;
208   result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem));
209 }
210
211 #endif  // defined(ENABLE_TASK_MANAGER)
212
213 }  // namespace
214
215 ProcessesEventRouter::ProcessesEventRouter(Profile* profile)
216     : profile_(profile),
217       listeners_(0),
218       task_manager_listening_(false) {
219 #if defined(ENABLE_TASK_MANAGER)
220   model_ = TaskManager::GetInstance()->model();
221   model_->AddObserver(this);
222
223   registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
224       content::NotificationService::AllSources());
225   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
226       content::NotificationService::AllSources());
227 #endif  // defined(ENABLE_TASK_MANAGER)
228 }
229
230 ProcessesEventRouter::~ProcessesEventRouter() {
231 #if defined(ENABLE_TASK_MANAGER)
232   registrar_.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
233       content::NotificationService::AllSources());
234   registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
235       content::NotificationService::AllSources());
236
237   if (task_manager_listening_)
238     model_->StopListening();
239
240   model_->RemoveObserver(this);
241 #endif  // defined(ENABLE_TASK_MANAGER)
242 }
243
244 void ProcessesEventRouter::ListenerAdded() {
245 #if defined(ENABLE_TASK_MANAGER)
246   // The task manager has its own ref count to balance other callers of
247   // StartUpdating/StopUpdating.
248   model_->StartUpdating();
249 #endif  // defined(ENABLE_TASK_MANAGER)
250   ++listeners_;
251 }
252
253 void ProcessesEventRouter::ListenerRemoved() {
254   DCHECK_GT(listeners_, 0);
255   --listeners_;
256 #if defined(ENABLE_TASK_MANAGER)
257   // The task manager has its own ref count to balance other callers of
258   // StartUpdating/StopUpdating.
259   model_->StopUpdating();
260 #endif  // defined(ENABLE_TASK_MANAGER)
261 }
262
263 void ProcessesEventRouter::StartTaskManagerListening() {
264 #if defined(ENABLE_TASK_MANAGER)
265   if (!task_manager_listening_) {
266     model_->StartListening();
267     task_manager_listening_ = true;
268   }
269 #endif  // defined(ENABLE_TASK_MANAGER)
270 }
271
272 void ProcessesEventRouter::Observe(
273     int type,
274     const content::NotificationSource& source,
275     const content::NotificationDetails& details) {
276
277   switch (type) {
278     case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
279       ProcessHangEvent(
280           content::Source<content::RenderWidgetHost>(source).ptr());
281       break;
282     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
283       ProcessClosedEvent(
284           content::Source<content::RenderProcessHost>(source).ptr(),
285           content::Details<content::RenderProcessHost::RendererClosedDetails>(
286               details).ptr());
287       break;
288     default:
289       NOTREACHED() << "Unexpected observe of type " << type;
290   }
291   return;
292 }
293
294 void ProcessesEventRouter::OnItemsAdded(int start, int length) {
295 #if defined(ENABLE_TASK_MANAGER)
296   DCHECK_EQ(length, 1);
297   int index = start;
298
299   std::string event(keys::kOnCreated);
300   if (!HasEventListeners(event))
301     return;
302
303   // If the item being added is not the first one in the group, find the base
304   // index and use it for retrieving the process data.
305   if (!model_->IsResourceFirstInGroup(start)) {
306     index = model_->GetGroupIndexForResource(start);
307   }
308
309   scoped_ptr<base::ListValue> args(new base::ListValue());
310   base::DictionaryValue* process = CreateProcessFromModel(
311       model_->GetUniqueChildProcessId(index), model_, index, false);
312   DCHECK(process != NULL);
313
314   if (process == NULL)
315     return;
316
317   args->Append(process);
318
319   DispatchEvent(keys::kOnCreated, args.Pass());
320 #endif  // defined(ENABLE_TASK_MANAGER)
321 }
322
323 void ProcessesEventRouter::OnItemsChanged(int start, int length) {
324 #if defined(ENABLE_TASK_MANAGER)
325   // If we don't have any listeners, return immediately.
326   if (listeners_ == 0)
327     return;
328
329   if (!model_)
330     return;
331
332   // We need to know which type of onUpdated events to fire and whether to
333   // collect memory or not.
334   std::string updated_event(keys::kOnUpdated);
335   std::string updated_event_memory(keys::kOnUpdatedWithMemory);
336   bool updated = HasEventListeners(updated_event);
337   bool updated_memory = HasEventListeners(updated_event_memory);
338
339   DCHECK(updated || updated_memory);
340
341   IDMap<base::DictionaryValue> processes_map;
342   for (int i = start; i < start + length; i++) {
343     if (model_->IsResourceFirstInGroup(i)) {
344       int id = model_->GetUniqueChildProcessId(i);
345       base::DictionaryValue* process = CreateProcessFromModel(id, model_, i,
346                                                               true);
347       processes_map.AddWithID(process, i);
348     }
349   }
350
351   int id;
352   std::string idkey(keys::kIdKey);
353   base::DictionaryValue* processes = new base::DictionaryValue();
354
355   if (updated) {
356     IDMap<base::DictionaryValue>::iterator it(&processes_map);
357     for (; !it.IsAtEnd(); it.Advance()) {
358       if (!it.GetCurrentValue()->GetInteger(idkey, &id))
359         continue;
360
361       // Store each process indexed by the string version of its id.
362       processes->Set(base::IntToString(id), it.GetCurrentValue());
363     }
364
365     scoped_ptr<base::ListValue> args(new base::ListValue());
366     args->Append(processes);
367     DispatchEvent(keys::kOnUpdated, args.Pass());
368   }
369
370   if (updated_memory) {
371     IDMap<base::DictionaryValue>::iterator it(&processes_map);
372     for (; !it.IsAtEnd(); it.Advance()) {
373       if (!it.GetCurrentValue()->GetInteger(idkey, &id))
374         continue;
375
376       AddMemoryDetails(it.GetCurrentValue(), model_, it.GetCurrentKey());
377
378       // Store each process indexed by the string version of its id if we didn't
379       // already insert it as part of the onUpdated processing above.
380       if (!updated)
381         processes->Set(base::IntToString(id), it.GetCurrentValue());
382     }
383
384     scoped_ptr<base::ListValue> args(new base::ListValue());
385     args->Append(processes);
386     DispatchEvent(keys::kOnUpdatedWithMemory, args.Pass());
387   }
388 #endif  // defined(ENABLE_TASK_MANAGER)
389 }
390
391 void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
392 #if defined(ENABLE_TASK_MANAGER)
393   DCHECK_EQ(length, 1);
394
395   // Process exit for renderer processes has the data about exit code and
396   // termination status, therefore we will rely on notifications and not on
397   // the Task Manager data. We do use the rest of this method for non-renderer
398   // processes.
399   if (model_->GetResourceType(start) == task_manager::Resource::RENDERER)
400     return;
401
402   // The callback function parameters.
403   scoped_ptr<base::ListValue> args(new base::ListValue());
404
405   // First arg: The id of the process that was closed.
406   args->Append(new base::FundamentalValue(
407       model_->GetUniqueChildProcessId(start)));
408
409   // Second arg: The exit type for the process.
410   args->Append(new base::FundamentalValue(0));
411
412   // Third arg: The exit code for the process.
413   args->Append(new base::FundamentalValue(0));
414
415   DispatchEvent(keys::kOnExited, args.Pass());
416 #endif  // defined(ENABLE_TASK_MANAGER)
417 }
418
419 void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) {
420 #if defined(ENABLE_TASK_MANAGER)
421   std::string event(keys::kOnUnresponsive);
422   if (!HasEventListeners(event))
423     return;
424
425   base::DictionaryValue* process = NULL;
426   int count = model_->ResourceCount();
427   int id = widget->GetProcess()->GetID();
428
429   for (int i = 0; i < count; ++i) {
430     if (model_->IsResourceFirstInGroup(i)) {
431       if (id == model_->GetUniqueChildProcessId(i)) {
432         process = CreateProcessFromModel(id, model_, i, false);
433         break;
434       }
435     }
436   }
437
438   DCHECK(process);
439   if (process == NULL)
440     return;
441
442   scoped_ptr<base::ListValue> args(new base::ListValue());
443   args->Append(process);
444
445   DispatchEvent(keys::kOnUnresponsive, args.Pass());
446 #endif  // defined(ENABLE_TASK_MANAGER)
447 }
448
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());
455
456   // First arg: The id of the process that was closed.
457   args->Append(new base::FundamentalValue(rph->GetID()));
458
459   // Second arg: The exit type for the process.
460   args->Append(new base::FundamentalValue(details->status));
461
462   // Third arg: The exit code for the process.
463   args->Append(new base::FundamentalValue(details->exit_code));
464
465   DispatchEvent(keys::kOnExited, args.Pass());
466 #endif  // defined(ENABLE_TASK_MANAGER)
467 }
468
469 void ProcessesEventRouter::DispatchEvent(
470     const std::string& event_name,
471     scoped_ptr<base::ListValue> event_args) {
472   if (extensions::ExtensionSystem::Get(profile_)->event_router()) {
473     scoped_ptr<extensions::Event> event(new extensions::Event(
474         event_name, event_args.Pass()));
475     extensions::ExtensionSystem::Get(profile_)->event_router()->
476         BroadcastEvent(event.Pass());
477   }
478 }
479
480 bool ProcessesEventRouter::HasEventListeners(const std::string& event_name) {
481   extensions::EventRouter* router =
482       extensions::ExtensionSystem::Get(profile_)->event_router();
483   if (router && router->HasEventListener(event_name))
484     return true;
485   return false;
486 }
487
488 ProcessesAPI::ProcessesAPI(Profile* profile) : profile_(profile) {
489   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
490       this, processes_api_constants::kOnUpdated);
491   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
492       this, processes_api_constants::kOnUpdatedWithMemory);
493   ExtensionFunctionRegistry* registry =
494       ExtensionFunctionRegistry::GetInstance();
495   registry->RegisterFunction<extensions::GetProcessIdForTabFunction>();
496   registry->RegisterFunction<extensions::TerminateFunction>();
497   registry->RegisterFunction<extensions::GetProcessInfoFunction>();
498 }
499
500 ProcessesAPI::~ProcessesAPI() {
501 }
502
503 void ProcessesAPI::Shutdown() {
504   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
505 }
506
507 static base::LazyInstance<ProfileKeyedAPIFactory<ProcessesAPI> >
508 g_factory = LAZY_INSTANCE_INITIALIZER;
509
510 // static
511 ProfileKeyedAPIFactory<ProcessesAPI>* ProcessesAPI::GetFactoryInstance() {
512   return g_factory.Pointer();
513 }
514
515 // static
516 ProcessesAPI* ProcessesAPI::Get(Profile* profile) {
517   return ProfileKeyedAPIFactory<ProcessesAPI>::GetForProfile(profile);
518 }
519
520 ProcessesEventRouter* ProcessesAPI::processes_event_router() {
521   if (!processes_event_router_)
522     processes_event_router_.reset(new ProcessesEventRouter(profile_));
523   return processes_event_router_.get();
524 }
525
526 void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
527   // We lazily tell the TaskManager to start updating when listeners to the
528   // processes.onUpdated or processes.onUpdatedWithMemory events arrive.
529   processes_event_router()->ListenerAdded();
530 }
531
532 void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
533   // If a processes.onUpdated or processes.onUpdatedWithMemory event listener
534   // is removed (or a process with one exits), then we let the extension API
535   // know that it has one fewer listener.
536   processes_event_router()->ListenerRemoved();
537 }
538
539 GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) {
540 }
541
542 bool GetProcessIdForTabFunction::RunImpl() {
543 #if defined(ENABLE_TASK_MANAGER)
544   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_));
545
546   // Add a reference, which is balanced in GetProcessIdForTab to keep the object
547   // around and allow for the callback to be invoked.
548   AddRef();
549
550   // If the task manager is already listening, just post a task to execute
551   // which will invoke the callback once we have returned from this function.
552   // Otherwise, wait for the notification that the task manager is done with
553   // the data gathering.
554   if (ProcessesAPI::Get(GetProfile())
555           ->processes_event_router()
556           ->is_task_manager_listening()) {
557     base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
558         &GetProcessIdForTabFunction::GetProcessIdForTab, this));
559   } else {
560     TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
561         base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab, this));
562
563     ProcessesAPI::Get(GetProfile())
564         ->processes_event_router()
565         ->StartTaskManagerListening();
566   }
567
568   return true;
569 #else
570   error_ = errors::kExtensionNotSupported;
571   return false;
572 #endif  // defined(ENABLE_TASK_MANAGER)
573 }
574
575 void GetProcessIdForTabFunction::GetProcessIdForTab() {
576   content::WebContents* contents = NULL;
577   int tab_index = -1;
578   if (!ExtensionTabUtil::GetTabById(tab_id_,
579                                     GetProfile(),
580                                     include_incognito(),
581                                     NULL,
582                                     NULL,
583                                     &contents,
584                                     &tab_index)) {
585     error_ = ErrorUtils::FormatErrorMessage(
586         extensions::tabs_constants::kTabNotFoundError,
587         base::IntToString(tab_id_));
588     SetResult(new base::FundamentalValue(-1));
589     SendResponse(false);
590   } else {
591     int process_id = contents->GetRenderProcessHost()->GetID();
592     SetResult(new base::FundamentalValue(process_id));
593     SendResponse(true);
594   }
595
596   // Balance the AddRef in the RunImpl.
597   Release();
598 }
599
600 TerminateFunction::TerminateFunction() : process_id_(-1) {
601 }
602
603 bool TerminateFunction::RunImpl() {
604 #if defined(ENABLE_TASK_MANAGER)
605   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_));
606
607   // Add a reference, which is balanced in TerminateProcess to keep the object
608   // around and allow for the callback to be invoked.
609   AddRef();
610
611   // If the task manager is already listening, just post a task to execute
612   // which will invoke the callback once we have returned from this function.
613   // Otherwise, wait for the notification that the task manager is done with
614   // the data gathering.
615   if (ProcessesAPI::Get(GetProfile())
616           ->processes_event_router()
617           ->is_task_manager_listening()) {
618     base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
619         &TerminateFunction::TerminateProcess, this));
620   } else {
621     TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
622         base::Bind(&TerminateFunction::TerminateProcess, this));
623
624     ProcessesAPI::Get(GetProfile())
625         ->processes_event_router()
626         ->StartTaskManagerListening();
627   }
628
629   return true;
630 #else
631   error_ = errors::kExtensionNotSupported;
632   return false;
633 #endif  // defined(ENABLE_TASK_MANAGER)
634 }
635
636
637 void TerminateFunction::TerminateProcess() {
638   TaskManagerModel* model = TaskManager::GetInstance()->model();
639
640   int count = model->ResourceCount();
641   bool killed = false;
642   bool found = false;
643
644   for (int i = 0; i < count; ++i) {
645     if (model->IsResourceFirstInGroup(i)) {
646       if (process_id_ == model->GetUniqueChildProcessId(i)) {
647         found = true;
648         killed = base::KillProcess(model->GetProcess(i),
649             content::RESULT_CODE_KILLED, true);
650         UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
651         break;
652       }
653     }
654   }
655
656   if (!found) {
657     error_ = ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
658         base::IntToString(process_id_));
659     SendResponse(false);
660   } else {
661     SetResult(new base::FundamentalValue(killed));
662     SendResponse(true);
663   }
664
665   // Balance the AddRef in the RunImpl.
666   Release();
667 }
668
669 GetProcessInfoFunction::GetProcessInfoFunction()
670 #if defined(ENABLE_TASK_MANAGER)
671   : memory_(false)
672 #endif
673     {
674 }
675
676 GetProcessInfoFunction::~GetProcessInfoFunction() {
677 }
678
679 bool GetProcessInfoFunction::RunImpl() {
680 #if defined(ENABLE_TASK_MANAGER)
681   base::Value* processes = NULL;
682
683   EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &processes));
684   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory_));
685
686   EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
687       processes, &process_ids_));
688
689   // Add a reference, which is balanced in GatherProcessInfo to keep the object
690   // around and allow for the callback to be invoked.
691   AddRef();
692
693   // If the task manager is already listening, just post a task to execute
694   // which will invoke the callback once we have returned from this function.
695   // Otherwise, wait for the notification that the task manager is done with
696   // the data gathering.
697   if (ProcessesAPI::Get(GetProfile())
698           ->processes_event_router()
699           ->is_task_manager_listening()) {
700     base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
701         &GetProcessInfoFunction::GatherProcessInfo, this));
702   } else {
703     TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
704         base::Bind(&GetProcessInfoFunction::GatherProcessInfo, this));
705
706     ProcessesAPI::Get(GetProfile())
707         ->processes_event_router()
708         ->StartTaskManagerListening();
709   }
710   return true;
711
712 #else
713   error_ = errors::kExtensionNotSupported;
714   return false;
715 #endif  // defined(ENABLE_TASK_MANAGER)
716 }
717
718 void GetProcessInfoFunction::GatherProcessInfo() {
719 #if defined(ENABLE_TASK_MANAGER)
720   TaskManagerModel* model = TaskManager::GetInstance()->model();
721   base::DictionaryValue* processes = new base::DictionaryValue();
722
723   // If there are no process IDs specified, it means we need to return all of
724   // the ones we know of.
725   if (process_ids_.size() == 0) {
726     int resources = model->ResourceCount();
727     for (int i = 0; i < resources; ++i) {
728       if (model->IsResourceFirstInGroup(i)) {
729         int id = model->GetUniqueChildProcessId(i);
730         base::DictionaryValue* d = CreateProcessFromModel(id, model, i, false);
731         if (memory_)
732           AddMemoryDetails(d, model, i);
733         processes->Set(base::IntToString(id), d);
734       }
735     }
736   } else {
737     int resources = model->ResourceCount();
738     for (int i = 0; i < resources; ++i) {
739       if (model->IsResourceFirstInGroup(i)) {
740         int id = model->GetUniqueChildProcessId(i);
741         std::vector<int>::iterator proc_id = std::find(process_ids_.begin(),
742                                                        process_ids_.end(), id);
743         if (proc_id != process_ids_.end()) {
744           base::DictionaryValue* d =
745               CreateProcessFromModel(id, model, i, false);
746           if (memory_)
747             AddMemoryDetails(d, model, i);
748           processes->Set(base::IntToString(id), d);
749
750           process_ids_.erase(proc_id);
751           if (process_ids_.size() == 0)
752             break;
753         }
754       }
755     }
756     DCHECK_EQ(process_ids_.size(), 0U);
757   }
758
759   SetResult(processes);
760   SendResponse(true);
761
762   // Balance the AddRef in the RunImpl.
763   Release();
764 #endif  // defined(ENABLE_TASK_MANAGER)
765 }
766
767 }  // namespace extensions