Commit 77c1f668 authored by Mathieu Perreault's avatar Mathieu Perreault Committed by Commit Bot

[New Tab Page] Create a source for NTP icons.

For privacy reasons, the native code will have to choose which icon
is served within a single source (so the implementer cannot deduct
history). The priority order is the local favicon, the server favicon
or a letter+circle fallback. This CL implements the first and the
last of those, and further CLs will improve the logic.

Bug: 853780
Cq-Include-Trybots: luci.chromium.try:closure_compilation
Change-Id: I6f56d02f3c953964e4968f191b8fb3177998c962
Reviewed-on: https://chromium-review.googlesource.com/1105508
Commit-Queue: Mathieu Perreault <mathp@chromium.org>
Reviewed-by: default avatarScott Chen <scottchen@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569414}
parent cd77188e
...@@ -1289,6 +1289,8 @@ jumbo_split_static_library("browser") { ...@@ -1289,6 +1289,8 @@ jumbo_split_static_library("browser") {
"resources_util.h", "resources_util.h",
"search/instant_io_context.cc", "search/instant_io_context.cc",
"search/instant_io_context.h", "search/instant_io_context.h",
"search/ntp_icon_source.cc",
"search/ntp_icon_source.h",
"search/search.cc", "search/search.cc",
"search/search.h", "search/search.h",
"search/suggestions/image_decoder_impl.cc", "search/suggestions/image_decoder_impl.cc",
......
...@@ -461,9 +461,9 @@ body.dark-theme .md-tile:active .md-menu::after { ...@@ -461,9 +461,9 @@ body.dark-theme .md-tile:active .md-menu::after {
} }
.md-favicon { .md-favicon {
height: var(--md-favicon-size); min-height: var(--md-favicon-size);
min-width: var(--md-favicon-size);
pointer-events: none; pointer-events: none;
width: var(--md-favicon-size);
} }
.md-favicon img { .md-favicon img {
......
...@@ -615,13 +615,18 @@ function renderMaterialDesignTile(data) { ...@@ -615,13 +615,18 @@ function renderMaterialDesignTile(data) {
let mdFavicon = document.createElement('div'); let mdFavicon = document.createElement('div');
mdFavicon.className = CLASSES.MD_FAVICON; mdFavicon.className = CLASSES.MD_FAVICON;
// Determine if a fallback icon should be displayed instead.
if (!data.fallbackBackgroundColorRgba || !data.fallbackTextColorRgba) {
let fi = document.createElement('img'); let fi = document.createElement('img');
fi.src = data.faviconUrl; // TODO(crbug.com/853780): Use data.fallbackBackgroundColorRgba and
// data.fallbackTextColorRgba;
fi.src = 'chrome-search://ntpicon/size/16@' + window.devicePixelRatio + 'x/' +
data.url;
// Set title and alt to empty so screen readers won't say the image name. // Set title and alt to empty so screen readers won't say the image name.
fi.title = ''; fi.title = '';
fi.alt = ''; fi.alt = '';
// Images from this source may come as multiples of 16dp or multiples of 40dp.
// We display them as full size (e.g. 48px for 3x) but zoom accordingly to
// keep the right visual appearance.
fi.style = 'zoom: ' + 100 / window.devicePixelRatio + '%;';
loadedCounter += 1; loadedCounter += 1;
fi.addEventListener('load', function(ev) { fi.addEventListener('load', function(ev) {
// Store the type for a potential later navigation. // Store the type for a potential later navigation.
...@@ -648,16 +653,6 @@ function renderMaterialDesignTile(data) { ...@@ -648,16 +653,6 @@ function renderMaterialDesignTile(data) {
countLoad(); countLoad();
}); });
mdFavicon.appendChild(fi); mdFavicon.appendChild(fi);
} else {
mdIconBackground.style.backgroundColor =
convertToRGBAColor(data.fallbackBackgroundColorRgba);
mdFavicon.classList.add(CLASSES.FALLBACK);
let fallbackLetter = document.createElement('div');
fallbackLetter.className = CLASSES.FALLBACK_LETTER;
fallbackLetter.style.color = convertToRGBAColor(data.fallbackTextColorRgba);
fallbackLetter.innerText = data.title.charAt(0);
mdFavicon.appendChild(fallbackLetter);
}
mdIconBackground.appendChild(mdFavicon); mdIconBackground.appendChild(mdFavicon);
mdIcon.appendChild(mdIconBackground); mdIcon.appendChild(mdIconBackground);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "chrome/browser/search/instant_service_observer.h" #include "chrome/browser/search/instant_service_observer.h"
#include "chrome/browser/search/local_ntp_source.h" #include "chrome/browser/search/local_ntp_source.h"
#include "chrome/browser/search/most_visited_iframe_source.h" #include "chrome/browser/search/most_visited_iframe_source.h"
#include "chrome/browser/search/ntp_icon_source.h"
#include "chrome/browser/search/search.h" #include "chrome/browser/search/search.h"
#include "chrome/browser/search/thumbnail_source.h" #include "chrome/browser/search/thumbnail_source.h"
#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_properties.h"
...@@ -88,6 +89,7 @@ InstantService::InstantService(Profile* profile) : profile_(profile) { ...@@ -88,6 +89,7 @@ InstantService::InstantService(Profile* profile) : profile_(profile) {
// Set up the data sources that Instant uses on the NTP. // Set up the data sources that Instant uses on the NTP.
content::URLDataSource::Add(profile_, new ThemeSource(profile_)); content::URLDataSource::Add(profile_, new ThemeSource(profile_));
content::URLDataSource::Add(profile_, new LocalNtpSource(profile_)); content::URLDataSource::Add(profile_, new LocalNtpSource(profile_));
content::URLDataSource::Add(profile_, new NtpIconSource(profile_));
content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, false)); content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, false));
content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, true)); content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, true));
content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_)); content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
......
// Copyright 2018 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/search/ntp_icon_source.h"
#include <stddef.h>
#include <algorithm>
#include <cmath>
#include "base/callback.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_number_conversions.h"
#include "cc/paint/skia_paint_canvas.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_io_context.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/platform_locale_settings.h"
#include "components/favicon/core/fallback_url_util.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon_base/favicon_types.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
namespace {
// Delimiter in the url that looks for the size specification.
const char kSizeParameter[] = "size/";
// Size of the fallback icon (letter + circle), in dp.
const int kFallbackIconSizeDip = 40;
// Used to parse the specification from the path.
struct ParsedNtpIconPath {
// The URL from which the icon is being requested.
GURL url;
// The size of the requested icon in dip.
int size_in_dip = 0;
// The device scale factor of the requested icon.
float device_scale_factor = 1.0;
};
// Returns true if |search| is a substring of |path| which starts at
// |start_index|.
bool HasSubstringAt(const std::string& path,
size_t start_index,
const std::string& search) {
return path.compare(start_index, search.length(), search) == 0;
}
// Parses the path after chrome-search://ntpicon/. Example path is
// "size/16@2x/https://cnn.com".
const ParsedNtpIconPath ParseNtpIconPath(const std::string& path) {
ParsedNtpIconPath parsed;
parsed.url = GURL();
parsed.size_in_dip = gfx::kFaviconSize;
parsed.device_scale_factor = 1.0f;
if (path.empty())
return parsed;
// Size specification has to be present.
size_t parsed_index = 0;
if (!HasSubstringAt(path, parsed_index, kSizeParameter))
return parsed;
parsed_index += strlen(kSizeParameter);
size_t slash = path.find("/", parsed_index);
if (slash == std::string::npos)
return parsed;
// Parse the size spec (e.g. "16@2x")
size_t scale_delimiter = path.find("@", parsed_index);
std::string size_str =
path.substr(parsed_index, scale_delimiter - parsed_index);
std::string scale_str =
path.substr(scale_delimiter + 1, slash - scale_delimiter - 1);
if (!base::StringToInt(size_str, &parsed.size_in_dip))
return parsed;
if (!scale_str.empty())
webui::ParseScaleFactor(scale_str, &parsed.device_scale_factor);
parsed_index = slash + 1;
parsed.url = GURL(path.substr(parsed_index));
return parsed;
}
// Will paint the letter + circle in the specified |canvas|.
void DrawFallbackIcon(const GURL& icon_url, int size, gfx::Canvas* canvas) {
const int kOffsetX = 0;
const int kOffsetY = 0;
cc::PaintFlags flags;
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setAntiAlias(true);
// Draw a filled, colored circle.
// TODO(crbug.com/853780): Set the appropriate background color.
flags.setColor(SK_ColorGRAY);
int corner_radius = static_cast<int>(size * 0.5 + 0.5);
canvas->DrawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
canvas->DrawRoundRect(gfx::Rect(kOffsetX, kOffsetY, size, size),
corner_radius, flags);
// Get the appropriate letter to draw, then eventually draw it.
base::string16 icon_text = favicon::GetFallbackIconText(icon_url);
if (icon_text.empty())
return;
const double kDefaultFontSizeRatio = 0.44;
int font_size = static_cast<int>(size * kDefaultFontSizeRatio);
if (font_size <= 0)
return;
// TODO(crbug.com/853780): Adjust the text color according to the background
// color.
canvas->DrawStringRectWithFlags(
icon_text,
gfx::FontList({l10n_util::GetStringUTF8(IDS_SANS_SERIF_FONT_FAMILY)},
gfx::Font::NORMAL, font_size, gfx::Font::Weight::NORMAL),
SK_ColorWHITE, gfx::Rect(kOffsetX, kOffsetY, size, size),
gfx::Canvas::TEXT_ALIGN_CENTER);
}
// For the given |icon_url|, will render a fallback icon with an appropriate
// letter in a circle.
std::vector<unsigned char> RenderFallbackIconBitmap(const GURL& icon_url,
int size) {
const int size_to_use = std::min(64, size);
SkBitmap bitmap;
bitmap.allocN32Pixels(size_to_use, size_to_use, false);
cc::SkiaPaintCanvas paint_canvas(bitmap);
gfx::Canvas canvas(&paint_canvas, 1.f);
DrawFallbackIcon(icon_url, size_to_use, &canvas);
std::vector<unsigned char> bitmap_data;
bool result = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data);
DCHECK(result);
return bitmap_data;
}
} // namespace
struct NtpIconSource::NtpIconRequest {
NtpIconRequest(const content::URLDataSource::GotDataCallback& cb,
const GURL& path,
int size,
float scale)
: callback(cb),
path(path),
size_in_dip(size),
device_scale_factor(scale) {}
NtpIconRequest(const NtpIconRequest& other) = default;
~NtpIconRequest() {}
content::URLDataSource::GotDataCallback callback;
GURL path;
int size_in_dip;
float device_scale_factor;
};
NtpIconSource::NtpIconSource(Profile* profile)
: profile_(profile), weak_ptr_factory_(this) {}
NtpIconSource::~NtpIconSource() = default;
std::string NtpIconSource::GetSource() const {
return chrome::kChromeUINewTabIconHost;
}
void NtpIconSource::StartDataRequest(
const std::string& path,
const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
const content::URLDataSource::GotDataCallback& callback) {
favicon::FaviconService* favicon_service =
FaviconServiceFactory::GetForProfile(profile_,
ServiceAccessType::EXPLICIT_ACCESS);
const ParsedNtpIconPath parsed = ParseNtpIconPath(path);
if (parsed.url.is_valid()) {
// This will query for a local favicon. If not found, will take alternative
// action in OnFaviconDataAvailable.
const bool fallback_to_host = true;
int desired_size_in_pixel =
std::ceil(parsed.size_in_dip * parsed.device_scale_factor);
favicon_service->GetRawFaviconForPageURL(
parsed.url, {favicon_base::IconType::kFavicon}, desired_size_in_pixel,
fallback_to_host,
base::Bind(&NtpIconSource::OnFaviconDataAvailable,
weak_ptr_factory_.GetWeakPtr(),
NtpIconRequest(callback, parsed.url, parsed.size_in_dip,
parsed.device_scale_factor)),
&cancelable_task_tracker_);
} else {
callback.Run(nullptr);
}
}
void NtpIconSource::OnFaviconDataAvailable(
const NtpIconRequest& request,
const favicon_base::FaviconRawBitmapResult& bitmap_result) {
if (bitmap_result.is_valid()) {
// Local favicon hit, return it.
request.callback.Run(bitmap_result.bitmap_data.get());
} else {
// Draw a fallback (letter in a circle).
int desired_size_in_pixel =
std::ceil(kFallbackIconSizeDip * request.device_scale_factor);
std::vector<unsigned char> bitmap_data =
RenderFallbackIconBitmap(request.path, desired_size_in_pixel);
request.callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data));
}
}
std::string NtpIconSource::GetMimeType(const std::string&) const {
// NOTE: this may not always be correct for all possible types that this
// source will serve. Seems to work fine, however.
return "image/png";
}
bool NtpIconSource::AllowCaching() const {
return false;
}
bool NtpIconSource::ShouldServiceRequest(
const GURL& url,
content::ResourceContext* resource_context,
int render_process_id) const {
if (url.SchemeIs(chrome::kChromeSearchScheme)) {
return InstantIOContext::ShouldServiceRequest(url, resource_context,
render_process_id);
}
return URLDataSource::ShouldServiceRequest(url, resource_context,
render_process_id);
}
// Copyright 2018 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.
#ifndef CHROME_BROWSER_SEARCH_NTP_ICON_SOURCE_H_
#define CHROME_BROWSER_SEARCH_NTP_ICON_SOURCE_H_
#include <string>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/task/cancelable_task_tracker.h"
#include "content/public/browser/url_data_source.h"
class GURL;
class Profile;
namespace favicon_base {
struct FaviconRawBitmapResult;
}
// NTP Icon Source is the gateway between network-level chrome: requests for
// NTP icons and the various backends that may serve them.
class NtpIconSource : public content::URLDataSource {
public:
explicit NtpIconSource(Profile* profile);
~NtpIconSource() override;
// content::URLDataSource implementation.
std::string GetSource() const override;
void StartDataRequest(
const std::string& path,
const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
const content::URLDataSource::GotDataCallback& callback) override;
std::string GetMimeType(const std::string& path) const override;
bool AllowCaching() const override;
bool ShouldServiceRequest(const GURL& url,
content::ResourceContext* resource_context,
int render_process_id) const override;
private:
struct NtpIconRequest;
void OnFaviconDataAvailable(
const NtpIconRequest& request,
const favicon_base::FaviconRawBitmapResult& bitmap_result);
base::CancelableTaskTracker cancelable_task_tracker_;
Profile* profile_;
base::WeakPtrFactory<NtpIconSource> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NtpIconSource);
};
#endif // CHROME_BROWSER_SEARCH_NTP_ICON_SOURCE_H_
...@@ -100,6 +100,7 @@ const char kChromeUINetExportHost[] = "net-export"; ...@@ -100,6 +100,7 @@ const char kChromeUINetExportHost[] = "net-export";
const char kChromeUINetInternalsHost[] = "net-internals"; const char kChromeUINetInternalsHost[] = "net-internals";
const char kChromeUINetInternalsURL[] = "chrome://net-internals/"; const char kChromeUINetInternalsURL[] = "chrome://net-internals/";
const char kChromeUINewTabHost[] = "newtab"; const char kChromeUINewTabHost[] = "newtab";
const char kChromeUINewTabIconHost[] = "ntpicon";
const char kChromeUINewTabURL[] = "chrome://newtab/"; const char kChromeUINewTabURL[] = "chrome://newtab/";
const char kChromeUIOmniboxHost[] = "omnibox"; const char kChromeUIOmniboxHost[] = "omnibox";
const char kChromeUIOmniboxURL[] = "chrome://omnibox/"; const char kChromeUIOmniboxURL[] = "chrome://omnibox/";
......
...@@ -104,6 +104,7 @@ extern const char kChromeUINetExportHost[]; ...@@ -104,6 +104,7 @@ extern const char kChromeUINetExportHost[];
extern const char kChromeUINetInternalsHost[]; extern const char kChromeUINetInternalsHost[];
extern const char kChromeUINetInternalsURL[]; extern const char kChromeUINetInternalsURL[];
extern const char kChromeUINewTabHost[]; extern const char kChromeUINewTabHost[];
extern const char kChromeUINewTabIconHost[];
extern const char kChromeUINewTabURL[]; extern const char kChromeUINewTabURL[];
extern const char kChromeUIOfflineInternalsHost[]; extern const char kChromeUIOfflineInternalsHost[];
extern const char kChromeUIOmniboxHost[]; extern const char kChromeUIOmniboxHost[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment