mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
servo: Merge #16630 - Make stylo traverse Native Anonymous Content, fixing a bunch of restyle bugs (from emilio:nac); r=bholley,hiro
Source-Repo: https://github.com/servo/servo Source-Revision: c633e291c93c942d34c731fe6ceb4db3b7347a16 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 2e9c223acb6e494c3945c47fc5ab7817c54de504
This commit is contained in:
parent
c5eba64d1c
commit
9d915de239
@ -461,16 +461,16 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||
self.element.has_selector_flags(flags)
|
||||
}
|
||||
|
||||
fn has_animations(&self, _pseudo: Option<&PseudoElement>) -> bool {
|
||||
panic!("this should be only called on gecko");
|
||||
fn has_animations(&self) -> bool {
|
||||
unreachable!("this should be only called on gecko");
|
||||
}
|
||||
|
||||
fn has_css_animations(&self, _pseudo: Option<&PseudoElement>) -> bool {
|
||||
panic!("this should be only called on gecko");
|
||||
fn has_css_animations(&self) -> bool {
|
||||
unreachable!("this should be only called on gecko");
|
||||
}
|
||||
|
||||
fn has_css_transitions(&self, _pseudo: Option<&PseudoElement>) -> bool {
|
||||
panic!("this should be only called on gecko");
|
||||
fn has_css_transitions(&self) -> bool {
|
||||
unreachable!("this should be only called on gecko");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ use font_metrics::FontMetricsProvider;
|
||||
use matching::StyleSharingCandidateCache;
|
||||
use parking_lot::RwLock;
|
||||
#[cfg(feature = "gecko")] use properties::ComputedValues;
|
||||
#[cfg(feature = "gecko")] use selector_parser::PseudoElement;
|
||||
use selectors::matching::ElementSelectorFlags;
|
||||
#[cfg(feature = "servo")] use servo_config::opts;
|
||||
use shared_lock::StylesheetGuards;
|
||||
@ -270,7 +269,8 @@ impl TraversalStatistics {
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
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 {
|
||||
/// Update CSS Animations.
|
||||
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.
|
||||
#[cfg(feature = "gecko")]
|
||||
UpdateAnimations {
|
||||
/// The target element.
|
||||
/// The target element or pseudo-element.
|
||||
el: SendElement<E>,
|
||||
/// The target pseudo element.
|
||||
pseudo: Option<PseudoElement>,
|
||||
/// The before-change style for transitions. We use before-change style as the initial
|
||||
/// value of its Keyframe. Required if |tasks| includes CSSTransitions.
|
||||
before_change_style: Option<Arc<ComputedValues>>,
|
||||
@ -316,8 +314,8 @@ impl<E: TElement> SequentialTask<E> {
|
||||
match self {
|
||||
Unused(_) => unreachable!(),
|
||||
#[cfg(feature = "gecko")]
|
||||
UpdateAnimations { el, pseudo, before_change_style, tasks } => {
|
||||
unsafe { el.update_animations(pseudo.as_ref(), before_change_style, tasks) };
|
||||
UpdateAnimations { el, 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.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn update_animations(el: E,
|
||||
pseudo: Option<PseudoElement>,
|
||||
before_change_style: Option<Arc<ComputedValues>>,
|
||||
tasks: UpdateAnimationsTasks) -> Self {
|
||||
use self::SequentialTask::*;
|
||||
UpdateAnimations { el: unsafe { SendElement::new(el) },
|
||||
pseudo: pseudo,
|
||||
before_change_style: before_change_style,
|
||||
tasks: tasks }
|
||||
UpdateAnimations {
|
||||
el: unsafe { SendElement::new(el) },
|
||||
before_change_style: before_change_style,
|
||||
tasks: tasks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,6 +532,21 @@ impl ElementData {
|
||||
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.
|
||||
pub fn has_restyle(&self) -> bool {
|
||||
self.restyle.is_some()
|
||||
|
@ -274,11 +274,20 @@ pub trait PresentationalHintsSynthetizer {
|
||||
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.
|
||||
pub struct AnimationRules(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.
|
||||
pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||
ElementExt + PresentationalHintsSynthetizer {
|
||||
@ -325,26 +334,25 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||
}
|
||||
|
||||
/// Get this element's animation rules.
|
||||
fn get_animation_rules(&self, _pseudo: Option<&PseudoElement>) -> AnimationRules {
|
||||
fn get_animation_rules(&self) -> AnimationRules {
|
||||
AnimationRules(None, None)
|
||||
}
|
||||
|
||||
/// Get this element's animation rule by the cascade level.
|
||||
fn get_animation_rule_by_cascade(&self,
|
||||
_pseudo: Option<&PseudoElement>,
|
||||
_cascade_level: CascadeLevel)
|
||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get this element's animation rule.
|
||||
fn get_animation_rule(&self, _pseudo: Option<&PseudoElement>)
|
||||
fn get_animation_rule(&self)
|
||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get this element's transition rule.
|
||||
fn get_transition_rule(&self, _pseudo: Option<&PseudoElement>)
|
||||
fn get_transition_rule(&self)
|
||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||
None
|
||||
}
|
||||
@ -428,6 +436,18 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||
/// anonymous content).
|
||||
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
|
||||
/// need to process during bottom-up traversal.
|
||||
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.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn update_animations(&self, _pseudo: Option<&PseudoElement>,
|
||||
fn update_animations(&self,
|
||||
before_change_style: Option<Arc<ComputedValues>>,
|
||||
tasks: UpdateAnimationsTasks);
|
||||
|
||||
/// Returns true if the element has relevant animations. Relevant
|
||||
/// animations are those animations that are affecting the element's style
|
||||
/// 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.
|
||||
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
|
||||
/// 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.
|
||||
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.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn get_css_transitions_info(&self,
|
||||
pseudo: Option<&PseudoElement>)
|
||||
fn get_css_transitions_info(&self)
|
||||
-> HashMap<TransitionProperty, Arc<AnimationValue>>;
|
||||
|
||||
/// 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.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn might_need_transitions_update(&self,
|
||||
old_values: &Option<&Arc<ComputedValues>>,
|
||||
new_values: &Arc<ComputedValues>,
|
||||
pseudo: Option<&PseudoElement>) -> bool;
|
||||
old_values: Option<&ComputedValues>,
|
||||
new_values: &ComputedValues)
|
||||
-> bool;
|
||||
|
||||
/// 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.
|
||||
@ -518,17 +537,17 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||
/// passed the same parameters.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn needs_transitions_update(&self,
|
||||
before_change_style: &Arc<ComputedValues>,
|
||||
after_change_style: &Arc<ComputedValues>,
|
||||
pseudo: Option<&PseudoElement>) -> bool;
|
||||
before_change_style: &ComputedValues,
|
||||
after_change_style: &ComputedValues)
|
||||
-> bool;
|
||||
|
||||
/// Returns true if we need to update transitions for the specified property on this element.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn needs_transitions_update_per_property(&self,
|
||||
property: &TransitionProperty,
|
||||
combined_duration: f32,
|
||||
before_change_style: &Arc<ComputedValues>,
|
||||
after_change_style: &Arc<ComputedValues>,
|
||||
before_change_style: &ComputedValues,
|
||||
after_change_style: &ComputedValues,
|
||||
existing_transitions: &HashMap<TransitionProperty,
|
||||
Arc<AnimationValue>>)
|
||||
-> bool;
|
||||
|
@ -54,6 +54,26 @@ pub const EAGER_PSEUDO_COUNT: usize = 2;
|
||||
|
||||
|
||||
impl PseudoElement {
|
||||
/// Returns the kind of cascade type that a given pseudo is going to use.
|
||||
///
|
||||
/// In Gecko we only compute ::before and ::after eagerly. We save the rules
|
||||
/// for anonymous boxes separately, so we resolve them as precomputed
|
||||
/// pseudos.
|
||||
///
|
||||
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
|
||||
pub fn cascade_type(&self) -> PseudoElementCascadeType {
|
||||
if self.is_eager() {
|
||||
debug_assert!(!self.is_anon_box());
|
||||
return PseudoElementCascadeType::Eager
|
||||
}
|
||||
|
||||
if self.is_anon_box() {
|
||||
return PseudoElementCascadeType::Precomputed
|
||||
}
|
||||
|
||||
PseudoElementCascadeType::Lazy
|
||||
}
|
||||
|
||||
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
||||
#[inline]
|
||||
pub fn eager_index(&self) -> usize {
|
||||
@ -437,24 +457,9 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
||||
|
||||
impl SelectorImpl {
|
||||
#[inline]
|
||||
/// 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`.
|
||||
/// Legacy alias for PseudoElement::cascade_type.
|
||||
pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
|
||||
if pseudo.is_eager() {
|
||||
debug_assert!(!pseudo.is_anon_box());
|
||||
return PseudoElementCascadeType::Eager
|
||||
}
|
||||
|
||||
if pseudo.is_anon_box() {
|
||||
return PseudoElementCascadeType::Precomputed
|
||||
}
|
||||
|
||||
PseudoElementCascadeType::Lazy
|
||||
pseudo.cascade_type()
|
||||
}
|
||||
|
||||
/// 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,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
cascade_level: CascadeLevel)
|
||||
-> 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()));
|
||||
if unsafe { Gecko_GetAnimationRule(element.0, atom_ptr, cascade_level,
|
||||
if unsafe { Gecko_GetAnimationRule(element.0,
|
||||
cascade_level,
|
||||
HasArcFFI::arc_as_borrowed(&animation_values)) } {
|
||||
let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
|
||||
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)
|
||||
}
|
||||
|
||||
fn get_animation_rules(&self, pseudo: Option<&PseudoElement>) -> AnimationRules {
|
||||
AnimationRules(self.get_animation_rule(pseudo),
|
||||
self.get_transition_rule(pseudo))
|
||||
fn get_animation_rules(&self) -> AnimationRules {
|
||||
AnimationRules(self.get_animation_rule(),
|
||||
self.get_transition_rule())
|
||||
}
|
||||
|
||||
fn get_animation_rule_by_cascade(&self,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
cascade_level: ServoCascadeLevel)
|
||||
fn get_animation_rule_by_cascade(&self, cascade_level: ServoCascadeLevel)
|
||||
-> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
||||
match cascade_level {
|
||||
ServoCascadeLevel::Animations => self.get_animation_rule(pseudo),
|
||||
ServoCascadeLevel::Transitions => self.get_transition_rule(pseudo),
|
||||
ServoCascadeLevel::Animations => self.get_animation_rule(),
|
||||
ServoCascadeLevel::Transitions => self.get_transition_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>>> {
|
||||
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>>> {
|
||||
get_animation_rule(self, pseudo, CascadeLevel::Transitions)
|
||||
get_animation_rule(self, CascadeLevel::Transitions)
|
||||
}
|
||||
|
||||
fn get_state(&self) -> ElementState {
|
||||
@ -589,7 +590,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||
-> Option<&'a nsStyleContext> {
|
||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -641,6 +642,22 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||
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) {
|
||||
// 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,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
before_change_style: Option<Arc<ComputedValues>>,
|
||||
tasks: UpdateAnimationsTasks) {
|
||||
// We have to update animations even if the element has no computed style
|
||||
// since it means the element is in a display:none subtree, we should destroy
|
||||
// all CSS animations in display:none subtree.
|
||||
// We have to update animations even if the element has no computed
|
||||
// style since it means the element is in a display:none subtree, we
|
||||
// should destroy all CSS animations in display:none subtree.
|
||||
let computed_data = self.borrow_data();
|
||||
let computed_values =
|
||||
computed_data.as_ref().map(|d|
|
||||
pseudo.map_or_else(|| d.styles().primary.values(),
|
||||
|p| d.styles().pseudos.get(p).unwrap().values())
|
||||
);
|
||||
let computed_values_opt = computed_values.map(|v|
|
||||
*HasArcFFI::arc_as_borrowed(v)
|
||||
);
|
||||
|
||||
let parent_element = if pseudo.is_none() {
|
||||
self.parent_element()
|
||||
} else {
|
||||
Some(*self)
|
||||
};
|
||||
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));
|
||||
computed_data.as_ref().map(|d| d.styles().primary.values());
|
||||
let computed_values_opt =
|
||||
computed_values.map(|v| *HasArcFFI::arc_as_borrowed(v));
|
||||
let parent_element = self.parent_element();
|
||||
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 before_change_values =
|
||||
before_change_style.as_ref().map(|v| *HasArcFFI::arc_as_borrowed(v));
|
||||
unsafe {
|
||||
Gecko_UpdateAnimations(self.0, atom_ptr,
|
||||
Gecko_UpdateAnimations(self.0,
|
||||
before_change_values,
|
||||
computed_values_opt,
|
||||
parent_values_opt,
|
||||
@ -711,35 +718,31 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||
}
|
||||
}
|
||||
|
||||
fn has_animations(&self, pseudo: Option<&PseudoElement>) -> bool {
|
||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
||||
unsafe { Gecko_ElementHasAnimations(self.0, atom_ptr) }
|
||||
fn has_animations(&self) -> bool {
|
||||
unsafe { Gecko_ElementHasAnimations(self.0) }
|
||||
}
|
||||
|
||||
fn has_css_animations(&self, pseudo: Option<&PseudoElement>) -> bool {
|
||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
||||
unsafe { Gecko_ElementHasCSSAnimations(self.0, atom_ptr) }
|
||||
fn has_css_animations(&self) -> bool {
|
||||
unsafe { Gecko_ElementHasCSSAnimations(self.0) }
|
||||
}
|
||||
|
||||
fn has_css_transitions(&self, pseudo: Option<&PseudoElement>) -> bool {
|
||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
||||
unsafe { Gecko_ElementHasCSSTransitions(self.0, atom_ptr) }
|
||||
fn has_css_transitions(&self) -> bool {
|
||||
unsafe { Gecko_ElementHasCSSTransitions(self.0) }
|
||||
}
|
||||
|
||||
fn get_css_transitions_info(&self,
|
||||
pseudo: Option<&PseudoElement>)
|
||||
fn get_css_transitions_info(&self)
|
||||
-> HashMap<TransitionProperty, Arc<AnimationValue>> {
|
||||
use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
|
||||
use gecko_bindings::bindings::Gecko_ElementTransitions_Length;
|
||||
use gecko_bindings::bindings::Gecko_ElementTransitions_PropertyAt;
|
||||
|
||||
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
|
||||
let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0, atom_ptr) };
|
||||
let collection_length =
|
||||
unsafe { Gecko_ElementTransitions_Length(self.0) };
|
||||
let mut map = HashMap::with_capacity(collection_length);
|
||||
for i in 0..collection_length {
|
||||
let (property, raw_end_value) = unsafe {
|
||||
(Gecko_ElementTransitions_PropertyAt(self.0, atom_ptr, i as usize).into(),
|
||||
Gecko_ElementTransitions_EndValueAt(self.0, atom_ptr, i as usize))
|
||||
(Gecko_ElementTransitions_PropertyAt(self.0, i as usize).into(),
|
||||
Gecko_ElementTransitions_EndValueAt(self.0, i as usize))
|
||||
};
|
||||
let end_value = AnimationValue::arc_from_borrowed(&raw_end_value);
|
||||
debug_assert!(end_value.is_some());
|
||||
@ -749,21 +752,21 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||
}
|
||||
|
||||
fn might_need_transitions_update(&self,
|
||||
old_values: &Option<&Arc<ComputedValues>>,
|
||||
new_values: &Arc<ComputedValues>,
|
||||
pseudo: Option<&PseudoElement>) -> bool {
|
||||
old_values: Option<&ComputedValues>,
|
||||
new_values: &ComputedValues) -> bool {
|
||||
use properties::longhands::display::computed_value as display;
|
||||
|
||||
if old_values.is_none() {
|
||||
return false;
|
||||
}
|
||||
let old_values = match old_values {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let ref new_box_style = new_values.get_box();
|
||||
let transition_not_running = !self.has_css_transitions(pseudo) &&
|
||||
let new_box_style = new_values.get_box();
|
||||
let transition_not_running = !self.has_css_transitions() &&
|
||||
new_box_style.transition_property_count() == 1 &&
|
||||
new_box_style.transition_combined_duration_at(0) <= 0.0f32;
|
||||
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 &&
|
||||
!transition_not_running &&
|
||||
@ -771,28 +774,31 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||
old_display_style != display::T::none)
|
||||
}
|
||||
|
||||
// Detect if there are any changes that require us to update transitions. This is used as a
|
||||
// more thoroughgoing check than the, cheaper might_need_transitions_update check.
|
||||
// Detect if there are any changes that require us to update transitions.
|
||||
// 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
|
||||
// (nsTransitionManager::DoUpdateTransitions) where we actually perform the update.
|
||||
// (nsTransitionManager::DoUpdateTransitions) where we actually perform the
|
||||
// update.
|
||||
//
|
||||
// https://drafts.csswg.org/css-transitions/#starting
|
||||
fn needs_transitions_update(&self,
|
||||
before_change_style: &Arc<ComputedValues>,
|
||||
after_change_style: &Arc<ComputedValues>,
|
||||
pseudo: Option<&PseudoElement>) -> bool {
|
||||
before_change_style: &ComputedValues,
|
||||
after_change_style: &ComputedValues)
|
||||
-> bool {
|
||||
use gecko_bindings::structs::nsCSSPropertyID;
|
||||
use properties::{PropertyId, animated_properties};
|
||||
use std::collections::HashSet;
|
||||
|
||||
debug_assert!(self.might_need_transitions_update(&Some(before_change_style),
|
||||
after_change_style,
|
||||
pseudo),
|
||||
debug_assert!(self.might_need_transitions_update(Some(before_change_style),
|
||||
after_change_style),
|
||||
"We should only call needs_transitions_update if \
|
||||
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 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() &&
|
||||
(after_change_box_style.transition_nscsspropertyid_at(0) !=
|
||||
nsCSSPropertyID::eCSSPropertyExtra_all_properties) {
|
||||
@ -865,8 +871,8 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||
fn needs_transitions_update_per_property(&self,
|
||||
property: &TransitionProperty,
|
||||
combined_duration: f32,
|
||||
before_change_style: &Arc<ComputedValues>,
|
||||
after_change_style: &Arc<ComputedValues>,
|
||||
before_change_style: &ComputedValues,
|
||||
after_change_style: &ComputedValues,
|
||||
existing_transitions: &HashMap<TransitionProperty,
|
||||
Arc<AnimationValue>>)
|
||||
-> bool {
|
||||
|
@ -615,8 +615,7 @@ extern "C" {
|
||||
-> RawServoDeclarationBlockStrongBorrowedOrNull;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_GetAnimationRule(aElement: RawGeckoElementBorrowed,
|
||||
aPseudoTag: *mut nsIAtom,
|
||||
pub fn Gecko_GetAnimationRule(aElementOrPseudo: RawGeckoElementBorrowed,
|
||||
aCascadeLevel:
|
||||
EffectCompositor_CascadeLevel,
|
||||
aAnimationValues:
|
||||
@ -636,46 +635,42 @@ extern "C" {
|
||||
-> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_UpdateAnimations(aElement: RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom,
|
||||
pub fn Gecko_UpdateAnimations(aElementOrPseudo: RawGeckoElementBorrowed,
|
||||
aOldComputedValues:
|
||||
ServoComputedValuesBorrowedOrNull,
|
||||
aComputedValues:
|
||||
ServoComputedValuesBorrowedOrNull,
|
||||
aParentComputedValues:
|
||||
ServoComputedValuesBorrowedOrNull,
|
||||
aTaskBits: UpdateAnimationsTasks);
|
||||
aTasks: UpdateAnimationsTasks);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementHasAnimations(aElement: RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom) -> bool;
|
||||
pub fn Gecko_ElementHasAnimations(aElementOrPseudo:
|
||||
RawGeckoElementBorrowed) -> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementHasCSSAnimations(aElement: RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom)
|
||||
pub fn Gecko_ElementHasCSSAnimations(aElementOrPseudo:
|
||||
RawGeckoElementBorrowed) -> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementHasCSSTransitions(aElementOrPseudo:
|
||||
RawGeckoElementBorrowed)
|
||||
-> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementHasCSSTransitions(aElement: RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom)
|
||||
-> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementTransitions_Length(aElement: RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom)
|
||||
pub fn Gecko_ElementTransitions_Length(aElementOrPseudo:
|
||||
RawGeckoElementBorrowed)
|
||||
-> usize;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementTransitions_PropertyAt(aElement:
|
||||
pub fn Gecko_ElementTransitions_PropertyAt(aElementOrPseudo:
|
||||
RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom,
|
||||
aIndex: usize)
|
||||
-> nsCSSPropertyID;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_ElementTransitions_EndValueAt(aElement:
|
||||
pub fn Gecko_ElementTransitions_EndValueAt(aElementOrPseudo:
|
||||
RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom,
|
||||
aIndex: usize)
|
||||
-> RawServoAnimationValueBorrowedOrNull;
|
||||
}
|
||||
@ -860,10 +855,14 @@ extern "C" {
|
||||
RawGeckoElementBorrowed);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_GetStyleContext(node: RawGeckoNodeBorrowed,
|
||||
pub fn Gecko_GetStyleContext(element: RawGeckoElementBorrowed,
|
||||
aPseudoTagOrNull: *mut nsIAtom)
|
||||
-> *mut nsStyleContext;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_GetImplementedPseudo(element: RawGeckoElementBorrowed)
|
||||
-> *mut nsIAtom;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
||||
newstyle: ServoComputedValuesBorrowed)
|
||||
|
@ -30,6 +30,15 @@ use sink::ForgetfulSink;
|
||||
use std::sync::Arc;
|
||||
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.
|
||||
#[inline]
|
||||
fn relations_are_shareable(relations: &StyleRelations) -> bool {
|
||||
@ -78,6 +87,8 @@ pub struct StyleSharingCandidateCache<E: TElement> {
|
||||
pub enum CacheMiss {
|
||||
/// The parents don't match.
|
||||
Parent,
|
||||
/// One element was NAC, while the other wasn't.
|
||||
NativeAnonymousContent,
|
||||
/// The local name of the element and the candidate don't match.
|
||||
LocalName,
|
||||
/// 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)
|
||||
}
|
||||
|
||||
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() {
|
||||
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
|
||||
// are either uncommon or expensive.
|
||||
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!");
|
||||
}
|
||||
|
||||
|
||||
let box_style = style.get_box();
|
||||
if box_style.specifies_transitions() {
|
||||
debug!("Failing to insert to the cache: transitions");
|
||||
@ -387,7 +408,7 @@ trait PrivateMatchMethods: TElement {
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
rule_node: &StrongRuleNode,
|
||||
primary_style: &ComputedStyle,
|
||||
is_pseudo: bool)
|
||||
inherit_mode: InheritMode)
|
||||
-> Arc<ComputedValues> {
|
||||
let mut cascade_info = CascadeInfo::new();
|
||||
let mut cascade_flags = CascadeFlags::empty();
|
||||
@ -398,24 +419,27 @@ trait PrivateMatchMethods: TElement {
|
||||
// Grab the inherited values.
|
||||
let parent_el;
|
||||
let parent_data;
|
||||
let style_to_inherit_from = if !is_pseudo {
|
||||
parent_el = self.parent_element();
|
||||
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
||||
let parent_values = parent_data.as_ref().map(|d| {
|
||||
// Sometimes Gecko eagerly styles things without processing
|
||||
// pending restyles first. In general we'd like to avoid this,
|
||||
// but there can be good reasons (for example, needing to
|
||||
// construct a frame for some small piece of newly-added
|
||||
// content in order to do something specific with that frame,
|
||||
// but not wanting to flush all of layout).
|
||||
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
|
||||
d.styles().primary.values()
|
||||
});
|
||||
let style_to_inherit_from = match inherit_mode {
|
||||
InheritMode::FromParentElement => {
|
||||
parent_el = self.parent_element();
|
||||
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
||||
let parent_values = parent_data.as_ref().map(|d| {
|
||||
// Sometimes Gecko eagerly styles things without processing
|
||||
// pending restyles first. In general we'd like to avoid this,
|
||||
// but there can be good reasons (for example, needing to
|
||||
// construct a frame for some small piece of newly-added
|
||||
// content in order to do something specific with that frame,
|
||||
// but not wanting to flush all of layout).
|
||||
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
|
||||
d.styles().primary.values()
|
||||
});
|
||||
|
||||
parent_values
|
||||
} else {
|
||||
parent_el = Some(self.clone());
|
||||
Some(primary_style.values())
|
||||
parent_values
|
||||
}
|
||||
InheritMode::FromPrimaryStyle => {
|
||||
parent_el = Some(self.clone());
|
||||
Some(primary_style.values())
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
// encapsulate this better.
|
||||
//
|
||||
// Note that this is not needed for pseudos since we already do that
|
||||
// when we resolve the non-pseudo style.
|
||||
if !is_pseudo {
|
||||
if let Some(ref p) = layout_parent_style {
|
||||
let 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); }
|
||||
}
|
||||
// Note that this is technically not needed for pseudos since we already
|
||||
// do that when we resolve the non-pseudo style, but it doesn't hurt
|
||||
// anyway.
|
||||
//
|
||||
// TODO(emilio): This is servo-only, move somewhere else?
|
||||
if let Some(ref p) = layout_parent_style {
|
||||
let 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.
|
||||
@ -463,16 +488,21 @@ trait PrivateMatchMethods: TElement {
|
||||
fn cascade_internal(&self,
|
||||
context: &StyleContext<Self>,
|
||||
primary_style: &ComputedStyle,
|
||||
pseudo_style: Option<&ComputedStyle>)
|
||||
eager_pseudo_style: Option<&ComputedStyle>)
|
||||
-> Arc<ComputedValues> {
|
||||
// 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,
|
||||
&context.thread_local.font_metrics_provider,
|
||||
rule_node,
|
||||
primary_style,
|
||||
pseudo_style.is_some())
|
||||
inherit_mode)
|
||||
}
|
||||
|
||||
/// 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,
|
||||
context: &mut StyleContext<Self>,
|
||||
data: &mut ElementData,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
animate: bool) {
|
||||
pseudo: Option<&PseudoElement>) {
|
||||
debug_assert!(pseudo.is_none() || self.implemented_pseudo_element().is_none(),
|
||||
"Pseudo-element-implementing elements can't have pseudos!");
|
||||
// Collect some values.
|
||||
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||
let mut primary_style = &mut styles.primary;
|
||||
@ -501,27 +532,65 @@ trait PrivateMatchMethods: TElement {
|
||||
};
|
||||
|
||||
// Compute the new values.
|
||||
let mut new_values = self.cascade_internal(context,
|
||||
primary_style,
|
||||
pseudo_style.as_ref().map(|s| &**s));
|
||||
let mut new_values = match self.implemented_pseudo_element() {
|
||||
Some(ref pseudo) => {
|
||||
// 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.
|
||||
if animate && !context.shared.traversal_flags.for_animation_only() {
|
||||
let parent_data = parent.borrow_data().unwrap();
|
||||
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,
|
||||
&mut old_values,
|
||||
&mut new_values,
|
||||
primary_style,
|
||||
pseudo,
|
||||
pseudo_style.as_ref().map(|s| &**s));
|
||||
primary_style);
|
||||
}
|
||||
|
||||
// Accumulate restyle damage.
|
||||
if let Some(old) = old_values {
|
||||
self.accumulate_damage(&context.shared,
|
||||
restyle.unwrap(),
|
||||
&old,
|
||||
&new_values,
|
||||
pseudo);
|
||||
// ::before and ::after are element-backed in Gecko, so they do
|
||||
// the damage calculation for themselves.
|
||||
//
|
||||
// FIXME(emilio): We have more element-backed stuff, and this is
|
||||
// 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.
|
||||
@ -534,11 +603,9 @@ trait PrivateMatchMethods: TElement {
|
||||
#[cfg(feature = "gecko")]
|
||||
fn get_after_change_style(&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
primary_style: &ComputedStyle,
|
||||
pseudo_style: Option<&ComputedStyle>)
|
||||
primary_style: &ComputedStyle)
|
||||
-> Option<Arc<ComputedValues>> {
|
||||
let relevant_style = pseudo_style.unwrap_or(primary_style);
|
||||
let rule_node = &relevant_style.rules;
|
||||
let rule_node = &primary_style.rules;
|
||||
let without_transition_rules =
|
||||
context.shared.stylist.rule_tree.remove_transition_rule_if_applicable(rule_node);
|
||||
if without_transition_rules == *rule_node {
|
||||
@ -551,21 +618,21 @@ trait PrivateMatchMethods: TElement {
|
||||
&context.thread_local.font_metrics_provider,
|
||||
&without_transition_rules,
|
||||
primary_style,
|
||||
pseudo_style.is_some()))
|
||||
InheritMode::FromParentElement))
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn needs_animations_update(&self,
|
||||
old_values: &Option<Arc<ComputedValues>>,
|
||||
new_values: &Arc<ComputedValues>,
|
||||
pseudo: Option<&PseudoElement>) -> bool {
|
||||
let ref new_box_style = new_values.get_box();
|
||||
old_values: Option<&Arc<ComputedValues>>,
|
||||
new_values: &ComputedValues)
|
||||
-> bool {
|
||||
let new_box_style = new_values.get_box();
|
||||
let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
|
||||
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| {
|
||||
let ref old_box_style = old.get_box();
|
||||
old_values.map_or(has_new_animation_style, |old| {
|
||||
let old_box_style = old.get_box();
|
||||
let old_display_style = old_box_style.clone_display();
|
||||
let new_display_style = new_box_style.clone_display();
|
||||
// FIXME: Bug 1344581: We still need to compare keyframe rules.
|
||||
@ -584,40 +651,34 @@ trait PrivateMatchMethods: TElement {
|
||||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
new_values: &mut Arc<ComputedValues>,
|
||||
primary_style: &ComputedStyle,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
pseudo_style: Option<&ComputedStyle>) {
|
||||
primary_style: &ComputedStyle) {
|
||||
use context::{CSS_ANIMATIONS, CSS_TRANSITIONS, EFFECT_PROPERTIES};
|
||||
use context::UpdateAnimationsTasks;
|
||||
|
||||
debug_assert_eq!(pseudo.is_some(), pseudo_style.is_some());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
let before_change_style = if self.might_need_transitions_update(&old_values.as_ref(),
|
||||
new_values,
|
||||
pseudo) {
|
||||
let after_change_style = if self.has_css_transitions(pseudo) {
|
||||
self.get_after_change_style(context, primary_style, pseudo_style)
|
||||
let before_change_style = if self.might_need_transitions_update(old_values.as_ref().map(|s| &**s),
|
||||
new_values) {
|
||||
let after_change_style = if self.has_css_transitions() {
|
||||
self.get_after_change_style(context, primary_style)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// In order to avoid creating a SequentialTask for transitions which may not be updated,
|
||||
// we check it per property to make sure Gecko side will really update transition.
|
||||
// In order to avoid creating a SequentialTask for transitions which
|
||||
// may not be updated, we check it per property to make sure Gecko
|
||||
// side will really update transition.
|
||||
let needs_transitions_update = {
|
||||
// We borrow new_values here, so need to add a scope to make sure we release it
|
||||
// before assigning a new value to it.
|
||||
let after_change_style_ref = match after_change_style {
|
||||
Some(ref value) => value,
|
||||
None => &new_values
|
||||
};
|
||||
// We borrow new_values here, so need to add a scope to make
|
||||
// sure we release it before assigning a new value to it.
|
||||
let after_change_style_ref =
|
||||
after_change_style.as_ref().unwrap_or(&new_values);
|
||||
|
||||
self.needs_transitions_update(old_values.as_ref().unwrap(),
|
||||
after_change_style_ref,
|
||||
pseudo)
|
||||
after_change_style_ref)
|
||||
};
|
||||
|
||||
if needs_transitions_update {
|
||||
@ -635,13 +696,12 @@ trait PrivateMatchMethods: TElement {
|
||||
None
|
||||
};
|
||||
|
||||
if self.has_animations(pseudo) {
|
||||
if self.has_animations() {
|
||||
tasks.insert(EFFECT_PROPERTIES);
|
||||
}
|
||||
|
||||
if !tasks.is_empty() {
|
||||
let task = ::context::SequentialTask::update_animations(*self,
|
||||
pseudo.cloned(),
|
||||
before_change_style,
|
||||
tasks);
|
||||
context.thread_local.tasks.push(task);
|
||||
@ -653,11 +713,7 @@ trait PrivateMatchMethods: TElement {
|
||||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
new_values: &mut Arc<ComputedValues>,
|
||||
_primary_style: &ComputedStyle,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
pseudo_style: Option<&ComputedStyle>) {
|
||||
debug_assert_eq!(pseudo.is_some(), pseudo_style.is_some());
|
||||
|
||||
_primary_style: &ComputedStyle) {
|
||||
let possibly_expired_animations =
|
||||
&mut context.thread_local.current_element_info.as_mut().unwrap()
|
||||
.possibly_expired_animations;
|
||||
@ -690,6 +746,10 @@ trait PrivateMatchMethods: TElement {
|
||||
}
|
||||
|
||||
/// 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")]
|
||||
fn accumulate_damage(&self,
|
||||
shared_context: &SharedStyleContext,
|
||||
@ -716,7 +776,9 @@ trait PrivateMatchMethods: TElement {
|
||||
// for followup work to make the optimization here more optimal by considering
|
||||
// each bit individually.
|
||||
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) {
|
||||
restyle.damage |= new_damage;
|
||||
}
|
||||
@ -813,7 +875,8 @@ pub enum StyleSharingBehavior {
|
||||
|
||||
/// The public API that elements expose for selector matching.
|
||||
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,
|
||||
context: &mut StyleContext<Self>,
|
||||
data: &mut ElementData,
|
||||
@ -878,14 +941,54 @@ pub trait MatchMethods : TElement {
|
||||
relations: &mut StyleRelations)
|
||||
-> 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 =
|
||||
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
|
||||
|
||||
let stylist = &context.shared.stylist;
|
||||
let style_attribute = self.style_attribute();
|
||||
let smil_override = self.get_smil_override();
|
||||
let animation_rules = self.get_animation_rules(None);
|
||||
let mut rule_nodes_changed = false;
|
||||
let animation_rules = self.get_animation_rules();
|
||||
let bloom = context.thread_local.bloom_filter.filter();
|
||||
|
||||
let map = &mut context.thread_local.selector_flags;
|
||||
@ -899,22 +1002,15 @@ pub trait MatchMethods : TElement {
|
||||
style_attribute,
|
||||
smil_override,
|
||||
animation_rules,
|
||||
None,
|
||||
implemented_pseudo.as_ref(),
|
||||
&context.shared.guards,
|
||||
&mut applicable_declarations,
|
||||
&mut set_selector_flags);
|
||||
|
||||
let primary_rule_node =
|
||||
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
|
||||
@ -927,9 +1023,13 @@ pub trait MatchMethods : TElement {
|
||||
data: &mut ElementData)
|
||||
-> bool
|
||||
{
|
||||
if self.implemented_pseudo_element().is_some() {
|
||||
// Element pseudos can't have any other pseudo.
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut applicable_declarations =
|
||||
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
|
||||
let mut rule_nodes_changed = false;
|
||||
|
||||
let map = &mut context.thread_local.selector_flags;
|
||||
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.
|
||||
let mut matches_different_pseudos = false;
|
||||
let mut rule_nodes_changed = false;
|
||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||
let mut pseudos = &mut data.styles_mut().pseudos;
|
||||
debug_assert!(applicable_declarations.is_empty());
|
||||
let pseudo_animation_rules = if pseudo.is_before_or_after() {
|
||||
self.get_animation_rules(Some(&pseudo))
|
||||
} else {
|
||||
AnimationRules(None, None)
|
||||
};
|
||||
// NB: We handle animation rules for ::before and ::after when
|
||||
// traversing them.
|
||||
stylist.push_applicable_declarations(self,
|
||||
Some(bloom_filter),
|
||||
None,
|
||||
None,
|
||||
pseudo_animation_rules,
|
||||
AnimationRules(None, None),
|
||||
Some(&pseudo),
|
||||
&guards,
|
||||
&mut applicable_declarations,
|
||||
@ -1073,9 +1171,11 @@ pub trait MatchMethods : TElement {
|
||||
}
|
||||
};
|
||||
|
||||
// Animation restyle hints are processed prior to other restyle hints in the
|
||||
// animation-only traversal. Non-animation restyle hints will be processed in
|
||||
// a subsequent normal traversal.
|
||||
// Animation restyle hints are processed prior to other restyle
|
||||
// hints in the animation-only traversal.
|
||||
//
|
||||
// Non-animation restyle hints will be processed in a subsequent
|
||||
// normal traversal.
|
||||
if hint.intersects(RestyleHint::for_animations()) {
|
||||
debug_assert!(context.shared.traversal_flags.for_animation_only());
|
||||
|
||||
@ -1085,37 +1185,24 @@ pub trait MatchMethods : TElement {
|
||||
primary_rules);
|
||||
}
|
||||
|
||||
use data::EagerPseudoStyles;
|
||||
let mut replace_rule_node_for_animation = |level: CascadeLevel,
|
||||
primary_rules: &mut StrongRuleNode,
|
||||
pseudos: &mut EagerPseudoStyles| {
|
||||
let animation_rule = self.get_animation_rule_by_cascade(None, level);
|
||||
primary_rules: &mut StrongRuleNode| {
|
||||
let animation_rule = self.get_animation_rule_by_cascade(level);
|
||||
replace_rule_node(level,
|
||||
animation_rule.as_ref(),
|
||||
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
|
||||
// is contained.
|
||||
let pseudos = &mut element_styles.pseudos;
|
||||
if hint.contains(RESTYLE_CSS_TRANSITIONS) {
|
||||
replace_rule_node_for_animation(CascadeLevel::Transitions,
|
||||
primary_rules,
|
||||
pseudos);
|
||||
primary_rules);
|
||||
}
|
||||
|
||||
if hint.contains(RESTYLE_CSS_ANIMATIONS) {
|
||||
replace_rule_node_for_animation(CascadeLevel::Animations,
|
||||
primary_rules,
|
||||
pseudos);
|
||||
primary_rules);
|
||||
}
|
||||
} else if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
||||
let style_attribute = self.style_attribute();
|
||||
@ -1125,11 +1212,9 @@ pub trait MatchMethods : TElement {
|
||||
replace_rule_node(CascadeLevel::StyleAttributeImportant,
|
||||
style_attribute,
|
||||
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
|
||||
}
|
||||
|
||||
@ -1151,6 +1236,11 @@ pub trait MatchMethods : TElement {
|
||||
return StyleSharingResult::CannotShare
|
||||
}
|
||||
|
||||
if self.is_native_anonymous() {
|
||||
debug!("{:?} Cannot share style: NAC", self);
|
||||
return StyleSharingResult::CannotShare;
|
||||
}
|
||||
|
||||
if self.style_attribute().is_some() {
|
||||
debug!("{:?} Cannot share style: element has style attribute", self);
|
||||
return StyleSharingResult::CannotShare
|
||||
@ -1310,7 +1400,7 @@ pub trait MatchMethods : TElement {
|
||||
context: &mut StyleContext<Self>,
|
||||
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.
|
||||
@ -1325,9 +1415,7 @@ pub trait MatchMethods : TElement {
|
||||
// let us pass the mutable |data| to the cascade function.
|
||||
let matched_pseudos = data.styles().pseudos.keys();
|
||||
for pseudo in matched_pseudos {
|
||||
// Only ::before and ::after are animatable.
|
||||
let animate = pseudo.is_before_or_after();
|
||||
self.cascade_primary_or_pseudo(context, data, Some(&pseudo), animate);
|
||||
self.cascade_primary_or_pseudo(context, data, Some(&pseudo));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1352,7 +1440,7 @@ pub trait MatchMethods : TElement {
|
||||
font_metrics_provider,
|
||||
&without_animation_rules,
|
||||
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::matching::{ElementSelectorFlags, StyleRelations};
|
||||
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::visitor::SelectorVisitor;
|
||||
use std::clone::Clone;
|
||||
@ -122,8 +122,7 @@ impl RestyleHint {
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<nsRestyleHint> for RestyleHint {
|
||||
fn from(raw: nsRestyleHint) -> Self {
|
||||
use std::mem;
|
||||
let raw_bits: u32 = unsafe { mem::transmute(raw) };
|
||||
let raw_bits: u32 = raw.0;
|
||||
// FIXME(bholley): Finish aligning the binary representations here and
|
||||
// then .expect() the result of the checked version.
|
||||
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
|
||||
/// cache revalidation, that is, whether two siblings of the same "shape"
|
||||
/// may have different style due to this selector.
|
||||
pub fn note_selector(&mut self,
|
||||
base: &ComplexSelector<SelectorImpl>)
|
||||
-> bool
|
||||
{
|
||||
let mut next = Some(base.clone());
|
||||
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) -> bool {
|
||||
let mut is_pseudo_element = selector.pseudo_element.is_some();
|
||||
|
||||
let mut next = Some(selector.inner.complex.clone());
|
||||
let mut combinator = None;
|
||||
let mut needs_revalidation = false;
|
||||
|
||||
@ -590,6 +588,19 @@ impl DependencySet {
|
||||
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.
|
||||
let mut iter = current.iter();
|
||||
|
@ -333,7 +333,7 @@ impl Stylist {
|
||||
|
||||
for selector in &style_rule.selectors.0 {
|
||||
let needs_cache_revalidation =
|
||||
self.dependencies.note_selector(&selector.inner.complex);
|
||||
self.dependencies.note_selector(selector);
|
||||
if needs_cache_revalidation {
|
||||
self.selectors_for_cache_revalidation.push(selector.clone());
|
||||
}
|
||||
@ -646,7 +646,10 @@ impl Stylist {
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
match node.as_element() {
|
||||
None => Self::text_node_needs_traversal(node),
|
||||
Some(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 el.is_native_anonymous() {
|
||||
if let Some(parent) = el.parent_element() {
|
||||
let parent_data = parent.borrow_data().unwrap();
|
||||
if let Some(r) = parent_data.get_restyle() {
|
||||
if (r.damage | r.damage_handled()).contains(RestyleDamage::reconstruct()) {
|
||||
debug!("Element {:?} is in doomed NAC subtree - culling traversal", el);
|
||||
return false;
|
||||
}
|
||||
let el = match node.as_element() {
|
||||
None => return Self::text_node_needs_traversal(node),
|
||||
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 wouldn't need to traverse the
|
||||
// subtree...
|
||||
//
|
||||
// Except if there could be transitions of pseudo-elements, in
|
||||
// which
|
||||
// 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
|
||||
// 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 going_to_reframe && !is_before_or_after_pseudo {
|
||||
debug!("Element {:?} is in doomed NAC subtree, \
|
||||
culling traversal", el);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -396,7 +426,6 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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);
|
||||
|
||||
// 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() ||
|
||||
context.shared.traversal_flags.for_animation_only(),
|
||||
"Should have computed style or haven't yet valid computed style in case of animation-only restyle");
|
||||
trace!("propagated_hint={:?}, inherited_style_changed={:?}",
|
||||
propagated_hint, inherited_style_changed);
|
||||
"Should have computed style or haven't yet valid computed \
|
||||
style in case of animation-only restyle");
|
||||
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 =
|
||||
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,
|
||||
// where we won't need to perform a post-traversal to pick up any
|
||||
// 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(); }
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ use style::font_metrics::get_metrics_provider_for_product;
|
||||
use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl};
|
||||
use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData};
|
||||
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::wrapper::GeckoElement;
|
||||
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) {
|
||||
// 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.
|
||||
@ -936,12 +937,15 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pseudo_style(guard: &SharedRwLockReadGuard, element: GeckoElement, pseudo_tag: *mut nsIAtom,
|
||||
styles: &ElementStyles, doc_data: &PerDocumentStyleData)
|
||||
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
|
||||
element: GeckoElement,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
styles: &ElementStyles,
|
||||
doc_data: &PerDocumentStyleData)
|
||||
-> Option<Arc<ComputedValues>>
|
||||
{
|
||||
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::Precomputed => unreachable!("No anonymous boxes"),
|
||||
PseudoElementCascadeType::Lazy => {
|
||||
|
Loading…
Reference in New Issue
Block a user