Chromium Code Reviews| 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..fa5202a083f617c1aeb142021034ea5027877b4b 100644 |
| --- a/chrome/browser/extensions/extension_processes_api.cc |
| +++ b/chrome/browser/extensions/extension_processes_api.cc |
| @@ -15,45 +15,248 @@ |
| #include "chrome/browser/extensions/extension_processes_api_constants.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/extensions/extension_tabs_module_constants.h" |
| +#include "chrome/browser/extensions/extension_service.h" |
|
Charlie Reis
2012/04/27 22:01:56
Nit: this goes at line 16.
Charlie Reis
2012/05/01 17:58:25
Looks like none of the comments in this file have
nasko
2012/05/01 21:28:41
Done.
nasko
2012/05/01 21:28:41
Done.
|
| #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; |
|
Charlie Reis
2012/04/27 22:01:56
nit: 2 space indent
nasko
2012/05/01 21:28:41
Done.
|
| + |
| + 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; |
| +} |
| + |
| +DictionaryValue* CreateProcessFromModel(int process_id, |
|
Charlie Reis
2012/04/27 22:01:56
Let's put a comment on this one, since it's kind o
nasko
2012/05/01 21:28:41
Done.
|
| + TaskManagerModel* model, |
| + int index) { |
| 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::kOsProcessId, model->GetProcessId(index)); |
| + SetProcessType(result, model, index); |
| + result->SetString(keys::kProfile, |
| + model->GetResourceProfileName(index)); |
| + |
| + result->Set(keys::kTabsList, GetTabsForProcess(process_id)); |
| + |
| + // If there are no listeners for update events, this means that the update |
| + // mechanism in Task Manager is not running, hence all the other metrics are |
| + // not available. |
| + if (ExtensionProcessesEventRouter::GetInstance()->listeners() == 0) |
| + return result; |
| + |
| + result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index)); |
| + |
| + if (model->GetV8Memory(index, &mem)) |
| + result->SetDouble(keys::kJsMemoryAllocated, |
| + static_cast<double>(mem)); |
| + |
| + if (model->GetV8MemoryUsed(index, &mem)) |
| + result->SetDouble(keys::kJsMemoryUsed, |
| + static_cast<double>(mem)); |
| + |
| + if (model->GetSqliteMemoryUsedBytes(index, &mem)) |
| + result->SetDouble(keys::kSqliteMemory, |
| + static_cast<double>(mem)); |
| + |
| + WebKit::WebCache::ResourceTypeStats cache_stats; |
| + if (model->GetWebCoreCacheStats(index, &cache_stats)) { |
| + result->Set(keys::kImageCache, |
| + CreateCacheData(cache_stats.images)); |
| + result->Set(keys::kScriptCache, |
| + CreateCacheData(cache_stats.scripts)); |
| + result->Set(keys::kCssCache, |
| + 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::kFPS, 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) |
| + |
| +// Borrowed from the tabs extension. Allows us to support both list and single |
| +// integer parameters as input from extensions. |
| +bool ReadOneOrMoreIntegers(Value* value, std::vector<int>* result) { |
| + if (value->IsType(Value::TYPE_INTEGER)) { |
| + int id = -1; |
| + if (!value->GetAsInteger(&id)) |
| + return false; |
| + result->push_back(id); |
| + return true; |
| + } else if (value->IsType(Value::TYPE_LIST)) { |
| + ListValue* ids = static_cast<ListValue*>(value); |
| + for (size_t i = 0; i < ids->GetSize(); ++i) { |
| + int id = -1; |
| + if (!ids->GetInteger(i, &id)) |
| + return false; |
| + result->push_back(id); |
| + } |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +} // 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) |
| } |
| @@ -66,97 +269,249 @@ void ExtensionProcessesEventRouter::ListenerAdded() { |
| #if defined(ENABLE_TASK_MANAGER) |
| model_->StartUpdating(); |
|
Charlie Reis
2012/04/27 22:01:56
These calls to StartUpdating and StopUpdating look
nasko
2012/05/01 21:28:41
Done.
|
| #endif // defined(ENABLE_TASK_MANAGER) |
| + ++listeners_; |
| } |
| void ExtensionProcessesEventRouter::ListenerRemoved() { |
| + DCHECK(listeners_ > 0); |
| + --listeners_; |
| #if defined(ENABLE_TASK_MANAGER) |
| model_->StopUpdating(); |
| #endif // defined(ENABLE_TASK_MANAGER) |
| } |
| +int ExtensionProcessesEventRouter::listeners() { |
| + return listeners_; |
| +} |
| + |
| +bool ExtensionProcessesEventRouter::task_manager_listening() { |
| + return task_manager_listening_; |
| +} |
| + |
| +bool ExtensionProcessesEventRouter::StartTaskManagerListening() { |
| +#if defined(ENABLE_TASK_MANAGER) |
| + if (!task_manager_listening_) { |
| + model_->StartListening(); |
| + task_manager_listening_ = true; |
| + return true; |
| + } |
| + return false; |
| +#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(length == 1); |
|
Charlie Reis
2012/04/27 22:01:56
DCHECK_EQ(1, length)
nasko
2012/05/01 21:28:41
Done.
|
| + 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); |
|
Charlie Reis
2012/04/27 22:01:56
Here's a reason to pass in the policy of whether o
nasko
2012/05/01 21:28:41
Done.
|
| + DCHECK(process != NULL); |
| + |
| + if (process == NULL) |
| + return; |
| + |
| + args.Append(Value::CreateIntegerValue( |
| + model_->GetUniqueChildProcessId(start))); |
| + 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); |
| + 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); |
| - // 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); |
| + // 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) |
| +} |
| + |
| +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); |
|
Charlie Reis
2012/04/27 22:01:56
Ditto. We would want to pass in false here. (I s
nasko
2012/05/01 21:28:41
Done.
|
| + break; |
| + } |
| } |
| } |
| + |
| + DCHECK(process != NULL); |
|
Charlie Reis
2012/04/27 22:01:56
DCHECK(process)
nasko
2012/05/01 21:28:41
Done.
|
| + 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 +520,260 @@ 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 extension 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 || !router->HasEventListener(event_name)) |
| + continue; |
| + |
| + const ExtensionService* extension_service = profile->GetExtensionService(); |
| + const ExtensionSet* extensions = extension_service->extensions(); |
| + for (ExtensionSet::const_iterator it = extensions->begin(); |
| + it != extensions->end(); ++it) { |
| + std::string extension_id = (*it)->id(); |
| + if (router->ExtensionHasEventListener(extension_id, event_name)) |
|
Charlie Reis
2012/04/27 22:01:56
Why is this part necessary if router->HasEventList
nasko
2012/05/01 21:28:41
Done.
|
| + 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(); |
|
Charlie Reis
2012/04/27 22:01:56
What happens if an extension calls this twice in a
nasko
2012/05/01 21:28:41
We can't really be no-op, we need to still call th
|
| + |
| + // 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()->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 |
|
Charlie Reis
2012/04/27 22:01:56
nit: End with period.
nasko
2012/05/01 21:28:41
Done.
nasko
2012/05/01 21:28:41
Done.
|
| + 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()->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); |
| + 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(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()->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); |
| + 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); |
| + 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(); |
| } |