Commit cf42dd28 authored by kristipark's avatar kristipark Committed by Commit Bot

Add fallback icon support to MV tiles

Tiles with missing/small favicons will display fallback icons instead, which consists of a colored circle and the first letter of the tile title. The fallback color will either be gray for missing favicons or the dominant color of the small favicon.

This change depends on https://crrev.com/c/1096670.

Screenshot: https://screenshot.googleplex.com/dbu8dxk97sX.png

Bug: 837798
Cq-Include-Trybots: luci.chromium.try:closure_compilation
Change-Id: Ie6ddf538396bcf8fbfd11cb9e9f0bad075301edd
Reviewed-on: https://chromium-review.googlesource.com/1096680
Commit-Queue: Kristi Park <kristipark@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarEmily Stark <estark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567493}
parent aee2f86a
...@@ -483,6 +483,25 @@ body.dark-theme .md-tile:active .md-menu::after { ...@@ -483,6 +483,25 @@ body.dark-theme .md-tile:active .md-menu::after {
display: none; display: none;
} }
.md-favicon.fallback {
align-items: center;
border-radius: 50%;
display: flex;
height: 24px;
justify-content: center;
width: 24px;
}
.fallback-letter {
color: white;
font-family: Roboto;
font-size: 12px;
height: 16px;
line-height: 16px;
text-align: center;
width: 16px;
}
.md-title-container { .md-title-container {
overflow: hidden; overflow: hidden;
} }
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
*/ */
const CLASSES = { const CLASSES = {
FAILED_FAVICON: 'failed-favicon', // Applied when the favicon fails to load. FAILED_FAVICON: 'failed-favicon', // Applied when the favicon fails to load.
FALLBACK: 'fallback',
FALLBACK_LETTER: 'fallback-letter',
MATERIAL_DESIGN: 'md', // Applies Material Design styles to the page. MATERIAL_DESIGN: 'md', // Applies Material Design styles to the page.
// Material Design classes. // Material Design classes.
MD_EMPTY_TILE: 'md-empty-tile', MD_EMPTY_TILE: 'md-empty-tile',
...@@ -373,6 +375,18 @@ var isSchemeAllowed = function(url) { ...@@ -373,6 +375,18 @@ var isSchemeAllowed = function(url) {
}; };
/**
* Converts an Array of color components into RGBA format "rgba(R,G,B,A)".
* @param {Array<number>} color Array of rgba color components.
* @return {string} CSS color in RGBA format.
* @private
*/
function convertToRGBAColor(color) {
return 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' +
color[3] / 255 + ')';
}
/** /**
* Renders a MostVisited tile to the DOM. * Renders a MostVisited tile to the DOM.
* @param {object} data Object containing rid, url, title, favicon, thumbnail. * @param {object} data Object containing rid, url, title, favicon, thumbnail.
...@@ -601,35 +615,50 @@ function renderMaterialDesignTile(data) { ...@@ -601,35 +615,50 @@ function renderMaterialDesignTile(data) {
let mdFavicon = document.createElement('div'); let mdFavicon = document.createElement('div');
mdFavicon.className = CLASSES.MD_FAVICON; mdFavicon.className = CLASSES.MD_FAVICON;
let fi = document.createElement('img'); // Determine if a fallback icon should be displayed instead.
fi.src = data.faviconUrl; if (!data.fallbackBackgroundColorRgba || !data.fallbackTextColorRgba) {
// Set title and alt to empty so screen readers won't say the image name. let fi = document.createElement('img');
fi.title = ''; fi.src = data.faviconUrl;
fi.alt = ''; // Set title and alt to empty so screen readers won't say the image name.
loadedCounter += 1; fi.title = '';
fi.addEventListener('load', function(ev) { fi.alt = '';
// Store the type for a potential later navigation. loadedCounter += 1;
tileType = TileVisualType.ICON_REAL; fi.addEventListener('load', function(ev) {
logMostVisitedImpression( // Store the type for a potential later navigation.
position, data.tileTitleSource, data.tileSource, tileType, tileType = TileVisualType.ICON_REAL;
data.dataGenerationTime); logMostVisitedImpression(
// Note: It's important to call countLoad last, because that might emit the position, data.tileTitleSource, data.tileSource, tileType,
// NTP_ALL_TILES_LOADED event, which must happen after the impression log. data.dataGenerationTime);
countLoad(); // Note: It's important to call countLoad last, because that might emit
}); // the NTP_ALL_TILES_LOADED event, which must happen after the impression
fi.addEventListener('error', function(ev) { // log.
mdFavicon.classList.add(CLASSES.FAILED_FAVICON); countLoad();
thumb.removeChild(img); });
// Store the type for a potential later navigation. fi.addEventListener('error', function(ev) {
tileType = TileVisualType.ICON_DEFAULT; mdFavicon.classList.add(CLASSES.FAILED_FAVICON);
logMostVisitedImpression( thumb.removeChild(img);
position, data.tileTitleSource, data.tileSource, tileType, // Store the type for a potential later navigation.
data.dataGenerationTime); tileType = TileVisualType.ICON_DEFAULT;
// Note: It's important to call countLoad last, because that might emit the logMostVisitedImpression(
// NTP_ALL_TILES_LOADED event, which must happen after the impression log. position, data.tileTitleSource, data.tileSource, tileType,
countLoad(); data.dataGenerationTime);
}); // Note: It's important to call countLoad last, because that might emit
mdFavicon.appendChild(fi); // the NTP_ALL_TILES_LOADED event, which must happen after the impression
// log.
countLoad();
});
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);
mdTileInner.appendChild(mdIcon); mdTileInner.appendChild(mdIcon);
......
...@@ -36,6 +36,22 @@ ...@@ -36,6 +36,22 @@
#include "content/public/browser/url_data_source.h" #include "content/public/browser/url_data_source.h"
#include "ui/gfx/color_utils.h" #include "ui/gfx/color_utils.h"
namespace {
const int kSectionBorderAlphaTransparency = 80;
// Converts SkColor to RGBAColor
RGBAColor SkColorToRGBAColor(const SkColor& skColor) {
RGBAColor color;
color.r = SkColorGetR(skColor);
color.g = SkColorGetG(skColor);
color.b = SkColorGetB(skColor);
color.a = SkColorGetA(skColor);
return color;
}
} // namespace
InstantService::InstantService(Profile* profile) : profile_(profile) { InstantService::InstantService(Profile* profile) : profile_(profile) {
// The initialization below depends on a typical set of browser threads. Skip // The initialization below depends on a typical set of browser threads. Skip
// it if we are running in a unit test without the full suite. // it if we are running in a unit test without the full suite.
...@@ -226,6 +242,12 @@ void InstantService::OnURLsAvailable( ...@@ -226,6 +242,12 @@ void InstantService::OnURLsAvailable(
item.source = tile.source; item.source = tile.source;
item.title_source = tile.title_source; item.title_source = tile.title_source;
item.data_generation_time = tile.data_generation_time; item.data_generation_time = tile.data_generation_time;
if (tile.has_fallback_style) {
item.has_fallback_style = true;
item.fallback_background_color =
SkColorToRGBAColor(tile.fallback_background_color);
item.fallback_text_color = SkColorToRGBAColor(tile.fallback_text_color);
}
most_visited_items_.push_back(item); most_visited_items_.push_back(item);
} }
...@@ -244,22 +266,6 @@ void InstantService::NotifyAboutThemeInfo() { ...@@ -244,22 +266,6 @@ void InstantService::NotifyAboutThemeInfo() {
observer.ThemeInfoChanged(*theme_info_); observer.ThemeInfoChanged(*theme_info_);
} }
namespace {
const int kSectionBorderAlphaTransparency = 80;
// Converts SkColor to RGBAColor
RGBAColor SkColorToRGBAColor(const SkColor& sKColor) {
RGBAColor color;
color.r = SkColorGetR(sKColor);
color.g = SkColorGetG(sKColor);
color.b = SkColorGetB(sKColor);
color.a = SkColorGetA(sKColor);
return color;
}
} // namespace
void InstantService::BuildThemeInfo() { void InstantService::BuildThemeInfo() {
// Get theme information from theme service. // Get theme information from theme service.
theme_info_.reset(new ThemeBackgroundInfo()); theme_info_.reset(new ThemeBackgroundInfo());
......
...@@ -47,6 +47,9 @@ IPC_STRUCT_TRAITS_BEGIN(InstantMostVisitedItem) ...@@ -47,6 +47,9 @@ IPC_STRUCT_TRAITS_BEGIN(InstantMostVisitedItem)
IPC_STRUCT_TRAITS_MEMBER(title) IPC_STRUCT_TRAITS_MEMBER(title)
IPC_STRUCT_TRAITS_MEMBER(thumbnail) IPC_STRUCT_TRAITS_MEMBER(thumbnail)
IPC_STRUCT_TRAITS_MEMBER(favicon) IPC_STRUCT_TRAITS_MEMBER(favicon)
IPC_STRUCT_TRAITS_MEMBER(has_fallback_style)
IPC_STRUCT_TRAITS_MEMBER(fallback_background_color)
IPC_STRUCT_TRAITS_MEMBER(fallback_text_color)
IPC_STRUCT_TRAITS_MEMBER(title_source) IPC_STRUCT_TRAITS_MEMBER(title_source)
IPC_STRUCT_TRAITS_MEMBER(source) IPC_STRUCT_TRAITS_MEMBER(source)
IPC_STRUCT_TRAITS_MEMBER(data_generation_time) IPC_STRUCT_TRAITS_MEMBER(data_generation_time)
......
...@@ -56,10 +56,20 @@ bool ThemeBackgroundInfo::operator==(const ThemeBackgroundInfo& rhs) const { ...@@ -56,10 +56,20 @@ bool ThemeBackgroundInfo::operator==(const ThemeBackgroundInfo& rhs) const {
} }
InstantMostVisitedItem::InstantMostVisitedItem() InstantMostVisitedItem::InstantMostVisitedItem()
: title_source(ntp_tiles::TileTitleSource::UNKNOWN), : has_fallback_style(false),
title_source(ntp_tiles::TileTitleSource::UNKNOWN),
source(ntp_tiles::TileSource::TOP_SITES) {} source(ntp_tiles::TileSource::TOP_SITES) {}
InstantMostVisitedItem::InstantMostVisitedItem( InstantMostVisitedItem::InstantMostVisitedItem(
const InstantMostVisitedItem& other) = default; const InstantMostVisitedItem& other) = default;
InstantMostVisitedItem::~InstantMostVisitedItem() {} InstantMostVisitedItem::~InstantMostVisitedItem() {}
bool InstantMostVisitedItem::IsEquivalentTo(
const InstantMostVisitedItem& rhs) const {
return url == rhs.url && title == rhs.title && thumbnail == rhs.thumbnail &&
favicon == rhs.favicon &&
has_fallback_style == rhs.has_fallback_style &&
fallback_background_color == rhs.fallback_background_color &&
fallback_text_color == rhs.fallback_text_color;
}
...@@ -115,6 +115,8 @@ struct InstantMostVisitedItem { ...@@ -115,6 +115,8 @@ struct InstantMostVisitedItem {
InstantMostVisitedItem(const InstantMostVisitedItem& other); InstantMostVisitedItem(const InstantMostVisitedItem& other);
~InstantMostVisitedItem(); ~InstantMostVisitedItem();
bool IsEquivalentTo(const InstantMostVisitedItem& rhs) const;
// The URL of the Most Visited item. // The URL of the Most Visited item.
GURL url; GURL url;
...@@ -128,6 +130,15 @@ struct InstantMostVisitedItem { ...@@ -128,6 +130,15 @@ struct InstantMostVisitedItem {
// The external URL of the favicon associated with this page. // The external URL of the favicon associated with this page.
GURL favicon; GURL favicon;
// True if a fallback icon should be used.
bool has_fallback_style;
// The fallback icon background color in RGBA format.
RGBAColor fallback_background_color;
// The fallback icon text color in RGBA format.
RGBAColor fallback_text_color;
// The source of the item's |title|. // The source of the item's |title|.
ntp_tiles::TileTitleSource title_source; ntp_tiles::TileTitleSource title_source;
......
...@@ -41,8 +41,7 @@ bool AreMostVisitedItemsEqual( ...@@ -41,8 +41,7 @@ bool AreMostVisitedItemsEqual(
return false; return false;
for (size_t i = 0; i < new_items.size(); ++i) { for (size_t i = 0; i < new_items.size(); ++i) {
if (new_items[i].url != old_item_id_pairs[i].second.url || if (!new_items[i].IsEquivalentTo(old_item_id_pairs[i].second)) {
new_items[i].title != old_item_id_pairs[i].second.title) {
return false; return false;
} }
} }
......
...@@ -184,6 +184,15 @@ v8::Local<v8::Object> GenerateMostVisitedItemData( ...@@ -184,6 +184,15 @@ v8::Local<v8::Object> GenerateMostVisitedItemData(
if (!mv_item.favicon.spec().empty()) if (!mv_item.favicon.spec().empty())
builder.Set("faviconUrl", mv_item.favicon.spec()); builder.Set("faviconUrl", mv_item.favicon.spec());
if (mv_item.has_fallback_style) {
builder.Set(
"fallbackBackgroundColorRgba",
internal::RGBAColorToArray(isolate, mv_item.fallback_background_color));
builder.Set(
"fallbackTextColorRgba",
internal::RGBAColorToArray(isolate, mv_item.fallback_text_color));
}
return builder.Build(); return builder.Build();
} }
......
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