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
20namespace errors = extension_manifest_errors;
21namespace keys = extension_manifest_keys;
22
23namespace {
24
25TEST(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
41TEST(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
58TEST(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
85TEST(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
106TEST(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
131TEST(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
150TEST(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
177TEST(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.
218ExtensionMessageBundle* 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
253TEST(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
263TEST(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
281TEST(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
299TEST(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
319TEST(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
347TEST(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
372TEST(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.
403TEST(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.
410TEST(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.
418TEST(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.
428TEST(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.
439TEST(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.
451TEST(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