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
TEST(ExtensionL10nUtil,GetValidLocalesEmptyLocaleFolder)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
TEST(ExtensionL10nUtil,GetValidLocalesWithValidLocaleNoMessagesFile)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
TEST(ExtensionL10nUtil,GetValidLocalesWithUnsupportedLocale)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
TEST(ExtensionL10nUtil,GetValidLocalesWithValidLocalesAndMessagesFile)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
TEST(ExtensionL10nUtil,LoadMessageCatalogsValidFallback)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
TEST(ExtensionL10nUtil,LoadMessageCatalogsMissingFiles)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
TEST(ExtensionL10nUtil,LoadMessageCatalogsBadJSONFormat)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
TEST(ExtensionL10nUtil,LoadMessageCatalogsDuplicateKeys)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.
CreateManifestBundle()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
TEST(ExtensionL10nUtil,LocalizeEmptyManifest)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
TEST(ExtensionL10nUtil,LocalizeManifestWithoutNameMsgAndEmptyDescription)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
TEST(ExtensionL10nUtil,LocalizeManifestWithNameMsgAndEmptyDescription)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
TEST(ExtensionL10nUtil,LocalizeManifestWithBadNameMsg)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
TEST(ExtensionL10nUtil,LocalizeManifestWithNameDescriptionDefaultTitleMsgs)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
TEST(ExtensionL10nUtil,LocalizeManifestWithNameDescriptionOmniboxMsgs)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
TEST(ExtensionL10nUtil,LocalizeManifestWithNameDescriptionFileHandlerTitle)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.
TEST(ExtensionL10nUtil,ShouldRelocalizeManifestWithNullManifest)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.
TEST(ExtensionL10nUtil,ShouldRelocalizeManifestEmptyManifest)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.
TEST(ExtensionL10nUtil,ShouldRelocalizeManifestWithDefaultLocale)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.
TEST(ExtensionL10nUtil,ShouldRelocalizeManifestWithCurrentLocale)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.
TEST(ExtensionL10nUtil,ShouldRelocalizeManifestSameCurrentLocale)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.
TEST(ExtensionL10nUtil,ShouldRelocalizeManifestDifferentCurrentLocale)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