script/dom/element/attributes/
storage.rs1use std::cell::Ref;
6use std::ops::Deref;
7
8use html5ever::{LocalName, Namespace, Prefix, ns};
9use js::context::JSContext;
10use script_bindings::cell::DomRefCell;
11use script_bindings::root::{Dom, DomRoot};
12use script_bindings::str::DOMString;
13use style::attr::{AttrIdentifier, AttrValue};
14use style::values::GenericAtomIdent;
15
16use crate::dom::attr::Attr;
17use crate::dom::bindings::root::{LayoutDom, ToLayout};
18use crate::dom::element::Element;
19use crate::dom::node::node::NodeTraits;
20
21static EMPTY_NAMESPACE: Namespace = ns!();
24
25#[derive(Clone, MallocSizeOf)]
27pub(crate) enum AttrName {
28 Unqualified(LocalName),
30 Qualified(Box<AttrIdentifier>),
32}
33
34impl AttrName {
35 pub(crate) fn new(
38 local_name: LocalName,
39 name: LocalName,
40 namespace: Namespace,
41 prefix: Option<Prefix>,
42 ) -> Self {
43 if prefix.is_none() && name == local_name && namespace == ns!() {
44 AttrName::Unqualified(local_name)
45 } else {
46 AttrName::Qualified(Box::new(AttrIdentifier {
47 local_name: GenericAtomIdent(local_name),
48 name: GenericAtomIdent(name),
49 namespace: GenericAtomIdent(namespace),
50 prefix: prefix.map(GenericAtomIdent),
51 }))
52 }
53 }
54
55 #[inline]
56 pub(crate) fn local_name(&self) -> &LocalName {
57 match self {
58 AttrName::Unqualified(local_name) => local_name,
59 AttrName::Qualified(identifier) => &identifier.local_name.0,
60 }
61 }
62
63 #[inline]
64 pub(crate) fn name(&self) -> &LocalName {
65 match self {
66 AttrName::Unqualified(local_name) => local_name,
67 AttrName::Qualified(identifier) => &identifier.name.0,
68 }
69 }
70
71 #[inline]
72 pub(crate) fn namespace(&self) -> &Namespace {
73 match self {
74 AttrName::Unqualified(_) => &EMPTY_NAMESPACE,
75 AttrName::Qualified(identifier) => &identifier.namespace.0,
76 }
77 }
78
79 #[inline]
80 pub(crate) fn prefix(&self) -> Option<&Prefix> {
81 match self {
82 AttrName::Unqualified(_) => None,
83 AttrName::Qualified(identifier) => Some(&identifier.prefix.as_ref()?.0),
84 }
85 }
86
87 pub(crate) fn into_parts(self) -> (LocalName, LocalName, Namespace, Option<Prefix>) {
89 match self {
90 AttrName::Unqualified(local_name) => (local_name.clone(), local_name, ns!(), None),
91 AttrName::Qualified(identifier) => {
92 let AttrIdentifier {
93 local_name,
94 name,
95 namespace,
96 prefix,
97 } = *identifier;
98 (local_name.0, name.0, namespace.0, prefix.map(|p| p.0))
99 },
100 }
101 }
102
103 pub(crate) fn as_identifier(&self) -> AttrIdentifier {
105 match self {
106 AttrName::Unqualified(local_name) => AttrIdentifier {
107 local_name: GenericAtomIdent(local_name.clone()),
108 name: GenericAtomIdent(local_name.clone()),
109 namespace: GenericAtomIdent(ns!()),
110 prefix: None,
111 },
112 AttrName::Qualified(identifier) => (**identifier).clone(),
113 }
114 }
115}
116
117#[derive(MallocSizeOf)]
119pub(crate) struct ContentAttributeData {
120 pub identifier: AttrName,
121 pub value: AttrValue,
122}
123
124impl ContentAttributeData {
125 #[inline]
126 pub(crate) fn local_name(&self) -> &LocalName {
127 self.identifier.local_name()
128 }
129
130 #[inline]
131 pub(crate) fn name(&self) -> &LocalName {
132 self.identifier.name()
133 }
134
135 #[inline]
136 pub(crate) fn namespace(&self) -> &Namespace {
137 self.identifier.namespace()
138 }
139
140 #[inline]
141 pub(crate) fn prefix(&self) -> Option<&Prefix> {
142 self.identifier.prefix()
143 }
144
145 #[inline]
146 pub(crate) fn value(&self) -> &AttrValue {
147 &self.value
148 }
149}
150
151pub(crate) enum AttrValueRef<'a> {
153 Direct(&'a AttrValue),
155 Borrowed(Ref<'a, AttrValue>),
157}
158
159impl Deref for AttrValueRef<'_> {
160 type Target = AttrValue;
161
162 fn deref(&self) -> &AttrValue {
163 match self {
164 AttrValueRef::Direct(value) => value,
165 AttrValueRef::Borrowed(value) => value,
166 }
167 }
168}
169
170impl AsRef<str> for AttrValueRef<'_> {
171 fn as_ref(&self) -> &str {
172 self.deref()
173 }
174}
175
176#[derive(Clone, Copy)]
180pub(crate) enum AttrRef<'a> {
181 Raw(&'a ContentAttributeData),
183 Dom(&'a Attr),
185}
186
187impl<'a> AttrRef<'a> {
188 #[inline]
189 pub(crate) fn local_name(&self) -> &'a LocalName {
190 match self {
191 AttrRef::Raw(data) => data.local_name(),
192 AttrRef::Dom(attr) => attr.local_name(),
193 }
194 }
195
196 #[inline]
197 pub(crate) fn name(&self) -> &'a LocalName {
198 match self {
199 AttrRef::Raw(data) => data.name(),
200 AttrRef::Dom(attr) => attr.name(),
201 }
202 }
203
204 #[inline]
205 pub(crate) fn namespace(&self) -> &'a Namespace {
206 match self {
207 AttrRef::Raw(data) => data.namespace(),
208 AttrRef::Dom(attr) => attr.namespace(),
209 }
210 }
211
212 #[inline]
213 pub(crate) fn prefix(&self) -> Option<&'a Prefix> {
214 match self {
215 AttrRef::Raw(data) => data.prefix(),
216 AttrRef::Dom(attr) => attr.prefix(),
217 }
218 }
219
220 #[inline]
221 pub(crate) fn value(&self) -> AttrValueRef<'a> {
222 match self {
223 AttrRef::Raw(data) => AttrValueRef::Direct(data.value()),
224 AttrRef::Dom(attr) => AttrValueRef::Borrowed(attr.value()),
225 }
226 }
227
228 #[inline]
231 pub(crate) fn as_attr(&self) -> Option<&'a Attr> {
232 match self {
233 AttrRef::Dom(attr) => Some(attr),
234 AttrRef::Raw(_) => None,
235 }
236 }
237
238 pub(crate) fn to_dom_string(self) -> DOMString {
240 DOMString::from(&**self.value())
241 }
242
243 #[cfg(feature = "devtools")]
245 pub(crate) fn summarize(&self) -> devtools_traits::AttrInfo {
246 devtools_traits::AttrInfo {
247 namespace: (**self.namespace()).to_owned(),
248 name: (**self.name()).to_owned(),
249 value: (**self.value()).to_owned(),
250 }
251 }
252
253 pub(crate) fn as_identifier(&self) -> AttrIdentifier {
255 match self {
256 AttrRef::Raw(data) => data.identifier.as_identifier(),
257 AttrRef::Dom(attr) => attr.identifier().clone(),
258 }
259 }
260}
261
262#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
264#[derive(MallocSizeOf)]
265pub(crate) enum AttributeEntry {
266 Raw(ContentAttributeData),
268 Dom(Dom<Attr>),
270}
271
272impl AttributeEntry {
273 #[inline]
275 pub(crate) fn as_ref(&self) -> AttrRef<'_> {
276 match self {
277 AttributeEntry::Raw(data) => AttrRef::Raw(data),
278 AttributeEntry::Dom(attr) => AttrRef::Dom(attr),
279 }
280 }
281
282 #[expect(unsafe_code)]
284 #[inline]
285 pub(crate) fn value_for_layout(&self) -> &AttrValue {
286 match self {
287 AttributeEntry::Raw(data) => &data.value,
288 AttributeEntry::Dom(attr) => unsafe {
289 let value: LayoutDom<'_, _> = attr.to_layout();
290 value
291 }
292 .value(),
293 }
294 }
295
296 #[expect(unsafe_code)]
298 #[inline]
299 pub(crate) fn local_name_for_layout(&self) -> &LocalName {
300 match self {
301 AttributeEntry::Raw(data) => data.local_name(),
302 AttributeEntry::Dom(attr) => unsafe {
303 let value: LayoutDom<'_, _> = attr.to_layout();
304 value
305 }
306 .local_name(),
307 }
308 }
309
310 #[expect(unsafe_code)]
312 #[inline]
313 pub(crate) fn namespace_for_layout(&self) -> &Namespace {
314 match self {
315 AttributeEntry::Raw(data) => data.namespace(),
316 AttributeEntry::Dom(attr) => unsafe {
317 let value: LayoutDom<'_, _> = attr.to_layout();
318 value
319 }
320 .namespace(),
321 }
322 }
323}
324
325#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
329#[derive(Default, MallocSizeOf)]
330pub(crate) struct AttributeStorage(DomRefCell<Vec<AttributeEntry>>);
331
332pub(crate) struct AttributesBorrow<'a>(Ref<'a, Vec<AttributeEntry>>);
335
336impl<'a> AttributesBorrow<'a> {
337 #[inline]
339 pub(crate) fn iter(&self) -> impl Iterator<Item = AttrRef<'_>> + '_ {
340 self.0.iter().map(AttributeEntry::as_ref)
341 }
342
343 #[inline]
344 pub(crate) fn len(&self) -> usize {
345 self.0.len()
346 }
347
348 #[inline]
349 pub(crate) fn is_empty(&self) -> bool {
350 self.0.is_empty()
351 }
352
353 #[inline]
354 pub(crate) fn first(&self) -> Option<AttrRef<'_>> {
355 self.0.first().map(AttributeEntry::as_ref)
356 }
357
358 #[inline]
359 pub(crate) fn get(&self, index: usize) -> Option<AttrRef<'_>> {
360 self.0.get(index).map(AttributeEntry::as_ref)
361 }
362}
363
364impl AttributeStorage {
365 #[inline]
367 pub(crate) fn borrow(&self) -> AttributesBorrow<'_> {
368 AttributesBorrow(self.0.borrow())
369 }
370
371 #[expect(unsafe_code)]
373 #[inline]
374 pub(crate) unsafe fn borrow_for_layout(&self) -> &Vec<AttributeEntry> {
375 unsafe { self.0.borrow_for_layout() }
376 }
377
378 #[inline]
380 pub(crate) fn reserve_exact(&self, additional: usize) {
381 self.0.borrow_mut().reserve_exact(additional);
382 }
383
384 pub(crate) fn push_raw(&self, data: ContentAttributeData) {
386 self.0.borrow_mut().push(AttributeEntry::Raw(data));
387 }
388
389 pub(crate) fn push_dom(&self, attr: &Attr) {
391 self.0
392 .borrow_mut()
393 .push(AttributeEntry::Dom(Dom::from_ref(attr)));
394 }
395
396 pub(crate) fn remove(&self, index: usize) -> AttributeEntry {
398 self.0.borrow_mut().remove(index)
399 }
400
401 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
403 pub(crate) fn set(&self, index: usize, entry: AttributeEntry) {
404 self.0.borrow_mut()[index] = entry;
405 }
406
407 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
413 pub(crate) fn ensure_dom(
414 &self,
415 cx: &mut JSContext,
416 index: usize,
417 element: &Element,
418 ) -> DomRoot<Attr> {
419 if let AttributeEntry::Dom(attr) = &self.0.borrow()[index] {
421 return DomRoot::from_ref(&**attr);
422 }
423
424 let data = {
426 let mut entries = self.0.borrow_mut();
427 let placeholder = AttributeEntry::Raw(ContentAttributeData {
428 identifier: AttrName::Unqualified(html5ever::local_name!("")),
429 value: AttrValue::String(String::new()),
430 });
431 let old = std::mem::replace(&mut entries[index], placeholder);
432 match old {
433 AttributeEntry::Raw(data) => data,
434 _ => unreachable!(),
435 }
436 };
437
438 let doc = element.owner_document();
439 let (local_name, name, namespace, prefix) = data.identifier.into_parts();
440 let attr = Attr::new(
441 cx,
442 &doc,
443 local_name,
444 data.value,
445 name,
446 namespace,
447 prefix,
448 Some(element),
449 );
450
451 self.0.borrow_mut()[index] = AttributeEntry::Dom(Dom::from_ref(&*attr));
452 attr
453 }
454}
455
456#[expect(unsafe_code)]
459unsafe impl crate::dom::bindings::trace::JSTraceable for AttributeStorage {
460 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
461 for entry in self.0.borrow().iter() {
462 if let AttributeEntry::Dom(attr) = entry {
463 unsafe { js::rust::Trace::trace(attr, trc) };
464 }
465 }
466 }
467}