layout_api/pseudo_element_chain.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
5use std::fmt::Debug;
6
7use malloc_size_of_derive::MallocSizeOf;
8use style::selector_parser::PseudoElement;
9
10/// A chain of pseudo-elements up to two levels deep. This is used to represent cases
11/// where a pseudo-element has its own child pseudo element (for instance
12/// `.div::after::marker`). If both [`Self::primary`] and [`Self::secondary`] are `None`,
13/// then this chain represents the element itself. Not all combinations of pseudo-elements
14/// are possible and we may not be able to calculate a style for all
15/// [`PseudoElementChain`]s.
16#[derive(Clone, Copy, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq)]
17pub struct PseudoElementChain {
18 pub primary: Option<PseudoElement>,
19 pub secondary: Option<PseudoElement>,
20}
21
22impl PseudoElementChain {
23 pub fn unnested(pseudo_element: PseudoElement) -> Self {
24 Self {
25 primary: Some(pseudo_element),
26 secondary: None,
27 }
28 }
29
30 pub fn innermost(&self) -> Option<PseudoElement> {
31 self.secondary.or(self.primary)
32 }
33
34 /// Return a possibly nested [`PseudoElementChain`]. Currently only `::before` and
35 /// `::after` only support nesting. If the primary [`PseudoElement`] on the chain is
36 /// not `::before` or `::after` a single element chain is returned for the given
37 /// [`PseudoElement`].
38 pub fn with_pseudo(&self, pseudo_element: PseudoElement) -> Self {
39 match self.primary {
40 Some(primary) if primary.is_before_or_after() => Self {
41 primary: self.primary,
42 secondary: Some(pseudo_element),
43 },
44 _ => {
45 assert!(self.secondary.is_none());
46 Self::unnested(pseudo_element)
47 },
48 }
49 }
50
51 pub fn without_innermost(&self) -> Option<Self> {
52 let primary = self.primary?;
53 Some(
54 self.secondary
55 .map_or_else(Self::default, |_| Self::unnested(primary)),
56 )
57 }
58
59 pub fn is_empty(&self) -> bool {
60 self.primary.is_none()
61 }
62}