| Index: chrome/browser/ui/libgtkui/gtk_util.cc
|
| diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
|
| index aab971785907c7b330a0fd56d43f49e7cde63823..caed0237fab0b4900d9c69d4db49dfc7998aadcd 100644
|
| --- a/chrome/browser/ui/libgtkui/gtk_util.cc
|
| +++ b/chrome/browser/ui/libgtkui/gtk_util.cc
|
| @@ -14,6 +14,9 @@
|
| #include "base/command_line.h"
|
| #include "base/debug/leak_annotations.h"
|
| #include "base/environment.h"
|
| +#include "base/strings/string_split.h"
|
| +#include "base/strings/string_tokenizer.h"
|
| +#include "base/strings/string_util.h"
|
| #include "ui/aura/window.h"
|
| #include "ui/aura/window_tree_host.h"
|
| #include "ui/base/accelerators/accelerator.h"
|
| @@ -188,4 +191,218 @@ void ClearAuraTransientParent(GtkWidget* dialog) {
|
| g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, NULL);
|
| }
|
|
|
| +#if GTK_MAJOR_VERSION > 2
|
| +ScopedStyleContext AppendNode(GtkStyleContext* context,
|
| + const std::string& css_node) {
|
| + GtkWidgetPath* path =
|
| + context ? gtk_widget_path_copy(gtk_style_context_get_path(context))
|
| + : gtk_widget_path_new();
|
| +
|
| + enum {
|
| + // TODO(thomasanderson): Add CSS_NAME here to handle the Gtk3.20 case.
|
| + CSS_TYPE,
|
| + CSS_CLASS,
|
| + CSS_PSEUDOCLASS,
|
| + } part_type = CSS_TYPE;
|
| + static const struct {
|
| + const char* name;
|
| + GtkStateFlags state_flag;
|
| + } pseudo_classes[] = {
|
| + {"active", GTK_STATE_FLAG_ACTIVE},
|
| + {"hover", GTK_STATE_FLAG_PRELIGHT},
|
| + {"selected", GTK_STATE_FLAG_SELECTED},
|
| + {"disabled", GTK_STATE_FLAG_INSENSITIVE},
|
| + {"indeterminate", GTK_STATE_FLAG_INCONSISTENT},
|
| + {"focus", GTK_STATE_FLAG_FOCUSED},
|
| + {"backdrop", GTK_STATE_FLAG_BACKDROP},
|
| + // TODO(thomasanderson): These state flags are only available in
|
| + // GTK 3.10 or later, which is unavailable in the wheezy
|
| + // sysroot. Add them once the sysroot is updated to jessie.
|
| + // { "link", GTK_STATE_FLAG_LINK },
|
| + // { "visited", GTK_STATE_FLAG_VISITED },
|
| + // { "checked", GTK_STATE_FLAG_CHECKED },
|
| + };
|
| + GtkStateFlags state =
|
| + context ? gtk_style_context_get_state(context) : GTK_STATE_FLAG_NORMAL;
|
| + base::StringTokenizer t(css_node, ".:");
|
| + t.set_options(base::StringTokenizer::RETURN_DELIMS);
|
| + while (t.GetNext()) {
|
| + if (t.token_is_delim()) {
|
| + if (t.token_begin() == css_node.begin()) {
|
| + // Special case for the first token.
|
| + gtk_widget_path_append_type(path, G_TYPE_NONE);
|
| + }
|
| + switch (*t.token_begin()) {
|
| + case '.':
|
| + part_type = CSS_CLASS;
|
| + break;
|
| + case ':':
|
| + part_type = CSS_PSEUDOCLASS;
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + } else {
|
| + switch (part_type) {
|
| + case CSS_TYPE: {
|
| + GType type = g_type_from_name(t.token().c_str());
|
| + DCHECK(type);
|
| + gtk_widget_path_append_type(path, type);
|
| + break;
|
| + }
|
| + case CSS_CLASS: {
|
| + gtk_widget_path_iter_add_class(path, -1, t.token().c_str());
|
| + break;
|
| + }
|
| + case CSS_PSEUDOCLASS: {
|
| + GtkStateFlags state_flag = GTK_STATE_FLAG_NORMAL;
|
| + for (const auto& pseudo_class_entry : pseudo_classes) {
|
| + if (strcmp(pseudo_class_entry.name, t.token().c_str()) == 0) {
|
| + state_flag = pseudo_class_entry.state_flag;
|
| + break;
|
| + }
|
| + }
|
| + state = static_cast<GtkStateFlags>(state | state_flag);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + auto child_context = ScopedStyleContext(gtk_style_context_new());
|
| + gtk_style_context_set_path(child_context, path);
|
| + gtk_style_context_set_state(child_context, state);
|
| + gtk_style_context_set_parent(child_context, context);
|
| + gtk_widget_path_unref(path);
|
| + return child_context;
|
| +}
|
| +
|
| +ScopedStyleContext GetStyleContextFromCss(const char* css_selector) {
|
| + // Prepend "GtkWindow.background" to the selector since all widgets must live
|
| + // in a window, but we don't want to specify that every time.
|
| + auto context = AppendNode(nullptr, "GtkWindow.background");
|
| +
|
| + for (const auto& widget_type :
|
| + base::SplitString(css_selector, base::kWhitespaceASCII,
|
| + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
|
| + context = AppendNode(context, widget_type);
|
| + }
|
| + return context;
|
| +}
|
| +
|
| +SkColor GdkRgbaToSkColor(const GdkRGBA& color) {
|
| + return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255,
|
| + color.blue * 255);
|
| +}
|
| +
|
| +SkColor GetFGColor(const char* css_selector) {
|
| + auto context = GetStyleContextFromCss(css_selector);
|
| + GdkRGBA color;
|
| + gtk_style_context_get_color(context, gtk_style_context_get_state(context),
|
| + &color);
|
| + return GdkRgbaToSkColor(color);
|
| +}
|
| +
|
| +GtkCssProvider* GetCssProvider(const char* css) {
|
| + GtkCssProvider* provider = gtk_css_provider_new();
|
| + GError* error = nullptr;
|
| + gtk_css_provider_load_from_data(provider, css, -1, &error);
|
| + DCHECK(!error);
|
| + return provider;
|
| +}
|
| +
|
| +void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) {
|
| + while (context) {
|
| + gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
|
| + G_MAXUINT);
|
| + context = gtk_style_context_get_parent(context);
|
| + }
|
| +}
|
| +
|
| +void RemoveBorders(GtkStyleContext* context) {
|
| + static GtkCssProvider* provider = GetCssProvider(
|
| + "* {"
|
| + "border-style: none;"
|
| + "border-radius: 0px;"
|
| + "border-width: 0px;"
|
| + "border-image-width: 0px;"
|
| + "padding: 0px;"
|
| + "margin: 0px;"
|
| + "}");
|
| + ApplyCssToContext(context, provider);
|
| +}
|
| +
|
| +void AddBorders(GtkStyleContext* context) {
|
| + static GtkCssProvider* provider = GetCssProvider(
|
| + "* {"
|
| + "border-style: solid;"
|
| + "border-radius: 0px;"
|
| + "border-width: 1px;"
|
| + "padding: 0px;"
|
| + "margin: 0px;"
|
| + "}");
|
| + ApplyCssToContext(context, provider);
|
| +}
|
| +
|
| +// A 1x1 cairo surface that GTK can render into.
|
| +class PixelSurface {
|
| + public:
|
| + PixelSurface()
|
| + : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1)),
|
| + cairo_(cairo_create(surface_)) {}
|
| +
|
| + ~PixelSurface() {
|
| + cairo_destroy(cairo_);
|
| + cairo_surface_destroy(surface_);
|
| + }
|
| +
|
| + // Get the drawing context for GTK to use.
|
| + cairo_t* cairo() { return cairo_; }
|
| +
|
| + // Get the color value of the single pixel.
|
| + SkColor GetPixelValue() {
|
| + return *reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
|
| + }
|
| +
|
| + private:
|
| + cairo_surface_t* surface_;
|
| + cairo_t* cairo_;
|
| +};
|
| +
|
| +SkColor GetBGColor(const char* 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 single pixel,
|
| + // removing any borders, and hope that we get a good color.
|
| + auto context = GetStyleContextFromCss(css_selector);
|
| + RemoveBorders(context);
|
| + PixelSurface surface;
|
| + gtk_render_background(context, surface.cairo(), 0, 0, 1, 1);
|
| + return surface.GetPixelValue();
|
| +}
|
| +
|
| +SkColor GetBorderColor(const char* css_selector) {
|
| + // Borders have the same issue as backgrounds, due to the
|
| + // border-image property.
|
| + auto context = GetStyleContextFromCss(css_selector);
|
| + GtkStateFlags state = gtk_style_context_get_state(context);
|
| + 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);
|
| + PixelSurface surface;
|
| + gtk_render_frame(context, surface.cairo(), 0, 0, 1, 1);
|
| + return surface.GetPixelValue();
|
| +}
|
| +#endif
|
| +
|
| } // namespace libgtkui
|
|
|