mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
commit
b1e6f05ae4
13 changed files with 489 additions and 257 deletions
|
@ -408,7 +408,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
RuleInclusion::All,
|
RuleInclusion::All,
|
||||||
data.styles.primary(),
|
data.styles.primary(),
|
||||||
/* is_probe = */ false,
|
/* is_probe = */ false,
|
||||||
&ServoMetricsProvider)
|
&ServoMetricsProvider,
|
||||||
|
/* matching_func = */ None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,10 @@ where
|
||||||
/// The current nesting level of selectors that we're matching.
|
/// The current nesting level of selectors that we're matching.
|
||||||
pub nesting_level: usize,
|
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,
|
quirks_mode: QuirksMode,
|
||||||
classes_and_ids_case_sensitivity: CaseSensitivity,
|
classes_and_ids_case_sensitivity: CaseSensitivity,
|
||||||
_impl: ::std::marker::PhantomData<Impl>,
|
_impl: ::std::marker::PhantomData<Impl>,
|
||||||
|
@ -152,6 +156,7 @@ where
|
||||||
classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
|
classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
|
||||||
scope_element: None,
|
scope_element: None,
|
||||||
nesting_level: 0,
|
nesting_level: 0,
|
||||||
|
pseudo_element_matching_fn: None,
|
||||||
_impl: ::std::marker::PhantomData,
|
_impl: ::std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,9 +387,20 @@ where
|
||||||
if context.nesting_level == 0 &&
|
if context.nesting_level == 0 &&
|
||||||
context.matching_mode == MatchingMode::ForStatelessPseudoElement {
|
context.matching_mode == MatchingMode::ForStatelessPseudoElement {
|
||||||
// Consume the pseudo.
|
// Consume the pseudo.
|
||||||
let pseudo = iter.next().unwrap();
|
match *iter.next().unwrap() {
|
||||||
debug_assert!(matches!(*pseudo, Component::PseudoElement(..)),
|
Component::PseudoElement(ref pseudo) => {
|
||||||
"Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector");
|
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
|
// The only other parser-allowed Component in this sequence is a state
|
||||||
// class. We just don't match in that case.
|
// class. We just don't match in that case.
|
||||||
|
|
|
@ -15,6 +15,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue;
|
||||||
use gecko_bindings::structs::mozilla::css::URLValue;
|
use gecko_bindings::structs::mozilla::css::URLValue;
|
||||||
use gecko_bindings::structs::mozilla::css::URLValueData;
|
use gecko_bindings::structs::mozilla::css::URLValueData;
|
||||||
use gecko_bindings::structs::mozilla::AnonymousCounterStyle;
|
use gecko_bindings::structs::mozilla::AnonymousCounterStyle;
|
||||||
|
use gecko_bindings::structs::mozilla::AtomArray;
|
||||||
use gecko_bindings::structs::mozilla::MallocSizeOf;
|
use gecko_bindings::structs::mozilla::MallocSizeOf;
|
||||||
use gecko_bindings::structs::mozilla::OriginFlags;
|
use gecko_bindings::structs::mozilla::OriginFlags;
|
||||||
use gecko_bindings::structs::mozilla::UniquePtr;
|
use gecko_bindings::structs::mozilla::UniquePtr;
|
||||||
|
@ -2945,6 +2946,19 @@ extern "C" {
|
||||||
set: RawServoStyleSetBorrowed)
|
set: RawServoStyleSetBorrowed)
|
||||||
-> ServoStyleContextStrong;
|
-> 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" {
|
extern "C" {
|
||||||
pub fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
|
pub fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
|
||||||
primary_style: ServoStyleContextBorrowed);
|
primary_style: ServoStyleContextBorrowed);
|
||||||
|
|
|
@ -140,29 +140,29 @@ pub enum PseudoElement {
|
||||||
/// :-moz-ruby-text-container
|
/// :-moz-ruby-text-container
|
||||||
RubyTextContainer,
|
RubyTextContainer,
|
||||||
/// :-moz-tree-column
|
/// :-moz-tree-column
|
||||||
MozTreeColumn(Box<[String]>),
|
MozTreeColumn(Box<[Atom]>),
|
||||||
/// :-moz-tree-row
|
/// :-moz-tree-row
|
||||||
MozTreeRow(Box<[String]>),
|
MozTreeRow(Box<[Atom]>),
|
||||||
/// :-moz-tree-separator
|
/// :-moz-tree-separator
|
||||||
MozTreeSeparator(Box<[String]>),
|
MozTreeSeparator(Box<[Atom]>),
|
||||||
/// :-moz-tree-cell
|
/// :-moz-tree-cell
|
||||||
MozTreeCell(Box<[String]>),
|
MozTreeCell(Box<[Atom]>),
|
||||||
/// :-moz-tree-indentation
|
/// :-moz-tree-indentation
|
||||||
MozTreeIndentation(Box<[String]>),
|
MozTreeIndentation(Box<[Atom]>),
|
||||||
/// :-moz-tree-line
|
/// :-moz-tree-line
|
||||||
MozTreeLine(Box<[String]>),
|
MozTreeLine(Box<[Atom]>),
|
||||||
/// :-moz-tree-twisty
|
/// :-moz-tree-twisty
|
||||||
MozTreeTwisty(Box<[String]>),
|
MozTreeTwisty(Box<[Atom]>),
|
||||||
/// :-moz-tree-image
|
/// :-moz-tree-image
|
||||||
MozTreeImage(Box<[String]>),
|
MozTreeImage(Box<[Atom]>),
|
||||||
/// :-moz-tree-cell-text
|
/// :-moz-tree-cell-text
|
||||||
MozTreeCellText(Box<[String]>),
|
MozTreeCellText(Box<[Atom]>),
|
||||||
/// :-moz-tree-checkbox
|
/// :-moz-tree-checkbox
|
||||||
MozTreeCheckbox(Box<[String]>),
|
MozTreeCheckbox(Box<[Atom]>),
|
||||||
/// :-moz-tree-progressmeter
|
/// :-moz-tree-progressmeter
|
||||||
MozTreeProgressmeter(Box<[String]>),
|
MozTreeProgressmeter(Box<[Atom]>),
|
||||||
/// :-moz-tree-drop-feedback
|
/// :-moz-tree-drop-feedback
|
||||||
MozTreeDropFeedback(Box<[String]>),
|
MozTreeDropFeedback(Box<[Atom]>),
|
||||||
/// :-moz-svg-marker-anon-child
|
/// :-moz-svg-marker-anon-child
|
||||||
MozSVGMarkerAnonChild,
|
MozSVGMarkerAnonChild,
|
||||||
/// :-moz-svg-outer-svg-anon-child
|
/// :-moz-svg-outer-svg-anon-child
|
||||||
|
@ -185,6 +185,12 @@ pub const EAGER_PSEUDO_COUNT: usize = 4;
|
||||||
/// The number of non-functional pseudo-elements.
|
/// The number of non-functional pseudo-elements.
|
||||||
pub const SIMPLE_PSEUDO_COUNT: usize = 71;
|
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.
|
/// The list of eager pseudos.
|
||||||
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
|
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
|
||||||
PseudoElement::Before,
|
PseudoElement::Before,
|
||||||
|
@ -286,162 +292,184 @@ impl PseudoElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an index if the pseudo-element is a simple (non-functional)
|
/// Returns an index of the pseudo-element.
|
||||||
/// pseudo.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn simple_index(&self) -> Option<usize> {
|
pub fn index(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
PseudoElement::After => Some(0),
|
PseudoElement::After => 0,
|
||||||
PseudoElement::Before => Some(1),
|
PseudoElement::Before => 1,
|
||||||
PseudoElement::Backdrop => Some(2),
|
PseudoElement::Backdrop => 2,
|
||||||
PseudoElement::Cue => Some(3),
|
PseudoElement::Cue => 3,
|
||||||
PseudoElement::FirstLetter => Some(4),
|
PseudoElement::FirstLetter => 4,
|
||||||
PseudoElement::FirstLine => Some(5),
|
PseudoElement::FirstLine => 5,
|
||||||
PseudoElement::MozSelection => Some(6),
|
PseudoElement::MozSelection => 6,
|
||||||
PseudoElement::MozFocusInner => Some(7),
|
PseudoElement::MozFocusInner => 7,
|
||||||
PseudoElement::MozFocusOuter => Some(8),
|
PseudoElement::MozFocusOuter => 8,
|
||||||
PseudoElement::MozListBullet => Some(9),
|
PseudoElement::MozListBullet => 9,
|
||||||
PseudoElement::MozListNumber => Some(10),
|
PseudoElement::MozListNumber => 10,
|
||||||
PseudoElement::MozMathAnonymous => Some(11),
|
PseudoElement::MozMathAnonymous => 11,
|
||||||
PseudoElement::MozNumberWrapper => Some(12),
|
PseudoElement::MozNumberWrapper => 12,
|
||||||
PseudoElement::MozNumberText => Some(13),
|
PseudoElement::MozNumberText => 13,
|
||||||
PseudoElement::MozNumberSpinBox => Some(14),
|
PseudoElement::MozNumberSpinBox => 14,
|
||||||
PseudoElement::MozNumberSpinUp => Some(15),
|
PseudoElement::MozNumberSpinUp => 15,
|
||||||
PseudoElement::MozNumberSpinDown => Some(16),
|
PseudoElement::MozNumberSpinDown => 16,
|
||||||
PseudoElement::MozProgressBar => Some(17),
|
PseudoElement::MozProgressBar => 17,
|
||||||
PseudoElement::MozRangeTrack => Some(18),
|
PseudoElement::MozRangeTrack => 18,
|
||||||
PseudoElement::MozRangeProgress => Some(19),
|
PseudoElement::MozRangeProgress => 19,
|
||||||
PseudoElement::MozRangeThumb => Some(20),
|
PseudoElement::MozRangeThumb => 20,
|
||||||
PseudoElement::MozMeterBar => Some(21),
|
PseudoElement::MozMeterBar => 21,
|
||||||
PseudoElement::MozPlaceholder => Some(22),
|
PseudoElement::MozPlaceholder => 22,
|
||||||
PseudoElement::Placeholder => Some(23),
|
PseudoElement::Placeholder => 23,
|
||||||
PseudoElement::MozColorSwatch => Some(24),
|
PseudoElement::MozColorSwatch => 24,
|
||||||
PseudoElement::MozText => Some(25),
|
PseudoElement::MozText => 25,
|
||||||
PseudoElement::OofPlaceholder => Some(26),
|
PseudoElement::OofPlaceholder => 26,
|
||||||
PseudoElement::FirstLetterContinuation => Some(27),
|
PseudoElement::FirstLetterContinuation => 27,
|
||||||
PseudoElement::MozBlockInsideInlineWrapper => Some(28),
|
PseudoElement::MozBlockInsideInlineWrapper => 28,
|
||||||
PseudoElement::MozMathMLAnonymousBlock => Some(29),
|
PseudoElement::MozMathMLAnonymousBlock => 29,
|
||||||
PseudoElement::MozXULAnonymousBlock => Some(30),
|
PseudoElement::MozXULAnonymousBlock => 30,
|
||||||
PseudoElement::HorizontalFramesetBorder => Some(31),
|
PseudoElement::HorizontalFramesetBorder => 31,
|
||||||
PseudoElement::VerticalFramesetBorder => Some(32),
|
PseudoElement::VerticalFramesetBorder => 32,
|
||||||
PseudoElement::MozLineFrame => Some(33),
|
PseudoElement::MozLineFrame => 33,
|
||||||
PseudoElement::ButtonContent => Some(34),
|
PseudoElement::ButtonContent => 34,
|
||||||
PseudoElement::CellContent => Some(35),
|
PseudoElement::CellContent => 35,
|
||||||
PseudoElement::DropDownList => Some(36),
|
PseudoElement::DropDownList => 36,
|
||||||
PseudoElement::FieldsetContent => Some(37),
|
PseudoElement::FieldsetContent => 37,
|
||||||
PseudoElement::FramesetBlank => Some(38),
|
PseudoElement::FramesetBlank => 38,
|
||||||
PseudoElement::MozDisplayComboboxControlFrame => Some(39),
|
PseudoElement::MozDisplayComboboxControlFrame => 39,
|
||||||
PseudoElement::HtmlCanvasContent => Some(40),
|
PseudoElement::HtmlCanvasContent => 40,
|
||||||
PseudoElement::InlineTable => Some(41),
|
PseudoElement::InlineTable => 41,
|
||||||
PseudoElement::Table => Some(42),
|
PseudoElement::Table => 42,
|
||||||
PseudoElement::TableCell => Some(43),
|
PseudoElement::TableCell => 43,
|
||||||
PseudoElement::TableColGroup => Some(44),
|
PseudoElement::TableColGroup => 44,
|
||||||
PseudoElement::TableCol => Some(45),
|
PseudoElement::TableCol => 45,
|
||||||
PseudoElement::TableWrapper => Some(46),
|
PseudoElement::TableWrapper => 46,
|
||||||
PseudoElement::TableRowGroup => Some(47),
|
PseudoElement::TableRowGroup => 47,
|
||||||
PseudoElement::TableRow => Some(48),
|
PseudoElement::TableRow => 48,
|
||||||
PseudoElement::Canvas => Some(49),
|
PseudoElement::Canvas => 49,
|
||||||
PseudoElement::PageBreak => Some(50),
|
PseudoElement::PageBreak => 50,
|
||||||
PseudoElement::Page => Some(51),
|
PseudoElement::Page => 51,
|
||||||
PseudoElement::PageContent => Some(52),
|
PseudoElement::PageContent => 52,
|
||||||
PseudoElement::PageSequence => Some(53),
|
PseudoElement::PageSequence => 53,
|
||||||
PseudoElement::ScrolledContent => Some(54),
|
PseudoElement::ScrolledContent => 54,
|
||||||
PseudoElement::ScrolledCanvas => Some(55),
|
PseudoElement::ScrolledCanvas => 55,
|
||||||
PseudoElement::ScrolledPageSequence => Some(56),
|
PseudoElement::ScrolledPageSequence => 56,
|
||||||
PseudoElement::ColumnContent => Some(57),
|
PseudoElement::ColumnContent => 57,
|
||||||
PseudoElement::Viewport => Some(58),
|
PseudoElement::Viewport => 58,
|
||||||
PseudoElement::ViewportScroll => Some(59),
|
PseudoElement::ViewportScroll => 59,
|
||||||
PseudoElement::AnonymousFlexItem => Some(60),
|
PseudoElement::AnonymousFlexItem => 60,
|
||||||
PseudoElement::AnonymousGridItem => Some(61),
|
PseudoElement::AnonymousGridItem => 61,
|
||||||
PseudoElement::Ruby => Some(62),
|
PseudoElement::Ruby => 62,
|
||||||
PseudoElement::RubyBase => Some(63),
|
PseudoElement::RubyBase => 63,
|
||||||
PseudoElement::RubyBaseContainer => Some(64),
|
PseudoElement::RubyBaseContainer => 64,
|
||||||
PseudoElement::RubyText => Some(65),
|
PseudoElement::RubyText => 65,
|
||||||
PseudoElement::RubyTextContainer => Some(66),
|
PseudoElement::RubyTextContainer => 66,
|
||||||
PseudoElement::MozSVGMarkerAnonChild => Some(67),
|
PseudoElement::MozTreeColumn(..) => 67,
|
||||||
PseudoElement::MozSVGOuterSVGAnonChild => Some(68),
|
PseudoElement::MozTreeRow(..) => 68,
|
||||||
PseudoElement::MozSVGForeignContent => Some(69),
|
PseudoElement::MozTreeSeparator(..) => 69,
|
||||||
PseudoElement::MozSVGText => Some(70),
|
PseudoElement::MozTreeCell(..) => 70,
|
||||||
_ => None,
|
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.
|
/// Returns an array of `None` values.
|
||||||
///
|
///
|
||||||
/// FIXME(emilio): Integer generics can't come soon enough.
|
/// 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,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
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)
|
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
|
/// Gets the flags associated to this pseudo-element, or 0 if it's an
|
||||||
/// anonymous box.
|
/// anonymous box.
|
||||||
pub fn flags(&self) -> u32 {
|
pub fn flags(&self) -> u32 {
|
||||||
|
@ -847,18 +895,18 @@ None
|
||||||
PseudoElement::RubyBaseContainer => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::RubyBaseContainer => CSSPseudoElementType_InheritingAnonBox,
|
||||||
PseudoElement::RubyText => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::RubyText => CSSPseudoElementType_InheritingAnonBox,
|
||||||
PseudoElement::RubyTextContainer => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::RubyTextContainer => CSSPseudoElementType_InheritingAnonBox,
|
||||||
PseudoElement::MozTreeColumn(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeColumn(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeRow(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeRow(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeSeparator(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeSeparator(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeCell(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeCell(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeIndentation(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeIndentation(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeLine(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeLine(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeTwisty(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeTwisty(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeImage(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeImage(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeCellText(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeCellText(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeCheckbox(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeCheckbox(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeProgressmeter(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeProgressmeter(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozTreeDropFeedback(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozTreeDropFeedback(..) => CSSPseudoElementType::XULTree,
|
||||||
PseudoElement::MozSVGMarkerAnonChild => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozSVGMarkerAnonChild => CSSPseudoElementType_InheritingAnonBox,
|
||||||
PseudoElement::MozSVGOuterSVGAnonChild => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozSVGOuterSVGAnonChild => CSSPseudoElementType_InheritingAnonBox,
|
||||||
PseudoElement::MozSVGForeignContent => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::MozSVGForeignContent => CSSPseudoElementType_InheritingAnonBox,
|
||||||
|
@ -871,6 +919,26 @@ None
|
||||||
(self.atom().as_ptr(), self.pseudo_type())
|
(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`.
|
/// Construct a pseudo-element from an `Atom`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_atom(atom: &Atom) -> Option<Self> {
|
pub fn from_atom(atom: &Atom) -> Option<Self> {
|
||||||
|
@ -1258,6 +1326,48 @@ None
|
||||||
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
|
/// Constructs an atom from a string of text, and whether we're in a
|
||||||
/// user-agent stylesheet.
|
/// user-agent stylesheet.
|
||||||
///
|
///
|
||||||
|
@ -1636,7 +1746,7 @@ None
|
||||||
///
|
///
|
||||||
/// Returns `None` if the pseudo-element is not recognized.
|
/// Returns `None` if the pseudo-element is not recognized.
|
||||||
#[inline]
|
#[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;
|
use std::ascii::AsciiExt;
|
||||||
debug_assert!(name.starts_with("-moz-tree-"));
|
debug_assert!(name.starts_with("-moz-tree-"));
|
||||||
let tree_part = &name[10..];
|
let tree_part = &name[10..];
|
||||||
|
@ -1768,31 +1878,20 @@ impl ToCss for PseudoElement {
|
||||||
PseudoElement::MozSVGForeignContent => dest.write_str(":-moz-svg-foreign-content")?,
|
PseudoElement::MozSVGForeignContent => dest.write_str(":-moz-svg-foreign-content")?,
|
||||||
PseudoElement::MozSVGText => dest.write_str(":-moz-svg-text")?,
|
PseudoElement::MozSVGText => dest.write_str(":-moz-svg-text")?,
|
||||||
}
|
}
|
||||||
match *self {
|
if let Some(args) = self.tree_pseudo_args() {
|
||||||
PseudoElement::MozTreeColumn(ref args) |
|
if !args.is_empty() {
|
||||||
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) => {
|
|
||||||
dest.write_char('(')?;
|
dest.write_char('(')?;
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
if let Some(first) = iter.next() {
|
if let Some(first) = iter.next() {
|
||||||
serialize_identifier(first, dest)?;
|
serialize_identifier(&first.to_string(), dest)?;
|
||||||
for item in iter {
|
for item in iter {
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
serialize_identifier(item, dest)?;
|
serialize_identifier(&item.to_string(), dest)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dest.write_char(')')
|
dest.write_char(')')?;
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4175,6 +4175,7 @@ pub mod root {
|
||||||
pub struct ShortcutKeyCandidate {
|
pub struct ShortcutKeyCandidate {
|
||||||
_unused: [u8; 0],
|
_unused: [u8; 0],
|
||||||
}
|
}
|
||||||
|
pub type AtomArray = root::nsTArray<root::RefPtr<root::nsAtom>>;
|
||||||
/// EventStates is the class used to represent the event states of nsIContent
|
/// EventStates is the class used to represent the event states of nsIContent
|
||||||
/// instances. These states are calculated by IntrinsicState() and
|
/// instances. These states are calculated by IntrinsicState() and
|
||||||
/// ContentStatesChanged() has to be called when one of them changes thus
|
/// ContentStatesChanged() has to be called when one of them changes thus
|
||||||
|
@ -22385,8 +22386,6 @@ pub mod root {
|
||||||
pub struct nsAttrValue {
|
pub struct nsAttrValue {
|
||||||
pub mBits: usize,
|
pub mBits: usize,
|
||||||
}
|
}
|
||||||
pub type nsAttrValue_AtomArray =
|
|
||||||
root::nsTArray<root::RefPtr<root::nsAtom>>;
|
|
||||||
pub const nsAttrValue_ValueType_eSVGTypesBegin:
|
pub const nsAttrValue_ValueType_eSVGTypesBegin:
|
||||||
root::nsAttrValue_ValueType =
|
root::nsAttrValue_ValueType =
|
||||||
nsAttrValue_ValueType::eSVGAngle;
|
nsAttrValue_ValueType::eSVGAngle;
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl PseudoElement {
|
||||||
return PseudoElementCascadeType::Eager
|
return PseudoElementCascadeType::Eager
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_anon_box() {
|
if self.is_precomputed() {
|
||||||
return PseudoElementCascadeType::Precomputed
|
return PseudoElementCascadeType::Precomputed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ impl PseudoElement {
|
||||||
/// Whether this pseudo-element is precomputed.
|
/// Whether this pseudo-element is precomputed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_precomputed(&self) -> bool {
|
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
|
/// Covert non-canonical pseudo-element to canonical one, and keep a
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub enum PseudoElement {
|
||||||
% for pseudo in PSEUDOS:
|
% for pseudo in PSEUDOS:
|
||||||
/// ${pseudo.value}
|
/// ${pseudo.value}
|
||||||
% if pseudo.is_tree_pseudo_element():
|
% if pseudo.is_tree_pseudo_element():
|
||||||
${pseudo.capitalized()}(Box<[String]>),
|
${pseudo.capitalized()}(Box<[Atom]>),
|
||||||
% else:
|
% else:
|
||||||
${pseudo.capitalized()},
|
${pseudo.capitalized()},
|
||||||
% endif
|
% endif
|
||||||
|
@ -27,6 +27,12 @@ pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
|
||||||
/// The number of non-functional pseudo-elements.
|
/// The number of non-functional pseudo-elements.
|
||||||
pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)};
|
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.
|
/// The list of eager pseudos.
|
||||||
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
|
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
|
||||||
% for eager_pseudo_name in EAGER_PSEUDOS:
|
% 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)
|
/// Returns an index of the pseudo-element.
|
||||||
/// pseudo.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn simple_index(&self) -> Option<usize> {
|
pub fn index(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
% for i, pseudo in enumerate(SIMPLE_PSEUDOS):
|
% for i, pseudo in enumerate(PSEUDOS):
|
||||||
${pseudo_element_variant(pseudo)} => Some(${i}),
|
${pseudo_element_variant(pseudo)} => ${i},
|
||||||
% endfor
|
% endfor
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an array of `None` values.
|
/// Returns an array of `None` values.
|
||||||
///
|
///
|
||||||
/// FIXME(emilio): Integer generics can't come soon enough.
|
/// 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))})
|
${" | ".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
|
/// Gets the flags associated to this pseudo-element, or 0 if it's an
|
||||||
/// anonymous box.
|
/// anonymous box.
|
||||||
pub fn flags(&self) -> u32 {
|
pub fn flags(&self) -> u32 {
|
||||||
|
@ -132,7 +147,7 @@ impl PseudoElement {
|
||||||
% if not pseudo.is_anon_box():
|
% if not pseudo.is_anon_box():
|
||||||
PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType::${pseudo.original_ident},
|
PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType::${pseudo.original_ident},
|
||||||
% elif pseudo.is_tree_pseudo_element():
|
% elif pseudo.is_tree_pseudo_element():
|
||||||
PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType::XULTree,
|
||||||
% elif pseudo.is_inheriting_anon_box():
|
% elif pseudo.is_inheriting_anon_box():
|
||||||
PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType_InheritingAnonBox,
|
PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType_InheritingAnonBox,
|
||||||
% else:
|
% else:
|
||||||
|
@ -147,6 +162,17 @@ impl PseudoElement {
|
||||||
(self.atom().as_ptr(), self.pseudo_type())
|
(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`.
|
/// Construct a pseudo-element from an `Atom`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_atom(atom: &Atom) -> Option<Self> {
|
pub fn from_atom(atom: &Atom) -> Option<Self> {
|
||||||
|
@ -177,6 +203,19 @@ impl PseudoElement {
|
||||||
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> {
|
||||||
|
% 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
|
/// Constructs an atom from a string of text, and whether we're in a
|
||||||
/// user-agent stylesheet.
|
/// user-agent stylesheet.
|
||||||
///
|
///
|
||||||
|
@ -207,7 +246,7 @@ impl PseudoElement {
|
||||||
///
|
///
|
||||||
/// Returns `None` if the pseudo-element is not recognized.
|
/// Returns `None` if the pseudo-element is not recognized.
|
||||||
#[inline]
|
#[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;
|
use std::ascii::AsciiExt;
|
||||||
debug_assert!(name.starts_with("-moz-tree-"));
|
debug_assert!(name.starts_with("-moz-tree-"));
|
||||||
let tree_part = &name[10..];
|
let tree_part = &name[10..];
|
||||||
|
@ -228,21 +267,20 @@ impl ToCss for PseudoElement {
|
||||||
${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
|
${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
match *self {
|
if let Some(args) = self.tree_pseudo_args() {
|
||||||
${" |\n ".join("PseudoElement::{}(ref args)".format(pseudo.capitalized())
|
if !args.is_empty() {
|
||||||
for pseudo in TREE_PSEUDOS)} => {
|
|
||||||
dest.write_char('(')?;
|
dest.write_char('(')?;
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
if let Some(first) = iter.next() {
|
if let Some(first) = iter.next() {
|
||||||
serialize_identifier(first, dest)?;
|
serialize_identifier(&first.to_string(), dest)?;
|
||||||
for item in iter {
|
for item in iter {
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
serialize_identifier(item, dest)?;
|
serialize_identifier(&item.to_string(), dest)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dest.write_char(')')
|
dest.write_char(')')?;
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use std::fmt;
|
||||||
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||||
use style_traits::{ParseError, StyleParseErrorKind};
|
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;
|
pub use gecko::snapshot::SnapshotMap;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -388,6 +388,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
|
fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
|
||||||
-> Result<PseudoElement, ParseError<'i>> {
|
-> Result<PseudoElement, ParseError<'i>> {
|
||||||
PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
|
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())))
|
.ok_or(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +408,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
loop {
|
loop {
|
||||||
let location = parser.current_source_location();
|
let location = parser.current_source_location();
|
||||||
match parser.next() {
|
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(&Token::Comma) => {},
|
||||||
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
|
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
|
||||||
Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => break,
|
Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => break,
|
||||||
|
|
|
@ -102,16 +102,16 @@ pub enum PseudoElementCascadeType {
|
||||||
Precomputed,
|
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)]
|
#[derive(MallocSizeOf)]
|
||||||
pub struct PerPseudoElementMap<T> {
|
pub struct PerPseudoElementMap<T> {
|
||||||
entries: [Option<T>; SIMPLE_PSEUDO_COUNT],
|
entries: [Option<T>; PSEUDO_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for PerPseudoElementMap<T> {
|
impl<T> Default for PerPseudoElementMap<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
entries: PseudoElement::simple_pseudo_none_array(),
|
entries: PseudoElement::pseudo_none_array(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,11 +137,7 @@ where
|
||||||
impl<T> PerPseudoElementMap<T> {
|
impl<T> PerPseudoElementMap<T> {
|
||||||
/// Get an entry in the map.
|
/// Get an entry in the map.
|
||||||
pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {
|
pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {
|
||||||
let index = match pseudo.simple_index() {
|
self.entries[pseudo.index()].as_ref()
|
||||||
Some(i) => i,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
self.entries[index].as_ref()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear this enumerated array.
|
/// Clear this enumerated array.
|
||||||
|
@ -161,13 +157,8 @@ impl<T> PerPseudoElementMap<T> {
|
||||||
/// Set an entry value.
|
/// Set an entry value.
|
||||||
///
|
///
|
||||||
/// Returns an error if the element is not a simple pseudo.
|
/// Returns an error if the element is not a simple pseudo.
|
||||||
pub fn set(&mut self, pseudo: &PseudoElement, value: T) -> Result<(), ()> {
|
pub fn set(&mut self, pseudo: &PseudoElement, value: T) {
|
||||||
let index = match pseudo.simple_index() {
|
self.entries[pseudo.index()] = Some(value);
|
||||||
Some(i) => i,
|
|
||||||
None => return Err(()),
|
|
||||||
};
|
|
||||||
self.entries[index] = Some(value);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an entry for `pseudo`, or create it with calling `f`.
|
/// Get an entry for `pseudo`, or create it with calling `f`.
|
||||||
|
@ -175,18 +166,15 @@ impl<T> PerPseudoElementMap<T> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<&mut T, ()>
|
) -> &mut T
|
||||||
where
|
where
|
||||||
F: FnOnce() -> T,
|
F: FnOnce() -> T,
|
||||||
{
|
{
|
||||||
let index = match pseudo.simple_index() {
|
let index = pseudo.index();
|
||||||
Some(i) => i,
|
|
||||||
None => return Err(()),
|
|
||||||
};
|
|
||||||
if self.entries[index].is_none() {
|
if self.entries[index].is_none() {
|
||||||
self.entries[index] = Some(f());
|
self.entries[index] = Some(f());
|
||||||
}
|
}
|
||||||
Ok(self.entries[index].as_mut().unwrap())
|
self.entries[index].as_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator for the entries.
|
/// Get an iterator for the entries.
|
||||||
|
|
|
@ -62,9 +62,8 @@ pub enum PseudoElement {
|
||||||
ServoInlineAbsolute,
|
ServoInlineAbsolute,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The count of simple (non-functional) pseudo-elements (that is, all
|
/// The count of all pseudo-elements.
|
||||||
/// pseudo-elements for now).
|
pub const PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1;
|
||||||
pub const SIMPLE_PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1;
|
|
||||||
|
|
||||||
impl ::selectors::parser::PseudoElement for PseudoElement {
|
impl ::selectors::parser::PseudoElement for PseudoElement {
|
||||||
type Impl = SelectorImpl;
|
type Impl = SelectorImpl;
|
||||||
|
@ -110,12 +109,12 @@ impl PseudoElement {
|
||||||
|
|
||||||
/// An index for this pseudo-element to be indexed in an enumerated array.
|
/// An index for this pseudo-element to be indexed in an enumerated array.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn simple_index(&self) -> Option<usize> {
|
pub fn index(&self) -> usize {
|
||||||
Some(self.clone() as usize)
|
self.clone() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An array of `None`, one per simple pseudo-element.
|
/// An array of `None`, one per pseudo-element.
|
||||||
pub fn simple_pseudo_none_array<T>() -> [Option<T>; SIMPLE_PSEUDO_COUNT] {
|
pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -819,13 +819,21 @@ impl Stylist {
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
parent_style: &ComputedValues,
|
parent_style: &ComputedValues,
|
||||||
is_probe: bool,
|
is_probe: bool,
|
||||||
font_metrics: &FontMetricsProvider
|
font_metrics: &FontMetricsProvider,
|
||||||
|
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
|
||||||
) -> Option<Arc<ComputedValues>>
|
) -> Option<Arc<ComputedValues>>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
let cascade_inputs =
|
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(
|
self.compute_pseudo_element_style_with_inputs(
|
||||||
&cascade_inputs,
|
&cascade_inputs,
|
||||||
pseudo,
|
pseudo,
|
||||||
|
@ -979,7 +987,8 @@ impl Stylist {
|
||||||
element: &E,
|
element: &E,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
is_probe: bool,
|
is_probe: bool,
|
||||||
rule_inclusion: RuleInclusion
|
rule_inclusion: RuleInclusion,
|
||||||
|
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
|
||||||
) -> CascadeInputs
|
) -> CascadeInputs
|
||||||
where
|
where
|
||||||
E: TElement
|
E: TElement
|
||||||
|
@ -1026,6 +1035,7 @@ impl Stylist {
|
||||||
None,
|
None,
|
||||||
self.quirks_mode,
|
self.quirks_mode,
|
||||||
);
|
);
|
||||||
|
matching_context.pseudo_element_matching_fn = matching_fn;
|
||||||
|
|
||||||
self.push_applicable_declarations(
|
self.push_applicable_declarations(
|
||||||
element,
|
element,
|
||||||
|
@ -1062,6 +1072,7 @@ impl Stylist {
|
||||||
VisitedHandlingMode::RelevantLinkVisited,
|
VisitedHandlingMode::RelevantLinkVisited,
|
||||||
self.quirks_mode,
|
self.quirks_mode,
|
||||||
);
|
);
|
||||||
|
matching_context.pseudo_element_matching_fn = matching_fn;
|
||||||
|
|
||||||
self.push_applicable_declarations(
|
self.push_applicable_declarations(
|
||||||
element,
|
element,
|
||||||
|
@ -1289,6 +1300,7 @@ impl Stylist {
|
||||||
context.nth_index_cache.as_mut().map(|s| &mut **s),
|
context.nth_index_cache.as_mut().map(|s| &mut **s),
|
||||||
stylist.quirks_mode,
|
stylist.quirks_mode,
|
||||||
);
|
);
|
||||||
|
matching_context.pseudo_element_matching_fn = context.pseudo_element_matching_fn;
|
||||||
|
|
||||||
map.get_all_matching_rules(
|
map.get_all_matching_rules(
|
||||||
element,
|
element,
|
||||||
|
@ -1994,19 +2006,13 @@ impl CascadeData {
|
||||||
|
|
||||||
let map = match selector.pseudo_element() {
|
let map = match selector.pseudo_element() {
|
||||||
Some(pseudo) if pseudo.is_precomputed() => {
|
Some(pseudo) if pseudo.is_precomputed() => {
|
||||||
if !selector.is_universal() ||
|
debug_assert!(selector.is_universal());
|
||||||
!matches!(origin, Origin::UserAgent) {
|
debug_assert!(matches!(origin, Origin::UserAgent));
|
||||||
// ::-moz-tree selectors may appear in
|
|
||||||
// non-UA sheets (even though they never
|
|
||||||
// match).
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
precomputed_pseudo_element_decls
|
precomputed_pseudo_element_decls
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("Expected precomputed declarations for the UA level")
|
.expect("Expected precomputed declarations for the UA level")
|
||||||
.get_or_insert_with(&pseudo.canonical(), Vec::new)
|
.get_or_insert_with(&pseudo.canonical(), Vec::new)
|
||||||
.expect("Unexpected tree pseudo-element?")
|
|
||||||
.push(ApplicableDeclarationBlock::new(
|
.push(ApplicableDeclarationBlock::new(
|
||||||
StyleSource::Style(locked.clone()),
|
StyleSource::Style(locked.clone()),
|
||||||
self.rules_source_order,
|
self.rules_source_order,
|
||||||
|
@ -2023,7 +2029,7 @@ impl CascadeData {
|
||||||
let mut map = Box::new(SelectorMap::new());
|
let mut map = Box::new(SelectorMap::new());
|
||||||
map.begin_mutation();
|
map.begin_mutation();
|
||||||
map
|
map
|
||||||
}).expect("Unexpected tree pseudo-element?")
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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::{ServoStyleSheet, SheetParsingMode, nsAtom, nsCSSPropertyID};
|
||||||
use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
|
use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
|
||||||
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
|
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::IterationCompositeOperation;
|
||||||
use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
|
use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
|
||||||
use style::gecko_bindings::structs::OriginFlags;
|
use style::gecko_bindings::structs::OriginFlags;
|
||||||
|
@ -126,7 +127,7 @@ use style::rule_cache::RuleCacheConditions;
|
||||||
use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
|
use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
|
||||||
use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
|
use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
|
||||||
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
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::style_adjuster::StyleAdjuster;
|
||||||
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
|
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
|
||||||
use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule};
|
use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule};
|
||||||
|
@ -1911,6 +1912,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
inherited_style,
|
inherited_style,
|
||||||
&*doc_data,
|
&*doc_data,
|
||||||
is_probe,
|
is_probe,
|
||||||
|
/* matching_func = */ None,
|
||||||
);
|
);
|
||||||
|
|
||||||
match style {
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
|
pub extern "C" fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
|
||||||
style: ServoStyleContextBorrowed)
|
style: ServoStyleContextBorrowed)
|
||||||
|
@ -1965,6 +2027,7 @@ fn get_pseudo_style(
|
||||||
inherited_styles: Option<&ComputedValues>,
|
inherited_styles: Option<&ComputedValues>,
|
||||||
doc_data: &PerDocumentStyleDataImpl,
|
doc_data: &PerDocumentStyleDataImpl,
|
||||||
is_probe: bool,
|
is_probe: bool,
|
||||||
|
matching_func: Option<&Fn(&PseudoElement) -> bool>,
|
||||||
) -> Option<Arc<ComputedValues>> {
|
) -> Option<Arc<ComputedValues>> {
|
||||||
let style = match pseudo.cascade_type() {
|
let style = match pseudo.cascade_type() {
|
||||||
PseudoElementCascadeType::Eager => {
|
PseudoElementCascadeType::Eager => {
|
||||||
|
@ -2038,6 +2101,7 @@ fn get_pseudo_style(
|
||||||
base,
|
base,
|
||||||
is_probe,
|
is_probe,
|
||||||
&metrics,
|
&metrics,
|
||||||
|
matching_func,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3269,6 +3333,7 @@ pub extern "C" fn Servo_ResolveStyleLazily(
|
||||||
/* inherited_styles = */ None,
|
/* inherited_styles = */ None,
|
||||||
&*data,
|
&*data,
|
||||||
is_probe,
|
is_probe,
|
||||||
|
/* matching_func = */ None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => Some(styles.primary().clone()),
|
None => Some(styles.primary().clone()),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue