Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /** |
michael@0 | 7 | * A test suite that runs WHATWG HTML parser tests. |
michael@0 | 8 | * The tests are from html5lib. |
michael@0 | 9 | * |
michael@0 | 10 | * http://html5lib.googlecode.com/ |
michael@0 | 11 | */ |
michael@0 | 12 | |
michael@0 | 13 | /** |
michael@0 | 14 | * A few utility functions. |
michael@0 | 15 | */ |
michael@0 | 16 | function log(entry) { |
michael@0 | 17 | |
michael@0 | 18 | } |
michael@0 | 19 | |
michael@0 | 20 | function startsWith(s, s2) { |
michael@0 | 21 | return s.indexOf(s2)==0; |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | function trimString(s) { |
michael@0 | 25 | return(s.replace(/^\s+/,'').replace(/\s+$/,'')); |
michael@0 | 26 | } |
michael@0 | 27 | |
michael@0 | 28 | /** |
michael@0 | 29 | * Parses an individual testcase into an array containing the input |
michael@0 | 30 | * string, a string representing the expected tree (DOM), and a list |
michael@0 | 31 | * of error messages. |
michael@0 | 32 | * |
michael@0 | 33 | * @param A string containing a single testcase |
michael@0 | 34 | */ |
michael@0 | 35 | function parseTestcase(testcase) { |
michael@0 | 36 | var lines = testcase.split("\n"); |
michael@0 | 37 | |
michael@0 | 38 | /* check that the first non-empty, non-comment line is #data */ |
michael@0 | 39 | for each (var line in lines) { |
michael@0 | 40 | if (!line || startsWith(line, "##")) { |
michael@0 | 41 | continue; |
michael@0 | 42 | } |
michael@0 | 43 | if (line == "#data") |
michael@0 | 44 | break; |
michael@0 | 45 | log(lines); |
michael@0 | 46 | throw "Unknown test format." |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | var input = []; |
michael@0 | 50 | var output = []; |
michael@0 | 51 | var errors = []; |
michael@0 | 52 | var fragment = []; |
michael@0 | 53 | var currentList = input; |
michael@0 | 54 | for each (var line in lines) { |
michael@0 | 55 | if (startsWith(line, "##todo")) { |
michael@0 | 56 | todo(false, line.substring(6)); |
michael@0 | 57 | continue; |
michael@0 | 58 | } |
michael@0 | 59 | if (!(startsWith(line, "#error") || |
michael@0 | 60 | startsWith(line, "#document") || |
michael@0 | 61 | startsWith(line, "#document-fragment") || |
michael@0 | 62 | startsWith(line, "#data"))) { |
michael@0 | 63 | currentList.push(line); |
michael@0 | 64 | } else if (line == "#errors") { |
michael@0 | 65 | currentList = errors; |
michael@0 | 66 | } else if (line == "#document") { |
michael@0 | 67 | currentList = output; |
michael@0 | 68 | } else if (line == "#document-fragment") { |
michael@0 | 69 | currentList = fragment; |
michael@0 | 70 | } |
michael@0 | 71 | } |
michael@0 | 72 | while (!output[output.length - 1]) { |
michael@0 | 73 | output.pop(); // zap trailing blank lines |
michael@0 | 74 | } |
michael@0 | 75 | //logger.log(input.length, output.length, errors.length); |
michael@0 | 76 | return [input.join("\n"), output.join("\n"), errors, fragment[0]]; |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | /** |
michael@0 | 80 | * A generator function that accepts a list of strings. Each list |
michael@0 | 81 | * member corresponds to the contents of a ".dat" file from the |
michael@0 | 82 | * html5lib test suite. |
michael@0 | 83 | * |
michael@0 | 84 | * @param The list of strings |
michael@0 | 85 | */ |
michael@0 | 86 | function test_parser(testlist) { |
michael@0 | 87 | for each (var testgroup in testlist) { |
michael@0 | 88 | var tests = testgroup.split("#data\n"); |
michael@0 | 89 | tests = ["#data\n" + test for each(test in tests) if (test)]; |
michael@0 | 90 | for each (var test in tests) { |
michael@0 | 91 | yield parseTestcase(test); |
michael@0 | 92 | } |
michael@0 | 93 | } |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | /** |
michael@0 | 97 | * Transforms a DOM document to a string matching the format in |
michael@0 | 98 | * the test cases. |
michael@0 | 99 | * |
michael@0 | 100 | * @param the DOM document |
michael@0 | 101 | */ |
michael@0 | 102 | function docToTestOutput(doc) { |
michael@0 | 103 | var walker = doc.createTreeWalker(doc, NodeFilter.SHOW_ALL, null); |
michael@0 | 104 | return addLevels(walker, "", "| ").slice(0,-1); // remove the last newline |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | /** |
michael@0 | 108 | * Creates a walker for a fragment that skips over the root node. |
michael@0 | 109 | * |
michael@0 | 110 | * @param an element |
michael@0 | 111 | */ |
michael@0 | 112 | function createFragmentWalker(elt) { |
michael@0 | 113 | return elt.ownerDocument.createTreeWalker(elt, NodeFilter.SHOW_ALL, |
michael@0 | 114 | function (node) { |
michael@0 | 115 | return elt == node ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT; |
michael@0 | 116 | }); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | /** |
michael@0 | 120 | * Transforms the descendants of an element to a string matching the format |
michael@0 | 121 | * in the test cases. |
michael@0 | 122 | * |
michael@0 | 123 | * @param an element |
michael@0 | 124 | */ |
michael@0 | 125 | function fragmentToTestOutput(elt) { |
michael@0 | 126 | var walker = createFragmentWalker(elt); |
michael@0 | 127 | return addLevels(walker, "", "| ").slice(0,-1); // remove the last newline |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | function addLevels(walker, buf, indent) { |
michael@0 | 131 | if(walker.firstChild()) { |
michael@0 | 132 | do { |
michael@0 | 133 | buf += indent; |
michael@0 | 134 | switch (walker.currentNode.nodeType) { |
michael@0 | 135 | case Node.ELEMENT_NODE: |
michael@0 | 136 | buf += "<" |
michael@0 | 137 | var ns = walker.currentNode.namespaceURI; |
michael@0 | 138 | if ("http://www.w3.org/1998/Math/MathML" == ns) { |
michael@0 | 139 | buf += "math "; |
michael@0 | 140 | } else if ("http://www.w3.org/2000/svg" == ns) { |
michael@0 | 141 | buf += "svg "; |
michael@0 | 142 | } else if ("http://www.w3.org/1999/xhtml" != ns) { |
michael@0 | 143 | buf += "otherns "; |
michael@0 | 144 | } |
michael@0 | 145 | buf += walker.currentNode.localName + ">"; |
michael@0 | 146 | if (walker.currentNode.hasAttributes()) { |
michael@0 | 147 | var valuesByName = {}; |
michael@0 | 148 | var attrs = walker.currentNode.attributes; |
michael@0 | 149 | for (var i = 0; i < attrs.length; ++i) { |
michael@0 | 150 | var localName = attrs[i].localName; |
michael@0 | 151 | var name; |
michael@0 | 152 | var attrNs = attrs[i].namespaceURI; |
michael@0 | 153 | if (null == attrNs) { |
michael@0 | 154 | name = localName; |
michael@0 | 155 | } else if ("http://www.w3.org/XML/1998/namespace" == attrNs) { |
michael@0 | 156 | name = "xml " + localName; |
michael@0 | 157 | } else if ("http://www.w3.org/1999/xlink" == attrNs) { |
michael@0 | 158 | name = "xlink " + localName; |
michael@0 | 159 | } else if ("http://www.w3.org/2000/xmlns/" == attrNs) { |
michael@0 | 160 | name = "xmlns " + localName; |
michael@0 | 161 | } else { |
michael@0 | 162 | name = "otherns " + localName; |
michael@0 | 163 | } |
michael@0 | 164 | valuesByName[name] = attrs[i].value; |
michael@0 | 165 | } |
michael@0 | 166 | var keys = Object.keys(valuesByName).sort(); |
michael@0 | 167 | for (var i = 0; i < keys.length; ++i) { |
michael@0 | 168 | buf += "\n" + indent + " " + keys[i] + |
michael@0 | 169 | "=\"" + valuesByName[keys[i]] +"\""; |
michael@0 | 170 | } |
michael@0 | 171 | } |
michael@0 | 172 | break; |
michael@0 | 173 | case Node.DOCUMENT_TYPE_NODE: |
michael@0 | 174 | buf += "<!DOCTYPE " + walker.currentNode.name; |
michael@0 | 175 | if (walker.currentNode.publicId || walker.currentNode.systemId) { |
michael@0 | 176 | buf += " \""; |
michael@0 | 177 | buf += walker.currentNode.publicId; |
michael@0 | 178 | buf += "\" \""; |
michael@0 | 179 | buf += walker.currentNode.systemId; |
michael@0 | 180 | buf += "\""; |
michael@0 | 181 | } |
michael@0 | 182 | buf += ">"; |
michael@0 | 183 | break; |
michael@0 | 184 | case Node.COMMENT_NODE: |
michael@0 | 185 | buf += "<!-- " + walker.currentNode.nodeValue + " -->"; |
michael@0 | 186 | break; |
michael@0 | 187 | case Node.TEXT_NODE: |
michael@0 | 188 | buf += "\"" + walker.currentNode.nodeValue + "\""; |
michael@0 | 189 | break; |
michael@0 | 190 | } |
michael@0 | 191 | buf += "\n"; |
michael@0 | 192 | // In the case of template elements, children do not get inserted as |
michael@0 | 193 | // children of the template element, instead they are inserted |
michael@0 | 194 | // as children of the template content (which is a document fragment). |
michael@0 | 195 | if (walker.currentNode instanceof HTMLTemplateElement) { |
michael@0 | 196 | buf += indent + " content\n"; |
michael@0 | 197 | // Walk through the template content. |
michael@0 | 198 | var templateWalker = createFragmentWalker(walker.currentNode.content); |
michael@0 | 199 | buf = addLevels(templateWalker, buf, indent + " "); |
michael@0 | 200 | } |
michael@0 | 201 | buf = addLevels(walker, buf, indent + " "); |
michael@0 | 202 | } while(walker.nextSibling()); |
michael@0 | 203 | walker.parentNode(); |
michael@0 | 204 | } |
michael@0 | 205 | return buf; |
michael@0 | 206 | } |
michael@0 | 207 |