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     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
    244 
    245     // Make sure we don't have phantom data.
    246     EXPECT_FALSE(pack->GetColor(ThemeProperties::COLOR_CONTROL_BACKGROUND,
    247                                 &color));
    248     EXPECT_FALSE(pack->GetTint(ThemeProperties::TINT_FRAME, &actual));
    249   }
    250 
    251   void VerifyHiDpiTheme(BrowserThemePack* pack) {
    252     // The high DPI theme defines the following images:
    253     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME));
    254     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INACTIVE));
    255     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
    256     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE));
    257     EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TOOLBAR));
    258 
    259     // The high DPI theme does not define the following images:
    260     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
    261     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
    262     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
    263     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_V));
    264     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_NTP_BACKGROUND));
    265     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_OVERLAY));
    266     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_OVERLAY_INACTIVE));
    267     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND));
    268     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION));
    269     EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_WINDOW_CONTROL_BACKGROUND));
    270 
    271     // Compare some known pixel colors at know locations for a theme
    272     // image where two different PNG files were specified for scales 100%
    273     // and 200% respectively.
    274     int idr = IDR_THEME_FRAME;
    275     gfx::Image image = pack->GetImageNamed(idr);
    276     EXPECT_FALSE(image.IsEmpty());
    277     const gfx::ImageSkia* image_skia = image.ToImageSkia();
    278     ASSERT_TRUE(image_skia);
    279     // Scale 100%.
    280     const gfx::ImageSkiaRep& rep1 = image_skia->GetRepresentation(1.0f);
    281     ASSERT_FALSE(rep1.is_null());
    282     EXPECT_EQ(80, rep1.sk_bitmap().width());
    283     EXPECT_EQ(80, rep1.sk_bitmap().height());
    284     rep1.sk_bitmap().lockPixels();
    285     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor( 4,  4));
    286     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor( 8,  8));
    287     EXPECT_EQ(SkColorSetRGB(  0, 241, 237), rep1.sk_bitmap().getColor(16, 16));
    288     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor(24, 24));
    289     EXPECT_EQ(SkColorSetRGB(  0, 241, 237), rep1.sk_bitmap().getColor(32, 32));
    290     rep1.sk_bitmap().unlockPixels();
    291     // Scale 200%.
    292     const gfx::ImageSkiaRep& rep2 = image_skia->GetRepresentation(2.0f);
    293     ASSERT_FALSE(rep2.is_null());
    294     EXPECT_EQ(160, rep2.sk_bitmap().width());
    295     EXPECT_EQ(160, rep2.sk_bitmap().height());
    296     rep2.sk_bitmap().lockPixels();
    297     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep2.sk_bitmap().getColor( 4,  4));
    298     EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor( 8,  8));
    299     EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor(16, 16));
    300     EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor(24, 24));
    301     EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep2.sk_bitmap().getColor(32, 32));
    302     rep2.sk_bitmap().unlockPixels();
    303 
    304     // TODO(sschmitz): I plan to remove the following (to the end of the fct)
    305     // Reason: this test may be brittle. It depends on details of how we scale
    306     // an 100% image to an 200% image. If there's filtering etc, this test would
    307     // break. Also High DPI is new, but scaling from 100% to 200% is not new
    308     // and need not be tested here. But in the interrim it is useful to verify
    309     // that this image was scaled and did not appear in the input.
    310 
    311     // Compare pixel colors and locations for a theme image that had
    312     // only one PNG file specified (for scale 100%). The representation
    313     // for scale of 200% was computed.
    314     idr = IDR_THEME_FRAME_INCOGNITO_INACTIVE;
    315     image = pack->GetImageNamed(idr);
    316     EXPECT_FALSE(image.IsEmpty());
    317     image_skia = image.ToImageSkia();
    318     ASSERT_TRUE(image_skia);
    319     // Scale 100%.
    320     const gfx::ImageSkiaRep& rep3 = image_skia->GetRepresentation(1.0f);
    321     ASSERT_FALSE(rep3.is_null());
    322     EXPECT_EQ(80, rep3.sk_bitmap().width());
    323     EXPECT_EQ(80, rep3.sk_bitmap().height());
    324     rep3.sk_bitmap().lockPixels();
    325     // We take samples of colors and locations along the diagonal whenever
    326     // the color changes. Note these colors are slightly different from
    327     // the input PNG file due to input processing.
    328     std::vector<std::pair<int, SkColor> > normal;
    329     int xy = 0;
    330     SkColor color = rep3.sk_bitmap().getColor(xy, xy);
    331     normal.push_back(std::make_pair(xy, color));
    332     for (int xy = 0; xy < 40; ++xy) {
    333       SkColor next_color = rep3.sk_bitmap().getColor(xy, xy);
    334       if (next_color != color) {
    335         color = next_color;
    336         normal.push_back(std::make_pair(xy, color));
    337       }
    338     }
    339     EXPECT_EQ(static_cast<size_t>(9), normal.size());
    340     rep3.sk_bitmap().unlockPixels();
    341     // Scale 200%.
    342     const gfx::ImageSkiaRep& rep4 = image_skia->GetRepresentation(2.0f);
    343     ASSERT_FALSE(rep4.is_null());
    344     EXPECT_EQ(160, rep4.sk_bitmap().width());
    345     EXPECT_EQ(160, rep4.sk_bitmap().height());
    346     rep4.sk_bitmap().lockPixels();
    347     // We expect the same colors and at locations scaled by 2
    348     // since this bitmap was scaled by 2.
    349     for (size_t i = 0; i < normal.size(); ++i) {
    350       int xy = 2 * normal[i].first;
    351       SkColor color = normal[i].second;
    352       EXPECT_EQ(color, rep4.sk_bitmap().getColor(xy, xy));
    353     }
    354     rep4.sk_bitmap().unlockPixels();
    355   }
    356 
    357   base::MessageLoop message_loop;
    358   content::TestBrowserThread fake_ui_thread;
    359   content::TestBrowserThread fake_file_thread;
    360 
    361   typedef scoped_ptr<ui::test::ScopedSetSupportedScaleFactors>
    362       ScopedSetSupportedScaleFactors;
    363   ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
    364   scoped_refptr<BrowserThemePack> theme_pack_;
    365 };
    366 
    367 
    368 TEST_F(BrowserThemePackTest, DeriveUnderlineLinkColor) {
    369   // If we specify a link color, but don't specify the underline color, the
    370   // theme provider should create one.
    371   std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
    372                            "  \"ntp_section_link\": [128, 128, 128] }";
    373   LoadColorJSON(color_json);
    374 
    375   std::map<int, SkColor> colors = GetDefaultColorMap();
    376   SkColor link_color = SkColorSetRGB(128, 128, 128);
    377   colors[ThemeProperties::COLOR_NTP_LINK] = link_color;
    378   colors[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] =
    379       BuildThirdOpacity(link_color);
    380   colors[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
    381   colors[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] =
    382       BuildThirdOpacity(link_color);
    383 
    384   VerifyColorMap(colors);
    385 }
    386 
    387 TEST_F(BrowserThemePackTest, ProvideUnderlineLinkColor) {
    388   // If we specify the underline color, it shouldn't try to generate one.
    389   std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
    390                            "  \"ntp_link_underline\": [255, 255, 255],"
    391                            "  \"ntp_section_link\": [128, 128, 128],"
    392                            "  \"ntp_section_link_underline\": [255, 255, 255]"
    393                            "}";
    394   LoadColorJSON(color_json);
    395 
    396   std::map<int, SkColor> colors = GetDefaultColorMap();
    397   SkColor link_color = SkColorSetRGB(128, 128, 128);
    398   SkColor underline_color = SkColorSetRGB(255, 255, 255);
    399   colors[ThemeProperties::COLOR_NTP_LINK] = link_color;
    400   colors[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] = underline_color;
    401   colors[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
    402   colors[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] =
    403       underline_color;
    404 
    405   VerifyColorMap(colors);
    406 }
    407 
    408 TEST_F(BrowserThemePackTest, UseSectionColorAsNTPHeader) {
    409   std::string color_json = "{ \"ntp_section\": [190, 190, 190] }";
    410   LoadColorJSON(color_json);
    411 
    412   std::map<int, SkColor> colors = GetDefaultColorMap();
    413   SkColor ntp_color = SkColorSetRGB(190, 190, 190);
    414   colors[ThemeProperties::COLOR_NTP_HEADER] = ntp_color;
    415   colors[ThemeProperties::COLOR_NTP_SECTION] = ntp_color;
    416   VerifyColorMap(colors);
    417 }
    418 
    419 TEST_F(BrowserThemePackTest, ProvideNtpHeaderColor) {
    420   std::string color_json = "{ \"ntp_header\": [120, 120, 120], "
    421                            "  \"ntp_section\": [190, 190, 190] }";
    422   LoadColorJSON(color_json);
    423 
    424   std::map<int, SkColor> colors = GetDefaultColorMap();
    425   SkColor ntp_header = SkColorSetRGB(120, 120, 120);
    426   SkColor ntp_section = SkColorSetRGB(190, 190, 190);
    427   colors[ThemeProperties::COLOR_NTP_HEADER] = ntp_header;
    428   colors[ThemeProperties::COLOR_NTP_SECTION] = ntp_section;
    429   VerifyColorMap(colors);
    430 }
    431 
    432 TEST_F(BrowserThemePackTest, CanReadTints) {
    433   std::string tint_json = "{ \"buttons\": [ 0.5, 0.5, 0.5 ] }";
    434   LoadTintJSON(tint_json);
    435 
    436   color_utils::HSL expected = { 0.5, 0.5, 0.5 };
    437   color_utils::HSL actual = { -1, -1, -1 };
    438   EXPECT_TRUE(theme_pack_->GetTint(
    439       ThemeProperties::TINT_BUTTONS, &actual));
    440   EXPECT_DOUBLE_EQ(expected.h, actual.h);
    441   EXPECT_DOUBLE_EQ(expected.s, actual.s);
    442   EXPECT_DOUBLE_EQ(expected.l, actual.l);
    443 }
    444 
    445 TEST_F(BrowserThemePackTest, CanReadDisplayProperties) {
    446   std::string json = "{ \"ntp_background_alignment\": \"bottom\", "
    447                      "  \"ntp_background_repeat\": \"repeat-x\", "
    448                      "  \"ntp_logo_alternate\": 0 }";
    449   LoadDisplayPropertiesJSON(json);
    450 
    451   int out_val;
    452   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
    453       ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &out_val));
    454   EXPECT_EQ(ThemeProperties::ALIGN_BOTTOM, out_val);
    455 
    456   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
    457       ThemeProperties::NTP_BACKGROUND_TILING, &out_val));
    458   EXPECT_EQ(ThemeProperties::REPEAT_X, out_val);
    459 
    460   EXPECT_TRUE(theme_pack_->GetDisplayProperty(
    461       ThemeProperties::NTP_LOGO_ALTERNATE, &out_val));
    462   EXPECT_EQ(0, out_val);
    463 }
    464 
    465 TEST_F(BrowserThemePackTest, CanParsePaths) {
    466   std::string path_json = "{ \"theme_button_background\": \"one\", "
    467                           "  \"theme_toolbar\": \"two\" }";
    468   TestFilePathMap out_file_paths;
    469   ParseImageNamesJSON(path_json, &out_file_paths);
    470 
    471   size_t expected_file_paths = 2u;
    472 #if defined(USE_ASH) && !defined(OS_CHROMEOS)
    473   // On desktop builds with ash, additional theme paths are generated to map to
    474   // the special resource ids like IDR_THEME_FRAME_DESKTOP, etc
    475   expected_file_paths = 3u;
    476 #endif
    477   EXPECT_EQ(expected_file_paths, out_file_paths.size());
    478   // "12" and "5" are internal constants to BrowserThemePack and are
    479   // PRS_THEME_BUTTON_BACKGROUND and PRS_THEME_TOOLBAR, but they are
    480   // implementation details that shouldn't be exported.
    481   // By default the scale factor is for 100%.
    482   EXPECT_TRUE(base::FilePath(FILE_PATH_LITERAL("one")) ==
    483               out_file_paths[12][ui::SCALE_FACTOR_100P]);
    484   EXPECT_TRUE(base::FilePath(FILE_PATH_LITERAL("two")) ==
    485               out_file_paths[5][ui::SCALE_FACTOR_100P]);
    486 }
    487 
    488 TEST_F(BrowserThemePackTest, InvalidPathNames) {
    489   std::string path_json = "{ \"wrong\": [1], "
    490                           "  \"theme_button_background\": \"one\", "
    491                           "  \"not_a_thing\": \"blah\" }";
    492   TestFilePathMap out_file_paths;
    493   ParseImageNamesJSON(path_json, &out_file_paths);
    494 
    495   // We should have only parsed one valid path out of that mess above.
    496   EXPECT_EQ(1u, out_file_paths.size());
    497 }
    498 
    499 TEST_F(BrowserThemePackTest, InvalidColors) {
    500   std::string invalid_color = "{ \"toolbar\": [\"dog\", \"cat\", [12]], "
    501                               "  \"sound\": \"woof\" }";
    502   LoadColorJSON(invalid_color);
    503   std::map<int, SkColor> colors = GetDefaultColorMap();
    504   VerifyColorMap(colors);
    505 }
    506 
    507 TEST_F(BrowserThemePackTest, InvalidTints) {
    508   std::string invalid_tints = "{ \"buttons\": [ \"dog\", \"cat\", [\"x\"]], "
    509                               "  \"invalid\": \"entry\" }";
    510   LoadTintJSON(invalid_tints);
    511 
    512   // We shouldn't have a buttons tint, as it was invalid.
    513   color_utils::HSL actual = { -1, -1, -1 };
    514   EXPECT_FALSE(theme_pack_->GetTint(ThemeProperties::TINT_BUTTONS,
    515                                     &actual));
    516 }
    517 
    518 TEST_F(BrowserThemePackTest, InvalidDisplayProperties) {
    519   std::string invalid_properties = "{ \"ntp_background_alignment\": [15], "
    520                                    "  \"junk\": [15.3] }";
    521   LoadDisplayPropertiesJSON(invalid_properties);
    522 
    523   int out_val;
    524   EXPECT_FALSE(theme_pack_->GetDisplayProperty(
    525       ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &out_val));
    526 }
    527 
    528 // These three tests should just not cause a segmentation fault.
    529 TEST_F(BrowserThemePackTest, NullPaths) {
    530   TestFilePathMap out_file_paths;
    531   ParseImageNamesDictionary(NULL, &out_file_paths);
    532 }
    533 
    534 TEST_F(BrowserThemePackTest, NullTints) {
    535   LoadTintDictionary(NULL);
    536 }
    537 
    538 TEST_F(BrowserThemePackTest, NullColors) {
    539   LoadColorDictionary(NULL);
    540 }
    541 
    542 TEST_F(BrowserThemePackTest, NullDisplayProperties) {
    543   LoadDisplayPropertiesDictionary(NULL);
    544 }
    545 
    546 TEST_F(BrowserThemePackTest, TestHasCustomImage) {
    547   // HasCustomImage should only return true for images that exist in the
    548   // extension and not for autogenerated images.
    549   std::string images = "{ \"theme_frame\": \"one\" }";
    550   TestFilePathMap out_file_paths;
    551   ParseImageNamesJSON(images, &out_file_paths);
    552 
    553   EXPECT_TRUE(theme_pack_->HasCustomImage(IDR_THEME_FRAME));
    554   EXPECT_FALSE(theme_pack_->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
    555 }
    556 
    557 TEST_F(BrowserThemePackTest, TestNonExistantImages) {
    558   std::string images = "{ \"theme_frame\": \"does_not_exist\" }";
    559   TestFilePathMap out_file_paths;
    560   ParseImageNamesJSON(images, &out_file_paths);
    561 
    562   EXPECT_FALSE(LoadRawBitmapsTo(out_file_paths));
    563 }
    564 
    565 // TODO(erg): This test should actually test more of the built resources from
    566 // the extension data, but for now, exists so valgrind can test some of the
    567 // tricky memory stuff that BrowserThemePack does.
    568 TEST_F(BrowserThemePackTest, CanBuildAndReadPack) {
    569   base::ScopedTempDir dir;
    570   ASSERT_TRUE(dir.CreateUniqueTempDir());
    571   base::FilePath file = dir.path().AppendASCII("data.pak");
    572 
    573   // Part 1: Build the pack from an extension.
    574   {
    575     base::FilePath star_gazing_path = GetStarGazingPath();
    576     scoped_refptr<BrowserThemePack> pack;
    577     BuildFromUnpackedExtension(star_gazing_path, pack);
    578     ASSERT_TRUE(pack->WriteToDisk(file));
    579     VerifyStarGazing(pack.get());
    580   }
    581 
    582   // Part 2: Try to read back the data pack that we just wrote to disk.
    583   {
    584     scoped_refptr<BrowserThemePack> pack =
    585         BrowserThemePack::BuildFromDataPack(
    586             file, "mblmlcbknbnfebdfjnolmcapmdofhmme");
    587     ASSERT_TRUE(pack.get());
    588     VerifyStarGazing(pack.get());
    589   }
    590 }
    591 
    592 TEST_F(BrowserThemePackTest, HiDpiThemeTest) {
    593   base::ScopedTempDir dir;
    594   ASSERT_TRUE(dir.CreateUniqueTempDir());
    595   base::FilePath file = dir.path().AppendASCII("theme_data.pak");
    596 
    597   // Part 1: Build the pack from an extension.
    598   {
    599     base::FilePath hidpi_path = GetHiDpiThemePath();
    600     scoped_refptr<BrowserThemePack> pack;
    601     BuildFromUnpackedExtension(hidpi_path, pack);
    602     ASSERT_TRUE(pack->WriteToDisk(file));
    603     VerifyHiDpiTheme(pack.get());
    604   }
    605 
    606   // Part 2: Try to read back the data pack that we just wrote to disk.
    607   {
    608     scoped_refptr<BrowserThemePack> pack =
    609         BrowserThemePack::BuildFromDataPack(file, "gllekhaobjnhgeag");
    610     ASSERT_TRUE(pack.get());
    611     VerifyHiDpiTheme(pack.get());
    612   }
    613 }
    614