Migrate ProcessesEventRouter to the new task manager
This CL migrates the dependency of the ProcessesAPIs to the new task manager
implementation.
The API of the process info has been fixed, since processes don't have titles, instead we added a list of tasks running in that process, with their titles and optional tab IDs.
BUG=525201,591581
TEST=browser_tests --gtest_filter=ProcessesApiTest.*
Review URL: https://codereview.chromium.org/1584473004
Cr-Commit-Position: refs/heads/master@{#380696}
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 7c93c82..a512418 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -85,6 +85,13 @@
"//url",
]
+ if (enable_task_manager) {
+ sources += rebase_path(
+ gypi_values.chrome_browser_extensions_task_manager_enabled_sources,
+ ".",
+ "//chrome")
+ }
+
if (is_chromeos) {
sources +=
rebase_path(gypi_values.chrome_browser_extensions_chromeos_sources,
diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
index ae43fd23..77a49a02 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 @@
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;
+ 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);
- 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;
+ // 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));
- 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->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)));
- size_t mem = 0;
- if (model->GetV8Memory(index, &mem)) {
+ out_process->network.reset(new double(static_cast<double>(
+ task_manager->GetProcessTotalNetworkUsage(id))));
+
+ 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)
-}
+ UpdateRefreshTypesFlagsBasedOnListeners();
-void ProcessesEventRouter::StartTaskManagerListening() {
-#if defined(ENABLE_TASK_MANAGER)
- if (!task_manager_listening_) {
- model_->StartListening();
- task_manager_listening_ = true;
+ if (--listeners_ == 0) {
+ // Last listener to be removed.
+ task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(
+ 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;
- }
- 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 @@
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)
+ api::processes::OnUpdatedWithMemory::Create(processes));
+ }
}
-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)
+void ProcessesEventRouter::OnTaskUnresponsive(task_management::TaskId id) {
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;
- }
- }
- }
-#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))
- 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 @@
}
}
-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() {
+ // 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 @@
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();
+base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO(
+ int child_process_host_id) const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- 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);
+ auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id);
+ if (host)
+ return host->GetData().handle;
- Respond(ArgumentList(
- api::processes::Terminate::Results::Create(did_terminate)));
- }
- found = true;
- break;
- }
+ 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());
+ 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;
- process_ids_.erase(proc_id);
- if (process_ids_.size() == 0)
- break;
- }
- }
+ 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
diff --git a/chrome/browser/extensions/api/processes/processes_api.h b/chrome/browser/extensions/api/processes/processes_api.h
index f1a2477..5ea54b0 100644
--- a/chrome/browser/extensions/api/processes/processes_api.h
+++ b/chrome/browser/extensions/api/processes/processes_api.h
@@ -5,35 +5,22 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_PROCESSES_PROCESSES_API_H__
#define CHROME_BROWSER_EXTENSIONS_API_PROCESSES_PROCESSES_API_H__
-#include <set>
-#include <string>
+#include <vector>
#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/extensions/chrome_extension_function.h"
-#include "chrome/browser/task_manager/task_manager.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_widget_host.h"
+#include "chrome/browser/task_management/task_manager_observer.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_event_histogram_value.h"
+#include "extensions/browser/extension_function.h"
-namespace base {
-class ListValue;
-}
-
-namespace content {
-class BrowserContext;
-}
+class ProcessesApiTest;
namespace extensions {
// Observes the Task Manager and routes the notifications as events to the
// extension system.
-class ProcessesEventRouter : public TaskManagerModelObserver,
- public content::NotificationObserver {
+class ProcessesEventRouter : public task_management::TaskManagerObserver {
public:
explicit ProcessesEventRouter(content::BrowserContext* context);
~ProcessesEventRouter() override;
@@ -44,58 +31,44 @@
// Called when an extension process with a listener exits or removes it.
void ListenerRemoved();
- // Called on the first invocation of extension API function. This will call
- // out to the Task Manager to start listening for notifications. Returns
- // true if this was the first call and false if this has already been called.
- void StartTaskManagerListening();
-
- bool is_task_manager_listening() { return task_manager_listening_; }
+ // task_management::TaskManagerObserver:
+ void OnTaskAdded(task_management::TaskId id) override;
+ void OnTaskToBeRemoved(task_management::TaskId id) override;
+ void OnTasksRefreshed(const task_management::TaskIdList& task_ids) override {}
+ void OnTasksRefreshedWithBackgroundCalculations(
+ const task_management::TaskIdList& task_ids) override;
+ void OnTaskUnresponsive(task_management::TaskId id) override;
private:
- // content::NotificationObserver implementation.
- void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) override;
-
- // TaskManagerModelObserver methods.
- void OnItemsAdded(int start, int length) override;
- void OnModelChanged() override {}
- void OnItemsChanged(int start, int length) override;
- void OnItemsRemoved(int start, int length) override {}
- void OnItemsToBeRemoved(int start, int length) override;
-
- // Internal helpers for processing notifications.
- void ProcessHangEvent(content::RenderWidgetHost* widget);
- void ProcessClosedEvent(
- content::RenderProcessHost* rph,
- content::RenderProcessHost::RendererClosedDetails* details);
+ friend class ::ProcessesApiTest;
void DispatchEvent(events::HistogramValue histogram_value,
const std::string& event_name,
- scoped_ptr<base::ListValue> event_args);
+ scoped_ptr<base::ListValue> event_args) const;
// Determines whether there is a registered listener for the specified event.
- // It helps to avoid collecing data if no one is interested in it.
- bool HasEventListeners(const std::string& event_name);
+ // It helps to avoid collecting data if no one is interested in it.
+ bool HasEventListeners(const std::string& event_name) const;
- // Used for tracking registrations to process related notifications.
- content::NotificationRegistrar registrar_;
+ // Returns true if the task with the given |id| should be reported as created
+ // or removed. |out_child_process_host_id| will be filled with the valid ID of
+ // the process to report in the event.
+ bool ShouldReportOnCreatedOrOnExited(task_management::TaskId id,
+ int* out_child_process_host_id) const;
+
+ // Updates the requested task manager refresh types flags depending on what
+ // events are being listened to by extensions.
+ void UpdateRefreshTypesFlagsBasedOnListeners();
content::BrowserContext* browser_context_;
- // TaskManager to observe for updates.
- TaskManagerModel* model_;
-
// Count of listeners, so we avoid sending updates if no one is interested.
int listeners_;
- // Indicator whether we've initialized the Task Manager listeners. This is
- // done once for the lifetime of this object.
- bool task_manager_listening_;
-
DISALLOW_COPY_AND_ASSIGN(ProcessesEventRouter);
};
+////////////////////////////////////////////////////////////////////////////////
// The profile-keyed service that manages the processes extension API.
class ProcessesAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer {
@@ -103,35 +76,35 @@
explicit ProcessesAPI(content::BrowserContext* context);
~ProcessesAPI() override;
- // KeyedService implementation.
- void Shutdown() override;
-
- // BrowserContextKeyedAPI implementation.
+ // BrowserContextKeyedAPI:
static BrowserContextKeyedAPIFactory<ProcessesAPI>* GetFactoryInstance();
// Convenience method to get the ProcessesAPI for a profile.
static ProcessesAPI* Get(content::BrowserContext* context);
- ProcessesEventRouter* processes_event_router();
+ // KeyedService:
+ void Shutdown() override;
- // EventRouter::Observer implementation.
+ // EventRouter::Observer:
void OnListenerAdded(const EventListenerInfo& details) override;
void OnListenerRemoved(const EventListenerInfo& details) override;
+ ProcessesEventRouter* processes_event_router();
+
private:
friend class BrowserContextKeyedAPIFactory<ProcessesAPI>;
- content::BrowserContext* browser_context_;
-
- // BrowserContextKeyedAPI implementation.
- static const char* service_name() {
- return "ProcessesAPI";
- }
+ // BrowserContextKeyedAPI:
+ static const char* service_name() { return "ProcessesAPI"; }
static const bool kServiceRedirectedInIncognito = true;
static const bool kServiceIsNULLWhileTesting = true;
+ content::BrowserContext* browser_context_;
+
// Created lazily on first access.
scoped_ptr<ProcessesEventRouter> processes_event_router_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessesAPI);
};
////////////////////////////////////////////////////////////////////////////////
@@ -139,21 +112,14 @@
// currently in use by the specified Tab.
class ProcessesGetProcessIdForTabFunction : public UIThreadExtensionFunction {
public:
- ProcessesGetProcessIdForTabFunction();
-
// UIThreadExtensionFunction:
ExtensionFunction::ResponseAction Run() override;
+ DECLARE_EXTENSION_FUNCTION("processes.getProcessIdForTab",
+ PROCESSES_GETPROCESSIDFORTAB);
+
private:
~ProcessesGetProcessIdForTabFunction() override {}
-
- void GetProcessIdForTab();
-
- // Storage for the tab ID parameter.
- int tab_id_;
-
- DECLARE_EXTENSION_FUNCTION("processes.getProcessIdForTab",
- PROCESSES_GETPROCESSIDFORTAB)
};
////////////////////////////////////////////////////////////////////////////////
@@ -164,46 +130,65 @@
// * guards against killing non-Chrome processes.
class ProcessesTerminateFunction : public UIThreadExtensionFunction {
public:
- ProcessesTerminateFunction();
-
// UIThreadExtensionFunction:
ExtensionFunction::ResponseAction Run() override;
+ DECLARE_EXTENSION_FUNCTION("processes.terminate", PROCESSES_TERMINATE);
+
private:
~ProcessesTerminateFunction() override {}
- void TerminateProcess();
+ // Functions to get the process handle on the IO thread and post it back to
+ // the UI thread from processing.
+ base::ProcessHandle GetProcessHandleOnIO(int child_process_host_id) const;
+ void OnProcessHandleOnUI(base::ProcessHandle handle);
- // Storage for the process ID parameter.
- int process_id_;
+ // Terminates the process with |handle| if it's valid and is allowed to be
+ // terminated. Returns the response value of this extension function to be
+ // sent.
+ ExtensionFunction::ResponseValue TerminateIfAllowed(
+ base::ProcessHandle handle);
- DECLARE_EXTENSION_FUNCTION("processes.terminate",
- PROCESSES_TERMINATE)
+ // Caches the parameter of this function. To be accessed only on the UI
+ // thread.
+ int child_process_host_id_ = 0;
};
////////////////////////////////////////////////////////////////////////////////
// Extension function which returns a set of Process objects, containing the
// details corresponding to the process IDs supplied as input.
-class ProcessesGetProcessInfoFunction : public UIThreadExtensionFunction {
+class ProcessesGetProcessInfoFunction :
+ public UIThreadExtensionFunction,
+ public task_management::TaskManagerObserver {
public:
ProcessesGetProcessInfoFunction();
// UIThreadExtensionFunction:
ExtensionFunction::ResponseAction Run() override;
+ // task_management::TaskManagerObserver:
+ void OnTaskAdded(task_management::TaskId id) override {}
+ void OnTaskToBeRemoved(task_management::TaskId id) override {}
+ void OnTasksRefreshed(const task_management::TaskIdList& task_ids) override;
+ void OnTasksRefreshedWithBackgroundCalculations(
+ const task_management::TaskIdList& task_ids) override;
+
+ DECLARE_EXTENSION_FUNCTION("processes.getProcessInfo",
+ PROCESSES_GETPROCESSINFO);
+
private:
~ProcessesGetProcessInfoFunction() override;
- void GatherProcessInfo();
+ // Since we don't report optional process data like CPU usage in the results
+ // of this function, the only background calculations we want to watch is
+ // memory usage (which will be requested only when |include_memory_| is true).
+ // This function will be called by either OnTasksRefreshed() or
+ // OnTasksRefreshedWithBackgroundCalculations() depending on whether memory is
+ // requested.
+ void GatherDataAndRespond(const task_management::TaskIdList& task_ids);
- // Member variables to store the function parameters
- std::vector<int> process_ids_;
-#if defined(ENABLE_TASK_MANAGER)
- bool memory_;
-#endif
-
- DECLARE_EXTENSION_FUNCTION("processes.getProcessInfo",
- PROCESSES_GETPROCESSINFO)
+ std::vector<int> process_host_ids_;
+ bool include_memory_ = false;
};
} // namespace extensions
diff --git a/chrome/browser/extensions/api/processes/processes_apitest.cc b/chrome/browser/extensions/api/processes/processes_apitest.cc
index caf3dcc..23d95dc 100644
--- a/chrome/browser/extensions/api/processes/processes_apitest.cc
+++ b/chrome/browser/extensions/api/processes/processes_apitest.cc
@@ -3,51 +3,68 @@
// found in the LICENSE file.
#include "base/command_line.h"
+#include "chrome/browser/extensions/api/processes/processes_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/task_manager/task_manager.h"
-#include "chrome/browser/task_manager/task_manager_browsertest_util.h"
+#include "chrome/browser/task_management/task_manager_interface.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_window.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
-using ProcessesApiTest = ExtensionApiTest;
+class ProcessesApiTest : public ExtensionApiTest {
+ public:
+ ProcessesApiTest() {}
+ ~ProcessesApiTest() override {}
-// Fails on some MSan bots crbug.com/591581.
-IN_PROC_BROWSER_TEST_F(ProcessesApiTest, DISABLED_Processes) {
+ int GetListenersCount() {
+ return extensions::ProcessesAPI::Get(profile())->
+ processes_event_router()->listeners_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProcessesApiTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ProcessesApiTest, Processes) {
ASSERT_TRUE(RunExtensionTest("processes/api")) << message_;
}
-IN_PROC_BROWSER_TEST_F(ProcessesApiTest, ProcessesVsTaskManager) {
- // This test is for the old implementation of the task manager. We must
- // explicitly disable the new one.
- task_manager::browsertest_util::EnableOldTaskManager();
+IN_PROC_BROWSER_TEST_F(ProcessesApiTest, ProcessesApiListeners) {
+ EXPECT_EQ(0, GetListenersCount());
- // Ensure task manager is not yet updating
- TaskManagerModel* model = TaskManager::GetInstance()->model();
- EXPECT_EQ(0, model->update_requests_);
- EXPECT_EQ(TaskManagerModel::IDLE, model->update_state_);
+ // Load extension that adds a listener in background page
+ ExtensionTestMessageListener listener1("ready", false /* will_reply */);
+ const extensions::Extension* extension1 = LoadExtension(
+ test_data_dir_.AppendASCII("processes").AppendASCII("onupdated"));
+ ASSERT_TRUE(extension1);
+ ASSERT_TRUE(listener1.WaitUntilSatisfied());
- // Load extension that adds listener in background page
- ExtensionTestMessageListener listener("ready", false);
- ASSERT_TRUE(LoadExtension(
- test_data_dir_.AppendASCII("processes").AppendASCII("onupdated")));
- ASSERT_TRUE(listener.WaitUntilSatisfied());
+ // The memory refresh type of the task manager may or may not be enabled by
+ // now depending on the presence of other task manager observers.
+ // Ensure the listeners count has changed.
+ EXPECT_EQ(1, GetListenersCount());
- // Ensure the task manager has started updating
- EXPECT_EQ(1, model->update_requests_);
- EXPECT_EQ(TaskManagerModel::TASK_PENDING, model->update_state_);
+ // Load another extension that listen to the onUpdatedWithMemory.
+ ExtensionTestMessageListener listener2("ready", false /* will_reply */);
+ const extensions::Extension* extension2 = LoadExtension(
+ test_data_dir_.AppendASCII("processes").AppendASCII(
+ "onupdated_with_memory"));
+ ASSERT_TRUE(extension2);
+ ASSERT_TRUE(listener2.WaitUntilSatisfied());
- // Now show the task manager and wait for it to be ready
- chrome::ShowTaskManager(browser());
+ // The memory refresh type must be enabled now.
+ const task_management::TaskManagerInterface* task_manager =
+ task_management::TaskManagerInterface::GetTaskManager();
+ EXPECT_EQ(2, GetListenersCount());
+ EXPECT_TRUE(task_manager->IsResourceRefreshEnabled(
+ task_management::REFRESH_TYPE_MEMORY));
- EXPECT_EQ(2, model->update_requests_);
- EXPECT_EQ(TaskManagerModel::TASK_PENDING, model->update_state_);
-
- // Unload the extension and check that listener count decreases
- UnloadExtension(last_loaded_extension_id());
- EXPECT_EQ(1, model->update_requests_);
+ // Unload the extensions and make sure the listeners count is updated.
+ UnloadExtension(extension2->id());
+ EXPECT_EQ(1, GetListenersCount());
+ UnloadExtension(extension1->id());
+ EXPECT_EQ(0, GetListenersCount());
}
IN_PROC_BROWSER_TEST_F(ProcessesApiTest, CannotTerminateBrowserProcess) {
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 96cbd399..33b3ac7e 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -72,7 +72,6 @@
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/extension_api_frame_id_map.h"
#include "extensions/browser/extension_function_dispatcher.h"
-#include "extensions/browser/extension_function_util.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_zoom_request_client.h"
#include "extensions/browser/file_reader.h"
diff --git a/chrome/browser/task_management/providers/arc/arc_process_task.cc b/chrome/browser/task_management/providers/arc/arc_process_task.cc
index 7ef3926b..816757a 100644
--- a/chrome/browser/task_management/providers/arc/arc_process_task.cc
+++ b/chrome/browser/task_management/providers/arc/arc_process_task.cc
@@ -8,6 +8,7 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/grit/generated_resources.h"
#include "components/arc/arc_bridge_service.h"
+#include "content/public/common/child_process_host.h"
#include "ui/base/l10n/l10n_util.h"
namespace task_management {
@@ -42,7 +43,7 @@
int ArcProcessTask::GetChildProcessUniqueID() const {
// ARC process is not a child process of the browser.
- return 0;
+ return content::ChildProcessHost::kInvalidUniqueID;
}
void ArcProcessTask::Kill() {
diff --git a/chrome/browser/task_management/providers/task.cc b/chrome/browser/task_management/providers/task.cc
index f62b077..c9ad00b2 100644
--- a/chrome/browser/task_management/providers/task.cc
+++ b/chrome/browser/task_management/providers/task.cc
@@ -86,10 +86,23 @@
current_byte_count_ += bytes_read;
}
+void Task::GetTerminationStatus(base::TerminationStatus* out_status,
+ int* out_error_code) const {
+ DCHECK(out_status);
+ DCHECK(out_error_code);
+
+ *out_status = base::TERMINATION_STATUS_STILL_RUNNING;
+ *out_error_code = 0;
+}
+
base::string16 Task::GetProfileName() const {
return base::string16();
}
+int Task::GetTabId() const {
+ return -1;
+}
+
bool Task::ReportsSqliteMemory() const {
return GetSqliteMemoryUsed() != -1;
}
diff --git a/chrome/browser/task_management/providers/task.h b/chrome/browser/task_management/providers/task.h
index 992f8bd..3899d20b 100644
--- a/chrome/browser/task_management/providers/task.h
+++ b/chrome/browser/task_management/providers/task.h
@@ -10,6 +10,7 @@
#include <string>
#include "base/macros.h"
+#include "base/process/kill.h"
#include "base/process/process_handle.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
@@ -85,9 +86,19 @@
// value is whatever unique IDs of their hosts in the browser process.
virtual int GetChildProcessUniqueID() const = 0;
+ // If the process, in which this task is running, is terminated, this gets the
+ // termination status. Currently implemented only for Renderer processes.
+ virtual void GetTerminationStatus(base::TerminationStatus* out_status,
+ int* out_error_code) const;
+
// The name of the profile owning this task.
virtual base::string16 GetProfileName() const;
+ // Returns the unique ID of the tab if this task represents a renderer
+ // WebContents used for a tab. Returns -1 if this task does not represent
+ // a renderer, or a contents of a tab.
+ virtual int GetTabId() const;
+
// Getting the Sqlite used memory (in bytes). Not all tasks reports Sqlite
// memory, in this case a default invalid value of -1 will be returned.
// Check for whether the task reports it or not first.
diff --git a/chrome/browser/task_management/providers/task_provider.cc b/chrome/browser/task_management/providers/task_provider.cc
index 1d08d8d7..8523942 100644
--- a/chrome/browser/task_management/providers/task_provider.cc
+++ b/chrome/browser/task_management/providers/task_provider.cc
@@ -36,4 +36,9 @@
observer_->TaskRemoved(task);
}
+void TaskProvider::NotifyObserverTaskUnresponsive(Task* task) const {
+ DCHECK(observer_);
+ observer_->TaskUnresponsive(task);
+}
+
} // namespace task_management
diff --git a/chrome/browser/task_management/providers/task_provider.h b/chrome/browser/task_management/providers/task_provider.h
index e4387cf..545e9a1 100644
--- a/chrome/browser/task_management/providers/task_provider.h
+++ b/chrome/browser/task_management/providers/task_provider.h
@@ -45,10 +45,11 @@
protected:
// Used by concrete task providers to notify the observer of tasks addition/
- // removal. These methods should only be called after StartUpdating() has been
- // called and before StopUpdating() is called.
+ // removal/renderer unresponsive. These methods should only be called after
+ // StartUpdating() has been called and before StopUpdating() is called.
void NotifyObserverTaskAdded(Task* task) const;
void NotifyObserverTaskRemoved(Task* task) const;
+ void NotifyObserverTaskUnresponsive(Task* task) const;
private:
// This will be called once an observer is set for this provider. When it is
diff --git a/chrome/browser/task_management/providers/task_provider_observer.h b/chrome/browser/task_management/providers/task_provider_observer.h
index 72f8696..db95bd2 100644
--- a/chrome/browser/task_management/providers/task_provider_observer.h
+++ b/chrome/browser/task_management/providers/task_provider_observer.h
@@ -24,6 +24,10 @@
// the observer and references to it must not be kept.
virtual void TaskRemoved(Task* task) = 0;
+ // This notifies of the event that |task| has become unresponsive. This event
+ // is only for tasks representing renderer processes.
+ virtual void TaskUnresponsive(Task* task) {}
+
private:
DISALLOW_COPY_AND_ASSIGN(TaskProviderObserver);
};
diff --git a/chrome/browser/task_management/providers/web_contents/renderer_task.cc b/chrome/browser/task_management/providers/web_contents/renderer_task.cc
index c045de42..5dd4278 100644
--- a/chrome/browser/task_management/providers/web_contents/renderer_task.cc
+++ b/chrome/browser/task_management/providers/web_contents/renderer_task.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/favicon/favicon_utils.h"
#include "chrome/browser/process_resource_usage.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/task_management/task_manager_observer.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/render_process_host.h"
@@ -73,7 +74,9 @@
v8_memory_allocated_(0),
v8_memory_used_(0),
webcache_stats_(),
- profile_name_(GetRendererProfileName(render_process_host_)) {
+ profile_name_(GetRendererProfileName(render_process_host_)),
+ termination_status_(base::TERMINATION_STATUS_STILL_RUNNING),
+ termination_error_code_(0) {
// All renderer tasks are capable of reporting network usage, so the default
// invalid value of -1 doesn't apply here.
OnNetworkBytesRead(0);
@@ -129,10 +132,23 @@
return render_process_id_;
}
+void RendererTask::GetTerminationStatus(base::TerminationStatus* out_status,
+ int* out_error_code) const {
+ DCHECK(out_status);
+ DCHECK(out_error_code);
+
+ *out_status = termination_status_;
+ *out_error_code = termination_error_code_;
+}
+
base::string16 RendererTask::GetProfileName() const {
return profile_name_;
}
+int RendererTask::GetTabId() const {
+ return SessionTabHelper::IdForTab(web_contents_);
+}
+
int64_t RendererTask::GetV8MemoryAllocated() const {
return v8_memory_allocated_;
}
diff --git a/chrome/browser/task_management/providers/web_contents/renderer_task.h b/chrome/browser/task_management/providers/web_contents/renderer_task.h
index 0a9bdeb5..9d7dfb73 100644
--- a/chrome/browser/task_management/providers/web_contents/renderer_task.h
+++ b/chrome/browser/task_management/providers/web_contents/renderer_task.h
@@ -54,7 +54,10 @@
int64_t refresh_flags) override;
Type GetType() const override;
int GetChildProcessUniqueID() const override;
+ void GetTerminationStatus(base::TerminationStatus* out_status,
+ int* out_error_code) const override;
base::string16 GetProfileName() const override;
+ int GetTabId() const override;
int64_t GetV8MemoryAllocated() const override;
int64_t GetV8MemoryUsed() const override;
bool ReportsWebCacheStats() const override;
@@ -67,6 +70,14 @@
bool icon_url_changed,
const gfx::Image& image) override;
+ void set_termination_status(base::TerminationStatus status) {
+ termination_status_ = status;
+ }
+
+ void set_termination_error_code(int error_code) {
+ termination_error_code_ = error_code;
+ }
+
protected:
// Returns the title of the given |web_contents|.
static base::string16 GetTitleFromWebContents(
@@ -114,6 +125,9 @@
// host.
const base::string16 profile_name_;
+ base::TerminationStatus termination_status_;
+ int termination_error_code_;
+
DISALLOW_COPY_AND_ASSIGN(RendererTask);
};
diff --git a/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc
index a80a9e57..3035ef4 100644
--- a/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc
@@ -11,10 +11,12 @@
#include "chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
using content::RenderFrameHost;
+using content::RenderWidgetHost;
using content::SiteInstance;
using content::WebContents;
@@ -52,6 +54,7 @@
void RenderViewReady() override;
void WebContentsDestroyed() override;
void RenderProcessGone(base::TerminationStatus status) override;
+ void OnRendererUnresponsive(RenderWidgetHost* render_widget_host) override;
void DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) override;
@@ -113,6 +116,10 @@
FramesList& frames_list = pair.second;
DCHECK(!frames_list.empty());
RendererTask* task = tasks_by_frames_[frames_list[0]];
+
+ task->set_termination_status(web_contents()->GetCrashedStatus());
+ task->set_termination_error_code(web_contents()->GetCrashedErrorCode());
+
if (notify_observer)
provider_->NotifyObserverTaskRemoved(task);
delete task;
@@ -156,6 +163,18 @@
ClearAllTasks(true);
}
+void WebContentsEntry::OnRendererUnresponsive(
+ RenderWidgetHost* render_widget_host) {
+ auto itr = tasks_by_frames_.find(web_contents()->GetMainFrame());
+ if (itr == tasks_by_frames_.end())
+ return;
+
+ DCHECK_EQ(render_widget_host->GetProcess(),
+ web_contents()->GetMainFrame()->GetProcess());
+
+ provider_->NotifyObserverTaskUnresponsive(itr->second);
+}
+
void WebContentsEntry::DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
diff --git a/chrome/browser/task_management/sampling/task_group.cc b/chrome/browser/task_management/sampling/task_group.cc
index 3787bd3..a2c72b09d 100644
--- a/chrome/browser/task_management/sampling/task_group.cc
+++ b/chrome/browser/task_management/sampling/task_group.cc
@@ -17,6 +17,16 @@
namespace {
+// A mask for the refresh types that are done in the background thread.
+const int kBackgroundRefreshTypesMask =
+ REFRESH_TYPE_CPU |
+ REFRESH_TYPE_MEMORY |
+ REFRESH_TYPE_IDLE_WAKEUPS |
+#if defined(OS_LINUX)
+ REFRESH_TYPE_FD_COUNT |
+#endif // defined(OS_LINUX)
+ REFRESH_TYPE_PRIORITY;
+
inline bool IsResourceRefreshEnabled(RefreshType refresh_type,
int refresh_flags) {
return (refresh_flags & refresh_type) != 0;
@@ -57,11 +67,14 @@
TaskGroup::TaskGroup(
base::ProcessHandle proc_handle,
base::ProcessId proc_id,
+ const base::Closure& on_background_calculations_done,
const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner)
: process_handle_(proc_handle),
process_id_(proc_id),
+ on_background_calculations_done_(on_background_calculations_done),
worker_thread_sampler_(nullptr),
- tasks_(),
+ expected_on_bg_done_flags_(kBackgroundRefreshTypesMask),
+ current_on_bg_done_flags_(0),
cpu_usage_(0.0),
memory_usage_(),
gpu_memory_(-1),
@@ -123,6 +136,10 @@
int64_t refresh_flags) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ expected_on_bg_done_flags_ = refresh_flags & kBackgroundRefreshTypesMask;
+ // If a refresh type was recently disabled, we need to account for that too.
+ current_on_bg_done_flags_ &= expected_on_bg_done_flags_;
+
// First refresh the enabled non-expensive resources usages on the UI thread.
// 1- Refresh all the tasks as well as the total network usage (if enabled).
const bool network_usage_refresh_enabled =
@@ -132,9 +149,8 @@
Task* task = task_pair.second;
task->Refresh(update_interval, refresh_flags);
- if (network_usage_refresh_enabled && task->ReportsNetworkUsage()) {
+ if (network_usage_refresh_enabled && task->ReportsNetworkUsage())
per_process_network_usage_ += task->network_usage();
- }
}
// 2- Refresh GPU memory (if enabled).
@@ -179,6 +195,14 @@
return tasks_.at(task_id);
}
+void TaskGroup::ClearCurrentBackgroundCalculationsFlags() {
+ current_on_bg_done_flags_ = 0;
+}
+
+bool TaskGroup::AreBackgroundCalculationsDone() const {
+ return expected_on_bg_done_flags_ == current_on_bg_done_flags_;
+}
+
void TaskGroup::RefreshGpuMemory(
const gpu::VideoMemoryUsageStats& gpu_memory_stats) {
auto itr = gpu_memory_stats.process_map.find(process_id_);
@@ -214,18 +238,21 @@
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
cpu_usage_ = cpu_usage;
+ OnBackgroundRefreshTypeFinished(REFRESH_TYPE_CPU);
}
void TaskGroup::OnMemoryUsageRefreshDone(MemoryUsageStats memory_usage) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
memory_usage_ = memory_usage;
+ OnBackgroundRefreshTypeFinished(REFRESH_TYPE_MEMORY);
}
void TaskGroup::OnIdleWakeupsRefreshDone(int idle_wakeups_per_second) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
idle_wakeups_per_second_ = idle_wakeups_per_second;
+ OnBackgroundRefreshTypeFinished(REFRESH_TYPE_IDLE_WAKEUPS);
}
#if defined(OS_LINUX)
@@ -233,6 +260,7 @@
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
open_fd_count_ = open_fd_count;
+ OnBackgroundRefreshTypeFinished(REFRESH_TYPE_FD_COUNT);
}
#endif // defined(OS_LINUX)
@@ -240,6 +268,15 @@
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
is_backgrounded_ = is_backgrounded;
+ OnBackgroundRefreshTypeFinished(REFRESH_TYPE_PRIORITY);
+}
+
+void TaskGroup::OnBackgroundRefreshTypeFinished(int64_t finished_refresh_type) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ current_on_bg_done_flags_ |= finished_refresh_type;
+ if (AreBackgroundCalculationsDone())
+ on_background_calculations_done_.Run();
}
} // namespace task_management
diff --git a/chrome/browser/task_management/sampling/task_group.h b/chrome/browser/task_management/sampling/task_group.h
index a9276fd..4d6eed2 100644
--- a/chrome/browser/task_management/sampling/task_group.h
+++ b/chrome/browser/task_management/sampling/task_group.h
@@ -32,6 +32,7 @@
TaskGroup(
base::ProcessHandle proc_handle,
base::ProcessId proc_id,
+ const base::Closure& on_background_calculations_done,
const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner);
~TaskGroup();
@@ -50,6 +51,15 @@
Task* GetTaskById(TaskId task_id) const;
+ // This is to be called after the task manager had informed its observers with
+ // OnTasksRefreshedWithBackgroundCalculations() to begin another cycle for
+ // this notification type.
+ void ClearCurrentBackgroundCalculationsFlags();
+
+ // True if all enabled background operations calculating resource usage of the
+ // process represented by this TaskGroup have completed.
+ bool AreBackgroundCalculationsDone() const;
+
const base::ProcessHandle& process_handle() const { return process_handle_; }
const base::ProcessId& process_id() const { return process_id_; }
@@ -104,16 +114,27 @@
void OnProcessPriorityDone(bool is_backgrounded);
+ void OnBackgroundRefreshTypeFinished(int64_t finished_refresh_type);
+
// The process' handle and ID.
base::ProcessHandle process_handle_;
base::ProcessId process_id_;
+ // This is a callback into the TaskManagerImpl to inform it that the
+ // background calculations for this TaskGroup has finished.
+ const base::Closure on_background_calculations_done_;
+
scoped_refptr<TaskGroupSampler> worker_thread_sampler_;
// Maps the Tasks by their IDs.
// Tasks are not owned by the TaskGroup. They're owned by the TaskProviders.
std::map<TaskId, Task*> tasks_;
+ // Flags will be used to determine when the background calculations has
+ // completed for the enabled refresh types for this TaskGroup.
+ int64_t expected_on_bg_done_flags_;
+ int64_t current_on_bg_done_flags_;
+
// The per process resources usages.
double cpu_usage_;
MemoryUsageStats memory_usage_;
diff --git a/chrome/browser/task_management/sampling/task_manager_impl.cc b/chrome/browser/task_management/sampling/task_manager_impl.cc
index 9c2abab..c7e7c8d 100644
--- a/chrome/browser/task_management/sampling/task_manager_impl.cc
+++ b/chrome/browser/task_management/sampling/task_manager_impl.cc
@@ -42,7 +42,10 @@
} // namespace
TaskManagerImpl::TaskManagerImpl()
- : blocking_pool_runner_(GetBlockingPoolRunner()),
+ : on_background_data_ready_callback_(base::Bind(
+ &TaskManagerImpl::OnTaskGroupBackgroundCalculationsDone,
+ base::Unretained(this))),
+ blocking_pool_runner_(GetBlockingPoolRunner()),
is_running_(false) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -183,6 +186,20 @@
return GetTaskByTaskId(task_id)->GetType();
}
+int TaskManagerImpl::GetTabId(TaskId task_id) const {
+ return GetTaskByTaskId(task_id)->GetTabId();
+}
+
+int TaskManagerImpl::GetChildProcessUniqueId(TaskId task_id) const {
+ return GetTaskByTaskId(task_id)->GetChildProcessUniqueID();
+}
+
+void TaskManagerImpl::GetTerminationStatus(TaskId task_id,
+ base::TerminationStatus* out_status,
+ int* out_error_code) const {
+ GetTaskByTaskId(task_id)->GetTerminationStatus(out_status, out_error_code);
+}
+
int64_t TaskManagerImpl::GetNetworkUsage(TaskId task_id) const {
return GetTaskByTaskId(task_id)->network_usage();
}
@@ -244,6 +261,16 @@
return sorted_task_ids_;
}
+TaskIdList TaskManagerImpl::GetIdsOfTasksSharingSameProcess(
+ TaskId task_id) const {
+ DCHECK(is_running_) << "Task manager is not running. You must observe the "
+ "task manager for it to start running";
+
+ TaskIdList result;
+ GetTaskGroupByTaskId(task_id)->AppendSortedTaskIds(&result);
+ return result;
+}
+
size_t TaskManagerImpl::GetNumberOfTasksOnSameProcess(TaskId task_id) const {
return GetTaskGroupByTaskId(task_id)->num_tasks();
}
@@ -259,6 +286,7 @@
if (itr == task_groups_by_proc_id_.end()) {
task_group = new TaskGroup(task->process_handle(),
proc_id,
+ on_background_data_ready_callback_,
blocking_pool_runner_);
task_groups_by_proc_id_[proc_id] = task_group;
} else {
@@ -300,6 +328,11 @@
}
}
+void TaskManagerImpl::TaskUnresponsive(Task* task) {
+ DCHECK(task);
+ NotifyObserversOnTaskUnresponsive(task->task_id());
+}
+
void TaskManagerImpl::OnVideoMemoryUsageStatsUpdate(
const gpu::VideoMemoryUsageStats& gpu_memory_stats) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -399,4 +432,18 @@
return GetTaskGroupByTaskId(task_id)->GetTaskById(task_id);
}
+void TaskManagerImpl::OnTaskGroupBackgroundCalculationsDone() {
+ // TODO(afakhry): There should be a better way for doing this!
+ bool are_all_processes_data_ready = true;
+ for (auto& groups_itr : task_groups_by_proc_id_) {
+ are_all_processes_data_ready &=
+ groups_itr.second->AreBackgroundCalculationsDone();
+ }
+ if (are_all_processes_data_ready) {
+ NotifyObserversOnRefreshWithBackgroundCalculations(GetTaskIdsList());
+ for (auto& groups_itr : task_groups_by_proc_id_)
+ groups_itr.second->ClearCurrentBackgroundCalculationsFlags();
+ }
+}
+
} // namespace task_management
diff --git a/chrome/browser/task_management/sampling/task_manager_impl.h b/chrome/browser/task_management/sampling/task_manager_impl.h
index 0f0eedd48..0e92023 100644
--- a/chrome/browser/task_management/sampling/task_manager_impl.h
+++ b/chrome/browser/task_management/sampling/task_manager_impl.h
@@ -60,6 +60,11 @@
const base::ProcessHandle& GetProcessHandle(TaskId task_id) const override;
const base::ProcessId& GetProcessId(TaskId task_id) const override;
Task::Type GetType(TaskId task_id) const override;
+ int GetTabId(TaskId task_id) const override;
+ int GetChildProcessUniqueId(TaskId task_id) const override;
+ void GetTerminationStatus(TaskId task_id,
+ base::TerminationStatus* out_status,
+ int* out_error_code) const override;
int64_t GetNetworkUsage(TaskId task_id) const override;
int64_t GetProcessTotalNetworkUsage(TaskId task_id) const override;
int64_t GetSqliteMemoryUsed(TaskId task_id) const override;
@@ -70,11 +75,13 @@
TaskId task_id,
blink::WebCache::ResourceTypeStats* stats) const override;
const TaskIdList& GetTaskIdsList() const override;
+ TaskIdList GetIdsOfTasksSharingSameProcess(TaskId task_id) const override;
size_t GetNumberOfTasksOnSameProcess(TaskId task_id) const override;
// task_management::TaskProviderObserver:
void TaskAdded(Task* task) override;
void TaskRemoved(Task* task) override;
+ void TaskUnresponsive(Task* task) override;
// content::GpuDataManagerObserver:
void OnVideoMemoryUsageStatsUpdate(
@@ -104,6 +111,12 @@
TaskGroup* GetTaskGroupByTaskId(TaskId task_id) const;
Task* GetTaskByTaskId(TaskId task_id) const;
+ // Called back by a TaskGroup when the resource calculations done on the
+ // background thread has completed.
+ void OnTaskGroupBackgroundCalculationsDone();
+
+ const base::Closure on_background_data_ready_callback_;
+
// Map TaskGroups by the IDs of the processes they represent.
// Keys and values are unique (no duplicates).
std::map<base::ProcessId, TaskGroup*> task_groups_by_proc_id_;
diff --git a/chrome/browser/task_management/task_manager_interface.cc b/chrome/browser/task_management/task_manager_interface.cc
index 790cbd0d..5776968 100644
--- a/chrome/browser/task_management/task_manager_interface.cc
+++ b/chrome/browser/task_management/task_manager_interface.cc
@@ -87,7 +87,7 @@
SetEnabledResourceFlags(flags);
}
-bool TaskManagerInterface::IsResourceRefreshEnabled(RefreshType type) {
+bool TaskManagerInterface::IsResourceRefreshEnabled(RefreshType type) const {
return (enabled_resources_flags_ & type) != 0;
}
@@ -115,6 +115,17 @@
OnTasksRefreshed(task_ids));
}
+void TaskManagerInterface::NotifyObserversOnRefreshWithBackgroundCalculations(
+ const TaskIdList& task_ids) {
+ FOR_EACH_OBSERVER(TaskManagerObserver,
+ observers_,
+ OnTasksRefreshedWithBackgroundCalculations(task_ids));
+}
+
+void TaskManagerInterface::NotifyObserversOnTaskUnresponsive(TaskId id) {
+ FOR_EACH_OBSERVER(TaskManagerObserver, observers_, OnTaskUnresponsive(id));
+}
+
base::TimeDelta TaskManagerInterface::GetCurrentRefreshTime() const {
return refresh_timer_->IsRunning() ? refresh_timer_->GetCurrentDelay()
: base::TimeDelta::Max();
diff --git a/chrome/browser/task_management/task_manager_interface.h b/chrome/browser/task_management/task_manager_interface.h
index d8dd95c..79d65e1 100644
--- a/chrome/browser/task_management/task_manager_interface.h
+++ b/chrome/browser/task_management/task_manager_interface.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
+#include "base/process/kill.h"
#include "base/process/process_handle.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -118,6 +119,28 @@
// Returns the type of the task with |task_id|.
virtual Task::Type GetType(TaskId task_id) const = 0;
+ // Gets the unique ID of the tab if the task with |task_id| represents a
+ // WebContents of a tab. Returns -1 otherwise.
+ virtual int GetTabId(TaskId task_id) const = 0;
+
+ // Returns the unique ID of the BrowserChildProcessHost/RenderProcessHost on
+ // which the task with |task_id| is running. It is not the PID nor the handle
+ // of the process.
+ // For a task that represents the browser process, the return value is 0.
+ // For a task that doesn't have a host on the browser process, the return
+ // value is also 0. For other tasks that represent renderers and other child
+ // processes, the return value is whatever unique IDs of their hosts in the
+ // browser process.
+ virtual int GetChildProcessUniqueId(TaskId task_id) const = 0;
+
+ // If the process, in which the task with |task_id| is running, is terminated
+ // this gets the termination status. Currently implemented only for Renderer
+ // processes. The values will only be valid if the process has actually
+ // terminated.
+ virtual void GetTerminationStatus(TaskId task_id,
+ base::TerminationStatus* out_status,
+ int* out_error_code) const = 0;
+
// Returns the network usage (in bytes per second) during the current refresh
// cycle for the task with |task_id|. A value of -1 means no valid value is
// currently available or that task has never been notified of any network
@@ -154,6 +177,10 @@
// IDs on which the tasks are running, then by the task IDs themselves.
virtual const TaskIdList& GetTaskIdsList() const = 0;
+ // Gets the list of task IDs of the tasks that run on the same process as the
+ // task with |task_id|. The returned list will at least include |task_id|.
+ virtual TaskIdList GetIdsOfTasksSharingSameProcess(TaskId task_id) const = 0;
+
// Gets the number of task-manager tasks running on the same process on which
// the Task with |task_id| is running.
virtual size_t GetNumberOfTasksOnSameProcess(TaskId task_id) const = 0;
@@ -161,7 +188,7 @@
// Returns true if the resource |type| usage calculation is enabled and
// the implementation should refresh its value (this means that at least one
// of the observers require this value). False otherwise.
- bool IsResourceRefreshEnabled(RefreshType type);
+ bool IsResourceRefreshEnabled(RefreshType type) const;
protected:
TaskManagerInterface();
@@ -171,6 +198,9 @@
void NotifyObserversOnTaskAdded(TaskId id);
void NotifyObserversOnTaskToBeRemoved(TaskId id);
void NotifyObserversOnRefresh(const TaskIdList& task_ids);
+ void NotifyObserversOnRefreshWithBackgroundCalculations(
+ const TaskIdList& task_ids);
+ void NotifyObserversOnTaskUnresponsive(TaskId id);
// Refresh all the enabled resources usage of all the available tasks.
virtual void Refresh() = 0;
diff --git a/chrome/browser/task_management/task_manager_observer.cc b/chrome/browser/task_management/task_manager_observer.cc
index a0a1a65..f75d27f 100644
--- a/chrome/browser/task_management/task_manager_observer.cc
+++ b/chrome/browser/task_management/task_manager_observer.cc
@@ -17,6 +17,8 @@
desired_resources_flags_(resources_flags) {}
TaskManagerObserver::~TaskManagerObserver() {
+ if (observed_task_manager())
+ observed_task_manager()->RemoveObserver(this);
}
void TaskManagerObserver::AddRefreshType(RefreshType type) {
@@ -33,5 +35,11 @@
observed_task_manager_->RecalculateRefreshFlags();
}
+void TaskManagerObserver::SetRefreshTypesFlags(int64_t flags) {
+ desired_resources_flags_ = flags;
+
+ if (observed_task_manager_)
+ observed_task_manager_->RecalculateRefreshFlags();
+}
} // namespace task_management
diff --git a/chrome/browser/task_management/task_manager_observer.h b/chrome/browser/task_management/task_manager_observer.h
index 408d2a87..84f2c21 100644
--- a/chrome/browser/task_management/task_manager_observer.h
+++ b/chrome/browser/task_management/task_manager_observer.h
@@ -86,6 +86,22 @@
// IDs themselves.
virtual void OnTasksRefreshed(const TaskIdList& task_ids) = 0;
+ // Notifies the observer that the task manager has just finished a refresh
+ // cycle that calculated all the resource usage of all tasks whose IDs are in
+ // |task_ids| including the resource usages that are calculated in the
+ // background such CPU and memory (If those refresh types are enabled).
+ // This event can take longer to be fired, and can miss some changes that may
+ // happen to non-background calculations in-between two successive
+ // invocations. Listen to this ONLY if you must know when all the background
+ // resource calculations to be valid for all the available processes.
+ // |task_ids| will be sorted as specified in OnTasksRefreshed() above.
+ virtual void OnTasksRefreshedWithBackgroundCalculations(
+ const TaskIdList& task_ids) {}
+
+ // Notifies the observer that the task with |id| is running on a renderer that
+ // has become unresponsive.
+ virtual void OnTaskUnresponsive(TaskId id) {}
+
const base::TimeDelta& desired_refresh_time() const {
return desired_refresh_time_;
}
@@ -100,6 +116,7 @@
// Add or Remove a refresh |type|.
void AddRefreshType(RefreshType type);
void RemoveRefreshType(RefreshType type);
+ void SetRefreshTypesFlags(int64_t flags);
private:
friend class TaskManagerInterface;
diff --git a/chrome/browser/task_management/test_task_manager.cc b/chrome/browser/task_management/test_task_manager.cc
index c49c5f1..9b71bf4 100644
--- a/chrome/browser/task_management/test_task_manager.cc
+++ b/chrome/browser/task_management/test_task_manager.cc
@@ -97,6 +97,24 @@
return Task::UNKNOWN;
}
+int TestTaskManager::GetTabId(TaskId task_id) const {
+ return -1;
+}
+
+int TestTaskManager::GetChildProcessUniqueId(TaskId task_id) const {
+ return 0;
+}
+
+void TestTaskManager::GetTerminationStatus(TaskId task_id,
+ base::TerminationStatus* out_status,
+ int* out_error_code) const {
+ DCHECK(out_status);
+ DCHECK(out_error_code);
+
+ *out_status = base::TERMINATION_STATUS_STILL_RUNNING;
+ *out_error_code = 0;
+}
+
int64_t TestTaskManager::GetNetworkUsage(TaskId task_id) const {
return -1;
}
@@ -125,6 +143,13 @@
return ids_;
}
+TaskIdList TestTaskManager::GetIdsOfTasksSharingSameProcess(
+ TaskId task_id) const {
+ TaskIdList result;
+ result.push_back(task_id);
+ return result;
+}
+
size_t TestTaskManager::GetNumberOfTasksOnSameProcess(TaskId task_id) const {
return 1;
}
diff --git a/chrome/browser/task_management/test_task_manager.h b/chrome/browser/task_management/test_task_manager.h
index b666bb0..e3a1838 100644
--- a/chrome/browser/task_management/test_task_manager.h
+++ b/chrome/browser/task_management/test_task_manager.h
@@ -47,6 +47,11 @@
const base::ProcessHandle& GetProcessHandle(TaskId task_id) const override;
const base::ProcessId& GetProcessId(TaskId task_id) const override;
Task::Type GetType(TaskId task_id) const override;
+ int GetTabId(TaskId task_id) const override;
+ int GetChildProcessUniqueId(TaskId task_id) const override;
+ void GetTerminationStatus(TaskId task_id,
+ base::TerminationStatus* out_status,
+ int* out_error_code) const override;
int64_t GetNetworkUsage(TaskId task_id) const override;
int64_t GetProcessTotalNetworkUsage(TaskId task_id) const override;
int64_t GetSqliteMemoryUsed(TaskId task_id) const override;
@@ -57,6 +62,7 @@
TaskId task_id,
blink::WebCache::ResourceTypeStats* stats) const override;
const TaskIdList& GetTaskIdsList() const override;
+ TaskIdList GetIdsOfTasksSharingSameProcess(TaskId task_id) const override;
size_t GetNumberOfTasksOnSameProcess(TaskId task_id) const override;
base::TimeDelta GetRefreshTime();
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index dfb1d74..3c22e8b 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -435,8 +435,6 @@
'browser/extensions/api/preferences_private/preferences_private_api.h',
'browser/extensions/api/principals_private/principals_private_api.cc',
'browser/extensions/api/principals_private/principals_private_api.h',
- 'browser/extensions/api/processes/processes_api.cc',
- 'browser/extensions/api/processes/processes_api.h',
'browser/extensions/api/proxy/proxy_api.cc',
'browser/extensions/api/proxy/proxy_api.h',
'browser/extensions/api/proxy/proxy_api_constants.cc',
@@ -895,6 +893,10 @@
'browser/web_applications/web_app_win.cc',
'browser/web_applications/web_app_win.h',
],
+ 'chrome_browser_extensions_task_manager_enabled_sources': [
+ 'browser/extensions/api/processes/processes_api.cc',
+ 'browser/extensions/api/processes/processes_api.h',
+ ],
'chrome_browser_extensions_app_list_sources': [
'browser/apps/drive/drive_app_converter.cc',
'browser/apps/drive/drive_app_converter.h',
@@ -1017,6 +1019,11 @@
'<@(chrome_browser_extensions_enabled_sources)',
],
'conditions': [
+ ['enable_task_manager==1', {
+ 'sources': [
+ '<@(chrome_browser_extensions_task_manager_enabled_sources)',
+ ],
+ }],
['chromeos==1', {
'dependencies': [
'../build/linux/system.gyp:dbus',
diff --git a/chrome/common/extensions/api/processes.idl b/chrome/common/extensions/api/processes.idl
index 556ba241..9e9808c 100644
--- a/chrome/common/extensions/api/processes.idl
+++ b/chrome/common/extensions/api/processes.idl
@@ -18,6 +18,16 @@
gpu,
other
};
+
+ // An object that represents a Chrome task running on a process. Several tasks
+ // can share the same process.
+ dictionary TaskInfo {
+ // The title of the task.
+ DOMString title;
+ // Optional tab ID, if this task represents a tab running on a renderer
+ // process.
+ long? tabId;
+ };
// The Cache object contains information about the size and utilization of a
// cache used by the browser.
@@ -34,8 +44,6 @@
long id;
// The ID of the process, as provided by the OS.
long osProcessId;
- // The title of the process as seen in the task manager.
- DOMString title;
// The type of process.
ProcessType type;
// The profile which the process is associated with.
@@ -43,9 +51,8 @@
// The debugging port for Native Client processes. Zero for other process
// types and for NaCl processes that do not have debugging enabled.
long naclDebugPort;
- // Array of Tab IDs that have a page rendered by this process. The list will
- // be non-empty for renderer processes only.
- long[] tabs;
+ // Array of TaskInfos representing the tasks running on this process.
+ TaskInfo[] tasks;
// The most recent measurement of the process CPU usage, between 0 and 100.
// Only available when receiving the object as part of a callback from
// onUpdated or onUpdatedWithMemory.
@@ -83,7 +90,7 @@
// or onUpdatedWithMemory.
Cache? cssCache;
};
-
+
// A callback to report the status of the termination.
// |didTerminate|: True if terminating the process was successful, and false
// otherwise.
diff --git a/chrome/common/extensions/api/schemas.gni b/chrome/common/extensions/api/schemas.gni
index 54cc684..8fc28c2 100644
--- a/chrome/common/extensions/api/schemas.gni
+++ b/chrome/common/extensions/api/schemas.gni
@@ -14,6 +14,9 @@
# Common sources that are both bundled and compiled.
sources = gypi_values.main_schema_files
+if (enable_task_manager) {
+ sources += gypi_values.task_manager_dependent_schema_files
+}
if (is_chromeos) {
sources += gypi_values.chromeos_schema_files
} else if (is_linux || is_win) {
diff --git a/chrome/common/extensions/api/schemas.gypi b/chrome/common/extensions/api/schemas.gypi
index eec30e0..39d61035 100644
--- a/chrome/common/extensions/api/schemas.gypi
+++ b/chrome/common/extensions/api/schemas.gypi
@@ -66,7 +66,6 @@
'passwords_private.idl',
'permissions.json',
'preferences_private.json',
- 'processes.idl',
'resources_private.idl',
'screenlock_private.idl',
'sessions.json',
@@ -145,6 +144,10 @@
'cast_streaming_session.idl',
'cast_streaming_udp_transport.idl',
],
+
+ 'task_manager_dependent_schema_files': [
+ 'processes.idl',
+ ],
# Input IME schema.
'input_ime_schema_file': [
@@ -168,6 +171,11 @@
# Disable schema compiler to generate model extension API code.
# Only register the extension functions in extension system.
'conditions': [
+ ['enable_task_manager==1', {
+ 'schema_files': [
+ '<@(task_manager_dependent_schema_files)',
+ ],
+ }],
['chromeos==1', {
'schema_files': [
'<@(chromeos_schema_files)',
diff --git a/chrome/test/data/extensions/api_test/processes/api/test.js b/chrome/test/data/extensions/api_test/processes/api/test.js
index edd4f27..a4ceb52 100644
--- a/chrome/test/data/extensions/api_test/processes/api/test.js
+++ b/chrome/test/data/extensions/api_test/processes/api/test.js
@@ -29,11 +29,15 @@
function dumpProcess(process) {
console.log("id " + process.id);
- console.log("title " + process.title);
console.log("osProcId " + process.osProcessId);
console.log("type " + process.type);
console.log("profile " + process.profile);
- console.log("tabs " + process.tabs);
+ console.log("tasks " + process.tasks);
+ for (var i = 0; i < process.tasks.length; ++i) {
+ console.log("task["+ i + "].title " + process.tasks[i].title);
+ if ("tabId" in process.tasks[i])
+ console.log("task["+ i + "].tabId " + process.tasks[i].tabId);
+ }
console.log("cpu " + process.cpu);
console.log("privMem " + process.privateMemory);
console.log("network " + process.network);
@@ -58,12 +62,12 @@
function validateProcessProperties(process, updating, memory_included) {
// Always present.
assertTrue("id" in process);
- assertTrue("title" in process);
assertTrue("naclDebugPort" in process);
assertTrue("osProcessId" in process);
assertTrue("type" in process);
assertTrue("profile" in process);
- assertTrue("tabs" in process);
+ assertTrue("tasks" in process);
+ assertTrue("title" in process.tasks[0]);
// Present if onUpdate(WithMemory) listener is registered.
assertEq(("cpu" in process), updating);
@@ -146,9 +150,9 @@
function (pl2) {
var proc1 = pl1[pid1];
var proc2 = pl2[pid2];
- assertTrue(proc1.tabs.length == proc2.tabs.length);
- for (var i = 0; i < proc1.tabs.length; ++i) {
- assertEq(proc1.tabs[i], proc2.tabs[i]);
+ assertTrue(proc1.tasks.length == proc2.tasks.length);
+ for (var i = 0; i < proc1.tasks.length; ++i) {
+ assertEq(proc1.tasks[i], proc2.tasks[i]);
}
});
});
@@ -261,13 +265,15 @@
function testOnCreated() {
listenOnce(chrome.processes.onCreated, function(process) {
assertTrue("id" in process, "process doesn't have id property");
+ // We don't report the creation of the browser process, hence process.id
+ // is expected to be > 0.
assertTrue(process.id > 0, "id is not positive " + process.id);
});
createTab(5, "chrome://newtab/");
},
// DISABLED: crbug.com/345411
- // Hangs consistently.
+ // Hangs consistently (On Windows).
/*
function testOnExited() {
listenOnce(chrome.processes.onExited,
diff --git a/chrome/test/data/extensions/api_test/processes/onupdated_with_memory/background.js b/chrome/test/data/extensions/api_test/processes/onupdated_with_memory/background.js
new file mode 100644
index 0000000..66ea6c1
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/processes/onupdated_with_memory/background.js
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Add a simple listener to onUpdatedWithMemory to ensure that the task
+// manager's refresh types are updated correctly.
+chrome.processes.onUpdatedWithMemory.addListener(function(processes) {
+ console.log("Received update with memory.");
+});
+
+chrome.test.sendMessage("ready");
\ No newline at end of file
diff --git a/chrome/test/data/extensions/api_test/processes/onupdated_with_memory/manifest.json b/chrome/test/data/extensions/api_test/processes/onupdated_with_memory/manifest.json
new file mode 100644
index 0000000..1b20b3f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/processes/onupdated_with_memory/manifest.json
@@ -0,0 +1,10 @@
+{
+ "name": "processes onupdated with memory test",
+ "description": "extension that listens to processes.onUpdatedWithMemory",
+ "version": "0.1",
+ "manifest_version": 2,
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": [ "tabs", "processes" ]
+}
\ No newline at end of file
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 17e4875d..1f2b90d 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1163,6 +1163,10 @@
return crashed_status_;
}
+int WebContentsImpl::GetCrashedErrorCode() const {
+ return crashed_error_code_;
+}
+
bool WebContentsImpl::IsBeingDestroyed() const {
return is_being_destroyed_;
}
@@ -4409,6 +4413,9 @@
void WebContentsImpl::RendererUnresponsive(
RenderWidgetHostImpl* render_widget_host) {
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ OnRendererUnresponsive(render_widget_host));
+
// Don't show hung renderer dialog for a swapped out RVH.
if (render_widget_host != GetRenderViewHost()->GetWidget())
return;
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 7e0da7d3..0624277 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -290,6 +290,7 @@
bool IsCrashed() const override;
void SetIsCrashed(base::TerminationStatus status, int error_code) override;
base::TerminationStatus GetCrashedStatus() const override;
+ int GetCrashedErrorCode() const override;
bool IsBeingDestroyed() const override;
void NotifyNavigationStateChanged(InvalidateTypes changed_flags) override;
base::TimeTicks GetLastActiveTime() const override;
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index bbee142..1662c395 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -368,6 +368,7 @@
virtual void SetIsCrashed(base::TerminationStatus status, int error_code) = 0;
virtual base::TerminationStatus GetCrashedStatus() const = 0;
+ virtual int GetCrashedErrorCode() const = 0;
// Whether the tab is in the process of being destroyed.
virtual bool IsBeingDestroyed() const = 0;
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index 29c2c1e..2ad466e 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -27,6 +27,7 @@
class NavigationHandle;
class RenderFrameHost;
class RenderViewHost;
+class RenderWidgetHost;
class WebContents;
class WebContentsImpl;
struct AXEventNotificationDetails;
@@ -121,6 +122,10 @@
virtual void RenderViewHostChanged(RenderViewHost* old_host,
RenderViewHost* new_host) {}
+ // This method is invoked when the process for the current main
+ // RenderFrameHost becomes unresponsive.
+ virtual void OnRendererUnresponsive(RenderWidgetHost* render_widget_host) {}
+
// Navigation related events ------------------------------------------------
// Called when a navigation started in the WebContents. |navigation_handle|
diff --git a/extensions/browser/extension_function_util.cc b/extensions/browser/extension_function_util.cc
deleted file mode 100644
index 5e5e763..0000000
--- a/extensions/browser/extension_function_util.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-
-#include "extensions/browser/extension_function_util.h"
-
-namespace extensions {
-
-bool ReadOneOrMoreIntegers(base::Value* value, std::vector<int>* result) {
- if (value->IsType(base::Value::TYPE_INTEGER)) {
- int v = -1;
- if (!value->GetAsInteger(&v))
- return false;
- result->push_back(v);
- return true;
-
- } else if (value->IsType(base::Value::TYPE_LIST)) {
- base::ListValue* values = static_cast<base::ListValue*>(value);
- for (size_t i = 0; i < values->GetSize(); ++i) {
- int v = -1;
- if (!values->GetInteger(i, &v))
- return false;
- result->push_back(v);
- }
- return true;
- }
- return false;
-}
-
-} // namespace extensions
diff --git a/extensions/browser/extension_function_util.h b/extensions/browser/extension_function_util.h
deleted file mode 100644
index e9943076d..0000000
--- a/extensions/browser/extension_function_util.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_BROWSER_EXTENSION_FUNCTION_UTIL_H__
-#define EXTENSIONS_BROWSER_EXTENSION_FUNCTION_UTIL_H__
-
-#include <vector>
-#include "base/values.h"
-
-namespace base {
-class Value;
-}
-
-// This file contains various utility functions for extension API
-// implementations.
-namespace extensions {
-
-// Reads the |value| as either a single integer value or a list of integers.
-bool ReadOneOrMoreIntegers(base::Value* value, std::vector<int>* result);
-
-} // namespace extensions
-
-#endif // EXTENSIONS_BROWSER_EXTENSION_FUNCTION_UTIL_H__
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index 04f3e45..4e3871e 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -582,8 +582,6 @@
'browser/extension_function_dispatcher.h',
'browser/extension_function_registry.cc',
'browser/extension_function_registry.h',
- 'browser/extension_function_util.cc',
- 'browser/extension_function_util.h',
'browser/extension_host.cc',
'browser/extension_host.h',
'browser/extension_host_delegate.h',