Home | History | Annotate | Download | only in themes
      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/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 #include "ui/gfx/image/image.h"
     20 #include "ui/gfx/image/image_skia.h"
     21 #include "ui/gfx/image/image_skia_rep.h"
     22 
     23 using content::BrowserThread;
     24 using extensions::Extension;
     25 
     26 // Maps scale factors (enum values) to file path.
     27 // A similar typedef in BrowserThemePack is private.
     28 typedef std::map<ui::ScaleFactor, base::FilePath> TestScaleFactorToFileMap;
     29 
     30 // Maps image ids to maps of scale factors to file paths.
     31 // A similar typedef in BrowserThemePack is private.
     32 typedef std::map<int, TestScaleFactorToFileMap> TestFilePathMap;
     33 
     34 class BrowserThemePackTest : public ::testing::Test {
     35  public:
     36   BrowserThemePackTest()
     37       : message_loop(),
     38         fake_ui_thread(BrowserThread::UI, &message_loop),
     39         fake_file_thread(BrowserThread::FILE, &message_loop) {
     40     std::vector<ui::ScaleFactor> scale_factors;
     41     scale_factors.push_back(ui::SCALE_FACTOR_100P);
     42     scale_factors.push_back(ui::SCALE_FACTOR_200P);
     43     scoped_set_supported_scale_factors_.reset(
     44       new ui::test::ScopedSetSupportedScaleFactors(scale_factors));
     45     theme_pack_ = new BrowserThemePack();
     46   }
     47 
     48   // Transformation for link underline colors.
     49   SkColor BuildThirdOpacity(SkColor color_link) {
     50     return SkColorSetA(color_link, SkColorGetA(color_link) / 3);
     51   }
     52 
     53   void GenerateDefaultFrameColor(std::map<int, SkColor>* colors,
     54                                  int color, int tint) {
     55     (*colors)[color] = HSLShift(
     56         ThemeProperties::GetDefaultColor(
     57             ThemeProperties::COLOR_FRAME),
     58         ThemeProperties::GetDefaultTint(tint));
     59   }
     60 
     61   // Returns a mapping from each COLOR_* constant to the default value for this
     62   // constant. Callers get this map, and then modify expected values and then
     63   // run the resulting thing through VerifyColorMap().
     64   std::map<int, SkColor> GetDefaultColorMap() {
     65     std::map<int, SkColor> colors;
     66     for (int i = ThemeProperties::COLOR_FRAME;
     67          i <= ThemeProperties::COLOR_BUTTON_BACKGROUND; ++i) {
     68       colors[i] = ThemeProperties::GetDefaultColor(i);
     69     }
     70 
     71     GenerateDefaultFrameColor(&colors, ThemeProperties::COLOR_FRAME,
     72                               ThemeProperties::TINT_FRAME);
     73     GenerateDefaultFrameColor(&colors,
     74                               ThemeProperties::COLOR_FRAME_INACTIVE,
     75                               ThemeProperties::TINT_FRAME_INACTIVE);
     76     GenerateDefaultFrameColor(&colors,
     77                               ThemeProperties::COLOR_FRAME_INCOGNITO,
     78                               ThemeProperties::TINT_FRAME_INCOGNITO);
     79     GenerateDefaultFrameColor(
     80         &colors,
     81         ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
     82         ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE);
     83 
     84     return colors;
     85   }
     86 
     87   void VerifyColorMap(const std::map<int, SkColor>& color_map) {
     88     for (std::map<int, SkColor>::const_iterator it = color_map.begin();
     89          it != color_map.end(); ++it) {
     90       SkColor color = ThemeProperties::GetDefaultColor(it->first);
     91       theme_pack_->GetColor(it->first, &color);
     92       EXPECT_EQ(it->second, color) << "Color id = " << it->first;
     93     }
     94   }
     95 
     96   void LoadColorJSON(const std::string& json) {
     97     scoped_ptr<base::Value> value(base::JSONReader::Read(json));
     98     ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
     99     LoadColorDictionary(static_cast<base::DictionaryValue*>(value.get()));
    100   }
    101 
    102   void LoadColorDictionary(base::DictionaryValue* value) {
    103     theme_pack_->BuildColorsFromJSON(value);
    104   }
    105 
    106   void LoadTintJSON(const std::string& json) {
    107     scoped_ptr<base::Value> value(base::JSONReader::Read(json));
    108     ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
    109     LoadTintDictionary(static_cast<base::DictionaryValue*>(value.get()));
    110   }
    111 
    112   void LoadTintDictionary(base::DictionaryValue* value) {
    113     theme_pack_->BuildTintsFromJSON(value);
    114   }
    115 
    116   void LoadDisplayPropertiesJSON(const std::string& json) {
    117     scoped_ptr<base::Value> value(base::JSONReader::Read(json));
    118     ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
    119     LoadDisplayPropertiesDictionary(
    120         static_cast<base::DictionaryValue*>(value.get()));
    121   }
    122 
    123   void LoadDisplayPropertiesDictionary(base::DictionaryValue* value) {
    124     theme_pack_->BuildDisplayPropertiesFromJSON(value);
    125   }
    126 
    127   void ParseImageNamesJSON(const std::string& json,
    128                            TestFilePathMap* out_file_paths) {
    129     scoped_ptr<base::Value> value(base::JSONReader::Read(json));
    130     ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
    131     ParseImageNamesDictionary(static_cast<base::DictionaryValue*>(value.get()),
    132                               out_file_paths);
    133   }
    134 
    135   void ParseImageNamesDictionary(
    136       base::DictionaryValue* value,
    137       TestFilePathMap* out_file_paths) {
    138     theme_pack_->ParseImageNamesFromJSON(value, base::FilePath(),
    139                                          out_file_paths);
    140 
    141     // Build the source image list for HasCustomImage().
    142     theme_pack_->BuildSourceImagesArray(*out_file_paths);
    143   }
    144 
    145   bool LoadRawBitmapsTo(const TestFilePathMap& out_file_paths) {
    146     return theme_pack_->LoadRawBitmapsTo(out_file_paths,
    147                                          &theme_pack_->images_on_ui_thread_);
    148   }
    149 
    150   // This function returns void in order to be able use ASSERT_...
    151   // The BrowserThemePack is returned in |pack|.
    152   void BuildFromUnpackedExtension(const base::FilePath& extension_path,
    153                                   scoped_refptr<BrowserThemePack>& pack) {
    154     base::FilePath manifest_path =
    155         extension_path.AppendASCII("manifest.json");
    156     std::string error;
    157     JSONFileValueSerializer serializer(manifest_path);
    158     scoped_ptr<base::DictionaryValue> valid_value(
    159         static_cast<base::DictionaryValue*>(
    160             serializer.Deserialize(NULL, &error)));
    161     EXPECT_EQ("", error);
    162     ASSERT_TRUE(valid_value.get());
    163     scoped_refptr<Extension> extension(
    164         Extension::Create(
    165             extension_path,
    166             extensions::Manifest::INVALID_LOCATION,
    167             *valid_value,
    168             Extension::REQUIRE_KEY,
    169             &error));
    170     ASSERT_TRUE(extension.get());
    171     ASSERT_EQ("", error);
    172     pack = BrowserThemePack::BuildFromExtension(extension.get());
    173     ASSERT_TRUE(pack.get());
    174   }
    175 
    176   base::FilePath GetStarGazingPath() {
    177     base::FilePath test_path;
    178     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_path)) {
    179       NOTREACHED();
    180       return test_path;
    181     }
    182 
    183     test_path = test_path.AppendASCII("profiles");
    184     test_path = test_path.AppendASCII("profile_with_complex_theme");
    185     test_path = test_path.AppendASCII("Default");
    186     test_path = test_path.AppendASCII("Extensions");
    187     test_path = test_path.AppendASCII("mblmlcbknbnfebdfjnolmcapmdofhmme");
    188     test_path = test_path.AppendASCII("1.1");
    189     return base::FilePath(test_path);
    190   }
    191 
    192   base::FilePath GetHiDpiThemePath() {
    193     base::FilePath test_path;
    194     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_path)) {
    195       NOTREACHED();
    196       return test_path;
    197     }
    198     test_path = test_path.AppendASCII("extensions");
    199     test_path = test_path.AppendASCII("theme_hidpi");
    200     return base::FilePath(test_path);
    201   }
    202 
    203   // Verifies the data in star gazing. We do this multiple times for different
    204   // BrowserThemePack objects to make sure it works in generated and mmapped
    205   // mode correctly.
    206   void VerifyStarGazing(BrowserThemePack* pack) {
    207     // First check that values we know exist, exist.
    208     SkColor color;
    209     EXPECT_TRUE(pack->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT,
    210                                &color));
    211     EXPECT_EQ(SK_ColorBLACK, color);
    212 
    213     EXPECT_TRUE(pack->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND,
    214                                &color));
    215     EXPECT_EQ(SkColorSetRGB(57, 137, 194), color);
    216 
    217     color_utils::HSL expected = { 0.6, 0.553, 0.5 };
    218     color_utils::HSL actual;
    219     EXPECT_TRUE(pack->GetTint(ThemeProperties::TINT_BUTTONS, &actual));
    220     EXPECT_DOUBLE_EQ(expected.h, actual.h);
    221     EXPECT_DOUBLE_EQ(expected.s, actual.s);
    222     EXPECT_DOUBLE_EQ(expected.l, actual.l);
    223 
    224     int val;
    225     EXPECT_TRUE(pack->GetDisplayProperty(
    226         ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &val));
    227     EXPECT_EQ(ThemeProperties::ALIGN_TOP, val);
    228 
    229     // The stargazing theme defines the following images:
    230     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND));
    231     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME));
    232     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_NTP_BACKGROUND));
    233     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
    234     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TOOLBAR));
    235     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_WINDOW_CONTROL_BACKGROUND));
    236 
    237     // Here are a few images that we shouldn't expect because even though
    238     // they're included in the theme pack, they were autogenerated and
    239     // therefore shouldn't show up when calling HasCustomImage().
    240     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INACTIVE));
    241     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
    242     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE));
    243 #if !defined(OS_MACOSX)
    244     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
    245 #endif
    246 
    247     // Make sure we don't have phantom data.
    248     EXPECT_FALSE(pack->GetColor(ThemeProperties::COLOR_CONTROL_BACKGROUND,
    249                                 &color));
    250     EXPECT_FALSE(pack->GetTint(ThemeProperties::TINT_FRAME, &actual));
    251   }
    252 
    253   void VerifyHiDpiTheme(BrowserThemePack* pack) {
    254     // The high DPI theme defines the following images:
    255     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME));
    256     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INACTIVE));
    257     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
    258     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE));
    259     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TOOLBAR));
    260 
    261     // The high DPI theme does not define the following images:
    262     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
    263 #if !defined(OS_MACOSX)
    264     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
    265 #endif
    266     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_V));
    267     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_NTP_BACKGROUND));
    268     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_OVERLAY));
    269     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_OVERLAY_INACTIVE));
    270     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND));
    271     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION));
    272     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_WINDOW_CONTROL_BACKGROUND));
    273 
    274     // Compare some known pixel colors at know locations for a theme
    275     // image where two different PNG files were specified for scales 100%
    276     // and 200% respectively.
    277     int idr = IDR_THEME_FRAME;
    278     gfx::Image image = pack->GetImageNamed(idr);
    279     EXPECT_FALSE(image.IsEmpty());
    280     const gfx::ImageSkia* image_skia = image.ToImageSkia();
    281     ASSERT_TRUE(image_skia);
    282     // Scale 100%.
    283     const gfx::ImageSkiaRep& rep1 = image_skia->GetRepresentation(1.0f);
    284     ASSERT_FALSE(rep1.is_null());
    285     EXPECT_EQ(80, rep1.sk_bitmap().width());
    286     EXPECT_EQ(80, rep1.sk_bitmap().height());
    287     rep1.sk_bitmap().lockPixels();
    288     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor( 4,  4));
    289     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor( 8,  8));
    290     EXPECT_EQ(SkColorSetRGB(  0, 241, 237), rep1.sk_bitmap().getColor(16, 16));
    291     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor(24, 24));
    292     EXPECT_EQ(SkColorSetRGB(  0, 241, 237), rep1.sk_bitmap().getColor(32, 32));
    293     rep1.sk_bitmap().unlockPixels();
    294     // Scale 200%.
    295     const gfx::ImageSkiaRep& rep2 = image_skia->GetRepresentation(2.0f);
    296     ASSERT_FALSE(rep2.is_null());
    297     EXPECT_EQ(160, rep2.sk_bitmap().width());
    298     EXPECT_EQ(160, rep2.sk_bitmap().height());
    299     rep2.sk_bitmap().lockPixels();
    300     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep2.sk_bitmap().getColor( 4,  4));
    301     EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor( 8,  8));
    302     EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor(16, 16));
    303     EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor(24, 24));
    304     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep2.sk_bitmap().getColor(32, 32));
    305     rep2.sk_bitmap().unlockPixels();
    306 
    307     // TODO(sschmitz): I plan to remove the following (to the end of the fct)
    308     // Reason: this test may be brittle. It depends on details of how we scale
    309     // an 100% image to an 200% image. If there's filtering etc, this test would
    310     // break. Also High DPI is new, but scaling from 100% to 200% is not new
    311     // and need not be tested here. But in the interrim it is useful to verify
    312     // that this image was scaled and did not appear in the input.
    313 
    314     // Compare pixel colors and locations for a theme image that had
    315     // only one PNG file specified (for scale 100%). The representation
    316     // for scale of 200% was computed.
    317     idr = IDR_THEME_FRAME_INCOGNITO_INACTIVE;
    318     image = pack->GetImageNamed(idr);
    319     EXPECT_FALSE(image.IsEmpty());
    320     image_skia = image.ToImageSkia();
    321     ASSERT_TRUE(image_skia);
    322     // Scale 100%.
    323     const gfx::ImageSkiaRep& rep3 = image_skia->GetRepresentation(1.0f);
    324     ASSERT_FALSE(rep3.is_null());
    325     EXPECT_EQ(80, rep3.sk_bitmap().width());
    326     EXPECT_EQ(80, rep3.sk_bitmap().height());
    327     rep3.sk_bitmap().lockPixels();
    328     // We take samples of colors and locations along the diagonal whenever
    329     // the color changes. Note these colors are slightly different from
    330     // the input PNG file due to input processing.
    331     std::vector<std::pair<int, SkColor> > normal;
    332     int xy = 0;
    333     SkColor color = rep3.sk_bitmap().getColor(xy, xy);
    334     normal.push_back(std::make_pair(xy, color));
    335     for (int xy = 0; xy < 40; ++xy) {
    336       SkColor next_color = rep3.sk_bitmap().getColor(xy, xy);
    337       if (next_color != color) {
    338         color = next_color;
    339         normal.push_back(std::make_pair(xy, color));
    340       }
    341     }
    342     EXPECT_EQ(static_cast<size_t>(9), normal.size());
    343     rep3.sk_bitmap().unlockPixels();
    344     // Scale 200%.
    345     const gfx::ImageSkiaRep& rep4 = image_skia->GetRepresentation(2.0f);
    346     ASSERT_FALSE(rep4.is_null());
    347     EXPECT_EQ(160, rep4.sk_bitmap().width());
    348     EXPECT_EQ(160, rep4.sk_bitmap().height());
    349     rep4.sk_bitmap().lockPixels();
    350     // We expect the same colors and at locations scaled by 2
    351     // since this bitmap was scaled by 2.
    352     for (size_t i = 0; i < normal.size(); ++i) {
    353       int xy = 2 * normal[i].first;
    354       SkColor color = normal[i].second;
    355       EXPECT_EQ(color, rep4.sk_bitmap().getColor(xy, xy));
    356     }
    357     rep4.sk_bitmap().unlockPixels();
    358   }
    359 
    360   base::MessageLoop message_loop;
    361   content::TestBrowserThread fake_ui_thread;
    362   content::TestBrowserThread fake_file_thread;
    363 
    364   typedef scoped_ptr<ui::test::ScopedSetSupportedScaleFactors>
    365       ScopedSetSupportedScaleFactors;
    366   ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
    367   scoped_refptr<BrowserThemePack> theme_pack_;
    368 };
    369 
    370 
    371 TEST_F(BrowserThemePackTest, DeriveUnderlineLinkColor) {
    372   // If we specify a link color, but don't specify the underline color, the
    373   // theme provider should create one.
    374   std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
    375                            "  \"ntp_section_link\": [128, 128, 128] }";
    376   LoadColorJSON(color_json);
    377 
    378   std::map<int, SkColor> colors = GetDefaultColorMap();
    379   SkColor link_color = SkColorSetRGB(128, 128, 128);
    380   colors[ThemeProperties::COLOR_NTP_LINK] = link_color;
    381   colors[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] =
    382       BuildThirdOpacity(link_color);
    383   colors[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
    384   colors[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] =
    385       BuildThirdOpacity(link_color);
    386 
    387   VerifyColorMap(colors);
    388 }
    389 
    390 TEST_F(BrowserThemePackTest, ProvideUnderlineLinkColor) {
    391   // If we specify the underline color, it shouldn't try to generate one.
    392   std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
    393                            "  \"ntp_link_underline\": [255, 255, 255],"
    394                            "  \"ntp_section_link\": [128, 128, 128],"
    395                            "  \"ntp_section_link_underline\": [255, 255, 255]"
    396                            "}";
    397   LoadColorJSON(color_json);
    398 
    399   std::map<int, SkColor> colors = GetDefaultColorMap();
    400   SkColor link_color = SkColorSetRGB(128, 128, 128);
    401   SkColor underline_color = SkColorSetRGB(255, 255, 255);
    402   colors[ThemeProperties::COLOR_NTP_LINK] = link_color;
    403   colors[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] = underline_color;
    404   colors[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
    405   colors[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] =
    406       underline_color;
    407 
    408   VerifyColorMap(colors);
    409 }
    410 
    411 TEST_F(BrowserThemePackTest, UseSectionColorAsNTPHeader) {
    412   std::string color_json = "{ \"ntp_section\": [190, 190, 190] }";
    413   LoadColorJSON(color_json);
    414 
    415   std::map<int, SkColor> colors = GetDefaultColorMap();
    416   SkColor ntp_color = SkColorSetRGB(190, 190, 190);
    417   colors[ThemeProperties::COLOR_NTP_HEADER] = ntp_color;
    418   colors[ThemeProperties::COLOR_NTP_SECTION] = ntp_color;
    419   VerifyColorMap(colors);
    420 }
    421 
    422 TEST_F(BrowserThemePackTest, ProvideNtpHeaderColor) {
    423   std::string color_json = "{ \"ntp_header\": [120, 120, 120], "
    424                            "  \"ntp_section\": [190, 190, 190] }";
    425   LoadColorJSON(color_json);
    426 
    427   std::map<int, SkColor> colors = GetDefaultColorMap();
    428   SkColor ntp_header = SkColorSetRGB(120, 120, 120);
    429   SkColor ntp_section = SkColorSetRGB(190, 190, 190);
    430   colors[ThemeProperties::COLOR_NTP_HEADER] = ntp_header;
    431   colors[ThemeProperties::COLOR_NTP_SECTION] = ntp_section;
    432   VerifyColorMap(colors);
    433 }
    434 
    435 TEST_F(BrowserThemePackTest, CanReadTints) {
    436   std::string tint_json = "{ \"buttons\": [ 0.5, 0.5, 0.5 ] }";
    437   LoadTintJSON(tint_json);
    438 
    439   color_utils::HSL expected = { 0.5, 0.5, 0.5 };
    440   color_utils::HSL actual = { -1, -1, -1 };
    441   EXPECT_TRUE(theme_pack_->GetTint(
    442       ThemeProperties::TINT_BUTTONS, &actual));
    443   EXPECT_DOUBLE_EQ(expected.h, actual.h);
    444   EXPECT_DOUBLE_EQ(expected.s, actual.s);
    445   EXPECT_DOUBLE_EQ(expected.l, actual.l);
    446 }
    447 
    448 TEST_F(BrowserThemePackTest, CanReadDisplayProperties) {
    449   std::string json = "{ \"ntp_background_alignment\": \"bottom\", "
    450                      "  \"ntp_background_repeat\": \"repeat-x\", "
    451                      "  \"ntp_logo_alternate\": 0 }";
    452   LoadDisplayPropertiesJSON(json);
    453 
    454   int out_val;
    455   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
    456       ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &out_val));
    457   EXPECT_EQ(ThemeProperties::ALIGN_BOTTOM, out_val);
    458 
    459   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
    460       ThemeProperties::NTP_BACKGROUND_TILING, &out_val));
    461   EXPECT_EQ(ThemeProperties::REPEAT_X, out_val);
    462 
    463   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
    464       ThemeProperties::NTP_LOGO_ALTERNATE, &out_val));
    465   EXPECT_EQ(0, out_val);
    466 }
    467 
    468 TEST_F(BrowserThemePackTest, CanParsePaths) {
    469   std::string path_json = "{ \"theme_button_background\": \"one\", "
    470                           "  \"theme_toolbar\": \"two\" }";
    471   TestFilePathMap out_file_paths;
    472   ParseImageNamesJSON(path_json, &out_file_paths);
    473 
    474   size_t expected_file_paths = 2u;
    475 #if defined(USE_ASH) && !defined(OS_CHROMEOS)
    476   // On desktop builds with ash, additional theme paths are generated to map to
    477   // the special resource ids like IDR_THEME_FRAME_DESKTOP, etc
    478   expected_file_paths = 3u;
    479 #endif
    480   EXPECT_EQ(expected_file_paths, out_file_paths.size());
    481   // "12" and "5" are internal constants to BrowserThemePack and are
    482   // PRS_THEME_BUTTON_BACKGROUND and PRS_THEME_TOOLBAR, but they are
    483   // implementation details that shouldn't be exported.
    484   // By default the scale factor is for 100%.
    485   EXPECT_TRUE(base::FilePath(FILE_PATH_LITERAL("one")) ==
    486               out_file_paths[12][ui::SCALE_FACTOR_100P]);
    487   EXPECT_TRUE(base::FilePath(FILE_PATH_LITERAL("two")) ==
    488               out_file_paths[5][ui::SCALE_FACTOR_100P]);
    489 }
    490 
    491 TEST_F(BrowserThemePackTest, InvalidPathNames) {
    492   std::string path_json = "{ \"wrong\": [1], "
    493                           "  \"theme_button_background\": \"one\", "
    494                           "  \"not_a_thing\": \"blah\" }";
    495   TestFilePathMap out_file_paths;
    496   ParseImageNamesJSON(path_json, &out_file_paths);
    497 
    498   // We should have only parsed one valid path out of that mess above.
    499   EXPECT_EQ(1u, out_file_paths.size());
    500 }
    501 
    502 TEST_F(BrowserThemePackTest, InvalidColors) {
    503   std::string invalid_color = "{ \"toolbar\": [\"dog\", \"cat\", [12]], "
    504                               "  \"sound\": \"woof\" }";
    505   LoadColorJSON(invalid_color);
    506   std::map<int, SkColor> colors = GetDefaultColorMap();
    507   VerifyColorMap(colors);
    508 }
    509 
    510 TEST_F(BrowserThemePackTest, InvalidTints) {
    511   std::string invalid_tints = "{ \"buttons\": [ \"dog\", \"cat\", [\"x\"]], "
    512                               "  \"invalid\": \"entry\" }";
    513   LoadTintJSON(invalid_tints);
    514 
    515   // We shouldn't have a buttons tint, as it was invalid.
    516   color_utils::HSL actual = { -1, -1, -1 };
    517   EXPECT_FALSE(theme_pack_->GetTint(ThemeProperties::TINT_BUTTONS,
    518                                     &actual));
    519 }
    520 
    521 TEST_F(BrowserThemePackTest, InvalidDisplayProperties) {
    522   std::string invalid_properties = "{ \"ntp_background_alignment\": [15], "
    523                                    "  \"junk\": [15.3] }";
    524   LoadDisplayPropertiesJSON(invalid_properties);
    525 
    526   int out_val;
    527   EXPECT_FALSE(theme_pack_->GetDisplayProperty(
    528       ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &out_val));
    529 }
    530 
    531 // These three tests should just not cause a segmentation fault.
    532 TEST_F(BrowserThemePackTest, NullPaths) {
    533   TestFilePathMap out_file_paths;
    534   ParseImageNamesDictionary(NULL, &out_file_paths);
    535 }
    536 
    537 TEST_F(BrowserThemePackTest, NullTints) {
    538   LoadTintDictionary(NULL);
    539 }
    540 
    541 TEST_F(BrowserThemePackTest, NullColors) {
    542   LoadColorDictionary(NULL);
    543 }
    544 
    545 TEST_F(BrowserThemePackTest, NullDisplayProperties) {
    546   LoadDisplayPropertiesDictionary(NULL);
    547 }
    548 
    549 TEST_F(BrowserThemePackTest, TestHasCustomImage) {
    550   // HasCustomImage should only return true for images that exist in the
    551   // extension and not for autogenerated images.
    552   std::string images = "{ \"theme_frame\": \"one\" }";
    553   TestFilePathMap out_file_paths;
    554   ParseImageNamesJSON(images, &out_file_paths);
    555 
    556   EXPECT_TRUE(theme_pack_->HasCustomImage(IDR_THEME_FRAME));
    557   EXPECT_FALSE(theme_pack_->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
    558 }
    559 
    560 TEST_F(BrowserThemePackTest, TestNonExistantImages) {
    561   std::string images = "{ \"theme_frame\": \"does_not_exist\" }";
    562   TestFilePathMap out_file_paths;
    563   ParseImageNamesJSON(images, &out_file_paths);
    564 
    565   EXPECT_FALSE(LoadRawBitmapsTo(out_file_paths));
    566 }
    567 
    568 // TODO(erg): This test should actually test more of the built resources from
    569 // the extension data, but for now, exists so valgrind can test some of the
    570 // tricky memory stuff that BrowserThemePack does.
    571 TEST_F(BrowserThemePackTest, CanBuildAndReadPack) {
    572   base::ScopedTempDir dir;
    573   ASSERT_TRUE(dir.CreateUniqueTempDir());
    574   base::FilePath file = dir.path().AppendASCII("data.pak");
    575 
    576   // Part 1: Build the pack from an extension.
    577   {
    578     base::FilePath star_gazing_path = GetStarGazingPath();
    579     scoped_refptr<BrowserThemePack> pack;
    580     BuildFromUnpackedExtension(star_gazing_path, pack);
    581     ASSERT_TRUE(pack->WriteToDisk(file));
    582     VerifyStarGazing(pack.get());
    583   }
    584 
    585   // Part 2: Try to read back the data pack that we just wrote to disk.
    586   {
    587     scoped_refptr<BrowserThemePack> pack =
    588         BrowserThemePack::BuildFromDataPack(
    589             file, "mblmlcbknbnfebdfjnolmcapmdofhmme");
    590     ASSERT_TRUE(pack.get());
    591     VerifyStarGazing(pack.get());
    592   }
    593 }
    594 
    595 TEST_F(BrowserThemePackTest, HiDpiThemeTest) {
    596   base::ScopedTempDir dir;
    597   ASSERT_TRUE(dir.CreateUniqueTempDir());
    598   base::FilePath file = dir.path().AppendASCII("theme_data.pak");
    599 
    600   // Part 1: Build the pack from an extension.
    601   {
    602     base::FilePath hidpi_path = GetHiDpiThemePath();
    603     scoped_refptr<BrowserThemePack> pack;
    604     BuildFromUnpackedExtension(hidpi_path, pack);
    605     ASSERT_TRUE(pack->WriteToDisk(file));
    606     VerifyHiDpiTheme(pack.get());
    607   }
    608 
    609   // Part 2: Try to read back the data pack that we just wrote to disk.
    610   {
    611     scoped_refptr<BrowserThemePack> pack =
    612         BrowserThemePack::BuildFromDataPack(file, "gllekhaobjnhgeag");
    613     ASSERT_TRUE(pack.get());
    614     VerifyHiDpiTheme(pack.get());
    615   }
    616 }
    617