querySelector-mixed-case.html (18831B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>querySelector with mixed-case attributes</title> 4 <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1997380"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 8 <body> 9 <div id="test-container"></div> 10 11 <script> 12 "use strict"; 13 14 const container = document.getElementById("test-container"); 15 16 function buildTestTree() { 17 // Build entire DOM tree structure first (without attributes) 18 const html1 = document.createElement("div"); 19 html1.id = "html1"; 20 21 const svg1 = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 22 svg1.id = "svg1"; 23 24 const svg2 = document.createElementNS("http://www.w3.org/2000/svg", "g"); 25 svg2.id = "svg2"; 26 27 const svg3 = document.createElementNS("http://www.w3.org/2000/svg", "circle"); 28 svg3.id = "svg3"; 29 30 const html2 = document.createElement("div"); 31 html2.id = "html2"; 32 33 const math1 = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); 34 math1.id = "math1"; 35 36 const math2 = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mi"); 37 math2.id = "math2"; 38 39 const svg4 = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 40 svg4.id = "svg4"; 41 42 const svg5 = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 43 svg5.id = "svg5"; 44 45 const html3 = document.createElement("div"); 46 html3.id = "html3"; 47 48 // Create foreignObject with HTML inside SVG (will go in svg1) 49 const foreign1 = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); 50 foreign1.id = "foreign1"; 51 52 const html4 = document.createElement("div"); 53 html4.id = "html4"; 54 55 const html5 = document.createElement("span"); 56 html5.id = "html5"; 57 58 // Create nested: HTML > SVG inside html2 59 const svg6 = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 60 svg6.id = "svg6"; 61 62 const foreign2 = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); 63 foreign2.id = "foreign2"; 64 65 const html6 = document.createElement("div"); 66 html6.id = "html6"; 67 68 const svg7 = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 69 svg7.id = "svg7"; 70 71 const svg8 = document.createElementNS("http://www.w3.org/2000/svg", "circle"); 72 svg8.id = "svg8"; 73 74 // Build tree structure 75 svg2.appendChild(svg3); 76 svg1.appendChild(svg2); 77 78 // Add foreignObject with HTML to svg1 79 html4.appendChild(html5); 80 foreign1.appendChild(html4); 81 svg1.appendChild(foreign1); 82 83 html1.appendChild(svg1); 84 85 math1.appendChild(math2); 86 html2.appendChild(math1); 87 88 // Add deeply nested SVG > foreignObject > HTML > SVG to html2 89 svg7.appendChild(svg8); 90 html6.appendChild(svg7); 91 foreign2.appendChild(html6); 92 svg6.appendChild(foreign2); 93 html2.appendChild(svg6); 94 95 html1.appendChild(html2); 96 97 svg4.appendChild(svg5); 98 99 // Create wrapper container for all trees 100 const wrapper = document.createElement("div"); 101 wrapper.appendChild(html1); 102 wrapper.appendChild(svg4); 103 wrapper.appendChild(html3); 104 105 // NOW set all attributes 106 html1.setAttribute("viewBox", "html-val"); 107 html1.setAttribute("dataIndex", "0"); 108 html1.setAttribute("testAttr", "alpha"); 109 110 svg1.setAttribute("viewBox", "svg-val"); 111 svg1.setAttribute("dataIndex", "1"); 112 svg1.setAttribute("testAttr", "beta"); 113 114 svg2.setAttribute("mixedCase", "foo"); 115 svg2.setAttribute("dataValue", "first"); 116 117 svg3.setAttribute("innerAttr", "found"); 118 119 html2.setAttribute("viewBox", "nested"); 120 html2.setAttribute("mathVariant", "bold"); 121 122 math1.setAttribute("mathVariant", "italic"); 123 math1.setAttribute("displayStyle", "true"); 124 125 math2.setAttribute("testIndex", "5"); 126 math2.setAttribute("dataValue", "second"); 127 128 svg5.setAttribute("viewBox", "rect-val"); 129 svg5.setAttribute("testIndex", "10"); 130 131 html3.setAttribute("MixedCase", "bar"); 132 html3.setAttribute("TestIndex", "20"); 133 134 // Set attributes on foreignObject tree in svg1 135 foreign1.setAttribute("foreignAttr", "foreign-val"); 136 137 html4.setAttribute("ForeignHTML", "inside-foreign"); 138 html4.setAttribute("DataType", "html-in-svg"); 139 140 html5.setAttribute("NestedCase", "span-val"); 141 142 // Set attributes on deeply nested tree in html2 143 svg6.setAttribute("DeepSVG", "middle-svg"); 144 svg6.setAttribute("CaseSensitive", "nested"); 145 146 foreign2.setAttribute("SecondForeign", "deep-foreign"); 147 148 html6.setAttribute("InnerHTML", "deep-html"); 149 html6.setAttribute("DataType", "nested"); 150 151 svg7.setAttribute("ReEnteredSVG", "back-to-svg"); 152 153 svg8.setAttribute("DeepestAttr", "circle-val"); 154 svg8.setAttribute("CaseSensitive", "deepest"); 155 156 return wrapper; 157 } 158 159 function runTests(root) { 160 // DOM tree structure with attributes: 161 // <div id="test-container"> 162 // <div id="html1" viewBox="html-val" dataIndex="0" testAttr="alpha"> 163 // <svg id="svg1" viewBox="svg-val" dataIndex="1" testAttr="beta"> 164 // <g id="svg2" mixedCase="foo" dataValue="first"> 165 // <circle id="svg3" innerAttr="found"> 166 // </g> 167 // <foreignObject id="foreign1" foreignAttr="foreign-val"> 168 // <div id="html4" ForeignHTML="inside-foreign" DataType="html-in-svg"> 169 // <span id="html5" NestedCase="span-val"> 170 // </div> 171 // </foreignObject> 172 // </svg> 173 // <div id="html2" viewBox="nested" mathVariant="bold"> 174 // <math id="math1" mathVariant="italic" displayStyle="true"> 175 // <mi id="math2" testIndex="5" dataValue="second"> 176 // </math> 177 // <svg id="svg6" DeepSVG="middle-svg" CaseSensitive="nested"> 178 // <foreignObject id="foreign2" SecondForeign="deep-foreign"> 179 // <div id="html6" InnerHTML="deep-html" DataType="nested"> 180 // <svg id="svg7" ReEnteredSVG="back-to-svg"> 181 // <circle id="svg8" DeepestAttr="circle-val" CaseSensitive="deepest"> 182 // </svg> 183 // </div> 184 // </foreignObject> 185 // </svg> 186 // </div> 187 // </div> 188 // <svg id="svg4"> 189 // <rect id="svg5" viewBox="rect-val" testIndex="10"> 190 // </svg> 191 // <div id="html3" MixedCase="bar" TestIndex="20"> 192 // </div> 193 194 // Test 1: viewBox with mixed case - HTML is case-insensitive, SVG is case-sensitive 195 let results = root.querySelectorAll("[viewBox]"); 196 assert_equals(results.length, 4, "[viewBox] should match 2 HTML elements + 2 SVG elements"); 197 let ids = Array.from(results).map(el => el.id).sort(); 198 assert_array_equals(ids, ["html1", "html2", "svg1", "svg5"], "[viewBox] should match correct elements"); 199 200 // Test 2: viewbox lowercase - should only match HTML elements 201 results = root.querySelectorAll("[viewbox]"); 202 assert_equals(results.length, 2, "[viewbox] should only match HTML elements"); 203 ids = Array.from(results).map(el => el.id).sort(); 204 assert_array_equals(ids, ["html1", "html2"], "[viewbox] should only match HTML divs"); 205 206 // Test 3: VIEWBOX uppercase - should only match HTML elements 207 results = root.querySelectorAll("[VIEWBOX]"); 208 assert_equals(results.length, 2, "[VIEWBOX] should only match HTML elements"); 209 ids = Array.from(results).map(el => el.id).sort(); 210 assert_array_equals(ids, ["html1", "html2"], "[VIEWBOX] should only match HTML divs"); 211 212 // Test 4: mathVariant - HTML is case-insensitive, MathML is case-sensitive 213 results = root.querySelectorAll("[mathVariant]"); 214 assert_equals(results.length, 2, "[mathVariant] should match HTML + MathML"); 215 ids = Array.from(results).map(el => el.id).sort(); 216 assert_array_equals(ids, ["html2", "math1"], "[mathVariant] should match correct elements"); 217 218 // Test 5: mathvariant lowercase - should only match HTML 219 results = root.querySelectorAll("[mathvariant]"); 220 assert_equals(results.length, 1, "[mathvariant] should only match HTML"); 221 assert_equals(results[0].id, "html2", "[mathvariant] should match html2"); 222 223 // Test 6: dataIndex exact case 224 results = root.querySelectorAll("[dataIndex]"); 225 assert_equals(results.length, 2, "[dataIndex] should match HTML and SVG"); 226 ids = Array.from(results).map(el => el.id).sort(); 227 assert_array_equals(ids, ["html1", "svg1"], "[dataIndex] should match html1 and svg1"); 228 229 // Test 7: dataindex lowercase - should only match HTML 230 results = root.querySelectorAll("[dataindex]"); 231 assert_equals(results.length, 1, "[dataindex] should only match HTML"); 232 assert_equals(results[0].id, "html1", "[dataindex] should match html1"); 233 234 // Test 8: testIndex mixed case 235 // HTML elements: selector lowercased → [testindex] → matches html3 236 // SVG/MathML: selector used as-is → [testIndex] → matches math2, svg5 (case-sensitive) 237 results = root.querySelectorAll("[testIndex]"); 238 assert_equals(results.length, 3, "[testIndex] should match 3 elements"); 239 ids = Array.from(results).map(el => el.id).sort(); 240 assert_array_equals(ids, ["html3", "math2", "svg5"], "[testIndex] should match html3, math2, and svg5"); 241 242 // Test 9: TestIndex with capital T 243 // HTML elements: selector lowercased → [testindex] → matches html3 244 // SVG/MathML: selector used as-is → [TestIndex] → no match (testIndex != TestIndex) 245 results = root.querySelectorAll("[TestIndex]"); 246 assert_equals(results.length, 1, "[TestIndex] should only match HTML element"); 247 assert_equals(results[0].id, "html3", "[TestIndex] should match html3"); 248 249 // Test 10: testindex all lowercase 250 // HTML elements: selector lowercased → [testindex] → matches html3 251 // SVG/MathML: selector used as-is → [testindex] → no match (testIndex != testindex) 252 results = root.querySelectorAll("[testindex]"); 253 assert_equals(results.length, 1, "[testindex] should only match HTML element"); 254 assert_equals(results[0].id, "html3", "[testindex] should match html3"); 255 256 // Test 11: mixedCase exact 257 // HTML elements: selector lowercased → [mixedcase] → matches html3 258 // SVG: selector used as-is → [mixedCase] → matches svg2 (case-sensitive) 259 results = root.querySelectorAll("[mixedCase]"); 260 assert_equals(results.length, 2, "[mixedCase] should match 2 elements"); 261 ids = Array.from(results).map(el => el.id).sort(); 262 assert_array_equals(ids, ["html3", "svg2"], "[mixedCase] should match html3 and svg2"); 263 264 // Test 12: MixedCase capital M 265 // HTML elements: selector lowercased → [mixedcase] → matches html3 266 // SVG: selector used as-is → [MixedCase] → no match (mixedCase != MixedCase) 267 results = root.querySelectorAll("[MixedCase]"); 268 assert_equals(results.length, 1, "[MixedCase] should only match HTML element"); 269 assert_equals(results[0].id, "html3", "[MixedCase] should match html3"); 270 271 // Test 13: mixedcase all lowercase 272 // HTML elements: selector lowercased → [mixedcase] → matches html3 273 // SVG: selector used as-is → [mixedcase] → no match (mixedCase != mixedcase) 274 results = root.querySelectorAll("[mixedcase]"); 275 assert_equals(results.length, 1, "[mixedcase] should only match HTML element"); 276 assert_equals(results[0].id, "html3", "[mixedcase] should match html3"); 277 278 // Test 14: testAttr exact case 279 results = root.querySelectorAll("[testAttr]"); 280 assert_equals(results.length, 2, "[testAttr] should match HTML and SVG"); 281 ids = Array.from(results).map(el => el.id).sort(); 282 assert_array_equals(ids, ["html1", "svg1"], "[testAttr] should match html1 and svg1"); 283 284 // Test 15: Attribute value matching with prefix 285 results = root.querySelectorAll('[viewBox^="svg"]'); 286 assert_equals(results.length, 1, '[viewBox^="svg"] should match 1 SVG element'); 287 assert_equals(results[0].id, "svg1", '[viewBox^="svg"] should match svg1'); 288 289 // Test 16: Attribute value matching with exact match 290 results = root.querySelectorAll('[dataIndex="0"]'); 291 assert_equals(results.length, 1, '[dataIndex="0"] should match 1 element'); 292 assert_equals(results[0].id, "html1", '[dataIndex="0"] should match html1'); 293 294 // Test 17: Nested element query 295 results = root.querySelectorAll("[innerAttr]"); 296 assert_equals(results.length, 1, "[innerAttr] should match nested circle"); 297 assert_equals(results[0].id, "svg3", "[innerAttr] should match svg3"); 298 299 // Test 18: displayStyle on MathML 300 results = root.querySelectorAll("[displayStyle]"); 301 assert_equals(results.length, 1, "[displayStyle] should match MathML"); 302 assert_equals(results[0].id, "math1", "[displayStyle] should match math1"); 303 304 // Test 19: displaystyle lowercase - should not match anything (no HTML element has it) 305 results = root.querySelectorAll("[displaystyle]"); 306 assert_equals(results.length, 0, "[displaystyle] should not match anything"); 307 308 // Test 20: Case-sensitive value matching with 's' flag 309 results = root.querySelectorAll('[testAttr="alpha" s]'); 310 assert_equals(results.length, 1, '[testAttr="alpha" s] should match 1 element'); 311 assert_equals(results[0].id, "html1", '[testAttr="alpha" s] should match html1'); 312 313 // Test 21: Case-insensitive value matching with 'i' flag 314 results = root.querySelectorAll('[testAttr="ALPHA" i]'); 315 assert_equals(results.length, 1, '[testAttr="ALPHA" i] should match 1 element'); 316 assert_equals(results[0].id, "html1", '[testAttr="ALPHA" i] should match html1'); 317 318 // Test 22: ForeignHTML - HTML inside foreignObject should be case-insensitive 319 results = root.querySelectorAll("[ForeignHTML]"); 320 assert_equals(results.length, 1, "[ForeignHTML] should match HTML in foreignObject"); 321 assert_equals(results[0].id, "html4", "[ForeignHTML] should match html4"); 322 323 // Test 23: foreignhtml lowercase - should match HTML element in foreignObject 324 results = root.querySelectorAll("[foreignhtml]"); 325 assert_equals(results.length, 1, "[foreignhtml] should match HTML in foreignObject"); 326 assert_equals(results[0].id, "html4", "[foreignhtml] should match html4"); 327 328 329 // Test 24: DataType - should match both HTML elements (case-insensitive) 330 results = root.querySelectorAll("[DataType]"); 331 assert_equals(results.length, 2, "[DataType] should match 2 HTML elements"); 332 ids = Array.from(results).map(el => el.id).sort(); 333 assert_array_equals(ids, ["html4", "html6"], "[DataType] should match html4 and html6"); 334 335 // Test 25: datatype lowercase - should match both HTML elements 336 results = root.querySelectorAll("[datatype]"); 337 assert_equals(results.length, 2, "[datatype] should match 2 HTML elements"); 338 ids = Array.from(results).map(el => el.id).sort(); 339 assert_array_equals(ids, ["html4", "html6"], "[datatype] should match html4 and html6"); 340 341 // Test 26: CaseSensitive on SVG elements - case-sensitive 342 results = root.querySelectorAll("[CaseSensitive]"); 343 assert_equals(results.length, 2, "[CaseSensitive] should match 2 SVG elements"); 344 ids = Array.from(results).map(el => el.id).sort(); 345 assert_array_equals(ids, ["svg6", "svg8"], "[CaseSensitive] should match svg6, svg8"); 346 347 // Test 27: casesensitive lowercase - should NOT match SVG elements 348 results = root.querySelectorAll("[casesensitive]"); 349 assert_equals(results.length, 0, "[casesensitive] should not match SVG elements"); 350 351 // Test 28: NestedCase - HTML span inside foreignObject should be case-insensitive 352 results = root.querySelectorAll("[NestedCase]"); 353 assert_equals(results.length, 1, "[NestedCase] should match HTML span in foreignObject"); 354 assert_equals(results[0].id, "html5", "[NestedCase] should match html5"); 355 356 // Test 29: nestedcase lowercase - should match HTML span 357 results = root.querySelectorAll("[nestedcase]"); 358 assert_equals(results.length, 1, "[nestedcase] should match HTML span in foreignObject"); 359 assert_equals(results[0].id, "html5", "[nestedcase] should match html5"); 360 361 // Test 30: ReEnteredSVG - SVG inside HTML inside foreignObject should be case-sensitive 362 results = root.querySelectorAll("[ReEnteredSVG]"); 363 assert_equals(results.length, 1, "[ReEnteredSVG] should match SVG nested in HTML in foreignObject"); 364 assert_equals(results[0].id, "svg7", "[ReEnteredSVG] should match svg7"); 365 366 // Test 31: reenteredsvg lowercase - should NOT match (SVG is case-sensitive) 367 results = root.querySelectorAll("[reenteredsvg]"); 368 assert_equals(results.length, 0, "[reenteredsvg] should not match SVG element"); 369 370 // Test 32: DeepestAttr - deeply nested SVG circle should be case-sensitive 371 results = root.querySelectorAll("[DeepestAttr]"); 372 assert_equals(results.length, 1, "[DeepestAttr] should match deeply nested circle"); 373 assert_equals(results[0].id, "svg8", "[DeepestAttr] should match svg8"); 374 375 // Test 33: deepestattr lowercase - should NOT match 376 results = root.querySelectorAll("[deepestattr]"); 377 assert_equals(results.length, 0, "[deepestattr] should not match SVG circle"); 378 379 // Test 34: foreignAttr on foreignObject element - case-sensitive 380 results = root.querySelectorAll("[foreignAttr]"); 381 assert_equals(results.length, 1, "[foreignAttr] should match foreignObject"); 382 assert_equals(results[0].id, "foreign1", "[foreignAttr] should match foreign1"); 383 384 // Test 35: foreignattr lowercase - should NOT match foreignObject (SVG namespace) 385 results = root.querySelectorAll("[foreignattr]"); 386 assert_equals(results.length, 0, "[foreignattr] should not match foreignObject"); 387 388 // Test 36: InnerHTML on HTML element inside foreignObject - case-insensitive 389 results = root.querySelectorAll("[InnerHTML]"); 390 assert_equals(results.length, 1, "[InnerHTML] should match HTML in foreignObject"); 391 assert_equals(results[0].id, "html6", "[InnerHTML] should match html6"); 392 393 // Test 37: innerhtml lowercase - should match HTML in foreignObject 394 results = root.querySelectorAll("[innerhtml]"); 395 assert_equals(results.length, 1, "[innerhtml] should match HTML in foreignObject"); 396 assert_equals(results[0].id, "html6", "[innerhtml] should match html6"); 397 398 // Test 38: DeepSVG on SVG element - case-sensitive 399 results = root.querySelectorAll("[DeepSVG]"); 400 assert_equals(results.length, 1, "[DeepSVG] should match SVG element"); 401 assert_equals(results[0].id, "svg6", "[DeepSVG] should match svg6"); 402 403 // Test 39: deepsvg lowercase - should NOT match SVG 404 results = root.querySelectorAll("[deepsvg]"); 405 assert_equals(results.length, 0, "[deepsvg] should not match SVG element"); 406 407 // Test 40: SecondForeign on foreignObject element - case-sensitive 408 results = root.querySelectorAll("[SecondForeign]"); 409 assert_equals(results.length, 1, "[SecondForeign] should match foreignObject"); 410 assert_equals(results[0].id, "foreign2", "[SecondForeign] should match foreign2"); 411 412 // Test 41: secondforeign lowercase - should NOT match foreignObject 413 results = root.querySelectorAll("[secondforeign]"); 414 assert_equals(results.length, 0, "[secondforeign] should not match foreignObject"); 415 } 416 417 test(() => { 418 const tree = buildTestTree(); 419 420 // Test on disconnected tree first 421 runTests(tree); 422 423 // Now append to document and test again 424 container.appendChild(tree); 425 runTests(container); 426 427 container.innerHTML = ""; 428 }, "Mixed HTML/SVG/MathML tree with various mixed-case attributes"); 429 430 </script> 431 </body>