mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Bug 1355343: Take all the snapshots into account. r=bholley
I've chosen this approach mainly because there's no other good way to guarantee the model is correct than holding the snapshots alive until a style refresh. What I tried before this (storing them in a sort of "immutable element data") is a pain, since we call into style from the frame constructor and other content notifications, which makes keeping track of which snapshots should be cleared an which shouldn't an insane task. Ideally we'd have a single entry-point for style, but that's not the case right now, and changing that requires pretty non-trivial changes to the frame constructor. MozReview-Commit-ID: FF1KWZv2iBM Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
7d45aad9b4
commit
46bf5d61f0
16 changed files with 439 additions and 308 deletions
|
@ -6,19 +6,17 @@
|
|||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use context::SharedStyleContext;
|
||||
use dom::TElement;
|
||||
use properties::ComputedValues;
|
||||
use properties::longhands::display::computed_value as display;
|
||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
|
||||
use rule_tree::StrongRuleNode;
|
||||
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage, Snapshot};
|
||||
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
|
||||
#[cfg(feature = "servo")] use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "servo")] use std::hash::BuildHasherDefault;
|
||||
use std::ops::Deref;
|
||||
use stylearc::Arc;
|
||||
use stylist::Stylist;
|
||||
use thread_state;
|
||||
use traversal::TraversalFlags;
|
||||
|
||||
/// The structure that represents the result of style computation. This is
|
||||
|
@ -199,9 +197,7 @@ impl StoredRestyleHint {
|
|||
// In the middle of an animation only restyle, we don't need to
|
||||
// propagate any restyle hints, and we need to remove ourselves.
|
||||
if traversal_flags.for_animation_only() {
|
||||
if self.0.intersects(RestyleHint::for_animations()) {
|
||||
self.0.remove(RestyleHint::for_animations());
|
||||
}
|
||||
self.0.remove(RestyleHint::for_animations());
|
||||
return Self::empty();
|
||||
}
|
||||
|
||||
|
@ -275,55 +271,6 @@ impl From<RestyleHint> for StoredRestyleHint {
|
|||
}
|
||||
}
|
||||
|
||||
static NO_SNAPSHOT: Option<Snapshot> = None;
|
||||
|
||||
/// We really want to store an Option<Snapshot> here, but we can't drop Gecko
|
||||
/// Snapshots off-main-thread. So we make a convenient little wrapper to provide
|
||||
/// the semantics of Option<Snapshot>, while deferring the actual drop.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SnapshotOption {
|
||||
snapshot: Option<Snapshot>,
|
||||
destroyed: bool,
|
||||
}
|
||||
|
||||
impl SnapshotOption {
|
||||
/// An empty snapshot.
|
||||
pub fn empty() -> Self {
|
||||
SnapshotOption {
|
||||
snapshot: None,
|
||||
destroyed: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy this snapshot.
|
||||
pub fn destroy(&mut self) {
|
||||
self.destroyed = true;
|
||||
debug_assert!(self.is_none());
|
||||
}
|
||||
|
||||
/// Ensure a snapshot is available and return a mutable reference to it.
|
||||
pub fn ensure<F: FnOnce() -> Snapshot>(&mut self, create: F) -> &mut Snapshot {
|
||||
debug_assert!(thread_state::get().is_layout());
|
||||
if self.is_none() {
|
||||
self.snapshot = Some(create());
|
||||
self.destroyed = false;
|
||||
}
|
||||
|
||||
self.snapshot.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SnapshotOption {
|
||||
type Target = Option<Snapshot>;
|
||||
fn deref(&self) -> &Option<Snapshot> {
|
||||
if self.destroyed {
|
||||
&NO_SNAPSHOT
|
||||
} else {
|
||||
&self.snapshot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Transient data used by the restyle algorithm. This structure is instantiated
|
||||
/// either before or during restyle traversal, and is cleared at the end of node
|
||||
/// processing.
|
||||
|
@ -350,54 +297,17 @@ pub struct RestyleData {
|
|||
/// for Servo for now.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub damage_handled: RestyleDamage,
|
||||
|
||||
/// An optional snapshot of the original state and attributes of the element,
|
||||
/// from which we may compute additional restyle hints at traversal time.
|
||||
pub snapshot: SnapshotOption,
|
||||
}
|
||||
|
||||
impl RestyleData {
|
||||
/// Computes the final restyle hint for this element.
|
||||
///
|
||||
/// This expands the snapshot (if any) into a restyle hint, and handles
|
||||
/// explicit sibling restyle hints from the stored restyle hint.
|
||||
///
|
||||
/// Returns true if later siblings must be restyled.
|
||||
pub fn compute_final_hint<E: TElement>(&mut self,
|
||||
element: E,
|
||||
stylist: &Stylist)
|
||||
-> bool {
|
||||
let mut hint = self.hint.0;
|
||||
|
||||
if let Some(snapshot) = self.snapshot.as_ref() {
|
||||
hint |= stylist.compute_restyle_hint(&element, snapshot);
|
||||
}
|
||||
|
||||
// If the hint includes a directive for later siblings, strip it out and
|
||||
// notify the caller to modify the base hint for future siblings.
|
||||
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
|
||||
hint.remove(RESTYLE_LATER_SIBLINGS);
|
||||
|
||||
// Insert the hint, overriding the previous hint. This effectively takes
|
||||
// care of removing the later siblings restyle hint.
|
||||
self.hint = hint.into();
|
||||
|
||||
// Destroy the snapshot.
|
||||
self.snapshot.destroy();
|
||||
|
||||
later_siblings
|
||||
}
|
||||
|
||||
/// Returns true if this RestyleData might invalidate the current style.
|
||||
pub fn has_invalidations(&self) -> bool {
|
||||
self.hint.has_self_invalidations() ||
|
||||
self.recascade ||
|
||||
self.snapshot.is_some()
|
||||
self.hint.has_self_invalidations() || self.recascade
|
||||
}
|
||||
|
||||
/// Returns true if this RestyleData might invalidate sibling styles.
|
||||
pub fn has_sibling_invalidations(&self) -> bool {
|
||||
self.hint.has_sibling_invalidations() || self.snapshot.is_some()
|
||||
self.hint.has_sibling_invalidations()
|
||||
}
|
||||
|
||||
/// Returns damage handled.
|
||||
|
@ -452,6 +362,51 @@ pub enum RestyleKind {
|
|||
}
|
||||
|
||||
impl ElementData {
|
||||
/// Computes the final restyle hint for this element, potentially allocating
|
||||
/// a `RestyleData` if we need to.
|
||||
///
|
||||
/// This expands the snapshot (if any) into a restyle hint, and handles
|
||||
/// explicit sibling restyle hints from the stored restyle hint.
|
||||
///
|
||||
/// Returns true if later siblings must be restyled.
|
||||
pub fn compute_final_hint<E: TElement>(
|
||||
&mut self,
|
||||
element: E,
|
||||
context: &SharedStyleContext)
|
||||
-> bool
|
||||
{
|
||||
debug!("compute_final_hint: {:?}, {:?}",
|
||||
element,
|
||||
context.traversal_flags);
|
||||
|
||||
let mut hint = match self.get_restyle() {
|
||||
Some(r) => r.hint.0,
|
||||
None => RestyleHint::empty(),
|
||||
};
|
||||
|
||||
if element.has_snapshot() && !element.handled_snapshot() {
|
||||
hint |= context.stylist.compute_restyle_hint(&element, context.snapshot_map);
|
||||
unsafe { element.set_handled_snapshot() }
|
||||
debug_assert!(element.handled_snapshot());
|
||||
}
|
||||
|
||||
let empty_hint = hint.is_empty();
|
||||
|
||||
// If the hint includes a directive for later siblings, strip it out and
|
||||
// notify the caller to modify the base hint for future siblings.
|
||||
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
|
||||
hint.remove(RESTYLE_LATER_SIBLINGS);
|
||||
|
||||
// Insert the hint, overriding the previous hint. This effectively takes
|
||||
// care of removing the later siblings restyle hint.
|
||||
if !empty_hint {
|
||||
self.ensure_restyle().hint = hint.into();
|
||||
}
|
||||
|
||||
later_siblings
|
||||
}
|
||||
|
||||
|
||||
/// Trivially construct an ElementData.
|
||||
pub fn new(existing: Option<ElementStyles>) -> Self {
|
||||
ElementData {
|
||||
|
@ -460,22 +415,21 @@ impl ElementData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if this element has a computed styled.
|
||||
/// Returns true if this element has a computed style.
|
||||
pub fn has_styles(&self) -> bool {
|
||||
self.styles.is_some()
|
||||
}
|
||||
|
||||
/// Returns true if this element's style is up-to-date and has no potential
|
||||
/// invalidation.
|
||||
pub fn has_current_styles(&self) -> bool {
|
||||
self.has_styles() &&
|
||||
self.restyle.as_ref().map_or(true, |r| !r.has_invalidations())
|
||||
/// Returns whether we have any outstanding style invalidation.
|
||||
pub fn has_invalidations(&self) -> bool {
|
||||
self.restyle.as_ref().map_or(false, |r| r.has_invalidations())
|
||||
}
|
||||
|
||||
/// Returns the kind of restyling that we're going to need to do on this
|
||||
/// element, based of the stored restyle hint.
|
||||
pub fn restyle_kind(&self) -> RestyleKind {
|
||||
debug_assert!(!self.has_current_styles(), "Should've stopped earlier");
|
||||
debug_assert!(!self.has_styles() || self.has_invalidations(),
|
||||
"Should've stopped earlier");
|
||||
if !self.has_styles() {
|
||||
return RestyleKind::MatchAndCascade;
|
||||
}
|
||||
|
@ -527,8 +481,6 @@ impl ElementData {
|
|||
|
||||
/// Sets the computed element styles.
|
||||
pub fn set_styles(&mut self, styles: ElementStyles) {
|
||||
debug_assert!(self.get_restyle().map_or(true, |r| r.snapshot.is_none()),
|
||||
"Traversal should have expanded snapshots");
|
||||
self.styles = Some(styles);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue