• 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/themes/browser_theme_pack.h"
6 
7 #include "base/file_util.h"
8 #include "base/json/json_reader.h"
9 #include "base/memory/scoped_temp_dir.h"
10 #include "base/message_loop.h"
11 #include "base/path_service.h"
12 #include "base/values.h"
13 #include "chrome/browser/themes/theme_service.h"
14 #include "chrome/common/chrome_paths.h"
15 #include "content/browser/browser_thread.h"
16 #include "content/common/json_value_serializer.h"
17 #include "grit/theme_resources.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/gfx/color_utils.h"
20 
21 class BrowserThemePackTest : public ::testing::Test {
22  public:
BrowserThemePackTest()23   BrowserThemePackTest()
24       : message_loop(),
25         fake_ui_thread(BrowserThread::UI, &message_loop),
26         fake_file_thread(BrowserThread::FILE, &message_loop),
27         theme_pack_(new BrowserThemePack) {
28   }
29 
30   // Transformation for link underline colors.
BuildThirdOpacity(SkColor color_link)31   SkColor BuildThirdOpacity(SkColor color_link) {
32     return SkColorSetA(color_link, SkColorGetA(color_link) / 3);
33   }
34 
GenerateDefaultFrameColor(std::map<int,SkColor> * colors,int color,int tint)35   void GenerateDefaultFrameColor(std::map<int, SkColor>* colors,
36                                  int color, int tint) {
37     (*colors)[color] = HSLShift(
38         ThemeService::GetDefaultColor(
39             ThemeService::COLOR_FRAME),
40         ThemeService::GetDefaultTint(tint));
41   }
42 
43   // Returns a mapping from each COLOR_* constant to the default value for this
44   // constant. Callers get this map, and then modify expected values and then
45   // run the resulting thing through VerifyColorMap().
GetDefaultColorMap()46   std::map<int, SkColor> GetDefaultColorMap() {
47     std::map<int, SkColor> colors;
48     for (int i = ThemeService::COLOR_FRAME;
49          i <= ThemeService::COLOR_BUTTON_BACKGROUND; ++i) {
50       colors[i] = ThemeService::GetDefaultColor(i);
51     }
52 
53     GenerateDefaultFrameColor(&colors, ThemeService::COLOR_FRAME,
54                               ThemeService::TINT_FRAME);
55     GenerateDefaultFrameColor(&colors,
56                               ThemeService::COLOR_FRAME_INACTIVE,
57                               ThemeService::TINT_FRAME_INACTIVE);
58     GenerateDefaultFrameColor(&colors,
59                               ThemeService::COLOR_FRAME_INCOGNITO,
60                               ThemeService::TINT_FRAME_INCOGNITO);
61     GenerateDefaultFrameColor(
62         &colors,
63         ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE,
64         ThemeService::TINT_FRAME_INCOGNITO_INACTIVE);
65 
66     return colors;
67   }
68 
VerifyColorMap(const std::map<int,SkColor> & color_map)69   void VerifyColorMap(const std::map<int, SkColor>& color_map) {
70     for (std::map<int, SkColor>::const_iterator it = color_map.begin();
71          it != color_map.end(); ++it) {
72       SkColor color = ThemeService::GetDefaultColor(it->first);
73       theme_pack_->GetColor(it->first, &color);
74       EXPECT_EQ(it->second, color) << "Color id = " << it->first;
75     }
76   }
77 
LoadColorJSON(const std::string & json)78   void LoadColorJSON(const std::string& json) {
79     scoped_ptr<Value> value(base::JSONReader::Read(json, false));
80     ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY));
81     LoadColorDictionary(static_cast<DictionaryValue*>(value.get()));
82   }
83 
LoadColorDictionary(DictionaryValue * value)84   void LoadColorDictionary(DictionaryValue* value) {
85     theme_pack_->BuildColorsFromJSON(value);
86   }
87 
LoadTintJSON(const std::string & json)88   void LoadTintJSON(const std::string& json) {
89     scoped_ptr<Value> value(base::JSONReader::Read(json, false));
90     ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY));
91     LoadTintDictionary(static_cast<DictionaryValue*>(value.get()));
92   }
93 
LoadTintDictionary(DictionaryValue * value)94   void LoadTintDictionary(DictionaryValue* value) {
95     theme_pack_->BuildTintsFromJSON(value);
96   }
97 
LoadDisplayPropertiesJSON(const std::string & json)98   void LoadDisplayPropertiesJSON(const std::string& json) {
99     scoped_ptr<Value> value(base::JSONReader::Read(json, false));
100     ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY));
101     LoadDisplayPropertiesDictionary(static_cast<DictionaryValue*>(value.get()));
102   }
103 
LoadDisplayPropertiesDictionary(DictionaryValue * value)104   void LoadDisplayPropertiesDictionary(DictionaryValue* value) {
105     theme_pack_->BuildDisplayPropertiesFromJSON(value);
106   }
107 
ParseImageNamesJSON(const std::string & json,std::map<int,FilePath> * out_file_paths)108   void ParseImageNamesJSON(const std::string& json,
109                        std::map<int, FilePath>* out_file_paths) {
110     scoped_ptr<Value> value(base::JSONReader::Read(json, false));
111     ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY));
112     ParseImageNamesDictionary(static_cast<DictionaryValue*>(value.get()),
113                               out_file_paths);
114   }
115 
ParseImageNamesDictionary(DictionaryValue * value,std::map<int,FilePath> * out_file_paths)116   void ParseImageNamesDictionary(DictionaryValue* value,
117                                  std::map<int, FilePath>* out_file_paths) {
118     theme_pack_->ParseImageNamesFromJSON(value, FilePath(), out_file_paths);
119 
120     // Build the source image list for HasCustomImage().
121     theme_pack_->BuildSourceImagesArray(*out_file_paths);
122   }
123 
LoadRawBitmapsTo(const std::map<int,FilePath> & out_file_paths)124   bool LoadRawBitmapsTo(const std::map<int, FilePath>& out_file_paths) {
125     return theme_pack_->LoadRawBitmapsTo(out_file_paths,
126                                          &theme_pack_->prepared_images_);
127   }
128 
GetStarGazingPath()129   FilePath GetStarGazingPath() {
130     FilePath test_path;
131     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_path)) {
132       NOTREACHED();
133       return test_path;
134     }
135 
136     test_path = test_path.AppendASCII("profiles");
137     test_path = test_path.AppendASCII("complex_theme");
138     test_path = test_path.AppendASCII("Default");
139     test_path = test_path.AppendASCII("Extensions");
140     test_path = test_path.AppendASCII("mblmlcbknbnfebdfjnolmcapmdofhmme");
141     test_path = test_path.AppendASCII("1.1");
142     return FilePath(test_path);
143   }
144 
145   // Verifies the data in star gazing. We do this multiple times for different
146   // BrowserThemePack objects to make sure it works in generated and mmapped
147   // mode correctly.
VerifyStarGazing(BrowserThemePack * pack)148   void VerifyStarGazing(BrowserThemePack* pack) {
149     // First check that values we know exist, exist.
150     SkColor color;
151     EXPECT_TRUE(pack->GetColor(ThemeService::COLOR_BOOKMARK_TEXT,
152                                &color));
153     EXPECT_EQ(SK_ColorBLACK, color);
154 
155     EXPECT_TRUE(pack->GetColor(ThemeService::COLOR_NTP_BACKGROUND,
156                                &color));
157     EXPECT_EQ(SkColorSetRGB(57, 137, 194), color);
158 
159     color_utils::HSL expected = { 0.6, 0.553, 0.5 };
160     color_utils::HSL actual;
161     EXPECT_TRUE(pack->GetTint(ThemeService::TINT_BUTTONS, &actual));
162     EXPECT_DOUBLE_EQ(expected.h, actual.h);
163     EXPECT_DOUBLE_EQ(expected.s, actual.s);
164     EXPECT_DOUBLE_EQ(expected.l, actual.l);
165 
166     int val;
167     EXPECT_TRUE(pack->GetDisplayProperty(
168         ThemeService::NTP_BACKGROUND_ALIGNMENT, &val));
169     EXPECT_EQ(ThemeService::ALIGN_TOP, val);
170 
171     // The stargazing theme defines the following images:
172     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND));
173     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME));
174     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_NTP_BACKGROUND));
175     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
176     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TOOLBAR));
177     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_WINDOW_CONTROL_BACKGROUND));
178 
179     // Here are a few images that we shouldn't expect because even though
180     // they're included in the theme pack, they were autogenerated and
181     // therefore shouldn't show up when calling HasCustomImage().
182     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INACTIVE));
183     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
184     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE));
185     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
186 
187     // Make sure we don't have phantom data.
188     EXPECT_FALSE(pack->GetColor(ThemeService::COLOR_CONTROL_BACKGROUND,
189                                 &color));
190     EXPECT_FALSE(pack->GetTint(ThemeService::TINT_FRAME, &actual));
191   }
192 
193   MessageLoop message_loop;
194   BrowserThread fake_ui_thread;
195   BrowserThread fake_file_thread;
196 
197   scoped_refptr<BrowserThemePack> theme_pack_;
198 };
199 
200 
TEST_F(BrowserThemePackTest,DeriveUnderlineLinkColor)201 TEST_F(BrowserThemePackTest, DeriveUnderlineLinkColor) {
202   // If we specify a link color, but don't specify the underline color, the
203   // theme provider should create one.
204   std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
205                            "  \"ntp_section_link\": [128, 128, 128] }";
206   LoadColorJSON(color_json);
207 
208   std::map<int, SkColor> colors = GetDefaultColorMap();
209   SkColor link_color = SkColorSetRGB(128, 128, 128);
210   colors[ThemeService::COLOR_NTP_LINK] = link_color;
211   colors[ThemeService::COLOR_NTP_LINK_UNDERLINE] =
212       BuildThirdOpacity(link_color);
213   colors[ThemeService::COLOR_NTP_SECTION_LINK] = link_color;
214   colors[ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE] =
215       BuildThirdOpacity(link_color);
216 
217   VerifyColorMap(colors);
218 }
219 
TEST_F(BrowserThemePackTest,ProvideUnderlineLinkColor)220 TEST_F(BrowserThemePackTest, ProvideUnderlineLinkColor) {
221   // If we specify the underline color, it shouldn't try to generate one.
222   std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
223                            "  \"ntp_link_underline\": [255, 255, 255],"
224                            "  \"ntp_section_link\": [128, 128, 128],"
225                            "  \"ntp_section_link_underline\": [255, 255, 255]"
226                            "}";
227   LoadColorJSON(color_json);
228 
229   std::map<int, SkColor> colors = GetDefaultColorMap();
230   SkColor link_color = SkColorSetRGB(128, 128, 128);
231   SkColor underline_color = SkColorSetRGB(255, 255, 255);
232   colors[ThemeService::COLOR_NTP_LINK] = link_color;
233   colors[ThemeService::COLOR_NTP_LINK_UNDERLINE] = underline_color;
234   colors[ThemeService::COLOR_NTP_SECTION_LINK] = link_color;
235   colors[ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE] =
236       underline_color;
237 
238   VerifyColorMap(colors);
239 }
240 
TEST_F(BrowserThemePackTest,UseSectionColorAsNTPHeader)241 TEST_F(BrowserThemePackTest, UseSectionColorAsNTPHeader) {
242   std::string color_json = "{ \"ntp_section\": [190, 190, 190] }";
243   LoadColorJSON(color_json);
244 
245   std::map<int, SkColor> colors = GetDefaultColorMap();
246   SkColor ntp_color = SkColorSetRGB(190, 190, 190);
247   colors[ThemeService::COLOR_NTP_HEADER] = ntp_color;
248   colors[ThemeService::COLOR_NTP_SECTION] = ntp_color;
249   VerifyColorMap(colors);
250 }
251 
TEST_F(BrowserThemePackTest,ProvideNtpHeaderColor)252 TEST_F(BrowserThemePackTest, ProvideNtpHeaderColor) {
253   std::string color_json = "{ \"ntp_header\": [120, 120, 120], "
254                            "  \"ntp_section\": [190, 190, 190] }";
255   LoadColorJSON(color_json);
256 
257   std::map<int, SkColor> colors = GetDefaultColorMap();
258   SkColor ntp_header = SkColorSetRGB(120, 120, 120);
259   SkColor ntp_section = SkColorSetRGB(190, 190, 190);
260   colors[ThemeService::COLOR_NTP_HEADER] = ntp_header;
261   colors[ThemeService::COLOR_NTP_SECTION] = ntp_section;
262   VerifyColorMap(colors);
263 }
264 
TEST_F(BrowserThemePackTest,CanReadTints)265 TEST_F(BrowserThemePackTest, CanReadTints) {
266   std::string tint_json = "{ \"buttons\": [ 0.5, 0.5, 0.5 ] }";
267   LoadTintJSON(tint_json);
268 
269   color_utils::HSL expected = { 0.5, 0.5, 0.5 };
270   color_utils::HSL actual = { -1, -1, -1 };
271   EXPECT_TRUE(theme_pack_->GetTint(
272       ThemeService::TINT_BUTTONS, &actual));
273   EXPECT_DOUBLE_EQ(expected.h, actual.h);
274   EXPECT_DOUBLE_EQ(expected.s, actual.s);
275   EXPECT_DOUBLE_EQ(expected.l, actual.l);
276 }
277 
TEST_F(BrowserThemePackTest,CanReadDisplayProperties)278 TEST_F(BrowserThemePackTest, CanReadDisplayProperties) {
279   std::string json = "{ \"ntp_background_alignment\": \"bottom\", "
280                      "  \"ntp_background_repeat\": \"repeat-x\", "
281                      "  \"ntp_logo_alternate\": 0 }";
282   LoadDisplayPropertiesJSON(json);
283 
284   int out_val;
285   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
286       ThemeService::NTP_BACKGROUND_ALIGNMENT, &out_val));
287   EXPECT_EQ(ThemeService::ALIGN_BOTTOM, out_val);
288 
289   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
290       ThemeService::NTP_BACKGROUND_TILING, &out_val));
291   EXPECT_EQ(ThemeService::REPEAT_X, out_val);
292 
293   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
294       ThemeService::NTP_LOGO_ALTERNATE, &out_val));
295   EXPECT_EQ(0, out_val);
296 }
297 
TEST_F(BrowserThemePackTest,CanParsePaths)298 TEST_F(BrowserThemePackTest, CanParsePaths) {
299   std::string path_json = "{ \"theme_button_background\": \"one\", "
300                           "  \"theme_toolbar\": \"two\" }";
301   std::map<int, FilePath> out_file_paths;
302   ParseImageNamesJSON(path_json, &out_file_paths);
303 
304   EXPECT_EQ(2u, out_file_paths.size());
305   // "12" and "5" are internal constants to BrowserThemePack and are
306   // PRS_THEME_BUTTON_BACKGROUND and PRS_THEME_TOOLBAR, but they are
307   // implementation details that shouldn't be exported.
308   EXPECT_TRUE(FilePath(FILE_PATH_LITERAL("one")) == out_file_paths[12]);
309   EXPECT_TRUE(FilePath(FILE_PATH_LITERAL("two")) == out_file_paths[5]);
310 }
311 
TEST_F(BrowserThemePackTest,InvalidPathNames)312 TEST_F(BrowserThemePackTest, InvalidPathNames) {
313   std::string path_json = "{ \"wrong\": [1], "
314                           "  \"theme_button_background\": \"one\", "
315                           "  \"not_a_thing\": \"blah\" }";
316   std::map<int, FilePath> out_file_paths;
317   ParseImageNamesJSON(path_json, &out_file_paths);
318 
319   // We should have only parsed one valid path out of that mess above.
320   EXPECT_EQ(1u, out_file_paths.size());
321 }
322 
TEST_F(BrowserThemePackTest,InvalidColors)323 TEST_F(BrowserThemePackTest, InvalidColors) {
324   std::string invalid_color = "{ \"toolbar\": [\"dog\", \"cat\", [12]], "
325                               "  \"sound\": \"woof\" }";
326   LoadColorJSON(invalid_color);
327   std::map<int, SkColor> colors = GetDefaultColorMap();
328   VerifyColorMap(colors);
329 }
330 
TEST_F(BrowserThemePackTest,InvalidTints)331 TEST_F(BrowserThemePackTest, InvalidTints) {
332   std::string invalid_tints = "{ \"buttons\": [ \"dog\", \"cat\", [\"x\"]], "
333                               "  \"invalid\": \"entry\" }";
334   LoadTintJSON(invalid_tints);
335 
336   // We shouldn't have a buttons tint, as it was invalid.
337   color_utils::HSL actual = { -1, -1, -1 };
338   EXPECT_FALSE(theme_pack_->GetTint(ThemeService::TINT_BUTTONS,
339                                     &actual));
340 }
341 
TEST_F(BrowserThemePackTest,InvalidDisplayProperties)342 TEST_F(BrowserThemePackTest, InvalidDisplayProperties) {
343   std::string invalid_properties = "{ \"ntp_background_alignment\": [15], "
344                                    "  \"junk\": [15.3] }";
345   LoadDisplayPropertiesJSON(invalid_properties);
346 
347   int out_val;
348   EXPECT_FALSE(theme_pack_->GetDisplayProperty(
349       ThemeService::NTP_BACKGROUND_ALIGNMENT, &out_val));
350 }
351 
352 // These three tests should just not cause a segmentation fault.
TEST_F(BrowserThemePackTest,NullPaths)353 TEST_F(BrowserThemePackTest, NullPaths) {
354   std::map<int, FilePath> out_file_paths;
355   ParseImageNamesDictionary(NULL, &out_file_paths);
356 }
357 
TEST_F(BrowserThemePackTest,NullTints)358 TEST_F(BrowserThemePackTest, NullTints) {
359   LoadTintDictionary(NULL);
360 }
361 
TEST_F(BrowserThemePackTest,NullColors)362 TEST_F(BrowserThemePackTest, NullColors) {
363   LoadColorDictionary(NULL);
364 }
365 
TEST_F(BrowserThemePackTest,NullDisplayProperties)366 TEST_F(BrowserThemePackTest, NullDisplayProperties) {
367   LoadDisplayPropertiesDictionary(NULL);
368 }
369 
TEST_F(BrowserThemePackTest,TestHasCustomImage)370 TEST_F(BrowserThemePackTest, TestHasCustomImage) {
371   // HasCustomImage should only return true for images that exist in the
372   // extension and not for autogenerated images.
373   std::string images = "{ \"theme_frame\": \"one\" }";
374   std::map<int, FilePath> out_file_paths;
375   ParseImageNamesJSON(images, &out_file_paths);
376 
377   EXPECT_TRUE(theme_pack_->HasCustomImage(IDR_THEME_FRAME));
378   EXPECT_FALSE(theme_pack_->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
379 }
380 
TEST_F(BrowserThemePackTest,TestNonExistantImages)381 TEST_F(BrowserThemePackTest, TestNonExistantImages) {
382   std::string images = "{ \"theme_frame\": \"does_not_exist\" }";
383   std::map<int, FilePath> out_file_paths;
384   ParseImageNamesJSON(images, &out_file_paths);
385 
386   EXPECT_FALSE(LoadRawBitmapsTo(out_file_paths));
387 }
388 
389 // TODO(erg): This test should actually test more of the built resources from
390 // the extension data, but for now, exists so valgrind can test some of the
391 // tricky memory stuff that BrowserThemePack does.
TEST_F(BrowserThemePackTest,CanBuildAndReadPack)392 TEST_F(BrowserThemePackTest, CanBuildAndReadPack) {
393   ScopedTempDir dir;
394   ASSERT_TRUE(dir.CreateUniqueTempDir());
395   FilePath file = dir.path().Append(FILE_PATH_LITERAL("data.pak"));
396 
397   // Part 1: Build the pack from an extension.
398   {
399     FilePath star_gazing_path = GetStarGazingPath();
400     FilePath manifest_path =
401         star_gazing_path.AppendASCII("manifest.json");
402     std::string error;
403     JSONFileValueSerializer serializer(manifest_path);
404     scoped_ptr<DictionaryValue> valid_value(
405         static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error)));
406     EXPECT_EQ("", error);
407     ASSERT_TRUE(valid_value.get());
408     scoped_refptr<Extension> extension(Extension::Create(
409         star_gazing_path, Extension::INVALID, *valid_value,
410         Extension::REQUIRE_KEY | Extension::STRICT_ERROR_CHECKS, &error));
411     ASSERT_TRUE(extension.get());
412     ASSERT_EQ("", error);
413 
414     scoped_refptr<BrowserThemePack> pack(
415         BrowserThemePack::BuildFromExtension(extension.get()));
416     ASSERT_TRUE(pack.get());
417     ASSERT_TRUE(pack->WriteToDisk(file));
418     VerifyStarGazing(pack.get());
419   }
420 
421   // Part 2: Try to read back the data pack that we just wrote to disk.
422   {
423     scoped_refptr<BrowserThemePack> pack =
424         BrowserThemePack::BuildFromDataPack(
425             file, "mblmlcbknbnfebdfjnolmcapmdofhmme");
426     ASSERT_TRUE(pack.get());
427     VerifyStarGazing(pack.get());
428   }
429 }
430