style/
custom_properties_map.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! The structure that contains the custom properties of a given element.
6
7use crate::custom_properties::Name;
8use crate::properties_and_values::value::ComputedValue as ComputedRegisteredValue;
9use crate::selector_map::PrecomputedHasher;
10use indexmap::IndexMap;
11use servo_arc::Arc;
12use std::hash::BuildHasherDefault;
13use std::sync::LazyLock;
14
15/// A map for a set of custom properties, which implements copy-on-write behavior on insertion with
16/// cheap copying.
17#[derive(Clone, Debug, PartialEq)]
18pub struct CustomPropertiesMap(Arc<Inner>);
19
20impl Default for CustomPropertiesMap {
21    fn default() -> Self {
22        Self(EMPTY.clone())
23    }
24}
25
26/// We use None in the value to represent a removed entry.
27type OwnMap =
28    IndexMap<Name, Option<ComputedRegisteredValue>, BuildHasherDefault<PrecomputedHasher>>;
29
30static EMPTY: LazyLock<Arc<Inner>> = LazyLock::new(|| {
31    Arc::new_leaked(Inner {
32        own_properties: Default::default(),
33        parent: None,
34        len: 0,
35        ancestor_count: 0,
36    })
37});
38
39#[derive(Debug, Clone)]
40struct Inner {
41    own_properties: OwnMap,
42    parent: Option<Arc<Inner>>,
43    /// The number of custom properties we store. Note that this is different from the sum of our
44    /// own and our parent's length, since we might store duplicate entries.
45    len: usize,
46    /// The number of ancestors we have.
47    ancestor_count: u8,
48}
49
50/// A not-too-large, not too small ancestor limit, to prevent creating too-big chains.
51const ANCESTOR_COUNT_LIMIT: usize = 4;
52
53/// An iterator over the custom properties.
54pub struct Iter<'a> {
55    current: &'a Inner,
56    current_iter: indexmap::map::Iter<'a, Name, Option<ComputedRegisteredValue>>,
57    descendants: smallvec::SmallVec<[&'a Inner; ANCESTOR_COUNT_LIMIT]>,
58}
59
60impl<'a> Iterator for Iter<'a> {
61    type Item = (&'a Name, &'a Option<ComputedRegisteredValue>);
62
63    fn next(&mut self) -> Option<Self::Item> {
64        loop {
65            let (name, value) = match self.current_iter.next() {
66                Some(v) => v,
67                None => {
68                    let parent = self.current.parent.as_deref()?;
69                    self.descendants.push(self.current);
70                    self.current = parent;
71                    self.current_iter = parent.own_properties.iter();
72                    continue;
73                },
74            };
75            // If the property is overridden by a descendant we've already visited it.
76            for descendant in &self.descendants {
77                if descendant.own_properties.contains_key(name) {
78                    continue;
79                }
80            }
81            return Some((name, value));
82        }
83    }
84}
85
86impl PartialEq for Inner {
87    fn eq(&self, other: &Self) -> bool {
88        if self.len != other.len {
89            return false;
90        }
91        // NOTE(emilio): In order to speed up custom property comparison when tons of custom
92        // properties are involved, we return false in some cases where the ordering might be
93        // different, but the computed values end up being the same.
94        //
95        // This is a performance trade-off, on the assumption that if the ordering is different,
96        // there's likely a different value as well, but might over-invalidate.
97        //
98        // Doing the slow thing (checking all the keys) shows up a lot in profiles, see
99        // bug 1926423.
100        //
101        // Note that self.own_properties != other.own_properties is not the same, as by default
102        // IndexMap comparison is not order-aware.
103        if self.own_properties.as_slice() != other.own_properties.as_slice() {
104            return false;
105        }
106        self.parent == other.parent
107    }
108}
109
110impl Inner {
111    fn iter(&self) -> Iter<'_> {
112        Iter {
113            current: self,
114            current_iter: self.own_properties.iter(),
115            descendants: Default::default(),
116        }
117    }
118
119    fn is_empty(&self) -> bool {
120        self.len == 0
121    }
122
123    fn len(&self) -> usize {
124        self.len
125    }
126
127    fn get(&self, name: &Name) -> Option<&ComputedRegisteredValue> {
128        if let Some(p) = self.own_properties.get(name) {
129            return p.as_ref();
130        }
131        self.parent.as_ref()?.get(name)
132    }
133
134    fn insert(&mut self, name: &Name, value: Option<ComputedRegisteredValue>) {
135        let new = self.own_properties.insert(name.clone(), value).is_none();
136        if new && self.parent.as_ref().map_or(true, |p| p.get(name).is_none()) {
137            self.len += 1;
138        }
139    }
140
141    /// Whether we should expand the chain, or just copy-on-write.
142    fn should_expand_chain(&self) -> bool {
143        const SMALL_THRESHOLD: usize = 8;
144        if self.own_properties.len() <= SMALL_THRESHOLD {
145            return false; // Just copy, to avoid very long chains.
146        }
147        self.ancestor_count < ANCESTOR_COUNT_LIMIT as u8
148    }
149}
150
151impl CustomPropertiesMap {
152    /// Returns whether the map has no properties in it.
153    pub fn is_empty(&self) -> bool {
154        self.0.is_empty()
155    }
156
157    /// Returns the amount of different properties in the map.
158    pub fn len(&self) -> usize {
159        self.0.len()
160    }
161
162    /// Returns the property name and value at a given index.
163    pub fn get_index(&self, index: usize) -> Option<(&Name, &Option<ComputedRegisteredValue>)> {
164        if index >= self.len() {
165            return None;
166        }
167        // FIXME: This is O(n) which is a bit unfortunate.
168        self.0.iter().nth(index)
169    }
170
171    /// Returns a given property value by name.
172    pub fn get(&self, name: &Name) -> Option<&ComputedRegisteredValue> {
173        self.0.get(name)
174    }
175
176    fn do_insert(&mut self, name: &Name, value: Option<ComputedRegisteredValue>) {
177        if let Some(inner) = Arc::get_mut(&mut self.0) {
178            return inner.insert(name, value);
179        }
180        if self.get(name) == value.as_ref() {
181            return;
182        }
183        if !self.0.should_expand_chain() {
184            return Arc::make_mut(&mut self.0).insert(name, value);
185        }
186        let len = self.0.len;
187        let ancestor_count = self.0.ancestor_count + 1;
188        let mut new_inner = Inner {
189            own_properties: Default::default(),
190            // FIXME: Would be nice to avoid this clone.
191            parent: Some(self.0.clone()),
192            len,
193            ancestor_count,
194        };
195        new_inner.insert(name, value);
196        self.0 = Arc::new(new_inner);
197    }
198
199    /// Inserts an element in the map.
200    pub fn insert(&mut self, name: &Name, value: ComputedRegisteredValue) {
201        self.do_insert(name, Some(value))
202    }
203
204    /// Removes an element from the map.
205    pub fn remove(&mut self, name: &Name) {
206        self.do_insert(name, None)
207    }
208
209    /// Shrinks the map as much as possible.
210    pub fn shrink_to_fit(&mut self) {
211        if let Some(inner) = Arc::get_mut(&mut self.0) {
212            inner.own_properties.shrink_to_fit()
213        }
214    }
215
216    /// Return iterator to go through all properties.
217    pub fn iter(&self) -> Iter<'_> {
218        self.0.iter()
219    }
220}