Home | History | Annotate | Download | only in favicon
      1 // Copyright (c) 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/favicon/favicon_tab_helper.h"
      6 
      7 #include "chrome/browser/chrome_notification_types.h"
      8 #include "chrome/browser/favicon/chrome_favicon_client.h"
      9 #include "chrome/browser/favicon/favicon_handler.h"
     10 #include "chrome/browser/favicon/favicon_service.h"
     11 #include "chrome/browser/favicon/favicon_service_factory.h"
     12 #include "chrome/browser/history/history_service.h"
     13 #include "chrome/browser/history/history_service_factory.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/search/search.h"
     16 #include "chrome/common/chrome_constants.h"
     17 #include "chrome/common/url_constants.h"
     18 #include "components/favicon_base/favicon_types.h"
     19 #include "content/public/browser/favicon_status.h"
     20 #include "content/public/browser/invalidate_type.h"
     21 #include "content/public/browser/navigation_controller.h"
     22 #include "content/public/browser/navigation_details.h"
     23 #include "content/public/browser/navigation_entry.h"
     24 #include "content/public/browser/notification_service.h"
     25 #include "content/public/browser/render_view_host.h"
     26 #include "content/public/browser/web_contents.h"
     27 #include "content/public/browser/web_contents_delegate.h"
     28 #include "content/public/common/favicon_url.h"
     29 #include "ui/gfx/codec/png_codec.h"
     30 #include "ui/gfx/image/image.h"
     31 #include "ui/gfx/image/image_skia.h"
     32 #include "ui/gfx/image/image_skia_rep.h"
     33 
     34 using content::FaviconStatus;
     35 using content::NavigationController;
     36 using content::NavigationEntry;
     37 using content::WebContents;
     38 
     39 DEFINE_WEB_CONTENTS_USER_DATA_KEY(FaviconTabHelper);
     40 
     41 FaviconTabHelper::FaviconTabHelper(WebContents* web_contents)
     42     : content::WebContentsObserver(web_contents),
     43       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())) {
     44   client_.reset(new ChromeFaviconClient(profile_));
     45 #if defined(OS_ANDROID)
     46   bool download_largest_icon = true;
     47 #else
     48   bool download_largest_icon = false;
     49 #endif
     50   favicon_handler_.reset(new FaviconHandler(client_.get(),
     51                                             this,
     52                                             FaviconHandler::FAVICON,
     53                                             download_largest_icon));
     54   if (chrome::kEnableTouchIcon)
     55     touch_icon_handler_.reset(new FaviconHandler(client_.get(),
     56                                                  this,
     57                                                  FaviconHandler::TOUCH,
     58                                                  download_largest_icon));
     59 }
     60 
     61 FaviconTabHelper::~FaviconTabHelper() {
     62 }
     63 
     64 void FaviconTabHelper::FetchFavicon(const GURL& url) {
     65   favicon_handler_->FetchFavicon(url);
     66   if (touch_icon_handler_.get())
     67     touch_icon_handler_->FetchFavicon(url);
     68 }
     69 
     70 gfx::Image FaviconTabHelper::GetFavicon() const {
     71   // Like GetTitle(), we also want to use the favicon for the last committed
     72   // entry rather than a pending navigation entry.
     73   const NavigationController& controller = web_contents()->GetController();
     74   NavigationEntry* entry = controller.GetTransientEntry();
     75   if (entry)
     76     return entry->GetFavicon().image;
     77 
     78   entry = controller.GetLastCommittedEntry();
     79   if (entry)
     80     return entry->GetFavicon().image;
     81   return gfx::Image();
     82 }
     83 
     84 bool FaviconTabHelper::FaviconIsValid() const {
     85   const NavigationController& controller = web_contents()->GetController();
     86   NavigationEntry* entry = controller.GetTransientEntry();
     87   if (entry)
     88     return entry->GetFavicon().valid;
     89 
     90   entry = controller.GetLastCommittedEntry();
     91   if (entry)
     92     return entry->GetFavicon().valid;
     93 
     94   return false;
     95 }
     96 
     97 bool FaviconTabHelper::ShouldDisplayFavicon() {
     98   // Always display a throbber during pending loads.
     99   const NavigationController& controller = web_contents()->GetController();
    100   if (controller.GetLastCommittedEntry() && controller.GetPendingEntry())
    101     return true;
    102 
    103   GURL url = web_contents()->GetURL();
    104   if (url.SchemeIs(content::kChromeUIScheme) &&
    105       url.host() == chrome::kChromeUINewTabHost) {
    106     return false;
    107   }
    108 
    109   // No favicon on Instant New Tab Pages.
    110   if (chrome::IsInstantNTP(web_contents()))
    111     return false;
    112 
    113   return true;
    114 }
    115 
    116 void FaviconTabHelper::SaveFavicon() {
    117   NavigationEntry* entry = web_contents()->GetController().GetActiveEntry();
    118   if (!entry || entry->GetURL().is_empty())
    119     return;
    120 
    121   // Make sure the page is in history, otherwise adding the favicon does
    122   // nothing.
    123   HistoryService* history = HistoryServiceFactory::GetForProfile(
    124       profile_->GetOriginalProfile(), Profile::IMPLICIT_ACCESS);
    125   if (!history)
    126     return;
    127   history->AddPageNoVisitForBookmark(entry->GetURL(), entry->GetTitle());
    128 
    129   FaviconService* service = FaviconServiceFactory::GetForProfile(
    130       profile_->GetOriginalProfile(), Profile::IMPLICIT_ACCESS);
    131   if (!service)
    132     return;
    133   const FaviconStatus& favicon(entry->GetFavicon());
    134   if (!favicon.valid || favicon.url.is_empty() ||
    135       favicon.image.IsEmpty()) {
    136     return;
    137   }
    138   service->SetFavicons(
    139       entry->GetURL(), favicon.url, favicon_base::FAVICON, favicon.image);
    140 }
    141 
    142 int FaviconTabHelper::StartDownload(const GURL& url, int max_image_size) {
    143   FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
    144       profile_->GetOriginalProfile(), Profile::IMPLICIT_ACCESS);
    145   if (favicon_service && favicon_service->WasUnableToDownloadFavicon(url)) {
    146     DVLOG(1) << "Skip Failed FavIcon: " << url;
    147     return 0;
    148   }
    149 
    150   return web_contents()->DownloadImage(
    151       url,
    152       true,
    153       max_image_size,
    154       base::Bind(&FaviconTabHelper::DidDownloadFavicon,base::Unretained(this)));
    155 }
    156 
    157 void FaviconTabHelper::NotifyFaviconUpdated(bool icon_url_changed) {
    158   content::NotificationService::current()->Notify(
    159       chrome::NOTIFICATION_FAVICON_UPDATED,
    160       content::Source<WebContents>(web_contents()),
    161       content::Details<bool>(&icon_url_changed));
    162   web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
    163 }
    164 
    165 bool FaviconTabHelper::IsOffTheRecord() {
    166   DCHECK(web_contents());
    167   return web_contents()->GetBrowserContext()->IsOffTheRecord();
    168 }
    169 
    170 const gfx::Image FaviconTabHelper::GetActiveFaviconImage() {
    171   return GetFaviconStatus().image;
    172 }
    173 
    174 const GURL FaviconTabHelper::GetActiveFaviconURL() {
    175   return GetFaviconStatus().url;
    176 }
    177 
    178 bool FaviconTabHelper::GetActiveFaviconValidity() {
    179   return GetFaviconStatus().valid;
    180 }
    181 
    182 const GURL FaviconTabHelper::GetActiveURL() {
    183   NavigationEntry* entry = web_contents()->GetController().GetActiveEntry();
    184   if (!entry || entry->GetURL().is_empty())
    185     return GURL();
    186   return entry->GetURL();
    187 }
    188 
    189 void FaviconTabHelper::SetActiveFaviconImage(gfx::Image image) {
    190   GetFaviconStatus().image = image;
    191 }
    192 
    193 void FaviconTabHelper::SetActiveFaviconURL(GURL url) {
    194   GetFaviconStatus().url = url;
    195 }
    196 
    197 void FaviconTabHelper::SetActiveFaviconValidity(bool validity) {
    198   GetFaviconStatus().valid = validity;
    199 }
    200 
    201 content::FaviconStatus& FaviconTabHelper::GetFaviconStatus() {
    202   DCHECK(web_contents()->GetController().GetActiveEntry());
    203   return web_contents()->GetController().GetActiveEntry()->GetFavicon();
    204 }
    205 
    206 void FaviconTabHelper::DidStartNavigationToPendingEntry(
    207     const GURL& url,
    208     NavigationController::ReloadType reload_type) {
    209   if (reload_type != NavigationController::NO_RELOAD &&
    210       !profile_->IsOffTheRecord()) {
    211     FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
    212         profile_, Profile::IMPLICIT_ACCESS);
    213     if (favicon_service) {
    214       favicon_service->SetFaviconOutOfDateForPage(url);
    215       if (reload_type == NavigationController::RELOAD_IGNORING_CACHE)
    216         favicon_service->ClearUnableToDownloadFavicons();
    217     }
    218   }
    219 }
    220 
    221 void FaviconTabHelper::DidNavigateMainFrame(
    222     const content::LoadCommittedDetails& details,
    223     const content::FrameNavigateParams& params) {
    224   favicon_urls_.clear();
    225   // Get the favicon, either from history or request it from the net.
    226   FetchFavicon(details.entry->GetURL());
    227 }
    228 
    229 // Returns favicon_base::IconType the given icon_type corresponds to.
    230 // TODO(jif): Move function to /components/favicon_base/content/
    231 // crbug.com/374281.
    232 favicon_base::IconType ToChromeIconType(
    233     content::FaviconURL::IconType icon_type) {
    234   switch (icon_type) {
    235     case content::FaviconURL::FAVICON:
    236       return favicon_base::FAVICON;
    237     case content::FaviconURL::TOUCH_ICON:
    238       return favicon_base::TOUCH_ICON;
    239     case content::FaviconURL::TOUCH_PRECOMPOSED_ICON:
    240       return favicon_base::TOUCH_PRECOMPOSED_ICON;
    241     case content::FaviconURL::INVALID_ICON:
    242       return favicon_base::INVALID_ICON;
    243   }
    244   NOTREACHED();
    245   return favicon_base::INVALID_ICON;
    246 }
    247 
    248 void FaviconTabHelper::DidUpdateFaviconURL(
    249     const std::vector<content::FaviconURL>& candidates) {
    250   DCHECK(!candidates.empty());
    251   favicon_urls_ = candidates;
    252   std::vector<favicon::FaviconURL> favicon_urls;
    253   for (size_t i = 0; i < candidates.size(); i++) {
    254     const content::FaviconURL& candidate = candidates[i];
    255     favicon_urls.push_back(
    256         favicon::FaviconURL(candidate.icon_url,
    257                             ToChromeIconType(candidate.icon_type),
    258                             candidate.icon_sizes));
    259   }
    260   favicon_handler_->OnUpdateFaviconURL(favicon_urls);
    261   if (touch_icon_handler_.get())
    262     touch_icon_handler_->OnUpdateFaviconURL(favicon_urls);
    263 }
    264 
    265 void FaviconTabHelper::DidDownloadFavicon(
    266     int id,
    267     int http_status_code,
    268     const GURL& image_url,
    269     const std::vector<SkBitmap>& bitmaps,
    270     const std::vector<gfx::Size>& original_bitmap_sizes) {
    271 
    272   if (bitmaps.empty() && http_status_code == 404) {
    273     DVLOG(1) << "Failed to Download Favicon:" << image_url;
    274     FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
    275         profile_->GetOriginalProfile(), Profile::IMPLICIT_ACCESS);
    276     if (favicon_service)
    277       favicon_service->UnableToDownloadFavicon(image_url);
    278   }
    279 
    280   favicon_handler_->OnDidDownloadFavicon(
    281       id, image_url, bitmaps, original_bitmap_sizes);
    282   if (touch_icon_handler_.get()) {
    283     touch_icon_handler_->OnDidDownloadFavicon(
    284         id, image_url, bitmaps, original_bitmap_sizes);
    285   }
    286 }
    287