Chromium Code Reviews| 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..2f721b8c75f2c841afc52cf93e7801bbdfdd4507 100644 |
| --- a/chrome/browser/extensions/api/processes/processes_api.cc |
| +++ b/chrome/browser/extensions/api/processes/processes_api.cc |
| @@ -4,475 +4,332 @@ |
| #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: |
|
ncarter (slow)
2016/02/08 22:37:09
If you omit the 'default' here and add a default-r
afakhry
2016/02/09 04:01:23
Done.
|
| + 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, |
|
ncarter (slow)
2016/02/08 22:37:09
|out_process| ought to be the last parameter.-- ht
afakhry
2016/02/09 04:01:23
Thanks for the pointer. Placed as the last paramet
|
| + 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) { |
|
ncarter (slow)
2016/02/08 22:37:09
Why not ==
afakhry
2016/02/09 04:01:23
Done.
|
| + // 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); |
| + // Ignore tasks that don't have a child host ID that is greater than 0 (i.e. |
| + // browser process task, ARC tasks). |
| + if (observed_task_manager()->GetChildProcessUniqueId(id) <= 0) |
| + return; |
| - 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 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; |
| - |
| - 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) |
| + // Ignore tasks that don't have a child host ID that is greater than 0 (i.e. |
| + // browser process task, ARC tasks). |
| + if (observed_task_manager()->GetChildProcessUniqueId(id) <= 0) |
| return; |
| - // 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( |
| - model_->GetUniqueChildProcessId(start))); |
| - |
| - // Second arg: The exit type for the process. |
| - args->Append(new base::FundamentalValue(0)); |
| + int exit_code = 0; |
| + base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING; |
| + observed_task_manager()->GetTerminationStatus(id, &status, &exit_code); |
| - // Third arg: The exit code for the process. |
| - args->Append(new base::FundamentalValue(0)); |
| + DispatchEvent(events::PROCESSES_ON_EXITED, |
| + api::processes::OnExited::kEventName, |
| + api::processes::OnExited::Create( |
| + observed_task_manager()->GetChildProcessUniqueId(id), |
| + status, |
| + exit_code)); |
| - 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)) |
| +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); |
| + |
| + 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 +338,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 +395,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 |