mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Make Restyle tracking more granular.
The primary idea of this patch is to ditch the rigid enum of Previous/Current styles, and replace it with a series of indicators for the various types of work that needs to be performed (expanding snapshots, rematching, recascading, and damage processing). This loses us a little bit of sanity checking (since the up-to-date-ness of our style is no longer baked into the type system), but gives us a lot more flexibility that we'll need going forward (especially when we separate matching from cascading). We also eliminate get_styling_mode in favor of a method on the traversal. This patch does a few other things as ridealongs: * Temporarily eliminates the handling for transfering ownership of styles to the frame. We'll need this again at some point, but for now it's causing too much complexity for a half-implemented feature. * Ditches TRestyleDamage, which is no longer necessary post-crate-merge, and is a constant source of compilation failures from either needing to be imported or being unnecessarily imported (which varies between gecko and servo). * Expands Snapshots for the traversal root, which was missing before. * Fixes up the skip_root stuff to avoid visiting the skipped root. * Unifies parallel traversal and avoids spawning for a single work item. * Adds an explicit pre_traverse step do any pre-processing and determine whether we need to traverse at all. MozReview-Commit-ID: IKhLAkAigXE
This commit is contained in:
parent
4cb3404c09
commit
80460cc549
27 changed files with 502 additions and 474 deletions
|
@ -13,7 +13,6 @@ use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as Conste
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use style::animation::{Animation, update_style_for_animation};
|
use style::animation::{Animation, update_style_for_animation};
|
||||||
use style::dom::TRestyleDamage;
|
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResp
|
||||||
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
|
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
@ -88,6 +89,12 @@ pub struct SharedLayoutContext {
|
||||||
BuildHasherDefault<FnvHasher>>>>,
|
BuildHasherDefault<FnvHasher>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Borrow<SharedStyleContext> for SharedLayoutContext {
|
||||||
|
fn borrow(&self) -> &SharedStyleContext {
|
||||||
|
&self.style_context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LayoutContext<'a> {
|
pub struct LayoutContext<'a> {
|
||||||
pub shared: &'a SharedLayoutContext,
|
pub shared: &'a SharedLayoutContext,
|
||||||
cached_local_layout_context: Rc<LocalLayoutContext>,
|
cached_local_layout_context: Rc<LocalLayoutContext>,
|
||||||
|
|
|
@ -50,7 +50,6 @@ use std::sync::Arc;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use style::computed_values::{clear, float, overflow_x, position, text_align};
|
use style::computed_values::{clear, float, overflow_x, position, text_align};
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::dom::TRestyleDamage;
|
|
||||||
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
|
|
|
@ -44,7 +44,6 @@ use style::computed_values::{overflow_wrap, overflow_x, position, text_decoratio
|
||||||
use style::computed_values::{transform_style, vertical_align, white_space, word_break, z_index};
|
use style::computed_values::{transform_style, vertical_align, white_space, word_break, z_index};
|
||||||
use style::computed_values::content::ContentItem;
|
use style::computed_values::content::ContentItem;
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::dom::TRestyleDamage;
|
|
||||||
use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
|
use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
|
|
|
@ -19,7 +19,6 @@ use std::collections::{HashMap, LinkedList};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::computed_values::{display, list_style_type};
|
use style::computed_values::{display, list_style_type};
|
||||||
use style::computed_values::content::ContentItem;
|
use style::computed_values::content::ContentItem;
|
||||||
use style::dom::TRestyleDamage;
|
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::servo::restyle_damage::RESOLVE_GENERATED_CONTENT;
|
use style::servo::restyle_damage::RESOLVE_GENERATED_CONTENT;
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
|
use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
|
||||||
use style::computed_values::float;
|
use style::computed_values::float;
|
||||||
use style::dom::TRestyleDamage;
|
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::servo::restyle_damage::{REFLOW, RECONSTRUCT_FLOW};
|
use style::servo::restyle_damage::{REFLOW, RECONSTRUCT_FLOW};
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ use std::mem;
|
||||||
use style::atomic_refcell::AtomicRefCell;
|
use style::atomic_refcell::AtomicRefCell;
|
||||||
use style::context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use style::context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
use style::data::ElementData;
|
use style::data::ElementData;
|
||||||
use style::dom::{StylingMode, TElement, TNode};
|
use style::dom::{TElement, TNode};
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT};
|
use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT};
|
||||||
use style::traversal::{DomTraversalContext, recalc_style_at, remove_from_bloom_filter};
|
use style::traversal::{DomTraversalContext, recalc_style_at, remove_from_bloom_filter};
|
||||||
|
@ -73,14 +73,15 @@ impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_preorder(&self, node: N, data: &mut PerLevelTraversalData) {
|
fn process_preorder(&self, node: N, traversal_data: &mut PerLevelTraversalData) {
|
||||||
// FIXME(pcwalton): Stop allocating here. Ideally this should just be
|
// FIXME(pcwalton): Stop allocating here. Ideally this should just be
|
||||||
// done by the HTML parser.
|
// done by the HTML parser.
|
||||||
node.initialize_data();
|
node.initialize_data();
|
||||||
|
|
||||||
if !node.is_text_node() {
|
if !node.is_text_node() {
|
||||||
let el = node.as_element().unwrap();
|
let el = node.as_element().unwrap();
|
||||||
recalc_style_at::<_, _, Self>(&self.context, data, el);
|
let mut data = el.mutate_data().unwrap();
|
||||||
|
recalc_style_at::<_, _, Self>(&self.context, traversal_data, el, &mut data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,19 +89,13 @@ impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc>
|
||||||
construct_flows_at(&self.context, self.root, node);
|
construct_flows_at(&self.context, self.root, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_traverse_child(child: N) -> bool {
|
fn text_node_needs_traversal(node: N) -> bool {
|
||||||
match child.as_element() {
|
|
||||||
// Elements should be traversed if they need styling or flow construction.
|
|
||||||
Some(el) => el.styling_mode() != StylingMode::Stop ||
|
|
||||||
el.as_node().to_threadsafe().restyle_damage() != RestyleDamage::empty(),
|
|
||||||
|
|
||||||
// Text nodes never need styling. However, there are two cases they may need
|
// Text nodes never need styling. However, there are two cases they may need
|
||||||
// flow construction:
|
// flow construction:
|
||||||
// (1) They child doesn't yet have layout data (preorder traversal initializes it).
|
// (1) They child doesn't yet have layout data (preorder traversal initializes it).
|
||||||
// (2) The parent element has restyle damage (so the text flow also needs fixup).
|
// (2) The parent element has restyle damage (so the text flow also needs fixup).
|
||||||
None => child.get_raw_data().is_none() ||
|
node.get_raw_data().is_none() ||
|
||||||
child.parent_node().unwrap().to_threadsafe().restyle_damage() != RestyleDamage::empty(),
|
node.parent_node().unwrap().to_threadsafe().restyle_damage() != RestyleDamage::empty()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell<ElementData> {
|
unsafe fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell<ElementData> {
|
||||||
|
|
|
@ -106,7 +106,7 @@ use std::sync::mpsc::{Receiver, Sender, channel};
|
||||||
use style::animation::Animation;
|
use style::animation::Animation;
|
||||||
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
|
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
|
||||||
use style::data::StoredRestyleHint;
|
use style::data::StoredRestyleHint;
|
||||||
use style::dom::{StylingMode, TElement, TNode};
|
use style::dom::{TElement, TNode};
|
||||||
use style::error_reporting::{ParseErrorReporter, StdoutErrorReporter};
|
use style::error_reporting::{ParseErrorReporter, StdoutErrorReporter};
|
||||||
use style::logical_geometry::LogicalPoint;
|
use style::logical_geometry::LogicalPoint;
|
||||||
use style::media_queries::{Device, MediaType};
|
use style::media_queries::{Device, MediaType};
|
||||||
|
@ -116,6 +116,7 @@ use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets};
|
||||||
use style::stylist::Stylist;
|
use style::stylist::Stylist;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
|
use style::traversal::DomTraversalContext;
|
||||||
use util::geometry::max_rect;
|
use util::geometry::max_rect;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::prefs::PREFS;
|
use util::prefs::PREFS;
|
||||||
|
@ -1122,7 +1123,7 @@ impl LayoutThread {
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
let mut style_data = &mut data.base.style_data;
|
let mut style_data = &mut data.base.style_data;
|
||||||
debug_assert!(!style_data.is_restyle());
|
debug_assert!(style_data.has_current_styles());
|
||||||
let mut restyle_data = match style_data.restyle() {
|
let mut restyle_data = match style_data.restyle() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => continue,
|
None => continue,
|
||||||
|
@ -1131,7 +1132,9 @@ impl LayoutThread {
|
||||||
// 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 = restyle.hint.into();
|
restyle_data.hint = restyle.hint.into();
|
||||||
restyle_data.damage = restyle.damage;
|
restyle_data.damage = restyle.damage;
|
||||||
restyle_data.snapshot = restyle.snapshot;
|
if let Some(s) = restyle.snapshot {
|
||||||
|
restyle_data.snapshot.ensure(move || s);
|
||||||
|
}
|
||||||
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
|
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1142,7 +1145,9 @@ impl LayoutThread {
|
||||||
data.reflow_info.goal);
|
data.reflow_info.goal);
|
||||||
|
|
||||||
let dom_depth = Some(0); // This is always the root node.
|
let dom_depth = Some(0); // This is always the root node.
|
||||||
if element.styling_mode() != StylingMode::Stop {
|
let token = <RecalcStyleAndConstructFlows as DomTraversalContext<ServoLayoutNode>>
|
||||||
|
::pre_traverse(element, &shared_layout_context.style_context.stylist, /* skip_root = */ false);
|
||||||
|
if token.should_traverse() {
|
||||||
// Recalculate CSS styles and rebuild flows and fragments.
|
// Recalculate CSS styles and rebuild flows and fragments.
|
||||||
profile(time::ProfilerCategory::LayoutStyleRecalc,
|
profile(time::ProfilerCategory::LayoutStyleRecalc,
|
||||||
self.profiler_metadata(),
|
self.profiler_metadata(),
|
||||||
|
@ -1152,11 +1157,11 @@ impl LayoutThread {
|
||||||
if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) {
|
if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) {
|
||||||
// Parallel mode
|
// Parallel mode
|
||||||
parallel::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
parallel::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
||||||
element.as_node(), dom_depth, &shared_layout_context, traversal);
|
element, dom_depth, &shared_layout_context, token, traversal);
|
||||||
} else {
|
} else {
|
||||||
// Sequential mode
|
// Sequential mode
|
||||||
sequential::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
sequential::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
||||||
element.as_node(), &shared_layout_context);
|
element, &shared_layout_context, token);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
||||||
|
|
|
@ -97,7 +97,6 @@ use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
|
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
|
||||||
use style::context::ReflowGoal;
|
use style::context::ReflowGoal;
|
||||||
use style::dom::TRestyleDamage;
|
|
||||||
use style::element_state::*;
|
use style::element_state::*;
|
||||||
use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
|
use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
|
||||||
use style::parser::ParserContextExtraData;
|
use style::parser::ParserContextExtraData;
|
||||||
|
|
|
@ -816,7 +816,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.current_styles().primary.values.clone()
|
parent_data.styles().primary.values.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_id(self) -> usize {
|
fn debug_id(self) -> usize {
|
||||||
|
|
|
@ -317,7 +317,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
if self.get_style_data()
|
if self.get_style_data()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow()
|
.borrow()
|
||||||
.current_styles().pseudos
|
.styles().pseudos
|
||||||
.contains_key(&PseudoElement::Before) {
|
.contains_key(&PseudoElement::Before) {
|
||||||
Some(self.with_pseudo(PseudoElementType::Before(None)))
|
Some(self.with_pseudo(PseudoElementType::Before(None)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -330,7 +330,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
if self.get_style_data()
|
if self.get_style_data()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow()
|
.borrow()
|
||||||
.current_styles().pseudos
|
.styles().pseudos
|
||||||
.contains_key(&PseudoElement::After) {
|
.contains_key(&PseudoElement::After) {
|
||||||
Some(self.with_pseudo(PseudoElementType::After(None)))
|
Some(self.with_pseudo(PseudoElementType::After(None)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -371,7 +371,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()
|
||||||
.current_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.
|
||||||
|
@ -383,14 +383,14 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
if !self.get_style_data()
|
if !self.get_style_data()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow()
|
.borrow()
|
||||||
.current_styles().pseudos.contains_key(&style_pseudo) {
|
.styles().pseudos.contains_key(&style_pseudo) {
|
||||||
let mut data = self.get_style_data().unwrap().borrow_mut();
|
let mut data = self.get_style_data().unwrap().borrow_mut();
|
||||||
let new_style =
|
let new_style =
|
||||||
context.stylist.precomputed_values_for_pseudo(
|
context.stylist.precomputed_values_for_pseudo(
|
||||||
&style_pseudo,
|
&style_pseudo,
|
||||||
Some(&data.current_styles().primary.values),
|
Some(&data.styles().primary.values),
|
||||||
false);
|
false);
|
||||||
data.current_styles_mut().pseudos
|
data.styles_mut().pseudos
|
||||||
.insert(style_pseudo.clone(), new_style.unwrap());
|
.insert(style_pseudo.clone(), new_style.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,22 +398,22 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
if !self.get_style_data()
|
if !self.get_style_data()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow()
|
.borrow()
|
||||||
.current_styles().pseudos.contains_key(&style_pseudo) {
|
.styles().pseudos.contains_key(&style_pseudo) {
|
||||||
let mut data = self.get_style_data().unwrap().borrow_mut();
|
let mut data = self.get_style_data().unwrap().borrow_mut();
|
||||||
let new_style =
|
let new_style =
|
||||||
context.stylist
|
context.stylist
|
||||||
.lazily_compute_pseudo_element_style(
|
.lazily_compute_pseudo_element_style(
|
||||||
self,
|
self,
|
||||||
&style_pseudo,
|
&style_pseudo,
|
||||||
&data.current_styles().primary.values);
|
&data.styles().primary.values);
|
||||||
data.current_styles_mut().pseudos
|
data.styles_mut().pseudos
|
||||||
.insert(style_pseudo.clone(), new_style.unwrap());
|
.insert(style_pseudo.clone(), new_style.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_style_data().unwrap().borrow()
|
self.get_style_data().unwrap().borrow()
|
||||||
.current_styles().pseudos.get(&style_pseudo)
|
.styles().pseudos.get(&style_pseudo)
|
||||||
.unwrap().values.clone()
|
.unwrap().values.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,9 +422,9 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
#[inline]
|
#[inline]
|
||||||
fn selected_style(&self) -> Arc<ServoComputedValues> {
|
fn selected_style(&self) -> Arc<ServoComputedValues> {
|
||||||
let data = self.get_style_data().unwrap().borrow();
|
let data = self.get_style_data().unwrap().borrow();
|
||||||
data.current_styles().pseudos
|
data.styles().pseudos
|
||||||
.get(&PseudoElement::Selection).map(|s| s)
|
.get(&PseudoElement::Selection).map(|s| s)
|
||||||
.unwrap_or(&data.current_styles().primary)
|
.unwrap_or(&data.styles().primary)
|
||||||
.values.clone()
|
.values.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,9 +440,9 @@ 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.current_styles().primary.values.clone(),
|
=> data.styles().primary.values.clone(),
|
||||||
other
|
other
|
||||||
=> data.current_styles().pseudos
|
=> data.styles().pseudos
|
||||||
.get(&other.style_pseudo_element()).unwrap().values.clone(),
|
.get(&other.style_pseudo_element()).unwrap().values.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,6 @@ pub struct SharedStyleContext {
|
||||||
/// Screen sized changed?
|
/// Screen sized changed?
|
||||||
pub screen_size_changed: bool,
|
pub screen_size_changed: bool,
|
||||||
|
|
||||||
/// Skip the root during traversal?
|
|
||||||
///
|
|
||||||
/// This is used in Gecko to style newly-appended children without restyling
|
|
||||||
/// the parent. It would be cleaner to add an API to allow us to enqueue the
|
|
||||||
/// children directly from glue.rs.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub skip_root: bool,
|
|
||||||
|
|
||||||
/// The CSS selector stylist.
|
/// The CSS selector stylist.
|
||||||
pub stylist: Arc<Stylist>,
|
pub stylist: Arc<Stylist>,
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
//! Per-node data used in style calculation.
|
//! Per-node data used in style calculation.
|
||||||
|
|
||||||
use dom::TRestyleDamage;
|
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::RestyleHint;
|
use restyle_hints::{RESTYLE_LATER_SIBLINGS, RestyleHint};
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
|
use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -16,6 +16,8 @@ use std::hash::BuildHasherDefault;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use stylist::Stylist;
|
||||||
|
use thread_state;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ComputedStyle {
|
pub struct ComputedStyle {
|
||||||
|
@ -133,7 +135,7 @@ impl StoredRestyleHint {
|
||||||
/// Propagates this restyle hint to a child element.
|
/// Propagates this restyle hint to a child element.
|
||||||
pub fn propagate(&self) -> Self {
|
pub fn propagate(&self) -> Self {
|
||||||
StoredRestyleHint {
|
StoredRestyleHint {
|
||||||
restyle_self: self.descendants == DescendantRestyleHint::Empty,
|
restyle_self: self.descendants != DescendantRestyleHint::Empty,
|
||||||
descendants: self.descendants.propagate(),
|
descendants: self.descendants.propagate(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,10 +185,50 @@ impl From<RestyleHint> for StoredRestyleHint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We really want to store an Option<Snapshot> here, but we can't drop Gecko
|
||||||
|
// Snapshots off-main-thread. So we make a convenient little wrapper to provide
|
||||||
|
// the semantics of Option<Snapshot>, while deferring the actual drop.
|
||||||
|
static NO_SNAPSHOT: Option<Snapshot> = None;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RestyleDataStyles {
|
pub struct SnapshotOption {
|
||||||
Previous(ElementStyles),
|
snapshot: Option<Snapshot>,
|
||||||
New(ElementStyles),
|
destroyed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SnapshotOption {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
SnapshotOption {
|
||||||
|
snapshot: None,
|
||||||
|
destroyed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&mut self) {
|
||||||
|
self.destroyed = true;
|
||||||
|
debug_assert!(self.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure<F: FnOnce() -> Snapshot>(&mut self, create: F) -> &mut Snapshot {
|
||||||
|
debug_assert!(thread_state::get().is_layout());
|
||||||
|
if self.is_none() {
|
||||||
|
self.snapshot = Some(create());
|
||||||
|
self.destroyed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.snapshot.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for SnapshotOption {
|
||||||
|
type Target = Option<Snapshot>;
|
||||||
|
fn deref(&self) -> &Option<Snapshot> {
|
||||||
|
if self.destroyed {
|
||||||
|
&NO_SNAPSHOT
|
||||||
|
} else {
|
||||||
|
&self.snapshot
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transient data used by the restyle algorithm. This structure is instantiated
|
/// Transient data used by the restyle algorithm. This structure is instantiated
|
||||||
|
@ -194,54 +236,71 @@ pub enum RestyleDataStyles {
|
||||||
/// processing.
|
/// processing.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RestyleData {
|
pub struct RestyleData {
|
||||||
pub styles: RestyleDataStyles,
|
pub styles: ElementStyles,
|
||||||
pub hint: StoredRestyleHint,
|
pub hint: StoredRestyleHint,
|
||||||
|
pub recascade: bool,
|
||||||
pub damage: RestyleDamage,
|
pub damage: RestyleDamage,
|
||||||
pub snapshot: Option<Snapshot>,
|
pub snapshot: SnapshotOption,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestyleData {
|
impl RestyleData {
|
||||||
fn new(previous: ElementStyles) -> Self {
|
fn new(styles: ElementStyles) -> Self {
|
||||||
RestyleData {
|
RestyleData {
|
||||||
styles: RestyleDataStyles::Previous(previous),
|
styles: styles,
|
||||||
hint: StoredRestyleHint::default(),
|
hint: StoredRestyleHint::default(),
|
||||||
|
recascade: false,
|
||||||
damage: RestyleDamage::empty(),
|
damage: RestyleDamage::empty(),
|
||||||
snapshot: None,
|
snapshot: SnapshotOption::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_styles(&self) -> Option<&ElementStyles> {
|
/// Expands the snapshot (if any) into a restyle hint. Returns true if later siblings
|
||||||
use self::RestyleDataStyles::*;
|
/// must be restyled.
|
||||||
match self.styles {
|
pub fn expand_snapshot<E: TElement>(&mut self, element: E, stylist: &Stylist) -> bool {
|
||||||
Previous(_) => None,
|
if self.snapshot.is_none() {
|
||||||
New(ref x) => Some(x),
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_styles(&self) -> &ElementStyles {
|
// Compute the hint.
|
||||||
self.get_current_styles().unwrap()
|
let state = element.get_state();
|
||||||
|
let mut hint = stylist.compute_restyle_hint(&element,
|
||||||
|
self.snapshot.as_ref().unwrap(),
|
||||||
|
state);
|
||||||
|
|
||||||
|
// If the hint includes a directive for later siblings, strip it out and
|
||||||
|
// notify the caller to modify the base hint for future siblings.
|
||||||
|
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
|
||||||
|
hint.remove(RESTYLE_LATER_SIBLINGS);
|
||||||
|
|
||||||
|
// Insert the hint.
|
||||||
|
self.hint.insert(&hint.into());
|
||||||
|
|
||||||
|
// Destroy the snapshot.
|
||||||
|
self.snapshot.destroy();
|
||||||
|
|
||||||
|
later_siblings
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_styles_mut(&mut self) -> &mut ElementStyles {
|
pub fn has_current_styles(&self) -> bool {
|
||||||
use self::RestyleDataStyles::*;
|
!(self.hint.restyle_self || self.recascade || self.snapshot.is_some())
|
||||||
match self.styles {
|
|
||||||
New(ref mut x) => x,
|
|
||||||
Previous(_) => panic!("Calling current_styles_mut before styling"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_or_previous_styles(&self) -> &ElementStyles {
|
pub fn styles(&self) -> &ElementStyles {
|
||||||
use self::RestyleDataStyles::*;
|
&self.styles
|
||||||
match self.styles {
|
|
||||||
Previous(ref x) => x,
|
|
||||||
New(ref x) => x,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn styles_mut(&mut self) -> &mut ElementStyles {
|
||||||
|
&mut self.styles
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_styling(&mut self, styles: ElementStyles, damage: RestyleDamage) {
|
fn finish_styling(&mut self, styles: ElementStyles, damage: RestyleDamage) {
|
||||||
debug_assert!(self.get_current_styles().is_none());
|
debug_assert!(!self.has_current_styles());
|
||||||
self.styles = RestyleDataStyles::New(styles);
|
debug_assert!(self.snapshot.is_none(), "Traversal should have expanded snapshots");
|
||||||
|
self.styles = styles;
|
||||||
self.damage |= damage;
|
self.damage |= damage;
|
||||||
|
// The hint and recascade bits get cleared by the traversal code. This
|
||||||
|
// is a bit confusing, and we should simplify it when we separate matching
|
||||||
|
// from cascading.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,10 +422,7 @@ impl ElementData {
|
||||||
let old = mem::replace(self, ElementData::new(None));
|
let old = mem::replace(self, ElementData::new(None));
|
||||||
let styles = match old {
|
let styles = match old {
|
||||||
ElementData::Initial(i) => i.unwrap(),
|
ElementData::Initial(i) => i.unwrap(),
|
||||||
ElementData::Restyle(r) => match r.styles {
|
ElementData::Restyle(r) => r.styles,
|
||||||
RestyleDataStyles::New(n) => n,
|
|
||||||
RestyleDataStyles::Previous(_) => panic!("Never restyled element"),
|
|
||||||
},
|
|
||||||
ElementData::Persistent(_) => unreachable!(),
|
ElementData::Persistent(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
*self = ElementData::Persistent(styles);
|
*self = ElementData::Persistent(styles);
|
||||||
|
@ -380,7 +436,7 @@ impl ElementData {
|
||||||
RestyleDamage::rebuild_and_reflow()
|
RestyleDamage::rebuild_and_reflow()
|
||||||
},
|
},
|
||||||
Restyle(ref r) => {
|
Restyle(ref r) => {
|
||||||
debug_assert!(r.get_current_styles().is_some());
|
debug_assert!(r.has_current_styles());
|
||||||
r.damage
|
r.damage
|
||||||
},
|
},
|
||||||
Persistent(_) => RestyleDamage::empty(),
|
Persistent(_) => RestyleDamage::empty(),
|
||||||
|
@ -400,7 +456,7 @@ impl ElementData {
|
||||||
RestyleDamage::rebuild_and_reflow()
|
RestyleDamage::rebuild_and_reflow()
|
||||||
},
|
},
|
||||||
Restyle(ref r) => {
|
Restyle(ref r) => {
|
||||||
if r.get_current_styles().is_none() {
|
if !r.has_current_styles() {
|
||||||
error!("Accessing damage on dirty element");
|
error!("Accessing damage on dirty element");
|
||||||
}
|
}
|
||||||
r.damage
|
r.damage
|
||||||
|
@ -409,61 +465,41 @@ impl ElementData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_styles(&self) -> &ElementStyles {
|
/// Returns true if this element's style is up-to-date and has no potential
|
||||||
self.get_current_styles().unwrap()
|
/// invalidation.
|
||||||
|
pub fn has_current_styles(&self) -> bool {
|
||||||
|
use self::ElementData::*;
|
||||||
|
match *self {
|
||||||
|
Initial(ref x) => x.is_some(),
|
||||||
|
Restyle(ref x) => x.has_current_styles(),
|
||||||
|
Persistent(_) => true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_styles(&self) -> Option<&ElementStyles> {
|
pub fn get_styles(&self) -> Option<&ElementStyles> {
|
||||||
use self::ElementData::*;
|
use self::ElementData::*;
|
||||||
match *self {
|
match *self {
|
||||||
Initial(ref x) => x.as_ref(),
|
Initial(ref x) => x.as_ref(),
|
||||||
Restyle(ref x) => x.get_current_styles(),
|
Restyle(ref x) => Some(x.styles()),
|
||||||
Persistent(ref x) => Some(x),
|
Persistent(ref x) => Some(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_styles_mut(&mut self) -> &mut ElementStyles {
|
pub fn styles(&self) -> &ElementStyles {
|
||||||
|
self.get_styles().expect("Calling styles() on unstyled ElementData")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_styles_mut(&mut self) -> Option<&mut ElementStyles> {
|
||||||
use self::ElementData::*;
|
use self::ElementData::*;
|
||||||
match *self {
|
match *self {
|
||||||
Initial(ref mut x) => x.as_mut().unwrap(),
|
Initial(ref mut x) => x.as_mut(),
|
||||||
Restyle(ref mut x) => x.current_styles_mut(),
|
Restyle(ref mut x) => Some(x.styles_mut()),
|
||||||
Persistent(ref mut x) => x,
|
Persistent(ref mut x) => Some(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous_styles(&self) -> Option<&ElementStyles> {
|
pub fn styles_mut(&mut self) -> &mut ElementStyles {
|
||||||
use self::ElementData::*;
|
self.get_styles_mut().expect("Calling styles_mut() on unstyled ElementData")
|
||||||
use self::RestyleDataStyles::*;
|
|
||||||
match *self {
|
|
||||||
Initial(_) => None,
|
|
||||||
Restyle(ref x) => match x.styles {
|
|
||||||
Previous(ref styles) => Some(styles),
|
|
||||||
New(_) => panic!("Calling previous_styles after finish_styling"),
|
|
||||||
},
|
|
||||||
Persistent(_) => panic!("Calling previous_styles on Persistent ElementData"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn previous_styles_mut(&mut self) -> Option<&mut ElementStyles> {
|
|
||||||
use self::ElementData::*;
|
|
||||||
use self::RestyleDataStyles::*;
|
|
||||||
match *self {
|
|
||||||
Initial(_) => None,
|
|
||||||
Restyle(ref mut x) => match x.styles {
|
|
||||||
Previous(ref mut styles) => Some(styles),
|
|
||||||
New(_) => panic!("Calling previous_styles after finish_styling"),
|
|
||||||
},
|
|
||||||
Persistent(_) => panic!("Calling previous_styles on Persistent ElementData"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_or_previous_styles(&self) -> &ElementStyles {
|
|
||||||
use self::ElementData::*;
|
|
||||||
match *self {
|
|
||||||
Initial(ref x) => x.as_ref().unwrap(),
|
|
||||||
Restyle(ref x) => x.current_or_previous_styles(),
|
|
||||||
Persistent(ref x) => x,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_styling(&mut self, styles: ElementStyles, damage: RestyleDamage) {
|
pub fn finish_styling(&mut self, styles: ElementStyles, damage: RestyleDamage) {
|
||||||
|
|
|
@ -8,17 +8,15 @@
|
||||||
|
|
||||||
use {Atom, Namespace, LocalName};
|
use {Atom, Namespace, LocalName};
|
||||||
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
||||||
use data::{ElementStyles, ElementData};
|
use data::ElementData;
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use properties::{ComputedValues, PropertyDeclarationBlock};
|
use properties::{ComputedValues, PropertyDeclarationBlock};
|
||||||
use selector_parser::{ElementExt, PseudoElement, RestyleDamage};
|
use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement};
|
||||||
use sink::Push;
|
use sink::Push;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::{BitOr, BitOrAssign};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stylist::ApplicableDeclarationBlock;
|
use stylist::ApplicableDeclarationBlock;
|
||||||
use util::opts;
|
|
||||||
|
|
||||||
pub use style_traits::UnsafeNode;
|
pub use style_traits::UnsafeNode;
|
||||||
|
|
||||||
|
@ -43,42 +41,6 @@ impl OpaqueNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum StylingMode {
|
|
||||||
/// The node has never been styled before, and needs a full style computation.
|
|
||||||
Initial,
|
|
||||||
/// The node has been styled before, but needs some amount of recomputation.
|
|
||||||
Restyle,
|
|
||||||
/// The node does not need any style processing, but one or more of its
|
|
||||||
/// descendants do.
|
|
||||||
Traverse,
|
|
||||||
/// No nodes in this subtree require style processing.
|
|
||||||
Stop,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait TRestyleDamage : BitOr<Output=Self> + BitOrAssign + Copy + Debug + PartialEq {
|
|
||||||
/// The source for our current computed values in the cascade. This is a
|
|
||||||
/// ComputedValues in Servo and a StyleContext in Gecko.
|
|
||||||
///
|
|
||||||
/// This is needed because Gecko has a few optimisations for the calculation
|
|
||||||
/// of the difference depending on which values have been used during
|
|
||||||
/// layout.
|
|
||||||
///
|
|
||||||
/// This should be obtained via TNode::existing_style_for_restyle_damage
|
|
||||||
type PreExistingComputedValues;
|
|
||||||
|
|
||||||
fn compute(old: &Self::PreExistingComputedValues,
|
|
||||||
new: &Arc<ComputedValues>) -> Self;
|
|
||||||
|
|
||||||
fn empty() -> Self;
|
|
||||||
|
|
||||||
fn rebuild_and_reflow() -> Self;
|
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
*self == Self::empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Simple trait to provide basic information about the type of an element.
|
/// Simple trait to provide basic information about the type of an element.
|
||||||
///
|
///
|
||||||
/// We avoid exposing the full type id, since computing it in the general case
|
/// We avoid exposing the full type id, since computing it in the general case
|
||||||
|
@ -174,7 +136,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
||||||
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: Option<&'a Arc<ComputedValues>>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> Option<&'a <RestyleDamage as TRestyleDamage>::PreExistingComputedValues>;
|
-> Option<&'a PreExistingComputedValues>;
|
||||||
|
|
||||||
/// Returns true if this element may have a descendant needing style processing.
|
/// Returns true if this element may have a descendant needing style processing.
|
||||||
///
|
///
|
||||||
|
@ -201,60 +163,11 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
||||||
/// traversal. Returns the number of children left to process.
|
/// traversal. Returns the number of children left to process.
|
||||||
fn did_process_child(&self) -> isize;
|
fn did_process_child(&self) -> isize;
|
||||||
|
|
||||||
/// Returns true if this element's current style is display:none. Only valid
|
/// Returns true if this element's style is display:none.
|
||||||
/// to call after styling.
|
|
||||||
fn is_display_none(&self) -> bool {
|
fn is_display_none(&self) -> bool {
|
||||||
self.borrow_data().unwrap().current_styles().is_display_none()
|
let data = self.borrow_data().unwrap();
|
||||||
}
|
debug_assert!(data.has_current_styles());
|
||||||
|
data.styles().is_display_none()
|
||||||
/// Returns true if this node has a styled layout frame that owns the style.
|
|
||||||
fn frame_has_style(&self) -> bool { false }
|
|
||||||
|
|
||||||
/// Returns the styles from the layout frame that owns them, if any.
|
|
||||||
///
|
|
||||||
/// FIXME(bholley): Once we start dropping ElementData from nodes when
|
|
||||||
/// creating frames, we'll want to teach this method to actually get
|
|
||||||
/// style data from the frame.
|
|
||||||
fn get_styles_from_frame(&self) -> Option<ElementStyles> { None }
|
|
||||||
|
|
||||||
/// Returns the styling mode for this node. This is only valid to call before
|
|
||||||
/// and during restyling, before finish_styling is invoked.
|
|
||||||
///
|
|
||||||
/// See the comments around StylingMode.
|
|
||||||
fn styling_mode(&self) -> StylingMode {
|
|
||||||
use self::StylingMode::*;
|
|
||||||
|
|
||||||
// Non-incremental layout impersonates Initial.
|
|
||||||
if opts::get().nonincremental_layout {
|
|
||||||
return Initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the default result if this node doesn't require processing.
|
|
||||||
let mode_for_descendants = if self.has_dirty_descendants() {
|
|
||||||
Traverse
|
|
||||||
} else {
|
|
||||||
Stop
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.borrow_data() {
|
|
||||||
// No element data, no style on the frame.
|
|
||||||
None if !self.frame_has_style() => Initial,
|
|
||||||
// No element data, style on the frame.
|
|
||||||
None => mode_for_descendants,
|
|
||||||
// We have element data. Decide below.
|
|
||||||
Some(d) => match *d {
|
|
||||||
ElementData::Restyle(_) => Restyle,
|
|
||||||
ElementData::Persistent(_) => mode_for_descendants,
|
|
||||||
ElementData::Initial(None) => Initial,
|
|
||||||
// We previously computed the initial style for this element
|
|
||||||
// and then never consumed it. This is arguably a bug, since
|
|
||||||
// it means we either styled an element unnecessarily, or missed
|
|
||||||
// an opportunity to coalesce style traversals. However, this
|
|
||||||
// happens now for various reasons, so we just let it slide and
|
|
||||||
// treat it as persistent for now.
|
|
||||||
ElementData::Initial(Some(_)) => mode_for_descendants,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to the ElementData container.
|
/// Gets a reference to the ElementData container.
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use dom::TRestyleDamage;
|
|
||||||
use gecko_bindings::bindings;
|
use gecko_bindings::bindings;
|
||||||
use gecko_bindings::structs;
|
use gecko_bindings::structs;
|
||||||
use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
|
use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
|
||||||
|
@ -22,16 +21,16 @@ impl GeckoRestyleDamage {
|
||||||
pub fn as_change_hint(&self) -> nsChangeHint {
|
pub fn as_change_hint(&self) -> nsChangeHint {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TRestyleDamage for GeckoRestyleDamage {
|
pub fn empty() -> Self {
|
||||||
type PreExistingComputedValues = nsStyleContext;
|
|
||||||
|
|
||||||
fn empty() -> Self {
|
|
||||||
GeckoRestyleDamage(nsChangeHint(0))
|
GeckoRestyleDamage(nsChangeHint(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(source: &nsStyleContext,
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0 == nsChangeHint(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute(source: &nsStyleContext,
|
||||||
new_style: &Arc<ComputedValues>) -> Self {
|
new_style: &Arc<ComputedValues>) -> Self {
|
||||||
let context = source as *const nsStyleContext as *mut nsStyleContext;
|
let context = source as *const nsStyleContext as *mut nsStyleContext;
|
||||||
let hint = unsafe {
|
let hint = unsafe {
|
||||||
|
@ -41,7 +40,7 @@ impl TRestyleDamage for GeckoRestyleDamage {
|
||||||
GeckoRestyleDamage(hint)
|
GeckoRestyleDamage(hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild_and_reflow() -> Self {
|
pub fn rebuild_and_reflow() -> Self {
|
||||||
GeckoRestyleDamage(structs::nsChangeHint_nsChangeHint_ReconstructFrame)
|
GeckoRestyleDamage(structs::nsChangeHint_nsChangeHint_ReconstructFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@ use string_cache::Atom;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GeckoElementSnapshot(bindings::ServoElementSnapshotOwned);
|
pub struct GeckoElementSnapshot(bindings::ServoElementSnapshotOwned);
|
||||||
|
|
||||||
|
// FIXME(bholley): Add support for *OwnedConst type, and then we get Sync
|
||||||
|
// automatically.
|
||||||
|
unsafe impl Sync for GeckoElementSnapshot {}
|
||||||
|
|
||||||
impl Drop for GeckoElementSnapshot {
|
impl Drop for GeckoElementSnapshot {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use atomic_refcell::AtomicRefCell;
|
use atomic_refcell::AtomicRefCell;
|
||||||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
use data::ElementData;
|
use data::ElementData;
|
||||||
use dom::{NodeInfo, OpaqueNode, StylingMode, TElement, TNode};
|
use dom::{NodeInfo, OpaqueNode, TNode};
|
||||||
use gecko::context::StandaloneStyleContext;
|
use gecko::context::StandaloneStyleContext;
|
||||||
use gecko::wrapper::{GeckoElement, GeckoNode};
|
use gecko::wrapper::{GeckoElement, GeckoNode};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -13,26 +13,25 @@ use traversal::{DomTraversalContext, PerLevelTraversalData, recalc_style_at};
|
||||||
|
|
||||||
pub struct RecalcStyleOnly<'lc> {
|
pub struct RecalcStyleOnly<'lc> {
|
||||||
context: StandaloneStyleContext<'lc>,
|
context: StandaloneStyleContext<'lc>,
|
||||||
root: OpaqueNode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
|
impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
|
||||||
type SharedContext = SharedStyleContext;
|
type SharedContext = SharedStyleContext;
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn new<'a>(shared: &'a Self::SharedContext, root: OpaqueNode) -> Self {
|
fn new<'a>(shared: &'a Self::SharedContext, _root: OpaqueNode) -> Self {
|
||||||
// See the comment in RecalcStyleAndConstructFlows::new for an explanation of why this is
|
// See the comment in RecalcStyleAndConstructFlows::new for an explanation of why this is
|
||||||
// necessary.
|
// necessary.
|
||||||
let shared_lc: &'lc Self::SharedContext = unsafe { mem::transmute(shared) };
|
let shared_lc: &'lc Self::SharedContext = unsafe { mem::transmute(shared) };
|
||||||
RecalcStyleOnly {
|
RecalcStyleOnly {
|
||||||
context: StandaloneStyleContext::new(shared_lc),
|
context: StandaloneStyleContext::new(shared_lc),
|
||||||
root: root,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_preorder(&self, node: GeckoNode<'ln>, data: &mut PerLevelTraversalData) {
|
fn process_preorder(&self, node: GeckoNode<'ln>, traversal_data: &mut PerLevelTraversalData) {
|
||||||
if node.is_element() && (!self.context.shared_context().skip_root || node.opaque() != self.root) {
|
if node.is_element() {
|
||||||
let el = node.as_element().unwrap();
|
let el = node.as_element().unwrap();
|
||||||
recalc_style_at::<_, _, Self>(&self.context, data, el);
|
let mut data = unsafe { el.ensure_data() }.borrow_mut();
|
||||||
|
recalc_style_at::<_, _, Self>(&self.context, traversal_data, el, &mut data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,14 +40,7 @@ impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We don't use the post-order traversal for anything.
|
/// We don't use the post-order traversal for anything.
|
||||||
fn needs_postorder_traversal(&self) -> bool { false }
|
fn needs_postorder_traversal() -> bool { false }
|
||||||
|
|
||||||
fn should_traverse_child(child: GeckoNode<'ln>) -> bool {
|
|
||||||
match child.as_element() {
|
|
||||||
Some(el) => el.styling_mode() != StylingMode::Stop,
|
|
||||||
None => false, // Gecko restyle doesn't need to traverse text nodes.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn ensure_element_data<'a>(element: &'a GeckoElement<'ln>) -> &'a AtomicRefCell<ElementData> {
|
unsafe fn ensure_element_data<'a>(element: &'a GeckoElement<'ln>) -> &'a AtomicRefCell<ElementData> {
|
||||||
element.ensure_data()
|
element.ensure_data()
|
||||||
|
|
|
@ -260,10 +260,10 @@ impl<'le> GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> {
|
pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> {
|
||||||
// NB: Gecko sometimes resolves pseudos after an element has already been
|
// FIXME(bholley): Gecko sometimes resolves pseudos after an element has
|
||||||
// marked for restyle. We should consider fixing this, but for now just allow
|
// already been marked for restyle. We should consider fixing this, and
|
||||||
// it with current_or_previous_styles.
|
// then assert has_current_styles here.
|
||||||
self.borrow_data().and_then(|data| data.current_or_previous_styles().pseudos
|
self.borrow_data().and_then(|data| data.styles().pseudos
|
||||||
.get(pseudo).map(|c| c.values.clone()))
|
.get(pseudo).map(|c| c.values.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,8 +273,7 @@ impl<'le> GeckoElement<'le> {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => {
|
None => {
|
||||||
debug!("Creating ElementData for {:?}", self);
|
debug!("Creating ElementData for {:?}", self);
|
||||||
let existing = self.get_styles_from_frame();
|
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new(None))));
|
||||||
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new(existing))));
|
|
||||||
self.0.mServoData.set(ptr);
|
self.0.mServoData.set(ptr);
|
||||||
unsafe { &* ptr }
|
unsafe { &* ptr }
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,7 @@ use cache::LRUCache;
|
||||||
use cascade_info::CascadeInfo;
|
use cascade_info::CascadeInfo;
|
||||||
use context::{SharedStyleContext, StyleContext};
|
use context::{SharedStyleContext, StyleContext};
|
||||||
use data::{ComputedStyle, ElementData, ElementStyles, PseudoStyles};
|
use data::{ComputedStyle, ElementData, ElementStyles, PseudoStyles};
|
||||||
use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
|
use dom::{TElement, TNode, UnsafeNode};
|
||||||
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;
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
|
@ -186,7 +186,8 @@ fn element_matches_candidate<E: TElement>(element: &E,
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = candidate_element.borrow_data().unwrap();
|
let data = candidate_element.borrow_data().unwrap();
|
||||||
let current_styles = data.current_styles();
|
debug_assert!(data.has_current_styles());
|
||||||
|
let current_styles = data.styles();
|
||||||
|
|
||||||
Ok(current_styles.primary.clone())
|
Ok(current_styles.primary.clone())
|
||||||
}
|
}
|
||||||
|
@ -600,7 +601,8 @@ pub trait MatchMethods : TElement {
|
||||||
// can decide more easily if it knows that it's a child of
|
// can decide more easily if it knows that it's a child of
|
||||||
// replaced content, or similar stuff!
|
// replaced content, or similar stuff!
|
||||||
let damage = {
|
let damage = {
|
||||||
let previous_values = data.previous_styles().map(|x| &x.primary.values);
|
debug_assert!(!data.has_current_styles());
|
||||||
|
let previous_values = data.get_styles().map(|x| &x.primary.values);
|
||||||
match self.existing_style_for_restyle_damage(previous_values, None) {
|
match self.existing_style_for_restyle_damage(previous_values, None) {
|
||||||
Some(ref source) => RestyleDamage::compute(source, &shared_style.values),
|
Some(ref source) => RestyleDamage::compute(source, &shared_style.values),
|
||||||
None => RestyleDamage::rebuild_and_reflow(),
|
None => RestyleDamage::rebuild_and_reflow(),
|
||||||
|
@ -730,13 +732,17 @@ pub trait MatchMethods : TElement {
|
||||||
{
|
{
|
||||||
// Get our parent's style.
|
// Get our parent's style.
|
||||||
let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap());
|
let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap());
|
||||||
let parent_style = parent_data.as_ref().map(|x| &x.current_styles().primary.values);
|
let parent_style = parent_data.as_ref().map(|d| {
|
||||||
|
debug_assert!(d.has_current_styles());
|
||||||
|
&d.styles().primary.values
|
||||||
|
});
|
||||||
|
|
||||||
let mut new_styles;
|
let mut new_styles;
|
||||||
let mut possibly_expired_animations = vec![];
|
let mut possibly_expired_animations = vec![];
|
||||||
|
|
||||||
let damage = {
|
let damage = {
|
||||||
let (old_primary, old_pseudos) = match data.previous_styles_mut() {
|
debug_assert!(!data.has_current_styles());
|
||||||
|
let (old_primary, old_pseudos) = match data.get_styles_mut() {
|
||||||
None => (None, None),
|
None => (None, None),
|
||||||
Some(previous) => {
|
Some(previous) => {
|
||||||
// Update animations before the cascade. This may modify the
|
// Update animations before the cascade. This may modify the
|
||||||
|
|
|
@ -9,15 +9,16 @@
|
||||||
use dom::{OpaqueNode, TElement, TNode, UnsafeNode};
|
use dom::{OpaqueNode, TElement, TNode, UnsafeNode};
|
||||||
use rayon;
|
use rayon;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use traversal::{DomTraversalContext, PerLevelTraversalData};
|
use traversal::{DomTraversalContext, PerLevelTraversalData, PreTraverseToken};
|
||||||
use traversal::{STYLE_SHARING_CACHE_HITS, STYLE_SHARING_CACHE_MISSES};
|
use traversal::{STYLE_SHARING_CACHE_HITS, STYLE_SHARING_CACHE_MISSES};
|
||||||
use util::opts;
|
use util::opts;
|
||||||
|
|
||||||
pub const CHUNK_SIZE: usize = 64;
|
pub const CHUNK_SIZE: usize = 64;
|
||||||
|
|
||||||
pub fn traverse_dom<N, C>(root: N,
|
pub fn traverse_dom<N, C>(root: N::ConcreteElement,
|
||||||
known_root_dom_depth: Option<usize>,
|
known_root_dom_depth: Option<usize>,
|
||||||
shared_context: &C::SharedContext,
|
shared_context: &C::SharedContext,
|
||||||
|
token: PreTraverseToken,
|
||||||
queue: &rayon::ThreadPool)
|
queue: &rayon::ThreadPool)
|
||||||
where N: TNode,
|
where N: TNode,
|
||||||
C: DomTraversalContext<N>
|
C: DomTraversalContext<N>
|
||||||
|
@ -27,15 +28,26 @@ pub fn traverse_dom<N, C>(root: N,
|
||||||
STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst);
|
STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nodes = vec![root.to_unsafe()].into_boxed_slice();
|
// Handle root skipping. We don't currently support it in conjunction with
|
||||||
let data = PerLevelTraversalData {
|
// bottom-up traversal. If we did, we'd need to put it on the context to make
|
||||||
current_dom_depth: known_root_dom_depth,
|
// it available to the bottom-up phase.
|
||||||
|
debug_assert!(!token.should_skip_root() || !C::needs_postorder_traversal());
|
||||||
|
let (nodes, depth) = if token.should_skip_root() {
|
||||||
|
let mut children = vec![];
|
||||||
|
C::traverse_children(root, |kid| children.push(kid.to_unsafe()));
|
||||||
|
(children, known_root_dom_depth.map(|x| x + 1))
|
||||||
|
} else {
|
||||||
|
(vec![root.as_node().to_unsafe()], known_root_dom_depth)
|
||||||
};
|
};
|
||||||
let root = root.opaque();
|
|
||||||
|
let data = PerLevelTraversalData {
|
||||||
|
current_dom_depth: depth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let root = root.as_node().opaque();
|
||||||
queue.install(|| {
|
queue.install(|| {
|
||||||
rayon::scope(|scope| {
|
rayon::scope(|scope| {
|
||||||
let nodes = nodes;
|
traverse_nodes::<_, C>(nodes, root, data, scope, shared_context);
|
||||||
top_down_dom::<N, C>(&nodes, root, data, scope, shared_context);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,7 +91,7 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode],
|
||||||
|
|
||||||
// Reset the count of children if we need to do a bottom-up traversal
|
// Reset the count of children if we need to do a bottom-up traversal
|
||||||
// after the top up.
|
// after the top up.
|
||||||
if context.needs_postorder_traversal() {
|
if C::needs_postorder_traversal() {
|
||||||
if children_to_process == 0 {
|
if children_to_process == 0 {
|
||||||
// If there were no more children, start walking back up.
|
// If there were no more children, start walking back up.
|
||||||
bottom_up_dom::<N, C>(root, *unsafe_node, shared_context)
|
bottom_up_dom::<N, C>(root, *unsafe_node, shared_context)
|
||||||
|
@ -99,7 +111,30 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode],
|
||||||
*depth += 1;
|
*depth += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for chunk in discovered_child_nodes.chunks(CHUNK_SIZE) {
|
traverse_nodes::<_, C>(discovered_child_nodes, root, data, scope, shared_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn traverse_nodes<'a, 'scope, N, C>(nodes: Vec<UnsafeNode>, root: OpaqueNode,
|
||||||
|
data: PerLevelTraversalData,
|
||||||
|
scope: &'a rayon::Scope<'scope>,
|
||||||
|
shared_context: &'scope C::SharedContext)
|
||||||
|
where N: TNode,
|
||||||
|
C: DomTraversalContext<N>,
|
||||||
|
{
|
||||||
|
if nodes.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization: traverse directly and avoid a heap-allocating spawn() call if
|
||||||
|
// we're only pushing one work unit.
|
||||||
|
if nodes.len() <= CHUNK_SIZE {
|
||||||
|
let nodes = nodes.into_boxed_slice();
|
||||||
|
top_down_dom::<N, C>(&nodes, root, data, scope, shared_context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// General case.
|
||||||
|
for chunk in nodes.chunks(CHUNK_SIZE) {
|
||||||
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
||||||
let data = data.clone();
|
let data = data.clone();
|
||||||
scope.spawn(move |scope| {
|
scope.spawn(move |scope| {
|
||||||
|
|
|
@ -446,9 +446,6 @@ impl DependencySet {
|
||||||
-> RestyleHint
|
-> RestyleHint
|
||||||
where E: ElementExt + Clone
|
where E: ElementExt + Clone
|
||||||
{
|
{
|
||||||
debug!("About to calculate restyle hint for element. Deps: {}",
|
|
||||||
self.len());
|
|
||||||
|
|
||||||
let state_changes = snapshot.state()
|
let state_changes = snapshot.state()
|
||||||
.map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
.map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
||||||
let attrs_changed = snapshot.has_attrs();
|
let attrs_changed = snapshot.has_attrs();
|
||||||
|
@ -458,21 +455,25 @@ impl DependencySet {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hint = RestyleHint::empty();
|
let mut hint = RestyleHint::empty();
|
||||||
let snapshot = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
|
let snapshot_el = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
|
||||||
|
|
||||||
Self::compute_partial_hint(&self.common_deps, el, &snapshot,
|
Self::compute_partial_hint(&self.common_deps, el, &snapshot_el,
|
||||||
&state_changes, attrs_changed, &mut hint);
|
&state_changes, attrs_changed, &mut hint);
|
||||||
|
|
||||||
if !state_changes.is_empty() {
|
if !state_changes.is_empty() {
|
||||||
Self::compute_partial_hint(&self.state_deps, el, &snapshot,
|
Self::compute_partial_hint(&self.state_deps, el, &snapshot_el,
|
||||||
&state_changes, attrs_changed, &mut hint);
|
&state_changes, attrs_changed, &mut hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if attrs_changed {
|
if attrs_changed {
|
||||||
Self::compute_partial_hint(&self.attr_deps, el, &snapshot,
|
Self::compute_partial_hint(&self.attr_deps, el, &snapshot_el,
|
||||||
&state_changes, attrs_changed, &mut hint);
|
&state_changes, attrs_changed, &mut hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("Calculated restyle hint: {:?}. (Element={:?}, State={:?}, Snapshot={:?}, {} Deps)",
|
||||||
|
hint, el, current_state, snapshot, self.len());
|
||||||
|
trace!("Deps: {:?}", self);
|
||||||
|
|
||||||
hint
|
hint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use cssparser::Parser as CssParser;
|
||||||
use matching::{common_style_affecting_attributes, CommonStyleAffectingAttributeMode};
|
use matching::{common_style_affecting_attributes, CommonStyleAffectingAttributeMode};
|
||||||
use selectors::Element;
|
use selectors::Element;
|
||||||
use selectors::parser::{AttrSelector, SelectorList};
|
use selectors::parser::{AttrSelector, SelectorList};
|
||||||
|
use std::fmt::Debug;
|
||||||
use stylesheets::{Origin, Namespaces};
|
use stylesheets::{Origin, Namespaces};
|
||||||
|
|
||||||
pub type AttrValue = <SelectorImpl as ::selectors::SelectorImpl>::AttrValue;
|
pub type AttrValue = <SelectorImpl as ::selectors::SelectorImpl>::AttrValue;
|
||||||
|
@ -30,6 +31,12 @@ pub use servo::restyle_damage::ServoRestyleDamage as RestyleDamage;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
|
pub use gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
pub type PreExistingComputedValues = ::std::sync::Arc<::properties::ServoComputedValues>;
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub type PreExistingComputedValues = ::gecko_bindings::structs::nsStyleContext;
|
||||||
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct SelectorParser<'a> {
|
pub struct SelectorParser<'a> {
|
||||||
pub stylesheet_origin: Origin,
|
pub stylesheet_origin: Origin,
|
||||||
|
@ -99,7 +106,7 @@ impl PseudoElementCascadeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ElementExt: Element<Impl=SelectorImpl> {
|
pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
|
||||||
fn is_link(&self) -> bool;
|
fn is_link(&self) -> bool;
|
||||||
|
|
||||||
fn matches_user_and_author_rules(&self) -> bool;
|
fn matches_user_and_author_rules(&self) -> bool;
|
||||||
|
|
|
@ -4,14 +4,17 @@
|
||||||
|
|
||||||
//! Implements sequential traversal over the DOM tree.
|
//! Implements sequential traversal over the DOM tree.
|
||||||
|
|
||||||
use dom::TNode;
|
use dom::{TElement, TNode};
|
||||||
use traversal::{DomTraversalContext, PerLevelTraversalData};
|
use traversal::{DomTraversalContext, PerLevelTraversalData, PreTraverseToken};
|
||||||
|
|
||||||
pub fn traverse_dom<N, C>(root: N,
|
pub fn traverse_dom<N, C>(root: N::ConcreteElement,
|
||||||
shared: &C::SharedContext)
|
shared: &C::SharedContext,
|
||||||
|
token: PreTraverseToken)
|
||||||
where N: TNode,
|
where N: TNode,
|
||||||
C: DomTraversalContext<N>
|
C: DomTraversalContext<N>
|
||||||
{
|
{
|
||||||
|
debug_assert!(token.should_traverse());
|
||||||
|
|
||||||
fn doit<'a, N, C>(context: &'a C, node: N, data: &mut PerLevelTraversalData)
|
fn doit<'a, N, C>(context: &'a C, node: N, data: &mut PerLevelTraversalData)
|
||||||
where N: TNode,
|
where N: TNode,
|
||||||
C: DomTraversalContext<N>
|
C: DomTraversalContext<N>
|
||||||
|
@ -29,7 +32,7 @@ pub fn traverse_dom<N, C>(root: N,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.needs_postorder_traversal() {
|
if C::needs_postorder_traversal() {
|
||||||
context.process_postorder(node);
|
context.process_postorder(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,8 +40,13 @@ pub fn traverse_dom<N, C>(root: N,
|
||||||
let mut data = PerLevelTraversalData {
|
let mut data = PerLevelTraversalData {
|
||||||
current_dom_depth: None,
|
current_dom_depth: None,
|
||||||
};
|
};
|
||||||
let context = C::new(shared, root.opaque());
|
let context = C::new(shared, root.as_node().opaque());
|
||||||
doit::<N, C>(&context, root, &mut data);
|
|
||||||
|
if token.should_skip_root() {
|
||||||
|
C::traverse_children(root, |kid| doit::<N, C>(&context, kid, &mut data));
|
||||||
|
} else {
|
||||||
|
doit::<N, C>(&context, root.as_node(), &mut data);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the local LRU cache since we store stateful elements inside.
|
// Clear the local LRU cache since we store stateful elements inside.
|
||||||
context.local_context().style_sharing_candidate_cache.borrow_mut().clear();
|
context.local_context().style_sharing_candidate_cache.borrow_mut().clear();
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use computed_values::display;
|
use computed_values::display;
|
||||||
use dom::TRestyleDamage;
|
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
use properties::ServoComputedValues;
|
use properties::ServoComputedValues;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -53,15 +52,8 @@ impl HeapSizeOf for ServoRestyleDamage {
|
||||||
fn heap_size_of_children(&self) -> usize { 0 }
|
fn heap_size_of_children(&self) -> usize { 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TRestyleDamage for ServoRestyleDamage {
|
impl ServoRestyleDamage {
|
||||||
/// For Servo the style source is always the computed values.
|
pub fn compute(old: &Arc<ServoComputedValues>,
|
||||||
type PreExistingComputedValues = Arc<ServoComputedValues>;
|
|
||||||
|
|
||||||
fn empty() -> Self {
|
|
||||||
ServoRestyleDamage::empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute(old: &Arc<ServoComputedValues>,
|
|
||||||
new: &Arc<ServoComputedValues>) -> ServoRestyleDamage {
|
new: &Arc<ServoComputedValues>) -> ServoRestyleDamage {
|
||||||
compute_damage(old, new)
|
compute_damage(old, new)
|
||||||
}
|
}
|
||||||
|
@ -72,13 +64,11 @@ impl TRestyleDamage for ServoRestyleDamage {
|
||||||
/// Use this instead of `ServoRestyleDamage::all()` because
|
/// Use this instead of `ServoRestyleDamage::all()` because
|
||||||
/// `ServoRestyleDamage::all()` will result in unnecessary sequential resolution
|
/// `ServoRestyleDamage::all()` will result in unnecessary sequential resolution
|
||||||
/// of generated content.
|
/// of generated content.
|
||||||
fn rebuild_and_reflow() -> ServoRestyleDamage {
|
pub fn rebuild_and_reflow() -> ServoRestyleDamage {
|
||||||
REPAINT | REPOSITION | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW |
|
REPAINT | REPOSITION | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW |
|
||||||
RECONSTRUCT_FLOW
|
RECONSTRUCT_FLOW
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ServoRestyleDamage {
|
|
||||||
/// Supposing a flow has the given `position` property and this damage,
|
/// Supposing a flow has the given `position` property and this damage,
|
||||||
/// returns the damage that we should add to the *parent* of this flow.
|
/// returns the damage that we should add to the *parent* of this flow.
|
||||||
pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> ServoRestyleDamage {
|
pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> ServoRestyleDamage {
|
||||||
|
|
|
@ -13,6 +13,7 @@ use selectors::{Element, MatchAttrGeneric};
|
||||||
use selectors::parser::AttrSelector;
|
use selectors::parser::AttrSelector;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// NB: If you add to this list, be sure to update `each_pseudo_element` too.
|
/// NB: If you add to this list, be sure to update `each_pseudo_element` too.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -398,7 +399,7 @@ impl MatchAttrGeneric for ServoElementSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Element<Impl=SelectorImpl>> ElementExt for E {
|
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
|
||||||
fn is_link(&self) -> bool {
|
fn is_link(&self) -> bool {
|
||||||
self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
|
self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,18 @@
|
||||||
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use bloom::StyleBloom;
|
use bloom::StyleBloom;
|
||||||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
use data::{ElementData, RestyleData, StoredRestyleHint};
|
use data::{ElementData, StoredRestyleHint};
|
||||||
use dom::{OpaqueNode, StylingMode, TElement, TNode};
|
use dom::{OpaqueNode, TElement, TNode};
|
||||||
use matching::{MatchMethods, StyleSharingResult};
|
use matching::{MatchMethods, StyleSharingResult};
|
||||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF};
|
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
||||||
|
use selector_parser::RestyleDamage;
|
||||||
|
use selectors::Element;
|
||||||
use selectors::matching::StyleRelations;
|
use selectors::matching::StyleRelations;
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::mem;
|
||||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||||
|
use stylist::Stylist;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
|
|
||||||
/// Every time we do another layout, the old bloom filters are invalid. This is
|
/// Every time we do another layout, the old bloom filters are invalid. This is
|
||||||
|
@ -36,7 +40,7 @@ thread_local!(
|
||||||
pub fn take_thread_local_bloom_filter(context: &SharedStyleContext)
|
pub fn take_thread_local_bloom_filter(context: &SharedStyleContext)
|
||||||
-> StyleBloom
|
-> StyleBloom
|
||||||
{
|
{
|
||||||
debug!("{} taking bf", ::tid::tid());
|
trace!("{} taking bf", ::tid::tid());
|
||||||
|
|
||||||
STYLE_BLOOM.with(|style_bloom| {
|
STYLE_BLOOM.with(|style_bloom| {
|
||||||
style_bloom.borrow_mut().take()
|
style_bloom.borrow_mut().take()
|
||||||
|
@ -45,7 +49,7 @@ pub fn take_thread_local_bloom_filter(context: &SharedStyleContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_thread_local_bloom_filter(bf: StyleBloom) {
|
pub fn put_thread_local_bloom_filter(bf: StyleBloom) {
|
||||||
debug!("[{}] putting bloom filter back", ::tid::tid());
|
trace!("[{}] putting bloom filter back", ::tid::tid());
|
||||||
|
|
||||||
STYLE_BLOOM.with(move |style_bloom| {
|
STYLE_BLOOM.with(move |style_bloom| {
|
||||||
debug_assert!(style_bloom.borrow().is_none(),
|
debug_assert!(style_bloom.borrow().is_none(),
|
||||||
|
@ -64,7 +68,7 @@ pub fn remove_from_bloom_filter<'a, E, C>(context: &C, root: OpaqueNode, element
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
C: StyleContext<'a>
|
C: StyleContext<'a>
|
||||||
{
|
{
|
||||||
debug!("[{}] remove_from_bloom_filter", ::tid::tid());
|
trace!("[{}] remove_from_bloom_filter", ::tid::tid());
|
||||||
|
|
||||||
// We may have arrived to `reconstruct_flows` without entering in style
|
// We may have arrived to `reconstruct_flows` without entering in style
|
||||||
// recalc at all due to our optimizations, nor that it's up to date, so we
|
// recalc at all due to our optimizations, nor that it's up to date, so we
|
||||||
|
@ -96,8 +100,25 @@ pub struct PerLevelTraversalData {
|
||||||
pub current_dom_depth: Option<usize>,
|
pub current_dom_depth: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This structure exists to enforce that callers invoke pre_traverse, and also
|
||||||
|
/// to pass information from the pre-traversal into the primary traversal.
|
||||||
|
pub struct PreTraverseToken {
|
||||||
|
traverse: bool,
|
||||||
|
skip_root: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreTraverseToken {
|
||||||
|
pub fn should_traverse(&self) -> bool {
|
||||||
|
self.traverse
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_skip_root(&self) -> bool {
|
||||||
|
self.skip_root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait DomTraversalContext<N: TNode> {
|
pub trait DomTraversalContext<N: TNode> {
|
||||||
type SharedContext: Sync + 'static;
|
type SharedContext: Sync + 'static + Borrow<SharedStyleContext>;
|
||||||
|
|
||||||
fn new<'a>(&'a Self::SharedContext, OpaqueNode) -> Self;
|
fn new<'a>(&'a Self::SharedContext, OpaqueNode) -> Self;
|
||||||
|
|
||||||
|
@ -113,24 +134,113 @@ pub trait DomTraversalContext<N: TNode> {
|
||||||
/// performed.
|
/// performed.
|
||||||
///
|
///
|
||||||
/// If it's false, then process_postorder has no effect at all.
|
/// If it's false, then process_postorder has no effect at all.
|
||||||
fn needs_postorder_traversal(&self) -> bool { true }
|
fn needs_postorder_traversal() -> bool { true }
|
||||||
|
|
||||||
/// Returns true if traversal should visit the given child.
|
/// Must be invoked before traversing the root element to determine whether
|
||||||
fn should_traverse_child(child: N) -> bool;
|
/// a traversal is needed. Returns a token that allows the caller to prove
|
||||||
|
/// that the call happened.
|
||||||
|
///
|
||||||
|
/// The skip_root parameter is used in Gecko to style newly-appended children
|
||||||
|
/// without restyling the parent.
|
||||||
|
fn pre_traverse(root: N::ConcreteElement, stylist: &Stylist, skip_root: bool)
|
||||||
|
-> PreTraverseToken
|
||||||
|
{
|
||||||
|
// If we should skip the root, traverse unconditionally.
|
||||||
|
if skip_root {
|
||||||
|
return PreTraverseToken {
|
||||||
|
traverse: true,
|
||||||
|
skip_root: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand the snapshot, if any. This is normally handled by the parent, so
|
||||||
|
// we need a special case for the root.
|
||||||
|
//
|
||||||
|
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
|
||||||
|
// we will drop on the floor. This is fine, because we don't traverse roots
|
||||||
|
// with siblings.
|
||||||
|
debug_assert!(root.next_sibling_element().is_none());
|
||||||
|
if let Some(mut data) = root.mutate_data() {
|
||||||
|
if let Some(r) = data.as_restyle_mut() {
|
||||||
|
let _later_siblings = r.expand_snapshot(root, stylist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PreTraverseToken {
|
||||||
|
traverse: Self::node_needs_traversal(root.as_node()),
|
||||||
|
skip_root: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if traversal should visit a text node. The style system never
|
||||||
|
/// processes text nodes, but Servo overrides this to visit them for flow
|
||||||
|
/// construction when necessary.
|
||||||
|
fn text_node_needs_traversal(node: N) -> bool { debug_assert!(node.is_text_node()); false }
|
||||||
|
|
||||||
|
/// Returns true if traversal is needed for the given node and subtree.
|
||||||
|
fn node_needs_traversal(node: N) -> bool {
|
||||||
|
// Non-incremental layout visits every node.
|
||||||
|
if cfg!(feature = "servo") && opts::get().nonincremental_layout {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match node.as_element() {
|
||||||
|
None => Self::text_node_needs_traversal(node),
|
||||||
|
Some(el) => {
|
||||||
|
// If the dirty descendants bit is set, we need to traverse no
|
||||||
|
// matter what. Skip examining the ElementData.
|
||||||
|
if el.has_dirty_descendants() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the element data. If it doesn't exist, we need to visit
|
||||||
|
// the element.
|
||||||
|
let data = match el.borrow_data() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check what kind of element data we have. If it's Initial or Persistent,
|
||||||
|
// we're done.
|
||||||
|
let restyle = match *data {
|
||||||
|
ElementData::Initial(ref i) => return i.is_none(),
|
||||||
|
ElementData::Persistent(_) => return false,
|
||||||
|
ElementData::Restyle(ref r) => r,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check whether we have any selector matching or re-cascading to
|
||||||
|
// do in this subtree.
|
||||||
|
debug_assert!(restyle.snapshot.is_none(), "Snapshots should already be expanded");
|
||||||
|
if !restyle.hint.is_empty() || restyle.recascade {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Servo uses the post-order traversal for flow construction, so
|
||||||
|
// we need to traverse any element with damage so that we can perform
|
||||||
|
// fixup / reconstruction on our way back up the tree.
|
||||||
|
if cfg!(feature = "servo") && restyle.damage != RestyleDamage::empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper for the traversal implementations to select the children that
|
/// Helper for the traversal implementations to select the children that
|
||||||
/// should be enqueued for processing.
|
/// should be enqueued for processing.
|
||||||
fn traverse_children<F: FnMut(N)>(parent: N::ConcreteElement, mut f: F)
|
fn traverse_children<F: FnMut(N)>(parent: N::ConcreteElement, mut f: F)
|
||||||
{
|
{
|
||||||
use dom::StylingMode::Restyle;
|
|
||||||
|
|
||||||
if parent.is_display_none() {
|
if parent.is_display_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for kid in parent.as_node().children() {
|
for kid in parent.as_node().children() {
|
||||||
if Self::should_traverse_child(kid) {
|
if Self::node_needs_traversal(kid) {
|
||||||
if kid.as_element().map_or(false, |el| el.styling_mode() == Restyle) {
|
let el = kid.as_element();
|
||||||
|
if el.as_ref().and_then(|el| el.borrow_data())
|
||||||
|
.map_or(false, |d| d.is_restyle())
|
||||||
|
{
|
||||||
unsafe { parent.set_dirty_descendants(); }
|
unsafe { parent.set_dirty_descendants(); }
|
||||||
}
|
}
|
||||||
f(kid);
|
f(kid);
|
||||||
|
@ -206,61 +316,75 @@ pub fn style_element_in_display_none_subtree<'a, E, C, F>(element: E,
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
||||||
data: &mut PerLevelTraversalData,
|
traversal_data: &mut PerLevelTraversalData,
|
||||||
element: E)
|
element: E,
|
||||||
|
mut data: &mut AtomicRefMut<ElementData>)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
C: StyleContext<'a>,
|
C: StyleContext<'a>,
|
||||||
D: DomTraversalContext<E::ConcreteNode>
|
D: DomTraversalContext<E::ConcreteNode>
|
||||||
{
|
{
|
||||||
let mode = element.styling_mode();
|
debug_assert!(data.as_restyle().map_or(true, |r| r.snapshot.is_none()),
|
||||||
let should_compute = element.borrow_data().map_or(true, |d| d.get_current_styles().is_none());
|
"Snapshots should be expanded by the caller");
|
||||||
debug!("recalc_style_at: {:?} (should_compute={:?} mode={:?}, data={:?})",
|
|
||||||
element, should_compute, mode, element.borrow_data());
|
|
||||||
|
|
||||||
let (computed_display_none, propagated_hint) = if should_compute {
|
let compute_self = !data.has_current_styles();
|
||||||
compute_style::<_, _, D>(context, data, element)
|
let mut inherited_style_changed = false;
|
||||||
} else {
|
|
||||||
(false, StoredRestyleHint::empty())
|
debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})",
|
||||||
|
element, compute_self, element.has_dirty_descendants(), data);
|
||||||
|
|
||||||
|
// Compute style for this element if necessary.
|
||||||
|
if compute_self {
|
||||||
|
inherited_style_changed = compute_style::<_, _, D>(context, &mut data, traversal_data, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that matching and cascading is done, clear the bits corresponding to
|
||||||
|
// those operations and compute the propagated restyle hint.
|
||||||
|
let empty_hint = StoredRestyleHint::empty();
|
||||||
|
let propagated_hint = match data.as_restyle_mut() {
|
||||||
|
None => empty_hint,
|
||||||
|
Some(r) => {
|
||||||
|
r.recascade = false;
|
||||||
|
mem::replace(&mut r.hint, empty_hint).propagate()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
debug_assert!(data.has_current_styles());
|
||||||
|
trace!("propagated_hint={:?}, inherited_style_changed={:?}", propagated_hint, inherited_style_changed);
|
||||||
|
|
||||||
// Preprocess children, computing restyle hints and handling sibling relationships.
|
// Preprocess children, propagating restyle hints and handling sibling relationships.
|
||||||
//
|
if !data.styles().is_display_none() &&
|
||||||
// We don't need to do this if we're not traversing children, or if we're performing
|
(element.has_dirty_descendants() || !propagated_hint.is_empty() || inherited_style_changed) {
|
||||||
// initial styling.
|
preprocess_children::<_, _, D>(context, element, propagated_hint, inherited_style_changed);
|
||||||
let will_traverse_children = !computed_display_none &&
|
|
||||||
(mode == StylingMode::Restyle ||
|
|
||||||
mode == StylingMode::Traverse);
|
|
||||||
if will_traverse_children {
|
|
||||||
preprocess_children::<_, _, D>(context, element, propagated_hint,
|
|
||||||
mode == StylingMode::Restyle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Computes style, returning true if the inherited styles changed for this
|
||||||
|
// element.
|
||||||
|
//
|
||||||
|
// FIXME(bholley): This should differentiate between matching and cascading,
|
||||||
|
// since we have separate bits for each now.
|
||||||
fn compute_style<'a, E, C, D>(context: &'a C,
|
fn compute_style<'a, E, C, D>(context: &'a C,
|
||||||
data: &mut PerLevelTraversalData,
|
mut data: &mut AtomicRefMut<ElementData>,
|
||||||
element: E) -> (bool, StoredRestyleHint)
|
traversal_data: &mut PerLevelTraversalData,
|
||||||
|
element: E) -> bool
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
C: StyleContext<'a>,
|
C: StyleContext<'a>,
|
||||||
D: DomTraversalContext<E::ConcreteNode>
|
D: DomTraversalContext<E::ConcreteNode>,
|
||||||
{
|
{
|
||||||
let shared_context = context.shared_context();
|
let shared_context = context.shared_context();
|
||||||
let mut bf = take_thread_local_bloom_filter(shared_context);
|
let mut bf = take_thread_local_bloom_filter(shared_context);
|
||||||
// Ensure the bloom filter is up to date.
|
// Ensure the bloom filter is up to date.
|
||||||
let dom_depth = bf.insert_parents_recovering(element,
|
let dom_depth = bf.insert_parents_recovering(element,
|
||||||
data.current_dom_depth,
|
traversal_data.current_dom_depth,
|
||||||
shared_context.generation);
|
shared_context.generation);
|
||||||
|
|
||||||
// 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, but it can
|
// Note that this is always the same than the pre-existing depth, but it can
|
||||||
// change from unknown to known at this step.
|
// change from unknown to known at this step.
|
||||||
data.current_dom_depth = Some(dom_depth);
|
traversal_data.current_dom_depth = Some(dom_depth);
|
||||||
|
|
||||||
bf.assert_complete(element);
|
bf.assert_complete(element);
|
||||||
|
|
||||||
let mut data = unsafe { D::ensure_element_data(&element).borrow_mut() };
|
|
||||||
debug_assert!(!data.is_persistent());
|
|
||||||
|
|
||||||
// Check to see whether we can share a style with someone.
|
// Check to see whether we can share a style with someone.
|
||||||
let style_sharing_candidate_cache =
|
let style_sharing_candidate_cache =
|
||||||
&mut context.local_context().style_sharing_candidate_cache.borrow_mut();
|
&mut context.local_context().style_sharing_candidate_cache.borrow_mut();
|
||||||
|
@ -304,7 +428,7 @@ fn compute_style<'a, E, C, D>(context: &'a C,
|
||||||
// Add ourselves to the LRU cache.
|
// Add ourselves to the LRU cache.
|
||||||
if let Some(element) = shareable_element {
|
if let Some(element) = shareable_element {
|
||||||
style_sharing_candidate_cache.insert_if_possible(&element,
|
style_sharing_candidate_cache.insert_if_possible(&element,
|
||||||
&data.current_styles().primary.values,
|
&data.styles().primary.values,
|
||||||
relations);
|
relations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,7 +442,7 @@ fn compute_style<'a, E, C, D>(context: &'a C,
|
||||||
|
|
||||||
// If we're restyling this element to display:none, throw away all style data
|
// If we're restyling this element to display:none, throw away all style data
|
||||||
// in the subtree, notify the caller to early-return.
|
// in the subtree, notify the caller to early-return.
|
||||||
let display_none = data.current_styles().is_display_none();
|
let display_none = data.styles().is_display_none();
|
||||||
if display_none {
|
if display_none {
|
||||||
debug!("New element style is display:none - clearing data from descendants.");
|
debug!("New element style is display:none - clearing data from descendants.");
|
||||||
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
||||||
|
@ -331,13 +455,16 @@ fn compute_style<'a, E, C, D>(context: &'a C,
|
||||||
// complexity.
|
// complexity.
|
||||||
put_thread_local_bloom_filter(bf);
|
put_thread_local_bloom_filter(bf);
|
||||||
|
|
||||||
(display_none, data.as_restyle().map_or(StoredRestyleHint::empty(), |r| r.hint.propagate()))
|
// FIXME(bholley): Compute this accurately from the call to CalcStyleDifference.
|
||||||
|
let inherited_styles_changed = true;
|
||||||
|
|
||||||
|
inherited_styles_changed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_children<'a, E, C, D>(context: &'a C,
|
fn preprocess_children<'a, E, C, D>(context: &'a C,
|
||||||
element: E,
|
element: E,
|
||||||
mut propagated_hint: StoredRestyleHint,
|
mut propagated_hint: StoredRestyleHint,
|
||||||
restyled_parent: bool)
|
parent_inherited_style_changed: bool)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
C: StyleContext<'a>,
|
C: StyleContext<'a>,
|
||||||
D: DomTraversalContext<E::ConcreteNode>
|
D: DomTraversalContext<E::ConcreteNode>
|
||||||
|
@ -350,41 +477,33 @@ fn preprocess_children<'a, E, C, D>(context: &'a C,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up our lazy child restyle data.
|
let mut child_data = unsafe { D::ensure_element_data(&child).borrow_mut() };
|
||||||
let mut child_data = unsafe { LazyRestyleData::<E, D>::new(&child) };
|
if child_data.is_unstyled_initial() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut restyle_data = match child_data.restyle() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
// Propagate the parent and sibling restyle hint.
|
// Propagate the parent and sibling restyle hint.
|
||||||
if !propagated_hint.is_empty() {
|
if !propagated_hint.is_empty() {
|
||||||
child_data.ensure().map(|d| d.hint.insert(&propagated_hint));
|
restyle_data.hint.insert(&propagated_hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle element snashots.
|
// Handle element snapshots.
|
||||||
if child_data.has_snapshot() {
|
let stylist = &context.shared_context().stylist;
|
||||||
// Compute the restyle hint.
|
let later_siblings = restyle_data.expand_snapshot(child, stylist);
|
||||||
let mut restyle_data = child_data.ensure().unwrap();
|
if later_siblings {
|
||||||
let mut hint = context.shared_context().stylist
|
|
||||||
.compute_restyle_hint(&child,
|
|
||||||
restyle_data.snapshot.as_ref().unwrap(),
|
|
||||||
child.get_state());
|
|
||||||
|
|
||||||
// If the hint includes a directive for later siblings, strip
|
|
||||||
// it out and modify the base hint for future siblings.
|
|
||||||
if hint.contains(RESTYLE_LATER_SIBLINGS) {
|
|
||||||
hint.remove(RESTYLE_LATER_SIBLINGS);
|
|
||||||
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
|
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the hint.
|
// If properties that we inherited from the parent changed, we need to recascade.
|
||||||
if !hint.is_empty() {
|
//
|
||||||
restyle_data.hint.insert(&hint.into());
|
// FIXME(bholley): Need to handle explicitly-inherited reset properties somewhere.
|
||||||
}
|
if parent_inherited_style_changed {
|
||||||
}
|
restyle_data.recascade = true;
|
||||||
|
|
||||||
// If we restyled this node, conservatively mark all our children as
|
|
||||||
// needing a re-cascade. Once we have the rule tree, we will be able
|
|
||||||
// to distinguish between re-matching and re-cascading.
|
|
||||||
if restyled_parent {
|
|
||||||
child_data.ensure();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,60 +523,3 @@ pub fn clear_descendant_data<E: TElement, F: Fn(E)>(el: E, clear_data: &F) {
|
||||||
|
|
||||||
unsafe { el.unset_dirty_descendants(); }
|
unsafe { el.unset_dirty_descendants(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various steps in the child preparation algorithm above may cause us to lazily
|
|
||||||
/// instantiate the ElementData on the child. Encapsulate that logic into a
|
|
||||||
/// convenient abstraction.
|
|
||||||
struct LazyRestyleData<'b, E: TElement + 'b, D: DomTraversalContext<E::ConcreteNode>> {
|
|
||||||
data: Option<AtomicRefMut<'b, ElementData>>,
|
|
||||||
element: &'b E,
|
|
||||||
phantom: PhantomData<D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'b, E: TElement, D: DomTraversalContext<E::ConcreteNode>> LazyRestyleData<'b, E, D> {
|
|
||||||
/// This may lazily instantiate ElementData, and is therefore only safe to
|
|
||||||
/// call on an element for which we have exclusive access.
|
|
||||||
unsafe fn new(element: &'b E) -> Self {
|
|
||||||
LazyRestyleData {
|
|
||||||
data: None,
|
|
||||||
element: element,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ensure(&mut self) -> Option<&mut RestyleData> {
|
|
||||||
if self.data.is_none() {
|
|
||||||
let mut d = unsafe { D::ensure_element_data(self.element).borrow_mut() };
|
|
||||||
d.restyle();
|
|
||||||
self.data = Some(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.data.as_mut().unwrap().as_restyle_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks for the existence of an element snapshot without lazily instantiating
|
|
||||||
/// anything. This allows the traversal to cheaply pass through already-styled
|
|
||||||
/// nodes when they don't need a restyle.
|
|
||||||
fn has_snapshot(&self) -> bool {
|
|
||||||
// If there's no element data, we're done.
|
|
||||||
let raw_data = self.element.get_data();
|
|
||||||
if raw_data.is_none() {
|
|
||||||
debug_assert!(self.data.is_none());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is element data, we still may not have committed to processing
|
|
||||||
// the node. Carefully get a reference to the data.
|
|
||||||
let maybe_tmp_borrow;
|
|
||||||
let borrow_ref = match self.data {
|
|
||||||
Some(ref d) => d,
|
|
||||||
None => {
|
|
||||||
maybe_tmp_borrow = raw_data.unwrap().borrow_mut();
|
|
||||||
&maybe_tmp_borrow
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check for a snapshot.
|
|
||||||
borrow_ref.as_restyle().map_or(false, |d| d.snapshot.is_some())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use style::arc_ptr_eq;
|
||||||
use style::atomic_refcell::AtomicRefMut;
|
use style::atomic_refcell::AtomicRefMut;
|
||||||
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
|
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
|
||||||
use style::data::{ElementData, RestyleData};
|
use style::data::{ElementData, RestyleData};
|
||||||
use style::dom::{StylingMode, TElement, TNode, TRestyleDamage};
|
use style::dom::{TElement, TNode};
|
||||||
use style::error_reporting::StdoutErrorReporter;
|
use style::error_reporting::StdoutErrorReporter;
|
||||||
use style::gecko::context::StandaloneStyleContext;
|
use style::gecko::context::StandaloneStyleContext;
|
||||||
use style::gecko::context::clear_local_context;
|
use style::gecko::context::clear_local_context;
|
||||||
|
@ -58,7 +58,7 @@ use style::string_cache::Atom;
|
||||||
use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule};
|
use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule};
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
use style::traversal::{recalc_style_at, PerLevelTraversalData};
|
use style::traversal::{recalc_style_at, DomTraversalContext, PerLevelTraversalData};
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -106,7 +106,6 @@ fn create_shared_context(mut per_doc_data: &mut AtomicRefMut<PerDocumentStyleDat
|
||||||
// FIXME (bug 1303229): Use the actual viewport size here
|
// FIXME (bug 1303229): Use the actual viewport size here
|
||||||
viewport_size: Size2D::new(Au(0), Au(0)),
|
viewport_size: Size2D::new(Au(0), Au(0)),
|
||||||
screen_size_changed: false,
|
screen_size_changed: false,
|
||||||
skip_root: false,
|
|
||||||
generation: 0,
|
generation: 0,
|
||||||
goal: ReflowGoal::ForScriptQuery,
|
goal: ReflowGoal::ForScriptQuery,
|
||||||
stylist: per_doc_data.stylist.clone(),
|
stylist: per_doc_data.stylist.clone(),
|
||||||
|
@ -138,20 +137,22 @@ fn traverse_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !skip_root && element.styling_mode() == StylingMode::Stop {
|
let mut per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
|
|
||||||
|
let token = RecalcStyleOnly::pre_traverse(element, &per_doc_data.stylist, skip_root);
|
||||||
|
if !token.should_traverse() {
|
||||||
error!("Unnecessary call to traverse_subtree");
|
error!("Unnecessary call to traverse_subtree");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let shared_style_context = create_shared_context(&mut per_doc_data);
|
||||||
let mut shared_style_context = create_shared_context(&mut per_doc_data);
|
|
||||||
shared_style_context.skip_root = skip_root;
|
|
||||||
let known_depth = None;
|
let known_depth = None;
|
||||||
|
|
||||||
if per_doc_data.num_threads == 1 || per_doc_data.work_queue.is_none() {
|
if per_doc_data.num_threads == 1 || per_doc_data.work_queue.is_none() {
|
||||||
sequential::traverse_dom::<_, RecalcStyleOnly>(element.as_node(), &shared_style_context);
|
sequential::traverse_dom::<_, RecalcStyleOnly>(element, &shared_style_context, token);
|
||||||
} else {
|
} else {
|
||||||
parallel::traverse_dom::<_, RecalcStyleOnly>(element.as_node(), known_depth, &shared_style_context,
|
parallel::traverse_dom::<_, RecalcStyleOnly>(element, known_depth,
|
||||||
|
&shared_style_context, token,
|
||||||
per_doc_data.work_queue.as_mut().unwrap());
|
per_doc_data.work_queue.as_mut().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -735,10 +736,7 @@ pub extern "C" fn Servo_Element_GetSnapshot(element: RawGeckoElementBorrowed) ->
|
||||||
let element = GeckoElement(element);
|
let element = GeckoElement(element);
|
||||||
let mut data = unsafe { element.ensure_data().borrow_mut() };
|
let mut data = unsafe { element.ensure_data().borrow_mut() };
|
||||||
let snapshot = if let Some(restyle_data) = unsafe { maybe_restyle(&mut data, element) } {
|
let snapshot = if let Some(restyle_data) = unsafe { maybe_restyle(&mut data, element) } {
|
||||||
if restyle_data.snapshot.is_none() {
|
restyle_data.snapshot.ensure(|| element.create_snapshot()).borrow_mut_raw()
|
||||||
restyle_data.snapshot = Some(element.create_snapshot());
|
|
||||||
}
|
|
||||||
restyle_data.snapshot.as_mut().unwrap().borrow_mut_raw()
|
|
||||||
} else {
|
} else {
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
};
|
};
|
||||||
|
@ -757,8 +755,6 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
|
||||||
debug!("Servo_NoteExplicitHints: {:?}, restyle_hint={:?}, change_hint={:?}",
|
debug!("Servo_NoteExplicitHints: {:?}, restyle_hint={:?}, change_hint={:?}",
|
||||||
element, restyle_hint, change_hint);
|
element, restyle_hint, change_hint);
|
||||||
|
|
||||||
let restore_current_style = restyle_hint.0 == 0 && data.get_current_styles().is_some();
|
|
||||||
|
|
||||||
if let Some(restyle_data) = unsafe { maybe_restyle(&mut data, element) } {
|
if let Some(restyle_data) = unsafe { maybe_restyle(&mut data, element) } {
|
||||||
let restyle_hint: RestyleHint = restyle_hint.into();
|
let restyle_hint: RestyleHint = restyle_hint.into();
|
||||||
restyle_data.hint.insert(&restyle_hint.into());
|
restyle_data.hint.insert(&restyle_hint.into());
|
||||||
|
@ -766,21 +762,6 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
|
||||||
} else {
|
} else {
|
||||||
debug!("(Element not styled, discarding hints)");
|
debug!("(Element not styled, discarding hints)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we had up-to-date style before and only posted a change hint,
|
|
||||||
// avoid invalidating that style.
|
|
||||||
//
|
|
||||||
// This allows for posting explicit change hints during restyle between
|
|
||||||
// the servo style traversal and the gecko post-traversal (i.e. during the
|
|
||||||
// call to CreateNeedeFrames in ServoRestyleManager::ProcessPendingRestyles).
|
|
||||||
//
|
|
||||||
// FIXME(bholley): The is a very inefficient and hacky way of doing this,
|
|
||||||
// we should fix the ElementData restyle() API to be more granular so that it
|
|
||||||
// does the right thing automatically.
|
|
||||||
if restore_current_style {
|
|
||||||
let styles = data.previous_styles().unwrap().clone();
|
|
||||||
data.finish_styling(styles, GeckoRestyleDamage::empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -817,12 +798,14 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
||||||
let element = GeckoElement(element);
|
let element = GeckoElement(element);
|
||||||
debug!("Servo_ResolveStyle: {:?}, consume={:?}, compute={:?}", element, consume, compute);
|
debug!("Servo_ResolveStyle: {:?}, consume={:?}, compute={:?}", element, consume, compute);
|
||||||
|
|
||||||
|
let mut data = unsafe { element.ensure_data() }.borrow_mut();
|
||||||
|
|
||||||
if compute == structs::LazyComputeBehavior::Allow {
|
if compute == structs::LazyComputeBehavior::Allow {
|
||||||
let should_compute = unsafe { element.ensure_data() }.borrow().get_current_styles().is_none();
|
let should_compute = !data.has_current_styles();
|
||||||
if should_compute {
|
if should_compute {
|
||||||
debug!("Performing manual style computation");
|
debug!("Performing manual style computation");
|
||||||
if let Some(parent) = element.parent_element() {
|
if let Some(parent) = element.parent_element() {
|
||||||
if parent.borrow_data().map_or(true, |d| d.get_current_styles().is_none()) {
|
if parent.borrow_data().map_or(true, |d| !d.has_current_styles()) {
|
||||||
error!("Attempting manual style computation with unstyled parent");
|
error!("Attempting manual style computation with unstyled parent");
|
||||||
return Arc::new(ComputedValues::initial_values().clone()).into_strong();
|
return Arc::new(ComputedValues::initial_values().clone()).into_strong();
|
||||||
}
|
}
|
||||||
|
@ -832,10 +815,11 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
||||||
let shared_style_context = create_shared_context(&mut per_doc_data);
|
let shared_style_context = create_shared_context(&mut per_doc_data);
|
||||||
let context = StandaloneStyleContext::new(&shared_style_context);
|
let context = StandaloneStyleContext::new(&shared_style_context);
|
||||||
|
|
||||||
let mut data = PerLevelTraversalData {
|
let mut traversal_data = PerLevelTraversalData {
|
||||||
current_dom_depth: None,
|
current_dom_depth: None,
|
||||||
};
|
};
|
||||||
recalc_style_at::<_, _, RecalcStyleOnly>(&context, &mut data, element);
|
|
||||||
|
recalc_style_at::<_, _, RecalcStyleOnly>(&context, &mut traversal_data, element, &mut data);
|
||||||
|
|
||||||
// The element was either unstyled or needed restyle. If it was unstyled, it may have
|
// The element was either unstyled or needed restyle. If it was unstyled, it may have
|
||||||
// additional unstyled children that subsequent traversals won't find now that the style
|
// additional unstyled children that subsequent traversals won't find now that the style
|
||||||
|
@ -846,19 +830,17 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = element.mutate_data();
|
if !data.has_current_styles() {
|
||||||
let values = match data.as_ref().and_then(|d| d.get_current_styles()) {
|
|
||||||
Some(x) => x.primary.values.clone(),
|
|
||||||
None => {
|
|
||||||
error!("Resolving style on unstyled element with lazy computation forbidden.");
|
error!("Resolving style on unstyled element with lazy computation forbidden.");
|
||||||
return Arc::new(ComputedValues::initial_values().clone()).into_strong();
|
return Arc::new(ComputedValues::initial_values().clone()).into_strong();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
let values = data.styles().primary.values.clone();
|
||||||
|
|
||||||
if consume == structs::ConsumeStyleBehavior::Consume {
|
if consume == structs::ConsumeStyleBehavior::Consume {
|
||||||
// FIXME(bholley): Once we start storing style data on frames, we'll want to
|
// FIXME(bholley): Once we start storing style data on frames, we'll want to
|
||||||
// drop the data here instead.
|
// drop the data here instead.
|
||||||
data.unwrap().persist();
|
data.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
values.into_strong()
|
values.into_strong()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue