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