• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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"
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 
CreateCacheData(const blink::WebCache::ResourceTypeStat & stat)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 
SetProcessType(base::DictionaryValue * result,TaskManagerModel * model,int index)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 
GetTabsForProcess(int process_id)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.
CreateProcessFromModel(int process_id,TaskManagerModel * model,int index,bool include_optional)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::kTitleKey, model->GetResourceTitle(index));
150   result->SetString(keys::kProfileKey,
151       model->GetResourceProfileName(index));
152   result->SetInteger(keys::kNaClDebugPortKey,
153                      model->GetNaClDebugStubPort(index));
154 
155   result->Set(keys::kTabsListKey, GetTabsForProcess(process_id));
156 
157   // If we don't need to include the optional properties, just return now.
158   if (!include_optional)
159     return result;
160 
161   result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index));
162 
163   if (model->GetV8Memory(index, &mem))
164     result->SetDouble(keys::kJsMemoryAllocatedKey,
165         static_cast<double>(mem));
166 
167   if (model->GetV8MemoryUsed(index, &mem))
168     result->SetDouble(keys::kJsMemoryUsedKey,
169         static_cast<double>(mem));
170 
171   if (model->GetSqliteMemoryUsedBytes(index, &mem))
172     result->SetDouble(keys::kSqliteMemoryKey,
173         static_cast<double>(mem));
174 
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));
183   }
184 
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));
193 
194   return result;
195 }
196 
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.
AddMemoryDetails(base::DictionaryValue * result,TaskManagerModel * model,int index)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));
207 }
208 
209 #endif  // defined(ENABLE_TASK_MANAGER)
210 
211 }  // namespace
212 
ProcessesEventRouter(content::BrowserContext * context)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);
218 
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)
224 }
225 
~ProcessesEventRouter()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());
232 
233   if (task_manager_listening_)
234     model_->StopListening();
235 
236   model_->RemoveObserver(this);
237 #endif  // defined(ENABLE_TASK_MANAGER)
238 }
239 
ListenerAdded()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_;
247 }
248 
ListenerRemoved()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)
257 }
258 
StartTaskManagerListening()259 void ProcessesEventRouter::StartTaskManagerListening() {
260 #if defined(ENABLE_TASK_MANAGER)
261   if (!task_manager_listening_) {
262     model_->StartListening();
263     task_manager_listening_ = true;
264   }
265 #endif  // defined(ENABLE_TASK_MANAGER)
266 }
267 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)268 void ProcessesEventRouter::Observe(
269     int type,
270     const content::NotificationSource& source,
271     const content::NotificationDetails& details) {
272 
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;
286   }
287   return;
288 }
289 
OnItemsAdded(int start,int length)290 void ProcessesEventRouter::OnItemsAdded(int start, int length) {
291 #if defined(ENABLE_TASK_MANAGER)
292   DCHECK_EQ(length, 1);
293   int index = start;
294 
295   std::string event(keys::kOnCreated);
296   if (!HasEventListeners(event))
297     return;
298 
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);
303   }
304 
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);
309 
310   if (process == NULL)
311     return;
312 
313   args->Append(process);
314 
315   DispatchEvent(keys::kOnCreated, args.Pass());
316 #endif  // defined(ENABLE_TASK_MANAGER)
317 }
318 
OnItemsChanged(int start,int length)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;
324 
325   if (!model_)
326     return;
327 
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);
334 
335   DCHECK(updated || updated_memory);
336 
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);
344     }
345   }
346 
347   int id;
348   std::string idkey(keys::kIdKey);
349   base::DictionaryValue* processes = new base::DictionaryValue();
350 
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;
356 
357       // Store each process indexed by the string version of its id.
358       processes->Set(base::IntToString(id), it.GetCurrentValue());
359     }
360 
361     scoped_ptr<base::ListValue> args(new base::ListValue());
362     args->Append(processes);
363     DispatchEvent(keys::kOnUpdated, args.Pass());
364   }
365 
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;
371 
372       AddMemoryDetails(it.GetCurrentValue(), model_, it.GetCurrentKey());
373 
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());
378     }
379 
380     scoped_ptr<base::ListValue> args(new base::ListValue());
381     args->Append(processes);
382     DispatchEvent(keys::kOnUpdatedWithMemory, args.Pass());
383   }
384 #endif  // defined(ENABLE_TASK_MANAGER)
385 }
386 
OnItemsToBeRemoved(int start,int length)387 void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
388 #if defined(ENABLE_TASK_MANAGER)
389   DCHECK_EQ(length, 1);
390 
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;
397 
398   // The callback function parameters.
399   scoped_ptr<base::ListValue> args(new base::ListValue());
400 
401   // First arg: The id of the process that was closed.
402   args->Append(new base::FundamentalValue(
403       model_->GetUniqueChildProcessId(start)));
404 
405   // Second arg: The exit type for the process.
406   args->Append(new base::FundamentalValue(0));
407 
408   // Third arg: The exit code for the process.
409   args->Append(new base::FundamentalValue(0));
410 
411   DispatchEvent(keys::kOnExited, args.Pass());
412 #endif  // defined(ENABLE_TASK_MANAGER)
413 }
414 
ProcessHangEvent(content::RenderWidgetHost * widget)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;
420 
421   base::DictionaryValue* process = NULL;
422   int count = model_->ResourceCount();
423   int id = widget->GetProcess()->GetID();
424 
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;
430       }
431     }
432   }
433 
434   if (process == NULL)
435     return;
436 
437   scoped_ptr<base::ListValue> args(new base::ListValue());
438   args->Append(process);
439 
440   DispatchEvent(keys::kOnUnresponsive, args.Pass());
441 #endif  // defined(ENABLE_TASK_MANAGER)
442 }
443 
ProcessClosedEvent(content::RenderProcessHost * rph,content::RenderProcessHost::RendererClosedDetails * details)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());
450 
451   // First arg: The id of the process that was closed.
452   args->Append(new base::FundamentalValue(rph->GetID()));
453 
454   // Second arg: The exit type for the process.
455   args->Append(new base::FundamentalValue(details->status));
456 
457   // Third arg: The exit code for the process.
458   args->Append(new base::FundamentalValue(details->exit_code));
459 
460   DispatchEvent(keys::kOnExited, args.Pass());
461 #endif  // defined(ENABLE_TASK_MANAGER)
462 }
463 
DispatchEvent(const std::string & event_name,scoped_ptr<base::ListValue> event_args)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());
472   }
473 }
474 
HasEventListeners(const std::string & event_name)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);
478 }
479 
ProcessesAPI(content::BrowserContext * context)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>();
491 }
492 
~ProcessesAPI()493 ProcessesAPI::~ProcessesAPI() {
494 }
495 
Shutdown()496 void ProcessesAPI::Shutdown() {
497   EventRouter::Get(browser_context_)->UnregisterObserver(this);
498 }
499 
500 static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
501     g_factory = LAZY_INSTANCE_INITIALIZER;
502 
503 // static
504 BrowserContextKeyedAPIFactory<ProcessesAPI>*
GetFactoryInstance()505 ProcessesAPI::GetFactoryInstance() {
506   return g_factory.Pointer();
507 }
508 
509 // static
Get(content::BrowserContext * context)510 ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
511   return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
512 }
513 
processes_event_router()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();
518 }
519 
OnListenerAdded(const EventListenerInfo & details)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();
524 }
525 
OnListenerRemoved(const EventListenerInfo & details)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();
531 }
532 
GetProcessIdForTabFunction()533 GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) {
534 }
535 
RunAsync()536 bool GetProcessIdForTabFunction::RunAsync() {
537 #if defined(ENABLE_TASK_MANAGER)
538   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_));
539 
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();
543 
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));
556 
557     ProcessesAPI::Get(GetProfile())
558         ->processes_event_router()
559         ->StartTaskManagerListening();
560   }
561 
562   return true;
563 #else
564   error_ = errors::kExtensionNotSupported;
565   return false;
566 #endif  // defined(ENABLE_TASK_MANAGER)
567 }
568 
GetProcessIdForTab()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);
588   }
589 
590   // Balance the AddRef in the RunAsync.
591   Release();
592 }
593 
TerminateFunction()594 TerminateFunction::TerminateFunction() : process_id_(-1) {
595 }
596 
RunAsync()597 bool TerminateFunction::RunAsync() {
598 #if defined(ENABLE_TASK_MANAGER)
599   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_));
600 
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();
604 
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));
617 
618     ProcessesAPI::Get(GetProfile())
619         ->processes_event_router()
620         ->StartTaskManagerListening();
621   }
622 
623   return true;
624 #else
625   error_ = errors::kExtensionNotSupported;
626   return false;
627 #endif  // defined(ENABLE_TASK_MANAGER)
628 }
629 
630 
TerminateProcess()631 void TerminateFunction::TerminateProcess() {
632 #if defined(ENABLE_TASK_MANAGER)
633   TaskManagerModel* model = TaskManager::GetInstance()->model();
634 
635   int count = model->ResourceCount();
636   bool killed = false;
637   bool found = false;
638 
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;
647       }
648     }
649   }
650 
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);
658   }
659 
660   // Balance the AddRef in the RunAsync.
661   Release();
662 #else
663   error_ = errors::kExtensionNotSupported;
664   SendResponse(false);
665 #endif  // defined(ENABLE_TASK_MANAGER)
666 }
667 
GetProcessInfoFunction()668 GetProcessInfoFunction::GetProcessInfoFunction()
669 #if defined(ENABLE_TASK_MANAGER)
670   : memory_(false)
671 #endif
672     {
673 }
674 
~GetProcessInfoFunction()675 GetProcessInfoFunction::~GetProcessInfoFunction() {
676 }
677 
RunAsync()678 bool GetProcessInfoFunction::RunAsync() {
679 #if defined(ENABLE_TASK_MANAGER)
680   base::Value* processes = NULL;
681 
682   EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &processes));
683   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory_));
684 
685   EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
686       processes, &process_ids_));
687 
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();
691 
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));
704 
705     ProcessesAPI::Get(GetProfile())
706         ->processes_event_router()
707         ->StartTaskManagerListening();
708   }
709   return true;
710 
711 #else
712   error_ = errors::kExtensionNotSupported;
713   return false;
714 #endif  // defined(ENABLE_TASK_MANAGER)
715 }
716 
GatherProcessInfo()717 void GetProcessInfoFunction::GatherProcessInfo() {
718 #if defined(ENABLE_TASK_MANAGER)
719   TaskManagerModel* model = TaskManager::GetInstance()->model();
720   base::DictionaryValue* processes = new base::DictionaryValue();
721 
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);
733       }
734     }
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);
748 
749           process_ids_.erase(proc_id);
750           if (process_ids_.size() == 0)
751             break;
752         }
753       }
754     }
755     DCHECK_EQ(process_ids_.size(), 0U);
756   }
757 
758   SetResult(processes);
759   SendResponse(true);
760 
761   // Balance the AddRef in the RunAsync.
762   Release();
763 #endif  // defined(ENABLE_TASK_MANAGER)
764 }
765 
766 }  // namespace extensions
767