aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/data.rs
blob: 9fc871c3bcf4b72b2513b434abda4647a85caed9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Per-node data used in style calculation.

use properties::ComputedValues;
use rule_tree::StrongRuleNode;
use selector_parser::PseudoElement;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;

type PseudoStylesInner = HashMap<PseudoElement, (Arc<ComputedValues>, StrongRuleNode),
                                 BuildHasherDefault<::fnv::FnvHasher>>;
#[derive(Clone, Debug)]
pub struct PseudoStyles(PseudoStylesInner);

impl PseudoStyles {
    pub fn empty() -> Self {
        PseudoStyles(HashMap::with_hasher(Default::default()))
    }
}

impl Deref for PseudoStyles {
    type Target = PseudoStylesInner;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl DerefMut for PseudoStyles {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

/// The styles associated with a node, including the styles for any
/// pseudo-elements.
#[derive(Clone, Debug)]
pub struct ElementStyles {
    /// The results of CSS styling for this node.
    pub primary: Arc<ComputedValues>,

    /// The rule node representing the last rule matched for this node.
    pub rule_node: StrongRuleNode,

    /// The results of CSS styling for each pseudo-element (if any).
    pub pseudos: PseudoStyles,
}

impl ElementStyles {
    pub fn new(primary: Arc<ComputedValues>, rule_node: StrongRuleNode) -> Self {
        ElementStyles {
            primary: primary,
            rule_node: rule_node,
            pseudos: PseudoStyles::empty(),
        }
    }
}

#[derive(Debug)]
enum ElementDataStyles {
    /// The field has not been initialized.
    Uninitialized,

    /// The field holds the previous style of the node. If this is None, the
    /// node has not been previously styled.
    ///
    /// This is the input to the styling algorithm. It would ideally be
    /// immutable, but for now we need to mutate it a bit before styling to
    /// handle animations.
    ///
    /// Note that since ElementStyles contains an Arc, the null pointer
    /// optimization prevents the Option<> here from consuming an extra word.
    Previous(Option<ElementStyles>),

    /// The field holds the current, up-to-date style.
    ///
    /// This is the output of the styling algorithm.
    Current(ElementStyles),
}

impl ElementDataStyles {
    fn is_previous(&self) -> bool {
        use self::ElementDataStyles::*;
        match *self {
            Previous(_) => true,
            _ => false,
        }
    }
}

/// Transient data used by the restyle algorithm. This structure is instantiated
/// either before or during restyle traversal, and is cleared at the end of node
/// processing.
#[derive(Debug)]
pub struct RestyleData {
    // FIXME(bholley): Start adding the fields from the algorithm doc.
    pub _dummy: u64,
}

impl RestyleData {
    fn new() -> Self {
        RestyleData {
            _dummy: 42,
        }
    }
}

/// Style system data associated with a node.
///
/// In Gecko, this hangs directly off a node, but is dropped when the frame takes
/// ownership of the computed style data.
///
/// In Servo, this is embedded inside of layout data, which itself hangs directly
/// off the node. Servo does not currently implement ownership transfer of the
/// computed style data to the frame.
///
/// In both cases, it is wrapped inside an AtomicRefCell to ensure thread
/// safety.
#[derive(Debug)]
pub struct ElementData {
    styles: ElementDataStyles,
    pub restyle_data: Option<RestyleData>,
}

impl ElementData {
    pub fn new() -> Self {
        ElementData {
            styles: ElementDataStyles::Uninitialized,
            restyle_data: None,
        }
    }

    pub fn has_current_styles(&self) -> bool {
        match self.styles {
            ElementDataStyles::Current(_) => true,
            _ => false,
        }
    }

    pub fn get_current_styles(&self) -> Option<&ElementStyles> {
        match self.styles {
            ElementDataStyles::Current(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn current_styles(&self) -> &ElementStyles {
        self.get_current_styles().expect("Calling current_styles before or during styling")
    }

    // Servo does lazy pseudo computation in layout and needs mutable access
    // to the current styles
    #[cfg(not(feature = "gecko"))]
    pub fn current_pseudos_mut(&mut self) -> &mut PseudoStyles {
        match self.styles {
            ElementDataStyles::Current(ref mut s) => &mut s.pseudos,
            _ => panic!("Calling current_pseudos_mut before or during styling"),
        }
    }

    pub fn previous_styles(&self) -> Option<&ElementStyles> {
        match self.styles {
            ElementDataStyles::Previous(ref s) => s.as_ref(),
            _ => panic!("Calling previous_styles without having gathered it"),
        }
    }

    pub fn previous_styles_mut(&mut self) -> Option<&mut ElementStyles> {
        match self.styles {
            ElementDataStyles::Previous(ref mut s) => s.as_mut(),
            _ => panic!("Calling previous_styles without having gathered it"),
        }
    }

    pub fn gather_previous_styles<F>(&mut self, f: F)
        where F: FnOnce() -> Option<ElementStyles>
    {
        use self::ElementDataStyles::*;
        self.styles = match mem::replace(&mut self.styles, Uninitialized) {
            Uninitialized => Previous(f()),
            Current(x) => Previous(Some(x)),
            Previous(x) => Previous(x),
        };
    }

    pub fn ensure_restyle_data(&mut self) {
        if self.restyle_data.is_none() {
            self.restyle_data = Some(RestyleData::new());
        }
    }

    pub fn finish_styling(&mut self, styles: ElementStyles) {
        debug_assert!(self.styles.is_previous());
        self.styles = ElementDataStyles::Current(styles);
        self.restyle_data = None;
    }
}