mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #15480 - bholley:refactor_style_computation, r=emilio
Refactor style computation See https://bugzilla.mozilla.org/show_bug.cgi?id=1338382 <!-- 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/15480) <!-- Reviewable:end -->
This commit is contained in:
commit
0dd4afcf6d
10 changed files with 376 additions and 428 deletions
|
@ -393,10 +393,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn existing_style_for_restyle_damage<'a>(&'a self,
|
fn existing_style_for_restyle_damage<'a>(&'a self,
|
||||||
current_cv: Option<&'a Arc<ComputedValues>>,
|
current_cv: &'a Arc<ComputedValues>,
|
||||||
_pseudo_element: Option<&PseudoElement>)
|
_pseudo_element: Option<&PseudoElement>)
|
||||||
-> Option<&'a Arc<ComputedValues>> {
|
-> Option<&'a Arc<ComputedValues>> {
|
||||||
current_cv
|
Some(current_cv)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_dirty_descendants(&self) -> bool {
|
fn has_dirty_descendants(&self) -> bool {
|
||||||
|
@ -777,7 +777,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
|
||||||
debug_assert!(self.is_text_node());
|
debug_assert!(self.is_text_node());
|
||||||
let parent = self.node.parent_node().unwrap().as_element().unwrap();
|
let parent = self.node.parent_node().unwrap().as_element().unwrap();
|
||||||
let parent_data = parent.get_data().unwrap().borrow();
|
let parent_data = parent.get_data().unwrap().borrow();
|
||||||
parent_data.styles().primary.values.clone()
|
parent_data.styles().primary.values().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_id(self) -> usize {
|
fn debug_id(self) -> usize {
|
||||||
|
|
|
@ -389,7 +389,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
fn style(&self, context: &SharedStyleContext) -> Arc<ServoComputedValues> {
|
fn style(&self, context: &SharedStyleContext) -> Arc<ServoComputedValues> {
|
||||||
match self.get_pseudo_element_type() {
|
match self.get_pseudo_element_type() {
|
||||||
PseudoElementType::Normal => self.get_style_data().unwrap().borrow()
|
PseudoElementType::Normal => self.get_style_data().unwrap().borrow()
|
||||||
.styles().primary.values.clone(),
|
.styles().primary.values().clone(),
|
||||||
other => {
|
other => {
|
||||||
// Precompute non-eagerly-cascaded pseudo-element styles if not
|
// Precompute non-eagerly-cascaded pseudo-element styles if not
|
||||||
// cached before.
|
// cached before.
|
||||||
|
@ -406,7 +406,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
let new_style =
|
let new_style =
|
||||||
context.stylist.precomputed_values_for_pseudo(
|
context.stylist.precomputed_values_for_pseudo(
|
||||||
&style_pseudo,
|
&style_pseudo,
|
||||||
Some(&data.styles().primary.values),
|
Some(data.styles().primary.values()),
|
||||||
&context.default_computed_values,
|
&context.default_computed_values,
|
||||||
false);
|
false);
|
||||||
data.styles_mut().pseudos
|
data.styles_mut().pseudos
|
||||||
|
@ -424,7 +424,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
.lazily_compute_pseudo_element_style(
|
.lazily_compute_pseudo_element_style(
|
||||||
unsafe { &self.unsafe_get() },
|
unsafe { &self.unsafe_get() },
|
||||||
&style_pseudo,
|
&style_pseudo,
|
||||||
&data.styles().primary.values,
|
data.styles().primary.values(),
|
||||||
&context.default_computed_values);
|
&context.default_computed_values);
|
||||||
data.styles_mut().pseudos
|
data.styles_mut().pseudos
|
||||||
.insert(style_pseudo.clone(), new_style.unwrap());
|
.insert(style_pseudo.clone(), new_style.unwrap());
|
||||||
|
@ -434,7 +434,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
|
|
||||||
self.get_style_data().unwrap().borrow()
|
self.get_style_data().unwrap().borrow()
|
||||||
.styles().pseudos.get(&style_pseudo)
|
.styles().pseudos.get(&style_pseudo)
|
||||||
.unwrap().values.clone()
|
.unwrap().values().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,7 +445,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
data.styles().pseudos
|
data.styles().pseudos
|
||||||
.get(&PseudoElement::Selection).map(|s| s)
|
.get(&PseudoElement::Selection).map(|s| s)
|
||||||
.unwrap_or(&data.styles().primary)
|
.unwrap_or(&data.styles().primary)
|
||||||
.values.clone()
|
.values().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the already resolved style of the node.
|
/// Returns the already resolved style of the node.
|
||||||
|
@ -460,10 +460,10 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
let data = self.get_style_data().unwrap().borrow();
|
let data = self.get_style_data().unwrap().borrow();
|
||||||
match self.get_pseudo_element_type() {
|
match self.get_pseudo_element_type() {
|
||||||
PseudoElementType::Normal
|
PseudoElementType::Normal
|
||||||
=> data.styles().primary.values.clone(),
|
=> data.styles().primary.values().clone(),
|
||||||
other
|
other
|
||||||
=> data.styles().pseudos
|
=> data.styles().pseudos
|
||||||
.get(&other.style_pseudo_element()).unwrap().values.clone(),
|
.get(&other.style_pseudo_element()).unwrap().values().clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,9 @@ pub struct ComputedStyle {
|
||||||
pub rules: StrongRuleNode,
|
pub rules: StrongRuleNode,
|
||||||
|
|
||||||
/// The computed values for each property obtained by cascading the
|
/// The computed values for each property obtained by cascading the
|
||||||
/// matched rules.
|
/// matched rules. This can only be none during a transient interval of
|
||||||
pub values: Arc<ComputedValues>,
|
/// the styling algorithm, and callers can safely unwrap it.
|
||||||
|
pub values: Option<Arc<ComputedValues>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedStyle {
|
impl ComputedStyle {
|
||||||
|
@ -39,9 +40,29 @@ impl ComputedStyle {
|
||||||
pub fn new(rules: StrongRuleNode, values: Arc<ComputedValues>) -> Self {
|
pub fn new(rules: StrongRuleNode, values: Arc<ComputedValues>) -> Self {
|
||||||
ComputedStyle {
|
ComputedStyle {
|
||||||
rules: rules,
|
rules: rules,
|
||||||
values: values,
|
values: Some(values),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a partial ComputedStyle, whose ComputedVaues will be filled
|
||||||
|
/// in later.
|
||||||
|
pub fn new_partial(rules: StrongRuleNode) -> Self {
|
||||||
|
ComputedStyle {
|
||||||
|
rules: rules,
|
||||||
|
values: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the ComputedValues. The values can only be null during
|
||||||
|
/// the styling algorithm, so this is safe to call elsewhere.
|
||||||
|
pub fn values(&self) -> &Arc<ComputedValues> {
|
||||||
|
self.values.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutable version of the above.
|
||||||
|
pub fn values_mut(&mut self) -> &mut Arc<ComputedValues> {
|
||||||
|
self.values.as_mut().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We manually implement Debug for ComputedStyle so that we can avoid the
|
// We manually implement Debug for ComputedStyle so that we can avoid the
|
||||||
|
@ -55,13 +76,6 @@ impl fmt::Debug for ComputedStyle {
|
||||||
type PseudoStylesInner = HashMap<PseudoElement, ComputedStyle,
|
type PseudoStylesInner = HashMap<PseudoElement, ComputedStyle,
|
||||||
BuildHasherDefault<::fnv::FnvHasher>>;
|
BuildHasherDefault<::fnv::FnvHasher>>;
|
||||||
|
|
||||||
/// The rule nodes for each of the pseudo-elements of an element.
|
|
||||||
///
|
|
||||||
/// TODO(emilio): Probably shouldn't be a `HashMap` by default, but a smaller
|
|
||||||
/// array.
|
|
||||||
pub type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode,
|
|
||||||
BuildHasherDefault<::fnv::FnvHasher>>;
|
|
||||||
|
|
||||||
/// A set of styles for a given element's pseudo-elements.
|
/// A set of styles for a given element's pseudo-elements.
|
||||||
///
|
///
|
||||||
/// This is a map from pseudo-element to `ComputedStyle`.
|
/// This is a map from pseudo-element to `ComputedStyle`.
|
||||||
|
@ -76,19 +90,6 @@ impl PseudoStyles {
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
PseudoStyles(HashMap::with_hasher(Default::default()))
|
PseudoStyles(HashMap::with_hasher(Default::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the rules that the different pseudo-elements matched.
|
|
||||||
///
|
|
||||||
/// FIXME(emilio): We could in theory avoid creating these when we have
|
|
||||||
/// support for just re-cascading an element. Then the input to
|
|
||||||
/// `cascade_node` could be `MatchResults` or just `UseExistingStyle`.
|
|
||||||
pub fn get_rules(&self) -> PseudoRuleNodes {
|
|
||||||
let mut rules = HashMap::with_hasher(Default::default());
|
|
||||||
for (pseudo, style) in &self.0 {
|
|
||||||
rules.insert(pseudo.clone(), style.rules.clone());
|
|
||||||
}
|
|
||||||
rules
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for PseudoStyles {
|
impl Deref for PseudoStyles {
|
||||||
|
@ -121,7 +122,7 @@ impl ElementStyles {
|
||||||
|
|
||||||
/// Whether this element `display` value is `none`.
|
/// Whether this element `display` value is `none`.
|
||||||
pub fn is_display_none(&self) -> bool {
|
pub fn is_display_none(&self) -> bool {
|
||||||
self.primary.values.get_box().clone_display() == display::T::none
|
self.primary.values().get_box().clone_display() == display::T::none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +447,14 @@ impl ElementData {
|
||||||
/// Gets a mutable reference to the element styles. Panic if the element has
|
/// Gets a mutable reference to the element styles. Panic if the element has
|
||||||
/// never been styled.
|
/// never been styled.
|
||||||
pub fn styles_mut(&mut self) -> &mut ElementStyles {
|
pub fn styles_mut(&mut self) -> &mut ElementStyles {
|
||||||
self.styles.as_mut().expect("Caling styles_mut() on unstyled ElementData")
|
self.styles.as_mut().expect("Calling styles_mut() on unstyled ElementData")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrows both styles and restyle mutably at the same time.
|
||||||
|
pub fn styles_and_restyle_mut(&mut self) -> (&mut ElementStyles,
|
||||||
|
Option<&mut RestyleData>) {
|
||||||
|
(self.styles.as_mut().unwrap(),
|
||||||
|
self.restyle.as_mut().map(|r| &mut **r))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the computed element styles.
|
/// Sets the computed element styles.
|
||||||
|
|
|
@ -197,7 +197,7 @@ fn fmt_with_data_and_primary_values<N: TNode>(f: &mut fmt::Formatter, n: N) -> f
|
||||||
let dd = el.has_dirty_descendants();
|
let dd = el.has_dirty_descendants();
|
||||||
let data = el.borrow_data();
|
let data = el.borrow_data();
|
||||||
let styles = data.as_ref().and_then(|d| d.get_styles());
|
let styles = data.as_ref().and_then(|d| d.get_styles());
|
||||||
let values = styles.map(|s| &s.primary.values);
|
let values = styles.map(|s| s.primary.values());
|
||||||
write!(f, "{:?} dd={} data={:?} values={:?}", el, dd, &data, values)
|
write!(f, "{:?} dd={} data={:?} values={:?}", el, dd, &data, values)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{:?}", n)
|
write!(f, "{:?}", n)
|
||||||
|
@ -276,7 +276,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
||||||
/// values as an argument here, but otherwise Servo would crash due to
|
/// values as an argument here, but otherwise Servo would crash due to
|
||||||
/// double borrows to return it.
|
/// double borrows to return it.
|
||||||
fn existing_style_for_restyle_damage<'a>(&'a self,
|
fn existing_style_for_restyle_damage<'a>(&'a self,
|
||||||
current_computed_values: Option<&'a Arc<ComputedValues>>,
|
current_computed_values: &'a Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> Option<&'a PreExistingComputedValues>;
|
-> Option<&'a PreExistingComputedValues>;
|
||||||
|
|
||||||
|
|
|
@ -451,14 +451,9 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn existing_style_for_restyle_damage<'a>(&'a self,
|
fn existing_style_for_restyle_damage<'a>(&'a self,
|
||||||
current_cv: Option<&'a Arc<ComputedValues>>,
|
_existing_values: &'a Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> Option<&'a nsStyleContext> {
|
-> Option<&'a nsStyleContext> {
|
||||||
if current_cv.is_none() {
|
|
||||||
// Don't bother in doing an ffi call to get null back.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let atom_ptr = pseudo.map(|p| p.as_atom().as_ptr())
|
let atom_ptr = pseudo.map(|p| p.as_atom().as_ptr())
|
||||||
.unwrap_or(ptr::null_mut());
|
.unwrap_or(ptr::null_mut());
|
||||||
|
|
|
@ -13,7 +13,7 @@ use atomic_refcell::AtomicRefMut;
|
||||||
use cache::LRUCache;
|
use cache::LRUCache;
|
||||||
use cascade_info::CascadeInfo;
|
use cascade_info::CascadeInfo;
|
||||||
use context::{SequentialTask, SharedStyleContext, StyleContext};
|
use context::{SequentialTask, SharedStyleContext, StyleContext};
|
||||||
use data::{ComputedStyle, ElementData, ElementStyles, PseudoRuleNodes, PseudoStyles};
|
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
||||||
use dom::{SendElement, TElement, TNode};
|
use dom::{SendElement, TElement, TNode};
|
||||||
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
|
@ -22,11 +22,11 @@ use rule_tree::{CascadeLevel, StrongRuleNode};
|
||||||
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
|
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
|
||||||
use selectors::MatchAttr;
|
use selectors::MatchAttr;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, AFFECTED_BY_STYLE_ATTRIBUTE};
|
|
||||||
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
||||||
|
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
use sink::ForgetfulSink;
|
use sink::ForgetfulSink;
|
||||||
use std::collections::HashMap;
|
use std::collections::hash_map::Entry;
|
||||||
use std::slice::IterMut;
|
use std::slice::IterMut;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stylist::ApplicableDeclarationBlock;
|
use stylist::ApplicableDeclarationBlock;
|
||||||
|
@ -61,22 +61,21 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The results of selector matching on an element.
|
/// The results returned from running selector matching on an element.
|
||||||
pub struct MatchResults {
|
pub struct MatchResults {
|
||||||
/// The rule node reference that represents the rules matched by the
|
|
||||||
/// element.
|
|
||||||
pub primary: StrongRuleNode,
|
|
||||||
/// A set of style relations (different hints about what rules matched or
|
/// A set of style relations (different hints about what rules matched or
|
||||||
/// could have matched).
|
/// could have matched). This is necessary if the style will be shared.
|
||||||
pub relations: StyleRelations,
|
/// If None, the style will not be shared.
|
||||||
/// The results of selector-matching the pseudo-elements.
|
pub primary_relations: Option<StyleRelations>,
|
||||||
pub per_pseudo: PseudoRuleNodes,
|
/// Whether the rule nodes changed during selector matching.
|
||||||
|
pub rule_nodes_changed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchResults {
|
impl MatchResults {
|
||||||
/// Returns true if the primary rule node is shareable with other nodes.
|
/// Returns true if the primary rule node is shareable with other nodes.
|
||||||
pub fn primary_is_shareable(&self) -> bool {
|
pub fn primary_is_shareable(&self) -> bool {
|
||||||
relations_are_shareable(&self.relations)
|
self.primary_relations.as_ref()
|
||||||
|
.map_or(false, relations_are_shareable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,7 +433,7 @@ pub enum StyleSharingResult {
|
||||||
StyleWasShared(usize),
|
StyleWasShared(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Callers need to pass several boolean flags to cascade_node_pseudo_element.
|
/// Callers need to pass several boolean flags to cascade_primary_or_pseudo.
|
||||||
/// We encapsulate them in this struct to avoid mixing them up.
|
/// We encapsulate them in this struct to avoid mixing them up.
|
||||||
///
|
///
|
||||||
/// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
|
/// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
|
||||||
|
@ -444,18 +443,12 @@ struct CascadeBooleans {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PrivateMatchMethods: TElement {
|
trait PrivateMatchMethods: TElement {
|
||||||
/// Actually cascades style for a node or a pseudo-element of a node.
|
fn cascade_internal(&self,
|
||||||
///
|
context: &StyleContext<Self>,
|
||||||
/// Note that animations only apply to nodes or ::before or ::after
|
primary_style: &ComputedStyle,
|
||||||
/// pseudo-elements.
|
pseudo_style: &mut Option<(&PseudoElement, &mut ComputedStyle)>,
|
||||||
fn cascade_node_pseudo_element<'a>(&self,
|
booleans: &CascadeBooleans)
|
||||||
context: &StyleContext<Self>,
|
-> Arc<ComputedValues> {
|
||||||
parent_style: Option<&Arc<ComputedValues>>,
|
|
||||||
old_style: Option<&Arc<ComputedValues>>,
|
|
||||||
rule_node: &StrongRuleNode,
|
|
||||||
possibly_expired_animations: &[PropertyAnimation],
|
|
||||||
booleans: CascadeBooleans)
|
|
||||||
-> Arc<ComputedValues> {
|
|
||||||
let shared_context = context.shared;
|
let shared_context = context.shared;
|
||||||
let mut cascade_info = CascadeInfo::new();
|
let mut cascade_info = CascadeInfo::new();
|
||||||
let mut cascade_flags = CascadeFlags::empty();
|
let mut cascade_flags = CascadeFlags::empty();
|
||||||
|
@ -466,53 +459,126 @@ trait PrivateMatchMethods: TElement {
|
||||||
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
|
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_style = match parent_style {
|
// Grab the rule node.
|
||||||
Some(ref parent_style) => {
|
let rule_node = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1).rules;
|
||||||
cascade(shared_context.viewport_size,
|
|
||||||
rule_node,
|
// Grab the inherited values.
|
||||||
Some(&***parent_style),
|
let parent_el;
|
||||||
&shared_context.default_computed_values,
|
let parent_data;
|
||||||
Some(&mut cascade_info),
|
let inherited_values_ = if pseudo_style.is_none() {
|
||||||
shared_context.error_reporter.clone(),
|
parent_el = self.parent_element();
|
||||||
cascade_flags)
|
parent_data = parent_el.as_ref().and_then(|x| x.borrow_data());
|
||||||
}
|
let parent_values = parent_data.as_ref().map(|d| {
|
||||||
None => {
|
// Sometimes Gecko eagerly styles things without processing
|
||||||
cascade(shared_context.viewport_size,
|
// pending restyles first. In general we'd like to avoid this,
|
||||||
rule_node,
|
// but there can be good reasons (for example, needing to
|
||||||
None,
|
// construct a frame for some small piece of newly-added
|
||||||
&shared_context.default_computed_values,
|
// content in order to do something specific with that frame,
|
||||||
Some(&mut cascade_info),
|
// but not wanting to flush all of layout).
|
||||||
shared_context.error_reporter.clone(),
|
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
|
||||||
cascade_flags)
|
d.styles().primary.values()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Propagate the "can be fragmented" bit. It would be nice to
|
||||||
|
// encapsulate this better.
|
||||||
|
if let Some(ref p) = parent_values {
|
||||||
|
let can_be_fragmented =
|
||||||
|
p.is_multicol() || parent_el.unwrap().as_node().can_be_fragmented();
|
||||||
|
unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent_values
|
||||||
|
} else {
|
||||||
|
Some(primary_style.values())
|
||||||
};
|
};
|
||||||
|
let inherited_values = inherited_values_.map(|x| &**x);
|
||||||
|
|
||||||
|
// Invoke the cascade algorithm.
|
||||||
|
let values =
|
||||||
|
Arc::new(cascade(shared_context.viewport_size,
|
||||||
|
rule_node,
|
||||||
|
inherited_values,
|
||||||
|
&shared_context.default_computed_values,
|
||||||
|
Some(&mut cascade_info),
|
||||||
|
shared_context.error_reporter.clone(),
|
||||||
|
cascade_flags));
|
||||||
|
|
||||||
cascade_info.finish(&self.as_node());
|
cascade_info.finish(&self.as_node());
|
||||||
|
values
|
||||||
|
}
|
||||||
|
|
||||||
let mut this_style = Arc::new(this_style);
|
/// Computes values and damage for the primary or pseudo style of an element,
|
||||||
|
/// setting them on the ElementData.
|
||||||
|
fn cascade_primary_or_pseudo<'a>(&self,
|
||||||
|
context: &StyleContext<Self>,
|
||||||
|
data: &mut ElementData,
|
||||||
|
pseudo: Option<&PseudoElement>,
|
||||||
|
possibly_expired_animations: &mut Vec<PropertyAnimation>,
|
||||||
|
booleans: CascadeBooleans) {
|
||||||
|
// Collect some values.
|
||||||
|
let shared_context = context.shared;
|
||||||
|
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||||
|
let mut primary_style = &mut styles.primary;
|
||||||
|
let pseudos = &mut styles.pseudos;
|
||||||
|
let mut pseudo_style = pseudo.map(|p| (p, pseudos.get_mut(p).unwrap()));
|
||||||
|
let mut old_values =
|
||||||
|
pseudo_style.as_mut().map_or_else(|| primary_style.values.take(), |p| p.1.values.take());
|
||||||
|
|
||||||
|
// Compute the new values.
|
||||||
|
let mut new_values = self.cascade_internal(context, primary_style,
|
||||||
|
&mut pseudo_style, &booleans);
|
||||||
|
|
||||||
|
// Handle animations.
|
||||||
if booleans.animate {
|
if booleans.animate {
|
||||||
|
if let Some(ref mut old) = old_values {
|
||||||
|
self.update_animations_for_cascade(shared_context, old,
|
||||||
|
possibly_expired_animations);
|
||||||
|
}
|
||||||
|
|
||||||
let new_animations_sender = &context.thread_local.new_animations_sender;
|
let new_animations_sender = &context.thread_local.new_animations_sender;
|
||||||
let this_opaque = self.as_node().opaque();
|
let this_opaque = self.as_node().opaque();
|
||||||
// Trigger any present animations if necessary.
|
// Trigger any present animations if necessary.
|
||||||
animation::maybe_start_animations(&shared_context,
|
animation::maybe_start_animations(&shared_context,
|
||||||
new_animations_sender,
|
new_animations_sender,
|
||||||
this_opaque, &this_style);
|
this_opaque, &new_values);
|
||||||
|
|
||||||
// Trigger transitions if necessary. This will reset `this_style` back
|
// Trigger transitions if necessary. This will reset `new_values` back
|
||||||
// to its old value if it did trigger a transition.
|
// to its old value if it did trigger a transition.
|
||||||
if let Some(ref style) = old_style {
|
if let Some(ref values) = old_values {
|
||||||
animation::start_transitions_if_applicable(
|
animation::start_transitions_if_applicable(
|
||||||
new_animations_sender,
|
new_animations_sender,
|
||||||
this_opaque,
|
this_opaque,
|
||||||
self.as_node().to_unsafe(),
|
self.as_node().to_unsafe(),
|
||||||
&**style,
|
&**values,
|
||||||
&mut this_style,
|
&mut new_values,
|
||||||
&shared_context.timer,
|
&shared_context.timer,
|
||||||
&possibly_expired_animations);
|
&possibly_expired_animations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this_style
|
// Accumulate restyle damage.
|
||||||
|
if let Some(old) = old_values {
|
||||||
|
self.accumulate_damage(restyle.unwrap(), &old, &new_values, pseudo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new computed values.
|
||||||
|
if let Some((_, ref mut style)) = pseudo_style {
|
||||||
|
style.values = Some(new_values);
|
||||||
|
} else {
|
||||||
|
primary_style.values = Some(new_values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes and applies restyle damage unless we've already maxed it out.
|
||||||
|
fn accumulate_damage(&self,
|
||||||
|
restyle: &mut RestyleData,
|
||||||
|
old_values: &Arc<ComputedValues>,
|
||||||
|
new_values: &Arc<ComputedValues>,
|
||||||
|
pseudo: Option<&PseudoElement>) {
|
||||||
|
if restyle.damage != RestyleDamage::rebuild_and_reflow() {
|
||||||
|
let d = self.compute_restyle_damage(&old_values, &new_values, pseudo);
|
||||||
|
restyle.damage |= d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_animations_for_cascade(&self,
|
fn update_animations_for_cascade(&self,
|
||||||
|
@ -576,9 +642,10 @@ impl<E: TElement> PrivateMatchMethods for E {}
|
||||||
|
|
||||||
/// The public API that elements expose for selector matching.
|
/// The public API that elements expose for selector matching.
|
||||||
pub trait MatchMethods : TElement {
|
pub trait MatchMethods : TElement {
|
||||||
/// Runs selector matching of this element, and returns the result.
|
/// Runs selector matching to (re)compute rule nodes for this element.
|
||||||
fn match_element(&self,
|
fn match_element(&self,
|
||||||
context: &mut StyleContext<Self>)
|
context: &mut StyleContext<Self>,
|
||||||
|
data: &mut ElementData)
|
||||||
-> MatchResults
|
-> MatchResults
|
||||||
{
|
{
|
||||||
let mut applicable_declarations =
|
let mut applicable_declarations =
|
||||||
|
@ -588,6 +655,7 @@ pub trait MatchMethods : TElement {
|
||||||
let style_attribute = self.style_attribute();
|
let style_attribute = self.style_attribute();
|
||||||
let animation_rules = self.get_animation_rules(None);
|
let animation_rules = self.get_animation_rules(None);
|
||||||
let mut flags = ElementSelectorFlags::empty();
|
let mut flags = ElementSelectorFlags::empty();
|
||||||
|
let mut rule_nodes_changed = false;
|
||||||
|
|
||||||
// Compute the primary rule node.
|
// Compute the primary rule node.
|
||||||
let mut primary_relations =
|
let mut primary_relations =
|
||||||
|
@ -599,10 +667,18 @@ pub trait MatchMethods : TElement {
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
&mut flags);
|
&mut flags);
|
||||||
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
|
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
|
||||||
|
if !data.has_styles() {
|
||||||
|
data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
|
||||||
|
rule_nodes_changed = true;
|
||||||
|
} else if data.styles().primary.rules != primary_rule_node {
|
||||||
|
data.styles_mut().primary.rules = primary_rule_node;
|
||||||
|
rule_nodes_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the pseudo rule nodes.
|
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
||||||
let mut per_pseudo: PseudoRuleNodes = HashMap::with_hasher(Default::default());
|
let mut matches_different_pseudos = false;
|
||||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
|
let mut per_pseudo = &mut data.styles_mut().pseudos;
|
||||||
debug_assert!(applicable_declarations.is_empty());
|
debug_assert!(applicable_declarations.is_empty());
|
||||||
let pseudo_animation_rules = self.get_animation_rules(Some(&pseudo));
|
let pseudo_animation_rules = self.get_animation_rules(Some(&pseudo));
|
||||||
stylist.push_applicable_declarations(self,
|
stylist.push_applicable_declarations(self,
|
||||||
|
@ -613,13 +689,35 @@ pub trait MatchMethods : TElement {
|
||||||
&mut flags);
|
&mut flags);
|
||||||
|
|
||||||
if !applicable_declarations.is_empty() {
|
if !applicable_declarations.is_empty() {
|
||||||
let rule_node = compute_rule_node(context, &mut applicable_declarations);
|
let new_rules = compute_rule_node(context, &mut applicable_declarations);
|
||||||
per_pseudo.insert(pseudo, rule_node);
|
match per_pseudo.entry(pseudo) {
|
||||||
|
Entry::Occupied(mut e) => {
|
||||||
|
if e.get().rules != new_rules {
|
||||||
|
e.get_mut().rules = new_rules;
|
||||||
|
rule_nodes_changed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
e.insert(ComputedStyle::new_partial(new_rules));
|
||||||
|
matches_different_pseudos = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if per_pseudo.remove(&pseudo).is_some() {
|
||||||
|
matches_different_pseudos = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if matches_different_pseudos {
|
||||||
|
rule_nodes_changed = true;
|
||||||
|
if let Some(r) = data.get_restyle_mut() {
|
||||||
|
// Any changes to the matched pseudo-elements trigger
|
||||||
|
// reconstruction.
|
||||||
|
r.damage |= RestyleDamage::rebuild_and_reflow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we have any pseudo elements, indicate so in the primary StyleRelations.
|
// If we have any pseudo elements, indicate so in the primary StyleRelations.
|
||||||
if !per_pseudo.is_empty() {
|
if !data.styles().pseudos.is_empty() {
|
||||||
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,63 +738,45 @@ pub trait MatchMethods : TElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResults {
|
MatchResults {
|
||||||
primary: primary_rule_node,
|
primary_relations: Some(primary_relations),
|
||||||
relations: primary_relations,
|
rule_nodes_changed: rule_nodes_changed,
|
||||||
per_pseudo: per_pseudo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the appropriate MatchResults from the current styles, to perform a
|
|
||||||
/// recascade.
|
|
||||||
///
|
|
||||||
/// TODO(emilio): Stop using `MachResults`, use an enum, or something.
|
|
||||||
fn match_results_from_current_style(&self, data: &ElementData) -> MatchResults {
|
|
||||||
let rule_node = data.styles().primary.rules.clone();
|
|
||||||
MatchResults {
|
|
||||||
primary: rule_node,
|
|
||||||
// FIXME(emilio): Same concern as below.
|
|
||||||
relations: StyleRelations::empty(),
|
|
||||||
// The per-pseudo rule-nodes haven't changed, but still need to be
|
|
||||||
// recascaded.
|
|
||||||
per_pseudo: data.styles().pseudos.get_rules(),
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the rule nodes without re-running selector matching, using just
|
/// Updates the rule nodes without re-running selector matching, using just
|
||||||
/// the rule tree.
|
/// the rule tree. Returns true if the rule nodes changed.
|
||||||
fn cascade_with_replacements(&self,
|
fn cascade_with_replacements(&self,
|
||||||
hint: RestyleHint,
|
hint: RestyleHint,
|
||||||
context: &StyleContext<Self>,
|
context: &StyleContext<Self>,
|
||||||
data: &mut AtomicRefMut<ElementData>)
|
data: &mut AtomicRefMut<ElementData>)
|
||||||
-> MatchResults {
|
-> bool {
|
||||||
let mut rule_node = data.styles().primary.rules.clone();
|
let primary_rules = &mut data.styles_mut().primary.rules;
|
||||||
|
let mut rule_node_changed = false;
|
||||||
|
|
||||||
if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
|
||||||
let style_attribute = self.style_attribute();
|
let style_attribute = self.style_attribute();
|
||||||
|
|
||||||
rule_node = context.shared.stylist.rule_tree
|
let new_node = context.shared.stylist.rule_tree
|
||||||
.update_rule_at_level(CascadeLevel::StyleAttributeNormal,
|
.update_rule_at_level(CascadeLevel::StyleAttributeNormal,
|
||||||
style_attribute,
|
style_attribute,
|
||||||
rule_node);
|
primary_rules);
|
||||||
|
if let Some(n) = new_node {
|
||||||
|
*primary_rules = n;
|
||||||
|
rule_node_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
rule_node = context.shared.stylist.rule_tree
|
let new_node = context.shared.stylist.rule_tree
|
||||||
.update_rule_at_level(CascadeLevel::StyleAttributeImportant,
|
.update_rule_at_level(CascadeLevel::StyleAttributeImportant,
|
||||||
style_attribute,
|
style_attribute,
|
||||||
rule_node);
|
primary_rules);
|
||||||
|
if let Some(n) = new_node {
|
||||||
|
*primary_rules = n;
|
||||||
|
rule_node_changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResults {
|
// The per-pseudo rule nodes never change in this path.
|
||||||
primary: rule_node,
|
rule_node_changed
|
||||||
// FIXME(emilio): This is ok, for now, we shouldn't need to fake
|
|
||||||
// this.
|
|
||||||
relations: AFFECTED_BY_STYLE_ATTRIBUTE,
|
|
||||||
// The per-pseudo rule-nodes haven't changed, but still need to be
|
|
||||||
// recomputed.
|
|
||||||
//
|
|
||||||
// TODO(emilio): We could probably optimize this quite a bit.
|
|
||||||
per_pseudo: data.styles().pseudos.get_rules(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to share a style with another node. This method is unsafe
|
/// Attempts to share a style with another node. This method is unsafe
|
||||||
|
@ -732,21 +812,18 @@ pub trait MatchMethods : TElement {
|
||||||
Ok(shared_style) => {
|
Ok(shared_style) => {
|
||||||
// Yay, cache hit. Share the style.
|
// Yay, cache hit. Share the style.
|
||||||
|
|
||||||
// TODO: add the display: none optimisation here too! Even
|
// Accumulate restyle damage.
|
||||||
// better, factor it out/make it a bit more generic so Gecko
|
debug_assert_eq!(data.has_styles(), data.has_restyle());
|
||||||
// can decide more easily if it knows that it's a child of
|
let old_values = data.get_styles_mut()
|
||||||
// replaced content, or similar stuff!
|
.and_then(|s| s.primary.values.take());
|
||||||
let maybe_damage = {
|
if let Some(old) = old_values {
|
||||||
let previous = data.get_styles().map(|x| &x.primary.values);
|
self.accumulate_damage(data.restyle_mut(), &old,
|
||||||
let existing = self.existing_style_for_restyle_damage(previous, None);
|
shared_style.values(), None);
|
||||||
existing.map(|e| RestyleDamage::compute(e, &shared_style.values))
|
|
||||||
};
|
|
||||||
if let Some(d) = maybe_damage {
|
|
||||||
data.restyle_mut().damage |= d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We never put elements with pseudo style into the style sharing cache,
|
// We never put elements with pseudo style into the style
|
||||||
// so we can just mint an ElementStyles directly here.
|
// sharing cache, so we can just mint an ElementStyles
|
||||||
|
// directly here.
|
||||||
//
|
//
|
||||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1329361
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1329361
|
||||||
let styles = ElementStyles::new(shared_style);
|
let styles = ElementStyles::new(shared_style);
|
||||||
|
@ -829,203 +906,77 @@ pub trait MatchMethods : TElement {
|
||||||
/// 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_restyle_damage(&self,
|
||||||
old_style: Option<&Arc<ComputedValues>>,
|
old_values: &Arc<ComputedValues>,
|
||||||
new_style: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> RestyleDamage
|
-> RestyleDamage
|
||||||
{
|
{
|
||||||
match self.existing_style_for_restyle_damage(old_style, pseudo) {
|
match self.existing_style_for_restyle_damage(old_values, pseudo) {
|
||||||
Some(ref source) => RestyleDamage::compute(source, new_style),
|
Some(ref source) => RestyleDamage::compute(source, new_values),
|
||||||
None => {
|
None => {
|
||||||
// If there's no style source, two things can happen:
|
// If there's no style source, that likely means that Gecko
|
||||||
//
|
// couldn't find a style context. This happens with display:none
|
||||||
// 1. This is not an incremental restyle (old_style is none).
|
// elements, and probably a number of other edge cases that
|
||||||
// In this case we can't do too much than sending
|
// we don't handle well yet (like display:contents).
|
||||||
// rebuild_and_reflow.
|
if new_values.get_box().clone_display() == display::T::none &&
|
||||||
//
|
old_values.get_box().clone_display() == display::T::none {
|
||||||
// 2. This is an incremental restyle, but the old display value
|
// The style remains display:none. No need for damage.
|
||||||
// is none, so there's no effective way for Gecko to get the
|
RestyleDamage::empty()
|
||||||
// style source (which is the style context).
|
} else {
|
||||||
//
|
// Something else. Be conservative for now.
|
||||||
// In this case, we could return either
|
RestyleDamage::rebuild_and_reflow()
|
||||||
// RestyleDamage::empty(), in the case both displays are
|
|
||||||
// none, or rebuild_and_reflow, otherwise.
|
|
||||||
//
|
|
||||||
if let Some(old_style) = old_style {
|
|
||||||
// FIXME(emilio): This should assert the old style is
|
|
||||||
// display: none, but we still can't get an old style
|
|
||||||
// context for other stuff that should give us a style
|
|
||||||
// context source like display: contents, so we fall on the
|
|
||||||
// safe side here.
|
|
||||||
if new_style.get_box().clone_display() == display::T::none &&
|
|
||||||
old_style.get_box().clone_display() == display::T::none {
|
|
||||||
return RestyleDamage::empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RestyleDamage::rebuild_and_reflow()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the results of selector matching, run the CSS cascade and style
|
/// Run the CSS cascade and compute values for the element, potentially
|
||||||
/// the node, potentially starting any new transitions or animations.
|
/// starting any new transitions or animations.
|
||||||
fn cascade_node(&self,
|
fn cascade_element(&self,
|
||||||
context: &StyleContext<Self>,
|
context: &StyleContext<Self>,
|
||||||
mut data: &mut AtomicRefMut<ElementData>,
|
mut data: &mut AtomicRefMut<ElementData>,
|
||||||
parent: Option<Self>,
|
primary_is_shareable: bool)
|
||||||
primary_rule_node: StrongRuleNode,
|
|
||||||
pseudo_rule_nodes: PseudoRuleNodes,
|
|
||||||
primary_is_shareable: bool)
|
|
||||||
{
|
{
|
||||||
// Get our parent's style.
|
|
||||||
let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap());
|
|
||||||
let parent_style = parent_data.as_ref().map(|d| {
|
|
||||||
// Sometimes Gecko eagerly styles things without processing pending
|
|
||||||
// restyles first. In general we'd like to avoid this, but there can
|
|
||||||
// be good reasons (for example, needing to construct a frame for
|
|
||||||
// some small piece of newly-added content in order to do something
|
|
||||||
// specific with that frame, but not wanting to flush all of
|
|
||||||
// layout).
|
|
||||||
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
|
|
||||||
&d.styles().primary.values
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut new_styles;
|
|
||||||
let mut possibly_expired_animations = vec![];
|
let mut possibly_expired_animations = vec![];
|
||||||
|
|
||||||
let damage = {
|
// Cascade the primary style.
|
||||||
debug_assert!(!data.has_current_styles());
|
self.cascade_primary_or_pseudo(context, data, None,
|
||||||
let (old_primary, old_pseudos) = match data.get_styles_mut() {
|
&mut possibly_expired_animations,
|
||||||
None => (None, None),
|
CascadeBooleans {
|
||||||
Some(previous) => {
|
shareable: primary_is_shareable,
|
||||||
// Update animations before the cascade. This may modify the
|
animate: true,
|
||||||
// value of the old primary style.
|
});
|
||||||
self.update_animations_for_cascade(&context.shared,
|
|
||||||
&mut previous.primary.values,
|
|
||||||
&mut possibly_expired_animations);
|
|
||||||
(Some(&previous.primary.values), Some(&mut previous.pseudos))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_style =
|
// Check whether the primary style is display:none.
|
||||||
self.cascade_node_pseudo_element(context,
|
let display_none = data.styles().primary.values().get_box().clone_display() ==
|
||||||
parent_style,
|
display::T::none;
|
||||||
old_primary,
|
|
||||||
&primary_rule_node,
|
|
||||||
&possibly_expired_animations,
|
|
||||||
CascadeBooleans {
|
|
||||||
shareable: primary_is_shareable,
|
|
||||||
animate: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let primary = ComputedStyle::new(primary_rule_node, new_style);
|
// Cascade each pseudo-element.
|
||||||
new_styles = ElementStyles::new(primary);
|
//
|
||||||
|
// Note that we've already set up the map of matching pseudo-elements
|
||||||
let damage =
|
// in match_element (and handled the damage implications of changing
|
||||||
self.compute_damage_and_cascade_pseudos(old_primary,
|
// which pseudos match), so now we can just iterate the map. This does
|
||||||
old_pseudos,
|
// mean collecting the keys, so that the borrow checker will let us pass
|
||||||
&new_styles.primary.values,
|
// the mutable |data| to the inner cascade function.
|
||||||
&mut new_styles.pseudos,
|
let matched_pseudos: Vec<PseudoElement> =
|
||||||
context,
|
data.styles().pseudos.keys().cloned().collect();
|
||||||
pseudo_rule_nodes,
|
for pseudo in matched_pseudos {
|
||||||
&mut possibly_expired_animations);
|
// If the new primary style is display:none, we don't need pseudo
|
||||||
|
// styles, but we still need to clear any stale values.
|
||||||
unsafe {
|
if display_none {
|
||||||
self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
|
data.styles_mut().pseudos.get_mut(&pseudo).unwrap().values = None;
|
||||||
p.as_node().can_be_fragmented() ||
|
continue;
|
||||||
parent_style.unwrap().is_multicol()
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
damage
|
// Only ::before and ::after are animatable.
|
||||||
};
|
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
|
||||||
|
self.cascade_primary_or_pseudo(context, data, Some(&pseudo),
|
||||||
if data.has_styles() {
|
&mut possibly_expired_animations,
|
||||||
data.restyle_mut().damage |= damage;
|
CascadeBooleans {
|
||||||
|
shareable: false,
|
||||||
|
animate: animate,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
data.set_styles(new_styles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given the old and new styling results, compute the final restyle damage.
|
|
||||||
fn compute_damage_and_cascade_pseudos(
|
|
||||||
&self,
|
|
||||||
old_primary: Option<&Arc<ComputedValues>>,
|
|
||||||
mut old_pseudos: Option<&mut PseudoStyles>,
|
|
||||||
new_primary: &Arc<ComputedValues>,
|
|
||||||
new_pseudos: &mut PseudoStyles,
|
|
||||||
context: &StyleContext<Self>,
|
|
||||||
mut pseudo_rule_nodes: PseudoRuleNodes,
|
|
||||||
possibly_expired_animations: &mut Vec<PropertyAnimation>)
|
|
||||||
-> RestyleDamage
|
|
||||||
{
|
|
||||||
// Compute the damage and sum up the damage related to pseudo-elements.
|
|
||||||
let mut damage =
|
|
||||||
self.compute_restyle_damage(old_primary, new_primary, None);
|
|
||||||
|
|
||||||
// If the new style is display:none, we don't need pseudo-elements styles.
|
|
||||||
if new_primary.get_box().clone_display() == display::T::none {
|
|
||||||
return damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rebuild_and_reflow = RestyleDamage::rebuild_and_reflow();
|
|
||||||
|
|
||||||
debug_assert!(new_pseudos.is_empty());
|
|
||||||
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
|
||||||
let maybe_rule_node = pseudo_rule_nodes.remove(&pseudo);
|
|
||||||
|
|
||||||
// Grab the old pseudo style for analysis.
|
|
||||||
let mut maybe_old_pseudo_style =
|
|
||||||
old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
|
|
||||||
|
|
||||||
if maybe_rule_node.is_some() {
|
|
||||||
let new_rule_node = maybe_rule_node.unwrap();
|
|
||||||
|
|
||||||
// We have declarations, so we need to cascade. Compute parameters.
|
|
||||||
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
|
|
||||||
if animate {
|
|
||||||
if let Some(ref mut old_pseudo_style) = maybe_old_pseudo_style {
|
|
||||||
// Update animations before the cascade. This may modify
|
|
||||||
// the value of old_pseudo_style.
|
|
||||||
self.update_animations_for_cascade(&context.shared,
|
|
||||||
&mut old_pseudo_style.values,
|
|
||||||
possibly_expired_animations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_pseudo_values =
|
|
||||||
self.cascade_node_pseudo_element(context,
|
|
||||||
Some(new_primary),
|
|
||||||
maybe_old_pseudo_style.as_ref()
|
|
||||||
.map(|s| &s.values),
|
|
||||||
&new_rule_node,
|
|
||||||
&possibly_expired_animations,
|
|
||||||
CascadeBooleans {
|
|
||||||
shareable: false,
|
|
||||||
animate: animate,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Compute restyle damage unless we've already maxed it out.
|
|
||||||
if damage != rebuild_and_reflow {
|
|
||||||
damage = damage | match maybe_old_pseudo_style {
|
|
||||||
None => rebuild_and_reflow,
|
|
||||||
Some(ref old) => self.compute_restyle_damage(Some(&old.values),
|
|
||||||
&new_pseudo_values,
|
|
||||||
Some(&pseudo)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the new entry into the map.
|
|
||||||
let new_pseudo_style = ComputedStyle::new(new_rule_node, new_pseudo_values);
|
|
||||||
let existing = new_pseudos.insert(pseudo, new_pseudo_style);
|
|
||||||
debug_assert!(existing.is_none());
|
|
||||||
} else {
|
|
||||||
if maybe_old_pseudo_style.is_some() {
|
|
||||||
damage = rebuild_and_reflow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
damage
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,12 +183,13 @@ impl RuleTree {
|
||||||
|
|
||||||
/// Replaces a rule in a given level (if present) for another rule.
|
/// Replaces a rule in a given level (if present) for another rule.
|
||||||
///
|
///
|
||||||
/// Returns the resulting node that represents the new path.
|
/// Returns the resulting node that represents the new path, or None if
|
||||||
|
/// the old path is still valid.
|
||||||
pub fn update_rule_at_level(&self,
|
pub fn update_rule_at_level(&self,
|
||||||
level: CascadeLevel,
|
level: CascadeLevel,
|
||||||
pdb: Option<&Arc<RwLock<PropertyDeclarationBlock>>>,
|
pdb: Option<&Arc<RwLock<PropertyDeclarationBlock>>>,
|
||||||
path: StrongRuleNode)
|
path: &StrongRuleNode)
|
||||||
-> StrongRuleNode {
|
-> Option<StrongRuleNode> {
|
||||||
debug_assert!(level.is_unique_per_element());
|
debug_assert!(level.is_unique_per_element());
|
||||||
// TODO(emilio): Being smarter with lifetimes we could avoid a bit of
|
// TODO(emilio): Being smarter with lifetimes we could avoid a bit of
|
||||||
// the refcount churn.
|
// the refcount churn.
|
||||||
|
@ -231,7 +232,7 @@ impl RuleTree {
|
||||||
|
|
||||||
if is_here_already {
|
if is_here_already {
|
||||||
debug!("Picking the fast path in rule replacement");
|
debug!("Picking the fast path in rule replacement");
|
||||||
return path;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current = current.parent().unwrap().clone();
|
current = current.parent().unwrap().clone();
|
||||||
|
@ -262,7 +263,7 @@ impl RuleTree {
|
||||||
|
|
||||||
// Now the rule is in the relevant place, push the children as
|
// Now the rule is in the relevant place, push the children as
|
||||||
// necessary.
|
// necessary.
|
||||||
self.insert_ordered_rules_from(current, children.into_iter().rev())
|
Some(self.insert_ordered_rules_from(current, children.into_iter().rev()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +502,7 @@ struct WeakRuleNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A strong reference to a rule node.
|
/// A strong reference to a rule node.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct StrongRuleNode {
|
pub struct StrongRuleNode {
|
||||||
ptr: *mut RuleNode,
|
ptr: *mut RuleNode,
|
||||||
}
|
}
|
||||||
|
|
|
@ -349,7 +349,7 @@ impl Stylist {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.precomputed_values_for_pseudo(&pseudo, Some(parent_style), default_style, inherit_all)
|
self.precomputed_values_for_pseudo(&pseudo, Some(parent_style), default_style, inherit_all)
|
||||||
.values
|
.values.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a pseudo-element style lazily during layout.
|
/// Computes a pseudo-element style lazily during layout.
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
||||||
use data::{ElementData, ElementStyles, RestyleKind, StoredRestyleHint};
|
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
||||||
use dom::{NodeInfo, TElement, TNode};
|
use dom::{NodeInfo, TElement, TNode};
|
||||||
use matching::{MatchMethods, StyleSharingResult};
|
use matching::{MatchMethods, MatchResults};
|
||||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
||||||
use selector_parser::RestyleDamage;
|
use selector_parser::RestyleDamage;
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
|
@ -246,7 +246,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
// recursively drops Servo ElementData when the XBL insertion parent of
|
// recursively drops Servo ElementData when the XBL insertion parent of
|
||||||
// an Element is changed.
|
// an Element is changed.
|
||||||
if cfg!(feature = "gecko") && thread_local.is_initial_style() &&
|
if cfg!(feature = "gecko") && thread_local.is_initial_style() &&
|
||||||
parent_data.styles().primary.values.has_moz_binding() {
|
parent_data.styles().primary.values().has_moz_binding() {
|
||||||
if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); }
|
if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -339,12 +339,9 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>, element: E, ensur
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute our style.
|
// Compute our style.
|
||||||
let match_results = element.match_element(context);
|
let match_results = element.match_element(context, &mut data);
|
||||||
let shareable = match_results.primary_is_shareable();
|
element.cascade_element(context, &mut data,
|
||||||
element.cascade_node(context, &mut data, element.parent_element(),
|
match_results.primary_is_shareable());
|
||||||
match_results.primary,
|
|
||||||
match_results.per_pseudo,
|
|
||||||
shareable);
|
|
||||||
|
|
||||||
// Conservatively mark us as having dirty descendants, since there might
|
// Conservatively mark us as having dirty descendants, since there might
|
||||||
// be other unstyled siblings we miss when walking straight up the parent
|
// be other unstyled siblings we miss when walking straight up the parent
|
||||||
|
@ -428,7 +425,18 @@ 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 {
|
||||||
inherited_style_changed = compute_style(traversal, traversal_data, context, element, &mut data);
|
compute_style(traversal, traversal_data, context, element, &mut data);
|
||||||
|
|
||||||
|
// If we're restyling this element to display:none, throw away all style
|
||||||
|
// data in the subtree, notify the caller to early-return.
|
||||||
|
let display_none = data.styles().is_display_none();
|
||||||
|
if display_none {
|
||||||
|
debug!("New element style is display:none - clearing data from descendants.");
|
||||||
|
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
|
||||||
|
@ -452,7 +460,7 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
|
|
||||||
// Make sure the dirty descendants bit is not set for the root of a
|
// Make sure the dirty descendants bit is not set for the root of a
|
||||||
// display:none subtree, even if the style didn't change (since, if
|
// display:none subtree, even if the style didn't change (since, if
|
||||||
// the style did change, we'd have already cleared it in compute_style).
|
// the style did change, we'd have already cleared it above).
|
||||||
//
|
//
|
||||||
// This keeps the tree in a valid state without requiring the DOM to
|
// This keeps the tree in a valid state without requiring the DOM to
|
||||||
// check display:none on the parent when inserting new children (which
|
// check display:none on the parent when inserting new children (which
|
||||||
|
@ -464,101 +472,86 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes style, returning true if the inherited styles changed for this
|
|
||||||
// element.
|
|
||||||
fn compute_style<E, D>(_traversal: &D,
|
fn compute_style<E, D>(_traversal: &D,
|
||||||
traversal_data: &mut PerLevelTraversalData,
|
traversal_data: &mut PerLevelTraversalData,
|
||||||
context: &mut StyleContext<E>,
|
context: &mut StyleContext<E>,
|
||||||
element: E,
|
element: E,
|
||||||
mut data: &mut AtomicRefMut<ElementData>) -> bool
|
mut data: &mut AtomicRefMut<ElementData>)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
|
use data::RestyleKind::*;
|
||||||
|
use matching::StyleSharingResult::*;
|
||||||
|
|
||||||
context.thread_local.statistics.elements_styled += 1;
|
context.thread_local.statistics.elements_styled += 1;
|
||||||
let shared_context = context.shared;
|
let shared_context = context.shared;
|
||||||
|
let kind = data.restyle_kind();
|
||||||
|
|
||||||
// TODO(emilio): Make cascade_input less expensive to compute in the cases
|
// First, try the style sharing cache. If we get a match we can skip the rest
|
||||||
// we don't need to run selector matching.
|
// of the work.
|
||||||
let cascade_input = match data.restyle_kind() {
|
if let MatchAndCascade = kind {
|
||||||
RestyleKind::MatchAndCascade => {
|
let sharing_result = unsafe {
|
||||||
// Check to see whether we can share a style with someone.
|
let cache = &mut context.thread_local.style_sharing_candidate_cache;
|
||||||
let sharing_result = unsafe {
|
element.share_style_if_possible(cache, shared_context, &mut data)
|
||||||
element.share_style_if_possible(&mut context.thread_local.style_sharing_candidate_cache,
|
};
|
||||||
shared_context,
|
if let StyleWasShared(index) = sharing_result {
|
||||||
&mut data)
|
context.thread_local.statistics.styles_shared += 1;
|
||||||
};
|
context.thread_local.style_sharing_candidate_cache.touch(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match sharing_result {
|
let match_results = match kind {
|
||||||
StyleSharingResult::StyleWasShared(index) => {
|
MatchAndCascade => {
|
||||||
context.thread_local.statistics.styles_shared += 1;
|
// Ensure the bloom filter is up to date.
|
||||||
context.thread_local.style_sharing_candidate_cache.touch(index);
|
let dom_depth =
|
||||||
None
|
context.thread_local.bloom_filter
|
||||||
}
|
.insert_parents_recovering(element,
|
||||||
StyleSharingResult::CannotShare => {
|
traversal_data.current_dom_depth);
|
||||||
// Ensure the bloom filter is up to date.
|
|
||||||
let dom_depth =
|
|
||||||
context.thread_local.bloom_filter
|
|
||||||
.insert_parents_recovering(element,
|
|
||||||
traversal_data.current_dom_depth);
|
|
||||||
|
|
||||||
// Update the dom depth with the up-to-date dom depth.
|
// Update the dom depth with the up-to-date dom depth.
|
||||||
//
|
//
|
||||||
// Note that this is always the same than the pre-existing depth,
|
// Note that this is always the same than the pre-existing depth,
|
||||||
// but it can change from unknown to known at this step.
|
// but it can change from unknown to known at this step.
|
||||||
traversal_data.current_dom_depth = Some(dom_depth);
|
traversal_data.current_dom_depth = Some(dom_depth);
|
||||||
|
|
||||||
context.thread_local.bloom_filter.assert_complete(element);
|
context.thread_local.bloom_filter.assert_complete(element);
|
||||||
|
|
||||||
// Perform the CSS selector matching.
|
|
||||||
context.thread_local.statistics.elements_matched += 1;
|
|
||||||
|
|
||||||
Some(element.match_element(context))
|
// Perform CSS selector matching.
|
||||||
}
|
context.thread_local.statistics.elements_matched += 1;
|
||||||
|
element.match_element(context, &mut data)
|
||||||
|
}
|
||||||
|
CascadeWithReplacements(hint) => {
|
||||||
|
let rule_nodes_changed =
|
||||||
|
element.cascade_with_replacements(hint, context, &mut data);
|
||||||
|
MatchResults {
|
||||||
|
primary_relations: None,
|
||||||
|
rule_nodes_changed: rule_nodes_changed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RestyleKind::CascadeWithReplacements(hint) => {
|
CascadeOnly => {
|
||||||
Some(element.cascade_with_replacements(hint, context, &mut data))
|
MatchResults {
|
||||||
}
|
primary_relations: None,
|
||||||
RestyleKind::CascadeOnly => {
|
rule_nodes_changed: false,
|
||||||
// TODO(emilio): Stop doing this work, and teach cascade_node about
|
}
|
||||||
// the current style instead.
|
|
||||||
Some(element.match_results_from_current_style(&*data))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(match_results) = cascade_input {
|
// Cascade properties and compute values.
|
||||||
// Perform the CSS cascade.
|
let shareable = match_results.primary_is_shareable();
|
||||||
let shareable = match_results.primary_is_shareable();
|
unsafe {
|
||||||
unsafe {
|
element.cascade_element(context, &mut data, shareable);
|
||||||
element.cascade_node(context, &mut data,
|
|
||||||
element.parent_element(),
|
|
||||||
match_results.primary,
|
|
||||||
match_results.per_pseudo,
|
|
||||||
shareable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if shareable {
|
|
||||||
// Add ourselves to the LRU cache.
|
|
||||||
context.thread_local
|
|
||||||
.style_sharing_candidate_cache
|
|
||||||
.insert_if_possible(&element,
|
|
||||||
&data.styles().primary.values,
|
|
||||||
match_results.relations);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're restyling this element to display:none, throw away all style data
|
// If the style is shareable, add it to the LRU cache.
|
||||||
// in the subtree, notify the caller to early-return.
|
if shareable {
|
||||||
let display_none = data.styles().is_display_none();
|
context.thread_local
|
||||||
if display_none {
|
.style_sharing_candidate_cache
|
||||||
debug!("New element style is display:none - clearing data from descendants.");
|
.insert_if_possible(&element,
|
||||||
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
data.styles().primary.values(),
|
||||||
|
match_results.primary_relations.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(bholley): Compute this accurately from the call to CalcStyleDifference.
|
|
||||||
let inherited_styles_changed = true;
|
|
||||||
|
|
||||||
inherited_styles_changed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_children<E, D>(traversal: &D,
|
fn preprocess_children<E, D>(traversal: &D,
|
||||||
|
|
|
@ -683,7 +683,7 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
||||||
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
|
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
|
||||||
data.stylist.precomputed_values_for_pseudo(&pseudo, maybe_parent,
|
data.stylist.precomputed_values_for_pseudo(&pseudo, maybe_parent,
|
||||||
data.default_computed_values(), false)
|
data.default_computed_values(), false)
|
||||||
.values
|
.values.unwrap()
|
||||||
.into_strong()
|
.into_strong()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +709,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
|
|
||||||
match get_pseudo_style(element, pseudo_tag, data.styles(), doc_data) {
|
match get_pseudo_style(element, pseudo_tag, data.styles(), doc_data) {
|
||||||
Some(values) => values.into_strong(),
|
Some(values) => values.into_strong(),
|
||||||
None if !is_probe => data.styles().primary.values.clone().into_strong(),
|
None if !is_probe => data.styles().primary.values().clone().into_strong(),
|
||||||
None => Strong::null(),
|
None => Strong::null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,13 +720,13 @@ fn get_pseudo_style(element: GeckoElement, pseudo_tag: *mut nsIAtom,
|
||||||
{
|
{
|
||||||
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
|
let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
|
||||||
match SelectorImpl::pseudo_element_cascade_type(&pseudo) {
|
match SelectorImpl::pseudo_element_cascade_type(&pseudo) {
|
||||||
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values.clone()),
|
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()),
|
||||||
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
||||||
PseudoElementCascadeType::Lazy => {
|
PseudoElementCascadeType::Lazy => {
|
||||||
let d = doc_data.borrow_mut();
|
let d = doc_data.borrow_mut();
|
||||||
let base = &styles.primary.values;
|
let base = styles.primary.values();
|
||||||
d.stylist.lazily_compute_pseudo_element_style(&element, &pseudo, base, &d.default_computed_values())
|
d.stylist.lazily_compute_pseudo_element_style(&element, &pseudo, base, &d.default_computed_values())
|
||||||
.map(|s| s.values.clone())
|
.map(|s| s.values().clone())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1167,7 +1167,7 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
||||||
return per_doc_data.default_computed_values().clone().into_strong();
|
return per_doc_data.default_computed_values().clone().into_strong();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.styles().primary.values.clone().into_strong()
|
data.styles().primary.values().clone().into_strong()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -1184,7 +1184,7 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
maybe_pseudo.unwrap_or_else(|| styles.primary.values.clone())
|
maybe_pseudo.unwrap_or_else(|| styles.primary.values().clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
// In the common case we already have the style. Check that before setting
|
// In the common case we already have the style. Check that before setting
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue