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