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