mirror of
https://github.com/servo/servo.git
synced 2025-08-11 08:25:32 +01:00
Bug 1364412: Convert pseudo-elements to an enum. r=hiro,xidorn
This commit is contained in:
parent
0bc185f1c2
commit
5820e3ecac
17 changed files with 2260 additions and 1505 deletions
|
@ -26,6 +26,7 @@ use gecko_bindings::structs::RawGeckoServoStyleRuleList;
|
|||
use gecko_bindings::structs::RawGeckoURLExtraData;
|
||||
use gecko_bindings::structs::RefPtr;
|
||||
use gecko_bindings::structs::CSSPseudoClassType;
|
||||
use gecko_bindings::structs::CSSPseudoElementType;
|
||||
use gecko_bindings::structs::TraversalRestyleBehavior;
|
||||
use gecko_bindings::structs::TraversalRootBehavior;
|
||||
use gecko_bindings::structs::ComputedTimingFunction_BeforeFlag;
|
||||
|
@ -905,7 +906,7 @@ extern "C" {
|
|||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed)
|
||||
-> *mut nsIAtom;
|
||||
-> CSSPseudoElementType;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
||||
|
@ -2217,7 +2218,7 @@ extern "C" {
|
|||
extern "C" {
|
||||
pub fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
||||
ServoComputedValuesBorrowedOrNull,
|
||||
pseudoTag: *mut nsIAtom,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
skip_display_fixup: bool,
|
||||
set:
|
||||
RawServoStyleSetBorrowed)
|
||||
|
@ -2257,25 +2258,19 @@ extern "C" {
|
|||
}
|
||||
extern "C" {
|
||||
pub fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||
pseudo_tag: *mut nsIAtom, is_probe: bool,
|
||||
pseudo_type: CSSPseudoElementType,
|
||||
is_probe: bool,
|
||||
set: RawServoStyleSetBorrowed)
|
||||
-> ServoComputedValuesStrong;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
set: RawServoStyleSetBorrowed)
|
||||
-> RawServoRuleNodeStrong;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed,
|
||||
element: RawGeckoElementBorrowed,
|
||||
pub fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed,
|
||||
rule_type_mask: u32,
|
||||
author_colors_allowed: bool) -> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
pseudo_type: CSSPseudoElementType,
|
||||
snapshots:
|
||||
*const ServoElementSnapshotTable,
|
||||
set: RawServoStyleSetBorrowed)
|
||||
|
@ -2299,8 +2294,8 @@ extern "C" {
|
|||
RawGeckoElementBorrowed,
|
||||
snapshots:
|
||||
*const ServoElementSnapshotTable,
|
||||
pseudo_tag:
|
||||
*mut nsIAtom)
|
||||
pseudo_type:
|
||||
CSSPseudoElementType)
|
||||
-> ServoComputedValuesStrong;
|
||||
}
|
||||
extern "C" {
|
||||
|
|
1252
components/style/gecko/generated/pseudo_element_definition.rs
Normal file
1252
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 global_style_data;
|
||||
pub mod media_queries;
|
||||
pub mod pseudo_element;
|
||||
pub mod restyle_damage;
|
||||
pub mod rules;
|
||||
pub mod selector_parser;
|
||||
|
|
71
components/style/gecko/pseudo_element.rs
Normal file
71
components/style/gecko/pseudo_element.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* 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::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()
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
128
components/style/gecko/pseudo_element_definition.mako.rs
Normal file
128
components/style/gecko/pseudo_element_definition.mako.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
/* 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))})
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is precomputed.
|
||||
#[inline]
|
||||
pub fn is_precomputed(&self) -> bool {
|
||||
self.is_anon_box()
|
||||
}
|
||||
|
||||
/// 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 !${str(pseudo.is_anon_box()).lower()} || in_ua_stylesheet {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
248
components/style/gecko/regen_atoms.py
Executable file
248
components/style/gecko/regen_atoms.py
Executable file
|
@ -0,0 +1,248 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# 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/.
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
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 = """
|
||||
/* 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 */
|
||||
"""[1:]
|
||||
|
||||
|
||||
def gnu_symbolify(source, ident):
|
||||
return "_ZN{}{}{}{}E".format(len(source.CLASS), source.CLASS, len(ident), ident)
|
||||
|
||||
|
||||
def msvc64_symbolify(source, ident):
|
||||
return "?{}@{}@@2PEAV{}@@EA".format(ident, source.CLASS, source.TYPE)
|
||||
|
||||
|
||||
def msvc32_symbolify(source, ident):
|
||||
# Prepend "\x01" to avoid LLVM prefixing the mangled name with "_".
|
||||
# See https://github.com/rust-lang/rust/issues/36097
|
||||
return "\\x01?{}@{}@@2PAV{}@@A".format(ident, source.CLASS, source.TYPE)
|
||||
|
||||
|
||||
class GkAtomSource:
|
||||
PATTERN = re.compile('^GK_ATOM\((.+),\s*"(.*)"\)')
|
||||
FILE = "include/nsGkAtomList.h"
|
||||
CLASS = "nsGkAtoms"
|
||||
TYPE = "nsIAtom"
|
||||
|
||||
|
||||
class CSSPseudoElementsAtomSource:
|
||||
PATTERN = re.compile('^CSS_PSEUDO_ELEMENT\((.+),\s*"(.*)",')
|
||||
FILE = "include/nsCSSPseudoElementList.h"
|
||||
CLASS = "nsCSSPseudoElements"
|
||||
# NB: nsICSSPseudoElement is effectively the same as a nsIAtom, but we need
|
||||
# this for MSVC name mangling.
|
||||
TYPE = "nsICSSPseudoElement"
|
||||
|
||||
|
||||
class CSSAnonBoxesAtomSource:
|
||||
PATTERN = re.compile('^(?:CSS_ANON_BOX|CSS_NON_INHERITING_ANON_BOX)\((.+),\s*"(.*)"(\,|\))')
|
||||
FILE = "include/nsCSSAnonBoxList.h"
|
||||
CLASS = "nsCSSAnonBoxes"
|
||||
TYPE = "nsICSSAnonBoxPseudo"
|
||||
|
||||
|
||||
SOURCES = [
|
||||
GkAtomSource,
|
||||
CSSPseudoElementsAtomSource,
|
||||
CSSAnonBoxesAtomSource,
|
||||
]
|
||||
|
||||
|
||||
def map_atom(ident):
|
||||
if ident in {"box", "loop", "match", "mod", "ref",
|
||||
"self", "type", "use", "where", "in"}:
|
||||
return ident + "_"
|
||||
return ident
|
||||
|
||||
|
||||
class Atom:
|
||||
def __init__(self, source, ident, value):
|
||||
self.ident = "{}_{}".format(source.CLASS, ident)
|
||||
self.original_ident = ident
|
||||
self.value = value
|
||||
self.source = source
|
||||
|
||||
def cpp_class(self):
|
||||
return self.source.CLASS
|
||||
|
||||
def gnu_symbol(self):
|
||||
return gnu_symbolify(self.source, self.original_ident)
|
||||
|
||||
def msvc32_symbol(self):
|
||||
return msvc32_symbolify(self.source, self.original_ident)
|
||||
|
||||
def msvc64_symbol(self):
|
||||
return msvc64_symbolify(self.source, self.original_ident)
|
||||
|
||||
def type(self):
|
||||
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):
|
||||
atoms = []
|
||||
for source in SOURCES:
|
||||
path = os.path.abspath(os.path.join(objdir, source.FILE))
|
||||
print("cargo:rerun-if-changed={}".format(path))
|
||||
with open(path) as f:
|
||||
for line in f.readlines():
|
||||
result = re.match(source.PATTERN, line)
|
||||
if result:
|
||||
atoms.append(Atom(source, result.group(1), result.group(2)))
|
||||
return atoms
|
||||
|
||||
|
||||
class FileAvoidWrite(BytesIO):
|
||||
"""File-like object that buffers output and only writes if content changed."""
|
||||
def __init__(self, filename):
|
||||
BytesIO.__init__(self)
|
||||
self.name = filename
|
||||
|
||||
def write(self, buf):
|
||||
if isinstance(buf, unicode):
|
||||
buf = buf.encode('utf-8')
|
||||
BytesIO.write(self, buf)
|
||||
|
||||
def close(self):
|
||||
buf = self.getvalue()
|
||||
BytesIO.close(self)
|
||||
try:
|
||||
with open(self.name, 'rb') as f:
|
||||
old_content = f.read()
|
||||
if old_content == buf:
|
||||
print("{} is not changed, skip".format(self.name))
|
||||
return
|
||||
except IOError:
|
||||
pass
|
||||
with open(self.name, 'wb') as f:
|
||||
f.write(buf)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if not self.closed:
|
||||
self.close()
|
||||
|
||||
|
||||
IMPORTS = ("\nuse gecko_bindings::structs::nsIAtom;"
|
||||
"\nuse string_cache::Atom;\n\n")
|
||||
|
||||
ATOM_TEMPLATE = (" #[link_name = \"{link_name}\"]\n"
|
||||
" pub static {name}: *mut {type};")
|
||||
|
||||
UNSAFE_STATIC = ("#[inline(always)]\n"
|
||||
"pub unsafe fn atom_from_static(ptr: *mut nsIAtom) -> Atom {\n"
|
||||
" Atom::from_static(ptr)\n"
|
||||
"}\n\n")
|
||||
|
||||
CFG_IF = '''
|
||||
cfg_if! {{
|
||||
if #[cfg(not(target_env = "msvc"))] {{
|
||||
extern {{
|
||||
{gnu}
|
||||
}}
|
||||
}} else if #[cfg(target_pointer_width = "64")] {{
|
||||
extern {{
|
||||
{msvc64}
|
||||
}}
|
||||
}} else {{
|
||||
extern {{
|
||||
{msvc32}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
'''
|
||||
|
||||
RULE_TEMPLATE = ('("{atom}") =>\n '
|
||||
'{{ '
|
||||
# FIXME(bholley): Uncomment this when rust 1.14 is released.
|
||||
# See the comment in components/style/lib.rs.
|
||||
# ' #[allow(unsafe_code)] #[allow(unused_unsafe)] '
|
||||
'unsafe {{ $crate::string_cache::atom_macro::atom_from_static'
|
||||
'($crate::string_cache::atom_macro::{name} as *mut _) }}'
|
||||
' }};')
|
||||
|
||||
MACRO = '''
|
||||
#[macro_export]
|
||||
macro_rules! atom {{
|
||||
{}
|
||||
}}
|
||||
'''
|
||||
|
||||
|
||||
def write_atom_macro(atoms, file_name):
|
||||
def get_symbols(func):
|
||||
return '\n'.join([ATOM_TEMPLATE.format(name=atom.ident,
|
||||
link_name=func(atom),
|
||||
type=atom.type()) for atom in atoms])
|
||||
|
||||
with FileAvoidWrite(file_name) as f:
|
||||
f.write(PRELUDE)
|
||||
f.write(IMPORTS)
|
||||
|
||||
for source in SOURCES:
|
||||
if source.TYPE != "nsIAtom":
|
||||
f.write("pub enum {} {{}}\n\n".format(source.TYPE))
|
||||
|
||||
f.write(UNSAFE_STATIC)
|
||||
|
||||
gnu_symbols = get_symbols(Atom.gnu_symbol)
|
||||
msvc32_symbols = get_symbols(Atom.msvc32_symbol)
|
||||
msvc64_symbols = get_symbols(Atom.msvc64_symbol)
|
||||
f.write(CFG_IF.format(gnu=gnu_symbols, msvc32=msvc32_symbols, msvc64=msvc64_symbols))
|
||||
|
||||
macro_rules = [RULE_TEMPLATE.format(atom=atom.value, name=atom.ident) for atom in atoms]
|
||||
f.write(MACRO.format('\n'.join(macro_rules)))
|
||||
|
||||
|
||||
def write_pseudo_elements(atoms, target_filename):
|
||||
pseudos = []
|
||||
for atom in atoms:
|
||||
if atom.type() == "nsICSSPseudoElement" or atom.type() == "nsICSSAnonBoxPseudo":
|
||||
pseudos.append(atom)
|
||||
|
||||
pseudo_definition_template = os.path.join(GECKO_DIR, "pseudo_element_definition.mako.rs")
|
||||
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):
|
||||
atoms = collect_atoms(dist)
|
||||
write_atom_macro(atoms, os.path.join(out, "atom_macro.rs"))
|
||||
write_pseudo_elements(atoms, os.path.join(out, "pseudo_element_definition.rs"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: {} dist out".format(sys.argv[0]))
|
||||
exit(2)
|
||||
generate_atoms(sys.argv[1], sys.argv[2])
|
|
@ -8,213 +8,16 @@ use cssparser::{Parser, ToCss};
|
|||
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
|
||||
use element_state::ElementState;
|
||||
use gecko_bindings::structs::CSSPseudoClassType;
|
||||
use gecko_bindings::structs::nsIAtom;
|
||||
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
||||
use selectors::parser::{ComplexSelector, SelectorMethods};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::ptr;
|
||||
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||
|
||||
pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
|
||||
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! {
|
||||
flags NonTSPseudoClassFlag: u8 {
|
||||
// See NonTSPseudoClass::is_internal()
|
||||
|
@ -561,25 +364,18 @@ impl SelectorImpl {
|
|||
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
|
||||
where F: FnMut(PseudoElement),
|
||||
{
|
||||
macro_rules! case {
|
||||
($atom:expr, $idx:expr) => { fun(PseudoElement($atom, false)); }
|
||||
for pseudo in &EAGER_PSEUDOS {
|
||||
fun(pseudo.clone())
|
||||
}
|
||||
each_eager_pseudo!(case, atom);
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
/// 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),
|
||||
{
|
||||
macro_rules! pseudo_element {
|
||||
($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"));
|
||||
PseudoElement::each(fun)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -627,7 +627,8 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
_existing_values: &'a Arc<ComputedValues>,
|
||||
pseudo: Option<&PseudoElement>)
|
||||
-> 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 {
|
||||
let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr);
|
||||
context_ptr.as_ref()
|
||||
|
@ -699,15 +700,9 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let maybe_atom =
|
||||
let pseudo_type =
|
||||
unsafe { bindings::Gecko_GetImplementedPseudo(self.0) };
|
||||
|
||||
if maybe_atom.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let atom = Atom::from(maybe_atom);
|
||||
Some(PseudoElement::from_atom_unchecked(atom, /* anon_box = */ false))
|
||||
PseudoElement::from_pseudo_type(pseudo_type)
|
||||
}
|
||||
|
||||
fn store_children_to_process(&self, _: isize) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue