mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #16630 - emilio:nac, r=bholley,hiro
Make stylo traverse Native Anonymous Content, fixing a bunch of restyle bugs. <!-- 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/16630) <!-- Reviewable:end -->
This commit is contained in:
commit
c633e291c9
12 changed files with 568 additions and 384 deletions
|
@ -461,16 +461,16 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
self.element.has_selector_flags(flags)
|
self.element.has_selector_flags(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_animations(&self, _pseudo: Option<&PseudoElement>) -> bool {
|
fn has_animations(&self) -> bool {
|
||||||
panic!("this should be only called on gecko");
|
unreachable!("this should be only called on gecko");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_animations(&self, _pseudo: Option<&PseudoElement>) -> bool {
|
fn has_css_animations(&self) -> bool {
|
||||||
panic!("this should be only called on gecko");
|
unreachable!("this should be only called on gecko");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_transitions(&self, _pseudo: Option<&PseudoElement>) -> bool {
|
fn has_css_transitions(&self) -> bool {
|
||||||
panic!("this should be only called on gecko");
|
unreachable!("this should be only called on gecko");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ use font_metrics::FontMetricsProvider;
|
||||||
use matching::StyleSharingCandidateCache;
|
use matching::StyleSharingCandidateCache;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
#[cfg(feature = "gecko")] use properties::ComputedValues;
|
#[cfg(feature = "gecko")] use properties::ComputedValues;
|
||||||
#[cfg(feature = "gecko")] use selector_parser::PseudoElement;
|
|
||||||
use selectors::matching::ElementSelectorFlags;
|
use selectors::matching::ElementSelectorFlags;
|
||||||
#[cfg(feature = "servo")] use servo_config::opts;
|
#[cfg(feature = "servo")] use servo_config::opts;
|
||||||
use shared_lock::StylesheetGuards;
|
use shared_lock::StylesheetGuards;
|
||||||
|
@ -270,7 +269,8 @@ impl TraversalStatistics {
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Represents which tasks are performed in a SequentialTask of UpdateAnimations.
|
/// Represents which tasks are performed in a SequentialTask of
|
||||||
|
/// UpdateAnimations.
|
||||||
pub flags UpdateAnimationsTasks: u8 {
|
pub flags UpdateAnimationsTasks: u8 {
|
||||||
/// Update CSS Animations.
|
/// Update CSS Animations.
|
||||||
const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations,
|
const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations,
|
||||||
|
@ -296,10 +296,8 @@ pub enum SequentialTask<E: TElement> {
|
||||||
/// of the non-animation style traversal, and updating the computed effect properties.
|
/// of the non-animation style traversal, and updating the computed effect properties.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
UpdateAnimations {
|
UpdateAnimations {
|
||||||
/// The target element.
|
/// The target element or pseudo-element.
|
||||||
el: SendElement<E>,
|
el: SendElement<E>,
|
||||||
/// The target pseudo element.
|
|
||||||
pseudo: Option<PseudoElement>,
|
|
||||||
/// The before-change style for transitions. We use before-change style as the initial
|
/// The before-change style for transitions. We use before-change style as the initial
|
||||||
/// value of its Keyframe. Required if |tasks| includes CSSTransitions.
|
/// value of its Keyframe. Required if |tasks| includes CSSTransitions.
|
||||||
before_change_style: Option<Arc<ComputedValues>>,
|
before_change_style: Option<Arc<ComputedValues>>,
|
||||||
|
@ -316,8 +314,8 @@ impl<E: TElement> SequentialTask<E> {
|
||||||
match self {
|
match self {
|
||||||
Unused(_) => unreachable!(),
|
Unused(_) => unreachable!(),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
UpdateAnimations { el, pseudo, before_change_style, tasks } => {
|
UpdateAnimations { el, before_change_style, tasks } => {
|
||||||
unsafe { el.update_animations(pseudo.as_ref(), before_change_style, tasks) };
|
unsafe { el.update_animations(before_change_style, tasks) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,14 +324,14 @@ impl<E: TElement> SequentialTask<E> {
|
||||||
/// a given (pseudo-)element.
|
/// a given (pseudo-)element.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn update_animations(el: E,
|
pub fn update_animations(el: E,
|
||||||
pseudo: Option<PseudoElement>,
|
|
||||||
before_change_style: Option<Arc<ComputedValues>>,
|
before_change_style: Option<Arc<ComputedValues>>,
|
||||||
tasks: UpdateAnimationsTasks) -> Self {
|
tasks: UpdateAnimationsTasks) -> Self {
|
||||||
use self::SequentialTask::*;
|
use self::SequentialTask::*;
|
||||||
UpdateAnimations { el: unsafe { SendElement::new(el) },
|
UpdateAnimations {
|
||||||
pseudo: pseudo,
|
el: unsafe { SendElement::new(el) },
|
||||||
before_change_style: before_change_style,
|
before_change_style: before_change_style,
|
||||||
tasks: tasks }
|
tasks: tasks,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -532,6 +532,21 @@ impl ElementData {
|
||||||
self.styles = Some(styles);
|
self.styles = Some(styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the computed element rules, and returns whether the rules changed.
|
||||||
|
pub fn set_primary_rules(&mut self, rules: StrongRuleNode) -> bool {
|
||||||
|
if !self.has_styles() {
|
||||||
|
self.set_styles(ElementStyles::new(ComputedStyle::new_partial(rules)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.styles().primary.rules == rules {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.styles_mut().primary.rules = rules;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the Element has a RestyleData.
|
/// Returns true if the Element has a RestyleData.
|
||||||
pub fn has_restyle(&self) -> bool {
|
pub fn has_restyle(&self) -> bool {
|
||||||
self.restyle.is_some()
|
self.restyle.is_some()
|
||||||
|
|
|
@ -274,11 +274,20 @@ pub trait PresentationalHintsSynthetizer {
|
||||||
where V: Push<ApplicableDeclarationBlock>;
|
where V: Push<ApplicableDeclarationBlock>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The animation rules. The first one is for Animation cascade level, and the second one is for
|
/// The animation rules.
|
||||||
|
///
|
||||||
|
/// The first one is for Animation cascade level, and the second one is for
|
||||||
/// Transition cascade level.
|
/// Transition cascade level.
|
||||||
pub struct AnimationRules(pub Option<Arc<Locked<PropertyDeclarationBlock>>>,
|
pub struct AnimationRules(pub Option<Arc<Locked<PropertyDeclarationBlock>>>,
|
||||||
pub Option<Arc<Locked<PropertyDeclarationBlock>>>);
|
pub Option<Arc<Locked<PropertyDeclarationBlock>>>);
|
||||||
|
|
||||||
|
impl AnimationRules {
|
||||||
|
/// Returns whether these animation rules represents an actual rule or not.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_none() && self.1.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The element trait, the main abstraction the style crate acts over.
|
/// The element trait, the main abstraction the style crate acts over.
|
||||||
pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
ElementExt + PresentationalHintsSynthetizer {
|
ElementExt + PresentationalHintsSynthetizer {
|
||||||
|
@ -325,26 +334,25 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this element's animation rules.
|
/// Get this element's animation rules.
|
||||||
fn get_animation_rules(&self, _pseudo: Option<&PseudoElement>) -> AnimationRules {
|
fn get_animation_rules(&self) -> AnimationRules {
|
||||||
AnimationRules(None, None)
|
AnimationRules(None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this element's animation rule by the cascade level.
|
/// Get this element's animation rule by the cascade level.
|
||||||
fn get_animation_rule_by_cascade(&self,
|
fn get_animation_rule_by_cascade(&self,
|
||||||
_pseudo: Option<&PseudoElement>,
|
|
||||||
_cascade_level: CascadeLevel)
|
_cascade_level: CascadeLevel)
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this element's animation rule.
|
/// Get this element's animation rule.
|
||||||
fn get_animation_rule(&self, _pseudo: Option<&PseudoElement>)
|
fn get_animation_rule(&self)
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this element's transition rule.
|
/// Get this element's transition rule.
|
||||||
fn get_transition_rule(&self, _pseudo: Option<&PseudoElement>)
|
fn get_transition_rule(&self)
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -428,6 +436,18 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
/// anonymous content).
|
/// anonymous content).
|
||||||
fn is_native_anonymous(&self) -> bool { false }
|
fn is_native_anonymous(&self) -> bool { false }
|
||||||
|
|
||||||
|
/// Returns the pseudo-element implemented by this element, if any.
|
||||||
|
///
|
||||||
|
/// Gecko traverses pseudo-elements during the style traversal, and we need
|
||||||
|
/// to know this so we can properly grab the pseudo-element style from the
|
||||||
|
/// parent element.
|
||||||
|
///
|
||||||
|
/// Note that we still need to compute the pseudo-elements before-hand,
|
||||||
|
/// given otherwise we don't know if we need to create an element or not.
|
||||||
|
///
|
||||||
|
/// Servo doesn't have to deal with this.
|
||||||
|
fn implemented_pseudo_element(&self) -> Option<PseudoElement> { None }
|
||||||
|
|
||||||
/// Atomically stores the number of children of this node that we will
|
/// Atomically stores the number of children of this node that we will
|
||||||
/// need to process during bottom-up traversal.
|
/// need to process during bottom-up traversal.
|
||||||
fn store_children_to_process(&self, n: isize);
|
fn store_children_to_process(&self, n: isize);
|
||||||
|
@ -469,21 +489,21 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
|
|
||||||
/// Creates a task to update various animation state on a given (pseudo-)element.
|
/// Creates a task to update various animation state on a given (pseudo-)element.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn update_animations(&self, _pseudo: Option<&PseudoElement>,
|
fn update_animations(&self,
|
||||||
before_change_style: Option<Arc<ComputedValues>>,
|
before_change_style: Option<Arc<ComputedValues>>,
|
||||||
tasks: UpdateAnimationsTasks);
|
tasks: UpdateAnimationsTasks);
|
||||||
|
|
||||||
/// Returns true if the element has relevant animations. Relevant
|
/// Returns true if the element has relevant animations. Relevant
|
||||||
/// animations are those animations that are affecting the element's style
|
/// animations are those animations that are affecting the element's style
|
||||||
/// or are scheduled to do so in the future.
|
/// or are scheduled to do so in the future.
|
||||||
fn has_animations(&self, _pseudo: Option<&PseudoElement>) -> bool;
|
fn has_animations(&self) -> bool;
|
||||||
|
|
||||||
/// Returns true if the element has a CSS animation.
|
/// Returns true if the element has a CSS animation.
|
||||||
fn has_css_animations(&self, _pseudo: Option<&PseudoElement>) -> bool;
|
fn has_css_animations(&self) -> bool;
|
||||||
|
|
||||||
/// Returns true if the element has a CSS transition (including running transitions and
|
/// Returns true if the element has a CSS transition (including running transitions and
|
||||||
/// completed transitions).
|
/// completed transitions).
|
||||||
fn has_css_transitions(&self, _pseudo: Option<&PseudoElement>) -> bool;
|
fn has_css_transitions(&self) -> bool;
|
||||||
|
|
||||||
/// Returns true if the element has animation restyle hints.
|
/// Returns true if the element has animation restyle hints.
|
||||||
fn has_animation_restyle_hints(&self) -> bool {
|
fn has_animation_restyle_hints(&self) -> bool {
|
||||||
|
@ -497,8 +517,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
|
|
||||||
/// Gets the current existing CSS transitions, by |property, end value| pairs in a HashMap.
|
/// Gets the current existing CSS transitions, by |property, end value| pairs in a HashMap.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn get_css_transitions_info(&self,
|
fn get_css_transitions_info(&self)
|
||||||
pseudo: Option<&PseudoElement>)
|
|
||||||
-> HashMap<TransitionProperty, Arc<AnimationValue>>;
|
-> HashMap<TransitionProperty, Arc<AnimationValue>>;
|
||||||
|
|
||||||
/// Does a rough (and cheap) check for whether or not transitions might need to be updated that
|
/// Does a rough (and cheap) check for whether or not transitions might need to be updated that
|
||||||
|
@ -508,9 +527,9 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
/// reduce the possibility of false positives.
|
/// reduce the possibility of false positives.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn might_need_transitions_update(&self,
|
fn might_need_transitions_update(&self,
|
||||||
old_values: &Option<&Arc<ComputedValues>>,
|
old_values: Option<&ComputedValues>,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &ComputedValues)
|
||||||
pseudo: Option<&PseudoElement>) -> bool;
|
-> bool;
|
||||||
|
|
||||||
/// Returns true if one of the transitions needs to be updated on this element. We check all
|
/// Returns true if one of the transitions needs to be updated on this element. We check all
|
||||||
/// the transition properties to make sure that updating transitions is necessary.
|
/// the transition properties to make sure that updating transitions is necessary.
|
||||||
|
@ -518,17 +537,17 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
/// passed the same parameters.
|
/// passed the same parameters.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn needs_transitions_update(&self,
|
fn needs_transitions_update(&self,
|
||||||
before_change_style: &Arc<ComputedValues>,
|
before_change_style: &ComputedValues,
|
||||||
after_change_style: &Arc<ComputedValues>,
|
after_change_style: &ComputedValues)
|
||||||
pseudo: Option<&PseudoElement>) -> bool;
|
-> bool;
|
||||||
|
|
||||||
/// Returns true if we need to update transitions for the specified property on this element.
|
/// Returns true if we need to update transitions for the specified property on this element.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn needs_transitions_update_per_property(&self,
|
fn needs_transitions_update_per_property(&self,
|
||||||
property: &TransitionProperty,
|
property: &TransitionProperty,
|
||||||
combined_duration: f32,
|
combined_duration: f32,
|
||||||
before_change_style: &Arc<ComputedValues>,
|
before_change_style: &ComputedValues,
|
||||||
after_change_style: &Arc<ComputedValues>,
|
after_change_style: &ComputedValues,
|
||||||
existing_transitions: &HashMap<TransitionProperty,
|
existing_transitions: &HashMap<TransitionProperty,
|
||||||
Arc<AnimationValue>>)
|
Arc<AnimationValue>>)
|
||||||
-> bool;
|
-> bool;
|
||||||
|
|
|
@ -54,6 +54,26 @@ pub const EAGER_PSEUDO_COUNT: usize = 2;
|
||||||
|
|
||||||
|
|
||||||
impl PseudoElement {
|
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.
|
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn eager_index(&self) -> usize {
|
pub fn eager_index(&self) -> usize {
|
||||||
|
@ -437,24 +457,9 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
||||||
|
|
||||||
impl SelectorImpl {
|
impl SelectorImpl {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Returns the kind of cascade type that a given pseudo is going to use.
|
/// Legacy alias for PseudoElement::cascade_type.
|
||||||
///
|
|
||||||
/// 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 pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
|
pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
|
||||||
if pseudo.is_eager() {
|
pseudo.cascade_type()
|
||||||
debug_assert!(!pseudo.is_anon_box());
|
|
||||||
return PseudoElementCascadeType::Eager
|
|
||||||
}
|
|
||||||
|
|
||||||
if pseudo.is_anon_box() {
|
|
||||||
return PseudoElementCascadeType::Precomputed
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoElementCascadeType::Lazy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper to traverse each eagerly cascaded pseudo-element, executing
|
/// A helper to traverse each eagerly cascaded pseudo-element, executing
|
||||||
|
|
|
@ -421,12 +421,15 @@ fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_animation_rule(element: &GeckoElement,
|
fn get_animation_rule(element: &GeckoElement,
|
||||||
pseudo: Option<&PseudoElement>,
|
|
||||||
cascade_level: CascadeLevel)
|
cascade_level: CascadeLevel)
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
// FIXME(emilio): This is quite dumb, why an RwLock, it's local to this
|
||||||
|
// function?
|
||||||
|
//
|
||||||
|
// Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
|
||||||
let animation_values = Arc::new(RwLock::new(AnimationValueMap::new()));
|
let animation_values = Arc::new(RwLock::new(AnimationValueMap::new()));
|
||||||
if unsafe { Gecko_GetAnimationRule(element.0, atom_ptr, cascade_level,
|
if unsafe { Gecko_GetAnimationRule(element.0,
|
||||||
|
cascade_level,
|
||||||
HasArcFFI::arc_as_borrowed(&animation_values)) } {
|
HasArcFFI::arc_as_borrowed(&animation_values)) } {
|
||||||
let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
|
let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
|
||||||
Some(Arc::new(shared_lock.wrap(
|
Some(Arc::new(shared_lock.wrap(
|
||||||
|
@ -531,30 +534,28 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
declarations.map(|s| s.as_arc_opt()).unwrap_or(None)
|
declarations.map(|s| s.as_arc_opt()).unwrap_or(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_animation_rules(&self, pseudo: Option<&PseudoElement>) -> AnimationRules {
|
fn get_animation_rules(&self) -> AnimationRules {
|
||||||
AnimationRules(self.get_animation_rule(pseudo),
|
AnimationRules(self.get_animation_rule(),
|
||||||
self.get_transition_rule(pseudo))
|
self.get_transition_rule())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_animation_rule_by_cascade(&self,
|
fn get_animation_rule_by_cascade(&self, cascade_level: ServoCascadeLevel)
|
||||||
pseudo: Option<&PseudoElement>,
|
|
||||||
cascade_level: ServoCascadeLevel)
|
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
match cascade_level {
|
match cascade_level {
|
||||||
ServoCascadeLevel::Animations => self.get_animation_rule(pseudo),
|
ServoCascadeLevel::Animations => self.get_animation_rule(),
|
||||||
ServoCascadeLevel::Transitions => self.get_transition_rule(pseudo),
|
ServoCascadeLevel::Transitions => self.get_transition_rule(),
|
||||||
_ => panic!("Unsupported cascade level for getting the animation rule")
|
_ => panic!("Unsupported cascade level for getting the animation rule")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_animation_rule(&self, pseudo: Option<&PseudoElement>)
|
fn get_animation_rule(&self)
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
get_animation_rule(self, pseudo, CascadeLevel::Animations)
|
get_animation_rule(self, CascadeLevel::Animations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_transition_rule(&self, pseudo: Option<&PseudoElement>)
|
fn get_transition_rule(&self)
|
||||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||||
get_animation_rule(self, pseudo, CascadeLevel::Transitions)
|
get_animation_rule(self, CascadeLevel::Transitions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state(&self) -> ElementState {
|
fn get_state(&self) -> ElementState {
|
||||||
|
@ -589,7 +590,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
-> Option<&'a nsStyleContext> {
|
-> Option<&'a nsStyleContext> {
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
||||||
unsafe {
|
unsafe {
|
||||||
let context_ptr = Gecko_GetStyleContext(self.as_node().0, atom_ptr);
|
let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr);
|
||||||
context_ptr.as_ref()
|
context_ptr.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,6 +642,22 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
|
self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
||||||
|
if !self.is_native_anonymous() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_atom =
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
fn store_children_to_process(&self, _: isize) {
|
fn store_children_to_process(&self, _: isize) {
|
||||||
// This is only used for bottom-up traversal, and is thus a no-op for Gecko.
|
// This is only used for bottom-up traversal, and is thus a no-op for Gecko.
|
||||||
}
|
}
|
||||||
|
@ -673,37 +690,27 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_animations(&self,
|
fn update_animations(&self,
|
||||||
pseudo: Option<&PseudoElement>,
|
|
||||||
before_change_style: Option<Arc<ComputedValues>>,
|
before_change_style: Option<Arc<ComputedValues>>,
|
||||||
tasks: UpdateAnimationsTasks) {
|
tasks: UpdateAnimationsTasks) {
|
||||||
// We have to update animations even if the element has no computed style
|
// We have to update animations even if the element has no computed
|
||||||
// since it means the element is in a display:none subtree, we should destroy
|
// style since it means the element is in a display:none subtree, we
|
||||||
// all CSS animations in display:none subtree.
|
// should destroy all CSS animations in display:none subtree.
|
||||||
let computed_data = self.borrow_data();
|
let computed_data = self.borrow_data();
|
||||||
let computed_values =
|
let computed_values =
|
||||||
computed_data.as_ref().map(|d|
|
computed_data.as_ref().map(|d| d.styles().primary.values());
|
||||||
pseudo.map_or_else(|| d.styles().primary.values(),
|
let computed_values_opt =
|
||||||
|p| d.styles().pseudos.get(p).unwrap().values())
|
computed_values.map(|v| *HasArcFFI::arc_as_borrowed(v));
|
||||||
);
|
let parent_element = self.parent_element();
|
||||||
let computed_values_opt = computed_values.map(|v|
|
let parent_data =
|
||||||
*HasArcFFI::arc_as_borrowed(v)
|
parent_element.as_ref().and_then(|e| e.borrow_data());
|
||||||
);
|
let parent_values =
|
||||||
|
parent_data.as_ref().map(|d| d.styles().primary.values());
|
||||||
let parent_element = if pseudo.is_none() {
|
let parent_values_opt =
|
||||||
self.parent_element()
|
parent_values.map(|v| *HasArcFFI::arc_as_borrowed(v));
|
||||||
} else {
|
let before_change_values =
|
||||||
Some(*self)
|
before_change_style.as_ref().map(|v| *HasArcFFI::arc_as_borrowed(v));
|
||||||
};
|
|
||||||
let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
|
|
||||||
let parent_values = parent_data.as_ref().map(|d| d.styles().primary.values());
|
|
||||||
let parent_values_opt = parent_values.map(|v|
|
|
||||||
*HasArcFFI::arc_as_borrowed(v)
|
|
||||||
);
|
|
||||||
|
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
|
||||||
let before_change_values = before_change_style.as_ref().map(|v| *HasArcFFI::arc_as_borrowed(v));
|
|
||||||
unsafe {
|
unsafe {
|
||||||
Gecko_UpdateAnimations(self.0, atom_ptr,
|
Gecko_UpdateAnimations(self.0,
|
||||||
before_change_values,
|
before_change_values,
|
||||||
computed_values_opt,
|
computed_values_opt,
|
||||||
parent_values_opt,
|
parent_values_opt,
|
||||||
|
@ -711,35 +718,31 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_animations(&self, pseudo: Option<&PseudoElement>) -> bool {
|
fn has_animations(&self) -> bool {
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
unsafe { Gecko_ElementHasAnimations(self.0) }
|
||||||
unsafe { Gecko_ElementHasAnimations(self.0, atom_ptr) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_animations(&self, pseudo: Option<&PseudoElement>) -> bool {
|
fn has_css_animations(&self) -> bool {
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
unsafe { Gecko_ElementHasCSSAnimations(self.0) }
|
||||||
unsafe { Gecko_ElementHasCSSAnimations(self.0, atom_ptr) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_transitions(&self, pseudo: Option<&PseudoElement>) -> bool {
|
fn has_css_transitions(&self) -> bool {
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
unsafe { Gecko_ElementHasCSSTransitions(self.0) }
|
||||||
unsafe { Gecko_ElementHasCSSTransitions(self.0, atom_ptr) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_css_transitions_info(&self,
|
fn get_css_transitions_info(&self)
|
||||||
pseudo: Option<&PseudoElement>)
|
|
||||||
-> HashMap<TransitionProperty, Arc<AnimationValue>> {
|
-> HashMap<TransitionProperty, Arc<AnimationValue>> {
|
||||||
use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
|
use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
|
||||||
use gecko_bindings::bindings::Gecko_ElementTransitions_Length;
|
use gecko_bindings::bindings::Gecko_ElementTransitions_Length;
|
||||||
use gecko_bindings::bindings::Gecko_ElementTransitions_PropertyAt;
|
use gecko_bindings::bindings::Gecko_ElementTransitions_PropertyAt;
|
||||||
|
|
||||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
let collection_length =
|
||||||
let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0, atom_ptr) };
|
unsafe { Gecko_ElementTransitions_Length(self.0) };
|
||||||
let mut map = HashMap::with_capacity(collection_length);
|
let mut map = HashMap::with_capacity(collection_length);
|
||||||
for i in 0..collection_length {
|
for i in 0..collection_length {
|
||||||
let (property, raw_end_value) = unsafe {
|
let (property, raw_end_value) = unsafe {
|
||||||
(Gecko_ElementTransitions_PropertyAt(self.0, atom_ptr, i as usize).into(),
|
(Gecko_ElementTransitions_PropertyAt(self.0, i as usize).into(),
|
||||||
Gecko_ElementTransitions_EndValueAt(self.0, atom_ptr, i as usize))
|
Gecko_ElementTransitions_EndValueAt(self.0, i as usize))
|
||||||
};
|
};
|
||||||
let end_value = AnimationValue::arc_from_borrowed(&raw_end_value);
|
let end_value = AnimationValue::arc_from_borrowed(&raw_end_value);
|
||||||
debug_assert!(end_value.is_some());
|
debug_assert!(end_value.is_some());
|
||||||
|
@ -749,21 +752,21 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn might_need_transitions_update(&self,
|
fn might_need_transitions_update(&self,
|
||||||
old_values: &Option<&Arc<ComputedValues>>,
|
old_values: Option<&ComputedValues>,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &ComputedValues) -> bool {
|
||||||
pseudo: Option<&PseudoElement>) -> bool {
|
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
|
|
||||||
if old_values.is_none() {
|
let old_values = match old_values {
|
||||||
return false;
|
Some(v) => v,
|
||||||
}
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
let ref new_box_style = new_values.get_box();
|
let new_box_style = new_values.get_box();
|
||||||
let transition_not_running = !self.has_css_transitions(pseudo) &&
|
let transition_not_running = !self.has_css_transitions() &&
|
||||||
new_box_style.transition_property_count() == 1 &&
|
new_box_style.transition_property_count() == 1 &&
|
||||||
new_box_style.transition_combined_duration_at(0) <= 0.0f32;
|
new_box_style.transition_combined_duration_at(0) <= 0.0f32;
|
||||||
let new_display_style = new_box_style.clone_display();
|
let new_display_style = new_box_style.clone_display();
|
||||||
let old_display_style = old_values.map(|ref old| old.get_box().clone_display()).unwrap();
|
let old_display_style = old_values.get_box().clone_display();
|
||||||
|
|
||||||
new_box_style.transition_property_count() > 0 &&
|
new_box_style.transition_property_count() > 0 &&
|
||||||
!transition_not_running &&
|
!transition_not_running &&
|
||||||
|
@ -771,28 +774,31 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
old_display_style != display::T::none)
|
old_display_style != display::T::none)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect if there are any changes that require us to update transitions. This is used as a
|
// Detect if there are any changes that require us to update transitions.
|
||||||
// more thoroughgoing check than the, cheaper might_need_transitions_update check.
|
// This is used as a more thoroughgoing check than the, cheaper
|
||||||
|
// might_need_transitions_update check.
|
||||||
|
//
|
||||||
// The following logic shadows the logic used on the Gecko side
|
// The following logic shadows the logic used on the Gecko side
|
||||||
// (nsTransitionManager::DoUpdateTransitions) where we actually perform the update.
|
// (nsTransitionManager::DoUpdateTransitions) where we actually perform the
|
||||||
|
// update.
|
||||||
|
//
|
||||||
// https://drafts.csswg.org/css-transitions/#starting
|
// https://drafts.csswg.org/css-transitions/#starting
|
||||||
fn needs_transitions_update(&self,
|
fn needs_transitions_update(&self,
|
||||||
before_change_style: &Arc<ComputedValues>,
|
before_change_style: &ComputedValues,
|
||||||
after_change_style: &Arc<ComputedValues>,
|
after_change_style: &ComputedValues)
|
||||||
pseudo: Option<&PseudoElement>) -> bool {
|
-> bool {
|
||||||
use gecko_bindings::structs::nsCSSPropertyID;
|
use gecko_bindings::structs::nsCSSPropertyID;
|
||||||
use properties::{PropertyId, animated_properties};
|
use properties::{PropertyId, animated_properties};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
debug_assert!(self.might_need_transitions_update(&Some(before_change_style),
|
debug_assert!(self.might_need_transitions_update(Some(before_change_style),
|
||||||
after_change_style,
|
after_change_style),
|
||||||
pseudo),
|
|
||||||
"We should only call needs_transitions_update if \
|
"We should only call needs_transitions_update if \
|
||||||
might_need_transitions_update returns true");
|
might_need_transitions_update returns true");
|
||||||
|
|
||||||
let ref after_change_box_style = after_change_style.get_box();
|
let after_change_box_style = after_change_style.get_box();
|
||||||
let transitions_count = after_change_box_style.transition_property_count();
|
let transitions_count = after_change_box_style.transition_property_count();
|
||||||
let existing_transitions = self.get_css_transitions_info(pseudo);
|
let existing_transitions = self.get_css_transitions_info();
|
||||||
let mut transitions_to_keep = if !existing_transitions.is_empty() &&
|
let mut transitions_to_keep = if !existing_transitions.is_empty() &&
|
||||||
(after_change_box_style.transition_nscsspropertyid_at(0) !=
|
(after_change_box_style.transition_nscsspropertyid_at(0) !=
|
||||||
nsCSSPropertyID::eCSSPropertyExtra_all_properties) {
|
nsCSSPropertyID::eCSSPropertyExtra_all_properties) {
|
||||||
|
@ -865,8 +871,8 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
fn needs_transitions_update_per_property(&self,
|
fn needs_transitions_update_per_property(&self,
|
||||||
property: &TransitionProperty,
|
property: &TransitionProperty,
|
||||||
combined_duration: f32,
|
combined_duration: f32,
|
||||||
before_change_style: &Arc<ComputedValues>,
|
before_change_style: &ComputedValues,
|
||||||
after_change_style: &Arc<ComputedValues>,
|
after_change_style: &ComputedValues,
|
||||||
existing_transitions: &HashMap<TransitionProperty,
|
existing_transitions: &HashMap<TransitionProperty,
|
||||||
Arc<AnimationValue>>)
|
Arc<AnimationValue>>)
|
||||||
-> bool {
|
-> bool {
|
||||||
|
|
|
@ -615,8 +615,7 @@ extern "C" {
|
||||||
-> RawServoDeclarationBlockStrongBorrowedOrNull;
|
-> RawServoDeclarationBlockStrongBorrowedOrNull;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_GetAnimationRule(aElement: RawGeckoElementBorrowed,
|
pub fn Gecko_GetAnimationRule(aElementOrPseudo: RawGeckoElementBorrowed,
|
||||||
aPseudoTag: *mut nsIAtom,
|
|
||||||
aCascadeLevel:
|
aCascadeLevel:
|
||||||
EffectCompositor_CascadeLevel,
|
EffectCompositor_CascadeLevel,
|
||||||
aAnimationValues:
|
aAnimationValues:
|
||||||
|
@ -636,46 +635,42 @@ extern "C" {
|
||||||
-> bool;
|
-> bool;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_UpdateAnimations(aElement: RawGeckoElementBorrowed,
|
pub fn Gecko_UpdateAnimations(aElementOrPseudo: RawGeckoElementBorrowed,
|
||||||
aPseudoTagOrNull: *mut nsIAtom,
|
|
||||||
aOldComputedValues:
|
aOldComputedValues:
|
||||||
ServoComputedValuesBorrowedOrNull,
|
ServoComputedValuesBorrowedOrNull,
|
||||||
aComputedValues:
|
aComputedValues:
|
||||||
ServoComputedValuesBorrowedOrNull,
|
ServoComputedValuesBorrowedOrNull,
|
||||||
aParentComputedValues:
|
aParentComputedValues:
|
||||||
ServoComputedValuesBorrowedOrNull,
|
ServoComputedValuesBorrowedOrNull,
|
||||||
aTaskBits: UpdateAnimationsTasks);
|
aTasks: UpdateAnimationsTasks);
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_ElementHasAnimations(aElement: RawGeckoElementBorrowed,
|
pub fn Gecko_ElementHasAnimations(aElementOrPseudo:
|
||||||
aPseudoTagOrNull: *mut nsIAtom) -> bool;
|
RawGeckoElementBorrowed) -> bool;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_ElementHasCSSAnimations(aElement: RawGeckoElementBorrowed,
|
pub fn Gecko_ElementHasCSSAnimations(aElementOrPseudo:
|
||||||
aPseudoTagOrNull: *mut nsIAtom)
|
RawGeckoElementBorrowed) -> bool;
|
||||||
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_ElementHasCSSTransitions(aElementOrPseudo:
|
||||||
|
RawGeckoElementBorrowed)
|
||||||
-> bool;
|
-> bool;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_ElementHasCSSTransitions(aElement: RawGeckoElementBorrowed,
|
pub fn Gecko_ElementTransitions_Length(aElementOrPseudo:
|
||||||
aPseudoTagOrNull: *mut nsIAtom)
|
RawGeckoElementBorrowed)
|
||||||
-> bool;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn Gecko_ElementTransitions_Length(aElement: RawGeckoElementBorrowed,
|
|
||||||
aPseudoTagOrNull: *mut nsIAtom)
|
|
||||||
-> usize;
|
-> usize;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_ElementTransitions_PropertyAt(aElement:
|
pub fn Gecko_ElementTransitions_PropertyAt(aElementOrPseudo:
|
||||||
RawGeckoElementBorrowed,
|
RawGeckoElementBorrowed,
|
||||||
aPseudoTagOrNull: *mut nsIAtom,
|
|
||||||
aIndex: usize)
|
aIndex: usize)
|
||||||
-> nsCSSPropertyID;
|
-> nsCSSPropertyID;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_ElementTransitions_EndValueAt(aElement:
|
pub fn Gecko_ElementTransitions_EndValueAt(aElementOrPseudo:
|
||||||
RawGeckoElementBorrowed,
|
RawGeckoElementBorrowed,
|
||||||
aPseudoTagOrNull: *mut nsIAtom,
|
|
||||||
aIndex: usize)
|
aIndex: usize)
|
||||||
-> RawServoAnimationValueBorrowedOrNull;
|
-> RawServoAnimationValueBorrowedOrNull;
|
||||||
}
|
}
|
||||||
|
@ -860,10 +855,14 @@ extern "C" {
|
||||||
RawGeckoElementBorrowed);
|
RawGeckoElementBorrowed);
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_GetStyleContext(node: RawGeckoNodeBorrowed,
|
pub fn Gecko_GetStyleContext(element: RawGeckoElementBorrowed,
|
||||||
aPseudoTagOrNull: *mut nsIAtom)
|
aPseudoTagOrNull: *mut nsIAtom)
|
||||||
-> *mut nsStyleContext;
|
-> *mut nsStyleContext;
|
||||||
}
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed)
|
||||||
|
-> *mut nsIAtom;
|
||||||
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
||||||
newstyle: ServoComputedValuesBorrowed)
|
newstyle: ServoComputedValuesBorrowed)
|
||||||
|
|
|
@ -30,6 +30,15 @@ use sink::ForgetfulSink;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stylist::ApplicableDeclarationBlock;
|
use stylist::ApplicableDeclarationBlock;
|
||||||
|
|
||||||
|
/// The way a style should be inherited.
|
||||||
|
enum InheritMode {
|
||||||
|
/// Inherit from the parent element, as normal CSS dictates.
|
||||||
|
FromParentElement,
|
||||||
|
/// Inherit from the primary style, this is used while computing eager
|
||||||
|
/// pseudos, like ::before and ::after when we're traversing the parent.
|
||||||
|
FromPrimaryStyle,
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines the amount of relations where we're going to share style.
|
/// Determines the amount of relations where we're going to share style.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn relations_are_shareable(relations: &StyleRelations) -> bool {
|
fn relations_are_shareable(relations: &StyleRelations) -> bool {
|
||||||
|
@ -78,6 +87,8 @@ pub struct StyleSharingCandidateCache<E: TElement> {
|
||||||
pub enum CacheMiss {
|
pub enum CacheMiss {
|
||||||
/// The parents don't match.
|
/// The parents don't match.
|
||||||
Parent,
|
Parent,
|
||||||
|
/// One element was NAC, while the other wasn't.
|
||||||
|
NativeAnonymousContent,
|
||||||
/// The local name of the element and the candidate don't match.
|
/// The local name of the element and the candidate don't match.
|
||||||
LocalName,
|
LocalName,
|
||||||
/// The namespace of the element and the candidate don't match.
|
/// The namespace of the element and the candidate don't match.
|
||||||
|
@ -137,6 +148,12 @@ fn element_matches_candidate<E: TElement>(element: &E,
|
||||||
miss!(Parent)
|
miss!(Parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if element.is_native_anonymous() {
|
||||||
|
debug_assert!(!candidate_element.is_native_anonymous(),
|
||||||
|
"Why inserting NAC into the cache?");
|
||||||
|
miss!(NativeAnonymousContent)
|
||||||
|
}
|
||||||
|
|
||||||
if *element.get_local_name() != *candidate_element.get_local_name() {
|
if *element.get_local_name() != *candidate_element.get_local_name() {
|
||||||
miss!(LocalName)
|
miss!(LocalName)
|
||||||
}
|
}
|
||||||
|
@ -298,6 +315,11 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if element.is_native_anonymous() {
|
||||||
|
debug!("Failing to insert into the cache: NAC");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// These are things we don't check in the candidate match because they
|
// These are things we don't check in the candidate match because they
|
||||||
// are either uncommon or expensive.
|
// are either uncommon or expensive.
|
||||||
if !relations_are_shareable(&relations) {
|
if !relations_are_shareable(&relations) {
|
||||||
|
@ -312,7 +334,6 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
debug_assert!(hints.is_empty(), "Style relations should not be shareable!");
|
debug_assert!(hints.is_empty(), "Style relations should not be shareable!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let box_style = style.get_box();
|
let box_style = style.get_box();
|
||||||
if box_style.specifies_transitions() {
|
if box_style.specifies_transitions() {
|
||||||
debug!("Failing to insert to the cache: transitions");
|
debug!("Failing to insert to the cache: transitions");
|
||||||
|
@ -387,7 +408,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
font_metrics_provider: &FontMetricsProvider,
|
font_metrics_provider: &FontMetricsProvider,
|
||||||
rule_node: &StrongRuleNode,
|
rule_node: &StrongRuleNode,
|
||||||
primary_style: &ComputedStyle,
|
primary_style: &ComputedStyle,
|
||||||
is_pseudo: bool)
|
inherit_mode: InheritMode)
|
||||||
-> Arc<ComputedValues> {
|
-> Arc<ComputedValues> {
|
||||||
let mut cascade_info = CascadeInfo::new();
|
let mut cascade_info = CascadeInfo::new();
|
||||||
let mut cascade_flags = CascadeFlags::empty();
|
let mut cascade_flags = CascadeFlags::empty();
|
||||||
|
@ -398,24 +419,27 @@ trait PrivateMatchMethods: TElement {
|
||||||
// Grab the inherited values.
|
// Grab the inherited values.
|
||||||
let parent_el;
|
let parent_el;
|
||||||
let parent_data;
|
let parent_data;
|
||||||
let style_to_inherit_from = if !is_pseudo {
|
let style_to_inherit_from = match inherit_mode {
|
||||||
parent_el = self.parent_element();
|
InheritMode::FromParentElement => {
|
||||||
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
parent_el = self.parent_element();
|
||||||
let parent_values = parent_data.as_ref().map(|d| {
|
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
||||||
// Sometimes Gecko eagerly styles things without processing
|
let parent_values = parent_data.as_ref().map(|d| {
|
||||||
// pending restyles first. In general we'd like to avoid this,
|
// Sometimes Gecko eagerly styles things without processing
|
||||||
// but there can be good reasons (for example, needing to
|
// pending restyles first. In general we'd like to avoid this,
|
||||||
// construct a frame for some small piece of newly-added
|
// but there can be good reasons (for example, needing to
|
||||||
// content in order to do something specific with that frame,
|
// construct a frame for some small piece of newly-added
|
||||||
// but not wanting to flush all of layout).
|
// content in order to do something specific with that frame,
|
||||||
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
|
// but not wanting to flush all of layout).
|
||||||
d.styles().primary.values()
|
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
|
||||||
});
|
d.styles().primary.values()
|
||||||
|
});
|
||||||
|
|
||||||
parent_values
|
parent_values
|
||||||
} else {
|
}
|
||||||
parent_el = Some(self.clone());
|
InheritMode::FromPrimaryStyle => {
|
||||||
Some(primary_style.values())
|
parent_el = Some(self.clone());
|
||||||
|
Some(primary_style.values())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layout_parent_el = parent_el.clone();
|
let mut layout_parent_el = parent_el.clone();
|
||||||
|
@ -433,15 +457,16 @@ trait PrivateMatchMethods: TElement {
|
||||||
// Propagate the "can be fragmented" bit. It would be nice to
|
// Propagate the "can be fragmented" bit. It would be nice to
|
||||||
// encapsulate this better.
|
// encapsulate this better.
|
||||||
//
|
//
|
||||||
// Note that this is not needed for pseudos since we already do that
|
// Note that this is technically not needed for pseudos since we already
|
||||||
// when we resolve the non-pseudo style.
|
// do that when we resolve the non-pseudo style, but it doesn't hurt
|
||||||
if !is_pseudo {
|
// anyway.
|
||||||
if let Some(ref p) = layout_parent_style {
|
//
|
||||||
let can_be_fragmented =
|
// TODO(emilio): This is servo-only, move somewhere else?
|
||||||
p.is_multicol() ||
|
if let Some(ref p) = layout_parent_style {
|
||||||
layout_parent_el.as_ref().unwrap().as_node().can_be_fragmented();
|
let can_be_fragmented =
|
||||||
unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
|
p.is_multicol() ||
|
||||||
}
|
layout_parent_el.as_ref().unwrap().as_node().can_be_fragmented();
|
||||||
|
unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the cascade algorithm.
|
// Invoke the cascade algorithm.
|
||||||
|
@ -463,16 +488,21 @@ trait PrivateMatchMethods: TElement {
|
||||||
fn cascade_internal(&self,
|
fn cascade_internal(&self,
|
||||||
context: &StyleContext<Self>,
|
context: &StyleContext<Self>,
|
||||||
primary_style: &ComputedStyle,
|
primary_style: &ComputedStyle,
|
||||||
pseudo_style: Option<&ComputedStyle>)
|
eager_pseudo_style: Option<&ComputedStyle>)
|
||||||
-> Arc<ComputedValues> {
|
-> Arc<ComputedValues> {
|
||||||
// Grab the rule node.
|
// Grab the rule node.
|
||||||
let rule_node = &pseudo_style.unwrap_or(primary_style).rules;
|
let rule_node = &eager_pseudo_style.unwrap_or(primary_style).rules;
|
||||||
|
let inherit_mode = if eager_pseudo_style.is_some() {
|
||||||
|
InheritMode::FromPrimaryStyle
|
||||||
|
} else {
|
||||||
|
InheritMode::FromParentElement
|
||||||
|
};
|
||||||
|
|
||||||
self.cascade_with_rules(context.shared,
|
self.cascade_with_rules(context.shared,
|
||||||
&context.thread_local.font_metrics_provider,
|
&context.thread_local.font_metrics_provider,
|
||||||
rule_node,
|
rule_node,
|
||||||
primary_style,
|
primary_style,
|
||||||
pseudo_style.is_some())
|
inherit_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes values and damage for the primary or pseudo style of an element,
|
/// Computes values and damage for the primary or pseudo style of an element,
|
||||||
|
@ -480,8 +510,9 @@ trait PrivateMatchMethods: TElement {
|
||||||
fn cascade_primary_or_pseudo(&self,
|
fn cascade_primary_or_pseudo(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
pseudo: Option<&PseudoElement>,
|
pseudo: Option<&PseudoElement>) {
|
||||||
animate: bool) {
|
debug_assert!(pseudo.is_none() || self.implemented_pseudo_element().is_none(),
|
||||||
|
"Pseudo-element-implementing elements can't have pseudos!");
|
||||||
// Collect some values.
|
// Collect some values.
|
||||||
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||||
let mut primary_style = &mut styles.primary;
|
let mut primary_style = &mut styles.primary;
|
||||||
|
@ -501,27 +532,65 @@ trait PrivateMatchMethods: TElement {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute the new values.
|
// Compute the new values.
|
||||||
let mut new_values = self.cascade_internal(context,
|
let mut new_values = match self.implemented_pseudo_element() {
|
||||||
primary_style,
|
Some(ref pseudo) => {
|
||||||
pseudo_style.as_ref().map(|s| &**s));
|
// This is an element-backed pseudo, just grab the styles from
|
||||||
|
// the parent if it's eager, and recascade otherwise.
|
||||||
|
//
|
||||||
|
// We also recascade if the eager pseudo-style has any animation
|
||||||
|
// rules, because we don't cascade those during the eager
|
||||||
|
// traversal. We could make that a bit better if the complexity
|
||||||
|
// cost is not too big, but given further restyles are posted
|
||||||
|
// directly to pseudo-elements, it doesn't seem worth the effort
|
||||||
|
// at a glance.
|
||||||
|
if pseudo.is_eager() &&
|
||||||
|
self.get_animation_rules().is_empty() {
|
||||||
|
let parent = self.parent_element().unwrap();
|
||||||
|
|
||||||
// Handle animations.
|
let parent_data = parent.borrow_data().unwrap();
|
||||||
if animate && !context.shared.traversal_flags.for_animation_only() {
|
let pseudo_style =
|
||||||
|
parent_data.styles().pseudos.get(pseudo).unwrap();
|
||||||
|
pseudo_style.values().clone()
|
||||||
|
} else {
|
||||||
|
self.cascade_internal(context,
|
||||||
|
primary_style,
|
||||||
|
None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Else it's an eager pseudo or a normal element, do the cascade
|
||||||
|
// work.
|
||||||
|
self.cascade_internal(context,
|
||||||
|
primary_style,
|
||||||
|
pseudo_style.as_ref().map(|s| &**s))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NB: Animations for pseudo-elements in Gecko are handled while
|
||||||
|
// traversing the pseudo-elements themselves.
|
||||||
|
if pseudo.is_none() &&
|
||||||
|
!context.shared.traversal_flags.for_animation_only() {
|
||||||
self.process_animations(context,
|
self.process_animations(context,
|
||||||
&mut old_values,
|
&mut old_values,
|
||||||
&mut new_values,
|
&mut new_values,
|
||||||
primary_style,
|
primary_style);
|
||||||
pseudo,
|
|
||||||
pseudo_style.as_ref().map(|s| &**s));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate restyle damage.
|
// Accumulate restyle damage.
|
||||||
if let Some(old) = old_values {
|
if let Some(old) = old_values {
|
||||||
self.accumulate_damage(&context.shared,
|
// ::before and ::after are element-backed in Gecko, so they do
|
||||||
restyle.unwrap(),
|
// the damage calculation for themselves.
|
||||||
&old,
|
//
|
||||||
&new_values,
|
// FIXME(emilio): We have more element-backed stuff, and this is
|
||||||
pseudo);
|
// redundant for them right now.
|
||||||
|
if cfg!(feature = "servo") ||
|
||||||
|
pseudo.map_or(true, |p| !p.is_before_or_after()) {
|
||||||
|
self.accumulate_damage(&context.shared,
|
||||||
|
restyle.unwrap(),
|
||||||
|
&old,
|
||||||
|
&new_values,
|
||||||
|
pseudo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the new computed values.
|
// Set the new computed values.
|
||||||
|
@ -534,11 +603,9 @@ trait PrivateMatchMethods: TElement {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn get_after_change_style(&self,
|
fn get_after_change_style(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
primary_style: &ComputedStyle,
|
primary_style: &ComputedStyle)
|
||||||
pseudo_style: Option<&ComputedStyle>)
|
|
||||||
-> Option<Arc<ComputedValues>> {
|
-> Option<Arc<ComputedValues>> {
|
||||||
let relevant_style = pseudo_style.unwrap_or(primary_style);
|
let rule_node = &primary_style.rules;
|
||||||
let rule_node = &relevant_style.rules;
|
|
||||||
let without_transition_rules =
|
let without_transition_rules =
|
||||||
context.shared.stylist.rule_tree.remove_transition_rule_if_applicable(rule_node);
|
context.shared.stylist.rule_tree.remove_transition_rule_if_applicable(rule_node);
|
||||||
if without_transition_rules == *rule_node {
|
if without_transition_rules == *rule_node {
|
||||||
|
@ -551,21 +618,21 @@ trait PrivateMatchMethods: TElement {
|
||||||
&context.thread_local.font_metrics_provider,
|
&context.thread_local.font_metrics_provider,
|
||||||
&without_transition_rules,
|
&without_transition_rules,
|
||||||
primary_style,
|
primary_style,
|
||||||
pseudo_style.is_some()))
|
InheritMode::FromParentElement))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn needs_animations_update(&self,
|
fn needs_animations_update(&self,
|
||||||
old_values: &Option<Arc<ComputedValues>>,
|
old_values: Option<&Arc<ComputedValues>>,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &ComputedValues)
|
||||||
pseudo: Option<&PseudoElement>) -> bool {
|
-> bool {
|
||||||
let ref new_box_style = new_values.get_box();
|
let new_box_style = new_values.get_box();
|
||||||
let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
|
let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
|
||||||
new_box_style.animation_name_at(0).0.is_some();
|
new_box_style.animation_name_at(0).0.is_some();
|
||||||
let has_animations = self.has_css_animations(pseudo);
|
let has_animations = self.has_css_animations();
|
||||||
|
|
||||||
old_values.as_ref().map_or(has_new_animation_style, |ref old| {
|
old_values.map_or(has_new_animation_style, |old| {
|
||||||
let ref old_box_style = old.get_box();
|
let old_box_style = old.get_box();
|
||||||
let old_display_style = old_box_style.clone_display();
|
let old_display_style = old_box_style.clone_display();
|
||||||
let new_display_style = new_box_style.clone_display();
|
let new_display_style = new_box_style.clone_display();
|
||||||
// FIXME: Bug 1344581: We still need to compare keyframe rules.
|
// FIXME: Bug 1344581: We still need to compare keyframe rules.
|
||||||
|
@ -584,40 +651,34 @@ trait PrivateMatchMethods: TElement {
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
old_values: &mut Option<Arc<ComputedValues>>,
|
old_values: &mut Option<Arc<ComputedValues>>,
|
||||||
new_values: &mut Arc<ComputedValues>,
|
new_values: &mut Arc<ComputedValues>,
|
||||||
primary_style: &ComputedStyle,
|
primary_style: &ComputedStyle) {
|
||||||
pseudo: Option<&PseudoElement>,
|
|
||||||
pseudo_style: Option<&ComputedStyle>) {
|
|
||||||
use context::{CSS_ANIMATIONS, CSS_TRANSITIONS, EFFECT_PROPERTIES};
|
use context::{CSS_ANIMATIONS, CSS_TRANSITIONS, EFFECT_PROPERTIES};
|
||||||
use context::UpdateAnimationsTasks;
|
use context::UpdateAnimationsTasks;
|
||||||
|
|
||||||
debug_assert_eq!(pseudo.is_some(), pseudo_style.is_some());
|
|
||||||
|
|
||||||
let mut tasks = UpdateAnimationsTasks::empty();
|
let mut tasks = UpdateAnimationsTasks::empty();
|
||||||
if self.needs_animations_update(old_values, new_values, pseudo) {
|
if self.needs_animations_update(old_values.as_ref(), new_values) {
|
||||||
tasks.insert(CSS_ANIMATIONS);
|
tasks.insert(CSS_ANIMATIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
let before_change_style = if self.might_need_transitions_update(&old_values.as_ref(),
|
let before_change_style = if self.might_need_transitions_update(old_values.as_ref().map(|s| &**s),
|
||||||
new_values,
|
new_values) {
|
||||||
pseudo) {
|
let after_change_style = if self.has_css_transitions() {
|
||||||
let after_change_style = if self.has_css_transitions(pseudo) {
|
self.get_after_change_style(context, primary_style)
|
||||||
self.get_after_change_style(context, primary_style, pseudo_style)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// In order to avoid creating a SequentialTask for transitions which may not be updated,
|
// In order to avoid creating a SequentialTask for transitions which
|
||||||
// we check it per property to make sure Gecko side will really update transition.
|
// may not be updated, we check it per property to make sure Gecko
|
||||||
|
// side will really update transition.
|
||||||
let needs_transitions_update = {
|
let needs_transitions_update = {
|
||||||
// We borrow new_values here, so need to add a scope to make sure we release it
|
// We borrow new_values here, so need to add a scope to make
|
||||||
// before assigning a new value to it.
|
// sure we release it before assigning a new value to it.
|
||||||
let after_change_style_ref = match after_change_style {
|
let after_change_style_ref =
|
||||||
Some(ref value) => value,
|
after_change_style.as_ref().unwrap_or(&new_values);
|
||||||
None => &new_values
|
|
||||||
};
|
|
||||||
self.needs_transitions_update(old_values.as_ref().unwrap(),
|
self.needs_transitions_update(old_values.as_ref().unwrap(),
|
||||||
after_change_style_ref,
|
after_change_style_ref)
|
||||||
pseudo)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if needs_transitions_update {
|
if needs_transitions_update {
|
||||||
|
@ -635,13 +696,12 @@ trait PrivateMatchMethods: TElement {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.has_animations(pseudo) {
|
if self.has_animations() {
|
||||||
tasks.insert(EFFECT_PROPERTIES);
|
tasks.insert(EFFECT_PROPERTIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tasks.is_empty() {
|
if !tasks.is_empty() {
|
||||||
let task = ::context::SequentialTask::update_animations(*self,
|
let task = ::context::SequentialTask::update_animations(*self,
|
||||||
pseudo.cloned(),
|
|
||||||
before_change_style,
|
before_change_style,
|
||||||
tasks);
|
tasks);
|
||||||
context.thread_local.tasks.push(task);
|
context.thread_local.tasks.push(task);
|
||||||
|
@ -653,11 +713,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
old_values: &mut Option<Arc<ComputedValues>>,
|
old_values: &mut Option<Arc<ComputedValues>>,
|
||||||
new_values: &mut Arc<ComputedValues>,
|
new_values: &mut Arc<ComputedValues>,
|
||||||
_primary_style: &ComputedStyle,
|
_primary_style: &ComputedStyle) {
|
||||||
pseudo: Option<&PseudoElement>,
|
|
||||||
pseudo_style: Option<&ComputedStyle>) {
|
|
||||||
debug_assert_eq!(pseudo.is_some(), pseudo_style.is_some());
|
|
||||||
|
|
||||||
let possibly_expired_animations =
|
let possibly_expired_animations =
|
||||||
&mut context.thread_local.current_element_info.as_mut().unwrap()
|
&mut context.thread_local.current_element_info.as_mut().unwrap()
|
||||||
.possibly_expired_animations;
|
.possibly_expired_animations;
|
||||||
|
@ -690,6 +746,10 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes and applies non-redundant damage.
|
/// Computes and applies non-redundant damage.
|
||||||
|
///
|
||||||
|
/// FIXME(emilio): Damage for non-::before and non-::after element-backed
|
||||||
|
/// pseudo-elements should be refactored to go on themselves (right now they
|
||||||
|
/// do, but we apply this twice).
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn accumulate_damage(&self,
|
fn accumulate_damage(&self,
|
||||||
shared_context: &SharedStyleContext,
|
shared_context: &SharedStyleContext,
|
||||||
|
@ -716,7 +776,9 @@ trait PrivateMatchMethods: TElement {
|
||||||
// for followup work to make the optimization here more optimal by considering
|
// for followup work to make the optimization here more optimal by considering
|
||||||
// each bit individually.
|
// each bit individually.
|
||||||
if !restyle.damage.contains(RestyleDamage::reconstruct()) {
|
if !restyle.damage.contains(RestyleDamage::reconstruct()) {
|
||||||
let new_damage = self.compute_restyle_damage(&old_values, &new_values, pseudo);
|
let new_damage = self.compute_restyle_damage(&old_values,
|
||||||
|
&new_values,
|
||||||
|
pseudo);
|
||||||
if !restyle.damage_handled.contains(new_damage) {
|
if !restyle.damage_handled.contains(new_damage) {
|
||||||
restyle.damage |= new_damage;
|
restyle.damage |= new_damage;
|
||||||
}
|
}
|
||||||
|
@ -813,7 +875,8 @@ pub enum StyleSharingBehavior {
|
||||||
|
|
||||||
/// The public API that elements expose for selector matching.
|
/// The public API that elements expose for selector matching.
|
||||||
pub trait MatchMethods : TElement {
|
pub trait MatchMethods : TElement {
|
||||||
/// Performs selector matching and property cascading on an element and its eager pseudos.
|
/// Performs selector matching and property cascading on an element and its
|
||||||
|
/// eager pseudos.
|
||||||
fn match_and_cascade(&self,
|
fn match_and_cascade(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
|
@ -878,14 +941,54 @@ pub trait MatchMethods : TElement {
|
||||||
relations: &mut StyleRelations)
|
relations: &mut StyleRelations)
|
||||||
-> bool
|
-> bool
|
||||||
{
|
{
|
||||||
|
let implemented_pseudo = self.implemented_pseudo_element();
|
||||||
|
if let Some(ref pseudo) = implemented_pseudo {
|
||||||
|
if pseudo.is_eager() {
|
||||||
|
// If it's an eager element-backed pseudo, just grab the matched
|
||||||
|
// rules from the parent, and update animations.
|
||||||
|
let parent = self.parent_element().unwrap();
|
||||||
|
let parent_data = parent.borrow_data().unwrap();
|
||||||
|
let pseudo_style =
|
||||||
|
parent_data.styles().pseudos.get(&pseudo).unwrap();
|
||||||
|
let mut rules = pseudo_style.rules.clone();
|
||||||
|
let animation_rules = self.get_animation_rules();
|
||||||
|
|
||||||
|
// Handle animations here.
|
||||||
|
if let Some(animation_rule) = animation_rules.0 {
|
||||||
|
let animation_rule_node =
|
||||||
|
context.shared.stylist.rule_tree
|
||||||
|
.update_rule_at_level(CascadeLevel::Animations,
|
||||||
|
Some(&animation_rule),
|
||||||
|
&mut rules,
|
||||||
|
&context.shared.guards);
|
||||||
|
if let Some(node) = animation_rule_node {
|
||||||
|
rules = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(animation_rule) = animation_rules.1 {
|
||||||
|
let animation_rule_node =
|
||||||
|
context.shared.stylist.rule_tree
|
||||||
|
.update_rule_at_level(CascadeLevel::Transitions,
|
||||||
|
Some(&animation_rule),
|
||||||
|
&mut rules,
|
||||||
|
&context.shared.guards);
|
||||||
|
if let Some(node) = animation_rule_node {
|
||||||
|
rules = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.set_primary_rules(rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut applicable_declarations =
|
let mut applicable_declarations =
|
||||||
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
|
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
|
||||||
|
|
||||||
let stylist = &context.shared.stylist;
|
let stylist = &context.shared.stylist;
|
||||||
let style_attribute = self.style_attribute();
|
let style_attribute = self.style_attribute();
|
||||||
let smil_override = self.get_smil_override();
|
let smil_override = self.get_smil_override();
|
||||||
let animation_rules = self.get_animation_rules(None);
|
let animation_rules = self.get_animation_rules();
|
||||||
let mut rule_nodes_changed = false;
|
|
||||||
let bloom = context.thread_local.bloom_filter.filter();
|
let bloom = context.thread_local.bloom_filter.filter();
|
||||||
|
|
||||||
let map = &mut context.thread_local.selector_flags;
|
let map = &mut context.thread_local.selector_flags;
|
||||||
|
@ -899,22 +1002,15 @@ pub trait MatchMethods : TElement {
|
||||||
style_attribute,
|
style_attribute,
|
||||||
smil_override,
|
smil_override,
|
||||||
animation_rules,
|
animation_rules,
|
||||||
None,
|
implemented_pseudo.as_ref(),
|
||||||
&context.shared.guards,
|
&context.shared.guards,
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
&mut set_selector_flags);
|
&mut set_selector_flags);
|
||||||
|
|
||||||
let primary_rule_node =
|
let primary_rule_node =
|
||||||
compute_rule_node::<Self>(&stylist.rule_tree, &mut applicable_declarations);
|
compute_rule_node::<Self>(&stylist.rule_tree, &mut applicable_declarations);
|
||||||
if !data.has_styles() {
|
|
||||||
data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
|
|
||||||
rule_nodes_changed = true;
|
|
||||||
} else if data.styles().primary.rules != primary_rule_node {
|
|
||||||
data.styles_mut().primary.rules = primary_rule_node;
|
|
||||||
rule_nodes_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
rule_nodes_changed
|
return data.set_primary_rules(primary_rule_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs selector matching to (re)compute eager pseudo-element rule nodes for this
|
/// Runs selector matching to (re)compute eager pseudo-element rule nodes for this
|
||||||
|
@ -927,9 +1023,13 @@ pub trait MatchMethods : TElement {
|
||||||
data: &mut ElementData)
|
data: &mut ElementData)
|
||||||
-> bool
|
-> bool
|
||||||
{
|
{
|
||||||
|
if self.implemented_pseudo_element().is_some() {
|
||||||
|
// Element pseudos can't have any other pseudo.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let mut applicable_declarations =
|
let mut applicable_declarations =
|
||||||
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
|
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
|
||||||
let mut rule_nodes_changed = false;
|
|
||||||
|
|
||||||
let map = &mut context.thread_local.selector_flags;
|
let map = &mut context.thread_local.selector_flags;
|
||||||
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
|
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
|
||||||
|
@ -945,19 +1045,17 @@ pub trait MatchMethods : TElement {
|
||||||
|
|
||||||
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
||||||
let mut matches_different_pseudos = false;
|
let mut matches_different_pseudos = false;
|
||||||
|
let mut rule_nodes_changed = false;
|
||||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
let mut pseudos = &mut data.styles_mut().pseudos;
|
let mut pseudos = &mut data.styles_mut().pseudos;
|
||||||
debug_assert!(applicable_declarations.is_empty());
|
debug_assert!(applicable_declarations.is_empty());
|
||||||
let pseudo_animation_rules = if pseudo.is_before_or_after() {
|
// NB: We handle animation rules for ::before and ::after when
|
||||||
self.get_animation_rules(Some(&pseudo))
|
// traversing them.
|
||||||
} else {
|
|
||||||
AnimationRules(None, None)
|
|
||||||
};
|
|
||||||
stylist.push_applicable_declarations(self,
|
stylist.push_applicable_declarations(self,
|
||||||
Some(bloom_filter),
|
Some(bloom_filter),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
pseudo_animation_rules,
|
AnimationRules(None, None),
|
||||||
Some(&pseudo),
|
Some(&pseudo),
|
||||||
&guards,
|
&guards,
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
|
@ -1073,9 +1171,11 @@ pub trait MatchMethods : TElement {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Animation restyle hints are processed prior to other restyle hints in the
|
// Animation restyle hints are processed prior to other restyle
|
||||||
// animation-only traversal. Non-animation restyle hints will be processed in
|
// hints in the animation-only traversal.
|
||||||
// a subsequent normal traversal.
|
//
|
||||||
|
// Non-animation restyle hints will be processed in a subsequent
|
||||||
|
// normal traversal.
|
||||||
if hint.intersects(RestyleHint::for_animations()) {
|
if hint.intersects(RestyleHint::for_animations()) {
|
||||||
debug_assert!(context.shared.traversal_flags.for_animation_only());
|
debug_assert!(context.shared.traversal_flags.for_animation_only());
|
||||||
|
|
||||||
|
@ -1085,37 +1185,24 @@ pub trait MatchMethods : TElement {
|
||||||
primary_rules);
|
primary_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
use data::EagerPseudoStyles;
|
|
||||||
let mut replace_rule_node_for_animation = |level: CascadeLevel,
|
let mut replace_rule_node_for_animation = |level: CascadeLevel,
|
||||||
primary_rules: &mut StrongRuleNode,
|
primary_rules: &mut StrongRuleNode| {
|
||||||
pseudos: &mut EagerPseudoStyles| {
|
let animation_rule = self.get_animation_rule_by_cascade(level);
|
||||||
let animation_rule = self.get_animation_rule_by_cascade(None, level);
|
|
||||||
replace_rule_node(level,
|
replace_rule_node(level,
|
||||||
animation_rule.as_ref(),
|
animation_rule.as_ref(),
|
||||||
primary_rules);
|
primary_rules);
|
||||||
|
|
||||||
for pseudo in pseudos.keys().iter().filter(|p| p.is_before_or_after()) {
|
|
||||||
let animation_rule = self.get_animation_rule_by_cascade(Some(&pseudo), level);
|
|
||||||
let pseudo_rules = &mut pseudos.get_mut(&pseudo).unwrap().rules;
|
|
||||||
replace_rule_node(level,
|
|
||||||
animation_rule.as_ref(),
|
|
||||||
pseudo_rules);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply Transition rules and Animation rules if the corresponding restyle hint
|
// Apply Transition rules and Animation rules if the corresponding restyle hint
|
||||||
// is contained.
|
// is contained.
|
||||||
let pseudos = &mut element_styles.pseudos;
|
|
||||||
if hint.contains(RESTYLE_CSS_TRANSITIONS) {
|
if hint.contains(RESTYLE_CSS_TRANSITIONS) {
|
||||||
replace_rule_node_for_animation(CascadeLevel::Transitions,
|
replace_rule_node_for_animation(CascadeLevel::Transitions,
|
||||||
primary_rules,
|
primary_rules);
|
||||||
pseudos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if hint.contains(RESTYLE_CSS_ANIMATIONS) {
|
if hint.contains(RESTYLE_CSS_ANIMATIONS) {
|
||||||
replace_rule_node_for_animation(CascadeLevel::Animations,
|
replace_rule_node_for_animation(CascadeLevel::Animations,
|
||||||
primary_rules,
|
primary_rules);
|
||||||
pseudos);
|
|
||||||
}
|
}
|
||||||
} else if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
} else if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
||||||
let style_attribute = self.style_attribute();
|
let style_attribute = self.style_attribute();
|
||||||
|
@ -1125,11 +1212,9 @@ pub trait MatchMethods : TElement {
|
||||||
replace_rule_node(CascadeLevel::StyleAttributeImportant,
|
replace_rule_node(CascadeLevel::StyleAttributeImportant,
|
||||||
style_attribute,
|
style_attribute,
|
||||||
primary_rules);
|
primary_rules);
|
||||||
// The per-pseudo rule nodes never change in this path.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The per-pseudo rule nodes never change in this path.
|
|
||||||
rule_node_changed
|
rule_node_changed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1151,6 +1236,11 @@ pub trait MatchMethods : TElement {
|
||||||
return StyleSharingResult::CannotShare
|
return StyleSharingResult::CannotShare
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.is_native_anonymous() {
|
||||||
|
debug!("{:?} Cannot share style: NAC", self);
|
||||||
|
return StyleSharingResult::CannotShare;
|
||||||
|
}
|
||||||
|
|
||||||
if self.style_attribute().is_some() {
|
if self.style_attribute().is_some() {
|
||||||
debug!("{:?} Cannot share style: element has style attribute", self);
|
debug!("{:?} Cannot share style: element has style attribute", self);
|
||||||
return StyleSharingResult::CannotShare
|
return StyleSharingResult::CannotShare
|
||||||
|
@ -1310,7 +1400,7 @@ pub trait MatchMethods : TElement {
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
mut data: &mut ElementData)
|
mut data: &mut ElementData)
|
||||||
{
|
{
|
||||||
self.cascade_primary_or_pseudo(context, &mut data, None, /* animate = */ true);
|
self.cascade_primary_or_pseudo(context, &mut data, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the cascade for the element's eager pseudos.
|
/// Performs the cascade for the element's eager pseudos.
|
||||||
|
@ -1325,9 +1415,7 @@ pub trait MatchMethods : TElement {
|
||||||
// let us pass the mutable |data| to the cascade function.
|
// let us pass the mutable |data| to the cascade function.
|
||||||
let matched_pseudos = data.styles().pseudos.keys();
|
let matched_pseudos = data.styles().pseudos.keys();
|
||||||
for pseudo in matched_pseudos {
|
for pseudo in matched_pseudos {
|
||||||
// Only ::before and ::after are animatable.
|
self.cascade_primary_or_pseudo(context, data, Some(&pseudo));
|
||||||
let animate = pseudo.is_before_or_after();
|
|
||||||
self.cascade_primary_or_pseudo(context, data, Some(&pseudo), animate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1352,7 +1440,7 @@ pub trait MatchMethods : TElement {
|
||||||
font_metrics_provider,
|
font_metrics_provider,
|
||||||
&without_animation_rules,
|
&without_animation_rules,
|
||||||
primary_style,
|
primary_style,
|
||||||
pseudo_style.is_some())
|
InheritMode::FromParentElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
|
||||||
use selectors::{Element, MatchAttr};
|
use selectors::{Element, MatchAttr};
|
||||||
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
||||||
use selectors::matching::matches_selector;
|
use selectors::matching::matches_selector;
|
||||||
use selectors::parser::{AttrSelector, Combinator, ComplexSelector, Component};
|
use selectors::parser::{AttrSelector, Combinator, Component, Selector};
|
||||||
use selectors::parser::{SelectorInner, SelectorIter, SelectorMethods};
|
use selectors::parser::{SelectorInner, SelectorIter, SelectorMethods};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
|
@ -122,8 +122,7 @@ impl RestyleHint {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl From<nsRestyleHint> for RestyleHint {
|
impl From<nsRestyleHint> for RestyleHint {
|
||||||
fn from(raw: nsRestyleHint) -> Self {
|
fn from(raw: nsRestyleHint) -> Self {
|
||||||
use std::mem;
|
let raw_bits: u32 = raw.0;
|
||||||
let raw_bits: u32 = unsafe { mem::transmute(raw) };
|
|
||||||
// FIXME(bholley): Finish aligning the binary representations here and
|
// FIXME(bholley): Finish aligning the binary representations here and
|
||||||
// then .expect() the result of the checked version.
|
// then .expect() the result of the checked version.
|
||||||
if Self::from_bits(raw_bits).is_none() {
|
if Self::from_bits(raw_bits).is_none() {
|
||||||
|
@ -574,11 +573,10 @@ impl DependencySet {
|
||||||
/// Adds a selector to this `DependencySet`, and returns whether it may need
|
/// Adds a selector to this `DependencySet`, and returns whether it may need
|
||||||
/// cache revalidation, that is, whether two siblings of the same "shape"
|
/// cache revalidation, that is, whether two siblings of the same "shape"
|
||||||
/// may have different style due to this selector.
|
/// may have different style due to this selector.
|
||||||
pub fn note_selector(&mut self,
|
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) -> bool {
|
||||||
base: &ComplexSelector<SelectorImpl>)
|
let mut is_pseudo_element = selector.pseudo_element.is_some();
|
||||||
-> bool
|
|
||||||
{
|
let mut next = Some(selector.inner.complex.clone());
|
||||||
let mut next = Some(base.clone());
|
|
||||||
let mut combinator = None;
|
let mut combinator = None;
|
||||||
let mut needs_revalidation = false;
|
let mut needs_revalidation = false;
|
||||||
|
|
||||||
|
@ -590,6 +588,19 @@ impl DependencySet {
|
||||||
needs_revalidation: false,
|
needs_revalidation: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if is_pseudo_element {
|
||||||
|
// TODO(emilio): use more fancy restyle hints to avoid restyling
|
||||||
|
// the whole subtree when pseudos change.
|
||||||
|
//
|
||||||
|
// We currently need is_pseudo_element to handle eager pseudos
|
||||||
|
// (so the style the parent stores doesn't become stale), and
|
||||||
|
// restyle_descendants to handle all of them (::before and
|
||||||
|
// ::after, because we find them in the subtree, and other lazy
|
||||||
|
// pseudos for the same reason).
|
||||||
|
visitor.hint |= RESTYLE_SELF | RESTYLE_DESCENDANTS;
|
||||||
|
is_pseudo_element = false;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Visit all the simple selectors.
|
// Visit all the simple selectors.
|
||||||
let mut iter = current.iter();
|
let mut iter = current.iter();
|
||||||
|
|
|
@ -333,7 +333,7 @@ impl Stylist {
|
||||||
|
|
||||||
for selector in &style_rule.selectors.0 {
|
for selector in &style_rule.selectors.0 {
|
||||||
let needs_cache_revalidation =
|
let needs_cache_revalidation =
|
||||||
self.dependencies.note_selector(&selector.inner.complex);
|
self.dependencies.note_selector(selector);
|
||||||
if needs_cache_revalidation {
|
if needs_cache_revalidation {
|
||||||
self.selectors_for_cache_revalidation.push(selector.clone());
|
self.selectors_for_cache_revalidation.push(selector.clone());
|
||||||
}
|
}
|
||||||
|
@ -646,7 +646,10 @@ impl Stylist {
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
{
|
{
|
||||||
debug_assert!(!self.is_device_dirty);
|
debug_assert!(!self.is_device_dirty);
|
||||||
debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
|
// Gecko definitely has pseudo-elements with style attributes, like
|
||||||
|
// ::-moz-color-swatch.
|
||||||
|
debug_assert!(cfg!(feature = "gecko") ||
|
||||||
|
style_attribute.is_none() || pseudo_element.is_none(),
|
||||||
"Style attributes do not apply to pseudo-elements");
|
"Style attributes do not apply to pseudo-elements");
|
||||||
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.is_precomputed()));
|
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.is_precomputed()));
|
||||||
|
|
||||||
|
|
|
@ -264,88 +264,118 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
match node.as_element() {
|
let el = match node.as_element() {
|
||||||
None => Self::text_node_needs_traversal(node),
|
None => return Self::text_node_needs_traversal(node),
|
||||||
Some(el) => {
|
Some(el) => el,
|
||||||
// If the element is native-anonymous and an ancestor frame will
|
};
|
||||||
// be reconstructed, the child and all its descendants will be
|
|
||||||
// destroyed. In that case, we don't need to traverse the subtree.
|
// If the element is native-anonymous and an ancestor frame will
|
||||||
if el.is_native_anonymous() {
|
// be reconstructed, the child and all its descendants will be
|
||||||
if let Some(parent) = el.parent_element() {
|
// destroyed. In that case, we wouldn't need to traverse the
|
||||||
let parent_data = parent.borrow_data().unwrap();
|
// subtree...
|
||||||
if let Some(r) = parent_data.get_restyle() {
|
//
|
||||||
if (r.damage | r.damage_handled()).contains(RestyleDamage::reconstruct()) {
|
// Except if there could be transitions of pseudo-elements, in
|
||||||
debug!("Element {:?} is in doomed NAC subtree - culling traversal", el);
|
// which
|
||||||
return false;
|
// case we still need to process them, unfortunately.
|
||||||
}
|
//
|
||||||
|
// We need to conservatively continue the traversal to style the
|
||||||
|
// pseudo-element in order to properly process potentially-new
|
||||||
|
// transitions that we won't see otherwise.
|
||||||
|
//
|
||||||
|
// But it may be that we no longer match, so detect that case
|
||||||
|
// and act appropriately here.
|
||||||
|
if el.is_native_anonymous() {
|
||||||
|
if let Some(parent) = el.parent_element() {
|
||||||
|
let parent_data = parent.borrow_data().unwrap();
|
||||||
|
let going_to_reframe = parent_data.get_restyle().map_or(false, |r| {
|
||||||
|
(r.damage | r.damage_handled())
|
||||||
|
.contains(RestyleDamage::reconstruct())
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut is_before_or_after_pseudo = false;
|
||||||
|
if let Some(pseudo) = el.implemented_pseudo_element() {
|
||||||
|
if pseudo.is_before_or_after() {
|
||||||
|
is_before_or_after_pseudo = true;
|
||||||
|
let still_match =
|
||||||
|
parent_data.styles().pseudos.get(&pseudo).is_some();
|
||||||
|
|
||||||
|
if !still_match {
|
||||||
|
debug_assert!(going_to_reframe,
|
||||||
|
"We're removing a pseudo, so we \
|
||||||
|
should reframe!");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case of animation-only traversal we need to traverse
|
if going_to_reframe && !is_before_or_after_pseudo {
|
||||||
// the element if the element has animation only dirty
|
debug!("Element {:?} is in doomed NAC subtree, \
|
||||||
// descendants bit, animation-only restyle hint or recascade.
|
culling traversal", el);
|
||||||
if traversal_flags.for_animation_only() {
|
return false;
|
||||||
if el.has_animation_only_dirty_descendants() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = match el.borrow_data() {
|
|
||||||
Some(d) => d,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
return data.get_restyle()
|
|
||||||
.map_or(false, |r| r.hint.has_animation_hint() || r.recascade);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// If the dirty descendants bit is set, we need to traverse no
|
|
||||||
// matter what. Skip examining the ElementData.
|
|
||||||
if el.has_dirty_descendants() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the element data. If it doesn't exist, we need to visit
|
|
||||||
// the element.
|
|
||||||
let data = match el.borrow_data() {
|
|
||||||
Some(d) => d,
|
|
||||||
None => return true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we don't have any style data, we need to visit the element.
|
|
||||||
if !data.has_styles() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the restyle data.
|
|
||||||
if let Some(r) = data.get_restyle() {
|
|
||||||
// If we have a restyle hint or need to recascade, we need to
|
|
||||||
// visit the element.
|
|
||||||
//
|
|
||||||
// Note that this is different than checking has_current_styles(),
|
|
||||||
// since that can return true even if we have a restyle hint
|
|
||||||
// indicating that the element's descendants (but not necessarily
|
|
||||||
// the element) need restyling.
|
|
||||||
if !r.hint.is_empty() || r.recascade {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Servo uses the post-order traversal for flow construction, so
|
|
||||||
// we need to traverse any element with damage so that we can perform
|
|
||||||
// fixup / reconstruction on our way back up the tree.
|
|
||||||
//
|
|
||||||
// We also need to traverse nodes with explicit damage and no other
|
|
||||||
// restyle data, so that this damage can be cleared.
|
|
||||||
if (cfg!(feature = "servo") ||
|
|
||||||
traversal_flags.for_reconstruct()) &&
|
|
||||||
data.get_restyle().map_or(false, |r| r.damage != RestyleDamage::empty())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In case of animation-only traversal we need to traverse
|
||||||
|
// the element if the element has animation only dirty
|
||||||
|
// descendants bit, animation-only restyle hint or recascade.
|
||||||
|
if traversal_flags.for_animation_only() {
|
||||||
|
if el.has_animation_only_dirty_descendants() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = match el.borrow_data() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
return data.get_restyle()
|
||||||
|
.map_or(false, |r| r.hint.has_animation_hint() || r.recascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the dirty descendants bit is set, we need to traverse no
|
||||||
|
// matter what. Skip examining the ElementData.
|
||||||
|
if el.has_dirty_descendants() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the element data. If it doesn't exist, we need to visit
|
||||||
|
// the element.
|
||||||
|
let data = match el.borrow_data() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we don't have any style data, we need to visit the element.
|
||||||
|
if !data.has_styles() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the restyle data.
|
||||||
|
if let Some(r) = data.get_restyle() {
|
||||||
|
// If we have a restyle hint or need to recascade, we need to
|
||||||
|
// visit the element.
|
||||||
|
//
|
||||||
|
// Note that this is different than checking has_current_styles(),
|
||||||
|
// since that can return true even if we have a restyle hint
|
||||||
|
// indicating that the element's descendants (but not necessarily
|
||||||
|
// the element) need restyling.
|
||||||
|
if !r.hint.is_empty() || r.recascade {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Servo uses the post-order traversal for flow construction, so
|
||||||
|
// we need to traverse any element with damage so that we can perform
|
||||||
|
// fixup / reconstruction on our way back up the tree.
|
||||||
|
//
|
||||||
|
// We also need to traverse nodes with explicit damage and no other
|
||||||
|
// restyle data, so that this damage can be cleared.
|
||||||
|
if (cfg!(feature = "servo") || traversal_flags.for_reconstruct()) &&
|
||||||
|
data.get_restyle().map_or(false, |r| !r.damage.is_empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if traversal of this element's children is allowed. We use
|
/// Returns true if traversal of this element's children is allowed. We use
|
||||||
|
@ -396,7 +426,6 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for the traversal implementations to select the children that
|
/// Helper for the traversal implementations to select the children that
|
||||||
|
@ -489,7 +518,9 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
|
||||||
|
|
||||||
// Compute our style.
|
// Compute our style.
|
||||||
context.thread_local.begin_element(element, &data);
|
context.thread_local.begin_element(element, &data);
|
||||||
element.match_and_cascade(context, &mut data, StyleSharingBehavior::Disallow);
|
element.match_and_cascade(context,
|
||||||
|
&mut data,
|
||||||
|
StyleSharingBehavior::Disallow);
|
||||||
context.thread_local.end_element(element);
|
context.thread_local.end_element(element);
|
||||||
|
|
||||||
// Conservatively mark us as having dirty descendants, since there might
|
// Conservatively mark us as having dirty descendants, since there might
|
||||||
|
@ -607,9 +638,13 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
};
|
};
|
||||||
debug_assert!(data.has_current_styles() ||
|
debug_assert!(data.has_current_styles() ||
|
||||||
context.shared.traversal_flags.for_animation_only(),
|
context.shared.traversal_flags.for_animation_only(),
|
||||||
"Should have computed style or haven't yet valid computed style in case of animation-only restyle");
|
"Should have computed style or haven't yet valid computed \
|
||||||
trace!("propagated_hint={:?}, inherited_style_changed={:?}",
|
style in case of animation-only restyle");
|
||||||
propagated_hint, inherited_style_changed);
|
trace!("propagated_hint={:?}, inherited_style_changed={:?}, \
|
||||||
|
is_display_none={:?}, implementing_pseudo={:?}",
|
||||||
|
propagated_hint, inherited_style_changed,
|
||||||
|
data.styles().is_display_none(),
|
||||||
|
element.implemented_pseudo_element());
|
||||||
|
|
||||||
let has_dirty_descendants_for_this_restyle =
|
let has_dirty_descendants_for_this_restyle =
|
||||||
if context.shared.traversal_flags.for_animation_only() {
|
if context.shared.traversal_flags.for_animation_only() {
|
||||||
|
@ -664,7 +699,8 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
// The second case is when we are in a restyle for reconstruction,
|
// The second case is when we are in a restyle for reconstruction,
|
||||||
// where we won't need to perform a post-traversal to pick up any
|
// where we won't need to perform a post-traversal to pick up any
|
||||||
// change hints.
|
// change hints.
|
||||||
if data.styles().is_display_none() || context.shared.traversal_flags.for_reconstruct() {
|
if data.styles().is_display_none() ||
|
||||||
|
context.shared.traversal_flags.for_reconstruct() {
|
||||||
unsafe { element.unset_dirty_descendants(); }
|
unsafe { element.unset_dirty_descendants(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use style::font_metrics::get_metrics_provider_for_product;
|
||||||
use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl};
|
use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl};
|
||||||
use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData};
|
use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData};
|
||||||
use style::gecko::restyle_damage::GeckoRestyleDamage;
|
use style::gecko::restyle_damage::GeckoRestyleDamage;
|
||||||
use style::gecko::selector_parser::{SelectorImpl, PseudoElement};
|
use style::gecko::selector_parser::PseudoElement;
|
||||||
use style::gecko::traversal::RecalcStyleOnly;
|
use style::gecko::traversal::RecalcStyleOnly;
|
||||||
use style::gecko::wrapper::GeckoElement;
|
use style::gecko::wrapper::GeckoElement;
|
||||||
use style::gecko_bindings::bindings;
|
use style::gecko_bindings::bindings;
|
||||||
|
@ -166,7 +166,8 @@ fn create_shared_context<'a>(global_style_data: &GlobalStyleData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed,
|
fn traverse_subtree(element: GeckoElement,
|
||||||
|
raw_data: RawServoStyleSetBorrowed,
|
||||||
traversal_flags: TraversalFlags) {
|
traversal_flags: TraversalFlags) {
|
||||||
// When new content is inserted in a display:none subtree, we will call into
|
// When new content is inserted in a display:none subtree, we will call into
|
||||||
// servo to try to style it. Detect that here and bail out.
|
// servo to try to style it. Detect that here and bail out.
|
||||||
|
@ -936,12 +937,15 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pseudo_style(guard: &SharedRwLockReadGuard, element: GeckoElement, pseudo_tag: *mut nsIAtom,
|
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
|
||||||
styles: &ElementStyles, doc_data: &PerDocumentStyleData)
|
element: GeckoElement,
|
||||||
|
pseudo_tag: *mut nsIAtom,
|
||||||
|
styles: &ElementStyles,
|
||||||
|
doc_data: &PerDocumentStyleData)
|
||||||
-> Option<Arc<ComputedValues>>
|
-> Option<Arc<ComputedValues>>
|
||||||
{
|
{
|
||||||
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
|
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
|
||||||
match SelectorImpl::pseudo_element_cascade_type(&pseudo) {
|
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"),
|
||||||
PseudoElementCascadeType::Lazy => {
|
PseudoElementCascadeType::Lazy => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue