script/dom/element/attributes/
accessors.rs1use html5ever::{LocalName, Namespace, local_name, ns};
6use js::context::JSContext;
7use servo_arc::Arc as ServoArc;
8use style::attr::AttrValue;
9use stylo_atoms::Atom;
10
11use crate::dom::bindings::codegen::UnionTypes::{TrustedHTMLOrString, TrustedScriptURLOrUSVString};
12use crate::dom::bindings::str::{DOMString, USVString};
13use crate::dom::element::attributes::storage::AttrRef;
14use crate::dom::element::{AttributeMutationReason, Element};
15use crate::dom::node::NodeTraits;
16
17impl Element {
18 pub(crate) fn get_attribute_string_value(&self, local_name: &LocalName) -> Option<String> {
21 debug_assert_eq!(
24 *local_name,
25 local_name.to_ascii_lowercase(),
26 "All namespace-less attribute accesses should use a lowercase ASCII name"
27 );
28
29 self.get_attribute_string_value_with_namespace(&ns!(), local_name)
30 }
31
32 pub(crate) fn get_attribute_string_value_with_namespace(
33 &self,
34 namespace: &Namespace,
35 local_name: &LocalName,
36 ) -> Option<String> {
37 self.with_attribute(namespace, local_name, |attribute| {
38 String::from(&**attribute.value())
39 })
40 }
41
42 pub(crate) fn get_int_attribute(&self, local_name: &LocalName, default: i32) -> i32 {
43 self.with_attribute(&ns!(), local_name, |attribute| {
44 if let AttrValue::Int(_, value) = *attribute.value() {
45 value
46 } else {
47 unreachable!("Expected an AttrValue::Int: implement parse_plain_attribute")
48 }
49 })
50 .unwrap_or(default)
51 }
52
53 pub(crate) fn set_atomic_attribute(
54 &self,
55 cx: &mut JSContext,
56 local_name: &LocalName,
57 value: DOMString,
58 ) {
59 self.set_attribute(cx, local_name, AttrValue::from_atomic(value.into()));
60 }
61
62 pub(crate) fn set_bool_attribute(
63 &self,
64 cx: &mut JSContext,
65 local_name: &LocalName,
66 value: bool,
67 ) {
68 if self.has_attribute(local_name) == value {
69 return;
70 }
71 if value {
72 self.set_string_attribute(cx, local_name, DOMString::new());
73 } else {
74 self.remove_attribute(cx, &ns!(), local_name);
75 }
76 }
77
78 pub(crate) fn get_url_attribute(&self, local_name: &LocalName) -> USVString {
79 let Some(value) = self.get_attribute_string_value(local_name) else {
80 return Default::default();
81 };
82 self.owner_document()
83 .encoding_parse_a_url(&value)
84 .map(|parsed| USVString(parsed.into_string()))
85 .unwrap_or_else(|_| USVString(value))
86 }
87
88 pub(crate) fn set_url_attribute(
89 &self,
90 cx: &mut JSContext,
91 local_name: &LocalName,
92 value: USVString,
93 ) {
94 self.set_attribute(cx, local_name, AttrValue::String(value.into()));
95 }
96
97 pub(crate) fn get_trusted_type_url_attribute(
98 &self,
99 local_name: &LocalName,
100 ) -> TrustedScriptURLOrUSVString {
101 let Some(value) = self.get_attribute_string_value(local_name) else {
102 return TrustedScriptURLOrUSVString::USVString(USVString::default());
103 };
104 self.owner_document()
105 .encoding_parse_a_url(&value)
106 .map(|parsed| TrustedScriptURLOrUSVString::USVString(USVString(parsed.into_string())))
107 .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value)))
108 }
109
110 pub(crate) fn get_trusted_html_attribute(&self, local_name: &LocalName) -> TrustedHTMLOrString {
111 TrustedHTMLOrString::String(self.get_string_attribute(local_name))
112 }
113
114 pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString {
115 self.get_attribute_string_value(local_name)
116 .map(|value| value.into())
117 .unwrap_or_default()
118 }
119
120 pub(crate) fn set_string_attribute(
121 &self,
122 cx: &mut JSContext,
123 local_name: &LocalName,
124 value: DOMString,
125 ) {
126 self.set_attribute(cx, local_name, value.str().to_string().into());
127 }
128
129 pub(crate) fn get_nullable_string_attribute(
132 &self,
133 local_name: &LocalName,
134 ) -> Option<DOMString> {
135 if self.has_attribute(local_name) {
136 Some(self.get_string_attribute(local_name))
137 } else {
138 None
139 }
140 }
141
142 pub(crate) fn set_nullable_string_attribute(
145 &self,
146 cx: &mut JSContext,
147 local_name: &LocalName,
148 value: Option<DOMString>,
149 ) {
150 match value {
151 Some(val) => {
152 self.set_string_attribute(cx, local_name, val);
153 },
154 None => {
155 self.remove_attribute(cx, &ns!(), local_name);
156 },
157 }
158 }
159
160 pub(crate) fn any_tokenlist_attribute(
163 &self,
164 local_name: &LocalName,
165 f: impl Fn(&Atom) -> bool,
166 ) -> bool {
167 self.with_attribute(&ns!(), local_name, |attribute| {
168 attribute.value().as_tokens().iter().any(f)
169 })
170 .unwrap_or(false)
171 }
172
173 pub(crate) fn get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom> {
174 self.with_attribute(&ns!(), local_name, |attribute| {
175 attribute.value().as_tokens().to_vec()
176 })
177 .unwrap_or_default()
178 }
179
180 pub(crate) fn set_tokenlist_attribute(
181 &self,
182 cx: &mut JSContext,
183 local_name: &LocalName,
184 value: DOMString,
185 ) {
186 self.set_attribute(
187 cx,
188 local_name,
189 AttrValue::from_serialized_tokenlist(value.into()),
190 );
191 }
192
193 pub(crate) fn get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u32 {
194 self.with_attribute(&ns!(), local_name, |attribute| {
195 if let AttrValue::UInt(_, value) = *attribute.value() {
196 value
197 } else {
198 unreachable!("Expected an AttrValue::Int: implement parse_plain_attribute")
199 }
200 })
201 .unwrap_or(default)
202 }
203
204 fn compute_attribute_value_with_style_fast_path(&self, attr: AttrRef<'_>) -> AttrValue {
210 if *attr.local_name() == local_name!("style") {
211 let document = self.owner_document();
212
213 if let AttrValue::Declaration {
214 block,
215 lock,
216 serialization,
217 } = &*attr.value()
218 {
219 let cloned_block = block.read_with(&lock.read()).clone();
223 return AttrValue::Declaration {
224 block: ServoArc::new(lock.wrap(cloned_block)),
225 lock: lock.clone(),
226 serialization: serialization.clone(),
227 };
228 }
229
230 if let Some(ref pdb) = *self.style_attribute().borrow() {
231 let shared_lock = document.style_shared_author_lock();
232 let new_pdb = pdb.read_with(&shared_lock.read()).clone();
233 return AttrValue::Declaration {
234 block: ServoArc::new(shared_lock.wrap(new_pdb)),
235 lock: shared_lock.clone(),
236 serialization: (**attr.value()).to_owned().into(),
240 };
241 }
242 }
243
244 attr.value().clone()
245 }
246
247 pub(crate) fn copy_all_attributes_to_other_element(
249 &self,
250 cx: &mut JSContext,
251 target_element: &Element,
252 ) {
253 for attr in self.attrs().borrow().iter() {
255 let new_value = self.compute_attribute_value_with_style_fast_path(attr);
257 target_element.push_new_attribute(
259 cx,
260 attr.local_name().clone(),
261 new_value,
262 attr.name().clone(),
263 attr.namespace().clone(),
264 attr.prefix().cloned(),
265 AttributeMutationReason::ByCloning,
266 );
267 }
268 }
269}