Bug 1364412: Convert pseudo-elements to an enum. r=hiro,xidorn

This commit is contained in:
Emilio Cobos Álvarez 2017-05-15 22:24:37 +02:00
parent 0bc185f1c2
commit 5820e3ecac
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
17 changed files with 2260 additions and 1505 deletions

View file

@ -1,2 +0,0 @@
llvm/
rust-bindgen/

View file

@ -1,7 +0,0 @@
# Geckolib tools
This directory contains simple tools for generating the Rust bindings for [stylo](https://public.etherpad-mozilla.org/p/stylo).
## `setup_bindgen.sh`
This clones Servo's version of bindgen, and uses `llvm-3.8` library to build it. It will then be used to generate the Rust bindings.

View file

@ -1,30 +0,0 @@
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
set -o errexit
set -o nounset
set -o pipefail
# Run in the tools directory.
cd "$(dirname ${0})"
# Setup and build bindgen.
if [[ "$(uname)" == "Linux" ]]; then
export LIBCLANG_PATH=/usr/lib/llvm-3.8/lib
else
export LIBCLANG_PATH="$(brew --prefix llvm38)/lib/llvm-3.8/lib"
fi
# Make sure we have llvm-3.8.
if [[ ! -x "$(command -v clang-3.8)" ]]; then
echo "llvm-3.8 is required." \
"Mac users should |brew install llvm38|," \
"Linux users can find it in clang-3.8."
exit 1
fi
export LD_LIBRARY_PATH="${LIBCLANG_PATH}"
export DYLD_LIBRARY_PATH="${LIBCLANG_PATH}"

View file

@ -672,6 +672,7 @@ mod bindings {
"RawGeckoURLExtraData", "RawGeckoURLExtraData",
"RefPtr", "RefPtr",
"CSSPseudoClassType", "CSSPseudoClassType",
"CSSPseudoElementType",
"TraversalRestyleBehavior", "TraversalRestyleBehavior",
"TraversalRootBehavior", "TraversalRootBehavior",
"ComputedTimingFunction_BeforeFlag", "ComputedTimingFunction_BeforeFlag",
@ -842,7 +843,7 @@ mod bindings {
} }
fn generate_atoms() { fn generate_atoms() {
let script = Path::new(file!()).parent().unwrap().join("binding_tools").join("regen_atoms.py"); let script = Path::new(file!()).parent().unwrap().join("gecko").join("regen_atoms.py");
println!("cargo:rerun-if-changed={}", script.display()); println!("cargo:rerun-if-changed={}", script.display());
let status = Command::new(&*PYTHON) let status = Command::new(&*PYTHON)
.arg(&script) .arg(&script)

View file

@ -26,6 +26,7 @@ use gecko_bindings::structs::RawGeckoServoStyleRuleList;
use gecko_bindings::structs::RawGeckoURLExtraData; use gecko_bindings::structs::RawGeckoURLExtraData;
use gecko_bindings::structs::RefPtr; use gecko_bindings::structs::RefPtr;
use gecko_bindings::structs::CSSPseudoClassType; use gecko_bindings::structs::CSSPseudoClassType;
use gecko_bindings::structs::CSSPseudoElementType;
use gecko_bindings::structs::TraversalRestyleBehavior; use gecko_bindings::structs::TraversalRestyleBehavior;
use gecko_bindings::structs::TraversalRootBehavior; use gecko_bindings::structs::TraversalRootBehavior;
use gecko_bindings::structs::ComputedTimingFunction_BeforeFlag; use gecko_bindings::structs::ComputedTimingFunction_BeforeFlag;
@ -905,7 +906,7 @@ extern "C" {
} }
extern "C" { extern "C" {
pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed) pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed)
-> *mut nsIAtom; -> CSSPseudoElementType;
} }
extern "C" { extern "C" {
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext, pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
@ -2217,7 +2218,7 @@ extern "C" {
extern "C" { extern "C" {
pub fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: pub fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
ServoComputedValuesBorrowedOrNull, ServoComputedValuesBorrowedOrNull,
pseudoTag: *mut nsIAtom, pseudo_tag: *mut nsIAtom,
skip_display_fixup: bool, skip_display_fixup: bool,
set: set:
RawServoStyleSetBorrowed) RawServoStyleSetBorrowed)
@ -2257,25 +2258,19 @@ extern "C" {
} }
extern "C" { extern "C" {
pub fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, pub fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, is_probe: bool, pseudo_type: CSSPseudoElementType,
is_probe: bool,
set: RawServoStyleSetBorrowed) set: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong; -> ServoComputedValuesStrong;
} }
extern "C" { extern "C" {
pub fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed, pub fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom,
set: RawServoStyleSetBorrowed)
-> RawServoRuleNodeStrong;
}
extern "C" {
pub fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed,
element: RawGeckoElementBorrowed,
rule_type_mask: u32, rule_type_mask: u32,
author_colors_allowed: bool) -> bool; author_colors_allowed: bool) -> bool;
} }
extern "C" { extern "C" {
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, pseudo_type: CSSPseudoElementType,
snapshots: snapshots:
*const ServoElementSnapshotTable, *const ServoElementSnapshotTable,
set: RawServoStyleSetBorrowed) set: RawServoStyleSetBorrowed)
@ -2299,8 +2294,8 @@ extern "C" {
RawGeckoElementBorrowed, RawGeckoElementBorrowed,
snapshots: snapshots:
*const ServoElementSnapshotTable, *const ServoElementSnapshotTable,
pseudo_tag: pseudo_type:
*mut nsIAtom) CSSPseudoElementType)
-> ServoComputedValuesStrong; -> ServoComputedValuesStrong;
} }
extern "C" { extern "C" {

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -12,6 +12,7 @@ pub mod conversions;
pub mod data; pub mod data;
pub mod global_style_data; pub mod global_style_data;
pub mod media_queries; pub mod media_queries;
pub mod pseudo_element;
pub mod restyle_damage; pub mod restyle_damage;
pub mod rules; pub mod rules;
pub mod selector_parser; pub mod selector_parser;

View 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())
}
}

