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::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 { attr.to_layout() }.value(),
185 }
186 }
187
188 #[expect(unsafe_code)]
190 #[inline]
191 pub(crate) fn local_name_for_layout(&self) -> &LocalName {
192 match self {
193 AttributeEntry::Raw(data) => data.local_name(),
194 AttributeEntry::Dom(attr) => unsafe { attr.to_layout() }.local_name(),
195 }
196 }
197
198 #[expect(unsafe_code)]
200 #[inline]
201 pub(crate) fn namespace_for_layout(&self) -> &Namespace {
202 match self {
203 AttributeEntry::Raw(data) => data.namespace(),
204 AttributeEntry::Dom(attr) => unsafe { attr.to_layout() }.namespace(),
205 }
206 }
207}
208
209#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
213#[derive(Default, MallocSizeOf)]
214pub(crate) struct AttributeStorage(DomRefCell<Vec<AttributeEntry>>);
215
216pub(crate) struct AttributesBorrow<'a>(Ref<'a, Vec<AttributeEntry>>);
219
220impl<'a> AttributesBorrow<'a> {
221 #[inline]
223 pub(crate) fn iter(&self) -> impl Iterator<Item = AttrRef<'_>> + '_ {
224 self.0.iter().map(AttributeEntry::as_ref)
225 }
226
227 #[inline]
228 pub(crate) fn len(&self) -> usize {
229 self.0.len()
230 }
231
232 #[inline]
233 pub(crate) fn is_empty(&self) -> bool {
234 self.0.is_empty()
235 }
236
237 #[inline]
238 pub(crate) fn first(&self) -> Option<AttrRef<'_>> {
239 self.0.first().map(AttributeEntry::as_ref)
240 }
241
242 #[inline]
243 pub(crate) fn get(&self, index: usize) -> Option<AttrRef<'_>> {
244 self.0.get(index).map(AttributeEntry::as_ref)
245 }
246}
247
248impl AttributeStorage {
249 #[inline]
251 pub(crate) fn borrow(&self) -> AttributesBorrow<'_> {
252 AttributesBorrow(self.0.borrow())
253 }
254
255 #[expect(unsafe_code)]
257 #[inline]
258 pub(crate) unsafe fn borrow_for_layout(&self) -> &Vec<AttributeEntry> {
259 unsafe { self.0.borrow_for_layout() }
260 }
261
262 pub(crate) fn push_raw(&self, data: ContentAttributeData) {
264 self.0.borrow_mut().push(AttributeEntry::Raw(data));
265 }
266
267 pub(crate) fn push_dom(&self, attr: &Attr) {
269 self.0
270 .borrow_mut()
271 .push(AttributeEntry::Dom(Dom::from_ref(attr)));
272 }
273
274 pub(crate) fn remove(&self, index: usize) -> AttributeEntry {
276 self.0.borrow_mut().remove(index)
277 }
278
279 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
281 pub(crate) fn set(&self, index: usize, entry: AttributeEntry) {
282 self.0.borrow_mut()[index] = entry;
283 }
284
285 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
291 pub(crate) fn ensure_dom(
292 &self,
293 cx: &mut JSContext,
294 index: usize,
295 element: &Element,
296 ) -> DomRoot<Attr> {
297 if let AttributeEntry::Dom(attr) = &self.0.borrow()[index] {
299 return DomRoot::from_ref(&**attr);
300 }
301
302 let data = {
304 let mut entries = self.0.borrow_mut();
305 let placeholder = AttributeEntry::Raw(ContentAttributeData {
306 identifier: AttrIdentifier {
307 local_name: style::values::GenericAtomIdent(html5ever::local_name!("")),
308 name: style::values::GenericAtomIdent(html5ever::local_name!("")),
309 namespace: style::values::GenericAtomIdent(html5ever::ns!()),
310 prefix: None,
311 },
312 value: AttrValue::String(String::new()),
313 });
314 let old = std::mem::replace(&mut entries[index], placeholder);
315 match old {
316 AttributeEntry::Raw(data) => data,
317 _ => unreachable!(),
318 }
319 };
320
321 let doc = element.owner_document();
322 let attr = Attr::new(
323 cx,
324 &doc,
325 data.identifier.local_name.0,
326 data.value,
327 data.identifier.name.0,
328 data.identifier.namespace.0,
329 data.identifier.prefix.map(|p| p.0),
330 Some(element),
331 );
332
333 self.0.borrow_mut()[index] = AttributeEntry::Dom(Dom::from_ref(&*attr));
334 attr
335 }
336}
337
338#[expect(unsafe_code)]
341unsafe impl crate::dom::bindings::trace::JSTraceable for AttributeStorage {
342 unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
343 for entry in self.0.borrow().iter() {
344 if let AttributeEntry::Dom(attr) = entry {
345 unsafe { js::rust::Trace::trace(attr, trc) };
346 }
347 }
348 }
349}