tor-browser

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

Event-dispatch-single-activation-behavior.html (5873B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <title> Only one activation behavior is executed during dispatch</title>
      4 <link rel="author" title="Vincent Hilla" href="mailto:vhilla@mozilla.com">
      5 <link rel="help" href="https://dom.spec.whatwg.org/#eventtarget-activation-behavior">
      6 <link rel="help" href="https://dom.spec.whatwg.org/#concept-event-dispatch">
      7 <script src="/resources/testharness.js"></script>
      8 <script src="/resources/testharnessreport.js"></script>
      9 <div id=log></div>
     10 
     11 <div id=test_container></div>
     12 
     13 <!--
     14    Three classes:
     15    click
     16        Element to be clicked to cause activation behavior
     17    activates
     18        Element that registers the activation behavior
     19    container
     20        Element in which other elements with activation behavior are placed.
     21        We test that those won't be activated too.
     22 -->
     23 <template>
     24    <!--input, change event bubble, so have to check if checked is true-->
     25    <input class="click activates container" type="checkbox" oninput="this.checked ? activated(this) : null">
     26    <input class="click activates container" type="radio" oninput="this.checked ? activated(this) : null">
     27    <form onsubmit="activated(this); return false" class="activates">
     28        <input class="click container" type="submit">
     29    </form>
     30    <form onsubmit="activated(this); return false" class="activates">
     31        <input class="click container" type="image">
     32    </form>
     33    <form onreset="activated(this)" class="activates">
     34        <input class="click container" type="reset">
     35    </form>
     36    <form onsubmit="activated(this); return false" class="activates">
     37        <button class="click container" type="submit"></button>
     38    </form>
     39    <form onreset="activated(this)" class="activates">
     40        <button class="click container" type="reset"></button>
     41    </form>
     42    <a href="#link" class="click container activates"></a>
     43    <area href="#link" class="click container activates">
     44    <details ontoggle="activated(this)" class="activates">
     45        <summary class="click container"></summary>
     46    </details>
     47    <label>
     48      <input type=checkbox onclick="this.checked ? activated(this) : null" class="activates">
     49      <span class="click container">label</span>
     50    </label>
     51    <!--activation behavior of label for event targeted at interactive content descendant is to do nothing-->
     52    <label class="container">
     53        <button class="click" type="button"></button>
     54    </label>
     55 </template>
     56 
     57 <script>
     58 let activations = [];
     59 function activated(e) {
     60    activations.push(e);
     61 }
     62 
     63 function getActivations(testidx) {
     64    return activations.filter(a =>
     65        (a.endsWith && a.endsWith("test"+testidx+"_link"))
     66        || (a.classList && a.classList.contains("test"+testidx))
     67    );
     68 }
     69 
     70 // for a and area elements
     71 window.onhashchange = function(e) {
     72    if (e.newURL.endsWith("link")) {
     73        activated(e.newURL);
     74    }
     75    window.location.hash = "";
     76 };
     77 
     78 function getElementsByClassNameInclusive(e, clsname) {
     79    let ls = Array.from(e.getElementsByClassName(clsname));
     80    if (e.classList.contains(clsname)) ls.push(e);
     81    return ls;
     82 }
     83 
     84 function getClickTarget(e) {
     85    return getElementsByClassNameInclusive(e, "click")[0];
     86 }
     87 
     88 function getContainer(e) {
     89    return getElementsByClassNameInclusive(e, "container")[0];
     90 }
     91 
     92 function getExpectedActivations(e) {
     93    let ls = getElementsByClassNameInclusive(e, "activates");
     94 
     95    // special case, for a and area the window registers the activation
     96    // have to use string, as testrunner cannot stringify the window object
     97    ls = ls.map(e => e.tagName === "A" || e.tagName === "AREA" ? e.href : e);
     98 
     99    return ls;
    100 }
    101 
    102 function toString(e) {
    103    const children = Array.from(e.children);
    104    const childstr = (children.map(toString)).join("");
    105    const tag = e.tagName;
    106    const typestr = e.type ? " type="+e.type : "";
    107    return `<${tag}${typestr}>${childstr}</${tag}>`;
    108 }
    109 
    110 // generate O(n^2) test combinations
    111 const template = document.querySelector("template");
    112 const elements = Array.from(template.content.children);
    113 const tests = []
    114 for (const target of elements) {
    115    for (const parent of elements) {
    116        if (target === parent) continue;
    117        tests.push([target.cloneNode(true), parent.cloneNode(true)])
    118    }
    119 }
    120 
    121 const test_container = document.getElementById("test_container");
    122 
    123 /**
    124 * Test that if two elements in an event target chain have activation behavior,
    125 * only one of them will be activated.
    126 *
    127 * Each child of <template> represents one case of activation behavior.
    128 * The behavior should be triggered by clicking the element of class click
    129 * and will manifest as a call to activated().
    130 *
    131 * For each [target, parent] in tests, we make target a descendant of parent
    132 * and test that only target gets activated when dispatching a click.
    133 */
    134 for (let i = 0; i < tests.length; i++) {
    135    let [target, parent] = tests[i];
    136    async_test(function(t) {
    137        let test = document.createElement("div");
    138        test_container.appendChild(test);
    139        test.appendChild(parent);
    140        getContainer(parent).appendChild(target);
    141 
    142        // for later filtering out the activations belonging to this test
    143        for (let e of test.getElementsByClassName("activates")) {
    144            e.classList.add("test"+i);
    145        }
    146        for (let e of test.querySelectorAll("a, area")) {
    147            e.href = "#test"+i+"_link";
    148        }
    149 
    150        getClickTarget(target).click();
    151 
    152        // Need to spin event loop twice, as some clicks might dispatch another task
    153        t.step_timeout(() => {
    154            t.step_timeout(t.step_func_done(() => {
    155                    assert_array_equals(getActivations(i), getExpectedActivations(target));
    156            }), 0);
    157        }, 0);
    158 
    159        t.add_cleanup(function() {
    160            test_container.removeChild(test);
    161        });
    162    }, `When clicking child ${toString(target)} of parent ${toString(parent)}, only child should be activated.`);
    163 }
    164 </script>