Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1574)

Unified Diff: chrome/browser/extensions/extension_processes_api.cc

Issue 10175008: Improving the process model extension API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Improved task manager sample and minor cleanup. Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..c6bbef098bb2515cc77bcd44c10d1f50f35bbb68 100644
--- a/chrome/browser/extensions/extension_processes_api.cc
+++ b/chrome/browser/extensions/extension_processes_api.cc
@@ -7,53 +7,237 @@
#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_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 are not added by this function, the
Charlie Reis 2012/05/04 20:41:30 nit: "For memory details *which* are not added..."
nasko 2012/05/07 17:08:47 Done.
+// 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 +248,247 @@ 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)
}
+bool ExtensionProcessesEventRouter::DidStartTaskManagerListening() {
+#if defined(ENABLE_TASK_MANAGER)
+ if (!task_manager_listening_) {
+ model_->StartListening();
+ task_manager_listening_ = true;
+ return true;
+ }
+ return false;
+#endif // defined(ENABLE_TASK_MANAGER)
Charlie Reis 2012/05/04 20:41:30 This won't compile without a return if the Task Ma
nasko 2012/05/07 17:08:47 Done.
+}
+
+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(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, 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 +497,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()->
+ DidStartTaskManagerListening();
+ }
+
+ 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()->
+ DidStartTaskManagerListening();
}
- // 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(ExtensionTabUtil::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()->
+ DidStartTaskManagerListening();
+ }
+ 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();
}

Powered by Google App Engine
This is Rietveld 408576698