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