mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
style: Handle TraversalRestyleBehavior::ForReconstruct in the Servo restyle.
This commit is contained in:
parent
2bbeb21551
commit
3f71c80e2f
5 changed files with 89 additions and 23 deletions
|
@ -321,6 +321,7 @@ mod bindings {
|
||||||
"mozilla::css::SheetParsingMode",
|
"mozilla::css::SheetParsingMode",
|
||||||
"mozilla::HalfCorner",
|
"mozilla::HalfCorner",
|
||||||
"mozilla::PropertyStyleAnimationValuePair",
|
"mozilla::PropertyStyleAnimationValuePair",
|
||||||
|
"mozilla::TraversalRestyleBehavior",
|
||||||
"mozilla::TraversalRootBehavior",
|
"mozilla::TraversalRootBehavior",
|
||||||
"mozilla::StyleShapeRadius",
|
"mozilla::StyleShapeRadius",
|
||||||
"mozilla::StyleGrid.*",
|
"mozilla::StyleGrid.*",
|
||||||
|
@ -616,6 +617,7 @@ mod bindings {
|
||||||
"RawGeckoURLExtraData",
|
"RawGeckoURLExtraData",
|
||||||
"RefPtr",
|
"RefPtr",
|
||||||
"CSSPseudoClassType",
|
"CSSPseudoClassType",
|
||||||
|
"TraversalRestyleBehavior",
|
||||||
"TraversalRootBehavior",
|
"TraversalRootBehavior",
|
||||||
"ComputedTimingFunction_BeforeFlag",
|
"ComputedTimingFunction_BeforeFlag",
|
||||||
"FontFamilyList",
|
"FontFamilyList",
|
||||||
|
|
|
@ -223,6 +223,13 @@ impl StoredRestyleHint {
|
||||||
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS)
|
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a restyle hint that forces the element and all its later
|
||||||
|
/// siblings to have their whole subtrees restyled, including the elements
|
||||||
|
/// themselves.
|
||||||
|
pub fn subtree_and_later_siblings() -> Self {
|
||||||
|
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS | RESTYLE_LATER_SIBLINGS)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the hint indicates that our style may be invalidated.
|
/// Returns true if the hint indicates that our style may be invalidated.
|
||||||
pub fn has_self_invalidations(&self) -> bool {
|
pub fn has_self_invalidations(&self) -> bool {
|
||||||
self.0.intersects(RestyleHint::for_self())
|
self.0.intersects(RestyleHint::for_self())
|
||||||
|
|
|
@ -557,7 +557,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
|
|
||||||
// Accumulate restyle damage.
|
// Accumulate restyle damage.
|
||||||
if let Some(old) = old_values {
|
if let Some(old) = old_values {
|
||||||
self.accumulate_damage(restyle.unwrap(), &old, &new_values, pseudo);
|
self.accumulate_damage(&context.shared, restyle.unwrap(), &old, &new_values, pseudo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the new computed values.
|
// Set the new computed values.
|
||||||
|
@ -677,10 +677,16 @@ trait PrivateMatchMethods: TElement {
|
||||||
/// Computes and applies non-redundant damage.
|
/// Computes and applies non-redundant damage.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn accumulate_damage(&self,
|
fn accumulate_damage(&self,
|
||||||
|
shared_context: &SharedStyleContext,
|
||||||
restyle: &mut RestyleData,
|
restyle: &mut RestyleData,
|
||||||
old_values: &Arc<ComputedValues>,
|
old_values: &Arc<ComputedValues>,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>) {
|
pseudo: Option<&PseudoElement>) {
|
||||||
|
// Don't accumulate damage if we're in a restyle for reconstruction.
|
||||||
|
if shared_context.traversal_flags.for_reconstruct() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If an ancestor is already getting reconstructed by Gecko's top-down
|
// If an ancestor is already getting reconstructed by Gecko's top-down
|
||||||
// frame constructor, no need to apply damage.
|
// frame constructor, no need to apply damage.
|
||||||
if restyle.damage_handled.contains(RestyleDamage::reconstruct()) {
|
if restyle.damage_handled.contains(RestyleDamage::reconstruct()) {
|
||||||
|
@ -705,6 +711,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
/// Computes and applies restyle damage unless we've already maxed it out.
|
/// Computes and applies restyle damage unless we've already maxed it out.
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
fn accumulate_damage(&self,
|
fn accumulate_damage(&self,
|
||||||
|
_shared_context: &SharedStyleContext,
|
||||||
restyle: &mut RestyleData,
|
restyle: &mut RestyleData,
|
||||||
old_values: &Arc<ComputedValues>,
|
old_values: &Arc<ComputedValues>,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
|
@ -1109,7 +1116,7 @@ pub trait MatchMethods : TElement {
|
||||||
let old_values = data.get_styles_mut()
|
let old_values = data.get_styles_mut()
|
||||||
.and_then(|s| s.primary.values.take());
|
.and_then(|s| s.primary.values.take());
|
||||||
if let Some(old) = old_values {
|
if let Some(old) = old_values {
|
||||||
self.accumulate_damage(data.restyle_mut(), &old,
|
self.accumulate_damage(shared_context, data.restyle_mut(), &old,
|
||||||
shared_style.values(), None);
|
shared_style.values(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,14 @@ pub struct PerLevelTraversalData {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Represents that target elements of the traversal.
|
/// Flags that control the traversal process.
|
||||||
pub flags TraversalFlags: u8 {
|
pub flags TraversalFlags: u8 {
|
||||||
/// Traverse only unstyled children.
|
/// Traverse only unstyled children.
|
||||||
const UNSTYLED_CHILDREN_ONLY = 0x01,
|
const UNSTYLED_CHILDREN_ONLY = 0x01,
|
||||||
/// Traverse only elements for animation restyles
|
/// Traverse only elements for animation restyles.
|
||||||
const ANIMATION_ONLY = 0x02,
|
const ANIMATION_ONLY = 0x02,
|
||||||
|
/// Traverse without generating any change hints.
|
||||||
|
const FOR_RECONSTRUCT = 0x04,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +52,11 @@ impl TraversalFlags {
|
||||||
pub fn for_unstyled_children_only(&self) -> bool {
|
pub fn for_unstyled_children_only(&self) -> bool {
|
||||||
self.contains(UNSTYLED_CHILDREN_ONLY)
|
self.contains(UNSTYLED_CHILDREN_ONLY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the traversal is for a frame reconstruction.
|
||||||
|
pub fn for_reconstruct(&self) -> bool {
|
||||||
|
self.contains(FOR_RECONSTRUCT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This structure exists to enforce that callers invoke pre_traverse, and also
|
/// This structure exists to enforce that callers invoke pre_traverse, and also
|
||||||
|
@ -148,6 +155,10 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
|
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
|
||||||
-> PreTraverseToken
|
-> PreTraverseToken
|
||||||
{
|
{
|
||||||
|
debug_assert!(!(traversal_flags.for_reconstruct() &&
|
||||||
|
traversal_flags.for_unstyled_children_only()),
|
||||||
|
"must not specify FOR_RECONSTRUCT in combination with UNSTYLED_CHILDREN_ONLY");
|
||||||
|
|
||||||
if traversal_flags.for_unstyled_children_only() {
|
if traversal_flags.for_unstyled_children_only() {
|
||||||
if root.borrow_data().map_or(true, |d| d.has_styles() && d.styles().is_display_none()) {
|
if root.borrow_data().map_or(true, |d| d.has_styles() && d.styles().is_display_none()) {
|
||||||
return PreTraverseToken {
|
return PreTraverseToken {
|
||||||
|
@ -165,12 +176,18 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
// we need a special case for the root.
|
// we need a special case for the root.
|
||||||
//
|
//
|
||||||
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
|
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
|
||||||
// we will drop on the floor. To prevent missed restyles, we assert against
|
// we propagate to the next sibling element.
|
||||||
// restyling a root with later siblings.
|
|
||||||
if let Some(mut data) = root.mutate_data() {
|
if let Some(mut data) = root.mutate_data() {
|
||||||
if let Some(r) = data.get_restyle_mut() {
|
if let Some(r) = data.get_restyle_mut() {
|
||||||
debug_assert!(root.next_sibling_element().is_none());
|
let later_siblings = r.compute_final_hint(root, stylist);
|
||||||
let _later_siblings = r.compute_final_hint(root, stylist);
|
if later_siblings {
|
||||||
|
if let Some(next) = root.next_sibling_element() {
|
||||||
|
if let Some(mut next_data) = next.mutate_data() {
|
||||||
|
let hint = StoredRestyleHint::subtree_and_later_siblings();
|
||||||
|
next_data.ensure_restyle().hint.insert(&hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +212,10 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if traversal_flags.for_reconstruct() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
match node.as_element() {
|
match node.as_element() {
|
||||||
None => Self::text_node_needs_traversal(node),
|
None => Self::text_node_needs_traversal(node),
|
||||||
Some(el) => {
|
Some(el) => {
|
||||||
|
@ -264,7 +285,11 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
// Servo uses the post-order traversal for flow construction, so
|
// Servo uses the post-order traversal for flow construction, so
|
||||||
// we need to traverse any element with damage so that we can perform
|
// we need to traverse any element with damage so that we can perform
|
||||||
// fixup / reconstruction on our way back up the tree.
|
// fixup / reconstruction on our way back up the tree.
|
||||||
if cfg!(feature = "servo") &&
|
//
|
||||||
|
// 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())
|
data.get_restyle().map_or(false, |r| r.damage != RestyleDamage::empty())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -342,11 +367,15 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
|
|
||||||
for kid in parent.as_node().children() {
|
for kid in parent.as_node().children() {
|
||||||
if Self::node_needs_traversal(kid, self.shared_context().traversal_flags) {
|
if Self::node_needs_traversal(kid, self.shared_context().traversal_flags) {
|
||||||
let el = kid.as_element();
|
// If we are in a restyle for reconstruction, there is no need to
|
||||||
if el.as_ref().and_then(|el| el.borrow_data())
|
// perform a post-traversal, so we don't need to set the dirty
|
||||||
.map_or(false, |d| d.has_styles())
|
// descendants bit on the parent.
|
||||||
{
|
if !self.shared_context().traversal_flags.for_reconstruct() {
|
||||||
unsafe { parent.set_dirty_descendants(); }
|
let el = kid.as_element();
|
||||||
|
if el.as_ref().and_then(|el| el.borrow_data())
|
||||||
|
.map_or(false, |d| d.has_styles()) {
|
||||||
|
unsafe { parent.set_dirty_descendants(); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f(thread_local, kid);
|
f(thread_local, kid);
|
||||||
}
|
}
|
||||||
|
@ -557,20 +586,34 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
inherited_style_changed);
|
inherited_style_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are in a restyle for reconstruction, drop the existing restyle
|
||||||
|
// data here, since we won't need to perform a post-traversal to pick up
|
||||||
|
// any change hints.
|
||||||
|
if context.shared.traversal_flags.for_reconstruct() {
|
||||||
|
data.clear_restyle();
|
||||||
|
}
|
||||||
|
|
||||||
if context.shared.traversal_flags.for_animation_only() {
|
if context.shared.traversal_flags.for_animation_only() {
|
||||||
unsafe { element.unset_animation_only_dirty_descendants(); }
|
unsafe { element.unset_animation_only_dirty_descendants(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the dirty descendants bit is not set for the root of a
|
// There are two cases when we want to clear the dity descendants bit
|
||||||
// display:none subtree, even if the style didn't change (since, if
|
// here after styling this element.
|
||||||
// the style did change, we'd have already cleared it above).
|
//
|
||||||
|
// The first case is when this element is the root of a display:none
|
||||||
|
// subtree, even if the style didn't change (since, if the style did
|
||||||
|
// change, we'd have already cleared it above).
|
||||||
//
|
//
|
||||||
// This keeps the tree in a valid state without requiring the DOM to
|
// This keeps the tree in a valid state without requiring the DOM to
|
||||||
// check display:none on the parent when inserting new children (which
|
// check display:none on the parent when inserting new children (which
|
||||||
// can be moderately expensive). Instead, DOM implementations can
|
// can be moderately expensive). Instead, DOM implementations can
|
||||||
// unconditionally set the dirty descendants bit on any styled parent,
|
// unconditionally set the dirty descendants bit on any styled parent,
|
||||||
// and let the traversal sort it out.
|
// and let the traversal sort it out.
|
||||||
if data.styles().is_display_none() {
|
//
|
||||||
|
// 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() {
|
||||||
unsafe { element.unset_dirty_descendants(); }
|
unsafe { element.unset_dirty_descendants(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
||||||
use style::supports::parse_condition_or_declaration;
|
use style::supports::parse_condition_or_declaration;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
use style::traversal::{ANIMATION_ONLY, UNSTYLED_CHILDREN_ONLY};
|
use style::traversal::{ANIMATION_ONLY, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY};
|
||||||
use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
|
use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use super::stylesheet_loader::StylesheetLoader;
|
use super::stylesheet_loader::StylesheetLoader;
|
||||||
|
@ -207,13 +207,20 @@ fn traverse_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed,
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
||||||
raw_data: RawServoStyleSetBorrowed,
|
raw_data: RawServoStyleSetBorrowed,
|
||||||
behavior: structs::TraversalRootBehavior) -> bool {
|
root_behavior: structs::TraversalRootBehavior,
|
||||||
|
restyle_behavior: structs::TraversalRestyleBehavior)
|
||||||
|
-> bool {
|
||||||
|
use self::structs::TraversalRestyleBehavior as Restyle;
|
||||||
|
use self::structs::TraversalRootBehavior as Root;
|
||||||
|
|
||||||
let element = GeckoElement(root);
|
let element = GeckoElement(root);
|
||||||
debug!("Servo_TraverseSubtree: {:?}", element);
|
debug!("Servo_TraverseSubtree: {:?}", element);
|
||||||
|
|
||||||
let traversal_flags = match behavior {
|
let traversal_flags = match (root_behavior, restyle_behavior) {
|
||||||
structs::TraversalRootBehavior::UnstyledChildrenOnly => UNSTYLED_CHILDREN_ONLY,
|
(Root::Normal, Restyle::Normal) => TraversalFlags::empty(),
|
||||||
_ => TraversalFlags::empty(),
|
(Root::UnstyledChildrenOnly, Restyle::Normal) => UNSTYLED_CHILDREN_ONLY,
|
||||||
|
(Root::Normal, Restyle::ForReconstruct) => FOR_RECONSTRUCT,
|
||||||
|
_ => panic!("invalid combination of TraversalRootBehavior and TraversalRestyleBehavior"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if element.has_animation_only_dirty_descendants() ||
|
if element.has_animation_only_dirty_descendants() ||
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue