blob: 6fdb5784d8226eb1afef4928c1e7ca8022bba293 [file] [log] [blame]
// 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.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/background_contents_service.h"
#include "chrome/browser/background/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/favicon/favicon_tab_helper.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/chrome_notification_types.h"
#include "chrome/common/chrome_switches.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 "content/common/url_constants.h"
#include "content/common/view_messages.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "grit/theme_resources_standard.h"
#include "third_party/sqlite/sqlite3.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "v8/include/v8.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, bool is_prerender) {
if (is_app) {
if (is_incognito)
return IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX;
else
return IDS_TASK_MANAGER_APP_PREFIX;
} else if (is_extension) {
if (is_incognito)
return IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX;
else
return IDS_TASK_MANAGER_EXTENSION_PREFIX;
} else if (is_prerender) {
return IDS_TASK_MANAGER_PRERENDER_PREFIX;
} else {
return 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),
fps_(0.0f),
pending_fps_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 ChromeViewMsg_GetCacheResourceStats);
pending_stats_update_ = true;
}
if (!pending_fps_update_) {
render_view_host_->Send(
new ViewMsg_GetFPS(render_view_host_->routing_id()));
pending_fps_update_ = true;
}
if (!pending_v8_memory_allocated_update_) {
render_view_host_->Send(new ChromeViewMsg_GetV8HeapStats);
pending_v8_memory_allocated_update_ = true;
}
}
WebKit::WebCache::ResourceTypeStats
TaskManagerRendererResource::GetWebCoreCacheStats() const {
return stats_;
}
float TaskManagerRendererResource::GetFPS() const {
return fps_;
}
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::NotifyFPS(float fps) {
fps_ = fps;
pending_fps_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;
}
int TaskManagerRendererResource::GetRoutingId() const {
return render_view_host_->routing_id();
}
bool TaskManagerRendererResource::ReportsCacheStats() const {
return true;
}
bool TaskManagerRendererResource::ReportsFPS() const {
return true;
}
bool TaskManagerRendererResource::ReportsV8MemoryStats() const {
return true;
}
bool TaskManagerRendererResource::SupportNetworkUsage() const {
return true;
}
////////////////////////////////////////////////////////////////////////////////
// TaskManagerTabContentsResource class
////////////////////////////////////////////////////////////////////////////////
// static
SkBitmap* TaskManagerTabContentsResource::prerender_icon_ = NULL;
TaskManagerTabContentsResource::TaskManagerTabContentsResource(
TabContentsWrapper* tab_contents)
: TaskManagerRendererResource(
tab_contents->tab_contents()->GetRenderProcessHost()->GetHandle(),
tab_contents->render_view_host()),
tab_contents_(tab_contents) {
if (!prerender_icon_) {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
prerender_icon_ = rb.GetBitmapNamed(IDR_PRERENDER);
}
}
TaskManagerTabContentsResource::~TaskManagerTabContentsResource() {
}
bool TaskManagerTabContentsResource::IsPrerendering() const {
prerender::PrerenderManager* prerender_manager =
tab_contents_->profile()->GetPrerenderManager();
return prerender_manager &&
prerender_manager->IsTabContentsPrerendering(
tab_contents_->tab_contents());
}
bool TaskManagerTabContentsResource::HostsExtension() const {
return tab_contents_->tab_contents()->GetURL().SchemeIs(
chrome::kExtensionScheme);
}
TaskManager::Resource::Type TaskManagerTabContentsResource::GetType() const {
return HostsExtension() ? EXTENSION : RENDERER;
}
string16 TaskManagerTabContentsResource::GetTitle() const {
// Fall back on the URL if there's no title.
TabContents* contents = tab_contents_->tab_contents();
string16 tab_title = contents->GetTitle();
GURL url = contents->GetURL();
if (tab_title.empty()) {
tab_title = UTF8ToUTF16(url.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);
}
// Only classify as an app if the URL is an app and the tab is hosting an
// extension process. (It's possible to be showing the URL from before it
// was installed as an app.)
ExtensionService* extensions_service =
tab_contents_->profile()->GetExtensionService();
bool is_app = extensions_service->IsInstalledApp(url) &&
contents->GetRenderProcessHost()->is_extension_process();
int message_id = GetMessagePrefixID(
is_app,
HostsExtension(),
tab_contents_->profile()->IsOffTheRecord(),
IsPrerendering());
return l10n_util::GetStringFUTF16(message_id, tab_title);
}
SkBitmap TaskManagerTabContentsResource::GetIcon() const {
if (IsPrerendering())
return *prerender_icon_;
return tab_contents_->favicon_tab_helper()->GetFavicon();
}
TabContentsWrapper* TaskManagerTabContentsResource::GetTabContents() const {
return tab_contents_;
}
const Extension* TaskManagerTabContentsResource::GetExtension() const {
if (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, content::NOTIFICATION_TAB_CONTENTS_CONNECTED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, content::NOTIFICATION_TAB_CONTENTS_SWAPPED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, content::NOTIFICATION_TAB_CONTENTS_DISCONNECTED,
NotificationService::AllBrowserContextsAndSources());
// 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, content::NOTIFICATION_TAB_CONTENTS_DESTROYED,
NotificationService::AllBrowserContextsAndSources());
}
void TaskManagerTabContentsResourceProvider::StopUpdating() {
DCHECK(updating_);
updating_ = false;
// Then we unregister for notifications to get new tabs.
registrar_.Remove(this, content::NOTIFICATION_TAB_CONTENTS_CONNECTED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, content::NOTIFICATION_TAB_CONTENTS_SWAPPED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, content::NOTIFICATION_TAB_CONTENTS_DISCONNECTED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, content::NOTIFICATION_TAB_CONTENTS_DESTROYED,
NotificationService::AllBrowserContextsAndSources());
// 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(int 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) {
case content::NOTIFICATION_TAB_CONTENTS_CONNECTED:
Add(tab_contents);
break;
case content::NOTIFICATION_TAB_CONTENTS_SWAPPED:
Remove(tab_contents);
Add(tab_contents);
break;
case content::NOTIFICATION_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 content::NOTIFICATION_TAB_CONTENTS_DISCONNECTED:
Remove(tab_contents);
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, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
NotificationService::AllBrowserContextsAndSources());
}
void TaskManagerBackgroundContentsResourceProvider::StopUpdating() {
DCHECK(updating_);
updating_ = false;
// Unregister for notifications
registrar_.Remove(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
NotificationService::AllBrowserContextsAndSources());
// 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(
int type,
const NotificationSource& source,
const NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_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 chrome::NOTIFICATION_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 chrome::NOTIFICATION_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 = 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, 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, content::NOTIFICATION_CHILD_PROCESS_HOST_CONNECTED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, content::NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED,
NotificationService::AllBrowserContextsAndSources());
// 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, content::NOTIFICATION_CHILD_PROCESS_HOST_CONNECTED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this,
content::NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED,
NotificationService::AllBrowserContextsAndSources());
// Delete all the resources.
STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
resources_.clear();
pid_to_resources_.clear();
existing_child_process_info_.clear();
}
void TaskManagerChildProcessResourceProvider::Observe(
int type,
const NotificationSource& source,
const NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_CHILD_PROCESS_HOST_CONNECTED:
Add(*Details<ChildProcessInfo>(details).ptr());
break;
case content::NOTIFICATION_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(), false);
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::IsOffTheRecordSessionActiveForProfile(profiles[i])) {
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, chrome::NOTIFICATION_EXTENSION_PROCESS_CREATED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
NotificationService::AllBrowserContextsAndSources());
}
void TaskManagerExtensionProcessResourceProvider::StopUpdating() {
DCHECK(updating_);
updating_ = false;
// Unregister for notifications about extension process changes.
registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_PROCESS_CREATED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
NotificationService::AllBrowserContextsAndSources());
registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
NotificationService::AllBrowserContextsAndSources());
// Delete all the resources.
STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
resources_.clear();
pid_to_resources_.clear();
}
void TaskManagerExtensionProcessResourceProvider::Observe(
int type,
const NotificationSource& source,
const NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_EXTENSION_PROCESS_CREATED:
AddToTaskManager(Details<ExtensionHost>(details).ptr());
break;
case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
case chrome::NOTIFICATION_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;
}
////////////////////////////////////////////////////////////////////////////////
// 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_POSIX) && !defined(OS_MACOSX)
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;
}
// BrowserProcess uses v8 for proxy resolver in certain cases.
bool TaskManagerBrowserProcessResource::ReportsV8MemoryStats() const {
const CommandLine* command_line = CommandLine::ForCurrentProcess();
bool using_v8 = !command_line->HasSwitch(switches::kWinHttpProxyResolver);
if (using_v8 && command_line->HasSwitch(switches::kSingleProcess)) {
using_v8 = false;
}
return using_v8;
}
size_t TaskManagerBrowserProcessResource::GetV8MemoryAllocated() const {
v8::HeapStatistics stats;
v8::V8::GetHeapStatistics(&stats);
return stats.total_heap_size();
}
size_t TaskManagerBrowserProcessResource::GetV8MemoryUsed() const {
v8::HeapStatistics stats;
v8::V8::GetHeapStatistics(&stats);
return stats.used_heap_size();
}
////////////////////////////////////////////////////////////////////////////////
// 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() {
}