servo/components/style/selector_impl.rs
author Emilio Cobos Álvarez <ecoal95@gmail.com>
Wed, 10 Aug 2016 21:02:30 -0500
changeset 368499 48d70a3b372b6eefc472d3ac777bc9fd47b82343
parent 368488 db13dfd0cfa9ee8de452d6083c2d5bdda74585af
child 368774 02693af659c458d3e856dcbe278757bb2125868c
permissions -rw-r--r--
servo: Merge #12757 - stylo: Stop restyling display: none elements, remove the has_changed hack that made us use ReconstructFrame unconditionally (from emilio:stylo); r=bholley,pcwalton <!-- 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 <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> r? @bholley Source-Repo: https://github.com/servo/servo Source-Revision: 1b2450339c40dbcb65e94a346ea434d45f0edf90

/* 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/. */

//! The pseudo-classes and pseudo-elements supported by the style system.

use matching::{common_style_affecting_attributes, CommonStyleAffectingAttributeMode};
use restyle_hints;
use selectors::Element;
use selectors::parser::{AttrSelector, SelectorImpl};

pub type AttrValue = <TheSelectorImpl as SelectorImpl>::AttrValue;

#[cfg(feature = "servo")]
pub use servo_selector_impl::*;

#[cfg(feature = "servo")]
pub use servo_selector_impl::{ServoSelectorImpl as TheSelectorImpl, ServoElementSnapshot as ElementSnapshot};

#[cfg(feature = "gecko")]
pub use gecko_selector_impl::*;

#[cfg(feature = "gecko")]
pub use gecko_selector_impl::{GeckoSelectorImpl as TheSelectorImpl};

/// This function determines if a pseudo-element is eagerly cascaded or not.
///
/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
/// `::before` and `::after`). They inherit styles normally as another
/// selector would do, and they're part of the cascade.
///
/// Lazy pseudo-elements are affected by selector matching, but they're only
/// computed when needed, and not before. They're useful for general
/// pseudo-elements that are not very common.
///
/// Note that in Servo lazy pseudo-elements are restricted to a subset of
/// selectors, so you can't use it for public pseudo-elements. This is not the
/// case with Gecko though.
///
/// Precomputed ones skip the cascade process entirely, mostly as an
/// optimisation since they are private pseudo-elements (like
/// `::-servo-details-content`).
///
/// This pseudo-elements are resolved on the fly using *only* global rules
/// (rules of the form `*|*`), and applying them to the parent style.
///
/// If you're implementing a public selector that the end-user might customize,
/// then you probably need to make it eager.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PseudoElementCascadeType {
    Eager,
    Lazy,
    Precomputed,
}

impl PseudoElementCascadeType {
    #[inline]
    pub fn is_eager(&self) -> bool {
        *self == PseudoElementCascadeType::Eager
    }

    #[inline]
    pub fn is_lazy(&self) -> bool {
        *self == PseudoElementCascadeType::Lazy
    }

    #[inline]
    pub fn is_precomputed(&self) -> bool {
        *self == PseudoElementCascadeType::Precomputed
    }
}

pub trait ElementExt: Element<Impl=TheSelectorImpl> {
    type Snapshot: restyle_hints::ElementSnapshot + 'static;

    fn is_link(&self) -> bool;
}

impl TheSelectorImpl {
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement)
    {
        Self::each_pseudo_element(|pseudo| {
            if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
                fun(pseudo)
            }
        })
    }

    #[inline]
    pub fn each_precomputed_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement)
    {
        Self::each_pseudo_element(|pseudo| {
            if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
                fun(pseudo)
            }
        })
    }
}

pub fn attr_exists_selector_is_shareable(attr_selector: &AttrSelector<TheSelectorImpl>) -> bool {
    // NB(pcwalton): If you update this, remember to update the corresponding list in
    // `can_share_style_with()` as well.
    common_style_affecting_attributes().iter().any(|common_attr_info| {
        common_attr_info.atom == attr_selector.name && match common_attr_info.mode {
            CommonStyleAffectingAttributeMode::IsPresent(_) => true,
            CommonStyleAffectingAttributeMode::IsEqual(..) => false,
        }
    })
}

pub fn attr_equals_selector_is_shareable(attr_selector: &AttrSelector<TheSelectorImpl>,
                                         value: &AttrValue) -> bool {
    // FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
    // here because the UA style otherwise disables all style sharing completely.
    atom!("dir") == *value ||
    common_style_affecting_attributes().iter().any(|common_attr_info| {
        common_attr_info.atom == attr_selector.name && match common_attr_info.mode {
            CommonStyleAffectingAttributeMode::IsEqual(ref target_value, _) => {
                *target_value == *value
            }
            CommonStyleAffectingAttributeMode::IsPresent(_) => false,
        }
    })
}