// Copyright 2013 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/child_process_resource_provider.h" #include #include "base/i18n/rtl.h" #include "base/strings/string16.h" #include "chrome/browser/task_manager/resource_provider.h" #include "chrome/browser/task_manager/task_manager.h" #include "components/nacl/common/nacl_process_type.h" #include "content/public/browser/browser_child_process_host_iterator.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image_skia.h" using content::BrowserChildProcessHostIterator; using content::BrowserThread; using content::WebContents; namespace task_manager { class ChildProcessResource : public Resource { public: ChildProcessResource(int process_type, const string16& name, base::ProcessHandle handle, int unique_process_id); virtual ~ChildProcessResource(); // Resource methods: virtual string16 GetTitle() const OVERRIDE; virtual string16 GetProfileName() const OVERRIDE; virtual gfx::ImageSkia GetIcon() const OVERRIDE; virtual base::ProcessHandle GetProcess() const OVERRIDE; virtual int GetUniqueChildProcessId() const OVERRIDE; virtual Type GetType() const OVERRIDE; virtual bool SupportNetworkUsage() const OVERRIDE; virtual void SetSupportNetworkUsage() OVERRIDE; // Returns the pid of the child process. int process_id() const { return pid_; } private: // Returns a localized title for the child process. For example, a plugin // process would be "Plug-in: Flash" when name is "Flash". string16 GetLocalizedTitle() const; int process_type_; string16 name_; base::ProcessHandle handle_; int pid_; int unique_process_id_; mutable string16 title_; bool network_usage_support_; // The icon painted for the child processs. // TODO(jcampan): we should have plugin specific icons for well-known // plugins. static gfx::ImageSkia* default_icon_; DISALLOW_COPY_AND_ASSIGN(ChildProcessResource); }; gfx::ImageSkia* ChildProcessResource::default_icon_ = NULL; ChildProcessResource::ChildProcessResource( int process_type, const string16& name, base::ProcessHandle handle, int unique_process_id) : process_type_(process_type), name_(name), handle_(handle), unique_process_id_(unique_process_id), 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_ = base::GetProcId(handle); if (!default_icon_) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); default_icon_ = rb.GetImageSkiaNamed(IDR_PLUGINS_FAVICON); // TODO(jabdelmalek): use different icon for web workers. } } ChildProcessResource::~ChildProcessResource() { } // Resource methods: string16 ChildProcessResource::GetTitle() const { if (title_.empty()) title_ = GetLocalizedTitle(); return title_; } string16 ChildProcessResource::GetProfileName() const { return string16(); } gfx::ImageSkia ChildProcessResource::GetIcon() const { return *default_icon_; } base::ProcessHandle ChildProcessResource::GetProcess() const { return handle_; } int ChildProcessResource::GetUniqueChildProcessId() const { return unique_process_id_; } Resource::Type ChildProcessResource::GetType() const { // Translate types to Resource::Type, since ChildProcessData's type // is not available for all TaskManager resources. switch (process_type_) { case content::PROCESS_TYPE_PLUGIN: case content::PROCESS_TYPE_PPAPI_PLUGIN: case content::PROCESS_TYPE_PPAPI_BROKER: return Resource::PLUGIN; case content::PROCESS_TYPE_UTILITY: return Resource::UTILITY; case content::PROCESS_TYPE_ZYGOTE: return Resource::ZYGOTE; case content::PROCESS_TYPE_SANDBOX_HELPER: return Resource::SANDBOX_HELPER; case content::PROCESS_TYPE_GPU: return Resource::GPU; case PROCESS_TYPE_NACL_LOADER: case PROCESS_TYPE_NACL_BROKER: return Resource::NACL; default: return Resource::UNKNOWN; } } bool ChildProcessResource::SupportNetworkUsage() const { return network_usage_support_; } void ChildProcessResource::SetSupportNetworkUsage() { network_usage_support_ = true; } string16 ChildProcessResource::GetLocalizedTitle() const { string16 title = name_; if (title.empty()) { switch (process_type_) { case content::PROCESS_TYPE_PLUGIN: case content::PROCESS_TYPE_PPAPI_PLUGIN: case content::PROCESS_TYPE_PPAPI_BROKER: title = l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME); break; default: // Nothing to do for non-plugin processes. break; } } // 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 (process_type_) { case content::PROCESS_TYPE_UTILITY: return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX); case content::PROCESS_TYPE_GPU: return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX); case content::PROCESS_TYPE_PLUGIN: case content::PROCESS_TYPE_PPAPI_PLUGIN: return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_PREFIX, title); case content::PROCESS_TYPE_PPAPI_BROKER: return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_BROKER_PREFIX, title); case PROCESS_TYPE_NACL_BROKER: return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX); case PROCESS_TYPE_NACL_LOADER: return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX, title); // These types don't need display names or get them from elsewhere. case content::PROCESS_TYPE_BROWSER: case content::PROCESS_TYPE_RENDERER: case content::PROCESS_TYPE_ZYGOTE: case content::PROCESS_TYPE_SANDBOX_HELPER: case content::PROCESS_TYPE_MAX: NOTREACHED(); break; case content::PROCESS_TYPE_WORKER: NOTREACHED() << "Workers are not handled by this provider."; break; case content::PROCESS_TYPE_UNKNOWN: NOTREACHED() << "Need localized name for child process type."; } return title; } //////////////////////////////////////////////////////////////////////////////// // ChildProcessResourceProvider class //////////////////////////////////////////////////////////////////////////////// ChildProcessResourceProvider:: ChildProcessResourceProvider(TaskManager* task_manager) : task_manager_(task_manager), updating_(false) { } ChildProcessResourceProvider::~ChildProcessResourceProvider() { } Resource* ChildProcessResourceProvider::GetResource( int origin_pid, int render_process_host_id, int routing_id) { PidResourceMap::iterator iter = pid_to_resources_.find(origin_pid); if (iter != pid_to_resources_.end()) return iter->second; else return NULL; } void ChildProcessResourceProvider::StartUpdating() { DCHECK(!updating_); updating_ = true; // Get the existing child processes. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &ChildProcessResourceProvider::RetrieveChildProcessData, this)); BrowserChildProcessObserver::Add(this); } void ChildProcessResourceProvider::StopUpdating() { DCHECK(updating_); updating_ = false; // Delete all the resources. STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); resources_.clear(); pid_to_resources_.clear(); BrowserChildProcessObserver::Remove(this); } void ChildProcessResourceProvider::BrowserChildProcessHostConnected( const content::ChildProcessData& data) { DCHECK(updating_); // Workers are handled by WorkerResourceProvider. if (data.process_type == content::PROCESS_TYPE_WORKER) return; if (resources_.count(data.handle)) { // 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(data); } void ChildProcessResourceProvider:: BrowserChildProcessHostDisconnected( const content::ChildProcessData& data) { DCHECK(updating_); if (data.process_type == content::PROCESS_TYPE_WORKER) return; ChildProcessMap::iterator iter = resources_.find(data.handle); if (iter == resources_.end()) { // ChildProcessData 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. ChildProcessResource* resource = iter->second; task_manager_->RemoveResource(resource); // Remove it from the provider. resources_.erase(iter); // Remove it from our pid map. PidResourceMap::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 ChildProcessResourceProvider::AddToTaskManager( const content::ChildProcessData& child_process_data) { ChildProcessResource* resource = new ChildProcessResource( child_process_data.process_type, child_process_data.name, child_process_data.handle, child_process_data.id); resources_[child_process_data.handle] = resource; pid_to_resources_[resource->process_id()] = resource; task_manager_->AddResource(resource); } // The ChildProcessData::Iterator has to be used from the IO thread. void ChildProcessResourceProvider::RetrieveChildProcessData() { std::vector child_processes; for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { // Only add processes which are already started, since we need their handle. if (iter.GetData().handle == base::kNullProcessHandle) continue; if (iter.GetData().process_type == content::PROCESS_TYPE_WORKER) continue; child_processes.push_back(iter.GetData()); } // Now notify the UI thread that we have retrieved information about child // processes. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( &ChildProcessResourceProvider::ChildProcessDataRetreived, this, child_processes)); } // This is called on the UI thread. void ChildProcessResourceProvider::ChildProcessDataRetreived( const std::vector& child_processes) { for (size_t i = 0; i < child_processes.size(); ++i) AddToTaskManager(child_processes[i]); task_manager_->model()->NotifyDataReady(); } } // namespace task_manager