// 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_resource_providers.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/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/background_contents_service.h"
#include "chrome/browser/background_contents_service_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.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/prerender/prerender_contents.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/tab_contents/background_contents.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/render_messages.h"
#include "content/browser/browser_child_process_host.h"
#include "content/browser/browser_thread.h"
#include "content/browser/renderer_host/render_message_filter.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_service.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "third_party/sqlite/sqlite3.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.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 "ui/gfx/icon_util.h"
#endif  // defined(OS_WIN)

namespace {

// Returns the appropriate message prefix ID for tabs and extensions,
// reflecting whether they are apps or in incognito mode.
int GetMessagePrefixID(bool is_app, bool is_extension,
                       bool is_incognito) {
  return is_app ?
      (is_incognito ?
          IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX :
          IDS_TASK_MANAGER_APP_PREFIX) :
      (is_extension ?
          (is_incognito ?
              IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX :
              IDS_TASK_MANAGER_EXTENSION_PREFIX) :
          IDS_TASK_MANAGER_TAB_PREFIX);
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// TaskManagerRendererResource class
////////////////////////////////////////////////////////////////////////////////
TaskManagerRendererResource::TaskManagerRendererResource(
    base::ProcessHandle process, RenderViewHost* render_view_host)
    : process_(process),
      render_view_host_(render_view_host),
      pending_stats_update_(false),
      v8_memory_allocated_(0),
      v8_memory_used_(0),
      pending_v8_memory_allocated_update_(false) {
  // We cache the process and pid as when a Tab/BackgroundContents is closed the
  // process reference becomes NULL and the TaskManager still needs it.
  pid_ = base::GetProcId(process_);
  stats_.images.size = 0;
  stats_.cssStyleSheets.size = 0;
  stats_.scripts.size = 0;
  stats_.xslStyleSheets.size = 0;
  stats_.fonts.size = 0;
}

TaskManagerRendererResource::~TaskManagerRendererResource() {
}

void TaskManagerRendererResource::Refresh() {
  if (!pending_stats_update_) {
    render_view_host_->Send(new ViewMsg_GetCacheResourceStats);
    pending_stats_update_ = true;
  }
  if (!pending_v8_memory_allocated_update_) {
    render_view_host_->Send(new ViewMsg_GetV8HeapStats);
    pending_v8_memory_allocated_update_ = true;
  }
}

WebKit::WebCache::ResourceTypeStats
TaskManagerRendererResource::GetWebCoreCacheStats() const {
  return stats_;
}

size_t TaskManagerRendererResource::GetV8MemoryAllocated() const {
  return v8_memory_allocated_;
}

size_t TaskManagerRendererResource::GetV8MemoryUsed() const {
  return v8_memory_used_;
}

void TaskManagerRendererResource::NotifyResourceTypeStats(
    const WebKit::WebCache::ResourceTypeStats& stats) {
  stats_ = stats;
  pending_stats_update_ = false;
}

void TaskManagerRendererResource::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;
}

base::ProcessHandle TaskManagerRendererResource::GetProcess() const {
  return process_;
}

TaskManager::Resource::Type TaskManagerRendererResource::GetType() const {
  return RENDERER;
}

bool TaskManagerRendererResource::ReportsCacheStats() const {
  return true;
}

bool TaskManagerRendererResource::ReportsV8MemoryStats() const {
  return true;
}

bool TaskManagerRendererResource::SupportNetworkUsage() const {
  return true;
}

////////////////////////////////////////////////////////////////////////////////
// TaskManagerTabContentsResource class
////////////////////////////////////////////////////////////////////////////////

TaskManagerTabContentsResource::TaskManagerTabContentsResource(
    TabContentsWrapper* tab_contents)
    : TaskManagerRendererResource(
          tab_contents->tab_contents()->GetRenderProcessHost()->GetHandle(),
          tab_contents->render_view_host()),
      tab_contents_(tab_contents) {
}

TaskManagerTabContentsResource::~TaskManagerTabContentsResource() {
}

TaskManager::Resource::Type TaskManagerTabContentsResource::GetType() const {
  return tab_contents_->tab_contents()->HostsExtension() ? EXTENSION : RENDERER;
}

string16 TaskManagerTabContentsResource::GetTitle() const {
  // Fall back on the URL if there's no title.
  string16 tab_title = tab_contents_->tab_contents()->GetTitle();
  if (tab_title.empty()) {
    tab_title = UTF8ToUTF16(tab_contents_->tab_contents()->GetURL().spec());
    // Force URL to be LTR.
    tab_title = base::i18n::GetDisplayStringInLTRDirectionality(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);
  }

  ExtensionService* extensions_service =
      tab_contents_->profile()->GetExtensionService();
  int message_id = GetMessagePrefixID(
      extensions_service->IsInstalledApp(
          tab_contents_->tab_contents()->GetURL()),
      tab_contents_->tab_contents()->HostsExtension(),
      tab_contents_->profile()->IsOffTheRecord());
  return l10n_util::GetStringFUTF16(message_id, tab_title);
}

SkBitmap TaskManagerTabContentsResource::GetIcon() const {
  return tab_contents_->tab_contents()->GetFavicon();
}

TabContentsWrapper* TaskManagerTabContentsResource::GetTabContents() const {
  return tab_contents_;
}

const Extension* TaskManagerTabContentsResource::GetExtension() const {
  if (tab_contents_->tab_contents()->HostsExtension()) {
    ExtensionService* extensions_service =
        tab_contents_->profile()->GetExtensionService();
    return extensions_service->GetExtensionByURL(
        tab_contents_->tab_contents()->GetURL());
  }

  return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// 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;

  // If an origin PID was specified then the request originated in a plugin
  // working on the TabContent's behalf, so ignore it.
  if (origin_pid)
    return NULL;

  TabContentsWrapper* wrapper =
      TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
  std::map<TabContentsWrapper*, TaskManagerTabContentsResource*>::iterator
      res_iter = resources_.find(wrapper);
  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(
    TabContentsWrapper* tab_contents) {
  TaskManagerTabContentsResource* resource =
      new TaskManagerTabContentsResource(tab_contents);
  resources_[tab_contents] = resource;
  task_manager_->AddResource(resource);
}

void TaskManagerTabContentsResourceProvider::Add(
    TabContentsWrapper* tab_contents) {
  if (!updating_)
    return;

  // Don't add dead tabs or tabs that haven't yet connected.
  if (!tab_contents->tab_contents()->GetRenderProcessHost()->GetHandle() ||
      !tab_contents->tab_contents()->notify_disconnection()) {
    return;
  }

  std::map<TabContentsWrapper*, 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(
    TabContentsWrapper* tab_contents) {
  if (!updating_)
    return;
  std::map<TabContentsWrapper*, 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) {
  TabContentsWrapper* tab_contents =
      TabContentsWrapper::GetCurrentWrapperForContents(
          Source<TabContents>(source).ptr());
  // A background page does not have a TabContentsWrapper.
  if (!tab_contents)
    return;
  switch (type.value) {
    case NotificationType::TAB_CONTENTS_CONNECTED:
      Add(tab_contents);
      break;
    case NotificationType::TAB_CONTENTS_SWAPPED:
      Remove(tab_contents);
      Add(tab_contents);
      break;
    case NotificationType::TAB_CONTENTS_DESTROYED:
      // If this DCHECK is triggered, it could explain http://crbug.com/7321 .
      DCHECK(resources_.find(tab_contents) ==
             resources_.end()) << "TAB_CONTENTS_DESTROYED with no associated "
                                  "TAB_CONTENTS_DISCONNECTED";
      // Fall through.
    case NotificationType::TAB_CONTENTS_DISCONNECTED:
      Remove(tab_contents);
      break;
    default:
      NOTREACHED() << "Unexpected notification.";
      return;
  }
}

////////////////////////////////////////////////////////////////////////////////
// TaskManagerPrerenderResource class
////////////////////////////////////////////////////////////////////////////////
// static
SkBitmap* TaskManagerPrerenderResource::default_icon_ = NULL;

TaskManagerPrerenderResource::TaskManagerPrerenderResource(
    RenderViewHost* render_view_host)
    : TaskManagerRendererResource(
          render_view_host->process()->GetHandle(),
          render_view_host),
      process_route_id_pair_(std::make_pair(render_view_host->process()->id(),
                                            render_view_host->routing_id())) {
  if (!default_icon_) {
    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    default_icon_ = rb.GetBitmapNamed(IDR_PRERENDER);
  }
}

TaskManagerPrerenderResource::~TaskManagerPrerenderResource() {
}

TaskManager::Resource::Type TaskManagerPrerenderResource::GetType() const {
  return RENDERER;
}

string16 TaskManagerPrerenderResource::GetTitle() const {
  // The URL is used as the title.
  // TODO(dominich): Expose document title through RenderHostDelegate.
  // http://crbug.com/77776
  RenderViewHost* render_view_host =
      RenderViewHost::FromID(process_route_id_pair_.first,
                             process_route_id_pair_.second);

  // In some instances, for instance when the RenderProcessHost has been
  // destroyed, we try to get the title for a RenderViewHost that has
  // been removed. Return an empty string in this case.
  if (!render_view_host)
    return EmptyString16();

  RenderViewHostDelegate* delegate = render_view_host->delegate();

  string16 title = UTF8ToUTF16(delegate->GetURL().spec());
  // Force URL to be LTR.
  title = base::i18n::GetDisplayStringInLTRDirectionality(title);

  int message_id = IDS_TASK_MANAGER_PRERENDER_PREFIX;
  return l10n_util::GetStringFUTF16(message_id, title);
}

SkBitmap TaskManagerPrerenderResource::GetIcon() const {
  // TODO(dominich): use the favicon if available.
  // http://crbug.com/77782
  return *default_icon_;
}

////////////////////////////////////////////////////////////////////////////////
// TaskManagerPrerenderResourceProvider class
////////////////////////////////////////////////////////////////////////////////

TaskManagerPrerenderResourceProvider::TaskManagerPrerenderResourceProvider(
    TaskManager* task_manager)
    :  updating_(false),
       task_manager_(task_manager) {
}

TaskManagerPrerenderResourceProvider::~TaskManagerPrerenderResourceProvider() {
  STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
}

TaskManager::Resource* TaskManagerPrerenderResourceProvider::GetResource(
    int origin_pid,
    int render_process_host_id,
    int routing_id) {
  // If an origin PID was specified then the request originated in a plugin so
  // ignore it.
  if (origin_pid)
    return NULL;

  ResourceMap::iterator res_iter = resources_.find(
      std::make_pair(render_process_host_id, routing_id));
  if (res_iter == resources_.end())
    return NULL;

  return res_iter->second;
}

void TaskManagerPrerenderResourceProvider::StartUpdating() {
  DCHECK(!updating_);
  updating_ = true;

  // Add all the existing PrerenderContents.
  const ResourceDispatcherHost* resource_dispatcher_host =
      g_browser_process->resource_dispatcher_host();
  const ResourceDispatcherHost::PrerenderChildRouteIdPairs&
      prerender_child_route_id_pairs =
          resource_dispatcher_host->prerender_child_route_id_pairs();
  for (ResourceDispatcherHost::PrerenderChildRouteIdPairs::const_iterator it =
           prerender_child_route_id_pairs.begin();
       it != prerender_child_route_id_pairs.end();
       ++it) {
    Add(*it);
  }

  // Then we register for notifications to get new prerender items.
  registrar_.Add(this, NotificationType::PRERENDER_CONTENTS_STARTED,
                 NotificationService::AllSources());
  registrar_.Add(this, NotificationType::PRERENDER_CONTENTS_USED,
                 NotificationService::AllSources());
  registrar_.Add(this, NotificationType::PRERENDER_CONTENTS_DESTROYED,
                 NotificationService::AllSources());
}

void TaskManagerPrerenderResourceProvider::StopUpdating() {
  DCHECK(updating_);
  updating_ = false;

  // Then we unregister for notifications to get new prerender items.
  registrar_.Remove(this, NotificationType::PRERENDER_CONTENTS_STARTED,
                    NotificationService::AllSources());
  registrar_.Remove(this, NotificationType::PRERENDER_CONTENTS_USED,
                    NotificationService::AllSources());
  registrar_.Remove(this, NotificationType::PRERENDER_CONTENTS_DESTROYED,
                    NotificationService::AllSources());

  // Delete all the resources.
  STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());

  resources_.clear();
}

void TaskManagerPrerenderResourceProvider::AddToTaskManager(
    const std::pair<int, int>& process_route_id_pair) {
  RenderViewHost* render_view_host =
      RenderViewHost::FromID(process_route_id_pair.first,
                             process_route_id_pair.second);
  CHECK(render_view_host);
  TaskManagerPrerenderResource* resource =
      new TaskManagerPrerenderResource(render_view_host);
  resources_[process_route_id_pair] = resource;
  task_manager_->AddResource(resource);
}

void TaskManagerPrerenderResourceProvider::Add(
    const std::pair<int, int>& process_route_id_pair) {
  if (!updating_)
    return;

  // Don't add dead prerender contents or prerender contents that haven't yet
  // started.
  RenderViewHost* render_view_host =
      RenderViewHost::FromID(process_route_id_pair.first,
                             process_route_id_pair.second);
  if (!render_view_host)
    return;

  AddToTaskManager(process_route_id_pair);
}

void TaskManagerPrerenderResourceProvider::Remove(
    const std::pair<int, int>& process_route_id_pair) {
  if (!updating_)
    return;

  RenderViewHost* render_view_host =
      RenderViewHost::FromID(process_route_id_pair.first,
                             process_route_id_pair.second);

  if (!render_view_host) {
    // This will happen if the PrerenderContents was used. We should have had a
    // PRERENDER_CONTENTS_USED message about it and already removed it, but
    // either way we can't remove a NULL resource.
    return;
  }

  ResourceMap::iterator iter = resources_.find(process_route_id_pair);
  DCHECK(iter != resources_.end());

  // Remove the resource from the Task Manager.
  TaskManagerPrerenderResource* resource = iter->second;
  task_manager_->RemoveResource(resource);
  // And from the provider.
  resources_.erase(iter);
  // Finally, delete the resource.
  delete resource;
}

void TaskManagerPrerenderResourceProvider::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  DCHECK(NotificationService::NoDetails() == details);
  switch (type.value) {
    case NotificationType::PRERENDER_CONTENTS_STARTED:
      Add(*Source<std::pair<int, int> >(source).ptr());
      break;
    case NotificationType::PRERENDER_CONTENTS_USED:
    case NotificationType::PRERENDER_CONTENTS_DESTROYED:
      Remove(*Source<std::pair<int, int> >(source).ptr());
      break;
    default:
      NOTREACHED() << "Unexpected notification.";
      return;
  }
}
////////////////////////////////////////////////////////////////////////////////
// TaskManagerBackgroundContentsResource class
////////////////////////////////////////////////////////////////////////////////

SkBitmap* TaskManagerBackgroundContentsResource::default_icon_ = NULL;

TaskManagerBackgroundContentsResource::TaskManagerBackgroundContentsResource(
    BackgroundContents* background_contents,
    const string16& application_name)
    : TaskManagerRendererResource(
          background_contents->render_view_host()->process()->GetHandle(),
          background_contents->render_view_host()),
      background_contents_(background_contents),
      application_name_(application_name) {
  // Just use the same icon that other extension resources do.
  // TODO(atwilson): Use the favicon when that's available.
  if (!default_icon_) {
    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN);
  }
  // Ensure that the string has the appropriate direction markers (see comment
  // in TaskManagerTabContentsResource::GetTitle()).
  base::i18n::AdjustStringForLocaleDirection(&application_name_);
}

TaskManagerBackgroundContentsResource::~TaskManagerBackgroundContentsResource(
    ) {
}

string16 TaskManagerBackgroundContentsResource::GetTitle() const {
  string16 title = application_name_;

  if (title.empty()) {
    // No title (can't locate the parent app for some reason) so just display
    // the URL (properly forced to be LTR).
    title = base::i18n::GetDisplayStringInLTRDirectionality(
        UTF8ToUTF16(background_contents_->GetURL().spec()));
  }
  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_BACKGROUND_PREFIX, title);
}


SkBitmap TaskManagerBackgroundContentsResource::GetIcon() const {
  return *default_icon_;
}

bool TaskManagerBackgroundContentsResource::IsBackground() const {
  return true;
}

////////////////////////////////////////////////////////////////////////////////
// TaskManagerBackgroundContentsResourceProvider class
////////////////////////////////////////////////////////////////////////////////

TaskManagerBackgroundContentsResourceProvider::
    TaskManagerBackgroundContentsResourceProvider(TaskManager* task_manager)
    : updating_(false),
      task_manager_(task_manager) {
}

TaskManagerBackgroundContentsResourceProvider::
    ~TaskManagerBackgroundContentsResourceProvider() {
}

TaskManager::Resource*
TaskManagerBackgroundContentsResourceProvider::GetResource(
    int origin_pid,
    int render_process_host_id,
    int routing_id) {
  BackgroundContents* contents = BackgroundContents::GetBackgroundContentsByID(
      render_process_host_id, routing_id);
  if (!contents)  // This resource no longer exists.
    return NULL;

  // If an origin PID was specified, the request is from a plugin, not the
  // render view host process
  if (origin_pid)
    return NULL;

  std::map<BackgroundContents*,
      TaskManagerBackgroundContentsResource*>::iterator res_iter =
      resources_.find(contents);
  if (res_iter == resources_.end())
    // Can happen if the page went away while a network request was being
    // performed.
    return NULL;

  return res_iter->second;
}

