Home | History | Annotate | Download | only in task_manager
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/task_manager/tab_contents_resource_provider.h"
      6 
      7 #include "chrome/browser/browser_process.h"
      8 #include "chrome/browser/chrome_notification_types.h"
      9 #include "chrome/browser/devtools/devtools_window.h"
     10 #include "chrome/browser/extensions/extension_service.h"
     11 #include "chrome/browser/favicon/favicon_tab_helper.h"
     12 #include "chrome/browser/prerender/prerender_manager.h"
     13 #include "chrome/browser/prerender/prerender_manager_factory.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/profiles/profile_manager.h"
     16 #include "chrome/browser/search/instant_service.h"
     17 #include "chrome/browser/search/instant_service_factory.h"
     18 #include "chrome/browser/search/search.h"
     19 #include "chrome/browser/tab_contents/tab_util.h"
     20 #include "chrome/browser/task_manager/renderer_resource.h"
     21 #include "chrome/browser/task_manager/resource_provider.h"
     22 #include "chrome/browser/task_manager/task_manager.h"
     23 #include "chrome/browser/task_manager/task_manager_util.h"
     24 #include "chrome/browser/ui/browser.h"
     25 #include "chrome/browser/ui/browser_finder.h"
     26 #include "chrome/browser/ui/browser_iterator.h"
     27 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
     28 #include "content/public/browser/notification_service.h"
     29 #include "content/public/browser/render_process_host.h"
     30 #include "content/public/browser/web_contents.h"
     31 #include "extensions/common/constants.h"
     32 #include "grit/theme_resources.h"
     33 #include "ui/base/l10n/l10n_util.h"
     34 #include "ui/base/resource/resource_bundle.h"
     35 #include "ui/gfx/image/image_skia.h"
     36 
     37 #if defined(ENABLE_FULL_PRINTING)
     38 #include "chrome/browser/printing/background_printing_manager.h"
     39 #endif
     40 
     41 using content::WebContents;
     42 using extensions::Extension;
     43 
     44 namespace {
     45 
     46 bool IsContentsPrerendering(WebContents* web_contents) {
     47   Profile* profile =
     48       Profile::FromBrowserContext(web_contents->GetBrowserContext());
     49   prerender::PrerenderManager* prerender_manager =
     50       prerender::PrerenderManagerFactory::GetForProfile(profile);
     51   return prerender_manager &&
     52          prerender_manager->IsWebContentsPrerendering(web_contents, NULL);
     53 }
     54 
     55 bool IsContentsBackgroundPrinted(WebContents* web_contents) {
     56 #if defined(ENABLE_FULL_PRINTING)
     57   printing::BackgroundPrintingManager* printing_manager =
     58       g_browser_process->background_printing_manager();
     59   return printing_manager->HasPrintPreviewDialog(web_contents);
     60 #else
     61   return false;
     62 #endif
     63 }
     64 
     65 }  // namespace
     66 
     67 namespace task_manager {
     68 
     69 // Tracks a single tab contents, prerendered page, Instant page, or background
     70 // printing page.
     71 class TabContentsResource : public RendererResource {
     72  public:
     73   explicit TabContentsResource(content::WebContents* web_contents);
     74   virtual ~TabContentsResource();
     75 
     76   // Resource methods:
     77   virtual Type GetType() const OVERRIDE;
     78   virtual base::string16 GetTitle() const OVERRIDE;
     79   virtual base::string16 GetProfileName() const OVERRIDE;
     80   virtual gfx::ImageSkia GetIcon() const OVERRIDE;
     81   virtual content::WebContents* GetWebContents() const OVERRIDE;
     82   virtual const extensions::Extension* GetExtension() const OVERRIDE;
     83 
     84  private:
     85   // Returns true if contains content rendered by an extension.
     86   bool HostsExtension() const;
     87 
     88   static gfx::ImageSkia* prerender_icon_;
     89   content::WebContents* web_contents_;
     90   Profile* profile_;
     91   bool is_instant_ntp_;
     92 
     93   DISALLOW_COPY_AND_ASSIGN(TabContentsResource);
     94 };
     95 
     96 gfx::ImageSkia* TabContentsResource::prerender_icon_ = NULL;
     97 
     98 TabContentsResource::TabContentsResource(
     99     WebContents* web_contents)
    100     : RendererResource(web_contents->GetRenderProcessHost()->GetHandle(),
    101                        web_contents->GetRenderViewHost()),
    102       web_contents_(web_contents),
    103       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
    104       is_instant_ntp_(chrome::IsPreloadedInstantExtendedNTP(web_contents)) {
    105   if (!prerender_icon_) {
    106     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    107     prerender_icon_ = rb.GetImageSkiaNamed(IDR_PRERENDER);
    108   }
    109 }
    110 
    111 TabContentsResource::~TabContentsResource() {
    112 }
    113 
    114 bool TabContentsResource::HostsExtension() const {
    115   return web_contents_->GetURL().SchemeIs(extensions::kExtensionScheme);
    116 }
    117 
    118 Resource::Type TabContentsResource::GetType() const {
    119   return HostsExtension() ? EXTENSION : RENDERER;
    120 }
    121 
    122 base::string16 TabContentsResource::GetTitle() const {
    123   // Fall back on the URL if there's no title.
    124   GURL url = web_contents_->GetURL();
    125   base::string16 tab_title = util::GetTitleFromWebContents(web_contents_);
    126 
    127   // Only classify as an app if the URL is an app and the tab is hosting an
    128   // extension process.  (It's possible to be showing the URL from before it
    129   // was installed as an app.)
    130   ExtensionService* extension_service = profile_->GetExtensionService();
    131   extensions::ProcessMap* process_map = extension_service->process_map();
    132   bool is_app = extension_service->IsInstalledApp(url) &&
    133       process_map->Contains(web_contents_->GetRenderProcessHost()->GetID());
    134 
    135   int message_id = util::GetMessagePrefixID(
    136       is_app,
    137       HostsExtension(),
    138       profile_->IsOffTheRecord(),
    139       IsContentsPrerendering(web_contents_),
    140       is_instant_ntp_,
    141       false);  // is_background
    142   return l10n_util::GetStringFUTF16(message_id, tab_title);
    143 }
    144 
    145 base::string16 TabContentsResource::GetProfileName() const {
    146   return util::GetProfileNameFromInfoCache(profile_);
    147 }
    148 
    149 gfx::ImageSkia TabContentsResource::GetIcon() const {
    150   if (IsContentsPrerendering(web_contents_))
    151     return *prerender_icon_;
    152   FaviconTabHelper::CreateForWebContents(web_contents_);
    153   return FaviconTabHelper::FromWebContents(web_contents_)->
    154       GetFavicon().AsImageSkia();
    155 }
    156 
    157 WebContents* TabContentsResource::GetWebContents() const {
    158   return web_contents_;
    159 }
    160 
    161 const Extension* TabContentsResource::GetExtension() const {
    162   if (HostsExtension()) {
    163     ExtensionService* extension_service = profile_->GetExtensionService();
    164     return extension_service->extensions()->GetByID(
    165         web_contents_->GetURL().host());
    166   }
    167 
    168   return NULL;
    169 }
    170 
    171 ////////////////////////////////////////////////////////////////////////////////
    172 // TabContentsResourceProvider class
    173 ////////////////////////////////////////////////////////////////////////////////
    174 
    175 TabContentsResourceProvider::
    176     TabContentsResourceProvider(TaskManager* task_manager)
    177     :  updating_(false),
    178        task_manager_(task_manager) {
    179 }
    180 
    181 TabContentsResourceProvider::~TabContentsResourceProvider() {
    182 }
    183 
    184 Resource* TabContentsResourceProvider::GetResource(
    185     int origin_pid,
    186     int render_process_host_id,
    187     int routing_id) {
    188   WebContents* web_contents =
    189       tab_util::GetWebContentsByID(render_process_host_id, routing_id);
    190   if (!web_contents)  // Not one of our resource.
    191     return NULL;
    192 
    193   // If an origin PID was specified then the request originated in a plugin
    194   // working on the WebContents's behalf, so ignore it.
    195   if (origin_pid)
    196     return NULL;
    197 
    198   std::map<WebContents*, TabContentsResource*>::iterator
    199       res_iter = resources_.find(web_contents);
    200   if (res_iter == resources_.end()) {
    201     // Can happen if the tab was closed while a network request was being
    202     // performed.
    203     return NULL;
    204   }
    205   return res_iter->second;
    206 }
    207 
    208 void TabContentsResourceProvider::StartUpdating() {
    209   DCHECK(!updating_);
    210   updating_ = true;
    211 
    212   // The contents that are tracked by this resource provider are those that
    213   // are tab contents (WebContents serving as a tab in a Browser), Instant
    214   // pages, prerender pages, and background printed pages.
    215 
    216   // Add all the existing WebContentses.
    217   for (TabContentsIterator iterator; !iterator.done(); iterator.Next()) {
    218     Add(*iterator);
    219     DevToolsWindow* docked =
    220         DevToolsWindow::GetDockedInstanceForInspectedTab(*iterator);
    221     if (docked)
    222       Add(docked->web_contents());
    223   }
    224 
    225   // Add all the prerender pages.
    226   std::vector<Profile*> profiles(
    227       g_browser_process->profile_manager()->GetLoadedProfiles());
    228   for (size_t i = 0; i < profiles.size(); ++i) {
    229     prerender::PrerenderManager* prerender_manager =
    230         prerender::PrerenderManagerFactory::GetForProfile(profiles[i]);
    231     if (prerender_manager) {
    232       const std::vector<content::WebContents*> contentses =
    233           prerender_manager->GetAllPrerenderingContents();
    234       for (size_t j = 0; j < contentses.size(); ++j)
    235         Add(contentses[j]);
    236     }
    237   }
    238 
    239   // Add all the Instant Extended prerendered NTPs.
    240   for (size_t i = 0; i < profiles.size(); ++i) {
    241     const InstantService* instant_service =
    242         InstantServiceFactory::GetForProfile(profiles[i]);
    243     if (instant_service && instant_service->GetNTPContents())
    244       Add(instant_service->GetNTPContents());
    245   }
    246 
    247 #if defined(ENABLE_FULL_PRINTING)
    248   // Add all the pages being background printed.
    249   printing::BackgroundPrintingManager* printing_manager =
    250       g_browser_process->background_printing_manager();
    251   for (printing::BackgroundPrintingManager::WebContentsSet::iterator i =
    252            printing_manager->begin();
    253        i != printing_manager->end(); ++i) {
    254     Add(*i);
    255   }
    256 #endif
    257 
    258   // Then we register for notifications to get new web contents.
    259   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
    260                  content::NotificationService::AllBrowserContextsAndSources());
    261   registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
    262                  content::NotificationService::AllBrowserContextsAndSources());
    263   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
    264                  content::NotificationService::AllBrowserContextsAndSources());
    265 }
    266 
    267 void TabContentsResourceProvider::StopUpdating() {
    268   DCHECK(updating_);
    269   updating_ = false;
    270 
    271   // Then we unregister for notifications to get new web contents.
    272   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
    273       content::NotificationService::AllBrowserContextsAndSources());
    274   registrar_.Remove(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
    275       content::NotificationService::AllBrowserContextsAndSources());
    276   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
    277       content::NotificationService::AllBrowserContextsAndSources());
    278 
    279   // Delete all the resources.
    280   STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
    281 
    282   resources_.clear();
    283 }
    284 
    285 void TabContentsResourceProvider::AddToTaskManager(WebContents* web_contents) {
    286   TabContentsResource* resource = new TabContentsResource(web_contents);
    287   resources_[web_contents] = resource;
    288   task_manager_->AddResource(resource);
    289 }
    290 
    291 void TabContentsResourceProvider::Add(WebContents* web_contents) {
    292   if (!updating_)
    293     return;
    294 
    295   // The contents that are tracked by this resource provider are those that
    296   // are tab contents (WebContents serving as a tab in a Browser), Instant
    297   // pages, prerender pages, and background printed pages.
    298   if (!chrome::FindBrowserWithWebContents(web_contents) &&
    299       !IsContentsPrerendering(web_contents) &&
    300       !chrome::IsPreloadedInstantExtendedNTP(web_contents) &&
    301       !IsContentsBackgroundPrinted(web_contents) &&
    302       !DevToolsWindow::IsDevToolsWindow(web_contents->GetRenderViewHost())) {
    303     return;
    304   }
    305 
    306   // Don't add dead tabs or tabs that haven't yet connected.
    307   if (!web_contents->GetRenderProcessHost()->GetHandle() ||
    308       !web_contents->WillNotifyDisconnection()) {
    309     return;
    310   }
    311 
    312   if (resources_.count(web_contents)) {
    313     // The case may happen that we have added a WebContents as part of the
    314     // iteration performed during StartUpdating() call but the notification that
    315     // it has connected was not fired yet. So when the notification happens, we
    316     // already know about this tab and just ignore it.
    317     return;
    318   }
    319   AddToTaskManager(web_contents);
    320 }
    321 
    322 void TabContentsResourceProvider::Remove(WebContents* web_contents) {
    323   if (!updating_)
    324     return;
    325   std::map<WebContents*, TabContentsResource*>::iterator
    326       iter = resources_.find(web_contents);
    327   if (iter == resources_.end()) {
    328     // Since WebContents are destroyed asynchronously (see TabContentsCollector
    329     // in navigation_controller.cc), we can be notified of a tab being removed
    330     // that we don't know.  This can happen if the user closes a tab and quickly
    331     // opens the task manager, before the tab is actually destroyed.
    332     return;
    333   }
    334 
    335   // Remove the resource from the Task Manager.
    336   TabContentsResource* resource = iter->second;
    337   task_manager_->RemoveResource(resource);
    338   // And from the provider.
    339   resources_.erase(iter);
    340   // Finally, delete the resource.
    341   delete resource;
    342 }
    343 
    344 void TabContentsResourceProvider::Observe(
    345     int type,
    346     const content::NotificationSource& source,
    347     const content::NotificationDetails& details) {
    348   WebContents* web_contents = content::Source<WebContents>(source).ptr();
    349 
    350   switch (type) {
    351     case content::NOTIFICATION_WEB_CONTENTS_CONNECTED:
    352       Add(web_contents);
    353       break;
    354     case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED:
    355       Remove(web_contents);
    356       Add(web_contents);
    357       break;
    358     case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED:
    359       Remove(web_contents);
    360       break;
    361     default:
    362       NOTREACHED() << "Unexpected notification.";
    363       return;
    364   }
    365 }
    366 
    367 }  // namespace task_manager
    368