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