void TaskManagerBackgroundContentsResourceProvider::StartUpdating() {
  DCHECK(!updating_);
  updating_ = true;

  // Add all the existing BackgroundContents from every profile.
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
  for (size_t i = 0; i < profiles.size(); ++i) {
    BackgroundContentsService* background_contents_service =
        BackgroundContentsServiceFactory::GetForProfile(profiles[i]);
    ExtensionService* extensions_service = profiles[i]->GetExtensionService();
    std::vector<BackgroundContents*> contents =
        background_contents_service->GetBackgroundContents();
    for (std::vector<BackgroundContents*>::iterator iterator = contents.begin();
         iterator != contents.end(); ++iterator) {
      string16 application_name;
      // Lookup the name from the parent extension.
      if (extensions_service) {
        const string16& application_id =
            background_contents_service->GetParentApplicationId(*iterator);
        const Extension* extension = extensions_service->GetExtensionById(
            UTF16ToUTF8(application_id), false);
        if (extension)
          application_name = UTF8ToUTF16(extension->name());
      }
      Add(*iterator, application_name);
    }
  }

  // Then we register for notifications to get new BackgroundContents.
  registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_OPENED,
                 NotificationService::AllSources());
  registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_NAVIGATED,
                 NotificationService::AllSources());
  registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_DELETED,
                 NotificationService::AllSources());
}

