OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/extensions/extension_message_bundle.h" | 5 #include "chrome/common/extensions/message_bundle.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/hash_tables.h" | 10 #include "base/hash_tables.h" |
11 #include "base/i18n/rtl.h" | 11 #include "base/i18n/rtl.h" |
12 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
13 #include "base/memory/linked_ptr.h" | 13 #include "base/memory/linked_ptr.h" |
14 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
16 #include "base/stringprintf.h" | 16 #include "base/stringprintf.h" |
17 #include "base/utf_string_conversions.h" | 17 #include "base/utf_string_conversions.h" |
18 #include "base/values.h" | 18 #include "base/values.h" |
19 #include "chrome/common/extensions/extension_manifest_constants.h" | 19 #include "chrome/common/extensions/extension_manifest_constants.h" |
20 #include "chrome/common/extensions/extension_error_utils.h" | 20 #include "chrome/common/extensions/extension_error_utils.h" |
21 #include "chrome/common/extensions/extension_l10n_util.h" | 21 #include "chrome/common/extensions/extension_l10n_util.h" |
22 #include "ui/base/l10n/l10n_util.h" | 22 #include "ui/base/l10n/l10n_util.h" |
23 | 23 |
24 namespace errors = extension_manifest_errors; | 24 namespace errors = extension_manifest_errors; |
25 | 25 |
26 const char* ExtensionMessageBundle::kContentKey = "content"; | 26 namespace extensions { |
27 const char* ExtensionMessageBundle::kMessageKey = "message"; | |
28 const char* ExtensionMessageBundle::kPlaceholdersKey = "placeholders"; | |
29 | 27 |
30 const char* ExtensionMessageBundle::kPlaceholderBegin = "$"; | 28 const char* MessageBundle::kContentKey = "content"; |
31 const char* ExtensionMessageBundle::kPlaceholderEnd = "$"; | 29 const char* MessageBundle::kMessageKey = "message"; |
32 const char* ExtensionMessageBundle::kMessageBegin = "__MSG_"; | 30 const char* MessageBundle::kPlaceholdersKey = "placeholders"; |
33 const char* ExtensionMessageBundle::kMessageEnd = "__"; | 31 |
| 32 const char* MessageBundle::kPlaceholderBegin = "$"; |
| 33 const char* MessageBundle::kPlaceholderEnd = "$"; |
| 34 const char* MessageBundle::kMessageBegin = "__MSG_"; |
| 35 const char* MessageBundle::kMessageEnd = "__"; |
34 | 36 |
35 // Reserved messages names. | 37 // Reserved messages names. |
36 const char* ExtensionMessageBundle::kUILocaleKey = "@@ui_locale"; | 38 const char* MessageBundle::kUILocaleKey = "@@ui_locale"; |
37 const char* ExtensionMessageBundle::kBidiDirectionKey = "@@bidi_dir"; | 39 const char* MessageBundle::kBidiDirectionKey = "@@bidi_dir"; |
38 const char* ExtensionMessageBundle::kBidiReversedDirectionKey = | 40 const char* MessageBundle::kBidiReversedDirectionKey = |
39 "@@bidi_reversed_dir"; | 41 "@@bidi_reversed_dir"; |
40 const char* ExtensionMessageBundle::kBidiStartEdgeKey = "@@bidi_start_edge"; | 42 const char* MessageBundle::kBidiStartEdgeKey = "@@bidi_start_edge"; |
41 const char* ExtensionMessageBundle::kBidiEndEdgeKey = "@@bidi_end_edge"; | 43 const char* MessageBundle::kBidiEndEdgeKey = "@@bidi_end_edge"; |
42 const char* ExtensionMessageBundle::kExtensionIdKey = "@@extension_id"; | 44 const char* MessageBundle::kExtensionIdKey = "@@extension_id"; |
43 | 45 |
44 // Reserved messages values. | 46 // Reserved messages values. |
45 const char* ExtensionMessageBundle::kBidiLeftEdgeValue = "left"; | 47 const char* MessageBundle::kBidiLeftEdgeValue = "left"; |
46 const char* ExtensionMessageBundle::kBidiRightEdgeValue = "right"; | 48 const char* MessageBundle::kBidiRightEdgeValue = "right"; |
47 | 49 |
48 // Formats message in case we encounter a bad formed key in the JSON object. | 50 // Formats message in case we encounter a bad formed key in the JSON object. |
49 // Returns false and sets |error| to actual error message. | 51 // Returns false and sets |error| to actual error message. |
50 static bool BadKeyMessage(const std::string& name, std::string* error) { | 52 static bool BadKeyMessage(const std::string& name, std::string* error) { |
51 *error = base::StringPrintf( | 53 *error = base::StringPrintf( |
52 "Name of a key \"%s\" is invalid. Only ASCII [a-z], " | 54 "Name of a key \"%s\" is invalid. Only ASCII [a-z], " |
53 "[A-Z], [0-9] and \"_\" are allowed.", | 55 "[A-Z], [0-9] and \"_\" are allowed.", |
54 name.c_str()); | 56 name.c_str()); |
55 return false; | 57 return false; |
56 } | 58 } |
57 | 59 |
58 // static | 60 // static |
59 ExtensionMessageBundle* ExtensionMessageBundle::Create( | 61 MessageBundle* MessageBundle::Create(const CatalogVector& locale_catalogs, |
60 const CatalogVector& locale_catalogs, | 62 std::string* error) { |
61 std::string* error) { | 63 scoped_ptr<MessageBundle> message_bundle(new MessageBundle); |
62 scoped_ptr<ExtensionMessageBundle> message_bundle( | |
63 new ExtensionMessageBundle); | |
64 if (!message_bundle->Init(locale_catalogs, error)) | 64 if (!message_bundle->Init(locale_catalogs, error)) |
65 return NULL; | 65 return NULL; |
66 | 66 |
67 return message_bundle.release(); | 67 return message_bundle.release(); |
68 } | 68 } |
69 | 69 |
70 bool ExtensionMessageBundle::Init(const CatalogVector& locale_catalogs, | 70 bool MessageBundle::Init(const CatalogVector& locale_catalogs, |
71 std::string* error) { | 71 std::string* error) { |
72 dictionary_.clear(); | 72 dictionary_.clear(); |
73 | 73 |
74 for (CatalogVector::const_reverse_iterator it = locale_catalogs.rbegin(); | 74 for (CatalogVector::const_reverse_iterator it = locale_catalogs.rbegin(); |
75 it != locale_catalogs.rend(); ++it) { | 75 it != locale_catalogs.rend(); ++it) { |
76 DictionaryValue* catalog = (*it).get(); | 76 DictionaryValue* catalog = (*it).get(); |
77 for (DictionaryValue::key_iterator key_it = catalog->begin_keys(); | 77 for (DictionaryValue::key_iterator key_it = catalog->begin_keys(); |
78 key_it != catalog->end_keys(); ++key_it) { | 78 key_it != catalog->end_keys(); ++key_it) { |
79 std::string key(StringToLowerASCII(*key_it)); | 79 std::string key(StringToLowerASCII(*key_it)); |
80 if (!IsValidName(*key_it)) | 80 if (!IsValidName(*key_it)) |
81 return BadKeyMessage(key, error); | 81 return BadKeyMessage(key, error); |
82 std::string value; | 82 std::string value; |
83 if (!GetMessageValue(*key_it, *catalog, &value, error)) | 83 if (!GetMessageValue(*key_it, *catalog, &value, error)) |
84 return false; | 84 return false; |
85 // Keys are not case-sensitive. | 85 // Keys are not case-sensitive. |
86 dictionary_[key] = value; | 86 dictionary_[key] = value; |
87 } | 87 } |
88 } | 88 } |
89 | 89 |
90 if (!AppendReservedMessagesForLocale( | 90 if (!AppendReservedMessagesForLocale( |
91 extension_l10n_util::CurrentLocaleOrDefault(), error)) | 91 extension_l10n_util::CurrentLocaleOrDefault(), error)) |
92 return false; | 92 return false; |
93 | 93 |
94 return true; | 94 return true; |
95 } | 95 } |
96 | 96 |
97 bool ExtensionMessageBundle::AppendReservedMessagesForLocale( | 97 bool MessageBundle::AppendReservedMessagesForLocale( |
98 const std::string& app_locale, std::string* error) { | 98 const std::string& app_locale, std::string* error) { |
99 SubstitutionMap append_messages; | 99 SubstitutionMap append_messages; |
100 append_messages[kUILocaleKey] = app_locale; | 100 append_messages[kUILocaleKey] = app_locale; |
101 | 101 |
102 // Calling base::i18n::GetTextDirection on non-UI threads doesn't seems safe, | 102 // Calling base::i18n::GetTextDirection on non-UI threads doesn't seems safe, |
103 // so we use GetTextDirectionForLocale instead. | 103 // so we use GetTextDirectionForLocale instead. |
104 if (base::i18n::GetTextDirectionForLocale(app_locale.c_str()) == | 104 if (base::i18n::GetTextDirectionForLocale(app_locale.c_str()) == |
105 base::i18n::RIGHT_TO_LEFT) { | 105 base::i18n::RIGHT_TO_LEFT) { |
106 append_messages[kBidiDirectionKey] = "rtl"; | 106 append_messages[kBidiDirectionKey] = "rtl"; |
107 append_messages[kBidiReversedDirectionKey] = "ltr"; | 107 append_messages[kBidiReversedDirectionKey] = "ltr"; |
(...skipping 14 matching lines...) Expand all Loading... |
122 errors::kReservedMessageFound, it->first); | 122 errors::kReservedMessageFound, it->first); |
123 return false; | 123 return false; |
124 } else { | 124 } else { |
125 dictionary_[it->first] = it->second; | 125 dictionary_[it->first] = it->second; |
126 } | 126 } |
127 } | 127 } |
128 | 128 |
129 return true; | 129 return true; |
130 } | 130 } |
131 | 131 |
132 bool ExtensionMessageBundle::GetMessageValue(const std::string& key, | 132 bool MessageBundle::GetMessageValue(const std::string& key, |
133 const DictionaryValue& catalog, | 133 const DictionaryValue& catalog, |
134 std::string* value, | 134 std::string* value, |
135 std::string* error) const { | 135 std::string* error) const { |
136 // Get the top level tree for given key (name part). | 136 // Get the top level tree for given key (name part). |
137 DictionaryValue* name_tree; | 137 DictionaryValue* name_tree; |
138 if (!catalog.GetDictionaryWithoutPathExpansion(key, &name_tree)) { | 138 if (!catalog.GetDictionaryWithoutPathExpansion(key, &name_tree)) { |
139 *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str()); | 139 *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str()); |
140 return false; | 140 return false; |
141 } | 141 } |
142 // Extract message from it. | 142 // Extract message from it. |
143 if (!name_tree->GetString(kMessageKey, value)) { | 143 if (!name_tree->GetString(kMessageKey, value)) { |
144 *error = base::StringPrintf( | 144 *error = base::StringPrintf( |
145 "There is no \"%s\" element for key %s.", kMessageKey, key.c_str()); | 145 "There is no \"%s\" element for key %s.", kMessageKey, key.c_str()); |
146 return false; | 146 return false; |
147 } | 147 } |
148 | 148 |
149 SubstitutionMap placeholders; | 149 SubstitutionMap placeholders; |
150 if (!GetPlaceholders(*name_tree, key, &placeholders, error)) | 150 if (!GetPlaceholders(*name_tree, key, &placeholders, error)) |
151 return false; | 151 return false; |
152 | 152 |
153 if (!ReplacePlaceholders(placeholders, value, error)) | 153 if (!ReplacePlaceholders(placeholders, value, error)) |
154 return false; | 154 return false; |
155 | 155 |
156 return true; | 156 return true; |
157 } | 157 } |
158 | 158 |
159 ExtensionMessageBundle::ExtensionMessageBundle() { | 159 MessageBundle::MessageBundle() { |
160 } | 160 } |
161 | 161 |
162 bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree, | 162 bool MessageBundle::GetPlaceholders(const DictionaryValue& name_tree, |
163 const std::string& name_key, | 163 const std::string& name_key, |
164 SubstitutionMap* placeholders, | 164 SubstitutionMap* placeholders, |
165 std::string* error) const { | 165 std::string* error) const { |
166 if (!name_tree.HasKey(kPlaceholdersKey)) | 166 if (!name_tree.HasKey(kPlaceholdersKey)) |
167 return true; | 167 return true; |
168 | 168 |
169 DictionaryValue* placeholders_tree; | 169 DictionaryValue* placeholders_tree; |
170 if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) { | 170 if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) { |
171 *error = base::StringPrintf("Not a valid \"%s\" element for key %s.", | 171 *error = base::StringPrintf("Not a valid \"%s\" element for key %s.", |
172 kPlaceholdersKey, name_key.c_str()); | 172 kPlaceholdersKey, name_key.c_str()); |
173 return false; | 173 return false; |
174 } | 174 } |
175 | 175 |
(...skipping 15 matching lines...) Expand all Loading... |
191 *error = base::StringPrintf("Invalid \"%s\" element for key %s.", | 191 *error = base::StringPrintf("Invalid \"%s\" element for key %s.", |
192 kContentKey, name_key.c_str()); | 192 kContentKey, name_key.c_str()); |
193 return false; | 193 return false; |
194 } | 194 } |
195 (*placeholders)[StringToLowerASCII(content_key)] = content; | 195 (*placeholders)[StringToLowerASCII(content_key)] = content; |
196 } | 196 } |
197 | 197 |
198 return true; | 198 return true; |
199 } | 199 } |
200 | 200 |
201 bool ExtensionMessageBundle::ReplacePlaceholders( | 201 bool MessageBundle::ReplacePlaceholders(const SubstitutionMap& placeholders, |
202 const SubstitutionMap& placeholders, | 202 std::string* message, |
203 std::string* message, | 203 std::string* error) const { |
204 std::string* error) const { | |
205 return ReplaceVariables(placeholders, | 204 return ReplaceVariables(placeholders, |
206 kPlaceholderBegin, | 205 kPlaceholderBegin, |
207 kPlaceholderEnd, | 206 kPlaceholderEnd, |
208 message, | 207 message, |
209 error); | 208 error); |
210 } | 209 } |
211 | 210 |
212 bool ExtensionMessageBundle::ReplaceMessages(std::string* text, | 211 bool MessageBundle::ReplaceMessages(std::string* text, |
213 std::string* error) const { | 212 std::string* error) const { |
214 return ReplaceMessagesWithExternalDictionary(dictionary_, text, error); | 213 return ReplaceMessagesWithExternalDictionary(dictionary_, text, error); |
215 } | 214 } |
216 | 215 |
217 ExtensionMessageBundle::~ExtensionMessageBundle() { | 216 MessageBundle::~MessageBundle() { |
218 } | 217 } |
219 | 218 |
220 // static | 219 // static |
221 bool ExtensionMessageBundle::ReplaceMessagesWithExternalDictionary( | 220 bool MessageBundle::ReplaceMessagesWithExternalDictionary( |
222 const SubstitutionMap& dictionary, std::string* text, std::string* error) { | 221 const SubstitutionMap& dictionary, std::string* text, std::string* error) { |
223 return ReplaceVariables(dictionary, kMessageBegin, kMessageEnd, text, error); | 222 return ReplaceVariables(dictionary, kMessageBegin, kMessageEnd, text, error); |
224 } | 223 } |
225 | 224 |
226 // static | 225 // static |
227 bool ExtensionMessageBundle::ReplaceVariables( | 226 bool MessageBundle::ReplaceVariables(const SubstitutionMap& variables, |
228 const SubstitutionMap& variables, | 227 const std::string& var_begin_delimiter, |
229 const std::string& var_begin_delimiter, | 228 const std::string& var_end_delimiter, |
230 const std::string& var_end_delimiter, | 229 std::string* message, |
231 std::string* message, | 230 std::string* error) { |
232 std::string* error) { | |
233 std::string::size_type beg_index = 0; | 231 std::string::size_type beg_index = 0; |
234 const std::string::size_type var_begin_delimiter_size = | 232 const std::string::size_type var_begin_delimiter_size = |
235 var_begin_delimiter.size(); | 233 var_begin_delimiter.size(); |
236 while (true) { | 234 while (true) { |
237 beg_index = message->find(var_begin_delimiter, beg_index); | 235 beg_index = message->find(var_begin_delimiter, beg_index); |
238 if (beg_index == message->npos) | 236 if (beg_index == message->npos) |
239 return true; | 237 return true; |
240 | 238 |
241 // Advance it immediately to the begining of possible variable name. | 239 // Advance it immediately to the begining of possible variable name. |
242 beg_index += var_begin_delimiter_size; | 240 beg_index += var_begin_delimiter_size; |
(...skipping 27 matching lines...) Expand all Loading... |
270 value); | 268 value); |
271 | 269 |
272 // And position pointer to after the replacement. | 270 // And position pointer to after the replacement. |
273 beg_index += value.size() - var_begin_delimiter_size; | 271 beg_index += value.size() - var_begin_delimiter_size; |
274 } | 272 } |
275 | 273 |
276 return true; | 274 return true; |
277 } | 275 } |
278 | 276 |
279 // static | 277 // static |
280 bool ExtensionMessageBundle::IsValidName(const std::string& name) { | 278 bool MessageBundle::IsValidName(const std::string& name) { |
281 if (name.empty()) | 279 if (name.empty()) |
282 return false; | 280 return false; |
283 | 281 |
284 std::string::const_iterator it = name.begin(); | 282 std::string::const_iterator it = name.begin(); |
285 for (; it != name.end(); ++it) { | 283 for (; it != name.end(); ++it) { |
286 // Allow only ascii 0-9, a-z, A-Z, and _ in the name. | 284 // Allow only ascii 0-9, a-z, A-Z, and _ in the name. |
287 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_' && *it != '@') | 285 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_' && *it != '@') |
288 return false; | 286 return false; |
289 } | 287 } |
290 | 288 |
291 return true; | 289 return true; |
292 } | 290 } |
293 | 291 |
294 // Dictionary interface. | 292 // Dictionary interface. |
295 | 293 |
296 std::string ExtensionMessageBundle::GetL10nMessage( | 294 std::string MessageBundle::GetL10nMessage(const std::string& name) const { |
297 const std::string& name) const { | |
298 return GetL10nMessage(name, dictionary_); | 295 return GetL10nMessage(name, dictionary_); |
299 } | 296 } |
300 | 297 |
301 // static | 298 // static |
302 std::string ExtensionMessageBundle::GetL10nMessage( | 299 std::string MessageBundle::GetL10nMessage(const std::string& name, |
303 const std::string& name, const SubstitutionMap& dictionary) { | 300 const SubstitutionMap& dictionary) { |
304 SubstitutionMap::const_iterator it = | 301 SubstitutionMap::const_iterator it = |
305 dictionary.find(StringToLowerASCII(name)); | 302 dictionary.find(StringToLowerASCII(name)); |
306 if (it != dictionary.end()) { | 303 if (it != dictionary.end()) { |
307 return it->second; | 304 return it->second; |
308 } | 305 } |
309 | 306 |
310 return ""; | 307 return ""; |
311 } | 308 } |
312 | 309 |
313 /////////////////////////////////////////////////////////////////////////////// | 310 /////////////////////////////////////////////////////////////////////////////// |
(...skipping 23 matching lines...) Expand all Loading... |
337 } | 334 } |
338 | 335 |
339 L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id) { | 336 L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id) { |
340 ExtensionToL10nMessagesMap::iterator it = | 337 ExtensionToL10nMessagesMap::iterator it = |
341 g_extension_to_messages_map.Get().messages_map.find(extension_id); | 338 g_extension_to_messages_map.Get().messages_map.find(extension_id); |
342 if (it != g_extension_to_messages_map.Get().messages_map.end()) | 339 if (it != g_extension_to_messages_map.Get().messages_map.end()) |
343 return &(it->second); | 340 return &(it->second); |
344 | 341 |
345 return NULL; | 342 return NULL; |
346 } | 343 } |
| 344 |
| 345 } // namespace extensions |
OLD | NEW |