tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>