1c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/message_bundle.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/containers/hash_tables.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/lazy_instance.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/linked_ptr.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
16c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/strings/string_util.h"
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/error_utils.h"
21c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/extension_l10n_util.h"
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "extensions/common/manifest_constants.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace errors = manifest_errors;
27d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kContentKey = "content";
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kMessageKey = "message";
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kPlaceholdersKey = "placeholders";
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kPlaceholderBegin = "$";
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kPlaceholderEnd = "$";
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kMessageBegin = "__MSG_";
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kMessageEnd = "__";
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Reserved messages names.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kUILocaleKey = "@@ui_locale";
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kBidiDirectionKey = "@@bidi_dir";
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kBidiReversedDirectionKey =
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "@@bidi_reversed_dir";
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kBidiStartEdgeKey = "@@bidi_start_edge";
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kBidiEndEdgeKey = "@@bidi_end_edge";
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kExtensionIdKey = "@@extension_id";
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Reserved messages values.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kBidiLeftEdgeValue = "left";
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* MessageBundle::kBidiRightEdgeValue = "right";
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Formats message in case we encounter a bad formed key in the JSON object.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns false and sets |error| to actual error message.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool BadKeyMessage(const std::string& name, std::string* error) {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *error = base::StringPrintf(
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "Name of a key \"%s\" is invalid. Only ASCII [a-z], "
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "[A-Z], [0-9] and \"_\" are allowed.",
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.c_str());
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MessageBundle* MessageBundle::Create(const CatalogVector& locale_catalogs,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     std::string* error) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<MessageBundle> message_bundle(new MessageBundle);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!message_bundle->Init(locale_catalogs, error))
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return message_bundle.release();
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::Init(const CatalogVector& locale_catalogs,
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         std::string* error) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary_.clear();
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (CatalogVector::const_reverse_iterator it = locale_catalogs.rbegin();
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != locale_catalogs.rend(); ++it) {
767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    base::DictionaryValue* catalog = (*it).get();
777d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    for (base::DictionaryValue::Iterator message_it(*catalog);
787d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)         !message_it.IsAtEnd(); message_it.Advance()) {
796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      std::string key(base::StringToLowerASCII(message_it.key()));
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!IsValidName(message_it.key()))
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return BadKeyMessage(key, error);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string value;
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!GetMessageValue(message_it.key(), message_it.value(), &value, error))
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Keys are not case-sensitive.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dictionary_[key] = value;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!AppendReservedMessagesForLocale(
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_l10n_util::CurrentLocaleOrDefault(), error))
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::AppendReservedMessagesForLocale(
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& app_locale, std::string* error) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SubstitutionMap append_messages;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  append_messages[kUILocaleKey] = app_locale;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Calling base::i18n::GetTextDirection on non-UI threads doesn't seems safe,
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so we use GetTextDirectionForLocale instead.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::i18n::GetTextDirectionForLocale(app_locale.c_str()) ==
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::i18n::RIGHT_TO_LEFT) {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiDirectionKey] = "rtl";
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiReversedDirectionKey] = "ltr";
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiStartEdgeKey] = kBidiRightEdgeValue;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiEndEdgeKey] = kBidiLeftEdgeValue;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiDirectionKey] = "ltr";
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiReversedDirectionKey] = "rtl";
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiStartEdgeKey] = kBidiLeftEdgeValue;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    append_messages[kBidiEndEdgeKey] = kBidiRightEdgeValue;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add all reserved messages to the dictionary, but check for collisions.
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SubstitutionMap::iterator it = append_messages.begin();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; it != append_messages.end(); ++it) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ContainsKey(dictionary_, it->first)) {
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessage(
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kReservedMessageFound, it->first);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dictionary_[it->first] = it->second;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::GetMessageValue(const std::string& key,
1337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                    const base::Value& name_value,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    std::string* value,
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    std::string* error) const {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the top level tree for given key (name part).
1377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::DictionaryValue* name_tree;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!name_value.GetAsDictionary(&name_tree)) {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str());
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract message from it.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!name_tree->GetString(kMessageKey, value)) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = base::StringPrintf(
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "There is no \"%s\" element for key %s.", kMessageKey, key.c_str());
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SubstitutionMap placeholders;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetPlaceholders(*name_tree, key, &placeholders, error))
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ReplacePlaceholders(placeholders, value, error))
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MessageBundle::MessageBundle() {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)bool MessageBundle::GetPlaceholders(const base::DictionaryValue& name_tree,
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const std::string& name_key,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    SubstitutionMap* placeholders,
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    std::string* error) const {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!name_tree.HasKey(kPlaceholdersKey))
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::DictionaryValue* placeholders_tree;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = base::StringPrintf("Not a valid \"%s\" element for key %s.",
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                kPlaceholdersKey, name_key.c_str());
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  for (base::DictionaryValue::Iterator it(*placeholders_tree); !it.IsAtEnd();
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it.Advance()) {
1787d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const base::DictionaryValue* placeholder;
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& content_key(it.key());
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!IsValidName(content_key))
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return BadKeyMessage(content_key, error);
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!it.value().GetAsDictionary(&placeholder)) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = base::StringPrintf("Invalid placeholder %s for key %s",
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  content_key.c_str(),
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  name_key.c_str());
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string content;
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!placeholder->GetString(kContentKey, &content)) {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = base::StringPrintf("Invalid \"%s\" element for key %s.",
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  kContentKey, name_key.c_str());
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    (*placeholders)[base::StringToLowerASCII(content_key)] = content;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::ReplacePlaceholders(const SubstitutionMap& placeholders,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        std::string* message,
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        std::string* error) const {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ReplaceVariables(placeholders,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          kPlaceholderBegin,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          kPlaceholderEnd,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          message,
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          error);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::ReplaceMessages(std::string* text,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    std::string* error) const {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ReplaceMessagesWithExternalDictionary(dictionary_, text, error);
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MessageBundle::~MessageBundle() {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::ReplaceMessagesWithExternalDictionary(
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SubstitutionMap& dictionary, std::string* text, std::string* error) {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ReplaceVariables(dictionary, kMessageBegin, kMessageEnd, text, error);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::ReplaceVariables(const SubstitutionMap& variables,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::string& var_begin_delimiter,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::string& var_end_delimiter,
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     std::string* message,
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     std::string* error) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string::size_type beg_index = 0;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string::size_type var_begin_delimiter_size =
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var_begin_delimiter.size();
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (true) {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    beg_index = message->find(var_begin_delimiter, beg_index);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (beg_index == message->npos)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Advance it immediately to the begining of possible variable name.
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    beg_index += var_begin_delimiter_size;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (beg_index >= message->size())
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string::size_type end_index =
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      message->find(var_end_delimiter, beg_index);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (end_index == message->npos)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Looking for 1 in substring of ...$1$....
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& var_name =
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      message->substr(beg_index, end_index - beg_index);
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!IsValidName(var_name))
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SubstitutionMap::const_iterator it =
2536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      variables.find(base::StringToLowerASCII(var_name));
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it == variables.end()) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = base::StringPrintf("Variable %s%s%s used but not defined.",
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  var_begin_delimiter.c_str(),
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  var_name.c_str(),
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  var_end_delimiter.c_str());
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Replace variable with its value.
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string value = it->second;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    message->replace(beg_index - var_begin_delimiter_size,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     end_index - beg_index + var_begin_delimiter_size +
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       var_end_delimiter.size(),
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     value);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // And position pointer to after the replacement.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    beg_index += value.size() - var_begin_delimiter_size;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MessageBundle::IsValidName(const std::string& name) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (name.empty())
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string::const_iterator it = name.begin();
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; it != name.end(); ++it) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Allow only ascii 0-9, a-z, A-Z, and _ in the name.
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_' && *it != '@')
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Dictionary interface.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string MessageBundle::GetL10nMessage(const std::string& name) const {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetL10nMessage(name, dictionary_);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string MessageBundle::GetL10nMessage(const std::string& name,
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          const SubstitutionMap& dictionary) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SubstitutionMap::const_iterator it =
3016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      dictionary.find(base::StringToLowerASCII(name));
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != dictionary.end()) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return it->second;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return std::string();
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Renderer helper functions.
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Unique class for Singleton.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct ExtensionToMessagesMap {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionToMessagesMap();
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~ExtensionToMessagesMap();
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Maps extension ID to message map.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionToL10nMessagesMap messages_map;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static base::LazyInstance<ExtensionToMessagesMap> g_extension_to_messages_map =
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LAZY_INSTANCE_INITIALIZER;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToMessagesMap::ExtensionToMessagesMap() {}
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToMessagesMap::~ExtensionToMessagesMap() {}
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return &g_extension_to_messages_map.Get().messages_map;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id) {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionToL10nMessagesMap::iterator it =
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_extension_to_messages_map.Get().messages_map.find(extension_id);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != g_extension_to_messages_map.Get().messages_map.end())
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return &(it->second);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void EraseL10nMessagesMap(const std::string& extension_id) {
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  g_extension_to_messages_map.Get().messages_map.erase(extension_id);
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
349