| Index: chrome/browser/extensions/extension_processes_api.cc
 | 
| diff --git a/chrome/browser/extensions/extension_processes_api.cc b/chrome/browser/extensions/extension_processes_api.cc
 | 
| index 9073abd16e198ba815378a7e69d4c31212bfa99c..db0e07bdc7c05df9fdd47b843a7e268e53e41c11 100644
 | 
| --- a/chrome/browser/extensions/extension_processes_api.cc
 | 
| +++ b/chrome/browser/extensions/extension_processes_api.cc
 | 
| @@ -7,53 +7,238 @@
 | 
|  #include "base/callback.h"
 | 
|  #include "base/json/json_writer.h"
 | 
|  #include "base/message_loop.h"
 | 
| +#include "base/metrics/histogram.h"
 | 
|  #include "base/string_number_conversions.h"
 | 
|  #include "base/utf_string_conversions.h"
 | 
|  #include "base/values.h"
 | 
|  
 | 
|  #include "chrome/browser/extensions/extension_event_router.h"
 | 
| +#include "chrome/browser/extensions/extension_function_util.h"
 | 
|  #include "chrome/browser/extensions/extension_processes_api_constants.h"
 | 
| +#include "chrome/browser/extensions/extension_service.h"
 | 
|  #include "chrome/browser/extensions/extension_tab_util.h"
 | 
|  #include "chrome/browser/extensions/extension_tabs_module_constants.h"
 | 
|  #include "chrome/browser/profiles/profile.h"
 | 
|  #include "chrome/browser/task_manager/task_manager.h"
 | 
|  #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
 | 
| +#include "chrome/common/chrome_notification_types.h"
 | 
|  #include "chrome/common/extensions/extension_error_utils.h"
 | 
| +#include "content/public/browser/browser_thread.h"
 | 
| +#include "content/public/browser/notification_details.h"
 | 
| +#include "content/public/browser/notification_service.h"
 | 
| +#include "content/public/browser/notification_source.h"
 | 
|  #include "content/public/browser/notification_types.h"
 | 
|  #include "content/public/browser/render_process_host.h"
 | 
| +#include "content/public/browser/render_view_host.h"
 | 
| +#include "content/public/browser/render_view_host_delegate.h"
 | 
| +#include "content/public/browser/render_widget_host.h"
 | 
|  #include "content/public/browser/web_contents.h"
 | 
| +#include "content/public/common/result_codes.h"
 | 
|  
 | 
|  namespace keys = extension_processes_api_constants;
 | 
| +namespace errors = extension_processes_api_constants;
 | 
|  
 | 
| -DictionaryValue* CreateProcessValue(int process_id,
 | 
| -                                    const std::string& type,
 | 
| -                                    double cpu,
 | 
| -                                    int64 net,
 | 
| -                                    int64 pr_mem,
 | 
| -                                    int64 sh_mem) {
 | 
| +namespace {
 | 
| +
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +
 | 
| +DictionaryValue* CreateCacheData(
 | 
| +    const WebKit::WebCache::ResourceTypeStat& stat) {
 | 
| +
 | 
| +  DictionaryValue* cache = new DictionaryValue();
 | 
| +  cache->SetDouble(keys::kCacheSize, static_cast<double>(stat.size));
 | 
| +  cache->SetDouble(keys::kCacheLiveSize, static_cast<double>(stat.liveSize));
 | 
| +  return cache;
 | 
| +}
 | 
| +
 | 
| +void SetProcessType(DictionaryValue* result,
 | 
| +                    TaskManagerModel* model,
 | 
| +                    int index) {
 | 
| +  // Determine process type.
 | 
| +  std::string type = keys::kProcessTypeOther;
 | 
| +  TaskManager::Resource::Type resource_type = model->GetResourceType(index);
 | 
| +  switch (resource_type) {
 | 
| +    case TaskManager::Resource::BROWSER:
 | 
| +      type = keys::kProcessTypeBrowser;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::RENDERER:
 | 
| +      type = keys::kProcessTypeRenderer;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::EXTENSION:
 | 
| +      type = keys::kProcessTypeExtension;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::NOTIFICATION:
 | 
| +      type = keys::kProcessTypeNotification;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::PLUGIN:
 | 
| +      type = keys::kProcessTypePlugin;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::WORKER:
 | 
| +      type = keys::kProcessTypeWorker;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::NACL:
 | 
| +      type = keys::kProcessTypeNacl;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::UTILITY:
 | 
| +      type = keys::kProcessTypeUtility;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::GPU:
 | 
| +      type = keys::kProcessTypeGPU;
 | 
| +      break;
 | 
| +    case TaskManager::Resource::PROFILE_IMPORT:
 | 
| +    case TaskManager::Resource::ZYGOTE:
 | 
| +    case TaskManager::Resource::SANDBOX_HELPER:
 | 
| +    case TaskManager::Resource::UNKNOWN:
 | 
| +      type = keys::kProcessTypeOther;
 | 
| +      break;
 | 
| +    default:
 | 
| +      NOTREACHED() << "Unknown resource type.";
 | 
| +  }
 | 
| +  result->SetString(keys::kTypeKey, type);
 | 
| +}
 | 
| +
 | 
| +ListValue* GetTabsForProcess(int process_id) {
 | 
| +  ListValue* tabs_list = new ListValue();
 | 
| +
 | 
| +  // The tabs list only makes sense for render processes, so if we don't find
 | 
| +  // one, just return the empty list.
 | 
| +  content::RenderProcessHost* rph =
 | 
| +      content::RenderProcessHost::FromID(process_id);
 | 
| +  if (rph == NULL)
 | 
| +    return tabs_list;
 | 
| +
 | 
| +  int tab_id = -1;
 | 
| +  // We need to loop through all the RVHs to ensure we collect the set of all
 | 
| +  // tabs using this renderer process.
 | 
| +  content::RenderProcessHost::RenderWidgetHostsIterator iter(
 | 
| +      rph->GetRenderWidgetHostsIterator());
 | 
| +  for (; !iter.IsAtEnd(); iter.Advance()) {
 | 
| +    const content::RenderWidgetHost* widget = iter.GetCurrentValue();
 | 
| +    DCHECK(widget);
 | 
| +    if (!widget || !widget->IsRenderView())
 | 
| +      continue;
 | 
| +
 | 
| +    content::RenderViewHost* host = content::RenderViewHost::From(
 | 
| +        const_cast<content::RenderWidgetHost*>(widget));
 | 
| +    content::RenderViewHostDelegate* host_delegate = host->GetDelegate();
 | 
| +    content::WebContents* contents = host_delegate->GetAsWebContents();
 | 
| +    if (contents != NULL) {
 | 
| +      tab_id = ExtensionTabUtil::GetTabId(contents);
 | 
| +      if (tab_id != -1)
 | 
| +        tabs_list->Append(Value::CreateIntegerValue(tab_id));
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return tabs_list;
 | 
| +}
 | 
| +
 | 
| +// This function creates a Process object to be returned to the extensions
 | 
| +// using these APIs. For memory details, which are not added by this function,
 | 
| +// the callers need to use AddMemoryDetails.
 | 
| +DictionaryValue* CreateProcessFromModel(int process_id,
 | 
| +                                        TaskManagerModel* model,
 | 
| +                                        int index,
 | 
| +                                        bool include_optional) {
 | 
|    DictionaryValue* result = new DictionaryValue();
 | 
| +  size_t mem;
 | 
| +
 | 
|    result->SetInteger(keys::kIdKey, process_id);
 | 
| -  result->SetString(keys::kTypeKey, type);
 | 
| -  result->SetDouble(keys::kCpuKey, cpu);
 | 
| +  result->SetInteger(keys::kOsProcessIdKey, model->GetProcessId(index));
 | 
| +  SetProcessType(result, model, index);
 | 
| +  result->SetString(keys::kProfileKey,
 | 
| +      model->GetResourceProfileName(index));
 | 
| +
 | 
| +  result->Set(keys::kTabsListKey, GetTabsForProcess(process_id));
 | 
| +
 | 
| +  // If we don't need to include the optional properties, just return now.
 | 
| +  if (!include_optional)
 | 
| +    return result;
 | 
| +
 | 
| +  result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index));
 | 
| +
 | 
| +  if (model->GetV8Memory(index, &mem))
 | 
| +    result->SetDouble(keys::kJsMemoryAllocatedKey,
 | 
| +        static_cast<double>(mem));
 | 
| +
 | 
| +  if (model->GetV8MemoryUsed(index, &mem))
 | 
| +    result->SetDouble(keys::kJsMemoryUsedKey,
 | 
| +        static_cast<double>(mem));
 | 
| +
 | 
| +  if (model->GetSqliteMemoryUsedBytes(index, &mem))
 | 
| +    result->SetDouble(keys::kSqliteMemoryKey,
 | 
| +        static_cast<double>(mem));
 | 
| +
 | 
| +  WebKit::WebCache::ResourceTypeStats cache_stats;
 | 
| +  if (model->GetWebCoreCacheStats(index, &cache_stats)) {
 | 
| +    result->Set(keys::kImageCacheKey,
 | 
| +                CreateCacheData(cache_stats.images));
 | 
| +    result->Set(keys::kScriptCacheKey,
 | 
| +                CreateCacheData(cache_stats.scripts));
 | 
| +    result->Set(keys::kCssCacheKey,
 | 
| +                CreateCacheData(cache_stats.cssStyleSheets));
 | 
| +  }
 | 
| +
 | 
| +  // Network and FPS are reported by the TaskManager per resource (tab), not
 | 
| +  // per process, therefore we need to iterate through the group of resources
 | 
| +  // and aggregate the data.
 | 
| +  float fps = 0, tmp = 0;
 | 
| +  int64 net = 0;
 | 
| +  int length = model->GetGroupRangeForResource(index).second;
 | 
| +  for (int i = 0; i < length; ++i) {
 | 
| +    net += model->GetNetworkUsage(index + i);
 | 
| +    if (model->GetFPS(index + i, &tmp))
 | 
| +      fps += tmp;
 | 
| +  }
 | 
| +  result->SetDouble(keys::kFPSKey, static_cast<double>(fps));
 | 
|    result->SetDouble(keys::kNetworkKey, static_cast<double>(net));
 | 
| -  result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem));
 | 
| -  result->SetDouble(keys::kSharedMemoryKey, static_cast<double>(sh_mem));
 | 
| +
 | 
|    return result;
 | 
|  }
 | 
