File: focus-fixup-rule-one-no-dialogs.html

package info (click to toggle)
firefox 149.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,767,760 kB
  • sloc: cpp: 7,416,064; javascript: 6,752,859; ansic: 3,774,850; python: 1,250,473; xml: 641,578; asm: 439,191; java: 186,617; sh: 56,634; makefile: 18,856; objc: 13,092; perl: 12,763; pascal: 5,960; yacc: 4,583; cs: 3,846; lex: 1,720; ruby: 1,002; php: 436; lisp: 258; awk: 105; sql: 66; sed: 53; csh: 10; exp: 6
file content (112 lines) | stat: -rw-r--r-- 3,778 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<!DOCTYPE html>
<meta charset="utf-8">
<title>Focus fixup rule one (no &lt;dialog>s involved)</title>
<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focus-fixup-rule">
<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#attr-fieldset-disabled">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<div>
  <button id="button1">Button 1</button>
  <button id="button2">Button 2</button>
  <button id="button3">Button 3</button>
  <fieldset id="fieldset1"><button id="button4">Button 4</button></fieldset>
  <fieldset id="fieldset2" disabled><legend><button id="button5">Button 5</button></legend></fieldset>
  <div id="div" tabindex="0">Div</div>
  <div id="editable" contenteditable=true>editor</div>
  <button id="button6">Button 6</button>
</div>

<script>
"use strict";

function test_focus_fixup(selector, change, expectSync = false) {
  promise_test(async function(t) {
    // Make sure we're not running from a ResizeObserver / etc notification.
    await new Promise(r => t.step_timeout(r, 0));

    const el = document.querySelector(selector);
    el.focus();

    assert_equals(document.activeElement, el, `Sanity check: ${selector} must start focused`);

    change(el);

    {
      const fn = expectSync ? assert_not_equals : assert_equals;
      fn(document.activeElement, el, `active element ${expectSync ? "is" : "isn't"} fixed-up sync`);
    }

    // We don't expect blur events in the sync case per spec yet, at least.
    if (!expectSync) {
      // Fixup should run after animation frame callbacks, right before the end of
      // "update the rendering", so after resize observations.
      let ranFirstRaf = false;
      let ranRO = false;

      let resolveDone;
      let done = new Promise(r => { resolveDone = r; });

      requestAnimationFrame(t.step_func(() => {
        ranFirstRaf = true;
        assert_equals(document.activeElement, el, "activeElement shouldn't have changed yet (rAF)");
        requestAnimationFrame(t.step_func(() => {
          assert_true(ranRO, "ResizeObserver should've ran on the previous frame");
          resolveDone();
        }));
      }));

      let ro = new ResizeObserver(t.step_func(() => {
        assert_true(ranFirstRaf, "requestAnimationFrame should run before ResizeObserver");
        ranRO = true;
        assert_equals(document.activeElement, el, "activeElement shouldn't have changed yet (ResizeObserver)");
      }));

      // TODO: Test IntersectionObserver timing. It's a bit trickier because it
      // uses its own task source and so on.
      ro.observe(document.documentElement);

      await done;

      ro.disconnect();
    }

    assert_not_equals(document.activeElement, el, "focus is fixed up");
    assert_equals(document.activeElement, document.body, "Body is focused");
  }, selector);
}

test_focus_fixup("#button1", function(button) {
  button.disabled = true;
});

test_focus_fixup("#button2", function(button) {
  button.hidden = true;
});

test_focus_fixup("#button3", function(button) {
  button.remove();
}, /* expectSync = */ true);

test_focus_fixup("#button4", function(button) {
  document.querySelector("#fieldset1").disabled = true;
});

test_focus_fixup("#button5", function(button) {
  const fieldset = document.querySelector("#fieldset2");
  fieldset.insertBefore(document.createElement("legend"), fieldset.firstChild);
});

test_focus_fixup("#div", function(div) {
  div.removeAttribute("tabindex");
});

test_focus_fixup("#editable", function(div) {
  div.contentEditable = false;
});

test_focus_fixup("#button6", function(button) {
  button.style.visibility = "hidden";
});
</script>