// Copyright 2012 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/tab_contents_resource_provider.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/devtools/devtools_window.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/prerender/prerender_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/search/instant_service.h" #include "chrome/browser/search/instant_service_factory.h" #include "chrome/browser/search/search.h" #include "chrome/browser/tab_contents/tab_util.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 "chrome/browser/task_manager/task_manager_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "extensions/common/constants.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" #if defined(ENABLE_FULL_PRINTING) #include "chrome/browser/printing/background_printing_manager.h" #endif using content::WebContents; using extensions::Extension; namespace { bool IsContentsPrerendering(WebContents* web_contents) { Profile* profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); prerender::PrerenderManager* prerender_manager = prerender::PrerenderManagerFactory::GetForProfile(profile); return prerender_manager && prerender_manager->IsWebContentsPrerendering(web_contents, NULL); } bool IsContentsBackgroundPrinted(WebContents* web_contents) { #if defined(ENABLE_FULL_PRINTING) printing::BackgroundPrintingManager* printing_manager = g_browser_process->background_printing_manager(); return printing_manager->HasPrintPreviewDialog(web_contents); #else return false; #endif } } // namespace namespace task_manager { // Tracks a single tab contents, prerendered page, Instant page, or background // printing page. class TabContentsResource : public RendererResource { public: explicit TabContentsResource(content::WebContents* web_contents); virtual ~TabContentsResource(); // Resource methods: virtual Type GetType() const OVERRIDE; virtual base::string16 GetTitle() const OVERRIDE; virtual base::string16 GetProfileName() const OVERRIDE; virtual gfx::ImageSkia GetIcon() const OVERRIDE; virtual content::WebContents* GetWebContents() const OVERRIDE; virtual const extensions::Extension* GetExtension() const OVERRIDE; private: // Returns true if contains content rendered by an extension. bool HostsExtension() const; static gfx::ImageSkia* prerender_icon_; content::WebContents* web_contents_; Profile* profile_; bool is_instant_ntp_; DISALLOW_COPY_AND_ASSIGN(TabContentsResource); }; gfx::ImageSkia* TabContentsResource::prerender_icon_ = NULL; TabContentsResource::TabContentsResource( WebContents* web_contents) : RendererResource(web_contents->GetRenderProcessHost()->GetHandle(), web_contents->GetRenderViewHost()), web_contents_(web_contents), profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), is_instant_ntp_(chrome::IsPreloadedInstantExtendedNTP(web_contents)) { if (!prerender_icon_) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); prerender_icon_ = rb.GetImageSkiaNamed(IDR_PRERENDER); } } TabContentsResource::~TabContentsResource() { } bool TabContentsResource::HostsExtension() const { return web_contents_->GetURL().SchemeIs(extensions::kExtensionScheme); } Resource::Type TabContentsResource::GetType() const { return HostsExtension() ? EXTENSION : RENDERER; } base::string16 TabContentsResource::GetTitle() const { // Fall back on the URL if there's no title. GURL url = web_contents_->GetURL(); base::string16 tab_title = util::GetTitleFromWebContents(web_contents_); // 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* extension_service = profile_->GetExtensionService(); extensions::ProcessMap* process_map = extension_service->process_map(); bool is_app = extension_service->IsInstalledApp(url) && process_map->Contains(web_contents_->GetRenderProcessHost()->GetID()); int message_id = util::GetMessagePrefixID( is_app, HostsExtension(), profile_->IsOffTheRecord(), IsContentsPrerendering(web_contents_), is_instant_ntp_, false); // is_background return l10n_util::GetStringFUTF16(message_id, tab_title); } base::string16 TabContentsResource::GetProfileName() const { return util::GetProfileNameFromInfoCache(profile_); } gfx::ImageSkia TabContentsResource::GetIcon() const { if (IsContentsPrerendering(web_contents_)) return *prerender_icon_; FaviconTabHelper::CreateForWebContents(web_contents_); return FaviconTabHelper::FromWebContents(web_contents_)-> GetFavicon().AsImageSkia(); } WebContents* TabContentsResource::GetWebContents() const { return web_contents_; } const Extension* TabContentsResource::GetExtension() const { if (HostsExtension()) { ExtensionService* extension_service = profile_->GetExtensionService(); return extension_service->extensions()->GetByID( web_contents_->GetURL().host()); } return NULL; } //////////////////////////////////////////////////////////////////////////////// // TabContentsResourceProvider class //////////////////////////////////////////////////////////////////////////////// TabContentsResourceProvider:: TabContentsResourceProvider(TaskManager* task_manager) : updating_(false), task_manager_(task_manager) { } TabContentsResourceProvider::~TabContentsResourceProvider() { } Resource* TabContentsResourceProvider::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 an origin PID was specified then the request originated in a plugin // working on the WebContents's behalf, so ignore it. if (origin_pid) return NULL; std::map<WebContents*, TabContentsResource*>::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 TabContentsResourceProvider::StartUpdating() { DCHECK(!updating_); updating_ = true; // The contents that are tracked by this resource provider are those that // are tab contents (WebContents serving as a tab in a Browser), Instant // pages, prerender pages, and background printed pages. // Add all the existing WebContentses. for (TabContentsIterator iterator; !iterator.done(); iterator.Next()) { Add(*iterator); DevToolsWindow* docked = DevToolsWindow::GetDockedInstanceForInspectedTab(*iterator); if (docked) Add(docked->web_contents()); } // Add all the prerender pages. std::vector<Profile*> profiles( g_browser_process->profile_manager()->GetLoadedProfiles()); for (size_t i = 0; i < profiles.size(); ++i) { prerender::PrerenderManager* prerender_manager = prerender::PrerenderManagerFactory::GetForProfile(profiles[i]); if (prerender_manager) { const std::vector<content::WebContents*> contentses = prerender_manager->GetAllPrerenderingContents(); for (size_t j = 0; j < contentses.size(); ++j) Add(contentses[j]); } } // Add all the Instant Extended prerendered NTPs. for (size_t i = 0; i < profiles.size(); ++i) { const InstantService* instant_service = InstantServiceFactory::GetForProfile(profiles[i]); if (instant_service && instant_service->GetNTPContents()) Add(instant_service->GetNTPContents()); } #if defined(ENABLE_FULL_PRINTING) // Add all the pages being background printed. printing::BackgroundPrintingManager* printing_manager = g_browser_process->background_printing_manager(); for (printing::BackgroundPrintingManager::WebContentsSet::iterator i = printing_manager->begin(); i != printing_manager->end(); ++i) { Add(*i); } #endif // Then we register for notifications to get new web contents. registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, content::NotificationService::AllBrowserContextsAndSources()); } void TabContentsResourceProvider::StopUpdating() { DCHECK(updating_); updating_ = false; // Then we unregister for notifications to get new web contents. registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Remove(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, content::NotificationService::AllBrowserContextsAndSources()); // Delete all the resources. STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); resources_.clear(); } void TabContentsResourceProvider::AddToTaskManager(WebContents* web_contents) { TabContentsResource* resource = new TabContentsResource(web_contents); resources_[web_contents] = resource; task_manager_->AddResource(resource); } void TabContentsResourceProvider::Add(WebContents* web_contents) { if (!updating_) return; // The contents that are tracked by this resource provider are those that // are tab contents (WebContents serving as a tab in a Browser), Instant // pages, prerender pages, and background printed pages. if (!chrome::FindBrowserWithWebContents(web_contents) && !IsContentsPrerendering(web_contents) && !chrome::IsPreloadedInstantExtendedNTP(web_contents) && !IsContentsBackgroundPrinted(web_contents) && !DevToolsWindow::IsDevToolsWindow(web_contents->GetRenderViewHost())) { return; } // Don't add dead tabs or tabs that haven't yet connected. if (!web_contents->GetRenderProcessHost()->GetHandle() || !web_contents->WillNotifyDisconnection()) { return; } if (resources_.count(web_contents)) { // 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 TabContentsResourceProvider::Remove(WebContents* web_contents) { if (!updating_) return; std::map<WebContents*, TabContentsResource*>::iterator iter = resources_.find(web_contents); if (iter == resources_.end()) { // Since WebContents 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. TabContentsResource* resource = iter->second; task_manager_->RemoveResource(resource); // And from the provider. resources_.erase(iter); // Finally, delete the resource. delete resource; } void TabContentsResourceProvider::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { WebContents* web_contents = content::Source<WebContents>(source).ptr(); switch (type) { case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: Add(web_contents); break; case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: Remove(web_contents); Add(web_contents); break; case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: Remove(web_contents); break; default: NOTREACHED() << "Unexpected notification."; return; } } } // namespace task_manager