Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include <set>
     10 #include <string>
     11 
     12 #include "base/environment.h"
     13 #include "base/nix/xdg_util.h"
     14 #include "base/stl_util-inl.h"
     15 #include "chrome/browser/metrics/user_metrics.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/themes/theme_service_factory.h"
     19 #include "chrome/browser/ui/browser_list.h"
     20 #include "chrome/browser/ui/browser_window.h"
     21 #include "chrome/browser/ui/gtk/cairo_cached_surface.h"
     22 #include "chrome/browser/ui/gtk/chrome_gtk_frame.h"
     23 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
     24 #include "chrome/browser/ui/gtk/gtk_util.h"
     25 #include "chrome/browser/ui/gtk/hover_controller_gtk.h"
     26 #include "chrome/common/pref_names.h"
     27 #include "content/common/notification_details.h"
     28 #include "content/common/notification_service.h"
     29 #include "content/common/notification_source.h"
     30 #include "content/common/notification_type.h"
     31 #include "grit/app_resources.h"
     32 #include "grit/theme_resources.h"
     33 #include "third_party/skia/include/core/SkBitmap.h"
     34 #include "third_party/skia/include/core/SkCanvas.h"
     35 #include "third_party/skia/include/core/SkColor.h"
     36 #include "third_party/skia/include/core/SkShader.h"
     37 #include "ui/base/gtk/gtk_signal_registrar.h"
     38 #include "ui/base/resource/resource_bundle.h"
     39 #include "ui/gfx/canvas_skia.h"
     40 #include "ui/gfx/color_utils.h"
     41 #include "ui/gfx/gtk_util.h"
     42 #include "ui/gfx/skbitmap_operations.h"
     43 #include "ui/gfx/skia_util.h"
     44 #include "ui/gfx/skia_utils_gtk.h"
     45 
     46 namespace {
     47 
     48 // The size of the rendered toolbar image.
     49 const int kToolbarImageWidth = 64;
     50 const int kToolbarImageHeight = 128;
     51 
     52 // How much to tint the GTK+ color lighter at the top of the window.
     53 const color_utils::HSL kGtkFrameShift = { -1, -1, 0.58 };
     54 
     55 // How much to tint the GTK+ color when an explicit frame color hasn't been
     56 // specified.
     57 const color_utils::HSL kDefaultFrameShift = { -1, -1, 0.4 };
     58 
     59 // Values used as the new luminance and saturation values in the inactive tab
     60 // text color.
     61 const double kDarkInactiveLuminance = 0.85;
     62 const double kLightInactiveLuminance = 0.15;
     63 const double kHeavyInactiveSaturation = 0.7;
     64 const double kLightInactiveSaturation = 0.3;
     65 
     66 // Number of times that the background color should be counted when trying to
     67 // calculate the border color in GTK theme mode.
     68 const int kBgWeight = 3;
     69 
     70 // Padding to left, top and bottom of vertical separators.
     71 const int kSeparatorPadding = 2;
     72 
     73 // Default color for links on the NTP when the GTK+ theme doesn't define a
     74 // link color. Constant taken from gtklinkbutton.c.
     75 const GdkColor kDefaultLinkColor = { 0, 0, 0, 0xeeee };
     76 
     77 // Middle color of the separator gradient.
     78 const double kMidSeparatorColor[] =
     79     { 194.0 / 255.0, 205.0 / 255.0, 212.0 / 212.0 };
     80 // Top color of the separator gradient.
     81 const double kTopSeparatorColor[] =
     82     { 222.0 / 255.0, 234.0 / 255.0, 248.0 / 255.0 };
     83 
     84 // Converts a GdkColor to a SkColor.
     85 SkColor GdkToSkColor(const GdkColor* color) {
     86   return SkColorSetRGB(color->red >> 8,
     87                        color->green >> 8,
     88                        color->blue >> 8);
     89 }
     90 
     91 // A list of images that we provide while in gtk mode.
     92 const int kThemeImages[] = {
     93   IDR_THEME_TOOLBAR,
     94   IDR_THEME_TAB_BACKGROUND,
     95   IDR_THEME_TAB_BACKGROUND_INCOGNITO,
     96   IDR_THEME_FRAME,
     97   IDR_THEME_FRAME_INACTIVE,
     98   IDR_THEME_FRAME_INCOGNITO,
     99   IDR_THEME_FRAME_INCOGNITO_INACTIVE,
    100 };
    101 
    102 // A list of icons used in the autocomplete view that should be tinted to the
    103 // current gtk theme selection color so they stand out against the GtkEntry's
    104 // base color.
    105 const int kAutocompleteImages[] = {
    106   IDR_OMNIBOX_EXTENSION_APP,
    107   IDR_OMNIBOX_HTTP,
    108   IDR_OMNIBOX_HTTP_DARK,
    109   IDR_OMNIBOX_HISTORY,
    110   IDR_OMNIBOX_HISTORY_DARK,
    111   IDR_OMNIBOX_SEARCH,
    112   IDR_OMNIBOX_SEARCH_DARK,
    113   IDR_OMNIBOX_STAR,
    114   IDR_OMNIBOX_STAR_DARK,
    115   IDR_GEOLOCATION_ALLOWED_LOCATIONBAR_ICON,
    116   IDR_GEOLOCATION_DENIED_LOCATIONBAR_ICON,
    117 };
    118 
    119 bool IsOverridableImage(int id) {
    120   static std::set<int> images;
    121   if (images.empty()) {
    122     images.insert(kThemeImages, kThemeImages + arraysize(kThemeImages));
    123     images.insert(kAutocompleteImages,
    124                   kAutocompleteImages + arraysize(kAutocompleteImages));
    125 
    126     const std::set<int>& buttons = ThemeService::GetTintableToolbarButtons();
    127     images.insert(buttons.begin(), buttons.end());
    128   }
    129 
    130   return images.count(id) > 0;
    131 }
    132 
    133 // Picks a button tint from a set of background colors. While
    134 // |accent_gdk_color| will usually be the same color through a theme, this
    135 // function will get called with the normal GtkLabel |text_color|/GtkWindow
    136 // |background_color| pair and the GtkEntry |text_color|/|background_color|
    137 // pair. While 3/4 of the time the resulting tint will be the same, themes that
    138 // have a dark window background (with light text) and a light text entry (with
    139 // dark text) will get better icons with this separated out.
    140 void PickButtonTintFromColors(const GdkColor& accent_gdk_color,
    141                               const GdkColor& text_color,
    142                               const GdkColor& background_color,
    143                               color_utils::HSL* tint) {
    144   SkColor accent_color = GdkToSkColor(&accent_gdk_color);
    145   color_utils::HSL accent_tint;
    146   color_utils::SkColorToHSL(accent_color, &accent_tint);
    147 
    148   color_utils::HSL text_tint;
    149   color_utils::SkColorToHSL(GdkToSkColor(&text_color), &text_tint);
    150 
    151   color_utils::HSL background_tint;
    152   color_utils::SkColorToHSL(GdkToSkColor(&background_color), &background_tint);
    153 
    154   // If the accent color is gray, then our normal HSL tomfoolery will bring out
    155   // whatever color is oddly dominant (for example, in rgb space [125, 128,
    156   // 125] will tint green instead of gray). Slight differences (+/-10 (4%) to
    157   // all color components) should be interpreted as this color being gray and
    158   // we should switch into a special grayscale mode.
    159   int rb_diff = abs(SkColorGetR(accent_color) - SkColorGetB(accent_color));
    160   int rg_diff = abs(SkColorGetR(accent_color) - SkColorGetG(accent_color));
    161   int bg_diff = abs(SkColorGetB(accent_color) - SkColorGetG(accent_color));
    162   if (rb_diff < 10 && rg_diff < 10 && bg_diff < 10) {
    163     // Our accent is white/gray/black. Only the luminance of the accent color
    164     // matters.
    165     tint->h = -1;
    166 
    167     // Use the saturation of the text.
    168     tint->s = text_tint.s;
    169 
    170     // Use the luminance of the accent color UNLESS there isn't enough
    171     // luminance contrast between the accent color and the base color.
    172     if (fabs(accent_tint.l - background_tint.l) > 0.3)
    173       tint->l = accent_tint.l;
    174     else
    175       tint->l = text_tint.l;
    176   } else {
    177     // Our accent is a color.
    178     tint->h = accent_tint.h;
    179 
    180     // Don't modify the saturation; the amount of color doesn't matter.
    181     tint->s = -1;
    182 
    183     // If the text wants us to darken the icon, don't change the luminance (the
    184     // icons are already dark enough). Otherwise, lighten the icon by no more
    185     // than 0.9 since we don't want a pure-white icon even if the text is pure
    186     // white.
    187     if (text_tint.l < 0.5)
    188       tint->l = -1;
    189     else if (text_tint.l <= 0.9)
    190       tint->l = text_tint.l;
    191     else
    192       tint->l = 0.9;
    193   }
    194 }
    195 
    196 
    197 // Builds and tints the image with |id| to the GtkStateType |state| and
    198 // places the result in |icon_set|.
    199 void BuildIconFromIDRWithColor(int id,
    200                                GtkStyle* style,
    201                                GtkStateType state,
    202                                GtkIconSet* icon_set) {
    203   SkColor color = GdkToSkColor(&style->fg[state]);
    204   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    205   SkBitmap original = *rb.GetBitmapNamed(id);
    206 
    207   SkBitmap fill_color;
    208   fill_color.setConfig(SkBitmap::kARGB_8888_Config,
    209                        original.width(), original.height(), 0);
    210   fill_color.allocPixels();
    211   fill_color.eraseColor(color);
    212   SkBitmap masked = SkBitmapOperations::CreateMaskedBitmap(
    213       fill_color, original);
    214 
    215   GtkIconSource* icon = gtk_icon_source_new();
    216   GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&masked);
    217   gtk_icon_source_set_pixbuf(icon, pixbuf);
    218   g_object_unref(pixbuf);
    219 
    220   gtk_icon_source_set_direction_wildcarded(icon, TRUE);
    221   gtk_icon_source_set_size_wildcarded(icon, TRUE);
    222 
    223   gtk_icon_source_set_state(icon, state);
    224   // All fields default to wildcarding being on and setting a property doesn't
    225   // turn off wildcarding. You need to do this yourself. This is stated once in
    226   // the documentation in the gtk_icon_source_new() function, and no where else.
    227   gtk_icon_source_set_state_wildcarded(
    228       icon, state == GTK_STATE_NORMAL);
    229 
    230   gtk_icon_set_add_source(icon_set, icon);
    231   gtk_icon_source_free(icon);
    232 }
    233 
    234 // Applies an HSL shift to a GdkColor (instead of an SkColor)
    235 void GdkColorHSLShift(const color_utils::HSL& shift, GdkColor* frame_color) {
    236   SkColor shifted = color_utils::HSLShift(GdkToSkColor(frame_color), shift);
    237   frame_color->pixel = 0;
    238   frame_color->red = SkColorGetR(shifted) * kSkiaToGDKMultiplier;
    239   frame_color->green = SkColorGetG(shifted) * kSkiaToGDKMultiplier;
    240   frame_color->blue = SkColorGetB(shifted) * kSkiaToGDKMultiplier;
    241 }
    242 
    243 }  // namespace
    244 
    245 GtkWidget* GtkThemeService::icon_widget_ = NULL;
    246 GdkPixbuf* GtkThemeService::default_folder_icon_ = NULL;
    247 GdkPixbuf* GtkThemeService::default_bookmark_icon_ = NULL;
    248 
    249 // static
    250 GtkThemeService* GtkThemeService::GetFrom(Profile* profile) {
    251   return static_cast<GtkThemeService*>(
    252       ThemeServiceFactory::GetForProfile(profile));
    253 }
    254 
    255 GtkThemeService::GtkThemeService()
    256     : ThemeService(),
    257       fake_window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
    258       fake_frame_(chrome_gtk_frame_new()),
    259       signals_(new ui::GtkSignalRegistrar),
    260       fullscreen_icon_set_(NULL) {
    261   fake_label_.Own(gtk_label_new(""));
    262   fake_entry_.Own(gtk_entry_new());
    263   fake_menu_item_.Own(gtk_menu_item_new());
    264 
    265   // Only realized widgets receive style-set notifications, which we need to
    266   // broadcast new theme images and colors. Only realized widgets have style
    267   // properties, too, which we query for some colors.
    268   gtk_widget_realize(fake_frame_);
    269   gtk_widget_realize(fake_window_);
    270   signals_->Connect(fake_frame_, "style-set",
    271                     G_CALLBACK(&OnStyleSetThunk), this);
    272 }
    273 
    274 GtkThemeService::~GtkThemeService() {
    275   gtk_widget_destroy(fake_window_);
    276   gtk_widget_destroy(fake_frame_);
    277   fake_label_.Destroy();
    278   fake_entry_.Destroy();
    279   fake_menu_item_.Destroy();
    280 
    281   FreeIconSets();
    282 
    283   // We have to call this because FreePlatformCached() in ~ThemeService
    284   // doesn't call the right virutal FreePlatformCaches.
    285   FreePlatformCaches();
    286 }
    287 
    288 void GtkThemeService::Init(Profile* profile) {
    289   registrar_.Init(profile->GetPrefs());
    290   registrar_.Add(prefs::kUsesSystemTheme, this);
    291   use_gtk_ = profile->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme);
    292 
    293   ThemeService::Init(profile);
    294 }
    295 
    296 SkBitmap* GtkThemeService::GetBitmapNamed(int id) const {
    297   // Try to get our cached version:
    298   ImageCache::const_iterator it = gtk_images_.find(id);
    299   if (it != gtk_images_.end())
    300     return it->second;
    301 
    302   if (use_gtk_ && IsOverridableImage(id)) {
    303     // We haven't built this image yet:
    304     SkBitmap* bitmap = GenerateGtkThemeBitmap(id);
    305     gtk_images_[id] = bitmap;
    306     return bitmap;
    307   }
    308 
    309   return ThemeService::GetBitmapNamed(id);
    310 }
    311 
    312 SkColor GtkThemeService::GetColor(int id) const {
    313   if (use_gtk_) {
    314     ColorMap::const_iterator it = colors_.find(id);
    315     if (it != colors_.end())
    316       return it->second;
    317   }
    318 
    319   return ThemeService::GetColor(id);
    320 }
    321 
    322 bool GtkThemeService::HasCustomImage(int id) const {
    323   if (use_gtk_)
    324     return IsOverridableImage(id);
    325 
    326   return ThemeService::HasCustomImage(id);
    327 }
    328 
    329 void GtkThemeService::InitThemesFor(NotificationObserver* observer) {
    330   observer->Observe(NotificationType::BROWSER_THEME_CHANGED,
    331                     Source<ThemeService>(this),
    332                     NotificationService::NoDetails());
    333 }
    334 
    335 void GtkThemeService::SetTheme(const Extension* extension) {
    336   profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
    337   LoadDefaultValues();
    338   ThemeService::SetTheme(extension);
    339 }
    340 
    341 void GtkThemeService::UseDefaultTheme() {
    342   profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
    343   LoadDefaultValues();
    344   ThemeService::UseDefaultTheme();
    345 }
    346 
    347 void GtkThemeService::SetNativeTheme() {
    348   profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, true);
    349   ClearAllThemeData();
    350   LoadGtkValues();
    351   NotifyThemeChanged();
    352 }
    353 
    354 bool GtkThemeService::UsingDefaultTheme() {
    355   return !use_gtk_ && ThemeService::UsingDefaultTheme();
    356 }
    357 
    358 void GtkThemeService::Observe(NotificationType type,
    359                               const NotificationSource& source,
    360                               const NotificationDetails& details) {
    361   if ((type == NotificationType::PREF_CHANGED) &&
    362       (*Details<std::string>(details).ptr() == prefs::kUsesSystemTheme)) {
    363     use_gtk_ = profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme);
    364   } else {
    365     ThemeService::Observe(type, source, details);
    366   }
    367 }
    368 
    369 GtkWidget* GtkThemeService::BuildChromeButton() {
    370   GtkWidget* button = HoverControllerGtk::CreateChromeButton();
    371   gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button), use_gtk_);
    372   chrome_buttons_.push_back(button);
    373 
    374   signals_->Connect(button, "destroy", G_CALLBACK(OnDestroyChromeButtonThunk),
    375                     this);
    376   return button;
    377 }
    378 
    379 GtkWidget* GtkThemeService::CreateToolbarSeparator() {
    380   GtkWidget* separator = gtk_vseparator_new();
    381   GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
    382   gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
    383       kSeparatorPadding, kSeparatorPadding, kSeparatorPadding, 0);
    384   gtk_container_add(GTK_CONTAINER(alignment), separator);
    385 
    386   signals_->Connect(separator, "expose-event",
    387                     G_CALLBACK(OnSeparatorExposeThunk), this);
    388   return alignment;
    389 }
    390 
    391 bool GtkThemeService::UseGtkTheme() const {
    392   return use_gtk_;
    393 }
    394 
    395 GdkColor GtkThemeService::GetGdkColor(int id) const {
    396   return gfx::SkColorToGdkColor(GetColor(id));
    397 }
    398 
    399 GdkColor GtkThemeService::GetBorderColor() const {
    400   GtkStyle* style = gtk_rc_get_style(fake_window_);
    401 
    402   GdkColor text;
    403   GdkColor bg;
    404   if (use_gtk_) {
    405     text = style->text[GTK_STATE_NORMAL];
    406     bg = style->bg[GTK_STATE_NORMAL];
    407   } else {
    408     text = GetGdkColor(COLOR_BOOKMARK_TEXT);
    409     bg = GetGdkColor(COLOR_TOOLBAR);
    410   }
    411 
    412   // Creates a weighted average between the text and base color where
    413   // the base color counts more than once.
    414   GdkColor color;
    415   color.pixel = 0;
    416   color.red = (text.red + (bg.red * kBgWeight)) / (1 + kBgWeight);
    417   color.green = (text.green + (bg.green * kBgWeight)) / (1 + kBgWeight);
    418   color.blue = (text.blue + (bg.blue * kBgWeight)) / (1 + kBgWeight);
    419 
    420   return color;
    421 }
    422 
    423 GtkIconSet* GtkThemeService::GetIconSetForId(int id) const {
    424   if (id == IDR_FULLSCREEN_MENU_BUTTON)
    425     return fullscreen_icon_set_;
    426 
    427   return NULL;
    428 }
    429 
    430 void GtkThemeService::GetScrollbarColors(GdkColor* thumb_active_color,
    431                                          GdkColor* thumb_inactive_color,
    432                                          GdkColor* track_color) {
    433   const GdkColor* theme_thumb_active = NULL;
    434   const GdkColor* theme_thumb_inactive = NULL;
    435   const GdkColor* theme_trough_color = NULL;
    436   gtk_widget_style_get(GTK_WIDGET(fake_frame_),
    437                        "scrollbar-slider-prelight-color", &theme_thumb_active,
    438                        "scrollbar-slider-normal-color", &theme_thumb_inactive,
    439                        "scrollbar-trough-color", &theme_trough_color,
    440                        NULL);
    441 
    442   // Ask the theme if the theme specifies all the scrollbar colors and short
    443   // circuit the expensive painting/compositing if we have all of them.
    444   if (theme_thumb_active && theme_thumb_inactive && theme_trough_color) {
    445     *thumb_active_color = *theme_thumb_active;
    446     *thumb_inactive_color = *theme_thumb_inactive;
    447     *track_color = *theme_trough_color;
    448     return;
    449   }
    450 
    451   // Create window containing scrollbar elements
    452   GtkWidget* window    = gtk_window_new(GTK_WINDOW_POPUP);
    453   GtkWidget* fixed     = gtk_fixed_new();
    454   GtkWidget* scrollbar = gtk_hscrollbar_new(NULL);
    455   gtk_container_add(GTK_CONTAINER(window), fixed);
    456   gtk_container_add(GTK_CONTAINER(fixed),  scrollbar);
    457   gtk_widget_realize(window);
    458   gtk_widget_realize(scrollbar);
    459 
    460   // Draw scrollbar thumb part and track into offscreen image
    461   const int kWidth  = 100;
    462   const int kHeight = 20;
    463   GtkStyle*  style  = gtk_rc_get_style(scrollbar);
    464   GdkPixmap* pm     = gdk_pixmap_new(window->window, kWidth, kHeight, -1);
    465   GdkRectangle rect = { 0, 0, kWidth, kHeight };
    466   unsigned char data[3 * kWidth * kHeight];
    467   for (int i = 0; i < 3; ++i) {
    468     if (i < 2) {
    469       // Thumb part
    470       gtk_paint_slider(style, pm,
    471                        i == 0 ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
    472                        GTK_SHADOW_OUT, &rect, scrollbar, "slider", 0, 0,
    473                        kWidth, kHeight, GTK_ORIENTATION_HORIZONTAL);
    474     } else {
    475       // Track
    476       gtk_paint_box(style, pm, GTK_STATE_ACTIVE, GTK_SHADOW_IN, &rect,
    477                     scrollbar, "trough-upper", 0, 0, kWidth, kHeight);
    478     }
    479     GdkPixbuf* pb = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB,
    480                                              FALSE, 8, kWidth, kHeight,
    481                                              3 * kWidth, 0, 0);
    482     gdk_pixbuf_get_from_drawable(pb, pm, NULL, 0, 0, 0, 0, kWidth, kHeight);
    483 
    484     // Sample pixels
    485     int components[3] = { 0 };
    486     for (int y = 2; y < kHeight - 2; ++y) {
    487       for (int c = 0; c < 3; ++c) {
    488         // Sample a vertical slice of pixels at about one-thirds from the
    489         // left edge. This allows us to avoid any fixed graphics that might be
    490         // located at the edges or in the center of the scrollbar.
    491         // Each pixel is made up of a red, green, and blue component; taking up
    492         // a total of three bytes.
    493         components[c] += data[3 * (kWidth / 3 + y * kWidth) + c];
    494       }
    495     }
    496     GdkColor* color = i == 0 ? thumb_active_color :
    497                       i == 1 ? thumb_inactive_color :
    498                                track_color;
    499     color->pixel = 0;
    500     // We sampled pixels across the full height of the image, ignoring a two
    501     // pixel border. In some themes, the border has a completely different
    502     // color which we do not want to factor into our average color computation.
    503     //
    504     // We now need to scale the colors from the 0..255 range, to the wider
    505     // 0..65535 range, and we need to actually compute the average color; so,
    506     // we divide by the total number of pixels in the sample.
    507     color->red   = components[0] * 65535 / (255 * (kHeight - 4));
    508     color->green = components[1] * 65535 / (255 * (kHeight - 4));
    509     color->blue  = components[2] * 65535 / (255 * (kHeight - 4));
    510 
    511     g_object_unref(pb);
    512   }
    513   g_object_unref(pm);
    514 
    515   gtk_widget_destroy(window);
    516 
    517   // Override any of the default colors with ones that were specified by the
    518   // theme.
    519   if (theme_thumb_active)
    520     *thumb_active_color = *theme_thumb_active;
    521 
    522   if (theme_thumb_inactive)
    523     *thumb_inactive_color = *theme_thumb_inactive;
    524 
    525   if (theme_trough_color)
    526     *track_color = *theme_trough_color;
    527 }
    528 
    529 CairoCachedSurface* GtkThemeService::GetSurfaceNamed(
    530     int id,
    531     GtkWidget* widget_on_display) {
    532   return GetSurfaceNamedImpl(id,
    533                              &per_display_surfaces_,
    534                              GetPixbufNamed(id),
    535                              widget_on_display);
    536 }
    537 
    538 CairoCachedSurface* GtkThemeService::GetRTLEnabledSurfaceNamed(
    539     int id,
    540     GtkWidget* widget_on_display) {
    541   // We flip the sign of |id| when passing it to GetSurfaceNamedImpl() for the
    542   // same reason that ThemeService::GetPixbufImpl() does: so that if one
    543   // location calls this function with a resource ID, and another place calls
    544   // GetSurfaceNamed() with the same ID, they'll correctly get different
    545   // surfaces in RTL mode.
    546   return GetSurfaceNamedImpl(-id,
    547                              &per_display_surfaces_,
    548                              GetRTLEnabledPixbufNamed(id),
    549                              widget_on_display);
    550 }
    551 
    552 CairoCachedSurface* GtkThemeService::GetUnthemedSurfaceNamed(
    553     int id,
    554     GtkWidget* widget_on_display) {
    555   return GetSurfaceNamedImpl(id,
    556       &per_display_unthemed_surfaces_,
    557       ResourceBundle::GetSharedInstance().GetPixbufNamed(id),
    558       widget_on_display);
    559 }
    560 
    561 // static
    562 GdkPixbuf* GtkThemeService::GetFolderIcon(bool native) {
    563   if (native) {
    564     if (!icon_widget_)
    565       icon_widget_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    566     // We never release our ref, so we will leak this on program shutdown.
    567     if (!default_folder_icon_) {
    568       default_folder_icon_ =
    569           gtk_widget_render_icon(icon_widget_, GTK_STOCK_DIRECTORY,
    570                                  GTK_ICON_SIZE_MENU, NULL);
    571     }
    572     if (default_folder_icon_)
    573       return default_folder_icon_;
    574   }
    575 
    576   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    577   static GdkPixbuf* default_folder_icon_ = rb.GetPixbufNamed(
    578       IDR_BOOKMARK_BAR_FOLDER);
    579   return default_folder_icon_;
    580 }
    581 
    582 // static
    583 GdkPixbuf* GtkThemeService::GetDefaultFavicon(bool native) {
    584   if (native) {
    585     if (!icon_widget_)
    586       icon_widget_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    587     // We never release our ref, so we will leak this on program shutdown.
    588     if (!default_bookmark_icon_) {
    589       default_bookmark_icon_ =
    590           gtk_widget_render_icon(icon_widget_, GTK_STOCK_FILE,
    591                                  GTK_ICON_SIZE_MENU, NULL);
    592     }
    593     if (default_bookmark_icon_)
    594       return default_bookmark_icon_;
    595   }
    596 
    597   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    598   static GdkPixbuf* default_bookmark_icon_ = rb.GetPixbufNamed(
    599       IDR_DEFAULT_FAVICON);
    600   return default_bookmark_icon_;
    601 }
    602 
    603 // static
    604 bool GtkThemeService::DefaultUsesSystemTheme() {
    605 #if defined(OS_CHROMEOS)
    606   return false;
    607 #else
    608   scoped_ptr<base::Environment> env(base::Environment::Create());
    609 
    610   switch (base::nix::GetDesktopEnvironment(env.get())) {
    611     case base::nix::DESKTOP_ENVIRONMENT_GNOME:
    612     case base::nix::DESKTOP_ENVIRONMENT_XFCE:
    613       return true;
    614     default:
    615       return false;
    616   }
    617 #endif
    618 }
    619 
    620 void GtkThemeService::ClearAllThemeData() {
    621   colors_.clear();
    622   tints_.clear();
    623 
    624   ThemeService::ClearAllThemeData();
    625 }
    626 
    627 void GtkThemeService::LoadThemePrefs() {
    628   if (use_gtk_) {
    629     LoadGtkValues();
    630   } else {
    631     LoadDefaultValues();
    632     ThemeService::LoadThemePrefs();
    633   }
    634 
    635   RebuildMenuIconSets();
    636 }
    637 
    638 void GtkThemeService::NotifyThemeChanged() {
    639   ThemeService::NotifyThemeChanged();
    640 
    641   // Notify all GtkChromeButtons of their new rendering mode:
    642   for (std::vector<GtkWidget*>::iterator it = chrome_buttons_.begin();
    643        it != chrome_buttons_.end(); ++it) {
    644     gtk_chrome_button_set_use_gtk_rendering(
    645         GTK_CHROME_BUTTON(*it), use_gtk_);
    646   }
    647 
    648   Browser* browser = BrowserList::GetLastActive();
    649   if (browser && browser->window()) {
    650     gtk_util::SetDefaultWindowIcon(browser->window()->GetNativeHandle());
    651   }
    652 }
    653 
    654 void GtkThemeService::FreePlatformCaches() {
    655   ThemeService::FreePlatformCaches();
    656   FreePerDisplaySurfaces(&per_display_surfaces_);
    657   FreePerDisplaySurfaces(&per_display_unthemed_surfaces_);
    658   STLDeleteValues(&gtk_images_);
    659 }
    660 
    661 void GtkThemeService::OnStyleSet(GtkWidget* widget,
    662                                  GtkStyle* previous_style) {
    663   GdkPixbuf* default_folder_icon = default_folder_icon_;
    664   GdkPixbuf* default_bookmark_icon = default_bookmark_icon_;
    665   default_folder_icon_ = NULL;
    666   default_bookmark_icon_ = NULL;
    667 
    668   if (profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme)) {
    669     ClearAllThemeData();
    670     LoadGtkValues();
    671     NotifyThemeChanged();
    672   }
    673 
    674   RebuildMenuIconSets();
    675 
    676   // Free the old icons only after the theme change notification has gone
    677   // through.
    678   if (default_folder_icon)
    679     g_object_unref(default_folder_icon);
    680   if (default_bookmark_icon)
    681     g_object_unref(default_bookmark_icon);
    682 }
    683 
    684 void GtkThemeService::LoadGtkValues() {
    685   // Before we start setting images and values, we have to clear out old, stale
    686   // values. (If we don't do this, we'll regress startup time in the case where
    687   // someone installs a heavyweight theme, then goes back to GTK.)
    688   profile()->GetPrefs()->ClearPref(prefs::kCurrentThemeImages);
    689 
    690   GtkStyle* frame_style = gtk_rc_get_style(fake_frame_);
    691 
    692   GtkStyle* window_style = gtk_rc_get_style(fake_window_);
    693   SetThemeColorFromGtk(ThemeService::COLOR_CONTROL_BACKGROUND,
    694                        &window_style->bg[GTK_STATE_NORMAL]);
    695   SetThemeColorFromGtk(ThemeService::COLOR_BUTTON_BACKGROUND,
    696                        &window_style->bg[GTK_STATE_NORMAL]);
    697 
    698   GdkColor toolbar_color = window_style->bg[GTK_STATE_NORMAL];
    699   SetThemeColorFromGtk(ThemeService::COLOR_TOOLBAR, &toolbar_color);
    700 
    701   GdkColor button_color = window_style->bg[GTK_STATE_SELECTED];
    702   SetThemeTintFromGtk(ThemeService::TINT_BUTTONS, &button_color);
    703 
    704   GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
    705   GdkColor label_color = label_style->fg[GTK_STATE_NORMAL];
    706   SetThemeColorFromGtk(ThemeService::COLOR_TAB_TEXT, &label_color);
    707   SetThemeColorFromGtk(ThemeService::COLOR_BOOKMARK_TEXT, &label_color);
    708 
    709   // Build the various icon tints.
    710   GetNormalButtonTintHSL(&button_tint_);
    711   GetNormalEntryForegroundHSL(&entry_tint_);
    712   GetSelectedEntryForegroundHSL(&selected_entry_tint_);
    713   GdkColor frame_color = BuildFrameColors(frame_style);
    714 
    715   // The inactive frame color never occurs naturally in the theme, as it is a
    716   // tinted version of |frame_color|. We generate another color based on the
    717   // background tab color, with the lightness and saturation moved in the
    718   // opposite direction. (We don't touch the hue, since there should be subtle
    719   // hints of the color in the text.)
    720   color_utils::HSL inactive_tab_text_hsl = tints_[TINT_BACKGROUND_TAB];
    721   if (inactive_tab_text_hsl.l < 0.5)
    722     inactive_tab_text_hsl.l = kDarkInactiveLuminance;
    723   else
    724     inactive_tab_text_hsl.l = kLightInactiveLuminance;
    725 
    726   if (inactive_tab_text_hsl.s < 0.5)
    727     inactive_tab_text_hsl.s = kHeavyInactiveSaturation;
    728   else
    729     inactive_tab_text_hsl.s = kLightInactiveSaturation;
    730 
    731   colors_[ThemeService::COLOR_BACKGROUND_TAB_TEXT] =
    732       color_utils::HSLToSkColor(inactive_tab_text_hsl, 255);
    733 
    734   // We pick the text and background colors for the NTP out of the colors for a
    735   // GtkEntry. We do this because GtkEntries background color is never the same
    736   // as |toolbar_color|, is usually a white, and when it isn't a white,
    737   // provides sufficient contrast to |toolbar_color|. Try this out with
    738   // Darklooks, HighContrastInverse or ThinIce.
    739   GtkStyle* entry_style = gtk_rc_get_style(fake_entry_.get());
    740   GdkColor ntp_background = entry_style->base[GTK_STATE_NORMAL];
    741   GdkColor ntp_foreground = entry_style->text[GTK_STATE_NORMAL];
    742   SetThemeColorFromGtk(ThemeService::COLOR_NTP_BACKGROUND,
    743                        &ntp_background);
    744   SetThemeColorFromGtk(ThemeService::COLOR_NTP_TEXT,
    745                        &ntp_foreground);
    746 
    747   // The NTP header is the color that surrounds the current active thumbnail on
    748   // the NTP, and acts as the border of the "Recent Links" box. It would be
    749   // awesome if they were separated so we could use GetBorderColor() for the
    750   // border around the "Recent Links" section, but matching the frame color is
    751   // more important.
    752   SetThemeColorFromGtk(ThemeService::COLOR_NTP_HEADER,
    753                        &frame_color);
    754   SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION,
    755                        &toolbar_color);
    756   SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION_TEXT,
    757                        &label_color);
    758 
    759   // Override the link color if the theme provides it.
    760   const GdkColor* link_color = NULL;
    761   gtk_widget_style_get(GTK_WIDGET(fake_window_),
    762                        "link-color", &link_color, NULL);
    763   if (!link_color)
    764     link_color = &kDefaultLinkColor;
    765 
    766   SetThemeColorFromGtk(ThemeService::COLOR_NTP_LINK,
    767                        link_color);
    768   SetThemeColorFromGtk(ThemeService::COLOR_NTP_LINK_UNDERLINE,
    769                        link_color);
    770   SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION_LINK,
    771                        link_color);
    772   SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE,
    773                        link_color);
    774 
    775   // Generate the colors that we pass to WebKit.
    776   focus_ring_color_ = GdkToSkColor(&frame_color);
    777   GdkColor thumb_active_color, thumb_inactive_color, track_color;
    778   GtkThemeService::GetScrollbarColors(&thumb_active_color,
    779                                       &thumb_inactive_color,
    780                                       &track_color);
    781   thumb_active_color_ = GdkToSkColor(&thumb_active_color);
    782   thumb_inactive_color_ = GdkToSkColor(&thumb_inactive_color);
    783   track_color_ = GdkToSkColor(&track_color);
    784 
    785   // Some GTK themes only define the text selection colors on the GtkEntry
    786   // class, so we need to use that for getting selection colors.
    787   active_selection_bg_color_ =
    788       GdkToSkColor(&entry_style->base[GTK_STATE_SELECTED]);
    789   active_selection_fg_color_ =
    790       GdkToSkColor(&entry_style->text[GTK_STATE_SELECTED]);
    791   inactive_selection_bg_color_ =
    792       GdkToSkColor(&entry_style->base[GTK_STATE_ACTIVE]);
    793   inactive_selection_fg_color_ =
    794       GdkToSkColor(&entry_style->text[GTK_STATE_ACTIVE]);
    795 }
    796 
    797 GdkColor GtkThemeService::BuildFrameColors(GtkStyle* frame_style) {
    798   const GdkColor* theme_frame = NULL;
    799   const GdkColor* theme_inactive_frame = NULL;
    800   const GdkColor* theme_incognito_frame = NULL;
    801   const GdkColor* theme_incognito_inactive_frame = NULL;
    802   gtk_widget_style_get(GTK_WIDGET(fake_frame_),
    803                        "frame-color", &theme_frame,
    804                        "inactive-frame-color", &theme_inactive_frame,
    805                        "incognito-frame-color", &theme_incognito_frame,
    806                        "incognito-inactive-frame-color",
    807                        &theme_incognito_inactive_frame,
    808                        NULL);
    809 
    810   GdkColor frame_color = BuildAndSetFrameColor(
    811       &frame_style->bg[GTK_STATE_SELECTED],
    812       theme_frame,
    813       kDefaultFrameShift,
    814       ThemeService::COLOR_FRAME,
    815       ThemeService::TINT_FRAME);
    816   SetThemeTintFromGtk(ThemeService::TINT_BACKGROUND_TAB, &frame_color);
    817 
    818   BuildAndSetFrameColor(
    819       &frame_style->bg[GTK_STATE_INSENSITIVE],
    820       theme_inactive_frame,
    821       kDefaultFrameShift,
    822       ThemeService::COLOR_FRAME_INACTIVE,
    823       ThemeService::TINT_FRAME_INACTIVE);
    824 
    825   BuildAndSetFrameColor(
    826       &frame_color,
    827       theme_incognito_frame,
    828       GetDefaultTint(ThemeService::TINT_FRAME_INCOGNITO),
    829       ThemeService::COLOR_FRAME_INCOGNITO,
    830       ThemeService::TINT_FRAME_INCOGNITO);
    831 
    832   BuildAndSetFrameColor(
    833       &frame_color,
    834       theme_incognito_inactive_frame,
    835       GetDefaultTint(ThemeService::TINT_FRAME_INCOGNITO_INACTIVE),
    836       ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE,
    837       ThemeService::TINT_FRAME_INCOGNITO_INACTIVE);
    838 
    839   return frame_color;
    840 }
    841 
    842 void GtkThemeService::LoadDefaultValues() {
    843   focus_ring_color_ = SkColorSetARGB(255, 229, 151, 0);
    844   thumb_active_color_ = SkColorSetRGB(244, 244, 244);
    845   thumb_inactive_color_ = SkColorSetRGB(234, 234, 234);
    846   track_color_ = SkColorSetRGB(211, 211, 211);
    847 
    848   active_selection_bg_color_ = SkColorSetRGB(30, 144, 255);
    849   active_selection_fg_color_ = SK_ColorWHITE;
    850   inactive_selection_bg_color_ = SkColorSetRGB(200, 200, 200);
    851   inactive_selection_fg_color_ = SkColorSetRGB(50, 50, 50);
    852 }
    853 
    854 void GtkThemeService::RebuildMenuIconSets() {
    855   FreeIconSets();
    856 
    857   GtkStyle* style = gtk_rc_get_style(fake_menu_item_.get());
    858 
    859   fullscreen_icon_set_ = gtk_icon_set_new();
    860   BuildIconFromIDRWithColor(IDR_FULLSCREEN_MENU_BUTTON,
    861                             style,
    862                             GTK_STATE_PRELIGHT,
    863                             fullscreen_icon_set_);
    864   BuildIconFromIDRWithColor(IDR_FULLSCREEN_MENU_BUTTON,
    865                             style,
    866                             GTK_STATE_NORMAL,
    867                             fullscreen_icon_set_);
    868 }
    869 
    870 void GtkThemeService::SetThemeColorFromGtk(int id, const GdkColor* color) {
    871   colors_[id] = GdkToSkColor(color);
    872 }
    873 
    874 void GtkThemeService::SetThemeTintFromGtk(int id, const GdkColor* color) {
    875   color_utils::HSL default_tint = GetDefaultTint(id);
    876   color_utils::HSL hsl;
    877   color_utils::SkColorToHSL(GdkToSkColor(color), &hsl);
    878 
    879   if (default_tint.s != -1)
    880     hsl.s = default_tint.s;
    881 
    882   if (default_tint.l != -1)
    883     hsl.l = default_tint.l;
    884 
    885   tints_[id] = hsl;
    886 }
    887 
    888 GdkColor GtkThemeService::BuildAndSetFrameColor(const GdkColor* base,
    889                                                 const GdkColor* gtk_base,
    890                                                 const color_utils::HSL& tint,
    891                                                 int color_id,
    892                                                 int tint_id) {
    893   GdkColor out_color = *base;
    894   if (gtk_base) {
    895     // The theme author specified a color to use, use it without modification.
    896     out_color = *gtk_base;
    897   } else {
    898     // Tint the basic color since this is a heuristic color instead of one
    899     // specified by the theme author.
    900     GdkColorHSLShift(tint, &out_color);
    901   }
    902   SetThemeColorFromGtk(color_id, &out_color);
    903   SetThemeTintFromGtk(tint_id, &out_color);
    904 
    905   return out_color;
    906 }
    907 
    908 void GtkThemeService::FreePerDisplaySurfaces(
    909     PerDisplaySurfaceMap* per_display_map) {
    910   for (PerDisplaySurfaceMap::iterator it = per_display_map->begin();
    911        it != per_display_map->end(); ++it) {
    912     for (CairoCachedSurfaceMap::iterator jt = it->second.begin();
    913          jt != it->second.end(); ++jt) {
    914       delete jt->second;
    915     }
    916   }
    917   per_display_map->clear();
    918 }
    919 
    920 void GtkThemeService::FreeIconSets() {
    921   if (fullscreen_icon_set_) {
    922     gtk_icon_set_unref(fullscreen_icon_set_);
    923     fullscreen_icon_set_ = NULL;
    924   }
    925 }
    926 
    927 SkBitmap* GtkThemeService::GenerateGtkThemeBitmap(int id) const {
    928   switch (id) {
    929     case IDR_THEME_TOOLBAR: {
    930       GtkStyle* style = gtk_rc_get_style(fake_window_);
    931       GdkColor* color = &style->bg[GTK_STATE_NORMAL];
    932       SkBitmap* bitmap = new SkBitmap;
    933       bitmap->setConfig(SkBitmap::kARGB_8888_Config,
    934                         kToolbarImageWidth, kToolbarImageHeight);
    935       bitmap->allocPixels();
    936       bitmap->eraseRGB(color->red >> 8, color->green >> 8, color->blue >> 8);
    937       return bitmap;
    938     }
    939     case IDR_THEME_TAB_BACKGROUND:
    940       return GenerateTabImage(IDR_THEME_FRAME);
    941     case IDR_THEME_TAB_BACKGROUND_INCOGNITO:
    942       return GenerateTabImage(IDR_THEME_FRAME_INCOGNITO);
    943     case IDR_THEME_FRAME:
    944       return GenerateFrameImage(ThemeService::COLOR_FRAME,
    945                                 "frame-gradient-color");
    946     case IDR_THEME_FRAME_INACTIVE:
    947       return GenerateFrameImage(ThemeService::COLOR_FRAME_INACTIVE,
    948                                 "inactive-frame-gradient-color");
    949     case IDR_THEME_FRAME_INCOGNITO:
    950       return GenerateFrameImage(ThemeService::COLOR_FRAME_INCOGNITO,
    951                                 "incognito-frame-gradient-color");
    952     case IDR_THEME_FRAME_INCOGNITO_INACTIVE: {
    953       return GenerateFrameImage(
    954           ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE,
    955           "incognito-inactive-frame-gradient-color");
    956     }
    957     // Icons that sit inside the omnibox shouldn't receive TINT_BUTTONS and
    958     // instead should tint based on the foreground text entry color in GTK+
    959     // mode because some themes that try to be dark *and* light have very
    960     // different colors between the omnibox and the normal background area.
    961     case IDR_OMNIBOX_EXTENSION_APP:
    962     case IDR_OMNIBOX_HISTORY:
    963     case IDR_OMNIBOX_HTTP:
    964     case IDR_OMNIBOX_SEARCH:
    965     case IDR_OMNIBOX_STAR:
    966     case IDR_GEOLOCATION_ALLOWED_LOCATIONBAR_ICON:
    967     case IDR_GEOLOCATION_DENIED_LOCATIONBAR_ICON: {
    968       return GenerateTintedIcon(id, entry_tint_);
    969     }
    970     // In GTK mode, the dark versions of the omnibox icons only ever appear in
    971     // the autocomplete popup and only against the current theme's GtkEntry
    972     // base[GTK_STATE_SELECTED] color, so tint the icons so they won't collide
    973     // with the selected color.
    974     case IDR_OMNIBOX_EXTENSION_APP_DARK:
    975     case IDR_OMNIBOX_HISTORY_DARK:
    976     case IDR_OMNIBOX_HTTP_DARK:
    977     case IDR_OMNIBOX_SEARCH_DARK:
    978     case IDR_OMNIBOX_STAR_DARK: {
    979       return GenerateTintedIcon(id, selected_entry_tint_);
    980     }
    981     default: {
    982       return GenerateTintedIcon(id, button_tint_);
    983     }
    984   }
    985 }
    986 
    987 SkBitmap* GtkThemeService::GenerateFrameImage(
    988     int color_id,
    989     const char* gradient_name) const {
    990   // We use two colors: the main color (passed in) and a lightened version of
    991   // that color (which is supposed to match the light gradient at the top of
    992   // several GTK+ themes, such as Ambiance, Clearlooks or Bluebird).
    993   ColorMap::const_iterator it = colors_.find(color_id);
    994   DCHECK(it != colors_.end());
    995   SkColor base = it->second;
    996 
    997   gfx::CanvasSkia canvas(kToolbarImageWidth, kToolbarImageHeight, true);
    998 
    999   int gradient_size;
   1000   const GdkColor* gradient_top_color = NULL;
   1001   gtk_widget_style_get(GTK_WIDGET(fake_frame_),
   1002                        "frame-gradient-size", &gradient_size,
   1003                        gradient_name, &gradient_top_color,
   1004                        NULL);
   1005   if (gradient_size) {
   1006     SkColor lighter = gradient_top_color ? GdkToSkColor(gradient_top_color)
   1007                       : color_utils::HSLShift(base, kGtkFrameShift);
   1008     SkShader* shader = gfx::CreateGradientShader(
   1009         0, gradient_size, lighter, base);
   1010     SkPaint paint;
   1011     paint.setStyle(SkPaint::kFill_Style);
   1012     paint.setAntiAlias(true);
   1013     paint.setShader(shader);
   1014     shader->unref();
   1015 
   1016     canvas.DrawRectInt(0, 0, kToolbarImageWidth, gradient_size, paint);
   1017   }
   1018 
   1019   canvas.FillRectInt(base, 0, gradient_size,
   1020                      kToolbarImageWidth,
   1021                      kToolbarImageHeight - gradient_size);
   1022   return new SkBitmap(canvas.ExtractBitmap());
   1023 }
   1024 
   1025 SkBitmap* GtkThemeService::GenerateTabImage(int base_id) const {
   1026   SkBitmap* base_image = GetBitmapNamed(base_id);
   1027   SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
   1028       *base_image, GetTint(ThemeService::TINT_BACKGROUND_TAB));
   1029   return new SkBitmap(SkBitmapOperations::CreateTiledBitmap(
   1030       bg_tint, 0, 0, bg_tint.width(), bg_tint.height()));
   1031 }
   1032 
   1033 SkBitmap* GtkThemeService::GenerateTintedIcon(
   1034     int base_id,
   1035     const color_utils::HSL& tint) const {
   1036   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   1037   scoped_ptr<SkBitmap> button(new SkBitmap(*rb.GetBitmapNamed(base_id)));
   1038   return new SkBitmap(SkBitmapOperations::CreateHSLShiftedBitmap(
   1039       *button, tint));
   1040 }
   1041 
   1042 void GtkThemeService::GetNormalButtonTintHSL(
   1043     color_utils::HSL* tint) const {
   1044   GtkStyle* window_style = gtk_rc_get_style(fake_window_);
   1045   const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
   1046   const GdkColor base_color = window_style->base[GTK_STATE_NORMAL];
   1047 
   1048   GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
   1049   const GdkColor text_color = label_style->fg[GTK_STATE_NORMAL];
   1050 
   1051   PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
   1052 }
   1053 
   1054 void GtkThemeService::GetNormalEntryForegroundHSL(
   1055     color_utils::HSL* tint) const {
   1056   GtkStyle* window_style = gtk_rc_get_style(fake_window_);
   1057   const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
   1058 
   1059   GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
   1060   const GdkColor text_color = style->text[GTK_STATE_NORMAL];
   1061   const GdkColor base_color = style->base[GTK_STATE_NORMAL];
   1062 
   1063   PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
   1064 }
   1065 
   1066 void GtkThemeService::GetSelectedEntryForegroundHSL(
   1067     color_utils::HSL* tint) const {
   1068   // The simplest of all the tints. We just use the selected text in the entry
   1069   // since the icons tinted this way will only be displayed against
   1070   // base[GTK_STATE_SELECTED].
   1071   GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
   1072   const GdkColor color = style->text[GTK_STATE_SELECTED];
   1073   color_utils::SkColorToHSL(GdkToSkColor(&color), tint);
   1074 }
   1075 
   1076 CairoCachedSurface* GtkThemeService::GetSurfaceNamedImpl(
   1077     int id,
   1078     PerDisplaySurfaceMap* display_surface_map,
   1079     GdkPixbuf* pixbuf,
   1080     GtkWidget* widget_on_display) {
   1081   GdkDisplay* display = gtk_widget_get_display(widget_on_display);
   1082   CairoCachedSurfaceMap& surface_map = (*display_surface_map)[display];
   1083 
   1084   // Check to see if we already have the pixbuf in the cache.
   1085   CairoCachedSurfaceMap::const_iterator found = surface_map.find(id);
   1086   if (found != surface_map.end())
   1087     return found->second;
   1088 
   1089   CairoCachedSurface* surface = new CairoCachedSurface;
   1090   surface->UsePixbuf(pixbuf);
   1091 
   1092   surface_map[id] = surface;
   1093 
   1094   return surface;
   1095 }
   1096 
   1097 void GtkThemeService::OnDestroyChromeButton(GtkWidget* button) {
   1098   std::vector<GtkWidget*>::iterator it =
   1099       find(chrome_buttons_.begin(), chrome_buttons_.end(), button);
   1100   if (it != chrome_buttons_.end())
   1101     chrome_buttons_.erase(it);
   1102 }
   1103 
   1104 gboolean GtkThemeService::OnSeparatorExpose(GtkWidget* widget,
   1105                                             GdkEventExpose* event) {
   1106   if (UseGtkTheme())
   1107     return FALSE;
   1108 
   1109   cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
   1110   gdk_cairo_rectangle(cr, &event->area);
   1111   cairo_clip(cr);
   1112 
   1113   GdkColor bottom_color = GetGdkColor(ThemeService::COLOR_TOOLBAR);
   1114   double bottom_color_rgb[] = {
   1115       static_cast<double>(bottom_color.red / 257) / 255.0,
   1116       static_cast<double>(bottom_color.green / 257) / 255.0,
   1117       static_cast<double>(bottom_color.blue / 257) / 255.0, };
   1118 
   1119   cairo_pattern_t* pattern =
   1120       cairo_pattern_create_linear(widget->allocation.x, widget->allocation.y,
   1121                                   widget->allocation.x,
   1122                                   widget->allocation.y +
   1123                                   widget->allocation.height);
   1124   cairo_pattern_add_color_stop_rgb(
   1125       pattern, 0.0,
   1126       kTopSeparatorColor[0], kTopSeparatorColor[1], kTopSeparatorColor[2]);
   1127   cairo_pattern_add_color_stop_rgb(
   1128       pattern, 0.5,
   1129       kMidSeparatorColor[0], kMidSeparatorColor[1], kMidSeparatorColor[2]);
   1130   cairo_pattern_add_color_stop_rgb(
   1131       pattern, 1.0,
   1132       bottom_color_rgb[0], bottom_color_rgb[1], bottom_color_rgb[2]);
   1133   cairo_set_source(cr, pattern);
   1134 
   1135   double start_x = 0.5 + widget->allocation.x;
   1136   cairo_new_path(cr);
   1137   cairo_set_line_width(cr, 1.0);
   1138   cairo_move_to(cr, start_x, widget->allocation.y);
   1139   cairo_line_to(cr, start_x,
   1140                 widget->allocation.y + widget->allocation.height);
   1141   cairo_stroke(cr);
   1142   cairo_destroy(cr);
   1143   cairo_pattern_destroy(pattern);
   1144 
   1145   return TRUE;
   1146 }
   1147