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