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 set_nullable_tokenlist_attribute(
161 &self,
162 cx: &mut JSContext,
163 local_name: &LocalName,
164 value: Option<DOMString>,
165 ) {
166 match value {
167 Some(string_value) => {
168 self.set_tokenlist_attribute(cx, local_name, string_value);
169 },
170 None => {
171 self.remove_attribute(cx, &ns!(), local_name);
172 },
173 }
174 }
175
176 pub(crate) fn any_tokenlist_attribute(
179 &self,
180 local_name: &LocalName,
181 f: impl Fn(&Atom) -> bool,
182 ) -> bool {
183 self.with_attribute(&ns!(), local_name, |attribute| {
184 attribute.value().as_tokens().iter().any(f)
185 })
186 .unwrap_or(false)
187 }
188
189 pub(crate) fn get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom> {
190 self.with_attribute(&ns!(), local_name, |attribute| {
191 attribute.value().as_tokens().to_vec()
192 })
193 .unwrap_or_default()
194 }
195
196 pub(crate) fn set_tokenlist_attribute(
197 &self,
198 cx: &mut JSContext,
199 local_name: &LocalName,
200 value: DOMString,
201 ) {
202 self.set_attribute(
203 cx,
204 local_name,
205 AttrValue::from_serialized_tokenlist(value.into()),
206 );
207 }
208
209 pub(crate) fn get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u32 {
210 self.with_attribute(&ns!(), local_name, |attribute| {
211 if let AttrValue::UInt(_, value) = *attribute.value() {
212 value
213 } else {
214 unreachable!("Expected an AttrValue::Int: implement parse_plain_attribute")
215 }
216 })
217 .unwrap_or(default)
218 }
219
220 fn compute_attribute_value_with_style_fast_path(&self, attr: AttrRef<'_>) -> AttrValue {
226 if *attr.local_name() == local_name!("style") {
227 let document = self.owner_document();
228
229 if let AttrValue::Declaration {
230 block,
231 lock,
232 serialization,
233 } = &*attr.value()
234 {
235 let cloned_block = block.read_with(&lock.read()).clone();
239 return AttrValue::Declaration {
240 block: ServoArc::new(lock.wrap(cloned_block)),
241 lock: lock.clone(),
242 serialization: serialization.clone(),
243 };
244 }
245
246 if let Some(ref pdb) = *self.style_attribute().borrow() {
247 let shared_lock = document.style_shared_author_lock();
248 let new_pdb = pdb.read_with(&shared_lock.read()).clone();
249 return AttrValue::Declaration {
250 block: ServoArc::new(shared_lock.wrap(new_pdb)),
251 lock: shared_lock.clone(),
252 serialization: (**attr.value()).to_owned().into(),
256 };
257 }
258 }
259
260 attr.value().clone()
261 }
262
263 pub(crate) fn copy_all_attributes_to_other_element(
265 &self,
266 cx: &mut JSContext,
267 target_element: &Element,
268 ) {
269 for attr in self.attrs().borrow().iter() {
271 let new_value = self.compute_attribute_value_with_style_fast_path(attr);
273 target_element.push_new_attribute(
275 cx,
276 attr.local_name().clone(),
277 new_value,
278 attr.name().clone(),
279 attr.namespace().clone(),
280 attr.prefix().cloned(),
281 AttributeMutationReason::ByCloning,
282 );
283 }
284 }
285}