aboutsummaryrefslogtreecommitdiffstats
path: root/components/style
diff options
context:
space:
mode:
authorBobby Holley <bobbyholley@gmail.com>2016-11-02 18:46:04 -0700
committerBobby Holley <bobbyholley@gmail.com>2016-11-07 11:10:48 -0800
commita2c7a9d0fb7174f9188640ba2fb5a3df7821c1a8 (patch)
tree8fe3228394856e7f55053c3399e2887a64d263e8 /components/style
parentb69fdad8e44d77b5a946ee4155ab3da3320d93cd (diff)
downloadservo-a2c7a9d0fb7174f9188640ba2fb5a3df7821c1a8.tar.gz
servo-a2c7a9d0fb7174f9188640ba2fb5a3df7821c1a8.zip
Stop using associated types for the concrete TRestyleDamage implementation.
MozReview-Commit-ID: LfaZFCVlIb1
Diffstat (limited to 'components/style')
-rw-r--r--components/style/dom.rs7
-rw-r--r--components/style/gecko/mod.rs1
-rw-r--r--components/style/gecko/restyle_damage.rs53
-rw-r--r--components/style/gecko/wrapper.rs45
-rw-r--r--components/style/lib.rs2
-rw-r--r--components/style/matching.rs34
-rw-r--r--components/style/selector_impl.rs12
-rw-r--r--components/style/servo/mod.rs6
-rw-r--r--components/style/servo/restyle_damage.rs267
-rw-r--r--components/style/servo/selector_impl.rs (renamed from components/style/servo_selector_impl.rs)0
10 files changed, 358 insertions, 69 deletions
diff --git a/components/style/dom.rs b/components/style/dom.rs
index 493a38bdc59..388a7310ab0 100644
--- a/components/style/dom.rs
+++ b/components/style/dom.rs
@@ -14,7 +14,7 @@ use parking_lot::RwLock;
use properties::{ComputedValues, PropertyDeclarationBlock};
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
-use selector_impl::{ElementExt, PseudoElement, Snapshot};
+use selector_impl::{ElementExt, PseudoElement, RestyleDamage, Snapshot};
use selector_matching::ApplicableDeclarationBlock;
use sink::Push;
use std::fmt::Debug;
@@ -173,7 +173,6 @@ pub trait PresentationalHintsSynthetizer {
pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer {
type ConcreteNode: TNode<ConcreteElement = Self, ConcreteDocument = Self::ConcreteDocument>;
type ConcreteDocument: TDocument<ConcreteNode = Self::ConcreteNode, ConcreteElement = Self>;
- type ConcreteRestyleDamage: TRestyleDamage;
fn as_node(&self) -> Self::ConcreteNode;
@@ -185,7 +184,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
fn attr_equals(&self, namespace: &Namespace, attr: &LocalName, value: &Atom) -> bool;
/// Set the restyle damage field.
- fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage);
+ fn set_restyle_damage(self, damage: RestyleDamage);
/// 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
@@ -193,7 +192,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
fn existing_style_for_restyle_damage<'a>(&'a self,
current_computed_values: Option<&'a Arc<ComputedValues>>,
pseudo: Option<&PseudoElement>)
- -> Option<&'a <Self::ConcreteRestyleDamage as TRestyleDamage>::PreExistingComputedValues>;
+ -> Option<&'a <RestyleDamage as TRestyleDamage>::PreExistingComputedValues>;
/// The concept of a dirty bit doesn't exist in our new restyle algorithm.
/// Instead, we associate restyle and change hints with nodes. However, we
diff --git a/components/style/gecko/mod.rs b/components/style/gecko/mod.rs
index 7153a7e3da2..ee462ef4b81 100644
--- a/components/style/gecko/mod.rs
+++ b/components/style/gecko/mod.rs
@@ -5,6 +5,7 @@
pub mod context;
pub mod data;
+pub mod restyle_damage;
pub mod snapshot;
pub mod snapshot_helpers;
pub mod traversal;
diff --git a/components/style/gecko/restyle_damage.rs b/components/style/gecko/restyle_damage.rs
new file mode 100644
index 00000000000..ed23bb15062
--- /dev/null
+++ b/components/style/gecko/restyle_damage.rs
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::TRestyleDamage;
+use gecko_bindings::bindings;
+use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
+use gecko_bindings::sugar::ownership::FFIArcHelpers;
+use properties::ComputedValues;
+use std::ops::BitOr;
+use std::sync::Arc;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct GeckoRestyleDamage(nsChangeHint);
+
+impl GeckoRestyleDamage {
+ pub fn as_change_hint(&self) -> nsChangeHint {
+ self.0
+ }
+}
+
+impl TRestyleDamage for GeckoRestyleDamage {
+ type PreExistingComputedValues = nsStyleContext;
+
+ fn empty() -> Self {
+ use std::mem;
+ GeckoRestyleDamage(unsafe { mem::transmute(0u32) })
+ }
+
+ fn compute(source: &nsStyleContext,
+ new_style: &Arc<ComputedValues>) -> Self {
+ let context = source as *const nsStyleContext as *mut nsStyleContext;
+ let hint = unsafe {
+ bindings::Gecko_CalcStyleDifference(context,
+ new_style.as_borrowed_opt().unwrap())
+ };
+ GeckoRestyleDamage(hint)
+ }
+
+ fn rebuild_and_reflow() -> Self {
+ GeckoRestyleDamage(nsChangeHint::nsChangeHint_ReconstructFrame)
+ }
+}
+
+impl BitOr for GeckoRestyleDamage {
+ type Output = Self;
+
+ fn bitor(self, other: Self) -> Self {
+ use std::mem;
+ GeckoRestyleDamage(unsafe { mem::transmute(self.0 as u32 | other.0 as u32) })
+ }
+}
+
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index 4da1e1af4f9..9adac5f269f 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -7,15 +7,15 @@
use atomic_refcell::{AtomicRef, AtomicRefCell};
use data::ElementData;
-use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
+use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, UnsafeNode};
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
use element_state::ElementState;
use error_reporting::StdoutErrorReporter;
+use gecko::restyle_damage::GeckoRestyleDamage;
use gecko::selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement};
use gecko::snapshot::GeckoElementSnapshot;
use gecko::snapshot_helpers;
use gecko_bindings::bindings;
-use gecko_bindings::bindings::{Gecko_CalcStyleDifference, Gecko_StoreStyleDifference};
use gecko_bindings::bindings::{Gecko_DropStyleChildrenIterator, Gecko_MaybeCreateStyleChildrenIterator};
use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentElement};
use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetNextStyleChild};
@@ -26,10 +26,10 @@ use gecko_bindings::bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode};
use gecko_bindings::bindings::Gecko_ClassOrClassList;
use gecko_bindings::bindings::Gecko_GetStyleContext;
use gecko_bindings::bindings::Gecko_SetNodeFlags;
+use gecko_bindings::bindings::Gecko_StoreStyleDifference;
use gecko_bindings::structs;
use gecko_bindings::structs::{NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO, NODE_IS_DIRTY_FOR_SERVO};
-use gecko_bindings::structs::{nsChangeHint, nsIAtom, nsIContent, nsStyleContext};
-use gecko_bindings::sugar::ownership::FFIArcHelpers;
+use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
use libc::uintptr_t;
use parking_lot::RwLock;
use parser::ParserContextExtraData;
@@ -41,7 +41,6 @@ use selectors::Element;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use sink::Push;
use std::fmt;
-use std::ops::BitOr;
use std::ptr;
use std::sync::Arc;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
@@ -66,39 +65,6 @@ impl<'ln> GeckoNode<'ln> {
}
}
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct GeckoRestyleDamage(nsChangeHint);
-
-impl TRestyleDamage for GeckoRestyleDamage {
- type PreExistingComputedValues = nsStyleContext;
-
- fn empty() -> Self {
- use std::mem;
- GeckoRestyleDamage(unsafe { mem::transmute(0u32) })
- }
-
- fn compute(source: &nsStyleContext,
- new_style: &Arc<ComputedValues>) -> Self {
- let context = source as *const nsStyleContext as *mut nsStyleContext;
- let hint = unsafe { Gecko_CalcStyleDifference(context, new_style.as_borrowed_opt().unwrap()) };
- GeckoRestyleDamage(hint)
- }
-
- fn rebuild_and_reflow() -> Self {
- GeckoRestyleDamage(nsChangeHint::nsChangeHint_ReconstructFrame)
- }
-}
-
-impl BitOr for GeckoRestyleDamage {
- type Output = Self;
-
- fn bitor(self, other: Self) -> Self {
- use std::mem;
- GeckoRestyleDamage(unsafe { mem::transmute(self.0 as u32 | other.0 as u32) })
- }
-}
-
-
impl<'ln> NodeInfo for GeckoNode<'ln> {
fn is_element(&self) -> bool {
use gecko_bindings::structs::nsINode_BooleanFlag;
@@ -358,7 +324,6 @@ lazy_static! {
impl<'le> TElement for GeckoElement<'le> {
type ConcreteNode = GeckoNode<'le>;
type ConcreteDocument = GeckoDocument<'le>;
- type ConcreteRestyleDamage = GeckoRestyleDamage;
fn as_node(&self) -> Self::ConcreteNode {
unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
@@ -400,7 +365,7 @@ impl<'le> TElement for GeckoElement<'le> {
// drive the post-traversal. This will go away soon.
unsafe { self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32) }
- unsafe { Gecko_StoreStyleDifference(self.as_node().0, damage.0) }
+ unsafe { Gecko_StoreStyleDifference(self.as_node().0, damage.as_change_hint()) }
}
fn existing_style_for_restyle_damage<'a>(&'a self,
diff --git a/components/style/lib.rs b/components/style/lib.rs
index c6010def374..d7312bbb904 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -117,8 +117,8 @@ pub mod restyle_hints;
pub mod rule_tree;
pub mod selector_impl;
pub mod selector_matching;
+#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
pub mod sequential;
-#[cfg(feature = "servo")] pub mod servo_selector_impl;
pub mod sink;
pub mod str;
pub mod stylesheets;
diff --git a/components/style/matching.rs b/components/style/matching.rs
index ac4bdb0b59a..97faf4d0a6a 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -17,7 +17,7 @@ use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, cascade};
use properties::longhands::display::computed_value as display;
use rule_tree::StrongRuleNode;
-use selector_impl::{TheSelectorImpl, PseudoElement};
+use selector_impl::{PseudoElement, RestyleDamage, TheSelectorImpl};
use selector_matching::{ApplicableDeclarationBlock, Stylist};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
@@ -373,14 +373,14 @@ impl StyleSharingCandidateCache {
}
/// The results of attempting to share a style.
-pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
+pub enum StyleSharingResult {
/// We didn't find anybody to share the style with.
CannotShare,
/// The node's style can be shared. The integer specifies the index in the
/// LRU cache that was hit and the damage that was done, and the restyle
/// result the original result of the candidate's styling, that is, whether
/// it should stop the traversal or not.
- StyleWasShared(usize, ConcreteRestyleDamage),
+ StyleWasShared(usize, RestyleDamage),
}
// Callers need to pass several boolean flags to cascade_node_pseudo_element.
@@ -565,7 +565,7 @@ pub trait MatchMethods : TElement {
&mut StyleSharingCandidateCache,
shared_context: &SharedStyleContext,
data: &mut AtomicRefMut<ElementData>)
- -> StyleSharingResult<Self::ConcreteRestyleDamage> {
+ -> StyleSharingResult {
if opts::get().disable_share_style_cache {
return StyleSharingResult::CannotShare
}
@@ -591,12 +591,8 @@ pub trait MatchMethods : TElement {
// replaced content, or similar stuff!
let damage =
match self.existing_style_for_restyle_damage(data.previous_styles().map(|x| &x.primary), None) {
- Some(ref source) => {
- Self::ConcreteRestyleDamage::compute(source, &shared_style)
- }
- None => {
- Self::ConcreteRestyleDamage::rebuild_and_reflow()
- }
+ Some(ref source) => RestyleDamage::compute(source, &shared_style),
+ None => RestyleDamage::rebuild_and_reflow(),
};
data.finish_styling(ElementStyles::new(shared_style, rule_node));
@@ -678,13 +674,10 @@ pub trait MatchMethods : TElement {
old_style: Option<&Arc<ComputedValues>>,
new_style: &Arc<ComputedValues>,
pseudo: Option<&PseudoElement>)
- -> Self::ConcreteRestyleDamage
+ -> RestyleDamage
{
match self.existing_style_for_restyle_damage(old_style, pseudo) {
- Some(ref source) => {
- Self::ConcreteRestyleDamage::compute(source,
- new_style)
- }
+ Some(ref source) => RestyleDamage::compute(source, new_style),
None => {
// If there's no style source, two things can happen:
//
@@ -710,7 +703,7 @@ pub trait MatchMethods : TElement {
// stick without the assertions.
debug_assert!(pseudo.is_none() ||
new_style.get_box().clone_display() != display::T::none);
- Self::ConcreteRestyleDamage::rebuild_and_reflow()
+ RestyleDamage::rebuild_and_reflow()
}
}
}
@@ -783,7 +776,7 @@ pub trait MatchMethods : TElement {
new_pseudos: &mut PseudoStyles,
context: &Ctx,
applicable_declarations: &mut ApplicableDeclarations)
- -> Self::ConcreteRestyleDamage
+ -> RestyleDamage
where Ctx: StyleContext<'a>
{
// Here we optimise the case of the style changing but both the
@@ -800,9 +793,9 @@ pub trait MatchMethods : TElement {
// otherwise, we don't do anything.
let damage = match old_display {
Some(display) if display == this_display => {
- Self::ConcreteRestyleDamage::empty()
+ RestyleDamage::empty()
}
- _ => Self::ConcreteRestyleDamage::rebuild_and_reflow()
+ _ => RestyleDamage::rebuild_and_reflow()
};
debug!("Short-circuiting traversal: {:?} {:?} {:?}",
@@ -820,8 +813,7 @@ pub trait MatchMethods : TElement {
return damage;
}
- let rebuild_and_reflow =
- Self::ConcreteRestyleDamage::rebuild_and_reflow();
+ let rebuild_and_reflow = RestyleDamage::rebuild_and_reflow();
debug_assert!(new_pseudos.is_empty());
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
diff --git a/components/style/selector_impl.rs b/components/style/selector_impl.rs
index d4a7f2645b4..85734d51387 100644
--- a/components/style/selector_impl.rs
+++ b/components/style/selector_impl.rs
@@ -11,23 +11,29 @@ use selectors::parser::{AttrSelector, SelectorImpl};
pub type AttrValue = <TheSelectorImpl as SelectorImpl>::AttrValue;
#[cfg(feature = "servo")]
-pub use servo_selector_impl::*;
+pub use servo::selector_impl::*;
#[cfg(feature = "gecko")]
pub use gecko::selector_impl::*;
#[cfg(feature = "servo")]
-pub use servo_selector_impl::ServoSelectorImpl as TheSelectorImpl;
+pub use servo::selector_impl::ServoSelectorImpl as TheSelectorImpl;
#[cfg(feature = "gecko")]
pub use gecko::selector_impl::GeckoSelectorImpl as TheSelectorImpl;
#[cfg(feature = "servo")]
-pub use servo_selector_impl::ServoElementSnapshot as Snapshot;
+pub use servo::selector_impl::ServoElementSnapshot as Snapshot;
#[cfg(feature = "gecko")]
pub use gecko::snapshot::GeckoElementSnapshot as Snapshot;
+#[cfg(feature = "servo")]
+pub use servo::restyle_damage::ServoRestyleDamage as RestyleDamage;
+
+#[cfg(feature = "gecko")]
+pub use gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
+
/// This function determines if a pseudo-element is eagerly cascaded or not.
///
/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
diff --git a/components/style/servo/mod.rs b/components/style/servo/mod.rs
new file mode 100644
index 00000000000..bd3db996eff
--- /dev/null
+++ b/components/style/servo/mod.rs
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+pub mod restyle_damage;
+pub mod selector_impl;
diff --git a/components/style/servo/restyle_damage.rs b/components/style/servo/restyle_damage.rs
new file mode 100644
index 00000000000..08dd549b288
--- /dev/null
+++ b/components/style/servo/restyle_damage.rs
@@ -0,0 +1,267 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use computed_values::display;
+use dom::TRestyleDamage;
+use properties::ServoComputedValues;
+use std::fmt;
+use std::sync::Arc;
+
+bitflags! {
+ #[doc = "Individual layout actions that may be necessary after restyling."]
+ pub flags ServoRestyleDamage: u8 {
+ #[doc = "Repaint the node itself."]
+ #[doc = "Currently unused; need to decide how this propagates."]
+ const REPAINT = 0x01,
+
+ #[doc = "The stacking-context-relative position of this node or its descendants has \
+ changed."]
+ #[doc = "Propagates both up and down the flow tree."]
+ const REPOSITION = 0x02,
+
+ #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
+ #[doc = "Propagates down the flow tree because the computation is bottom-up."]
+ const STORE_OVERFLOW = 0x04,
+
+ #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
+ #[doc = "Propagates down the flow tree because the computation is"]
+ #[doc = "bottom-up."]
+ const BUBBLE_ISIZES = 0x08,
+
+ #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
+ into account. \
+ Propagates up the flow tree because the computation is top-down."]
+ const REFLOW_OUT_OF_FLOW = 0x10,
+
+ #[doc = "Recompute actual inline_sizes and block_sizes."]
+ #[doc = "Propagates up the flow tree because the computation is"]
+ #[doc = "top-down."]
+ const REFLOW = 0x20,
+
+ #[doc = "Re-resolve generated content. \
+ Propagates up the flow tree because the computation is inorder."]
+ const RESOLVE_GENERATED_CONTENT = 0x40,
+
+ #[doc = "The entire flow needs to be reconstructed."]
+ const RECONSTRUCT_FLOW = 0x80
+ }
+}
+
+impl TRestyleDamage for ServoRestyleDamage {
+ /// For Servo the style source is always the computed values.
+ type PreExistingComputedValues = Arc<ServoComputedValues>;
+
+ fn empty() -> Self {
+ ServoRestyleDamage::empty()
+ }
+
+ fn compute(old: &Arc<ServoComputedValues>,
+ new: &Arc<ServoComputedValues>) -> ServoRestyleDamage {
+ compute_damage(old, new)
+ }
+
+ /// Returns a bitmask that represents a flow that needs to be rebuilt and
+ /// reflowed.
+ ///
+ /// Use this instead of `ServoRestyleDamage::all()` because
+ /// `ServoRestyleDamage::all()` will result in unnecessary sequential resolution
+ /// of generated content.
+ fn rebuild_and_reflow() -> ServoRestyleDamage {
+ REPAINT | REPOSITION | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW |
+ RECONSTRUCT_FLOW
+ }
+}
+
+impl ServoRestyleDamage {
+ /// 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) -> ServoRestyleDamage {
+ if child_is_absolutely_positioned {
+ self & (REPAINT | REPOSITION | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW |
+ RESOLVE_GENERATED_CONTENT)
+ } else {
+ self & (REPAINT | REPOSITION | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
+ RESOLVE_GENERATED_CONTENT)
+ }
+ }
+
+ /// Supposing the *parent* of a flow with the given `position` property has this damage,
+ /// returns the damage that we should add to this flow.
+ pub fn damage_for_child(self,
+ parent_is_absolutely_positioned: bool,
+ child_is_absolutely_positioned: bool)
+ -> ServoRestyleDamage {
+ match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
+ (false, true) => {
+ // Absolute children are out-of-flow and therefore insulated from changes.
+ //
+ // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
+ self & (REPAINT | REPOSITION)
+ }
+ (true, false) => {
+ // Changing the position of an absolutely-positioned block requires us to reflow
+ // its kids.
+ if self.contains(REFLOW_OUT_OF_FLOW) {
+ self | REFLOW
+ } else {
+ self
+ }
+ }
+ _ => {
+ // TODO(pcwalton): Take floatedness into account.
+ self & (REPAINT | REPOSITION | REFLOW)
+ }
+ }
+ }
+}
+
+impl fmt::Display for ServoRestyleDamage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ let mut first_elem = true;
+
+ let to_iter =
+ [ (REPAINT, "Repaint")
+ , (REPOSITION, "Reposition")
+ , (STORE_OVERFLOW, "StoreOverflow")
+ , (BUBBLE_ISIZES, "BubbleISizes")
+ , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
+ , (REFLOW, "Reflow")
+ , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
+ , (RECONSTRUCT_FLOW, "ReconstructFlow")
+ ];
+
+ for &(damage, damage_str) in &to_iter {
+ if self.contains(damage) {
+ if !first_elem { try!(write!(f, " | ")); }
+ try!(write!(f, "{}", damage_str));
+ first_elem = false;
+ }
+ }
+
+ if first_elem {
+ try!(write!(f, "NoDamage"));
+ }
+
+ Ok(())
+ }
+}
+
+// NB: We need the braces inside the RHS due to Rust #8012. This particular
+// version of this macro might be safe anyway, but we want to avoid silent
+// breakage on modifications.
+macro_rules! add_if_not_equal(
+ ($old:ident, $new:ident, $damage:ident,
+ [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
+ if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
+ $damage.insert($($effect)|*);
+ true
+ } else {
+ false
+ }
+ })
+);
+
+fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> ServoRestyleDamage {
+ let mut damage = ServoRestyleDamage::empty();
+
+ // This should check every CSS property, as enumerated in the fields of
+ // http://doc.servo.org/style/properties/struct.ServoComputedValues.html
+
+ // FIXME: Test somehow that every property is included.
+
+ add_if_not_equal!(old, new, damage,
+ [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW,
+ REFLOW, RECONSTRUCT_FLOW], [
+ get_box.float, get_box.display, get_box.position, get_counters.content,
+ get_counters.counter_reset, get_counters.counter_increment,
+ get_inheritedbox._servo_under_display_none,
+ get_list.quotes, get_list.list_style_type,
+
+ // If these text or font properties change, we need to reconstruct the flow so that
+ // text shaping is re-run.
+ get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
+ get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
+ get_inheritedtext.overflow_wrap, get_inheritedtext.text_justify,
+ get_inheritedtext.white_space, get_inheritedtext.word_break, get_text.text_overflow,
+ get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
+ get_font.font_size, get_font.font_stretch,
+ get_inheritedbox.direction, get_inheritedbox.writing_mode,
+ get_inheritedbox.text_orientation,
+ get_text.text_decoration, get_text.unicode_bidi,
+ get_inheritedtable.empty_cells, get_inheritedtable.caption_side,
+ get_column.column_width, get_column.column_count
+ ]) || (new.get_box().display == display::T::inline &&
+ add_if_not_equal!(old, new, damage,
+ [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES,
+ REFLOW_OUT_OF_FLOW, REFLOW, RECONSTRUCT_FLOW], [
+ // For inline boxes only, border/padding styles are used in flow construction (to decide
+ // whether to create fragments for empty flows).
+ get_border.border_top_width, get_border.border_right_width,
+ get_border.border_bottom_width, get_border.border_left_width,
+ get_padding.padding_top, get_padding.padding_right,
+ get_padding.padding_bottom, get_padding.padding_left
+ ])) || add_if_not_equal!(old, new, damage,
+ [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES,
+ REFLOW_OUT_OF_FLOW, REFLOW],
+ [get_border.border_top_width, get_border.border_right_width,
+ get_border.border_bottom_width, get_border.border_left_width,
+ get_margin.margin_top, get_margin.margin_right,
+ get_margin.margin_bottom, get_margin.margin_left,
+ get_padding.padding_top, get_padding.padding_right,
+ get_padding.padding_bottom, get_padding.padding_left,
+ get_position.width, get_position.height,
+ get_inheritedtext.line_height,
+ get_inheritedtext.text_align, get_inheritedtext.text_indent,
+ get_table.table_layout,
+ get_inheritedtable.border_collapse,
+ get_inheritedtable.border_spacing,
+ get_column.column_gap,
+ get_position.flex_direction,
+ get_position.flex_wrap,
+ get_position.justify_content,
+ get_position.align_items,
+ get_position.align_content,
+ get_position.order,
+ get_position.flex_basis,
+ get_position.flex_grow,
+ get_position.flex_shrink,
+ get_position.align_self
+ ]) || add_if_not_equal!(old, new, damage,
+ [REPAINT, REPOSITION, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW], [
+ get_position.top, get_position.left,
+ get_position.right, get_position.bottom,
+ get_effects.opacity,
+ get_effects.transform, get_effects.transform_style, get_effects.transform_origin,
+ get_effects.perspective, get_effects.perspective_origin
+ ]) || add_if_not_equal!(old, new, damage,
+ [REPAINT], [
+ get_color.color, get_background.background_color,
+ get_background.background_image, get_background.background_position,
+ get_background.background_repeat, get_background.background_attachment,
+ get_background.background_clip, get_background.background_origin,
+ get_background.background_size,
+ get_border.border_top_color, get_border.border_right_color,
+ get_border.border_bottom_color, get_border.border_left_color,
+ get_border.border_top_style, get_border.border_right_style,
+ get_border.border_bottom_style, get_border.border_left_style,
+ get_border.border_top_left_radius, get_border.border_top_right_radius,
+ get_border.border_bottom_left_radius, get_border.border_bottom_right_radius,
+ get_position.z_index, get_box._servo_overflow_clip_box,
+ get_inheritedtext._servo_text_decorations_in_effect,
+ get_pointing.cursor, get_pointing.pointer_events,
+ get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter,
+ get_effects.mix_blend_mode, get_inheritedbox.image_rendering,
+
+ // Note: May require REFLOW et al. if `visibility: collapse` is implemented.
+ get_inheritedbox.visibility
+ ]);
+
+ // If the layer requirements of this flow have changed due to the value
+ // of the transform, then reflow is required to rebuild the layers.
+ if old.transform_requires_layer() != new.transform_requires_layer() {
+ damage.insert(ServoRestyleDamage::rebuild_and_reflow());
+ }
+
+ damage
+}
diff --git a/components/style/servo_selector_impl.rs b/components/style/servo/selector_impl.rs
index 9e5516da9cc..9e5516da9cc 100644
--- a/components/style/servo_selector_impl.rs
+++ b/components/style/servo/selector_impl.rs