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