普通文本  |  1005行  |  34.34 KB

// Copyright (c) 2011 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 "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/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/url_request_tracking.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/task_manager/task_manager_resource_providers.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/browser/renderer_host/resource_dispatcher_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/result_codes.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 "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.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_requests_(0),
      update_state_(IDLE),
      goat_salt_(rand()) {
  AddResourceProvider(
      new TaskManagerBrowserProcessResourceProvider(task_manager));
  AddResourceProvider(
      new TaskManagerBackgroundContentsResourceProvider(task_manager));
  AddResourceProvider(new TaskManagerTabContentsResourceProvider(task_manager));
  AddResourceProvider(new TaskManagerPrerenderResourceProvider(task_manager));
  AddResourceProvider(
      new TaskManagerChildProcessResourceProvider(task_manager));
  AddResourceProvider(
      new TaskManagerExtensionProcessResourceProvider(task_manager));
  AddResourceProvider(
      new TaskManagerNotificationResourceProvider(task_manager));
}

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 {
  CHECK_LT(index, ResourceCount());
  return resources_[index]->GetTitle();
}

int64 TaskManagerModel::GetNetworkUsage(int index) const {
  CHECK_LT(index, ResourceCount());
  return GetNetworkUsage(resources_[index]);
}

string16 TaskManagerModel::GetResourceNetworkUsage(int index) const {
  int64 net_usage = GetNetworkUsage(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);
}

double TaskManagerModel::GetCPUUsage(int index) const {
  CHECK_LT(index, ResourceCount());
  return GetCPUUsage(resources_[index]);
}

