Commit 1db88d20 authored by Ryan Meier's avatar Ryan Meier Committed by Commit Bot

Make theme background tab text readable

While importing a theme, browser_theme_pack now runs through a pass to ensure that tab title text for background tabs maintains a minimum contrast ratio with the tab background.



Bug: 871026
Change-Id: Ic1d76c9d50ac0255de671916138933dd96af49ef
Reviewed-on: https://chromium-review.googlesource.com/1173491
Commit-Queue: Ryan Meier <rameier@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584806}
parent d08c051e
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "ui/gfx/canvas.h" #include "ui/gfx/canvas.h"
#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/color_analysis.h" #include "ui/gfx/color_analysis.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/canvas_image_source.h"
...@@ -43,6 +44,7 @@ ...@@ -43,6 +44,7 @@
using content::BrowserThread; using content::BrowserThread;
using extensions::Extension; using extensions::Extension;
using TP = ThemeProperties;
namespace { namespace {
...@@ -56,7 +58,7 @@ constexpr int kTallestFrameHeight = kTallestTabHeight + 19; ...@@ -56,7 +58,7 @@ constexpr int kTallestFrameHeight = kTallestTabHeight + 19;
// theme packs that aren't int-equal to this. Increment this number if you // theme packs that aren't int-equal to this. Increment this number if you
// change default theme assets or if you need themes to recreate their generated // change default theme assets or if you need themes to recreate their generated
// images (which are cached). // images (which are cached).
const int kThemePackVersion = 57; const int kThemePackVersion = 58;
// IDs that are in the DataPack won't clash with the positive integer // IDs that are in the DataPack won't clash with the positive integer
// uint16_t. kHeaderID should always have the maximum value because we want the // uint16_t. kHeaderID should always have the maximum value because we want the
...@@ -214,57 +216,52 @@ std::string GetScaleFactorsAsString( ...@@ -214,57 +216,52 @@ std::string GetScaleFactorsAsString(
struct StringToIntTable { struct StringToIntTable {
const char* key; const char* key;
ThemeProperties::OverwritableByUserThemeProperty id; TP::OverwritableByUserThemeProperty id;
}; };
// Strings used by themes to identify tints in the JSON. // Strings used by themes to identify tints in the JSON.
const StringToIntTable kTintTable[] = { const StringToIntTable kTintTable[] = {
{ "buttons", ThemeProperties::TINT_BUTTONS }, {"buttons", TP::TINT_BUTTONS},
{ "frame", ThemeProperties::TINT_FRAME }, {"frame", TP::TINT_FRAME},
{ "frame_inactive", ThemeProperties::TINT_FRAME_INACTIVE }, {"frame_inactive", TP::TINT_FRAME_INACTIVE},
{ "frame_incognito", ThemeProperties::TINT_FRAME_INCOGNITO }, {"frame_incognito", TP::TINT_FRAME_INCOGNITO},
{ "frame_incognito_inactive", {"frame_incognito_inactive", TP::TINT_FRAME_INCOGNITO_INACTIVE},
ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE }, {"background_tab", TP::TINT_BACKGROUND_TAB},
{ "background_tab", ThemeProperties::TINT_BACKGROUND_TAB },
}; };
const size_t kTintTableLength = arraysize(kTintTable); const size_t kTintTableLength = arraysize(kTintTable);
// Strings used by themes to identify colors in the JSON. // Strings used by themes to identify colors in the JSON.
const StringToIntTable kColorTable[] = { const StringToIntTable kColorTable[] = {
{ "frame", ThemeProperties::COLOR_FRAME }, {"frame", TP::COLOR_FRAME},
{ "frame_inactive", ThemeProperties::COLOR_FRAME_INACTIVE }, {"frame_inactive", TP::COLOR_FRAME_INACTIVE},
{ "frame_incognito", ThemeProperties::COLOR_FRAME_INCOGNITO }, {"frame_incognito", TP::COLOR_FRAME_INCOGNITO},
{ "frame_incognito_inactive", {"frame_incognito_inactive", TP::COLOR_FRAME_INCOGNITO_INACTIVE},
ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE }, {"background_tab", TP::COLOR_BACKGROUND_TAB},
{ "background_tab", ThemeProperties::COLOR_BACKGROUND_TAB }, {"background_tab_inactive", TP::COLOR_BACKGROUND_TAB_INACTIVE},
{ "background_tab_inactive", ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE }, {"background_tab_incognito", TP::COLOR_BACKGROUND_TAB_INCOGNITO},
{ "background_tab_incognito", ThemeProperties::COLOR_BACKGROUND_TAB }, {"background_tab_incognito_inactive",
{ "background_tab_incognito_inactive", TP::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE},
ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE }, {"toolbar", TP::COLOR_TOOLBAR},
{ "toolbar", ThemeProperties::COLOR_TOOLBAR }, {"tab_text", TP::COLOR_TAB_TEXT},
{ "tab_text", ThemeProperties::COLOR_TAB_TEXT }, {"tab_background_text", TP::COLOR_BACKGROUND_TAB_TEXT},
{ "tab_background_text", ThemeProperties::COLOR_BACKGROUND_TAB_TEXT }, {"tab_background_text_inactive", TP::COLOR_BACKGROUND_TAB_TEXT_INACTIVE},
{ "tab_background_text_inactive", {"tab_background_text_incognito", TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO},
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INACTIVE }, {"tab_background_text_incognito_inactive",
{ "tab_background_text_incognito", TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE},
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO }, {"bookmark_text", TP::COLOR_BOOKMARK_TEXT},
{ "tab_background_text_incognito_inactive", {"ntp_background", TP::COLOR_NTP_BACKGROUND},
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE }, {"ntp_text", TP::COLOR_NTP_TEXT},
{ "bookmark_text", ThemeProperties::COLOR_BOOKMARK_TEXT }, {"ntp_link", TP::COLOR_NTP_LINK},
{ "ntp_background", ThemeProperties::COLOR_NTP_BACKGROUND }, {"ntp_header", TP::COLOR_NTP_HEADER},
{ "ntp_text", ThemeProperties::COLOR_NTP_TEXT }, {"button_background", TP::COLOR_BUTTON_BACKGROUND},
{ "ntp_link", ThemeProperties::COLOR_NTP_LINK },
{ "ntp_header", ThemeProperties::COLOR_NTP_HEADER },
{ "button_background", ThemeProperties::COLOR_BUTTON_BACKGROUND },
}; };
const size_t kColorTableLength = arraysize(kColorTable); const size_t kColorTableLength = arraysize(kColorTable);
// Strings used by themes to identify display properties keys in JSON. // Strings used by themes to identify display properties keys in JSON.
const StringToIntTable kDisplayProperties[] = { const StringToIntTable kDisplayProperties[] = {
{ "ntp_background_alignment", {"ntp_background_alignment", TP::NTP_BACKGROUND_ALIGNMENT},
ThemeProperties::NTP_BACKGROUND_ALIGNMENT }, {"ntp_background_repeat", TP::NTP_BACKGROUND_TILING},
{ "ntp_background_repeat", ThemeProperties::NTP_BACKGROUND_TILING }, {"ntp_logo_alternate", TP::NTP_LOGO_ALTERNATE},
{ "ntp_logo_alternate", ThemeProperties::NTP_LOGO_ALTERNATE },
}; };
const size_t kDisplayPropertiesSize = arraysize(kDisplayProperties); const size_t kDisplayPropertiesSize = arraysize(kDisplayProperties);
...@@ -603,6 +600,11 @@ void BrowserThemePack::BuildFromExtension( ...@@ -603,6 +600,11 @@ void BrowserThemePack::BuildFromExtension(
// creating these. // creating these.
pack->CreateTabBackgroundImagesAndColors(&pack->images_); pack->CreateTabBackgroundImagesAndColors(&pack->images_);
// Generate any missing text colors. This must be done after generating frame
// and tab colors, as generated text colors will try to appropriately contrast
// with the frame/tab behind them.
pack->GenerateMissingTextColors();
// Make sure the |images_on_file_thread_| has bitmaps for supported // Make sure the |images_on_file_thread_| has bitmaps for supported
// scale factors before passing to FILE thread. // scale factors before passing to FILE thread.
pack->images_on_file_thread_ = pack->images_; pack->images_on_file_thread_ = pack->images_;
...@@ -775,27 +777,24 @@ bool BrowserThemePack::GetTint(int id, color_utils::HSL* hsl) const { ...@@ -775,27 +777,24 @@ bool BrowserThemePack::GetTint(int id, color_utils::HSL* hsl) const {
bool BrowserThemePack::GetColor(int id, SkColor* color) const { bool BrowserThemePack::GetColor(int id, SkColor* color) const {
static const base::NoDestructor< static const base::NoDestructor<
base::flat_set<ThemeProperties::OverwritableByUserThemeProperty>> base::flat_set<TP::OverwritableByUserThemeProperty>>
kOpaqueColors( kOpaqueColors(
// Explicitly creating a base::flat_set here is not strictly // Explicitly creating a base::flat_set here is not strictly
// necessary according to C++, but we do so to work around // necessary according to C++, but we do so to work around
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84849. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84849.
base::flat_set<ThemeProperties::OverwritableByUserThemeProperty>({ base::flat_set<TP::OverwritableByUserThemeProperty>({
// Background tabs must be opaque since the tabstrip expects to be // Background tabs must be opaque since the tabstrip expects to be
// able to render text opaquely atop them. // able to render text opaquely atop them.
ThemeProperties::COLOR_BACKGROUND_TAB, TP::COLOR_BACKGROUND_TAB, TP::COLOR_BACKGROUND_TAB_INACTIVE,
ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE, TP::COLOR_BACKGROUND_TAB_INCOGNITO,
ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO, TP::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE,
// The frame colors will be used for background tabs when not // The frame colors will be used for background tabs when not
// otherwise overridden and thus must be opaque as well. // otherwise overridden and thus must be opaque as well.
ThemeProperties::COLOR_FRAME, TP::COLOR_FRAME, TP::COLOR_FRAME_INACTIVE,
ThemeProperties::COLOR_FRAME_INACTIVE, TP::COLOR_FRAME_INCOGNITO, TP::COLOR_FRAME_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_FRAME_INCOGNITO,
ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
// The toolbar is used as the foreground tab color, so it must be // The toolbar is used as the foreground tab color, so it must be
// opaque just like background tabs. // opaque just like background tabs.
ThemeProperties::COLOR_TOOLBAR, TP::COLOR_TOOLBAR,
})); }));
if (colors_) { if (colors_) {
...@@ -894,15 +893,15 @@ void BrowserThemePack::BuildHeader(const Extension* extension) { ...@@ -894,15 +893,15 @@ void BrowserThemePack::BuildHeader(const Extension* extension) {
header_ = new BrowserThemePackHeader; header_ = new BrowserThemePackHeader;
header_->version = kThemePackVersion; header_->version = kThemePackVersion;
// TODO(erg): Need to make this endian safe on other computers. Prerequisite // TODO(erg): Need to make this endian safe on other computers. Prerequisite
// is that ui::DataPack removes this same check. // is that ui::DataPack removes this same check.
#if defined(__BYTE_ORDER) #if defined(__BYTE_ORDER)
// Linux check // Linux check
static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, static_assert(__BYTE_ORDER == __LITTLE_ENDIAN,
"datapack assumes little endian"); "datapack assumes little endian");
#elif defined(__BIG_ENDIAN__) #elif defined(__BIG_ENDIAN__)
// Mac check // Mac check
#error DataPack assumes little endian #error DataPack assumes little endian
#endif #endif
header_->little_endian = 1; header_->little_endian = 1;
...@@ -969,23 +968,6 @@ void BrowserThemePack::BuildColorsFromJSON( ...@@ -969,23 +968,6 @@ void BrowserThemePack::BuildColorsFromJSON(
if (colors_value) if (colors_value)
ReadColorsFromJSON(colors_value, &temp_colors); ReadColorsFromJSON(colors_value, &temp_colors);
// Generate inactive background tab text colors if active colors were
// specified.
static constexpr int kColorsToCopy[][2] = {
{ThemeProperties::COLOR_BACKGROUND_TAB_TEXT,
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INACTIVE},
{ThemeProperties::COLOR_BACKGROUND_TAB_TEXT,
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO},
{ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO,
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE},
};
for (const int* text_colors : kColorsToCopy) {
const auto src_it = temp_colors.find(text_colors[0]);
if (src_it != temp_colors.end() &&
!base::ContainsKey(temp_colors, text_colors[1]))
temp_colors[text_colors[1]] = src_it->second;
}
// Copy data from the intermediary data structure to the array. // Copy data from the intermediary data structure to the array.
size_t count = 0; size_t count = 0;
for (std::map<int, SkColor>::const_iterator it = temp_colors.begin(); for (std::map<int, SkColor>::const_iterator it = temp_colors.begin();
...@@ -1029,8 +1011,8 @@ void BrowserThemePack::ReadColorsFromJSON( ...@@ -1029,8 +1011,8 @@ void BrowserThemePack::ReadColorsFromJSON(
// We no longer use ntp_section, but to support legacy // We no longer use ntp_section, but to support legacy
// themes we still need to use it as a fallback for // themes we still need to use it as a fallback for
// ntp_header. // ntp_header.
if (!temp_colors->count(ThemeProperties::COLOR_NTP_HEADER)) if (!temp_colors->count(TP::COLOR_NTP_HEADER))
(*temp_colors)[ThemeProperties::COLOR_NTP_HEADER] = color; (*temp_colors)[TP::COLOR_NTP_HEADER] = color;
} else { } else {
int id = GetIntForString(iter.key(), kColorTable, kColorTableLength); int id = GetIntForString(iter.key(), kColorTable, kColorTableLength);
if (id != -1) if (id != -1)
...@@ -1058,26 +1040,25 @@ void BrowserThemePack::BuildDisplayPropertiesFromJSON( ...@@ -1058,26 +1040,25 @@ void BrowserThemePack::BuildDisplayPropertiesFromJSON(
int property_id = GetIntForString(iter.key(), kDisplayProperties, int property_id = GetIntForString(iter.key(), kDisplayProperties,
kDisplayPropertiesSize); kDisplayPropertiesSize);
switch (property_id) { switch (property_id) {
case ThemeProperties::NTP_BACKGROUND_ALIGNMENT: { case TP::NTP_BACKGROUND_ALIGNMENT: {
std::string val; std::string val;
if (iter.value().GetAsString(&val)) { if (iter.value().GetAsString(&val)) {
temp_properties[ThemeProperties::NTP_BACKGROUND_ALIGNMENT] = temp_properties[TP::NTP_BACKGROUND_ALIGNMENT] =
ThemeProperties::StringToAlignment(val); TP::StringToAlignment(val);
} }
break; break;
} }
case ThemeProperties::NTP_BACKGROUND_TILING: { case TP::NTP_BACKGROUND_TILING: {
std::string val; std::string val;
if (iter.value().GetAsString(&val)) { if (iter.value().GetAsString(&val)) {
temp_properties[ThemeProperties::NTP_BACKGROUND_TILING] = temp_properties[TP::NTP_BACKGROUND_TILING] = TP::StringToTiling(val);
ThemeProperties::StringToTiling(val);
} }
break; break;
} }
case ThemeProperties::NTP_LOGO_ALTERNATE: { case TP::NTP_LOGO_ALTERNATE: {
int val = 0; int val = 0;
if (iter.value().GetAsInteger(&val)) if (iter.value().GetAsInteger(&val))
temp_properties[ThemeProperties::NTP_LOGO_ALTERNATE] = val; temp_properties[TP::NTP_LOGO_ALTERNATE] = val;
break; break;
} }
} }
...@@ -1240,18 +1221,16 @@ void BrowserThemePack::CreateFrameImagesAndColors(ImageCache* images) { ...@@ -1240,18 +1221,16 @@ void BrowserThemePack::CreateFrameImagesAndColors(ImageCache* images) {
int tint_id; int tint_id;
base::Optional<int> color_id; base::Optional<int> color_id;
} kFrameValues[] = { } kFrameValues[] = {
{PRS_THEME_FRAME, ThemeProperties::TINT_FRAME, {PRS_THEME_FRAME, TP::TINT_FRAME, TP::COLOR_FRAME},
ThemeProperties::COLOR_FRAME}, {PRS_THEME_FRAME_INACTIVE, TP::TINT_FRAME_INACTIVE,
{PRS_THEME_FRAME_INACTIVE, ThemeProperties::TINT_FRAME_INACTIVE, TP::COLOR_FRAME_INACTIVE},
ThemeProperties::COLOR_FRAME_INACTIVE}, {PRS_THEME_FRAME_OVERLAY, TP::TINT_FRAME, base::nullopt},
{PRS_THEME_FRAME_OVERLAY, ThemeProperties::TINT_FRAME, base::nullopt}, {PRS_THEME_FRAME_OVERLAY_INACTIVE, TP::TINT_FRAME_INACTIVE,
{PRS_THEME_FRAME_OVERLAY_INACTIVE, ThemeProperties::TINT_FRAME_INACTIVE,
base::nullopt}, base::nullopt},
{PRS_THEME_FRAME_INCOGNITO, ThemeProperties::TINT_FRAME_INCOGNITO, {PRS_THEME_FRAME_INCOGNITO, TP::TINT_FRAME_INCOGNITO,
ThemeProperties::COLOR_FRAME_INCOGNITO}, TP::COLOR_FRAME_INCOGNITO},
{PRS_THEME_FRAME_INCOGNITO_INACTIVE, {PRS_THEME_FRAME_INCOGNITO_INACTIVE, TP::TINT_FRAME_INCOGNITO_INACTIVE,
ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE, TP::COLOR_FRAME_INCOGNITO_INACTIVE},
ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE},
}; };
// Create all the output images in a separate cache and move them back into // Create all the output images in a separate cache and move them back into
...@@ -1298,33 +1277,27 @@ void BrowserThemePack::CreateFrameImagesAndColors(ImageCache* images) { ...@@ -1298,33 +1277,27 @@ void BrowserThemePack::CreateFrameImagesAndColors(ImageCache* images) {
} }
void BrowserThemePack::GenerateFrameColors() { void BrowserThemePack::GenerateFrameColors() {
using TP = ThemeProperties;
SkColor frame; SkColor frame;
if (!GetColor(TP::COLOR_FRAME, &frame)) { if (!GetColor(TP::COLOR_FRAME, &frame)) {
frame = ThemeProperties::GetDefaultColor(TP::COLOR_FRAME, false); frame = TP::GetDefaultColor(TP::COLOR_FRAME, false);
SetColor(TP::COLOR_FRAME, SetColor(TP::COLOR_FRAME, HSLShift(frame, GetTintInternal(TP::TINT_FRAME)));
HSLShift(frame, GetTintInternal(ThemeProperties::TINT_FRAME)));
} }
SkColor temp; SkColor temp;
if (!GetColor(TP::COLOR_FRAME_INACTIVE, &temp)) { if (!GetColor(TP::COLOR_FRAME_INACTIVE, &temp)) {
SetColor( SetColor(TP::COLOR_FRAME_INACTIVE,
TP::COLOR_FRAME_INACTIVE, HSLShift(frame, GetTintInternal(TP::TINT_FRAME_INACTIVE)));
HSLShift(frame, GetTintInternal(ThemeProperties::TINT_FRAME_INACTIVE)));
} }
if (!GetColor(TP::COLOR_FRAME_INCOGNITO, &temp)) { if (!GetColor(TP::COLOR_FRAME_INCOGNITO, &temp)) {
SetColor(TP::COLOR_FRAME_INCOGNITO, SetColor(TP::COLOR_FRAME_INCOGNITO,
HSLShift(frame, HSLShift(frame, GetTintInternal(TP::TINT_FRAME_INCOGNITO)));
GetTintInternal(ThemeProperties::TINT_FRAME_INCOGNITO)));
} }
if (!GetColor(TP::COLOR_FRAME_INCOGNITO_INACTIVE, &temp)) { if (!GetColor(TP::COLOR_FRAME_INCOGNITO_INACTIVE, &temp)) {
SetColor( SetColor(
TP::COLOR_FRAME_INCOGNITO_INACTIVE, TP::COLOR_FRAME_INCOGNITO_INACTIVE,
HSLShift(frame, GetTintInternal( HSLShift(frame, GetTintInternal(TP::TINT_FRAME_INCOGNITO_INACTIVE)));
ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE)));
} }
} }
...@@ -1348,17 +1321,17 @@ void BrowserThemePack::CreateTabBackgroundImagesAndColors(ImageCache* images) { ...@@ -1348,17 +1321,17 @@ void BrowserThemePack::CreateTabBackgroundImagesAndColors(ImageCache* images) {
int color_id; int color_id;
} kTabBackgroundMap[] = { } kTabBackgroundMap[] = {
{PRS_THEME_TAB_BACKGROUND, base::nullopt, PRS_THEME_FRAME, {PRS_THEME_TAB_BACKGROUND, base::nullopt, PRS_THEME_FRAME,
ThemeProperties::COLOR_FRAME, ThemeProperties::COLOR_BACKGROUND_TAB}, TP::COLOR_FRAME, TP::COLOR_BACKGROUND_TAB},
{PRS_THEME_TAB_BACKGROUND_INACTIVE, PRS_THEME_TAB_BACKGROUND, {PRS_THEME_TAB_BACKGROUND_INACTIVE, PRS_THEME_TAB_BACKGROUND,
PRS_THEME_FRAME_INACTIVE, ThemeProperties::COLOR_FRAME_INACTIVE, PRS_THEME_FRAME_INACTIVE, TP::COLOR_FRAME_INACTIVE,
ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE}, TP::COLOR_BACKGROUND_TAB_INACTIVE},
{PRS_THEME_TAB_BACKGROUND_INCOGNITO, base::nullopt, {PRS_THEME_TAB_BACKGROUND_INCOGNITO, base::nullopt,
PRS_THEME_FRAME_INCOGNITO, ThemeProperties::COLOR_FRAME_INCOGNITO, PRS_THEME_FRAME_INCOGNITO, TP::COLOR_FRAME_INCOGNITO,
ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO}, TP::COLOR_BACKGROUND_TAB_INCOGNITO},
{PRS_THEME_TAB_BACKGROUND_INCOGNITO_INACTIVE, {PRS_THEME_TAB_BACKGROUND_INCOGNITO_INACTIVE,
PRS_THEME_TAB_BACKGROUND_INCOGNITO, PRS_THEME_FRAME_INCOGNITO_INACTIVE, PRS_THEME_TAB_BACKGROUND_INCOGNITO, PRS_THEME_FRAME_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE, TP::COLOR_FRAME_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE}, TP::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE},
}; };
ImageCache temp_output; ImageCache temp_output;
...@@ -1392,8 +1365,7 @@ void BrowserThemePack::CreateTabBackgroundImagesAndColors(ImageCache* images) { ...@@ -1392,8 +1365,7 @@ void BrowserThemePack::CreateTabBackgroundImagesAndColors(ImageCache* images) {
auto source = std::make_unique<TabBackgroundImageSource>( auto source = std::make_unique<TabBackgroundImageSource>(
frame_color, image_to_tint, overlay, frame_color, image_to_tint, overlay,
GetTintInternal(ThemeProperties::TINT_BACKGROUND_TAB), GetTintInternal(TP::TINT_BACKGROUND_TAB), TP::kFrameHeightAboveTabs);
ThemeProperties::kFrameHeightAboveTabs);
gfx::Size dest_size = image_to_tint.size(); gfx::Size dest_size = image_to_tint.size();
dest_size.SetToMax(gfx::Size(0, kTallestTabHeight)); dest_size.SetToMax(gfx::Size(0, kTallestTabHeight));
const gfx::Image dest_image(gfx::ImageSkia(std::move(source), dest_size)); const gfx::Image dest_image(gfx::ImageSkia(std::move(source), dest_size));
...@@ -1406,6 +1378,67 @@ void BrowserThemePack::CreateTabBackgroundImagesAndColors(ImageCache* images) { ...@@ -1406,6 +1378,67 @@ void BrowserThemePack::CreateTabBackgroundImagesAndColors(ImageCache* images) {
MergeImageCaches(temp_output, images); MergeImageCaches(temp_output, images);
} }
void BrowserThemePack::GenerateMissingTextColors() {
// Background Tab
constexpr int kDefaultSourceTextColorId = TP::COLOR_BACKGROUND_TAB_TEXT;
GenerateMissingTextColorForID(TP::COLOR_BACKGROUND_TAB_TEXT,
TP::COLOR_BACKGROUND_TAB,
kDefaultSourceTextColorId);
// Background Tab - Inactive
GenerateMissingTextColorForID(TP::COLOR_BACKGROUND_TAB_TEXT_INACTIVE,
TP::COLOR_BACKGROUND_TAB_INACTIVE,
kDefaultSourceTextColorId);
// Incognito
GenerateMissingTextColorForID(TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO,
TP::COLOR_BACKGROUND_TAB_INCOGNITO,
kDefaultSourceTextColorId);
// Incognito - Inactive
GenerateMissingTextColorForID(
TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE,
TP::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO);
}
void BrowserThemePack::GenerateMissingTextColorForID(int text_color_id,
int tab_color_id,
int source_color_id) {
SkColor text_color, tab_color;
const bool has_text_color = GetColor(text_color_id, &text_color);
const bool has_tab_color = GetColor(tab_color_id, &tab_color);
// If neither the text color nor the tab color has been set by the theme,
// do nothing.
if (!has_text_color && !has_tab_color)
return;
// Determine the text color to start with, in order of preference:
// 1) The color specified by the theme (if it exists)
// 2) The color passed in to use as a source function (if it exists)
// 3) The default color for the text property
SkColor blend_source_color;
if (has_text_color) {
blend_source_color = text_color;
} else {
SkColor source_text_color;
if (GetColor(source_color_id, &source_text_color)) {
blend_source_color = source_text_color;
} else {
// GetDefaultColor() requires incognito-aware lookup, so we first have to
// get the appropriate lookup ID information
TP::PropertyLookupPair lookup_pair = TP::GetLookupID(text_color_id);
blend_source_color = TP::GetDefaultColor(lookup_pair);
}
}
const SkColor result_color =
color_utils::GetColorWithMinimumContrast(blend_source_color, tab_color);
SetColor(text_color_id, result_color);
}
void BrowserThemePack::RepackImages(const ImageCache& images, void BrowserThemePack::RepackImages(const ImageCache& images,
RawImages* reencoded_images) const { RawImages* reencoded_images) const {
for (ImageCache::const_iterator it = images.begin(); for (ImageCache::const_iterator it = images.begin();
...@@ -1457,12 +1490,12 @@ color_utils::HSL BrowserThemePack::GetTintInternal(int id) const { ...@@ -1457,12 +1490,12 @@ color_utils::HSL BrowserThemePack::GetTintInternal(int id) const {
return hsl; return hsl;
int original_id = id; int original_id = id;
if (id == ThemeProperties::TINT_FRAME_INCOGNITO) if (id == TP::TINT_FRAME_INCOGNITO)
original_id = ThemeProperties::TINT_FRAME; original_id = TP::TINT_FRAME;
else if (id == ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE) else if (id == TP::TINT_FRAME_INCOGNITO_INACTIVE)
original_id = ThemeProperties::TINT_FRAME_INACTIVE; original_id = TP::TINT_FRAME_INACTIVE;
return ThemeProperties::GetDefaultTint(original_id, original_id != id); return TP::GetDefaultTint(original_id, original_id != id);
} }
int BrowserThemePack::GetRawIDByPersistentID( int BrowserThemePack::GetRawIDByPersistentID(
......
...@@ -175,6 +175,16 @@ class BrowserThemePack : public CustomThemeSupplier { ...@@ -175,6 +175,16 @@ class BrowserThemePack : public CustomThemeSupplier {
// color has been specified. Must be called after GenerateFrameImages(). // color has been specified. Must be called after GenerateFrameImages().
void CreateTabBackgroundImagesAndColors(ImageCache* images); void CreateTabBackgroundImagesAndColors(ImageCache* images);
// Generates any text colors which have not already been set.
void GenerateMissingTextColors();
// Generates text color for the specified id |text_color_id|, based on the
// background color of the tab |tab_color_id|, and using the color already
// defined for |source_color_id| as a starting point (if it exists).
void GenerateMissingTextColorForID(int text_color_id,
int tab_color_id,
int source_color_id);
// Takes all the SkBitmaps in |images|, encodes them as PNGs and places // Takes all the SkBitmaps in |images|, encodes them as PNGs and places
// them in |reencoded_images|. // them in |reencoded_images|.
void RepackImages(const ImageCache& images, void RepackImages(const ImageCache& images,
......
...@@ -64,6 +64,8 @@ class BrowserThemePackTest : public ::testing::Test { ...@@ -64,6 +64,8 @@ class BrowserThemePackTest : public ::testing::Test {
static void BuildFromUnpackedExtension(const base::FilePath& extension_path, static void BuildFromUnpackedExtension(const base::FilePath& extension_path,
scoped_refptr<BrowserThemePack>* pack); scoped_refptr<BrowserThemePack>* pack);
static base::FilePath GetTestExtensionThemePath(
base::StringPiece theme_folder);
static base::FilePath GetStarGazingPath(); static base::FilePath GetStarGazingPath();
static base::FilePath GetHiDpiThemePath(); static base::FilePath GetHiDpiThemePath();
...@@ -74,6 +76,12 @@ class BrowserThemePackTest : public ::testing::Test { ...@@ -74,6 +76,12 @@ class BrowserThemePackTest : public ::testing::Test {
static void VerifyHiDpiTheme(BrowserThemePack* pack); static void VerifyHiDpiTheme(BrowserThemePack* pack);
// Verify that the colors in the theme for |color_id_a| and |color_id_b| are
// the same.
static void VerifyColorsMatch(BrowserThemePack* pack,
TP::OverwritableByUserThemeProperty color_id_a,
TP::OverwritableByUserThemeProperty color_id_b);
const BrowserThemePack& theme_pack() const { return *theme_pack_; } const BrowserThemePack& theme_pack() const { return *theme_pack_; }
private: private:
...@@ -214,6 +222,18 @@ void BrowserThemePackTest::BuildFromUnpackedExtension( ...@@ -214,6 +222,18 @@ void BrowserThemePackTest::BuildFromUnpackedExtension(
ASSERT_TRUE((*pack)->is_valid()); ASSERT_TRUE((*pack)->is_valid());
} }
// static
base::FilePath BrowserThemePackTest::GetTestExtensionThemePath(
base::StringPiece theme_folder) {
base::FilePath test_path;
const bool result = base::PathService::Get(chrome::DIR_TEST_DATA, &test_path);
DCHECK(result);
test_path = test_path.AppendASCII("extensions");
test_path = test_path.AppendASCII(theme_folder);
return base::FilePath(test_path);
}
// static // static
base::FilePath BrowserThemePackTest::GetStarGazingPath() { base::FilePath BrowserThemePackTest::GetStarGazingPath() {
base::FilePath test_path; base::FilePath test_path;
...@@ -412,6 +432,26 @@ void BrowserThemePackTest::VerifyHiDpiTheme(BrowserThemePack* pack) { ...@@ -412,6 +432,26 @@ void BrowserThemePackTest::VerifyHiDpiTheme(BrowserThemePack* pack) {
} }
} }
// static
void BrowserThemePackTest::VerifyColorsMatch(
BrowserThemePack* pack,
TP::OverwritableByUserThemeProperty color_id_a,
TP::OverwritableByUserThemeProperty color_id_b) {
SkColor color_a;
SkColor color_b;
bool color_a_set = pack->GetColor(color_id_a, &color_a);
bool color_b_set = pack->GetColor(color_id_b, &color_b);
SCOPED_TRACE(testing::Message()
<< "Color A: " << std::hex << color_a << " (ID: " << std::dec
<< color_id_a << "), Color B: " << std::hex << color_b
<< " (ID: " << std::dec << color_id_b << ")");
EXPECT_TRUE(color_a_set);
EXPECT_TRUE(color_b_set);
EXPECT_EQ(color_a, color_b);
}
// static // static
SkColor BrowserThemePackTest::BuildThirdOpacity(SkColor color_link) { SkColor BrowserThemePackTest::BuildThirdOpacity(SkColor color_link) {
return SkColorSetA(color_link, SkColorGetA(color_link) / 3); return SkColorSetA(color_link, SkColorGetA(color_link) / 3);
...@@ -490,12 +530,6 @@ TEST_F(BrowserThemePackTest, SupportsAlpha) { ...@@ -490,12 +530,6 @@ TEST_F(BrowserThemePackTest, SupportsAlpha) {
colors[TP::COLOR_TOOLBAR] = SkColorSetARGB(255, 0, 20, 40); colors[TP::COLOR_TOOLBAR] = SkColorSetARGB(255, 0, 20, 40);
colors[TP::COLOR_TAB_TEXT] = SkColorSetARGB(255, 60, 80, 100); colors[TP::COLOR_TAB_TEXT] = SkColorSetARGB(255, 60, 80, 100);
colors[TP::COLOR_BACKGROUND_TAB_TEXT] = SkColorSetARGB(0, 120, 140, 160); colors[TP::COLOR_BACKGROUND_TAB_TEXT] = SkColorSetARGB(0, 120, 140, 160);
colors[TP::COLOR_BACKGROUND_TAB_TEXT_INACTIVE] =
colors[TP::COLOR_BACKGROUND_TAB_TEXT];
colors[TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO] =
colors[TP::COLOR_BACKGROUND_TAB_TEXT];
colors[TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE] =
colors[TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO];
colors[TP::COLOR_BOOKMARK_TEXT] = SkColorSetARGB(255, 180, 200, 220); colors[TP::COLOR_BOOKMARK_TEXT] = SkColorSetARGB(255, 180, 200, 220);
colors[TP::COLOR_NTP_TEXT] = SkColorSetARGB(128, 240, 255, 0); colors[TP::COLOR_NTP_TEXT] = SkColorSetARGB(128, 240, 255, 0);
VerifyColorMap(colors); VerifyColorMap(colors);
...@@ -703,3 +737,88 @@ TEST_F(BrowserThemePackTest, HiDpiThemeTest) { ...@@ -703,3 +737,88 @@ TEST_F(BrowserThemePackTest, HiDpiThemeTest) {
VerifyHiDpiTheme(pack.get()); VerifyHiDpiTheme(pack.get());
} }
} }
// Ensure that, given a theme that specifies background tab/text colors which
// are too similar, the importing process modifies the text color so that it
// maintains a minimum readable contrast ratio with the background.
TEST_F(BrowserThemePackTest, TestBackgroundTabTextMinimumContrast) {
// Build a theme from test file (theme_tabcontrast).
base::FilePath contrast_theme_path =
GetTestExtensionThemePath("theme_tabcontrast");
scoped_refptr<BrowserThemePack> pack;
BuildFromUnpackedExtension(contrast_theme_path, &pack);
// Check the contrast ratio of text/tab color pairs to make sure that they
// meet the minimum criteria for readable contrast ratio.
struct TabColorPair {
TP::OverwritableByUserThemeProperty tab_color_id;
TP::OverwritableByUserThemeProperty text_color_id;
};
const TabColorPair color_pairs_to_check[] = {
{TP::COLOR_BACKGROUND_TAB, TP::COLOR_BACKGROUND_TAB_TEXT},
{TP::COLOR_BACKGROUND_TAB_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT_INACTIVE},
{TP::COLOR_BACKGROUND_TAB_INCOGNITO,
TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO},
{TP::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE},
};
for (const TabColorPair& current_pair : color_pairs_to_check) {
SkColor cur_tab_color;
SkColor cur_text_color;
pack->GetColor(current_pair.tab_color_id, &cur_tab_color);
pack->GetColor(current_pair.text_color_id, &cur_text_color);
float contrast_ratio =
color_utils::GetContrastRatio(cur_tab_color, cur_text_color);
EXPECT_GE(contrast_ratio, color_utils::kMinimumReadableContrastRatio);
}
}
// Ensure that, given a theme which only specifies a color for
// COLOR_BACKGROUND_TAB_TEXT, that color is used for the other variants of
// background tab text (inactive, incognito, and incognito+inactive).
TEST_F(BrowserThemePackTest, TestBGTabTextColorAutoAssign) {
// Build a theme from the test file (theme_testinherittextcolor)
// This theme specifies a color for background_tab_text, but none of its
// variants.
base::FilePath contrast_theme_path =
GetTestExtensionThemePath("theme_testinherittextcolor");
scoped_refptr<BrowserThemePack> pack;
BuildFromUnpackedExtension(contrast_theme_path, &pack);
// Verify that all background tab text colors match the color for background
// tab text.
BrowserThemePack* pack_ptr = pack.get();
VerifyColorsMatch(pack_ptr, TP::COLOR_BACKGROUND_TAB_TEXT_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT);
VerifyColorsMatch(pack_ptr, TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO,
TP::COLOR_BACKGROUND_TAB_TEXT);
VerifyColorsMatch(pack_ptr, TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT);
}
// Ensure that, given a theme which only specifies colors for
// COLOR_BACKGROUND_TAB_TEXT and COLOR_BACKGROUND_TAB_TEXT_INCOGNITO, those
// colors are also used for their respective inactive variants.
TEST_F(BrowserThemePackTest, TestBGTabTextColorAutoAssign_WithIncognito) {
// Build a theme from the test file (theme_testinherittextcolor_withincog)
// This theme specifies a color for background_tab_text and
// background_tab_text_incognito, but neither of their inactive variants.
base::FilePath contrast_theme_path =
GetTestExtensionThemePath("theme_testinherittextcolor_withincog");
scoped_refptr<BrowserThemePack> pack;
BuildFromUnpackedExtension(contrast_theme_path, &pack);
// Verify that background_inactive is getting its color from background, and
// background_incognito_inactive is getting its color from
// background_incognito.
BrowserThemePack* pack_ptr = pack.get();
VerifyColorsMatch(pack_ptr, TP::COLOR_BACKGROUND_TAB_TEXT_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT);
VerifyColorsMatch(pack_ptr, TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE,
TP::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO);
}
...@@ -194,7 +194,6 @@ base::Optional<SkColor> MaybeGetDefaultColorForNewerMaterialUi(int id, ...@@ -194,7 +194,6 @@ base::Optional<SkColor> MaybeGetDefaultColorForNewerMaterialUi(int id,
return base::nullopt; return base::nullopt;
} }
} }
} // namespace } // namespace
// static // static
...@@ -399,3 +398,35 @@ SkColor ThemeProperties::GetDefaultColor(int id, bool incognito) { ...@@ -399,3 +398,35 @@ SkColor ThemeProperties::GetDefaultColor(int id, bool incognito) {
return gfx::kPlaceholderColor; return gfx::kPlaceholderColor;
} }
// static
SkColor ThemeProperties::GetDefaultColor(PropertyLookupPair lookup_pair) {
return GetDefaultColor(lookup_pair.property_id, lookup_pair.is_incognito);
}
// static
ThemeProperties::PropertyLookupPair ThemeProperties::GetLookupID(int input_id) {
// Mapping of incognito property ids to their corresponding non-incognito
// property ids.
base::flat_map<int, int> incognito_property_map({
{ThemeProperties::COLOR_FRAME_INCOGNITO, ThemeProperties::COLOR_FRAME},
{ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_FRAME_INACTIVE},
{ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO,
ThemeProperties::COLOR_BACKGROUND_TAB},
{ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE},
{ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO,
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT},
{ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE,
ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INACTIVE},
{ThemeProperties::TINT_FRAME_INCOGNITO, ThemeProperties::TINT_FRAME},
{ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE,
ThemeProperties::TINT_FRAME_INACTIVE},
});
auto found_entry = incognito_property_map.find(input_id);
if (found_entry != incognito_property_map.end())
return {found_entry->second, true};
return {input_id, false};
}
...@@ -171,6 +171,14 @@ class ThemeProperties { ...@@ -171,6 +171,14 @@ class ThemeProperties {
#endif // OS_WIN #endif // OS_WIN
}; };
// Represents the lookup values for a theme property.
struct PropertyLookupPair {
int property_id; // ID of the property to lookup (should never be an
// incognito variant)
bool is_incognito; // Whether the lookup should use the incognito value
// of this property or not
};
// Themes are hardcoded to draw frame images as if they start this many DIPs // Themes are hardcoded to draw frame images as if they start this many DIPs
// above the top of the tabstrip, no matter how much space actually exists. // above the top of the tabstrip, no matter how much space actually exists.
// This aids with backwards compatibility (for some themes; Chrome's behavior // This aids with backwards compatibility (for some themes; Chrome's behavior
...@@ -204,6 +212,16 @@ class ThemeProperties { ...@@ -204,6 +212,16 @@ class ThemeProperties {
// Returns gfx::kPlaceholderColor if |id| is invalid. // Returns gfx::kPlaceholderColor if |id| is invalid.
static SkColor GetDefaultColor(int id, bool incognito); static SkColor GetDefaultColor(int id, bool incognito);
// Returns the default color for the color represented by |lookup_pair|
// Returns gfx::kPlaceholderColor if |id| is invalid.
static SkColor GetDefaultColor(PropertyLookupPair lookup_pair);
// Get the PropertyLookupPair necessary to look up a property for |input_id|
// in an incognito-aware context. Returns a pair with the id to lookup
// (always a non-incognito variant), and a boolean representing whether
// |input_id| was an incognito variant of the id to lookup
static PropertyLookupPair GetLookupID(int input_id);
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ThemeProperties); DISALLOW_IMPLICIT_CONSTRUCTORS(ThemeProperties);
}; };
......
{
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1P1I1EQSqVS1eIEFWGNSvEuKHSb1iJZMhIubqZF7cAEnZkF9BzTfvW46p3Nbr/2U4I7z7ndFK2nuEJPsA6p/GNJmIyqjTN7Pv9tjGUW0i5sDteB6ohCpuLrmBU9SjSdR9TI1lm6Q3N6+GcrYmL/Jm6he6CB7h0Fs1rSNO53z/9rB9J7tm9YmmsBi91J4PIrW+njhOIe+rtM2+xVTZBsvIreLFCsDTo3jlqdx5ZmaDBIlBCmp7JA7YSaBVgPVlYAKuV9g18ye25Y7bE62OyOHjhnBQAee2MXm+6T3cmCWQqOe62K2sZmtBwnlq08nxtb8pJSHaah16q6JJEqNPlimcwIDAQAB",
"manifest_version": 2,
"name": "tabcontrast",
"theme": {
"colors": {
"background_tab": [0, 0, 0],
"tab_background_text": [0,0,0],
"background_tab_inactive": [50,50,50],
"tab_background_text_inactive": [50,50,50],
"background_tab_incognito": [100,100,100],
"tab_background_text_incognito": [150,150,150],
"background_tab_incognito_inactive": [250,250,250],
"tab_background_text_incognito_inactive": [250,250,250]
}
},
"version": "0.0.1"
}
{
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZ6azhu+B5FfbcdldseZLMe73TPeTplm+fQ/gYWjsn3Ms40PtUDNHbpI+YPa8fmjml9cKa13BgDdGUXDBw9wx+QZ7nMOxlcmNYg8+GYp76Ee4Y/KTQss8kJ+rDJZMwZnEUL2ZxW0ejEjgbvtwpTOe4kqQokA1vggODMYD25KxqOp/pNUIqf1kuvFpQyEBpVifF4zRH7VV/OKyZKM3zp52JCr9rqKw+25LcrZTUj54XnnFwVdsXYwWTef9rYlefuB0vC62NqY2DstvhmJBdTXEievsMQO37zsbYaRksqwtsM7LbG7jJrCnAZgV14qRfKvlNciNU7vzelkvh0OdGPpqwIDAQAB",
"manifest_version": 2,
"name": "test_inherit_text_color",
"theme": {
"colors": {
"background_tab": [0, 0, 0],
"tab_background_text": [0,255,0],
"background_tab_inactive": [0,0,0],
"background_tab_incognito": [0,0,0],
"background_tab_incognito_inactive": [0,0,0]
}
},
"version": "0.0.1"
}
\ No newline at end of file
{
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2vnzjgsJi9KCBCQoY2mS8dBTk8qvEfsUGqjeJjXjoXwW01rJvuuYckCOaUJK/HIXl03iom1D8WZ4BlDRTKRmAlsRpllUmHsC7tDmcFB4+3rv1k5VGCmSH3FO1FoCSD1ZulE7pupq7Ifkwx9/NxfzYqsfkRjrr6P062Qtzaj5pVW3Ijv/tSEwTh8cXskFMERtjx+Bb86iqDddytuT/14ABSjMb3BuEaDj1bKhF+RrdNubMM9eliA7HAi0b1WXPVskXUeR1zGIOZYCqjpVfedaRbLtQ5Jk+zoTgkF9zH8LQpDwC2Ew5oaP/rnb7NGUowkT+l0uI+7rAet5tBKTn8HFJwIDAQAB",
"manifest_version": 2,
"name": "test_inherit_text_color_with_incog",
"theme": {
"colors": {
"background_tab": [0, 0, 0],
"tab_background_text": [0,255,0],
"background_tab_inactive": [0,0,0],
"background_tab_incognito": [0,0,0],
"tab_background_text_incognito": [0,0,255],
"background_tab_incognito_inactive": [0,0,0]
}
},
"version": "0.0.1"
}
\ No newline at end of file
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