1use std::borrow::ToOwned;
6use std::mem;
7use std::sync::LazyLock;
8
9use devtools_traits::AttrInfo;
10use dom_struct::dom_struct;
11use html5ever::{LocalName, Namespace, Prefix, local_name, ns};
12use style::attr::{AttrIdentifier, AttrValue};
13use style::values::GenericAtomIdent;
14use stylo_atoms::Atom;
15
16use crate::dom::bindings::cell::{DomRefCell, Ref};
17use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
18use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString;
19use crate::dom::bindings::error::Fallible;
20use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
21use crate::dom::bindings::str::DOMString;
22use crate::dom::document::Document;
23use crate::dom::element::Element;
24use crate::dom::node::{Node, NodeTraits};
25use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
26use crate::script_runtime::CanGc;
27
28#[dom_struct]
30pub(crate) struct Attr {
31 node_: Node,
32 #[no_trace]
33 identifier: AttrIdentifier,
34 #[no_trace]
35 value: DomRefCell<AttrValue>,
36
37 owner: MutNullableDom<Element>,
39}
40
41impl Attr {
42 fn new_inherited(
43 document: &Document,
44 local_name: LocalName,
45 value: AttrValue,
46 name: LocalName,
47 namespace: Namespace,
48 prefix: Option<Prefix>,
49 owner: Option<&Element>,
50 ) -> Attr {
51 Attr {
52 node_: Node::new_inherited(document),
53 identifier: AttrIdentifier {
54 local_name: GenericAtomIdent(local_name),
55 name: GenericAtomIdent(name),
56 namespace: GenericAtomIdent(namespace),
57 prefix: prefix.map(GenericAtomIdent),
58 },
59 value: DomRefCell::new(value),
60 owner: MutNullableDom::new(owner),
61 }
62 }
63 #[allow(clippy::too_many_arguments)]
64 pub(crate) fn new(
65 document: &Document,
66 local_name: LocalName,
67 value: AttrValue,
68 name: LocalName,
69 namespace: Namespace,
70 prefix: Option<Prefix>,
71 owner: Option<&Element>,
72 can_gc: CanGc,
73 ) -> DomRoot<Attr> {
74 Node::reflect_node(
75 Box::new(Attr::new_inherited(
76 document, local_name, value, name, namespace, prefix, owner,
77 )),
78 document,
79 can_gc,
80 )
81 }
82
83 #[inline]
84 pub(crate) fn name(&self) -> &LocalName {
85 &self.identifier.name.0
86 }
87
88 #[inline]
89 pub(crate) fn namespace(&self) -> &Namespace {
90 &self.identifier.namespace.0
91 }
92
93 #[inline]
94 pub(crate) fn prefix(&self) -> Option<&Prefix> {
95 Some(&self.identifier.prefix.as_ref()?.0)
96 }
97}
98
99impl AttrMethods<crate::DomTypeHolder> for Attr {
100 fn LocalName(&self) -> DOMString {
102 DOMString::from(&**self.local_name())
104 }
105
106 fn Value(&self) -> DOMString {
108 DOMString::from(&**self.value())
110 }
111
112 fn SetValue(&self, value: DOMString, can_gc: CanGc) -> Fallible<()> {
114 if let Some(owner) = self.owner() {
116 let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
121 owner.namespace(),
122 owner.local_name(),
123 self.local_name(),
124 Some(self.namespace()),
125 TrustedTypeOrString::String(value),
126 &owner.owner_global(),
127 can_gc,
128 )?;
129 if let Some(owner) = self.owner() {
130 let value = owner.parse_attribute(self.namespace(), self.local_name(), value);
132 owner.change_attribute(self, value, can_gc);
133 } else {
134 self.set_value(value);
136 }
137 } else {
138 self.set_value(value);
140 }
141 Ok(())
142 }
143
144 fn Name(&self) -> DOMString {
146 DOMString::from(&**self.name())
148 }
149
150 fn GetNamespaceURI(&self) -> Option<DOMString> {
152 match *self.namespace() {
153 ns!() => None,
154 ref url => Some(DOMString::from(&**url)),
155 }
156 }
157
158 fn GetPrefix(&self) -> Option<DOMString> {
160 self.prefix().map(|p| DOMString::from(&**p))
162 }
163
164 fn GetOwnerElement(&self) -> Option<DomRoot<Element>> {
166 self.owner()
167 }
168
169 fn Specified(&self) -> bool {
171 true }
173}
174
175impl Attr {
176 pub(crate) fn swap_value(&self, value: &mut AttrValue) {
178 mem::swap(&mut *self.value.borrow_mut(), value);
179 }
180
181 pub(crate) fn identifier(&self) -> &AttrIdentifier {
182 &self.identifier
183 }
184
185 pub(crate) fn value(&self) -> Ref<'_, AttrValue> {
186 self.value.borrow()
187 }
188
189 fn set_value(&self, value: DOMString) {
190 *self.value.borrow_mut() = AttrValue::String(value.into());
191 }
192
193 pub(crate) fn local_name(&self) -> &LocalName {
194 &self.identifier.local_name
195 }
196
197 pub(crate) fn set_owner(&self, owner: Option<&Element>) {
200 let ns = self.namespace();
201 match (self.owner(), owner) {
202 (Some(old), None) => {
203 assert!(
205 old.get_attribute(ns, &self.identifier.local_name)
206 .as_deref() !=
207 Some(self)
208 )
209 },
210 (Some(old), Some(new)) => assert_eq!(&*old, new),
211 _ => {},
212 }
213 self.owner.set(owner);
214 }
215
216 pub(crate) fn owner(&self) -> Option<DomRoot<Element>> {
217 self.owner.get()
218 }
219
220 pub(crate) fn summarize(&self) -> AttrInfo {
221 AttrInfo {
222 namespace: (**self.namespace()).to_owned(),
223 name: (**self.name()).to_owned(),
224 value: (**self.value()).to_owned(),
225 }
226 }
227
228 pub(crate) fn qualified_name(&self) -> DOMString {
229 match self.prefix() {
230 Some(ref prefix) => DOMString::from(format!("{}:{}", prefix, &**self.local_name())),
231 None => DOMString::from(&**self.local_name()),
232 }
233 }
234}
235
236#[allow(unsafe_code)]
237pub(crate) trait AttrHelpersForLayout<'dom> {
238 fn value(self) -> &'dom AttrValue;
239 fn as_str(&self) -> &'dom str;
240 fn to_tokens(self) -> Option<&'dom [Atom]>;
241 fn local_name(self) -> &'dom LocalName;
242 fn namespace(self) -> &'dom Namespace;
243}
244
245#[allow(unsafe_code)]
246impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> {
247 #[inline]
248 fn value(self) -> &'dom AttrValue {
249 unsafe { self.unsafe_get().value.borrow_for_layout() }
250 }
251
252 #[inline]
253 fn as_str(&self) -> &'dom str {
254 self.value()
255 }
256
257 #[inline]
258 fn to_tokens(self) -> Option<&'dom [Atom]> {
259 match *self.value() {
260 AttrValue::TokenList(_, ref tokens) => Some(tokens),
261 _ => None,
262 }
263 }
264
265 #[inline]
266 fn local_name(self) -> &'dom LocalName {
267 &self.unsafe_get().identifier.local_name.0
268 }
269
270 #[inline]
271 fn namespace(self) -> &'dom Namespace {
272 &self.unsafe_get().identifier.namespace.0
273 }
274}
275
276pub(crate) fn is_relevant_attribute(namespace: &Namespace, local_name: &LocalName) -> bool {
278 namespace == &ns!() || (namespace == &ns!(xlink) && local_name == &local_name!("href"))
280}
281
282pub(crate) fn is_boolean_attribute(name: &str) -> bool {
284 static BOOLEAN_ATTRIBUTES: LazyLock<[&str; 30]> = LazyLock::new(|| {
291 [
292 "allowfullscreen",
293 "alpha",
294 "async",
295 "autofocus",
296 "autoplay",
297 "checked",
298 "controls",
299 "default",
300 "defer",
301 "disabled",
302 "formnovalidate",
303 "hidden",
304 "inert",
305 "ismap",
306 "itemscope",
307 "loop",
308 "multiple",
309 "muted",
310 "nomodule",
311 "novalidate",
312 "open",
313 "playsinline",
314 "readonly",
315 "required",
316 "reversed",
317 "selected",
318 "shadowrootclonable",
319 "shadowrootcustomelementregistry",
320 "shadowrootdelegatesfocus",
321 "shadowrootserializable",
322 ]
323 });
324
325 BOOLEAN_ATTRIBUTES
326 .iter()
327 .any(|&boolean_attr| boolean_attr.eq_ignore_ascii_case(name))
328}