root/chrome/browser/ui/gtk/gtk_theme_service.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IsOverridableImage
  2. PickButtonTintFromColors
  3. BuildIconFromIDRWithColor
  4. GdkColorHSLShift
  5. GetFrom
  6. fullscreen_icon_set_
  7. Init
  8. GetImageSkiaNamed
  9. GetImageNamed
  10. GetColor
  11. HasCustomImage
  12. InitThemesFor
  13. SetTheme
  14. UseDefaultTheme
  15. SetNativeTheme
  16. UsingDefaultTheme
  17. SetCustomDefaultTheme
  18. ShouldInitWithNativeTheme
  19. UsingNativeTheme
  20. BuildChromeButton
  21. BuildChromeLinkButton
  22. BuildLabel
  23. CreateToolbarSeparator
  24. GetGdkColor
  25. GetBorderColor
  26. GetIconSetForId
  27. GetScrollbarColors
  28. GetFolderIcon
  29. GetDefaultFavicon
  30. DefaultUsesSystemTheme
  31. LoadThemePrefs
  32. NotifyThemeChanged
  33. FreePlatformCaches
  34. OnStyleSet
  35. SetXDGIconTheme
  36. LoadGtkValues
  37. BuildFrameColors
  38. LoadDefaultValues
  39. RebuildMenuIconSets
  40. SetThemeColorFromGtk
  41. SetThemeTintFromGtk
  42. BuildAndSetFrameColor
  43. FreeIconSets
  44. GenerateGtkThemeBitmap
  45. GenerateFrameImage
  46. GenerateTabImage
  47. GenerateTintedIcon
  48. GetNormalButtonTintHSL
  49. GetNormalEntryForegroundHSL
  50. GetSelectedEntryForegroundHSL
  51. OnDestroyChromeButton
  52. OnDestroyChromeLinkButton
  53. OnDestroyLabel
  54. OnSeparatorExpose
  55. OnUsesSystemThemeChanged

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/gtk/gtk_theme_service.h"

#include <gtk/gtk.h>

#include <set>
#include <string>

#include "base/debug/trace_event.h"
#include "base/environment.h"
#include "base/nix/mime_util_xdg.h"
#include "base/nix/xdg_util.h"
#include "base/prefs/pref_service.h"
#include "base/stl_util.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/custom_theme_supplier.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/gtk/chrome_gtk_frame.h"
#include "chrome/browser/ui/gtk/gtk_chrome_button.h"
#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/hover_controller_gtk.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "extensions/common/extension.h"
#include "grit/theme_resources.h"
#include "grit/ui_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkShader.h"
#include "ui/base/gtk/gtk_hig_constants.h"
#include "ui/base/gtk/gtk_signal_registrar.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/image/cairo_cached_surface.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/skbitmap_operations.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/skia_utils_gtk.h"

