| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/task_manager_resource_providers.h" |
| |
| #include "base/file_version_info.h" |
| #include "base/message_loop.h" |
| #include "base/process_util.h" |
| #include "base/string_util.h" |
| #include "chrome/app/chrome_dll_resource.h" |
| #include "chrome/app/theme/theme_resources.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_list.h" |
| #include "chrome/browser/plugin_process_host.h" |
| #include "chrome/browser/plugin_service.h" |
| #include "chrome/browser/renderer_host/render_process_host.h" |
| #include "chrome/browser/resource_message_filter.h" |
| #include "chrome/browser/tab_contents/tab_util.h" |
| #include "chrome/browser/tab_contents/web_contents.h" |
| #include "chrome/common/resource_bundle.h" |
| #include "chrome/common/stl_util-inl.h" |
| #include "chrome/common/gfx/icon_util.h" |
| |
| #include "generated_resources.h" |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TaskManagerWebContentsResource class |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| TaskManagerWebContentsResource::TaskManagerWebContentsResource( |
| WebContents* web_contents) |
| : web_contents_(web_contents) { |
| // We cache the process as when the WebContents is closed the process |
| // becomes NULL and the TaskManager still needs it. |
| process_ = web_contents_->process()->process().handle(); |
| pid_ = base::GetProcId(process_); |
| } |
| |
| TaskManagerWebContentsResource::~TaskManagerWebContentsResource() { |
| } |
| |
| std::wstring TaskManagerWebContentsResource::GetTitle() const { |
| // GetTitle() and GetURL() can only be invoked when the WebContents has a |
| // controller. |
| if (!web_contents_->controller()) |
| return std::wstring(); |
| |
| // Fall back on the URL if there's no title. |
| std::wstring tab_title(web_contents_->GetTitle()); |
| if (tab_title.empty()) |
| tab_title = UTF8ToWide(web_contents_->GetURL().spec()); |
| |
| return l10n_util::GetStringF(IDS_TASK_MANAGER_TAB_PREFIX, tab_title); |
| } |
| |
| SkBitmap TaskManagerWebContentsResource::GetIcon() const { |
| return web_contents_->GetFavIcon(); |
| } |
| |
| HANDLE TaskManagerWebContentsResource::GetProcess() const { |
| return process_; |
| } |
| |
| TabContents* TaskManagerWebContentsResource::GetTabContents() const { |
| return dynamic_cast<TabContents*>(web_contents_); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TaskManagerWebContentsResourceProvider class |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| TaskManagerWebContentsResourceProvider:: |
| TaskManagerWebContentsResourceProvider(TaskManager* task_manager) |
| : task_manager_(task_manager), |
| updating_(false) { |
| } |
| |
| TaskManagerWebContentsResourceProvider:: |
| ~TaskManagerWebContentsResourceProvider() { |
| } |
| |
| TaskManager::Resource* TaskManagerWebContentsResourceProvider::GetResource( |
| int origin_pid, |
| int render_process_host_id, |
| int routing_id) { |
| |
| WebContents* web_contents = |
| tab_util::GetWebContentsByID(render_process_host_id, routing_id); |
| if (!web_contents) // Not one of our resource. |
| return NULL; |
| |
| if (!web_contents->process()->process().handle()) { |
| // We should not be holding on to a dead tab (it should have been removed |
| // through the NOTIFY_WEB_CONTENTS_DISCONNECTED notification. |
| NOTREACHED(); |
| return NULL; |
| } |
| |
| int pid = web_contents->process()->process().pid(); |
| if (pid != origin_pid) |
| return NULL; |
| |
| std::map<WebContents*, TaskManagerWebContentsResource*>::iterator |
| res_iter = resources_.find(web_contents); |
| if (res_iter == resources_.end()) |
| // Can happen if the tab was closed while a network request was being |
| // performed. |
| return NULL; |
| |
| return res_iter->second; |
| } |
| |
| void TaskManagerWebContentsResourceProvider::StartUpdating() { |
| DCHECK(!updating_); |
| updating_ = true; |
| // Add all the existing WebContents. |
| for (WebContentsIterator iterator; !iterator.done(); iterator++) { |
| WebContents* web_contents = *iterator; |
| // Don't add dead tabs or tabs that haven't yet connected. |
| if (web_contents->process()->process().handle() && |
| web_contents->notify_disconnection()) |
| AddToTaskManager(web_contents); |
| } |
| // Then we register for notifications to get new tabs. |
| NotificationService* service = NotificationService::current(); |
| service->AddObserver(this, NOTIFY_WEB_CONTENTS_CONNECTED, |
| NotificationService::AllSources()); |
| service->AddObserver(this, NOTIFY_WEB_CONTENTS_SWAPPED, |
| NotificationService::AllSources()); |
| service->AddObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED, |
| NotificationService::AllSources()); |
| } |
| |
| void TaskManagerWebContentsResourceProvider::StopUpdating() { |
| DCHECK(updating_); |
| updating_ = false; |
| |
| // Then we unregister for notifications to get new tabs. |
| NotificationService* service = NotificationService::current(); |
| service->RemoveObserver(this, NOTIFY_WEB_CONTENTS_CONNECTED, |
| NotificationService::AllSources()); |
| service->RemoveObserver(this, NOTIFY_WEB_CONTENTS_SWAPPED, |
| NotificationService::AllSources()); |
| service->RemoveObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED, |
| NotificationService::AllSources()); |
| |
| // Delete all the resources. |
| STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); |
| |
| resources_.clear(); |
| } |
| |
| void TaskManagerWebContentsResourceProvider::AddToTaskManager( |
| WebContents* web_contents) { |
| TaskManagerWebContentsResource* resource = |
| new TaskManagerWebContentsResource(web_contents); |
| resources_[web_contents] = resource; |
| task_manager_->AddResource(resource); |
| } |
| |
| void TaskManagerWebContentsResourceProvider::Add(WebContents* web_contents) { |
| if (!updating_) |
| return; |
| |
| if (!web_contents->process()->process().handle()) { |
| // Don't add sad tabs, we would have no information to show for them since |
| // they have no associated process. |
| return; |
| } |
| |
| std::map<WebContents*, TaskManagerWebContentsResource*>::const_iterator |
| iter = resources_.find(web_contents); |
| if (iter != resources_.end()) { |
| // The case may happen that we have added a WebContents 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(web_contents); |
| } |
| |
| void TaskManagerWebContentsResourceProvider::Remove(WebContents* web_contents) { |
| if (!updating_) |
| return; |
| std::map<WebContents*, TaskManagerWebContentsResource*>::iterator |
| iter = resources_.find(web_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. |
| TaskManagerWebContentsResource* resource = iter->second; |
| task_manager_->RemoveResource(resource); |
| // And from the provider. |
| resources_.erase(iter); |
| // Finally, delete the resource. |
| delete resource; |
| } |
| |
| void TaskManagerWebContentsResourceProvider::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type) { |
| case NOTIFY_WEB_CONTENTS_CONNECTED: |
| Add(Source<WebContents>(source).ptr()); |
| break; |
| case NOTIFY_WEB_CONTENTS_SWAPPED: |
| Remove(Source<WebContents>(source).ptr()); |
| Add(Source<WebContents>(source).ptr()); |
| break; |
| case NOTIFY_WEB_CONTENTS_DISCONNECTED: |
| Remove(Source<WebContents>(source).ptr()); |
| break; |
| default: |
| NOTREACHED() << "Unexpected notification."; |
| return; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TaskManagerPluginProcessResource class |
| //////////////////////////////////////////////////////////////////////////////// |
| SkBitmap* TaskManagerPluginProcessResource::default_icon_ = NULL; |
| |
| TaskManagerPluginProcessResource::TaskManagerPluginProcessResource( |
| PluginProcessInfo plugin_proc) |
| : plugin_process_(plugin_proc), |
| title_(), |
| network_usage_support_(false) { |
| pid_ = base::GetProcId(plugin_proc.process()); |
| if (!default_icon_) { |
| ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN); |
| } |
| } |
| |
| TaskManagerPluginProcessResource::~TaskManagerPluginProcessResource() { |
| } |
| |
| // TaskManagerResource methods: |
| std::wstring TaskManagerPluginProcessResource::GetTitle() const { |
| if (title_.empty()) { |
| std::wstring plugin_name; |
| WebPluginInfo info; |
| if (PluginService::GetInstance()-> |
| GetPluginInfoByPath(plugin_process_.plugin_path(), &info)) |
| plugin_name = info.name; |
| else |
| plugin_name = l10n_util::GetString(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME); |
| title_ = l10n_util::GetStringF(IDS_TASK_MANAGER_PLUGIN_PREFIX, |
| plugin_name); |
| } |
| return title_; |
| } |
| |
| SkBitmap TaskManagerPluginProcessResource::GetIcon() const { |
| return *default_icon_; |
| } |
| |
| HANDLE TaskManagerPluginProcessResource::GetProcess() const { |
| return plugin_process_.process(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TaskManagerPluginProcessResourceProvider class |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| TaskManagerPluginProcessResourceProvider:: |
| TaskManagerPluginProcessResourceProvider(TaskManager* task_manager) |
| : task_manager_(task_manager), |
| updating_(false), |
| ui_loop_(MessageLoop::current()) { |
| } |
| |
| TaskManagerPluginProcessResourceProvider:: |
| ~TaskManagerPluginProcessResourceProvider() { |
| } |
| |
| TaskManager::Resource* TaskManagerPluginProcessResourceProvider::GetResource( |
| int origin_pid, |
| int render_process_host_id, |
| int routing_id) { |
| std::map<int, TaskManagerPluginProcessResource*>::iterator iter = |
| pid_to_resources_.find(origin_pid); |
| if (iter != pid_to_resources_.end()) |
| return iter->second; |
| else |
| return NULL; |
| } |
| |
| void TaskManagerPluginProcessResourceProvider::StartUpdating() { |
| DCHECK(!updating_); |
| updating_ = true; |
| |
| // Register for notifications to get new plugin processes. |
| NotificationService* service = NotificationService::current(); |
| service->AddObserver(this, NOTIFY_PLUGIN_PROCESS_HOST_CONNECTED, |
| NotificationService::AllSources()); |
| service->AddObserver(this, NOTIFY_PLUGIN_PROCESS_HOST_DISCONNECTED, |
| NotificationService::AllSources()); |
| |
| // Get the existing plugins |
| MessageLoop* io_loop_ = g_browser_process->io_thread()->message_loop(); |
| io_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
| &TaskManagerPluginProcessResourceProvider::RetrievePluginProcessInfo)); |
| } |
| |
| void TaskManagerPluginProcessResourceProvider::StopUpdating() { |
| DCHECK(updating_); |
| updating_ = false; |
| |
| // Unregister for notifications to get new plugin processes. |
| NotificationService* service = NotificationService::current(); |
| service->RemoveObserver(this, NOTIFY_PLUGIN_PROCESS_HOST_CONNECTED, |
| NotificationService::AllSources()); |
| service->RemoveObserver(this, NOTIFY_PLUGIN_PROCESS_HOST_DISCONNECTED, |
| NotificationService::AllSources()); |
| |
| // Delete all the resources. |
| STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); |
| |
| resources_.clear(); |
| pid_to_resources_.clear(); |
| existing_plugin_process_info.clear(); |
| } |
| |
| void TaskManagerPluginProcessResourceProvider::Observe( |
| NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type) { |
| case NOTIFY_PLUGIN_PROCESS_HOST_CONNECTED: |
| Add(*Details<PluginProcessInfo>(details).ptr()); |
| break; |
| case NOTIFY_PLUGIN_PROCESS_HOST_DISCONNECTED: |
| Remove(*Details<PluginProcessInfo>(details).ptr()); |
| break; |
| default: |
| NOTREACHED() << "Unexpected notification."; |
| return; |
| } |
| } |
| |
| void TaskManagerPluginProcessResourceProvider::Add( |
| PluginProcessInfo plugin_process_info) { |
| if (!updating_) |
| return; |
| std::map<PluginProcessInfo, TaskManagerPluginProcessResource*>:: |
| const_iterator iter = resources_.find(plugin_process_info); |
| if (iter != resources_.end()) { |
| // The case may happen that we have added a plugin_process_host 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(plugin_process_info); |
| } |
| |
| void TaskManagerPluginProcessResourceProvider::Remove( |
| PluginProcessInfo plugin_process_info) { |
| if (!updating_) |
| return; |
| std::map<PluginProcessInfo, TaskManagerPluginProcessResource*> |
| ::iterator iter = resources_.find(plugin_process_info); |
| if (iter == resources_.end()) { |
| // PluginProcessHost 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. |
| TaskManagerPluginProcessResource* resource = iter->second; |
| task_manager_->RemoveResource(resource); |
| // Remove it from the provider. |
| resources_.erase(iter); |
| // Remove it from our pid map. |
| std::map<int, TaskManagerPluginProcessResource*>::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 TaskManagerPluginProcessResourceProvider::AddToTaskManager( |
| PluginProcessInfo plugin_process_info) { |
| TaskManagerPluginProcessResource* resource = |
| new TaskManagerPluginProcessResource(plugin_process_info); |
| resources_[plugin_process_info] = resource; |
| pid_to_resources_[base::GetProcId(plugin_process_info.process())] = |
| resource; |
| task_manager_->AddResource(resource); |
| } |
| |
| // The PluginProcessIterator has to be used from the IO thread. |
| void TaskManagerPluginProcessResourceProvider::RetrievePluginProcessInfo() { |
| for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { |
| PluginProcessHost* plugin = const_cast<PluginProcessHost*>(*iter); |
| DCHECK(plugin->process()); |
| PluginProcessInfo plugin_info(plugin->plugin_path(), plugin->process()); |
| existing_plugin_process_info.push_back(plugin_info); |
| } |
| // Now notify the UI thread that we have retrieved the PluginProcessHosts. |
| ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
| &TaskManagerPluginProcessResourceProvider::PluginProcessInfoRetreived)); |
| } |
| |
| // This is called on the UI thread. |
| void TaskManagerPluginProcessResourceProvider::PluginProcessInfoRetreived() { |
| std::vector<PluginProcessInfo>::const_iterator iter; |
| for (iter = existing_plugin_process_info.begin(); |
| iter != existing_plugin_process_info.end(); ++iter) { |
| Add(*iter); |
| } |
| existing_plugin_process_info.clear(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TaskManagerBrowserProcessResource class |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| SkBitmap* TaskManagerBrowserProcessResource::default_icon_ = NULL; |
| |
| TaskManagerBrowserProcessResource::TaskManagerBrowserProcessResource() |
| : title_(), |
| network_usage_support_(false) { |
| pid_ = GetCurrentProcessId(); |
| process_ = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, |
| FALSE, |
| pid_); |
| DCHECK(process_); |
| if (!default_icon_) { |
| HICON icon = LoadIcon(_AtlBaseModule.GetResourceInstance(), |
| MAKEINTRESOURCE(IDR_MAINFRAME)); |
| 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); |
| } |
| } |
| } |
| |
| TaskManagerBrowserProcessResource::~TaskManagerBrowserProcessResource() { |
| CloseHandle(process_); |
| } |
| |
| // TaskManagerResource methods: |
| std::wstring TaskManagerBrowserProcessResource::GetTitle() const { |
| if (title_.empty()) { |
| title_ = l10n_util::GetString(IDS_TASK_MANAGER_WEB_BROWSER_CELL_TEXT); |
| } |
| return title_; |
| } |
| |
| SkBitmap TaskManagerBrowserProcessResource::GetIcon() const { |
| return *default_icon_; |
| } |
| |
| HANDLE TaskManagerBrowserProcessResource::GetProcess() const { |
| return GetCurrentProcess(); // process_; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TaskManagerBrowserProcessResourceProvider class |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| TaskManagerBrowserProcessResourceProvider:: |
| TaskManagerBrowserProcessResourceProvider(TaskManager* task_manager) |
| : task_manager_(task_manager) { |
| } |
| |
| TaskManagerBrowserProcessResourceProvider:: |
| ~TaskManagerBrowserProcessResourceProvider() { |
| } |
| |
| TaskManager::Resource* TaskManagerBrowserProcessResourceProvider::GetResource( |
| int origin_pid, |
| int render_process_host_id, |
| int routing_id) { |
| if (origin_pid != resource_.process_id()) { |
| return NULL; |
| } |
| |
| return &resource_; |
| } |
| |
| void TaskManagerBrowserProcessResourceProvider::StartUpdating() { |
| task_manager_->AddResource(&resource_); |
| } |
| |
| void TaskManagerBrowserProcessResourceProvider::StopUpdating() { |
| } |
| |
| |