Home | History | Annotate | Download | only in webui
      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/ui/webui/favicon_source.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "chrome/browser/favicon/favicon_service_factory.h"
     13 #include "chrome/browser/history/top_sites.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/search/instant_io_context.h"
     16 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
     17 #include "chrome/browser/sync/profile_sync_service.h"
     18 #include "chrome/browser/sync/profile_sync_service_factory.h"
     19 #include "chrome/common/favicon/favicon_url_parser.h"
     20 #include "chrome/common/url_constants.h"
     21 #include "chrome/grit/locale_settings.h"
     22 #include "net/url_request/url_request.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "ui/base/layout.h"
     25 #include "ui/base/resource/resource_bundle.h"
     26 #include "ui/base/webui/web_ui_util.h"
     27 #include "ui/resources/grit/ui_resources.h"
     28 
     29 FaviconSource::IconRequest::IconRequest()
     30     : size_in_dip(gfx::kFaviconSize), device_scale_factor(1.0f) {
     31 }
     32 
     33 FaviconSource::IconRequest::IconRequest(
     34     const content::URLDataSource::GotDataCallback& cb,
     35     const GURL& path,
     36     int size,
     37     float scale)
     38     : callback(cb),
     39       request_path(path),
     40       size_in_dip(size),
     41       device_scale_factor(scale) {
     42 }
     43 
     44 FaviconSource::IconRequest::~IconRequest() {
     45 }
     46 
     47 FaviconSource::FaviconSource(Profile* profile, IconType type)
     48     : profile_(profile->GetOriginalProfile()),
     49       icon_types_(type == FAVICON ? favicon_base::FAVICON
     50                                   : favicon_base::TOUCH_PRECOMPOSED_ICON |
     51                                         favicon_base::TOUCH_ICON |
     52                                         favicon_base::FAVICON) {}
     53 
     54 FaviconSource::~FaviconSource() {
     55 }
     56 
     57 std::string FaviconSource::GetSource() const {
     58   return icon_types_ == favicon_base::FAVICON ? chrome::kChromeUIFaviconHost
     59                                               : chrome::kChromeUITouchIconHost;
     60 }
     61 
     62 void FaviconSource::StartDataRequest(
     63     const std::string& path,
     64     int render_process_id,
     65     int render_frame_id,
     66     const content::URLDataSource::GotDataCallback& callback) {
     67   FaviconService* favicon_service =
     68       FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
     69   if (!favicon_service) {
     70     SendDefaultResponse(callback);
     71     return;
     72   }
     73 
     74   chrome::ParsedFaviconPath parsed;
     75   bool success = chrome::ParseFaviconPath(path, icon_types_, &parsed);
     76   if (!success) {
     77     SendDefaultResponse(callback);
     78     return;
     79   }
     80 
     81   GURL url(parsed.url);
     82   int desired_size_in_pixel =
     83       std::ceil(parsed.size_in_dip * parsed.device_scale_factor);
     84 
     85   if (parsed.is_icon_url) {
     86     // TODO(michaelbai): Change GetRawFavicon to support combination of
     87     // IconType.
     88    favicon_service->GetRawFavicon(
     89         url,
     90         favicon_base::FAVICON,
     91         desired_size_in_pixel,
     92         base::Bind(
     93             &FaviconSource::OnFaviconDataAvailable,
     94             base::Unretained(this),
     95             IconRequest(
     96                 callback, url, parsed.size_in_dip, parsed.device_scale_factor)),
     97         &cancelable_task_tracker_);
     98   } else {
     99     // Intercept requests for prepopulated pages.
    100     for (size_t i = 0; i < arraysize(history::kPrepopulatedPages); i++) {
    101       if (url.spec() ==
    102           l10n_util::GetStringUTF8(history::kPrepopulatedPages[i].url_id)) {
    103         ui::ScaleFactor resource_scale_factor =
    104             ui::GetSupportedScaleFactor(parsed.device_scale_factor);
    105         callback.Run(
    106             ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
    107                 history::kPrepopulatedPages[i].favicon_id,
    108                 resource_scale_factor));
    109         return;
    110       }
    111     }
    112 
    113     favicon_service->GetRawFaviconForPageURL(
    114         url,
    115         icon_types_,
    116         desired_size_in_pixel,
    117         base::Bind(
    118             &FaviconSource::OnFaviconDataAvailable,
    119             base::Unretained(this),
    120             IconRequest(
    121                 callback, url, parsed.size_in_dip, parsed.device_scale_factor)),
    122         &cancelable_task_tracker_);
    123   }
    124 }
    125 
    126 std::string FaviconSource::GetMimeType(const std::string&) const {
    127   // We need to explicitly return a mime type, otherwise if the user tries to
    128   // drag the image they get no extension.
    129   return "image/png";
    130 }
    131 
    132 bool FaviconSource::ShouldReplaceExistingSource() const {
    133   // Leave the existing DataSource in place, otherwise we'll drop any pending
    134   // requests on the floor.
    135   return false;
    136 }
    137 
    138 bool FaviconSource::ShouldServiceRequest(const net::URLRequest* request) const {
    139   if (request->url().SchemeIs(chrome::kChromeSearchScheme))
    140     return InstantIOContext::ShouldServiceRequest(request);
    141   return URLDataSource::ShouldServiceRequest(request);
    142 }
    143 
    144 bool FaviconSource::HandleMissingResource(const IconRequest& request) {
    145   // If the favicon is not available, try to use the synced favicon.
    146   ProfileSyncService* sync_service =
    147       ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
    148   browser_sync::OpenTabsUIDelegate* open_tabs = sync_service ?
    149       sync_service->GetOpenTabsUIDelegate() : NULL;
    150 
    151   scoped_refptr<base::RefCountedMemory> response;
    152   if (open_tabs &&
    153       open_tabs->GetSyncedFaviconForPageURL(request.request_path.spec(),
    154                                             &response)) {
    155     request.callback.Run(response.get());
    156     return true;
    157   }
    158   return false;
    159 }
    160 
    161 void FaviconSource::OnFaviconDataAvailable(
    162     const IconRequest& request,
    163     const favicon_base::FaviconRawBitmapResult& bitmap_result) {
    164   if (bitmap_result.is_valid()) {
    165     // Forward the data along to the networking system.
    166     request.callback.Run(bitmap_result.bitmap_data.get());
    167   } else if (!HandleMissingResource(request)) {
    168     SendDefaultResponse(request);
    169   }
    170 }
    171 
    172 void FaviconSource::SendDefaultResponse(
    173     const content::URLDataSource::GotDataCallback& callback) {
    174   SendDefaultResponse(IconRequest(callback, GURL(), 16, 1.0f));
    175 }
    176 
    177 void FaviconSource::SendDefaultResponse(const IconRequest& icon_request) {
    178   int favicon_index;
    179   int resource_id;
    180   switch (icon_request.size_in_dip) {
    181     case 64:
    182       favicon_index = SIZE_64;
    183       resource_id = IDR_DEFAULT_FAVICON_64;
    184       break;
    185     case 32:
    186       favicon_index = SIZE_32;
    187       resource_id = IDR_DEFAULT_FAVICON_32;
    188       break;
    189     default:
    190       favicon_index = SIZE_16;
    191       resource_id = IDR_DEFAULT_FAVICON;
    192       break;
    193   }
    194   base::RefCountedMemory* default_favicon =
    195       default_favicons_[favicon_index].get();
    196 
    197   if (!default_favicon) {
    198     ui::ScaleFactor resource_scale_factor =
    199         ui::GetSupportedScaleFactor(icon_request.device_scale_factor);
    200     default_favicon =
    201         ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
    202             resource_id, resource_scale_factor);
    203     default_favicons_[favicon_index] = default_favicon;
    204   }
    205 
    206   icon_request.callback.Run(default_favicon);
    207 }
    208