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