void TaskManagerBackgroundContentsResourceProvider::StopUpdating() {
  DCHECK(updating_);
  updating_ = false;

  // Unregister for notifications
  registrar_.Remove(this, NotificationType::BACKGROUND_CONTENTS_OPENED,
                    NotificationService::AllSources());
  registrar_.Remove(this, NotificationType::BACKGROUND_CONTENTS_NAVIGATED,
                    NotificationService::AllSources());
  registrar_.Remove(this, NotificationType::BACKGROUND_CONTENTS_DELETED,
                    NotificationService::AllSources());

  // Delete all the resources.
  STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());

  resources_.clear();
}

void TaskManagerBackgroundContentsResourceProvider::AddToTaskManager(
    BackgroundContents* background_contents,
    const string16& application_name) {
  TaskManagerBackgroundContentsResource* resource =
      new TaskManagerBackgroundContentsResource(background_contents,
                                                application_name);
  resources_[background_contents] = resource;
  task_manager_->AddResource(resource);
}

void TaskManagerBackgroundContentsResourceProvider::Add(
    BackgroundContents* contents, const string16& application_name) {
  if (!updating_)
    return;

  // Don't add contents whose process is dead.
  if (!contents->render_view_host()->process()->GetHandle())
    return;

  // Should never add the same BackgroundContents twice.
  DCHECK(resources_.find(contents) == resources_.end());
  AddToTaskManager(contents, application_name);
}

