diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 5a67b1af5bf..5222d4ba70a 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -14,6 +14,7 @@ use script_traits::{AnimationState, LayoutMsg as ConstellationMsg}; use std::collections::HashMap; use std::sync::mpsc::Receiver; use style::animation::{Animation, update_style_for_animation}; +use style::dom::TRestyleDamage; use style::timer::Timer; /// Processes any new animations that were discovered after style recalculation. @@ -130,10 +131,11 @@ pub fn recalc_style_for_animations(context: &SharedLayoutContext, flow.mutate_fragments(&mut |fragment| { if let Some(ref animations) = animations.get(&fragment.node) { for animation in animations.iter() { + let old_style = fragment.style.clone(); update_style_for_animation(&context.style_context, animation, - &mut fragment.style, - Some(&mut damage)); + &mut fragment.style); + damage |= RestyleDamage::compute(Some(&old_style), &fragment.style); } } }); diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index bfd7a8273d9..6dcfc06884f 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -51,6 +51,7 @@ use selectors::matching::{DeclarationBlock, ElementFlags}; use selectors::parser::{AttrSelector, NamespaceConstraint}; use std::marker::PhantomData; use std::mem::{transmute, transmute_copy}; +use std::sync::Arc; use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; use style::attr::AttrValue; use style::computed_values::display; @@ -58,7 +59,7 @@ use style::context::SharedStyleContext; use style::data::PrivateStyleData; use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode}; use style::element_state::*; -use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; +use style::properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock}; use style::refcell::{Ref, RefCell, RefMut}; use style::selector_impl::{ElementSnapshot, NonTSPseudoClass, ServoSelectorImpl}; use style::sink::Push; @@ -262,6 +263,13 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) } } + + #[inline] + fn existing_style_for_restyle_damage<'a>(&'a self, + current_cv: Option<&'a Arc>) + -> Option<&'a Arc> { + current_cv + } } impl<'ln> LayoutNode for ServoLayoutNode<'ln> { diff --git a/components/script_layout_interface/restyle_damage.rs b/components/script_layout_interface/restyle_damage.rs index 733b33bead0..fc7c11d4259 100644 --- a/components/script_layout_interface/restyle_damage.rs +++ b/components/script_layout_interface/restyle_damage.rs @@ -44,21 +44,28 @@ bitflags! { } impl TRestyleDamage for RestyleDamage { - fn compute(old: Option<&Arc>, new: &Arc) -> - RestyleDamage { compute_damage(old, new) } + /// For Servo the style source is always the computed values. + type PreExistingComputedValues = Arc; - /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed. + fn compute(old: Option<&Arc>, + new: &Arc) -> RestyleDamage { + compute_damage(old, new) + } + + /// Returns a bitmask that represents a flow that needs to be rebuilt and + /// reflowed. /// - /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in - /// unnecessary sequential resolution of generated content. + /// Use this instead of `RestyleDamage::all()` because + /// `RestyleDamage::all()` will result in unnecessary sequential resolution + /// of generated content. fn rebuild_and_reflow() -> RestyleDamage { REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW } } impl RestyleDamage { - /// Supposing a flow has the given `position` property and this damage, returns the damage that - /// we should add to the *parent* of this flow. + /// Supposing a flow has the given `position` property and this damage, + /// returns the damage that we should add to the *parent* of this flow. pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage { if child_is_absolutely_positioned { self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) diff --git a/components/style/animation.rs b/components/style/animation.rs index a4600418023..1767cd49234 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -6,7 +6,7 @@ use bezier::Bezier; use context::SharedStyleContext; -use dom::{OpaqueNode, TRestyleDamage}; +use dom::OpaqueNode; use euclid::point::Point2D; use keyframes::{KeyframesStep, KeyframesStepValue}; use properties::animated_properties::{AnimatedProperty, TransitionProperty}; @@ -490,13 +490,12 @@ pub fn update_style_for_animation_frame(mut new_style: &mut Arc, } /// Updates a single animation and associated style based on the current time. /// If `damage` is provided, inserts the appropriate restyle damage. -pub fn update_style_for_animation(context: &SharedStyleContext, - animation: &Animation, - style: &mut Arc, - damage: Option<&mut Damage>) -where Damage: TRestyleDamage { +pub fn update_style_for_animation(context: &SharedStyleContext, + animation: &Animation, + style: &mut Arc) { debug!("update_style_for_animation: entering"); debug_assert!(!animation.is_expired()); + match *animation { Animation::Transition(_, start_time, ref frame, _) => { debug!("update_style_for_animation: transition found"); @@ -506,10 +505,6 @@ where Damage: TRestyleDamage { now, start_time, frame); if updated_style { - if let Some(damage) = damage { - *damage = *damage | Damage::compute(Some(style), &new_style); - } - *style = new_style } } @@ -660,10 +655,6 @@ where Damage: TRestyleDamage { } debug!("update_style_for_animation: got style change in animation \"{}\"", name); - if let Some(damage) = damage { - *damage = *damage | Damage::compute(Some(style), &new_style); - } - *style = new_style; } } diff --git a/components/style/dom.rs b/components/style/dom.rs index 13d7e37d70c..3406cf2815e 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -46,7 +46,19 @@ impl OpaqueNode { } pub trait TRestyleDamage : BitOr + Copy { - fn compute(old: Option<&Arc>, new: &Arc) -> Self; + /// The source for our current computed values in the cascade. This is a + /// ComputedValues in Servo and a StyleContext in Gecko. + /// + /// This is needed because Gecko has a few optimisations for the calculation + /// of the difference depending on which values have been used during + /// layout. + /// + /// This should be obtained via TNode::existing_style_for_restyle_damage + type PreExistingComputedValues; + + fn compute(old: Option<&Self::PreExistingComputedValues>, + new: &Arc) -> Self; + fn rebuild_and_reflow() -> Self; } @@ -159,6 +171,13 @@ pub trait TNode : Sized + Copy + Clone { fn unstyle(self) { self.mutate_data().unwrap().style = None; } + + /// XXX: It's a bit unfortunate we need to pass the current computed values + /// as an argument here, but otherwise Servo would crash due to double + /// borrows to return it. + fn existing_style_for_restyle_damage<'a>(&'a self, + current_computed_values: Option<&'a Arc>) + -> Option<&'a ::PreExistingComputedValues>; } pub trait TDocument : Sized + Copy + Clone { diff --git a/components/style/gecko_glue.rs b/components/style/gecko_glue.rs index b9cbbb15a85..d9ec0bd5562 100644 --- a/components/style/gecko_glue.rs +++ b/components/style/gecko_glue.rs @@ -46,8 +46,13 @@ impl ArcHelpers { unsafe { transmute(owned) } } - pub unsafe fn borrow(borrowed: &Arc) -> *const &mut GeckoType { - transmute(borrowed) + pub fn borrow(borrowed: &Arc, cb: F) -> Output + where F: FnOnce(&mut GeckoType) -> Output + { + let borrowed_gecko_type: *const &mut GeckoType = + unsafe { transmute(borrowed) }; + + unsafe { cb(*borrowed_gecko_type) } } pub unsafe fn addref(ptr: *mut GeckoType) { diff --git a/components/style/matching.rs b/components/style/matching.rs index 1b5885422fc..f8a7c0dc160 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -6,7 +6,7 @@ #![allow(unsafe_code)] -use animation::{self, Animation}; +use animation; use arc_ptr_eq; use cache::{LRUCache, SimpleHashCache}; use context::{StyleContext, SharedStyleContext}; @@ -442,8 +442,13 @@ trait PrivateMatchMethods: TNode cacheable = cacheable && !animations_started } + + let existing_style = + self.existing_style_for_restyle_damage(style.map(|s| &*s)); + // Calculate style difference. - let damage = Self::ConcreteRestyleDamage::compute(style.map(|s| &*s), &this_style); + let damage = + Self::ConcreteRestyleDamage::compute(existing_style, &this_style); // Cache the resolved style if it was cacheable. if cacheable { @@ -490,8 +495,9 @@ trait PrivateMatchMethods: TNode // See #12171 and the associated PR for an example where this // happened while debugging other release panic. if !running_animation.is_expired() { - animation::update_style_for_animation::( - context, running_animation, style, None); + animation::update_style_for_animation(context, + running_animation, + style); running_animation.mark_as_expired(); } } @@ -585,9 +591,17 @@ pub trait ElementMatchMethods : TElement { if let Some(shared_style) = self.share_style_with_candidate_if_possible(parent.clone(), candidate) { // Yay, cache hit. Share the style. let node = self.as_node(); + let style = &mut node.mutate_data().unwrap().style; - let damage = <::ConcreteNode as TNode> - ::ConcreteRestyleDamage::compute((*style).as_ref(), &shared_style); + + let damage = { + let source = + node.existing_style_for_restyle_damage((*style).as_ref()); + let damage = <::ConcreteNode as TNode> + ::ConcreteRestyleDamage::compute(source, &shared_style); + damage + }; + *style = Some(shared_style); return StyleSharingResult::StyleWasShared(i, damage) } @@ -675,8 +689,14 @@ pub trait MatchMethods : TNode { let mut data_ref = self.mutate_data().unwrap(); let mut data = &mut *data_ref; let cloned_parent_style = ComputedValues::style_for_child_text_node(parent_style.unwrap()); - damage = Self::ConcreteRestyleDamage::compute(data.style.as_ref(), - &cloned_parent_style); + + { + let existing_style = + self.existing_style_for_restyle_damage(data.style.as_ref()); + damage = Self::ConcreteRestyleDamage::compute(existing_style, + &cloned_parent_style); + } + data.style = Some(cloned_parent_style); } else { damage = { @@ -697,7 +717,6 @@ pub trait MatchMethods : TNode { let applicable_declarations_for_this_pseudo = applicable_declarations.per_pseudo.get(&pseudo).unwrap(); - if !applicable_declarations_for_this_pseudo.is_empty() { // NB: Transitions and animations should only work for // pseudo-elements ::before and ::after diff --git a/ports/geckolib/gecko_bindings/bindings.rs b/ports/geckolib/gecko_bindings/bindings.rs index 35f89f89074..646e1cffc4e 100644 --- a/ports/geckolib/gecko_bindings/bindings.rs +++ b/ports/geckolib/gecko_bindings/bindings.rs @@ -144,6 +144,10 @@ use structs::nsFont; use structs::FontFamilyList; use structs::FontFamilyType; use structs::nsIAtom; +use structs::nsStyleContext; +unsafe impl Send for nsStyleContext {} +unsafe impl Sync for nsStyleContext {} +impl HeapSizeOf for nsStyleContext { fn heap_size_of_children(&self) -> usize { 0 } } pub type RawGeckoNode = nsINode; pub enum Element { } @@ -294,7 +298,9 @@ extern "C" { pub fn Gecko_GetNodeFlags(node: *mut RawGeckoNode) -> u32; pub fn Gecko_SetNodeFlags(node: *mut RawGeckoNode, flags: u32); pub fn Gecko_UnsetNodeFlags(node: *mut RawGeckoNode, flags: u32); - pub fn Gecko_CalcStyleDifference(oldstyle: *mut ServoComputedValues, + pub fn Gecko_GetStyleContext(node: *mut RawGeckoNode) + -> *mut nsStyleContext; + pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext, newstyle: *mut ServoComputedValues) -> nsChangeHint; pub fn Gecko_StoreStyleDifference(node: *mut RawGeckoNode, diff --git a/ports/geckolib/gecko_bindings/tools/regen.py b/ports/geckolib/gecko_bindings/tools/regen.py index 821a5e475a7..534acbeb146 100755 --- a/ports/geckolib/gecko_bindings/tools/regen.py +++ b/ports/geckolib/gecko_bindings/tools/regen.py @@ -128,7 +128,7 @@ COMPILATION_TARGETS = { "nsStyleCoord::Calc", "nsRestyleHint", "ServoElementSnapshot", "nsChangeHint", "SheetParsingMode", "nsMainThreadPtrHandle", "nsMainThreadPtrHolder", "nscolor", "nsFont", "FontFamilyList", - "FontFamilyType", "nsIAtom", + "FontFamilyType", "nsIAtom", "nsStyleContext" ], "void_types": [ "nsINode", "nsIDocument", "nsIPrincipal", "nsIURI", diff --git a/ports/geckolib/wrapper.rs b/ports/geckolib/wrapper.rs index c236ddeb7f1..b750fda883f 100644 --- a/ports/geckolib/wrapper.rs +++ b/ports/geckolib/wrapper.rs @@ -8,6 +8,7 @@ use gecko_bindings::bindings; use gecko_bindings::bindings::Gecko_ChildrenCount; use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_GetNodeData; +use gecko_bindings::bindings::Gecko_GetStyleContext; use gecko_bindings::bindings::ServoComputedValues; use gecko_bindings::bindings::ServoNodeData; use gecko_bindings::bindings::{Gecko_CalcStyleDifference, Gecko_StoreStyleDifference}; @@ -24,7 +25,7 @@ use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink}; use gecko_bindings::bindings::{Gecko_LocalName, Gecko_Namespace, Gecko_NodeIsElement, Gecko_SetNodeData}; use gecko_bindings::bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode}; use gecko_bindings::structs::{NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO, NODE_IS_DIRTY_FOR_SERVO}; -use gecko_bindings::structs::{nsIAtom, nsChangeHint}; +use gecko_bindings::structs::{nsIAtom, nsChangeHint, nsStyleContext}; use glue::GeckoDeclarationBlock; use libc::uintptr_t; use selectors::Element; @@ -97,19 +98,19 @@ impl<'ln> GeckoNode<'ln> { pub struct GeckoRestyleDamage(nsChangeHint); impl TRestyleDamage for GeckoRestyleDamage { - fn compute(previous_style: Option<&Arc>, - current_style: &Arc) -> Self { + type PreExistingComputedValues = nsStyleContext; + fn compute(source: Option<&nsStyleContext>, + new_style: &Arc) -> Self { type Helpers = ArcHelpers; - let previous_style = match previous_style { - Some(previous) => previous, + let context = match source { + Some(ctx) => ctx as *const nsStyleContext as *mut nsStyleContext, None => return Self::rebuild_and_reflow(), }; - let previous = unsafe { Helpers::borrow(previous_style) }; - let current = unsafe { Helpers::borrow(current_style) }; - let hint = unsafe { Gecko_CalcStyleDifference(*previous, *current) }; - - GeckoRestyleDamage(hint) + Helpers::borrow(new_style, |new_style| { + let hint = unsafe { Gecko_CalcStyleDifference(context, new_style) }; + GeckoRestyleDamage(hint) + }) } fn rebuild_and_reflow() -> Self { @@ -307,6 +308,20 @@ impl<'ln> TNode for GeckoNode<'ln> { } } + fn existing_style_for_restyle_damage<'a>(&'a self, + current_cv: Option<&'a Arc>) + -> Option<&'a nsStyleContext> { + if current_cv.is_none() { + // Don't bother in doing an ffi call to get null back. + return None; + } + + unsafe { + let context_ptr = Gecko_GetStyleContext(self.node); + context_ptr.as_ref() + } + } + fn needs_dirty_on_viewport_size_changed(&self) -> bool { // Gecko's node doesn't have the DIRTY_ON_VIEWPORT_SIZE_CHANGE flag, // so we force them to be dirtied on viewport size change, regardless if