// 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/background_resource_provider.h" #include "base/i18n/rtl.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.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/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/tab_contents/background_contents.h" #include "chrome/browser/task_manager/renderer_resource.h" #include "chrome/browser/task_manager/resource_provider.h" #include "chrome/browser/task_manager/task_manager.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "extensions/common/extension.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::RenderProcessHost; using content::RenderViewHost; using content::WebContents; using extensions::Extension; namespace task_manager { class BackgroundContentsResource : public RendererResource { public: BackgroundContentsResource( BackgroundContents* background_contents, const base::string16& application_name); virtual ~BackgroundContentsResource(); // Resource methods: virtual base::string16 GetTitle() const OVERRIDE; virtual base::string16 GetProfileName() const OVERRIDE; virtual gfx::ImageSkia GetIcon() const OVERRIDE; virtual bool IsBackground() const OVERRIDE; const base::string16& application_name() const { return application_name_; } private: BackgroundContents* background_contents_; base::string16 application_name_; // The icon painted for BackgroundContents. // TODO(atwilson): Use the favicon when there's a way to get the favicon for // BackgroundContents. static gfx::ImageSkia* default_icon_; DISALLOW_COPY_AND_ASSIGN(BackgroundContentsResource); }; gfx::ImageSkia* BackgroundContentsResource::default_icon_ = NULL; // TODO(atwilson): http://crbug.com/116893 // HACK: if the process handle is invalid, we use the current process's handle. // This preserves old behavior but is incorrect, and should be fixed. BackgroundContentsResource::BackgroundContentsResource( BackgroundContents* background_contents, const base::string16& application_name) : RendererResource( background_contents->web_contents()->GetRenderProcessHost()-> GetHandle() ? background_contents->web_contents()->GetRenderProcessHost()-> GetHandle() : base::Process::Current().handle(), background_contents->web_contents()->GetRenderViewHost()), 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.GetImageSkiaNamed(IDR_PLUGINS_FAVICON); } // Ensure that the string has the appropriate direction markers (see comment // in TabContentsResource::GetTitle()). base::i18n::AdjustStringForLocaleDirection(&application_name_); } BackgroundContentsResource::~BackgroundContentsResource() { } base::string16 BackgroundContentsResource::GetTitle() const { base::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); } base::string16 BackgroundContentsResource::GetProfileName() const { return base::string16(); } gfx::ImageSkia BackgroundContentsResource::GetIcon() const { return *default_icon_; } bool BackgroundContentsResource::IsBackground() const { return true; } //////////////////////////////////////////////////////////////////////////////// // BackgroundContentsResourceProvider class //////////////////////////////////////////////////////////////////////////////// BackgroundContentsResourceProvider:: BackgroundContentsResourceProvider(TaskManager* task_manager) : updating_(false), task_manager_(task_manager) { } BackgroundContentsResourceProvider::~BackgroundContentsResourceProvider() { } Resource* BackgroundContentsResourceProvider::GetResource( int origin_pid, int render_process_host_id, int routing_id) { // If an origin PID was specified, the request is from a plugin, not the // render view host process if (origin_pid) return NULL; for (Resources::iterator i = resources_.begin(); i != resources_.end(); i++) { WebContents* tab = i->first->web_contents(); if (tab->GetRenderProcessHost()->GetID() == render_process_host_id && tab->GetRenderViewHost()->GetRoutingID() == routing_id) { return i->second; } } // Can happen if the page went away while a network request was being // performed. return NULL; } void BackgroundContentsResourceProvider::StartUpdating() { DCHECK(!updating_); updating_ = true; // Add all the existing BackgroundContents from every profile, including // incognito profiles. ProfileManager* profile_manager = g_browser_process->profile_manager(); std::vector profiles(profile_manager->GetLoadedProfiles()); size_t num_default_profiles = profiles.size(); for (size_t i = 0; i < num_default_profiles; ++i) { if (profiles[i]->HasOffTheRecordProfile()) { profiles.push_back(profiles[i]->GetOffTheRecordProfile()); } } for (size_t i = 0; i < profiles.size(); ++i) { BackgroundContentsService* background_contents_service = BackgroundContentsServiceFactory::GetForProfile(profiles[i]); std::vector contents = background_contents_service->GetBackgroundContents(); ExtensionService* extension_service = profiles[i]->GetExtensionService(); for (std::vector::iterator iterator = contents.begin(); iterator != contents.end(); ++iterator) { base::string16 application_name; // Lookup the name from the parent extension. if (extension_service) { const base::string16& application_id = background_contents_service->GetParentApplicationId(*iterator); const Extension* extension = extension_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, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::NotificationService::AllBrowserContextsAndSources()); } void BackgroundContentsResourceProvider::StopUpdating() { DCHECK(updating_); updating_ = false; // Unregister for notifications registrar_.Remove( this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Remove( this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Remove( this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Remove( this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::NotificationService::AllBrowserContextsAndSources()); // Delete all the resources. STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); resources_.clear(); } void BackgroundContentsResourceProvider::AddToTaskManager( BackgroundContents* background_contents, const base::string16& application_name) { BackgroundContentsResource* resource = new BackgroundContentsResource(background_contents, application_name); resources_[background_contents] = resource; task_manager_->AddResource(resource); } void BackgroundContentsResourceProvider::Add( BackgroundContents* contents, const base::string16& application_name) { if (!updating_) return; // TODO(atwilson): http://crbug.com/116893 // We should check that the process handle is valid here, but it won't // be in the case of NOTIFICATION_BACKGROUND_CONTENTS_OPENED. // Should never add the same BackgroundContents twice. DCHECK(resources_.find(contents) == resources_.end()); AddToTaskManager(contents, application_name); } void BackgroundContentsResourceProvider::Remove(BackgroundContents* contents) { if (!updating_) return; Resources::iterator iter = resources_.find(contents); DCHECK(iter != resources_.end()); // Remove the resource from the Task Manager. BackgroundContentsResource* resource = iter->second; task_manager_->RemoveResource(resource); // And from the provider. resources_.erase(iter); // Finally, delete the resource. delete resource; } void BackgroundContentsResourceProvider::Observe( int type, const content::NotificationSource& source, const content::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. base::string16 application_name; ExtensionService* service = content::Source(source)->GetExtensionService(); if (service) { std::string application_id = UTF16ToUTF8( content::Details(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(content::Details(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 = content::Details(details).ptr(); // Should never get a NAVIGATED before OPENED. DCHECK(resources_.find(contents) != resources_.end()); // Preserve the application name. base::string16 application_name( resources_.find(contents)->second->application_name()); Remove(contents); Add(contents, application_name); break; } case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED: Remove(content::Details(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; case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: { WebContents* web_contents = content::Source(source).ptr(); for (Resources::iterator i = resources_.begin(); i != resources_.end(); i++) { if (i->first->web_contents() == web_contents) { base::string16 application_name = i->second->application_name(); BackgroundContents* contents = i->first; Remove(contents); Add(contents, application_name); return; } } break; } default: NOTREACHED() << "Unexpected notification."; return; } } } // namespace task_manager