mirror of
https://github.com/servo/servo.git
synced 2025-10-18 09:19:16 +01:00
When unhidding a ::marker element, we construct its generated item, and then call StyleNewSubtree() on this generated item. During traversal, we may update any animation related values in Gecko_UpdateAnimations(), which may update the base styles for animation properties. The test case is an animation segment from "null" to "inital" value. We replace the "null" value with the base style value for the specific animation property, so we can do interpolation properly. (e.g. opacity: "null => initial" becomes "1.0 => initial") If we don't update the animation related values in Gecko_UpdateAnimations after generating ::marker, we may do interpolation from "null" to "initial", which causes a panic. Differential Revision: https://phabricator.services.mozilla.com/D73408
218 lines
7.1 KiB
Rust
218 lines
7.1 KiB
Rust
/* 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 https://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 crate::gecko_bindings::structs::{self, PseudoStyleType};
|
|
use crate::properties::longhands::display::computed_value::T as Display;
|
|
use crate::properties::{ComputedValues, PropertyFlags};
|
|
use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl};
|
|
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
|
|
use crate::string_cache::Atom;
|
|
use crate::values::serialize_atom_identifier;
|
|
use cssparser::ToCss;
|
|
use std::fmt;
|
|
use thin_slice::ThinBoxedSlice;
|
|
|
|
include!(concat!(
|
|
env!("OUT_DIR"),
|
|
"/gecko/pseudo_element_definition.rs"
|
|
));
|
|
|
|
impl ::selectors::parser::PseudoElement for PseudoElement {
|
|
type Impl = SelectorImpl;
|
|
|
|
// ::slotted() should support all tree-abiding pseudo-elements, see
|
|
// https://drafts.csswg.org/css-scoping/#slotted-pseudo
|
|
// https://drafts.csswg.org/css-pseudo-4/#treelike
|
|
#[inline]
|
|
fn valid_after_slotted(&self) -> bool {
|
|
matches!(
|
|
*self,
|
|
PseudoElement::Before |
|
|
PseudoElement::After |
|
|
PseudoElement::Marker |
|
|
PseudoElement::Placeholder
|
|
)
|
|
}
|
|
|
|
#[inline]
|
|
fn accepts_state_pseudo_classes(&self) -> bool {
|
|
self.supports_user_action_state()
|
|
}
|
|
}
|
|
|
|
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_precomputed() {
|
|
return PseudoElementCascadeType::Precomputed;
|
|
}
|
|
|
|
PseudoElementCascadeType::Lazy
|
|
}
|
|
|
|
/// Whether the pseudo-element should inherit from the default computed
|
|
/// values instead of from the parent element.
|
|
///
|
|
/// This is not the common thing, but there are some pseudos (namely:
|
|
/// ::backdrop), that shouldn't inherit from the parent element.
|
|
pub fn inherits_from_default_values(&self) -> bool {
|
|
matches!(*self, PseudoElement::Backdrop)
|
|
}
|
|
|
|
/// 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 the current pseudo element is animatable.
|
|
#[inline]
|
|
pub fn is_animatable(&self) -> bool {
|
|
matches!(*self, Self::Before | Self::After | Self::Marker)
|
|
}
|
|
|
|
/// Whether the current pseudo element is ::before or ::after.
|
|
#[inline]
|
|
pub fn is_before_or_after(&self) -> bool {
|
|
self.is_before() || self.is_after()
|
|
}
|
|
|
|
/// Whether this pseudo-element is the ::before pseudo.
|
|
#[inline]
|
|
pub fn is_before(&self) -> bool {
|
|
*self == PseudoElement::Before
|
|
}
|
|
|
|
/// Whether this pseudo-element is the ::after pseudo.
|
|
#[inline]
|
|
pub fn is_after(&self) -> bool {
|
|
*self == PseudoElement::After
|
|
}
|
|
|
|
/// Whether this pseudo-element is the ::marker pseudo.
|
|
#[inline]
|
|
pub fn is_marker(&self) -> bool {
|
|
*self == PseudoElement::Marker
|
|
}
|
|
|
|
/// Whether this pseudo-element is the ::selection pseudo.
|
|
#[inline]
|
|
pub fn is_selection(&self) -> bool {
|
|
*self == PseudoElement::Selection
|
|
}
|
|
|
|
/// Whether this pseudo-element is ::first-letter.
|
|
#[inline]
|
|
pub fn is_first_letter(&self) -> bool {
|
|
*self == PseudoElement::FirstLetter
|
|
}
|
|
|
|
/// Whether this pseudo-element is ::first-line.
|
|
#[inline]
|
|
pub fn is_first_line(&self) -> bool {
|
|
*self == PseudoElement::FirstLine
|
|
}
|
|
|
|
/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
|
|
#[inline]
|
|
pub fn is_color_swatch(&self) -> bool {
|
|
*self == PseudoElement::MozColorSwatch
|
|
}
|
|
|
|
/// Whether this pseudo-element is lazily-cascaded.
|
|
#[inline]
|
|
pub fn is_lazy(&self) -> bool {
|
|
!self.is_eager() && !self.is_precomputed()
|
|
}
|
|
|
|
/// Whether this pseudo-element supports user action selectors.
|
|
pub fn supports_user_action_state(&self) -> bool {
|
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0
|
|
}
|
|
|
|
/// Whether this pseudo-element is enabled for all content.
|
|
pub fn enabled_in_content(&self) -> bool {
|
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) == 0
|
|
}
|
|
|
|
/// Whether this pseudo is enabled explicitly in UA sheets.
|
|
pub fn enabled_in_ua_sheets(&self) -> bool {
|
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS) != 0
|
|
}
|
|
|
|
/// Whether this pseudo is enabled explicitly in chrome sheets.
|
|
pub fn enabled_in_chrome(&self) -> bool {
|
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_CHROME) != 0
|
|
}
|
|
|
|
/// Whether this pseudo-element skips flex/grid container display-based
|
|
/// fixup.
|
|
#[inline]
|
|
pub fn skip_item_display_fixup(&self) -> bool {
|
|
(self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0
|
|
}
|
|
|
|
/// Whether this pseudo-element is precomputed.
|
|
#[inline]
|
|
pub fn is_precomputed(&self) -> bool {
|
|
self.is_anon_box() && !self.is_tree_pseudo_element()
|
|
}
|
|
|
|
/// Property flag that properties must have to apply to this pseudo-element.
|
|
#[inline]
|
|
pub fn property_restriction(&self) -> Option<PropertyFlags> {
|
|
Some(match *self {
|
|
PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,
|
|
PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE,
|
|
PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
|
|
PseudoElement::Cue => PropertyFlags::APPLIES_TO_CUE,
|
|
PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => {
|
|
PropertyFlags::APPLIES_TO_MARKER
|
|
},
|
|
_ => return None,
|
|
})
|
|
}
|
|
|
|
/// Whether this pseudo-element should actually exist if it has
|
|
/// the given styles.
|
|
pub fn should_exist(&self, style: &ComputedValues) -> bool {
|
|
debug_assert!(self.is_eager());
|
|
|
|
if style.get_box().clone_display() == Display::None {
|
|
return false;
|
|
}
|
|
|
|
if self.is_before_or_after() && style.ineffective_content_property() {
|
|
return false;
|
|
}
|
|
|
|
true
|
|
}
|
|
}
|