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