Home | History | Annotate | Download | only in favicon
      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 
     26 using base::Bind;
     27 
     28 namespace {
     29 
     30 void 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.
     41 base::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.
     51 base::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().
     69 std::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 
     80 FaviconService::FaviconService(Profile* profile)
     81     : history_service_(HistoryServiceFactory::GetForProfile(
     82           profile, Profile::EXPLICIT_ACCESS)),
     83       profile_(profile) {
     84 }
     85 
     86 // static
     87 void FaviconService::FaviconResultsCallbackRunner(
     88     const favicon_base::FaviconResultsCallback& callback,
     89     const std::vector<favicon_base::FaviconRawBitmapResult>* results) {
     90   callback.Run(*results);
     91 }
     92 
     93 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImage(
     94     const GURL& icon_url,
     95     favicon_base::IconType icon_type,
     96     int desired_size_in_dip,
     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, desired_size_in_dip);
    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         icon_type,
    108         GetPixelSizesForFaviconScales(desired_size_in_dip),
    109         callback_runner,
    110         tracker);
    111   }
    112   return RunWithEmptyResultAsync(callback_runner, tracker);
    113 }
    114 
    115 base::CancelableTaskTracker::TaskId FaviconService::GetRawFavicon(
    116     const GURL& icon_url,
    117     favicon_base::IconType icon_type,
    118     int desired_size_in_dip,
    119     float desired_favicon_scale,
    120     const favicon_base::FaviconRawBitmapCallback& callback,
    121     base::CancelableTaskTracker* tracker) {
    122   int desired_size_in_pixel =
    123       std::ceil(desired_size_in_dip * desired_favicon_scale);
    124   favicon_base::FaviconResultsCallback callback_runner =
    125       Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
    126            base::Unretained(this),
    127            callback,
    128            desired_size_in_pixel);
    129 
    130   if (history_service_) {
    131     std::vector<GURL> icon_urls;
    132     icon_urls.push_back(icon_url);
    133     std::vector<int> desired_sizes_in_pixel;
    134     desired_sizes_in_pixel.push_back(desired_size_in_pixel);
    135 
    136     return history_service_->GetFavicons(
    137         icon_urls, icon_type, desired_sizes_in_pixel, callback_runner, tracker);
    138   }
    139   return RunWithEmptyResultAsync(callback_runner, tracker);
    140 }
    141 
    142 base::CancelableTaskTracker::TaskId FaviconService::GetFavicon(
    143     const GURL& icon_url,
    144     favicon_base::IconType icon_type,
    145     int desired_size_in_dip,
    146     const favicon_base::FaviconResultsCallback& callback,
    147     base::CancelableTaskTracker* tracker) {
    148   if (history_service_) {
    149     std::vector<GURL> icon_urls;
    150     icon_urls.push_back(icon_url);
    151     return history_service_->GetFavicons(
    152         icon_urls,
    153         icon_type,
    154         GetPixelSizesForFaviconScales(desired_size_in_dip),
    155         callback,
    156         tracker);
    157   }
    158   return RunWithEmptyResultAsync(callback, tracker);
    159 }
    160 
    161 base::CancelableTaskTracker::TaskId
    162 FaviconService::UpdateFaviconMappingsAndFetch(
    163     const GURL& page_url,
    164     const std::vector<GURL>& icon_urls,
    165     int icon_types,
    166     int desired_size_in_dip,
    167     const favicon_base::FaviconResultsCallback& callback,
    168     base::CancelableTaskTracker* tracker) {
    169   if (history_service_) {
    170     return history_service_->UpdateFaviconMappingsAndFetch(
    171         page_url,
    172         icon_urls,
    173         icon_types,
    174         GetPixelSizesForFaviconScales(desired_size_in_dip),
    175         callback,
    176         tracker);
    177   }
    178   return RunWithEmptyResultAsync(callback, tracker);
    179 }
    180 
    181 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImageForPageURL(
    182     const FaviconForPageURLParams& params,
    183     const favicon_base::FaviconImageCallback& callback,
    184     base::CancelableTaskTracker* tracker) {
    185   return GetFaviconForPageURLImpl(
    186       params,
    187       GetPixelSizesForFaviconScales(params.desired_size_in_dip),
    188       Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults,
    189            base::Unretained(this),
    190            callback,
    191            params.desired_size_in_dip),
    192       tracker);
    193 }
    194 
    195 base::CancelableTaskTracker::TaskId FaviconService::GetRawFaviconForPageURL(
    196     const FaviconForPageURLParams& params,
    197     float desired_favicon_scale,
    198     const favicon_base::FaviconRawBitmapCallback& callback,
    199     base::CancelableTaskTracker* tracker) {
    200   int desired_size_in_pixel =
    201       std::ceil(params.desired_size_in_dip * desired_favicon_scale);
    202   std::vector<int> desired_sizes_in_pixel;
    203   desired_sizes_in_pixel.push_back(desired_size_in_pixel);
    204   return GetFaviconForPageURLImpl(
    205       params,
    206       desired_sizes_in_pixel,
    207       Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
    208            base::Unretained(this),
    209            callback,
    210            desired_size_in_pixel),
    211       tracker);
    212 }
    213 
    214 base::CancelableTaskTracker::TaskId
    215 FaviconService::GetLargestRawFaviconForPageURL(
    216     Profile* profile,
    217     const GURL& page_url,
    218     const std::vector<int>& icon_types,
    219     int minimum_size_in_pixels,
    220     const favicon_base::FaviconRawBitmapCallback& callback,
    221     base::CancelableTaskTracker* tracker) {
    222   favicon_base::FaviconResultsCallback favicon_results_callback =
    223       Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
    224            base::Unretained(this),
    225            callback,
    226            0);
    227   if (page_url.SchemeIs(content::kChromeUIScheme) ||
    228       page_url.SchemeIs(extensions::kExtensionScheme)) {
    229     std::vector<int> desired_sizes_in_pixel;
    230     desired_sizes_in_pixel.push_back(0);
    231     return GetFaviconForChromeURL(profile,
    232                                   page_url,
    233                                   desired_sizes_in_pixel,
    234                                   favicon_results_callback,
    235                                   tracker);
    236   }
    237   if (history_service_) {
    238     return history_service_->GetLargestFaviconForURL(page_url, icon_types,
    239         minimum_size_in_pixels, callback, tracker);
    240   }
    241   return RunWithEmptyResultAsync(favicon_results_callback, tracker);
    242 }
    243 
    244 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURL(
    245     const FaviconForPageURLParams& params,
    246     const favicon_base::FaviconResultsCallback& callback,
    247     base::CancelableTaskTracker* tracker) {
    248   return GetFaviconForPageURLImpl(
    249       params,
    250       GetPixelSizesForFaviconScales(params.desired_size_in_dip),
    251       callback,
    252       tracker);
    253 }
    254 
    255 base::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 
    275 void FaviconService::SetFaviconOutOfDateForPage(const GURL& page_url) {
    276   if (history_service_)
    277     history_service_->SetFaviconsOutOfDateForPage(page_url);
    278 }
    279 
    280 void 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 
    286 void FaviconService::SetImportedFavicons(
    287     const std::vector<ImportedFaviconUsage>& favicon_usage) {
    288   if (history_service_)
    289     history_service_->SetImportedFavicons(favicon_usage);
    290 }
    291 
    292 void 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 
    304 void 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<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
    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 scale.
    318     if (std::find(favicon_scales.begin(),
    319                   favicon_scales.end(),
    320                   image_reps[i].scale()) == favicon_scales.end()) {
    321       continue;
    322     }
    323 
    324     scoped_refptr<base::RefCountedBytes> bitmap_data(
    325         new base::RefCountedBytes());
    326     if (gfx::PNGCodec::EncodeBGRASkBitmap(image_reps[i].sk_bitmap(),
    327                                           false,
    328                                           &bitmap_data->data())) {
    329       gfx::Size pixel_size(image_reps[i].pixel_width(),
    330                            image_reps[i].pixel_height());
    331       favicon_base::FaviconRawBitmapData bitmap_data_element;
    332       bitmap_data_element.bitmap_data = bitmap_data;
    333       bitmap_data_element.pixel_size = pixel_size;
    334       bitmap_data_element.icon_url = icon_url;
    335 
    336       favicon_bitmap_data.push_back(bitmap_data_element);
    337     }
    338   }
    339   history_service_->SetFavicons(page_url, icon_type, favicon_bitmap_data);
    340 }
    341 
    342 void FaviconService::UnableToDownloadFavicon(const GURL& icon_url) {
    343   MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
    344   missing_favicon_urls_.insert(url_hash);
    345 }
    346 
    347 bool FaviconService::WasUnableToDownloadFavicon(const GURL& icon_url) const {
    348   MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
    349   return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end();
    350 }
    351 
    352 void FaviconService::ClearUnableToDownloadFavicons() {
    353   missing_favicon_urls_.clear();
    354 }
    355 
    356 FaviconService::~FaviconService() {}
    357 
    358 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURLImpl(
    359     const FaviconForPageURLParams& params,
    360     const std::vector<int>& desired_sizes_in_pixel,
    361     const favicon_base::FaviconResultsCallback& callback,
    362     base::CancelableTaskTracker* tracker) {
    363   if (params.page_url.SchemeIs(content::kChromeUIScheme) ||
    364       params.page_url.SchemeIs(extensions::kExtensionScheme)) {
    365     return GetFaviconForChromeURL(
    366         profile_, params.page_url, desired_sizes_in_pixel, callback, tracker);
    367   }
    368   if (history_service_) {
    369     return history_service_->GetFaviconsForURL(params.page_url,
    370                                                params.icon_types,
    371                                                desired_sizes_in_pixel,
    372                                                callback,
    373                                                tracker);
    374   }
    375   return RunWithEmptyResultAsync(callback, tracker);
    376 }
    377 
    378 void FaviconService::RunFaviconImageCallbackWithBitmapResults(
    379     const favicon_base::FaviconImageCallback& callback,
    380     int desired_size_in_dip,
    381     const std::vector<favicon_base::FaviconRawBitmapResult>&
    382         favicon_bitmap_results) {
    383   favicon_base::FaviconImageResult image_result;
    384   image_result.image = favicon_base::SelectFaviconFramesFromPNGs(
    385       favicon_bitmap_results,
    386       favicon_base::GetFaviconScales(),
    387       desired_size_in_dip);
    388   favicon_base::SetFaviconColorSpace(&image_result.image);
    389 
    390   image_result.icon_url = image_result.image.IsEmpty() ?
    391       GURL() : favicon_bitmap_results[0].icon_url;
    392   callback.Run(image_result);
    393 }
    394 
    395 void FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults(
    396     const favicon_base::FaviconRawBitmapCallback& callback,
    397     int desired_size_in_pixel,
    398     const std::vector<favicon_base::FaviconRawBitmapResult>&
    399         favicon_bitmap_results) {
    400   if (favicon_bitmap_results.empty() || !favicon_bitmap_results[0].is_valid()) {
    401     callback.Run(favicon_base::FaviconRawBitmapResult());
    402     return;
    403   }
    404 
    405   DCHECK_EQ(1u, favicon_bitmap_results.size());
    406   favicon_base::FaviconRawBitmapResult bitmap_result =
    407       favicon_bitmap_results[0];
    408 
    409   // If the desired size is 0, SelectFaviconFrames() will return the largest
    410   // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap
    411   // data for a single bitmap, return it and avoid an unnecessary decode.
    412   if (desired_size_in_pixel == 0) {
    413     callback.Run(bitmap_result);
    414     return;
    415   }
    416 
    417   // If history bitmap is already desired pixel size, return early.
    418   if (bitmap_result.pixel_size.width() == desired_size_in_pixel &&
    419       bitmap_result.pixel_size.height() == desired_size_in_pixel) {
    420     callback.Run(bitmap_result);
    421     return;
    422   }
    423 
    424   // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then
    425   // convert back.
    426   std::vector<float> desired_favicon_scales;
    427   desired_favicon_scales.push_back(1.0f);
    428   gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs(
    429       favicon_bitmap_results, desired_favicon_scales, desired_size_in_pixel);
    430 
    431   std::vector<unsigned char> resized_bitmap_data;
    432   if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image.AsBitmap(), false,
    433                                          &resized_bitmap_data)) {
    434     callback.Run(favicon_base::FaviconRawBitmapResult());
    435     return;
    436   }
    437 
    438   bitmap_result.bitmap_data = base::RefCountedBytes::TakeVector(
    439       &resized_bitmap_data);
    440   callback.Run(bitmap_result);
    441 }
    442