From 86f76ebbe5b1da4438e257663daa40091a5883ee Mon Sep 17 00:00:00 2001 From: "arv@chromium.org" Date: Mon, 26 Apr 2010 22:30:44 +0000 Subject: Refactor parts of the NTP to split things into more managable chunks. BUG=None TEST=Manual Review URL: http://codereview.chromium.org/1759007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45631 0039d316-1c4b-4281-b951-d872f2087c98 --- .../browser/resources/shared/js/i18n_template.js | 108 +++++++++++++++++++++ .../browser/resources/shared/js/local_strings.js | 47 +++++++++ .../resources/shared/js/parse_html_subset.js | 79 +++++++++++++++ .../shared/js/parse_html_subset_test.html | 85 ++++++++++++++++ 4 files changed, 319 insertions(+) create mode 100644 chrome/browser/resources/shared/js/i18n_template.js create mode 100644 chrome/browser/resources/shared/js/local_strings.js create mode 100644 chrome/browser/resources/shared/js/parse_html_subset.js create mode 100644 chrome/browser/resources/shared/js/parse_html_subset_test.html (limited to 'chrome/browser/resources/shared') diff --git a/chrome/browser/resources/shared/js/i18n_template.js b/chrome/browser/resources/shared/js/i18n_template.js new file mode 100644 index 0000000..2645bdb --- /dev/null +++ b/chrome/browser/resources/shared/js/i18n_template.js @@ -0,0 +1,108 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview This is a simple template engine inspired by JsTemplates + * optimized for i18n. + * + * It currently supports two handlers: + * + * * i18n-content which sets the textContent of the element + * + * + * i18nTemplate.process(element, {'myContent': 'Content'}); + * + * * i18n-values is a list of attribute-value or property-value pairs. + * Properties are prefixed with a '.' and can contain nested properties. + * + * + * i18nTemplate.process(element, { + * 'myTitle': 'Title', + * 'fontSize': '13px' + * }); + */ + +var i18nTemplate = (function() { + /** + * This provides the handlers for the templating engine. The key is used as + * the attribute name and the value is the function that gets called for every + * single node that has this attribute. + * @type {Object} + */ + var handlers = { + /** + * This handler sets the textContent of the element. + */ + 'i18n-content': function(element, attributeValue, obj) { + element.textContent = obj[attributeValue]; + }, + + /** + * This is used to set HTML attributes and DOM properties,. The syntax is: + * attributename:key; + * .domProperty:key; + * .nested.dom.property:key + */ + 'i18n-values': function(element, attributeValue, obj) { + var parts = attributeValue.replace(/\s/g, '').split(/;/); + for (var j = 0; j < parts.length; j++) { + var a = parts[j].match(/^([^:]+):(.+)$/); + if (a) { + var propName = a[1]; + var propExpr = a[2]; + + // Ignore missing properties + if (propExpr in obj) { + var value = obj[propExpr]; + if (propName.charAt(0) == '.') { + var path = propName.slice(1).split('.'); + var object = element; + while (object && path.length > 1) { + object = object[path.shift()]; + } + if (object) { + object[path] = value; + // In case we set innerHTML (ignoring others) we need to + // recursively check the content + if (path == 'innerHTML') { + process(element, obj); + } + } + } else { + element.setAttribute(propName, value); + } + } else { + console.warn('i18n-values: Missing value for "' + propExpr + '"'); + } + } + } + } + }; + + var attributeNames = []; + for (var key in handlers) { + attributeNames.push(key); + } + var selector = '[' + attributeNames.join('],[') + ']'; + + /** + * Processes a DOM tree with the {@code obj} map. + */ + function process(node, obj) { + var elements = node.querySelectorAll(selector); + for (var element, i = 0; element = elements[i]; i++) { + for (var j = 0; j < attributeNames.length; j++) { + var name = attributeNames[j]; + var att = element.getAttribute(name); + if (att != null) { + handlers[name](element, att, obj); + } + } + } + } + + return { + process: process + }; +})(); diff --git a/chrome/browser/resources/shared/js/local_strings.js b/chrome/browser/resources/shared/js/local_strings.js new file mode 100644 index 0000000..75b9859 --- /dev/null +++ b/chrome/browser/resources/shared/js/local_strings.js @@ -0,0 +1,47 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * The local strings get injected into the page usig a variable named + * {@code templateData}. This class provides a simpler interface to access those + * strings. + * @constructor + */ +function LocalStrings() { +} + +LocalStrings.prototype = { + /** + * The template data object. + * @type {Object} + */ + templateData: null, + + /** + * Gets a localized string by its id. + * @param {string} s The ID of the string we want. + * @return {string} The localized string. + */ + getString: function(id) { + // TODO(arv): We should not rely on a global variable here. + return (this.templateData || window.templateData)[id] || ''; + }, + + /** + * Returns a formatted localized string where $1 to $9 are replaced by the + * second to the tenth argument. + * @param {string} id The ID of the string we want. + * @param {...string} The extra values to include in the formatted output. + * @return {string} The formatted string. + */ + getStringF: function(id, var_args) { + var s = this.getString(id); + var args = arguments; + return s.replace(/\$[$1-9]/g, function(m) { + if (m == '$$') + return '$'; + return args[m[1]]; + }); + } +}; diff --git a/chrome/browser/resources/shared/js/parse_html_subset.js b/chrome/browser/resources/shared/js/parse_html_subset.js new file mode 100644 index 0000000..eecf6ee --- /dev/null +++ b/chrome/browser/resources/shared/js/parse_html_subset.js @@ -0,0 +1,79 @@ +/** + * Whitelist of tag names allowed in parseHtmlSubset. + * @type {[string]} + */ +var allowedTags = ['A', 'B', 'STRONG']; + +/** + * Parse a very small subset of HTML. + * @param {string} s The string to parse. + * @throws {Error} In case of non supported markup. + * @return {DocumentFragment} A document fragment containing the DOM tree. + */ +var allowedAttributes = { + 'href': function(node, value) { + // Only allow a[href] starting with http:// and https:// + return node.tagName == 'A' && (value.indexOf('http://') == 0 || + value.indexOf('https://') == 0); + }, + 'target': function(node, value) { + // Allow a[target] but reset the value to "". + if (node.tagName != 'A') + return false; + node.setAttribute('target', ''); + return true; + } +} + +/** + * Parse a very small subset of HTML. This ensures that insecure HTML / + * javascript cannot be injected into the new tab page. + * @param {string} s The string to parse. + * @throws {Error} In case of non supported markup. + * @return {DocumentFragment} A document fragment containing the DOM tree. + */ +function parseHtmlSubset(s) { + function walk(n, f) { + f(n); + for (var i = 0; i < n.childNodes.length; i++) { + walk(n.childNodes[i], f); + } + } + + function assertElement(node) { + if (allowedTags.indexOf(node.tagName) == -1) + throw Error(node.tagName + ' is not supported'); + } + + function assertAttribute(attrNode, node) { + var n = attrNode.nodeName; + var v = attrNode.nodeValue; + if (!allowedAttributes.hasOwnProperty(n) || !allowedAttributes[n](node, v)) + throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported'); + } + + var r = document.createRange(); + r.selectNode(document.body); + // This does not execute any scripts. + var df = r.createContextualFragment(s); + walk(df, function(node) { + switch (node.nodeType) { + case Node.ELEMENT_NODE: + assertElement(node); + var attrs = node.attributes; + for (var i = 0; i < attrs.length; i++) { + assertAttribute(attrs[i], node); + } + break; + + case Node.COMMENT_NODE: + case Node.DOCUMENT_FRAGMENT_NODE: + case Node.TEXT_NODE: + break; + + default: + throw Error('Node type ' + node.nodeType + ' is not supported'); + } + }); + return df; +} diff --git a/chrome/browser/resources/shared/js/parse_html_subset_test.html b/chrome/browser/resources/shared/js/parse_html_subset_test.html new file mode 100644 index 0000000..54bb3da --- /dev/null +++ b/chrome/browser/resources/shared/js/parse_html_subset_test.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + -- cgit v1.1