Home | History | Annotate | Download | only in task_manager
      1 // Copyright 2013 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/background_resource_provider.h"
      6 
      7 #include "base/i18n/rtl.h"
      8 #include "base/strings/string16.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "chrome/browser/background/background_contents_service.h"
     11 #include "chrome/browser/background/background_contents_service_factory.h"
     12 #include "chrome/browser/browser_process.h"
     13 #include "chrome/browser/chrome_notification_types.h"
     14 #include "chrome/browser/extensions/extension_service.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/profiles/profile_manager.h"
     17 #include "chrome/browser/tab_contents/background_contents.h"
     18 #include "chrome/browser/task_manager/renderer_resource.h"
     19 #include "chrome/browser/task_manager/resource_provider.h"
     20 #include "chrome/browser/task_manager/task_manager.h"
     21 #include "content/public/browser/notification_service.h"
     22 #include "content/public/browser/render_process_host.h"
     23 #include "content/public/browser/render_view_host.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "extensions/common/extension.h"
     26 #include "grit/generated_resources.h"
     27 #include "grit/theme_resources.h"
     28 #include "ui/base/l10n/l10n_util.h"
     29 #include "ui/base/resource/resource_bundle.h"
     30 #include "ui/gfx/image/image_skia.h"
     31 
     32 using content::RenderProcessHost;
     33 using content::RenderViewHost;
     34 using content::WebContents;
     35 using extensions::Extension;
     36 
     37 namespace task_manager {
     38 
     39 class BackgroundContentsResource : public RendererResource {
     40  public:
     41   BackgroundContentsResource(
     42       BackgroundContents* background_contents,
     43       const base::string16& application_name);
     44   virtual ~BackgroundContentsResource();
     45 
     46   // Resource methods:
     47   virtual base::string16 GetTitle() const OVERRIDE;
     48   virtual base::string16 GetProfileName() const OVERRIDE;
     49   virtual gfx::ImageSkia GetIcon() const OVERRIDE;
     50   virtual bool IsBackground() const OVERRIDE;
     51 
     52   const base::string16& application_name() const { return application_name_; }
     53  private:
     54   BackgroundContents* background_contents_;
     55 
     56   base::string16 application_name_;
     57 
     58   // The icon painted for BackgroundContents.
     59   // TODO(atwilson): Use the favicon when there's a way to get the favicon for
     60   // BackgroundContents.
     61   static gfx::ImageSkia* default_icon_;
     62 
     63   DISALLOW_COPY_AND_ASSIGN(BackgroundContentsResource);
     64 };
     65 
     66 gfx::ImageSkia* BackgroundContentsResource::default_icon_ = NULL;
     67 
     68 // TODO(atwilson): http://crbug.com/116893
     69 // HACK: if the process handle is invalid, we use the current process's handle.
     70 // This preserves old behavior but is incorrect, and should be fixed.
     71 BackgroundContentsResource::BackgroundContentsResource(
     72     BackgroundContents* background_contents,
     73     const base::string16& application_name)
     74     : RendererResource(
     75           background_contents->web_contents()->GetRenderProcessHost()->
     76               GetHandle() ?
     77               background_contents->web_contents()->GetRenderProcessHost()->
     78                   GetHandle() :
     79               base::Process::Current().handle(),
     80           background_contents->web_contents()->GetRenderViewHost()),
     81       background_contents_(background_contents),
     82       application_name_(application_name) {
     83   // Just use the same icon that other extension resources do.
     84   // TODO(atwilson): Use the favicon when that's available.
     85   if (!default_icon_) {
     86     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     87     default_icon_ = rb.GetImageSkiaNamed(IDR_PLUGINS_FAVICON);
     88   }
     89   // Ensure that the string has the appropriate direction markers (see comment
     90   // in TabContentsResource::GetTitle()).
     91   base::i18n::AdjustStringForLocaleDirection(&application_name_);
     92 }
     93 
     94 BackgroundContentsResource::~BackgroundContentsResource() {
     95 }
     96 
     97 base::string16 BackgroundContentsResource::GetTitle() const {
     98   base::string16 title = application_name_;
     99 
    100   if (title.empty()) {
    101     // No title (can't locate the parent app for some reason) so just display
    102     // the URL (properly forced to be LTR).
    103     title = base::i18n::GetDisplayStringInLTRDirectionality(
    104         UTF8ToUTF16(background_contents_->GetURL().spec()));
    105   }
    106   return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_BACKGROUND_PREFIX, title);
    107 }
    108 
    109 base::string16 BackgroundContentsResource::GetProfileName() const {
    110   return base::string16();
    111 }
    112 
    113 gfx::ImageSkia BackgroundContentsResource::GetIcon() const {
    114   return *default_icon_;
    115 }
    116 
    117 bool BackgroundContentsResource::IsBackground() const {
    118   return true;
    119 }
    120 
    121 ////////////////////////////////////////////////////////////////////////////////
    122 // BackgroundContentsResourceProvider class
    123 ////////////////////////////////////////////////////////////////////////////////
    124 
    125 BackgroundContentsResourceProvider::
    126     BackgroundContentsResourceProvider(TaskManager* task_manager)
    127     : updating_(false),
    128       task_manager_(task_manager) {
    129 }
    130 
    131 BackgroundContentsResourceProvider::~BackgroundContentsResourceProvider() {
    132 }
    133 
    134 Resource* BackgroundContentsResourceProvider::GetResource(
    135     int origin_pid,
    136     int render_process_host_id,
    137     int routing_id) {
    138   // If an origin PID was specified, the request is from a plugin, not the
    139   // render view host process
    140   if (origin_pid)
    141     return NULL;
    142 
    143   for (Resources::iterator i = resources_.begin(); i != resources_.end(); i++) {
    144     WebContents* tab = i->first->web_contents();
    145     if (tab->GetRenderProcessHost()->GetID() == render_process_host_id
    146         && tab->GetRenderViewHost()->GetRoutingID() == routing_id) {
    147       return i->second;
    148     }
    149   }
    150 
    151   // Can happen if the page went away while a network request was being
    152   // performed.
    153   return NULL;
    154 }
    155 
    156 void BackgroundContentsResourceProvider::StartUpdating() {
    157   DCHECK(!updating_);
    158   updating_ = true;
    159 
    160   // Add all the existing BackgroundContents from every profile, including
    161   // incognito profiles.
    162   ProfileManager* profile_manager = g_browser_process->profile_manager();
    163   std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
    164   size_t num_default_profiles = profiles.size();
    165   for (size_t i = 0; i < num_default_profiles; ++i) {
    166     if (profiles[i]->HasOffTheRecordProfile()) {
    167       profiles.push_back(profiles[i]->GetOffTheRecordProfile());
    168     }
    169   }
    170   for (size_t i = 0; i < profiles.size(); ++i) {
    171     BackgroundContentsService* background_contents_service =
    172         BackgroundContentsServiceFactory::GetForProfile(profiles[i]);
    173     std::vector<BackgroundContents*> contents =
    174         background_contents_service->GetBackgroundContents();
    175     ExtensionService* extension_service = profiles[i]->GetExtensionService();
    176     for (std::vector<BackgroundContents*>::iterator iterator = contents.begin();
    177          iterator != contents.end(); ++iterator) {
    178       base::string16 application_name;
    179       // Lookup the name from the parent extension.
    180       if (extension_service) {
    181         const base::string16& application_id =
    182             background_contents_service->GetParentApplicationId(*iterator);
    183         const Extension* extension = extension_service->GetExtensionById(
    184             UTF16ToUTF8(application_id), false);
    185         if (extension)
    186           application_name = UTF8ToUTF16(extension->name());
    187       }
    188       Add(*iterator, application_name);
    189     }
    190   }
    191 
    192   // Then we register for notifications to get new BackgroundContents.
    193   registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED,
    194                  content::NotificationService::AllBrowserContextsAndSources());
    195   registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
    196                  content::NotificationService::AllBrowserContextsAndSources());
    197   registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
    198                  content::NotificationService::AllBrowserContextsAndSources());
    199   registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
    200                  content::NotificationService::AllBrowserContextsAndSources());
    201 }
    202 
    203 void BackgroundContentsResourceProvider::StopUpdating() {
    204   DCHECK(updating_);
    205   updating_ = false;
    206 
    207   // Unregister for notifications
    208   registrar_.Remove(
    209       this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED,
    210       content::NotificationService::AllBrowserContextsAndSources());
    211   registrar_.Remove(
    212       this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
    213       content::NotificationService::AllBrowserContextsAndSources());
    214   registrar_.Remove(
    215       this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
    216       content::NotificationService::AllBrowserContextsAndSources());
    217   registrar_.Remove(
    218       this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
    219       content::NotificationService::AllBrowserContextsAndSources());
    220 
    221   // Delete all the resources.
    222   STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
    223 
    224   resources_.clear();
    225 }
    226 
    227 void BackgroundContentsResourceProvider::AddToTaskManager(
    228     BackgroundContents* background_contents,
    229     const base::string16& application_name) {
    230   BackgroundContentsResource* resource =
    231       new BackgroundContentsResource(background_contents, application_name);
    232   resources_[background_contents] = resource;
    233   task_manager_->AddResource(resource);
    234 }
    235 
    236 void BackgroundContentsResourceProvider::Add(
    237     BackgroundContents* contents, const base::string16& application_name) {
    238   if (!updating_)
    239     return;
    240 
    241   // TODO(atwilson): http://crbug.com/116893
    242   // We should check that the process handle is valid here, but it won't
    243   // be in the case of NOTIFICATION_BACKGROUND_CONTENTS_OPENED.
    244 
    245   // Should never add the same BackgroundContents twice.
    246   DCHECK(resources_.find(contents) == resources_.end());
    247   AddToTaskManager(contents, application_name);
    248 }
    249 
    250 void BackgroundContentsResourceProvider::Remove(BackgroundContents* contents) {
    251   if (!updating_)
    252     return;
    253   Resources::iterator iter = resources_.find(contents);
    254   DCHECK(iter != resources_.end());
    255 
    256   // Remove the resource from the Task Manager.
    257   BackgroundContentsResource* resource = iter->second;
    258   task_manager_->RemoveResource(resource);
    259   // And from the provider.
    260   resources_.erase(iter);
    261   // Finally, delete the resource.
    262   delete resource;
    263 }
    264 
    265 void BackgroundContentsResourceProvider::Observe(
    266     int type,
    267     const content::NotificationSource& source,
    268     const content::NotificationDetails& details) {
    269   switch (type) {
    270     case chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED: {
    271       // Get the name from the parent application. If no parent application is
    272       // found, just pass an empty string - BackgroundContentsResource::GetTitle
    273       // will display the URL instead in this case. This should never happen
    274       // except in rare cases when an extension is being unloaded or chrome is
    275       // exiting while the task manager is displayed.
    276       base::string16 application_name;
    277       ExtensionService* service =
    278           content::Source<Profile>(source)->GetExtensionService();
    279       if (service) {
    280         std::string application_id = UTF16ToUTF8(
    281             content::Details<BackgroundContentsOpenedDetails>(details)->
    282                 application_id);
    283         const Extension* extension =
    284             service->GetExtensionById(application_id, false);
    285         // Extension can be NULL when running unit tests.
    286         if (extension)
    287           application_name = UTF8ToUTF16(extension->name());
    288       }
    289       Add(content::Details<BackgroundContentsOpenedDetails>(details)->contents,
    290           application_name);
    291       // Opening a new BackgroundContents needs to force the display to refresh
    292       // (applications may now be considered "background" that weren't before).
    293       task_manager_->ModelChanged();
    294       break;
    295     }
    296     case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED: {
    297       BackgroundContents* contents =
    298           content::Details<BackgroundContents>(details).ptr();
    299       // Should never get a NAVIGATED before OPENED.
    300       DCHECK(resources_.find(contents) != resources_.end());
    301       // Preserve the application name.
    302       base::string16 application_name(
    303           resources_.find(contents)->second->application_name());
    304       Remove(contents);
    305       Add(contents, application_name);
    306       break;
    307     }
    308     case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED:
    309       Remove(content::Details<BackgroundContents>(details).ptr());
    310       // Closing a BackgroundContents needs to force the display to refresh
    311       // (applications may now be considered "foreground" that weren't before).
    312       task_manager_->ModelChanged();
    313       break;
    314     case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
    315       WebContents* web_contents = content::Source<WebContents>(source).ptr();
    316       for (Resources::iterator i = resources_.begin(); i != resources_.end();
    317            i++) {
    318         if (i->first->web_contents() == web_contents) {
    319           base::string16 application_name = i->second->application_name();
    320           BackgroundContents* contents = i->first;
    321           Remove(contents);
    322           Add(contents, application_name);
    323           return;
    324         }
    325       }
    326       break;
    327     }
    328     default:
    329       NOTREACHED() << "Unexpected notification.";
    330       return;
    331   }
    332 }
    333 
    334 }  // namespace task_manager
    335