summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-pseudo/support/selections.js
blob: d0cd3409a7333ba6f22616767564f0327ab9db07 (plain)
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
/**
 * Replaces the current selection (if any) with a new range, after
 * it’s configured by the given function.
 *
 * See also: selectNodeContents
 * Example:
 *
 * selectRangeWith(range => {
 *     range.selectNodeContents(foo);
 *     range.setStart(foo.childNodes[0], 3);
 *     range.setEnd(foo.childNodes[0], 5);
 * });
 */
function selectRangeWith(fun) {
    const selection = getSelection();

    // Deselect any ranges that happen to be selected, to prevent the
    // Selection#addRange call from ignoring our new range (see
    // <https://www.chromestatus.com/feature/6680566019653632> for
    // more details).
    selection.removeAllRanges();

    // Create and configure a range.
    const range = document.createRange();
    fun(range);

    // Select our new range.
    selection.addRange(range);
}

/**
 * Replaces the current selection (if any) with a new range, spanning
 * the contents of the given node.
 */
function selectNodeContents(node) {
    const previousActive = document.activeElement;

    selectRangeWith(range => range.selectNodeContents(node));

    // If the selection update causes the node or an ancestor to be
    // focused (Chromium 80+), unfocus it, to avoid any focus-related
    // styling such as outlines.
    if (document.activeElement != previousActive) {
        document.activeElement.blur();
    }
}

/**
 * Tries to convince a UA with lazy spellcheck to check and highlight
 * the contents of the given nodes (form fields or @contenteditables).
 *
 * Each node is focused then immediately unfocused. Both focus and
 * selection can be used for this purpose, but only focus works for
 * @contenteditables.
 */
function trySpellcheck(...nodes) {
    // This is inherently a flaky test risk, but Chromium (as of 87)
    // seems to cancel spellcheck on a node if it wasn’t the last one
    // focused for “long enough” (though immediate unfocus is ok).
    // Using requestAnimationFrame or setInterval(0) are usually not
    // long enough (see <https://bucket.daz.cat/work/igalia/0/0.html>
    // under “trySpellcheck strategy” for an example).
    const interval = setInterval(() => {
        if (nodes.length > 0) {
            const node = nodes.shift();
            node.focus();
            node.blur();
        } else {
            clearInterval(interval);
        }
    }, 250);
}