namespace {

// The size of the rendered toolbar image.
const int kToolbarImageWidth = 64;
const int kToolbarImageHeight = 128;

// How much to tint the GTK+ color lighter at the top of the window.
const color_utils::HSL kGtkFrameShift = { -1, -1, 0.58 };

// How much to tint the GTK+ color when an explicit frame color hasn't been
// specified.
const color_utils::HSL kDefaultFrameShift = { -1, -1, 0.4 };

// Values used as the new luminance and saturation values in the inactive tab
// text color.
const double kDarkInactiveLuminance = 0.85;
const double kLightInactiveLuminance = 0.15;
const double kHeavyInactiveSaturation = 0.7;
const double kLightInactiveSaturation = 0.3;

// Number of times that the background color should be counted when trying to
// calculate the border color in GTK theme mode.
const int kBgWeight = 3;

// Padding to left, top and bottom of vertical separators.
const int kSeparatorPadding = 2;

// Default color for links on the NTP when the GTK+ theme doesn't define a
// link color. Constant taken from gtklinkbutton.c.
const GdkColor kDefaultLinkColor = { 0, 0, 0, 0xeeee };

// Middle color of the separator gradient.
const double kMidSeparatorColor[] =
    { 194.0 / 255.0, 205.0 / 255.0, 212.0 / 212.0 };
// Top color of the separator gradient.
const double kTopSeparatorColor[] =
    { 222.0 / 255.0, 234.0 / 255.0, 248.0 / 255.0 };

// A list of images that we provide while in gtk mode.
const int kThemeImages[] = {
  IDR_THEME_TOOLBAR,
  IDR_THEME_TAB_BACKGROUND,
  IDR_THEME_TAB_BACKGROUND_INCOGNITO,
  IDR_THEME_FRAME,
  IDR_THEME_FRAME_INACTIVE,
  IDR_THEME_FRAME_INCOGNITO,
  IDR_THEME_FRAME_INCOGNITO_INACTIVE,
};

// A list of icons used in the autocomplete view that should be tinted to the
// current gtk theme selection color so they stand out against the GtkEntry's
// base color.
// TODO(erg): Decide what to do about other icons that appear in the omnibox,
// e.g. content settings icons.
const int kAutocompleteImages[] = {
  IDR_OMNIBOX_EXTENSION_APP,
  IDR_OMNIBOX_HTTP,
  IDR_OMNIBOX_HTTP_DARK,
  IDR_OMNIBOX_SEARCH,
  IDR_OMNIBOX_SEARCH_DARK,
  IDR_OMNIBOX_STAR,
  IDR_OMNIBOX_STAR_DARK,
  IDR_OMNIBOX_TTS,
  IDR_OMNIBOX_TTS_DARK,
};

bool IsOverridableImage(int id) {
  CR_DEFINE_STATIC_LOCAL(std::set<int>, images, ());
  if (images.empty()) {
    images.insert(kThemeImages, kThemeImages + arraysize(kThemeImages));
    images.insert(kAutocompleteImages,
                  kAutocompleteImages + arraysize(kAutocompleteImages));

    const std::set<int>& buttons =
        ThemeProperties::GetTintableToolbarButtons();
    images.insert(buttons.begin(), buttons.end());
  }

  return images.count(id) > 0;
}

// Picks a button tint from a set of background colors. While
// |accent_gdk_color| will usually be the same color through a theme, this
// function will get called with the normal GtkLabel |text_color|/GtkWindow
// |background_color| pair and the GtkEntry |text_color|/|background_color|
// pair. While 3/4 of the time the resulting tint will be the same, themes that
// have a dark window background (with light text) and a light text entry (with
// dark text) will get better icons with this separated out.
void PickButtonTintFromColors(const GdkColor& accent_gdk_color,
                              const GdkColor& text_color,
                              const GdkColor& background_color,
                              color_utils::HSL* tint) {
  SkColor accent_color = gfx::GdkColorToSkColor(accent_gdk_color);
  color_utils::HSL accent_tint;
  color_utils::SkColorToHSL(accent_color, &accent_tint);

  color_utils::HSL text_tint;
  color_utils::SkColorToHSL(gfx::GdkColorToSkColor(text_color), &text_tint);

  color_utils::HSL background_tint;
  color_utils::SkColorToHSL(gfx::GdkColorToSkColor(background_color),
                            &background_tint);

  // If the accent color is gray, then our normal HSL tomfoolery will bring out
  // whatever color is oddly dominant (for example, in rgb space [125, 128,
  // 125] will tint green instead of gray). Slight differences (+/-10 (4%) to
  // all color components) should be interpreted as this color being gray and
  // we should switch into a special grayscale mode.
  int rb_diff = abs(SkColorGetR(accent_color) - SkColorGetB(accent_color));
  int rg_diff = abs(SkColorGetR(accent_color) - SkColorGetG(accent_color));
  int bg_diff = abs(SkColorGetB(accent_color) - SkColorGetG(accent_color));
  if (rb_diff < 10 && rg_diff < 10 && bg_diff < 10) {
    // Our accent is white/gray/black. Only the luminance of the accent color
    // matters.
    tint->h = -1;

    // Use the saturation of the text.
    tint->s = text_tint.s;

    // Use the luminance of the accent color UNLESS there isn't enough
    // luminance contrast between the accent color and the base color.
    if (fabs(accent_tint.l - background_tint.l) > 0.3)
      tint->l = accent_tint.l;
    else
      tint->l = text_tint.l;
  } else {
    // Our accent is a color.
    tint->h = accent_tint.h;

    // Don't modify the saturation; the amount of color doesn't matter.
    tint->s = -1;

    // If the text wants us to darken the icon, don't change the luminance (the
    // icons are already dark enough). Otherwise, lighten the icon by no more
    // than 0.9 since we don't want a pure-white icon even if the text is pure
    // white.
    if (text_tint.l < 0.5)
      tint->l = -1;
    else if (text_tint.l <= 0.9)
      tint->l = text_tint.l;
    else
      tint->l = 0.9;
  }
}


// Builds and tints the image with |id| to the GtkStateType |state| and
// places the result in |icon_set|.
void BuildIconFromIDRWithColor(int id,
                               GtkStyle* style,
                               GtkStateType state,
                               GtkIconSet* icon_set) {
  SkColor color = gfx::GdkColorToSkColor(style->fg[state]);
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  SkBitmap original = rb.GetImageNamed(id).AsBitmap();

  SkBitmap fill_color;
  fill_color.setConfig(SkBitmap::kARGB_8888_Config,
                       original.width(), original.height(), 0);
  fill_color.allocPixels();
  fill_color.eraseColor(color);
  SkBitmap masked = SkBitmapOperations::CreateMaskedBitmap(
      fill_color, original);

  GtkIconSource* icon = gtk_icon_source_new();
  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(masked);
  gtk_icon_source_set_pixbuf(icon, pixbuf);
  g_object_unref(pixbuf);

  gtk_icon_source_set_direction_wildcarded(icon, TRUE);
  gtk_icon_source_set_size_wildcarded(icon, TRUE);

  gtk_icon_source_set_state(icon, state);
  // All fields default to wildcarding being on and setting a property doesn't
  // turn off wildcarding. You need to do this yourself. This is stated once in
  // the documentation in the gtk_icon_source_new() function, and no where else.
  gtk_icon_source_set_state_wildcarded(
      icon, state == GTK_STATE_NORMAL);

  gtk_icon_set_add_source(icon_set, icon);
  gtk_icon_source_free(icon);
}

// Applies an HSL shift to a GdkColor (instead of an SkColor)
void GdkColorHSLShift(const color_utils::HSL& shift, GdkColor* frame_color) {
  SkColor shifted = color_utils::HSLShift(gfx::GdkColorToSkColor(*frame_color),
                                                                 shift);
  frame_color->pixel = 0;
  frame_color->red = SkColorGetR(shifted) * ui::kSkiaToGDKMultiplier;
  frame_color->green = SkColorGetG(shifted) * ui::kSkiaToGDKMultiplier;
  frame_color->blue = SkColorGetB(shifted) * ui::kSkiaToGDKMultiplier;
}

}  // namespace

GtkWidget* GtkThemeService::icon_widget_ = NULL;
base::LazyInstance<gfx::Image> GtkThemeService::default_folder_icon_ =
    LAZY_INSTANCE_INITIALIZER;
base::LazyInstance<gfx::Image> GtkThemeService::default_bookmark_icon_ =
    LAZY_INSTANCE_INITIALIZER;

// static
GtkThemeService* GtkThemeService::GetFrom(Profile* profile) {
  return static_cast<GtkThemeService*>(
      ThemeServiceFactory::GetForProfile(profile));
}

GtkThemeService::GtkThemeService()
    : ThemeService(),
      use_gtk_(false),
      fake_window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
      fake_frame_(chrome_gtk_frame_new()),
      signals_(new ui::GtkSignalRegistrar),
      fullscreen_icon_set_(NULL) {
  fake_label_.Own(gtk_label_new(""));
  fake_entry_.Own(gtk_entry_new());
  fake_menu_item_.Own(gtk_menu_item_new());

  // Only realized widgets receive style-set notifications, which we need to
  // broadcast new theme images and colors. Only realized widgets have style
  // properties, too, which we query for some colors.
  gtk_widget_realize(fake_frame_);
  gtk_widget_realize(fake_window_);
  signals_->Connect(fake_frame_, "style-set",
                    G_CALLBACK(&OnStyleSetThunk), this);
}

GtkThemeService::~GtkThemeService() {
  gtk_widget_destroy(fake_window_);
  gtk_widget_destroy(fake_frame_);
  fake_label_.Destroy();
  fake_entry_.Destroy();
  fake_menu_item_.Destroy();

  FreeIconSets();

  // We have to call this because FreePlatformCached() in ~ThemeService
  // doesn't call the right virutal FreePlatformCaches.
  FreePlatformCaches();
}

void GtkThemeService::Init(Profile* profile) {
  registrar_.Init(profile->GetPrefs());
  registrar_.Add(prefs::kUsesSystemTheme,
                 base::Bind(&GtkThemeService::OnUsesSystemThemeChanged,
                            base::Unretained(this)));
  ThemeService::Init(profile);
}

gfx::ImageSkia* GtkThemeService::GetImageSkiaNamed(int id) const {
  // TODO(pkotwicz): Remove this const cast.  The gfx::Image interface returns
  // its images const. GetImageSkiaNamed() also should but has many callsites.
  return const_cast<gfx::ImageSkia*>(GetImageNamed(id).ToImageSkia());
}

gfx::Image GtkThemeService::GetImageNamed(int id) const {
  // Try to get our cached version:
  ImageCache::const_iterator it = gtk_images_.find(id);
  if (it != gtk_images_.end())
    return *it->second;

  if (use_gtk_ && IsOverridableImage(id)) {
    gfx::Image* image = new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(
        GenerateGtkThemeBitmap(id)));
    gtk_images_[id] = image;
    return *image;
  }

  return ThemeService::GetImageNamed(id);
}

SkColor GtkThemeService::GetColor(int id) const {
  if (use_gtk_) {
    ColorMap::const_iterator it = colors_.find(id);
    if (it != colors_.end())
      return it->second;
  }

  return ThemeService::GetColor(id);
}

bool GtkThemeService::HasCustomImage(int id) const {
  if (use_gtk_)
    return IsOverridableImage(id);

  return ThemeService::HasCustomImage(id);
}

void GtkThemeService::InitThemesFor(content::NotificationObserver* observer) {
  observer->Observe(chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
                    content::Source<ThemeService>(this),
                    content::NotificationService::NoDetails());
}

void GtkThemeService::SetTheme(const extensions::Extension* extension) {
  profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
  use_gtk_ = false;
  LoadDefaultValues();
  ThemeService::SetTheme(extension);
}

void GtkThemeService::UseDefaultTheme() {
  profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
  use_gtk_ = false;
  LoadDefaultValues();
  ThemeService::UseDefaultTheme();
}

void GtkThemeService::SetNativeTheme() {
  profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, true);
  use_gtk_ = true;
  ClearAllThemeData();
  LoadGtkValues();
  NotifyThemeChanged();
}

bool GtkThemeService::UsingDefaultTheme() const {
  return !use_gtk_ && ThemeService::UsingDefaultTheme();
}

void GtkThemeService::SetCustomDefaultTheme(
    scoped_refptr<CustomThemeSupplier> theme_supplier) {
  profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
  use_gtk_ = false;
  LoadDefaultValues();
  ThemeService::SetCustomDefaultTheme(theme_supplier);
}

bool GtkThemeService::ShouldInitWithNativeTheme() const {
  return profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme);
}

bool GtkThemeService::UsingNativeTheme() const {
  return use_gtk_;
}

GtkWidget* GtkThemeService::BuildChromeButton() {
  GtkWidget* button = HoverControllerGtk::CreateChromeButton();
  gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button), use_gtk_);
  chrome_buttons_.push_back(button);

  signals_->Connect(button, "destroy", G_CALLBACK(OnDestroyChromeButtonThunk),
                    this);
  return button;
}

GtkWidget* GtkThemeService::BuildChromeLinkButton(const std::string& text) {
  GtkWidget* link_button = gtk_chrome_link_button_new(text.c_str());
  gtk_chrome_link_button_set_use_gtk_theme(
      GTK_CHROME_LINK_BUTTON(link_button),
      use_gtk_);
  link_buttons_.push_back(link_button);

  signals_->Connect(link_button, "destroy",
                    G_CALLBACK(OnDestroyChromeLinkButtonThunk), this);

  return link_button;
}

GtkWidget* GtkThemeService::BuildLabel(const std::string& text,
                                       const GdkColor& color) {
  GtkWidget* label = gtk_label_new(text.empty() ? NULL : text.c_str());
  if (!use_gtk_)
    gtk_util::SetLabelColor(label, &color);
  labels_.insert(std::make_pair(label, color));

  signals_->Connect(label, "destroy", G_CALLBACK(OnDestroyLabelThunk), this);

  return label;
}

GtkWidget* GtkThemeService::CreateToolbarSeparator() {
  GtkWidget* separator = gtk_vseparator_new();
  GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
      kSeparatorPadding, kSeparatorPadding, kSeparatorPadding, 0);
  gtk_container_add(GTK_CONTAINER(alignment), separator);

  signals_->Connect(separator, "expose-event",
                    G_CALLBACK(OnSeparatorExposeThunk), this);
  return alignment;
}

GdkColor GtkThemeService::GetGdkColor(int id) const {
  return gfx::SkColorToGdkColor(GetColor(id));
}

GdkColor GtkThemeService::GetBorderColor() const {
  GtkStyle* style = gtk_rc_get_style(fake_window_);

  GdkColor text;
  GdkColor bg;
  if (use_gtk_) {
    text = style->text[GTK_STATE_NORMAL];
    bg = style->bg[GTK_STATE_NORMAL];
  } else {
    text = GetGdkColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
    bg = GetGdkColor(ThemeProperties::COLOR_TOOLBAR);
  }

  // Creates a weighted average between the text and base color where
  // the base color counts more than once.
  GdkColor color;
  color.pixel = 0;
  color.red = (text.red + (bg.red * kBgWeight)) / (1 + kBgWeight);
  color.green = (text.green + (bg.green * kBgWeight)) / (1 + kBgWeight);
  color.blue = (text.blue + (bg.blue * kBgWeight)) / (1 + kBgWeight);

  return color;
}

GtkIconSet* GtkThemeService::GetIconSetForId(int id) const {
  if (id == IDR_FULLSCREEN_MENU_BUTTON)
    return fullscreen_icon_set_;

  return NULL;
}

void GtkThemeService::GetScrollbarColors(GdkColor* thumb_active_color,
                                         GdkColor* thumb_inactive_color,
                                         GdkColor* track_color) {
  GdkColor* theme_thumb_active = NULL;
  GdkColor* theme_thumb_inactive = NULL;
  GdkColor* theme_trough_color = NULL;
  gtk_widget_style_get(GTK_WIDGET(fake_frame_),
                       "scrollbar-slider-prelight-color", &theme_thumb_active,
                       "scrollbar-slider-normal-color", &theme_thumb_inactive,
                       "scrollbar-trough-color", &theme_trough_color,
                       NULL);

  // Ask the theme if the theme specifies all the scrollbar colors and short
  // circuit the expensive painting/compositing if we have all of them.
  if (theme_thumb_active && theme_thumb_inactive && theme_trough_color) {
    *thumb_active_color = *theme_thumb_active;
    *thumb_inactive_color = *theme_thumb_inactive;
    *track_color = *theme_trough_color;
    gdk_color_free(theme_thumb_active);
    gdk_color_free(theme_thumb_inactive);
    gdk_color_free(theme_trough_color);
    return;
  }

  // Create window containing scrollbar elements
  GtkWidget* window    = gtk_window_new(GTK_WINDOW_POPUP);
  GtkWidget* fixed     = gtk_fixed_new();
  GtkWidget* scrollbar = gtk_hscrollbar_new(NULL);
  gtk_container_add(GTK_CONTAINER(window), fixed);
  gtk_container_add(GTK_CONTAINER(fixed),  scrollbar);
  gtk_widget_realize(window);
  gtk_widget_realize(scrollbar);

  // Draw scrollbar thumb part and track into offscreen image
  const int kWidth  = 100;
  const int kHeight = 20;
  GtkStyle*  style  = gtk_rc_get_style(scrollbar);
  GdkWindow* gdk_window = gtk_widget_get_window(window);
  GdkPixmap* pm     = gdk_pixmap_new(gdk_window, kWidth, kHeight, -1);
  GdkRectangle rect = { 0, 0, kWidth, kHeight };
  unsigned char data[3 * kWidth * kHeight];
  for (int i = 0; i < 3; ++i) {
    if (i < 2) {
      // Thumb part
      gtk_paint_slider(style, pm,
                       i == 0 ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
                       GTK_SHADOW_OUT, &rect, scrollbar, "slider", 0, 0,
                       kWidth, kHeight, GTK_ORIENTATION_HORIZONTAL);
    } else {
      // Track
      gtk_paint_box(style, pm, GTK_STATE_ACTIVE, GTK_SHADOW_IN, &rect,
                    scrollbar, "trough-upper", 0, 0, kWidth, kHeight);
    }
    GdkPixbuf* pb = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB,
                                             FALSE, 8, kWidth, kHeight,
                                             3 * kWidth, 0, 0);
    gdk_pixbuf_get_from_drawable(pb, pm, NULL, 0, 0, 0, 0, kWidth, kHeight);

    // Sample pixels
    int components[3] = { 0 };
    for (int y = 2; y < kHeight - 2; ++y) {
      for (int c = 0; c < 3; ++c) {
        // Sample a vertical slice of pixels at about one-thirds from the
        // left edge. This allows us to avoid any fixed graphics that might be
        // located at the edges or in the center of the scrollbar.
        // Each pixel is made up of a red, green, and blue component; taking up
        // a total of three bytes.
        components[c] += data[3 * (kWidth / 3 + y * kWidth) + c];
      }
    }
    GdkColor* color = i == 0 ? thumb_active_color :
                      i == 1 ? thumb_inactive_color :
                               track_color;
    color->pixel = 0;
    // We sampled pixels across the full height of the image, ignoring a two
    // pixel border. In some themes, the border has a completely different
    // color which we do not want to factor into our average color computation.
    //
    // We now need to scale the colors from the 0..255 range, to the wider
    // 0..65535 range, and we need to actually compute the average color; so,
    // we divide by the total number of pixels in the sample.
    color->red   = components[0] * 65535 / (255 * (kHeight - 4));
    color->green = components[1] * 65535 / (255 * (kHeight - 4));
    color->blue  = components[2] * 65535 / (255 * (kHeight - 4));

    g_object_unref(pb);
  }
  g_object_unref(pm);

  gtk_widget_destroy(window);

  // Override any of the default colors with ones that were specified by the
  // theme.
  if (theme_thumb_active) {
    *thumb_active_color = *theme_thumb_active;
    gdk_color_free(theme_thumb_active);
  }

  if (theme_thumb_inactive) {
    *thumb_inactive_color = *theme_thumb_inactive;
    gdk_color_free(theme_thumb_inactive);
  }

  if (theme_trough_color) {
    *track_color = *theme_trough_color;
    gdk_color_free(theme_trough_color);
  }
}

// static
gfx::Image GtkThemeService::GetFolderIcon(bool native) {
  if (native) {
    if (!icon_widget_)
      icon_widget_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    if (default_folder_icon_.Get().IsEmpty()) {
      // This seems to leak.
      GdkPixbuf* pixbuf = gtk_widget_render_icon(
          icon_widget_, GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
      if (pixbuf)
        default_folder_icon_.Get() = gfx::Image(pixbuf);
    }
    if (!default_folder_icon_.Get().IsEmpty())
      return default_folder_icon_.Get();
  }

  return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
      IDR_BOOKMARK_BAR_FOLDER);
}

