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(); |
} |