|  
 | 
| +// Since memory details are expensive to gather, we don't do it by default.
 | 
| +// This function is a helper to add memory details data to an existing
 | 
| +// Process object representation.
 | 
| +void AddMemoryDetails(DictionaryValue* result,
 | 
| +                      TaskManagerModel* model,
 | 
| +                      int index) {
 | 
| +  size_t mem;
 | 
| +  int64 pr_mem = model->GetPrivateMemory(index, &mem) ?
 | 
| +      static_cast<int64>(mem) : -1;
 | 
| +  result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem));
 | 
| +}
 | 
| +
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +
 | 
| +} // local namespace
 | 
| +
 | 
|  ExtensionProcessesEventRouter* ExtensionProcessesEventRouter::GetInstance() {
 | 
|    return Singleton<ExtensionProcessesEventRouter>::get();
 | 
|  }
 | 
|  
 | 
| -ExtensionProcessesEventRouter::ExtensionProcessesEventRouter() {
 | 
| +ExtensionProcessesEventRouter::ExtensionProcessesEventRouter()
 | 
| +    : listeners_(0),
 | 
| +      task_manager_listening_(false) {
 | 
|  #if defined(ENABLE_TASK_MANAGER)
 | 
|    model_ = TaskManager::GetInstance()->model();
 | 
|    model_->AddObserver(this);
 | 
| +
 | 
| +  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_HANG,
 | 
| +      content::NotificationService::AllSources());
 | 
| +  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
 | 
| +      content::NotificationService::AllSources());
 | 
|  #endif  // defined(ENABLE_TASK_MANAGER)
 | 
|  }
 | 
|  
 | 
|  ExtensionProcessesEventRouter::~ExtensionProcessesEventRouter() {
 | 
|  #if defined(ENABLE_TASK_MANAGER)
 | 
| +  registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_HANG,
 | 
| +      content::NotificationService::AllSources());
 | 
| +  registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
 | 
| +      content::NotificationService::AllSources());
 | 
| +
 | 
| +  if (task_manager_listening_)
 | 
| +    model_->StopListening();
 | 
| +
 | 
|    model_->RemoveObserver(this);
 | 
|  #endif  // defined(ENABLE_TASK_MANAGER)
 | 
|  }
 | 