// static
gfx::Image GtkThemeService::GetDefaultFavicon(bool native) {
  if (native) {
    if (!icon_widget_)
      icon_widget_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    if (default_bookmark_icon_.Get().IsEmpty()) {
      // This seems to leak.
      GdkPixbuf* pixbuf = gtk_widget_render_icon(
          icon_widget_, GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
      if (pixbuf)
        default_bookmark_icon_.Get() = gfx::Image(pixbuf);
    }
    if (!default_bookmark_icon_.Get().IsEmpty())
      return default_bookmark_icon_.Get();
  }

  return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
      IDR_DEFAULT_FAVICON);
}

// static
bool GtkThemeService::DefaultUsesSystemTheme() {
  scoped_ptr<base::Environment> env(base::Environment::Create());

  switch (base::nix::GetDesktopEnvironment(env.get())) {
    case base::nix::DESKTOP_ENVIRONMENT_GNOME:
    case base::nix::DESKTOP_ENVIRONMENT_UNITY:
    case base::nix::DESKTOP_ENVIRONMENT_XFCE:
      return true;
    case base::nix::DESKTOP_ENVIRONMENT_KDE3:
    case base::nix::DESKTOP_ENVIRONMENT_KDE4:
    case base::nix::DESKTOP_ENVIRONMENT_OTHER:
      return false;
  }
  // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
  NOTREACHED();
  return false;
}

void GtkThemeService::LoadThemePrefs() {
  // Initialize the values sent to webkit with the default values.
  // ThemeService::LoadThemePrefs() will replace them with values for the native
  // gtk theme if necessary.
  LoadDefaultValues();

  // This takes care of calling SetNativeTheme() if necessary.
  ThemeService::LoadThemePrefs();

  SetXDGIconTheme();
  RebuildMenuIconSets();
}

void GtkThemeService::NotifyThemeChanged() {
  if (!ready_)
    return;

  ThemeService::NotifyThemeChanged();

  // Notify all GtkChromeButtons of their new rendering mode:
  for (std::vector<GtkWidget*>::iterator it = chrome_buttons_.begin();
       it != chrome_buttons_.end(); ++it) {
    gtk_chrome_button_set_use_gtk_rendering(
        GTK_CHROME_BUTTON(*it), use_gtk_);
  }

  for (std::vector<GtkWidget*>::iterator it = link_buttons_.begin();
       it != link_buttons_.end(); ++it) {
    gtk_chrome_link_button_set_use_gtk_theme(
        GTK_CHROME_LINK_BUTTON(*it), use_gtk_);
  }

  for (std::map<GtkWidget*, GdkColor>::iterator it = labels_.begin();
       it != labels_.end(); ++it) {
    const GdkColor* color = use_gtk_ ? NULL : &it->second;
    gtk_util::SetLabelColor(it->first, color);
  }

  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    Browser* browser = *it;
    if (!browser->window())
      continue;
    GtkWindow* window = browser->window()->GetNativeWindow();
    gtk_util::SetDefaultWindowIcon(window);
    gtk_util::SetWindowIcon(window, browser->profile());
  }
}

void GtkThemeService::FreePlatformCaches() {
  colors_.clear();
  tints_.clear();
  ThemeService::FreePlatformCaches();
  STLDeleteValues(&gtk_images_);
}

void GtkThemeService::OnStyleSet(GtkWidget* widget,
                                 GtkStyle* previous_style) {
  default_folder_icon_.Get() = gfx::Image();
  default_bookmark_icon_.Get() = gfx::Image();

  if (profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme)) {
    ClearAllThemeData();
    LoadGtkValues();
    NotifyThemeChanged();
  }

  RebuildMenuIconSets();
}

void GtkThemeService::SetXDGIconTheme() {
  gchar* gtk_theme_name;
  g_object_get(gtk_settings_get_default(),
               "gtk-icon-theme-name",
               &gtk_theme_name, NULL);
  base::nix::SetIconThemeName(gtk_theme_name);
  g_free(gtk_theme_name);
}

void GtkThemeService::LoadGtkValues() {
  // Before we start setting images and values, we have to clear out old, stale
  // values. (If we don't do this, we'll regress startup time in the case where
  // someone installs a heavyweight theme, then goes back to GTK.)
  profile()->GetPrefs()->ClearPref(prefs::kCurrentThemeImages);

  GtkStyle* frame_style = gtk_rc_get_style(fake_frame_);

  GtkStyle* window_style = gtk_rc_get_style(fake_window_);
  SetThemeColorFromGtk(ThemeProperties::COLOR_CONTROL_BACKGROUND,
                       &window_style->bg[GTK_STATE_NORMAL]);

  GdkColor toolbar_color = window_style->bg[GTK_STATE_NORMAL];
  SetThemeColorFromGtk(ThemeProperties::COLOR_TOOLBAR, &toolbar_color);

  GdkColor button_color = window_style->bg[GTK_STATE_SELECTED];
  SetThemeTintFromGtk(ThemeProperties::TINT_BUTTONS, &button_color);

  GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
  GdkColor label_color = label_style->fg[GTK_STATE_NORMAL];
  SetThemeColorFromGtk(ThemeProperties::COLOR_TAB_TEXT, &label_color);
  SetThemeColorFromGtk(ThemeProperties::COLOR_BOOKMARK_TEXT, &label_color);

  // Build the various icon tints.
  GetNormalButtonTintHSL(&button_tint_);
  GetNormalEntryForegroundHSL(&entry_tint_);
  GetSelectedEntryForegroundHSL(&selected_entry_tint_);
  GdkColor frame_color = BuildFrameColors(frame_style);

  // The inactive frame color never occurs naturally in the theme, as it is a
  // tinted version of |frame_color|. We generate another color based on the
  // background tab color, with the lightness and saturation moved in the
  // opposite direction. (We don't touch the hue, since there should be subtle
  // hints of the color in the text.)
  color_utils::HSL inactive_tab_text_hsl =
      tints_[ThemeProperties::TINT_BACKGROUND_TAB];
  if (inactive_tab_text_hsl.l < 0.5)
    inactive_tab_text_hsl.l = kDarkInactiveLuminance;
  else
    inactive_tab_text_hsl.l = kLightInactiveLuminance;

  if (inactive_tab_text_hsl.s < 0.5)
    inactive_tab_text_hsl.s = kHeavyInactiveSaturation;
  else
    inactive_tab_text_hsl.s = kLightInactiveSaturation;

  colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] =
      color_utils::HSLToSkColor(inactive_tab_text_hsl, 255);

  // We pick the text and background colors for the NTP out of the colors for a
  // GtkEntry. We do this because GtkEntries background color is never the same
  // as |toolbar_color|, is usually a white, and when it isn't a white,
  // provides sufficient contrast to |toolbar_color|. Try this out with
  // Darklooks, HighContrastInverse or ThinIce.
  GtkStyle* entry_style = gtk_rc_get_style(fake_entry_.get());
  GdkColor ntp_background = entry_style->base[GTK_STATE_NORMAL];
  GdkColor ntp_foreground = entry_style->text[GTK_STATE_NORMAL];
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_BACKGROUND,
                       &ntp_background);
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_TEXT,
                       &ntp_foreground);

  // The NTP header is the color that surrounds the current active thumbnail on
  // the NTP, and acts as the border of the "Recent Links" box. It would be
  // awesome if they were separated so we could use GetBorderColor() for the
  // border around the "Recent Links" section, but matching the frame color is
  // more important.
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_HEADER,
                       &frame_color);
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION,
                       &toolbar_color);
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION_TEXT,
                       &label_color);

  // Override the link color if the theme provides it.
  const GdkColor* link_color = NULL;
  gtk_widget_style_get(GTK_WIDGET(fake_window_),
                       "link-color", &link_color, NULL);

  bool is_default_link_color = false;
  if (!link_color) {
    link_color = &kDefaultLinkColor;
    is_default_link_color = true;
  }

  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_LINK,
                       link_color);
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_LINK_UNDERLINE,
                       link_color);
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION_LINK,
                       link_color);
  SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE,
                       link_color);

  if (!is_default_link_color)
    gdk_color_free(const_cast<GdkColor*>(link_color));

  // Generate the colors that we pass to WebKit.
  focus_ring_color_ = gfx::GdkColorToSkColor(frame_color);
  GdkColor thumb_active_color, thumb_inactive_color, track_color;
  GtkThemeService::GetScrollbarColors(&thumb_active_color,
                                      &thumb_inactive_color,
                                      &track_color);
  thumb_active_color_ = gfx::GdkColorToSkColor(thumb_active_color);
  thumb_inactive_color_ = gfx::GdkColorToSkColor(thumb_inactive_color);
  track_color_ = gfx::GdkColorToSkColor(track_color);

  // Some GTK themes only define the text selection colors on the GtkEntry
  // class, so we need to use that for getting selection colors.
  active_selection_bg_color_ =
      gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_SELECTED]);
  active_selection_fg_color_ =
      gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_SELECTED]);
  inactive_selection_bg_color_ =
      gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_ACTIVE]);
  inactive_selection_fg_color_ =
      gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_ACTIVE]);
  location_bar_bg_color_ =
      gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_NORMAL]);
  location_bar_text_color_ =
      gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_NORMAL]);
}

GdkColor GtkThemeService::BuildFrameColors(GtkStyle* frame_style) {
  GdkColor* theme_frame = NULL;
  GdkColor* theme_inactive_frame = NULL;
  GdkColor* theme_incognito_frame = NULL;
  GdkColor* theme_incognito_inactive_frame = NULL;
  gtk_widget_style_get(GTK_WIDGET(fake_frame_),
                       "frame-color", &theme_frame,
                       "inactive-frame-color", &theme_inactive_frame,
                       "incognito-frame-color", &theme_incognito_frame,
                       "incognito-inactive-frame-color",
                       &theme_incognito_inactive_frame,
                       NULL);

  GdkColor frame_color = BuildAndSetFrameColor(
      &frame_style->bg[GTK_STATE_SELECTED],
      theme_frame,
      kDefaultFrameShift,
      ThemeProperties::COLOR_FRAME,
      ThemeProperties::TINT_FRAME);
  if (theme_frame)
    gdk_color_free(theme_frame);
  SetThemeTintFromGtk(ThemeProperties::TINT_BACKGROUND_TAB, &frame_color);

  BuildAndSetFrameColor(
      &frame_style->bg[GTK_STATE_INSENSITIVE],
      theme_inactive_frame,
      kDefaultFrameShift,
      ThemeProperties::COLOR_FRAME_INACTIVE,
      ThemeProperties::TINT_FRAME_INACTIVE);
  if (theme_inactive_frame)
    gdk_color_free(theme_inactive_frame);

  BuildAndSetFrameColor(
      &frame_color,
      theme_incognito_frame,
      ThemeProperties::GetDefaultTint(
          ThemeProperties::TINT_FRAME_INCOGNITO),
      ThemeProperties::COLOR_FRAME_INCOGNITO,
      ThemeProperties::TINT_FRAME_INCOGNITO);
  if (theme_incognito_frame)
    gdk_color_free(theme_incognito_frame);

  BuildAndSetFrameColor(
      &frame_color,
      theme_incognito_inactive_frame,
      ThemeProperties::GetDefaultTint(
          ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE),
      ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
      ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE);
  if (theme_incognito_inactive_frame)
    gdk_color_free(theme_incognito_inactive_frame);

  return frame_color;
}

