script/dom/element/attributes/
storage.rs1use std::cell::Ref;
6use std::ops::Deref;
7
8use devtools_traits::AttrInfo;
9use html5ever::{LocalName, Namespace, Prefix, ns};
10use js::context::JSContext;
11use script_bindings::cell::DomRefCell;
12use script_bindings::root::{Dom, DomRoot};
13use script_bindings::str::DOMString;
14use style::attr::{AttrIdentifier, AttrValue};
15use style::values::GenericAtomIdent;
16
17use crate::dom::attr::Attr;
18use crate::dom::bindings::root::{LayoutDom, ToLayout};
19use crate::dom::element::Element;
20use crate::dom::node::node::NodeTraits;
21
22static EMPTY_NAMESPACE: Namespace = ns!();
25
26#[derive(Clone, MallocSizeOf)]
28pub(crate) enum AttrName {
29 Unqualified(LocalName),
31 Qualified(Box<AttrIdentifier>),
33}
34
35impl AttrName {
36 pub(crate) fn new(
39 local_name: LocalName,
40 name: LocalName,
41 namespace: Namespace,
42 prefix: Option<Prefix>,
43 ) -> Self {
44 if prefix.is_none() && name == local_name && namespace == ns!() {
45 AttrName::Unqualified(local_name)
46 } else {
47 AttrName::Qualified(Box::new(AttrIdentifier {
48 local_name: GenericAtomIdent(local_name),
49 name: GenericAtomIdent(name),
50 namespace: GenericAtomIdent(namespace),
51 prefix: prefix.map(GenericAtomIdent),
52 }))
53 }
54 }
55
56 #[inline]
57 pub(crate) fn local_name(&self) -> &LocalName {
58 match self {
59 AttrName::Unqualified(local_name) => local_name,
60 AttrName::Qualified(identifier) => &identifier.local_name.0,
61 }
62 }
63
64 #[inline]
65 pub(crate) fn name(&self) -> &LocalName {
66 match self {
67 AttrName::Unqualified(local_name) => local_name,
68 AttrName::Qualified(identifier) => &identifier.name.0,
69 }
70 }
71
72 #[inline]
73 pub(crate) fn namespace(&self) -> &Namespace {
74 match self {
75 AttrName::Unqualified(_) => &EMPTY_NAMESPACE,
76 AttrName::Qualified(identifier) => &identifier.namespace.0,
77 }
78 }
79
80 #[inline]
81 pub(crate) fn prefix(&self) -> Option<&Prefix> {
82 match self {
83 AttrName::Unqualified(_) => None,
84 AttrName::Qualified(identifier) => Some(&identifier.prefix.as_ref()?.0),
85 }
86 }
87
88 pub(crate) fn into_parts(self) -> (LocalName, LocalName, Namespace, Option<Prefix>) {
90 match self {
91 AttrName::Unqualified(local_name) => (local_name.clone(), local_name, ns!(), None),
92 AttrName::Qualified(identifier) => {
93 let AttrIdentifier {
94 local_name,
95 name,
96 namespace,
97 prefix,
98 } = *identifier;
99 (local_name.0, name.0, namespace.0, prefix.map(|p| p.0))
100 },
101 }
102 }
103
104 pub(crate) fn as_identifier(&self) -> AttrIdentifier {
106 match self {
107 AttrName::Unqualified(local_name) => AttrIdentifier {
108 local_name: GenericAtomIdent(local_name.clone()),
109 name: GenericAtomIdent(local_name.clone()),
110 namespace: GenericAtomIdent(ns!()),
111 prefix: None,
112 },
113 AttrName::Qualified(identifier) => (**identifier).clone(),
114 }
115 }
116}
117
118#[derive(MallocSizeOf)]
120pub(crate) struct ContentAttributeData {
121 pub identifier: AttrName,
122 pub value: AttrValue,
123}
124
125impl ContentAttributeData {
126 #[inline]
127 pub(crate) fn local_name(&self) -> &LocalName {
128 self.identifier.local_name()
129 }
130
131 #[inline]
132 pub(crate) fn name(&self) -> &LocalName {
133 self.identifier.name()
134 }
135
136 #[inline]
137 pub(crate) fn namespace(&self) -> &Namespace {
138 self.identifier.namespace()
139 }
140
141 #[inline]
142 pub(crate) fn prefix(&self) -> Option<&Prefix> {
143 self.identifier.prefix()
144 }
145
146 #[inline]
147 pub(crate) fn value(&self) -> &AttrValue {
148 &self.value
149 }
150}
151
152pub(crate) enum AttrValueRef<'a> {
154 Direct(&'a AttrValue),
156 Borrowed(Ref<'a, AttrValue>),
158}
159
160impl Deref for AttrValueRef<'_> {
161 type Target = AttrValue;
162
163 fn deref(&self) -> &AttrValue {
164 match self {
165 AttrValueRef::Direct(value) => value,
166 AttrValueRef::Borrowed(value) => value,
167 }
168 }
169}
170
171impl AsRef<str> for AttrValueRef<'_> {
172 fn as_ref(&self) -> &str {
173 self.deref()
174 }
175}
176
177#[derive(Clone, Copy)]
181pub(crate) enum AttrRef<'a> {
182 Raw(&'a ContentAttributeData),
184 Dom(&'a Attr),
186}
187
188impl<'a> AttrRef<'a> {
189 #[inline]
190 pub(crate) fn local_name(&self) -> &'a LocalName {
191 match self {
192 AttrRef::Raw(data) => data.local_name(),
193 AttrRef::Dom(attr) => attr.local_name(),
194 }
195 }
196
197 #[inline]
198 pub(crate) fn name(&self) -> &'a LocalName {
199 match self {
200 AttrRef::Raw(data) => data.name(),
201 AttrRef::Dom(attr) => attr.name(),
202 }
203 }
204
205 #[inline]
206 pub(crate) fn namespace(&self) -> &'a Namespace {
207 match self {
208 AttrRef::Raw(data) => data.namespace(),
209 AttrRef::Dom(attr) => attr.namespace(),
210 }
211 }
212
213 #[inline]
214 pub(crate) fn prefix(&self) -> Option<&'a Prefix> {
215 match self {
216 AttrRef::Raw(data) => data.prefix(),
217 AttrRef::Dom(attr) => attr.prefix(),
218 }
219 }
220
221 #[inline]
222 pub(crate) fn value(&self) -> AttrValueRef<'a> {
223 match self {
224 AttrRef::Raw(data) => AttrValueRef::Direct(data.value()),
225 AttrRef::Dom(attr) => AttrValueRef::Borrowed(attr.value()),
226 }
227 }
228
229 #[inline]
232 pub(crate) fn as_attr(&self) -> Option<&'a Attr> {
233 match self {
234 AttrRef::Dom(attr) => Some(attr),
235 AttrRef::Raw(_) => None,
236 }
237 }
238
239 pub(crate) fn to_dom_string(self) -> DOMString {
241 DOMString::from(&**self.value())
242 }
243
244 pub(crate) fn summarize(&self) -> AttrInfo {
246 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}