OLD | NEW |
| 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. |
| 4 |
| 5 'use strict'; |
| 6 |
1 /** | 7 /** |
2 * Whitelist of tag names allowed in parseHtmlSubset. | 8 * Whitelist of tag names allowed in parseHtmlSubset. |
3 * @type {[string]} | 9 * @type {[string]} |
4 */ | 10 */ |
5 var allowedTags = ['A', 'B', 'STRONG']; | 11 var allowedTags = ['A', 'B', 'STRONG']; |
6 | 12 |
7 /** | 13 /** |
8 * Parse a very small subset of HTML. | 14 * Parse a very small subset of HTML. |
9 * @param {string} s The string to parse. | 15 * @param {string} s The string to parse. |
10 * @throws {Error} In case of non supported markup. | 16 * @throws {Error} In case of non supported markup. |
11 * @return {DocumentFragment} A document fragment containing the DOM tree. | 17 * @return {DocumentFragment} A document fragment containing the DOM tree. |
12 */ | 18 */ |
13 var allowedAttributes = { | 19 var allowedAttributes = { |
14 'href': function(node, value) { | 20 'href': function(node, value) { |
15 // Only allow a[href] starting with http:// and https:// | 21 // Only allow a[href] starting with http:// and https:// |
16 return node.tagName == 'A' && (value.indexOf('http://') == 0 || | 22 return node.tagName == 'A' && (value.indexOf('http://') == 0 || |
17 value.indexOf('https://') == 0); | 23 value.indexOf('https://') == 0); |
18 }, | 24 }, |
19 'target': function(node, value) { | 25 'target': function(node, value) { |
20 // Allow a[target] but reset the value to "". | 26 // Allow a[target] but reset the value to "". |
21 if (node.tagName != 'A') | 27 if (node.tagName != 'A') |
22 return false; | 28 return false; |
23 node.setAttribute('target', ''); | 29 node.setAttribute('target', ''); |
24 return true; | 30 return true; |
25 } | 31 }, |
26 } | 32 }; |
27 | 33 |
28 /** | 34 /** |
29 * Parse a very small subset of HTML. This ensures that insecure HTML / | 35 * Parse a very small subset of HTML. This ensures that insecure HTML / |
30 * javascript cannot be injected into the new tab page. | 36 * javascript cannot be injected into the new tab page. |
31 * @param {string} s The string to parse. | 37 * @param {string} s The string to parse. |
| 38 * @param {array=} extraTags Extra allowed tags. |
| 39 * @param {object=} extraAttrs Extra allowed attributes (all tags are run |
| 40 * through these). |
32 * @throws {Error} In case of non supported markup. | 41 * @throws {Error} In case of non supported markup. |
33 * @return {DocumentFragment} A document fragment containing the DOM tree. | 42 * @return {DocumentFragment} A document fragment containing the DOM tree. |
34 */ | 43 */ |
35 function parseHtmlSubset(s) { | 44 function parseHtmlSubset(s, extraTags, extraAttrs) { |
| 45 function merge() { |
| 46 var clone = {}; |
| 47 for (var i = 0; i < arguments.length; ++i) { |
| 48 if (typeof arguments[i] == 'object') { |
| 49 for (var key in arguments[i]) { |
| 50 if (arguments[i].hasOwnProperty(key)) |
| 51 clone[key] = arguments[i][key]; |
| 52 } |
| 53 } |
| 54 } |
| 55 return clone; |
| 56 } |
| 57 |
36 function walk(n, f) { | 58 function walk(n, f) { |
37 f(n); | 59 f(n); |
38 for (var i = 0; i < n.childNodes.length; i++) { | 60 for (var i = 0; i < n.childNodes.length; i++) { |
39 walk(n.childNodes[i], f); | 61 walk(n.childNodes[i], f); |
40 } | 62 } |
41 } | 63 } |
42 | 64 |
43 function assertElement(node) { | 65 function assertElement(node) { |
44 if (allowedTags.indexOf(node.tagName) == -1) | 66 if (tags.indexOf(node.tagName) == -1) |
45 throw Error(node.tagName + ' is not supported'); | 67 throw Error(node.tagName + ' is not supported'); |
46 } | 68 } |
47 | 69 |
48 function assertAttribute(attrNode, node) { | 70 function assertAttribute(attrNode, node) { |
49 var n = attrNode.nodeName; | 71 var n = attrNode.nodeName; |
50 var v = attrNode.nodeValue; | 72 var v = attrNode.nodeValue; |
51 if (!allowedAttributes.hasOwnProperty(n) || !allowedAttributes[n](node, v)) | 73 if (!attrs.hasOwnProperty(n) || !attrs[n](node, v)) |
52 throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported'); | 74 throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported'); |
53 } | 75 } |
54 | 76 |
| 77 var tags = allowedTags.concat(extraTags); |
| 78 var attrs = merge(allowedAttributes, extraAttrs); |
| 79 |
55 var r = document.createRange(); | 80 var r = document.createRange(); |
56 r.selectNode(document.body); | 81 r.selectNode(document.body); |
57 // This does not execute any scripts. | 82 // This does not execute any scripts. |
58 var df = r.createContextualFragment(s); | 83 var df = r.createContextualFragment(s); |
59 walk(df, function(node) { | 84 walk(df, function(node) { |
60 switch (node.nodeType) { | 85 switch (node.nodeType) { |
61 case Node.ELEMENT_NODE: | 86 case Node.ELEMENT_NODE: |
62 assertElement(node); | 87 assertElement(node); |
63 var attrs = node.attributes; | 88 var attrs = node.attributes; |
64 for (var i = 0; i < attrs.length; i++) { | 89 for (var i = 0; i < attrs.length; ++i) { |
65 assertAttribute(attrs[i], node); | 90 assertAttribute(attrs[i], node); |
66 } | 91 } |
67 break; | 92 break; |
68 | 93 |
69 case Node.COMMENT_NODE: | 94 case Node.COMMENT_NODE: |
70 case Node.DOCUMENT_FRAGMENT_NODE: | 95 case Node.DOCUMENT_FRAGMENT_NODE: |
71 case Node.TEXT_NODE: | 96 case Node.TEXT_NODE: |
72 break; | 97 break; |
73 | 98 |
74 default: | 99 default: |
75 throw Error('Node type ' + node.nodeType + ' is not supported'); | 100 throw Error('Node type ' + node.nodeType + ' is not supported'); |
76 } | 101 } |
77 }); | 102 }); |
78 return df; | 103 return df; |
79 } | 104 } |
OLD | NEW |