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 73f8cc68e546100cde3094dad8c098ad6784c90d..784f4c667823091557533ced4f74e7946837900a 100644 | 
| --- a/chrome/browser/extensions/extension_processes_api.cc | 
| +++ b/chrome/browser/extensions/extension_processes_api.cc | 
| @@ -15,42 +15,166 @@ | 
| #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" | 
| #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_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) { | 
| +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); | 
| +} | 
| + | 
| +DictionaryValue* CreateProcessFromModel(int process_id, | 
| + 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->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)); | 
| 
 
Charlie Reis
2012/04/23 22:19:51
nit: Should line up with open paren.
 
nasko
2012/04/24 18:14:29
Done.
 
 | 
| + 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)); | 
| +} | 
| + | 
| ExtensionProcessesEventRouter* ExtensionProcessesEventRouter::GetInstance() { | 
| return Singleton<ExtensionProcessesEventRouter>::get(); | 
| } | 
| -ExtensionProcessesEventRouter::ExtensionProcessesEventRouter() { | 
| +ExtensionProcessesEventRouter::ExtensionProcessesEventRouter() : listeners_(0) { | 
| model_ = TaskManager::GetInstance()->model(); | 
| model_->AddObserver(this); | 
| + model_->StartUpdating(); | 
| 
 
Charlie Reis
2012/04/23 22:19:51
Why is this needed without any listeners?
 
nasko
2012/04/24 18:14:29
The reason is that we can return CPU/Network stats
 
 | 
| + | 
| + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_HANG, | 
| + content::NotificationService::AllSources()); | 
| + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 
| + content::NotificationService::AllSources()); | 
| } | 
| ExtensionProcessesEventRouter::~ExtensionProcessesEventRouter() { | 
| + registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_HANG, | 
| + content::NotificationService::AllSources()); | 
| + registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 
| + content::NotificationService::AllSources()); | 
| + | 
| + model_->StopUpdating(); | 
| model_->RemoveObserver(this); | 
| } | 
| @@ -58,95 +182,231 @@ void ExtensionProcessesEventRouter::ObserveProfile(Profile* profile) { | 
| profiles_.insert(profile); | 
| } | 
| +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; | 
| +} | 
| + | 
| +bool ExtensionProcessesEventRouter::HasEventListeners(std::string& event_name) { | 
| 
 
Charlie Reis
2012/04/23 22:19:51
There's a fair amount going on here.  Maybe add a
 
nasko
2012/04/24 18:14:29
Added a comment and filed crbug.com/124804 to get
 
 | 
| + 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(); | 
| 
 
Charlie Reis
2012/04/23 22:19:51
FYI, this may be changing soon, so be aware of pot
 
nasko
2012/04/24 18:14:29
Done.
 
 | 
| + it != extensions->end(); ++it) { | 
| + std::string extension_id = (*it)->id(); | 
| + if (router->ExtensionHasEventListener(extension_id, event_name)) | 
| + return true; | 
| + } | 
| + } | 
| + return false; | 
| +} | 
| + | 
| void ExtensionProcessesEventRouter::ListenerAdded() { | 
| - model_->StartUpdating(); | 
| + ++listeners_; | 
| } | 
| void ExtensionProcessesEventRouter::ListenerRemoved() { | 
| - model_->StopUpdating(); | 
| + DCHECK(listeners_ > 0); | 
| + --listeners_; | 
| +} | 
| + | 
| +void ExtensionProcessesEventRouter::OnItemsAdded(int start, int length) { | 
| + DCHECK(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); | 
| + 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); | 
| +} | 
| + | 
| +void ExtensionProcessesEventRouter::OnItemsToBeRemoved(int start, int length) { | 
| + 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. | 
| 
 
Charlie Reis
2012/04/23 22:19:51
But we do use the Task Manager's ToBeRemoved for n
 
nasko
2012/04/24 18:14:29
Done.
 
 | 
| + 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. | 
| 
 
Charlie Reis
2012/04/23 22:19:51
Thanks for documenting these.  Is it not possible
 
nasko
2012/04/24 18:14:29
It is just a simplification, since we don't have t
 
 | 
| + args.Append(Value::CreateIntegerValue(0)); | 
| + | 
| + std::string json_args; | 
| + base::JSONWriter::Write(&args, &json_args); | 
| + NotifyProfiles(keys::kOnExited, json_args); | 
| } | 
| void ExtensionProcessesEventRouter::OnItemsChanged(int start, int length) { | 
| - 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 | 
| 
 
Charlie Reis
2012/04/23 22:19:51
nit: End with period, here and below.  (Yep, proba
 
nasko
2012/04/24 18:14:29
Done.
 
 | 
| + 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()); | 
| 
 
Charlie Reis
2012/04/23 22:19:51
Hmm.  I was thinking we could combine the two loop
 
nasko
2012/04/24 18:14:29
As we discussed verbally, the goal is to keep the
 
 | 
| + // 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::kOnUpdatedWithMemory, json_args); | 
| + } | 
| +} | 
| + | 
| +void ExtensionProcessesEventRouter::ProcessHangEvent( | 
| 
 
Charlie Reis
2012/04/23 22:19:51
Should we skip this if no one is listening, like O
 
nasko
2012/04/24 18:14:29
Good catch!
 
 | 
| + content::RenderWidgetHost* widget) { | 
| + DictionaryValue* process = NULL; | 
| + | 
| + int count = model_->ResourceCount(); | 
| + int id = widget->GetProcess()->GetID(); | 
| - // 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); | 
| + for (int i = 0; i < count; ++i) { | 
| + if (model_->IsResourceFirstInGroup(i)) { | 
| + if (id == model_->GetUniqueChildProcessId(i)) { | 
| + process = CreateProcessFromModel(id, model_, i); | 
| + break; | 
| + } | 
| } | 
| } | 
| + | 
| + DCHECK(process != NULL); | 
| + if (process == NULL) | 
| + return; | 
| + | 
| + ListValue args; | 
| + args.Append(process); | 
| + | 
| + std::string json_args; | 
| + base::JSONWriter::Write(&args, &json_args); | 
| + NotifyProfiles(keys::kOnUnresponsive, json_args); | 
| } | 
| -void ExtensionProcessesEventRouter::DispatchEvent(Profile* profile, | 
| +void ExtensionProcessesEventRouter::ProcessClosedEvent( | 
| + content::RenderProcessHost* rph, | 
| + content::RenderProcessHost::RendererClosedDetails* details) { | 
| + // 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); | 
| +} | 
| + | 
| +void ExtensionProcessesEventRouter::DispatchEvent( | 
| + Profile* profile, | 
| const char* event_name, | 
| const std::string& json_args) { | 
| if (profile && profile->GetExtensionEventRouter()) { | 
| @@ -155,6 +415,15 @@ 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); | 
| + } | 
| +} | 
| + | 
| bool GetProcessIdForTabFunction::RunImpl() { | 
| int tab_id; | 
| EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | 
| @@ -169,9 +438,123 @@ bool GetProcessIdForTabFunction::RunImpl() { | 
| return false; | 
| } | 
| - // Return the process ID of the tab as an integer. | 
| - int id = base::GetProcId(contents->web_contents()-> | 
| - GetRenderProcessHost()->GetHandle()); | 
| - result_.reset(Value::CreateIntegerValue(id)); | 
| + int process_id = base::GetProcId( | 
| + contents->web_contents()->GetRenderProcessHost()->GetHandle()); | 
| + | 
| + TaskManagerModel* model = TaskManager::GetInstance()->model(); | 
| + int count = model->ResourceCount(); | 
| + | 
| + for (int i = 0; i < count; ++i) { | 
| + if (model->IsResourceFirstInGroup(i)) { | 
| + if (process_id == model->GetProcessId(i)) { | 
| + // Return the unique ID of the process as an integer. | 
| + result_.reset(Value::CreateIntegerValue( | 
| + model->GetUniqueChildProcessId(i))); | 
| + return true; | 
| + } | 
| + } | 
| + } | 
| + | 
| + error_ = ExtensionErrorUtils::FormatErrorMessage(errors::kProcessNotFound, | 
| + base::IntToString(process_id)); | 
| + return false; | 
| +} | 
| + | 
| + | 
| +bool TerminateFunction::RunImpl() { | 
| + int process_id; | 
| + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id)); | 
| + | 
| + // Since we use the RenderProcessHost unique IDs to give to extensions, it is | 
| + // safe to just search for the corresponding RPH and kill its process. | 
| + // This will ensure we are not killing random non-Chrome processes. | 
| + content::RenderProcessHost* rph = content::RenderProcessHost::FromID( | 
| + process_id); | 
| + if (rph == NULL) { | 
| + error_ = ExtensionErrorUtils::FormatErrorMessage(errors::kProcessNotFound, | 
| + base::IntToString(process_id)); | 
| + return false; | 
| + } | 
| + | 
| + bool killed = false; | 
| + killed = base::KillProcess(rph->GetHandle(), | 
| + content::RESULT_CODE_KILLED, | 
| + true); | 
| 
 