void TaskManagerBackgroundContentsResourceProvider::Remove(
    BackgroundContents* contents) {
  if (!updating_)
    return;
  std::map<BackgroundContents*,
      TaskManagerBackgroundContentsResource*>::iterator iter =
      resources_.find(contents);
  DCHECK(iter != resources_.end());

  // Remove the resource from the Task Manager.
  TaskManagerBackgroundContentsResource* resource = iter->second;
  task_manager_->RemoveResource(resource);
  // And from the provider.
  resources_.erase(iter);
  // Finally, delete the resource.
  delete resource;
}

void TaskManagerBackgroundContentsResourceProvider::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  switch (type.value) {
    case NotificationType::BACKGROUND_CONTENTS_OPENED: {
      // Get the name from the parent application. If no parent application is
      // found, just pass an empty string - BackgroundContentsResource::GetTitle
      // will display the URL instead in this case. This should never happen
      // except in rare cases when an extension is being unloaded or chrome is
      // exiting while the task manager is displayed.
      string16 application_name;
      ExtensionService* service =
          Source<Profile>(source)->GetExtensionService();
      if (service) {
        std::string application_id = UTF16ToUTF8(
            Details<BackgroundContentsOpenedDetails>(details)->application_id);
        const Extension* extension =
            service->GetExtensionById(application_id, false);
        // Extension can be NULL when running unit tests.
        if (extension)
          application_name = UTF8ToUTF16(extension->name());
      }
      Add(Details<BackgroundContentsOpenedDetails>(details)->contents,
          application_name);
      // Opening a new BackgroundContents needs to force the display to refresh
      // (applications may now be considered "background" that weren't before).
      task_manager_->ModelChanged();
      break;
    }
    case NotificationType::BACKGROUND_CONTENTS_NAVIGATED: {
      BackgroundContents* contents = Details<BackgroundContents>(details).ptr();
      // Should never get a NAVIGATED before OPENED.
      DCHECK(resources_.find(contents) != resources_.end());
      // Preserve the application name.
      string16 application_name(
          resources_.find(contents)->second->application_name());
      Remove(contents);
      Add(contents, application_name);
      break;
    }
    case NotificationType::BACKGROUND_CONTENTS_DELETED:
      Remove(Details<BackgroundContents>(details).ptr());
      // Closing a BackgroundContents needs to force the display to refresh
      // (applications may now be considered "foreground" that weren't before).
      task_manager_->ModelChanged();
      break;
    default:
      NOTREACHED() << "Unexpected notification.";
      return;
  }
}

