gecko-dev/servo/components/style/data.rs
Simon Sapin 9c206a6cf3 servo: Merge #13202 - Rule tree, v1 (from emilio:rule-tree); r=SimonSapin,Manishearth,emilio
<!-- Please describe your changes on the following line: -->
---

<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors

<!-- Either: -->
- [x] There are tests for these changes OR

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

This patch introduces infrastructure for the rule tree, and constructs it.

We don't use it yet, nor have good heuristics for GC'ing it, but this should not
happen anymore once we store the rule node reference in the node.

I haven't messed up with memory orders because I want to do a try run with it,
then mess with them.

Source-Repo: https://github.com/servo/servo
Source-Revision: ac0475971bb24a63ca5d36d1d17e3036ddb99049
2016-11-05 17:11:24 -05:00

199 lines
5.9 KiB
Rust

/* 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_impl::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;
}
}