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.
6 * Parse a very small subset of HTML. This ensures that insecure HTML /
7 * javascript cannot be injected into the new tab page.
8 * @param {string} s The string to parse.
9 * @param {Array.<string>=} opt_extraTags Optional extra allowed tags.
10 * @param {Object.<string, function(Node, string):boolean>=} opt_extraAttrs
11 * Optional extra allowed attributes (all tags are run through these).
12 * @throws {Error} In case of non supported markup.
13 * @return {DocumentFragment} A document fragment containing the DOM tree.
15 var parseHtmlSubset = (function() {
18 var allowedAttributes = {
19 'href': function(node, value) {
20 // Only allow a[href] starting with chrome:// and https://
21 return node.tagName == 'A' && (value.indexOf('chrome://') == 0 ||
22 value.indexOf('https://') == 0);
24 'target': function(node, value) {
25 // Allow a[target] but reset the value to "".
26 if (node.tagName != 'A')
28 node.setAttribute('target', '');
34 * Whitelist of tag names allowed in parseHtmlSubset.
35 * @type {!Array.<string>}
38 var allowedTags = ['A', 'B', 'STRONG'];
42 for (var i = 0; i < arguments.length; ++i) {
43 if (typeof arguments[i] == 'object') {
44 for (var key in arguments[i]) {
45 if (arguments[i].hasOwnProperty(key))
46 clone[key] = arguments[i][key];
55 for (var i = 0; i < n.childNodes.length; i++) {
56 walk(n.childNodes[i], f);
60 function assertElement(tags, node) {
61 if (tags.indexOf(node.tagName) == -1)
62 throw Error(node.tagName + ' is not supported');
65 function assertAttribute(attrs, attrNode, node) {
66 var n = attrNode.nodeName;
67 var v = attrNode.nodeValue;
68 if (!attrs.hasOwnProperty(n) || !attrs[n](node, v))
69 throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported');
72 return function(s, opt_extraTags, opt_extraAttrs) {
74 (opt_extraTags || []).map(function(str) { return str.toUpperCase(); });
75 var tags = allowedTags.concat(extraTags);
76 var attrs = merge(allowedAttributes, opt_extraAttrs || {});
78 var doc = document.implementation.createHTMLDocument('');
79 var r = doc.createRange();
80 r.selectNode(doc.body);
81 // This does not execute any scripts because the document has no view.
82 var df = r.createContextualFragment(s);
83 walk(df, function(node) {
84 switch (node.nodeType) {
85 case Node.ELEMENT_NODE:
86 assertElement(tags, node);
87 var nodeAttrs = node.attributes;
88 for (var i = 0; i < nodeAttrs.length; ++i) {
89 assertAttribute(attrs, nodeAttrs[i], node);
93 case Node.COMMENT_NODE:
94 case Node.DOCUMENT_FRAGMENT_NODE:
99 throw Error('Node type ' + node.nodeType + ' is not supported');