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