script/dom/element/attributes/
storage.rs1use std::cell::Ref;
6
7use devtools_traits::AttrInfo;
8use html5ever::{LocalName, Namespace, Prefix};
9use script_bindings::cell::DomRefCell;
10use script_bindings::root::{Dom, DomRoot};
11use script_bindings::str::DOMString;
12use style::attr::{AttrIdentifier, AttrValue};
13
14use crate::dom::attr::Attr;
15use crate::dom::bindings::root::ToLayout;
16use crate::dom::element::Element;
17use crate::dom::node::node::NodeTraits;
18
19#[derive(MallocSizeOf)]
21pub(crate) struct ContentAttributeData {
22 pub identifier: AttrIdentifier,
23 pub value: AttrValue,
24}
25
26impl ContentAttributeData {
27 #[inline]
28 pub(crate) fn local_name(&self) -> &LocalName {
29 &self.identifier.local_name.0
30 }
31
32 #[inline]
33 pub(crate) fn name(&self) -> &LocalName {
34 &self.identifier.name.0
35 }
36
37 #[inline]
38 pub(crate) fn namespace(&self) -> &Namespace {
39 &self.identifier.namespace.0
40 }
41
42 #[inline]
43 pub(crate) fn prefix(&self) -> Option<&Prefix> {
44 Some(&self.identifier.prefix.as_ref()?.0)
45 }
46
47 #[inline]
48 pub(crate) fn value(&self) -> &AttrValue {
49 &self.value
50 }
51}
52
53pub(crate) enum AttrValueRef<'a> {
55 Direct(&'a AttrValue),
57 Borrowed(Ref<'a, AttrValue>),
59}
60
61impl std::ops::Deref for AttrValueRef<'_> {
62 type Target = AttrValue;
63
64 fn deref(&self) -> &AttrValue {
65 match self {
66 AttrValueRef::Direct(value) => value,
67 AttrValueRef::Borrowed(value) => value,
68 }
69 }
70}
71
72#[derive(Clone, Copy)]
76pub(crate) enum AttrRef<'a> {
77 Raw(&'a ContentAttributeData),
79 Dom(&'a Attr),
81}
82
83impl<'a> AttrRef<'a> {
84 #[inline]
85 pub(crate) fn local_name(&self) -> &'a LocalName {
86 match self {
87 AttrRef::Raw(data) => data.local_name(),
88 AttrRef::Dom(attr) => attr.local_name(),
89 }
90 }
91
92 #[inline]
93 pub(crate) fn name(&self) -> &'a LocalName {
94 match self {
95 AttrRef::Raw(data) => data.name(),
96 AttrRef::Dom(attr) => attr.name(),
97 }
98 }
99
100 #[inline]
101 pub(crate) fn namespace(&self) -> &'a Namespace {
102 match self {
103 AttrRef::Raw(data) => data.namespace(),
104 AttrRef::Dom(attr) => attr.namespace(),
105 }
106 }
107
108 #[inline]
109 pub(crate) fn prefix(&self) -> Option<&'a Prefix> {
110 match self {
111 AttrRef::Raw(data) => data.prefix(),
112 AttrRef::Dom(attr) => attr.prefix(),
113 }
114 }
115
116 #[inline]
117 pub(crate) fn value(&self) -> AttrValueRef<'a> {
118 match self {
119 AttrRef::Raw(data) => AttrValueRef::Direct(data.value()),
120 AttrRef::Dom(attr) => AttrValueRef::Borrowed(attr.value()),
121 }
122 }
123
124 #[inline]
127 pub(crate) fn as_attr(&self) -> Option<&'a Attr> {
128 match self {
129 AttrRef::Dom(attr) => Some(attr),
130 AttrRef::Raw(_) => None,
131 }
132 }
133
134 pub(crate) fn to_dom_string(self) -> DOMString {
136 DOMString::from(&**self.value())
137 }
138
139 pub(crate) fn summarize(&self) -> AttrInfo {
141 AttrInfo {
142 namespace: (**self.namespace()).to_owned(),
143 name: (**self.name()).to_owned(),
144 value: (**self.value()).to_owned(),
145 }
146 }
147
148 pub(crate) fn identifier(&self) -> &AttrIdentifier {
150 match self {
151 AttrRef::Raw(data) => &data.identifier,
152 AttrRef::Dom(attr) => attr.identifier(),
153 }
154 }
155}
156
157#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
159#[derive(MallocSizeOf)]
160pub(crate) enum AttributeEntry {
161 Raw(ContentAttributeData),
163 Dom(Dom<Attr>),
165}
166
167impl AttributeEntry {
168 #[inline]
170 pub(crate) fn as_ref(&self) -> AttrRef<'_> {
171 match self {
172 AttributeEntry::Raw(data) => AttrRef::Raw(data),
173 AttributeEntry::Dom(attr) => AttrRef::Dom(attr),
174 }
175 }
176
177 #[expect(unsafe_code)]
179 #[inline]
180 pub(crate) fn value_for_layout(&self) -> &AttrValue {
181 match self {
182 AttributeEntry::Raw(data) => &data.value,
183 AttributeEntry::Dom(attr) => unsafe { attr.to_layout() }.value(),
184 }
185 }
186
187 #[expect(unsafe_code)]
189 #[inline]
190 pub(crate) fn local_name_for_layout(&self) -> &LocalName {
191 match self {
192 AttributeEntry::Raw(data) => data.local_name(),
193 AttributeEntry::Dom(attr) => unsafe { attr.to_layout() }.local_name(),
194 }
195 }
196
197 #[expect(unsafe_code)]
199 #[inline]
200 pub(crate) fn namespace_for_layout(&self) -> &Namespace {
201 match self {
202 AttributeEntry::Raw(data) => data.namespace(),
203 AttributeEntry::Dom(attr) => unsafe { attr.to_layout() }.namespace(),
204 }
205 }
206}
207
208#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
212#[derive(Default, MallocSizeOf)]
213pub(crate) struct AttributeStorage(DomRefCell<Vec<AttributeEntry>>);
214
215pub(crate) struct AttributesBorrow<'a>(Ref<'a, Vec<AttributeEntry>>);
218
219impl<'a> AttributesBorrow<'a> {
220 #[inline]
222 pub(crate) fn iter(&self) -> impl Iterator<Item = AttrRef<'_>> + '_ {
223 self.0.iter().map(AttributeEntry::as_ref)
224 }
225
226 #[inline]
227 pub(crate) fn len(&self) -> usize {
228 self.0.len()
229 }
230
231 #[inline]
232 pub(crate) fn is_empty(&self) -> bool {
233 self.0.is_empty()
234 }
235
236 #[inline]
237 pub(crate) fn first(&self) -> Option<AttrRef<'_>> {
238 self.0.first().map(AttributeEntry::as_ref)
239 }
240
241 #[inline]
242 pub(crate) fn get(&self, index: usize) -> Option<AttrRef<'_>> {
243 self.0.get(index).map(AttributeEntry::as_ref)
244 }
245}
246
247impl AttributeStorage {
248 #[inline]
250 pub(crate) fn borrow(&self) -> AttributesBorrow<'_> {
251 AttributesBorrow(self.0.borrow())
252 }
253
254 #[expect(unsafe_code)]
256 #[inline]
257 pub(crate) unsafe fn borrow_for_layout(&self) -> &Vec<AttributeEntry> {
258 unsafe { self.0.borrow_for_layout() }
259 }
260
261 pub(crate) fn push_raw(&self, data: ContentAttributeData) {
263 self.0.borrow_mut().push(AttributeEntry::Raw(data));
264 }
265
266 pub(crate) fn push_dom(&self, attr: &Attr) {
268 self.0
269 .borrow_mut()
270 .push(AttributeEntry::Dom(Dom::from_ref(attr)));
271 }
272
273 pub(crate) fn remove(&self, index: usize) -> AttributeEntry {
275 self.0.borrow_mut().remove(index)
276 }
277
278 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
280 pub(crate) fn set(&self, index: usize, entry: AttributeEntry) {
281 self.0.borrow_mut()[index] = entry;
282 }
283
284 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
290 #[expect(unsafe_code)]
291 pub(crate) fn ensure_dom(&self, index: usize, element: &Element) -> DomRoot<Attr> {
292 if let AttributeEntry::Dom(attr) = &self.0.borrow()[index] {
294 return DomRoot::from_ref(&**attr);
295 }
296
297 let data = {
299 let mut entries = self.0.borrow_mut();
300 let placeholder = AttributeEntry::Raw(ContentAttributeData {
301 identifier: AttrIdentifier {
302 local_name: style::values::GenericAtomIdent(html5ever::local_name!("")),
303 name: style::values::GenericAtomIdent(html5ever::local_name!("")),
304 namespace: style::values::GenericAtomIdent(html5ever::ns!()),
305 prefix: None,
306 },
307 value: AttrValue::String(String::new()),
308 });
309 let old = std::mem::replace(&mut entries[index], placeholder);
310 match old {
311 AttributeEntry::Raw(data) => data,
312 _ => unreachable!(),
313 }
314 };
315
316 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
318 let cx = &mut cx;
319 let doc = element.owner_document();
320 let attr = Attr::new(
321 cx,
322 &doc,
323 data.identifier.local_name.0,
324 data.value,
325 data.identifier.name.0,
326 data.identifier.namespace.0,
327 data.identifier.prefix.map(|p| p.0),
328 Some(element),
329 );
330
331 self.0.borrow_mut()[index] = AttributeEntry::Dom(Dom::from_ref(&*attr));
332 attr
333 }
334}
335
336#[expect(unsafe_code)]
339unsafe impl crate::dom::bindings::trace::JSTraceable for AttributeStorage {
340 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
341 for entry in self.0.borrow().iter() {
342 if let AttributeEntry::Dom(attr) = entry {
343 unsafe { js::rust::Trace::trace(attr, trc) };
344 }
345 }
346 }
347}