rdom_core/content_editable.rs
1//! The `contenteditable` enumerated attribute — HTML §6.8.1. Attribute
2//! semantics only; what editing does lives in the backend.
3//!
4//! Keywords match ASCII case-insensitively (§2.3.3): `true` and the
5//! empty string are the *true* state, `false` the *false* state,
6//! `plaintext-only` the *plaintext-only* state. A missing or invalid
7//! value is the *inherit* state (both defaults are inherit), which
8//! [`Dom::content_editable_state`] reports as `None`.
9
10use crate::dom::Dom;
11use crate::node_id::NodeId;
12
13/// A `contenteditable` attribute's explicit state (HTML §6.8.1). The
14/// inherit state is the absence of one (`None`).
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[non_exhaustive]
17pub enum ContentEditableState {
18 /// `true` or `""`: the element is an editing host.
19 True,
20 /// `false`: the element is not editable, whatever its ancestors are.
21 False,
22 /// `plaintext-only`: an editing host whose edits are plain text.
23 PlaintextOnly,
24}
25
26impl ContentEditableState {
27 /// Parse an attribute value: the keyword, ASCII case-insensitively;
28 /// `None` (inherit) for anything else.
29 pub fn from_attribute(value: &str) -> Option<Self> {
30 if value.is_empty() || value.eq_ignore_ascii_case("true") {
31 Some(Self::True)
32 } else if value.eq_ignore_ascii_case("false") {
33 Some(Self::False)
34 } else if value.eq_ignore_ascii_case("plaintext-only") {
35 Some(Self::PlaintextOnly)
36 } else {
37 None
38 }
39 }
40
41 /// The canonical keyword — what the `contentEditable` IDL getter
42 /// returns for this state.
43 pub fn keyword(self) -> &'static str {
44 match self {
45 Self::True => "true",
46 Self::False => "false",
47 Self::PlaintextOnly => "plaintext-only",
48 }
49 }
50
51 /// `true` for the two states that make an element an editing host.
52 pub fn is_editing_host(self) -> bool {
53 matches!(self, Self::True | Self::PlaintextOnly)
54 }
55}
56
57impl<Ext> Dom<Ext> {
58 /// `id`'s explicit `contenteditable` state, or `None` for the
59 /// inherit state (attribute missing or invalid) and for non-elements.
60 pub fn content_editable_state(&self, id: NodeId) -> Option<ContentEditableState> {
61 self.get_attribute(id, "contenteditable")
62 .and_then(ContentEditableState::from_attribute)
63 }
64}