Home | History | Annotate | Download | only in extensions
      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.
     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
     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 
     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 
     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 
    132 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 
    159 ExtensionMessageBundle::ExtensionMessageBundle() {
    160 }
    161 
    162 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 
    201 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 
    212 bool ExtensionMessageBundle::ReplaceMessages(std::string* text,
    213                                              std::string* error) const {
    214   return ReplaceMessagesWithExternalDictionary(dictionary_, text, error);
    215 }
    216 
    217 ExtensionMessageBundle::~ExtensionMessageBundle() {
    218 }
    219 
    220 // static
    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
    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
    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 
    296 std::string ExtensionMessageBundle::GetL10nMessage(
    297     const std::string& name) const {
    298   return GetL10nMessage(name, dictionary_);
    299 }
    300 
    301 // static
    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 
    331 ExtensionToMessagesMap::ExtensionToMessagesMap() {}
    332 
    333 ExtensionToMessagesMap::~ExtensionToMessagesMap() {}
    334 
    335 ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
    336   return &g_extension_to_messages_map.Get().messages_map;
    337 }
    338 
    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