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_service.h"
6
7#include <cmath>
8
9#include "base/hash.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "chrome/browser/history/history_backend.h"
12#include "chrome/browser/history/history_service.h"
13#include "chrome/browser/history/history_service_factory.h"
14#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
15#include "chrome/common/importer/imported_favicon_usage.h"
16#include "chrome/common/url_constants.h"
17#include "components/favicon_base/favicon_types.h"
18#include "components/favicon_base/favicon_util.h"
19#include "components/favicon_base/select_favicon_frames.h"
20#include "extensions/common/constants.h"
21#include "third_party/skia/include/core/SkBitmap.h"
22#include "ui/gfx/codec/png_codec.h"
23#include "ui/gfx/favicon_size.h"
24#include "ui/gfx/image/image_skia.h"
25
26using base::Bind;
27
28namespace {
29
30void CancelOrRunFaviconResultsCallback(
31    const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
32    const favicon_base::FaviconResultsCallback& callback,
33    const std::vector<favicon_base::FaviconRawBitmapResult>& results) {
34  if (is_canceled.Run())
35    return;
36  callback.Run(results);
37}
38
39// Helper to run callback with empty results if we cannot get the history
40// service.
41base::CancelableTaskTracker::TaskId RunWithEmptyResultAsync(
42    const favicon_base::FaviconResultsCallback& callback,
43    base::CancelableTaskTracker* tracker) {
44  return tracker->PostTask(
45      base::MessageLoopProxy::current().get(),
46      FROM_HERE,
47      Bind(callback, std::vector<favicon_base::FaviconRawBitmapResult>()));
48}
49
50// Return the TaskId to retreive the favicon from chrome specific URL.
51base::CancelableTaskTracker::TaskId GetFaviconForChromeURL(
52    Profile* profile,
53    const GURL& page_url,
54    const std::vector<int>& desired_sizes_in_pixel,
55    const favicon_base::FaviconResultsCallback& callback,
56    base::CancelableTaskTracker* tracker) {
57  base::CancelableTaskTracker::IsCanceledCallback is_canceled_cb;
58  base::CancelableTaskTracker::TaskId id =
59      tracker->NewTrackedTaskId(&is_canceled_cb);
60  favicon_base::FaviconResultsCallback cancelable_cb =
61      Bind(&CancelOrRunFaviconResultsCallback, is_canceled_cb, callback);
62  ChromeWebUIControllerFactory::GetInstance()->GetFaviconForURL(
63      profile, page_url, desired_sizes_in_pixel, cancelable_cb);
64  return id;
65}
66
67// Returns a vector of pixel edge sizes from |size_in_dip| and
68// favicon_base::GetFaviconScales().
69std::vector<int> GetPixelSizesForFaviconScales(int size_in_dip) {
70  std::vector<float> scales = favicon_base::GetFaviconScales();
71  std::vector<int> sizes_in_pixel;
72  for (size_t i = 0; i < scales.size(); ++i) {
73    sizes_in_pixel.push_back(std::ceil(size_in_dip * scales[i]));
74  }
75  return sizes_in_pixel;
76}
77
78}  // namespace
79
80FaviconService::FaviconService(Profile* profile, FaviconClient* favicon_client)
81    : history_service_(
82          HistoryServiceFactory::GetForProfile(profile,
83                                               Profile::EXPLICIT_ACCESS)),
84      profile_(profile),
85      favicon_client_(favicon_client) {
86}
87
88// static
89void FaviconService::FaviconResultsCallbackRunner(
90    const favicon_base::FaviconResultsCallback& callback,
91    const std::vector<favicon_base::FaviconRawBitmapResult>* results) {
92  callback.Run(*results);
93}
94
95base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImage(
96    const GURL& icon_url,
97    const favicon_base::FaviconImageCallback& callback,
98    base::CancelableTaskTracker* tracker) {
99  favicon_base::FaviconResultsCallback callback_runner =
100      Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults,
101           base::Unretained(this), callback, gfx::kFaviconSize);
102  if (history_service_) {
103    std::vector<GURL> icon_urls;
104    icon_urls.push_back(icon_url);
105    return history_service_->GetFavicons(
106        icon_urls,
107        favicon_base::FAVICON,
108        GetPixelSizesForFaviconScales(gfx::kFaviconSize),
109        callback_runner,
110        tracker);
111  }
112  return RunWithEmptyResultAsync(callback_runner, tracker);
113}
114
115base::CancelableTaskTracker::TaskId FaviconService::GetRawFavicon(
116    const GURL& icon_url,
117    favicon_base::IconType icon_type,
118    int desired_size_in_pixel,
119    const favicon_base::FaviconRawBitmapCallback& callback,
120    base::CancelableTaskTracker* tracker) {
121  favicon_base::FaviconResultsCallback callback_runner =
122      Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
123           base::Unretained(this),
124           callback,
125           desired_size_in_pixel);
126
127  if (history_service_) {
128    std::vector<GURL> icon_urls;
129    icon_urls.push_back(icon_url);
130    std::vector<int> desired_sizes_in_pixel;
131    desired_sizes_in_pixel.push_back(desired_size_in_pixel);
132
133    return history_service_->GetFavicons(
134        icon_urls, icon_type, desired_sizes_in_pixel, callback_runner, tracker);
135  }
136  return RunWithEmptyResultAsync(callback_runner, tracker);
137}
138
139base::CancelableTaskTracker::TaskId FaviconService::GetFavicon(
140    const GURL& icon_url,
141    favicon_base::IconType icon_type,
142    int desired_size_in_dip,
143    const favicon_base::FaviconResultsCallback& callback,
144    base::CancelableTaskTracker* tracker) {
145  if (history_service_) {
146    std::vector<GURL> icon_urls;
147    icon_urls.push_back(icon_url);
148    return history_service_->GetFavicons(
149        icon_urls,
150        icon_type,
151        GetPixelSizesForFaviconScales(desired_size_in_dip),
152        callback,
153        tracker);
154  }
155  return RunWithEmptyResultAsync(callback, tracker);
156}
157
158base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImageForPageURL(
159    const GURL& page_url,
160    const favicon_base::FaviconImageCallback& callback,
161    base::CancelableTaskTracker* tracker) {
162  return GetFaviconForPageURLImpl(
163      page_url,
164      favicon_base::FAVICON,
165      GetPixelSizesForFaviconScales(gfx::kFaviconSize),
166      Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults,
167           base::Unretained(this),
168           callback,
169           gfx::kFaviconSize),
170      tracker);
171}
172
173base::CancelableTaskTracker::TaskId FaviconService::GetRawFaviconForPageURL(
174    const GURL& page_url,
175    int icon_types,
176    int desired_size_in_pixel,
177    const favicon_base::FaviconRawBitmapCallback& callback,
178    base::CancelableTaskTracker* tracker) {
179  std::vector<int> desired_sizes_in_pixel;
180  desired_sizes_in_pixel.push_back(desired_size_in_pixel);
181  return GetFaviconForPageURLImpl(
182      page_url,
183      icon_types,
184      desired_sizes_in_pixel,
185      Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
186           base::Unretained(this),
187           callback,
188           desired_size_in_pixel),
189      tracker);
190}
191
192base::CancelableTaskTracker::TaskId
193FaviconService::GetLargestRawFaviconForPageURL(
194    const GURL& page_url,
195    const std::vector<int>& icon_types,
196    int minimum_size_in_pixels,
197    const favicon_base::FaviconRawBitmapCallback& callback,
198    base::CancelableTaskTracker* tracker) {
199  favicon_base::FaviconResultsCallback favicon_results_callback =
200      Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
201           base::Unretained(this),
202           callback,
203           0);
204  if (page_url.SchemeIs(content::kChromeUIScheme) ||
205      page_url.SchemeIs(extensions::kExtensionScheme)) {
206    std::vector<int> desired_sizes_in_pixel;
207    desired_sizes_in_pixel.push_back(0);
208    return GetFaviconForChromeURL(profile_,
209                                  page_url,
210                                  desired_sizes_in_pixel,
211                                  favicon_results_callback,
212                                  tracker);
213  }
214  if (history_service_) {
215    return history_service_->GetLargestFaviconForURL(page_url, icon_types,
216        minimum_size_in_pixels, callback, tracker);
217  }
218  return RunWithEmptyResultAsync(favicon_results_callback, tracker);
219}
220
221base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURL(
222    const GURL& page_url,
223    int icon_types,
224    int desired_size_in_dip,
225    const favicon_base::FaviconResultsCallback& callback,
226    base::CancelableTaskTracker* tracker) {
227  return GetFaviconForPageURLImpl(
228      page_url,
229      icon_types,
230      GetPixelSizesForFaviconScales(desired_size_in_dip),
231      callback,
232      tracker);
233}
234
235base::CancelableTaskTracker::TaskId
236FaviconService::UpdateFaviconMappingsAndFetch(
237    const GURL& page_url,
238    const std::vector<GURL>& icon_urls,
239    int icon_types,
240    int desired_size_in_dip,
241    const favicon_base::FaviconResultsCallback& callback,
242    base::CancelableTaskTracker* tracker) {
243  if (history_service_) {
244    return history_service_->UpdateFaviconMappingsAndFetch(
245        page_url,
246        icon_urls,
247        icon_types,
248        GetPixelSizesForFaviconScales(desired_size_in_dip),
249        callback,
250        tracker);
251  }
252  return RunWithEmptyResultAsync(callback, tracker);
253}
254
255base::CancelableTaskTracker::TaskId FaviconService::GetLargestRawFaviconForID(
256    favicon_base::FaviconID favicon_id,
257    const favicon_base::FaviconRawBitmapCallback& callback,
258    base::CancelableTaskTracker* tracker) {
259  // Use 0 as |desired_size| to get the largest bitmap for |favicon_id| without
260  // any resizing.
261  int desired_size = 0;
262  favicon_base::FaviconResultsCallback callback_runner =
263      Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
264           base::Unretained(this),
265           callback,
266           desired_size);
267
268  if (history_service_) {
269    return history_service_->GetFaviconForID(
270        favicon_id, desired_size, callback_runner, tracker);
271  }
272  return RunWithEmptyResultAsync(callback_runner, tracker);
273}
274
275void FaviconService::SetFaviconOutOfDateForPage(const GURL& page_url) {
276  if (history_service_)
277    history_service_->SetFaviconsOutOfDateForPage(page_url);
278}
279
280void FaviconService::CloneFavicon(const GURL& old_page_url,
281                                  const GURL& new_page_url) {
282  if (history_service_)
283    history_service_->CloneFavicons(old_page_url, new_page_url);
284}
285
286void FaviconService::SetImportedFavicons(
287    const std::vector<ImportedFaviconUsage>& favicon_usage) {
288  if (history_service_)
289    history_service_->SetImportedFavicons(favicon_usage);
290}
291
292void FaviconService::MergeFavicon(
293    const GURL& page_url,
294    const GURL& icon_url,
295    favicon_base::IconType icon_type,
296    scoped_refptr<base::RefCountedMemory> bitmap_data,
297    const gfx::Size& pixel_size) {
298  if (history_service_) {
299    history_service_->MergeFavicon(page_url, icon_url, icon_type, bitmap_data,
300                                   pixel_size);
301  }
302}
303
304void FaviconService::SetFavicons(const GURL& page_url,
305                                 const GURL& icon_url,
306                                 favicon_base::IconType icon_type,
307                                 const gfx::Image& image) {
308  if (!history_service_)
309    return;
310
311  gfx::ImageSkia image_skia = image.AsImageSkia();
312  image_skia.EnsureRepsForSupportedScales();
313  const std::vector<gfx::ImageSkiaRep>& image_reps = image_skia.image_reps();
314  std::vector<SkBitmap> bitmaps;
315  const std::vector<float> favicon_scales = favicon_base::GetFaviconScales();
316  for (size_t i = 0; i < image_reps.size(); ++i) {
317    // Don't save if the scale isn't one of supported favicon scales.
318    if (std::find(favicon_scales.begin(),
319                  favicon_scales.end(),
320                  image_reps[i].scale()) == favicon_scales.end()) {
321      continue;
322    }
323    bitmaps.push_back(image_reps[i].sk_bitmap());
324  }
325  history_service_->SetFavicons(page_url, icon_type, icon_url, bitmaps);
326}
327
328void FaviconService::UnableToDownloadFavicon(const GURL& icon_url) {
329  MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
330  missing_favicon_urls_.insert(url_hash);
331}
332
333bool FaviconService::WasUnableToDownloadFavicon(const GURL& icon_url) const {
334  MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
335  return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end();
336}
337
338void FaviconService::ClearUnableToDownloadFavicons() {
339  missing_favicon_urls_.clear();
340}
341
342FaviconService::~FaviconService() {}
343
344base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURLImpl(
345    const GURL& page_url,
346    int icon_types,
347    const std::vector<int>& desired_sizes_in_pixel,
348    const favicon_base::FaviconResultsCallback& callback,
349    base::CancelableTaskTracker* tracker) {
350  if (page_url.SchemeIs(content::kChromeUIScheme) ||
351      page_url.SchemeIs(extensions::kExtensionScheme)) {
352    return GetFaviconForChromeURL(
353        profile_, page_url, desired_sizes_in_pixel, callback, tracker);
354  }
355  if (history_service_) {
356    return history_service_->GetFaviconsForURL(page_url,
357                                               icon_types,
358                                               desired_sizes_in_pixel,
359                                               callback,
360                                               tracker);
361  }
362  return RunWithEmptyResultAsync(callback, tracker);
363}
364
365void FaviconService::RunFaviconImageCallbackWithBitmapResults(
366    const favicon_base::FaviconImageCallback& callback,
367    int desired_size_in_dip,
368    const std::vector<favicon_base::FaviconRawBitmapResult>&
369        favicon_bitmap_results) {
370  favicon_base::FaviconImageResult image_result;
371  image_result.image = favicon_base::SelectFaviconFramesFromPNGs(
372      favicon_bitmap_results,
373      favicon_base::GetFaviconScales(),
374      desired_size_in_dip);
375  favicon_base::SetFaviconColorSpace(&image_result.image);
376
377  image_result.icon_url = image_result.image.IsEmpty() ?
378      GURL() : favicon_bitmap_results[0].icon_url;
379  callback.Run(image_result);
380}
381
382void FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults(
383    const favicon_base::FaviconRawBitmapCallback& callback,
384    int desired_size_in_pixel,
385    const std::vector<favicon_base::FaviconRawBitmapResult>&
386        favicon_bitmap_results) {
387  if (favicon_bitmap_results.empty() || !favicon_bitmap_results[0].is_valid()) {
388    callback.Run(favicon_base::FaviconRawBitmapResult());
389    return;
390  }
391
392  favicon_base::FaviconRawBitmapResult bitmap_result =
393      favicon_bitmap_results[0];
394
395  // If the desired size is 0, SelectFaviconFrames() will return the largest
396  // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap
397  // data for a single bitmap, return it and avoid an unnecessary decode.
398  if (desired_size_in_pixel == 0) {
399    callback.Run(bitmap_result);
400    return;
401  }
402
403  // If history bitmap is already desired pixel size, return early.
404  if (bitmap_result.pixel_size.width() == desired_size_in_pixel &&
405      bitmap_result.pixel_size.height() == desired_size_in_pixel) {
406    callback.Run(bitmap_result);
407    return;
408  }
409
410  // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then
411  // convert back.
412  std::vector<float> desired_favicon_scales;
413  desired_favicon_scales.push_back(1.0f);
414  gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs(
415      favicon_bitmap_results, desired_favicon_scales, desired_size_in_pixel);
416
417  std::vector<unsigned char> resized_bitmap_data;
418  if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image.AsBitmap(), false,
419                                         &resized_bitmap_data)) {
420    callback.Run(favicon_base::FaviconRawBitmapResult());
421    return;
422  }
423
424  bitmap_result.bitmap_data = base::RefCountedBytes::TakeVector(
425      &resized_bitmap_data);
426  callback.Run(bitmap_result);
427}
428