string16 TaskManagerModel::GetResourceCPUUsage(int index) const {
  CHECK_LT(index, ResourceCount());
  return UTF8ToUTF16(StringPrintf(
#if defined(OS_MACOSX)
      // Activity Monitor shows %cpu with one decimal digit -- be
      // consistent with that.
      "%.1f",
#else
      "%.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);
}

int TaskManagerModel::GetProcessId(int index) const {
  CHECK_LT(index, ResourceCount());
  return base::GetProcId(resources_[index]->GetProcess());
}

string16 TaskManagerModel::GetResourceProcessId(int index) const {
  return base::IntToString16(GetProcessId(index));
}

string16 TaskManagerModel::GetResourceGoatsTeleported(int index) const {
  CHECK_LT(index, ResourceCount());
  return base::FormatNumber(GetGoatsTeleported(index));
}

string16 TaskManagerModel::GetResourceWebCoreImageCacheSize(
    int index) const {
  CHECK_LT(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 {
  CHECK_LT(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 {
  CHECK_LT(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 {
  CHECK_LT(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 {
  CHECK_LT(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);
}

bool TaskManagerModel::IsBackgroundResource(int index) const {
  CHECK_LT(index, ResourceCount());
  return resources_[index]->IsBackground();
}

SkBitmap TaskManagerModel::GetResourceIcon(int index) const {
  CHECK_LT(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 {
  CHECK_LT(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 {
  CHECK(row1 < ResourceCount() && row2 < ResourceCount());
  if (col_id == 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;
  } else if (col_id == IDS_TASK_MANAGER_NET_COLUMN) {
    return ValueCompare<int64>(GetNetworkUsage(resources_[row1]),
                               GetNetworkUsage(resources_[row2]));
  } else if (col_id == IDS_TASK_MANAGER_CPU_COLUMN) {
    return ValueCompare<double>(GetCPUUsage(resources_[row1]),
                                GetCPUUsage(resources_[row2]));
  } else if (col_id == 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);
  } else if (col_id == 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);
  } else if (col_id == 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);
  } else if (col_id == 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);
  } else if (col_id == IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN ||
             col_id == IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN ||
             col_id == 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);
  } else if (col_id == IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN) {
    return ValueCompare<int>(GetGoatsTeleported(row1),
                             GetGoatsTeleported(row2));
  } else if (col_id == 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;
  } else {
    NOTREACHED();
    return 0;
  }
}

base::ProcessHandle TaskManagerModel::GetResourceProcessHandle(int index)
    const {
  CHECK_LT(index, ResourceCount());
  return resources_[index]->GetProcess();
}

TaskManager::Resource::Type TaskManagerModel::GetResourceType(int index) const {
  CHECK_LT(index, ResourceCount());
  return resources_[index]->GetType();
}

TabContentsWrapper* TaskManagerModel::GetResourceTabContents(int index) const {
  CHECK_LT(index, ResourceCount());
  return resources_[index]->GetTabContents();
}

const Extension* TaskManagerModel::GetResourceExtension(int index) const {
  CHECK_LT(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);
  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() {
  // Multiple StartUpdating requests may come in, and we only need to take
  // action the first time.
  update_requests_++;
  if (update_requests_ > 1)
    return;
  DCHECK_EQ(1, update_requests_);
  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).
  BrowserThread::PostTask(
      BrowserThread::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() {
  // Don't actually stop updating until we have heard as many calls as those
  // to StartUpdating.
  update_requests_--;
  if (update_requests_ > 0)
    return;
  // Make sure that update_requests_ cannot go negative.
  CHECK_EQ(0, update_requests_);
  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).
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(
          this, &TaskManagerModel::UnregisterForJobDoneNotifications));

  // Must clear the resources before the next attempt to start updating.
  Clear();
}

void TaskManagerModel::AddResourceProvider(
    TaskManager::ResourceProvider* provider) {
  DCHECK(provider);
  // AddRef matched with Release in destructor.
  provider->AddRef();
  providers_.push_back(provider);
}

void TaskManagerModel::RegisterForJobDoneNotifications() {
  net::g_url_request_job_tracker.AddObserver(this);
}

void TaskManagerModel::UnregisterForJobDoneNotifications() {
  net::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()) + 1;
    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::GetInstance());
#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::ModelChanged() {
  // Notify the table that the contents have changed for it to redraw.
  FOR_EACH_OBSERVER(TaskManagerModelObserver, observer_list_, OnModelChanged());
}

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_pid,
                                    param.render_process_host_child_id,
                                    param.routing_id);
    if (resource)
      break;
  }

  if (resource == NULL) {
    // We can't match a resource to the notification.  That might mean the
    // tab that started a download was closed, or the request may have had
    // no originating resource associated with it in the first place.
    // We attribute orphaned/unaccounted activity to the Browser process.
    CHECK(param.origin_pid || (param.render_process_host_child_id != -1));
    param.origin_pid = 0;
    param.render_process_host_child_id = param.routing_id = -1;
    BytesRead(param);
    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 net::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(net::URLRequestJob* job) {
}

void TaskManagerModel::OnJobRemoved(net::URLRequestJob* job) {
}

void TaskManagerModel::OnJobDone(net::URLRequestJob* job,
                                 const net::URLRequestStatus& status) {
}

void TaskManagerModel::OnJobRedirect(net::URLRequestJob* job,
                                     const GURL& location,
                                     int status_code) {
}

void TaskManagerModel::OnBytesRead(net::URLRequestJob* job, const char* buf,
                                   int byte_count) {
  // Only net::URLRequestJob instances created by the ResourceDispatcherHost
  // have a render view associated.  All other jobs will have -1 returned for
  // the render process child and routing ids - the jobs may still match a
  // resource based on their origin id, otherwise BytesRead() will attribute
  // the activity to the Browser resource.
  int render_process_host_child_id = -1, routing_id = -1;
  ResourceDispatcherHost::RenderViewForRequest(job->request(),
                                               &render_process_host_child_id,
                                               &routing_id);

  // Get the origin PID of the request's originator.  This will only be set for
  // plugins - for renderer or browser initiated requests it will be zero.
  int origin_pid =
      chrome_browser_net::GetOriginPIDForRequest(job->request());

  // This happens in the IO thread, post it to the UI thread.
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this,
          &TaskManagerModel::BytesRead,
          BytesReadParam(origin_pid,
          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, ResultCodes::KILLED, 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.
  TabContentsWrapper* chosen_tab_contents =
      model_->GetResourceTabContents(index);
  if (chosen_tab_contents)
    chosen_tab_contents->tab_contents()->Activate();
}

void TaskManager::AddResource(Resource* resource) {
  model_->AddResource(resource);
}

void TaskManager::RemoveResource(Resource* resource) {
  model_->RemoveResource(resource);
}

void TaskManager::OnWindowClosed() {
  model_->StopUpdating();
}

void TaskManager::ModelChanged() {
  model_->ModelChanged();
}

// 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())
      return;
    Profile* profile =
        g_browser_process->profile_manager()->GetDefaultProfile();
    if (!profile)
      return;
    browser = Browser::Create(profile);
    browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_FOREGROUND_TAB,
                     PageTransition::LINK);
    browser->window()->Show();
  } 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_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;
}