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 get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom> {
161 self.with_attribute(&ns!(), local_name, |attribute| {
162 attribute.value().as_tokens().to_vec()
163 })
164 .unwrap_or_default()
165 }
166
167 pub(crate) fn set_tokenlist_attribute(
168 &self,
169 cx: &mut JSContext,
170 local_name: &LocalName,
171 value: DOMString,
172 ) {
173 self.set_attribute(
174 cx,
175 local_name,
176 AttrValue::from_serialized_tokenlist(value.into()),
177 );
178 }
179
180 pub(crate) fn get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u32 {
181 self.with_attribute(&ns!(), local_name, |attribute| {
182 if let AttrValue::UInt(_, value) = *attribute.value() {
183 value
184 } else {
185 unreachable!("Expected an AttrValue::Int: implement parse_plain_attribute")
186 }
187 })
188 .unwrap_or(default)
189 }
190
191 fn compute_attribute_value_with_style_fast_path(&self, attr: AttrRef<'_>) -> AttrValue {
197 if *attr.local_name() == local_name!("style") {
198 let document = self.owner_document();
199
200 if let AttrValue::Declaration {
201 block,
202 lock,
203 serialization,
204 } = &*attr.value()
205 {
206 let cloned_block = block.read_with(&lock.read()).clone();
210 return AttrValue::Declaration {
211 block: ServoArc::new(lock.wrap(cloned_block)),
212 lock: lock.clone(),
213 serialization: serialization.clone(),
214 };
215 }
216
217 if let Some(ref pdb) = *self.style_attribute().borrow() {
218 let shared_lock = document.style_shared_author_lock();
219 let new_pdb = pdb.read_with(&shared_lock.read()).clone();
220 return AttrValue::Declaration {
221 block: ServoArc::new(shared_lock.wrap(new_pdb)),
222 lock: shared_lock.clone(),
223 serialization: (**attr.value()).to_owned().into(),
227 };
228 }
229 }
230
231 attr.value().clone()
232 }
233
234 pub(crate) fn copy_all_attributes_to_other_element(
236 &self,
237 cx: &mut JSContext,
238 target_element: &Element,
239 ) {
240 for attr in self.attrs().borrow().iter() {
242 let new_value = self.compute_attribute_value_with_style_fast_path(attr);
244 target_element.push_new_attribute(
246 cx,
247 attr.local_name().clone(),
248 new_value,
249 attr.name().clone(),
250 attr.namespace().clone(),
251 attr.prefix().cloned(),
252 AttributeMutationReason::ByCloning,
253 );
254 }
255 }
256}