Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / extensions / common / message_bundle.cc
blob1340e549935fade3a68f2e70dcdeac801982974e
1 // Copyright 2014 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.
5 #include "extensions/common/message_bundle.h"
7 #include <string>
8 #include <vector>
10 #include "base/containers/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.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "extensions/common/error_utils.h"
21 #include "extensions/common/extension_l10n_util.h"
22 #include "extensions/common/manifest_constants.h"
24 namespace extensions {
26 namespace errors = manifest_errors;
28 const char* MessageBundle::kContentKey = "content";
29 const char* MessageBundle::kMessageKey = "message";
30 const char* MessageBundle::kPlaceholdersKey = "placeholders";
32 const char* MessageBundle::kPlaceholderBegin = "$";
33 const char* MessageBundle::kPlaceholderEnd = "$";
34 const char* MessageBundle::kMessageBegin = "__MSG_";
35 const char* MessageBundle::kMessageEnd = "__";
37 // Reserved messages names.
38 const char* MessageBundle::kUILocaleKey = "@@ui_locale";
39 const char* MessageBundle::kBidiDirectionKey = "@@bidi_dir";
40 const char* MessageBundle::kBidiReversedDirectionKey =
41 "@@bidi_reversed_dir";
42 const char* MessageBundle::kBidiStartEdgeKey = "@@bidi_start_edge";
43 const char* MessageBundle::kBidiEndEdgeKey = "@@bidi_end_edge";
44 const char* MessageBundle::kExtensionIdKey = "@@extension_id";
46 // Reserved messages values.
47 const char* MessageBundle::kBidiLeftEdgeValue = "left";
48 const char* MessageBundle::kBidiRightEdgeValue = "right";
50 // Formats message in case we encounter a bad formed key in the JSON object.
51 // Returns false and sets |error| to actual error message.
52 static bool BadKeyMessage(const std::string& name, std::string* error) {
53 *error = base::StringPrintf(
54 "Name of a key \"%s\" is invalid. Only ASCII [a-z], "
55 "[A-Z], [0-9] and \"_\" are allowed.",
56 name.c_str());
57 return false;
60 // static
61 MessageBundle* MessageBundle::Create(const CatalogVector& locale_catalogs,
62 std::string* error) {
63 scoped_ptr<MessageBundle> message_bundle(new MessageBundle);
64 if (!message_bundle->Init(locale_catalogs, error))
65 return NULL;
67 return message_bundle.release();
70 bool MessageBundle::Init(const CatalogVector& locale_catalogs,
71 std::string* error) {
72 dictionary_.clear();
74 for (CatalogVector::const_reverse_iterator it = locale_catalogs.rbegin();
75 it != locale_catalogs.rend(); ++it) {
76 base::DictionaryValue* catalog = (*it).get();
77 for (base::DictionaryValue::Iterator message_it(*catalog);
78 !message_it.IsAtEnd(); message_it.Advance()) {
79 std::string key(base::StringToLowerASCII(message_it.key()));
80 if (!IsValidName(message_it.key()))
81 return BadKeyMessage(key, error);
82 std::string value;
83 if (!GetMessageValue(message_it.key(), message_it.value(), &value, error))
84 return false;
85 // Keys are not case-sensitive.
86 dictionary_[key] = value;
90 if (!AppendReservedMessagesForLocale(
91 extension_l10n_util::CurrentLocaleOrDefault(), error))
92 return false;
94 return true;
97 bool MessageBundle::AppendReservedMessagesForLocale(
98 const std::string& app_locale, std::string* error) {
99 SubstitutionMap append_messages;
100 append_messages[kUILocaleKey] = app_locale;
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;
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 = ErrorUtils::FormatErrorMessage(
122 errors::kReservedMessageFound, it->first);
123 return false;
124 } else {
125 dictionary_[it->first] = it->second;
129 return true;
132 bool MessageBundle::GetMessageValue(const std::string& key,
133 const base::Value& name_value,
134 std::string* value,
135 std::string* error) const {
136 // Get the top level tree for given key (name part).
137 const base::DictionaryValue* name_tree;
138 if (!name_value.GetAsDictionary(&name_tree)) {
139 *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str());
140 return false;
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;
149 SubstitutionMap placeholders;
150 if (!GetPlaceholders(*name_tree, key, &placeholders, error))
151 return false;
153 if (!ReplacePlaceholders(placeholders, value, error))
154 return false;
156 return true;
159 MessageBundle::MessageBundle() {
162 bool MessageBundle::GetPlaceholders(const base::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;
169 const base::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;
176 for (base::DictionaryValue::Iterator it(*placeholders_tree); !it.IsAtEnd();
177 it.Advance()) {
178 const base::DictionaryValue* placeholder;
179 const std::string& content_key(it.key());
180 if (!IsValidName(content_key))
181 return BadKeyMessage(content_key, error);
182 if (!it.value().GetAsDictionary(&placeholder)) {
183 *error = base::StringPrintf("Invalid placeholder %s for key %s",
184 content_key.c_str(),
185 name_key.c_str());
186 return false;
188 std::string content;
189 if (!placeholder->GetString(kContentKey, &content)) {
190 *error = base::StringPrintf("Invalid \"%s\" element for key %s.",
191 kContentKey, name_key.c_str());
192 return false;
194 (*placeholders)[base::StringToLowerASCII(content_key)] = content;
197 return true;
200 bool MessageBundle::ReplacePlaceholders(const SubstitutionMap& placeholders,
201 std::string* message,
202 std::string* error) const {
203 return ReplaceVariables(placeholders,
204 kPlaceholderBegin,
205 kPlaceholderEnd,
206 message,
207 error);
210 bool MessageBundle::ReplaceMessages(std::string* text,
211 std::string* error) const {
212 return ReplaceMessagesWithExternalDictionary(dictionary_, text, error);
215 MessageBundle::~MessageBundle() {
218 // static
219 bool MessageBundle::ReplaceMessagesWithExternalDictionary(
220 const SubstitutionMap& dictionary, std::string* text, std::string* error) {
221 return ReplaceVariables(dictionary, kMessageBegin, kMessageEnd, text, error);
224 // static
225 bool MessageBundle::ReplaceVariables(const SubstitutionMap& variables,
226 const std::string& var_begin_delimiter,
227 const std::string& var_end_delimiter,
228 std::string* message,
229 std::string* error) {
230 std::string::size_type beg_index = 0;
231 const std::string::size_type var_begin_delimiter_size =
232 var_begin_delimiter.size();
233 while (true) {
234 beg_index = message->find(var_begin_delimiter, beg_index);
235 if (beg_index == message->npos)
236 return true;
238 // Advance it immediately to the begining of possible variable name.
239 beg_index += var_begin_delimiter_size;
240 if (beg_index >= message->size())
241 return true;
242 std::string::size_type end_index =
243 message->find(var_end_delimiter, beg_index);
244 if (end_index == message->npos)
245 return true;
247 // Looking for 1 in substring of ...$1$....
248 const std::string& var_name =
249 message->substr(beg_index, end_index - beg_index);
250 if (!IsValidName(var_name))
251 continue;
252 SubstitutionMap::const_iterator it =
253 variables.find(base::StringToLowerASCII(var_name));
254 if (it == variables.end()) {
255 *error = base::StringPrintf("Variable %s%s%s used but not defined.",
256 var_begin_delimiter.c_str(),
257 var_name.c_str(),
258 var_end_delimiter.c_str());
259 return false;
262 // Replace variable with its value.
263 std::string value = it->second;
264 message->replace(beg_index - var_begin_delimiter_size,
265 end_index - beg_index + var_begin_delimiter_size +
266 var_end_delimiter.size(),
267 value);
269 // And position pointer to after the replacement.
270 beg_index += value.size() - var_begin_delimiter_size;
273 return true;
276 // static
277 bool MessageBundle::IsValidName(const std::string& name) {
278 if (name.empty())
279 return false;
281 std::string::const_iterator it = name.begin();
282 for (; it != name.end(); ++it) {
283 // Allow only ascii 0-9, a-z, A-Z, and _ in the name.
284 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_' && *it != '@')
285 return false;
288 return true;
291 // Dictionary interface.
293 std::string MessageBundle::GetL10nMessage(const std::string& name) const {
294 return GetL10nMessage(name, dictionary_);
297 // static
298 std::string MessageBundle::GetL10nMessage(const std::string& name,
299 const SubstitutionMap& dictionary) {
300 SubstitutionMap::const_iterator it =
301 dictionary.find(base::StringToLowerASCII(name));
302 if (it != dictionary.end()) {
303 return it->second;
306 return std::string();
309 ///////////////////////////////////////////////////////////////////////////////
311 // Renderer helper functions.
313 ///////////////////////////////////////////////////////////////////////////////
315 // Unique class for Singleton.
316 struct ExtensionToMessagesMap {
317 ExtensionToMessagesMap();
318 ~ExtensionToMessagesMap();
320 // Maps extension ID to message map.
321 ExtensionToL10nMessagesMap messages_map;
324 static base::LazyInstance<ExtensionToMessagesMap> g_extension_to_messages_map =
325 LAZY_INSTANCE_INITIALIZER;
327 ExtensionToMessagesMap::ExtensionToMessagesMap() {}
329 ExtensionToMessagesMap::~ExtensionToMessagesMap() {}
331 ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
332 return &g_extension_to_messages_map.Get().messages_map;
335 L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id) {
336 ExtensionToL10nMessagesMap::iterator it =
337 g_extension_to_messages_map.Get().messages_map.find(extension_id);
338 if (it != g_extension_to_messages_map.Get().messages_map.end())
339 return &(it->second);
341 return NULL;
344 void EraseL10nMessagesMap(const std::string& extension_id) {
345 g_extension_to_messages_map.Get().messages_map.erase(extension_id);
348 } // namespace extensions