0

Gtk3: More fixes and refactorings

This CL
* Uses entry colors instead of window colors for the NTP
* Fixes a leak involving ScopedGObject
* Simplifies how background and border colors are calculated
* Adds the ".label" class only on versions of Gtk before 3.20
* Fixes text selection colors on one theme
* Updates the doc to reflect the new way colors are calculated

BUG=132847

Review-Url: https://codereview.chromium.org/2683953005
Cr-Commit-Position: refs/heads/master@{#449709}
This commit is contained in:
thomasanderson
2017-02-10 12:05:48 -08:00
committed by Commit bot
parent b0e1c97b05
commit 419a91a17b
5 changed files with 235 additions and 224 deletions

@ -822,14 +822,15 @@ void GtkUi::LoadGtkValues() {
colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] = colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] =
color_utils::BlendTowardOppositeLuma(toolbar_text_color, 50); color_utils::BlendTowardOppositeLuma(toolbar_text_color, 50);
// Color drawn around the location bar. SkColor location_bar_border =
colors_[ThemeProperties::COLOR_LOCATION_BAR_BORDER] =
GetBorderColor("GtkToolbar#toolbar GtkEntry#entry"); GetBorderColor("GtkToolbar#toolbar GtkEntry#entry");
if (SkColorGetA(location_bar_border)) {
colors_[ThemeProperties::COLOR_LOCATION_BAR_BORDER] = location_bar_border;
}
inactive_selection_bg_color_ = inactive_selection_bg_color_ = GetSelectedBgColor("GtkEntry#entry:backdrop");
GetBgColor("GtkEntry#entry:backdrop #selection:selected");
inactive_selection_fg_color_ = inactive_selection_fg_color_ =
GetFgColor("GtkEntry#entry:backdrop #selection:selected"); GetSelectedTextColor("GtkEntry#entry:backdrop");
SkColor toolbar_separator_horizontal = SkColor toolbar_separator_horizontal =
GetSeparatorColor("GtkToolbar#toolbar GtkSeparator#separator.horizontal"); GetSeparatorColor("GtkToolbar#toolbar GtkSeparator#separator.horizontal");
@ -865,10 +866,8 @@ void GtkUi::LoadGtkValues() {
header_button_inactive_border; header_button_inactive_border;
} }
SkColor ntp_bg = GetBgColor(""); colors_[ThemeProperties::COLOR_NTP_BACKGROUND] = GetBgColor("GtkEntry#entry");
colors_[ThemeProperties::COLOR_NTP_BACKGROUND] = ntp_bg; colors_[ThemeProperties::COLOR_NTP_TEXT] = GetFgColor("GtkEntry#entry");
colors_[ThemeProperties::COLOR_NTP_TEXT] =
color_utils::GetReadableColor(GetFgColor("GtkLabel#label"), ntp_bg);
colors_[ThemeProperties::COLOR_NTP_HEADER] = colors_[ThemeProperties::COLOR_NTP_HEADER] =
GetBorderColor("GtkButton#button"); GetBorderColor("GtkButton#button");
#endif #endif
@ -876,13 +875,13 @@ void GtkUi::LoadGtkValues() {
colors_[ThemeProperties::COLOR_TOOLBAR] = toolbar_color; colors_[ThemeProperties::COLOR_TOOLBAR] = toolbar_color;
colors_[ThemeProperties::COLOR_CONTROL_BACKGROUND] = toolbar_color; colors_[ThemeProperties::COLOR_CONTROL_BACKGROUND] = toolbar_color;
colors_[ThemeProperties::COLOR_NTP_LINK] = colors_[ThemeProperties::COLOR_NTP_LINK] = native_theme_->GetSystemColor(
native_theme_->GetSystemColor(ui::NativeTheme::kColorId_LinkEnabled); ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused);
// Generate the colors that we pass to WebKit. // Generate the colors that we pass to WebKit.
SetScrollbarColors(); SetScrollbarColors();
focus_ring_color_ = native_theme_->GetSystemColor( focus_ring_color_ =
ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused); native_theme_->GetSystemColor(ui::NativeTheme::kColorId_LinkEnabled);
// Some GTK themes only define the text selection colors on the GtkEntry // Some GTK themes only define the text selection colors on the GtkEntry
// class, so we need to use that for getting selection colors. // class, so we need to use that for getting selection colors.

@ -220,49 +220,42 @@ CairoSurface::CairoSurface(const gfx::Size& size)
: surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
size.width(), size.width(),
size.height())), size.height())),
cairo_(cairo_create(surface_)) {} cairo_(cairo_create(surface_)) {
DCHECK(cairo_surface_status(surface_) == CAIRO_STATUS_SUCCESS);
// Clear the surface.
cairo_save(cairo_);
cairo_set_source_rgba(cairo_, 0, 0, 0, 0);
cairo_set_operator(cairo_, CAIRO_OPERATOR_SOURCE);
cairo_paint(cairo_);
cairo_restore(cairo_);
}
CairoSurface::~CairoSurface() { CairoSurface::~CairoSurface() {
cairo_destroy(cairo_); cairo_destroy(cairo_);
cairo_surface_destroy(surface_); cairo_surface_destroy(surface_);
} }
SkColor CairoSurface::GetAveragePixelValue(bool only_frame_pixels) { SkColor CairoSurface::GetAveragePixelValue(bool frame) {
cairo_surface_flush(surface_); cairo_surface_flush(surface_);
int num_samples = 0;
long a = 0, r = 0, g = 0, b = 0;
SkColor* data = SkColor* data =
reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_)); reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
int width = cairo_image_surface_get_width(surface_); int width = cairo_image_surface_get_width(surface_);
int height = cairo_image_surface_get_height(surface_); int height = cairo_image_surface_get_height(surface_);
DCHECK(4 * width == cairo_image_surface_get_stride(surface_)); DCHECK(4 * width == cairo_image_surface_get_stride(surface_));
auto accumulate = [&](int x, int y) mutable { long a = 0, r = 0, g = 0, b = 0;
SkColor color = data[y * width + x]; unsigned int max_alpha = 0;
int alpha = SkColorGetA(color); for (int i = 0; i < width * height; i++) {
a += alpha; SkColor color = data[i];
r += alpha * SkColorGetR(color); max_alpha = std::max(SkColorGetA(color), max_alpha);
g += alpha * SkColorGetG(color); a += SkColorGetA(color);
b += alpha * SkColorGetB(color); r += SkColorGetR(color);
num_samples++; g += SkColorGetG(color);
}; b += SkColorGetB(color);
if (width == 1 || height == 1 || !only_frame_pixels) {
// Count every pixel in the surface.
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
accumulate(x, y);
} else {
// Count the pixels in the top and bottom rows.
for (int x = 0; x < width; x++)
for (int y : {0, height - 1})
accumulate(x, y);
// Count the pixels in the left and right columns.
for (int x : {0, width - 1})
for (int y = 1; y < height - 1; y++)
accumulate(x, y);
} }
if (a == 0) if (a == 0)
return SK_ColorTRANSPARENT; return SK_ColorTRANSPARENT;
return SkColorSetARGB(a / num_samples, r / a, g / a, b / a); return SkColorSetARGB(frame ? max_alpha : a / (width * height), r * 255 / a,
g * 255 / a, b * 255 / a);
} }
bool GtkVersionCheck(int major, int minor, int micro) { bool GtkVersionCheck(int major, int minor, int micro) {
@ -336,14 +329,12 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
NOTREACHED(); NOTREACHED();
} }
} else { } else {
static auto* _gtk_widget_path_iter_set_object_name =
reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(dlsym(
GetGtk3SharedLibrary(), "gtk_widget_path_iter_set_object_name"));
switch (part_type) { switch (part_type) {
case CSS_NAME: { case CSS_NAME: {
if (GtkVersionCheck(3, 20)) { if (GtkVersionCheck(3, 20)) {
static auto* _gtk_widget_path_iter_set_object_name =
reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(
dlsym(GetGtk3SharedLibrary(),
"gtk_widget_path_iter_set_object_name"));
DCHECK(_gtk_widget_path_iter_set_object_name);
_gtk_widget_path_iter_set_object_name(path, -1, t.token().c_str()); _gtk_widget_path_iter_set_object_name(path, -1, t.token().c_str());
} else { } else {
gtk_widget_path_iter_add_class(path, -1, t.token().c_str()); gtk_widget_path_iter_add_class(path, -1, t.token().c_str());
@ -354,6 +345,10 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
GType type = g_type_from_name(t.token().c_str()); GType type = g_type_from_name(t.token().c_str());
DCHECK(type); DCHECK(type);
gtk_widget_path_append_type(path, type); gtk_widget_path_append_type(path, type);
if (GtkVersionCheck(3, 20)) {
if (t.token() == "GtkLabel")
_gtk_widget_path_iter_set_object_name(path, -1, "label");
}
break; break;
} }
case CSS_CLASS: { case CSS_CLASS: {
@ -379,7 +374,7 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
// widgets specially if they want to. // widgets specially if they want to.
gtk_widget_path_iter_add_class(path, -1, "chromium"); gtk_widget_path_iter_add_class(path, -1, "chromium");
auto child_context = ScopedStyleContext(gtk_style_context_new()); ScopedStyleContext child_context(gtk_style_context_new());
gtk_style_context_set_path(child_context, path); gtk_style_context_set_path(child_context, path);
gtk_style_context_set_state(child_context, state); gtk_style_context_set_state(child_context, state);
gtk_style_context_set_parent(child_context, context); gtk_style_context_set_parent(child_context, context);
@ -388,9 +383,10 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
} }
ScopedStyleContext GetStyleContextFromCss(const char* css_selector) { ScopedStyleContext GetStyleContextFromCss(const char* css_selector) {
// Prepend "GtkWindow.background" to the selector since all widgets must live // Prepend a window node to the selector since all widgets must live
// in a window, but we don't want to specify that every time. // in a window, but we don't want to specify that every time.
auto context = AppendCssNodeToStyleContext(nullptr, "GtkWindow.background"); auto context =
AppendCssNodeToStyleContext(nullptr, "GtkWindow#window.background");
for (const auto& widget_type : for (const auto& widget_type :
base::SplitString(css_selector, base::kWhitespaceASCII, base::SplitString(css_selector, base::kWhitespaceASCII,
@ -405,26 +401,46 @@ SkColor GdkRgbaToSkColor(const GdkRGBA& color) {
color.blue * 255); color.blue * 255);
} }
SkColor SkColorFromStyleContext(GtkStyleContext* context) { SkColor GetFgColorFromStyleContext(GtkStyleContext* context) {
GdkRGBA color; GdkRGBA color;
gtk_style_context_get_color(context, gtk_style_context_get_state(context), gtk_style_context_get_color(context, gtk_style_context_get_state(context),
&color); &color);
return GdkRgbaToSkColor(color); return GdkRgbaToSkColor(color);
} }
SkColor GetFgColor(const char* css_selector) { SkColor GetBgColorFromStyleContext(GtkStyleContext* context) {
return SkColorFromStyleContext(GetStyleContextFromCss(css_selector)); // Backgrounds are more general than solid colors (eg. gradients),
// but chromium requires us to boil this down to one color. We
// cannot use the background-color here because some themes leave it
// set to a garbage color because a background-image will cover it
// anyway. So we instead render the background into a 24x24 bitmap,
// removing any borders, and hope that we get a good color.
ApplyCssToContext(context,
"* {"
"border-radius: 0px;"
"border-style: none;"
"box-shadow: none;"
"}");
gfx::Size size(24, 24);
CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
return surface.GetAveragePixelValue(false);
} }
GtkCssProvider* GetCssProvider(const char* css) { SkColor GetFgColor(const char* css_selector) {
return GetFgColorFromStyleContext(GetStyleContextFromCss(css_selector));
}
ScopedCssProvider GetCssProvider(const char* css) {
GtkCssProvider* provider = gtk_css_provider_new(); GtkCssProvider* provider = gtk_css_provider_new();
GError* error = nullptr; GError* error = nullptr;
gtk_css_provider_load_from_data(provider, css, -1, &error); gtk_css_provider_load_from_data(provider, css, -1, &error);
DCHECK(!error); DCHECK(!error);
return provider; return ScopedCssProvider(provider);
} }
void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) { void ApplyCssProviderToContext(GtkStyleContext* context,
GtkCssProvider* provider) {
while (context) { while (context) {
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
G_MAXUINT); G_MAXUINT);
@ -432,35 +448,9 @@ void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) {
} }
} }
void RemoveBorders(GtkStyleContext* context) { void ApplyCssToContext(GtkStyleContext* context, const char* css) {
static GtkCssProvider* provider = GetCssProvider( auto provider = GetCssProvider(css);
"* {" ApplyCssProviderToContext(context, provider);
"border-style: none;"
"border-radius: 0px;"
"border-width: 0px;"
"border-image-width: 0px;"
"box-shadow: none;"
"padding: 0px;"
"margin: 0px;"
"outline: none;"
"outline-width: 0px;"
"}");
ApplyCssToContext(context, provider);
}
void AddBorders(GtkStyleContext* context) {
static GtkCssProvider* provider = GetCssProvider(
"* {"
"border-style: solid;"
"border-radius: 0px;"
"border-width: 1px;"
"box-shadow: none;"
"padding: 0px;"
"margin: 0px;"
"outline: none;"
"outline-width: 0px;"
"}");
ApplyCssToContext(context, provider);
} }
void RenderBackground(const gfx::Size& size, void RenderBackground(const gfx::Size& size,
@ -472,66 +462,78 @@ void RenderBackground(const gfx::Size& size,
gtk_render_background(context, cr, 0, 0, size.width(), size.height()); gtk_render_background(context, cr, 0, 0, size.width(), size.height());
} }
gfx::Size GetMinimumWidgetSize(GtkStyleContext* context) {
if (!GtkVersionCheck(3, 20))
return gfx::Size(1, 1);
int w = 1, h = 1;
gtk_style_context_get(context, gtk_style_context_get_state(context),
"min-width", &w, "min-height", &h, NULL);
DCHECK(w >= 0 && h >= 0);
return gfx::Size(w ? w : 1, h ? h : 1);
}
SkColor GetBgColor(const char* css_selector) { SkColor GetBgColor(const char* css_selector) {
// Backgrounds are more general than solid colors (eg. gradients), return GetBgColorFromStyleContext(GetStyleContextFromCss(css_selector));
// but chromium requires us to boil this down to one color. We
// cannot use the background-color here because some themes leave it
// set to a garbage color because a background-image will cover it
// anyway. So we instead render the background into a single pixel,
// removing any borders, and hope that we get a good color.
auto context = GetStyleContextFromCss(css_selector);
RemoveBorders(context);
gfx::Size size = GetMinimumWidgetSize(context);
CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
return surface.GetAveragePixelValue(false);
} }
SkColor GetBorderColor(const char* css_selector) { SkColor GetBorderColor(const char* css_selector) {
// Borders have the same issue as backgrounds, due to the // Borders have the same issue as backgrounds, due to the
// border-image property. // border-image property.
auto context = GetStyleContextFromCss(css_selector); auto context = GetStyleContextFromCss(css_selector);
GtkStateFlags state = gtk_style_context_get_state(context); gfx::Size size(24, 24);
GtkBorderStyle border_style = GTK_BORDER_STYLE_NONE;
gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_BORDER_STYLE,
&border_style, nullptr);
GtkBorder border;
gtk_style_context_get_border(context, state, &border);
if ((border_style == GTK_BORDER_STYLE_NONE ||
border_style == GTK_BORDER_STYLE_HIDDEN) ||
(!border.left && !border.right && !border.top && !border.bottom)) {
return SK_ColorTRANSPARENT;
}
AddBorders(context);
gfx::Size size = GetMinimumWidgetSize(context);
CairoSurface surface(size); CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
gtk_render_frame(context, surface.cairo(), 0, 0, size.width(), size.height()); gtk_render_frame(context, surface.cairo(), 0, 0, size.width(), size.height());
return surface.GetAveragePixelValue(true); return surface.GetAveragePixelValue(true);
} }
ScopedStyleContext GetSelectedStyleContext(const char* css_selector) {
auto context = GetStyleContextFromCss(css_selector);
if (GtkVersionCheck(3, 20)) {
context = AppendCssNodeToStyleContext(context, "#selection");
} else {
GtkStateFlags state = gtk_style_context_get_state(context);
state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_SELECTED);
gtk_style_context_set_state(context, state);
}
return context;
}
SkColor GetSelectedTextColor(const char* css_selector) {
return GetFgColorFromStyleContext(GetSelectedStyleContext(css_selector));
}
SkColor GetSelectedBgColor(const char* css_selector) {
auto context = GetSelectedStyleContext(css_selector);
if (GtkVersionCheck(3, 20))
return GetBgColorFromStyleContext(context);
// This is verbatim how Gtk gets the selection color on versions before 3.20.
GdkRGBA selection_color;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
gtk_style_context_get_background_color(
context, gtk_style_context_get_state(context), &selection_color);
G_GNUC_END_IGNORE_DEPRECATIONS;
return GdkRgbaToSkColor(selection_color);
}
SkColor GetSeparatorColor(const char* css_selector) { SkColor GetSeparatorColor(const char* css_selector) {
if (!GtkVersionCheck(3, 20)) if (!GtkVersionCheck(3, 20))
return GetFgColor(css_selector); return GetFgColor(css_selector);
// Some themes use borders to render separators, others use a auto context = GetStyleContextFromCss(css_selector);
// background color. Only return the border color if there is one. int w = 1, h = 1;
SkColor border = GetBorderColor(css_selector); gtk_style_context_get(context, gtk_style_context_get_state(context),
if (SkColorGetA(border)) "min-width", &w, "min-height", &h, nullptr);
return border; GtkBorder border, padding;
GtkStateFlags state = gtk_style_context_get_state(context);
gtk_style_context_get_border(context, state, &border);
gtk_style_context_get_padding(context, state, &padding);
w += border.left + padding.left + padding.right + border.right;
h += border.top + padding.top + padding.bottom + border.bottom;
return GetBgColor(css_selector); bool horizontal = gtk_style_context_has_class(context, "horizontal");
if (horizontal) {
w = 24;
h = std::max(h, 1);
} else {
DCHECK(gtk_style_context_has_class(context, "vertical"));
h = 24;
w = std::max(w, 1);
}
CairoSurface surface(gfx::Size(w, h));
gtk_render_background(context, surface.cairo(), 0, 0, w, h);
gtk_render_frame(context, surface.cairo(), 0, 0, w, h);
return surface.GetAveragePixelValue(false);
} }
#endif #endif

@ -98,10 +98,10 @@ class CairoSurface {
// Get the drawing context for GTK to use. // Get the drawing context for GTK to use.
cairo_t* cairo() { return cairo_; } cairo_t* cairo() { return cairo_; }
// If |only_frame_pixels| is false, returns the average of all // Returns the average of all pixels in the surface. If |frame| is
// pixels in the surface, otherwise returns the average of only the // true, the resulting alpha will be the average alpha, otherwise it
// edge pixels. // will be the max alpha across all pixels.
SkColor GetAveragePixelValue(bool only_frame_pixels); SkColor GetAveragePixelValue(bool frame);
private: private:
cairo_surface_t* surface_; cairo_surface_t* surface_;
@ -112,18 +112,18 @@ class CairoSurface {
// |major|.|minor|.|micro|. // |major|.|minor|.|micro|.
bool GtkVersionCheck(int major, int minor = 0, int micro = 0); bool GtkVersionCheck(int major, int minor = 0, int micro = 0);
// Similar in spirit to a std::unique_ptr.
template <typename T> template <typename T>
class ScopedGObject { class ScopedGObject {
public: public:
explicit ScopedGObject(T* obj) : obj_(obj) { explicit ScopedGObject(T* obj) : obj_(obj) {
// Increase the reference count of |obj_|, removing the floating // Remove the floating reference from |obj_| if it has one.
// reference if it has one. if (g_object_is_floating(obj_))
g_object_ref_sink(obj_); g_object_ref_sink(obj_);
DCHECK(G_OBJECT(obj_)->ref_count == 1);
} }
ScopedGObject(const ScopedGObject<T>& other) : obj_(other.obj_) { ScopedGObject(const ScopedGObject<T>& other) = delete;
g_object_ref(obj_);
}
ScopedGObject(ScopedGObject<T>&& other) : obj_(other.obj_) { ScopedGObject(ScopedGObject<T>&& other) : obj_(other.obj_) {
other.obj_ = nullptr; other.obj_ = nullptr;
@ -131,15 +131,10 @@ class ScopedGObject {
~ScopedGObject() { ~ScopedGObject() {
if (obj_) if (obj_)
g_object_unref(obj_); Unref();
} }
ScopedGObject<T>& operator=(const ScopedGObject<T>& other) { ScopedGObject<T>& operator=(const ScopedGObject<T>& other) = delete;
g_object_ref(other.obj_);
g_object_unref(obj_);
obj_ = other.obj_;
return *this;
}
ScopedGObject<T>& operator=(ScopedGObject<T>&& other) { ScopedGObject<T>& operator=(ScopedGObject<T>&& other) {
g_object_unref(obj_); g_object_unref(obj_);
@ -151,10 +146,35 @@ class ScopedGObject {
operator T*() { return obj_; } operator T*() { return obj_; }
private: private:
void Unref() { g_object_unref(obj_); }
T* obj_; T* obj_;
}; };
template <>
inline void ScopedGObject<GtkStyleContext>::Unref() {
// Versions of GTK earlier than 3.15.4 had a bug where a g_assert
// would be triggered when trying to free a GtkStyleContext that had
// a parent whose only reference was the child context in question.
// This is a hack to work around that case. See GTK commit
// "gtkstylecontext: Don't try to emit a signal when finalizing".
GtkStyleContext* context = obj_;
while (context) {
GtkStyleContext* parent = gtk_style_context_get_parent(context);
if (parent && G_OBJECT(context)->ref_count == 1 &&
!GtkVersionCheck(3, 15, 4)) {
g_object_ref(parent);
gtk_style_context_set_parent(context, nullptr);
} else {
g_object_unref(context);
return;
}
context = parent;
}
}
typedef ScopedGObject<GtkStyleContext> ScopedStyleContext; typedef ScopedGObject<GtkStyleContext> ScopedStyleContext;
typedef ScopedGObject<GtkCssProvider> ScopedCssProvider;
// If |context| is NULL, creates a new top-level style context // If |context| is NULL, creates a new top-level style context
// specified by parsing |css_node|. Otherwise, creates the child // specified by parsing |css_node|. Otherwise, creates the child
@ -170,10 +190,11 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
// must g_object_unref() the returned context. // must g_object_unref() the returned context.
ScopedStyleContext GetStyleContextFromCss(const char* css_selector); ScopedStyleContext GetStyleContextFromCss(const char* css_selector);
SkColor SkColorFromStyleContext(GtkStyleContext* context); SkColor GetFgColorFromStyleContext(GtkStyleContext* context);
// Removes all border-type properties on |context| and all of its parents. // Overrides properties on |context| and all its parents with those
void RemoveBorders(GtkStyleContext* context); // provided by |css|.
void ApplyCssToContext(GtkStyleContext* context, const char* css);
// Get the 'color' property from the style context created by // Get the 'color' property from the style context created by
// GetStyleContextFromCss(|css_selector|). // GetStyleContextFromCss(|css_selector|).
@ -186,15 +207,18 @@ void RenderBackground(const gfx::Size& size,
GtkStyleContext* context); GtkStyleContext* context);
// Renders a background from the style context created by // Renders a background from the style context created by
// GetStyleContextFromCss(|css_selector|) into a single pixel and // GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and
// returns the color. // returns the average color.
SkColor GetBgColor(const char* css_selector); SkColor GetBgColor(const char* css_selector);
// If there is a border, renders the border from the style context // Renders the border from the style context created by
// created by GetStyleContextFromCss(|css_selector|) into a single // GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and
// pixel and returns the color. Otherwise returns kInvalidColor. // returns the average color.
SkColor GetBorderColor(const char* css_selector); SkColor GetBorderColor(const char* css_selector);
SkColor GetSelectedTextColor(const char* css_selector);
SkColor GetSelectedBgColor(const char* css_selector);
// Get the color of the GtkSeparator specified by |css_selector|. // Get the color of the GtkSeparator specified by |css_selector|.
SkColor GetSeparatorColor(const char* css_selector); SkColor GetSeparatorColor(const char* css_selector);
#endif #endif

@ -106,35 +106,35 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
case ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor: case ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor:
return GetBgColor("GtkMenu#menu GtkMenuItem#menuitem:hover"); return GetBgColor("GtkMenu#menu GtkMenuItem#menuitem:hover");
case ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor: case ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor:
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem GtkLabel#label"); return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem GtkLabel");
case ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor: case ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor:
return GetFgColor( return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem:hover GtkLabel");
"GtkMenu#menu GtkMenuItem#menuitem:hover GtkLabel#label");
case ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor: case ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor:
return GetFgColor( return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem:disabled GtkLabel");
"GtkMenu#menu GtkMenuItem#menuitem:disabled GtkLabel#label");
case ui::NativeTheme::kColorId_MenuItemSubtitleColor: case ui::NativeTheme::kColorId_MenuItemSubtitleColor:
return GetFgColor( return GetFgColor(
"GtkMenu#menu GtkMenuItem#menuitem GtkLabel#label.accelerator"); "GtkMenu#menu GtkMenuItem#menuitem GtkLabel.accelerator");
case ui::NativeTheme::kColorId_MenuSeparatorColor: case ui::NativeTheme::kColorId_MenuSeparatorColor:
// MenuButton borders are used as vertical menu separators in Chrome. // MenuButton borders are used as vertical menu separators in Chrome.
case ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor: case ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor:
case ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor: case ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor:
case ui::NativeTheme::kColorId_HoverMenuButtonBorderColor: case ui::NativeTheme::kColorId_HoverMenuButtonBorderColor:
if (GtkVersionCheck(3, 20)) if (GtkVersionCheck(3, 20)) {
return GetBgColor("GtkMenu#menu GtkSeparator#separator"); return GetSeparatorColor(
else "GtkMenu#menu GtkSeparator#separator.horizontal");
} else {
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem.separator"); return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem.separator");
}
// Label // Label
case ui::NativeTheme::kColorId_LabelEnabledColor: case ui::NativeTheme::kColorId_LabelEnabledColor:
return GetFgColor("GtkLabel#label"); return GetFgColor("GtkLabel");
case ui::NativeTheme::kColorId_LabelDisabledColor: case ui::NativeTheme::kColorId_LabelDisabledColor:
return GetFgColor("GtkLabel#label:disabled"); return GetFgColor("GtkLabel:disabled");
case ui::NativeTheme::kColorId_LabelTextSelectionColor: case ui::NativeTheme::kColorId_LabelTextSelectionColor:
return GetFgColor("GtkLabel#label #selection:selected"); return GetSelectedTextColor("GtkLabel");
case ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused: case ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused:
return GetBgColor("GtkLabel#label #selection:selected"); return GetSelectedBgColor("GtkLabel");
// Link // Link
case ui::NativeTheme::kColorId_LinkDisabled: case ui::NativeTheme::kColorId_LinkDisabled:
@ -142,13 +142,13 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
SkColorFromColorId(ui::NativeTheme::kColorId_LinkEnabled), 0xBB); SkColorFromColorId(ui::NativeTheme::kColorId_LinkEnabled), 0xBB);
case ui::NativeTheme::kColorId_LinkPressed: case ui::NativeTheme::kColorId_LinkPressed:
if (GtkVersionCheck(3, 12)) if (GtkVersionCheck(3, 12))
return GetFgColor("GtkLabel#label.link:link:hover:active"); return GetFgColor("GtkLabel.link:link:hover:active");
// fallthrough // fallthrough
case ui::NativeTheme::kColorId_LinkEnabled: { case ui::NativeTheme::kColorId_LinkEnabled: {
if (GtkVersionCheck(3, 12)) { if (GtkVersionCheck(3, 12)) {
return GetFgColor("GtkLabel#label.link:link"); return GetFgColor("GtkLabel.link:link");
} }
auto link_context = GetStyleContextFromCss("GtkLabel#label.view"); auto link_context = GetStyleContextFromCss("GtkLabel.view");
GdkColor* color; GdkColor* color;
gtk_style_context_get_style(link_context, "link-color", &color, nullptr); gtk_style_context_get_style(link_context, "link-color", &color, nullptr);
if (color) { if (color) {
@ -173,28 +173,28 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
// Button // Button
case ui::NativeTheme::kColorId_ButtonEnabledColor: case ui::NativeTheme::kColorId_ButtonEnabledColor:
return GetFgColor("GtkButton#button.text-button GtkLabel#label"); return GetFgColor("GtkButton#button.text-button GtkLabel");
case ui::NativeTheme::kColorId_ButtonDisabledColor: case ui::NativeTheme::kColorId_ButtonDisabledColor:
return GetFgColor("GtkButton#button.text-button:disabled GtkLabel#label"); return GetFgColor("GtkButton#button.text-button:disabled GtkLabel");
case ui::NativeTheme::kColorId_ButtonHoverColor: case ui::NativeTheme::kColorId_ButtonHoverColor:
return GetFgColor("GtkButton#button.text-button:hover GtkLabel#label"); return GetFgColor("GtkButton#button.text-button:hover GtkLabel");
case ui::NativeTheme::kColorId_ButtonPressedShade: case ui::NativeTheme::kColorId_ButtonPressedShade:
return SK_ColorTRANSPARENT; return SK_ColorTRANSPARENT;
case ui::NativeTheme::kColorId_BlueButtonEnabledColor: case ui::NativeTheme::kColorId_BlueButtonEnabledColor:
return GetFgColor( return GetFgColor(
"GtkButton#button.text-button.suggested-action GtkLabel#label"); "GtkButton#button.text-button.suggested-action GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonDisabledColor: case ui::NativeTheme::kColorId_BlueButtonDisabledColor:
return GetFgColor( return GetFgColor(
"GtkButton#button.text-button.suggested-action:disabled " "GtkButton#button.text-button.suggested-action:disabled "
"GtkLabel#label"); "GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonHoverColor: case ui::NativeTheme::kColorId_BlueButtonHoverColor:
return GetFgColor( return GetFgColor(
"GtkButton#button.text-button.suggested-action:hover GtkLabel#label"); "GtkButton#button.text-button.suggested-action:hover GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonPressedColor: case ui::NativeTheme::kColorId_BlueButtonPressedColor:
return GetFgColor( return GetFgColor(
"GtkButton#button.text-button.suggested-action:hover:active " "GtkButton#button.text-button.suggested-action:hover:active "
"GtkLabel#label"); "GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonShadowColor: case ui::NativeTheme::kColorId_BlueButtonShadowColor:
return SK_ColorTRANSPARENT; return SK_ColorTRANSPARENT;
@ -202,7 +202,7 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
return GetBgColor("GtkButton#button.text-button.destructive-action"); return GetBgColor("GtkButton#button.text-button.destructive-action");
case ui::NativeTheme::kColorId_TextOnProminentButtonColor: case ui::NativeTheme::kColorId_TextOnProminentButtonColor:
return GetFgColor( return GetFgColor(
"GtkButton#button.text-button.destructive-action GtkLabel#label"); "GtkButton#button.text-button.destructive-action GtkLabel");
// Textfield // Textfield
case ui::NativeTheme::kColorId_TextfieldDefaultColor: case ui::NativeTheme::kColorId_TextfieldDefaultColor:
@ -214,9 +214,9 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
case ui::NativeTheme::kColorId_TextfieldReadOnlyBackground: case ui::NativeTheme::kColorId_TextfieldReadOnlyBackground:
return GetBgColor("GtkEntry#entry:disabled"); return GetBgColor("GtkEntry#entry:disabled");
case ui::NativeTheme::kColorId_TextfieldSelectionColor: case ui::NativeTheme::kColorId_TextfieldSelectionColor:
return GetFgColor("GtkEntry#entry #selection:selected"); return GetSelectedTextColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused: case ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused:
return GetBgColor("GtkEntry#entry #selection:selected"); return GetSelectedBgColor("GtkEntry#entry");
// Tooltips // Tooltips
case ui::NativeTheme::kColorId_TooltipBackground: case ui::NativeTheme::kColorId_TooltipBackground:
@ -233,13 +233,13 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
case ui::NativeTheme::kColorId_TreeText: case ui::NativeTheme::kColorId_TreeText:
case ui::NativeTheme::kColorId_TreeArrow: case ui::NativeTheme::kColorId_TreeArrow:
case ui::NativeTheme::kColorId_TableGroupingIndicatorColor: case ui::NativeTheme::kColorId_TableGroupingIndicatorColor:
return GetFgColor("GtkTreeView#treeview.view .view.cell GtkLabel#label"); return GetFgColor("GtkTreeView#treeview.view .view.cell GtkLabel");
case ui::NativeTheme::kColorId_TableSelectedText: case ui::NativeTheme::kColorId_TableSelectedText:
case ui::NativeTheme::kColorId_TableSelectedTextUnfocused: case ui::NativeTheme::kColorId_TableSelectedTextUnfocused:
case ui::NativeTheme::kColorId_TreeSelectedText: case ui::NativeTheme::kColorId_TreeSelectedText:
case ui::NativeTheme::kColorId_TreeSelectedTextUnfocused: case ui::NativeTheme::kColorId_TreeSelectedTextUnfocused:
return GetFgColor( return GetFgColor(
"GtkTreeView#treeview.view .view.cell:selected:focus GtkLabel#label"); "GtkTreeView#treeview.view .view.cell:selected:focus GtkLabel");
case ui::NativeTheme::kColorId_TableSelectionBackgroundFocused: case ui::NativeTheme::kColorId_TableSelectionBackgroundFocused:
case ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused: case ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused:
case ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused: case ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused:
@ -248,43 +248,39 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
// Table Header // Table Header
case ui::NativeTheme::kColorId_TableHeaderText: case ui::NativeTheme::kColorId_TableHeaderText:
return GetFgColor( return GetFgColor("GtkTreeView#treeview.view GtkButton#button GtkLabel");
"GtkTreeView#treeview.view GtkButton#button GtkLabel#label");
case ui::NativeTheme::kColorId_TableHeaderBackground: case ui::NativeTheme::kColorId_TableHeaderBackground:
return GetBgColor("GtkTreeView#treeview.view GtkButton#button"); return GetBgColor("GtkTreeView#treeview.view GtkButton#button");
case ui::NativeTheme::kColorId_TableHeaderSeparator: case ui::NativeTheme::kColorId_TableHeaderSeparator:
return GetBorderColor("GtkTreeView#treeview.view GtkButton#button"); return GetBorderColor("GtkTreeView#treeview.view GtkButton#button");
// Results Table // Results Table
// TODO(thomasanderson): The GtkEntry selectors was how the gtk2 theme got
// these colors. Update this code to use a different widget.
case ui::NativeTheme::kColorId_ResultsTableNormalBackground: case ui::NativeTheme::kColorId_ResultsTableNormalBackground:
return GetBgColor("GtkEntry#entry"); return GetBgColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableHoveredBackground: case ui::NativeTheme::kColorId_ResultsTableHoveredBackground:
return color_utils::AlphaBlend( return color_utils::AlphaBlend(GetBgColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry"), GetSelectedBgColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry #selection:selected"), 0x80); 0x80);
case ui::NativeTheme::kColorId_ResultsTableSelectedBackground: case ui::NativeTheme::kColorId_ResultsTableSelectedBackground:
return GetBgColor("GtkEntry#entry #selection:selected"); return GetSelectedBgColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableNormalText: case ui::NativeTheme::kColorId_ResultsTableNormalText:
case ui::NativeTheme::kColorId_ResultsTableHoveredText: case ui::NativeTheme::kColorId_ResultsTableHoveredText:
return GetFgColor("GtkEntry#entry"); return GetFgColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableSelectedText: case ui::NativeTheme::kColorId_ResultsTableSelectedText:
return GetFgColor("GtkEntry#entry #selection:selected"); return GetSelectedTextColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableNormalDimmedText: case ui::NativeTheme::kColorId_ResultsTableNormalDimmedText:
case ui::NativeTheme::kColorId_ResultsTableHoveredDimmedText: case ui::NativeTheme::kColorId_ResultsTableHoveredDimmedText:
return color_utils::AlphaBlend(GetFgColor("GtkEntry#entry"), return color_utils::AlphaBlend(GetFgColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry"), 0x80); GetBgColor("GtkEntry#entry"), 0x80);
case ui::NativeTheme::kColorId_ResultsTableSelectedDimmedText: case ui::NativeTheme::kColorId_ResultsTableSelectedDimmedText:
return color_utils::AlphaBlend( return color_utils::AlphaBlend(GetSelectedTextColor("GtkEntry#entry"),
GetFgColor("GtkEntry#entry #selection:selected"), GetBgColor("GtkEntry#entry"), 0x80);
GetBgColor("GtkEntry#entry"), 0x80);
case ui::NativeTheme::kColorId_ResultsTableNormalUrl: case ui::NativeTheme::kColorId_ResultsTableNormalUrl:
case ui::NativeTheme::kColorId_ResultsTableHoveredUrl: case ui::NativeTheme::kColorId_ResultsTableHoveredUrl:
return NormalURLColor(GetFgColor("GtkEntry#entry")); return NormalURLColor(GetFgColor("GtkEntry#entry"));
case ui::NativeTheme::kColorId_ResultsTableSelectedUrl: case ui::NativeTheme::kColorId_ResultsTableSelectedUrl:
return SelectedURLColor(GetFgColor("GtkEntry#entry #selection:selected"), return SelectedURLColor(GetSelectedTextColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry #selection:selected")); GetSelectedBgColor("GtkEntry#entry"));
case ui::NativeTheme::kColorId_ResultsTablePositiveText: case ui::NativeTheme::kColorId_ResultsTablePositiveText:
return color_utils::GetReadableColor(kPositiveTextColor, return color_utils::GetReadableColor(kPositiveTextColor,
@ -413,7 +409,7 @@ void NativeThemeGtk3::PaintArrowButton(SkCanvas* canvas,
} }
PaintWidget(canvas, rect, context, BG_RENDER_NORMAL, true); PaintWidget(canvas, rect, context, BG_RENDER_NORMAL, true);
PaintArrow(canvas, rect, direction, SkColorFromStyleContext(context)); PaintArrow(canvas, rect, direction, GetFgColorFromStyleContext(context));
} }
void NativeThemeGtk3::PaintScrollbarTrack( void NativeThemeGtk3::PaintScrollbarTrack(
@ -483,28 +479,29 @@ void NativeThemeGtk3::PaintMenuSeparator(
case ui::UPPER_SEPARATOR: case ui::UPPER_SEPARATOR:
return 0; return 0;
default: default:
return rect.height() / 2; return (rect.height() - separator_thickness) / 2;
} }
}; };
if (GtkVersionCheck(3, 20)) { if (GtkVersionCheck(3, 20)) {
auto context = auto context = GetStyleContextFromCss(
GetStyleContextFromCss("GtkMenu#menu GtkSeparator#separator"); "GtkMenu#menu GtkSeparator#separator.horizontal");
GtkBorder margin, border, padding; GtkBorder margin, border, padding;
GtkStateFlags state = gtk_style_context_get_state(context); GtkStateFlags state = gtk_style_context_get_state(context);
gtk_style_context_get_margin(context, state, &margin); gtk_style_context_get_margin(context, state, &margin);
gtk_style_context_get_border(context, state, &border); gtk_style_context_get_border(context, state, &border);
gtk_style_context_get_padding(context, state, &padding); gtk_style_context_get_padding(context, state, &padding);
int min_height = 0; int min_height = 1;
gtk_style_context_get(context, state, "min-height", &min_height, NULL); gtk_style_context_get(context, state, "min-height", &min_height, NULL);
int w = rect.width() - margin.left - margin.right; int w = rect.width() - margin.left - margin.right;
int h = int h = std::max(
min_height + padding.top + padding.bottom + border.top + border.bottom; min_height + padding.top + padding.bottom + border.top + border.bottom,
1);
int x = margin.left; int x = margin.left;
int y = separator_offset(h); int y = separator_offset(h);
PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NORMAL, true); PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NORMAL, true);
} else { } else {
auto context = auto context = GetStyleContextFromCss(
GetStyleContextFromCss("GtkMenu#menu GtkMenuItem#menuitem.separator"); "GtkMenu#menu GtkMenuItem#menuitem.separator.horizontal");
gboolean wide_separators = false; gboolean wide_separators = false;
gint separator_height = 0; gint separator_height = 0;
gtk_style_context_get_style(context, "wide-separators", &wide_separators, gtk_style_context_get_style(context, "wide-separators", &wide_separators,
@ -522,7 +519,7 @@ void NativeThemeGtk3::PaintMenuSeparator(
PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NONE, true); PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NONE, true);
} else { } else {
SkPaint paint; SkPaint paint;
paint.setColor(SkColorFromStyleContext(context)); paint.setColor(GetFgColorFromStyleContext(context));
canvas->drawLine(x, y, x + w, y, paint); canvas->drawLine(x, y, x + w, y, paint);
} }
} }
@ -536,7 +533,7 @@ void NativeThemeGtk3::PaintFrameTopArea(
auto context = GetStyleContextFromCss(frame_top_area.use_custom_frame auto context = GetStyleContextFromCss(frame_top_area.use_custom_frame
? "#headerbar.header-bar.titlebar" ? "#headerbar.header-bar.titlebar"
: "GtkMenuBar#menubar"); : "GtkMenuBar#menubar");
RemoveBorders(context); ApplyCssToContext(context, "* { border-radius: 0px; border-style: none; }");
gtk_style_context_set_state(context, frame_top_area.is_active gtk_style_context_set_state(context, frame_top_area.is_active
? GTK_STATE_FLAG_NORMAL ? GTK_STATE_FLAG_NORMAL
: GTK_STATE_FLAG_BACKDROP); : GTK_STATE_FLAG_BACKDROP);

@ -16,24 +16,18 @@ mostly a list of about 80 colors (see //src/ui/native_theme/native_theme.h)
overridden by the theme. Chromium usually doesn't use GTK to render entire overridden by the theme. Chromium usually doesn't use GTK to render entire
widgets, but instead tries to determine colors from them. widgets, but instead tries to determine colors from them.
There are three types of colors Chromium needs from widgets: Chromium needs foreground, background and border colors from widgets. The
foreground color is simply taken from the CSS "color" property. Backgrounds and
* Foreground color: determined by the CSS "color" property borders are complicated because in general they might have multiple gradients or
* Background color: determined by the CSS "background-color" and "background-image" properties images. To get the color, Chromium uses GTK to render the background or border
* Border color: determined by the "border-color", "border-image", into a 24x24 bitmap and uses the average color for theming. This mostly gives
"border-style", and "border-width" properties reasonable results, but in case theme authors do not like the resulting color,
they have the option to theme Chromium widgets specially.
Backgrounds and borders are complicated because in general they might have
multiple gradients or images. To get the color, Chromium uses GTK to render the
background or border into a single pixel and uses the resulting color for
theming. This mostly gives reasonable results, but in case theme authors do not
like the resulting color, they have the option to theme Chromium widgets
specially.
## Note to GTK theme authors: How to theme Chromium widgets ## Note to GTK theme authors: How to theme Chromium widgets
Every widget Chromium uses will have a "chromium" style class added to it. For Every widget Chromium uses will have a "chromium" style class added to it. For
example, a texfield selector might look like: example, a textfield selector might look like:
``` ```
.window.background.chromium .entry.chromium .window.background.chromium .entry.chromium
@ -59,11 +53,6 @@ they might use:
For GTK3.20 or later, themes will as usual have to replace ".entry" with For GTK3.20 or later, themes will as usual have to replace ".entry" with
"entry". "entry".
Additional requirements for border colors to be picked up:
* Must have a border-style that is not none.
* Must have a border-width that is nonzero.
The list of CSS selectors that Chromium uses to determine its colors is in The list of CSS selectors that Chromium uses to determine its colors is in
//src/chrome/browser/ui/libgtkui/native_theme_gtk3.cc. //src/chrome/browser/ui/libgtkui/native_theme_gtk3.cc.