1d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org// Use of this source code is governed by a BSD-style license that can be
3d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org// found in the LICENSE file.
4d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org
5d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/extensions/api/processes/processes_api.h"
6d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org
7d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/callback.h"
8d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/json/json_writer.h"
9d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/lazy_instance.h"
10d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/message_loop/message_loop.h"
11d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/metrics/histogram.h"
12d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/strings/string_number_conversions.h"
13d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/strings/utf_string_conversions.h"
14d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "base/values.h"
15d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/chrome_notification_types.h"
16d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/extensions/api/processes/processes_api_constants.h"
17d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
18d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/extensions/extension_service.h"
19d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/extensions/extension_tab_util.h"
20d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/profiles/profile.h"
21d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/task_manager/resource_provider.h"
22d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "chrome/browser/task_manager/task_manager.h"
23d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/browser_context.h"
24d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/notification_details.h"
25d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/notification_service.h"
26d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/notification_source.h"
27d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/notification_types.h"
28d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/render_process_host.h"
29d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/render_view_host.h"
30d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/render_widget_host.h"
31d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#include "content/public/browser/render_widget_host_iterator.h"
32d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org#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
39namespace extensions {
40
41namespace keys = processes_api_constants;
42namespace errors = processes_api_constants;
43
44namespace {
45
46#if defined(ENABLE_TASK_MANAGER)
47
48base::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
57void 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
102base::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.
139base::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.
200void 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
213ProcessesEventRouter::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
226ProcessesEventRouter::~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
240void 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
249void 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
259void 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
268void 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
290void 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
319void 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
387void 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
415void 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
444void 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
464void 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
475bool 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
480ProcessesAPI::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
493ProcessesAPI::~ProcessesAPI() {
494}
495
496void ProcessesAPI::Shutdown() {
497  EventRouter::Get(browser_context_)->UnregisterObserver(this);
498}
499
500static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
501    g_factory = LAZY_INSTANCE_INITIALIZER;
502
503// static
504BrowserContextKeyedAPIFactory<ProcessesAPI>*
505ProcessesAPI::GetFactoryInstance() {
506  return g_factory.Pointer();
507}
508
509// static
510ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
511  return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
512}
513
514ProcessesEventRouter* 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
520void 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
526void 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
533GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) {
534}
535
536bool 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
569void 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
594TerminateFunction::TerminateFunction() : process_id_(-1) {
595}
596
597bool 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
631void 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
668GetProcessInfoFunction::GetProcessInfoFunction()
669#if defined(ENABLE_TASK_MANAGER)
670  : memory_(false)
671#endif
672    {
673}
674
675GetProcessInfoFunction::~GetProcessInfoFunction() {
676}
677
678bool 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
717void 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