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