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