mirror of
https://github.com/servo/servo.git
synced 2025-09-20 11:50:09 +01:00
Auto merge of #16967 - emilio:after, r=heycam,emilio
Bug 1366144: Correctly diff ::before and ::after pseudo-element styles if there's no generated content. r=heycam <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16967) <!-- Reviewable:end -->
This commit is contained in:
commit
05a26a2996
15 changed files with 692 additions and 227 deletions
|
@ -160,7 +160,9 @@ pub fn recalc_style_for_animations(context: &LayoutContext,
|
||||||
animation,
|
animation,
|
||||||
&mut fragment.style,
|
&mut fragment.style,
|
||||||
&ServoMetricsProvider);
|
&ServoMetricsProvider);
|
||||||
damage |= RestyleDamage::compute(&old_style, &fragment.style);
|
let difference =
|
||||||
|
RestyleDamage::compute_style_difference(&old_style, &fragment.style);
|
||||||
|
damage |= difference.damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1100,7 +1100,7 @@ impl LayoutThread {
|
||||||
let el = node.as_element().unwrap();
|
let el = node.as_element().unwrap();
|
||||||
if let Some(mut d) = element.mutate_data() {
|
if let Some(mut d) = element.mutate_data() {
|
||||||
if d.has_styles() {
|
if d.has_styles() {
|
||||||
d.ensure_restyle().hint.insert(&StoredRestyleHint::subtree());
|
d.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(p) = el.parent_element() {
|
if let Some(p) = el.parent_element() {
|
||||||
|
@ -1136,7 +1136,7 @@ impl LayoutThread {
|
||||||
if needs_dirtying {
|
if needs_dirtying {
|
||||||
if let Some(mut d) = element.mutate_data() {
|
if let Some(mut d) = element.mutate_data() {
|
||||||
if d.has_styles() {
|
if d.has_styles() {
|
||||||
d.ensure_restyle().hint.insert(&StoredRestyleHint::subtree());
|
d.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1184,7 +1184,7 @@ impl LayoutThread {
|
||||||
let mut restyle_data = style_data.ensure_restyle();
|
let mut restyle_data = style_data.ensure_restyle();
|
||||||
|
|
||||||
// Stash the data on the element for processing by the style system.
|
// Stash the data on the element for processing by the style system.
|
||||||
restyle_data.hint.insert(&restyle.hint.into());
|
restyle_data.hint.insert(restyle.hint.into());
|
||||||
restyle_data.damage = restyle.damage;
|
restyle_data.damage = restyle.damage;
|
||||||
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
|
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ use std::rc::Rc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use style::attr::AttrValue;
|
use style::attr::AttrValue;
|
||||||
use style::context::{QuirksMode, ReflowGoal};
|
use style::context::{QuirksMode, ReflowGoal};
|
||||||
use style::restyle_hints::{RestyleHint, RESTYLE_SELF, RESTYLE_STYLE_ATTRIBUTE};
|
use style::restyle_hints::{RestyleHint, RestyleReplacements, RESTYLE_STYLE_ATTRIBUTE};
|
||||||
use style::selector_parser::{RestyleDamage, Snapshot};
|
use style::selector_parser::{RestyleDamage, Snapshot};
|
||||||
use style::shared_lock::SharedRwLock as StyleSharedRwLock;
|
use style::shared_lock::SharedRwLock as StyleSharedRwLock;
|
||||||
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
|
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
|
||||||
|
@ -2361,14 +2361,14 @@ impl Document {
|
||||||
entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document()));
|
entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document()));
|
||||||
}
|
}
|
||||||
if attr.local_name() == &local_name!("style") {
|
if attr.local_name() == &local_name!("style") {
|
||||||
entry.hint |= RESTYLE_STYLE_ATTRIBUTE;
|
entry.hint.insert(RestyleHint::for_replacements(RESTYLE_STYLE_ATTRIBUTE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(emilio): This should become something like
|
// FIXME(emilio): This should become something like
|
||||||
// element.is_attribute_mapped(attr.local_name()).
|
// element.is_attribute_mapped(attr.local_name()).
|
||||||
if attr.local_name() == &local_name!("width") ||
|
if attr.local_name() == &local_name!("width") ||
|
||||||
attr.local_name() == &local_name!("height") {
|
attr.local_name() == &local_name!("height") {
|
||||||
entry.hint |= RESTYLE_SELF;
|
entry.hint.insert(RestyleHint::for_self());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut snapshot = entry.snapshot.as_mut().unwrap();
|
let mut snapshot = entry.snapshot.as_mut().unwrap();
|
||||||
|
|
|
@ -102,7 +102,7 @@ use style::context::{QuirksMode, ReflowGoal};
|
||||||
use style::element_state::*;
|
use style::element_state::*;
|
||||||
use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
|
use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
|
||||||
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
|
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
|
||||||
use style::restyle_hints::RESTYLE_SELF;
|
use style::restyle_hints::RestyleHint;
|
||||||
use style::rule_tree::CascadeLevel;
|
use style::rule_tree::CascadeLevel;
|
||||||
use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
|
use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
|
||||||
use style::shared_lock::{SharedRwLock, Locked};
|
use style::shared_lock::{SharedRwLock, Locked};
|
||||||
|
@ -245,7 +245,7 @@ impl Element {
|
||||||
|
|
||||||
// FIXME(bholley): I think we should probably only do this for
|
// FIXME(bholley): I think we should probably only do this for
|
||||||
// NodeStyleDamaged, but I'm preserving existing behavior.
|
// NodeStyleDamaged, but I'm preserving existing behavior.
|
||||||
restyle.hint |= RESTYLE_SELF;
|
restyle.hint.insert(RestyleHint::for_self());
|
||||||
|
|
||||||
if damage == NodeDamage::OtherNodeDamage {
|
if damage == NodeDamage::OtherNodeDamage {
|
||||||
restyle.damage = RestyleDamage::rebuild_and_reflow();
|
restyle.damage = RestyleDamage::rebuild_and_reflow();
|
||||||
|
|
|
@ -10,7 +10,7 @@ use context::SharedStyleContext;
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
|
use restyle_hints::{RestyleReplacements, RestyleHint};
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
|
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
|
||||||
use shared_lock::StylesheetGuards;
|
use shared_lock::StylesheetGuards;
|
||||||
|
@ -198,21 +198,18 @@ impl StoredRestyleHint {
|
||||||
// In the middle of an animation only restyle, we don't need to
|
// In the middle of an animation only restyle, we don't need to
|
||||||
// propagate any restyle hints, and we need to remove ourselves.
|
// propagate any restyle hints, and we need to remove ourselves.
|
||||||
if traversal_flags.for_animation_only() {
|
if traversal_flags.for_animation_only() {
|
||||||
self.0.remove(RestyleHint::for_animations());
|
self.0.remove_animation_hints();
|
||||||
return Self::empty();
|
return Self::empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(!self.0.intersects(RestyleHint::for_animations()),
|
debug_assert!(!self.0.has_animation_hint(),
|
||||||
"There should not be any animation restyle hints \
|
"There should not be any animation restyle hints \
|
||||||
during normal traversal");
|
during normal traversal");
|
||||||
|
|
||||||
// Else we should clear ourselves, and return the propagated hint.
|
// Else we should clear ourselves, and return the propagated hint.
|
||||||
let hint = mem::replace(&mut self.0, RestyleHint::empty());
|
let new_hint = mem::replace(&mut self.0, RestyleHint::empty())
|
||||||
StoredRestyleHint(if hint.contains(RESTYLE_DESCENDANTS) {
|
.propagate_for_non_animation_restyle();
|
||||||
RESTYLE_SELF | RESTYLE_DESCENDANTS
|
StoredRestyleHint(new_hint)
|
||||||
} else {
|
|
||||||
RestyleHint::empty()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty `StoredRestyleHint`.
|
/// Creates an empty `StoredRestyleHint`.
|
||||||
|
@ -223,25 +220,25 @@ impl StoredRestyleHint {
|
||||||
/// Creates a restyle hint that forces the whole subtree to be restyled,
|
/// Creates a restyle hint that forces the whole subtree to be restyled,
|
||||||
/// including the element.
|
/// including the element.
|
||||||
pub fn subtree() -> Self {
|
pub fn subtree() -> Self {
|
||||||
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS)
|
StoredRestyleHint(RestyleHint::subtree())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a restyle hint that forces the element and all its later
|
/// Creates a restyle hint that forces the element and all its later
|
||||||
/// siblings to have their whole subtrees restyled, including the elements
|
/// siblings to have their whole subtrees restyled, including the elements
|
||||||
/// themselves.
|
/// themselves.
|
||||||
pub fn subtree_and_later_siblings() -> Self {
|
pub fn subtree_and_later_siblings() -> Self {
|
||||||
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS | RESTYLE_LATER_SIBLINGS)
|
StoredRestyleHint(RestyleHint::subtree_and_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.affects_self()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the hint indicates that our sibling's style may be
|
/// Returns true if the hint indicates that our sibling's style may be
|
||||||
/// invalidated.
|
/// invalidated.
|
||||||
pub fn has_sibling_invalidations(&self) -> bool {
|
pub fn has_sibling_invalidations(&self) -> bool {
|
||||||
self.0.intersects(RESTYLE_LATER_SIBLINGS)
|
self.0.affects_later_siblings()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the restyle hint is empty (nothing requires to be restyled).
|
/// Whether the restyle hint is empty (nothing requires to be restyled).
|
||||||
|
@ -250,13 +247,18 @@ impl StoredRestyleHint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert another restyle hint, effectively resulting in the union of both.
|
/// Insert another restyle hint, effectively resulting in the union of both.
|
||||||
pub fn insert(&mut self, other: &Self) {
|
pub fn insert(&mut self, other: Self) {
|
||||||
self.0 |= other.0
|
self.0.insert(other.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert another restyle hint, effectively resulting in the union of both.
|
||||||
|
pub fn insert_from(&mut self, other: &Self) {
|
||||||
|
self.0.insert_from(&other.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the hint has animation-only restyle.
|
/// Returns true if the hint has animation-only restyle.
|
||||||
pub fn has_animation_hint(&self) -> bool {
|
pub fn has_animation_hint(&self) -> bool {
|
||||||
self.0.intersects(RestyleHint::for_animations())
|
self.0.has_animation_hint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +358,7 @@ pub enum RestyleKind {
|
||||||
MatchAndCascade,
|
MatchAndCascade,
|
||||||
/// We need to recascade with some replacement rule, such as the style
|
/// We need to recascade with some replacement rule, such as the style
|
||||||
/// attribute, or animation rules.
|
/// attribute, or animation rules.
|
||||||
CascadeWithReplacements(RestyleHint),
|
CascadeWithReplacements(RestyleReplacements),
|
||||||
/// We only need to recascade, for example, because only inherited
|
/// We only need to recascade, for example, because only inherited
|
||||||
/// properties in the parent changed.
|
/// properties in the parent changed.
|
||||||
CascadeOnly,
|
CascadeOnly,
|
||||||
|
@ -381,7 +383,7 @@ impl ElementData {
|
||||||
context.traversal_flags);
|
context.traversal_flags);
|
||||||
|
|
||||||
let mut hint = match self.get_restyle() {
|
let mut hint = match self.get_restyle() {
|
||||||
Some(r) => r.hint.0,
|
Some(r) => r.hint.0.clone(),
|
||||||
None => RestyleHint::empty(),
|
None => RestyleHint::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -393,7 +395,7 @@ impl ElementData {
|
||||||
element.implemented_pseudo_element());
|
element.implemented_pseudo_element());
|
||||||
|
|
||||||
if element.has_snapshot() && !element.handled_snapshot() {
|
if element.has_snapshot() && !element.handled_snapshot() {
|
||||||
hint |= context.stylist.compute_restyle_hint(&element, context.snapshot_map);
|
hint.insert(context.stylist.compute_restyle_hint(&element, context.snapshot_map));
|
||||||
unsafe { element.set_handled_snapshot() }
|
unsafe { element.set_handled_snapshot() }
|
||||||
debug_assert!(element.handled_snapshot());
|
debug_assert!(element.handled_snapshot());
|
||||||
}
|
}
|
||||||
|
@ -402,8 +404,7 @@ impl ElementData {
|
||||||
|
|
||||||
// If the hint includes a directive for later siblings, strip it out and
|
// If the hint includes a directive for later siblings, strip it out and
|
||||||
// notify the caller to modify the base hint for future siblings.
|
// notify the caller to modify the base hint for future siblings.
|
||||||
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
|
let later_siblings = hint.remove_later_siblings_hint();
|
||||||
hint.remove(RESTYLE_LATER_SIBLINGS);
|
|
||||||
|
|
||||||
// Insert the hint, overriding the previous hint. This effectively takes
|
// Insert the hint, overriding the previous hint. This effectively takes
|
||||||
// care of removing the later siblings restyle hint.
|
// care of removing the later siblings restyle hint.
|
||||||
|
@ -445,13 +446,13 @@ impl ElementData {
|
||||||
debug_assert!(self.restyle.is_some());
|
debug_assert!(self.restyle.is_some());
|
||||||
let restyle_data = self.restyle.as_ref().unwrap();
|
let restyle_data = self.restyle.as_ref().unwrap();
|
||||||
|
|
||||||
let hint = restyle_data.hint.0;
|
let hint = &restyle_data.hint.0;
|
||||||
if hint.contains(RESTYLE_SELF) {
|
if hint.match_self() {
|
||||||
return RestyleKind::MatchAndCascade;
|
return RestyleKind::MatchAndCascade;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hint.is_empty() {
|
if !hint.is_empty() {
|
||||||
return RestyleKind::CascadeWithReplacements(hint);
|
return RestyleKind::CascadeWithReplacements(hint.replacements);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(restyle_data.recascade,
|
debug_assert!(restyle_data.recascade,
|
||||||
|
|
|
@ -961,7 +961,8 @@ extern "C" {
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
pub fn Gecko_CalcStyleDifference(oldstyle: *mut nsStyleContext,
|
||||||
newstyle: ServoComputedValuesBorrowed)
|
newstyle: ServoComputedValuesBorrowed,
|
||||||
|
any_style_changed: *mut bool)
|
||||||
-> nsChangeHint;
|
-> nsChangeHint;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use gecko_bindings::bindings;
|
||||||
use gecko_bindings::structs;
|
use gecko_bindings::structs;
|
||||||
use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
|
use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
|
||||||
use gecko_bindings::sugar::ownership::FFIArcHelpers;
|
use gecko_bindings::sugar::ownership::FFIArcHelpers;
|
||||||
|
use matching::{StyleChange, StyleDifference};
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use std::ops::{BitAnd, BitOr, BitOrAssign, Not};
|
use std::ops::{BitAnd, BitOr, BitOrAssign, Not};
|
||||||
use stylearc::Arc;
|
use stylearc::Arc;
|
||||||
|
@ -38,22 +39,27 @@ impl GeckoRestyleDamage {
|
||||||
self.0 == nsChangeHint(0)
|
self.0 == nsChangeHint(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a change hint given an old style (in the form of a
|
/// Computes the `StyleDifference` (including the appropriate change hint)
|
||||||
/// `nsStyleContext`, and a new style (in the form of `ComputedValues`).
|
/// given an old style (in the form of a `nsStyleContext`, and a new style
|
||||||
|
/// (in the form of `ComputedValues`).
|
||||||
///
|
///
|
||||||
/// Note that we could in theory just get two `ComputedValues` here and diff
|
/// Note that we could in theory just get two `ComputedValues` here and diff
|
||||||
/// them, but Gecko has an interesting optimization when they mark accessed
|
/// them, but Gecko has an interesting optimization when they mark accessed
|
||||||
/// structs, so they effectively only diff structs that have ever been
|
/// structs, so they effectively only diff structs that have ever been
|
||||||
/// accessed from layout.
|
/// accessed from layout.
|
||||||
pub fn compute(source: &nsStyleContext,
|
pub fn compute_style_difference(source: &nsStyleContext,
|
||||||
new_style: &Arc<ComputedValues>) -> Self {
|
new_style: &Arc<ComputedValues>)
|
||||||
|
-> StyleDifference {
|
||||||
// TODO(emilio): Const-ify this?
|
// TODO(emilio): Const-ify this?
|
||||||
let context = source as *const nsStyleContext as *mut nsStyleContext;
|
let context = source as *const nsStyleContext as *mut nsStyleContext;
|
||||||
|
let mut any_style_changed: bool = false;
|
||||||
let hint = unsafe {
|
let hint = unsafe {
|
||||||
bindings::Gecko_CalcStyleDifference(context,
|
bindings::Gecko_CalcStyleDifference(context,
|
||||||
new_style.as_borrowed_opt().unwrap())
|
new_style.as_borrowed_opt().unwrap(),
|
||||||
|
&mut any_style_changed)
|
||||||
};
|
};
|
||||||
GeckoRestyleDamage(hint)
|
let change = if any_style_changed { StyleChange::Changed } else { StyleChange::Unchanged };
|
||||||
|
StyleDifference::new(GeckoRestyleDamage(hint), change)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this restyle damage contains all the damage of |other|.
|
/// Returns true if this restyle damage contains all the damage of |other|.
|
||||||
|
|
|
@ -16,9 +16,10 @@ use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleCon
|
||||||
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
||||||
use dom::{AnimationRules, SendElement, TElement, TNode};
|
use dom::{AnimationRules, SendElement, TElement, TNode};
|
||||||
use font_metrics::FontMetricsProvider;
|
use font_metrics::FontMetricsProvider;
|
||||||
|
use log::LogLevel::Trace;
|
||||||
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint};
|
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleReplacements};
|
||||||
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
|
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
|
||||||
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
|
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
|
||||||
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
|
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
|
||||||
|
@ -51,6 +52,34 @@ fn relations_are_shareable(relations: &StyleRelations) -> bool {
|
||||||
AFFECTED_BY_PRESENTATIONAL_HINTS)
|
AFFECTED_BY_PRESENTATIONAL_HINTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the result of comparing an element's old and new style.
|
||||||
|
pub struct StyleDifference {
|
||||||
|
/// The resulting damage.
|
||||||
|
pub damage: RestyleDamage,
|
||||||
|
|
||||||
|
/// Whether any styles changed.
|
||||||
|
pub change: StyleChange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyleDifference {
|
||||||
|
/// Creates a new `StyleDifference`.
|
||||||
|
pub fn new(damage: RestyleDamage, change: StyleChange) -> Self {
|
||||||
|
StyleDifference {
|
||||||
|
change: change,
|
||||||
|
damage: damage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents whether or not the style of an element has changed.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum StyleChange {
|
||||||
|
/// The style hasn't changed.
|
||||||
|
Unchanged,
|
||||||
|
/// The style has changed.
|
||||||
|
Changed,
|
||||||
|
}
|
||||||
|
|
||||||
/// Information regarding a style sharing candidate.
|
/// Information regarding a style sharing candidate.
|
||||||
///
|
///
|
||||||
/// Note that this information is stored in TLS and cleared after the traversal,
|
/// Note that this information is stored in TLS and cleared after the traversal,
|
||||||
|
@ -375,8 +404,37 @@ pub enum StyleSharingResult {
|
||||||
/// We didn't find anybody to share the style with.
|
/// We didn't find anybody to share the style with.
|
||||||
CannotShare,
|
CannotShare,
|
||||||
/// The node's style can be shared. The integer specifies the index in the
|
/// 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.
|
/// LRU cache that was hit and the damage that was done. The
|
||||||
StyleWasShared(usize),
|
/// `ChildCascadeRequirement` indicates whether style changes due to using
|
||||||
|
/// the shared style mean we need to recascade to children.
|
||||||
|
StyleWasShared(usize, ChildCascadeRequirement),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether or not newly computed values for an element need to be cascade
|
||||||
|
/// to children.
|
||||||
|
pub enum ChildCascadeRequirement {
|
||||||
|
/// Old and new computed values were the same, or we otherwise know that
|
||||||
|
/// we won't bother recomputing style for children, so we can skip cascading
|
||||||
|
/// the new values into child elements.
|
||||||
|
CanSkipCascade,
|
||||||
|
/// Old and new computed values were different, so we must cascade the
|
||||||
|
/// new values to children.
|
||||||
|
///
|
||||||
|
/// 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
|
||||||
|
/// ignored".
|
||||||
|
MustCascade,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StyleChange> for ChildCascadeRequirement {
|
||||||
|
fn from(change: StyleChange) -> ChildCascadeRequirement {
|
||||||
|
match change {
|
||||||
|
StyleChange::Unchanged => ChildCascadeRequirement::CanSkipCascade,
|
||||||
|
StyleChange::Changed => ChildCascadeRequirement::MustCascade,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result status for match primary rules.
|
/// The result status for match primary rules.
|
||||||
|
@ -570,7 +628,8 @@ trait PrivateMatchMethods: TElement {
|
||||||
fn cascade_primary(&self,
|
fn cascade_primary(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
important_rules_changed: bool) {
|
important_rules_changed: bool)
|
||||||
|
-> ChildCascadeRequirement {
|
||||||
// Collect some values.
|
// Collect some values.
|
||||||
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||||
let mut primary_style = &mut styles.primary;
|
let mut primary_style = &mut styles.primary;
|
||||||
|
@ -589,16 +648,19 @@ trait PrivateMatchMethods: TElement {
|
||||||
important_rules_changed);
|
important_rules_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(old) = old_values {
|
let child_cascade_requirement =
|
||||||
self.accumulate_damage(&context.shared,
|
self.accumulate_damage(&context.shared,
|
||||||
restyle.unwrap(),
|
restyle,
|
||||||
&old,
|
old_values.as_ref().map(|v| v.as_ref()),
|
||||||
&new_values,
|
&new_values,
|
||||||
None);
|
None);
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new computed values.
|
// Set the new computed values.
|
||||||
primary_style.values = Some(new_values);
|
primary_style.values = Some(new_values);
|
||||||
|
|
||||||
|
// Return whether the damage indicates we must cascade new inherited
|
||||||
|
// values into children.
|
||||||
|
child_cascade_requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cascade_eager_pseudo(&self,
|
fn cascade_eager_pseudo(&self,
|
||||||
|
@ -613,17 +675,11 @@ trait PrivateMatchMethods: TElement {
|
||||||
let new_values =
|
let new_values =
|
||||||
self.cascade_internal(context, &styles.primary, Some(pseudo_style));
|
self.cascade_internal(context, &styles.primary, Some(pseudo_style));
|
||||||
|
|
||||||
if let Some(old) = old_values {
|
|
||||||
// ::before and ::after are element-backed in Gecko, so they do
|
|
||||||
// the damage calculation for themselves.
|
|
||||||
if cfg!(feature = "servo") || !pseudo.is_before_or_after() {
|
|
||||||
self.accumulate_damage(&context.shared,
|
self.accumulate_damage(&context.shared,
|
||||||
restyle.unwrap(),
|
restyle,
|
||||||
&old,
|
old_values.as_ref().map(|v| &**v),
|
||||||
&new_values,
|
&new_values,
|
||||||
Some(pseudo));
|
Some(pseudo));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pseudo_style.values = Some(new_values)
|
pseudo_style.values = Some(new_values)
|
||||||
}
|
}
|
||||||
|
@ -782,54 +838,89 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes and applies restyle damage.
|
||||||
|
fn accumulate_damage(&self,
|
||||||
|
shared_context: &SharedStyleContext,
|
||||||
|
restyle: Option<&mut RestyleData>,
|
||||||
|
old_values: Option<&ComputedValues>,
|
||||||
|
new_values: &Arc<ComputedValues>,
|
||||||
|
pseudo: Option<&PseudoElement>)
|
||||||
|
-> ChildCascadeRequirement {
|
||||||
|
let restyle = match restyle {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return ChildCascadeRequirement::MustCascade,
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_values = match old_values {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return ChildCascadeRequirement::MustCascade,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ::before and ::after are element-backed in Gecko, so they do the
|
||||||
|
// damage calculation for themselves, when there's an actual pseudo.
|
||||||
|
let is_existing_before_or_after =
|
||||||
|
cfg!(feature = "gecko") &&
|
||||||
|
pseudo.map_or(false, |p| p.is_before_or_after()) &&
|
||||||
|
self.existing_style_for_restyle_damage(old_values, pseudo)
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if is_existing_before_or_after {
|
||||||
|
return ChildCascadeRequirement::CanSkipCascade;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.accumulate_damage_for(shared_context,
|
||||||
|
restyle,
|
||||||
|
old_values,
|
||||||
|
new_values,
|
||||||
|
pseudo)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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_for(&self,
|
||||||
shared_context: &SharedStyleContext,
|
shared_context: &SharedStyleContext,
|
||||||
restyle: &mut RestyleData,
|
restyle: &mut RestyleData,
|
||||||
old_values: &ComputedValues,
|
old_values: &ComputedValues,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>) {
|
pseudo: Option<&PseudoElement>)
|
||||||
|
-> ChildCascadeRequirement {
|
||||||
// Don't accumulate damage if we're in a restyle for reconstruction.
|
// Don't accumulate damage if we're in a restyle for reconstruction.
|
||||||
if shared_context.traversal_flags.for_reconstruct() {
|
if shared_context.traversal_flags.for_reconstruct() {
|
||||||
return;
|
return ChildCascadeRequirement::MustCascade;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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. Similarly if we already
|
||||||
if restyle.damage_handled.contains(RestyleDamage::reconstruct()) {
|
// have an explicitly stored ReconstructFrame hint.
|
||||||
restyle.damage = RestyleDamage::empty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add restyle damage, but only the bits that aren't redundant with respect
|
|
||||||
// to damage applied on our ancestors.
|
|
||||||
//
|
//
|
||||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301258#c12
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301258#c12
|
||||||
// for followup work to make the optimization here more optimal by considering
|
// for followup work to make the optimization here more optimal by considering
|
||||||
// each bit individually.
|
// each bit individually.
|
||||||
if !restyle.damage.contains(RestyleDamage::reconstruct()) {
|
let skip_applying_damage =
|
||||||
let new_damage = self.compute_restyle_damage(&old_values,
|
restyle.damage_handled.contains(RestyleDamage::reconstruct()) ||
|
||||||
|
restyle.damage.contains(RestyleDamage::reconstruct());
|
||||||
|
|
||||||
|
let difference = self.compute_style_difference(&old_values,
|
||||||
&new_values,
|
&new_values,
|
||||||
pseudo);
|
pseudo);
|
||||||
if !restyle.damage_handled.contains(new_damage) {
|
if !skip_applying_damage {
|
||||||
restyle.damage |= new_damage;
|
restyle.damage |= difference.damage;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
difference.change.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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_for(&self,
|
||||||
_shared_context: &SharedStyleContext,
|
_shared_context: &SharedStyleContext,
|
||||||
restyle: &mut RestyleData,
|
restyle: &mut RestyleData,
|
||||||
old_values: &ComputedValues,
|
old_values: &ComputedValues,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>) {
|
pseudo: Option<&PseudoElement>)
|
||||||
if restyle.damage != RestyleDamage::rebuild_and_reflow() {
|
-> ChildCascadeRequirement {
|
||||||
restyle.damage |=
|
let difference = self.compute_style_difference(&old_values, &new_values, pseudo);
|
||||||
self.compute_restyle_damage(&old_values, &new_values, pseudo);
|
restyle.damage |= difference.damage;
|
||||||
}
|
difference.change.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
@ -917,14 +1008,19 @@ pub trait MatchMethods : TElement {
|
||||||
fn match_and_cascade(&self,
|
fn match_and_cascade(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
sharing: StyleSharingBehavior)
|
sharing: StyleSharingBehavior) -> ChildCascadeRequirement
|
||||||
{
|
{
|
||||||
// Perform selector matching for the primary style.
|
// Perform selector matching for the primary style.
|
||||||
let mut relations = StyleRelations::empty();
|
let mut relations = StyleRelations::empty();
|
||||||
let result = self.match_primary(context, data, &mut relations);
|
let result = self.match_primary(context, data, &mut relations);
|
||||||
|
|
||||||
// Cascade properties and compute primary values.
|
// Cascade properties and compute primary values.
|
||||||
self.cascade_primary(context, data, result.important_rules_overriding_animation_changed);
|
let child_cascade_requirement =
|
||||||
|
self.cascade_primary(
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
result.important_rules_overriding_animation_changed
|
||||||
|
);
|
||||||
|
|
||||||
// Match and cascade eager pseudo-elements.
|
// Match and cascade eager pseudo-elements.
|
||||||
if !data.styles().is_display_none() {
|
if !data.styles().is_display_none() {
|
||||||
|
@ -957,6 +1053,8 @@ pub trait MatchMethods : TElement {
|
||||||
relations,
|
relations,
|
||||||
revalidation_match_results);
|
revalidation_match_results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child_cascade_requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the cascade, without matching.
|
/// Performs the cascade, without matching.
|
||||||
|
@ -964,9 +1062,12 @@ pub trait MatchMethods : TElement {
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
mut data: &mut ElementData,
|
mut data: &mut ElementData,
|
||||||
important_rules_changed: bool)
|
important_rules_changed: bool)
|
||||||
|
-> ChildCascadeRequirement
|
||||||
{
|
{
|
||||||
|
let child_cascade_requirement =
|
||||||
self.cascade_primary(context, &mut data, important_rules_changed);
|
self.cascade_primary(context, &mut data, important_rules_changed);
|
||||||
self.cascade_pseudos(context, &mut data);
|
self.cascade_pseudos(context, &mut data);
|
||||||
|
child_cascade_requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs selector matching to (re)compute the primary rule node for this element.
|
/// Runs selector matching to (re)compute the primary rule node for this element.
|
||||||
|
@ -1063,10 +1164,22 @@ pub trait MatchMethods : TElement {
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
&context.shared.guards);
|
&context.shared.guards);
|
||||||
|
|
||||||
let important_rules_changed = self.has_animations() &&
|
if log_enabled!(Trace) {
|
||||||
|
trace!("Matched rules:");
|
||||||
|
for rn in primary_rule_node.self_and_ancestors() {
|
||||||
|
if let Some(source) = rn.style_source() {
|
||||||
|
trace!(" > {:?}", source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let important_rules_changed =
|
||||||
|
self.has_animations() &&
|
||||||
data.has_styles() &&
|
data.has_styles() &&
|
||||||
data.important_rules_are_different(&primary_rule_node,
|
data.important_rules_are_different(
|
||||||
&context.shared.guards);
|
&primary_rule_node,
|
||||||
|
&context.shared.guards
|
||||||
|
);
|
||||||
|
|
||||||
RulesMatchedResult {
|
RulesMatchedResult {
|
||||||
rule_nodes_changed: data.set_primary_rules(primary_rule_node),
|
rule_nodes_changed: data.set_primary_rules(primary_rule_node),
|
||||||
|
@ -1214,7 +1327,7 @@ pub trait MatchMethods : TElement {
|
||||||
/// the rule tree. Returns RulesChanged which indicates whether the rule nodes changed
|
/// the rule tree. Returns RulesChanged which indicates whether the rule nodes changed
|
||||||
/// and whether the important rules changed.
|
/// and whether the important rules changed.
|
||||||
fn replace_rules(&self,
|
fn replace_rules(&self,
|
||||||
hint: RestyleHint,
|
replacements: RestyleReplacements,
|
||||||
context: &StyleContext<Self>,
|
context: &StyleContext<Self>,
|
||||||
data: &mut AtomicRefMut<ElementData>)
|
data: &mut AtomicRefMut<ElementData>)
|
||||||
-> RulesChanged {
|
-> RulesChanged {
|
||||||
|
@ -1246,10 +1359,10 @@ pub trait MatchMethods : TElement {
|
||||||
//
|
//
|
||||||
// Non-animation restyle hints will be processed in a subsequent
|
// Non-animation restyle hints will be processed in a subsequent
|
||||||
// normal traversal.
|
// normal traversal.
|
||||||
if hint.intersects(RestyleHint::for_animations()) {
|
if replacements.intersects(RestyleReplacements::for_animations()) {
|
||||||
debug_assert!(context.shared.traversal_flags.for_animation_only());
|
debug_assert!(context.shared.traversal_flags.for_animation_only());
|
||||||
|
|
||||||
if hint.contains(RESTYLE_SMIL) {
|
if replacements.contains(RESTYLE_SMIL) {
|
||||||
replace_rule_node(CascadeLevel::SMILOverride,
|
replace_rule_node(CascadeLevel::SMILOverride,
|
||||||
self.get_smil_override(),
|
self.get_smil_override(),
|
||||||
primary_rules);
|
primary_rules);
|
||||||
|
@ -1265,16 +1378,16 @@ pub trait MatchMethods : TElement {
|
||||||
|
|
||||||
// Apply Transition rules and Animation rules if the corresponding restyle hint
|
// Apply Transition rules and Animation rules if the corresponding restyle hint
|
||||||
// is contained.
|
// is contained.
|
||||||
if hint.contains(RESTYLE_CSS_TRANSITIONS) {
|
if replacements.contains(RESTYLE_CSS_TRANSITIONS) {
|
||||||
replace_rule_node_for_animation(CascadeLevel::Transitions,
|
replace_rule_node_for_animation(CascadeLevel::Transitions,
|
||||||
primary_rules);
|
primary_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
if hint.contains(RESTYLE_CSS_ANIMATIONS) {
|
if replacements.contains(RESTYLE_CSS_ANIMATIONS) {
|
||||||
replace_rule_node_for_animation(CascadeLevel::Animations,
|
replace_rule_node_for_animation(CascadeLevel::Animations,
|
||||||
primary_rules);
|
primary_rules);
|
||||||
}
|
}
|
||||||
} else if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
} else if replacements.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
||||||
let style_attribute = self.style_attribute();
|
let style_attribute = self.style_attribute();
|
||||||
replace_rule_node(CascadeLevel::StyleAttributeNormal,
|
replace_rule_node(CascadeLevel::StyleAttributeNormal,
|
||||||
style_attribute,
|
style_attribute,
|
||||||
|
@ -1342,11 +1455,12 @@ pub trait MatchMethods : TElement {
|
||||||
debug_assert_eq!(data.has_styles(), data.has_restyle());
|
debug_assert_eq!(data.has_styles(), data.has_restyle());
|
||||||
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 {
|
let child_cascade_requirement =
|
||||||
self.accumulate_damage(&context.shared,
|
self.accumulate_damage(&context.shared,
|
||||||
data.restyle_mut(), &old,
|
data.get_restyle_mut(),
|
||||||
shared_style.values(), None);
|
old_values.as_ref().map(|v| v.as_ref()),
|
||||||
}
|
shared_style.values(),
|
||||||
|
None);
|
||||||
|
|
||||||
// We never put elements with pseudo style into the style
|
// We never put elements with pseudo style into the style
|
||||||
// sharing cache, so we can just mint an ElementStyles
|
// sharing cache, so we can just mint an ElementStyles
|
||||||
|
@ -1356,7 +1470,7 @@ pub trait MatchMethods : TElement {
|
||||||
let styles = ElementStyles::new(shared_style);
|
let styles = ElementStyles::new(shared_style);
|
||||||
data.set_styles(styles);
|
data.set_styles(styles);
|
||||||
|
|
||||||
return StyleSharingResult::StyleWasShared(i)
|
return StyleSharingResult::StyleWasShared(i, child_cascade_requirement)
|
||||||
}
|
}
|
||||||
Err(miss) => {
|
Err(miss) => {
|
||||||
debug!("Cache miss: {:?}", miss);
|
debug!("Cache miss: {:?}", miss);
|
||||||
|
@ -1391,32 +1505,51 @@ pub trait MatchMethods : TElement {
|
||||||
/// Given the old and new style of this element, and whether it's a
|
/// Given the old and new style of this element, and whether it's a
|
||||||
/// pseudo-element, compute the restyle damage used to determine which
|
/// pseudo-element, compute the restyle damage used to determine which
|
||||||
/// kind of layout or painting operations we'll need.
|
/// kind of layout or painting operations we'll need.
|
||||||
fn compute_restyle_damage(&self,
|
fn compute_style_difference(&self,
|
||||||
old_values: &ComputedValues,
|
old_values: &ComputedValues,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> RestyleDamage
|
-> StyleDifference
|
||||||
{
|
{
|
||||||
match self.existing_style_for_restyle_damage(old_values, pseudo) {
|
if let Some(source) = self.existing_style_for_restyle_damage(old_values, pseudo) {
|
||||||
Some(ref source) => RestyleDamage::compute(source, new_values),
|
return RestyleDamage::compute_style_difference(source, new_values)
|
||||||
None => {
|
|
||||||
// If there's no style source, that likely means that Gecko
|
|
||||||
// couldn't find a style context. This happens with display:none
|
|
||||||
// elements, and probably a number of other edge cases that
|
|
||||||
// we don't handle well yet (like display:contents).
|
|
||||||
if new_values.get_box().clone_display() == display::T::none &&
|
|
||||||
old_values.get_box().clone_display() == display::T::none {
|
|
||||||
// The style remains display:none. No need for damage.
|
|
||||||
RestyleDamage::empty()
|
|
||||||
} else {
|
|
||||||
// Something else. Be conservative for now.
|
|
||||||
RestyleDamage::reconstruct()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cascade the eager pseudo-elements of this element.
|
let new_style_is_display_none =
|
||||||
|
new_values.get_box().clone_display() == display::T::none;
|
||||||
|
let old_style_is_display_none =
|
||||||
|
old_values.get_box().clone_display() == display::T::none;
|
||||||
|
|
||||||
|
// If there's no style source, that likely means that Gecko couldn't
|
||||||
|
// find a style context.
|
||||||
|
//
|
||||||
|
// This happens with display:none elements, and not-yet-existing
|
||||||
|
// pseudo-elements.
|
||||||
|
if new_style_is_display_none && old_style_is_display_none {
|
||||||
|
// The style remains display:none. No need for damage.
|
||||||
|
return StyleDifference::new(RestyleDamage::empty(), StyleChange::Unchanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pseudo.map_or(false, |p| p.is_before_or_after()) {
|
||||||
|
if (old_style_is_display_none ||
|
||||||
|
old_values.ineffective_content_property()) &&
|
||||||
|
(new_style_is_display_none ||
|
||||||
|
new_values.ineffective_content_property()) {
|
||||||
|
// The pseudo-element will remain undisplayed, so just avoid
|
||||||
|
// triggering any change.
|
||||||
|
return StyleDifference::new(RestyleDamage::empty(), StyleChange::Unchanged)
|
||||||
|
}
|
||||||
|
return StyleDifference::new(RestyleDamage::reconstruct(), StyleChange::Changed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something else. Be conservative for now.
|
||||||
|
warn!("Reframing due to lack of old style source: {:?}, pseudo: {:?}",
|
||||||
|
self, pseudo);
|
||||||
|
// Something else. Be conservative for now.
|
||||||
|
StyleDifference::new(RestyleDamage::reconstruct(), StyleChange::Changed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the cascade for the element's eager pseudos.
|
||||||
fn cascade_pseudos(&self,
|
fn cascade_pseudos(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
mut data: &mut ElementData)
|
mut data: &mut ElementData)
|
||||||
|
|
|
@ -141,6 +141,13 @@ impl ComputedValues {
|
||||||
self.get_box().clone_display() == longhands::display::computed_value::T::contents
|
self.get_box().clone_display() == longhands::display::computed_value::T::contents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the value of the `content` property would make a
|
||||||
|
/// pseudo-element not rendered.
|
||||||
|
#[inline]
|
||||||
|
pub fn ineffective_content_property(&self) -> bool {
|
||||||
|
self.get_counters().ineffective_content_property()
|
||||||
|
}
|
||||||
|
|
||||||
% for style_struct in data.style_structs:
|
% for style_struct in data.style_structs:
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
|
pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
|
||||||
|
@ -4204,6 +4211,10 @@ clip-path
|
||||||
|
|
||||||
<%self:impl_trait style_struct_name="Counters"
|
<%self:impl_trait style_struct_name="Counters"
|
||||||
skip_longhands="content counter-increment counter-reset">
|
skip_longhands="content counter-increment counter-reset">
|
||||||
|
pub fn ineffective_content_property(&self) -> bool {
|
||||||
|
self.gecko.mContents.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
|
pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
|
||||||
use properties::longhands::content::computed_value::T;
|
use properties::longhands::content::computed_value::T;
|
||||||
use properties::longhands::content::computed_value::ContentItem;
|
use properties::longhands::content::computed_value::ContentItem;
|
||||||
|
|
|
@ -1813,6 +1813,19 @@ impl ComputedValues {
|
||||||
/// Since this isn't supported in Servo, this is always false for Servo.
|
/// Since this isn't supported in Servo, this is always false for Servo.
|
||||||
pub fn is_display_contents(&self) -> bool { false }
|
pub fn is_display_contents(&self) -> bool { false }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Returns whether the "content" property for the given style is completely
|
||||||
|
/// ineffective, and would yield an empty `::before` or `::after`
|
||||||
|
/// pseudo-element.
|
||||||
|
pub fn ineffective_content_property(&self) -> bool {
|
||||||
|
use properties::longhands::content::computed_value::T;
|
||||||
|
match self.get_counters().content {
|
||||||
|
T::normal |
|
||||||
|
T::none => true,
|
||||||
|
T::Content(ref items) => items.is_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the current style is multicolumn.
|
/// Whether the current style is multicolumn.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_multicol(&self) -> bool {
|
pub fn is_multicol(&self) -> bool {
|
||||||
|
|
|
@ -26,34 +26,38 @@ use smallvec::SmallVec;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
|
use std::cmp;
|
||||||
use stylist::SelectorMap;
|
use stylist::SelectorMap;
|
||||||
|
|
||||||
|
/// When the ElementState of an element (like IN_HOVER_STATE) changes,
|
||||||
|
/// certain pseudo-classes (like :hover) may require us to restyle that
|
||||||
|
/// element, its siblings, and/or its descendants. Similarly, when various
|
||||||
|
/// attributes of an element change, we may also need to restyle things with
|
||||||
|
/// id, class, and attribute selectors. Doing this conservatively is
|
||||||
|
/// expensive, and so we use RestyleHints to short-circuit work we know is
|
||||||
|
/// unnecessary.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct RestyleHint {
|
||||||
|
/// Depths at which selector matching must be re-run.
|
||||||
|
match_under_self: RestyleDepths,
|
||||||
|
|
||||||
|
/// Rerun selector matching on all later siblings of the element and all
|
||||||
|
/// of their descendants.
|
||||||
|
match_later_siblings: bool,
|
||||||
|
|
||||||
|
/// Levels of the cascade whose rule nodes should be recomputed and
|
||||||
|
/// replaced.
|
||||||
|
pub replacements: RestyleReplacements,
|
||||||
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// When the ElementState of an element (like IN_HOVER_STATE) changes,
|
/// Cascade levels that can be updated for an element by simply replacing
|
||||||
/// certain pseudo-classes (like :hover) may require us to restyle that
|
/// their rule node.
|
||||||
/// element, its siblings, and/or its descendants. Similarly, when various
|
|
||||||
/// attributes of an element change, we may also need to restyle things with
|
|
||||||
/// id, class, and attribute selectors. Doing this conservatively is
|
|
||||||
/// expensive, and so we use RestyleHints to short-circuit work we know is
|
|
||||||
/// unnecessary.
|
|
||||||
///
|
///
|
||||||
/// Note that the bit values here must be kept in sync with the Gecko
|
/// Note that the bit values here must be kept in sync with the Gecko
|
||||||
/// nsRestyleHint values. If you add more bits with matching values,
|
/// nsRestyleHint values. If you add more bits with matching values,
|
||||||
/// please add assertions to assert_restyle_hints_match below.
|
/// please add assertions to assert_restyle_hints_match below.
|
||||||
pub flags RestyleHint: u32 {
|
pub flags RestyleReplacements: u8 {
|
||||||
/// Rerun selector matching on the element.
|
|
||||||
const RESTYLE_SELF = 0x01,
|
|
||||||
|
|
||||||
/// Rerun selector matching on all of the element's descendants.
|
|
||||||
///
|
|
||||||
/// NB: In Gecko, we have RESTYLE_SUBTREE which is inclusive of self,
|
|
||||||
/// but heycam isn't aware of a good reason for that.
|
|
||||||
const RESTYLE_DESCENDANTS = 0x04,
|
|
||||||
|
|
||||||
/// Rerun selector matching on all later siblings of the element and all
|
|
||||||
/// of their descendants.
|
|
||||||
const RESTYLE_LATER_SIBLINGS = 0x08,
|
|
||||||
|
|
||||||
/// Replace the style data coming from CSS transitions without updating
|
/// Replace the style data coming from CSS transitions without updating
|
||||||
/// any other style data. This hint is only processed in animation-only
|
/// any other style data. This hint is only processed in animation-only
|
||||||
/// traversal which is prior to normal traversal.
|
/// traversal which is prior to normal traversal.
|
||||||
|
@ -76,7 +80,82 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that all RestyleHint flags have a matching nsRestyleHint value.
|
/// Eight bit wide bitfield representing depths of a DOM subtree's descendants,
|
||||||
|
/// used to represent which elements must have selector matching re-run on them.
|
||||||
|
///
|
||||||
|
/// The least significant bit indicates that selector matching must be re-run
|
||||||
|
/// for the element itself, the second least significant bit for the element's
|
||||||
|
/// children, the third its grandchildren, and so on. If the most significant
|
||||||
|
/// bit it set, it indicates that that selector matching must be re-run for
|
||||||
|
/// elements at that depth and all of their descendants.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct RestyleDepths(u8);
|
||||||
|
|
||||||
|
impl RestyleDepths {
|
||||||
|
/// Returns a `RestyleDepths` representing no element depths.
|
||||||
|
fn empty() -> Self {
|
||||||
|
RestyleDepths(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `RestyleDepths` representing the current element depth.
|
||||||
|
fn for_self() -> Self {
|
||||||
|
RestyleDepths(0x01)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `RestyleDepths` representing the depths of all descendants of
|
||||||
|
/// the current element.
|
||||||
|
fn for_descendants() -> Self {
|
||||||
|
RestyleDepths(0xfe)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `RestyleDepths` representing the current element depth and the
|
||||||
|
/// depths of all the current element's descendants.
|
||||||
|
fn for_self_and_descendants() -> Self {
|
||||||
|
RestyleDepths(0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `RestyleDepths` representing the specified depth, where zero
|
||||||
|
/// is the current element depth, one is its children's depths, etc.
|
||||||
|
fn for_depth(depth: u32) -> Self {
|
||||||
|
RestyleDepths(1u8 << cmp::min(depth, 7))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleDepths` represents the current element
|
||||||
|
/// depth and the depths of all the current element's descendants.
|
||||||
|
fn is_self_and_descendants(&self) -> bool {
|
||||||
|
self.0 == 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleDepths` includes any element depth.
|
||||||
|
fn is_any(&self) -> bool {
|
||||||
|
self.0 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleDepths` includes the current element depth.
|
||||||
|
fn has_self(&self) -> bool {
|
||||||
|
(self.0 & 0x01) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `RestyleDepths` with all depth values represented by this
|
||||||
|
/// `RestyleDepths` reduced by one.
|
||||||
|
fn propagate(&self) -> Self {
|
||||||
|
RestyleDepths((self.0 >> 1) | (self.0 & 0x80))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `RestyleDepths` that represents the union of the depths
|
||||||
|
/// from `self` and `other`.
|
||||||
|
fn insert(&mut self, other: RestyleDepths) {
|
||||||
|
self.0 |= other.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleDepths` includes all depths represented
|
||||||
|
/// by `other`.
|
||||||
|
fn contains(&self, other: RestyleDepths) -> bool {
|
||||||
|
(self.0 & other.0) == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts that all RestyleReplacements have a matching nsRestyleHint value.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn assert_restyle_hints_match() {
|
pub fn assert_restyle_hints_match() {
|
||||||
|
@ -85,24 +164,18 @@ pub fn assert_restyle_hints_match() {
|
||||||
macro_rules! check_restyle_hints {
|
macro_rules! check_restyle_hints {
|
||||||
( $( $a:ident => $b:ident ),*, ) => {
|
( $( $a:ident => $b:ident ),*, ) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
let mut hints = RestyleHint::all();
|
let mut replacements = RestyleReplacements::all();
|
||||||
$(
|
$(
|
||||||
assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
|
assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
|
||||||
hints.remove($b);
|
replacements.remove($b);
|
||||||
)*
|
)*
|
||||||
assert_eq!(hints, RestyleHint::empty(), "all RestyleHint bits should have an assertion");
|
assert_eq!(replacements, RestyleReplacements::empty(),
|
||||||
|
"all RestyleReplacements bits should have an assertion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check_restyle_hints! {
|
check_restyle_hints! {
|
||||||
nsRestyleHint_eRestyle_Self => RESTYLE_SELF,
|
|
||||||
// Note that eRestyle_Subtree means "self and descendants", while
|
|
||||||
// RESTYLE_DESCENDANTS means descendants only. The From impl
|
|
||||||
// below handles converting eRestyle_Subtree into
|
|
||||||
// (RESTYLE_SELF | RESTYLE_DESCENDANTS).
|
|
||||||
nsRestyleHint_eRestyle_Subtree => RESTYLE_DESCENDANTS,
|
|
||||||
nsRestyleHint_eRestyle_LaterSiblings => RESTYLE_LATER_SIBLINGS,
|
|
||||||
nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
|
nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
|
||||||
nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
|
nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
|
||||||
nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
|
nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
|
||||||
|
@ -111,39 +184,233 @@ pub fn assert_restyle_hints_match() {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestyleHint {
|
impl RestyleHint {
|
||||||
/// The subset hints that affect the styling of a single element during the
|
/// Creates a new, empty `RestyleHint`, which represents no restyling work
|
||||||
/// traversal.
|
/// needs to be done.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn for_self() -> Self {
|
pub fn empty() -> Self {
|
||||||
RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::empty(),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The subset hints that are used for animation restyle.
|
/// Creates a new `RestyleHint` that indicates selector matching must be
|
||||||
|
/// re-run on the element.
|
||||||
|
#[inline]
|
||||||
|
pub fn for_self() -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::for_self(),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `RestyleHint` that indicates selector matching must be
|
||||||
|
/// re-run on all of the element's descendants.
|
||||||
|
#[inline]
|
||||||
|
pub fn descendants() -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::for_descendants(),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `RestyleHint` that indicates selector matching must be
|
||||||
|
/// re-run on the descendants of element at the specified depth, where 0
|
||||||
|
/// indicates the element itself, 1 is its children, 2 its grandchildren,
|
||||||
|
/// etc.
|
||||||
|
#[inline]
|
||||||
|
pub fn descendants_at_depth(depth: u32) -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::for_depth(depth),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `RestyleHint` that indicates selector matching must be
|
||||||
|
/// re-run on all of the element's later siblings and their descendants.
|
||||||
|
#[inline]
|
||||||
|
pub fn later_siblings() -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::empty(),
|
||||||
|
match_later_siblings: true,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `RestyleHint` that indicates selector matching must be
|
||||||
|
/// re-run on the element and all of its descendants.
|
||||||
|
#[inline]
|
||||||
|
pub fn subtree() -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::for_self_and_descendants(),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `RestyleHint` that indicates selector matching must be
|
||||||
|
/// re-run on the element, its descendants, its later siblings, and
|
||||||
|
/// their descendants.
|
||||||
|
#[inline]
|
||||||
|
pub fn subtree_and_later_siblings() -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::for_self_and_descendants(),
|
||||||
|
match_later_siblings: true,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `RestyleHint` that indicates the specified rule node
|
||||||
|
/// replacements must be performed on the element.
|
||||||
|
#[inline]
|
||||||
|
pub fn for_replacements(replacements: RestyleReplacements) -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: RestyleDepths::empty(),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: replacements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleHint` represents no needed restyle work.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
*self == RestyleHint::empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleHint` represents the maximum possible
|
||||||
|
/// restyle work, and thus any `insert()` calls will have no effect.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_maximum(&self) -> bool {
|
||||||
|
self.match_under_self.is_self_and_descendants() &&
|
||||||
|
self.match_later_siblings &&
|
||||||
|
self.replacements.is_all()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the hint specifies that some work must be performed on
|
||||||
|
/// the current element.
|
||||||
|
#[inline]
|
||||||
|
pub fn affects_self(&self) -> bool {
|
||||||
|
self.match_self() || !self.replacements.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the hint specifies that later siblings must be restyled.
|
||||||
|
#[inline]
|
||||||
|
pub fn affects_later_siblings(&self) -> bool {
|
||||||
|
self.match_later_siblings
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the hint specifies that an animation cascade level must
|
||||||
|
/// be replaced.
|
||||||
|
#[inline]
|
||||||
|
pub fn has_animation_hint(&self) -> bool {
|
||||||
|
self.replacements.intersects(RestyleReplacements::for_animations())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the hint specifies some restyle work other than an
|
||||||
|
/// animation cascade level replacement.
|
||||||
|
#[inline]
|
||||||
|
pub fn has_non_animation_hint(&self) -> bool {
|
||||||
|
self.match_under_self.is_any() || self.match_later_siblings ||
|
||||||
|
self.replacements.contains(RESTYLE_STYLE_ATTRIBUTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the hint specifies that selector matching must be re-run
|
||||||
|
/// for the element.
|
||||||
|
#[inline]
|
||||||
|
pub fn match_self(&self) -> bool {
|
||||||
|
self.match_under_self.has_self()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `RestyleHint` appropriate for children of the current
|
||||||
|
/// element.
|
||||||
|
#[inline]
|
||||||
|
pub fn propagate_for_non_animation_restyle(&self) -> Self {
|
||||||
|
RestyleHint {
|
||||||
|
match_under_self: self.match_under_self.propagate(),
|
||||||
|
match_later_siblings: false,
|
||||||
|
replacements: RestyleReplacements::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all of the animation-related hints.
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_animation_hints(&mut self) {
|
||||||
|
self.replacements.remove(RestyleReplacements::for_animations());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the later siblings hint, and returns whether it was present.
|
||||||
|
pub fn remove_later_siblings_hint(&mut self) -> bool {
|
||||||
|
let later_siblings = self.match_later_siblings;
|
||||||
|
self.match_later_siblings = false;
|
||||||
|
later_siblings
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unions the specified `RestyleHint` into this one.
|
||||||
|
#[inline]
|
||||||
|
pub fn insert_from(&mut self, other: &Self) {
|
||||||
|
self.match_under_self.insert(other.match_under_self);
|
||||||
|
self.match_later_siblings |= other.match_later_siblings;
|
||||||
|
self.replacements.insert(other.replacements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unions the specified `RestyleHint` into this one.
|
||||||
|
#[inline]
|
||||||
|
pub fn insert(&mut self, other: Self) {
|
||||||
|
// A later patch should make it worthwhile to have an insert() function
|
||||||
|
// that consumes its argument.
|
||||||
|
self.insert_from(&other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this `RestyleHint` represents at least as much restyle
|
||||||
|
/// work as the specified one.
|
||||||
|
#[inline]
|
||||||
|
pub fn contains(&mut self, other: &Self) -> bool {
|
||||||
|
self.match_under_self.contains(other.match_under_self) &&
|
||||||
|
(self.match_later_siblings & other.match_later_siblings) == other.match_later_siblings &&
|
||||||
|
self.replacements.contains(other.replacements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RestyleReplacements {
|
||||||
|
/// The replacements for the animation cascade levels.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn for_animations() -> Self {
|
pub fn for_animations() -> Self {
|
||||||
RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
|
RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
impl From<nsRestyleHint> for RestyleReplacements {
|
||||||
|
fn from(raw: nsRestyleHint) -> Self {
|
||||||
|
Self::from_bits_truncate(raw.0 as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl From<nsRestyleHint> for RestyleHint {
|
impl From<nsRestyleHint> for RestyleHint {
|
||||||
fn from(raw: nsRestyleHint) -> Self {
|
fn from(raw: nsRestyleHint) -> Self {
|
||||||
let raw_bits: u32 = raw.0;
|
use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings;
|
||||||
|
use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self;
|
||||||
|
use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants;
|
||||||
|
use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree;
|
||||||
|
|
||||||
// FIXME(bholley): Finish aligning the binary representations here and
|
let mut match_under_self = RestyleDepths::empty();
|
||||||
// then .expect() the result of the checked version.
|
if (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0 {
|
||||||
if Self::from_bits(raw_bits).is_none() {
|
match_under_self.insert(RestyleDepths::for_self());
|
||||||
warn!("stylo: dropping unsupported restyle hint bits");
|
}
|
||||||
|
if (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0 {
|
||||||
|
match_under_self.insert(RestyleDepths::for_descendants());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bits = Self::from_bits_truncate(raw_bits);
|
RestyleHint {
|
||||||
|
match_under_self: match_under_self,
|
||||||
// eRestyle_Subtree converts to (RESTYLE_SELF | RESTYLE_DESCENDANTS).
|
match_later_siblings: (raw.0 & eRestyle_LaterSiblings.0) != 0,
|
||||||
if bits.contains(RESTYLE_DESCENDANTS) {
|
replacements: raw.into(),
|
||||||
bits |= RESTYLE_SELF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bits
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,21 +702,6 @@ fn is_attr_selector(sel: &Component<SelectorImpl>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
|
|
||||||
match combinator {
|
|
||||||
None => RESTYLE_SELF,
|
|
||||||
Some(c) => match c {
|
|
||||||
// NB: RESTYLE_SELF is needed to handle properly eager pseudos,
|
|
||||||
// otherwise we may leave a stale style on the parent.
|
|
||||||
Combinator::PseudoElement => RESTYLE_SELF | RESTYLE_DESCENDANTS,
|
|
||||||
Combinator::Child => RESTYLE_DESCENDANTS,
|
|
||||||
Combinator::Descendant => RESTYLE_DESCENDANTS,
|
|
||||||
Combinator::NextSibling => RESTYLE_LATER_SIBLINGS,
|
|
||||||
Combinator::LaterSibling => RESTYLE_LATER_SIBLINGS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
/// The aspects of an selector which are sensitive.
|
/// The aspects of an selector which are sensitive.
|
||||||
|
@ -579,6 +831,8 @@ impl DependencySet {
|
||||||
let mut combinator = None;
|
let mut combinator = None;
|
||||||
let mut iter = selector.inner.complex.iter();
|
let mut iter = selector.inner.complex.iter();
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
|
let mut child_combinators_seen = 0;
|
||||||
|
let mut saw_descendant_combinator = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let sequence_start = index;
|
let sequence_start = index;
|
||||||
|
@ -596,9 +850,34 @@ impl DependencySet {
|
||||||
index += 1; // Account for the simple selector.
|
index += 1; // Account for the simple selector.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of how many child combinators we've encountered,
|
||||||
|
// and whether we've encountered a descendant combinator at all.
|
||||||
|
match combinator {
|
||||||
|
Some(Combinator::Child) => child_combinators_seen += 1,
|
||||||
|
Some(Combinator::Descendant) => saw_descendant_combinator = true,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
// If we found a sensitivity, add an entry in the dependency set.
|
// If we found a sensitivity, add an entry in the dependency set.
|
||||||
if !visitor.sensitivities.is_empty() {
|
if !visitor.sensitivities.is_empty() {
|
||||||
let hint = combinator_to_restyle_hint(combinator);
|
// Compute a RestyleHint given the current combinator and the
|
||||||
|
// tracked number of child combinators and presence of a
|
||||||
|
// descendant combinator.
|
||||||
|
let hint = match combinator {
|
||||||
|
// NB: RestyleHint::subtree() and not
|
||||||
|
// RestyleHint::descendants() is needed to handle properly
|
||||||
|
// eager pseudos, otherwise we may leave a stale style on
|
||||||
|
// the parent.
|
||||||
|
Some(Combinator::PseudoElement) => RestyleHint::subtree(),
|
||||||
|
Some(Combinator::Child) if !saw_descendant_combinator => {
|
||||||
|
RestyleHint::descendants_at_depth(child_combinators_seen)
|
||||||
|
}
|
||||||
|
Some(Combinator::Child) |
|
||||||
|
Some(Combinator::Descendant) => RestyleHint::descendants(),
|
||||||
|
Some(Combinator::NextSibling) |
|
||||||
|
Some(Combinator::LaterSibling) => RestyleHint::later_siblings(),
|
||||||
|
None => RestyleHint::for_self(),
|
||||||
|
};
|
||||||
|
|
||||||
let dep_selector = if sequence_start == 0 {
|
let dep_selector = if sequence_start == 0 {
|
||||||
// Reuse the bloom hashes if this is the base selector.
|
// Reuse the bloom hashes if this is the base selector.
|
||||||
|
@ -700,7 +979,7 @@ impl DependencySet {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if hint.contains(dep.hint) {
|
if hint.contains(&dep.hint) {
|
||||||
trace!(" > hint was already there");
|
trace!(" > hint was already there");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -717,10 +996,10 @@ impl DependencySet {
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
if matched_then != matches_now {
|
if matched_then != matches_now {
|
||||||
hint.insert(dep.hint);
|
hint.insert_from(&dep.hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
!hint.is_all()
|
!hint.is_maximum()
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
|
debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
use computed_values::display;
|
use computed_values::display;
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
|
use matching::{StyleChange, StyleDifference};
|
||||||
use properties::ServoComputedValues;
|
use properties::ServoComputedValues;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -57,12 +58,14 @@ impl HeapSizeOf for ServoRestyleDamage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServoRestyleDamage {
|
impl ServoRestyleDamage {
|
||||||
/// Compute the appropriate restyle damage for a given style change between
|
/// Compute the `StyleDifference` (including the appropriate restyle damage)
|
||||||
/// `old` and `new`.
|
/// for a given style change between `old` and `new`.
|
||||||
pub fn compute(old: &ServoComputedValues,
|
pub fn compute_style_difference(old: &ServoComputedValues,
|
||||||
new: &ServoComputedValues)
|
new: &ServoComputedValues)
|
||||||
-> ServoRestyleDamage {
|
-> StyleDifference {
|
||||||
compute_damage(old, new)
|
let damage = compute_damage(old, new);
|
||||||
|
let change = if damage.is_empty() { StyleChange::Unchanged } else { StyleChange::Changed };
|
||||||
|
StyleDifference::new(damage, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a bitmask that represents a flow that needs to be rebuilt and
|
/// Returns a bitmask that represents a flow that needs to be rebuilt and
|
||||||
|
|
|
@ -8,8 +8,8 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
||||||
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
||||||
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
|
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
|
||||||
use matching::{MatchMethods, StyleSharingBehavior};
|
use matching::{ChildCascadeRequirement, MatchMethods, StyleSharingBehavior};
|
||||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
use restyle_hints::RestyleHint;
|
||||||
use selector_parser::RestyleDamage;
|
use selector_parser::RestyleDamage;
|
||||||
#[cfg(feature = "servo")] use servo_config::opts;
|
#[cfg(feature = "servo")] use servo_config::opts;
|
||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
|
@ -232,7 +232,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
if let Some(next) = root.next_sibling_element() {
|
if let Some(next) = root.next_sibling_element() {
|
||||||
if let Some(mut next_data) = next.mutate_data() {
|
if let Some(mut next_data) = next.mutate_data() {
|
||||||
let hint = StoredRestyleHint::subtree_and_later_siblings();
|
let hint = StoredRestyleHint::subtree_and_later_siblings();
|
||||||
next_data.ensure_restyle().hint.insert(&hint);
|
next_data.ensure_restyle().hint.insert(hint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +611,12 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
|
|
||||||
// Compute style for this element if necessary.
|
// Compute style for this element if necessary.
|
||||||
if compute_self {
|
if compute_self {
|
||||||
compute_style(traversal, traversal_data, context, element, &mut data);
|
match compute_style(traversal, traversal_data, context, element, &mut data) {
|
||||||
|
ChildCascadeRequirement::MustCascade => {
|
||||||
|
inherited_style_changed = true;
|
||||||
|
}
|
||||||
|
ChildCascadeRequirement::CanSkipCascade => {}
|
||||||
|
};
|
||||||
|
|
||||||
// If we're restyling this element to display:none, throw away all style
|
// If we're restyling this element to display:none, throw away all style
|
||||||
// data in the subtree, notify the caller to early-return.
|
// data in the subtree, notify the caller to early-return.
|
||||||
|
@ -620,9 +625,6 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
element);
|
element);
|
||||||
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(bholley): Compute this accurately from the call to CalcStyleDifference.
|
|
||||||
inherited_style_changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that matching and cascading is done, clear the bits corresponding to
|
// Now that matching and cascading is done, clear the bits corresponding to
|
||||||
|
@ -711,7 +713,7 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
traversal_data: &PerLevelTraversalData,
|
traversal_data: &PerLevelTraversalData,
|
||||||
context: &mut StyleContext<E>,
|
context: &mut StyleContext<E>,
|
||||||
element: E,
|
element: E,
|
||||||
mut data: &mut AtomicRefMut<ElementData>)
|
mut data: &mut AtomicRefMut<ElementData>) -> ChildCascadeRequirement
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
|
@ -727,10 +729,10 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
let sharing_result = unsafe {
|
let sharing_result = unsafe {
|
||||||
element.share_style_if_possible(context, &mut data)
|
element.share_style_if_possible(context, &mut data)
|
||||||
};
|
};
|
||||||
if let StyleWasShared(index) = sharing_result {
|
if let StyleWasShared(index, had_damage) = sharing_result {
|
||||||
context.thread_local.statistics.styles_shared += 1;
|
context.thread_local.statistics.styles_shared += 1;
|
||||||
context.thread_local.style_sharing_candidate_cache.touch(index);
|
context.thread_local.style_sharing_candidate_cache.touch(index);
|
||||||
return;
|
return had_damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,17 +746,28 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
context.thread_local.statistics.elements_matched += 1;
|
context.thread_local.statistics.elements_matched += 1;
|
||||||
|
|
||||||
// Perform the matching and cascading.
|
// Perform the matching and cascading.
|
||||||
element.match_and_cascade(context, &mut data, StyleSharingBehavior::Allow);
|
element.match_and_cascade(
|
||||||
|
context,
|
||||||
|
&mut data,
|
||||||
|
StyleSharingBehavior::Allow
|
||||||
|
)
|
||||||
}
|
}
|
||||||
CascadeWithReplacements(hint) => {
|
CascadeWithReplacements(flags) => {
|
||||||
let rules_changed = element.replace_rules(hint, context, &mut data);
|
let rules_changed = element.replace_rules(flags, context, &mut data);
|
||||||
element.cascade_primary_and_pseudos(context, &mut data,
|
element.cascade_primary_and_pseudos(
|
||||||
rules_changed.important_rules_changed());
|
context,
|
||||||
|
&mut data,
|
||||||
|
rules_changed.important_rules_changed()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
CascadeOnly => {
|
CascadeOnly => {
|
||||||
element.cascade_primary_and_pseudos(context, &mut data, false);
|
element.cascade_primary_and_pseudos(
|
||||||
|
context,
|
||||||
|
&mut data,
|
||||||
|
/* important_rules_changed = */ false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_children<E, D>(traversal: &D,
|
fn preprocess_children<E, D>(traversal: &D,
|
||||||
|
@ -806,11 +819,11 @@ fn preprocess_children<E, D>(traversal: &D,
|
||||||
|
|
||||||
// Propagate the parent and sibling restyle hint.
|
// Propagate the parent and sibling restyle hint.
|
||||||
if !propagated_hint.is_empty() {
|
if !propagated_hint.is_empty() {
|
||||||
restyle_data.hint.insert(&propagated_hint);
|
restyle_data.hint.insert_from(&propagated_hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if later_siblings {
|
if later_siblings {
|
||||||
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
|
propagated_hint.insert(RestyleHint::subtree().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the damage already handled by ancestors.
|
// Store the damage already handled by ancestors.
|
||||||
|
|
|
@ -2116,16 +2116,16 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
|
||||||
element, restyle_hint, change_hint);
|
element, restyle_hint, change_hint);
|
||||||
|
|
||||||
let restyle_hint: RestyleHint = restyle_hint.into();
|
let restyle_hint: RestyleHint = restyle_hint.into();
|
||||||
debug_assert!(RestyleHint::for_animations().contains(restyle_hint) ||
|
debug_assert!(!(restyle_hint.has_animation_hint() &&
|
||||||
!RestyleHint::for_animations().intersects(restyle_hint),
|
restyle_hint.has_non_animation_hint()),
|
||||||
"Animation restyle hints should not appear with non-animation restyle hints");
|
"Animation restyle hints should not appear with non-animation restyle hints");
|
||||||
|
|
||||||
let mut maybe_data = element.mutate_data();
|
let mut maybe_data = element.mutate_data();
|
||||||
let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe {
|
let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe {
|
||||||
maybe_restyle(d, element, restyle_hint.intersects(RestyleHint::for_animations()))
|
maybe_restyle(d, element, restyle_hint.has_animation_hint())
|
||||||
});
|
});
|
||||||
if let Some(restyle_data) = maybe_restyle_data {
|
if let Some(restyle_data) = maybe_restyle_data {
|
||||||
restyle_data.hint.insert(&restyle_hint.into());
|
restyle_data.hint.insert(restyle_hint.into());
|
||||||
restyle_data.damage |= damage;
|
restyle_data.damage |= damage;
|
||||||
} else {
|
} else {
|
||||||
debug!("(Element not styled, discarding hints)");
|
debug!("(Element not styled, discarding hints)");
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[border-collapse-dynamic-table-002.htm]
|
||||||
|
type: reftest
|
||||||
|
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue