mirror of
https://github.com/servo/servo.git
synced 2025-08-08 06:55:31 +01:00
Auto merge of #16878 - emilio:pseudos, r=bholley,xidorn,hiro
stylo: Rework pseudo-elements to support pseudo selectors with state. <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16878) <!-- Reviewable:end -->
This commit is contained in:
commit
161dc666b0
28 changed files with 3111 additions and 1691 deletions
|
@ -20,6 +20,7 @@ use style::context::SharedStyleContext;
|
||||||
use style::data::ElementData;
|
use style::data::ElementData;
|
||||||
use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode};
|
use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode};
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
|
use style::element_state::ElementState;
|
||||||
use style::font_metrics::ServoMetricsProvider;
|
use style::font_metrics::ServoMetricsProvider;
|
||||||
use style::properties::{CascadeFlags, ServoComputedValues};
|
use style::properties::{CascadeFlags, ServoComputedValues};
|
||||||
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
|
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
|
||||||
|
@ -434,6 +435,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
&context.guards,
|
&context.guards,
|
||||||
unsafe { &self.unsafe_get() },
|
unsafe { &self.unsafe_get() },
|
||||||
&style_pseudo,
|
&style_pseudo,
|
||||||
|
ElementState::empty(),
|
||||||
data.styles().primary.values(),
|
data.styles().primary.values(),
|
||||||
&ServoMetricsProvider);
|
&ServoMetricsProvider);
|
||||||
data.styles_mut().cached_pseudos
|
data.styles_mut().cached_pseudos
|
||||||
|
|
|
@ -61,7 +61,7 @@ macro_rules! with_all_bounds {
|
||||||
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>;
|
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>;
|
||||||
|
|
||||||
/// pseudo-elements
|
/// pseudo-elements
|
||||||
type PseudoElement: $($CommonBounds)* + Sized + ToCss;
|
type PseudoElementSelector: $($CommonBounds)* + Sized + ToCss;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,8 @@ pub trait Parser {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pseudo_element(&self, _name: Cow<str>)
|
fn parse_pseudo_element(&self, _name: Cow<str>, _input: &mut CssParser)
|
||||||
-> Result<<Self::Impl as SelectorImpl>::PseudoElement, ()> {
|
-> Result<<Self::Impl as SelectorImpl>::PseudoElementSelector, ()> {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ impl<Impl: SelectorImpl> SelectorInner<Impl> {
|
||||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
pub struct Selector<Impl: SelectorImpl> {
|
pub struct Selector<Impl: SelectorImpl> {
|
||||||
pub inner: SelectorInner<Impl>,
|
pub inner: SelectorInner<Impl>,
|
||||||
pub pseudo_element: Option<Impl::PseudoElement>,
|
pub pseudo_element: Option<Impl::PseudoElementSelector>,
|
||||||
pub specificity: u32,
|
pub specificity: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,7 +816,7 @@ impl From<Specificity> for u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>,
|
fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>,
|
||||||
pseudo_element: Option<&Impl::PseudoElement>)
|
pseudo_element: Option<&Impl::PseudoElementSelector>)
|
||||||
-> u32
|
-> u32
|
||||||
where Impl: SelectorImpl {
|
where Impl: SelectorImpl {
|
||||||
let mut specificity = complex_selector_specificity(complex_selector);
|
let mut specificity = complex_selector_specificity(complex_selector);
|
||||||
|
@ -916,7 +916,7 @@ type ParseVec<Impl> = SmallVec<[Component<Impl>; 8]>;
|
||||||
fn parse_complex_selector_and_pseudo_element<P, Impl>(
|
fn parse_complex_selector_and_pseudo_element<P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser)
|
input: &mut CssParser)
|
||||||
-> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElement>), ()>
|
-> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElementSelector>), ()>
|
||||||
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
||||||
{
|
{
|
||||||
let mut sequence = ParseVec::new();
|
let mut sequence = ParseVec::new();
|
||||||
|
@ -1014,7 +1014,7 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
||||||
SimpleSelector(Component<Impl>),
|
SimpleSelector(Component<Impl>),
|
||||||
PseudoElement(Impl::PseudoElement),
|
PseudoElement(Impl::PseudoElementSelector),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// * `Err(())`: Invalid selector, abort
|
/// * `Err(())`: Invalid selector, abort
|
||||||
|
@ -1210,7 +1210,7 @@ fn parse_compound_selector<P, Impl>(
|
||||||
input: &mut CssParser,
|
input: &mut CssParser,
|
||||||
mut sequence: &mut ParseVec<Impl>,
|
mut sequence: &mut ParseVec<Impl>,
|
||||||
inside_negation: bool)
|
inside_negation: bool)
|
||||||
-> Result<Option<Impl::PseudoElement>, ()>
|
-> Result<Option<Impl::PseudoElementSelector>, ()>
|
||||||
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
||||||
{
|
{
|
||||||
// Consume any leading whitespace.
|
// Consume any leading whitespace.
|
||||||
|
@ -1335,7 +1335,7 @@ fn parse_one_simple_selector<P, Impl>(parser: &P,
|
||||||
name.eq_ignore_ascii_case("after") ||
|
name.eq_ignore_ascii_case("after") ||
|
||||||
name.eq_ignore_ascii_case("first-line") ||
|
name.eq_ignore_ascii_case("first-line") ||
|
||||||
name.eq_ignore_ascii_case("first-letter") {
|
name.eq_ignore_ascii_case("first-letter") {
|
||||||
let pseudo_element = P::parse_pseudo_element(parser, name)?;
|
let pseudo_element = P::parse_pseudo_element(parser, name, input)?;
|
||||||
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
|
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
|
||||||
} else {
|
} else {
|
||||||
let pseudo_class = parse_simple_pseudo_class(parser, name)?;
|
let pseudo_class = parse_simple_pseudo_class(parser, name)?;
|
||||||
|
@ -1351,7 +1351,7 @@ fn parse_one_simple_selector<P, Impl>(parser: &P,
|
||||||
Ok(Token::Colon) => {
|
Ok(Token::Colon) => {
|
||||||
match input.next_including_whitespace() {
|
match input.next_including_whitespace() {
|
||||||
Ok(Token::Ident(name)) => {
|
Ok(Token::Ident(name)) => {
|
||||||
let pseudo = P::parse_pseudo_element(parser, name)?;
|
let pseudo = P::parse_pseudo_element(parser, name, input)?;
|
||||||
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo)))
|
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo)))
|
||||||
}
|
}
|
||||||
_ => Err(())
|
_ => Err(())
|
||||||
|
@ -1455,7 +1455,7 @@ pub mod tests {
|
||||||
type BorrowedLocalName = DummyAtom;
|
type BorrowedLocalName = DummyAtom;
|
||||||
type BorrowedNamespaceUrl = DummyAtom;
|
type BorrowedNamespaceUrl = DummyAtom;
|
||||||
type NonTSPseudoClass = PseudoClass;
|
type NonTSPseudoClass = PseudoClass;
|
||||||
type PseudoElement = PseudoElement;
|
type PseudoElementSelector = PseudoElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Hash, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Hash, Clone, PartialEq, Eq)]
|
||||||
|
@ -1505,7 +1505,7 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pseudo_element(&self, name: Cow<str>)
|
fn parse_pseudo_element(&self, name: Cow<str>, input: &mut CssParser)
|
||||||
-> Result<PseudoElement, ()> {
|
-> Result<PseudoElement, ()> {
|
||||||
match_ignore_ascii_case! { &name,
|
match_ignore_ascii_case! { &name,
|
||||||
"before" => Ok(PseudoElement::Before),
|
"before" => Ok(PseudoElement::Before),
|
||||||
|
|
2
components/style/binding_tools/.gitignore
vendored
2
components/style/binding_tools/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
llvm/
|
|
||||||
rust-bindgen/
|
|
|
@ -1,7 +0,0 @@
|
||||||
# Geckolib tools
|
|
||||||
|
|
||||||
This directory contains simple tools for generating the Rust bindings for [stylo](https://public.etherpad-mozilla.org/p/stylo).
|
|
||||||
|
|
||||||
## `setup_bindgen.sh`
|
|
||||||
|
|
||||||
This clones Servo's version of bindgen, and uses `llvm-3.8` library to build it. It will then be used to generate the Rust bindings.
|
|
|
@ -1,30 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# 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/.
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o nounset
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Run in the tools directory.
|
|
||||||
cd "$(dirname ${0})"
|
|
||||||
|
|
||||||
# Setup and build bindgen.
|
|
||||||
if [[ "$(uname)" == "Linux" ]]; then
|
|
||||||
export LIBCLANG_PATH=/usr/lib/llvm-3.8/lib
|
|
||||||
else
|
|
||||||
export LIBCLANG_PATH="$(brew --prefix llvm38)/lib/llvm-3.8/lib"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make sure we have llvm-3.8.
|
|
||||||
if [[ ! -x "$(command -v clang-3.8)" ]]; then
|
|
||||||
echo "llvm-3.8 is required." \
|
|
||||||
"Mac users should |brew install llvm38|," \
|
|
||||||
"Linux users can find it in clang-3.8."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH="${LIBCLANG_PATH}"
|
|
||||||
export DYLD_LIBRARY_PATH="${LIBCLANG_PATH}"
|
|
|
@ -348,6 +348,8 @@ mod bindings {
|
||||||
"BORDER_COLOR_.*",
|
"BORDER_COLOR_.*",
|
||||||
"BORDER_STYLE_.*",
|
"BORDER_STYLE_.*",
|
||||||
"mozilla::SERVO_PREF_.*",
|
"mozilla::SERVO_PREF_.*",
|
||||||
|
"CSS_PSEUDO_ELEMENT_.*",
|
||||||
|
"SERVO_CSS_PSEUDO_ELEMENT_FLAGS_.*",
|
||||||
"kNameSpaceID_.*",
|
"kNameSpaceID_.*",
|
||||||
"kGenericFont_.*",
|
"kGenericFont_.*",
|
||||||
"kPresContext_.*",
|
"kPresContext_.*",
|
||||||
|
@ -672,6 +674,7 @@ mod bindings {
|
||||||
"RawGeckoURLExtraData",
|
"RawGeckoURLExtraData",
|
||||||
"RefPtr",
|
"RefPtr",
|
||||||
"CSSPseudoClassType",
|
"CSSPseudoClassType",
|
||||||
|
"CSSPseudoElementType",
|
||||||
"TraversalRestyleBehavior",
|
"TraversalRestyleBehavior",
|
||||||
"TraversalRootBehavior",
|
"TraversalRootBehavior",
|
||||||
"ComputedTimingFunction_BeforeFlag",
|
"ComputedTimingFunction_BeforeFlag",
|
||||||
|
@ -842,7 +845,7 @@ mod bindings {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_atoms() {
|
fn generate_atoms() {
|
||||||
let script = Path::new(file!()).parent().unwrap().join("binding_tools").join("regen_atoms.py");
|
let script = Path::new(file!()).parent().unwrap().join("gecko").join("regen_atoms.py");
|
||||||
println!("cargo:rerun-if-changed={}", script.display());
|
println!("cargo:rerun-if-changed={}", script.display());
|
||||||
let status = Command::new(&*PYTHON)
|
let status = Command::new(&*PYTHON)
|
||||||
.arg(&script)
|
.arg(&script)
|
||||||
|
|
|
@ -384,6 +384,13 @@ impl ElementData {
|
||||||
None => RestyleHint::empty(),
|
None => RestyleHint::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("compute_final_hint: {:?}, has_snapshot: {}, handled_snapshot: {}, \
|
||||||
|
pseudo: {:?}",
|
||||||
|
element,
|
||||||
|
element.has_snapshot(),
|
||||||
|
element.handled_snapshot(),
|
||||||
|
element.implemented_pseudo_element());
|
||||||
|
|
||||||
if element.has_snapshot() && !element.handled_snapshot() {
|
if element.has_snapshot() && !element.handled_snapshot() {
|
||||||
hint |= context.stylist.compute_restyle_hint(&element, context.snapshot_map);
|
hint |= context.stylist.compute_restyle_hint(&element, context.snapshot_map);
|
||||||
unsafe { element.set_handled_snapshot() }
|
unsafe { element.set_handled_snapshot() }
|
||||||
|
|
|
@ -26,6 +26,7 @@ use gecko_bindings::structs::RawGeckoServoStyleRuleList;
|
||||||
use gecko_bindings::structs::RawGeckoURLExtraData;
|
use gecko_bindings::structs::RawGeckoURLExtraData;
|
||||||
use gecko_bindings::structs::RefPtr;
|
use gecko_bindings::structs::RefPtr;
|
||||||
use gecko_bindings::structs::CSSPseudoClassType;
|
use gecko_bindings::structs::CSSPseudoClassType;
|
||||||
|
use gecko_bindings::structs::CSSPseudoElementType;
|
||||||
use gecko_bindings::structs::TraversalRestyleBehavior;
|
use gecko_bindings::structs::TraversalRestyleBehavior;
|
||||||
use gecko_bindings::structs::TraversalRootBehavior;
|
use gecko_bindings::structs::TraversalRootBehavior;
|
||||||
use gecko_bindings::structs::ComputedTimingFunction_BeforeFlag;
|
use gecko_bindings::structs::ComputedTimingFunction_BeforeFlag;
|
||||||
|
@ -905,7 +906,7 @@ extern "C" {
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed)
|
pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed)
|
||||||
-> *mut nsIAtom;
|
-> CSSPseudoElementType;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
||||||
|
@ -2217,7 +2218,7 @@ extern "C" {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
pub fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
||||||
ServoComputedValuesBorrowedOrNull,
|
ServoComputedValuesBorrowedOrNull,
|
||||||
pseudoTag: *mut nsIAtom,
|
pseudo_tag: *mut nsIAtom,
|
||||||
skip_display_fixup: bool,
|
skip_display_fixup: bool,
|
||||||
set:
|
set:
|
||||||
RawServoStyleSetBorrowed)
|
RawServoStyleSetBorrowed)
|
||||||
|
@ -2257,25 +2258,19 @@ extern "C" {
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
pub fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
pseudo_tag: *mut nsIAtom, is_probe: bool,
|
pseudo_type: CSSPseudoElementType,
|
||||||
|
is_probe: bool,
|
||||||
set: RawServoStyleSetBorrowed)
|
set: RawServoStyleSetBorrowed)
|
||||||
-> ServoComputedValuesStrong;
|
-> ServoComputedValuesStrong;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed,
|
pub fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed,
|
||||||
pseudo_tag: *mut nsIAtom,
|
|
||||||
set: RawServoStyleSetBorrowed)
|
|
||||||
-> RawServoRuleNodeStrong;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed,
|
|
||||||
element: RawGeckoElementBorrowed,
|
|
||||||
rule_type_mask: u32,
|
rule_type_mask: u32,
|
||||||
author_colors_allowed: bool) -> bool;
|
author_colors_allowed: bool) -> bool;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
pseudo_tag: *mut nsIAtom,
|
pseudo_type: CSSPseudoElementType,
|
||||||
snapshots:
|
snapshots:
|
||||||
*const ServoElementSnapshotTable,
|
*const ServoElementSnapshotTable,
|
||||||
set: RawServoStyleSetBorrowed)
|
set: RawServoStyleSetBorrowed)
|
||||||
|
@ -2299,8 +2294,8 @@ extern "C" {
|
||||||
RawGeckoElementBorrowed,
|
RawGeckoElementBorrowed,
|
||||||
snapshots:
|
snapshots:
|
||||||
*const ServoElementSnapshotTable,
|
*const ServoElementSnapshotTable,
|
||||||
pseudo_tag:
|
pseudo_type:
|
||||||
*mut nsIAtom)
|
CSSPseudoElementType)
|
||||||
-> ServoComputedValuesStrong;
|
-> ServoComputedValuesStrong;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
1502
components/style/gecko/generated/pseudo_element_definition.rs
Normal file
1502
components/style/gecko/generated/pseudo_element_definition.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,280 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Autogenerated file created by components/style/binding_tools/regen_atoms.py, DO NOT EDIT DIRECTLY */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file contains a helper macro invocation to aid Gecko's style system
|
|
||||||
* pseudo-element integration.
|
|
||||||
*
|
|
||||||
* This file is NOT INTENDED to be compiled as a standalone module.
|
|
||||||
*
|
|
||||||
* Also, it guarantees the property that normal pseudo-elements are processed
|
|
||||||
* before anonymous boxes.
|
|
||||||
*
|
|
||||||
* Expected usage is as follows:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* fn have_to_use_pseudo_elements() {
|
|
||||||
* macro_rules! pseudo_element {
|
|
||||||
* ($pseudo_str_with_colon:expr, $pseudo_atom:expr, $is_anon_box:true) => {{
|
|
||||||
* // Stuff stuff stuff.
|
|
||||||
* }}
|
|
||||||
* }
|
|
||||||
* include!("path/to/helper.rs")
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
pseudo_element!(":after",
|
|
||||||
atom!(":after"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":before",
|
|
||||||
atom!(":before"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":backdrop",
|
|
||||||
atom!(":backdrop"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":cue",
|
|
||||||
atom!(":cue"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":first-letter",
|
|
||||||
atom!(":first-letter"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":first-line",
|
|
||||||
atom!(":first-line"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-selection",
|
|
||||||
atom!(":-moz-selection"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-focus-inner",
|
|
||||||
atom!(":-moz-focus-inner"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-focus-outer",
|
|
||||||
atom!(":-moz-focus-outer"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-list-bullet",
|
|
||||||
atom!(":-moz-list-bullet"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-list-number",
|
|
||||||
atom!(":-moz-list-number"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-math-anonymous",
|
|
||||||
atom!(":-moz-math-anonymous"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-number-wrapper",
|
|
||||||
atom!(":-moz-number-wrapper"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-number-text",
|
|
||||||
atom!(":-moz-number-text"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-number-spin-box",
|
|
||||||
atom!(":-moz-number-spin-box"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-number-spin-up",
|
|
||||||
atom!(":-moz-number-spin-up"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-number-spin-down",
|
|
||||||
atom!(":-moz-number-spin-down"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-progress-bar",
|
|
||||||
atom!(":-moz-progress-bar"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-range-track",
|
|
||||||
atom!(":-moz-range-track"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-range-progress",
|
|
||||||
atom!(":-moz-range-progress"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-range-thumb",
|
|
||||||
atom!(":-moz-range-thumb"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-meter-bar",
|
|
||||||
atom!(":-moz-meter-bar"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-placeholder",
|
|
||||||
atom!(":-moz-placeholder"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":placeholder",
|
|
||||||
atom!(":placeholder"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-color-swatch",
|
|
||||||
atom!(":-moz-color-swatch"),
|
|
||||||
false);
|
|
||||||
pseudo_element!(":-moz-text",
|
|
||||||
atom!(":-moz-text"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-oof-placeholder",
|
|
||||||
atom!(":-moz-oof-placeholder"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-first-letter-continuation",
|
|
||||||
atom!(":-moz-first-letter-continuation"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-block-inside-inline-wrapper",
|
|
||||||
atom!(":-moz-block-inside-inline-wrapper"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-mathml-anonymous-block",
|
|
||||||
atom!(":-moz-mathml-anonymous-block"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-xul-anonymous-block",
|
|
||||||
atom!(":-moz-xul-anonymous-block"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-hframeset-border",
|
|
||||||
atom!(":-moz-hframeset-border"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-vframeset-border",
|
|
||||||
atom!(":-moz-vframeset-border"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-line-frame",
|
|
||||||
atom!(":-moz-line-frame"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-button-content",
|
|
||||||
atom!(":-moz-button-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-cell-content",
|
|
||||||
atom!(":-moz-cell-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-dropdown-list",
|
|
||||||
atom!(":-moz-dropdown-list"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-fieldset-content",
|
|
||||||
atom!(":-moz-fieldset-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-frameset-blank",
|
|
||||||
atom!(":-moz-frameset-blank"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-display-comboboxcontrol-frame",
|
|
||||||
atom!(":-moz-display-comboboxcontrol-frame"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-html-canvas-content",
|
|
||||||
atom!(":-moz-html-canvas-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-inline-table",
|
|
||||||
atom!(":-moz-inline-table"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table",
|
|
||||||
atom!(":-moz-table"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table-cell",
|
|
||||||
atom!(":-moz-table-cell"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table-column-group",
|
|
||||||
atom!(":-moz-table-column-group"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table-column",
|
|
||||||
atom!(":-moz-table-column"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table-wrapper",
|
|
||||||
atom!(":-moz-table-wrapper"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table-row-group",
|
|
||||||
atom!(":-moz-table-row-group"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-table-row",
|
|
||||||
atom!(":-moz-table-row"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-canvas",
|
|
||||||
atom!(":-moz-canvas"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-pagebreak",
|
|
||||||
atom!(":-moz-pagebreak"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-page",
|
|
||||||
atom!(":-moz-page"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-pagecontent",
|
|
||||||
atom!(":-moz-pagecontent"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-page-sequence",
|
|
||||||
atom!(":-moz-page-sequence"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-scrolled-content",
|
|
||||||
atom!(":-moz-scrolled-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-scrolled-canvas",
|
|
||||||
atom!(":-moz-scrolled-canvas"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-scrolled-page-sequence",
|
|
||||||
atom!(":-moz-scrolled-page-sequence"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-column-content",
|
|
||||||
atom!(":-moz-column-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-viewport",
|
|
||||||
atom!(":-moz-viewport"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-viewport-scroll",
|
|
||||||
atom!(":-moz-viewport-scroll"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-anonymous-flex-item",
|
|
||||||
atom!(":-moz-anonymous-flex-item"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-anonymous-grid-item",
|
|
||||||
atom!(":-moz-anonymous-grid-item"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-ruby",
|
|
||||||
atom!(":-moz-ruby"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-ruby-base",
|
|
||||||
atom!(":-moz-ruby-base"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-ruby-base-container",
|
|
||||||
atom!(":-moz-ruby-base-container"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-ruby-text",
|
|
||||||
atom!(":-moz-ruby-text"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-ruby-text-container",
|
|
||||||
atom!(":-moz-ruby-text-container"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-column",
|
|
||||||
atom!(":-moz-tree-column"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-row",
|
|
||||||
atom!(":-moz-tree-row"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-separator",
|
|
||||||
atom!(":-moz-tree-separator"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-cell",
|
|
||||||
atom!(":-moz-tree-cell"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-indentation",
|
|
||||||
atom!(":-moz-tree-indentation"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-line",
|
|
||||||
atom!(":-moz-tree-line"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-twisty",
|
|
||||||
atom!(":-moz-tree-twisty"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-image",
|
|
||||||
atom!(":-moz-tree-image"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-cell-text",
|
|
||||||
atom!(":-moz-tree-cell-text"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-checkbox",
|
|
||||||
atom!(":-moz-tree-checkbox"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-progressmeter",
|
|
||||||
atom!(":-moz-tree-progressmeter"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-tree-drop-feedback",
|
|
||||||
atom!(":-moz-tree-drop-feedback"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-svg-marker-anon-child",
|
|
||||||
atom!(":-moz-svg-marker-anon-child"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-svg-outer-svg-anon-child",
|
|
||||||
atom!(":-moz-svg-outer-svg-anon-child"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-svg-foreign-content",
|
|
||||||
atom!(":-moz-svg-foreign-content"),
|
|
||||||
true);
|
|
||||||
pseudo_element!(":-moz-svg-text",
|
|
||||||
atom!(":-moz-svg-text"),
|
|
||||||
true);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@ pub mod conversions;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod global_style_data;
|
pub mod global_style_data;
|
||||||
pub mod media_queries;
|
pub mod media_queries;
|
||||||
|
pub mod pseudo_element;
|
||||||
pub mod restyle_damage;
|
pub mod restyle_damage;
|
||||||
pub mod rules;
|
pub mod rules;
|
||||||
pub mod selector_parser;
|
pub mod selector_parser;
|
||||||
|
|
91
components/style/gecko/pseudo_element.rs
Normal file
91
components/style/gecko/pseudo_element.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! Gecko's definition of a pseudo-element.
|
||||||
|
//!
|
||||||
|
//! Note that a few autogenerated bits of this live in
|
||||||
|
//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
|
||||||
|
//! need to update the checked-in files for Servo.
|
||||||
|
|
||||||
|
use cssparser::ToCss;
|
||||||
|
use gecko_bindings::structs::{self, CSSPseudoElementType};
|
||||||
|
use selector_parser::PseudoElementCascadeType;
|
||||||
|
use std::fmt;
|
||||||
|
use string_cache::Atom;
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs"));
|
||||||
|
|
||||||
|
impl PseudoElement {
|
||||||
|
/// Returns the kind of cascade type that a given pseudo is going to use.
|
||||||
|
///
|
||||||
|
/// In Gecko we only compute ::before and ::after eagerly. We save the rules
|
||||||
|
/// for anonymous boxes separately, so we resolve them as precomputed
|
||||||
|
/// pseudos.
|
||||||
|
///
|
||||||
|
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
|
||||||
|
pub fn cascade_type(&self) -> PseudoElementCascadeType {
|
||||||
|
if self.is_eager() {
|
||||||
|
debug_assert!(!self.is_anon_box());
|
||||||
|
return PseudoElementCascadeType::Eager
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_anon_box() {
|
||||||
|
return PseudoElementCascadeType::Precomputed
|
||||||
|
}
|
||||||
|
|
||||||
|
PseudoElementCascadeType::Lazy
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
||||||
|
#[inline]
|
||||||
|
pub fn eager_index(&self) -> usize {
|
||||||
|
EAGER_PSEUDOS.iter().position(|p| p == self)
|
||||||
|
.expect("Not an eager pseudo")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a pseudo-element from an eager index.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_eager_index(i: usize) -> Self {
|
||||||
|
EAGER_PSEUDOS[i].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is ::before or ::after.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_before_or_after(&self) -> bool {
|
||||||
|
matches!(*self, PseudoElement::Before | PseudoElement::After)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is lazily-cascaded.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_lazy(&self) -> bool {
|
||||||
|
!self.is_eager() && !self.is_precomputed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is web-exposed.
|
||||||
|
pub fn exposed_in_non_ua_sheets(&self) -> bool {
|
||||||
|
if self.is_anon_box() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element supports user action selectors.
|
||||||
|
pub fn supports_user_action_state(&self) -> bool {
|
||||||
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is precomputed.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_precomputed(&self) -> bool {
|
||||||
|
self.is_anon_box()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for PseudoElement {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
dest.write_char(':')?;
|
||||||
|
dest.write_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
138
components/style/gecko/pseudo_element_definition.mako.rs
Normal file
138
components/style/gecko/pseudo_element_definition.mako.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/// Gecko's pseudo-element definition.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PseudoElement {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
/// ${pseudo.value}
|
||||||
|
${pseudo.capitalized()},
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
<% EAGER_PSEUDOS = ["Before", "After"] %>
|
||||||
|
|
||||||
|
/// The number of eager pseudo-elements.
|
||||||
|
pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
|
||||||
|
|
||||||
|
/// The list of eager pseudos.
|
||||||
|
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
|
||||||
|
% for eager_pseudo_name in EAGER_PSEUDOS:
|
||||||
|
PseudoElement::${eager_pseudo_name},
|
||||||
|
% endfor
|
||||||
|
];
|
||||||
|
|
||||||
|
impl PseudoElement {
|
||||||
|
/// Executes a closure with each pseudo-element as an argument.
|
||||||
|
pub fn each<F>(mut fun: F)
|
||||||
|
where F: FnMut(Self),
|
||||||
|
{
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
fun(PseudoElement::${pseudo.capitalized()});
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the pseudo-element as an atom.
|
||||||
|
#[inline]
|
||||||
|
pub fn atom(&self) -> Atom {
|
||||||
|
match *self {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
PseudoElement::${pseudo.capitalized()} => atom!("${pseudo.value}"),
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is an anonymous box.
|
||||||
|
#[inline]
|
||||||
|
fn is_anon_box(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
PseudoElement::${pseudo.capitalized()} => ${str(pseudo.is_anon_box()).lower()},
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is eagerly-cascaded.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_eager(&self) -> bool {
|
||||||
|
matches!(*self,
|
||||||
|
${" | ".join(map(lambda name: "PseudoElement::{}".format(name), EAGER_PSEUDOS))})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the flags associated to this pseudo-element, or 0 if it's an
|
||||||
|
/// anonymous box.
|
||||||
|
pub fn flags(&self) -> u32 {
|
||||||
|
match *self {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
PseudoElement::${pseudo.capitalized()} => {
|
||||||
|
% if pseudo.is_anon_box():
|
||||||
|
0
|
||||||
|
% else:
|
||||||
|
structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.original_ident}
|
||||||
|
% endif
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a pseudo-element from a `CSSPseudoElementType`.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_pseudo_type(type_: CSSPseudoElementType) -> Option<Self> {
|
||||||
|
match type_ {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
% if not pseudo.is_anon_box():
|
||||||
|
CSSPseudoElementType::${pseudo.original_ident} => {
|
||||||
|
Some(PseudoElement::${pseudo.capitalized()})
|
||||||
|
},
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a pseudo-element from an anonymous box `Atom`.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_anon_box_atom(atom: &Atom) -> Option<Self> {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
% if pseudo.is_anon_box():
|
||||||
|
if atom == &atom!("${pseudo.value}") {
|
||||||
|
return Some(PseudoElement::${pseudo.capitalized()});
|
||||||
|
}
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs an atom from a string of text, and whether we're in a
|
||||||
|
/// user-agent stylesheet.
|
||||||
|
///
|
||||||
|
/// If we're not in a user-agent stylesheet, we will never parse anonymous
|
||||||
|
/// box pseudo-elements.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the pseudo-element is not recognised.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
if in_ua_stylesheet || PseudoElement::${pseudo.capitalized()}.exposed_in_non_ua_sheets() {
|
||||||
|
if s.eq_ignore_ascii_case("${pseudo.value[1:]}") {
|
||||||
|
return Some(PseudoElement::${pseudo.capitalized()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the pseudo-element's definition as a string, with only one colon
|
||||||
|
/// before it.
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
% for pseudo in PSEUDOS:
|
||||||
|
PseudoElement::${pseudo.capitalized()} => "${pseudo.value}",
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,10 @@ import sys
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
GECKO_DIR = os.path.dirname(__file__.replace('\\', '/'))
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
|
||||||
|
|
||||||
|
import build
|
||||||
|
|
||||||
PRELUDE = """
|
PRELUDE = """
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
@ -74,7 +78,7 @@ def map_atom(ident):
|
||||||
class Atom:
|
class Atom:
|
||||||
def __init__(self, source, ident, value):
|
def __init__(self, source, ident, value):
|
||||||
self.ident = "{}_{}".format(source.CLASS, ident)
|
self.ident = "{}_{}".format(source.CLASS, ident)
|
||||||
self._original_ident = ident
|
self.original_ident = ident
|
||||||
self.value = value
|
self.value = value
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
||||||
|
@ -82,17 +86,23 @@ class Atom:
|
||||||
return self.source.CLASS
|
return self.source.CLASS
|
||||||
|
|
||||||
def gnu_symbol(self):
|
def gnu_symbol(self):
|
||||||
return gnu_symbolify(self.source, self._original_ident)
|
return gnu_symbolify(self.source, self.original_ident)
|
||||||
|
|
||||||
def msvc32_symbol(self):
|
def msvc32_symbol(self):
|
||||||
return msvc32_symbolify(self.source, self._original_ident)
|
return msvc32_symbolify(self.source, self.original_ident)
|
||||||
|
|
||||||
def msvc64_symbol(self):
|
def msvc64_symbol(self):
|
||||||
return msvc64_symbolify(self.source, self._original_ident)
|
return msvc64_symbolify(self.source, self.original_ident)
|
||||||
|
|
||||||
def type(self):
|
def type(self):
|
||||||
return self.source.TYPE
|
return self.source.TYPE
|
||||||
|
|
||||||
|
def capitalized(self):
|
||||||
|
return self.original_ident[0].upper() + self.original_ident[1:]
|
||||||
|
|
||||||
|
def is_anon_box(self):
|
||||||
|
return self.type() == "nsICSSAnonBoxPseudo"
|
||||||
|
|
||||||
|
|
||||||
def collect_atoms(objdir):
|
def collect_atoms(objdir):
|
||||||
atoms = []
|
atoms = []
|
||||||
|
@ -211,56 +221,24 @@ def write_atom_macro(atoms, file_name):
|
||||||
f.write(MACRO.format('\n'.join(macro_rules)))
|
f.write(MACRO.format('\n'.join(macro_rules)))
|
||||||
|
|
||||||
|
|
||||||
PSEUDO_ELEMENT_HEADER = """
|
def write_pseudo_elements(atoms, target_filename):
|
||||||
/*
|
pseudos = []
|
||||||
* This file contains a helper macro invocation to aid Gecko's style system
|
|
||||||
* pseudo-element integration.
|
|
||||||
*
|
|
||||||
* This file is NOT INTENDED to be compiled as a standalone module.
|
|
||||||
*
|
|
||||||
* Also, it guarantees the property that normal pseudo-elements are processed
|
|
||||||
* before anonymous boxes.
|
|
||||||
*
|
|
||||||
* Expected usage is as follows:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* fn have_to_use_pseudo_elements() {
|
|
||||||
* macro_rules! pseudo_element {
|
|
||||||
* ($pseudo_str_with_colon:expr, $pseudo_atom:expr, $is_anon_box:true) => {{
|
|
||||||
* // Stuff stuff stuff.
|
|
||||||
* }}
|
|
||||||
* }
|
|
||||||
* include!("path/to/helper.rs")
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
"""
|
|
||||||
|
|
||||||
PSEUDO_ELEMENT_MACRO_INVOCATION = """
|
|
||||||
pseudo_element!(\"{}\",
|
|
||||||
atom!(\"{}\"),
|
|
||||||
{});
|
|
||||||
"""[1:]
|
|
||||||
|
|
||||||
|
|
||||||
def write_pseudo_element_helper(atoms, target_filename):
|
|
||||||
with FileAvoidWrite(target_filename) as f:
|
|
||||||
f.write(PRELUDE)
|
|
||||||
f.write(PSEUDO_ELEMENT_HEADER)
|
|
||||||
f.write("{\n")
|
|
||||||
for atom in atoms:
|
for atom in atoms:
|
||||||
if atom.type() == "nsICSSPseudoElement":
|
if atom.type() == "nsICSSPseudoElement" or atom.type() == "nsICSSAnonBoxPseudo":
|
||||||
f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "false"))
|
pseudos.append(atom)
|
||||||
elif atom.type() == "nsICSSAnonBoxPseudo":
|
|
||||||
f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "true"))
|
pseudo_definition_template = os.path.join(GECKO_DIR, "pseudo_element_definition.mako.rs")
|
||||||
f.write("}\n")
|
print("cargo:rerun-if-changed={}".format(pseudo_definition_template))
|
||||||
|
contents = build.render(pseudo_definition_template, PSEUDOS=pseudos)
|
||||||
|
|
||||||
|
with FileAvoidWrite(target_filename) as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
def generate_atoms(dist, out):
|
def generate_atoms(dist, out):
|
||||||
atoms = collect_atoms(dist)
|
atoms = collect_atoms(dist)
|
||||||
write_atom_macro(atoms, os.path.join(out, "atom_macro.rs"))
|
write_atom_macro(atoms, os.path.join(out, "atom_macro.rs"))
|
||||||
write_pseudo_element_helper(atoms, os.path.join(out, "pseudo_element_helper.rs"))
|
write_pseudo_elements(atoms, os.path.join(out, "pseudo_element_definition.rs"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
|
@ -5,215 +5,19 @@
|
||||||
//! Gecko-specific bits for selector-parsing.
|
//! Gecko-specific bits for selector-parsing.
|
||||||
|
|
||||||
use cssparser::{Parser, ToCss};
|
use cssparser::{Parser, ToCss};
|
||||||
|
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
use gecko_bindings::structs::CSSPseudoClassType;
|
use gecko_bindings::structs::CSSPseudoClassType;
|
||||||
use gecko_bindings::structs::nsIAtom;
|
|
||||||
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
||||||
use selectors::parser::{ComplexSelector, SelectorMethods};
|
use selectors::parser::{ComplexSelector, SelectorMethods};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ptr;
|
|
||||||
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||||
|
|
||||||
|
pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
|
||||||
pub use gecko::snapshot::SnapshotMap;
|
pub use gecko::snapshot::SnapshotMap;
|
||||||
|
|
||||||
/// A representation of a CSS pseudo-element.
|
|
||||||
///
|
|
||||||
/// In Gecko, we represent pseudo-elements as plain `Atom`s.
|
|
||||||
///
|
|
||||||
/// The boolean field represents whether this element is an anonymous box. This
|
|
||||||
/// is just for convenience, instead of recomputing it.
|
|
||||||
///
|
|
||||||
/// Also, note that the `Atom` member is always a static atom, so if space is a
|
|
||||||
/// concern, we can use the raw pointer and use the lower bit to represent it
|
|
||||||
/// without space overhead.
|
|
||||||
///
|
|
||||||
/// FIXME(emilio): we know all these atoms are static. Patches are starting to
|
|
||||||
/// pile up, but a further potential optimisation is generating bindings without
|
|
||||||
/// `-no-gen-bitfield-methods` (that was removed to compile on stable, but it no
|
|
||||||
/// longer depends on it), and using the raw *mut nsIAtom (properly asserting
|
|
||||||
/// we're a static atom).
|
|
||||||
///
|
|
||||||
/// This should allow us to avoid random FFI overhead when cloning/dropping
|
|
||||||
/// pseudos.
|
|
||||||
///
|
|
||||||
/// Also, we can further optimize PartialEq and hash comparing/hashing only the
|
|
||||||
/// atoms.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct PseudoElement(Atom, bool);
|
|
||||||
|
|
||||||
/// List of eager pseudos. Keep this in sync with the count below.
|
|
||||||
macro_rules! each_eager_pseudo {
|
|
||||||
($macro_name:ident, $atom_macro:ident) => {
|
|
||||||
$macro_name!($atom_macro!(":after"), 0);
|
|
||||||
$macro_name!($atom_macro!(":before"), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The number of eager pseudo-elements (just ::before and ::after).
|
|
||||||
pub const EAGER_PSEUDO_COUNT: usize = 2;
|
|
||||||
|
|
||||||
|
|
||||||
impl PseudoElement {
|
|
||||||
/// Returns the kind of cascade type that a given pseudo is going to use.
|
|
||||||
///
|
|
||||||
/// In Gecko we only compute ::before and ::after eagerly. We save the rules
|
|
||||||
/// for anonymous boxes separately, so we resolve them as precomputed
|
|
||||||
/// pseudos.
|
|
||||||
///
|
|
||||||
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
|
|
||||||
pub fn cascade_type(&self) -> PseudoElementCascadeType {
|
|
||||||
if self.is_eager() {
|
|
||||||
debug_assert!(!self.is_anon_box());
|
|
||||||
return PseudoElementCascadeType::Eager
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_anon_box() {
|
|
||||||
return PseudoElementCascadeType::Precomputed
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoElementCascadeType::Lazy
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
|
||||||
#[inline]
|
|
||||||
pub fn eager_index(&self) -> usize {
|
|
||||||
macro_rules! case {
|
|
||||||
($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return $idx; } }
|
|
||||||
}
|
|
||||||
each_eager_pseudo!(case, atom);
|
|
||||||
panic!("Not eager")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a pseudo-element from an eager index.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_eager_index(i: usize) -> Self {
|
|
||||||
macro_rules! case {
|
|
||||||
($atom:expr, $idx:expr) => { if i == $idx { return PseudoElement($atom, false); } }
|
|
||||||
}
|
|
||||||
each_eager_pseudo!(case, atom);
|
|
||||||
panic!("Not eager")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the pseudo-element as an atom.
|
|
||||||
#[inline]
|
|
||||||
pub fn as_atom(&self) -> &Atom {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this pseudo-element is an anonymous box.
|
|
||||||
#[inline]
|
|
||||||
fn is_anon_box(&self) -> bool {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this pseudo-element is ::before or ::after.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_before_or_after(&self) -> bool {
|
|
||||||
*self.as_atom() == atom!(":before") ||
|
|
||||||
*self.as_atom() == atom!(":after")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this pseudo-element is eagerly-cascaded.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_eager(&self) -> bool {
|
|
||||||
macro_rules! case {
|
|
||||||
($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return true; } }
|
|
||||||
}
|
|
||||||
each_eager_pseudo!(case, atom);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this pseudo-element is lazily-cascaded.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_lazy(&self) -> bool {
|
|
||||||
!self.is_eager() && !self.is_precomputed()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this pseudo-element is precomputed.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_precomputed(&self) -> bool {
|
|
||||||
self.is_anon_box()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a pseudo-element from an `Atom`, receiving whether it is also
|
|
||||||
/// an anonymous box, and don't check it on release builds.
|
|
||||||
///
|
|
||||||
/// On debug builds we assert it's the result we expect.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_atom_unchecked(atom: Atom, is_anon_box: bool) -> Self {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
// Do the check on debug regardless.
|
|
||||||
match Self::from_atom(&*atom, true) {
|
|
||||||
Some(pseudo) => {
|
|
||||||
assert_eq!(pseudo.is_anon_box(), is_anon_box);
|
|
||||||
return pseudo;
|
|
||||||
}
|
|
||||||
None => panic!("Unknown pseudo: {:?}", atom),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoElement(atom, is_anon_box)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_atom(atom: &WeakAtom, _in_ua: bool) -> Option<Self> {
|
|
||||||
macro_rules! pseudo_element {
|
|
||||||
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
|
|
||||||
if atom == &*$atom {
|
|
||||||
return Some(PseudoElement($atom, $is_anon_box));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_helper.rs"));
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an atom from a string of text, and whether we're in a
|
|
||||||
/// user-agent stylesheet.
|
|
||||||
///
|
|
||||||
/// If we're not in a user-agent stylesheet, we will never parse anonymous
|
|
||||||
/// box pseudo-elements.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the pseudo-element is not recognised.
|
|
||||||
#[inline]
|
|
||||||
fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
|
|
||||||
use std::ascii::AsciiExt;
|
|
||||||
macro_rules! pseudo_element {
|
|
||||||
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
|
|
||||||
if !$is_anon_box || in_ua_stylesheet {
|
|
||||||
if s.eq_ignore_ascii_case(&$pseudo_str_with_colon[1..]) {
|
|
||||||
return Some(PseudoElement($atom, $is_anon_box))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_helper.rs"));
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns null or nsIAtom pointer corresponding to a given PseudoElement.
|
|
||||||
#[inline]
|
|
||||||
pub fn ns_atom_or_null_from_opt(pseudo: Option<&PseudoElement>) -> *mut nsIAtom {
|
|
||||||
pseudo.map(|p| p.as_atom().as_ptr()).unwrap_or(ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for PseudoElement {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
// FIXME: why does the atom contain one colon? Pseudo-element has two
|
|
||||||
debug_assert!(self.0.as_slice().starts_with(&[b':' as u16]) &&
|
|
||||||
!self.0.as_slice().starts_with(&[b':' as u16, b':' as u16]));
|
|
||||||
try!(dest.write_char(':'));
|
|
||||||
write!(dest, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
flags NonTSPseudoClassFlag: u8 {
|
flags NonTSPseudoClassFlag: u8 {
|
||||||
// See NonTSPseudoClass::is_internal()
|
// See NonTSPseudoClass::is_internal()
|
||||||
|
@ -325,6 +129,15 @@ impl NonTSPseudoClass {
|
||||||
apply_non_ts_list!(pseudo_class_check_internal)
|
apply_non_ts_list!(pseudo_class_check_internal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos
|
||||||
|
///
|
||||||
|
/// We intentionally skip the link-related ones.
|
||||||
|
fn is_safe_user_action_state(&self) -> bool {
|
||||||
|
matches!(*self, NonTSPseudoClass::Hover |
|
||||||
|
NonTSPseudoClass::Active |
|
||||||
|
NonTSPseudoClass::Focus)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the state flag associated with a pseudo-class, if any.
|
/// Get the state flag associated with a pseudo-class, if any.
|
||||||
pub fn state_flag(&self) -> ElementState {
|
pub fn state_flag(&self) -> ElementState {
|
||||||
macro_rules! flag {
|
macro_rules! flag {
|
||||||
|
@ -375,6 +188,58 @@ impl NonTSPseudoClass {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct SelectorImpl;
|
pub struct SelectorImpl;
|
||||||
|
|
||||||
|
/// Some subset of pseudo-elements in Gecko are sensitive to some state
|
||||||
|
/// selectors.
|
||||||
|
///
|
||||||
|
/// We store the sensitive states in this struct in order to properly handle
|
||||||
|
/// these.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct PseudoElementSelector {
|
||||||
|
pseudo: PseudoElement,
|
||||||
|
state: ElementState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PseudoElementSelector {
|
||||||
|
/// Returns the pseudo-element this selector represents.
|
||||||
|
pub fn pseudo_element(&self) -> &PseudoElement {
|
||||||
|
&self.pseudo
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the pseudo-element selector state.
|
||||||
|
pub fn state(&self) -> ElementState {
|
||||||
|
self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for PseudoElementSelector {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
let mut state = self.state;
|
||||||
|
state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
|
||||||
|
assert_eq!(state, ElementState::empty(),
|
||||||
|
"Unhandled pseudo-element state selector?");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pseudo.to_css(dest)?;
|
||||||
|
|
||||||
|
if self.state.contains(IN_HOVER_STATE) {
|
||||||
|
dest.write_str(":hover")?
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.state.contains(IN_ACTIVE_STATE) {
|
||||||
|
dest.write_str(":active")?
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.state.contains(IN_FOCUS_STATE) {
|
||||||
|
dest.write_str(":focus")?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||||
type AttrValue = Atom;
|
type AttrValue = Atom;
|
||||||
type Identifier = Atom;
|
type Identifier = Atom;
|
||||||
|
@ -385,7 +250,7 @@ impl ::selectors::SelectorImpl for SelectorImpl {
|
||||||
type BorrowedNamespaceUrl = WeakNamespace;
|
type BorrowedNamespaceUrl = WeakNamespace;
|
||||||
type BorrowedLocalName = WeakAtom;
|
type BorrowedLocalName = WeakAtom;
|
||||||
|
|
||||||
type PseudoElement = PseudoElement;
|
type PseudoElementSelector = PseudoElementSelector;
|
||||||
type NonTSPseudoClass = NonTSPseudoClass;
|
type NonTSPseudoClass = NonTSPseudoClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,11 +312,38 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
|
fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
|
||||||
|
let pseudo =
|
||||||
match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
|
match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
|
||||||
Some(pseudo) => Ok(pseudo),
|
Some(pseudo) => pseudo,
|
||||||
None => Err(()),
|
None => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = if pseudo.supports_user_action_state() {
|
||||||
|
input.try(|input| {
|
||||||
|
let mut state = ElementState::empty();
|
||||||
|
|
||||||
|
while !input.is_exhausted() {
|
||||||
|
input.expect_colon()?;
|
||||||
|
let ident = input.expect_ident()?;
|
||||||
|
let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;
|
||||||
|
|
||||||
|
if !pseudo_class.is_safe_user_action_state() {
|
||||||
|
return Err(())
|
||||||
}
|
}
|
||||||
|
state.insert(pseudo_class.state_flag());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(state)
|
||||||
|
}).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(PseudoElementSelector {
|
||||||
|
pseudo: pseudo,
|
||||||
|
state: state.unwrap_or(ElementState::empty()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_namespace(&self) -> Option<Namespace> {
|
fn default_namespace(&self) -> Option<Namespace> {
|
||||||
|
@ -476,25 +368,18 @@ impl SelectorImpl {
|
||||||
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
|
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
|
||||||
where F: FnMut(PseudoElement),
|
where F: FnMut(PseudoElement),
|
||||||
{
|
{
|
||||||
macro_rules! case {
|
for pseudo in &EAGER_PSEUDOS {
|
||||||
($atom:expr, $idx:expr) => { fun(PseudoElement($atom, false)); }
|
fun(pseudo.clone())
|
||||||
}
|
}
|
||||||
each_eager_pseudo!(case, atom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Executes a function for each pseudo-element.
|
/// Executes a function for each pseudo-element.
|
||||||
pub fn each_pseudo_element<F>(mut fun: F)
|
pub fn each_pseudo_element<F>(fun: F)
|
||||||
where F: FnMut(PseudoElement),
|
where F: FnMut(PseudoElement),
|
||||||
{
|
{
|
||||||
macro_rules! pseudo_element {
|
PseudoElement::each(fun)
|
||||||
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
|
|
||||||
fun(PseudoElement($atom, $is_anon_box));
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_helper.rs"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -627,7 +627,8 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
_existing_values: &'a Arc<ComputedValues>,
|
_existing_values: &'a Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> Option<&'a nsStyleContext> {
|
-> Option<&'a nsStyleContext> {
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
// TODO(emilio): Migrate this to CSSPseudoElementType.
|
||||||
|
let atom_ptr = pseudo.map_or(ptr::null_mut(), |p| p.atom().as_ptr());
|
||||||
unsafe {
|
unsafe {
|
||||||
let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr);
|
let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr);
|
||||||
context_ptr.as_ref()
|
context_ptr.as_ref()
|
||||||
|
@ -699,15 +700,9 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_atom =
|
let pseudo_type =
|
||||||
unsafe { bindings::Gecko_GetImplementedPseudo(self.0) };
|
unsafe { bindings::Gecko_GetImplementedPseudo(self.0) };
|
||||||
|
PseudoElement::from_pseudo_type(pseudo_type)
|
||||||
if maybe_atom.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let atom = Atom::from(maybe_atom);
|
|
||||||
Some(PseudoElement::from_atom_unchecked(atom, /* anon_box = */ false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_children_to_process(&self, _: isize) {
|
fn store_children_to_process(&self, _: isize) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use cascade_info::CascadeInfo;
|
||||||
use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
|
use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||||
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
||||||
use dom::{AnimationRules, SendElement, TElement, TNode};
|
use dom::{AnimationRules, SendElement, TElement, TNode};
|
||||||
|
use element_state::ElementState;
|
||||||
use font_metrics::FontMetricsProvider;
|
use font_metrics::FontMetricsProvider;
|
||||||
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
|
@ -1015,13 +1016,18 @@ pub trait MatchMethods : TElement {
|
||||||
None => *self,
|
None => *self,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pseudo_and_state = match implemented_pseudo {
|
||||||
|
Some(ref pseudo) => Some((pseudo, self.get_state())),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
// Compute the primary rule node.
|
// Compute the primary rule node.
|
||||||
*relations = stylist.push_applicable_declarations(&selector_matching_target,
|
*relations = stylist.push_applicable_declarations(&selector_matching_target,
|
||||||
Some(bloom),
|
Some(bloom),
|
||||||
style_attribute,
|
style_attribute,
|
||||||
smil_override,
|
smil_override,
|
||||||
animation_rules,
|
animation_rules,
|
||||||
implemented_pseudo.as_ref(),
|
pseudo_and_state,
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
&mut set_selector_flags);
|
&mut set_selector_flags);
|
||||||
|
|
||||||
|
@ -1076,7 +1082,7 @@ pub trait MatchMethods : TElement {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
AnimationRules(None, None),
|
AnimationRules(None, None),
|
||||||
Some(&pseudo),
|
Some((&pseudo, ElementState::empty())),
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
&mut set_selector_flags);
|
&mut set_selector_flags);
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
use Atom;
|
use Atom;
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
|
use fnv::FnvHashMap;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use gecko_bindings::structs::nsRestyleHint;
|
use gecko_bindings::structs::nsRestyleHint;
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
use selector_parser::{AttrValue, NonTSPseudoClass, SelectorImpl, Snapshot, SnapshotMap};
|
use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap};
|
||||||
use selectors::{Element, MatchAttr};
|
use selectors::{Element, MatchAttr};
|
||||||
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
||||||
use selectors::matching::matches_selector;
|
use selectors::matching::matches_selector;
|
||||||
|
@ -228,6 +229,18 @@ impl<'a, E> ElementWrapper<'a, E>
|
||||||
|
|
||||||
snapshot
|
snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn state_changes(&self) -> ElementState {
|
||||||
|
let snapshot = match self.snapshot() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return ElementState::empty(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match snapshot.state() {
|
||||||
|
Some(state) => state ^ self.element.get_state(),
|
||||||
|
None => ElementState::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, E> MatchAttr for ElementWrapper<'a, E>
|
impl<'a, E> MatchAttr for ElementWrapper<'a, E>
|
||||||
|
@ -564,6 +577,38 @@ impl Borrow<SelectorInner<SelectorImpl>> for Dependency {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A similar version of the above, but for pseudo-elements, which only care
|
||||||
|
/// about the full selector, and need it in order to properly track
|
||||||
|
/// pseudo-element selector state.
|
||||||
|
///
|
||||||
|
/// NOTE(emilio): We could add a `hint` and `sensitivities` field to the
|
||||||
|
/// `PseudoElementDependency` and stop posting `RESTYLE_DESCENDANTS`s hints if
|
||||||
|
/// we visited all the pseudo-elements of an element unconditionally as part of
|
||||||
|
/// the traversal.
|
||||||
|
///
|
||||||
|
/// That would allow us to stop posting `RESTYLE_DESCENDANTS` hints for dumb
|
||||||
|
/// selectors, and storing pseudo dependencies in the element dependency map.
|
||||||
|
///
|
||||||
|
/// That would allow us to avoid restyling the element itself when a selector
|
||||||
|
/// has only changed a pseudo-element's style, too.
|
||||||
|
///
|
||||||
|
/// There's no good way to do that right now though, and I think for the
|
||||||
|
/// foreseeable future we may just want to optimize that `RESTYLE_DESCENDANTS`
|
||||||
|
/// to become a `RESTYLE_PSEUDO_ELEMENTS` or something like that, in order to at
|
||||||
|
/// least not restyle the whole subtree.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
struct PseudoElementDependency {
|
||||||
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")]
|
||||||
|
selector: Selector<SelectorImpl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<SelectorInner<SelectorImpl>> for PseudoElementDependency {
|
||||||
|
fn borrow(&self) -> &SelectorInner<SelectorImpl> {
|
||||||
|
&self.selector.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The following visitor visits all the simple selectors for a given complex
|
/// The following visitor visits all the simple selectors for a given complex
|
||||||
/// selector, taking care of :not and :any combinators, collecting whether any
|
/// selector, taking care of :not and :any combinators, collecting whether any
|
||||||
/// of them is sensitive to attribute or state changes.
|
/// of them is sensitive to attribute or state changes.
|
||||||
|
@ -588,13 +633,19 @@ impl SelectorVisitor for SensitivitiesVisitor {
|
||||||
/// SelectorMap and the bloom filter.
|
/// SelectorMap and the bloom filter.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct DependencySet(pub SelectorMap<Dependency>);
|
pub struct DependencySet {
|
||||||
|
/// A map used for pseudo-element's dependencies.
|
||||||
|
///
|
||||||
|
/// Note that pseudo-elements are somewhat special, because some of them in
|
||||||
|
/// Gecko track state, and also because they don't do selector-matching as
|
||||||
|
/// normal, but against their parent element.
|
||||||
|
pseudo_dependencies: FnvHashMap<PseudoElement, SelectorMap<PseudoElementDependency>>,
|
||||||
|
|
||||||
impl DependencySet {
|
/// This is for all other normal element's selectors/selector parts.
|
||||||
fn add_dependency(&mut self, dep: Dependency) {
|
dependencies: SelectorMap<Dependency>,
|
||||||
self.0.insert(dep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DependencySet {
|
||||||
/// Adds a selector to this `DependencySet`.
|
/// Adds a selector to this `DependencySet`.
|
||||||
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) {
|
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) {
|
||||||
let mut combinator = None;
|
let mut combinator = None;
|
||||||
|
@ -617,31 +668,43 @@ impl DependencySet {
|
||||||
index += 1; // Account for the simple selector.
|
index += 1; // Account for the simple selector.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let pseudo_selector_is_state_dependent =
|
||||||
|
sequence_start == 0 &&
|
||||||
|
selector.pseudo_element.as_ref().map_or(false, |pseudo_selector| {
|
||||||
|
!pseudo_selector.state().is_empty()
|
||||||
|
});
|
||||||
|
|
||||||
|
if pseudo_selector_is_state_dependent {
|
||||||
|
let pseudo_selector = selector.pseudo_element.as_ref().unwrap();
|
||||||
|
self.pseudo_dependencies
|
||||||
|
.entry(pseudo_selector.pseudo_element().clone())
|
||||||
|
.or_insert_with(SelectorMap::new)
|
||||||
|
.insert(PseudoElementDependency {
|
||||||
|
selector: selector.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// If we found a sensitivity, add an entry in the dependency set.
|
// If we found a sensitivity, add an entry in the dependency set.
|
||||||
if !visitor.sensitivities.is_empty() {
|
if !visitor.sensitivities.is_empty() {
|
||||||
let mut hint = combinator_to_restyle_hint(combinator);
|
let mut hint = combinator_to_restyle_hint(combinator);
|
||||||
let dep_selector;
|
|
||||||
if sequence_start == 0 {
|
if sequence_start == 0 && selector.pseudo_element.is_some() {
|
||||||
if selector.pseudo_element.is_some() {
|
// FIXME(emilio): Be more granular about this. See the
|
||||||
// TODO(emilio): use more fancy restyle hints to avoid
|
// comment in `PseudoElementDependency` about how could this
|
||||||
// restyling the whole subtree when pseudos change.
|
// be modified in order to be more efficient and restyle
|
||||||
//
|
// less.
|
||||||
// We currently need is_pseudo_element to handle eager
|
hint |= RESTYLE_DESCENDANTS;
|
||||||
// pseudos (so the style the parent stores doesn't
|
|
||||||
// become stale), and restyle_descendants to handle all
|
|
||||||
// of them (::before and ::after, because we find them
|
|
||||||
// in the subtree, and other lazy pseudos for the same
|
|
||||||
// reason).
|
|
||||||
hint |= RESTYLE_SELF | RESTYLE_DESCENDANTS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dep_selector = if sequence_start == 0 {
|
||||||
// Reuse the bloom hashes if this is the base selector.
|
// Reuse the bloom hashes if this is the base selector.
|
||||||
dep_selector = selector.inner.clone();
|
selector.inner.clone()
|
||||||
} else {
|
} else {
|
||||||
dep_selector = SelectorInner::new(selector.inner.complex.slice_from(sequence_start));
|
SelectorInner::new(selector.inner.complex.slice_from(sequence_start))
|
||||||
}
|
};
|
||||||
|
|
||||||
self.add_dependency(Dependency {
|
self.dependencies.insert(Dependency {
|
||||||
sensitivities: visitor.sensitivities,
|
sensitivities: visitor.sensitivities,
|
||||||
hint: hint,
|
hint: hint,
|
||||||
selector: dep_selector,
|
selector: dep_selector,
|
||||||
|
@ -659,40 +722,98 @@ impl DependencySet {
|
||||||
|
|
||||||
/// Create an empty `DependencySet`.
|
/// Create an empty `DependencySet`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
DependencySet(SelectorMap::new())
|
DependencySet {
|
||||||
|
dependencies: SelectorMap::new(),
|
||||||
|
pseudo_dependencies: FnvHashMap::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the total number of dependencies that this set contains.
|
/// Return the total number of dependencies that this set contains.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.dependencies.len() +
|
||||||
|
self.pseudo_dependencies.values().fold(0, |acc, val| acc + val.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear this dependency set.
|
/// Clear this dependency set.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.0 = SelectorMap::new();
|
self.dependencies = SelectorMap::new();
|
||||||
|
self.pseudo_dependencies.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a restyle hint given an element and a snapshot, per the rules
|
fn compute_pseudo_hint<E>(
|
||||||
/// explained in the rest of the documentation.
|
&self,
|
||||||
pub fn compute_hint<E>(&self,
|
pseudo: &E,
|
||||||
|
pseudo_element: PseudoElement,
|
||||||
|
snapshots: &SnapshotMap)
|
||||||
|
-> RestyleHint
|
||||||
|
where E: TElement,
|
||||||
|
{
|
||||||
|
debug!("compute_pseudo_hint: {:?}, {:?}", pseudo, pseudo_element);
|
||||||
|
debug_assert!(pseudo.has_snapshot());
|
||||||
|
|
||||||
|
let map = match self.pseudo_dependencies.get(&pseudo_element) {
|
||||||
|
Some(map) => map,
|
||||||
|
None => return RestyleHint::empty(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only pseudo-element's state is relevant.
|
||||||
|
let pseudo_state_changes =
|
||||||
|
ElementWrapper::new(*pseudo, snapshots).state_changes();
|
||||||
|
|
||||||
|
debug!("pseudo_state_changes: {:?}", pseudo_state_changes);
|
||||||
|
if pseudo_state_changes.is_empty() {
|
||||||
|
return RestyleHint::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
let selector_matching_target =
|
||||||
|
pseudo.closest_non_native_anonymous_ancestor().unwrap();
|
||||||
|
|
||||||
|
// Note that we rely on that, if the originating element changes, it'll
|
||||||
|
// post a restyle hint that would make us redo selector matching, so we
|
||||||
|
// don't need to care about that.
|
||||||
|
//
|
||||||
|
// If that ever changes, we'd need to share more code with
|
||||||
|
// `compute_element_hint`.
|
||||||
|
let mut hint = RestyleHint::empty();
|
||||||
|
map.lookup(selector_matching_target, &mut |dep| {
|
||||||
|
// If the selector didn't match before, it either doesn't match now
|
||||||
|
// either (or it doesn't matter because our parent posted a restyle
|
||||||
|
// for us above).
|
||||||
|
if !matches_selector(&dep.selector.inner, &selector_matching_target,
|
||||||
|
None, &mut StyleRelations::empty(),
|
||||||
|
&mut |_, _| {}) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pseudo_selector = dep.selector.pseudo_element.as_ref().unwrap();
|
||||||
|
debug_assert!(!pseudo_selector.state().is_empty());
|
||||||
|
|
||||||
|
if pseudo_selector.state().intersects(pseudo_state_changes) {
|
||||||
|
hint = RESTYLE_SELF;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
hint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_element_hint<E>(
|
||||||
|
&self,
|
||||||
el: &E,
|
el: &E,
|
||||||
snapshots: &SnapshotMap)
|
snapshots: &SnapshotMap)
|
||||||
-> RestyleHint
|
-> RestyleHint
|
||||||
where E: TElement + Clone,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
debug_assert!(el.has_snapshot(), "Shouldn't be here!");
|
debug_assert!(el.has_snapshot(), "Shouldn't be here!");
|
||||||
let snapshot_el = ElementWrapper::new(el.clone(), snapshots);
|
|
||||||
|
|
||||||
|
let snapshot_el = ElementWrapper::new(el.clone(), snapshots);
|
||||||
let snapshot =
|
let snapshot =
|
||||||
snapshot_el.snapshot().expect("has_snapshot lied so badly");
|
snapshot_el.snapshot().expect("has_snapshot lied so badly");
|
||||||
|
|
||||||
let current_state = el.get_state();
|
let state_changes = snapshot_el.state_changes();
|
||||||
let state_changes =
|
|
||||||
snapshot.state()
|
|
||||||
.map_or_else(ElementState::empty,
|
|
||||||
|old_state| current_state ^ old_state);
|
|
||||||
let attrs_changed = snapshot.has_attrs();
|
let attrs_changed = snapshot.has_attrs();
|
||||||
|
|
||||||
if state_changes.is_empty() && !attrs_changed {
|
if state_changes.is_empty() && !attrs_changed {
|
||||||
return RestyleHint::empty();
|
return RestyleHint::empty();
|
||||||
}
|
}
|
||||||
|
@ -704,18 +825,30 @@ impl DependencySet {
|
||||||
// that we get all the possible applicable selectors from the rulehash.
|
// that we get all the possible applicable selectors from the rulehash.
|
||||||
let mut additional_id = None;
|
let mut additional_id = None;
|
||||||
let mut additional_classes = SmallVec::<[Atom; 8]>::new();
|
let mut additional_classes = SmallVec::<[Atom; 8]>::new();
|
||||||
if snapshot.has_attrs() {
|
if attrs_changed {
|
||||||
let id = snapshot.id_attr();
|
let id = snapshot.id_attr();
|
||||||
if id.is_some() && id != el.get_id() {
|
if id.is_some() && id != el.get_id() {
|
||||||
additional_id = id;
|
additional_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot.each_class(|c| if !el.has_class(c) { additional_classes.push(c.clone()) });
|
snapshot.each_class(|c| {
|
||||||
|
if !el.has_class(c) {
|
||||||
|
additional_classes.push(c.clone())
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0.lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| {
|
self.dependencies
|
||||||
if !dep.sensitivities.sensitive_to(attrs_changed, state_changes) ||
|
.lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| {
|
||||||
hint.contains(dep.hint) {
|
trace!("scanning dependency: {:?}", dep);
|
||||||
|
if !dep.sensitivities.sensitive_to(attrs_changed,
|
||||||
|
state_changes) {
|
||||||
|
trace!(" > non-sensitive");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if hint.contains(dep.hint) {
|
||||||
|
trace!(" > hint was already there");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,9 +871,25 @@ impl DependencySet {
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
|
debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
|
||||||
hint, el, current_state, self.len());
|
hint, el, el.get_state(), self.len());
|
||||||
trace!("Deps: {:?}", self);
|
|
||||||
|
|
||||||
hint
|
hint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Compute a restyle hint given an element and a snapshot, per the rules
|
||||||
|
/// explained in the rest of the documentation.
|
||||||
|
pub fn compute_hint<E>(&self,
|
||||||
|
el: &E,
|
||||||
|
snapshots: &SnapshotMap)
|
||||||
|
-> RestyleHint
|
||||||
|
where E: TElement + Clone,
|
||||||
|
{
|
||||||
|
debug!("DependencySet::compute_hint({:?})", el);
|
||||||
|
if let Some(pseudo) = el.implemented_pseudo_element() {
|
||||||
|
return self.compute_pseudo_hint(el, pseudo, snapshots);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.compute_element_hint(el, snapshots)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1071,10 +1071,11 @@ impl StrongRuleNode {
|
||||||
if !have_explicit_ua_inherit { break }
|
if !have_explicit_ua_inherit { break }
|
||||||
|
|
||||||
// Continue to the parent element and search for the inherited properties.
|
// Continue to the parent element and search for the inherited properties.
|
||||||
element = match element.parent_element() {
|
element = match element.inheritance_parent() {
|
||||||
Some(parent) => parent,
|
Some(parent) => parent,
|
||||||
None => break
|
None => break
|
||||||
};
|
};
|
||||||
|
|
||||||
let parent_data = element.mutate_data().unwrap();
|
let parent_data = element.mutate_data().unwrap();
|
||||||
let parent_rule_node = parent_data.styles().primary.rules.clone();
|
let parent_rule_node = parent_data.styles().primary.rules.clone();
|
||||||
element_rule_node = Cow::Owned(parent_rule_node);
|
element_rule_node = Cow::Owned(parent_rule_node);
|
||||||
|
|
|
@ -78,6 +78,18 @@ impl ToCss for PseudoElement {
|
||||||
pub const EAGER_PSEUDO_COUNT: usize = 3;
|
pub const EAGER_PSEUDO_COUNT: usize = 3;
|
||||||
|
|
||||||
impl PseudoElement {
|
impl PseudoElement {
|
||||||
|
/// The pseudo-element, used for compatibility with Gecko's
|
||||||
|
/// `PseudoElementSelector`.
|
||||||
|
pub fn pseudo_element(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The pseudo-element selector's state, used for compatibility with Gecko's
|
||||||
|
/// `PseudoElementSelector`.
|
||||||
|
pub fn state(&self) -> ElementState {
|
||||||
|
ElementState::empty()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn eager_index(&self) -> usize {
|
pub fn eager_index(&self) -> usize {
|
||||||
|
@ -252,7 +264,7 @@ impl NonTSPseudoClass {
|
||||||
pub struct SelectorImpl;
|
pub struct SelectorImpl;
|
||||||
|
|
||||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||||
type PseudoElement = PseudoElement;
|
type PseudoElementSelector = PseudoElement;
|
||||||
type NonTSPseudoClass = NonTSPseudoClass;
|
type NonTSPseudoClass = NonTSPseudoClass;
|
||||||
|
|
||||||
type AttrValue = String;
|
type AttrValue = String;
|
||||||
|
@ -311,7 +323,10 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
||||||
Ok(pseudo_class)
|
Ok(pseudo_class)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
|
fn parse_pseudo_element(&self,
|
||||||
|
name: Cow<str>,
|
||||||
|
_input: &mut CssParser)
|
||||||
|
-> Result<PseudoElement, ()> {
|
||||||
use self::PseudoElement::*;
|
use self::PseudoElement::*;
|
||||||
let pseudo_element = match_ignore_ascii_case! { &name,
|
let pseudo_element = match_ignore_ascii_case! { &name,
|
||||||
"before" => Before,
|
"before" => Before,
|
||||||
|
|
|
@ -449,19 +449,18 @@ impl Stylist {
|
||||||
rule: &Arc<Locked<StyleRule>>,
|
rule: &Arc<Locked<StyleRule>>,
|
||||||
stylesheet: &Stylesheet)
|
stylesheet: &Stylesheet)
|
||||||
{
|
{
|
||||||
let map = if let Some(ref pseudo) = selector.pseudo_element {
|
let map = if let Some(ref pseudo_selector) = selector.pseudo_element {
|
||||||
self.pseudos_map
|
self.pseudos_map
|
||||||
.entry(pseudo.clone())
|
.entry(pseudo_selector.pseudo_element().clone())
|
||||||
.or_insert_with(PerPseudoElementSelectorMap::new)
|
.or_insert_with(PerPseudoElementSelectorMap::new)
|
||||||
.borrow_for_origin(&stylesheet.origin)
|
.borrow_for_origin(&stylesheet.origin)
|
||||||
} else {
|
} else {
|
||||||
self.element_map.borrow_for_origin(&stylesheet.origin)
|
self.element_map.borrow_for_origin(&stylesheet.origin)
|
||||||
};
|
};
|
||||||
|
|
||||||
map.insert(Rule::new(selector.inner.clone(),
|
map.insert(Rule::new(selector.clone(),
|
||||||
rule.clone(),
|
rule.clone(),
|
||||||
self.rules_source_order,
|
self.rules_source_order));
|
||||||
selector.specificity));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -496,6 +495,9 @@ impl Stylist {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
|
fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
|
||||||
|
if let Some(ref pseudo_selector) = selector.pseudo_element {
|
||||||
|
self.state_dependencies.insert(pseudo_selector.state());
|
||||||
|
}
|
||||||
selector.visit(&mut AttributeAndStateDependencyVisitor(self));
|
selector.visit(&mut AttributeAndStateDependencyVisitor(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,12 +605,14 @@ impl Stylist {
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
element: &E,
|
element: &E,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
|
pseudo_state: ElementState,
|
||||||
parent: &Arc<ComputedValues>,
|
parent: &Arc<ComputedValues>,
|
||||||
font_metrics: &FontMetricsProvider)
|
font_metrics: &FontMetricsProvider)
|
||||||
-> Option<ComputedStyle>
|
-> Option<ComputedStyle>
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
let rule_node = match self.lazy_pseudo_rules(guards, element, pseudo) {
|
let rule_node =
|
||||||
|
match self.lazy_pseudo_rules(guards, element, pseudo, pseudo_state) {
|
||||||
Some(rule_node) => rule_node,
|
Some(rule_node) => rule_node,
|
||||||
None => return None
|
None => return None
|
||||||
};
|
};
|
||||||
|
@ -639,7 +643,8 @@ impl Stylist {
|
||||||
pub fn lazy_pseudo_rules<E>(&self,
|
pub fn lazy_pseudo_rules<E>(&self,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
element: &E,
|
element: &E,
|
||||||
pseudo: &PseudoElement)
|
pseudo: &PseudoElement,
|
||||||
|
pseudo_state: ElementState)
|
||||||
-> Option<StrongRuleNode>
|
-> Option<StrongRuleNode>
|
||||||
where E: TElement
|
where E: TElement
|
||||||
{
|
{
|
||||||
|
@ -679,7 +684,7 @@ impl Stylist {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
AnimationRules(None, None),
|
AnimationRules(None, None),
|
||||||
Some(pseudo),
|
Some((pseudo, pseudo_state)),
|
||||||
&mut declarations,
|
&mut declarations,
|
||||||
&mut set_selector_flags);
|
&mut set_selector_flags);
|
||||||
if declarations.is_empty() {
|
if declarations.is_empty() {
|
||||||
|
@ -808,7 +813,7 @@ impl Stylist {
|
||||||
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
||||||
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
||||||
animation_rules: AnimationRules,
|
animation_rules: AnimationRules,
|
||||||
pseudo_element: Option<&PseudoElement>,
|
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||||
applicable_declarations: &mut V,
|
applicable_declarations: &mut V,
|
||||||
flags_setter: &mut F)
|
flags_setter: &mut F)
|
||||||
-> StyleRelations
|
-> StyleRelations
|
||||||
|
@ -822,18 +827,21 @@ impl Stylist {
|
||||||
debug_assert!(cfg!(feature = "gecko") ||
|
debug_assert!(cfg!(feature = "gecko") ||
|
||||||
style_attribute.is_none() || pseudo_element.is_none(),
|
style_attribute.is_none() || pseudo_element.is_none(),
|
||||||
"Style attributes do not apply to pseudo-elements");
|
"Style attributes do not apply to pseudo-elements");
|
||||||
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.is_precomputed()));
|
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed()));
|
||||||
|
|
||||||
let map = match pseudo_element {
|
let map = match pseudo_element {
|
||||||
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),
|
Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(),
|
||||||
None => &self.element_map,
|
None => &self.element_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut relations = StyleRelations::empty();
|
let mut relations = StyleRelations::empty();
|
||||||
|
|
||||||
debug!("Determining if style is shareable: pseudo: {}", pseudo_element.is_some());
|
debug!("Determining if style is shareable: pseudo: {}",
|
||||||
|
pseudo_element.is_some());
|
||||||
|
|
||||||
// Step 1: Normal user-agent rules.
|
// Step 1: Normal user-agent rules.
|
||||||
map.user_agent.get_all_matching_rules(element,
|
map.user_agent.get_all_matching_rules(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut relations,
|
&mut relations,
|
||||||
|
@ -860,6 +868,7 @@ impl Stylist {
|
||||||
if element.matches_user_and_author_rules() {
|
if element.matches_user_and_author_rules() {
|
||||||
// Step 3: User and author normal rules.
|
// Step 3: User and author normal rules.
|
||||||
map.user.get_all_matching_rules(element,
|
map.user.get_all_matching_rules(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut relations,
|
&mut relations,
|
||||||
|
@ -867,6 +876,7 @@ impl Stylist {
|
||||||
CascadeLevel::UserNormal);
|
CascadeLevel::UserNormal);
|
||||||
debug!("user normal: {:?}", relations);
|
debug!("user normal: {:?}", relations);
|
||||||
map.author.get_all_matching_rules(element,
|
map.author.get_all_matching_rules(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut relations,
|
&mut relations,
|
||||||
|
@ -1250,6 +1260,7 @@ impl SelectorMap<Rule> {
|
||||||
/// Sort the Rules at the end to maintain cascading order.
|
/// Sort the Rules at the end to maintain cascading order.
|
||||||
pub fn get_all_matching_rules<E, V, F>(&self,
|
pub fn get_all_matching_rules<E, V, F>(&self,
|
||||||
element: &E,
|
element: &E,
|
||||||
|
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||||
parent_bf: Option<&BloomFilter>,
|
parent_bf: Option<&BloomFilter>,
|
||||||
matching_rules_list: &mut V,
|
matching_rules_list: &mut V,
|
||||||
relations: &mut StyleRelations,
|
relations: &mut StyleRelations,
|
||||||
|
@ -1267,6 +1278,7 @@ impl SelectorMap<Rule> {
|
||||||
let init_len = matching_rules_list.len();
|
let init_len = matching_rules_list.len();
|
||||||
if let Some(id) = element.get_id() {
|
if let Some(id) = element.get_id() {
|
||||||
SelectorMap::get_matching_rules_from_hash(element,
|
SelectorMap::get_matching_rules_from_hash(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
&self.id_hash,
|
&self.id_hash,
|
||||||
&id,
|
&id,
|
||||||
|
@ -1278,6 +1290,7 @@ impl SelectorMap<Rule> {
|
||||||
|
|
||||||
element.each_class(|class| {
|
element.each_class(|class| {
|
||||||
SelectorMap::get_matching_rules_from_hash(element,
|
SelectorMap::get_matching_rules_from_hash(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
&self.class_hash,
|
&self.class_hash,
|
||||||
class,
|
class,
|
||||||
|
@ -1288,6 +1301,7 @@ impl SelectorMap<Rule> {
|
||||||
});
|
});
|
||||||
|
|
||||||
SelectorMap::get_matching_rules_from_hash(element,
|
SelectorMap::get_matching_rules_from_hash(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
&self.local_name_hash,
|
&self.local_name_hash,
|
||||||
element.get_local_name(),
|
element.get_local_name(),
|
||||||
|
@ -1297,6 +1311,7 @@ impl SelectorMap<Rule> {
|
||||||
cascade_level);
|
cascade_level);
|
||||||
|
|
||||||
SelectorMap::get_matching_rules(element,
|
SelectorMap::get_matching_rules(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
&self.other,
|
&self.other,
|
||||||
matching_rules_list,
|
matching_rules_list,
|
||||||
|
@ -1321,7 +1336,7 @@ impl SelectorMap<Rule> {
|
||||||
|
|
||||||
let mut rules_list = vec![];
|
let mut rules_list = vec![];
|
||||||
for rule in self.other.iter() {
|
for rule in self.other.iter() {
|
||||||
if rule.selector.complex.iter_raw().next().is_none() {
|
if rule.selector.inner.complex.iter_raw().next().is_none() {
|
||||||
rules_list.push(rule.to_applicable_declaration_block(cascade_level));
|
rules_list.push(rule.to_applicable_declaration_block(cascade_level));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1334,6 +1349,7 @@ impl SelectorMap<Rule> {
|
||||||
|
|
||||||
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
|
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
|
||||||
element: &E,
|
element: &E,
|
||||||
|
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||||
parent_bf: Option<&BloomFilter>,
|
parent_bf: Option<&BloomFilter>,
|
||||||
hash: &FnvHashMap<Str, Vec<Rule>>,
|
hash: &FnvHashMap<Str, Vec<Rule>>,
|
||||||
key: &BorrowedStr,
|
key: &BorrowedStr,
|
||||||
|
@ -1349,6 +1365,7 @@ impl SelectorMap<Rule> {
|
||||||
{
|
{
|
||||||
if let Some(rules) = hash.get(key) {
|
if let Some(rules) = hash.get(key) {
|
||||||
SelectorMap::get_matching_rules(element,
|
SelectorMap::get_matching_rules(element,
|
||||||
|
pseudo_element,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
rules,
|
rules,
|
||||||
matching_rules,
|
matching_rules,
|
||||||
|
@ -1360,6 +1377,7 @@ impl SelectorMap<Rule> {
|
||||||
|
|
||||||
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
|
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
|
||||||
fn get_matching_rules<E, V, F>(element: &E,
|
fn get_matching_rules<E, V, F>(element: &E,
|
||||||
|
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||||
parent_bf: Option<&BloomFilter>,
|
parent_bf: Option<&BloomFilter>,
|
||||||
rules: &[Rule],
|
rules: &[Rule],
|
||||||
matching_rules: &mut V,
|
matching_rules: &mut V,
|
||||||
|
@ -1371,8 +1389,32 @@ impl SelectorMap<Rule> {
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
{
|
{
|
||||||
for rule in rules.iter() {
|
for rule in rules.iter() {
|
||||||
if matches_selector(&rule.selector, element, parent_bf,
|
debug_assert_eq!(rule.selector.pseudo_element.is_some(),
|
||||||
relations, flags_setter) {
|
pseudo_element.is_some(),
|
||||||
|
"Testing pseudo-elements against the wrong map");
|
||||||
|
|
||||||
|
if let Some((pseudo, pseudo_state)) = pseudo_element {
|
||||||
|
let pseudo_selector =
|
||||||
|
rule.selector.pseudo_element.as_ref().unwrap();
|
||||||
|
|
||||||
|
debug_assert_eq!(pseudo_selector.pseudo_element(), pseudo,
|
||||||
|
"Testing pseudo-element against the wrong entry");
|
||||||
|
|
||||||
|
let state = pseudo_selector.state();
|
||||||
|
|
||||||
|
// NB: We only allow a subset of the flags here, so using
|
||||||
|
// contains for them is fine, (and it's necessary, to handle
|
||||||
|
// multiple state flags properly).
|
||||||
|
if !state.is_empty() && !pseudo_state.contains(state) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches_selector(&rule.selector.inner,
|
||||||
|
element,
|
||||||
|
parent_bf,
|
||||||
|
relations,
|
||||||
|
flags_setter) {
|
||||||
matching_rules.push(
|
matching_rules.push(
|
||||||
rule.to_applicable_declaration_block(cascade_level));
|
rule.to_applicable_declaration_block(cascade_level));
|
||||||
}
|
}
|
||||||
|
@ -1579,29 +1621,24 @@ pub struct Rule {
|
||||||
/// pointer-chasing when gathering applicable declarations, which
|
/// pointer-chasing when gathering applicable declarations, which
|
||||||
/// can ruin performance when there are a lot of rules.
|
/// can ruin performance when there are a lot of rules.
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
pub selector: SelectorInner<SelectorImpl>,
|
pub selector: Selector<SelectorImpl>,
|
||||||
/// The actual style rule.
|
/// The actual style rule.
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
pub style_rule: Arc<Locked<StyleRule>>,
|
pub style_rule: Arc<Locked<StyleRule>>,
|
||||||
/// The source order this style rule appears in.
|
/// The source order this style rule appears in.
|
||||||
pub source_order: usize,
|
pub source_order: usize,
|
||||||
/// The specificity of the rule this selector represents.
|
|
||||||
///
|
|
||||||
/// Note: The top two bits of this are unused, and could be used to store
|
|
||||||
/// flags.
|
|
||||||
specificity: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<SelectorInner<SelectorImpl>> for Rule {
|
impl Borrow<SelectorInner<SelectorImpl>> for Rule {
|
||||||
fn borrow(&self) -> &SelectorInner<SelectorImpl> {
|
fn borrow(&self) -> &SelectorInner<SelectorImpl> {
|
||||||
&self.selector
|
&self.selector.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rule {
|
impl Rule {
|
||||||
/// Returns the specificity of the rule.
|
/// Returns the specificity of the rule.
|
||||||
pub fn specificity(&self) -> u32 {
|
pub fn specificity(&self) -> u32 {
|
||||||
self.specificity
|
self.selector.specificity
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock {
|
fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock {
|
||||||
|
@ -1614,17 +1651,15 @@ impl Rule {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Rule.
|
/// Creates a new Rule.
|
||||||
pub fn new(selector: SelectorInner<SelectorImpl>,
|
pub fn new(selector: Selector<SelectorImpl>,
|
||||||
style_rule: Arc<Locked<StyleRule>>,
|
style_rule: Arc<Locked<StyleRule>>,
|
||||||
source_order: usize,
|
source_order: usize)
|
||||||
specificity: u32)
|
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
Rule {
|
Rule {
|
||||||
selector: selector,
|
selector: selector,
|
||||||
style_rule: style_rule,
|
style_rule: style_rule,
|
||||||
source_order: source_order,
|
source_order: source_order,
|
||||||
specificity: specificity,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -787,10 +787,11 @@ fn preprocess_children<E, D>(traversal: &D,
|
||||||
let later_siblings =
|
let later_siblings =
|
||||||
child_data.compute_final_hint(child, traversal.shared_context());
|
child_data.compute_final_hint(child, traversal.shared_context());
|
||||||
|
|
||||||
trace!(" > {:?} -> {:?} + {:?}, later_siblings: {:?}",
|
trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}, later_siblings: {:?}",
|
||||||
child,
|
child,
|
||||||
child_data.get_restyle().map(|r| &r.hint),
|
child_data.get_restyle().map(|r| &r.hint),
|
||||||
propagated_hint,
|
propagated_hint,
|
||||||
|
child.implemented_pseudo_element(),
|
||||||
later_siblings);
|
later_siblings);
|
||||||
|
|
||||||
// If the child doesn't have pre-existing RestyleData and we don't have
|
// If the child doesn't have pre-existing RestyleData and we don't have
|
||||||
|
|
|
@ -33,7 +33,6 @@ use style::gecko_bindings::bindings::{RawServoMediaList, RawServoMediaListBorrow
|
||||||
use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed};
|
use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed};
|
||||||
use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
|
use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
|
||||||
use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
|
use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
|
||||||
use style::gecko_bindings::bindings::{RawServoRuleNodeBorrowed, RawServoRuleNodeStrong};
|
|
||||||
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
|
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
|
||||||
use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
|
use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
|
||||||
use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
|
use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
|
||||||
|
@ -59,10 +58,10 @@ use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
|
||||||
use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
|
use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
|
||||||
use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
|
use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
|
||||||
use style::gecko_bindings::structs;
|
use style::gecko_bindings::structs;
|
||||||
|
use style::gecko_bindings::structs::{CSSPseudoElementType, CompositeOperation};
|
||||||
use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
|
use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
|
||||||
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
|
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
|
||||||
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule};
|
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule};
|
||||||
use style::gecko_bindings::structs::CompositeOperation;
|
|
||||||
use style::gecko_bindings::structs::Loader;
|
use style::gecko_bindings::structs::Loader;
|
||||||
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||||
use style::gecko_bindings::structs::ServoElementSnapshotTable;
|
use style::gecko_bindings::structs::ServoElementSnapshotTable;
|
||||||
|
@ -84,7 +83,7 @@ use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
|
||||||
use style::properties::animated_properties::{Animatable, AnimationValue, TransitionProperty};
|
use style::properties::animated_properties::{Animatable, AnimationValue, TransitionProperty};
|
||||||
use style::properties::parse_one_declaration;
|
use style::properties::parse_one_declaration;
|
||||||
use style::restyle_hints::{self, RestyleHint};
|
use style::restyle_hints::{self, RestyleHint};
|
||||||
use style::rule_tree::{StrongRuleNode, StyleSource};
|
use style::rule_tree::StyleSource;
|
||||||
use style::selector_parser::PseudoElementCascadeType;
|
use style::selector_parser::PseudoElementCascadeType;
|
||||||
use style::sequential;
|
use style::sequential;
|
||||||
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
||||||
|
@ -475,7 +474,7 @@ pub extern "C" fn Servo_AnimationValue_Uncompute(value: RawServoAnimationValueBo
|
||||||
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed,
|
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed,
|
||||||
element: RawGeckoElementBorrowed,
|
element: RawGeckoElementBorrowed,
|
||||||
snapshots: *const ServoElementSnapshotTable,
|
snapshots: *const ServoElementSnapshotTable,
|
||||||
pseudo_tag: *mut nsIAtom)
|
pseudo_type: CSSPseudoElementType)
|
||||||
-> ServoComputedValuesStrong
|
-> ServoComputedValuesStrong
|
||||||
{
|
{
|
||||||
use style::matching::MatchMethods;
|
use style::matching::MatchMethods;
|
||||||
|
@ -493,12 +492,7 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe
|
||||||
let element_data = element.borrow_data().unwrap();
|
let element_data = element.borrow_data().unwrap();
|
||||||
let styles = element_data.styles();
|
let styles = element_data.styles();
|
||||||
|
|
||||||
let pseudo = if pseudo_tag.is_null() {
|
let pseudo = PseudoElement::from_pseudo_type(pseudo_type);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let atom = Atom::from(pseudo_tag);
|
|
||||||
Some(PseudoElement::from_atom_unchecked(atom, /* anon_box = */ false))
|
|
||||||
};
|
|
||||||
let pseudos = &styles.pseudos;
|
let pseudos = &styles.pseudos;
|
||||||
let pseudo_style = match pseudo {
|
let pseudo_style = match pseudo {
|
||||||
Some(ref p) => {
|
Some(ref p) => {
|
||||||
|
@ -975,7 +969,8 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
||||||
let guards = StylesheetGuards::same(&guard);
|
let guards = StylesheetGuards::same(&guard);
|
||||||
let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
let atom = Atom::from(pseudo_tag);
|
let atom = Atom::from(pseudo_tag);
|
||||||
let pseudo = PseudoElement::from_atom_unchecked(atom, /* anon_box = */ true);
|
let pseudo = PseudoElement::from_anon_box_atom(&atom)
|
||||||
|
.expect("Not an anon box pseudo?");
|
||||||
|
|
||||||
|
|
||||||
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
|
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
|
||||||
|
@ -990,40 +985,10 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
||||||
.into_strong()
|
.into_strong()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed,
|
|
||||||
pseudo_tag: *mut nsIAtom,
|
|
||||||
raw_data: RawServoStyleSetBorrowed)
|
|
||||||
-> RawServoRuleNodeStrong
|
|
||||||
{
|
|
||||||
let element = GeckoElement(element);
|
|
||||||
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
|
|
||||||
let guard = (*GLOBAL_STYLE_DATA).shared_lock.read();
|
|
||||||
|
|
||||||
let data = element.mutate_data().unwrap();
|
|
||||||
let styles = match data.get_styles() {
|
|
||||||
Some(styles) => styles,
|
|
||||||
None => {
|
|
||||||
warn!("Calling Servo_ResolveRuleNode on unstyled element");
|
|
||||||
return Strong::null()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let maybe_rules = if pseudo_tag.is_null() {
|
|
||||||
Some(styles.primary.rules.clone())
|
|
||||||
} else {
|
|
||||||
get_pseudo_rule_node(&guard, element, pseudo_tag, styles, doc_data)
|
|
||||||
};
|
|
||||||
|
|
||||||
match maybe_rules {
|
|
||||||
Some(rule_node) => rule_node.into_strong(),
|
|
||||||
None => Strong::null(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
pseudo_tag: *mut nsIAtom, is_probe: bool,
|
pseudo_type: CSSPseudoElementType,
|
||||||
|
is_probe: bool,
|
||||||
raw_data: RawServoStyleSetBorrowed)
|
raw_data: RawServoStyleSetBorrowed)
|
||||||
-> ServoComputedValuesStrong
|
-> ServoComputedValuesStrong
|
||||||
{
|
{
|
||||||
|
@ -1041,59 +1006,47 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pseudo = PseudoElement::from_pseudo_type(pseudo_type)
|
||||||
|
.expect("ResolvePseudoStyle with a non-pseudo?");
|
||||||
|
|
||||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
match get_pseudo_style(&guard, element, pseudo_tag, data.styles(), doc_data) {
|
match get_pseudo_style(&guard, element, &pseudo, data.styles(), doc_data) {
|
||||||
Some(values) => values.into_strong(),
|
Some(values) => values.into_strong(),
|
||||||
|
// FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an
|
||||||
|
// empty style inheriting from the element?
|
||||||
None if !is_probe => data.styles().primary.values().clone().into_strong(),
|
None if !is_probe => data.styles().primary.values().clone().into_strong(),
|
||||||
None => Strong::null(),
|
None => Strong::null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed,
|
pub extern "C" fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed,
|
||||||
element: RawGeckoElementBorrowed,
|
|
||||||
rule_type_mask: u32,
|
rule_type_mask: u32,
|
||||||
author_colors_allowed: bool)
|
author_colors_allowed: bool)
|
||||||
-> bool
|
-> bool
|
||||||
{
|
{
|
||||||
let element = GeckoElement(element);
|
let element = GeckoElement(element);
|
||||||
|
|
||||||
|
let data = element.borrow_data().unwrap();
|
||||||
|
let primary_style = &data.styles().primary;
|
||||||
|
|
||||||
let guard = (*GLOBAL_STYLE_DATA).shared_lock.read();
|
let guard = (*GLOBAL_STYLE_DATA).shared_lock.read();
|
||||||
let guards = StylesheetGuards::same(&guard);
|
let guards = StylesheetGuards::same(&guard);
|
||||||
|
|
||||||
StrongRuleNode::from_ffi(&rule_node).has_author_specified_rules(element,
|
primary_style.rules.has_author_specified_rules(element,
|
||||||
&guards,
|
&guards,
|
||||||
rule_type_mask,
|
rule_type_mask,
|
||||||
author_colors_allowed)
|
author_colors_allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pseudo_rule_node(guard: &SharedRwLockReadGuard,
|
|
||||||
element: GeckoElement,
|
|
||||||
pseudo_tag: *mut nsIAtom,
|
|
||||||
styles: &ElementStyles,
|
|
||||||
doc_data: &PerDocumentStyleData)
|
|
||||||
-> Option<StrongRuleNode>
|
|
||||||
{
|
|
||||||
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
|
|
||||||
match pseudo.cascade_type() {
|
|
||||||
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.rules.clone()),
|
|
||||||
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
|
||||||
PseudoElementCascadeType::Lazy => {
|
|
||||||
let d = doc_data.borrow_mut();
|
|
||||||
let guards = StylesheetGuards::same(guard);
|
|
||||||
d.stylist.lazy_pseudo_rules(&guards, &element, &pseudo)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
|
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
|
||||||
element: GeckoElement,
|
element: GeckoElement,
|
||||||
pseudo_tag: *mut nsIAtom,
|
pseudo: &PseudoElement,
|
||||||
styles: &ElementStyles,
|
styles: &ElementStyles,
|
||||||
doc_data: &PerDocumentStyleData)
|
doc_data: &PerDocumentStyleData)
|
||||||
-> Option<Arc<ComputedValues>>
|
-> Option<Arc<ComputedValues>>
|
||||||
{
|
{
|
||||||
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
|
|
||||||
match pseudo.cascade_type() {
|
match pseudo.cascade_type() {
|
||||||
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()),
|
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()),
|
||||||
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
||||||
|
@ -1105,6 +1058,7 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard,
|
||||||
d.stylist.lazily_compute_pseudo_element_style(&guards,
|
d.stylist.lazily_compute_pseudo_element_style(&guards,
|
||||||
&element,
|
&element,
|
||||||
&pseudo,
|
&pseudo,
|
||||||
|
ElementState::empty(),
|
||||||
base,
|
base,
|
||||||
&metrics)
|
&metrics)
|
||||||
.map(|s| s.values().clone())
|
.map(|s| s.values().clone())
|
||||||
|
@ -2025,7 +1979,7 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
pseudo_tag: *mut nsIAtom,
|
pseudo_type: CSSPseudoElementType,
|
||||||
snapshots: *const ServoElementSnapshotTable,
|
snapshots: *const ServoElementSnapshotTable,
|
||||||
raw_data: RawServoStyleSetBorrowed)
|
raw_data: RawServoStyleSetBorrowed)
|
||||||
-> ServoComputedValuesStrong
|
-> ServoComputedValuesStrong
|
||||||
|
@ -2036,12 +1990,9 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
let element = GeckoElement(element);
|
let element = GeckoElement(element);
|
||||||
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
|
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
|
||||||
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
|
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
|
||||||
let maybe_pseudo = if !pseudo_tag.is_null() {
|
PseudoElement::from_pseudo_type(pseudo_type).and_then(|ref pseudo| {
|
||||||
get_pseudo_style(&guard, element, pseudo_tag, styles, doc_data)
|
get_pseudo_style(&guard, element, pseudo, styles, doc_data)
|
||||||
} else {
|
}).unwrap_or_else(|| styles.primary.values().clone())
|
||||||
None
|
|
||||||
};
|
|
||||||
maybe_pseudo.unwrap_or_else(|| styles.primary.values().clone())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// In the common case we already have the style. Check that before setting
|
// In the common case we already have the style. Check that before setting
|
||||||
|
|
|
@ -24,7 +24,4 @@ fn smoke_restyle_hints() {
|
||||||
let selector = (selectors.0).first().unwrap();
|
let selector = (selectors.0).first().unwrap();
|
||||||
dependencies.note_selector(selector);
|
dependencies.note_selector(selector);
|
||||||
assert_eq!(dependencies.len(), 1);
|
assert_eq!(dependencies.len(), 1);
|
||||||
let dep = &dependencies.0.other[0];
|
|
||||||
assert!(!dep.sensitivities.states.is_empty());
|
|
||||||
assert!(dep.hint.contains(RESTYLE_LATER_SIBLINGS));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,10 +42,7 @@ fn get_mock_rules(css_selectors: &[&str]) -> (Vec<Vec<Rule>>, SharedRwLock) {
|
||||||
let guard = shared_lock.read();
|
let guard = shared_lock.read();
|
||||||
let rule = locked.read_with(&guard);
|
let rule = locked.read_with(&guard);
|
||||||
rule.selectors.0.iter().map(|s| {
|
rule.selectors.0.iter().map(|s| {
|
||||||
Rule::new(s.inner.clone(),
|
Rule::new(s.clone(), locked.clone(), i)
|
||||||
locked.clone(),
|
|
||||||
i,
|
|
||||||
s.specificity)
|
|
||||||
}).collect()
|
}).collect()
|
||||||
}).collect(), shared_lock)
|
}).collect(), shared_lock)
|
||||||
}
|
}
|
||||||
|
@ -175,22 +172,22 @@ fn test_rule_ordering_same_specificity() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_id_name() {
|
fn test_get_id_name() {
|
||||||
let (rules_list, _) = get_mock_rules(&[".intro", "#top"]);
|
let (rules_list, _) = get_mock_rules(&[".intro", "#top"]);
|
||||||
assert_eq!(stylist::get_id_name(&rules_list[0][0].selector), None);
|
assert_eq!(stylist::get_id_name(&rules_list[0][0].selector.inner), None);
|
||||||
assert_eq!(stylist::get_id_name(&rules_list[1][0].selector), Some(Atom::from("top")));
|
assert_eq!(stylist::get_id_name(&rules_list[1][0].selector.inner), Some(Atom::from("top")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_class_name() {
|
fn test_get_class_name() {
|
||||||
let (rules_list, _) = get_mock_rules(&[".intro.foo", "#top"]);
|
let (rules_list, _) = get_mock_rules(&[".intro.foo", "#top"]);
|
||||||
assert_eq!(stylist::get_class_name(&rules_list[0][0].selector), Some(Atom::from("foo")));
|
assert_eq!(stylist::get_class_name(&rules_list[0][0].selector.inner), Some(Atom::from("foo")));
|
||||||
assert_eq!(stylist::get_class_name(&rules_list[1][0].selector), None);
|
assert_eq!(stylist::get_class_name(&rules_list[1][0].selector.inner), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_local_name() {
|
fn test_get_local_name() {
|
||||||
let (rules_list, _) = get_mock_rules(&["img.foo", "#top", "IMG", "ImG"]);
|
let (rules_list, _) = get_mock_rules(&["img.foo", "#top", "IMG", "ImG"]);
|
||||||
let check = |i: usize, names: Option<(&str, &str)>| {
|
let check = |i: usize, names: Option<(&str, &str)>| {
|
||||||
assert!(stylist::get_local_name(&rules_list[i][0].selector)
|
assert!(stylist::get_local_name(&rules_list[i][0].selector.inner)
|
||||||
== names.map(|(name, lower_name)| LocalNameSelector {
|
== names.map(|(name, lower_name)| LocalNameSelector {
|
||||||
name: LocalName::from(name),
|
name: LocalName::from(name),
|
||||||
lower_name: LocalName::from(lower_name) }))
|
lower_name: LocalName::from(lower_name) }))
|
||||||
|
|
|
@ -26,28 +26,3 @@ macro_rules! check_enum_value_non_static {
|
||||||
assert_eq!($a.0 as usize, $b as usize);
|
assert_eq!($a.0 as usize, $b as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that we can't call each_pseudo_element, parse_pseudo_element, or
|
|
||||||
// similar, because we'd need the foreign atom symbols to link.
|
|
||||||
#[test]
|
|
||||||
fn assert_basic_pseudo_elements() {
|
|
||||||
let saw_before;
|
|
||||||
let saw_after;
|
|
||||||
|
|
||||||
macro_rules! pseudo_element {
|
|
||||||
(":before", $atom:expr, false) => {
|
|
||||||
saw_before = true;
|
|
||||||
};
|
|
||||||
(":after", $atom:expr, false) => {
|
|
||||||
saw_after = true;
|
|
||||||
};
|
|
||||||
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {
|
|
||||||
// Do nothing
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
include!("../../../components/style/gecko/generated/pseudo_element_helper.rs");
|
|
||||||
|
|
||||||
assert!(saw_before);
|
|
||||||
assert!(saw_after);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue