ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / themes / theme_service_mac.mm
blob1b9a5410caf44136215ba394fb9aaf242b4aad31
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.
5 #include "chrome/browser/themes/theme_service.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/logging.h"
10 #include "chrome/browser/themes/browser_theme_pack.h"
11 #include "chrome/browser/themes/theme_properties.h"
12 #include "skia/ext/skia_utils_mac.h"
13 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSColor+Luminance.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/gfx/color_utils.h"
16 #include "ui/gfx/image/image.h"
18 NSString* const kBrowserThemeDidChangeNotification =
19     @"BrowserThemeDidChangeNotification";
21 typedef ThemeProperties Properties;
23 namespace {
25 void HSLToHSB(const color_utils::HSL& hsl, CGFloat* h, CGFloat* s, CGFloat* b) {
26   SkColor color = color_utils::HSLToSkColor(hsl, 255);  // alpha doesn't matter
27   SkScalar hsv[3];
28   SkColorToHSV(color, hsv);
30   *h = SkScalarToDouble(hsv[0]) / 360.0;
31   *s = SkScalarToDouble(hsv[1]);
32   *b = SkScalarToDouble(hsv[2]);
35 }  // namespace
37 NSImage* ThemeService::GetNSImageNamed(int id) const {
38   DCHECK(CalledOnValidThread());
40   // Check to see if we already have the image in the cache.
41   NSImageMap::const_iterator nsimage_iter = nsimage_cache_.find(id);
42   if (nsimage_iter != nsimage_cache_.end())
43     return nsimage_iter->second;
45   // Why don't we load the file directly into the image instead of the whole
46   // gfx::Image > native conversion?
47   // - For consistency with other platforms.
48   // - To get the generated tinted images.
49   NSImage* nsimage = nil;
50   if (theme_supplier_.get()) {
51     gfx::Image image = theme_supplier_->GetImageNamed(id);
52     if (!image.IsEmpty())
53       nsimage = image.ToNSImage();
54   }
56   // If the theme didn't override this image then load it from the resource
57   // bundle.
58   if (!nsimage) {
59     nsimage = rb_.GetNativeImageNamed(id).ToNSImage();
60   }
62   // We loaded successfully.  Cache the image.
63   if (nsimage) {
64     nsimage_cache_[id] = [nsimage retain];
65     return nsimage;
66   }
68   // We failed to retrieve the bitmap, show a debugging red square.
69   LOG(WARNING) << "Unable to load NSImage with id " << id;
70   NOTREACHED();  // Want to assert in debug mode.
72   static NSImage* empty_image = NULL;
73   if (!empty_image) {
74     // The placeholder image is bright red so people notice the problem.  This
75     // image will be leaked, but this code should never be hit.
76     NSRect image_rect = NSMakeRect(0, 0, 32, 32);
77     empty_image = [[NSImage alloc] initWithSize:image_rect.size];
78     [empty_image lockFocus];
79     [[NSColor redColor] set];
80     NSRectFill(image_rect);
81     [empty_image unlockFocus];
82   }
84   return empty_image;
87 NSColor* ThemeService::GetNSImageColorNamed(int id) const {
88   DCHECK(CalledOnValidThread());
90   // Check to see if we already have the color in the cache.
91   NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
92   if (nscolor_iter != nscolor_cache_.end())
93     return nscolor_iter->second;
95   NSImage* image = GetNSImageNamed(id);
96   if (!image)
97     return nil;
98   NSColor* image_color = [NSColor colorWithPatternImage:image];
100   // We loaded successfully.  Cache the color.
101   if (image_color)
102     nscolor_cache_[id] = [image_color retain];
104   return image_color;
107 NSColor* ThemeService::GetNSColor(int id) const {
108   DCHECK(CalledOnValidThread());
110   // Check to see if we already have the color in the cache.
111   NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
112   if (nscolor_iter != nscolor_cache_.end())
113     return nscolor_iter->second;
115   SkColor sk_color = GetColor(id);
116   NSColor* color = gfx::SkColorToCalibratedNSColor(sk_color);
118   // We loaded successfully.  Cache the color.
119   if (color)
120     nscolor_cache_[id] = [color retain];
122   return color;
125 NSColor* ThemeService::GetNSColorTint(int id) const {
126   DCHECK(CalledOnValidThread());
128   // Check to see if we already have the color in the cache.
129   NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
130   if (nscolor_iter != nscolor_cache_.end())
131     return nscolor_iter->second;
133   color_utils::HSL tint = GetTint(id);
134   NSColor* tint_color = nil;
135   if (tint.h == -1 && tint.s == -1 && tint.l == -1) {
136     tint_color = [NSColor blackColor];
137   } else {
138     CGFloat hue, saturation, brightness;
139     HSLToHSB(tint, &hue, &saturation, &brightness);
141     tint_color = [NSColor colorWithCalibratedHue:hue
142                                       saturation:saturation
143                                       brightness:brightness
144                                            alpha:1.0];
145   }
147   // We loaded successfully.  Cache the color.
148   if (tint_color)
149     nscolor_cache_[id] = [tint_color retain];
151   return tint_color;
154 NSGradient* ThemeService::GetNSGradient(int id) const {
155   DCHECK(CalledOnValidThread());
157   // Check to see if we already have the gradient in the cache.
158   NSGradientMap::const_iterator nsgradient_iter = nsgradient_cache_.find(id);
159   if (nsgradient_iter != nsgradient_cache_.end())
160     return nsgradient_iter->second;
162   NSGradient* gradient = nil;
164   // Note that we are not leaking when we assign a retained object to
165   // |gradient|; in all cases we cache it before we return.
166   switch (id) {
167     case Properties::GRADIENT_FRAME_INCOGNITO:
168     case Properties::GRADIENT_FRAME_INCOGNITO_INACTIVE: {
169       // TODO(avi): can we simplify this?
170       BOOL active = id == Properties::GRADIENT_FRAME_INCOGNITO;
171       NSColor* base_color = [NSColor colorWithCalibratedRed:83/255.0
172                                                       green:108.0/255.0
173                                                        blue:140/255.0
174                                                       alpha:1.0];
176       NSColor *start_color =
177           [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
178                                      faded:!active];
179       NSColor *end_color =
180           [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
181                                      faded:!active];
183       if (!active) {
184         start_color = [start_color gtm_colorByAdjustingLuminance:0.1
185                                                       saturation:0.5];
186         end_color = [end_color gtm_colorByAdjustingLuminance:0.1
187                                                   saturation:0.5];
188       }
190       gradient = [[NSGradient alloc] initWithStartingColor:start_color
191                                                endingColor:end_color];
192       break;
193     }
195     case Properties::GRADIENT_TOOLBAR:
196     case Properties::GRADIENT_TOOLBAR_INACTIVE: {
197       NSColor* base_color = [NSColor colorWithCalibratedWhite:0.2 alpha:1.0];
198       BOOL faded = (id == Properties::GRADIENT_TOOLBAR_INACTIVE ) ||
199                    (id == Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE);
200       NSColor* start_color =
201           [base_color gtm_colorAdjustedFor:GTMColorationLightHighlight
202                                      faded:faded];
203       NSColor* mid_color =
204           [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone
205                                      faded:faded];
206       NSColor* end_color =
207           [base_color gtm_colorAdjustedFor:GTMColorationLightShadow
208                                      faded:faded];
209       NSColor* glow_color =
210           [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra
211                                      faded:faded];
213       gradient =
214           [[NSGradient alloc] initWithColorsAndLocations:start_color, 0.0,
215                                                          mid_color, 0.25,
216                                                          end_color, 0.5,
217                                                          glow_color, 0.75,
218                                                          nil];
219       break;
220     }
222     case Properties::GRADIENT_TOOLBAR_BUTTON:
223     case Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE: {
224       NSColor* start_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.0];
225       NSColor* end_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.3];
226       gradient = [[NSGradient alloc] initWithStartingColor:start_color
227                                                endingColor:end_color];
228       break;
229     }
230     case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED:
231     case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE: {
232       NSColor* base_color = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0];
233       BOOL faded = id == Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE;
234       NSColor* start_color =
235           [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
236                                      faded:faded];
237       NSColor* end_color =
238           [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
239                                      faded:faded];
241       gradient = [[NSGradient alloc] initWithStartingColor:start_color
242                                                endingColor:end_color];
243       break;
244     }
245     default:
246       LOG(WARNING) << "Gradient request with unknown id " << id;
247       NOTREACHED();  // Want to assert in debug mode.
248       break;
249   }
251   // We loaded successfully.  Cache the gradient.
252   if (gradient)
253     nsgradient_cache_[id] = gradient;  // created retained
255   return gradient;
258 // Let all the browser views know that themes have changed in a platform way.
259 void ThemeService::NotifyPlatformThemeChanged() {
260   NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
261   [defaultCenter postNotificationName:kBrowserThemeDidChangeNotification
262                                object:[NSValue valueWithPointer:this]];
265 void ThemeService::FreePlatformCaches() {
266   DCHECK(CalledOnValidThread());
268   // Free images.
269   for (NSImageMap::iterator i = nsimage_cache_.begin();
270        i != nsimage_cache_.end(); i++) {
271     [i->second release];
272   }
273   nsimage_cache_.clear();
275   // Free colors.
276   for (NSColorMap::iterator i = nscolor_cache_.begin();
277        i != nscolor_cache_.end(); i++) {
278     [i->second release];
279   }
280   nscolor_cache_.clear();
282   // Free gradients.
283   for (NSGradientMap::iterator i = nsgradient_cache_.begin();
284        i != nsgradient_cache_.end(); i++) {
285     [i->second release];
286   }
287   nsgradient_cache_.clear();