style: Recascade the document when rem units are used and the root font-size changes.

This commit is contained in:
Cameron McCormack 2017-06-03 13:18:43 +08:00
parent 7b61d55421
commit 19b61dfc08
6 changed files with 76 additions and 21 deletions

View file

@ -9,7 +9,7 @@ use context::SharedStyleContext;
use dom::TElement;
use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
use properties::longhands::display::computed_value as display;
use restyle_hints::{HintComputationContext, RestyleReplacements, RestyleHint};
use restyle_hints::{CascadeHint, HintComputationContext, RestyleReplacements, RestyleHint};
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
use selectors::matching::VisitedHandlingMode;
@ -414,6 +414,11 @@ impl StoredRestyleHint {
pub fn has_recascade_self(&self) -> bool {
self.0.has_recascade_self()
}
/// Insert the specified `CascadeHint`.
pub fn insert_cascade_hint(&mut self, cascade_hint: CascadeHint) {
self.0.insert_cascade_hint(cascade_hint);
}
}
impl Default for StoredRestyleHint {

View file

@ -20,7 +20,7 @@ use parser::ParserContext;
use properties::{ComputedValues, StyleBuilder};
use properties::longhands::font_size;
use std::fmt::{self, Write};
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use str::starts_with_ignore_ascii_case;
use string_cache::Atom;
use style_traits::ToCss;
@ -47,6 +47,9 @@ pub struct Device {
/// the parent to compute everything else. So it is correct to just use
/// a relaxed atomic here.
root_font_size: AtomicIsize,
/// Whether any styles computed in the document relied on the root font-size
/// by using rem units.
used_root_font_size: AtomicBool,
}
unsafe impl Sync for Device {}
@ -61,6 +64,7 @@ impl Device {
default_values: ComputedValues::default_values(unsafe { &*pres_context }),
viewport_override: None,
root_font_size: AtomicIsize::new(font_size::get_initial_value().0 as isize), // FIXME(bz): Seems dubious?
used_root_font_size: AtomicBool::new(false),
}
}
@ -91,6 +95,7 @@ impl Device {
/// Get the font size of the root element (for rem)
pub fn root_font_size(&self) -> Au {
self.used_root_font_size.store(true, Ordering::Relaxed);
Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
}
@ -104,6 +109,12 @@ impl Device {
// NB: A following stylesheet flush will populate this if appropriate.
self.viewport_override = None;
self.default_values = ComputedValues::default_values(unsafe { &*self.pres_context });
self.used_root_font_size.store(false, Ordering::Relaxed);
}
/// Returns whether we ever looked up the root font size of the Device.
pub fn used_root_font_size(&self) -> bool {
self.used_root_font_size.load(Ordering::Relaxed)
}
/// Recreates all the temporary state that the `Device` stores.

View file

@ -78,17 +78,21 @@ pub enum ChildCascadeRequirement {
///
/// FIXME(heycam) Although this is "must" cascade, in the future we should
/// track whether child elements rely specifically on inheriting particular
/// property values. When we do that, we can treat `MustCascade` as "must
/// cascade unless we know that changes to these properties can be
/// property values. When we do that, we can treat `MustCascadeChildren` as
/// "must cascade unless we know that changes to these properties can be
/// ignored".
MustCascade,
MustCascadeChildren,
/// The same as `MustCascadeChildren`, but for the entire subtree. This is
/// used to handle root font-size updates needing to recascade the whole
/// document.
MustCascadeDescendants,
}
impl From<StyleChange> for ChildCascadeRequirement {
fn from(change: StyleChange) -> ChildCascadeRequirement {
match change {
StyleChange::Unchanged => ChildCascadeRequirement::CanSkipCascade,
StyleChange::Changed => ChildCascadeRequirement::MustCascade,
StyleChange::Changed => ChildCascadeRequirement::MustCascadeChildren,
}
}
}
@ -447,6 +451,22 @@ trait PrivateMatchMethods: TElement {
old_values.as_ref().map(|v| v.as_ref()),
&new_values,
None);
// Handle root font-size changes.
if self.is_root() && !self.is_native_anonymous() {
// The new root font-size has already been updated on the Device
// in properties::apply_declarations.
let device = context.shared.stylist.device();
let new_font_size = new_values.get_font().clone_font_size();
// If the root font-size changed since last time, and something
// in the document did use rem units, ensure we recascade the
// entire tree.
if old_values.map_or(false, |v| v.get_font().clone_font_size() != new_font_size) &&
device.used_root_font_size() {
child_cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants;
}
}
}
// Set the new computed values.
@ -664,7 +684,7 @@ trait PrivateMatchMethods: TElement {
-> ChildCascadeRequirement {
// Don't accumulate damage if we're in a restyle for reconstruction.
if shared_context.traversal_flags.for_reconstruct() {
return ChildCascadeRequirement::MustCascade;
return ChildCascadeRequirement::MustCascadeChildren;
}
// If an ancestor is already getting reconstructed by Gecko's top-down
@ -1220,12 +1240,12 @@ pub trait MatchMethods : TElement {
-> ChildCascadeRequirement {
let restyle = match restyle {
Some(r) => r,
None => return ChildCascadeRequirement::MustCascade,
None => return ChildCascadeRequirement::MustCascadeChildren,
};
let old_values = match old_values {
Some(v) => v,
None => return ChildCascadeRequirement::MustCascade,
None => return ChildCascadeRequirement::MustCascadeChildren,
};
// ::before and ::after are element-backed in Gecko, so they do the

View file

@ -446,6 +446,12 @@ impl RestyleHint {
self.insert_from(&other)
}
/// Inserts the specified `CascadeHint`.
#[inline]
pub fn insert_cascade_hint(&mut self, cascade_hint: CascadeHint) {
self.recascade.insert(cascade_hint);
}
/// Returns whether this `RestyleHint` represents at least as much restyle
/// work as the specified one.
#[inline]

View file

@ -14,7 +14,7 @@ use parser::ParserContext;
use properties::{ComputedValues, StyleBuilder};
use properties::longhands::font_size;
use std::fmt;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use style_traits::{CSSPixel, ToCss};
use style_traits::viewport::ViewportConstraints;
use values::computed::{self, ToComputedValue};
@ -41,6 +41,10 @@ pub struct Device {
/// a relaxed atomic here.
#[ignore_heap_size_of = "Pure stack type"]
root_font_size: AtomicIsize,
/// Whether any styles computed in the document relied on the root font-size
/// by using rem units.
#[ignore_heap_size_of = "Pure stack type"]
used_root_font_size: AtomicBool,
}
impl Device {
@ -52,6 +56,7 @@ impl Device {
media_type: media_type,
viewport_size: viewport_size,
root_font_size: AtomicIsize::new(font_size::get_initial_value().0 as isize), // FIXME(bz): Seems dubious?
used_root_font_size: AtomicBool::new(false),
}
}
@ -65,6 +70,7 @@ impl Device {
/// Get the font size of the root element (for rem)
pub fn root_font_size(&self) -> Au {
self.used_root_font_size.store(true, Ordering::Relaxed);
Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
}
@ -73,6 +79,11 @@ impl Device {
self.root_font_size.store(size.0 as isize, Ordering::Relaxed)
}
/// Returns whether we ever looked up the root font size of the Device.
pub fn used_root_font_size(&self) -> bool {
self.used_root_font_size.load(Ordering::Relaxed)
}
/// Returns the viewport size of the current device in app units, needed,
/// among other things, to resolve viewport units.
#[inline]

View file

@ -9,7 +9,8 @@ use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
use data::{ElementData, ElementStyles, StoredRestyleHint};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
use matching::{ChildCascadeRequirement, MatchMethods};
use restyle_hints::{HintComputationContext, RestyleHint};
use restyle_hints::{CascadeHint, HintComputationContext, RECASCADE_SELF};
use restyle_hints::{RECASCADE_DESCENDANTS, RestyleHint};
use selector_parser::RestyleDamage;
use sharing::{StyleSharingBehavior, StyleSharingTarget};
#[cfg(feature = "servo")] use servo_config::opts;
@ -672,7 +673,7 @@ pub fn recalc_style_at<E, D>(traversal: &D,
}), "Should've computed the final hint and handled later_siblings already");
let compute_self = !element.has_current_styles(data);
let mut inherited_style_changed = false;
let mut cascade_hint = CascadeHint::empty();
debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})",
element, compute_self, element.has_dirty_descendants(), data);
@ -680,8 +681,11 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// Compute style for this element if necessary.
if compute_self {
match compute_style(traversal, traversal_data, context, element, data) {
ChildCascadeRequirement::MustCascade => {
inherited_style_changed = true;
ChildCascadeRequirement::MustCascadeChildren => {
cascade_hint |= RECASCADE_SELF;
}
ChildCascadeRequirement::MustCascadeDescendants => {
cascade_hint |= RECASCADE_SELF | RECASCADE_DESCENDANTS;
}
ChildCascadeRequirement::CanSkipCascade => {}
};
@ -708,15 +712,13 @@ pub fn recalc_style_at<E, D>(traversal: &D,
},
};
if inherited_style_changed {
// FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
propagated_hint.insert(StoredRestyleHint::recascade_self());
}
// FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
propagated_hint.insert_cascade_hint(cascade_hint);
trace!("propagated_hint={:?}, inherited_style_changed={:?}, \
trace!("propagated_hint={:?}, cascade_hint={:?}, \
is_display_none={:?}, implementing_pseudo={:?}",
propagated_hint, inherited_style_changed,
propagated_hint, cascade_hint,
data.styles().is_display_none(),
element.implemented_pseudo_element());
debug_assert!(element.has_current_styles(data) ||