Charlie Reis
2012/04/23 22:19:51
Given our recent experiences, I wonder if we want
 
nasko
2012/04/24 18:14:29
This is a good idea. I would suggest we add it in
 
Charlie Reis
2012/04/27 22:01:55
I found out that the timing of the histogram CL do
 
 | 
| + | 
| + result_.reset(Value::CreateBooleanValue(killed)); | 
| + return true; | 
| +} | 
| + | 
| +// Borrowed from the tabs extension. Allows us to support both list and single | 
| +// integer parameters as input from extensions. | 
| 
 
Charlie Reis
2012/04/23 22:19:51
Sounds useful.  Is there any shared place we can p
 
nasko
2012/04/24 18:14:29
Not quite sure. I will look around to see if there
 
Charlie Reis
2012/04/27 22:01:55
extension_tab_util.h, perhaps?
 
 | 
| +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; | 
| +} | 
| + | 
| +bool GetProcessInfoFunction::RunImpl() { | 
| + Value* args = NULL; | 
| + bool memory = false; | 
| + | 
| + EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &args)); | 
| + EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &memory)); | 
| + | 
| + std::vector<int> process_ids; | 
| + EXTENSION_FUNCTION_VALIDATE(ReadOneOrMoreIntegers(args, &process_ids)); | 
| + | 
| + 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); | 
| return true; | 
| } |