| @@ -64,99 +249,243 @@ void ExtensionProcessesEventRouter::ObserveProfile(Profile* profile) {
 | 
|  
 | 
|  void ExtensionProcessesEventRouter::ListenerAdded() {
 | 
|  #if defined(ENABLE_TASK_MANAGER)
 | 
| +  // The task manager has its own ref count to balance other callers of
 | 
| +  // StartUpdating/StopUpdating.
 | 
|    model_->StartUpdating();
 | 
|  #endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +  ++listeners_;
 | 
|  }
 | 
|  
 | 
|  void ExtensionProcessesEventRouter::ListenerRemoved() {
 | 
| +  DCHECK(listeners_ > 0);
 | 
| +  --listeners_;
 | 
|  #if defined(ENABLE_TASK_MANAGER)
 | 
| +  // The task manager has its own ref count to balance other callers of
 | 
| +  // StartUpdating/StopUpdating.
 | 
|    model_->StopUpdating();
 | 
|  #endif  // defined(ENABLE_TASK_MANAGER)
 | 
|  }
 | 
|  
 | 
| +void ExtensionProcessesEventRouter::StartTaskManagerListening() {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  if (!task_manager_listening_) {
 | 
| +    model_->StartListening();
 | 
| +    task_manager_listening_ = true;
 | 
| +  }
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
| +void ExtensionProcessesEventRouter::Observe(
 | 
| +    int type,
 | 
| +    const content::NotificationSource& source,
 | 
| +    const content::NotificationDetails& details) {
 | 
| +
 | 
| +  switch (type) {
 | 
| +    case content::NOTIFICATION_RENDERER_PROCESS_HANG:
 | 
| +      ProcessHangEvent(
 | 
| +          content::Source<content::RenderWidgetHost>(source).ptr());
 | 
| +      break;
 | 
| +    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
 | 
| +      ProcessClosedEvent(
 | 
| +          content::Source<content::RenderProcessHost>(source).ptr(),
 | 
| +          content::Details<content::RenderProcessHost::RendererClosedDetails>(
 | 
| +              details).ptr());
 | 
| +      break;
 | 
| +    default:
 | 
| +      NOTREACHED() << "Unexpected observe of type " << type;
 | 
| +  }
 | 
| +  return;
 | 
| +}
 | 
| +
 | 
| +void ExtensionProcessesEventRouter::OnItemsAdded(int start, int length) {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  DCHECK_EQ(length, 1);
 | 
| +  int index = start;
 | 
| +
 | 
| +  std::string event(keys::kOnCreated);
 | 
| +  if (!HasEventListeners(event))
 | 
| +    return;
 | 
| +
 | 
| +  // If the item being added is not the first one in the group, find the base
 | 
| +  // index and use it for retrieving the process data.
 | 
| +  if (!model_->IsResourceFirstInGroup(start)) {
 | 
| +    index = model_->GetGroupIndexForResource(start);
 | 
| +  }
 | 
| +
 | 
| +  ListValue args;
 | 
| +  DictionaryValue* process = CreateProcessFromModel(
 | 
| +      model_->GetUniqueChildProcessId(index), model_, index, false);
 | 
| +  DCHECK(process != NULL);
 | 
| +
 | 
| +  if (process == NULL)
 | 
| +    return;
 | 
| +
 | 
| +  args.Append(process);
 | 
| +
 | 
| +  std::string json_args;
 | 
| +  base::JSONWriter::Write(&args, &json_args);
 | 
| +  NotifyProfiles(keys::kOnCreated, json_args);
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
|  void ExtensionProcessesEventRouter::OnItemsChanged(int start, int length) {
 | 
|  #if defined(ENABLE_TASK_MANAGER)
 | 
| -  if (model_) {
 | 
| -    ListValue args;
 | 
| -    DictionaryValue* processes = new DictionaryValue();
 | 
| -    for (int i = start; i < start + length; i++) {
 | 
| -      if (model_->IsResourceFirstInGroup(i)) {
 | 
| -        int id = model_->GetProcessId(i);
 | 
| -
 | 
| -        // Determine process type
 | 
| -        std::string type = keys::kProcessTypeOther;
 | 
| -        TaskManager::Resource::Type resource_type = model_->GetResourceType(i);
 | 
| -        switch (resource_type) {
 | 
| -          case TaskManager::Resource::BROWSER:
 | 
| -            type = keys::kProcessTypeBrowser;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::RENDERER:
 | 
| -            type = keys::kProcessTypeRenderer;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::EXTENSION:
 | 
| -            type = keys::kProcessTypeExtension;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::NOTIFICATION:
 | 
| -            type = keys::kProcessTypeNotification;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::PLUGIN:
 | 
| -            type = keys::kProcessTypePlugin;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::WORKER:
 | 
| -            type = keys::kProcessTypeWorker;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::NACL:
 | 
| -            type = keys::kProcessTypeNacl;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::UTILITY:
 | 
| -            type = keys::kProcessTypeUtility;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::GPU:
 | 
| -            type = keys::kProcessTypeGPU;
 | 
| -            break;
 | 
| -          case TaskManager::Resource::PROFILE_IMPORT:
 | 
| -          case TaskManager::Resource::ZYGOTE:
 | 
| -          case TaskManager::Resource::SANDBOX_HELPER:
 | 
| -          case TaskManager::Resource::UNKNOWN:
 | 
| -            type = keys::kProcessTypeOther;
 | 
| -            break;
 | 
| -          default:
 | 
| -            NOTREACHED() << "Unknown resource type.";
 | 
| -        }
 | 
| +  // If we don't have any listeners, return immediately.
 | 
| +  if (listeners_ == 0)
 | 
| +    return;
 | 
|  
 | 
| -        // Get process metrics as numbers
 | 
| -        double cpu = model_->GetCPUUsage(i);
 | 
| -
 | 
| -        // TODO(creis): Network is actually reported per-resource (tab),
 | 
| -        // not per-process.  We should aggregate it here.
 | 
| -        int64 net = model_->GetNetworkUsage(i);
 | 
| -        size_t mem;
 | 
| -        int64 pr_mem = model_->GetPrivateMemory(i, &mem) ?
 | 
| -            static_cast<int64>(mem) : -1;
 | 
| -        int64 sh_mem = model_->GetSharedMemory(i, &mem) ?
 | 
| -            static_cast<int64>(mem) : -1;
 | 
| -
 | 
| -        // Store each process indexed by the string version of its id
 | 
| -        processes->Set(base::IntToString(id),
 | 
| -                       CreateProcessValue(id, type, cpu, net, pr_mem, sh_mem));
 | 
| -      }
 | 
| +  if (!model_)
 | 
| +    return;
 | 
| +
 | 
| +  // We need to know which type of onUpdated events to fire and whether to
 | 
| +  // collect memory or not.
 | 
| +  std::string updated_event(keys::kOnUpdated);
 | 
| +  std::string updated_event_memory(keys::kOnUpdatedWithMemory);
 | 
| +  bool updated = HasEventListeners(updated_event);
 | 
| +  bool updated_memory = HasEventListeners(updated_event_memory);
 | 
| +
 | 
| +  DCHECK(updated || updated_memory);
 | 
| +
 | 
| +  IDMap<DictionaryValue> processes_map;
 | 
| +  for (int i = start; i < start + length; i++) {
 | 
| +    if (model_->IsResourceFirstInGroup(i)) {
 | 
| +      int id = model_->GetUniqueChildProcessId(i);
 | 
| +      DictionaryValue* process = CreateProcessFromModel(id, model_, i, true);
 | 
| +      processes_map.AddWithID(process, i);
 | 
|      }
 | 
| +  }
 | 
| +
 | 
| +  int id;
 | 
| +  std::string idkey(keys::kIdKey);
 | 
| +  DictionaryValue* processes = new DictionaryValue();
 | 
| +
 | 
| +  if (updated) {
 | 
| +    IDMap<DictionaryValue>::iterator it(&processes_map);
 | 
| +    for (; !it.IsAtEnd(); it.Advance()) {
 | 
| +      if (!it.GetCurrentValue()->GetInteger(idkey, &id))
 | 
| +        continue;
 | 
| +
 | 
| +      // Store each process indexed by the string version of its id.
 | 
| +      processes->Set(base::IntToString(id), it.GetCurrentValue());
 | 
| +    }
 | 
| +
 | 
| +    ListValue args;
 | 
|      args.Append(processes);
 | 
| +    std::string json_args;
 | 
| +    base::JSONWriter::Write(&args, &json_args);
 | 
| +    NotifyProfiles(keys::kOnUpdated, json_args);
 | 
| +  }
 | 
| +
 | 
| +  if (updated_memory) {
 | 
| +    IDMap<DictionaryValue>::iterator it(&processes_map);
 | 
| +    for (; !it.IsAtEnd(); it.Advance()) {
 | 
| +      if (!it.GetCurrentValue()->GetInteger(idkey, &id))
 | 
| +        continue;
 | 
| +
 | 
| +      AddMemoryDetails(it.GetCurrentValue(), model_, it.GetCurrentKey());
 | 
|  
 | 
| +      // Store each process indexed by the string version of its id if we didn't
 | 
| +      // already insert it as part of the onUpdated processing above.
 | 
| +      if (!updated)
 | 
| +        processes->Set(base::IntToString(id), it.GetCurrentValue());
 | 
| +    }
 | 
| +
 | 
| +    ListValue args;
 | 
| +    args.Append(processes);
 | 
|      std::string json_args;
 | 
|      base::JSONWriter::Write(&args, &json_args);
 | 
| +    NotifyProfiles(keys::kOnUpdatedWithMemory, json_args);
 | 
| +  }
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
| +void ExtensionProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  DCHECK(length == 1);
 | 
| +
 | 
| +  // Process exit for renderer processes has the data about exit code and
 | 
| +  // termination status, therefore we will rely on notifications and not on
 | 
| +  // the Task Manager data. We do use the rest of this method for non-renderer
 | 
| +  // processes.
 | 
| +  if (model_->GetResourceType(start) == TaskManager::Resource::RENDERER)
 | 
| +    return;
 | 
| +
 | 
| +  // The callback function parameters.
 | 
| +  ListValue args;
 | 
| +
 | 
| +  // First arg: The id of the process that was closed.
 | 
| +  args.Append(Value::CreateIntegerValue(
 | 
| +      model_->GetUniqueChildProcessId(start)));
 | 
| +
 | 
| +  // Second arg: The exit type for the process.
 | 
| +  args.Append(Value::CreateIntegerValue(0));
 | 
| +
 | 
| +  // Third arg: The exit code for the process.
 | 
| +  args.Append(Value::CreateIntegerValue(0));
 | 
| +
 | 
| +  std::string json_args;
 | 
| +  base::JSONWriter::Write(&args, &json_args);
 | 
| +  NotifyProfiles(keys::kOnExited, json_args);
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
|  
 | 
| -    // Notify each profile that is interested.
 | 
| -    for (ProfileSet::iterator it = profiles_.begin();
 | 
| -         it != profiles_.end(); it++) {
 | 
| -      Profile* profile = *it;
 | 
| -      DispatchEvent(profile, keys::kOnUpdated, json_args);
 | 
| +void ExtensionProcessesEventRouter::ProcessHangEvent(
 | 
| +    content::RenderWidgetHost* widget) {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  std::string event(keys::kOnUnresponsive);
 | 
| +  if (!HasEventListeners(event))
 | 
| +    return;
 | 
| +
 | 
| +  DictionaryValue* process = NULL;
 | 
| +  int count = model_->ResourceCount();
 | 
| +  int id = widget->GetProcess()->GetID();
 | 
| +
 | 
| +  for (int i = 0; i < count; ++i) {
 | 
| +    if (model_->IsResourceFirstInGroup(i)) {
 | 
| +      if (id == model_->GetUniqueChildProcessId(i)) {
 | 
| +        process = CreateProcessFromModel(id, model_, i, false);
 | 
| +        break;
 | 
| +      }
 | 
|      }
 | 
|    }
 | 
| +
 | 
| +  DCHECK(process);
 | 
| +  if (process == NULL)
 | 
| +    return;
 | 
| +
 | 
| +  ListValue args;
 | 
| +  args.Append(process);
 | 
| +
 | 
| +  std::string json_args;
 | 
| +  base::JSONWriter::Write(&args, &json_args);
 | 
| +  NotifyProfiles(keys::kOnUnresponsive, json_args);
 | 
|  #endif  // defined(ENABLE_TASK_MANAGER)
 | 
|  }
 | 
|  
 | 
| -void ExtensionProcessesEventRouter::DispatchEvent(Profile* profile,
 | 
| +void ExtensionProcessesEventRouter::ProcessClosedEvent(
 | 
| +    content::RenderProcessHost* rph,
 | 
| +    content::RenderProcessHost::RendererClosedDetails* details) {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  // The callback function parameters.
 | 
| +  ListValue args;
 | 
| +
 | 
| +  // First arg: The id of the process that was closed.
 | 
| +  args.Append(Value::CreateIntegerValue(rph->GetID()));
 | 
| +
 | 
| +  // Second arg: The exit type for the process.
 | 
| +  args.Append(Value::CreateIntegerValue(details->status));
 | 
| +
 | 
| +  // Third arg: The exit code for the process.
 | 
| +  args.Append(Value::CreateIntegerValue(details->exit_code));
 | 
| +
 | 
| +  std::string json_args;
 | 
| +  base::JSONWriter::Write(&args, &json_args);
 | 
| +  NotifyProfiles(keys::kOnExited, json_args);
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
| +void ExtensionProcessesEventRouter::DispatchEvent(
 | 
| +    Profile* profile,
 | 
|      const char* event_name,
 | 
|      const std::string& json_args) {
 | 
|    if (profile && profile->GetExtensionEventRouter()) {
 | 
| @@ -165,23 +494,262 @@ void ExtensionProcessesEventRouter::DispatchEvent(Profile* profile,
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +void ExtensionProcessesEventRouter::NotifyProfiles(const char* event_name,
 | 
| +                                                   std::string json_args) {
 | 
| +  for (ProfileSet::iterator it = profiles_.begin();
 | 
| +       it != profiles_.end(); it++) {
 | 
| +    Profile* profile = *it;
 | 
| +    DispatchEvent(profile, event_name, json_args);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// In order to determine whether there are any listeners for the event of
 | 
| +// interest, we need to ask each profile whether it has one registered.
 | 
| +// We only need to look for the profiles that have registered with the
 | 
| +// this extension API.
 | 
| +bool ExtensionProcessesEventRouter::HasEventListeners(std::string& event_name) {
 | 
| +  for (ProfileSet::iterator it = profiles_.begin();
 | 
| +       it != profiles_.end(); it++) {
 | 
| +    Profile* profile = *it;
 | 
| +    ExtensionEventRouter* router = profile->GetExtensionEventRouter();
 | 
| +    if (!router)
 | 
| +      continue;
 | 
| +
 | 
| +    if (router->HasEventListener(event_name))
 | 
| +      return true;
 | 
| +  }
 | 
| +  return false;
 | 
| +}
 | 
| +
 | 
|  bool GetProcessIdForTabFunction::RunImpl() {
 | 
| -  int tab_id;
 | 
| -  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_));
 | 
| +
 | 
| +  // Add a reference, which is balanced in GetProcessIdForTab to keep the object
 | 
| +  // around and allow for the callback to be invoked.
 | 
| +  AddRef();
 | 
|  
 | 
| +  // If the task manager is already listening, just post a task to execute
 | 
| +  // which will invoke the callback once we have returned from this function.
 | 
| +  // Otherwise, wait for the notification that the task manager is done with
 | 
| +  // the data gathering.
 | 
| +  if (ExtensionProcessesEventRouter::GetInstance()->
 | 
| +          is_task_manager_listening()) {
 | 
| +    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
 | 
| +        &GetProcessIdForTabFunction::GetProcessIdForTab, this));
 | 
| +  } else {
 | 
| +    registrar_.Add(this,
 | 
| +                   chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY,
 | 
| +                   content::NotificationService::AllSources());
 | 
| +    ExtensionProcessesEventRouter::GetInstance()->
 | 
| +        StartTaskManagerListening();
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +#else
 | 
| +  error_ = ExtensionErrorUtils::FormatErrorMessage(
 | 
| +      errors::kExtensionNotSupported);
 | 
| +  return false;
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
| +void GetProcessIdForTabFunction::Observe(
 | 
| +    int type,
 | 
| +    const content::NotificationSource& source,
 | 
| +    const content::NotificationDetails& details) {
 | 
| +  DCHECK_EQ(type, chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY);
 | 
| +  registrar_.RemoveAll();
 | 
| +  GetProcessIdForTab();
 | 
| +}
 | 
| +
 | 
| +void GetProcessIdForTabFunction::GetProcessIdForTab() {
 | 
|    TabContentsWrapper* contents = NULL;
 | 
|    int tab_index = -1;
 | 
| -  if (!ExtensionTabUtil::GetTabById(tab_id, profile(), include_incognito(),
 | 
| +  if (!ExtensionTabUtil::GetTabById(tab_id_, profile(), include_incognito(),
 | 
|                                      NULL, NULL, &contents, &tab_index)) {
 | 
|      error_ = ExtensionErrorUtils::FormatErrorMessage(
 | 
|          extension_tabs_module_constants::kTabNotFoundError,
 | 
| -        base::IntToString(tab_id));
 | 
| -    return false;
 | 
| +        base::IntToString(tab_id_));
 | 
| +    result_.reset(Value::CreateIntegerValue(-1));
 | 
| +    SendResponse(false);
 | 
| +  } else {
 | 
| +    int process_id = contents->web_contents()->GetRenderProcessHost()->GetID();
 | 
| +    result_.reset(Value::CreateIntegerValue(process_id));
 | 
| +    SendResponse(true);
 | 
| +  }
 | 
| +
 | 
| +  // Balance the AddRef in the RunImpl.
 | 
| +  Release();
 | 
| +}
 | 
| +
 | 
| +bool TerminateFunction::RunImpl() {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_));
 | 
| +
 | 
| +  // Add a reference, which is balanced in TerminateProcess to keep the object
 | 
| +  // around and allow for the callback to be invoked.
 | 
| +  AddRef();
 | 
| +
 | 
| +  // If the task manager is already listening, just post a task to execute
 | 
| +  // which will invoke the callback once we have returned from this function.
 | 
| +  // Otherwise, wait for the notification that the task manager is done with
 | 
| +  // the data gathering.
 | 
| +  if (ExtensionProcessesEventRouter::GetInstance()->
 | 
| +          is_task_manager_listening()) {
 | 
| +    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
 | 
| +        &TerminateFunction::TerminateProcess, this));
 | 
| +  } else {
 | 
| +    registrar_.Add(this,
 | 
| +                   chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY,
 | 
| +                   content::NotificationService::AllSources());
 | 
| +    ExtensionProcessesEventRouter::GetInstance()->
 | 
| +        StartTaskManagerListening();
 | 
|    }
 | 
|  
 | 
| -  // Return the process ID of the tab as an integer.
 | 
| -  int id = base::GetProcId(contents->web_contents()->
 | 
| -      GetRenderProcessHost()->GetHandle());
 | 
| -  result_.reset(Value::CreateIntegerValue(id));
 | 
|    return true;
 | 
| +#else
 | 
| +  error_ = ExtensionErrorUtils::FormatErrorMessage(
 | 
| +      errors::kExtensionNotSupported);
 | 
| +  return false;
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
| +void TerminateFunction::Observe(
 | 
| +    int type,
 | 
| +    const content::NotificationSource& source,
 | 
| +    const content::NotificationDetails& details) {
 | 
| +  DCHECK_EQ(type, chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY);
 | 
| +  registrar_.RemoveAll();
 | 
| +  TerminateProcess();
 | 
| +}
 | 
| +
 | 
| +void TerminateFunction::TerminateProcess() {
 | 
| +  TaskManagerModel* model = TaskManager::GetInstance()->model();
 | 
| +
 | 
| +  int count = model->ResourceCount();
 | 
| +  bool killed = false;
 | 
| +  bool found = false;
 | 
| +
 | 
| +  for (int i = 0; i < count; ++i) {
 | 
| +    if (model->IsResourceFirstInGroup(i)) {
 | 
| +      if (process_id_ == model->GetUniqueChildProcessId(i)) {
 | 
| +        found = true;
 | 
| +        killed = base::KillProcess(model->GetProcess(i),
 | 
| +            content::RESULT_CODE_KILLED, true);
 | 
| +        UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
 | 
| +        break;
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  if (!found) {
 | 
| +    error_ = ExtensionErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
 | 
| +        base::IntToString(process_id_));
 | 
| +    SendResponse(false);
 | 
| +  } else {
 | 
| +    result_.reset(Value::CreateBooleanValue(killed));
 | 
| +    SendResponse(true);
 | 
| +  }
 | 
| +
 | 
| +  // Balance the AddRef in the RunImpl.
 | 
| +  Release();
 | 
| +}
 | 
| +
 | 
| +GetProcessInfoFunction::GetProcessInfoFunction() {
 | 
| +}
 | 
| +
 | 
| +GetProcessInfoFunction::~GetProcessInfoFunction() {
 | 
| +}
 | 
| +
 | 
| +bool GetProcessInfoFunction::RunImpl() {
 | 
| +#if defined(ENABLE_TASK_MANAGER)
 | 
| +  Value* processes = NULL;
 | 
| +
 | 
| +  EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &processes));
 | 
| +  EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory_));
 | 
| +
 | 
| +  EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
 | 
| +      processes, &process_ids_));
 | 
| +
 | 
| +  // Add a reference, which is balanced in GatherProcessInfo to keep the object
 | 
| +  // around and allow for the callback to be invoked.
 | 
| +  AddRef();
 | 
| +
 | 
| +  // If the task manager is already listening, just post a task to execute
 | 
| +  // which will invoke the callback once we have returned from this function.
 | 
| +  // Otherwise, wait for the notification that the task manager is done with
 | 
| +  // the data gathering.
 | 
| +  if (ExtensionProcessesEventRouter::GetInstance()->
 | 
| +          is_task_manager_listening()) {
 | 
| +    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
 | 
| +        &GetProcessInfoFunction::GatherProcessInfo, this));
 | 
| +  } else {
 | 
| +    registrar_.Add(this,
 | 
| +                   chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY,
 | 
| +                   content::NotificationService::AllSources());
 | 
| +    ExtensionProcessesEventRouter::GetInstance()->
 | 
| +        StartTaskManagerListening();
 | 
| +  }
 | 
| +  return true;
 | 
| +
 | 
| +#else
 | 
| +  error_ = ExtensionErrorUtils::FormatErrorMessage(
 | 
| +      errors::kExtensionNotSupported);
 | 
| +  return false;
 | 
| +#endif  // defined(ENABLE_TASK_MANAGER)
 | 
| +}
 | 
| +
 | 
| +void GetProcessInfoFunction::Observe(
 | 
| +    int type,
 | 
| +    const content::NotificationSource& source,
 | 
| +    const content::NotificationDetails& details) {
 | 
| +  DCHECK_EQ(type, chrome::NOTIFICATION_TASK_MANAGER_CHILD_PROCESSES_DATA_READY);
 | 
| +  registrar_.RemoveAll();
 | 
| +  GatherProcessInfo();
 | 
| +}
 | 
| +
 | 
| +void GetProcessInfoFunction::GatherProcessInfo() {
 | 
| +  TaskManagerModel* model = TaskManager::GetInstance()->model();
 | 
| +  DictionaryValue* processes = new DictionaryValue();
 | 
| +
 | 
| +  // If there are no process IDs specified, it means we need to return all of
 | 
| +  // the ones we know of.
 | 
| +  if (process_ids_.size() == 0) {
 | 
| +    int resources = model->ResourceCount();
 | 
| +    for (int i = 0; i < resources; ++i) {
 | 
| +      if (model->IsResourceFirstInGroup(i)) {
 | 
| +        int id = model->GetUniqueChildProcessId(i);
 | 
| +        DictionaryValue* d = CreateProcessFromModel(id, model, i, false);
 | 
| +        if (memory_)
 | 
| +          AddMemoryDetails(d, model, i);
 | 
| +        processes->Set(base::IntToString(id), d);
 | 
| +      }
 | 
| +    }
 | 
| +  } else {
 | 
| +    int resources = model->ResourceCount();
 | 
| +    for (int i = 0; i < resources; ++i) {
 | 
| +      if (model->IsResourceFirstInGroup(i)) {
 | 
| +        int id = model->GetUniqueChildProcessId(i);
 | 
| +        std::vector<int>::iterator proc_id = std::find(process_ids_.begin(),
 | 
| +                                                       process_ids_.end(), id);
 | 
| +        if (proc_id != process_ids_.end()) {
 | 
| +          DictionaryValue* d = CreateProcessFromModel(id, model, i, false);
 | 
| +          if (memory_)
 | 
| +            AddMemoryDetails(d, model, i);
 | 
| +          processes->Set(base::IntToString(id), d);
 | 
| +
 | 
| +          process_ids_.erase(proc_id);
 | 
| +          if (process_ids_.size() == 0)
 | 
| +            break;
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +    DCHECK(process_ids_.size() == 0);
 | 
| +  }
 | 
| +
 | 
| +  result_.reset(processes);
 | 
| +  SendResponse(true);
 | 
| +
 | 
| +  // Balance the AddRef in the RunImpl.
 | 
| +  Release();
 | 
|  }
 | 
| 
 |