Home | History | Annotate | Download | only in extensions
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/common/extensions/message_bundle.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     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/stringprintf.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/values.h"
     19 #include "chrome/common/extensions/extension_l10n_util.h"
     20 #include "chrome/common/extensions/extension_manifest_constants.h"
     21 #include "extensions/common/error_utils.h"
     22 #include "ui/base/l10n/l10n_util.h"
     23 
     24 namespace errors = extension_manifest_errors;
     25 
     26 namespace extensions {
     27 
     28 const char* MessageBundle::kContentKey = "content";
     29 const char* MessageBundle::kMessageKey = "message";
     30 const char* MessageBundle::kPlaceholdersKey = "placeholders";
     31 
     32 const char* MessageBundle::kPlaceholderBegin = "$";
     33 const char* MessageBundle::kPlaceholderEnd = "$";
     34 const char* MessageBundle::kMessageBegin = "__MSG_";
     35 const char* MessageBundle::kMessageEnd = "__";
     36 
     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";
     45 
     46 // Reserved messages values.
     47 const char* MessageBundle::kBidiLeftEdgeValue = "left";
     48 const char* MessageBundle::kBidiRightEdgeValue = "right";
     49 
     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;
     58 }
     59 
     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;
     66 
     67   return message_bundle.release();
     68 }
     69 
     70 bool MessageBundle::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     base::DictionaryValue* catalog = (*it).get();
     77     for (base::DictionaryValue::Iterator message_it(*catalog);
     78          !message_it.IsAtEnd(); message_it.Advance()) {
     79       std::string key(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;
     87     }
     88   }
     89 
     90   if (!AppendReservedMessagesForLocale(
     91       extension_l10n_util::CurrentLocaleOrDefault(), error))
     92     return false;
     93 
     94   return true;
     95 }
     96 
     97 bool MessageBundle::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 = ErrorUtils::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 
    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;
    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 
    159 MessageBundle::MessageBundle() {
    160 }
    161 
    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;
    168 
    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;
    174   }
    175 
    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;
    187     }
    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;
    193     }
    194     (*placeholders)[StringToLowerASCII(content_key)] = content;
    195   }
    196 
    197   return true;
    198 }
    199 
    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);
    208 }
    209 
    210 bool MessageBundle::ReplaceMessages(std::string* text,
    211                                     std::string* error) const {
    212   return ReplaceMessagesWithExternalDictionary(dictionary_, text, error);
    213 }
    214 
    215 MessageBundle::~MessageBundle() {
    216 }
    217 
    218 // static
    219 bool MessageBundle::ReplaceMessagesWithExternalDictionary(
    220     const SubstitutionMap& dictionary, std::string* text, std::string* error) {
    221   return ReplaceVariables(dictionary, kMessageBegin, kMessageEnd, text, error);
    222 }
    223 
    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;
    237 
    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;
    246 
    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(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;
    260     }
    261 
    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);
    268 
    269     // And position pointer to after the replacement.
    270     beg_index += value.size() - var_begin_delimiter_size;
    271   }
    272 
    273   return true;
    274 }
    275 
    276 // static
    277 bool MessageBundle::IsValidName(const std::string& name) {
    278   if (name.empty())
    279     return false;
    280 
    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;
    286   }
    287 
    288   return true;
    289 }
    290 
    291 // Dictionary interface.
    292 
    293 std::string MessageBundle::GetL10nMessage(const std::string& name) const {
    294   return GetL10nMessage(name, dictionary_);
    295 }
    296 
    297 // static
    298 std::string MessageBundle::GetL10nMessage(const std::string& name,
    299                                           const SubstitutionMap& dictionary) {
    300   SubstitutionMap::const_iterator it =
    301     dictionary.find(StringToLowerASCII(name));
    302   if (it != dictionary.end()) {
    303     return it->second;
    304   }
    305 
    306   return std::string();
    307 }
    308 
    309 ///////////////////////////////////////////////////////////////////////////////
    310 //
    311 // Renderer helper functions.
    312 //
    313 ///////////////////////////////////////////////////////////////////////////////
    314 
    315 // Unique class for Singleton.
    316 struct ExtensionToMessagesMap {
    317   ExtensionToMessagesMap();
    318   ~ExtensionToMessagesMap();
    319 
    320   // Maps extension ID to message map.
    321   ExtensionToL10nMessagesMap messages_map;
    322 };
    323 
    324 static base::LazyInstance<ExtensionToMessagesMap> g_extension_to_messages_map =
    325     LAZY_INSTANCE_INITIALIZER;
    326 
    327 ExtensionToMessagesMap::ExtensionToMessagesMap() {}
    328 
    329 ExtensionToMessagesMap::~ExtensionToMessagesMap() {}
    330 
    331 ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
    332   return &g_extension_to_messages_map.Get().messages_map;
    333 }
    334 
    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);
    340 
    341   return NULL;
    342 }
    343 
    344 void EraseL10nMessagesMap(const std::string& extension_id) {
    345   g_extension_to_messages_map.Get().messages_map.erase(extension_id);
    346 }
    347 
    348 }  // namespace extensions
    349