summaryrefslogtreecommitdiffstats
path: root/chrome/browser/task_manager
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-23 19:34:12 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-23 19:34:12 +0000
commita5d1e1e2cf6deb70a8476fd1f970c697274cbca7 (patch)
treef2842a6ea90359429a5c9f46c889a8a943861929 /chrome/browser/task_manager
parenta4a5740d7ae8c5b54bc4dea0b3ea2deb43edad88 (diff)
downloadchromium_src-a5d1e1e2cf6deb70a8476fd1f970c697274cbca7.zip
chromium_src-a5d1e1e2cf6deb70a8476fd1f970c697274cbca7.tar.gz
chromium_src-a5d1e1e2cf6deb70a8476fd1f970c697274cbca7.tar.bz2
Move task manager code into a subdirectory.
BUG=50548 TEST=compiles Review URL: http://codereview.chromium.org/3454028 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60325 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/task_manager')
-rw-r--r--chrome/browser/task_manager/task_manager.cc981
-rw-r--r--chrome/browser/task_manager/task_manager.h415
-rw-r--r--chrome/browser/task_manager/task_manager_browsertest.cc265
-rw-r--r--chrome/browser/task_manager/task_manager_resource_providers.cc935
-rw-r--r--chrome/browser/task_manager/task_manager_resource_providers.h396
-rw-r--r--chrome/browser/task_manager/task_manager_unittest.cc110
6 files changed, 3102 insertions, 0 deletions
diff --git a/chrome/browser/task_manager/task_manager.cc b/chrome/browser/task_manager/task_manager.cc
new file mode 100644
index 0000000..6d4147c
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager.cc
@@ -0,0 +1,981 @@
+// Copyright (c) 2010 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 "chrome/browser/task_manager/task_manager.h"
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/compiler_specific.h"
+#include "base/i18n/number_formatting.h"
+#include "base/i18n/rtl.h"
+#include "base/process_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/net/url_request_tracking.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/task_manager/task_manager_resource_providers.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "grit/app_resources.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "unicode/coll.h"
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/mach_broker_mac.h"
+#endif
+
+namespace {
+
+// The delay between updates of the information (in ms).
+#if defined(OS_MACOSX)
+// Match Activity Monitor's default refresh rate.
+const int kUpdateTimeMs = 2000;
+#else
+const int kUpdateTimeMs = 1000;
+#endif
+
+template <class T>
+int ValueCompare(T value1, T value2) {
+ if (value1 < value2)
+ return -1;
+ if (value1 == value2)
+ return 0;
+ return 1;
+}
+
+string16 FormatStatsSize(const WebKit::WebCache::ResourceTypeStat& stat) {
+ return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT,
+ FormatBytes(stat.size, DATA_UNITS_KIBIBYTE, false),
+ FormatBytes(stat.liveSize, DATA_UNITS_KIBIBYTE, false));
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerModel class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerModel::TaskManagerModel(TaskManager* task_manager)
+ : update_state_(IDLE),
+ goat_salt_(rand()) {
+
+ TaskManagerBrowserProcessResourceProvider* browser_provider =
+ new TaskManagerBrowserProcessResourceProvider(task_manager);
+ browser_provider->AddRef();
+ providers_.push_back(browser_provider);
+ TaskManagerTabContentsResourceProvider* wc_provider =
+ new TaskManagerTabContentsResourceProvider(task_manager);
+ wc_provider->AddRef();
+ providers_.push_back(wc_provider);
+ TaskManagerChildProcessResourceProvider* child_process_provider =
+ new TaskManagerChildProcessResourceProvider(task_manager);
+ child_process_provider->AddRef();
+ providers_.push_back(child_process_provider);
+ TaskManagerExtensionProcessResourceProvider* extension_process_provider =
+ new TaskManagerExtensionProcessResourceProvider(task_manager);
+ extension_process_provider->AddRef();
+ providers_.push_back(extension_process_provider);
+ TaskManagerNotificationResourceProvider* notification_provider =
+ new TaskManagerNotificationResourceProvider(task_manager);
+ notification_provider->AddRef();
+ providers_.push_back(notification_provider);
+}
+
+TaskManagerModel::~TaskManagerModel() {
+ for (ResourceProviderList::iterator iter = providers_.begin();
+ iter != providers_.end(); ++iter) {
+ (*iter)->Release();
+ }
+}
+
+int TaskManagerModel::ResourceCount() const {
+ return resources_.size();
+}
+
+void TaskManagerModel::AddObserver(TaskManagerModelObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void TaskManagerModel::RemoveObserver(TaskManagerModelObserver* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+string16 TaskManagerModel::GetResourceTitle(int index) const {
+ DCHECK(index < ResourceCount());
+ return WideToUTF16Hack(resources_[index]->GetTitle());
+}
+
+string16 TaskManagerModel::GetResourceNetworkUsage(int index) const {
+ DCHECK(index < ResourceCount());
+ int64 net_usage = GetNetworkUsage(resources_[index]);
+ if (net_usage == -1)
+ return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
+ if (net_usage == 0)
+ return ASCIIToUTF16("0");
+ string16 net_byte = FormatSpeed(net_usage, GetByteDisplayUnits(net_usage),
+ true);
+ // Force number string to have LTR directionality.
+ return base::i18n::GetDisplayStringInLTRDirectionality(net_byte);
+}
+
+string16 TaskManagerModel::GetResourceCPUUsage(int index) const {
+ DCHECK(index < ResourceCount());
+ return WideToUTF16Hack(StringPrintf(
+#if defined(OS_MACOSX)
+ // Activity Monitor shows %cpu with one decimal digit -- be
+ // consistent with that.
+ L"%.1f",
+#else
+ L"%.0f",
+#endif
+ GetCPUUsage(resources_[index])));
+}
+
+string16 TaskManagerModel::GetResourcePrivateMemory(int index) const {
+ size_t private_mem;
+ if (!GetPrivateMemory(index, &private_mem))
+ return ASCIIToUTF16("N/A");
+ return GetMemCellText(private_mem);
+}
+
+string16 TaskManagerModel::GetResourceSharedMemory(int index) const {
+ size_t shared_mem;
+ if (!GetSharedMemory(index, &shared_mem))
+ return ASCIIToUTF16("N/A");
+ return GetMemCellText(shared_mem);
+}
+
+string16 TaskManagerModel::GetResourcePhysicalMemory(int index) const {
+ size_t phys_mem;
+ GetPhysicalMemory(index, &phys_mem);
+ return GetMemCellText(phys_mem);
+}
+
+string16 TaskManagerModel::GetResourceProcessId(int index) const {
+ DCHECK(index < ResourceCount());
+ return base::IntToString16(base::GetProcId(resources_[index]->GetProcess()));
+}
+
+string16 TaskManagerModel::GetResourceGoatsTeleported(int index) const {
+ DCHECK(index < ResourceCount());
+ return base::FormatNumber(GetGoatsTeleported(index));
+}
+
+string16 TaskManagerModel::GetResourceWebCoreImageCacheSize(
+ int index) const {
+ DCHECK(index < ResourceCount());
+ if (!resources_[index]->ReportsCacheStats())
+ return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
+ const WebKit::WebCache::ResourceTypeStats stats(
+ resources_[index]->GetWebCoreCacheStats());
+ return FormatStatsSize(stats.images);
+}
+
+string16 TaskManagerModel::GetResourceWebCoreScriptsCacheSize(
+ int index) const {
+ DCHECK(index < ResourceCount());
+ if (!resources_[index]->ReportsCacheStats())
+ return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
+ const WebKit::WebCache::ResourceTypeStats stats(
+ resources_[index]->GetWebCoreCacheStats());
+ return FormatStatsSize(stats.scripts);
+}
+
+string16 TaskManagerModel::GetResourceWebCoreCSSCacheSize(
+ int index) const {
+ DCHECK(index < ResourceCount());
+ if (!resources_[index]->ReportsCacheStats())
+ return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
+ const WebKit::WebCache::ResourceTypeStats stats(
+ resources_[index]->GetWebCoreCacheStats());
+ return FormatStatsSize(stats.cssStyleSheets);
+}
+
+string16 TaskManagerModel::GetResourceSqliteMemoryUsed(int index) const {
+ DCHECK(index < ResourceCount());
+ if (!resources_[index]->ReportsSqliteMemoryUsed())
+ return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
+ return GetMemCellText(resources_[index]->SqliteMemoryUsedBytes());
+}
+
+string16 TaskManagerModel::GetResourceV8MemoryAllocatedSize(
+ int index) const {
+ if (!resources_[index]->ReportsV8MemoryStats())
+ return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT);
+ return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_CACHE_SIZE_CELL_TEXT,
+ FormatBytes(resources_[index]->GetV8MemoryAllocated(),
+ DATA_UNITS_KIBIBYTE,
+ false),
+ FormatBytes(resources_[index]->GetV8MemoryUsed(),
+ DATA_UNITS_KIBIBYTE,
+ false));
+}
+
+bool TaskManagerModel::IsResourceFirstInGroup(int index) const {
+ DCHECK(index < ResourceCount());
+ TaskManager::Resource* resource = resources_[index];
+ GroupMap::const_iterator iter = group_map_.find(resource->GetProcess());
+ DCHECK(iter != group_map_.end());
+ const ResourceList* group = iter->second;
+ return ((*group)[0] == resource);
+}
+
+SkBitmap TaskManagerModel::GetResourceIcon(int index) const {
+ DCHECK(index < ResourceCount());
+ SkBitmap icon = resources_[index]->GetIcon();
+ if (!icon.isNull())
+ return icon;
+
+ static SkBitmap* default_icon = ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_DEFAULT_FAVICON);
+ return *default_icon;
+}
+
+std::pair<int, int> TaskManagerModel::GetGroupRangeForResource(int index)
+ const {
+ DCHECK(index < ResourceCount());
+ TaskManager::Resource* resource = resources_[index];
+ GroupMap::const_iterator group_iter =
+ group_map_.find(resource->GetProcess());
+ DCHECK(group_iter != group_map_.end());
+ ResourceList* group = group_iter->second;
+ DCHECK(group);
+ if (group->size() == 1) {
+ return std::make_pair(index, 1);
+ } else {
+ for (int i = index; i >= 0; --i) {
+ if (resources_[i] == (*group)[0])
+ return std::make_pair(i, group->size());
+ }
+ NOTREACHED();
+ return std::make_pair(-1, -1);
+ }
+}
+
+int TaskManagerModel::CompareValues(int row1, int row2, int col_id) const {
+ DCHECK(row1 < ResourceCount() && row2 < ResourceCount());
+ switch (col_id) {
+ case IDS_TASK_MANAGER_PAGE_COLUMN: {
+ // Let's do the default, string compare on the resource title.
+ static icu::Collator* collator = NULL;
+ if (!collator) {
+ UErrorCode create_status = U_ZERO_ERROR;
+ collator = icu::Collator::createInstance(create_status);
+ if (!U_SUCCESS(create_status)) {
+ collator = NULL;
+ NOTREACHED();
+ }
+ }
+ string16 title1 = GetResourceTitle(row1);
+ string16 title2 = GetResourceTitle(row2);
+ UErrorCode compare_status = U_ZERO_ERROR;
+ UCollationResult compare_result = collator->compare(
+ static_cast<const UChar*>(title1.c_str()),
+ static_cast<int>(title1.length()),
+ static_cast<const UChar*>(title2.c_str()),
+ static_cast<int>(title2.length()),
+ compare_status);
+ DCHECK(U_SUCCESS(compare_status));
+ return compare_result;
+ }
+
+ case IDS_TASK_MANAGER_NET_COLUMN:
+ return ValueCompare<int64>(GetNetworkUsage(resources_[row1]),
+ GetNetworkUsage(resources_[row2]));
+
+ case IDS_TASK_MANAGER_CPU_COLUMN:
+ return ValueCompare<double>(GetCPUUsage(resources_[row1]),
+ GetCPUUsage(resources_[row2]));
+
+ case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: {
+ size_t value1;
+ size_t value2;
+ if (!GetPrivateMemory(row1, &value1) || !GetPrivateMemory(row2, &value2))
+ return 0;
+ return ValueCompare<size_t>(value1, value2);
+ }
+
+ case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: {
+ size_t value1;
+ size_t value2;
+ if (!GetSharedMemory(row1, &value1) || !GetSharedMemory(row2, &value2))
+ return 0;
+ return ValueCompare<size_t>(value1, value2);
+ }
+
+ case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN: {
+ size_t value1;
+ size_t value2;
+ if (!GetPhysicalMemory(row1, &value1) ||
+ !GetPhysicalMemory(row2, &value2))
+ return 0;
+ return ValueCompare<size_t>(value1, value2);
+ }
+
+ case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: {
+ int proc1_id = base::GetProcId(resources_[row1]->GetProcess());
+ int proc2_id = base::GetProcId(resources_[row2]->GetProcess());
+ return ValueCompare<int>(proc1_id, proc2_id);
+ }
+
+ case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN:
+ case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN:
+ case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: {
+ WebKit::WebCache::ResourceTypeStats stats1 = { { 0 } };
+ WebKit::WebCache::ResourceTypeStats stats2 = { { 0 } };
+ if (resources_[row1]->ReportsCacheStats())
+ stats1 = resources_[row1]->GetWebCoreCacheStats();
+ if (resources_[row2]->ReportsCacheStats())
+ stats2 = resources_[row2]->GetWebCoreCacheStats();
+ if (IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN == col_id)
+ return ValueCompare<size_t>(stats1.images.size, stats2.images.size);
+ if (IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN == col_id)
+ return ValueCompare<size_t>(stats1.scripts.size, stats2.scripts.size);
+ DCHECK_EQ(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, col_id);
+ return ValueCompare<size_t>(stats1.cssStyleSheets.size,
+ stats2.cssStyleSheets.size);
+ }
+
+ case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN: {
+ return ValueCompare<int>(GetGoatsTeleported(row1),
+ GetGoatsTeleported(row2));
+ }
+
+ case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: {
+ size_t value1;
+ size_t value2;
+ bool reports_v8_memory1 = GetV8Memory(row1, &value1);
+ bool reports_v8_memory2 = GetV8Memory(row2, &value2);
+ if (reports_v8_memory1 == reports_v8_memory2)
+ return ValueCompare<size_t>(value1, value2);
+ else
+ return reports_v8_memory1 ? 1 : -1;
+ }
+
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+base::ProcessHandle TaskManagerModel::GetResourceProcessHandle(int index)
+ const {
+ DCHECK(index < ResourceCount());
+ return resources_[index]->GetProcess();
+}
+
+TabContents* TaskManagerModel::GetResourceTabContents(int index) const {
+ DCHECK(index < ResourceCount());
+ return resources_[index]->GetTabContents();
+}
+
+const Extension* TaskManagerModel::GetResourceExtension(int index) const {
+ DCHECK(index < ResourceCount());
+ return resources_[index]->GetExtension();
+}
+
+int64 TaskManagerModel::GetNetworkUsage(TaskManager::Resource* resource)
+ const {
+ int64 net_usage = GetNetworkUsageForResource(resource);
+ if (net_usage == 0 && !resource->SupportNetworkUsage())
+ return -1;
+ return net_usage;
+}
+
+double TaskManagerModel::GetCPUUsage(TaskManager::Resource* resource) const {
+ CPUUsageMap::const_iterator iter =
+ cpu_usage_map_.find(resource->GetProcess());
+ if (iter == cpu_usage_map_.end())
+ return 0;
+ return iter->second;
+}
+
+bool TaskManagerModel::GetPrivateMemory(int index, size_t* result) const {
+ base::ProcessHandle handle = resources_[index]->GetProcess();
+ MemoryUsageMap::const_iterator iter = memory_usage_map_.find(handle);
+ if (iter == memory_usage_map_.end()) {
+ std::pair<size_t, size_t> usage;
+ if (!GetAndCacheMemoryMetrics(handle, &usage))
+ return false;
+
+ *result = usage.first;
+ } else {
+ *result = iter->second.first;
+ }
+
+ return true;
+}
+
+bool TaskManagerModel::GetSharedMemory(int index, size_t* result) const {
+ base::ProcessHandle handle = resources_[index]->GetProcess();
+ MemoryUsageMap::const_iterator iter = memory_usage_map_.find(handle);
+ if (iter == memory_usage_map_.end()) {
+ std::pair<size_t, size_t> usage;
+ if (!GetAndCacheMemoryMetrics(handle, &usage))
+ return false;
+
+ *result = usage.second;
+ } else {
+ *result = iter->second.second;
+ }
+
+ return true;
+}
+
+bool TaskManagerModel::GetPhysicalMemory(int index, size_t* result) const {
+ *result = 0;
+ base::ProcessMetrics* process_metrics;
+ if (!GetProcessMetricsForRow(index, &process_metrics))
+ return false;
+ base::WorkingSetKBytes ws_usage;
+ if (!process_metrics->GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ // Memory = working_set.private + working_set.shareable.
+ // We exclude the shared memory.
+ size_t total_bytes = process_metrics->GetWorkingSetSize();
+ total_bytes -= ws_usage.shared * 1024;
+ *result = total_bytes;
+ return true;
+}
+
+bool TaskManagerModel::GetV8Memory(int index, size_t* result) const {
+ *result = 0;
+ if (!resources_[index]->ReportsV8MemoryStats())
+ return false;
+
+ *result = resources_[index]->GetV8MemoryAllocated();
+ return true;
+}
+
+int TaskManagerModel::GetGoatsTeleported(int index) const {
+ int seed = goat_salt_ * (index + 1);
+ return (seed >> 16) & 255;
+}
+
+string16 TaskManagerModel::GetMemCellText(int64 number) const {
+#if !defined(OS_MACOSX)
+ string16 str = base::FormatNumber(number / 1024);
+
+ // Adjust number string if necessary.
+ base::i18n::AdjustStringForLocaleDirection(str, &str);
+ return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_MEM_CELL_TEXT, str);
+#else
+ // System expectation is to show "100 KB", "200 MB", etc.
+ // TODO(thakis): Switch to metric units (as opposed to powers of two).
+ return FormatBytes(number, GetByteDisplayUnits(number), /*show_units=*/true);
+#endif
+}
+
+void TaskManagerModel::StartUpdating() {
+ DCHECK_NE(TASK_PENDING, update_state_);
+
+ // If update_state_ is STOPPING, it means a task is still pending. Setting
+ // it to TASK_PENDING ensures the tasks keep being posted (by Refresh()).
+ if (update_state_ == IDLE) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &TaskManagerModel::Refresh),
+ kUpdateTimeMs);
+ }
+ update_state_ = TASK_PENDING;
+
+ // Register jobs notifications so we can compute network usage (it must be
+ // done from the IO thread).
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &TaskManagerModel::RegisterForJobDoneNotifications));
+
+ // Notify resource providers that we are updating.
+ for (ResourceProviderList::iterator iter = providers_.begin();
+ iter != providers_.end(); ++iter) {
+ (*iter)->StartUpdating();
+ }
+}
+
+void TaskManagerModel::StopUpdating() {
+ DCHECK_EQ(TASK_PENDING, update_state_);
+ update_state_ = STOPPING;
+
+ // Notify resource providers that we are done updating.
+ for (ResourceProviderList::const_iterator iter = providers_.begin();
+ iter != providers_.end(); ++iter) {
+ (*iter)->StopUpdating();
+ }
+
+ // Unregister jobs notification (must be done from the IO thread).
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &TaskManagerModel::UnregisterForJobDoneNotifications));
+}
+
+void TaskManagerModel::AddResourceProvider(
+ TaskManager::ResourceProvider* provider) {
+ DCHECK(provider);
+ providers_.push_back(provider);
+}
+
+void TaskManagerModel::RemoveResourceProvider(
+ TaskManager::ResourceProvider* provider) {
+ DCHECK(provider);
+ ResourceProviderList::iterator iter = std::find(providers_.begin(),
+ providers_.end(),
+ provider);
+ DCHECK(iter != providers_.end());
+ providers_.erase(iter);
+}
+
+void TaskManagerModel::RegisterForJobDoneNotifications() {
+ g_url_request_job_tracker.AddObserver(this);
+}
+
+void TaskManagerModel::UnregisterForJobDoneNotifications() {
+ g_url_request_job_tracker.RemoveObserver(this);
+}
+
+void TaskManagerModel::AddResource(TaskManager::Resource* resource) {
+ base::ProcessHandle process = resource->GetProcess();
+
+ ResourceList* group_entries = NULL;
+ GroupMap::const_iterator group_iter = group_map_.find(process);
+ int new_entry_index = 0;
+ if (group_iter == group_map_.end()) {
+ group_entries = new ResourceList();
+ group_map_[process] = group_entries;
+ group_entries->push_back(resource);
+
+ // Not part of a group, just put at the end of the list.
+ resources_.push_back(resource);
+ new_entry_index = static_cast<int>(resources_.size() - 1);
+ } else {
+ group_entries = group_iter->second;
+ group_entries->push_back(resource);
+
+ // Insert the new entry right after the last entry of its group.
+ ResourceList::iterator iter =
+ std::find(resources_.begin(),
+ resources_.end(),
+ (*group_entries)[group_entries->size() - 2]);
+ DCHECK(iter != resources_.end());
+ new_entry_index = static_cast<int>(iter - resources_.begin());
+ resources_.insert(++iter, resource);
+ }
+
+ // Create the ProcessMetrics for this process if needed (not in map).
+ if (metrics_map_.find(process) == metrics_map_.end()) {
+ base::ProcessMetrics* pm =
+#if !defined(OS_MACOSX)
+ base::ProcessMetrics::CreateProcessMetrics(process);
+#else
+ base::ProcessMetrics::CreateProcessMetrics(process,
+ MachBroker::instance());
+#endif
+
+ metrics_map_[process] = pm;
+ }
+
+ // Notify the table that the contents have changed for it to redraw.
+ FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
+ OnItemsAdded(new_entry_index, 1));
+}
+
+void TaskManagerModel::RemoveResource(TaskManager::Resource* resource) {
+ base::ProcessHandle process = resource->GetProcess();
+
+ // Find the associated group.
+ GroupMap::iterator group_iter = group_map_.find(process);
+ DCHECK(group_iter != group_map_.end());
+ ResourceList* group_entries = group_iter->second;
+
+ // Remove the entry from the group map.
+ ResourceList::iterator iter = std::find(group_entries->begin(),
+ group_entries->end(),
+ resource);
+ DCHECK(iter != group_entries->end());
+ group_entries->erase(iter);
+
+ // If there are no more entries for that process, do the clean-up.
+ if (group_entries->empty()) {
+ delete group_entries;
+ group_map_.erase(process);
+
+ // Nobody is using this process, we don't need the process metrics anymore.
+ MetricsMap::iterator pm_iter = metrics_map_.find(process);
+ DCHECK(pm_iter != metrics_map_.end());
+ if (pm_iter != metrics_map_.end()) {
+ delete pm_iter->second;
+ metrics_map_.erase(process);
+ }
+ // And we don't need the CPU usage anymore either.
+ CPUUsageMap::iterator cpu_iter = cpu_usage_map_.find(process);
+ if (cpu_iter != cpu_usage_map_.end())
+ cpu_usage_map_.erase(cpu_iter);
+ }
+
+ // Remove the entry from the model list.
+ iter = std::find(resources_.begin(), resources_.end(), resource);
+ DCHECK(iter != resources_.end());
+ int index = static_cast<int>(iter - resources_.begin());
+ resources_.erase(iter);
+
+ // Remove the entry from the network maps.
+ ResourceValueMap::iterator net_iter =
+ current_byte_count_map_.find(resource);
+ if (net_iter != current_byte_count_map_.end())
+ current_byte_count_map_.erase(net_iter);
+ net_iter = displayed_network_usage_map_.find(resource);
+ if (net_iter != displayed_network_usage_map_.end())
+ displayed_network_usage_map_.erase(net_iter);
+
+ // Notify the table that the contents have changed.
+ FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
+ OnItemsRemoved(index, 1));
+}
+
+void TaskManagerModel::Clear() {
+ int size = ResourceCount();
+ if (size > 0) {
+ resources_.clear();
+
+ // Clear the groups.
+ for (GroupMap::iterator iter = group_map_.begin();
+ iter != group_map_.end(); ++iter) {
+ delete iter->second;
+ }
+ group_map_.clear();
+
+ // Clear the process related info.
+ for (MetricsMap::iterator iter = metrics_map_.begin();
+ iter != metrics_map_.end(); ++iter) {
+ delete iter->second;
+ }
+ metrics_map_.clear();
+ cpu_usage_map_.clear();
+
+ // Clear the network maps.
+ current_byte_count_map_.clear();
+ displayed_network_usage_map_.clear();
+
+ FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
+ OnItemsRemoved(0, size));
+ }
+}
+
+void TaskManagerModel::NotifyResourceTypeStats(
+ base::ProcessId renderer_id,
+ const WebKit::WebCache::ResourceTypeStats& stats) {
+ for (ResourceList::iterator it = resources_.begin();
+ it != resources_.end(); ++it) {
+ if (base::GetProcId((*it)->GetProcess()) == renderer_id) {
+ (*it)->NotifyResourceTypeStats(stats);
+ }
+ }
+}
+
+void TaskManagerModel::NotifyV8HeapStats(base::ProcessId renderer_id,
+ size_t v8_memory_allocated,
+ size_t v8_memory_used) {
+ for (ResourceList::iterator it = resources_.begin();
+ it != resources_.end(); ++it) {
+ if (base::GetProcId((*it)->GetProcess()) == renderer_id) {
+ (*it)->NotifyV8HeapStats(v8_memory_allocated, v8_memory_used);
+ }
+ }
+}
+
+void TaskManagerModel::Refresh() {
+ DCHECK_NE(IDLE, update_state_);
+
+ if (update_state_ == STOPPING) {
+ // We have been asked to stop.
+ update_state_ = IDLE;
+ return;
+ }
+
+ goat_salt_ = rand();
+
+ // Compute the CPU usage values.
+ // Note that we compute the CPU usage for all resources (instead of doing it
+ // lazily) as process_util::GetCPUUsage() returns the CPU usage since the last
+ // time it was called, and not calling it everytime would skew the value the
+ // next time it is retrieved (as it would be for more than 1 cycle).
+ cpu_usage_map_.clear();
+ for (ResourceList::iterator iter = resources_.begin();
+ iter != resources_.end(); ++iter) {
+ base::ProcessHandle process = (*iter)->GetProcess();
+ CPUUsageMap::iterator cpu_iter = cpu_usage_map_.find(process);
+ if (cpu_iter != cpu_usage_map_.end())
+ continue; // Already computed.
+
+ MetricsMap::iterator metrics_iter = metrics_map_.find(process);
+ DCHECK(metrics_iter != metrics_map_.end());
+ cpu_usage_map_[process] = metrics_iter->second->GetCPUUsage();
+ }
+
+ // Clear the memory values so they can be querried lazily.
+ memory_usage_map_.clear();
+
+ // Compute the new network usage values.
+ displayed_network_usage_map_.clear();
+ for (ResourceValueMap::iterator iter = current_byte_count_map_.begin();
+ iter != current_byte_count_map_.end(); ++iter) {
+ if (kUpdateTimeMs > 1000) {
+ int divider = (kUpdateTimeMs / 1000);
+ displayed_network_usage_map_[iter->first] = iter->second / divider;
+ } else {
+ displayed_network_usage_map_[iter->first] = iter->second *
+ (1000 / kUpdateTimeMs);
+ }
+
+ // Then we reset the current byte count.
+ iter->second = 0;
+ }
+
+ // Let resources update themselves if they need to.
+ for (ResourceList::iterator iter = resources_.begin();
+ iter != resources_.end(); ++iter) {
+ (*iter)->Refresh();
+ }
+
+ if (!resources_.empty()) {
+ FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_,
+ OnItemsChanged(0, ResourceCount()));
+ }
+
+ // Schedule the next update.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &TaskManagerModel::Refresh),
+ kUpdateTimeMs);
+}
+
+int64 TaskManagerModel::GetNetworkUsageForResource(
+ TaskManager::Resource* resource) const {
+ ResourceValueMap::const_iterator iter =
+ displayed_network_usage_map_.find(resource);
+ if (iter == displayed_network_usage_map_.end())
+ return 0;
+ return iter->second;
+}
+
+void TaskManagerModel::BytesRead(BytesReadParam param) {
+ if (update_state_ != TASK_PENDING) {
+ // A notification sneaked in while we were stopping the updating, just
+ // ignore it.
+ return;
+ }
+
+ if (param.byte_count == 0) {
+ // Nothing to do if no bytes were actually read.
+ return;
+ }
+
+ // TODO(jcampan): this should be improved once we have a better way of
+ // linking a network notification back to the object that initiated it.
+ TaskManager::Resource* resource = NULL;
+ for (ResourceProviderList::iterator iter = providers_.begin();
+ iter != providers_.end(); ++iter) {
+ resource = (*iter)->GetResource(param.origin_child_id,
+ param.render_process_host_child_id,
+ param.routing_id);
+ if (resource)
+ break;
+ }
+ if (resource == NULL) {
+ // We may not have that resource anymore (example: close a tab while a
+ // a network resource is being retrieved), in which case we just ignore the
+ // notification.
+ return;
+ }
+
+ // We do support network usage, mark the resource as such so it can report 0
+ // instead of N/A.
+ if (!resource->SupportNetworkUsage())
+ resource->SetSupportNetworkUsage();
+
+ ResourceValueMap::const_iterator iter_res =
+ current_byte_count_map_.find(resource);
+ if (iter_res == current_byte_count_map_.end())
+ current_byte_count_map_[resource] = param.byte_count;
+ else
+ current_byte_count_map_[resource] = iter_res->second + param.byte_count;
+}
+
+
+// In order to retrieve the network usage, we register for URLRequestJob
+// notifications. Every time we get notified some bytes were read we bump a
+// counter of read bytes for the associated resource. When the timer ticks,
+// we'll compute the actual network usage (see the Refresh method).
+void TaskManagerModel::OnJobAdded(URLRequestJob* job) {
+}
+
+void TaskManagerModel::OnJobRemoved(URLRequestJob* job) {
+}
+
+void TaskManagerModel::OnJobDone(URLRequestJob* job,
+ const URLRequestStatus& status) {
+}
+
+void TaskManagerModel::OnJobRedirect(URLRequestJob* job,
+ const GURL& location,
+ int status_code) {
+}
+
+void TaskManagerModel::OnBytesRead(URLRequestJob* job, const char* buf,
+ int byte_count) {
+ int render_process_host_child_id = -1, routing_id = -1;
+ ResourceDispatcherHost::RenderViewForRequest(job->request(),
+ &render_process_host_child_id,
+ &routing_id);
+ // This happens in the IO thread, post it to the UI thread.
+ int origin_child_id =
+ chrome_browser_net::GetOriginProcessUniqueIDForRequest(job->request());
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &TaskManagerModel::BytesRead,
+ BytesReadParam(origin_child_id,
+ render_process_host_child_id,
+ routing_id, byte_count)));
+}
+
+bool TaskManagerModel::GetProcessMetricsForRow(
+ int row, base::ProcessMetrics** proc_metrics) const {
+ DCHECK(row < ResourceCount());
+ *proc_metrics = NULL;
+
+ MetricsMap::const_iterator iter =
+ metrics_map_.find(resources_[row]->GetProcess());
+ if (iter == metrics_map_.end())
+ return false;
+ *proc_metrics = iter->second;
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManager class
+////////////////////////////////////////////////////////////////////////////////
+
+// static
+void TaskManager::RegisterPrefs(PrefService* prefs) {
+ prefs->RegisterDictionaryPref(prefs::kTaskManagerWindowPlacement);
+}
+
+TaskManager::TaskManager()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TaskManagerModel(this))) {
+}
+
+TaskManager::~TaskManager() {
+}
+
+bool TaskManager::IsBrowserProcess(int index) const {
+ // If some of the selection is out of bounds, ignore. This may happen when
+ // killing a process that manages several pages.
+ return index < model_->ResourceCount() &&
+ model_->GetResourceProcessHandle(index) ==
+ base::GetCurrentProcessHandle();
+}
+
+void TaskManager::KillProcess(int index) {
+ base::ProcessHandle process = model_->GetResourceProcessHandle(index);
+ DCHECK(process);
+ if (process != base::GetCurrentProcessHandle())
+ base::KillProcess(process, base::PROCESS_END_KILLED_BY_USER, false);
+}
+
+void TaskManager::ActivateProcess(int index) {
+ // GetResourceTabContents returns a pointer to the relevant tab contents for
+ // the resource. If the index doesn't correspond to a Tab (i.e. refers to
+ // the Browser process or a plugin), GetTabContents will return NULL.
+ TabContents* chosen_tab_contents = model_->GetResourceTabContents(index);
+ if (chosen_tab_contents)
+ chosen_tab_contents->Activate();
+}
+
+void TaskManager::AddResourceProvider(ResourceProvider* provider) {
+ model_->AddResourceProvider(provider);
+}
+
+void TaskManager::RemoveResourceProvider(ResourceProvider* provider) {
+ model_->RemoveResourceProvider(provider);
+}
+
+void TaskManager::AddResource(Resource* resource) {
+ model_->AddResource(resource);
+}
+
+void TaskManager::RemoveResource(Resource* resource) {
+ model_->RemoveResource(resource);
+}
+
+void TaskManager::OnWindowClosed() {
+ model_->StopUpdating();
+ model_->Clear();
+}
+
+// static
+TaskManager* TaskManager::GetInstance() {
+ return Singleton<TaskManager>::get();
+}
+
+void TaskManager::OpenAboutMemory() {
+ Browser* browser = BrowserList::GetLastActive();
+
+ if (!browser) {
+ // On OS X, the task manager can be open without any open browser windows.
+ if (!g_browser_process ||
+ !g_browser_process->profile_manager() ||
+ g_browser_process->profile_manager()->begin() ==
+ g_browser_process->profile_manager()->end())
+ return;
+ browser = Browser::Create(*g_browser_process->profile_manager()->begin());
+ browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_WINDOW,
+ PageTransition::LINK);
+ } else {
+ browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_FOREGROUND_TAB,
+ PageTransition::LINK);
+
+ // In case the browser window is minimzed, show it. If |browser| is a
+ // non-tabbed window, the call to OpenURL above will have opened a
+ // TabContents in a tabbed browser, so we need to grab it with GetLastActive
+ // before the call to show().
+ if (browser->type() & (Browser::TYPE_APP |
+ Browser::TYPE_APP_PANEL |
+ Browser::TYPE_DEVTOOLS |
+ Browser::TYPE_POPUP)) {
+ browser = BrowserList::GetLastActive();
+ DCHECK(browser);
+ }
+
+ browser->window()->Show();
+ }
+}
+
+bool TaskManagerModel::GetAndCacheMemoryMetrics(
+ base::ProcessHandle handle,
+ std::pair<size_t, size_t>* usage) const {
+ MetricsMap::const_iterator iter = metrics_map_.find(handle);
+ if (iter == metrics_map_.end())
+ return false;
+
+ if (!iter->second->GetMemoryBytes(&usage->first, &usage->second))
+ return false;
+
+ memory_usage_map_.insert(std::make_pair(handle, *usage));
+ return true;
+}
diff --git a/chrome/browser/task_manager/task_manager.h b/chrome/browser/task_manager/task_manager.h
new file mode 100644
index 0000000..b7b6890
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager.h
@@ -0,0 +1,415 @@
+// Copyright (c) 2010 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 CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_H_
+#define CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/lock.h"
+#include "base/observer_list.h"
+#include "base/process_util.h"
+#include "base/ref_counted.h"
+#include "base/singleton.h"
+#include "base/string16.h"
+#include "base/timer.h"
+#include "chrome/browser/renderer_host/web_cache_manager.h"
+#include "net/url_request/url_request_job_tracker.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCache.h"
+
+class Extension;
+class SkBitmap;
+class TabContents;
+class TaskManagerModel;
+
+namespace base {
+class ProcessMetrics;
+}
+
+// This class is a singleton.
+class TaskManager {
+ public:
+ // A resource represents one row in the task manager.
+ // Resources from similar processes are grouped together by the task manager.
+ class Resource {
+ public:
+ virtual ~Resource() {}
+
+ virtual std::wstring GetTitle() const = 0;
+ virtual SkBitmap GetIcon() const = 0;
+ virtual base::ProcessHandle GetProcess() const = 0;
+
+ virtual bool ReportsCacheStats() const { return false; }
+ virtual WebKit::WebCache::ResourceTypeStats GetWebCoreCacheStats() const {
+ return WebKit::WebCache::ResourceTypeStats();
+ }
+
+ virtual bool ReportsSqliteMemoryUsed() const { return false; }
+ virtual size_t SqliteMemoryUsedBytes() const { return 0; }
+
+ // Return extension associated with the resource, or NULL
+ // if not applicable.
+ virtual const Extension* GetExtension() const { return NULL; }
+
+ virtual bool ReportsV8MemoryStats() const { return false; }
+ virtual size_t GetV8MemoryAllocated() const { return 0; }
+ virtual size_t GetV8MemoryUsed() const { return 0; }
+
+ // A helper function for ActivateFocusedTab. Returns NULL by default
+ // because not all resources have an associated tab.
+ virtual TabContents* GetTabContents() const { return NULL; }
+
+ // Whether this resource does report the network usage accurately.
+ // This controls whether 0 or N/A is displayed when no bytes have been
+ // reported as being read. This is because some plugins do not report the
+ // bytes read and we don't want to display a misleading 0 value in that
+ // case.
+ virtual bool SupportNetworkUsage() const = 0;
+
+ // Called when some bytes have been read and support_network_usage returns
+ // false (meaning we do have network usage support).
+ virtual void SetSupportNetworkUsage() = 0;
+
+ // The TaskManagerModel periodically refreshes its data and call this
+ // on all live resources.
+ virtual void Refresh() {}
+
+ virtual void NotifyResourceTypeStats(
+ const WebKit::WebCache::ResourceTypeStats& stats) {}
+ virtual void NotifyV8HeapStats(size_t v8_memory_allocated,
+ size_t v8_memory_used) {}
+ };
+
+ // ResourceProviders are responsible for adding/removing resources to the task
+ // manager. The task manager notifies the ResourceProvider that it is ready
+ // to receive resource creation/termination notifications with a call to
+ // StartUpdating(). At that point, the resource provider should call
+ // AddResource with all the existing resources, and after that it should call
+ // AddResource/RemoveResource as resources are created/terminated.
+ // The provider remains the owner of the resource objects and is responsible
+ // for deleting them (when StopUpdating() is called).
+ // After StopUpdating() is called the provider should also stop reporting
+ // notifications to the task manager.
+ // Note: ResourceProviders have to be ref counted as they are used in
+ // MessageLoop::InvokeLater().
+ class ResourceProvider : public base::RefCountedThreadSafe<ResourceProvider> {
+ public:
+ // Should return the resource associated to the specified ids, or NULL if
+ // the resource does not belong to this provider.
+ virtual TaskManager::Resource* GetResource(int process_id,
+ int render_process_host_id,
+ int routing_id) = 0;
+ virtual void StartUpdating() = 0;
+ virtual void StopUpdating() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<ResourceProvider>;
+
+ virtual ~ResourceProvider() {}
+ };
+
+ static void RegisterPrefs(PrefService* prefs);
+
+ // Returns true if the process at the specified index is the browser process.
+ bool IsBrowserProcess(int index) const;
+
+ // Terminates the process at the specified index.
+ void KillProcess(int index);
+
+ // Activates the browser tab associated with the process in the specified
+ // index.
+ void ActivateProcess(int index);
+
+ void AddResourceProvider(ResourceProvider* provider);
+ void RemoveResourceProvider(ResourceProvider* provider);
+
+ // These methods are invoked by the resource providers to add/remove resources
+ // to the Task Manager. Note that the resources are owned by the
+ // ResourceProviders and are not valid after StopUpdating() has been called
+ // on the ResourceProviders.
+ void AddResource(Resource* resource);
+ void RemoveResource(Resource* resource);
+
+ void OnWindowClosed();
+
+ // Returns the singleton instance (and initializes it if necessary).
+ static TaskManager* GetInstance();
+
+ TaskManagerModel* model() const { return model_.get(); }
+
+ void OpenAboutMemory();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, Basic);
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, Resources);
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, RefreshCalled);
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, Init);
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest, Sort);
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerWindowControllerTest,
+ SelectionAdaptsToSorting);
+
+ // Obtain an instance via GetInstance().
+ TaskManager();
+ friend struct DefaultSingletonTraits<TaskManager>;
+
+ ~TaskManager();
+
+ // The model used for gathering and processing task data. It is ref counted
+ // because it is passed as a parameter to MessageLoop::InvokeLater().
+ scoped_refptr<TaskManagerModel> model_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManager);
+};
+
+class TaskManagerModelObserver {
+ public:
+ virtual ~TaskManagerModelObserver() {}
+
+ // Invoked when the model has been completely changed.
+ virtual void OnModelChanged() = 0;
+
+ // Invoked when a range of items has changed.
+ virtual void OnItemsChanged(int start, int length) = 0;
+
+ // Invoked when new items are added.
+ virtual void OnItemsAdded(int start, int length) = 0;
+
+ // Invoked when a range of items has been removed.
+ virtual void OnItemsRemoved(int start, int length) = 0;
+};
+
+// The model that the TaskManager is using.
+class TaskManagerModel : public URLRequestJobTracker::JobObserver,
+ public base::RefCountedThreadSafe<TaskManagerModel> {
+ public:
+ explicit TaskManagerModel(TaskManager* task_manager);
+
+ void AddObserver(TaskManagerModelObserver* observer);
+ void RemoveObserver(TaskManagerModelObserver* observer);
+
+ // Returns number of registered resources.
+ int ResourceCount() const;
+
+ // Methods to return formatted resource information.
+ string16 GetResourceTitle(int index) const;
+ string16 GetResourceNetworkUsage(int index) const;
+ string16 GetResourceCPUUsage(int index) const;
+ string16 GetResourcePrivateMemory(int index) const;
+ string16 GetResourceSharedMemory(int index) const;
+ string16 GetResourcePhysicalMemory(int index) const;
+ string16 GetResourceProcessId(int index) const;
+ string16 GetResourceWebCoreImageCacheSize(int index) const;
+ string16 GetResourceWebCoreScriptsCacheSize(int index) const;
+ string16 GetResourceWebCoreCSSCacheSize(int index) const;
+ string16 GetResourceSqliteMemoryUsed(int index) const;
+ string16 GetResourceGoatsTeleported(int index) const;
+ string16 GetResourceV8MemoryAllocatedSize(int index) const;
+
+ // Returns true if the resource is first in its group (resources
+ // rendered by the same process are groupped together).
+ bool IsResourceFirstInGroup(int index) const;
+
+ // Returns icon to be used for resource (for example a favicon).
+ SkBitmap GetResourceIcon(int index) const;
+
+ // Returns a pair (start, length) of the group range of resource.
+ std::pair<int, int> GetGroupRangeForResource(int index) const;
+
+ // Compares values in column |col_id| and rows |row1|, |row2|.
+ // Returns -1 if value in |row1| is less than value in |row2|,
+ // 0 if they are equal, and 1 otherwise.
+ int CompareValues(int row1, int row2, int col_id) const;
+
+ // Returns process handle for given resource.
+ base::ProcessHandle GetResourceProcessHandle(int index) const;
+
+ // Returns TabContents of given resource or NULL if not applicable.
+ TabContents* GetResourceTabContents(int index) const;
+
+ // Returns Extension of given resource or NULL if not applicable.
+ const Extension* GetResourceExtension(int index) const;
+
+ // JobObserver methods:
+ void OnJobAdded(URLRequestJob* job);
+ void OnJobRemoved(URLRequestJob* job);
+ void OnJobDone(URLRequestJob* job, const URLRequestStatus& status);
+ void OnJobRedirect(URLRequestJob* job, const GURL& location, int status_code);
+ void OnBytesRead(URLRequestJob* job, const char* buf, int byte_count);
+
+ void AddResourceProvider(TaskManager::ResourceProvider* provider);
+ void RemoveResourceProvider(TaskManager::ResourceProvider* provider);
+
+ void AddResource(TaskManager::Resource* resource);
+ void RemoveResource(TaskManager::Resource* resource);
+
+ void StartUpdating();
+ void StopUpdating();
+
+ void Clear(); // Removes all items.
+
+ void NotifyResourceTypeStats(
+ base::ProcessId renderer_id,
+ const WebKit::WebCache::ResourceTypeStats& stats);
+
+ void NotifyV8HeapStats(base::ProcessId renderer_id,
+ size_t v8_memory_allocated,
+ size_t v8_memory_used);
+
+ private:
+ friend class base::RefCountedThreadSafe<TaskManagerModel>;
+ FRIEND_TEST_ALL_PREFIXES(TaskManagerTest, RefreshCalled);
+
+ ~TaskManagerModel();
+
+ enum UpdateState {
+ IDLE = 0, // Currently not updating.
+ TASK_PENDING, // An update task is pending.
+ STOPPING // A update task is pending and it should stop the update.
+ };
+
+ // This struct is used to exchange information between the io and ui threads.
+ struct BytesReadParam {
+ BytesReadParam(int origin_child_id,
+ int render_process_host_child_id,
+ int routing_id,
+ int byte_count)
+ : origin_child_id(origin_child_id),
+ render_process_host_child_id(render_process_host_child_id),
+ routing_id(routing_id),
+ byte_count(byte_count) {}
+
+ // This is the child ID of the originator of the request. It will often be
+ // the same as the render_process_host_child_id, but will be different when
+ // another sub-process like a plugin is routing requests through a renderer.
+ int origin_child_id;
+
+ // The child ID of the RenderProcessHist this request was routed through.
+ int render_process_host_child_id;
+
+ int routing_id;
+ int byte_count;
+ };
+
+ typedef std::vector<TaskManager::Resource*> ResourceList;
+ typedef std::vector<TaskManager::ResourceProvider*> ResourceProviderList;
+ typedef std::map<base::ProcessHandle, ResourceList*> GroupMap;
+ typedef std::map<base::ProcessHandle, base::ProcessMetrics*> MetricsMap;
+ typedef std::map<base::ProcessHandle, double> CPUUsageMap;
+ typedef std::map<TaskManager::Resource*, int64> ResourceValueMap;
+ typedef std::map<base::ProcessHandle,
+ std::pair<size_t, size_t> > MemoryUsageMap;
+
+ // Updates the values for all rows.
+ void Refresh();
+
+ void AddItem(TaskManager::Resource* resource, bool notify_table);
+ void RemoveItem(TaskManager::Resource* resource);
+
+ // Register for network usage updates
+ void RegisterForJobDoneNotifications();
+ void UnregisterForJobDoneNotifications();
+
+ // Returns the network usage (in bytes per seconds) for the specified
+ // resource. That's the value retrieved at the last timer's tick.
+ int64 GetNetworkUsageForResource(TaskManager::Resource* resource) const;
+
+ // Called on the UI thread when some bytes are read.
+ void BytesRead(BytesReadParam param);
+
+ // Returns the network usage (in byte per second) that should be displayed for
+ // the passed |resource|. -1 means the information is not available for that
+ // resource.
+ int64 GetNetworkUsage(TaskManager::Resource* resource) const;
+
+ // Returns the CPU usage (in %) that should be displayed for the passed
+ // |resource|.
+ double GetCPUUsage(TaskManager::Resource* resource) const;
+
+ // Gets the private memory (in bytes) that should be displayed for the passed
+ // resource index. Caches the result since this calculation can take time on
+ // some platforms.
+ bool GetPrivateMemory(int index, size_t* result) const;
+
+ // Gets the shared memory (in bytes) that should be displayed for the passed
+ // resource index. Caches the result since this calculation can take time on
+ // some platforms.
+ bool GetSharedMemory(int index, size_t* result) const;
+
+ // Gets the physical memory (in bytes) that should be displayed for the passed
+ // resource index.
+ bool GetPhysicalMemory(int index, size_t* result) const;
+
+ // Gets the amount of memory allocated for javascript. Returns false if the
+ // resource for the given row isn't a renderer.
+ bool GetV8Memory(int index, size_t* result) const;
+
+ // See design doc at http://go/at-teleporter for more information.
+ int GetGoatsTeleported(int index) const;
+
+ // Retrieves the ProcessMetrics for the resources at the specified row.
+ // Returns true if there was a ProcessMetrics available.
+ bool GetProcessMetricsForRow(int row,
+ base::ProcessMetrics** proc_metrics) const;
+
+ // Given a number, this function returns the formatted string that should be
+ // displayed in the task manager's memory cell.
+ string16 GetMemCellText(int64 number) const;
+
+ // Looks up the data for |handle| and puts it in the mutable cache
+ // |memory_usage_map_|.
+ bool GetAndCacheMemoryMetrics(base::ProcessHandle handle,
+ std::pair<size_t, size_t>* usage) const;
+
+ // The list of providers to the task manager. They are ref counted.
+ ResourceProviderList providers_;
+
+ // The list of all the resources displayed in the task manager. They are owned
+ // by the ResourceProviders.
+ ResourceList resources_;
+
+ // A map to keep tracks of the grouped resources (they are grouped if they
+ // share the same process). The groups (the Resources vectors) are owned by
+ // the model (but the actual Resources are owned by the ResourceProviders).
+ GroupMap group_map_;
+
+ // A map to retrieve the process metrics for a process. The ProcessMetrics are
+ // owned by the model.
+ MetricsMap metrics_map_;
+
+ // A map that keeps track of the number of bytes read per process since last
+ // tick. The Resources are owned by the ResourceProviders.
+ ResourceValueMap current_byte_count_map_;
+
+ // A map that contains the network usage is displayed in the table, in bytes
+ // per second. It is computed every time the timer ticks. The Resources are
+ // owned by the ResourceProviders.
+ ResourceValueMap displayed_network_usage_map_;
+
+ // A map that contains the CPU usage (in %) for a process since last refresh.
+ CPUUsageMap cpu_usage_map_;
+
+ // A map that contains the private/shared memory usage of the process. We
+ // cache this because the same APIs are called on linux and windows, and
+ // because the linux call takes >10ms to complete. This cache is cleared on
+ // every Refresh().
+ mutable MemoryUsageMap memory_usage_map_;
+
+ ObserverList<TaskManagerModelObserver> observer_list_;
+
+ // Whether we are currently in the process of updating.
+ UpdateState update_state_;
+
+ // A salt lick for the goats.
+ int goat_salt_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerModel);
+};
+
+#endif // CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_H_
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
new file mode 100644
index 0000000..bce58f6
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2010 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 "chrome/browser/task_manager/task_manager.h"
+
+#include "app/l10n_util.h"
+#include "base/file_path.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/extensions/crashed_extension_infobar.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/tab_contents/infobar_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/page_transition_types.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const FilePath::CharType* kTitle1File = FILE_PATH_LITERAL("title1.html");
+
+class ResourceChangeObserver : public TaskManagerModelObserver {
+ public:
+ ResourceChangeObserver(const TaskManagerModel* model,
+ int target_resource_count)
+ : model_(model),
+ target_resource_count_(target_resource_count) {
+ }
+
+ virtual void OnModelChanged() {
+ OnResourceChange();
+ }
+
+ virtual void OnItemsChanged(int start, int length) {
+ OnResourceChange();
+ }
+
+ virtual void OnItemsAdded(int start, int length) {
+ OnResourceChange();
+ }
+
+ virtual void OnItemsRemoved(int start, int length) {
+ OnResourceChange();
+ }
+
+ private:
+ void OnResourceChange() {
+ if (model_->ResourceCount() == target_resource_count_)
+ MessageLoopForUI::current()->Quit();
+ }
+
+ const TaskManagerModel* model_;
+ const int target_resource_count_;
+};
+
+} // namespace
+
+class TaskManagerBrowserTest : public ExtensionBrowserTest {
+ public:
+ TaskManagerModel* model() const {
+ return TaskManager::GetInstance()->model();
+ }
+
+ void WaitForResourceChange(int target_count) {
+ if (model()->ResourceCount() == target_count)
+ return;
+ ResourceChangeObserver observer(model(), target_count);
+ model()->AddObserver(&observer);
+ ui_test_utils::RunMessageLoop();
+ model()->RemoveObserver(&observer);
+ }
+};
+
+// Crashes on Vista (dbg): http://crbug.com/44991
+#if defined(OS_WIN)
+#define ShutdownWhileOpen DISABLED_ShutdownWhileOpen
+#endif
+// Regression test for http://crbug.com/13361
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, ShutdownWhileOpen) {
+ browser()->window()->ShowTaskManager();
+}
+
+// Times out on Vista; disabled to keep tests fast. http://crbug.com/44991
+#if defined(OS_WIN)
+#define NoticeTabContentsChanges DISABLED_NoticeTabContentsChanges
+#endif
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeTabContentsChanges) {
+ EXPECT_EQ(0, model()->ResourceCount());
+
+ // Show the task manager. This populates the model, and helps with debugging
+ // (you see the task manager).
+ browser()->window()->ShowTaskManager();
+
+ // Browser and the New Tab Page.
+ EXPECT_EQ(2, model()->ResourceCount());
+
+ // Open a new tab and make sure we notice that.
+ GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
+ FilePath(kTitle1File)));
+ Browser* browser_used = NULL;
+ browser()->AddTabWithURL(url, GURL(), PageTransition::TYPED, 0,
+ TabStripModel::ADD_SELECTED, NULL, std::string(),
+ &browser_used);
+ EXPECT_EQ(browser(), browser_used);
+ WaitForResourceChange(3);
+
+ // Close the tab and verify that we notice.
+ TabContents* first_tab = browser()->GetTabContentsAt(0);
+ ASSERT_TRUE(first_tab);
+ browser()->CloseTabContents(first_tab);
+ WaitForResourceChange(2);
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/31663
+#define NoticeExtensionChanges DISABLED_NoticeExtensionChanges
+#endif
+
+// Flaky test bug filed in http://crbug.com/51701
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, FLAKY_NoticeExtensionChanges) {
+ EXPECT_EQ(0, model()->ResourceCount());
+
+ // Show the task manager. This populates the model, and helps with debugging
+ // (you see the task manager).
+ browser()->window()->ShowTaskManager();
+
+ // Browser and the New Tab Page.
+ EXPECT_EQ(2, model()->ResourceCount());
+
+ // Loading an extension with a background page should result in a new
+ // resource being created for it.
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
+ WaitForResourceChange(3);
+}
+
+// Times out on Vista; disabled to keep tests fast. http://crbug.com/44991
+#if defined(OS_WIN)
+#define KillExtension DISABLED_KillExtension
+#endif
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, KillExtension) {
+ // Show the task manager. This populates the model, and helps with debugging
+ // (you see the task manager).
+ browser()->window()->ShowTaskManager();
+
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
+
+ // Wait until we see the loaded extension in the task manager (the three
+ // resources are: the browser process, New Tab Page, and the extension).
+ WaitForResourceChange(3);
+
+ EXPECT_TRUE(model()->GetResourceExtension(0) == NULL);
+ EXPECT_TRUE(model()->GetResourceExtension(1) == NULL);
+ ASSERT_TRUE(model()->GetResourceExtension(2) != NULL);
+
+ // Kill the extension process and make sure we notice it.
+ TaskManager::GetInstance()->KillProcess(2);
+ WaitForResourceChange(2);
+}
+
+// Times out on Vista; disabled to keep tests fast. http://crbug.com/44991
+#if defined(OS_WIN)
+#define KillExtensionAndReload DISABLED_KillExtensionAndReload
+#endif
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, KillExtensionAndReload) {
+ // Show the task manager. This populates the model, and helps with debugging
+ // (you see the task manager).
+ browser()->window()->ShowTaskManager();
+
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
+
+ // Wait until we see the loaded extension in the task manager (the three
+ // resources are: the browser process, New Tab Page, and the extension).
+ WaitForResourceChange(3);
+
+ EXPECT_TRUE(model()->GetResourceExtension(0) == NULL);
+ EXPECT_TRUE(model()->GetResourceExtension(1) == NULL);
+ ASSERT_TRUE(model()->GetResourceExtension(2) != NULL);
+
+ // Kill the extension process and make sure we notice it.
+ TaskManager::GetInstance()->KillProcess(2);
+ WaitForResourceChange(2);
+
+ // Reload the extension using the "crashed extension" infobar while the task
+ // manager is still visible. Make sure we don't crash and the extension
+ // gets reloaded and noticed in the task manager.
+ TabContents* current_tab = browser()->GetSelectedTabContents();
+ ASSERT_EQ(1, current_tab->infobar_delegate_count());
+ InfoBarDelegate* delegate = current_tab->GetInfoBarDelegateAt(0);
+ CrashedExtensionInfoBarDelegate* crashed_delegate =
+ delegate->AsCrashedExtensionInfoBarDelegate();
+ ASSERT_TRUE(crashed_delegate);
+ crashed_delegate->Accept();
+ WaitForResourceChange(3);
+}
+
+// Regression test for http://crbug.com/18693.
+// Crashy, http://crbug.com/42315.
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DISABLED_ReloadExtension) {
+ // Show the task manager. This populates the model, and helps with debugging
+ // (you see the task manager).
+ browser()->window()->ShowTaskManager();
+
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
+
+ // Wait until we see the loaded extension in the task manager (the three
+ // resources are: the browser process, New Tab Page, and the extension).
+ WaitForResourceChange(3);
+
+ EXPECT_TRUE(model()->GetResourceExtension(0) == NULL);
+ EXPECT_TRUE(model()->GetResourceExtension(1) == NULL);
+ ASSERT_TRUE(model()->GetResourceExtension(2) != NULL);
+
+ const Extension* extension = model()->GetResourceExtension(2);
+
+ // Reload the extension a few times and make sure our resource count
+ // doesn't increase.
+ ReloadExtension(extension->id());
+ WaitForResourceChange(3);
+ extension = model()->GetResourceExtension(2);
+
+ ReloadExtension(extension->id());
+ WaitForResourceChange(3);
+ extension = model()->GetResourceExtension(2);
+
+ ReloadExtension(extension->id());
+ WaitForResourceChange(3);
+}
+
+// Crashy, http://crbug.com/42301.
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest,
+ DISABLED_PopulateWebCacheFields) {
+ EXPECT_EQ(0, model()->ResourceCount());
+
+ // Show the task manager. This populates the model, and helps with debugging
+ // (you see the task manager).
+ browser()->window()->ShowTaskManager();
+
+ // Browser and the New Tab Page.
+ EXPECT_EQ(2, model()->ResourceCount());
+
+ // Open a new tab and make sure we notice that.
+ GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
+ FilePath(kTitle1File)));
+ browser()->AddTabWithURL(url, GURL(), PageTransition::TYPED, 0,
+ TabStripModel::ADD_SELECTED, NULL, std::string(),
+ NULL);
+ WaitForResourceChange(3);
+
+ // Check that we get some value for the cache columns.
+ DCHECK_NE(model()->GetResourceWebCoreImageCacheSize(2),
+ l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT));
+ DCHECK_NE(model()->GetResourceWebCoreScriptsCacheSize(2),
+ l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT));
+ DCHECK_NE(model()->GetResourceWebCoreCSSCacheSize(2),
+ l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT));
+}
diff --git a/chrome/browser/task_manager/task_manager_resource_providers.cc b/chrome/browser/task_manager/task_manager_resource_providers.cc
new file mode 100644
index 0000000..3ba38ce
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager_resource_providers.cc
@@ -0,0 +1,935 @@
+// Copyright (c) 2010 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 "chrome/browser/task_manager/task_manager_resource_providers.h"
+
+#include "build/build_config.h"
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/basictypes.h"
+#include "base/file_version_info.h"
+#include "base/i18n/rtl.h"
+#include "base/process_util.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/browser_child_process_host.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/notifications/balloon_collection.h"
+#include "chrome/browser/notifications/balloon_host.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/resource_message_filter.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/sqlite_utils.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+
+#if defined(OS_MACOSX)
+#include "skia/ext/skia_utils_mac.h"
+#endif
+#if defined(OS_WIN)
+#include "chrome/browser/app_icon_win.h"
+#include "gfx/icon_util.h"
+#endif // defined(OS_WIN)
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerTabContentsResource class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerTabContentsResource::TaskManagerTabContentsResource(
+ TabContents* tab_contents)
+ : tab_contents_(tab_contents),
+ pending_stats_update_(false),
+ v8_memory_allocated_(0),
+ v8_memory_used_(0),
+ pending_v8_memory_allocated_update_(false) {
+ // We cache the process as when the TabContents is closed the process
+ // becomes NULL and the TaskManager still needs it.
+ process_ = tab_contents_->GetRenderProcessHost()->GetHandle();
+ pid_ = base::GetProcId(process_);
+ stats_.images.size = 0;
+ stats_.cssStyleSheets.size = 0;
+ stats_.scripts.size = 0;
+ stats_.xslStyleSheets.size = 0;
+ stats_.fonts.size = 0;
+}
+
+TaskManagerTabContentsResource::~TaskManagerTabContentsResource() {
+}
+
+std::wstring TaskManagerTabContentsResource::GetTitle() const {
+ // Fall back on the URL if there's no title.
+ std::wstring tab_title(UTF16ToWideHack(tab_contents_->GetTitle()));
+ if (tab_title.empty()) {
+ tab_title = UTF8ToWide(tab_contents_->GetURL().spec());
+ // Force URL to be LTR.
+ tab_title = UTF16ToWide(base::i18n::GetDisplayStringInLTRDirectionality(
+ WideToUTF16(tab_title)));
+ } else {
+ // Since the tab_title will be concatenated with
+ // IDS_TASK_MANAGER_TAB_PREFIX, we need to explicitly set the tab_title to
+ // be LTR format if there is no strong RTL charater in it. Otherwise, if
+ // IDS_TASK_MANAGER_TAB_PREFIX is an RTL word, the concatenated result
+ // might be wrong. For example, http://mail.yahoo.com, whose title is
+ // "Yahoo! Mail: The best web-based Email!", without setting it explicitly
+ // as LTR format, the concatenated result will be "!Yahoo! Mail: The best
+ // web-based Email :BAT", in which the capital letters "BAT" stands for
+ // the Hebrew word for "tab".
+ base::i18n::AdjustStringForLocaleDirection(tab_title, &tab_title);
+ }
+
+ return l10n_util::GetStringF(IDS_TASK_MANAGER_TAB_PREFIX, tab_title);
+}
+
+void TaskManagerTabContentsResource::Refresh() {
+ if (!pending_stats_update_) {
+ tab_contents_->render_view_host()->Send(new ViewMsg_GetCacheResourceStats);
+ pending_stats_update_ = true;
+ }
+ if (!pending_v8_memory_allocated_update_) {
+ tab_contents_->render_view_host()->Send(new ViewMsg_GetV8HeapStats);
+ pending_v8_memory_allocated_update_ = true;
+ }
+}
+
+WebKit::WebCache::ResourceTypeStats
+ TaskManagerTabContentsResource::GetWebCoreCacheStats() const {
+ return stats_;
+}
+
+size_t TaskManagerTabContentsResource::GetV8MemoryAllocated() const {
+ return v8_memory_allocated_;
+}
+
+size_t TaskManagerTabContentsResource::GetV8MemoryUsed() const {
+ return v8_memory_used_;
+}
+
+void TaskManagerTabContentsResource::NotifyResourceTypeStats(
+ const WebKit::WebCache::ResourceTypeStats& stats) {
+ stats_ = stats;
+ pending_stats_update_ = false;
+}
+
+void TaskManagerTabContentsResource::NotifyV8HeapStats(
+ size_t v8_memory_allocated, size_t v8_memory_used) {
+ v8_memory_allocated_ = v8_memory_allocated;
+ v8_memory_used_ = v8_memory_used;
+ pending_v8_memory_allocated_update_ = false;
+}
+
+SkBitmap TaskManagerTabContentsResource::GetIcon() const {
+ return tab_contents_->GetFavIcon();
+}
+
+base::ProcessHandle TaskManagerTabContentsResource::GetProcess() const {
+ return process_;
+}
+
+TabContents* TaskManagerTabContentsResource::GetTabContents() const {
+ return static_cast<TabContents*>(tab_contents_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerTabContentsResourceProvider class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerTabContentsResourceProvider::
+ TaskManagerTabContentsResourceProvider(TaskManager* task_manager)
+ : updating_(false),
+ task_manager_(task_manager) {
+}
+
+TaskManagerTabContentsResourceProvider::
+ ~TaskManagerTabContentsResourceProvider() {
+}
+
+TaskManager::Resource* TaskManagerTabContentsResourceProvider::GetResource(
+ int origin_pid,
+ int render_process_host_id,
+ int routing_id) {
+
+ TabContents* tab_contents =
+ tab_util::GetTabContentsByID(render_process_host_id, routing_id);
+ if (!tab_contents) // Not one of our resource.
+ return NULL;
+
+ base::ProcessHandle process_handle =
+ tab_contents->GetRenderProcessHost()->GetHandle();
+ if (!process_handle) {
+ // We should not be holding on to a dead tab (it should have been removed
+ // through the NOTIFY_TAB_CONTENTS_DISCONNECTED notification.
+ NOTREACHED();
+ return NULL;
+ }
+
+ int pid = base::GetProcId(process_handle);
+ if (pid != origin_pid)
+ return NULL;
+
+ std::map<TabContents*, TaskManagerTabContentsResource*>::iterator
+ res_iter = resources_.find(tab_contents);
+ if (res_iter == resources_.end())
+ // Can happen if the tab was closed while a network request was being
+ // performed.
+ return NULL;
+
+ return res_iter->second;
+}
+
+void TaskManagerTabContentsResourceProvider::StartUpdating() {
+ DCHECK(!updating_);
+ updating_ = true;
+
+ // Add all the existing TabContents.
+ for (TabContentsIterator iterator; !iterator.done(); ++iterator)
+ Add(*iterator);
+
+ // Then we register for notifications to get new tabs.
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_CONNECTED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_SWAPPED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED,
+ NotificationService::AllSources());
+ // TAB_CONTENTS_DISCONNECTED should be enough to know when to remove a
+ // resource. This is an attempt at mitigating a crasher that seem to
+ // indicate a resource is still referencing a deleted TabContents
+ // (http://crbug.com/7321).
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
+ NotificationService::AllSources());
+}
+
+void TaskManagerTabContentsResourceProvider::StopUpdating() {
+ DCHECK(updating_);
+ updating_ = false;
+
+ // Then we unregister for notifications to get new tabs.
+ registrar_.Remove(this, NotificationType::TAB_CONTENTS_CONNECTED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::TAB_CONTENTS_SWAPPED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::TAB_CONTENTS_DISCONNECTED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
+ NotificationService::AllSources());
+
+ // Delete all the resources.
+ STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
+
+ resources_.clear();
+}
+
+void TaskManagerTabContentsResourceProvider::AddToTaskManager(
+ TabContents* tab_contents) {
+ TaskManagerTabContentsResource* resource =
+ new TaskManagerTabContentsResource(tab_contents);
+ resources_[tab_contents] = resource;
+ task_manager_->AddResource(resource);
+}
+
+void TaskManagerTabContentsResourceProvider::Add(TabContents* tab_contents) {
+ if (!updating_)
+ return;
+
+ // Don't add dead tabs or tabs that haven't yet connected.
+ // Also ignore tabs which display extension content. We collapse
+ // all of these into one extension row.
+ if (!tab_contents->GetRenderProcessHost()->GetHandle() ||
+ !tab_contents->notify_disconnection() ||
+ tab_contents->HostsExtension()) {
+ return;
+ }
+
+ std::map<TabContents*, TaskManagerTabContentsResource*>::const_iterator
+ iter = resources_.find(tab_contents);
+ if (iter != resources_.end()) {
+ // The case may happen that we have added a TabContents as part of the
+ // iteration performed during StartUpdating() call but the notification that
+ // it has connected was not fired yet. So when the notification happens, we
+ // already know about this tab and just ignore it.
+ return;
+ }
+ AddToTaskManager(tab_contents);
+}
+
+void TaskManagerTabContentsResourceProvider::Remove(TabContents* tab_contents) {
+ if (!updating_)
+ return;
+ std::map<TabContents*, TaskManagerTabContentsResource*>::iterator
+ iter = resources_.find(tab_contents);
+ if (iter == resources_.end()) {
+ // Since TabContents are destroyed asynchronously (see TabContentsCollector
+ // in navigation_controller.cc), we can be notified of a tab being removed
+ // that we don't know. This can happen if the user closes a tab and quickly
+ // opens the task manager, before the tab is actually destroyed.
+ return;
+ }
+
+ // Remove the resource from the Task Manager.
+ TaskManagerTabContentsResource* resource = iter->second;
+ task_manager_->RemoveResource(resource);
+ // And from the provider.
+ resources_.erase(iter);
+ // Finally, delete the resource.
+ delete resource;
+}
+
+void TaskManagerTabContentsResourceProvider::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::TAB_CONTENTS_CONNECTED:
+ Add(Source<TabContents>(source).ptr());
+ break;
+ case NotificationType::TAB_CONTENTS_SWAPPED:
+ Remove(Source<TabContents>(source).ptr());
+ Add(Source<TabContents>(source).ptr());
+ break;
+ case NotificationType::TAB_CONTENTS_DESTROYED:
+ // If this DCHECK is triggered, it could explain http://crbug.com/7321.
+ DCHECK(resources_.find(Source<TabContents>(source).ptr()) ==
+ resources_.end()) << "TAB_CONTENTS_DESTROYED with no associated "
+ "TAB_CONTENTS_DISCONNECTED";
+ // Fall through.
+ case NotificationType::TAB_CONTENTS_DISCONNECTED:
+ Remove(Source<TabContents>(source).ptr());
+ break;
+ default:
+ NOTREACHED() << "Unexpected notification.";
+ return;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerChildProcessResource class
+////////////////////////////////////////////////////////////////////////////////
+SkBitmap* TaskManagerChildProcessResource::default_icon_ = NULL;
+
+TaskManagerChildProcessResource::TaskManagerChildProcessResource(
+ ChildProcessInfo child_proc)
+ : child_process_(child_proc),
+ title_(),
+ network_usage_support_(false) {
+ // We cache the process id because it's not cheap to calculate, and it won't
+ // be available when we get the plugin disconnected notification.
+ pid_ = child_proc.id();
+ if (!default_icon_) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN);
+ // TODO(jabdelmalek): use different icon for web workers.
+ }
+}
+
+TaskManagerChildProcessResource::~TaskManagerChildProcessResource() {
+}
+
+// TaskManagerResource methods:
+std::wstring TaskManagerChildProcessResource::GetTitle() const {
+ if (title_.empty())
+ title_ = UTF16ToWideHack(child_process_.GetLocalizedTitle());
+
+ return title_;
+}
+
+SkBitmap TaskManagerChildProcessResource::GetIcon() const {
+ return *default_icon_;
+}
+
+base::ProcessHandle TaskManagerChildProcessResource::GetProcess() const {
+ return child_process_.handle();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerChildProcessResourceProvider class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerChildProcessResourceProvider::
+ TaskManagerChildProcessResourceProvider(TaskManager* task_manager)
+ : updating_(false),
+ task_manager_(task_manager) {
+}
+
+TaskManagerChildProcessResourceProvider::
+ ~TaskManagerChildProcessResourceProvider() {
+}
+
+TaskManager::Resource* TaskManagerChildProcessResourceProvider::GetResource(
+ int origin_pid,
+ int render_process_host_id,
+ int routing_id) {
+ std::map<int, TaskManagerChildProcessResource*>::iterator iter =
+ pid_to_resources_.find(origin_pid);
+ if (iter != pid_to_resources_.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+void TaskManagerChildProcessResourceProvider::StartUpdating() {
+ DCHECK(!updating_);
+ updating_ = true;
+
+ // Register for notifications to get new child processes.
+ registrar_.Add(this, NotificationType::CHILD_PROCESS_HOST_CONNECTED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::CHILD_PROCESS_HOST_DISCONNECTED,
+ NotificationService::AllSources());
+
+ // Get the existing child processes.
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &TaskManagerChildProcessResourceProvider::RetrieveChildProcessInfo));
+}
+
+void TaskManagerChildProcessResourceProvider::StopUpdating() {
+ DCHECK(updating_);
+ updating_ = false;
+
+ // Unregister for notifications to get new plugin processes.
+ registrar_.Remove(this, NotificationType::CHILD_PROCESS_HOST_CONNECTED,
+ NotificationService::AllSources());
+ registrar_.Remove(this,
+ NotificationType::CHILD_PROCESS_HOST_DISCONNECTED,
+ NotificationService::AllSources());
+
+ // Delete all the resources.
+ STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
+
+ resources_.clear();
+ pid_to_resources_.clear();
+ existing_child_process_info_.clear();
+}
+
+void TaskManagerChildProcessResourceProvider::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::CHILD_PROCESS_HOST_CONNECTED:
+ Add(*Details<ChildProcessInfo>(details).ptr());
+ break;
+ case NotificationType::CHILD_PROCESS_HOST_DISCONNECTED:
+ Remove(*Details<ChildProcessInfo>(details).ptr());
+ break;
+ default:
+ NOTREACHED() << "Unexpected notification.";
+ return;
+ }
+}
+
+void TaskManagerChildProcessResourceProvider::Add(
+ ChildProcessInfo child_process_info) {
+ if (!updating_)
+ return;
+ std::map<ChildProcessInfo, TaskManagerChildProcessResource*>::
+ const_iterator iter = resources_.find(child_process_info);
+ if (iter != resources_.end()) {
+ // The case may happen that we have added a child_process_info as part of
+ // the iteration performed during StartUpdating() call but the notification
+ // that it has connected was not fired yet. So when the notification
+ // happens, we already know about this plugin and just ignore it.
+ return;
+ }
+ AddToTaskManager(child_process_info);
+}
+
+void TaskManagerChildProcessResourceProvider::Remove(
+ ChildProcessInfo child_process_info) {
+ if (!updating_)
+ return;
+ std::map<ChildProcessInfo, TaskManagerChildProcessResource*>
+ ::iterator iter = resources_.find(child_process_info);
+ if (iter == resources_.end()) {
+ // ChildProcessInfo disconnection notifications are asynchronous, so we
+ // might be notified for a plugin we don't know anything about (if it was
+ // closed before the task manager was shown and destroyed after that).
+ return;
+ }
+ // Remove the resource from the Task Manager.
+ TaskManagerChildProcessResource* resource = iter->second;
+ task_manager_->RemoveResource(resource);
+ // Remove it from the provider.
+ resources_.erase(iter);
+ // Remove it from our pid map.
+ std::map<int, TaskManagerChildProcessResource*>::iterator pid_iter =
+ pid_to_resources_.find(resource->process_id());
+ DCHECK(pid_iter != pid_to_resources_.end());
+ if (pid_iter != pid_to_resources_.end())
+ pid_to_resources_.erase(pid_iter);
+
+ // Finally, delete the resource.
+ delete resource;
+}
+
+void TaskManagerChildProcessResourceProvider::AddToTaskManager(
+ ChildProcessInfo child_process_info) {
+ TaskManagerChildProcessResource* resource =
+ new TaskManagerChildProcessResource(child_process_info);
+ resources_[child_process_info] = resource;
+ pid_to_resources_[resource->process_id()] = resource;
+ task_manager_->AddResource(resource);
+}
+
+// The ChildProcessInfo::Iterator has to be used from the IO thread.
+void TaskManagerChildProcessResourceProvider::RetrieveChildProcessInfo() {
+ for (BrowserChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
+ // Only add processes which are already started, since we need their handle.
+ if ((*iter)->handle() != base::kNullProcessHandle)
+ existing_child_process_info_.push_back(**iter);
+ }
+ // Now notify the UI thread that we have retrieved information about child
+ // processes.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &TaskManagerChildProcessResourceProvider::ChildProcessInfoRetreived));
+}
+
+// This is called on the UI thread.
+void TaskManagerChildProcessResourceProvider::ChildProcessInfoRetreived() {
+ std::vector<ChildProcessInfo>::const_iterator iter;
+ for (iter = existing_child_process_info_.begin();
+ iter != existing_child_process_info_.end(); ++iter) {
+ Add(*iter);
+ }
+ existing_child_process_info_.clear();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerExtensionProcessResource class
+////////////////////////////////////////////////////////////////////////////////
+
+SkBitmap* TaskManagerExtensionProcessResource::default_icon_ = NULL;
+
+TaskManagerExtensionProcessResource::TaskManagerExtensionProcessResource(
+ ExtensionHost* extension_host)
+ : extension_host_(extension_host) {
+ if (!default_icon_) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN);
+ }
+ process_handle_ = extension_host_->render_process_host()->GetHandle();
+ pid_ = base::GetProcId(process_handle_);
+ std::wstring extension_name(UTF8ToWide(GetExtension()->name()));
+ DCHECK(!extension_name.empty());
+
+ int message_id =
+ GetExtension()->is_app() ?
+ (extension_host_->profile()->IsOffTheRecord() ?
+ IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX :
+ IDS_TASK_MANAGER_APP_PREFIX) :
+ (extension_host_->profile()->IsOffTheRecord() ?
+ IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX :
+ IDS_TASK_MANAGER_EXTENSION_PREFIX);
+ title_ = l10n_util::GetStringF(message_id, extension_name);
+}
+
+TaskManagerExtensionProcessResource::~TaskManagerExtensionProcessResource() {
+}
+
+std::wstring TaskManagerExtensionProcessResource::GetTitle() const {
+ return title_;
+}
+
+SkBitmap TaskManagerExtensionProcessResource::GetIcon() const {
+ return *default_icon_;
+}
+
+base::ProcessHandle TaskManagerExtensionProcessResource::GetProcess() const {
+ return process_handle_;
+}
+
+const Extension* TaskManagerExtensionProcessResource::GetExtension() const {
+ return extension_host_->extension();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerExtensionProcessResourceProvider class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerExtensionProcessResourceProvider::
+ TaskManagerExtensionProcessResourceProvider(TaskManager* task_manager)
+ : task_manager_(task_manager),
+ updating_(false) {
+}
+
+TaskManagerExtensionProcessResourceProvider::
+ ~TaskManagerExtensionProcessResourceProvider() {
+}
+
+TaskManager::Resource* TaskManagerExtensionProcessResourceProvider::GetResource(
+ int origin_pid,
+ int render_process_host_id,
+ int routing_id) {
+ std::map<int, TaskManagerExtensionProcessResource*>::iterator iter =
+ pid_to_resources_.find(origin_pid);
+ if (iter != pid_to_resources_.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+void TaskManagerExtensionProcessResourceProvider::StartUpdating() {
+ DCHECK(!updating_);
+ updating_ = true;
+
+ // Add all the existing ExtensionHosts.
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ for (ProfileManager::const_iterator it = profile_manager->begin();
+ it != profile_manager->end(); ++it) {
+ ExtensionProcessManager* process_manager =
+ (*it)->GetExtensionProcessManager();
+ if (process_manager) {
+ ExtensionProcessManager::const_iterator jt;
+ for (jt = process_manager->begin(); jt != process_manager->end(); ++jt)
+ AddToTaskManager(*jt);
+ }
+
+ // If we have an incognito profile active, include the split-mode incognito
+ // extensions.
+ if (BrowserList::IsOffTheRecordSessionActive()) {
+ ExtensionProcessManager* process_manager =
+ (*it)->GetOffTheRecordProfile()->GetExtensionProcessManager();
+ if (process_manager) {
+ ExtensionProcessManager::const_iterator jt;
+ for (jt = process_manager->begin(); jt != process_manager->end(); ++jt)
+ AddToTaskManager(*jt);
+ }
+ }
+ }
+
+ // Register for notifications about extension process changes.
+ registrar_.Add(this, NotificationType::EXTENSION_PROCESS_CREATED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED,
+ NotificationService::AllSources());
+}
+
+void TaskManagerExtensionProcessResourceProvider::StopUpdating() {
+ DCHECK(updating_);
+ updating_ = false;
+
+ // Unregister for notifications about extension process changes.
+ registrar_.Remove(this, NotificationType::EXTENSION_PROCESS_CREATED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::EXTENSION_HOST_DESTROYED,
+ NotificationService::AllSources());
+
+ // Delete all the resources.
+ STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
+
+ resources_.clear();
+ pid_to_resources_.clear();
+}
+
+void TaskManagerExtensionProcessResourceProvider::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::EXTENSION_PROCESS_CREATED:
+ AddToTaskManager(Details<ExtensionHost>(details).ptr());
+ break;
+ case NotificationType::EXTENSION_PROCESS_TERMINATED:
+ case NotificationType::EXTENSION_HOST_DESTROYED:
+ RemoveFromTaskManager(Details<ExtensionHost>(details).ptr());
+ break;
+ default:
+ NOTREACHED() << "Unexpected notification.";
+ return;
+ }
+}
+
+void TaskManagerExtensionProcessResourceProvider::AddToTaskManager(
+ ExtensionHost* extension_host) {
+ // Don't add dead extension processes.
+ if (!extension_host->IsRenderViewLive())
+ return;
+
+ TaskManagerExtensionProcessResource* resource =
+ new TaskManagerExtensionProcessResource(extension_host);
+ DCHECK(resources_.find(extension_host) == resources_.end());
+ resources_[extension_host] = resource;
+ pid_to_resources_[resource->process_id()] = resource;
+ task_manager_->AddResource(resource);
+}
+
+void TaskManagerExtensionProcessResourceProvider::RemoveFromTaskManager(
+ ExtensionHost* extension_host) {
+ if (!updating_)
+ return;
+ std::map<ExtensionHost*, TaskManagerExtensionProcessResource*>
+ ::iterator iter = resources_.find(extension_host);
+ if (iter == resources_.end())
+ return;
+
+ // Remove the resource from the Task Manager.
+ TaskManagerExtensionProcessResource* resource = iter->second;
+ task_manager_->RemoveResource(resource);
+
+ // Remove it from the provider.
+ resources_.erase(iter);
+
+ // Remove it from our pid map.
+ std::map<int, TaskManagerExtensionProcessResource*>::iterator pid_iter =
+ pid_to_resources_.find(resource->process_id());
+ DCHECK(pid_iter != pid_to_resources_.end());
+ if (pid_iter != pid_to_resources_.end())
+ pid_to_resources_.erase(pid_iter);
+
+ // Finally, delete the resource.
+ delete resource;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerNotificationResource class
+////////////////////////////////////////////////////////////////////////////////
+
+SkBitmap* TaskManagerNotificationResource::default_icon_ = NULL;
+
+TaskManagerNotificationResource::TaskManagerNotificationResource(
+ BalloonHost* balloon_host)
+ : balloon_host_(balloon_host) {
+ if (!default_icon_) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN);
+ }
+ process_handle_ = balloon_host_->render_view_host()->process()->GetHandle();
+ pid_ = base::GetProcId(process_handle_);
+ title_ = UTF16ToWide(l10n_util::GetStringFUTF16(
+ IDS_TASK_MANAGER_NOTIFICATION_PREFIX,
+ balloon_host_->GetSource()));
+}
+
+TaskManagerNotificationResource::~TaskManagerNotificationResource() {
+}
+
+SkBitmap TaskManagerNotificationResource::GetIcon() const {
+ return *default_icon_;
+}
+
+base::ProcessHandle TaskManagerNotificationResource::GetProcess() const {
+ return process_handle_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerNotificationResourceProvider class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerNotificationResourceProvider::
+ TaskManagerNotificationResourceProvider(TaskManager* task_manager)
+ : task_manager_(task_manager),
+ updating_(false) {
+}
+
+TaskManagerNotificationResourceProvider::
+ ~TaskManagerNotificationResourceProvider() {
+}
+
+TaskManager::Resource* TaskManagerNotificationResourceProvider::GetResource(
+ int origin_pid,
+ int render_process_host_id,
+ int routing_id) {
+ // TODO(johnnyg): provide resources by pid if necessary.
+ return NULL;
+}
+
+void TaskManagerNotificationResourceProvider::StartUpdating() {
+ DCHECK(!updating_);
+ updating_ = true;
+
+ // Add all the existing BalloonHosts.
+ BalloonCollection* collection =
+ g_browser_process->notification_ui_manager()->balloon_collection();
+ const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons();
+ for (BalloonCollection::Balloons::const_iterator it = balloons.begin();
+ it != balloons.end(); ++it) {
+ AddToTaskManager((*it)->view()->GetHost());
+ }
+
+ // Register for notifications about extension process changes.
+ registrar_.Add(this, NotificationType::NOTIFY_BALLOON_CONNECTED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::NOTIFY_BALLOON_DISCONNECTED,
+ NotificationService::AllSources());
+}
+
+void TaskManagerNotificationResourceProvider::StopUpdating() {
+ DCHECK(updating_);
+ updating_ = false;
+
+ // Unregister for notifications about extension process changes.
+ registrar_.Remove(this, NotificationType::NOTIFY_BALLOON_CONNECTED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::NOTIFY_BALLOON_DISCONNECTED,
+ NotificationService::AllSources());
+
+ // Delete all the resources.
+ STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
+ resources_.clear();
+}
+
+void TaskManagerNotificationResourceProvider::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::NOTIFY_BALLOON_CONNECTED:
+ AddToTaskManager(Source<BalloonHost>(source).ptr());
+ break;
+ case NotificationType::NOTIFY_BALLOON_DISCONNECTED:
+ RemoveFromTaskManager(Source<BalloonHost>(source).ptr());
+ break;
+ default:
+ NOTREACHED() << "Unexpected notification.";
+ return;
+ }
+}
+
+void TaskManagerNotificationResourceProvider::AddToTaskManager(
+ BalloonHost* balloon_host) {
+ TaskManagerNotificationResource* resource =
+ new TaskManagerNotificationResource(balloon_host);
+ DCHECK(resources_.find(balloon_host) == resources_.end());
+ resources_[balloon_host] = resource;
+ task_manager_->AddResource(resource);
+}
+
+void TaskManagerNotificationResourceProvider::RemoveFromTaskManager(
+ BalloonHost* balloon_host) {
+ if (!updating_)
+ return;
+ std::map<BalloonHost*, TaskManagerNotificationResource*>::iterator iter =
+ resources_.find(balloon_host);
+ if (iter == resources_.end())
+ return;
+
+ // Remove the resource from the Task Manager.
+ TaskManagerNotificationResource* resource = iter->second;
+ task_manager_->RemoveResource(resource);
+
+ // Remove it from the map.
+ resources_.erase(iter);
+
+ // Finally, delete the resource.
+ delete resource;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerBrowserProcessResource class
+////////////////////////////////////////////////////////////////////////////////
+
+SkBitmap* TaskManagerBrowserProcessResource::default_icon_ = NULL;
+
+TaskManagerBrowserProcessResource::TaskManagerBrowserProcessResource()
+ : title_() {
+ pid_ = base::GetCurrentProcId();
+ bool success = base::OpenPrivilegedProcessHandle(pid_, &process_);
+ DCHECK(success);
+#if defined(OS_WIN)
+ if (!default_icon_) {
+ HICON icon = GetAppIcon();
+ if (icon) {
+ ICONINFO icon_info = {0};
+ BITMAP bitmap_info = {0};
+
+ GetIconInfo(icon, &icon_info);
+ GetObject(icon_info.hbmMask, sizeof(bitmap_info), &bitmap_info);
+
+ gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight);
+ default_icon_ = IconUtil::CreateSkBitmapFromHICON(icon, icon_size);
+ }
+ }
+#elif defined(OS_LINUX)
+ if (!default_icon_) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ default_icon_ = rb.GetBitmapNamed(IDR_PRODUCT_LOGO_16);
+ }
+#elif defined(OS_MACOSX)
+ if (!default_icon_) {
+ // IDR_PRODUCT_LOGO_16 doesn't quite look like chrome/mac's icns icon. Load
+ // the real app icon (requires a nsimage->skbitmap->nsimage conversion :-().
+ default_icon_ = new SkBitmap(gfx::AppplicationIconAtSize(16));
+ }
+#else
+ // TODO(port): Port icon code.
+ NOTIMPLEMENTED();
+#endif // defined(OS_WIN)
+}
+
+TaskManagerBrowserProcessResource::~TaskManagerBrowserProcessResource() {
+ base::CloseProcessHandle(process_);
+}
+
+// TaskManagerResource methods:
+std::wstring TaskManagerBrowserProcessResource::GetTitle() const {
+ if (title_.empty()) {
+ title_ = l10n_util::GetString(IDS_TASK_MANAGER_WEB_BROWSER_CELL_TEXT);
+ }
+ return title_;
+}
+
+SkBitmap TaskManagerBrowserProcessResource::GetIcon() const {
+ return *default_icon_;
+}
+
+size_t TaskManagerBrowserProcessResource::SqliteMemoryUsedBytes() const {
+ return static_cast<size_t>(sqlite3_memory_used());
+}
+
+base::ProcessHandle TaskManagerBrowserProcessResource::GetProcess() const {
+ return base::GetCurrentProcessHandle(); // process_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TaskManagerBrowserProcessResourceProvider class
+////////////////////////////////////////////////////////////////////////////////
+
+TaskManagerBrowserProcessResourceProvider::
+ TaskManagerBrowserProcessResourceProvider(TaskManager* task_manager)
+ : updating_(false),
+ task_manager_(task_manager) {
+}
+
+TaskManagerBrowserProcessResourceProvider::
+ ~TaskManagerBrowserProcessResourceProvider() {
+}
+
+TaskManager::Resource* TaskManagerBrowserProcessResourceProvider::GetResource(
+ int origin_pid,
+ int render_process_host_id,
+ int routing_id) {
+ if (origin_pid != resource_.process_id()) {
+ return NULL;
+ }
+
+ return &resource_;
+}
+
+void TaskManagerBrowserProcessResourceProvider::StartUpdating() {
+ task_manager_->AddResource(&resource_);
+}
+
+void TaskManagerBrowserProcessResourceProvider::StopUpdating() {
+}
diff --git a/chrome/browser/task_manager/task_manager_resource_providers.h b/chrome/browser/task_manager/task_manager_resource_providers.h
new file mode 100644
index 0000000..d794111
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager_resource_providers.h
@@ -0,0 +1,396 @@
+// Copyright (c) 2006-2008 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 CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_RESOURCE_PROVIDERS_H_
+#define CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_RESOURCE_PROVIDERS_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+#include "chrome/browser/task_manager/task_manager.h"
+#include "chrome/common/child_process_info.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCache.h"
+
+class BalloonHost;
+class Extension;
+class ExtensionHost;
+class TabContents;
+
+// These file contains the resource providers used in the task manager.
+
+class TaskManagerTabContentsResource : public TaskManager::Resource {
+ public:
+ explicit TaskManagerTabContentsResource(TabContents* tab_contents);
+ ~TaskManagerTabContentsResource();
+
+ // TaskManagerResource methods:
+ std::wstring GetTitle() const;
+ SkBitmap GetIcon() const;
+ base::ProcessHandle GetProcess() const;
+ TabContents* GetTabContents() const;
+
+ virtual bool ReportsCacheStats() const { return true; }
+ virtual WebKit::WebCache::ResourceTypeStats GetWebCoreCacheStats() const;
+
+ virtual bool ReportsV8MemoryStats() const { return true; }
+ virtual size_t GetV8MemoryAllocated() const;
+ virtual size_t GetV8MemoryUsed() const;
+
+ // TabContents always provide the network usage.
+ bool SupportNetworkUsage() const { return true; }
+ void SetSupportNetworkUsage() { }
+
+ virtual void Refresh();
+
+ virtual void NotifyResourceTypeStats(
+ const WebKit::WebCache::ResourceTypeStats& stats);
+
+ virtual void NotifyV8HeapStats(size_t v8_memory_allocated,
+ size_t v8_memory_used);
+
+ private:
+ TabContents* tab_contents_;
+ base::ProcessHandle process_;
+ int pid_;
+ // The stats_ field holds information about resource usage in the renderer
+ // process and so it is updated asynchronously by the Refresh() call.
+ WebKit::WebCache::ResourceTypeStats stats_;
+ // This flag is true if we are waiting for the renderer to report its stats.
+ bool pending_stats_update_;
+
+ // We do a similar dance to gather the V8 memory usage in a process.
+ size_t v8_memory_allocated_;
+ size_t v8_memory_used_;
+ bool pending_v8_memory_allocated_update_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerTabContentsResource);
+};
+
+class TaskManagerTabContentsResourceProvider
+ : public TaskManager::ResourceProvider,
+ public NotificationObserver {
+ public:
+ explicit TaskManagerTabContentsResourceProvider(TaskManager* task_manager);
+
+ virtual TaskManager::Resource* GetResource(int origin_pid,
+ int render_process_host_id,
+ int routing_id);
+ virtual void StartUpdating();
+ virtual void StopUpdating();
+
+ // NotificationObserver method:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ virtual ~TaskManagerTabContentsResourceProvider();
+
+ void Add(TabContents* tab_contents);
+ void Remove(TabContents* tab_contents);
+
+ void AddToTaskManager(TabContents* tab_contents);
+
+ // Whether we are currently reporting to the task manager. Used to ignore
+ // notifications sent after StopUpdating().
+ bool updating_;
+
+ TaskManager* task_manager_;
+
+ // Maps the actual resources (the TabContents) to the Task Manager
+ // resources.
+ std::map<TabContents*, TaskManagerTabContentsResource*> resources_;
+
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerTabContentsResourceProvider);
+};
+
+class TaskManagerChildProcessResource : public TaskManager::Resource {
+ public:
+ explicit TaskManagerChildProcessResource(ChildProcessInfo child_proc);
+ ~TaskManagerChildProcessResource();
+
+ // TaskManagerResource methods:
+ std::wstring GetTitle() const;
+ SkBitmap GetIcon() const;
+ base::ProcessHandle GetProcess() const;
+
+ bool SupportNetworkUsage() const {
+ return network_usage_support_;
+ }
+
+ void SetSupportNetworkUsage() {
+ network_usage_support_ = true;
+ }
+
+ // Returns the pid of the child process.
+ int process_id() const { return pid_; }
+
+ private:
+ ChildProcessInfo child_process_;
+ int pid_;
+ mutable std::wstring title_;
+ bool network_usage_support_;
+
+ // The icon painted for the child processs.
+ // TODO(jcampan): we should have plugin specific icons for well-known
+ // plugins.
+ static SkBitmap* default_icon_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerChildProcessResource);
+};
+
+class TaskManagerChildProcessResourceProvider
+ : public TaskManager::ResourceProvider,
+ public NotificationObserver {
+ public:
+ explicit TaskManagerChildProcessResourceProvider(TaskManager* task_manager);
+
+ virtual TaskManager::Resource* GetResource(int origin_pid,
+ int render_process_host_id,
+ int routing_id);
+ virtual void StartUpdating();
+ virtual void StopUpdating();
+
+ // NotificationObserver method:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Retrieves the current ChildProcessInfo (performed in the IO thread).
+ virtual void RetrieveChildProcessInfo();
+
+ // Notifies the UI thread that the ChildProcessInfo have been retrieved.
+ virtual void ChildProcessInfoRetreived();
+
+ // Whether we are currently reporting to the task manager. Used to ignore
+ // notifications sent after StopUpdating().
+ bool updating_;
+
+ // The list of ChildProcessInfo retrieved when starting the update.
+ std::vector<ChildProcessInfo> existing_child_process_info_;
+
+ private:
+ virtual ~TaskManagerChildProcessResourceProvider();
+
+ void Add(ChildProcessInfo child_process_info);
+ void Remove(ChildProcessInfo child_process_info);
+
+ void AddToTaskManager(ChildProcessInfo child_process_info);
+
+ TaskManager* task_manager_;
+
+ // Maps the actual resources (the ChildProcessInfo) to the Task Manager
+ // resources.
+ std::map<ChildProcessInfo, TaskManagerChildProcessResource*> resources_;
+
+ // Maps the pids to the resources (used for quick access to the resource on
+ // byte read notifications).
+ std::map<int, TaskManagerChildProcessResource*> pid_to_resources_;
+
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerChildProcessResourceProvider);
+};
+
+class TaskManagerExtensionProcessResource : public TaskManager::Resource {
+ public:
+ explicit TaskManagerExtensionProcessResource(ExtensionHost* extension_host);
+ ~TaskManagerExtensionProcessResource();
+
+ // TaskManagerResource methods:
+ std::wstring GetTitle() const;
+ SkBitmap GetIcon() const;
+ base::ProcessHandle GetProcess() const;
+ bool SupportNetworkUsage() const { return true; }
+ void SetSupportNetworkUsage() { NOTREACHED(); }
+ const Extension* GetExtension() const;
+
+ // Returns the pid of the extension process.
+ int process_id() const { return pid_; }
+
+ private:
+ // The icon painted for the extension process.
+ static SkBitmap* default_icon_;
+
+ ExtensionHost* extension_host_;
+
+ // Cached data about the extension.
+ base::ProcessHandle process_handle_;
+ int pid_;
+ std::wstring title_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerExtensionProcessResource);
+};
+
+class TaskManagerExtensionProcessResourceProvider
+ : public TaskManager::ResourceProvider,
+ public NotificationObserver {
+ public:
+ explicit TaskManagerExtensionProcessResourceProvider(
+ TaskManager* task_manager);
+
+ virtual TaskManager::Resource* GetResource(int origin_pid,
+ int render_process_host_id,
+ int routing_id);
+ virtual void StartUpdating();
+ virtual void StopUpdating();
+
+ // NotificationObserver method:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ virtual ~TaskManagerExtensionProcessResourceProvider();
+
+ void AddToTaskManager(ExtensionHost* extension_host);
+ void RemoveFromTaskManager(ExtensionHost* extension_host);
+
+ TaskManager* task_manager_;
+
+ // Maps the actual resources (ExtensionHost*) to the Task Manager resources.
+ std::map<ExtensionHost*, TaskManagerExtensionProcessResource*> resources_;
+
+ // Maps the pids to the resources (used for quick access to the resource on
+ // byte read notifications).
+ std::map<int, TaskManagerExtensionProcessResource*> pid_to_resources_;
+
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ bool updating_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerExtensionProcessResourceProvider);
+};
+
+class TaskManagerNotificationResource : public TaskManager::Resource {
+ public:
+ explicit TaskManagerNotificationResource(BalloonHost* balloon_host);
+ ~TaskManagerNotificationResource();
+
+ // TaskManager::Resource interface
+ std::wstring GetTitle() const { return title_; }
+ SkBitmap GetIcon() const;
+ base::ProcessHandle GetProcess() const;
+ virtual bool SupportNetworkUsage() const { return false; }
+ virtual void SetSupportNetworkUsage() { }
+
+ private:
+ // The icon painted for notifications. .
+ static SkBitmap* default_icon_;
+
+ // Non-owned pointer to the balloon host.
+ BalloonHost* balloon_host_;
+
+ // Cached data about the balloon host.
+ base::ProcessHandle process_handle_;
+ int pid_;
+ std::wstring title_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerNotificationResource);
+};
+
+class TaskManagerNotificationResourceProvider
+ : public TaskManager::ResourceProvider,
+ public NotificationObserver {
+ public:
+ explicit TaskManagerNotificationResourceProvider(TaskManager* task_manager);
+
+ // TaskManager::ResourceProvider interface
+ virtual TaskManager::Resource* GetResource(int origin_pid,
+ int render_process_host_id,
+ int routing_id);
+ virtual void StartUpdating();
+ virtual void StopUpdating();
+
+ // NotificationObserver interface
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ virtual ~TaskManagerNotificationResourceProvider();
+
+ void AddToTaskManager(BalloonHost* balloon_host);
+ void RemoveFromTaskManager(BalloonHost* balloon_host);
+
+ TaskManager* task_manager_;
+
+ // Maps the actual resources (BalloonHost*) to the Task Manager resources.
+ std::map<BalloonHost*, TaskManagerNotificationResource*> resources_;
+
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ bool updating_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerNotificationResourceProvider);
+};
+
+class TaskManagerBrowserProcessResource : public TaskManager::Resource {
+ public:
+ TaskManagerBrowserProcessResource();
+ ~TaskManagerBrowserProcessResource();
+
+ // TaskManagerResource methods:
+ std::wstring GetTitle() const;
+ SkBitmap GetIcon() const;
+ base::ProcessHandle GetProcess() const;
+
+ bool SupportNetworkUsage() const { return true; }
+ void SetSupportNetworkUsage() { NOTREACHED(); }
+
+ bool ReportsSqliteMemoryUsed() const { return true; }
+ size_t SqliteMemoryUsedBytes() const;
+
+ // Returns the pid of the browser process.
+ int process_id() const { return pid_; }
+
+ private:
+ base::ProcessHandle process_;
+ int pid_;
+ mutable std::wstring title_;
+
+ static SkBitmap* default_icon_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerBrowserProcessResource);
+};
+
+class TaskManagerBrowserProcessResourceProvider
+ : public TaskManager::ResourceProvider {
+ public:
+ explicit TaskManagerBrowserProcessResourceProvider(
+ TaskManager* task_manager);
+
+ virtual TaskManager::Resource* GetResource(int origin_pid,
+ int render_process_host_id,
+ int routing_id);
+ virtual void StartUpdating();
+ virtual void StopUpdating();
+
+ // Whether we are currently reporting to the task manager. Used to ignore
+ // notifications sent after StopUpdating().
+ bool updating_;
+
+ private:
+ virtual ~TaskManagerBrowserProcessResourceProvider();
+
+ void AddToTaskManager(ChildProcessInfo child_process_info);
+
+ TaskManager* task_manager_;
+ TaskManagerBrowserProcessResource resource_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskManagerBrowserProcessResourceProvider);
+};
+
+#endif // CHROME_BROWSER_TASK_MANAGER_TASK_MANAGER_RESOURCE_PROVIDERS_H_
diff --git a/chrome/browser/task_manager/task_manager_unittest.cc b/chrome/browser/task_manager/task_manager_unittest.cc
new file mode 100644
index 0000000..44d0e31
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2010 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 "chrome/browser/task_manager/task_manager.h"
+
+#include <string>
+
+#include "app/l10n_util.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/utf_string_conversions.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace {
+
+#if defined(OS_MACOSX)
+// From task_manager.cc:
+// Activity Monitor shows %cpu with one decimal digit -- be
+// consistent with that.
+const char* kZeroCPUUsage = "0.0";
+#else
+const char* kZeroCPUUsage = "0";
+#endif
+
+class TestResource : public TaskManager::Resource {
+ public:
+ TestResource() : refresh_called_(false) {}
+
+ virtual std::wstring GetTitle() const { return L"test title"; }
+ virtual SkBitmap GetIcon() const { return SkBitmap(); }
+ virtual base::ProcessHandle GetProcess() const {
+ return base::GetCurrentProcessHandle();
+ }
+ virtual bool SupportNetworkUsage() const { return false; }
+ virtual void SetSupportNetworkUsage() { NOTREACHED(); }
+ virtual void Refresh() { refresh_called_ = true; }
+ bool refresh_called() const { return refresh_called_; }
+ void set_refresh_called(bool refresh_called) {
+ refresh_called_ = refresh_called;
+ }
+
+ private:
+ bool refresh_called_;
+};
+
+} // namespace
+
+class TaskManagerTest : public testing::Test {
+};
+
+TEST_F(TaskManagerTest, Basic) {
+ TaskManager task_manager;
+ TaskManagerModel* model = task_manager.model_;
+ EXPECT_EQ(0, model->ResourceCount());
+}
+
+TEST_F(TaskManagerTest, Resources) {
+ TaskManager task_manager;
+ TaskManagerModel* model = task_manager.model_;
+
+ TestResource resource1, resource2;
+
+ task_manager.AddResource(&resource1);
+ ASSERT_EQ(1, model->ResourceCount());
+ EXPECT_TRUE(model->IsResourceFirstInGroup(0));
+ EXPECT_EQ(ASCIIToUTF16("test title"), model->GetResourceTitle(0));
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT),
+ model->GetResourceNetworkUsage(0));
+ EXPECT_EQ(ASCIIToUTF16(kZeroCPUUsage), model->GetResourceCPUUsage(0));
+
+ task_manager.AddResource(&resource2); // Will be in the same group.
+ ASSERT_EQ(2, model->ResourceCount());
+ EXPECT_TRUE(model->IsResourceFirstInGroup(0));
+ EXPECT_FALSE(model->IsResourceFirstInGroup(1));
+ EXPECT_EQ(ASCIIToUTF16("test title"), model->GetResourceTitle(1));
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT).c_str(),
+ model->GetResourceNetworkUsage(1));
+ EXPECT_EQ(ASCIIToUTF16(kZeroCPUUsage), model->GetResourceCPUUsage(1));
+
+ task_manager.RemoveResource(&resource1);
+ // Now resource2 will be first in group.
+ ASSERT_EQ(1, model->ResourceCount());
+ EXPECT_TRUE(model->IsResourceFirstInGroup(0));
+ EXPECT_EQ(ASCIIToUTF16("test title"), model->GetResourceTitle(0));
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NA_CELL_TEXT),
+ model->GetResourceNetworkUsage(0));
+ EXPECT_EQ(ASCIIToUTF16(kZeroCPUUsage), model->GetResourceCPUUsage(0));
+
+ task_manager.RemoveResource(&resource2);
+ EXPECT_EQ(0, model->ResourceCount());
+}
+
+// Tests that the model is calling Refresh() on its resources.
+TEST_F(TaskManagerTest, RefreshCalled) {
+ MessageLoop loop;
+ TaskManager task_manager;
+ TaskManagerModel* model = task_manager.model_;
+ TestResource resource;
+
+ task_manager.AddResource(&resource);
+ ASSERT_FALSE(resource.refresh_called());
+ model->update_state_ = TaskManagerModel::TASK_PENDING;
+ model->Refresh();
+ ASSERT_TRUE(resource.refresh_called());
+ task_manager.RemoveResource(&resource);
+}