diff --git a/accesskit/src/lib.rs b/accesskit/src/lib.rs index 73dcf4d63..3112a3a40 100644 --- a/accesskit/src/lib.rs +++ b/accesskit/src/lib.rs @@ -918,6 +918,7 @@ enum PropertyId { AuthorId, ClassName, FontFamily, + HtmlId, HtmlTag, InnerHtml, KeyboardShortcut, @@ -2082,6 +2083,12 @@ string_property_methods! { /// The font family used for this node's text. Only set this when it differs /// from the parent. (FontFamily, font_family, set_font_family, clear_font_family), + /// The ID of the HTML element represented by this node. On platforms + /// where both are mapped to the same platform property, this takes + /// precedence over [`author_id`]. + /// + /// [`author_id`]: Node::author_id + (HtmlId, html_id, set_html_id, clear_html_id), /// The name of the HTML element represented by this node. (HtmlTag, html_tag, set_html_tag, clear_html_tag), /// Inner HTML of an element. Only used for a top-level math element, @@ -2833,6 +2840,7 @@ impl<'de> Visitor<'de> for PropertiesVisitor { AuthorId, ClassName, FontFamily, + HtmlId, HtmlTag, InnerHtml, KeyboardShortcut, @@ -2985,6 +2993,7 @@ impl JsonSchema for Properties { AuthorId, ClassName, FontFamily, + HtmlId, HtmlTag, InnerHtml, KeyboardShortcut, diff --git a/accesskit_consumer/src/node.rs b/accesskit_consumer/src/node.rs index 87b748ccc..6e92d988c 100644 --- a/accesskit_consumer/src/node.rs +++ b/accesskit_consumer/src/node.rs @@ -982,6 +982,14 @@ impl<'a> NodeRef<'a> { self.data().author_id() } + pub fn html_id(&self) -> Option<&str> { + self.data().html_id() + } + + pub fn has_html_id(&self) -> bool { + self.data().html_id().is_some() + } + pub fn class_name(&self) -> Option<&str> { self.data().class_name() } diff --git a/adapters/android/Cargo.toml b/adapters/android/Cargo.toml index 60c4a06bc..102ab96a8 100644 --- a/adapters/android/Cargo.toml +++ b/adapters/android/Cargo.toml @@ -19,3 +19,4 @@ accesskit = { version = "0.24.1", path = "../../accesskit" } accesskit_consumer = { version = "0.38.0", path = "../../accesskit_consumer" } jni = "0.21.1" log = "0.4.17" + diff --git a/adapters/atspi-common/src/node.rs b/adapters/atspi-common/src/node.rs index 4e7453dcc..c84677c68 100644 --- a/adapters/atspi-common/src/node.rs +++ b/adapters/atspi-common/src/node.rs @@ -397,6 +397,10 @@ impl NodeWrapper<'_> { .map(|s| s.to_string()) } + fn html_id(&self) -> Option<&str> { + self.0.html_id() + } + fn braille_label(&self) -> Option<&str> { self.0.braille_label() } @@ -416,6 +420,9 @@ impl NodeWrapper<'_> { if let Some(size_of_set) = self.size_of_set() { attributes.insert("setsize", size_of_set); } + if let Some(html_id) = self.html_id() { + attributes.insert("id", html_id.to_string()); + } if let Some(label) = self.braille_label() { attributes.insert("braillelabel", label.to_string()); } diff --git a/adapters/ios/src/node.rs b/adapters/ios/src/node.rs index c7109e1c5..902f39079 100644 --- a/adapters/ios/src/node.rs +++ b/adapters/ios/src/node.rs @@ -179,6 +179,10 @@ impl NodeWrapper<'_> { _ => UIAccessibilityContainerType::None, } } + + fn identifier(&self) -> Option<&str> { + self.0.html_id().or_else(|| self.0.author_id()) + } } pub(crate) struct PlatformNodeIvars { @@ -425,7 +429,8 @@ declare_class!( #[method_id(accessibilityIdentifier)] fn identifier(&self) -> Option> { self.resolve(|node| { - node.author_id().map(NSString::from_str) + let wrapper = NodeWrapper(node); + wrapper.identifier().map(NSString::from_str) }) .flatten() } @@ -556,6 +561,10 @@ mod tests { NodeWrapper(&node).can_be_focused() } + fn wrapper_identifier(node: &Node) -> Option { + with_single(node, |n| NodeWrapper(n).identifier().map(String::from)) + } + // ---- label ---- #[test] @@ -1012,4 +1021,34 @@ mod tests { FrameSource::Rect(_), )); } + + // ---- identifier ---- + + #[test] + fn identifier_from_html_id() { + let mut node = Node::new(Role::Button); + node.set_html_id("html-btn1"); + assert_eq!(wrapper_identifier(&node), Some("html-btn1".into())); + } + + #[test] + fn identifier_from_author_id() { + let mut node = Node::new(Role::Button); + node.set_author_id("native-btn1"); + assert_eq!(wrapper_identifier(&node), Some("native-btn1".into())); + } + + #[test] + fn html_id_takes_precedence_over_author_id_for_identifier() { + let mut node = Node::new(Role::Button); + node.set_author_id("native-btn1"); + node.set_html_id("html-btn1"); + assert_eq!(wrapper_identifier(&node), Some("html-btn1".into())); + } + + #[test] + fn identifier_absent() { + let node = Node::new(Role::Button); + assert!(wrapper_identifier(&node).is_none()); + } } diff --git a/adapters/macos/src/node.rs b/adapters/macos/src/node.rs index 00a07c0da..bedcedbe1 100644 --- a/adapters/macos/src/node.rs +++ b/adapters/macos/src/node.rs @@ -1086,6 +1086,8 @@ declare_class!( return Some(NSString::from_str(node.braille_label().unwrap())) } else if attr == ns_string!("AXBrailleRoleDescription") && node.has_braille_role_description() { return Some(NSString::from_str(node.braille_role_description().unwrap())) + } else if attr == ns_string!("AXDOMIdentifier") && node.has_html_id() { + return Some(NSString::from_str(node.html_id().unwrap())) } None @@ -1280,7 +1282,9 @@ declare_class!( return node.is_dialog(); } if selector == sel!(accessibilityAttributeValue:) { - return node.has_braille_label() || node.has_braille_role_description() + return node.has_braille_label() + || node.has_braille_role_description() + || node.has_html_id() } if selector == sel!(accessibilityURL) { return node.supports_url(); diff --git a/adapters/unix/Cargo.toml b/adapters/unix/Cargo.toml index de3d137af..ae94e6185 100644 --- a/adapters/unix/Cargo.toml +++ b/adapters/unix/Cargo.toml @@ -36,3 +36,4 @@ tokio-stream = { version = "0.1.14", optional = true } version = "1.32.0" optional = true features = ["macros", "net", "rt", "sync", "time"] + diff --git a/adapters/windows/src/node.rs b/adapters/windows/src/node.rs index 7cf2b594b..f414f770f 100644 --- a/adapters/windows/src/node.rs +++ b/adapters/windows/src/node.rs @@ -554,7 +554,8 @@ impl NodeWrapper<'_> { fn automation_id(&mut self) -> Option> { self.node - .author_id() + .html_id() + .or_else(|| self.node.author_id()) .map(|s| StrWrapper::new(s, self.string_buffer)) } diff --git a/adapters/winit/Cargo.toml b/adapters/winit/Cargo.toml index 558b43a30..1f024fc10 100644 --- a/adapters/winit/Cargo.toml +++ b/adapters/winit/Cargo.toml @@ -57,3 +57,4 @@ features = [ [target.'cfg(target_os = "ios")'.dev-dependencies.softbuffer] version = "0.4.8" default-features = false +