View 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
}
}
}

View file

@ -10,6 +10,10 @@ import sys
from io import BytesIO from io import BytesIO
GECKO_DIR = os.path.dirname(__file__.replace('\\', '/'))
sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
import build
PRELUDE = """ PRELUDE = """
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
@ -74,7 +78,7 @@ def map_atom(ident):
class Atom: class Atom:
def __init__(self, source, ident, value): def __init__(self, source, ident, value):
self.ident = "{}_{}".format(source.CLASS, ident) self.ident = "{}_{}".format(source.CLASS, ident)
self._original_ident = ident self.original_ident = ident
self.value = value self.value = value
self.source = source self.source = source
@ -82,17 +86,23 @@ class Atom:
return self.source.CLASS return self.source.CLASS
def gnu_symbol(self): def gnu_symbol(self):
return gnu_symbolify(self.source, self._original_ident) return gnu_symbolify(self.source, self.original_ident)
def msvc32_symbol(self): def msvc32_symbol(self):
return msvc32_symbolify(self.source, self._original_ident) return msvc32_symbolify(self.source, self.original_ident)
def msvc64_symbol(self): def msvc64_symbol(self):
return msvc64_symbolify(self.source, self._original_ident) return msvc64_symbolify(self.source, self.original_ident)
def type(self): def type(self):
return self.source.TYPE return self.source.TYPE
def capitalized(self):
return self.original_ident[0].upper() + self.original_ident[1:]
def is_anon_box(self):
return self.type() == "nsICSSAnonBoxPseudo"
def collect_atoms(objdir): def collect_atoms(objdir):
atoms = [] atoms = []
@ -211,56 +221,24 @@ def write_atom_macro(atoms, file_name):
f.write(MACRO.format('\n'.join(macro_rules))) f.write(MACRO.format('\n'.join(macro_rules)))
PSEUDO_ELEMENT_HEADER = """ def write_pseudo_elements(atoms, target_filename):
/* pseudos = []
* This file contains a helper macro invocation to aid Gecko's style system
* pseudo-element integration.
*
* This file is NOT INTENDED to be compiled as a standalone module.
*
* Also, it guarantees the property that normal pseudo-elements are processed
* before anonymous boxes.
*
* Expected usage is as follows:
*
* ```
* fn have_to_use_pseudo_elements() {
* macro_rules! pseudo_element {
* ($pseudo_str_with_colon:expr, $pseudo_atom:expr, $is_anon_box:true) => {{
* // Stuff stuff stuff.
* }}
* }
* include!("path/to/helper.rs")
* }
* ```
*
*/
"""
PSEUDO_ELEMENT_MACRO_INVOCATION = """
pseudo_element!(\"{}\",
atom!(\"{}\"),
{});
"""[1:]
def write_pseudo_element_helper(atoms, target_filename):
with FileAvoidWrite(target_filename) as f:
f.write(PRELUDE)
f.write(PSEUDO_ELEMENT_HEADER)
f.write("{\n")
for atom in atoms: for atom in atoms:
if atom.type() == "nsICSSPseudoElement": if atom.type() == "nsICSSPseudoElement" or atom.type() == "nsICSSAnonBoxPseudo":
f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "false")) pseudos.append(atom)
elif atom.type() == "nsICSSAnonBoxPseudo":
f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "true")) pseudo_definition_template = os.path.join(GECKO_DIR, "pseudo_element_definition.mako.rs")
f.write("}\n") print("cargo:rerun-if-changed={}".format(pseudo_definition_template))
contents = build.render(pseudo_definition_template, PSEUDOS=pseudos)
with FileAvoidWrite(target_filename) as f:
f.write(contents)
def generate_atoms(dist, out): def generate_atoms(dist, out):
atoms = collect_atoms(dist) atoms = collect_atoms(dist)
write_atom_macro(atoms, os.path.join(out, "atom_macro.rs")) write_atom_macro(atoms, os.path.join(out, "atom_macro.rs"))
write_pseudo_element_helper(atoms, os.path.join(out, "pseudo_element_helper.rs")) write_pseudo_elements(atoms, os.path.join(out, "pseudo_element_definition.rs"))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -8,213 +8,16 @@ use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE}; use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState; use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType; use gecko_bindings::structs::CSSPseudoClassType;
use gecko_bindings::structs::nsIAtom;
use selector_parser::{SelectorParser, PseudoElementCascadeType}; use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods}; use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::ptr;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap; pub use gecko::snapshot::SnapshotMap;
/// A representation of a CSS pseudo-element.
///
/// In Gecko, we represent pseudo-elements as plain `Atom`s.
///
/// The boolean field represents whether this element is an anonymous box. This
/// is just for convenience, instead of recomputing it.
///
/// Also, note that the `Atom` member is always a static atom, so if space is a
/// concern, we can use the raw pointer and use the lower bit to represent it
/// without space overhead.
///
/// FIXME(emilio): we know all these atoms are static. Patches are starting to
/// pile up, but a further potential optimisation is generating bindings without
/// `-no-gen-bitfield-methods` (that was removed to compile on stable, but it no
/// longer depends on it), and using the raw *mut nsIAtom (properly asserting
/// we're a static atom).
///
/// This should allow us to avoid random FFI overhead when cloning/dropping
/// pseudos.
///
/// Also, we can further optimize PartialEq and hash comparing/hashing only the
/// atoms.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElement(Atom, bool);
/// List of eager pseudos. Keep this in sync with the count below.
macro_rules! each_eager_pseudo {
($macro_name:ident, $atom_macro:ident) => {
$macro_name!($atom_macro!(":after"), 0);
$macro_name!($atom_macro!(":before"), 1);
}
}
/// The number of eager pseudo-elements (just ::before and ::after).
pub const EAGER_PSEUDO_COUNT: usize = 2;
impl PseudoElement {
/// Returns the kind of cascade type that a given pseudo is going to use.
///
/// In Gecko we only compute ::before and ::after eagerly. We save the rules
/// for anonymous boxes separately, so we resolve them as precomputed
/// pseudos.
///
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
pub fn cascade_type(&self) -> PseudoElementCascadeType {
if self.is_eager() {
debug_assert!(!self.is_anon_box());
return PseudoElementCascadeType::Eager
}
if self.is_anon_box() {
return PseudoElementCascadeType::Precomputed
}
PseudoElementCascadeType::Lazy
}
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
#[inline]
pub fn eager_index(&self) -> usize {
macro_rules! case {
($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return $idx; } }
}
each_eager_pseudo!(case, atom);
panic!("Not eager")
}
/// Creates a pseudo-element from an eager index.
#[inline]
pub fn from_eager_index(i: usize) -> Self {
macro_rules! case {
($atom:expr, $idx:expr) => { if i == $idx { return PseudoElement($atom, false); } }
}
each_eager_pseudo!(case, atom);
panic!("Not eager")
}
/// Get the pseudo-element as an atom.
#[inline]
pub fn as_atom(&self) -> &Atom {
&self.0
}
/// Whether this pseudo-element is an anonymous box.
#[inline]
fn is_anon_box(&self) -> bool {
self.1
}
/// Whether this pseudo-element is ::before or ::after.
#[inline]
pub fn is_before_or_after(&self) -> bool {
*self.as_atom() == atom!(":before") ||
*self.as_atom() == atom!(":after")
}
/// Whether this pseudo-element is eagerly-cascaded.
#[inline]
pub fn is_eager(&self) -> bool {
macro_rules! case {
($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return true; } }
}
each_eager_pseudo!(case, atom);
return false;
}
/// Whether this pseudo-element is lazily-cascaded.
#[inline]
pub fn is_lazy(&self) -> bool {
!self.is_eager() && !self.is_precomputed()
}
/// Whether this pseudo-element is precomputed.
#[inline]
pub fn is_precomputed(&self) -> bool {
self.is_anon_box()
}
/// Construct a pseudo-element from an `Atom`, receiving whether it is also
/// an anonymous box, and don't check it on release builds.
///
/// On debug builds we assert it's the result we expect.
#[inline]
pub fn from_atom_unchecked(atom: Atom, is_anon_box: bool) -> Self {
if cfg!(debug_assertions) {
// Do the check on debug regardless.
match Self::from_atom(&*atom, true) {
Some(pseudo) => {
assert_eq!(pseudo.is_anon_box(), is_anon_box);
return pseudo;
}
None => panic!("Unknown pseudo: {:?}", atom),
}
}
PseudoElement(atom, is_anon_box)
}
#[inline]
fn from_atom(atom: &WeakAtom, _in_ua: bool) -> Option<Self> {
macro_rules! pseudo_element {
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
if atom == &*$atom {
return Some(PseudoElement($atom, $is_anon_box));
}
}}
}
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_helper.rs"));
None
}
/// Constructs an atom from a string of text, and whether we're in a
/// user-agent stylesheet.
///
/// If we're not in a user-agent stylesheet, we will never parse anonymous
/// box pseudo-elements.
///
/// Returns `None` if the pseudo-element is not recognised.
#[inline]
fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
use std::ascii::AsciiExt;
macro_rules! pseudo_element {
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
if !$is_anon_box || in_ua_stylesheet {
if s.eq_ignore_ascii_case(&$pseudo_str_with_colon[1..]) {
return Some(PseudoElement($atom, $is_anon_box))
}
}
}}
}
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_helper.rs"));
None
}
/// Returns null or nsIAtom pointer corresponding to a given PseudoElement.
#[inline]
pub fn ns_atom_or_null_from_opt(pseudo: Option<&PseudoElement>) -> *mut nsIAtom {
pseudo.map(|p| p.as_atom().as_ptr()).unwrap_or(ptr::null_mut())
}
}
impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// FIXME: why does the atom contain one colon? Pseudo-element has two
debug_assert!(self.0.as_slice().starts_with(&[b':' as u16]) &&
!self.0.as_slice().starts_with(&[b':' as u16, b':' as u16]));
try!(dest.write_char(':'));
write!(dest, "{}", self.0)
}
}
bitflags! { bitflags! {
flags NonTSPseudoClassFlag: u8 { flags NonTSPseudoClassFlag: u8 {
// See NonTSPseudoClass::is_internal() // See NonTSPseudoClass::is_internal()
@ -561,25 +364,18 @@ impl SelectorImpl {
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F) pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement), where F: FnMut(PseudoElement),
{ {
macro_rules! case { for pseudo in &EAGER_PSEUDOS {
($atom:expr, $idx:expr) => { fun(PseudoElement($atom, false)); } fun(pseudo.clone())
} }
each_eager_pseudo!(case, atom);
} }
#[inline] #[inline]
/// Executes a function for each pseudo-element. /// Executes a function for each pseudo-element.
pub fn each_pseudo_element<F>(mut fun: F) pub fn each_pseudo_element<F>(fun: F)
where F: FnMut(PseudoElement), where F: FnMut(PseudoElement),
{ {
macro_rules! pseudo_element { PseudoElement::each(fun)
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
fun(PseudoElement($atom, $is_anon_box));
}}
}
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_helper.rs"));
} }
#[inline] #[inline]

View file

@ -627,7 +627,8 @@ impl<'le> TElement for GeckoElement<'le> {
_existing_values: &'a Arc<ComputedValues>, _existing_values: &'a Arc<ComputedValues>,
pseudo: Option<&PseudoElement>) pseudo: Option<&PseudoElement>)
-> Option<&'a nsStyleContext> { -> Option<&'a nsStyleContext> {
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo); // TODO(emilio): Migrate this to CSSPseudoElementType.
let atom_ptr = pseudo.map_or(ptr::null_mut(), |p| p.atom().as_ptr());
unsafe { unsafe {
let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr); let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr);
context_ptr.as_ref() context_ptr.as_ref()
@ -699,15 +700,9 @@ impl<'le> TElement for GeckoElement<'le> {
return None; return None;
} }
let maybe_atom = let pseudo_type =
unsafe { bindings::Gecko_GetImplementedPseudo(self.0) }; unsafe { bindings::Gecko_GetImplementedPseudo(self.0) };
PseudoElement::from_pseudo_type(pseudo_type)
if maybe_atom.is_null() {
return None;
}
let atom = Atom::from(maybe_atom);
Some(PseudoElement::from_atom_unchecked(atom, /* anon_box = */ false))
} }
fn store_children_to_process(&self, _: isize) { fn store_children_to_process(&self, _: isize) {

View file

@ -58,10 +58,10 @@ use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
use style::gecko_bindings::bindings::nsTimingFunctionBorrowed; use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut; use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
use style::gecko_bindings::structs; use style::gecko_bindings::structs;
use style::gecko_bindings::structs::{CSSPseudoElementType, CompositeOperation};
use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet}; use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID}; use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule}; use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule};
use style::gecko_bindings::structs::CompositeOperation;
use style::gecko_bindings::structs::Loader; use style::gecko_bindings::structs::Loader;
use style::gecko_bindings::structs::RawGeckoPresContextOwned; use style::gecko_bindings::structs::RawGeckoPresContextOwned;
use style::gecko_bindings::structs::ServoElementSnapshotTable; use style::gecko_bindings::structs::ServoElementSnapshotTable;
@ -474,7 +474,7 @@ pub extern "C" fn Servo_AnimationValue_Uncompute(value: RawServoAnimationValueBo
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed, pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed,
element: RawGeckoElementBorrowed, element: RawGeckoElementBorrowed,
snapshots: *const ServoElementSnapshotTable, snapshots: *const ServoElementSnapshotTable,
pseudo_tag: *mut nsIAtom) pseudo_type: CSSPseudoElementType)
-> ServoComputedValuesStrong -> ServoComputedValuesStrong
{ {
use style::matching::MatchMethods; use style::matching::MatchMethods;
@ -492,12 +492,7 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe
let element_data = element.borrow_data().unwrap(); let element_data = element.borrow_data().unwrap();
let styles = element_data.styles(); let styles = element_data.styles();
let pseudo = if pseudo_tag.is_null() { let pseudo = PseudoElement::from_pseudo_type(pseudo_type);
None
} else {
let atom = Atom::from(pseudo_tag);
Some(PseudoElement::from_atom_unchecked(atom, /* anon_box = */ false))
};
let pseudos = &styles.pseudos; let pseudos = &styles.pseudos;
let pseudo_style = match pseudo { let pseudo_style = match pseudo {
Some(ref p) => { Some(ref p) => {
@ -974,7 +969,8 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
let guards = StylesheetGuards::same(&guard); let guards = StylesheetGuards::same(&guard);
let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
let atom = Atom::from(pseudo_tag); let atom = Atom::from(pseudo_tag);
let pseudo = PseudoElement::from_atom_unchecked(atom, /* anon_box = */ true); let pseudo = PseudoElement::from_anon_box_atom(&atom)
.expect("Not an anon box pseudo?");
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null); let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
@ -991,7 +987,7 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, pseudo_type: CSSPseudoElementType,
is_probe: bool, is_probe: bool,
raw_data: RawServoStyleSetBorrowed) raw_data: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong -> ServoComputedValuesStrong
@ -1010,9 +1006,12 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
}; };
} }
let pseudo = PseudoElement::from_pseudo_type(pseudo_type)
.expect("ResolvePseudoStyle with a non-pseudo?");
let global_style_data = &*GLOBAL_STYLE_DATA; let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
match get_pseudo_style(&guard, element, pseudo_tag, data.styles(), doc_data) { match get_pseudo_style(&guard, element, &pseudo, data.styles(), doc_data) {
Some(values) => values.into_strong(), Some(values) => values.into_strong(),
// FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an // FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an
// empty style inheriting from the element? // empty style inheriting from the element?
@ -1043,12 +1042,11 @@ pub extern "C" fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed
fn get_pseudo_style(guard: &SharedRwLockReadGuard, fn get_pseudo_style(guard: &SharedRwLockReadGuard,
element: GeckoElement, element: GeckoElement,
pseudo_tag: *mut nsIAtom, pseudo: &PseudoElement,
styles: &ElementStyles, styles: &ElementStyles,
doc_data: &PerDocumentStyleData) doc_data: &PerDocumentStyleData)
-> Option<Arc<ComputedValues>> -> Option<Arc<ComputedValues>>
{ {
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
match pseudo.cascade_type() { match pseudo.cascade_type() {
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()), PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()),
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"), PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
@ -1981,7 +1979,7 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, pseudo_type: CSSPseudoElementType,
snapshots: *const ServoElementSnapshotTable, snapshots: *const ServoElementSnapshotTable,
raw_data: RawServoStyleSetBorrowed) raw_data: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong -> ServoComputedValuesStrong
@ -1992,12 +1990,9 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
let element = GeckoElement(element); let element = GeckoElement(element);
let doc_data = PerDocumentStyleData::from_ffi(raw_data); let doc_data = PerDocumentStyleData::from_ffi(raw_data);
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> { let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
let maybe_pseudo = if !pseudo_tag.is_null() { PseudoElement::from_pseudo_type(pseudo_type).and_then(|ref pseudo| {
get_pseudo_style(&guard, element, pseudo_tag, styles, doc_data) get_pseudo_style(&guard, element, pseudo, styles, doc_data)
} else { }).unwrap_or_else(|| styles.primary.values().clone())
None
};
maybe_pseudo.unwrap_or_else(|| styles.primary.values().clone())
}; };
// In the common case we already have the style. Check that before setting // In the common case we already have the style. Check that before setting

View file

@ -26,28 +26,3 @@ macro_rules! check_enum_value_non_static {
assert_eq!($a.0 as usize, $b as usize); assert_eq!($a.0 as usize, $b as usize);
} }
} }
// Note that we can't call each_pseudo_element, parse_pseudo_element, or
// similar, because we'd need the foreign atom symbols to link.
#[test]
fn assert_basic_pseudo_elements() {
let saw_before;
let saw_after;
macro_rules! pseudo_element {
(":before", $atom:expr, false) => {
saw_before = true;
};
(":after", $atom:expr, false) => {
saw_after = true;
};
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {
// Do nothing
};
}
include!("../../../components/style/gecko/generated/pseudo_element_helper.rs");
assert!(saw_before);
assert!(saw_after);
}