Auto merge of #18962 - upsuper:tree-pseudos, r=emilio

Support matching for ::-moz-tree-* pseudo-elements

This is the Servo side change of [bug 1397644](https://bugzilla.mozilla.org/show_bug.cgi?id=1397644).

<!-- 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/18962)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-10-20 04:18:53 -05:00 committed by GitHub
commit b1e6f05ae4
13 changed files with 489 additions and 257 deletions

View file

@ -408,7 +408,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
RuleInclusion::All,
data.styles.primary(),
/* is_probe = */ false,
&ServoMetricsProvider)
&ServoMetricsProvider,
/* matching_func = */ None)
.unwrap()
.clone()
}

View file

@ -109,6 +109,10 @@ where
/// The current nesting level of selectors that we're matching.
pub nesting_level: usize,
/// An optional hook function for checking whether a pseudo-element
/// should match when matching_mode is ForStatelessPseudoElement.
pub pseudo_element_matching_fn: Option<&'a Fn(&Impl::PseudoElement) -> bool>,
quirks_mode: QuirksMode,
classes_and_ids_case_sensitivity: CaseSensitivity,
_impl: ::std::marker::PhantomData<Impl>,
@ -152,6 +156,7 @@ where
classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
scope_element: None,
nesting_level: 0,
pseudo_element_matching_fn: None,
_impl: ::std::marker::PhantomData,
}
}

View file

@ -387,9 +387,20 @@ where
if context.nesting_level == 0 &&
context.matching_mode == MatchingMode::ForStatelessPseudoElement {
// Consume the pseudo.
let pseudo = iter.next().unwrap();
debug_assert!(matches!(*pseudo, Component::PseudoElement(..)),
"Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector");
match *iter.next().unwrap() {
Component::PseudoElement(ref pseudo) => {
if let Some(ref f) = context.pseudo_element_matching_fn {
if !f(pseudo) {
return false;
}
}
}
_ => {
debug_assert!(false,
"Used MatchingMode::ForStatelessPseudoElement \
in a non-pseudo selector");
}
}
// The only other parser-allowed Component in this sequence is a state
// class. We just don't match in that case.

View file

@ -15,6 +15,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue;
use gecko_bindings::structs::mozilla::css::URLValue;
use gecko_bindings::structs::mozilla::css::URLValueData;
use gecko_bindings::structs::mozilla::AnonymousCounterStyle;
use gecko_bindings::structs::mozilla::AtomArray;
use gecko_bindings::structs::mozilla::MallocSizeOf;
use gecko_bindings::structs::mozilla::OriginFlags;
use gecko_bindings::structs::mozilla::UniquePtr;
@ -2945,6 +2946,19 @@ extern "C" {
set: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_ComputedValues_ResolveXULTreePseudoStyle(element:
RawGeckoElementBorrowed,
pseudo_tag:
*mut nsAtom,
inherited_style:
ServoStyleContextBorrowed,
input_word:
*const AtomArray,
set:
RawServoStyleSetBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
primary_style: ServoStyleContextBorrowed);

View file

@ -140,29 +140,29 @@ pub enum PseudoElement {
/// :-moz-ruby-text-container
RubyTextContainer,
/// :-moz-tree-column
MozTreeColumn(Box<[String]>),
MozTreeColumn(Box<[Atom]>),
/// :-moz-tree-row
MozTreeRow(Box<[String]>),
MozTreeRow(Box<[Atom]>),
/// :-moz-tree-separator
MozTreeSeparator(Box<[String]>),
MozTreeSeparator(Box<[Atom]>),
/// :-moz-tree-cell
MozTreeCell(Box<[String]>),
MozTreeCell(Box<[Atom]>),
/// :-moz-tree-indentation
MozTreeIndentation(Box<[String]>),
MozTreeIndentation(Box<[Atom]>),
/// :-moz-tree-line
MozTreeLine(Box<[String]>),
MozTreeLine(Box<[Atom]>),
/// :-moz-tree-twisty
MozTreeTwisty(Box<[String]>),
MozTreeTwisty(Box<[Atom]>),
/// :-moz-tree-image
MozTreeImage(Box<[String]>),
MozTreeImage(Box<[Atom]>),
/// :-moz-tree-cell-text
MozTreeCellText(Box<[String]>),
MozTreeCellText(Box<[Atom]>),
/// :-moz-tree-checkbox
MozTreeCheckbox(Box<[String]>),
MozTreeCheckbox(Box<[Atom]>),
/// :-moz-tree-progressmeter
MozTreeProgressmeter(Box<[String]>),
MozTreeProgressmeter(Box<[Atom]>),
/// :-moz-tree-drop-feedback
MozTreeDropFeedback(Box<[String]>),
MozTreeDropFeedback(Box<[Atom]>),
/// :-moz-svg-marker-anon-child
MozSVGMarkerAnonChild,
/// :-moz-svg-outer-svg-anon-child
@ -185,6 +185,12 @@ pub const EAGER_PSEUDO_COUNT: usize = 4;
/// The number of non-functional pseudo-elements.
pub const SIMPLE_PSEUDO_COUNT: usize = 71;
/// The number of tree pseudo-elements.
pub const TREE_PSEUDO_COUNT: usize = 12;
/// The number of all pseudo-elements.
pub const PSEUDO_COUNT: usize = 83;
/// The list of eager pseudos.
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
PseudoElement::Before,
@ -286,90 +292,100 @@ impl PseudoElement {
}
}
/// Returns an index if the pseudo-element is a simple (non-functional)
/// pseudo.
/// Returns an index of the pseudo-element.
#[inline]
pub fn simple_index(&self) -> Option<usize> {
pub fn index(&self) -> usize {
match *self {
PseudoElement::After => Some(0),
PseudoElement::Before => Some(1),
PseudoElement::Backdrop => Some(2),
PseudoElement::Cue => Some(3),
PseudoElement::FirstLetter => Some(4),
PseudoElement::FirstLine => Some(5),
PseudoElement::MozSelection => Some(6),
PseudoElement::MozFocusInner => Some(7),
PseudoElement::MozFocusOuter => Some(8),
PseudoElement::MozListBullet => Some(9),
PseudoElement::MozListNumber => Some(10),
PseudoElement::MozMathAnonymous => Some(11),
PseudoElement::MozNumberWrapper => Some(12),
PseudoElement::MozNumberText => Some(13),
PseudoElement::MozNumberSpinBox => Some(14),
PseudoElement::MozNumberSpinUp => Some(15),
PseudoElement::MozNumberSpinDown => Some(16),
PseudoElement::MozProgressBar => Some(17),
PseudoElement::MozRangeTrack => Some(18),
PseudoElement::MozRangeProgress => Some(19),
PseudoElement::MozRangeThumb => Some(20),
PseudoElement::MozMeterBar => Some(21),
PseudoElement::MozPlaceholder => Some(22),
PseudoElement::Placeholder => Some(23),
PseudoElement::MozColorSwatch => Some(24),
PseudoElement::MozText => Some(25),
PseudoElement::OofPlaceholder => Some(26),
PseudoElement::FirstLetterContinuation => Some(27),
PseudoElement::MozBlockInsideInlineWrapper => Some(28),
PseudoElement::MozMathMLAnonymousBlock => Some(29),
PseudoElement::MozXULAnonymousBlock => Some(30),
PseudoElement::HorizontalFramesetBorder => Some(31),
PseudoElement::VerticalFramesetBorder => Some(32),
PseudoElement::MozLineFrame => Some(33),
PseudoElement::ButtonContent => Some(34),
PseudoElement::CellContent => Some(35),
PseudoElement::DropDownList => Some(36),
PseudoElement::FieldsetContent => Some(37),
PseudoElement::FramesetBlank => Some(38),
PseudoElement::MozDisplayComboboxControlFrame => Some(39),
PseudoElement::HtmlCanvasContent => Some(40),
PseudoElement::InlineTable => Some(41),
PseudoElement::Table => Some(42),
PseudoElement::TableCell => Some(43),
PseudoElement::TableColGroup => Some(44),
PseudoElement::TableCol => Some(45),
PseudoElement::TableWrapper => Some(46),
PseudoElement::TableRowGroup => Some(47),
PseudoElement::TableRow => Some(48),
PseudoElement::Canvas => Some(49),
PseudoElement::PageBreak => Some(50),
PseudoElement::Page => Some(51),
PseudoElement::PageContent => Some(52),
PseudoElement::PageSequence => Some(53),
PseudoElement::ScrolledContent => Some(54),
PseudoElement::ScrolledCanvas => Some(55),
PseudoElement::ScrolledPageSequence => Some(56),
PseudoElement::ColumnContent => Some(57),
PseudoElement::Viewport => Some(58),
PseudoElement::ViewportScroll => Some(59),
PseudoElement::AnonymousFlexItem => Some(60),
PseudoElement::AnonymousGridItem => Some(61),
PseudoElement::Ruby => Some(62),
PseudoElement::RubyBase => Some(63),
PseudoElement::RubyBaseContainer => Some(64),
PseudoElement::RubyText => Some(65),
PseudoElement::RubyTextContainer => Some(66),
PseudoElement::MozSVGMarkerAnonChild => Some(67),
PseudoElement::MozSVGOuterSVGAnonChild => Some(68),
PseudoElement::MozSVGForeignContent => Some(69),
PseudoElement::MozSVGText => Some(70),
_ => None,
PseudoElement::After => 0,
PseudoElement::Before => 1,
PseudoElement::Backdrop => 2,
PseudoElement::Cue => 3,
PseudoElement::FirstLetter => 4,
PseudoElement::FirstLine => 5,
PseudoElement::MozSelection => 6,
PseudoElement::MozFocusInner => 7,
PseudoElement::MozFocusOuter => 8,
PseudoElement::MozListBullet => 9,
PseudoElement::MozListNumber => 10,
PseudoElement::MozMathAnonymous => 11,
PseudoElement::MozNumberWrapper => 12,
PseudoElement::MozNumberText => 13,
PseudoElement::MozNumberSpinBox => 14,
PseudoElement::MozNumberSpinUp => 15,
PseudoElement::MozNumberSpinDown => 16,
PseudoElement::MozProgressBar => 17,
PseudoElement::MozRangeTrack => 18,
PseudoElement::MozRangeProgress => 19,
PseudoElement::MozRangeThumb => 20,
PseudoElement::MozMeterBar => 21,
PseudoElement::MozPlaceholder => 22,
PseudoElement::Placeholder => 23,
PseudoElement::MozColorSwatch => 24,
PseudoElement::MozText => 25,
PseudoElement::OofPlaceholder => 26,
PseudoElement::FirstLetterContinuation => 27,
PseudoElement::MozBlockInsideInlineWrapper => 28,
PseudoElement::MozMathMLAnonymousBlock => 29,
PseudoElement::MozXULAnonymousBlock => 30,
PseudoElement::HorizontalFramesetBorder => 31,
PseudoElement::VerticalFramesetBorder => 32,
PseudoElement::MozLineFrame => 33,
PseudoElement::ButtonContent => 34,
PseudoElement::CellContent => 35,
PseudoElement::DropDownList => 36,
PseudoElement::FieldsetContent => 37,
PseudoElement::FramesetBlank => 38,
PseudoElement::MozDisplayComboboxControlFrame => 39,
PseudoElement::HtmlCanvasContent => 40,
PseudoElement::InlineTable => 41,
PseudoElement::Table => 42,
PseudoElement::TableCell => 43,
PseudoElement::TableColGroup => 44,
PseudoElement::TableCol => 45,
PseudoElement::TableWrapper => 46,
PseudoElement::TableRowGroup => 47,
PseudoElement::TableRow => 48,
PseudoElement::Canvas => 49,
PseudoElement::PageBreak => 50,
PseudoElement::Page => 51,
PseudoElement::PageContent => 52,
PseudoElement::PageSequence => 53,
PseudoElement::ScrolledContent => 54,
PseudoElement::ScrolledCanvas => 55,
PseudoElement::ScrolledPageSequence => 56,
PseudoElement::ColumnContent => 57,
PseudoElement::Viewport => 58,
PseudoElement::ViewportScroll => 59,
PseudoElement::AnonymousFlexItem => 60,
PseudoElement::AnonymousGridItem => 61,
PseudoElement::Ruby => 62,
PseudoElement::RubyBase => 63,
PseudoElement::RubyBaseContainer => 64,
PseudoElement::RubyText => 65,
PseudoElement::RubyTextContainer => 66,
PseudoElement::MozTreeColumn(..) => 67,
PseudoElement::MozTreeRow(..) => 68,
PseudoElement::MozTreeSeparator(..) => 69,
PseudoElement::MozTreeCell(..) => 70,
PseudoElement::MozTreeIndentation(..) => 71,
PseudoElement::MozTreeLine(..) => 72,
PseudoElement::MozTreeTwisty(..) => 73,
PseudoElement::MozTreeImage(..) => 74,
PseudoElement::MozTreeCellText(..) => 75,
PseudoElement::MozTreeCheckbox(..) => 76,
PseudoElement::MozTreeProgressmeter(..) => 77,
PseudoElement::MozTreeDropFeedback(..) => 78,
PseudoElement::MozSVGMarkerAnonChild => 79,
PseudoElement::MozSVGOuterSVGAnonChild => 80,
PseudoElement::MozSVGForeignContent => 81,
PseudoElement::MozSVGText => 82,
}
}
/// Returns an array of `None` values.
///
/// FIXME(emilio): Integer generics can't come soon enough.
pub fn simple_pseudo_none_array<T>() -> [Option<T>; SIMPLE_PSEUDO_COUNT] {
pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
[
None,
None,
@ -441,6 +457,18 @@ None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None
]
}
@ -518,6 +546,26 @@ None
PseudoElement::Before | PseudoElement::After | PseudoElement::FirstLine | PseudoElement::FirstLetter)
}
/// Whether this pseudo-element is tree pseudo-element.
#[inline]
pub fn is_tree_pseudo_element(&self) -> bool {
match *self {
PseudoElement::MozTreeColumn(..) => true,
PseudoElement::MozTreeRow(..) => true,
PseudoElement::MozTreeSeparator(..) => true,
PseudoElement::MozTreeCell(..) => true,
PseudoElement::MozTreeIndentation(..) => true,
PseudoElement::MozTreeLine(..) => true,
PseudoElement::MozTreeTwisty(..) => true,
PseudoElement::MozTreeImage(..) => true,
PseudoElement::MozTreeCellText(..) => true,
PseudoElement::MozTreeCheckbox(..) => true,
PseudoElement::MozTreeProgressmeter(..) => true,
PseudoElement::MozTreeDropFeedback(..) => true,
_ => false,
}
}
/// Gets the flags associated to this pseudo-element, or 0 if it's an
/// anonymous box.
pub fn flags(&self) -> u32 {
@ -847,18 +895,18 @@ None
PseudoElement::RubyBaseContainer => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::RubyText => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::RubyTextContainer => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeColumn(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeRow(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeSeparator(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeCell(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeIndentation(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeLine(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeTwisty(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeImage(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeCellText(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeCheckbox(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeProgressmeter(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeDropFeedback(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozTreeColumn(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeRow(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeSeparator(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeCell(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeIndentation(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeLine(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeTwisty(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeImage(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeCellText(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeCheckbox(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeProgressmeter(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozTreeDropFeedback(..) => CSSPseudoElementType::XULTree,
PseudoElement::MozSVGMarkerAnonChild => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozSVGOuterSVGAnonChild => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::MozSVGForeignContent => CSSPseudoElementType_InheritingAnonBox,
@ -871,6 +919,26 @@ None
(self.atom().as_ptr(), self.pseudo_type())
}
/// Get the argument list of a tree pseudo-element.
#[inline]
pub fn tree_pseudo_args(&self) -> Option<&[Atom]> {
match *self {
PseudoElement::MozTreeColumn(ref args) => Some(args),
PseudoElement::MozTreeRow(ref args) => Some(args),
PseudoElement::MozTreeSeparator(ref args) => Some(args),
PseudoElement::MozTreeCell(ref args) => Some(args),
PseudoElement::MozTreeIndentation(ref args) => Some(args),
PseudoElement::MozTreeLine(ref args) => Some(args),
PseudoElement::MozTreeTwisty(ref args) => Some(args),
PseudoElement::MozTreeImage(ref args) => Some(args),
PseudoElement::MozTreeCellText(ref args) => Some(args),
PseudoElement::MozTreeCheckbox(ref args) => Some(args),
PseudoElement::MozTreeProgressmeter(ref args) => Some(args),
PseudoElement::MozTreeDropFeedback(ref args) => Some(args),
_ => None,
}
}
/// Construct a pseudo-element from an `Atom`.
#[inline]
pub fn from_atom(atom: &Atom) -> Option<Self> {
@ -1258,6 +1326,48 @@ None
None
}
/// Construct a tree pseudo-element from atom and args.
#[inline]
pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
if atom == &atom!(":-moz-tree-column") {
return Some(PseudoElement::MozTreeColumn(args));
}
if atom == &atom!(":-moz-tree-row") {
return Some(PseudoElement::MozTreeRow(args));
}
if atom == &atom!(":-moz-tree-separator") {
return Some(PseudoElement::MozTreeSeparator(args));
}
if atom == &atom!(":-moz-tree-cell") {
return Some(PseudoElement::MozTreeCell(args));
}
if atom == &atom!(":-moz-tree-indentation") {
return Some(PseudoElement::MozTreeIndentation(args));
}
if atom == &atom!(":-moz-tree-line") {
return Some(PseudoElement::MozTreeLine(args));
}
if atom == &atom!(":-moz-tree-twisty") {
return Some(PseudoElement::MozTreeTwisty(args));
}
if atom == &atom!(":-moz-tree-image") {
return Some(PseudoElement::MozTreeImage(args));
}
if atom == &atom!(":-moz-tree-cell-text") {
return Some(PseudoElement::MozTreeCellText(args));
}
if atom == &atom!(":-moz-tree-checkbox") {
return Some(PseudoElement::MozTreeCheckbox(args));
}
if atom == &atom!(":-moz-tree-progressmeter") {
return Some(PseudoElement::MozTreeProgressmeter(args));
}
if atom == &atom!(":-moz-tree-drop-feedback") {
return Some(PseudoElement::MozTreeDropFeedback(args));
}
None
}
/// Constructs an atom from a string of text, and whether we're in a
/// user-agent stylesheet.
///
@ -1636,7 +1746,7 @@ None
///
/// Returns `None` if the pseudo-element is not recognized.
#[inline]
pub fn tree_pseudo_element(name: &str, args: Box<[String]>) -> Option<Self> {
pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option<Self> {
use std::ascii::AsciiExt;
debug_assert!(name.starts_with("-moz-tree-"));
let tree_part = &name[10..];
@ -1768,31 +1878,20 @@ impl ToCss for PseudoElement {
PseudoElement::MozSVGForeignContent => dest.write_str(":-moz-svg-foreign-content")?,
PseudoElement::MozSVGText => dest.write_str(":-moz-svg-text")?,
}
match *self {
PseudoElement::MozTreeColumn(ref args) |
PseudoElement::MozTreeRow(ref args) |
PseudoElement::MozTreeSeparator(ref args) |
PseudoElement::MozTreeCell(ref args) |
PseudoElement::MozTreeIndentation(ref args) |
PseudoElement::MozTreeLine(ref args) |
PseudoElement::MozTreeTwisty(ref args) |
PseudoElement::MozTreeImage(ref args) |
PseudoElement::MozTreeCellText(ref args) |
PseudoElement::MozTreeCheckbox(ref args) |
PseudoElement::MozTreeProgressmeter(ref args) |
PseudoElement::MozTreeDropFeedback(ref args) => {
if let Some(args) = self.tree_pseudo_args() {
if !args.is_empty() {
dest.write_char('(')?;
let mut iter = args.iter();
if let Some(first) = iter.next() {
serialize_identifier(first, dest)?;
serialize_identifier(&first.to_string(), dest)?;
for item in iter {
dest.write_str(", ")?;
serialize_identifier(item, dest)?;
serialize_identifier(&item.to_string(), dest)?;
}
}
dest.write_char(')')
}
_ => Ok(()),
dest.write_char(')')?;
}
}
Ok(())
}
}

View file

@ -4175,6 +4175,7 @@ pub mod root {
pub struct ShortcutKeyCandidate {
_unused: [u8; 0],
}
pub type AtomArray = root::nsTArray<root::RefPtr<root::nsAtom>>;
/// EventStates is the class used to represent the event states of nsIContent
/// instances. These states are calculated by IntrinsicState() and
/// ContentStatesChanged() has to be called when one of them changes thus
@ -22385,8 +22386,6 @@ pub mod root {
pub struct nsAttrValue {
pub mBits: usize,
}
pub type nsAttrValue_AtomArray =
root::nsTArray<root::RefPtr<root::nsAtom>>;
pub const nsAttrValue_ValueType_eSVGTypesBegin:
root::nsAttrValue_ValueType =
nsAttrValue_ValueType::eSVGAngle;

View file

@ -46,7 +46,7 @@ impl PseudoElement {
return PseudoElementCascadeType::Eager
}
if self.is_anon_box() {
if self.is_precomputed() {
return PseudoElementCascadeType::Precomputed
}
@ -137,7 +137,7 @@ impl PseudoElement {
/// Whether this pseudo-element is precomputed.
#[inline]
pub fn is_precomputed(&self) -> bool {
self.is_anon_box()
self.is_anon_box() && !self.is_tree_pseudo_element()
}
/// Covert non-canonical pseudo-element to canonical one, and keep a

View file

@ -8,7 +8,7 @@ pub enum PseudoElement {
% for pseudo in PSEUDOS:
/// ${pseudo.value}
% if pseudo.is_tree_pseudo_element():
${pseudo.capitalized()}(Box<[String]>),
${pseudo.capitalized()}(Box<[Atom]>),
% else:
${pseudo.capitalized()},
% endif
@ -27,6 +27,12 @@ pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
/// The number of non-functional pseudo-elements.
pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)};
/// The number of tree pseudo-elements.
pub const TREE_PSEUDO_COUNT: usize = ${len(TREE_PSEUDOS)};
/// The number of all pseudo-elements.
pub const PSEUDO_COUNT: usize = ${len(PSEUDOS)};
/// The list of eager pseudos.
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
% for eager_pseudo_name in EAGER_PSEUDOS:
@ -49,24 +55,22 @@ impl PseudoElement {
}
}
/// Returns an index if the pseudo-element is a simple (non-functional)
/// pseudo.
/// Returns an index of the pseudo-element.
#[inline]
pub fn simple_index(&self) -> Option<usize> {
pub fn index(&self) -> usize {
match *self {
% for i, pseudo in enumerate(SIMPLE_PSEUDOS):
${pseudo_element_variant(pseudo)} => Some(${i}),
% for i, pseudo in enumerate(PSEUDOS):
${pseudo_element_variant(pseudo)} => ${i},
% endfor
_ => None,
}
}
/// Returns an array of `None` values.
///
/// FIXME(emilio): Integer generics can't come soon enough.
pub fn simple_pseudo_none_array<T>() -> [Option<T>; SIMPLE_PSEUDO_COUNT] {
pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
[
${",\n".join(["None" for pseudo in SIMPLE_PSEUDOS])}
${",\n ".join(["None" for pseudo in PSEUDOS])}
]
}
@ -90,6 +94,17 @@ impl PseudoElement {
${" | ".join(map(lambda name: "PseudoElement::{}".format(name), EAGER_PSEUDOS))})
}
/// Whether this pseudo-element is tree pseudo-element.
#[inline]
pub fn is_tree_pseudo_element(&self) -> bool {
match *self {
% for pseudo in TREE_PSEUDOS:
${pseudo_element_variant(pseudo)} => true,
% endfor
_ => false,
}
}
/// Gets the flags associated to this pseudo-element, or 0 if it's an
/// anonymous box.
pub fn flags(&self) -> u32 {
@ -132,7 +147,7 @@ impl PseudoElement {
% if not pseudo.is_anon_box():
PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType::${pseudo.original_ident},
% elif pseudo.is_tree_pseudo_element():
PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType_InheritingAnonBox,
PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType::XULTree,
% elif pseudo.is_inheriting_anon_box():
PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType_InheritingAnonBox,
% else:
@ -147,6 +162,17 @@ impl PseudoElement {
(self.atom().as_ptr(), self.pseudo_type())
}
/// Get the argument list of a tree pseudo-element.
#[inline]
pub fn tree_pseudo_args(&self) -> Option<<&[Atom]> {
match *self {
% for pseudo in TREE_PSEUDOS:
PseudoElement::${pseudo.capitalized()}(ref args) => Some(args),
% endfor
_ => None,
}
}
/// Construct a pseudo-element from an `Atom`.
#[inline]
pub fn from_atom(atom: &Atom) -> Option<Self> {
@ -177,6 +203,19 @@ impl PseudoElement {
None
}
/// Construct a tree pseudo-element from atom and args.
#[inline]
pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
% for pseudo in PSEUDOS:
% if pseudo.is_tree_pseudo_element():
if atom == &atom!("${pseudo.value}") {
return Some(PseudoElement::${pseudo.capitalized()}(args));
}
% endif
% endfor
None
}
/// Constructs an atom from a string of text, and whether we're in a
/// user-agent stylesheet.
///
@ -207,7 +246,7 @@ impl PseudoElement {
///
/// Returns `None` if the pseudo-element is not recognized.
#[inline]
pub fn tree_pseudo_element(name: &str, args: Box<[String]>) -> Option<Self> {
pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option<Self> {
use std::ascii::AsciiExt;
debug_assert!(name.starts_with("-moz-tree-"));
let tree_part = &name[10..];
@ -228,21 +267,20 @@ impl ToCss for PseudoElement {
${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
% endfor
}
match *self {
${" |\n ".join("PseudoElement::{}(ref args)".format(pseudo.capitalized())
for pseudo in TREE_PSEUDOS)} => {
if let Some(args) = self.tree_pseudo_args() {
if !args.is_empty() {
dest.write_char('(')?;
let mut iter = args.iter();
if let Some(first) = iter.next() {
serialize_identifier(first, dest)?;
serialize_identifier(&first.to_string(), dest)?;
for item in iter {
dest.write_str(", ")?;
serialize_identifier(item, dest)?;
serialize_identifier(&item.to_string(), dest)?;
}
}
dest.write_char(')')
}
_ => Ok(()),
dest.write_char(')')?;
}
}
Ok(())
}
}

View file

@ -17,7 +17,7 @@ use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use style_traits::{ParseError, StyleParseErrorKind};
pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, SIMPLE_PSEUDO_COUNT};
pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;
bitflags! {
@ -388,6 +388,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
-> Result<PseudoElement, ParseError<'i>> {
PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
.or_else(|| {
if name.starts_with("-moz-tree-") {
PseudoElement::tree_pseudo_element(&name, Box::new([]))
} else {
None
}
})
.ok_or(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())))
}
@ -401,7 +408,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
loop {
let location = parser.current_source_location();
match parser.next() {
Ok(&Token::Ident(ref ident)) => args.push(ident.as_ref().to_owned()),
Ok(&Token::Ident(ref ident)) => args.push(Atom::from(ident.as_ref())),
Ok(&Token::Comma) => {},
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => break,

View file

@ -102,16 +102,16 @@ pub enum PseudoElementCascadeType {
Precomputed,
}
/// A per-functional-pseudo map, from a given pseudo to a `T`.
/// A per-pseudo map, from a given pseudo to a `T`.
#[derive(MallocSizeOf)]
pub struct PerPseudoElementMap<T> {
entries: [Option<T>; SIMPLE_PSEUDO_COUNT],
entries: [Option<T>; PSEUDO_COUNT],
}
impl<T> Default for PerPseudoElementMap<T> {
fn default() -> Self {
Self {
entries: PseudoElement::simple_pseudo_none_array(),
entries: PseudoElement::pseudo_none_array(),
}
}
}
@ -137,11 +137,7 @@ where
impl<T> PerPseudoElementMap<T> {
/// Get an entry in the map.
pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {
let index = match pseudo.simple_index() {
Some(i) => i,
None => return None,
};
self.entries[index].as_ref()
self.entries[pseudo.index()].as_ref()
}
/// Clear this enumerated array.
@ -161,13 +157,8 @@ impl<T> PerPseudoElementMap<T> {
/// Set an entry value.
///
/// Returns an error if the element is not a simple pseudo.
pub fn set(&mut self, pseudo: &PseudoElement, value: T) -> Result<(), ()> {
let index = match pseudo.simple_index() {
Some(i) => i,
None => return Err(()),
};
self.entries[index] = Some(value);
Ok(())
pub fn set(&mut self, pseudo: &PseudoElement, value: T) {
self.entries[pseudo.index()] = Some(value);
}
/// Get an entry for `pseudo`, or create it with calling `f`.
@ -175,18 +166,15 @@ impl<T> PerPseudoElementMap<T> {
&mut self,
pseudo: &PseudoElement,
f: F,
) -> Result<&mut T, ()>
) -> &mut T
where
F: FnOnce() -> T,
{
let index = match pseudo.simple_index() {
Some(i) => i,
None => return Err(()),
};
let index = pseudo.index();
if self.entries[index].is_none() {
self.entries[index] = Some(f());
}
Ok(self.entries[index].as_mut().unwrap())
self.entries[index].as_mut().unwrap()
}
/// Get an iterator for the entries.

View file

@ -62,9 +62,8 @@ pub enum PseudoElement {
ServoInlineAbsolute,
}
/// The count of simple (non-functional) pseudo-elements (that is, all
/// pseudo-elements for now).
pub const SIMPLE_PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1;
/// The count of all pseudo-elements.
pub const PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1;
impl ::selectors::parser::PseudoElement for PseudoElement {
type Impl = SelectorImpl;
@ -110,12 +109,12 @@ impl PseudoElement {
/// An index for this pseudo-element to be indexed in an enumerated array.
#[inline]
pub fn simple_index(&self) -> Option<usize> {
Some(self.clone() as usize)
pub fn index(&self) -> usize {
self.clone() as usize
}
/// An array of `None`, one per simple pseudo-element.
pub fn simple_pseudo_none_array<T>() -> [Option<T>; SIMPLE_PSEUDO_COUNT] {
/// An array of `None`, one per pseudo-element.
pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
Default::default()
}

View file

@ -819,13 +819,21 @@ impl Stylist {
rule_inclusion: RuleInclusion,
parent_style: &ComputedValues,
is_probe: bool,
font_metrics: &FontMetricsProvider
font_metrics: &FontMetricsProvider,
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
) -> Option<Arc<ComputedValues>>
where
E: TElement,
{
let cascade_inputs =
self.lazy_pseudo_rules(guards, element, pseudo, is_probe, rule_inclusion);
self.lazy_pseudo_rules(
guards,
element,
pseudo,
is_probe,
rule_inclusion,
matching_fn
);
self.compute_pseudo_element_style_with_inputs(
&cascade_inputs,
pseudo,
@ -979,7 +987,8 @@ impl Stylist {
element: &E,
pseudo: &PseudoElement,
is_probe: bool,
rule_inclusion: RuleInclusion
rule_inclusion: RuleInclusion,
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
) -> CascadeInputs
where
E: TElement
@ -1026,6 +1035,7 @@ impl Stylist {
None,
self.quirks_mode,
);
matching_context.pseudo_element_matching_fn = matching_fn;
self.push_applicable_declarations(
element,
@ -1062,6 +1072,7 @@ impl Stylist {
VisitedHandlingMode::RelevantLinkVisited,
self.quirks_mode,
);
matching_context.pseudo_element_matching_fn = matching_fn;
self.push_applicable_declarations(
element,
@ -1289,6 +1300,7 @@ impl Stylist {
context.nth_index_cache.as_mut().map(|s| &mut **s),
stylist.quirks_mode,
);
matching_context.pseudo_element_matching_fn = context.pseudo_element_matching_fn;
map.get_all_matching_rules(
element,
@ -1994,19 +2006,13 @@ impl CascadeData {
let map = match selector.pseudo_element() {
Some(pseudo) if pseudo.is_precomputed() => {
if !selector.is_universal() ||
!matches!(origin, Origin::UserAgent) {
// ::-moz-tree selectors may appear in
// non-UA sheets (even though they never
// match).
continue;
}
debug_assert!(selector.is_universal());
debug_assert!(matches!(origin, Origin::UserAgent));
precomputed_pseudo_element_decls
.as_mut()
.expect("Expected precomputed declarations for the UA level")
.get_or_insert_with(&pseudo.canonical(), Vec::new)
.expect("Unexpected tree pseudo-element?")
.push(ApplicableDeclarationBlock::new(
StyleSource::Style(locked.clone()),
self.rules_source_order,
@ -2023,7 +2029,7 @@ impl CascadeData {
let mut map = Box::new(SelectorMap::new());
map.begin_mutation();
map
}).expect("Unexpected tree pseudo-element?")
})
}
};

View file

@ -84,6 +84,7 @@ use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleContextStrong,
use style::gecko_bindings::structs::{ServoStyleSheet, SheetParsingMode, nsAtom, nsCSSPropertyID};
use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
use style::gecko_bindings::structs::AtomArray;
use style::gecko_bindings::structs::IterationCompositeOperation;
use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
use style::gecko_bindings::structs::OriginFlags;
@ -126,7 +127,7 @@ use style::rule_cache::RuleCacheConditions;
use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
use style::string_cache::Atom;
use style::string_cache::{Atom, WeakAtom};
use style::style_adjuster::StyleAdjuster;
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule};
@ -1911,6 +1912,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
inherited_style,
&*doc_data,
is_probe,
/* matching_func = */ None,
);
match style {
@ -1922,6 +1924,66 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
}
}
fn debug_atom_array(atoms: &AtomArray) -> String {
let mut result = String::from("[");
for atom in atoms.iter() {
if atom.mRawPtr.is_null() {
result += "(null), ";
} else {
let atom = unsafe { WeakAtom::new(atom.mRawPtr) };
write!(result, "{}, ", atom).unwrap();
}
}
result.push(']');
result
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ResolveXULTreePseudoStyle(
element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsAtom,
inherited_style: ServoStyleContextBorrowed,
input_word: *const AtomArray,
raw_data: RawServoStyleSetBorrowed
) -> ServoStyleContextStrong {
let element = GeckoElement(element);
let data = element.borrow_data()
.expect("Calling ResolveXULTreePseudoStyle on unstyled element?");
let pseudo = unsafe {
Atom::with(pseudo_tag, |atom| {
PseudoElement::from_tree_pseudo_atom(atom, Box::new([]))
}).expect("ResolveXULTreePseudoStyle with a non-tree pseudo?")
};
let input_word = unsafe { input_word.as_ref().unwrap() };
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
debug!("ResolveXULTreePseudoStyle: {:?} {:?} {}",
element, pseudo, debug_atom_array(input_word));
let matching_fn = |pseudo: &PseudoElement| {
let args = pseudo.tree_pseudo_args().expect("Not a tree pseudo-element?");
args.iter().all(|atom| {
input_word.iter().any(|item| atom.as_ptr() == item.mRawPtr)
})
};
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
get_pseudo_style(
&guard,
element,
&pseudo,
RuleInclusion::All,
&data.styles,
Some(inherited_style),
&*doc_data,
/* is_probe = */ false,
Some(&matching_fn),
).unwrap().into()
}
#[no_mangle]
pub extern "C" fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
style: ServoStyleContextBorrowed)
@ -1965,6 +2027,7 @@ fn get_pseudo_style(
inherited_styles: Option<&ComputedValues>,
doc_data: &PerDocumentStyleDataImpl,
is_probe: bool,
matching_func: Option<&Fn(&PseudoElement) -> bool>,
) -> Option<Arc<ComputedValues>> {
let style = match pseudo.cascade_type() {
PseudoElementCascadeType::Eager => {
@ -2038,6 +2101,7 @@ fn get_pseudo_style(
base,
is_probe,
&metrics,
matching_func,
)
},
};
@ -3269,6 +3333,7 @@ pub extern "C" fn Servo_ResolveStyleLazily(
/* inherited_styles = */ None,
&*data,
is_probe,
/* matching_func = */ None,
)
}
None => Some(styles.primary().clone()),