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
35 using content::FaviconStatus;
36 using content::NavigationController;
37 using content::NavigationEntry;
38 using content::WebContents;
39
40 DEFINE_WEB_CONTENTS_USER_DATA_KEY(FaviconTabHelper);
41
FaviconTabHelper(WebContents * web_contents)42 FaviconTabHelper::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
~FaviconTabHelper()58 FaviconTabHelper::~FaviconTabHelper() {
59 }
60
FetchFavicon(const GURL & url)61 void FaviconTabHelper::FetchFavicon(const GURL& url) {
62 favicon_handler_->FetchFavicon(url);
63 if (touch_icon_handler_.get())
64 touch_icon_handler_->FetchFavicon(url);
65 }
66
GetFavicon() const67 gfx::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
FaviconIsValid() const81 bool 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
ShouldDisplayFavicon()94 bool 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
SaveFavicon()113 void 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
StartDownload(const GURL & url,int max_image_size)139 int 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
NotifyFaviconUpdated(bool icon_url_changed)154 void 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
IsOffTheRecord()162 bool FaviconTabHelper::IsOffTheRecord() {
163 DCHECK(web_contents());
164 return web_contents()->GetBrowserContext()->IsOffTheRecord();
165 }
166
GetActiveFaviconImage()167 const gfx::Image FaviconTabHelper::GetActiveFaviconImage() {
168 return GetFaviconStatus().image;
169 }
170
GetActiveFaviconURL()171 const GURL FaviconTabHelper::GetActiveFaviconURL() {
172 return GetFaviconStatus().url;
173 }
174
GetActiveFaviconValidity()175 bool FaviconTabHelper::GetActiveFaviconValidity() {
176 return GetFaviconStatus().valid;
177 }
178
GetActiveURL()179 const 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
SetActiveFaviconImage(gfx::Image image)186 void FaviconTabHelper::SetActiveFaviconImage(gfx::Image image) {
187 GetFaviconStatus().image = image;
188 }
189
SetActiveFaviconURL(GURL url)190 void FaviconTabHelper::SetActiveFaviconURL(GURL url) {
191 GetFaviconStatus().url = url;
192 }
193
SetActiveFaviconValidity(bool validity)194 void FaviconTabHelper::SetActiveFaviconValidity(bool validity) {
195 GetFaviconStatus().valid = validity;
196 }
197
GetFaviconStatus()198 content::FaviconStatus& FaviconTabHelper::GetFaviconStatus() {
199 DCHECK(web_contents()->GetController().GetActiveEntry());
200 return web_contents()->GetController().GetActiveEntry()->GetFavicon();
201 }
202
DidStartNavigationToPendingEntry(const GURL & url,NavigationController::ReloadType reload_type)203 void 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
DidNavigateMainFrame(const content::LoadCommittedDetails & details,const content::FrameNavigateParams & params)218 void 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.
ToChromeIconType(content::FaviconURL::IconType icon_type)229 favicon_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
DidUpdateFaviconURL(const std::vector<content::FaviconURL> & candidates)245 void 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
DidDownloadFavicon(int id,int http_status_code,const GURL & image_url,const std::vector<SkBitmap> & bitmaps,const std::vector<gfx::Size> & original_bitmap_sizes)262 void 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