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