| 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 ae43fd23870720bc227e4e58662776163e1a1c94..77a49a02cd2156f3f3609bb2e454b70c714d1f97 100644
|
| --- a/chrome/browser/extensions/api/processes/processes_api.cc
|
| +++ b/chrome/browser/extensions/api/processes/processes_api.cc
|
| @@ -4,57 +4,57 @@
|
|
|
| #include "chrome/browser/extensions/api/processes/processes_api.h"
|
|
|
| -#include <stddef.h>
|
| #include <stdint.h>
|
|
|
| #include <algorithm>
|
| -#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/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 "chrome/browser/task_management/task_manager_interface.h"
|
| #include "chrome/common/extensions/api/processes.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 "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/child_process_host.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 errors {
|
| const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *.";
|
| const char kProcessNotFound[] = "Process not found: *.";
|
| -const char kInavlidArgument[] = "Invalid argument: *.";
|
| +const char kInvalidArgument[] = "Invalid argument: *.";
|
| } // namespace errors
|
|
|
| namespace {
|
|
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| +base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI>>
|
| + g_processes_api_factory = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +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;
|
| +}
|
| +
|
| +// This does not include memory. The memory refresh flag will only be added once
|
| +// a listener to OnUpdatedWithMemory event is added.
|
| +int64_t GetRefreshTypesForProcessOptionalData() {
|
| + return 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;
|
| +}
|
|
|
| scoped_ptr<api::processes::Cache> CreateCacheData(
|
| const blink::WebCache::ResourceTypeStat& stat) {
|
| @@ -64,279 +64,232 @@ scoped_ptr<api::processes::Cache> CreateCacheData(
|
| return cache;
|
| }
|
|
|
| -api::processes::ProcessType GetProcessType(TaskManagerModel* model,
|
| - int index) {
|
| - // Determine process type.
|
| - task_manager::Resource::Type resource_type = model->GetResourceType(index);
|
| - switch (resource_type) {
|
| - case task_manager::Resource::BROWSER:
|
| +api::processes::ProcessType GetProcessType(
|
| + task_management::Task::Type task_type) {
|
| + switch (task_type) {
|
| + case task_management::Task::BROWSER:
|
| return api::processes::PROCESS_TYPE_BROWSER;
|
|
|
| - case task_manager::Resource::RENDERER:
|
| + case task_management::Task::RENDERER:
|
| return api::processes::PROCESS_TYPE_RENDERER;
|
|
|
| - case task_manager::Resource::EXTENSION:
|
| - case task_manager::Resource::GUEST:
|
| + case task_management::Task::EXTENSION:
|
| + case task_management::Task::GUEST:
|
| return api::processes::PROCESS_TYPE_EXTENSION;
|
|
|
| - case task_manager::Resource::NOTIFICATION:
|
| - return api::processes::PROCESS_TYPE_NOTIFICATION;
|
| -
|
| - case task_manager::Resource::PLUGIN:
|
| + case task_management::Task::PLUGIN:
|
| return api::processes::PROCESS_TYPE_PLUGIN;
|
|
|
| - case task_manager::Resource::WORKER:
|
| + case task_management::Task::WORKER:
|
| return api::processes::PROCESS_TYPE_WORKER;
|
|
|
| - case task_manager::Resource::NACL:
|
| + case task_management::Task::NACL:
|
| return api::processes::PROCESS_TYPE_NACL;
|
|
|
| - case task_manager::Resource::UTILITY:
|
| + case task_management::Task::UTILITY:
|
| return api::processes::PROCESS_TYPE_UTILITY;
|
|
|
| - case task_manager::Resource::GPU:
|
| + case task_management::Task::GPU:
|
| return api::processes::PROCESS_TYPE_GPU;
|
|
|
| - case task_manager::Resource::ZYGOTE:
|
| - case task_manager::Resource::SANDBOX_HELPER:
|
| - case task_manager::Resource::UNKNOWN:
|
| + 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;
|
| }
|
|
|
| - NOTREACHED() << "Unknown resource type.";
|
| + NOTREACHED() << "Unknown task type.";
|
| return api::processes::PROCESS_TYPE_NONE;
|
| }
|
|
|
| -void FillTabsForProcess(int process_id, api::processes::Process* out_process) {
|
| +// 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(
|
| + task_management::TaskId id,
|
| + task_management::TaskManagerInterface* task_manager,
|
| + bool include_optional,
|
| + api::processes::Process* out_process) {
|
| DCHECK(out_process);
|
|
|
| - // 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)
|
| - return;
|
| -
|
| - 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;
|
| -
|
| - 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)
|
| - out_process->tabs.push_back(tab_id);
|
| - }
|
| + 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);
|
| }
|
| -}
|
| -
|
| -// This function fills |out_process| with the data of the process with
|
| -// |process_id|. For memory details, which are not added by this function,
|
| -// the callers need to use AddMemoryDetails.
|
| -void FillProcessData(int process_id,
|
| - TaskManagerModel* model,
|
| - int index,
|
| - bool include_optional,
|
| - api::processes::Process* out_process) {
|
| - DCHECK(out_process);
|
| -
|
| - out_process->id = process_id;
|
| - out_process->os_process_id = model->GetProcessId(index);
|
| - out_process->type = GetProcessType(model, index);
|
| - out_process->title = base::UTF16ToUTF8(model->GetResourceTitle(index));
|
| - out_process->profile =
|
| - base::UTF16ToUTF8(model->GetResourceProfileName(index));
|
| - out_process->nacl_debug_port = model->GetNaClDebugStubPort(index);
|
| -
|
| - FillTabsForProcess(process_id, out_process);
|
|
|
| // If we don't need to include the optional properties, just return now.
|
| if (!include_optional)
|
| return;
|
|
|
| - out_process->cpu.reset(new double(model->GetCPUUsage(index)));
|
| + out_process->cpu.reset(new double(task_manager->GetCpuUsage(id)));
|
| +
|
| + out_process->network.reset(new double(static_cast<double>(
|
| + task_manager->GetProcessTotalNetworkUsage(id))));
|
|
|
| - size_t mem = 0;
|
| - if (model->GetV8Memory(index, &mem)) {
|
| + int64_t v8_allocated = 0;
|
| + int64_t v8_used = 0;
|
| + if (task_manager->GetV8Memory(id, &v8_allocated, &v8_used)) {
|
| out_process->js_memory_allocated.reset(new double(static_cast<double>(
|
| - mem)));
|
| + v8_allocated)));
|
| + out_process->js_memory_used.reset(new double(static_cast<double>(v8_used)));
|
| }
|
|
|
| - if (model->GetV8MemoryUsed(index, &mem))
|
| - out_process->js_memory_used.reset(new double(static_cast<double>(mem)));
|
| -
|
| - if (model->GetSqliteMemoryUsedBytes(index, &mem))
|
| - out_process->sqlite_memory.reset(new double(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)) {
|
| + 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);
|
| - out_process->network.reset(new double(static_cast<double>(net)));
|
| -}
|
| -
|
| -// 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 |out_process|.
|
| -void AddMemoryDetails(TaskManagerModel* model,
|
| - int index,
|
| - api::processes::Process* out_process) {
|
| - DCHECK(out_process);
|
| -
|
| - size_t mem;
|
| - int64_t pr_mem =
|
| - model->GetPrivateMemory(index, &mem) ? static_cast<int64_t>(mem) : -1;
|
| - out_process->private_memory.reset(new double(static_cast<double>(pr_mem)));
|
| }
|
|
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| -
|
| } // namespace
|
|
|
| -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);
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesEventRouter:
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|
| - 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)
|
| +ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
|
| + : task_management::TaskManagerObserver(base::TimeDelta::FromSeconds(1),
|
| + task_management::REFRESH_TYPE_NONE),
|
| + 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)
|
| }
|
|
|
| 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_;
|
| + UpdateRefreshTypesFlagsBasedOnListeners();
|
| +
|
| + if (listeners_++ == 0) {
|
| + // The first listener to be added.
|
| + task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
|
| + }
|
| }
|
|
|
| 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)
|
| -}
|
| -
|
| -void ProcessesEventRouter::StartTaskManagerListening() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - if (!task_manager_listening_) {
|
| - model_->StartListening();
|
| - task_manager_listening_ = true;
|
| - }
|
| -#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;
|
| + UpdateRefreshTypesFlagsBasedOnListeners();
|
| +
|
| + 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);
|
| +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))
|
| - start = model_->GetGroupIndexForResource(start);
|
| + int child_process_host_id = 0;
|
| + if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
|
| + return;
|
|
|
| api::processes::Process process;
|
| - FillProcessData(model_->GetUniqueChildProcessId(start), model_, start,
|
| - false /* include_optional */, &process);
|
| + FillProcessData(id,
|
| + observed_task_manager(),
|
| + false, // include_optional
|
| + &process);
|
| DispatchEvent(events::PROCESSES_ON_CREATED,
|
| api::processes::OnCreated::kEventName,
|
| api::processes::OnCreated::Create(process));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| -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_)
|
| + int child_process_host_id = 0;
|
| + if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
|
| return;
|
|
|
| - // We need to know which type of onUpdated events to fire and whether to
|
| - // collect memory or not.
|
| - bool updated = HasEventListeners(api::processes::OnUpdated::kEventName);
|
| - bool updated_memory =
|
| + int exit_code = 0;
|
| + base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
|
| + observed_task_manager()->GetTerminationStatus(id, &status, &exit_code);
|
| +
|
| + DispatchEvent(events::PROCESSES_ON_EXITED,
|
| + api::processes::OnExited::kEventName,
|
| + api::processes::OnExited::Create(child_process_host_id,
|
| + status,
|
| + exit_code));
|
| +}
|
| +
|
| +void ProcessesEventRouter::OnTasksRefreshedWithBackgroundCalculations(
|
| + 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);
|
| - if (!updated && !updated_memory)
|
| +
|
| + if (!has_on_updated_listeners && !has_on_updated_with_memory_listeners)
|
| return;
|
|
|
| + // Get the data of tasks sharing the same process only once.
|
| + std::set<base::ProcessId> seen_processes;
|
| base::DictionaryValue processes_dictionary;
|
| - for (int i = start; i < start + length; ++i) {
|
| - if (model_->IsResourceFirstInGroup(i)) {
|
| - int id = model_->GetUniqueChildProcessId(i);
|
| - api::processes::Process process;
|
| - FillProcessData(id, model_, i, true /* include_optional */, &process);
|
| - if (updated_memory)
|
| - AddMemoryDetails(model_, i, &process);
|
| - processes_dictionary.Set(base::IntToString(id), process.ToValue());
|
| + 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;
|
| +
|
| + const int child_process_host_id =
|
| + observed_task_manager()->GetChildProcessUniqueId(task_id);
|
| + // Ignore tasks that don't have a valid child process host ID like ARC
|
| + // processes. We report the browser process info here though.
|
| + if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID)
|
| + continue;
|
| +
|
| + seen_processes.insert(proc_id);
|
| + api::processes::Process process;
|
| + FillProcessData(task_id,
|
| + observed_task_manager(),
|
| + true, // include_optional
|
| + &process);
|
| +
|
| + 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)));
|
| }
|
| +
|
| + // Store each process indexed by the string version of its ChildProcessHost
|
| + // ID.
|
| + processes_dictionary.Set(base::IntToString(child_process_host_id),
|
| + process.ToValue());
|
| }
|
|
|
| - if (updated) {
|
| + // 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,
|
| @@ -347,80 +300,33 @@ void ProcessesEventRouter::OnItemsChanged(int start, int length) {
|
| api::processes::OnUpdated::Create(processes));
|
| }
|
|
|
| - if (updated_memory) {
|
| + 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));}
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| -}
|
| -
|
| -void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - DCHECK_EQ(length, 1);
|
| -
|
| - if (!HasEventListeners(api::processes::OnExited::kEventName))
|
| - return;
|
| -
|
| - // 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;
|
| -
|
| - DispatchEvent(events::PROCESSES_ON_EXITED,
|
| - api::processes::OnExited::kEventName,
|
| - api::processes::OnExited::Create(
|
| - model_->GetUniqueChildProcessId(start),
|
| - 0 /* exit_type */,
|
| - 0 /* exit_code */));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| -}
|
| -
|
| -void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
|
| - return;
|
| -
|
| - 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)) {
|
| - api::processes::Process process;
|
| - FillProcessData(id, model_, i, false /* include_optional */, &process);
|
| - DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE,
|
| - api::processes::OnUnresponsive::kEventName,
|
| - api::processes::OnUnresponsive::Create(process));
|
| - return;
|
| - }
|
| - }
|
| + api::processes::OnUpdatedWithMemory::Create(processes));
|
| }
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| -void ProcessesEventRouter::ProcessClosedEvent(
|
| - content::RenderProcessHost* rph,
|
| - content::RenderProcessHost::RendererClosedDetails* details) {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - if (!HasEventListeners(api::processes::OnExited::kEventName))
|
| +void ProcessesEventRouter::OnTaskUnresponsive(task_management::TaskId id) {
|
| + if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
|
| return;
|
|
|
| - DispatchEvent(events::PROCESSES_ON_EXITED,
|
| - api::processes::OnExited::kEventName,
|
| - api::processes::OnExited::Create(rph->GetID(),
|
| - details->status,
|
| - details->exit_code));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + api::processes::Process process;
|
| + FillProcessData(id,
|
| + observed_task_manager(),
|
| + false, // include_optional
|
| + &process);
|
| + 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(
|
| @@ -429,11 +335,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);
|
| }
|
|
|
| +bool ProcessesEventRouter::ShouldReportOnCreatedOrOnExited(
|
| + task_management::TaskId id,
|
| + int* out_child_process_host_id) const {
|
| + // Is it the first task to be created or the last one to be removed?
|
| + if (observed_task_manager()->GetNumberOfTasksOnSameProcess(id) != 1)
|
| + return false;
|
| +
|
| + // Ignore tasks that don't have a valid child process host ID like ARC
|
| + // processes, as well as the browser process (neither onCreated() nor
|
| + // onExited() shouldn't report the browser process).
|
| + *out_child_process_host_id =
|
| + observed_task_manager()->GetChildProcessUniqueId(id);
|
| + if (*out_child_process_host_id ==
|
| + content::ChildProcessHost::kInvalidUniqueID ||
|
| + *out_child_process_host_id == 0) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void ProcessesEventRouter::UpdateRefreshTypesFlagsBasedOnListeners() {
|
| + int64_t refresh_types = task_management::REFRESH_TYPE_NONE;
|
| + if (HasEventListeners(api::processes::OnCreated::kEventName) ||
|
| + HasEventListeners(api::processes::OnUnresponsive::kEventName)) {
|
| + refresh_types |= GetRefreshTypesFlagOnlyEssentialData();
|
| + }
|
| +
|
| + if (HasEventListeners(api::processes::OnUpdated::kEventName))
|
| + refresh_types |= GetRefreshTypesForProcessOptionalData();
|
| +
|
| + if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName))
|
| + refresh_types |= task_management::REFRESH_TYPE_MEMORY;
|
| +
|
| + SetRefreshTypesFlags(refresh_types);
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ProcessesAPI:
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
|
| : browser_context_(context) {
|
| EventRouter* event_router = EventRouter::Get(browser_context_);
|
| @@ -449,19 +397,13 @@ ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
|
| }
|
|
|
| ProcessesAPI::~ProcessesAPI() {
|
| + // This object has already been unregistered as an observer in Shutdown().
|
| }
|
|
|
| -void ProcessesAPI::Shutdown() {
|
| - EventRouter::Get(browser_context_)->UnregisterObserver(this);
|
| -}
|
| -
|
| -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
|
| @@ -469,292 +411,280 @@ 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/.onUpdatedWithMemory/.onCreated ...
|
| + // etc. events.
|
| processes_event_router()->ListenerAdded();
|
| }
|
|
|
| void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
|
| - // If a processes.onUpdated or processes.onUpdatedWithMemory event listener
|
| - // is removed (or a process with one exits), then we let the extension API
|
| - // know that it has one fewer listener.
|
| + // If a processes.onUpdated/.onUpdatedWithMemory/.onCreated ... etc. event
|
| + // listener is removed (or a process with one exits), then we let the
|
| + // extension API know that it has one fewer listener.
|
| processes_event_router()->ListenerRemoved();
|
| }
|
|
|
| +ProcessesEventRouter* ProcessesAPI::processes_event_router() {
|
| + if (!processes_event_router_.get())
|
| + processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
|
| + return processes_event_router_.get();
|
| +}
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
| // ProcessesGetProcessIdForTabFunction:
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| -ProcessesGetProcessIdForTabFunction::ProcessesGetProcessIdForTabFunction()
|
| - : tab_id_(-1) {
|
| -}
|
| -
|
| ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| + // 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_));
|
| + api::processes::GetProcessIdForTab::Params::Create(*args_));
|
| EXTENSION_FUNCTION_VALIDATE(params.get());
|
| - tab_id_ = params->tab_id;
|
| -
|
| - if (tab_id_ < 0) {
|
| - return RespondNow(Error(errors::kInavlidArgument,
|
| - base::IntToString(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 (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
|
| - ->processes_event_router()
|
| - ->is_task_manager_listening()) {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&ProcessesGetProcessIdForTabFunction::GetProcessIdForTab,
|
| - this));
|
| - } else {
|
| - TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
|
| - base::Bind(&ProcessesGetProcessIdForTabFunction::GetProcessIdForTab,
|
| - this));
|
| -
|
| - ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
|
| - ->processes_event_router()
|
| - ->StartTaskManagerListening();
|
| - }
|
|
|
| - return RespondLater();
|
| -#else
|
| - return RespondNow(Error(errors::kExtensionNotSupported));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| -}
|
| -
|
| -void ProcessesGetProcessIdForTabFunction::GetProcessIdForTab() {
|
| - content::WebContents* contents = NULL;
|
| + const int tab_id = params->tab_id;
|
| + content::WebContents* contents = nullptr;
|
| int tab_index = -1;
|
| if (!ExtensionTabUtil::GetTabById(
|
| - tab_id_,
|
| + tab_id,
|
| Profile::FromBrowserContext(browser_context()),
|
| include_incognito(),
|
| nullptr,
|
| nullptr,
|
| &contents,
|
| &tab_index)) {
|
| - Respond(Error(tabs_constants::kTabNotFoundError,
|
| - base::IntToString(tab_id_)));
|
| - } else {
|
| - int process_id = contents->GetRenderProcessHost()->GetID();
|
| - Respond(ArgumentList(
|
| - api::processes::GetProcessIdForTab::Results::Create(process_id)));
|
| + return RespondNow(Error(tabs_constants::kTabNotFoundError,
|
| + base::IntToString(tab_id)));
|
| }
|
|
|
| - // Balance the AddRef in the Run.
|
| - Release();
|
| + const int process_id = contents->GetRenderProcessHost()->GetID();
|
| + return RespondNow(ArgumentList(
|
| + api::processes::GetProcessIdForTab::Results::Create(process_id)));
|
| }
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
| -// ProcessesTerminateFunction
|
| +// ProcessesTerminateFunction:
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| -ProcessesTerminateFunction::ProcessesTerminateFunction() : process_id_(-1) {
|
| -}
|
| -
|
| ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + // 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());
|
| - process_id_ = params->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 (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
|
| - ->processes_event_router()
|
| - ->is_task_manager_listening()) {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE, base::Bind(&ProcessesTerminateFunction::TerminateProcess,
|
| - this));
|
| - } else {
|
| - TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
|
| - base::Bind(&ProcessesTerminateFunction::TerminateProcess, this));
|
| -
|
| - ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
|
| - ->processes_event_router()
|
| - ->StartTaskManagerListening();
|
| + child_process_host_id_ = params->process_id;
|
| + if (child_process_host_id_ < 0) {
|
| + return RespondNow(Error(errors::kInvalidArgument,
|
| + base::IntToString(child_process_host_id_)));
|
| + } else if (child_process_host_id_ == 0) {
|
| + // Cannot kill the browser process.
|
| + return RespondNow(Error(errors::kNotAllowedToTerminate,
|
| + base::IntToString(child_process_host_id_)));
|
| }
|
|
|
| + // Check if it's a renderer.
|
| + auto* render_process_host =
|
| + content::RenderProcessHost::FromID(child_process_host_id_);
|
| + if (render_process_host)
|
| + return RespondNow(TerminateIfAllowed(render_process_host->GetHandle()));
|
| +
|
| + // This could be a non-renderer child process like a plugin or a nacl
|
| + // process. Try to get its handle from the BrowserChildProcessHost on the
|
| + // IO thread.
|
| + content::BrowserThread::PostTaskAndReplyWithResult(
|
| + content::BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(&ProcessesTerminateFunction::GetProcessHandleOnIO,
|
| + this,
|
| + child_process_host_id_),
|
| + base::Bind(&ProcessesTerminateFunction::OnProcessHandleOnUI, this));
|
| +
|
| + // Promise to respond later.
|
| return RespondLater();
|
| -#else
|
| - return RespondNow(Error(errors::kExtensionNotSupported));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| -}
|
| -
|
| -void ProcessesTerminateFunction::TerminateProcess() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - TaskManagerModel* model = TaskManager::GetInstance()->model();
|
| -
|
| - bool found = false;
|
| - for (int i = 0, count = model->ResourceCount(); i < count; ++i) {
|
| - if (model->IsResourceFirstInGroup(i) &&
|
| - process_id_ == model->GetUniqueChildProcessId(i)) {
|
| - base::ProcessHandle process_handle = model->GetProcess(i);
|
| - if (process_handle == base::GetCurrentProcessHandle()) {
|
| - // Cannot kill the browser process.
|
| - // TODO(kalman): Are there other sensitive processes?
|
| - Respond(Error(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);
|
| -
|
| - Respond(ArgumentList(
|
| - api::processes::Terminate::Results::Create(did_terminate)));
|
| - }
|
| - found = true;
|
| - break;
|
| - }
|
| +}
|
| +
|
| +base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO(
|
| + int child_process_host_id) const {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id);
|
| + if (host)
|
| + return host->GetData().handle;
|
| +
|
| + return base::kNullProcessHandle;
|
| +}
|
| +
|
| +void ProcessesTerminateFunction::OnProcessHandleOnUI(
|
| + base::ProcessHandle handle) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + Respond(TerminateIfAllowed(handle));
|
| +}
|
| +
|
| +ExtensionFunction::ResponseValue
|
| +ProcessesTerminateFunction::TerminateIfAllowed(base::ProcessHandle handle) {
|
| + if (handle == base::kNullProcessHandle) {
|
| + return Error(errors::kProcessNotFound,
|
| + base::IntToString(child_process_host_id_));
|
| }
|
|
|
| - if (!found)
|
| - Respond(Error(errors::kProcessNotFound, base::IntToString(process_id_)));
|
| + if (handle == base::GetCurrentProcessHandle()) {
|
| + // Cannot kill the browser process.
|
| + return Error(errors::kNotAllowedToTerminate,
|
| + base::IntToString(child_process_host_id_));
|
| + }
|
|
|
| - // Balance the AddRef in the Run.
|
| - Release();
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| + base::Process process = base::Process::Open(base::GetProcId(handle));
|
| + if (!process.IsValid()) {
|
| + return Error(errors::kProcessNotFound,
|
| + base::IntToString(child_process_host_id_));
|
| + }
|
| +
|
| + const bool did_terminate =
|
| + process.Terminate(content::RESULT_CODE_KILLED, true /* wait */);
|
| + if (did_terminate)
|
| + UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
|
| +
|
| + return ArgumentList(
|
| + api::processes::Terminate::Results::Create(did_terminate));
|
| }
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
| -// ProcessesGetProcessInfoFunction
|
| +// ProcessesGetProcessInfoFunction:
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction()
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - : memory_(false)
|
| -#endif
|
| - {
|
| + : task_management::TaskManagerObserver(
|
| + base::TimeDelta::FromSeconds(1),
|
| + GetRefreshTypesFlagOnlyEssentialData()) {
|
| }
|
|
|
| ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| 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_ids_.push_back(*params->process_ids.as_integer);
|
| + process_host_ids_.push_back(*params->process_ids.as_integer);
|
| else
|
| - process_ids_.swap(*params->process_ids.as_integers);
|
| + process_host_ids_.swap(*params->process_ids.as_integers);
|
|
|
| - memory_ = params->include_memory;
|
| + include_memory_ = params->include_memory;
|
| + if (include_memory_)
|
| + AddRefreshType(task_management::REFRESH_TYPE_MEMORY);
|
|
|
| - // 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 first of either OnTasksRefreshed() or
|
| + // OnTasksRefreshedWithBackgroundCalculations() is received depending on
|
| + // |include_memory_|.
|
| 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(Profile::FromBrowserContext(browser_context()))
|
| - ->processes_event_router()
|
| - ->is_task_manager_listening()) {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&ProcessesGetProcessInfoFunction::GatherProcessInfo, this));
|
| - } else {
|
| - TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
|
| - base::Bind(&ProcessesGetProcessInfoFunction::GatherProcessInfo, this));
|
| -
|
| - ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
|
| - ->processes_event_router()
|
| - ->StartTaskManagerListening();
|
| - }
|
| + // 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);
|
|
|
| return RespondLater();
|
| -#else
|
| - return RespondNow(Error(errors::kExtensionNotSupported));
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| -ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {
|
| +void ProcessesGetProcessInfoFunction::OnTasksRefreshed(
|
| + const task_management::TaskIdList& task_ids) {
|
| + // Memory is background calculated and will be ready when
|
| + // OnTasksRefreshedWithBackgroundCalculations() is invoked.
|
| + if (include_memory_)
|
| + return;
|
| +
|
| + GatherDataAndRespond(task_ids);
|
| }
|
|
|
| -void ProcessesGetProcessInfoFunction::GatherProcessInfo() {
|
| -#if defined(ENABLE_TASK_MANAGER)
|
| - TaskManagerModel* model = TaskManager::GetInstance()->model();
|
| - api::processes::GetProcessInfo::Results::Processes processes;
|
| +void
|
| +ProcessesGetProcessInfoFunction::OnTasksRefreshedWithBackgroundCalculations(
|
| + const task_management::TaskIdList& task_ids) {
|
| + if (!include_memory_)
|
| + return;
|
|
|
| + GatherDataAndRespond(task_ids);
|
| +}
|
| +
|
| +ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {}
|
| +
|
| +void ProcessesGetProcessInfoFunction::GatherDataAndRespond(
|
| + 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);
|
| - api::processes::Process process;
|
| - FillProcessData(id, model, i, false, &process);
|
| - if (memory_)
|
| - AddMemoryDetails(model, i, &process);
|
| - processes.additional_properties.Set(base::IntToString(id),
|
| - process.ToValue());
|
| - }
|
| - }
|
| - } 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()) {
|
| - api::processes::Process process;
|
| - FillProcessData(id, model, i, false, &process);
|
| - if (memory_)
|
| - AddMemoryDetails(model, i, &process);
|
| - processes.additional_properties.Set(base::IntToString(id),
|
| - process.ToValue());
|
| -
|
| - 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);
|
| + // Ignore tasks that don't have a valid child process host ID like ARC
|
| + // processes. We report the browser process info here though.
|
| + if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID)
|
| + continue;
|
| +
|
| + 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(task_id,
|
| + observed_task_manager(),
|
| + false, // include_optional
|
| + &process);
|
| +
|
| + 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)));
|
| }
|
|
|
| // Send the response.
|
| Respond(ArgumentList(
|
| api::processes::GetProcessInfo::Results::Create(processes)));
|
|
|
| - // Balance the AddRef in the Run.
|
| + // Stop observing the task manager, and balance the AddRef() in Run().
|
| + task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(this);
|
| Release();
|
| -#endif // defined(ENABLE_TASK_MANAGER)
|
| }
|
|
|
| } // namespace extensions
|
|
|