Skip to main content

layout_api/
accessibility.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::LazyLock;
6
7use accesskit::Role;
8use bitflags::bitflags;
9use rustc_hash::{FxHashMap, FxHashSet};
10use style::Atom;
11
12bitflags! {
13    #[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
14    pub struct AccessibilityDamage: u16 {
15        /// The properties of this node (other than children) have changed.
16        const Node = 0b0001;
17        /// Children have been added to or removed from this node.
18        const Children = 0b0010;
19        /// This node and its descendants have bounds damage from layout.
20        const Layout = 0b0100;
21
22        /// All properties of this node need to be recomputed.
23        const Rebuild = 0b0111;
24    }
25}
26malloc_size_of::malloc_size_of_is_0!(AccessibilityDamage);
27
28/// A safe-list of ARIA role attribute values which we map to AccessKit roles.
29///
30/// This is currently just the roles that don't have any [supported] or [required] properties
31/// and also don't require an [accessible name].
32///
33/// [supported]: https://w3c.github.io/aria/#supportedState
34/// [required]: https://w3c.github.io/aria/#requiredState
35/// [accessible name]: https://w3c.github.io/aria/#namefromauthor
36pub static SUPPORTED_ARIA_ROLES: LazyLock<FxHashSet<Atom>> = LazyLock::new(|| {
37    [
38        Atom::from("alert"),
39        Atom::from("article"),
40        Atom::from("banner"),
41        Atom::from("blockquote"),
42        Atom::from("caption"),
43        Atom::from("code"),
44        Atom::from("complementary"),
45        Atom::from("contentinfo"),
46        Atom::from("definition"),
47        Atom::from("deletion"),
48        Atom::from("directory"),
49        Atom::from("document"),
50        Atom::from("emphasis"),
51        Atom::from("feed"),
52        Atom::from("figure"),
53        Atom::from("generic"),
54        Atom::from("heading"),
55        Atom::from("insertion"),
56        Atom::from("link"),
57        Atom::from("list"),
58        Atom::from("log"),
59        Atom::from("main"),
60        Atom::from("math"),
61        Atom::from("navigation"),
62        Atom::from("none"),
63        Atom::from("note"),
64        Atom::from("paragraph"),
65        Atom::from("presentation"),
66        Atom::from("rowgroup"),
67        Atom::from("search"),
68        Atom::from("status"),
69        Atom::from("strong"),
70        Atom::from("term"),
71        Atom::from("time"),
72        Atom::from("timer"),
73    ]
74    .into_iter()
75    .collect()
76});
77
78/// A map from ARIA role name to the corresponding [`Role`] in AccessKit.
79pub static ARIA_ROLE_TO_ACCESSKIT_ROLE: LazyLock<FxHashMap<Atom, Role>> = LazyLock::new(|| {
80    [
81        (Atom::from("alert"), Role::Alert),
82        (Atom::from("article"), Role::Article),
83        (Atom::from("banner"), Role::Banner),
84        (Atom::from("blockquote"), Role::Blockquote),
85        (Atom::from("caption"), Role::Caption),
86        (Atom::from("code"), Role::Code),
87        (Atom::from("complementary"), Role::Complementary),
88        (Atom::from("contentinfo"), Role::ContentInfo),
89        (Atom::from("definition"), Role::Definition),
90        (Atom::from("deletion"), Role::ContentDeletion),
91        (Atom::from("directory"), Role::Unknown),
92        (Atom::from("document"), Role::Document),
93        (Atom::from("emphasis"), Role::Emphasis),
94        (Atom::from("feed"), Role::Feed),
95        (Atom::from("figure"), Role::Figure),
96        (Atom::from("generic"), Role::Section),
97        (Atom::from("heading"), Role::Heading),
98        (Atom::from("insertion"), Role::ContentInsertion),
99        (Atom::from("link"), Role::Link),
100        (Atom::from("list"), Role::List),
101        (Atom::from("log"), Role::Log),
102        (Atom::from("main"), Role::Main),
103        (Atom::from("math"), Role::Math),
104        (Atom::from("navigation"), Role::Navigation),
105        (Atom::from("none"), Role::GenericContainer),
106        (Atom::from("note"), Role::Note),
107        (Atom::from("paragraph"), Role::Paragraph),
108        (Atom::from("presentation"), Role::GenericContainer),
109        (Atom::from("rowgroup"), Role::RowGroup),
110        (Atom::from("search"), Role::Search),
111        (Atom::from("separator"), Role::Splitter),
112        (Atom::from("status"), Role::Status),
113        (Atom::from("strong"), Role::Strong),
114        // (Atom::from("subscript"), Role::Subscript), // no corresponding accesskit role.
115        // (Atom::from("superscript"), Role::Superscript), // no corresponding accesskit role.
116        (Atom::from("term"), Role::Term),
117        (Atom::from("time"), Role::Time),
118        (Atom::from("timer"), Role::Timer),
119    ]
120    .into_iter()
121    .collect()
122});
123
124/// The inverse of the map above, i.e. a map from supported AccessKit role to the corresponding ARIA
125/// role.
126///
127/// This is created by inverting the map above to ensure consistency, then for roles where multiple
128/// ARIA roles map to the same AccessKit role an explicit entry is added.
129pub static ACCESSKIT_ROLE_TO_ARIA_ROLE: LazyLock<FxHashMap<Role, Atom>> = LazyLock::new(|| {
130    let mut accesskit_to_aria: FxHashMap<Role, Atom> = ARIA_ROLE_TO_ACCESSKIT_ROLE
131        .iter()
132        .map(|(aria_role, accesskit_role)| (*accesskit_role, aria_role.clone()))
133        .collect();
134    // Note: AccessKit doesn't expose nodes with the `GenericContainer` role by default, so it acts
135    // more like ARIA's `none`/`presentation` role.
136    accesskit_to_aria.insert(Role::GenericContainer, Atom::from("none"));
137    accesskit_to_aria
138});