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