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