Home | History | Annotate | Download | only in extensions
      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 "base/file_util.h"
      6 #include "base/files/file_path.h"
      7 #include "base/files/scoped_temp_dir.h"
      8 #include "base/memory/linked_ptr.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/path_service.h"
     11 #include "base/values.h"
     12 #include "chrome/common/chrome_paths.h"
     13 #include "chrome/common/extensions/extension_l10n_util.h"
     14 #include "chrome/common/extensions/extension_manifest_constants.h"
     15 #include "chrome/common/extensions/message_bundle.h"
     16 #include "extensions/common/constants.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 #include "ui/base/l10n/l10n_util.h"
     19 
     20 using extensions::kLocaleFolder;
     21 using extensions::kMessagesFilename;
     22 using extensions::MessageBundle;
     23 
     24 namespace errors = extension_manifest_errors;
     25 namespace keys = extension_manifest_keys;
     26 
     27 namespace {
     28 
     29 TEST(ExtensionL10nUtil, GetValidLocalesEmptyLocaleFolder) {
     30   base::ScopedTempDir temp;
     31   ASSERT_TRUE(temp.CreateUniqueTempDir());
     32 
     33   base::FilePath src_path = temp.path().Append(kLocaleFolder);
     34   ASSERT_TRUE(file_util::CreateDirectory(src_path));
     35 
     36   std::string error;
     37   std::set<std::string> locales;
     38   EXPECT_FALSE(extension_l10n_util::GetValidLocales(src_path,
     39                                                     &locales,
     40                                                     &error));
     41 
     42   EXPECT_TRUE(locales.empty());
     43 }
     44 
     45 TEST(ExtensionL10nUtil, GetValidLocalesWithValidLocaleNoMessagesFile) {
     46   base::ScopedTempDir temp;
     47   ASSERT_TRUE(temp.CreateUniqueTempDir());
     48 
     49   base::FilePath src_path = temp.path().Append(kLocaleFolder);
     50   ASSERT_TRUE(file_util::CreateDirectory(src_path));
     51   ASSERT_TRUE(file_util::CreateDirectory(src_path.AppendASCII("sr")));
     52 
     53   std::string error;
     54   std::set<std::string> locales;
     55   EXPECT_FALSE(extension_l10n_util::GetValidLocales(src_path,
     56                                                     &locales,
     57                                                     &error));
     58 
     59   EXPECT_TRUE(locales.empty());
     60 }
     61 
     62 TEST(ExtensionL10nUtil, GetValidLocalesWithUnsupportedLocale) {
     63   base::ScopedTempDir temp;
     64   ASSERT_TRUE(temp.CreateUniqueTempDir());
     65 
     66   base::FilePath src_path = temp.path().Append(kLocaleFolder);
     67   ASSERT_TRUE(file_util::CreateDirectory(src_path));
     68   // Supported locale.
     69   base::FilePath locale_1 = src_path.AppendASCII("sr");
     70   ASSERT_TRUE(file_util::CreateDirectory(locale_1));
     71   std::string data("whatever");
     72   ASSERT_TRUE(file_util::WriteFile(
     73       locale_1.Append(kMessagesFilename),
     74       data.c_str(), data.length()));
     75   // Unsupported locale.
     76   ASSERT_TRUE(file_util::CreateDirectory(src_path.AppendASCII("xxx_yyy")));
     77 
     78   std::string error;
     79   std::set<std::string> locales;
     80   EXPECT_TRUE(extension_l10n_util::GetValidLocales(src_path,
     81                                                    &locales,
     82                                                    &error));
     83 
     84   EXPECT_FALSE(locales.empty());
     85   EXPECT_TRUE(locales.find("sr") != locales.end());
     86   EXPECT_FALSE(locales.find("xxx_yyy") != locales.end());
     87 }
     88 
     89 TEST(ExtensionL10nUtil, GetValidLocalesWithValidLocalesAndMessagesFile) {
     90   base::FilePath install_dir;
     91   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir));
     92   install_dir = install_dir.AppendASCII("extensions")
     93       .AppendASCII("good")
     94       .AppendASCII("Extensions")
     95       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
     96       .AppendASCII("1.0.0.0")
     97       .Append(kLocaleFolder);
     98 
     99   std::string error;
    100   std::set<std::string> locales;
    101   EXPECT_TRUE(extension_l10n_util::GetValidLocales(install_dir,
    102                                                    &locales,
    103                                                    &error));
    104   EXPECT_EQ(3U, locales.size());
    105   EXPECT_TRUE(locales.find("sr") != locales.end());
    106   EXPECT_TRUE(locales.find("en") != locales.end());
    107   EXPECT_TRUE(locales.find("en_US") != locales.end());
    108 }
    109 
    110 TEST(ExtensionL10nUtil, LoadMessageCatalogsValidFallback) {
    111   base::FilePath install_dir;
    112   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir));
    113   install_dir = install_dir.AppendASCII("extensions")
    114       .AppendASCII("good")
    115       .AppendASCII("Extensions")
    116       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
    117       .AppendASCII("1.0.0.0")
    118       .Append(kLocaleFolder);
    119 
    120   std::string error;
    121   std::set<std::string> locales;
    122   EXPECT_TRUE(extension_l10n_util::GetValidLocales(install_dir,
    123                                                    &locales,
    124                                                    &error));
    125 
    126   scoped_ptr<MessageBundle> bundle(extension_l10n_util::LoadMessageCatalogs(
    127       install_dir, "sr", "en_US", locales, &error));
    128   ASSERT_FALSE(NULL == bundle.get());
    129   EXPECT_TRUE(error.empty());
    130   EXPECT_EQ("Color", bundle->GetL10nMessage("color"));
    131   EXPECT_EQ("Not in the US or GB.", bundle->GetL10nMessage("not_in_US_or_GB"));
    132 }
    133 
    134 TEST(ExtensionL10nUtil, LoadMessageCatalogsMissingFiles) {
    135   base::ScopedTempDir temp;
    136   ASSERT_TRUE(temp.CreateUniqueTempDir());
    137 
    138   base::FilePath src_path = temp.path().Append(kLocaleFolder);
    139   ASSERT_TRUE(file_util::CreateDirectory(src_path));
    140 
    141   std::set<std::string> valid_locales;
    142   valid_locales.insert("sr");
    143   valid_locales.insert("en");
    144   std::string error;
    145   EXPECT_TRUE(NULL == extension_l10n_util::LoadMessageCatalogs(src_path,
    146                                                                "en",
    147                                                                "sr",
    148                                                                valid_locales,
    149                                                                &error));
    150   EXPECT_FALSE(error.empty());
    151 }
    152 
    153 TEST(ExtensionL10nUtil, LoadMessageCatalogsBadJSONFormat) {
    154   base::ScopedTempDir temp;
    155   ASSERT_TRUE(temp.CreateUniqueTempDir());
    156 
    157   base::FilePath src_path = temp.path().Append(kLocaleFolder);
    158   ASSERT_TRUE(file_util::CreateDirectory(src_path));
    159 
    160   base::FilePath locale = src_path.AppendASCII("sr");
    161   ASSERT_TRUE(file_util::CreateDirectory(locale));
    162 
    163   std::string data = "{ \"name\":";
    164   ASSERT_TRUE(
    165       file_util::WriteFile(locale.Append(kMessagesFilename),
    166                            data.c_str(), data.length()));
    167 
    168   std::set<std::string> valid_locales;
    169   valid_locales.insert("sr");
    170   valid_locales.insert("en_US");
    171   std::string error;
    172   EXPECT_TRUE(NULL == extension_l10n_util::LoadMessageCatalogs(src_path,
    173                                                                "en_US",
    174                                                                "sr",
    175                                                                valid_locales,
    176                                                                &error));
    177   EXPECT_EQ("Line: 1, column: 10, Unexpected token.", error);
    178 }
    179 
    180 TEST(ExtensionL10nUtil, LoadMessageCatalogsDuplicateKeys) {
    181   base::ScopedTempDir temp;
    182   ASSERT_TRUE(temp.CreateUniqueTempDir());
    183 
    184   base::FilePath src_path = temp.path().Append(kLocaleFolder);
    185   ASSERT_TRUE(file_util::CreateDirectory(src_path));
    186 
    187   base::FilePath locale_1 = src_path.AppendASCII("en");
    188   ASSERT_TRUE(file_util::CreateDirectory(locale_1));
    189 
    190   std::string data =
    191     "{ \"name\": { \"message\": \"something\" }, "
    192     "\"name\": { \"message\": \"something else\" } }";
    193   ASSERT_TRUE(
    194       file_util::WriteFile(locale_1.Append(kMessagesFilename),
    195                            data.c_str(), data.length()));
    196 
    197   base::FilePath locale_2 = src_path.AppendASCII("sr");
    198   ASSERT_TRUE(file_util::CreateDirectory(locale_2));
    199 
    200   ASSERT_TRUE(
    201       file_util::WriteFile(locale_2.Append(kMessagesFilename),
    202                            data.c_str(), data.length()));
    203 
    204   std::set<std::string> valid_locales;
    205   valid_locales.insert("sr");
    206   valid_locales.insert("en");
    207   std::string error;
    208   // JSON parser hides duplicates. We are going to get only one key/value
    209   // pair at the end.
    210   scoped_ptr<MessageBundle> message_bundle(
    211       extension_l10n_util::LoadMessageCatalogs(src_path,
    212                                                "en",
    213                                                "sr",
    214                                                valid_locales,
    215                                                &error));
    216   EXPECT_TRUE(NULL != message_bundle.get());
    217   EXPECT_TRUE(error.empty());
    218 }
    219 
    220 // Caller owns the returned object.
    221 MessageBundle* CreateManifestBundle() {
    222   linked_ptr<base::DictionaryValue> catalog(new base::DictionaryValue);
    223 
    224   base::DictionaryValue* name_tree = new base::DictionaryValue();
    225   name_tree->SetString("message", "name");
    226   catalog->Set("name", name_tree);
    227 
    228   base::DictionaryValue* description_tree = new base::DictionaryValue();
    229   description_tree->SetString("message", "description");
    230   catalog->Set("description", description_tree);
    231 
    232   base::DictionaryValue* action_title_tree = new base::DictionaryValue();
    233   action_title_tree->SetString("message", "action title");
    234   catalog->Set("title", action_title_tree);
    235 
    236   base::DictionaryValue* omnibox_keyword_tree = new base::DictionaryValue();
    237   omnibox_keyword_tree->SetString("message", "omnibox keyword");
    238   catalog->Set("omnibox_keyword", omnibox_keyword_tree);
    239 
    240   base::DictionaryValue* file_handler_title_tree = new base::DictionaryValue();
    241   file_handler_title_tree->SetString("message", "file handler title");
    242   catalog->Set("file_handler_title", file_handler_title_tree);
    243 
    244   base::DictionaryValue* launch_local_path_tree = new base::DictionaryValue();
    245   launch_local_path_tree->SetString("message", "main.html");
    246   catalog->Set("launch_local_path", launch_local_path_tree);
    247 
    248   base::DictionaryValue* launch_web_url_tree = new base::DictionaryValue();
    249   launch_web_url_tree->SetString("message", "http://www.google.com/");
    250   catalog->Set("launch_web_url", launch_web_url_tree);
    251 
    252   std::vector<linked_ptr<base::DictionaryValue> > catalogs;
    253   catalogs.push_back(catalog);
    254 
    255   std::string error;
    256   MessageBundle* bundle = MessageBundle::Create(catalogs, &error);
    257   EXPECT_TRUE(bundle);
    258   EXPECT_TRUE(error.empty());
    259 
    260   return bundle;
    261 }
    262 
    263 TEST(ExtensionL10nUtil, LocalizeEmptyManifest) {
    264   base::DictionaryValue manifest;
    265   std::string error;
    266   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    267 
    268   EXPECT_FALSE(
    269       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    270   EXPECT_EQ(std::string(errors::kInvalidName), error);
    271 }
    272 
    273 TEST(ExtensionL10nUtil, LocalizeManifestWithoutNameMsgAndEmptyDescription) {
    274   base::DictionaryValue manifest;
    275   manifest.SetString(keys::kName, "no __MSG");
    276   std::string error;
    277   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    278 
    279   EXPECT_TRUE(
    280       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    281 
    282   std::string result;
    283   ASSERT_TRUE(manifest.GetString(keys::kName, &result));
    284   EXPECT_EQ("no __MSG", result);
    285 
    286   EXPECT_FALSE(manifest.HasKey(keys::kDescription));
    287 
    288   EXPECT_TRUE(error.empty());
    289 }
    290 
    291 TEST(ExtensionL10nUtil, LocalizeManifestWithNameMsgAndEmptyDescription) {
    292   base::DictionaryValue manifest;
    293   manifest.SetString(keys::kName, "__MSG_name__");
    294   std::string error;
    295   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    296 
    297   EXPECT_TRUE(
    298       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    299 
    300   std::string result;
    301   ASSERT_TRUE(manifest.GetString(keys::kName, &result));
    302   EXPECT_EQ("name", result);
    303 
    304   EXPECT_FALSE(manifest.HasKey(keys::kDescription));
    305 
    306   EXPECT_TRUE(error.empty());
    307 }
    308 
    309 TEST(ExtensionL10nUtil, LocalizeManifestWithLocalLaunchURL) {
    310   base::DictionaryValue manifest;
    311   manifest.SetString(keys::kName, "name");
    312   manifest.SetString(keys::kLaunchLocalPath, "__MSG_launch_local_path__");
    313   std::string error;
    314   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    315 
    316   EXPECT_TRUE(
    317       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    318 
    319   std::string result;
    320   ASSERT_TRUE(manifest.GetString(keys::kLaunchLocalPath, &result));
    321   EXPECT_EQ("main.html", result);
    322 
    323   EXPECT_TRUE(error.empty());
    324 }
    325 
    326 TEST(ExtensionL10nUtil, LocalizeManifestWithHostedLaunchURL) {
    327   base::DictionaryValue manifest;
    328   manifest.SetString(keys::kName, "name");
    329   manifest.SetString(keys::kLaunchWebURL, "__MSG_launch_web_url__");
    330   std::string error;
    331   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    332 
    333   EXPECT_TRUE(
    334       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    335 
    336   std::string result;
    337   ASSERT_TRUE(manifest.GetString(keys::kLaunchWebURL, &result));
    338   EXPECT_EQ("http://www.google.com/", result);
    339 
    340   EXPECT_TRUE(error.empty());
    341 }
    342 
    343 TEST(ExtensionL10nUtil, LocalizeManifestWithBadNameMsg) {
    344   base::DictionaryValue manifest;
    345   manifest.SetString(keys::kName, "__MSG_name_is_bad__");
    346   manifest.SetString(keys::kDescription, "__MSG_description__");
    347   std::string error;
    348   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    349 
    350   EXPECT_FALSE(
    351       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    352 
    353   std::string result;
    354   ASSERT_TRUE(manifest.GetString(keys::kName, &result));
    355   EXPECT_EQ("__MSG_name_is_bad__", result);
    356 
    357   ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
    358   EXPECT_EQ("__MSG_description__", result);
    359 
    360   EXPECT_EQ("Variable __MSG_name_is_bad__ used but not defined.", error);
    361 }
    362 
    363 TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionDefaultTitleMsgs) {
    364   base::DictionaryValue manifest;
    365   manifest.SetString(keys::kName, "__MSG_name__");
    366   manifest.SetString(keys::kDescription, "__MSG_description__");
    367   std::string action_title(keys::kBrowserAction);
    368   action_title.append(".");
    369   action_title.append(keys::kPageActionDefaultTitle);
    370   manifest.SetString(action_title, "__MSG_title__");
    371 
    372   std::string error;
    373   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    374 
    375   EXPECT_TRUE(
    376       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    377 
    378   std::string result;
    379   ASSERT_TRUE(manifest.GetString(keys::kName, &result));
    380   EXPECT_EQ("name", result);
    381 
    382   ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
    383   EXPECT_EQ("description", result);
    384 
    385   ASSERT_TRUE(manifest.GetString(action_title, &result));
    386   EXPECT_EQ("action title", result);
    387 
    388   EXPECT_TRUE(error.empty());
    389 }
    390 
    391 TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionOmniboxMsgs) {
    392   base::DictionaryValue manifest;
    393   manifest.SetString(keys::kName, "__MSG_name__");
    394   manifest.SetString(keys::kDescription, "__MSG_description__");
    395   manifest.SetString(keys::kOmniboxKeyword, "__MSG_omnibox_keyword__");
    396 
    397   std::string error;
    398   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    399 
    400   EXPECT_TRUE(
    401       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    402 
    403   std::string result;
    404   ASSERT_TRUE(manifest.GetString(keys::kName, &result));
    405   EXPECT_EQ("name", result);
    406 
    407   ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
    408   EXPECT_EQ("description", result);
    409 
    410   ASSERT_TRUE(manifest.GetString(keys::kOmniboxKeyword, &result));
    411   EXPECT_EQ("omnibox keyword", result);
    412 
    413   EXPECT_TRUE(error.empty());
    414 }
    415 
    416 TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionFileHandlerTitle) {
    417   base::DictionaryValue manifest;
    418   manifest.SetString(keys::kName, "__MSG_name__");
    419   manifest.SetString(keys::kDescription, "__MSG_description__");
    420   base::ListValue* handlers = new base::ListValue();
    421   manifest.Set(keys::kFileBrowserHandlers, handlers);
    422   base::DictionaryValue* handler = new base::DictionaryValue();
    423   handlers->Append(handler);
    424   handler->SetString(keys::kPageActionDefaultTitle,
    425                      "__MSG_file_handler_title__");
    426 
    427   std::string error;
    428   scoped_ptr<MessageBundle> messages(CreateManifestBundle());
    429 
    430   EXPECT_TRUE(
    431       extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
    432 
    433   std::string result;
    434   ASSERT_TRUE(manifest.GetString(keys::kName, &result));
    435   EXPECT_EQ("name", result);
    436 
    437   ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
    438   EXPECT_EQ("description", result);
    439 
    440   ASSERT_TRUE(handler->GetString(keys::kPageActionDefaultTitle, &result));
    441   EXPECT_EQ("file handler title", result);
    442 
    443   EXPECT_TRUE(error.empty());
    444 }
    445 
    446 // Try with NULL manifest.
    447 TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithNullManifest) {
    448   EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(NULL));
    449 }
    450 
    451 // Try with default and current locales missing.
    452 TEST(ExtensionL10nUtil, ShouldRelocalizeManifestEmptyManifest) {
    453   base::DictionaryValue manifest;
    454   EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(&manifest));
    455 }
    456 
    457 // Try with missing current_locale.
    458 TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithDefaultLocale) {
    459   base::DictionaryValue manifest;
    460   manifest.SetString(keys::kDefaultLocale, "en_US");
    461   EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(&manifest));
    462 }
    463 
    464 // Try with missing default_locale.
    465 TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithCurrentLocale) {
    466   base::DictionaryValue manifest;
    467   manifest.SetString(keys::kCurrentLocale,
    468                      extension_l10n_util::CurrentLocaleOrDefault());
    469   EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(&manifest));
    470 }
    471 
    472 // Try with all data present, but with same current_locale as system locale.
    473 TEST(ExtensionL10nUtil, ShouldRelocalizeManifestSameCurrentLocale) {
    474   base::DictionaryValue manifest;
    475   manifest.SetString(keys::kDefaultLocale, "en_US");
    476   manifest.SetString(keys::kCurrentLocale,
    477                      extension_l10n_util::CurrentLocaleOrDefault());
    478   EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(&manifest));
    479 }
    480 
    481 // Try with all data present, but with different current_locale.
    482 TEST(ExtensionL10nUtil, ShouldRelocalizeManifestDifferentCurrentLocale) {
    483   base::DictionaryValue manifest;
    484   manifest.SetString(keys::kDefaultLocale, "en_US");
    485   manifest.SetString(keys::kCurrentLocale, "sr");
    486   EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(&manifest));
    487 }
    488 
    489 TEST(ExtensionL10nUtil, GetAllFallbackLocales) {
    490   std::vector<std::string> fallback_locales;
    491   extension_l10n_util::GetAllFallbackLocales("en_US", "all", &fallback_locales);
    492   ASSERT_EQ(3U, fallback_locales.size());
    493 
    494   CHECK_EQ("en_US", fallback_locales[0]);
    495   CHECK_EQ("en", fallback_locales[1]);
    496   CHECK_EQ("all", fallback_locales[2]);
    497 }
    498 
    499 }  // namespace
    500