blob: 0b8e995c19fad33f23c72f9f5df194d5096ff0e0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/favicon_source.h"
#include <cmath>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/favicon/favicon_utils.h"
#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
#include "chrome/browser/history/top_sites_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_io_context.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "components/favicon/core/history_ui_favicon_request_handler.h"
#include "components/favicon_base/favicon_url_parser.h"
#include "components/history/core/browser/top_sites.h"
#include "content/public/browser/web_contents.h"
#include "net/url_request/url_request.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/layout.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/native_theme/native_theme.h"
#include "ui/resources/grit/ui_resources.h"
#include "url/gurl.h"
namespace {
// web_contents->GetLastCommittedURL in general will not necessarily yield the
// original URL that started the request, but we're only interested in verifying
// if it was issued by a history page, for whom this is the case. If it is not
// possible to obtain the URL, we return the empty GURL.
GURL GetUnsafeRequestOrigin(const content::WebContents::Getter& wc_getter) {
content::WebContents* web_contents = wc_getter.Run();
return web_contents ? web_contents->GetLastCommittedURL() : GURL();
}
bool ParseHistoryUiOrigin(const GURL& url,
favicon::HistoryUiFaviconRequestOrigin* origin) {
GURL history_url(chrome::kChromeUIHistoryURL);
if (url == history_url) {
*origin = favicon::HistoryUiFaviconRequestOrigin::kHistory;
return true;
}
if (url == history_url.Resolve(chrome::kChromeUIHistorySyncedTabs)) {
*origin = favicon::HistoryUiFaviconRequestOrigin::kHistorySyncedTabs;
return true;
}
return false;
}
} // namespace
FaviconSource::FaviconSource(Profile* profile,
chrome::FaviconUrlFormat url_format)
: profile_(profile->GetOriginalProfile()), url_format_(url_format) {}
FaviconSource::~FaviconSource() {
}
std::string FaviconSource::GetSource() {
switch (url_format_) {
case chrome::FaviconUrlFormat::kFaviconLegacy:
return chrome::kChromeUIFaviconHost;
case chrome::FaviconUrlFormat::kFavicon2:
return chrome::kChromeUIFavicon2Host;
}
NOTREACHED();
return "";
}
void FaviconSource::StartDataRequest(
const GURL& url,
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) {
const std::string path = content::URLDataSource::URLToRequestPath(url);
favicon::FaviconService* favicon_service =
FaviconServiceFactory::GetForProfile(profile_,
ServiceAccessType::EXPLICIT_ACCESS);
if (!favicon_service) {
SendDefaultResponse(std::move(callback));
return;
}
chrome::ParsedFaviconPath parsed;
bool success = chrome::ParseFaviconPath(path, url_format_, &parsed);
if (!success) {
SendDefaultResponse(std::move(callback));
return;
}
GURL page_url(parsed.page_url);
GURL icon_url(parsed.icon_url);
if (!page_url.is_valid() && !icon_url.is_valid()) {
SendDefaultResponse(std::move(callback));
return;
}
int desired_size_in_pixel =
std::ceil(parsed.size_in_dip * parsed.device_scale_factor);
if (parsed.page_url.empty()) {
// Request by icon url.
// TODO(michaelbai): Change GetRawFavicon to support combination of
// IconType.
favicon_service->GetRawFavicon(
icon_url, favicon_base::IconType::kFavicon, desired_size_in_pixel,
base::BindOnce(&FaviconSource::OnFaviconDataAvailable,
base::Unretained(this), std::move(callback), parsed),
&cancelable_task_tracker_);
} else {
// Intercept requests for prepopulated pages if TopSites exists.
scoped_refptr<history::TopSites> top_sites =
TopSitesFactory::GetForProfile(profile_);
if (top_sites) {
for (const auto& prepopulated_page : top_sites->GetPrepopulatedPages()) {
if (page_url == prepopulated_page.most_visited.url) {
ui::ScaleFactor resource_scale_factor =
ui::GetSupportedScaleFactor(parsed.device_scale_factor);
std::move(callback).Run(
ui::ResourceBundle::GetSharedInstance()
.LoadDataResourceBytesForScale(prepopulated_page.favicon_id,
resource_scale_factor));
return;
}
}
}
favicon::HistoryUiFaviconRequestOrigin parsed_history_ui_origin;
if (!parsed.allow_favicon_server_fallback ||
!ParseHistoryUiOrigin(GetUnsafeRequestOrigin(wc_getter),
&parsed_history_ui_origin)) {
// Request from local storage only.
// TODO(victorvianna): Expose fallback_to_host in FaviconRequestHandler
// API and move the explanatory comment for |fallback_to_host| here.
const bool fallback_to_host = true;
favicon_service->GetRawFaviconForPageURL(
page_url, {favicon_base::IconType::kFavicon}, desired_size_in_pixel,
fallback_to_host,
base::BindOnce(&FaviconSource::OnFaviconDataAvailable,
base::Unretained(this), std::move(callback), parsed),
&cancelable_task_tracker_);
return;
}
// Request from both local storage and favicon server using
// HistoryUiFaviconRequestHandler.
favicon::HistoryUiFaviconRequestHandler*
history_ui_favicon_request_handler =
HistoryUiFaviconRequestHandlerFactory::GetForBrowserContext(
profile_);
if (!history_ui_favicon_request_handler) {
SendDefaultResponse(std::move(callback), parsed);
return;
}
history_ui_favicon_request_handler->GetRawFaviconForPageURL(
page_url, desired_size_in_pixel,
base::BindOnce(&FaviconSource::OnFaviconDataAvailable,
base::Unretained(this), std::move(callback), parsed),
favicon::FaviconRequestPlatform::kDesktop, parsed_history_ui_origin,
/*icon_url_for_uma=*/
GURL(parsed.icon_url), &cancelable_task_tracker_);
}
}
std::string FaviconSource::GetMimeType(const std::string&) {
// We need to explicitly return a mime type, otherwise if the user tries to
// drag the image they get no extension.
return "image/png";
}
bool FaviconSource::AllowCaching() {
return false;
}
bool FaviconSource::ShouldReplaceExistingSource() {
// Leave the existing DataSource in place, otherwise we'll drop any pending
// requests on the floor.
return false;
}
bool FaviconSource::ShouldServiceRequest(
const GURL& url,
content::ResourceContext* resource_context,
int render_process_id) {
if (url.SchemeIs(chrome::kChromeSearchScheme)) {
return InstantIOContext::ShouldServiceRequest(url, resource_context,
render_process_id);
}
return URLDataSource::ShouldServiceRequest(url, resource_context,
render_process_id);
}
ui::NativeTheme* FaviconSource::GetNativeTheme() {
return ui::NativeTheme::GetInstanceForNativeUi();
}
void FaviconSource::OnFaviconDataAvailable(
content::URLDataSource::GotDataCallback callback,
const chrome::ParsedFaviconPath& parsed,
const favicon_base::FaviconRawBitmapResult& bitmap_result) {
if (bitmap_result.is_valid()) {
// Forward the data along to the networking system.
std::move(callback).Run(bitmap_result.bitmap_data.get());
} else {
SendDefaultResponse(std::move(callback), parsed);
}
}
void FaviconSource::SendDefaultResponse(
content::URLDataSource::GotDataCallback callback,
const chrome::ParsedFaviconPath& parsed) {
if (!parsed.show_fallback_monogram) {
SendDefaultResponse(std::move(callback), parsed.size_in_dip,
parsed.device_scale_factor);
return;
}
int icon_size = std::ceil(parsed.size_in_dip * parsed.device_scale_factor);
SkBitmap bitmap = favicon::GenerateMonogramFavicon(GURL(parsed.page_url),
icon_size, icon_size);
std::vector<unsigned char> bitmap_data;
bool result = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data);
DCHECK(result);
std::move(callback).Run(base::RefCountedBytes::TakeVector(&bitmap_data));
}
void FaviconSource::SendDefaultResponse(
content::URLDataSource::GotDataCallback callback) {
SendDefaultResponse(std::move(callback), 16, 1.0f);
}
void FaviconSource::SendDefaultResponse(
content::URLDataSource::GotDataCallback callback,
int size_in_dip,
float scale_factor) {
const bool dark = GetNativeTheme()->ShouldUseDarkColors();
int resource_id;
switch (size_in_dip) {
case 64:
resource_id = dark ? IDR_DEFAULT_FAVICON_DARK_64 : IDR_DEFAULT_FAVICON_64;
break;
case 32:
resource_id = dark ? IDR_DEFAULT_FAVICON_DARK_32 : IDR_DEFAULT_FAVICON_32;
break;
default:
resource_id = dark ? IDR_DEFAULT_FAVICON_DARK : IDR_DEFAULT_FAVICON;
break;
}
std::move(callback).Run(LoadIconBytes(scale_factor, resource_id));
}
base::RefCountedMemory* FaviconSource::LoadIconBytes(float scale_factor,
int resource_id) {
return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
resource_id, ui::GetSupportedScaleFactor(scale_factor));
}