////////////////////////////////////////////////////////////////////////////////
// TaskManagerChildProcessResource class
////////////////////////////////////////////////////////////////////////////////
SkBitmap* TaskManagerChildProcessResource::default_icon_ = NULL;

TaskManagerChildProcessResource::TaskManagerChildProcessResource(
    const 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.pid();
  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:
string16 TaskManagerChildProcessResource::GetTitle() const {
  if (title_.empty())
    title_ = GetLocalizedTitle();

  return title_;
}

SkBitmap TaskManagerChildProcessResource::GetIcon() const {
  return *default_icon_;
}

base::ProcessHandle TaskManagerChildProcessResource::GetProcess() const {
  return child_process_.handle();
}

TaskManager::Resource::Type TaskManagerChildProcessResource::GetType() const {
  // Translate types to TaskManager::ResourceType, since ChildProcessInfo's type
  // is not available for all TaskManager resources.
  switch (child_process_.type()) {
    case ChildProcessInfo::BROWSER_PROCESS:
      return TaskManager::Resource::BROWSER;
    case ChildProcessInfo::RENDER_PROCESS:
      return TaskManager::Resource::RENDERER;
    case ChildProcessInfo::PLUGIN_PROCESS:
      return TaskManager::Resource::PLUGIN;
    case ChildProcessInfo::WORKER_PROCESS:
      return TaskManager::Resource::WORKER;
    case ChildProcessInfo::NACL_LOADER_PROCESS:
    case ChildProcessInfo::NACL_BROKER_PROCESS:
      return TaskManager::Resource::NACL;
    case ChildProcessInfo::UTILITY_PROCESS:
      return TaskManager::Resource::UTILITY;
    case ChildProcessInfo::PROFILE_IMPORT_PROCESS:
      return TaskManager::Resource::PROFILE_IMPORT;
    case ChildProcessInfo::ZYGOTE_PROCESS:
      return TaskManager::Resource::ZYGOTE;
    case ChildProcessInfo::SANDBOX_HELPER_PROCESS:
      return TaskManager::Resource::SANDBOX_HELPER;
    case ChildProcessInfo::GPU_PROCESS:
      return TaskManager::Resource::GPU;
    default:
      return TaskManager::Resource::UNKNOWN;
  }
}

bool TaskManagerChildProcessResource::SupportNetworkUsage() const {
  return network_usage_support_;
}

void TaskManagerChildProcessResource::SetSupportNetworkUsage() {
  network_usage_support_ = true;
}

string16 TaskManagerChildProcessResource::GetLocalizedTitle() const {
  string16 title = WideToUTF16Hack(child_process_.name());
  if (child_process_.type() == ChildProcessInfo::PLUGIN_PROCESS &&
      title.empty()) {
    title = l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME);
  }

  // Explicitly mark name as LTR if there is no strong RTL character,
  // to avoid the wrong concatenation result similar to "!Yahoo! Mail: the
  // best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew
  // or Arabic word for "plugin".
  base::i18n::AdjustStringForLocaleDirection(&title);

  switch (child_process_.type()) {
    case ChildProcessInfo::UTILITY_PROCESS:
      return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX);

    case ChildProcessInfo::PROFILE_IMPORT_PROCESS:
      return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX);

    case ChildProcessInfo::GPU_PROCESS:
      return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX);

    case ChildProcessInfo::NACL_BROKER_PROCESS:
      return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX);

    case ChildProcessInfo::PLUGIN_PROCESS:
    case ChildProcessInfo::PPAPI_PLUGIN_PROCESS:
    case ChildProcessInfo::PPAPI_BROKER_PROCESS: {
      return l10n_util::GetStringFUTF16(
          IDS_TASK_MANAGER_PLUGIN_PREFIX, title,
          WideToUTF16Hack(child_process_.version()));
    }

    case ChildProcessInfo::NACL_LOADER_PROCESS:
      return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX, title);

    case ChildProcessInfo::WORKER_PROCESS:
      return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_WORKER_PREFIX, title);

    // These types don't need display names or get them from elsewhere.
    case ChildProcessInfo::BROWSER_PROCESS:
    case ChildProcessInfo::RENDER_PROCESS:
    case ChildProcessInfo::ZYGOTE_PROCESS:
    case ChildProcessInfo::SANDBOX_HELPER_PROCESS:
      NOTREACHED();
      break;

    case ChildProcessInfo::UNKNOWN_PROCESS:
      NOTREACHED() << "Need localized name for child process type.";
  }

  return title;
}