void GtkThemeService::LoadDefaultValues() {
  focus_ring_color_ = SkColorSetARGB(255, 229, 151, 0);
  thumb_active_color_ = SkColorSetRGB(244, 244, 244);
  thumb_inactive_color_ = SkColorSetRGB(234, 234, 234);
  track_color_ = SkColorSetRGB(211, 211, 211);

  active_selection_bg_color_ = SkColorSetRGB(30, 144, 255);
  active_selection_fg_color_ = SK_ColorWHITE;
  inactive_selection_bg_color_ = SkColorSetRGB(200, 200, 200);
  inactive_selection_fg_color_ = SkColorSetRGB(50, 50, 50);
}

void GtkThemeService::RebuildMenuIconSets() {
  FreeIconSets();

  GtkStyle* style = gtk_rc_get_style(fake_menu_item_.get());

  fullscreen_icon_set_ = gtk_icon_set_new();
  BuildIconFromIDRWithColor(IDR_FULLSCREEN_MENU_BUTTON,
                            style,
                            GTK_STATE_PRELIGHT,
                            fullscreen_icon_set_);
  BuildIconFromIDRWithColor(IDR_FULLSCREEN_MENU_BUTTON,
                            style,
                            GTK_STATE_NORMAL,
                            fullscreen_icon_set_);
}

void GtkThemeService::SetThemeColorFromGtk(int id, const GdkColor* color) {
  colors_[id] = gfx::GdkColorToSkColor(*color);
}

void GtkThemeService::SetThemeTintFromGtk(int id, const GdkColor* color) {
  color_utils::HSL default_tint = ThemeProperties::GetDefaultTint(id);
  color_utils::HSL hsl;
  color_utils::SkColorToHSL(gfx::GdkColorToSkColor(*color), &hsl);

  if (default_tint.s != -1)
    hsl.s = default_tint.s;

  if (default_tint.l != -1)
    hsl.l = default_tint.l;

  tints_[id] = hsl;
}

GdkColor GtkThemeService::BuildAndSetFrameColor(const GdkColor* base,
                                                const GdkColor* gtk_base,
                                                const color_utils::HSL& tint,
                                                int color_id,
                                                int tint_id) {
  GdkColor out_color = *base;
  if (gtk_base) {
    // The theme author specified a color to use, use it without modification.
    out_color = *gtk_base;
  } else {
    // Tint the basic color since this is a heuristic color instead of one
    // specified by the theme author.
    GdkColorHSLShift(tint, &out_color);
  }
  SetThemeColorFromGtk(color_id, &out_color);
  SetThemeTintFromGtk(tint_id, &out_color);

  return out_color;
}

void GtkThemeService::FreeIconSets() {
  if (fullscreen_icon_set_) {
    gtk_icon_set_unref(fullscreen_icon_set_);
    fullscreen_icon_set_ = NULL;
  }
}

SkBitmap GtkThemeService::GenerateGtkThemeBitmap(int id) const {
  switch (id) {
    case IDR_THEME_TOOLBAR: {
      GtkStyle* style = gtk_rc_get_style(fake_window_);
      GdkColor* color = &style->bg[GTK_STATE_NORMAL];
      SkBitmap bitmap;
      bitmap.setConfig(SkBitmap::kARGB_8888_Config,
                       kToolbarImageWidth, kToolbarImageHeight);
      bitmap.allocPixels();
      bitmap.eraseRGB(color->red >> 8, color->green >> 8, color->blue >> 8);
      return bitmap;
    }
    case IDR_THEME_TAB_BACKGROUND:
      return GenerateTabImage(IDR_THEME_FRAME);
    case IDR_THEME_TAB_BACKGROUND_INCOGNITO:
      return GenerateTabImage(IDR_THEME_FRAME_INCOGNITO);
    case IDR_THEME_FRAME:
      return GenerateFrameImage(ThemeProperties::COLOR_FRAME,
                                "frame-gradient-color");
    case IDR_THEME_FRAME_INACTIVE:
      return GenerateFrameImage(ThemeProperties::COLOR_FRAME_INACTIVE,
                                "inactive-frame-gradient-color");
    case IDR_THEME_FRAME_INCOGNITO:
      return GenerateFrameImage(ThemeProperties::COLOR_FRAME_INCOGNITO,
                                "incognito-frame-gradient-color");
    case IDR_THEME_FRAME_INCOGNITO_INACTIVE: {
      return GenerateFrameImage(
          ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
          "incognito-inactive-frame-gradient-color");
    }
    // Icons that sit inside the omnibox shouldn't receive TINT_BUTTONS and
    // instead should tint based on the foreground text entry color in GTK+
    // mode because some themes that try to be dark *and* light have very
    // different colors between the omnibox and the normal background area.
    // TODO(erg): Decide what to do about other icons that appear in the
    // omnibox, e.g. content settings icons.
    case IDR_OMNIBOX_EXTENSION_APP:
    case IDR_OMNIBOX_HTTP:
    case IDR_OMNIBOX_SEARCH:
    case IDR_OMNIBOX_STAR:
    case IDR_OMNIBOX_TTS: {
      return GenerateTintedIcon(id, entry_tint_);
    }
    // In GTK mode, the dark versions of the omnibox icons only ever appear in
    // the autocomplete popup and only against the current theme's GtkEntry
    // base[GTK_STATE_SELECTED] color, so tint the icons so they won't collide
    // with the selected color.
    case IDR_OMNIBOX_EXTENSION_APP_DARK:
    case IDR_OMNIBOX_HTTP_DARK:
    case IDR_OMNIBOX_SEARCH_DARK:
    case IDR_OMNIBOX_STAR_DARK:
    case IDR_OMNIBOX_TTS_DARK: {
      return GenerateTintedIcon(id, selected_entry_tint_);
    }
    default: {
      return GenerateTintedIcon(id, button_tint_);
    }
  }
}

