| Index: chrome/browser/extensions/api/processes/processes_api.cc
|
| diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
|
| index 4b6a14cd18ab34a3c245dfb4dc1cf003d8b1aa21..eb14d772db1850739cac4e59e161caa1c91b5af2 100644
|
| --- a/chrome/browser/extensions/api/processes/processes_api.cc
|
| +++ b/chrome/browser/extensions/api/processes/processes_api.cc
|
| @@ -4,475 +4,322 @@
|
|
|
| #include "chrome/browser/extensions/api/processes/processes_api.h"
|
|
|
| -#include <stddef.h>
|
| -#include <stdint.h>
|
| -#include <utility>
|
| -
|
| -#include "base/callback.h"
|
| -#include "base/json/json_writer.h"
|
| #include "base/lazy_instance.h"
|
| -#include "base/location.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/single_thread_task_runner.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| +#include "base/process/process.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| -#include "base/thread_task_runner_handle.h"
|
| -#include "base/values.h"
|
| -#include "chrome/browser/chrome_notification_types.h"
|
| -#include "chrome/browser/extensions/api/processes/processes_api_constants.h"
|
| #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
|
| -#include "chrome/browser/extensions/extension_service.h"
|
| #include "chrome/browser/extensions/extension_tab_util.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| -#include "chrome/browser/task_manager/resource_provider.h"
|
| -#include "chrome/browser/task_manager/task_manager.h"
|
| -#include "content/public/browser/browser_context.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 "chrome/browser/task_management/task_manager_interface.h"
|
| +#include "chrome/common/extensions/api/processes.h"
|
| +#include "content/public/browser/browser_child_process_host.h"
|
| +#include "content/public/browser/child_process_data.h"
|
| #include "content/public/browser/render_process_host.h"
|
| -#include "content/public/browser/render_view_host.h"
|
| -#include "content/public/browser/render_widget_host.h"
|
| -#include "content/public/browser/render_widget_host_iterator.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "content/public/common/result_codes.h"
|
| -#include "extensions/browser/event_router.h"
|
| -#include "extensions/browser/extension_function_registry.h"
|
| -#include "extensions/browser/extension_function_util.h"
|
| #include "extensions/common/error_utils.h"
|
| +#include "third_party/WebKit/public/web/WebCache.h"
|
|
|
| namespace extensions {
|
|
|
| -namespace keys = processes_api_constants;
|
| -namespace errors = processes_api_constants;
|
| +namespace errors {
|
| +const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *.";
|
| +const char kProcessNotFound[] = "Process not found: *.";
|
| +} // namespace errors
|
|
|
| namespace {
|
|
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| +base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
|
| + g_processes_api_factory = LAZY_INSTANCE_INITIALIZER;
|
|
|
| -base::DictionaryValue* CreateCacheData(
|
| - const blink::WebCache::ResourceTypeStat& stat) {
|
| +api::processes::ProcessType GetProcessType(
|
| + task_management::Task::Type task_type) {
|
| + switch (task_type) {
|
| + case task_management::Task::BROWSER:
|
| + return api::processes::PROCESS_TYPE_BROWSER;
|
|
|
| - base::DictionaryValue* cache = new base::DictionaryValue();
|
| - cache->SetDouble(keys::kCacheSize, static_cast<double>(stat.size));
|
| - cache->SetDouble(keys::kCacheLiveSize, static_cast<double>(stat.liveSize));
|
| - return cache;
|
| -}
|
| + case task_management::Task::RENDERER:
|
| + return api::processes::PROCESS_TYPE_RENDERER;
|
|
|
| -void SetProcessType(base::DictionaryValue* result,
|
| - TaskManagerModel* model,
|
| - int index) {
|
| - // Determine process type.
|
| - std::string type = keys::kProcessTypeOther;
|
| - task_manager::Resource::Type resource_type = model->GetResourceType(index);
|
| - switch (resource_type) {
|
| - case task_manager::Resource::BROWSER:
|
| - type = keys::kProcessTypeBrowser;
|
| - break;
|
| - case task_manager::Resource::RENDERER:
|
| - type = keys::kProcessTypeRenderer;
|
| - break;
|
| - case task_manager::Resource::EXTENSION:
|
| - type = keys::kProcessTypeExtension;
|
| - break;
|
| - case task_manager::Resource::NOTIFICATION:
|
| - type = keys::kProcessTypeNotification;
|
| - break;
|
| - case task_manager::Resource::PLUGIN:
|
| - type = keys::kProcessTypePlugin;
|
| - break;
|
| - case task_manager::Resource::WORKER:
|
| - type = keys::kProcessTypeWorker;
|
| - break;
|
| - case task_manager::Resource::NACL:
|
| - type = keys::kProcessTypeNacl;
|
| - break;
|
| - case task_manager::Resource::UTILITY:
|
| - type = keys::kProcessTypeUtility;
|
| - break;
|
| - case task_manager::Resource::GPU:
|
| - type = keys::kProcessTypeGPU;
|
| - break;
|
| - case task_manager::Resource::ZYGOTE:
|
| - case task_manager::Resource::SANDBOX_HELPER:
|
| - case task_manager::Resource::UNKNOWN:
|
| - type = keys::kProcessTypeOther;
|
| - break;
|
| - default:
|
| - NOTREACHED() << "Unknown resource type.";
|
| - }
|
| - result->SetString(keys::kTypeKey, type);
|
| -}
|
| + case task_management::Task::EXTENSION:
|
| + case task_management::Task::GUEST:
|
| + return api::processes::PROCESS_TYPE_EXTENSION;
|
|
|
| -base::ListValue* GetTabsForProcess(int process_id) {
|
| - base::ListValue* tabs_list = new base::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.
|
| - scoped_ptr<content::RenderWidgetHostIterator> widgets(
|
| - content::RenderWidgetHost::GetRenderWidgetHosts());
|
| - while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
|
| - if (widget->GetProcess()->GetID() != process_id)
|
| - continue;
|
| + case task_management::Task::PLUGIN:
|
| + return api::processes::PROCESS_TYPE_PLUGIN;
|
|
|
| - content::RenderViewHost* host = content::RenderViewHost::From(widget);
|
| - content::WebContents* contents =
|
| - content::WebContents::FromRenderViewHost(host);
|
| - if (contents) {
|
| - tab_id = ExtensionTabUtil::GetTabId(contents);
|
| - if (tab_id != -1)
|
| - tabs_list->Append(new base::FundamentalValue(tab_id));
|
| - }
|
| + case task_management::Task::WORKER:
|
| + return api::processes::PROCESS_TYPE_WORKER;
|
| +
|
| + case task_management::Task::NACL:
|
| + return api::processes::PROCESS_TYPE_NACL;
|
| +
|
| + case task_management::Task::UTILITY:
|
| + return api::processes::PROCESS_TYPE_UTILITY;
|
| +
|
| + case task_management::Task::GPU:
|
| + return api::processes::PROCESS_TYPE_GPU;
|
| +
|
| + case task_management::Task::UNKNOWN:
|
| + case task_management::Task::ARC:
|
| + case task_management::Task::SANDBOX_HELPER:
|
| + case task_management::Task::ZYGOTE:
|
| + return api::processes::PROCESS_TYPE_OTHER;
|
| +
|
| + default:
|
| + NOTREACHED() << "Unknown task type.";
|
| + return api::processes::PROCESS_TYPE_OTHER;
|
| }
|
| +}
|
|
|
| - return tabs_list;
|
| +scoped_ptr<api::processes::Cache> CreateCacheData(
|
| + const blink::WebCache::ResourceTypeStat& stat) {
|
| + scoped_ptr<api::processes::Cache> cache(new api::processes::Cache);
|
| + cache->size = static_cast<double>(stat.size);
|
| + cache->live_size = static_cast<double>(stat.liveSize);
|
| + return cache;
|
| }
|
|
|
| -// 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.
|
| -base::DictionaryValue* CreateProcessFromModel(int process_id,
|
| - TaskManagerModel* model,
|
| - int index,
|
| - bool include_optional) {
|
| - base::DictionaryValue* result = new base::DictionaryValue();
|
| - size_t mem;
|
| -
|
| - result->SetInteger(keys::kIdKey, process_id);
|
| - result->SetInteger(keys::kOsProcessIdKey, model->GetProcessId(index));
|
| - SetProcessType(result, model, index);
|
| - result->SetString(keys::kTitleKey, model->GetResourceTitle(index));
|
| - result->SetString(keys::kProfileKey,
|
| - model->GetResourceProfileName(index));
|
| - result->SetInteger(keys::kNaClDebugPortKey,
|
| - model->GetNaClDebugStubPort(index));
|
| -
|
| - result->Set(keys::kTabsListKey, GetTabsForProcess(process_id));
|
| +// Fills |out_process| with the data of the process in which the task with |id|
|
| +// is running. If |include_optional| is true, this function will fill the
|
| +// optional fields in |api::processes::Process| except for |private_memory|,
|
| +// which should be filled later if needed.
|
| +void FillProcessData(
|
| + api::processes::Process* out_process,
|
| + task_management::TaskId id,
|
| + task_management::TaskManagerInterface* task_manager,
|
| + bool include_optional) {
|
| + out_process->id = task_manager->GetChildProcessUniqueID(id);
|
| + out_process->os_process_id = task_manager->GetProcessId(id);
|
| + out_process->type = GetProcessType(task_manager->GetType(id));
|
| + out_process->profile = base::UTF16ToUTF8(task_manager->GetProfileName(id));
|
| + out_process->nacl_debug_port = task_manager->GetNaClDebugStubPort(id);
|
| +
|
| + // Collect the tab IDs of all the tasks sharing this renderer if any.
|
| + const task_management::TaskIdList tasks_on_process =
|
| + task_manager->GetIdsOfTasksSharingSameProcess(id);
|
| + for (const auto& task_id : tasks_on_process) {
|
| + linked_ptr<api::processes::TaskInfo> task_info(
|
| + new api::processes::TaskInfo);
|
| + task_info->title = base::UTF16ToUTF8(task_manager->GetTitle(task_id));
|
| + const int tab_id = task_manager->GetTabId(task_id);
|
| + if (tab_id != -1)
|
| + task_info->tab_id.reset(new int(tab_id));
|
| +
|
| + out_process->tasks.push_back(task_info);
|
| + }
|
|
|
| // If we don't need to include the optional properties, just return now.
|
| if (!include_optional)
|
| - return result;
|
| + return;
|
|
|
| - result->SetDouble(keys::kCpuKey, model->GetCPUUsage(index));
|
| + out_process->cpu.reset(new double(task_manager->GetCpuUsage(id)));
|
|
|
| - if (model->GetV8Memory(index, &mem))
|
| - result->SetDouble(keys::kJsMemoryAllocatedKey,
|
| - static_cast<double>(mem));
|
| + out_process->network.reset(new double(static_cast<double>(
|
| + task_manager->GetProcessTotalNetworkUsage(id))));
|
|
|
| - if (model->GetV8MemoryUsed(index, &mem))
|
| - result->SetDouble(keys::kJsMemoryUsedKey,
|
| - static_cast<double>(mem));
|
| + int64_t v8_allocated;
|
| + int64_t v8_used;
|
| + if (task_manager->GetV8Memory(id, &v8_allocated, & v8_used)) {
|
| + out_process->js_memory_allocated.reset(new double(static_cast<double>(
|
| + v8_allocated)));
|
| + out_process->js_memory_used.reset(new double(static_cast<double>(v8_used)));
|
| + }
|
|
|
| - if (model->GetSqliteMemoryUsedBytes(index, &mem))
|
| - result->SetDouble(keys::kSqliteMemoryKey,
|
| - static_cast<double>(mem));
|
| + const int64_t sqlite_bytes = task_manager->GetSqliteMemoryUsed(id);
|
| + if (sqlite_bytes != -1) {
|
| + out_process->sqlite_memory.reset(new double(static_cast<double>(
|
| + sqlite_bytes)));
|
| + }
|
|
|
| blink::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));
|
| + if (task_manager->GetWebCacheStats(id, &cache_stats)) {
|
| + out_process->image_cache = CreateCacheData(cache_stats.images);
|
| + out_process->script_cache = CreateCacheData(cache_stats.scripts);
|
| + out_process->css_cache = CreateCacheData(cache_stats.cssStyleSheets);
|
| }
|
| -
|
| - // Network is reported by the TaskManager per resource (tab), not per
|
| - // process, therefore we need to iterate through the group of resources
|
| - // and aggregate the data.
|
| - int64_t net = 0;
|
| - int length = model->GetGroupRangeForResource(index).second;
|
| - for (int i = 0; i < length; ++i)
|
| - net += model->GetNetworkUsage(index + i);
|
| - result->SetDouble(keys::kNetworkKey, static_cast<double>(net));
|
| -
|
| - 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(base::DictionaryValue* result,
|
| - TaskManagerModel* model,
|
| - int index) {
|
| - size_t mem;
|
| - int64_t pr_mem =
|
| - model->GetPrivateMemory(index, &mem) ? static_cast<int64_t>(mem) : -1;
|
| - result->SetDouble(keys::kPrivateMemoryKey, static_cast<double>(pr_mem));
|
| +int64_t GetRefreshTypesFlagOnlyEssentialData() {
|
| + // This is the only non-optional data in the Process as defined by the API in
|
| + // processes.idl.
|
| + return task_management::REFRESH_TYPE_NACL;
|
| }
|
|
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| +// This does not include memory. The memory refresh flag will only be added once
|
| +// a listener to OnUpdatedWithMemory event is added.
|
| +int64_t GetRefreshTypesFlagIncludeOptionalData() {
|
| + return GetRefreshTypesFlagOnlyEssentialData() |
|
| + task_management::REFRESH_TYPE_CPU |
|
| + task_management::REFRESH_TYPE_NETWORK_USAGE |
|
| + task_management::REFRESH_TYPE_SQLITE_MEMORY |
|
| + task_management::REFRESH_TYPE_V8_MEMORY |
|
| + task_management::REFRESH_TYPE_WEBCACHE_STATS;
|
| +}
|
|
|
| } // namespace
|
|
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesEventRouter:
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
|
| - : browser_context_(context), listeners_(0), task_manager_listening_(false) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - model_ = TaskManager::GetInstance()->model();
|
| - model_->AddObserver(this);
|
| -
|
| - registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
|
| - content::NotificationService::AllSources());
|
| - registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
|
| - content::NotificationService::AllSources());
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + : task_management::TaskManagerObserver(
|
| + base::TimeDelta::FromSeconds(1),
|
| + GetRefreshTypesFlagIncludeOptionalData()),
|
| + browser_context_(context),
|
| + listeners_(0) {
|
| }
|
|
|
| ProcessesEventRouter::~ProcessesEventRouter() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - registrar_.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_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)
|
| + if (observed_task_manager()) {
|
| + task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(
|
| + this);
|
| + }
|
| }
|
|
|
| void ProcessesEventRouter::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 ProcessesEventRouter::ListenerRemoved() {
|
| - DCHECK_GT(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)
|
| -}
|
| + // Do we need to update memory usage?
|
| + if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName))
|
| + AddRefreshType(task_management::REFRESH_TYPE_MEMORY);
|
|
|
| -void ProcessesEventRouter::StartTaskManagerListening() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - if (!task_manager_listening_) {
|
| - model_->StartListening();
|
| - task_manager_listening_ = true;
|
| + if (listeners_++ == 0) {
|
| + // The first listener to be added.
|
| + task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
|
| }
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| -void ProcessesEventRouter::Observe(
|
| - int type,
|
| - const content::NotificationSource& source,
|
| - const content::NotificationDetails& details) {
|
| -
|
| - switch (type) {
|
| - case content::NOTIFICATION_RENDER_WIDGET_HOST_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;
|
| +void ProcessesEventRouter::ListenerRemoved() {
|
| + // No more listeners for memory usage?
|
| + if (!HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName))
|
| + RemoveRefreshType(task_management::REFRESH_TYPE_MEMORY);
|
| +
|
| + if (--listeners_ <= 0) {
|
| + // Last listener to be removed.
|
| + task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(
|
| + this);
|
| }
|
| - return;
|
| }
|
|
|
| -void ProcessesEventRouter::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))
|
| +void ProcessesEventRouter::OnTaskAdded(task_management::TaskId id) {
|
| + if (!HasEventListeners(api::processes::OnCreated::kEventName))
|
| 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);
|
| - }
|
| -
|
| - scoped_ptr<base::ListValue> args(new base::ListValue());
|
| - base::DictionaryValue* process = CreateProcessFromModel(
|
| - model_->GetUniqueChildProcessId(index), model_, index, false);
|
| - DCHECK(process != NULL);
|
| -
|
| - if (process == NULL)
|
| + // Is this the first task to be created on the process and hence its creation
|
| + // corresponds to the creation of the process?
|
| + if (observed_task_manager()->GetNumberOfTasksOnSameProcess(id) != 1)
|
| return;
|
|
|
| - args->Append(process);
|
| -
|
| - DispatchEvent(events::PROCESSES_ON_CREATED, keys::kOnCreated,
|
| - std::move(args));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + api::processes::Process process;
|
| + FillProcessData(&process, id, observed_task_manager(), false);
|
| + DispatchEvent(events::PROCESSES_ON_CREATED,
|
| + api::processes::OnCreated::kEventName,
|
| + api::processes::OnCreated::Create(process));
|
| }
|
|
|
| -void ProcessesEventRouter::OnItemsChanged(int start, int length) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - // If we don't have any listeners, return immediately.
|
| - if (listeners_ == 0)
|
| +void ProcessesEventRouter::OnTaskToBeRemoved(task_management::TaskId id) {
|
| + if (!HasEventListeners(api::processes::OnExited::kEventName))
|
| return;
|
|
|
| - if (!model_)
|
| + // Is this the last task to be removed from the process, and hence its removal
|
| + // corresponds to the termination of the process?
|
| + if (observed_task_manager()->GetNumberOfTasksOnSameProcess(id) != 1)
|
| 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<base::DictionaryValue> processes_map;
|
| - for (int i = start; i < start + length; i++) {
|
| - if (model_->IsResourceFirstInGroup(i)) {
|
| - int id = model_->GetUniqueChildProcessId(i);
|
| - base::DictionaryValue* process = CreateProcessFromModel(id, model_, i,
|
| - true);
|
| - processes_map.AddWithID(process, i);
|
| - }
|
| - }
|
| + int exit_code = 0;
|
| + base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
|
| + observed_task_manager()->GetTerminationStatus(id, &status, &exit_code);
|
|
|
| - int id;
|
| - std::string idkey(keys::kIdKey);
|
| - base::DictionaryValue* processes = new base::DictionaryValue();
|
| -
|
| - if (updated) {
|
| - IDMap<base::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());
|
| - }
|
| -
|
| - scoped_ptr<base::ListValue> args(new base::ListValue());
|
| - args->Append(processes);
|
| - DispatchEvent(events::PROCESSES_ON_UPDATED, keys::kOnUpdated,
|
| - std::move(args));
|
| - }
|
| -
|
| - if (updated_memory) {
|
| - IDMap<base::DictionaryValue>::iterator it(&processes_map);
|
| - for (; !it.IsAtEnd(); it.Advance()) {
|
| - if (!it.GetCurrentValue()->GetInteger(idkey, &id))
|
| - continue;
|
| + DispatchEvent(events::PROCESSES_ON_EXITED,
|
| + api::processes::OnExited::kEventName,
|
| + api::processes::OnExited::Create(
|
| + observed_task_manager()->GetChildProcessUniqueID(id),
|
| + status,
|
| + exit_code));
|
|
|
| - 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());
|
| - }
|
| -
|
| - scoped_ptr<base::ListValue> args(new base::ListValue());
|
| - args->Append(processes);
|
| - DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY,
|
| - keys::kOnUpdatedWithMemory, std::move(args));
|
| - }
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| -void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - DCHECK_EQ(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) == task_manager::Resource::RENDERER)
|
| - return;
|
| -
|
| - // The callback function parameters.
|
| - scoped_ptr<base::ListValue> args(new base::ListValue());
|
| +void ProcessesEventRouter::OnTasksRefreshed(
|
| + const task_management::TaskIdList& task_ids) {
|
| + const bool has_on_updated_listeners =
|
| + HasEventListeners(api::processes::OnUpdated::kEventName);
|
| + const bool has_on_updated_with_memory_listeners =
|
| + HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName);
|
|
|
| - // First arg: The id of the process that was closed.
|
| - args->Append(new base::FundamentalValue(
|
| - model_->GetUniqueChildProcessId(start)));
|
| -
|
| - // Second arg: The exit type for the process.
|
| - args->Append(new base::FundamentalValue(0));
|
| -
|
| - // Third arg: The exit code for the process.
|
| - args->Append(new base::FundamentalValue(0));
|
| -
|
| - DispatchEvent(events::PROCESSES_ON_EXITED, keys::kOnExited, std::move(args));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| -}
|
| -
|
| -void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - std::string event(keys::kOnUnresponsive);
|
| - if (!HasEventListeners(event))
|
| + if (!has_on_updated_listeners && !has_on_updated_with_memory_listeners)
|
| return;
|
|
|
| - base::DictionaryValue* process = NULL;
|
| - int count = model_->ResourceCount();
|
| - int id = widget->GetProcess()->GetID();
|
| + // Get the data of tasks sharing the same process only once.
|
| + std::set<base::ProcessId> seen_processes;
|
| + base::DictionaryValue processes_dictionary;
|
| + for (const auto& task_id : task_ids) {
|
| + // We are not interested in tasks, but rather the processes on which they
|
| + // run.
|
| + const base::ProcessId proc_id =
|
| + observed_task_manager()->GetProcessId(task_id);
|
| + if (seen_processes.count(proc_id))
|
| + continue;
|
|
|
| - for (int i = 0; i < count; ++i) {
|
| - if (model_->IsResourceFirstInGroup(i)) {
|
| - if (id == model_->GetUniqueChildProcessId(i)) {
|
| - process = CreateProcessFromModel(id, model_, i, false);
|
| - break;
|
| - }
|
| + seen_processes.insert(proc_id);
|
| + const int child_process_host_id =
|
| + observed_task_manager()->GetChildProcessUniqueID(task_id);
|
| + api::processes::Process process;
|
| + FillProcessData(&process, task_id, observed_task_manager(), true);
|
| +
|
| + if (has_on_updated_with_memory_listeners) {
|
| + // Append the private memory usage to the process data.
|
| + const int64_t private_memory =
|
| + observed_task_manager()->GetPrivateMemoryUsage(task_id);
|
| + process.private_memory.reset(new double(static_cast<double>(
|
| + private_memory)));
|
| }
|
| - }
|
|
|
| - if (process == NULL)
|
| - return;
|
| + // Store each process indexed by the string version of its ChildProcessHost
|
| + // ID.
|
| + processes_dictionary.Set(base::IntToString(child_process_host_id),
|
| + process.ToValue());
|
| + }
|
|
|
| - scoped_ptr<base::ListValue> args(new base::ListValue());
|
| - args->Append(process);
|
| + // Done with data collection. Now dispatch the appropriate events according to
|
| + // the present listeners.
|
| + DCHECK(has_on_updated_listeners || has_on_updated_with_memory_listeners);
|
| + if (has_on_updated_listeners) {
|
| + api::processes::OnUpdated::Processes processes;
|
| + processes.additional_properties.MergeDictionary(&processes_dictionary);
|
| + // NOTE: If there are listeners to the updates with memory as well,
|
| + // listeners to onUpdated (without memory) will also get the memory info
|
| + // of processes as an added bonus.
|
| + DispatchEvent(events::PROCESSES_ON_UPDATED,
|
| + api::processes::OnUpdated::kEventName,
|
| + api::processes::OnUpdated::Create(processes));
|
| + }
|
|
|
| - DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE, keys::kOnUnresponsive,
|
| - std::move(args));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + if (has_on_updated_with_memory_listeners) {
|
| + api::processes::OnUpdatedWithMemory::Processes processes;
|
| + processes.additional_properties.MergeDictionary(&processes_dictionary);
|
| + DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY,
|
| + api::processes::OnUpdatedWithMemory::kEventName,
|
| + api::processes::OnUpdatedWithMemory::Create(processes));
|
| + }
|
| }
|
|
|
| -void ProcessesEventRouter::ProcessClosedEvent(
|
| - content::RenderProcessHost* rph,
|
| - content::RenderProcessHost::RendererClosedDetails* details) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - // The callback function parameters.
|
| - scoped_ptr<base::ListValue> args(new base::ListValue());
|
| -
|
| - // First arg: The id of the process that was closed.
|
| - args->Append(new base::FundamentalValue(rph->GetID()));
|
| -
|
| - // Second arg: The exit type for the process.
|
| - args->Append(new base::FundamentalValue(details->status));
|
| -
|
| - // Third arg: The exit code for the process.
|
| - args->Append(new base::FundamentalValue(details->exit_code));
|
| +void ProcessesEventRouter::OnTaskUnresponsive(task_management::TaskId id) {
|
| + if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
|
| + return;
|
|
|
| - DispatchEvent(events::PROCESSES_ON_EXITED, keys::kOnExited, std::move(args));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + api::processes::Process process;
|
| + FillProcessData(&process, id, observed_task_manager(), false);
|
| + DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE,
|
| + api::processes::OnUnresponsive::kEventName,
|
| + api::processes::OnUnresponsive::Create(process));
|
| }
|
|
|
| void ProcessesEventRouter::DispatchEvent(
|
| events::HistogramValue histogram_value,
|
| const std::string& event_name,
|
| - scoped_ptr<base::ListValue> event_args) {
|
| + scoped_ptr<base::ListValue> event_args) const {
|
| EventRouter* event_router = EventRouter::Get(browser_context_);
|
| if (event_router) {
|
| scoped_ptr<Event> event(
|
| @@ -481,54 +328,53 @@ void ProcessesEventRouter::DispatchEvent(
|
| }
|
| }
|
|
|
| -bool ProcessesEventRouter::HasEventListeners(const std::string& event_name) {
|
| +bool ProcessesEventRouter::HasEventListeners(
|
| + const std::string& event_name) const {
|
| EventRouter* event_router = EventRouter::Get(browser_context_);
|
| return event_router && event_router->HasEventListener(event_name);
|
| }
|
|
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesAPI:
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
|
| : browser_context_(context) {
|
| EventRouter* event_router = EventRouter::Get(browser_context_);
|
| - event_router->RegisterObserver(this, processes_api_constants::kOnUpdated);
|
| + // Monitor when the following events are being listened to in order to know
|
| + // when to start the task manager.
|
| + event_router->RegisterObserver(this, api::processes::OnUpdated::kEventName);
|
| + event_router->RegisterObserver(
|
| + this, api::processes::OnUpdatedWithMemory::kEventName);
|
| + event_router->RegisterObserver(this, api::processes::OnCreated::kEventName);
|
| event_router->RegisterObserver(this,
|
| - processes_api_constants::kOnUpdatedWithMemory);
|
| - ExtensionFunctionRegistry* registry =
|
| - ExtensionFunctionRegistry::GetInstance();
|
| - registry->RegisterFunction<GetProcessIdForTabFunction>();
|
| - registry->RegisterFunction<TerminateFunction>();
|
| - registry->RegisterFunction<GetProcessInfoFunction>();
|
| + api::processes::OnUnresponsive::kEventName);
|
| + event_router->RegisterObserver(this, api::processes::OnExited::kEventName);
|
| }
|
|
|
| ProcessesAPI::~ProcessesAPI() {
|
| + // This object has already been unregistered as an observer in Shutdown().
|
| }
|
|
|
| -void ProcessesAPI::Shutdown() {
|
| - EventRouter::Get(browser_context_)->UnregisterObserver(this);
|
| +// static
|
| +ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
|
| + return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
|
| }
|
|
|
| -static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
|
| - g_factory = LAZY_INSTANCE_INITIALIZER;
|
| -
|
| // static
|
| BrowserContextKeyedAPIFactory<ProcessesAPI>*
|
| ProcessesAPI::GetFactoryInstance() {
|
| - return g_factory.Pointer();
|
| + return g_processes_api_factory.Pointer();
|
| }
|
|
|
| -// static
|
| -ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
|
| - return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
|
| -}
|
| -
|
| -ProcessesEventRouter* ProcessesAPI::processes_event_router() {
|
| - if (!processes_event_router_)
|
| - processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
|
| - return processes_event_router_.get();
|
| +void ProcessesAPI::Shutdown() {
|
| + EventRouter::Get(browser_context_)->UnregisterObserver(this);
|
| }
|
|
|
| void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
|
| - // We lazily tell the TaskManager to start updating when listeners to the
|
| - // processes.onUpdated or processes.onUpdatedWithMemory events arrive.
|
| + // The ProcessesEventRouter will observe the TaskManager as long as there are
|
| + // listeners for the processes.onUpdated or processes.onUpdatedWithMemory
|
| + // events.
|
| processes_event_router()->ListenerAdded();
|
| }
|
|
|
| @@ -539,247 +385,219 @@ void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
|
| processes_event_router()->ListenerRemoved();
|
| }
|
|
|
| -GetProcessIdForTabFunction::GetProcessIdForTabFunction() : tab_id_(-1) {
|
| +ProcessesEventRouter* ProcessesAPI::processes_event_router() {
|
| + if (!processes_event_router_.get())
|
| + processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
|
| + return processes_event_router_.get();
|
| }
|
|
|
| -bool GetProcessIdForTabFunction::RunAsync() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id_));
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesTerminateFunction:
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|
| - // Add a reference, which is balanced in GetProcessIdForTab to keep the object
|
| - // around and allow for the callback to be invoked.
|
| - AddRef();
|
| +ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
| - // 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 (ProcessesAPI::Get(GetProfile())
|
| - ->processes_event_router()
|
| - ->is_task_manager_listening()) {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab, this));
|
| - } else {
|
| - TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
|
| - base::Bind(&GetProcessIdForTabFunction::GetProcessIdForTab, this));
|
| -
|
| - ProcessesAPI::Get(GetProfile())
|
| - ->processes_event_router()
|
| - ->StartTaskManagerListening();
|
| + // For this function, the task manager doesn't even need to be running.
|
| + scoped_ptr<api::processes::Terminate::Params> params(
|
| + api::processes::Terminate::Params::Create(*args_));
|
| + EXTENSION_FUNCTION_VALIDATE(params.get());
|
| +
|
| + child_process_host_id_ = params->process_id;
|
| + if (child_process_host_id_ == 0) {
|
| + // Cannot kill the browser process.
|
| + return RespondNow(Error(errors::kNotAllowedToTerminate,
|
| + base::IntToString(child_process_host_id_)));
|
| }
|
|
|
| - return true;
|
| -#else
|
| - error_ = errors::kExtensionNotSupported;
|
| - return false;
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + content::BrowserThread::PostTaskAndReplyWithResult(
|
| + content::BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(&ProcessesTerminateFunction::GetProcessHandleIO,
|
| + this,
|
| + child_process_host_id_),
|
| + base::Bind(&ProcessesTerminateFunction::OnProcessHandleUI, this));
|
| +
|
| + // Promise to respond later.
|
| + return RespondLater();
|
| }
|
|
|
| -void GetProcessIdForTabFunction::GetProcessIdForTab() {
|
| - content::WebContents* contents = NULL;
|
| - int tab_index = -1;
|
| - if (!ExtensionTabUtil::GetTabById(tab_id_,
|
| - GetProfile(),
|
| - include_incognito(),
|
| - NULL,
|
| - NULL,
|
| - &contents,
|
| - &tab_index)) {
|
| - error_ = ErrorUtils::FormatErrorMessage(tabs_constants::kTabNotFoundError,
|
| - base::IntToString(tab_id_));
|
| - SetResult(new base::FundamentalValue(-1));
|
| - SendResponse(false);
|
| - } else {
|
| - int process_id = contents->GetRenderProcessHost()->GetID();
|
| - SetResult(new base::FundamentalValue(process_id));
|
| - SendResponse(true);
|
| - }
|
| +base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleIO(
|
| + int child_process_host_id) const {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
|
|
| - // Balance the AddRef in the RunAsync.
|
| - Release();
|
| -}
|
| + auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id);
|
| + if (host)
|
| + return host->GetData().handle;
|
|
|
| -TerminateFunction::TerminateFunction() : process_id_(-1) {
|
| + return base::kNullProcessHandle;
|
| }
|
|
|
| -bool TerminateFunction::RunAsync() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &process_id_));
|
| +void ProcessesTerminateFunction::OnProcessHandleUI(base::ProcessHandle handle) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
| - // Add a reference, which is balanced in TerminateProcess to keep the object
|
| - // around and allow for the callback to be invoked.
|
| - AddRef();
|
| + if (handle == base::kNullProcessHandle) {
|
| + // If we didn't get anything from BrowserChildProcessHost, then it could be
|
| + // a renderer.
|
| + auto* render_host =
|
| + content::RenderProcessHost::FromID(child_process_host_id_);
|
| + if (render_host)
|
| + handle = render_host->GetHandle();
|
| + }
|
|
|
| - // 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 (ProcessesAPI::Get(GetProfile())
|
| - ->processes_event_router()
|
| - ->is_task_manager_listening()) {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE, base::Bind(&TerminateFunction::TerminateProcess, this));
|
| - } else {
|
| - TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
|
| - base::Bind(&TerminateFunction::TerminateProcess, this));
|
| -
|
| - ProcessesAPI::Get(GetProfile())
|
| - ->processes_event_router()
|
| - ->StartTaskManagerListening();
|
| + if (handle == base::kNullProcessHandle) {
|
| + Respond(Error(errors::kProcessNotFound,
|
| + base::IntToString(child_process_host_id_)));
|
| + return;
|
| }
|
|
|
| - return true;
|
| -#else
|
| - error_ = errors::kExtensionNotSupported;
|
| - return false;
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + if (handle == base::GetCurrentProcessHandle()) {
|
| + // Cannot kill the browser process.
|
| + Respond(Error(errors::kNotAllowedToTerminate,
|
| + base::IntToString(child_process_host_id_)));
|
| + return;
|
| + }
|
| +
|
| + base::Process process = base::Process::Open(base::GetProcId(handle));
|
| + const bool did_terminate =
|
| + process.Terminate(content::RESULT_CODE_KILLED, true);
|
| + if (did_terminate)
|
| + UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
|
| +
|
| + Respond(ArgumentList(
|
| + api::processes::Terminate::Results::Create(did_terminate)));
|
| }
|
|
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesGetProcessIdForTabFunction:
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|
| -void TerminateFunction::TerminateProcess() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - TaskManagerModel* model = TaskManager::GetInstance()->model();
|
| +ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() {
|
| + // For this function, the task manager doesn't even need to be running.
|
| + scoped_ptr<api::processes::GetProcessIdForTab::Params> params(
|
| + api::processes::GetProcessIdForTab::Params::Create(*args_));
|
| + EXTENSION_FUNCTION_VALIDATE(params.get());
|
|
|
| - bool found = false;
|
| - for (int i = 0, count = model->ResourceCount(); i < count; ++i) {
|
| - if (!model->IsResourceFirstInGroup(i) ||
|
| - process_id_ != model->GetUniqueChildProcessId(i)) {
|
| - continue;
|
| - }
|
| - base::ProcessHandle process_handle = model->GetProcess(i);
|
| - if (process_handle == base::GetCurrentProcessHandle()) {
|
| - // Cannot kill the browser process.
|
| - // TODO(kalman): Are there other sensitive processes?
|
| - error_ = ErrorUtils::FormatErrorMessage(errors::kNotAllowedToTerminate,
|
| - base::IntToString(process_id_));
|
| - } else {
|
| - base::Process process =
|
| - base::Process::DeprecatedGetProcessFromHandle(process_handle);
|
| - bool did_terminate = process.Terminate(content::RESULT_CODE_KILLED, true);
|
| - if (did_terminate)
|
| - UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
|
| - SetResult(new base::FundamentalValue(did_terminate));
|
| - }
|
| - found = true;
|
| - break;
|
| - }
|
| - if (!found) {
|
| - error_ = ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
|
| - base::IntToString(process_id_));
|
| + const int tab_id = params->tab_id;
|
| + content::WebContents* contents = nullptr;
|
| + int tab_index = -1;
|
| + if (!ExtensionTabUtil::GetTabById(
|
| + tab_id,
|
| + Profile::FromBrowserContext(browser_context()),
|
| + include_incognito(),
|
| + nullptr,
|
| + nullptr,
|
| + &contents,
|
| + &tab_index)) {
|
| + return RespondNow(Error(tabs_constants::kTabNotFoundError,
|
| + base::IntToString(tab_id)));
|
| }
|
|
|
| - SendResponse(error_.empty());
|
| -
|
| - // Balance the AddRef in the RunAsync.
|
| - Release();
|
| -#else
|
| - error_ = errors::kExtensionNotSupported;
|
| - SendResponse(false);
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + const int process_id = contents->GetRenderProcessHost()->GetID();
|
| + return RespondNow(ArgumentList(
|
| + api::processes::GetProcessIdForTab::Results::Create(process_id)));
|
| }
|
|
|
| -GetProcessInfoFunction::GetProcessInfoFunction()
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - : memory_(false)
|
| -#endif
|
| - {
|
| -}
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesGetProcessInfoFunction:
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|
| -GetProcessInfoFunction::~GetProcessInfoFunction() {
|
| +ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction()
|
| + : task_management::TaskManagerObserver(
|
| + base::TimeDelta::FromSeconds(1),
|
| + GetRefreshTypesFlagOnlyEssentialData()) {
|
| }
|
|
|
| -bool GetProcessInfoFunction::RunAsync() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - base::Value* processes = NULL;
|
| +ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() {
|
| + scoped_ptr<api::processes::GetProcessInfo::Params> params(
|
| + api::processes::GetProcessInfo::Params::Create(*args_));
|
| + EXTENSION_FUNCTION_VALIDATE(params.get());
|
| + if (params->process_ids.as_integer)
|
| + process_host_ids_.push_back(*params->process_ids.as_integer);
|
| + else
|
| + process_host_ids_.swap(*params->process_ids.as_integers);
|
| + include_memory_ = params->include_memory;
|
|
|
| - 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.
|
| + // Keep this object alive until the OnTasksRefreshed() is received.
|
| 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 (ProcessesAPI::Get(GetProfile())
|
| - ->processes_event_router()
|
| - ->is_task_manager_listening()) {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&GetProcessInfoFunction::GatherProcessInfo, this));
|
| - } else {
|
| - TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
|
| - base::Bind(&GetProcessInfoFunction::GatherProcessInfo, this));
|
| -
|
| - ProcessesAPI::Get(GetProfile())
|
| - ->processes_event_router()
|
| - ->StartTaskManagerListening();
|
| - }
|
| - return true;
|
| + // The task manager needs to be enabled for this function.
|
| + // Start observing the task manager and wait for the next refresh event.
|
| + task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
|
|
|
| -#else
|
| - error_ = errors::kExtensionNotSupported;
|
| - return false;
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + return RespondLater();
|
| }
|
|
|
| -void GetProcessInfoFunction::GatherProcessInfo() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - TaskManagerModel* model = TaskManager::GetInstance()->model();
|
| - base::DictionaryValue* processes = new base::DictionaryValue();
|
| -
|
| +void ProcessesGetProcessInfoFunction::OnTasksRefreshed(
|
| + const task_management::TaskIdList& task_ids) {
|
| // 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);
|
| - base::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()) {
|
| - base::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;
|
| - }
|
| - }
|
| + const bool specific_processes_requested = !process_host_ids_.empty();
|
| + std::set<base::ProcessId> seen_processes;
|
| + // Create the results object as defined in the generated API from process.idl
|
| + // and fill it with the processes info.
|
| + api::processes::GetProcessInfo::Results::Processes processes;
|
| + for (const auto& task_id : task_ids) {
|
| + const base::ProcessId proc_id =
|
| + observed_task_manager()->GetProcessId(task_id);
|
| + if (seen_processes.count(proc_id))
|
| + continue;
|
| +
|
| + const int child_process_host_id =
|
| + observed_task_manager()->GetChildProcessUniqueID(task_id);
|
| +
|
| + if (specific_processes_requested) {
|
| + // Note: we can't use |!process_host_ids_.empty()| directly in the above
|
| + // condition as we will erase from |process_host_ids_| below.
|
| + auto itr = std::find(process_host_ids_.begin(),
|
| + process_host_ids_.end(),
|
| + child_process_host_id);
|
| + if (itr == process_host_ids_.end())
|
| + continue;
|
| +
|
| + // If found, we remove it from |process_host_ids|, so that at the end if
|
| + // anything remains in |process_host_ids|, those were invalid arguments
|
| + // that will be reported on the console.
|
| + process_host_ids_.erase(itr);
|
| }
|
| - // If not all processes were found, log them to the extension's console to
|
| - // help the developer, but don't fail the API call.
|
| - for (int pid : process_ids_) {
|
| - WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
|
| - ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
|
| - base::IntToString(pid)));
|
| +
|
| + seen_processes.insert(proc_id);
|
| +
|
| + // We do not include the optional data in this function results.
|
| + api::processes::Process process;
|
| + FillProcessData(&process, task_id, observed_task_manager(), false);
|
| +
|
| + if (include_memory_) {
|
| + // Append the private memory usage to the process data.
|
| + const int64_t private_memory =
|
| + observed_task_manager()->GetPrivateMemoryUsage(task_id);
|
| + process.private_memory.reset(new double(static_cast<double>(
|
| + private_memory)));
|
| }
|
| +
|
| + // Store each process indexed by the string version of its
|
| + // ChildProcessHost ID.
|
| + processes.additional_properties.Set(
|
| + base::IntToString(child_process_host_id),
|
| + process.ToValue());
|
| + }
|
| +
|
| + // Report the invalid host ids sent in the arguments.
|
| + for (const auto& host_id : process_host_ids_) {
|
| + WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
|
| + ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
|
| + base::IntToString(host_id)));
|
| }
|
|
|
| - SetResult(processes);
|
| - SendResponse(true);
|
| + // Send the response.
|
| + Respond(ArgumentList(
|
| + api::processes::GetProcessInfo::Results::Create(processes)));
|
|
|
| - // Balance the AddRef in the RunAsync.
|
| + // Stop observing the task manager, and balance the AddRef() in Run().
|
| + task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(this);
|
| Release();
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| +ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {}
|
| +
|
| } // namespace extensions
|
|
|