////////////////////////////////////////////////////////////////////////////////
// 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.
  BrowserThread::PostTask(
      BrowserThread::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(
    const 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(
    const 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(
    const 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.
  BrowserThread::PostTask(
      BrowserThread::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_);
  string16 extension_name = UTF8ToUTF16(GetExtension()->name());
  DCHECK(!extension_name.empty());

  int message_id = GetMessagePrefixID(GetExtension()->is_app(), true,
      extension_host_->profile()->IsOffTheRecord());
  title_ = l10n_util::GetStringFUTF16(message_id, extension_name);
}

TaskManagerExtensionProcessResource::~TaskManagerExtensionProcessResource() {
}

string16 TaskManagerExtensionProcessResource::GetTitle() const {
  return title_;
}

SkBitmap TaskManagerExtensionProcessResource::GetIcon() const {
  return *default_icon_;
}

base::ProcessHandle TaskManagerExtensionProcessResource::GetProcess() const {
  return process_handle_;
}

TaskManager::Resource::Type
TaskManagerExtensionProcessResource::GetType() const {
  return EXTENSION;
}

bool TaskManagerExtensionProcessResource::SupportNetworkUsage() const {
  return true;
}

void TaskManagerExtensionProcessResource::SetSupportNetworkUsage() {
  NOTREACHED();
}

const Extension* TaskManagerExtensionProcessResource::GetExtension() const {
  return extension_host_->extension();
}

bool TaskManagerExtensionProcessResource::IsBackground() const {
  return extension_host_->GetRenderViewType() ==
      ViewType::EXTENSION_BACKGROUND_PAGE;
}

////////////////////////////////////////////////////////////////////////////////
// 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();
  std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
  for (size_t i = 0; i < profiles.size(); ++i) {
    ExtensionProcessManager* process_manager =
        profiles[i]->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 =
          profiles[i]->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_ = l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NOTIFICATION_PREFIX,
                                      balloon_host_->GetSource());
}

TaskManagerNotificationResource::~TaskManagerNotificationResource() {
}

string16 TaskManagerNotificationResource::GetTitle() const {
  return title_;
}

SkBitmap TaskManagerNotificationResource::GetIcon() const {
  return *default_icon_;
}

base::ProcessHandle TaskManagerNotificationResource::GetProcess() const {
  return process_handle_;
}

TaskManager::Resource::Type TaskManagerNotificationResource::GetType() const {
  return NOTIFICATION;
}

bool TaskManagerNotificationResource::SupportNetworkUsage() const {
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// 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_() {
  int 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:
string16 TaskManagerBrowserProcessResource::GetTitle() const {
  if (title_.empty()) {
    title_ = l10n_util::GetStringUTF16(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_;
}

TaskManager::Resource::Type TaskManagerBrowserProcessResource::GetType() const {
  return BROWSER;
}

bool TaskManagerBrowserProcessResource::SupportNetworkUsage() const {
  return true;
}

void TaskManagerBrowserProcessResource::SetSupportNetworkUsage() {
  NOTREACHED();
}

bool TaskManagerBrowserProcessResource::ReportsSqliteMemoryUsed() const {
  return true;
}

////////////////////////////////////////////////////////////////////////////////
// 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 || render_process_host_id != -1) {
    return NULL;
  }

  return &resource_;
}

void TaskManagerBrowserProcessResourceProvider::StartUpdating() {
  task_manager_->AddResource(&resource_);
}

void TaskManagerBrowserProcessResourceProvider::StopUpdating() {
}