SkBitmap GtkThemeService::GenerateFrameImage(
    int color_id,
    const char* gradient_name) const {
  // We use two colors: the main color (passed in) and a lightened version of
  // that color (which is supposed to match the light gradient at the top of
  // several GTK+ themes, such as Ambiance, Clearlooks or Bluebird).
  ColorMap::const_iterator it = colors_.find(color_id);
  DCHECK(it != colors_.end());
  SkColor base = it->second;

  gfx::Canvas canvas(gfx::Size(kToolbarImageWidth, kToolbarImageHeight),
      1.0f, true);

  int gradient_size;
  GdkColor* gradient_top_color = NULL;
  gtk_widget_style_get(GTK_WIDGET(fake_frame_),
                       "frame-gradient-size", &gradient_size,
                       gradient_name, &gradient_top_color,
                       NULL);
  if (gradient_size) {
    SkColor lighter = gradient_top_color ?
        gfx::GdkColorToSkColor(*gradient_top_color) :
        color_utils::HSLShift(base, kGtkFrameShift);
    if (gradient_top_color)
      gdk_color_free(gradient_top_color);
    skia::RefPtr<SkShader> shader = gfx::CreateGradientShader(
        0, gradient_size, lighter, base);
    SkPaint paint;
    paint.setStyle(SkPaint::kFill_Style);
    paint.setAntiAlias(true);
    paint.setShader(shader.get());

    canvas.DrawRect(gfx::Rect(0, 0, kToolbarImageWidth, gradient_size), paint);
  }

  canvas.FillRect(gfx::Rect(0, gradient_size, kToolbarImageWidth,
                            kToolbarImageHeight - gradient_size), base);
  return canvas.ExtractImageRep().sk_bitmap();
}

SkBitmap GtkThemeService::GenerateTabImage(int base_id) const {
  SkBitmap base_image = GetImageNamed(base_id).AsBitmap();
  SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
      base_image, GetTint(ThemeProperties::TINT_BACKGROUND_TAB));
  return SkBitmapOperations::CreateTiledBitmap(
      bg_tint, 0, 0, bg_tint.width(), bg_tint.height());
}

SkBitmap GtkThemeService::GenerateTintedIcon(
    int base_id,
    const color_utils::HSL& tint) const {
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  return SkBitmapOperations::CreateHSLShiftedBitmap(
      rb.GetImageNamed(base_id).AsBitmap(), tint);
}

void GtkThemeService::GetNormalButtonTintHSL(
    color_utils::HSL* tint) const {
  GtkStyle* window_style = gtk_rc_get_style(fake_window_);
  const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
  const GdkColor base_color = window_style->base[GTK_STATE_NORMAL];

  GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
  const GdkColor text_color = label_style->fg[GTK_STATE_NORMAL];

  PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
}

void GtkThemeService::GetNormalEntryForegroundHSL(
    color_utils::HSL* tint) const {
  GtkStyle* window_style = gtk_rc_get_style(fake_window_);
  const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];

  GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
  const GdkColor text_color = style->text[GTK_STATE_NORMAL];
  const GdkColor base_color = style->base[GTK_STATE_NORMAL];

  PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
}

void GtkThemeService::GetSelectedEntryForegroundHSL(
    color_utils::HSL* tint) const {
  // The simplest of all the tints. We just use the selected text in the entry
  // since the icons tinted this way will only be displayed against
  // base[GTK_STATE_SELECTED].
  GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
  const GdkColor color = style->text[GTK_STATE_SELECTED];
  color_utils::SkColorToHSL(gfx::GdkColorToSkColor(color), tint);
}

void GtkThemeService::OnDestroyChromeButton(GtkWidget* button) {
  std::vector<GtkWidget*>::iterator it =
      find(chrome_buttons_.begin(), chrome_buttons_.end(), button);
  if (it != chrome_buttons_.end())
    chrome_buttons_.erase(it);
}

void GtkThemeService::OnDestroyChromeLinkButton(GtkWidget* button) {
  std::vector<GtkWidget*>::iterator it =
      find(link_buttons_.begin(), link_buttons_.end(), button);
  if (it != link_buttons_.end())
    link_buttons_.erase(it);
}

void GtkThemeService::OnDestroyLabel(GtkWidget* button) {
  std::map<GtkWidget*, GdkColor>::iterator it = labels_.find(button);
  if (it != labels_.end())
    labels_.erase(it);
}

gboolean GtkThemeService::OnSeparatorExpose(GtkWidget* widget,
                                            GdkEventExpose* event) {
  UNSHIPPED_TRACE_EVENT0("ui::gtk", "GtkThemeService::OnSeparatorExpose");
  if (UsingNativeTheme())
    return FALSE;

  cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
  gdk_cairo_rectangle(cr, &event->area);
  cairo_clip(cr);

  GdkColor bottom_color = GetGdkColor(ThemeProperties::COLOR_TOOLBAR);
  double bottom_color_rgb[] = {
      static_cast<double>(bottom_color.red / 257) / 255.0,
      static_cast<double>(bottom_color.green / 257) / 255.0,
      static_cast<double>(bottom_color.blue / 257) / 255.0, };

  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);

  cairo_pattern_t* pattern =
      cairo_pattern_create_linear(allocation.x, allocation.y,
                                  allocation.x,
                                  allocation.y + allocation.height);
  cairo_pattern_add_color_stop_rgb(
      pattern, 0.0,
      kTopSeparatorColor[0], kTopSeparatorColor[1], kTopSeparatorColor[2]);
  cairo_pattern_add_color_stop_rgb(
      pattern, 0.5,
      kMidSeparatorColor[0], kMidSeparatorColor[1], kMidSeparatorColor[2]);
  cairo_pattern_add_color_stop_rgb(
      pattern, 1.0,
      bottom_color_rgb[0], bottom_color_rgb[1], bottom_color_rgb[2]);
  cairo_set_source(cr, pattern);

  double start_x = 0.5 + allocation.x;
  cairo_new_path(cr);
  cairo_set_line_width(cr, 1.0);
  cairo_move_to(cr, start_x, allocation.y);
  cairo_line_to(cr, start_x, allocation.y + allocation.height);
  cairo_stroke(cr);
  cairo_destroy(cr);
  cairo_pattern_destroy(pattern);

  return TRUE;
}

void GtkThemeService::OnUsesSystemThemeChanged() {
  use_gtk_ = profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme);
}

/* [<][>][^][v][top][bottom][index][help] */