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