• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "chrome/common/extensions/extension_message_bundle.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/hash_tables.h"
11 #include "base/i18n/rtl.h"
12 #include "base/lazy_instance.h"
13 #include "base/memory/linked_ptr.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/stl_util-inl.h"
16 #include "base/string_util.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/common/extensions/extension_error_utils.h"
21 #include "chrome/common/extensions/extension_l10n_util.h"
22 #include "ui/base/l10n/l10n_util.h"
23 
24 namespace errors = extension_manifest_errors;
25 
26 const char* ExtensionMessageBundle::kContentKey = "content";
27 const char* ExtensionMessageBundle::kMessageKey = "message";
28 const char* ExtensionMessageBundle::kPlaceholdersKey = "placeholders";
29 
30 const char* ExtensionMessageBundle::kPlaceholderBegin = "$";
31 const char* ExtensionMessageBundle::kPlaceholderEnd = "$";
32 const char* ExtensionMessageBundle::kMessageBegin = "__MSG_";
33 const char* ExtensionMessageBundle::kMessageEnd = "__";
34 
35 // Reserved messages names.
36 const char* ExtensionMessageBundle::kUILocaleKey = "@@ui_locale";
37 const char* ExtensionMessageBundle::kBidiDirectionKey = "@@bidi_dir";
38 const char* ExtensionMessageBundle::kBidiReversedDirectionKey =
39     "@@bidi_reversed_dir";
40 const char* ExtensionMessageBundle::kBidiStartEdgeKey = "@@bidi_start_edge";
41 const char* ExtensionMessageBundle::kBidiEndEdgeKey = "@@bidi_end_edge";
42 const char* ExtensionMessageBundle::kExtensionIdKey = "@@extension_id";
43 
44 // Reserved messages values.
45 const char* ExtensionMessageBundle::kBidiLeftEdgeValue = "left";
46 const char* ExtensionMessageBundle::kBidiRightEdgeValue = "right";
47 
48 // Formats message in case we encounter a bad formed key in the JSON object.
49 // Returns false and sets |error| to actual error message.
BadKeyMessage(const std::string & name,std::string * error)50 static bool BadKeyMessage(const std::string& name, std::string* error) {
51   *error = base::StringPrintf(
52       "Name of a key \"%s\" is invalid. Only ASCII [a-z], "
53       "[A-Z], [0-9] and \"_\" are allowed.",
54       name.c_str());
55   return false;
56 }
57 
58 // static
Create(const CatalogVector & locale_catalogs,std::string * error)59 ExtensionMessageBundle* ExtensionMessageBundle::Create(
60     const CatalogVector& locale_catalogs,
61     std::string* error) {
62   scoped_ptr<ExtensionMessageBundle> message_bundle(
63       new ExtensionMessageBundle);
64   if (!message_bundle->Init(locale_catalogs, error))
65     return NULL;
66 
67   return message_bundle.release();
68 }
69 
Init(const CatalogVector & locale_catalogs,std::string * error)70 bool ExtensionMessageBundle::Init(const CatalogVector& locale_catalogs,
71                                   std::string* error) {
72   dictionary_.clear();
73 
74   for (CatalogVector::const_reverse_iterator it = locale_catalogs.rbegin();
75        it != locale_catalogs.rend(); ++it) {
76     DictionaryValue* catalog = (*it).get();
77     for (DictionaryValue::key_iterator key_it = catalog->begin_keys();
78          key_it != catalog->end_keys(); ++key_it) {
79       std::string key(StringToLowerASCII(*key_it));
80       if (!IsValidName(*key_it))
81         return BadKeyMessage(key, error);
82       std::string value;
83       if (!GetMessageValue(*key_it, *catalog, &value, error))
84         return false;
85       // Keys are not case-sensitive.
86       dictionary_[key] = value;
87     }
88   }
89 
90   if (!AppendReservedMessagesForLocale(
91       extension_l10n_util::CurrentLocaleOrDefault(), error))
92     return false;
93 
94   return true;
95 }
96 
AppendReservedMessagesForLocale(const std::string & app_locale,std::string * error)97 bool ExtensionMessageBundle::AppendReservedMessagesForLocale(
98     const std::string& app_locale, std::string* error) {
99   SubstitutionMap append_messages;
100   append_messages[kUILocaleKey] = app_locale;
101 
102   // Calling base::i18n::GetTextDirection on non-UI threads doesn't seems safe,
103   // so we use GetTextDirectionForLocale instead.
104   if (base::i18n::GetTextDirectionForLocale(app_locale.c_str()) ==
105       base::i18n::RIGHT_TO_LEFT) {
106     append_messages[kBidiDirectionKey] = "rtl";
107     append_messages[kBidiReversedDirectionKey] = "ltr";
108     append_messages[kBidiStartEdgeKey] = kBidiRightEdgeValue;
109     append_messages[kBidiEndEdgeKey] = kBidiLeftEdgeValue;
110   } else {
111     append_messages[kBidiDirectionKey] = "ltr";
112     append_messages[kBidiReversedDirectionKey] = "rtl";
113     append_messages[kBidiStartEdgeKey] = kBidiLeftEdgeValue;
114     append_messages[kBidiEndEdgeKey] = kBidiRightEdgeValue;
115   }
116 
117   // Add all reserved messages to the dictionary, but check for collisions.
118   SubstitutionMap::iterator it = append_messages.begin();
119   for (; it != append_messages.end(); ++it) {
120     if (ContainsKey(dictionary_, it->first)) {
121       *error = ExtensionErrorUtils::FormatErrorMessage(
122           errors::kReservedMessageFound, it->first);
123       return false;
124     } else {
125       dictionary_[it->first] = it->second;
126     }
127   }
128 
129   return true;
130 }
131 
GetMessageValue(const std::string & key,const DictionaryValue & catalog,std::string * value,std::string * error) const132 bool ExtensionMessageBundle::GetMessageValue(const std::string& key,
133                                              const DictionaryValue& catalog,
134                                              std::string* value,
135                                              std::string* error) const {
136   // Get the top level tree for given key (name part).
137   DictionaryValue* name_tree;
138   if (!catalog.GetDictionaryWithoutPathExpansion(key, &name_tree)) {
139     *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str());
140     return false;
141   }
142   // Extract message from it.
143   if (!name_tree->GetString(kMessageKey, value)) {
144     *error = base::StringPrintf(
145         "There is no \"%s\" element for key %s.", kMessageKey, key.c_str());
146     return false;
147   }
148 
149   SubstitutionMap placeholders;
150   if (!GetPlaceholders(*name_tree, key, &placeholders, error))
151     return false;
152 
153   if (!ReplacePlaceholders(placeholders, value, error))
154     return false;
155 
156   return true;
157 }
158 
ExtensionMessageBundle()159 ExtensionMessageBundle::ExtensionMessageBundle() {
160 }
161 
GetPlaceholders(const DictionaryValue & name_tree,const std::string & name_key,SubstitutionMap * placeholders,std::string * error) const162 bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree,
163                                              const std::string& name_key,
164                                              SubstitutionMap* placeholders,
165                                              std::string* error) const {
166   if (!name_tree.HasKey(kPlaceholdersKey))
167     return true;
168 
169   DictionaryValue* placeholders_tree;
170   if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) {
171     *error = base::StringPrintf("Not a valid \"%s\" element for key %s.",
172                                 kPlaceholdersKey, name_key.c_str());
173     return false;
174   }
175 
176   for (DictionaryValue::key_iterator key_it = placeholders_tree->begin_keys();
177        key_it != placeholders_tree->end_keys(); ++key_it) {
178     DictionaryValue* placeholder;
179     const std::string& content_key(*key_it);
180     if (!IsValidName(content_key))
181       return BadKeyMessage(content_key, error);
182     if (!placeholders_tree->GetDictionaryWithoutPathExpansion(content_key,
183                                                               &placeholder)) {
184       *error = base::StringPrintf("Invalid placeholder %s for key %s",
185                                   content_key.c_str(),
186                                   name_key.c_str());
187       return false;
188     }
189     std::string content;
190     if (!placeholder->GetString(kContentKey, &content)) {
191       *error = base::StringPrintf("Invalid \"%s\" element for key %s.",
192                                   kContentKey, name_key.c_str());
193       return false;
194     }
195     (*placeholders)[StringToLowerASCII(content_key)] = content;
196   }
197 
198   return true;
199 }
200 
ReplacePlaceholders(const SubstitutionMap & placeholders,std::string * message,std::string * error) const201 bool ExtensionMessageBundle::ReplacePlaceholders(
202     const SubstitutionMap& placeholders,
203     std::string* message,
204     std::string* error) const {
205   return ReplaceVariables(placeholders,
206                           kPlaceholderBegin,
207                           kPlaceholderEnd,
208                           message,
209                           error);
210 }
211 
ReplaceMessages(std::string * text,std::string * error) const212 bool ExtensionMessageBundle::ReplaceMessages(std::string* text,
213                                              std::string* error) const {
214   return ReplaceMessagesWithExternalDictionary(dictionary_, text, error);
215 }
216 
~ExtensionMessageBundle()217 ExtensionMessageBundle::~ExtensionMessageBundle() {
218 }
219 
220 // static
ReplaceMessagesWithExternalDictionary(const SubstitutionMap & dictionary,std::string * text,std::string * error)221 bool ExtensionMessageBundle::ReplaceMessagesWithExternalDictionary(
222     const SubstitutionMap& dictionary, std::string* text, std::string* error) {
223   return ReplaceVariables(dictionary, kMessageBegin, kMessageEnd, text, error);
224 }
225 
226 // static
ReplaceVariables(const SubstitutionMap & variables,const std::string & var_begin_delimiter,const std::string & var_end_delimiter,std::string * message,std::string * error)227 bool ExtensionMessageBundle::ReplaceVariables(
228     const SubstitutionMap& variables,
229     const std::string& var_begin_delimiter,
230     const std::string& var_end_delimiter,
231     std::string* message,
232     std::string* error) {
233   std::string::size_type beg_index = 0;
234   const std::string::size_type var_begin_delimiter_size =
235     var_begin_delimiter.size();
236   while (true) {
237     beg_index = message->find(var_begin_delimiter, beg_index);
238     if (beg_index == message->npos)
239       return true;
240 
241     // Advance it immediately to the begining of possible variable name.
242     beg_index += var_begin_delimiter_size;
243     if (beg_index >= message->size())
244       return true;
245     std::string::size_type end_index =
246       message->find(var_end_delimiter, beg_index);
247     if (end_index == message->npos)
248       return true;
249 
250     // Looking for 1 in substring of ...$1$....
251     const std::string& var_name =
252       message->substr(beg_index, end_index - beg_index);
253     if (!IsValidName(var_name))
254       continue;
255     SubstitutionMap::const_iterator it =
256       variables.find(StringToLowerASCII(var_name));
257     if (it == variables.end()) {
258       *error = base::StringPrintf("Variable %s%s%s used but not defined.",
259                                   var_begin_delimiter.c_str(),
260                                   var_name.c_str(),
261                                   var_end_delimiter.c_str());
262       return false;
263     }
264 
265     // Replace variable with its value.
266     std::string value = it->second;
267     message->replace(beg_index - var_begin_delimiter_size,
268                      end_index - beg_index + var_begin_delimiter_size +
269                        var_end_delimiter.size(),
270                      value);
271 
272     // And position pointer to after the replacement.
273     beg_index += value.size() - var_begin_delimiter_size;
274   }
275 
276   return true;
277 }
278 
279 // static
IsValidName(const std::string & name)280 bool ExtensionMessageBundle::IsValidName(const std::string& name) {
281   if (name.empty())
282     return false;
283 
284   std::string::const_iterator it = name.begin();
285   for (; it != name.end(); ++it) {
286     // Allow only ascii 0-9, a-z, A-Z, and _ in the name.
287     if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_' && *it != '@')
288       return false;
289   }
290 
291   return true;
292 }
293 
294 // Dictionary interface.
295 
GetL10nMessage(const std::string & name) const296 std::string ExtensionMessageBundle::GetL10nMessage(
297     const std::string& name) const {
298   return GetL10nMessage(name, dictionary_);
299 }
300 
301 // static
GetL10nMessage(const std::string & name,const SubstitutionMap & dictionary)302 std::string ExtensionMessageBundle::GetL10nMessage(
303     const std::string& name, const SubstitutionMap& dictionary) {
304   SubstitutionMap::const_iterator it =
305     dictionary.find(StringToLowerASCII(name));
306   if (it != dictionary.end()) {
307     return it->second;
308   }
309 
310   return "";
311 }
312 
313 ///////////////////////////////////////////////////////////////////////////////
314 //
315 // Renderer helper functions.
316 //
317 ///////////////////////////////////////////////////////////////////////////////
318 
319 // Unique class for Singleton.
320 struct ExtensionToMessagesMap {
321   ExtensionToMessagesMap();
322   ~ExtensionToMessagesMap();
323 
324   // Maps extension ID to message map.
325   ExtensionToL10nMessagesMap messages_map;
326 };
327 
328 static base::LazyInstance<ExtensionToMessagesMap> g_extension_to_messages_map(
329     base::LINKER_INITIALIZED);
330 
ExtensionToMessagesMap()331 ExtensionToMessagesMap::ExtensionToMessagesMap() {}
332 
~ExtensionToMessagesMap()333 ExtensionToMessagesMap::~ExtensionToMessagesMap() {}
334 
GetExtensionToL10nMessagesMap()335 ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
336   return &g_extension_to_messages_map.Get().messages_map;
337 }
338 
GetL10nMessagesMap(const std::string & extension_id)339 L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id) {
340   ExtensionToL10nMessagesMap::iterator it =
341       g_extension_to_messages_map.Get().messages_map.find(extension_id);
342   if (it != g_extension_to_messages_map.Get().messages_map.end())
343     return &(it->second);
344 
345   return NULL;
346 }
347