--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -1110,17 +1110,17 @@ impl LayoutThread {
if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change()) {
let mut iter = element.as_node().traverse_preorder();
let mut next = iter.next();
while let Some(node) = next {
if node.needs_dirty_on_viewport_size_changed() {
let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() {
- if d.has_styles() {
+ if d.has_values() {
d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
if let Some(p) = el.parent_element() {
unsafe { p.note_dirty_descendant() };
}
next = iter.next_skipping_children();
@@ -1146,17 +1146,17 @@ impl LayoutThread {
&guards,
Some(ua_stylesheets),
data.stylesheets_changed,
/* author_styles_disabled = */ false,
&mut extra_data);
let needs_reflow = viewport_size_changed && !needs_dirtying;
if needs_dirtying {
if let Some(mut d) = element.mutate_data() {
- if d.has_styles() {
+ if d.has_values() {
d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
}
if needs_reflow {
if let Some(mut flow) = self.try_get_layout_root(element.as_node()) {
LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
}
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -897,17 +897,17 @@ impl<'ln> ThreadSafeLayoutNode for Servo
#[inline]
fn type_id_without_excluding_pseudo_elements(&self) -> LayoutNodeType {
self.node.type_id()
}
fn parent_style(&self) -> Arc<ComputedValues> {
let parent = self.node.parent_node().unwrap().as_element().unwrap();
let parent_data = parent.get_data().unwrap().borrow();
- parent_data.styles().primary.values().clone()
+ parent_data.values.primary().clone()
}
fn debug_id(self) -> usize {
self.node.debug_id()
}
fn children(&self) -> LayoutIterator<Self::ChildrenIterator> {
LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new(*self))
--- a/servo/components/script_layout_interface/lib.rs
+++ b/servo/components/script_layout_interface/lib.rs
@@ -60,17 +60,17 @@ pub struct StyleData {
/// Information needed during parallel traversals.
pub parallel: DomParallelInfo,
}
impl StyleData {
pub fn new() -> Self {
Self {
- element_data: AtomicRefCell::new(ElementData::new(None)),
+ element_data: AtomicRefCell::new(ElementData::default()),
parallel: DomParallelInfo::new(),
}
}
}
#[derive(Copy, Clone, HeapSizeOf)]
pub struct OpaqueStyleAndLayoutData {
// NB: We really store a `StyleAndLayoutData` here, so be careful!
--- a/servo/components/script_layout_interface/wrapper_traits.rs
+++ b/servo/components/script_layout_interface/wrapper_traits.rs
@@ -341,26 +341,26 @@ pub trait ThreadSafeLayoutElement: Clone
fn style_data(&self) -> AtomicRef<ElementData>;
#[inline]
fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>>;
#[inline]
fn get_before_pseudo(&self) -> Option<Self> {
- if self.style_data().styles().pseudos.has(&PseudoElement::Before) {
+ if self.style_data().values.pseudos.has(&PseudoElement::Before) {
Some(self.with_pseudo(PseudoElementType::Before(None)))
} else {
None
}
}
#[inline]
fn get_after_pseudo(&self) -> Option<Self> {
- if self.style_data().styles().pseudos.has(&PseudoElement::After) {
+ if self.style_data().values.pseudos.has(&PseudoElement::After) {
Some(self.with_pseudo(PseudoElementType::After(None)))
} else {
None
}
}
#[inline]
fn get_details_summary_pseudo(&self) -> Option<Self> {
@@ -391,75 +391,75 @@ pub trait ThreadSafeLayoutElement: Clone
/// has not yet been performed, fails.
///
/// Unlike the version on TNode, this handles pseudo-elements.
#[inline]
fn style(&self, context: &SharedStyleContext) -> Arc<ServoComputedValues> {
let data = self.style_data();
match self.get_pseudo_element_type() {
PseudoElementType::Normal => {
- data.styles().primary.values().clone()
+ data.values.primary().clone()
},
other => {
// Precompute non-eagerly-cascaded pseudo-element styles if not
// cached before.
let style_pseudo = other.style_pseudo_element();
match style_pseudo.cascade_type() {
// Already computed during the cascade.
PseudoElementCascadeType::Eager => {
self.style_data()
- .styles().pseudos.get(&style_pseudo)
- .unwrap().values().clone()
+ .values.pseudos.get(&style_pseudo)
+ .unwrap().clone()
},
PseudoElementCascadeType::Precomputed => {
context.stylist.precomputed_values_for_pseudo(
&context.guards,
&style_pseudo,
- Some(data.styles().primary.values()),
+ Some(data.values.primary()),
CascadeFlags::empty(),
&ServoMetricsProvider)
- .values().clone()
+ .clone()
}
PseudoElementCascadeType::Lazy => {
context.stylist
.lazily_compute_pseudo_element_style(
&context.guards,
unsafe { &self.unsafe_get() },
&style_pseudo,
RuleInclusion::All,
- data.styles().primary.values(),
+ data.values.primary(),
&ServoMetricsProvider)
.unwrap()
- .values().clone()
+ .clone()
}
}
}
}
}
#[inline]
fn selected_style(&self) -> Arc<ServoComputedValues> {
let data = self.style_data();
- data.styles().pseudos
+ data.values.pseudos
.get(&PseudoElement::Selection).map(|s| s)
- .unwrap_or(&data.styles().primary)
- .values().clone()
+ .unwrap_or(data.values.primary())
+ .clone()
}
/// Returns the already resolved style of the node.
///
/// This differs from `style(ctx)` in that if the pseudo-element has not yet
/// been computed it would panic.
///
/// This should be used just for querying layout, or when we know the
/// element style is precomputed, not from general layout itself.
#[inline]
fn resolved_style(&self) -> Arc<ServoComputedValues> {
let data = self.style_data();
match self.get_pseudo_element_type() {
PseudoElementType::Normal
- => data.styles().primary.values().clone(),
+ => data.values.primary().clone(),
other
- => data.styles().pseudos
- .get(&other.style_pseudo_element()).unwrap().values().clone(),
+ => data.values.pseudos
+ .get(&other.style_pseudo_element()).unwrap().clone(),
}
}
}
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -475,17 +475,17 @@ pub fn start_transitions_if_applicable(n
}
}
had_animations
}
fn compute_style_for_animation_step(context: &SharedStyleContext,
step: &KeyframesStep,
- previous_style: &ComputedValues,
+ previous_values: &ComputedValues,
style_from_cascade: &ComputedValues,
font_metrics_provider: &FontMetricsProvider)
-> ComputedValues {
match step.value {
KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
KeyframesStepValue::Declarations { block: ref declarations } => {
let guard = declarations.read_with(context.guards.author);
@@ -497,20 +497,21 @@ fn compute_style_for_animation_step(cont
guard.declarations().iter().rev()
.map(|&(ref decl, _importance)| (decl, CascadeLevel::Animations))
};
// This currently ignores visited styles, which seems acceptable,
// as existing browsers don't appear to animate visited styles.
let computed =
properties::apply_declarations(context.stylist.device(),
+ previous_values.rules(),
/* is_root = */ false,
iter,
- previous_style,
- previous_style,
+ previous_values,
+ previous_values,
/* cascade_info = */ None,
/* visited_style = */ None,
&*context.error_reporter,
font_metrics_provider,
CascadeFlags::empty(),
context.quirks_mode);
computed
}
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -5,26 +5,25 @@
//! The context within which style is calculated.
#[cfg(feature = "servo")] use animation::Animation;
use animation::PropertyAnimation;
use app_units::Au;
use arrayvec::ArrayVec;
use bloom::StyleBloom;
use cache::LRUCache;
-use data::ElementData;
+use data::{EagerPseudoValues, ElementData};
use dom::{OpaqueNode, TNode, TElement, SendElement};
use error_reporting::ParseErrorReporter;
use euclid::Size2D;
use fnv::FnvHashMap;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")] use gecko_bindings::structs;
#[cfg(feature = "servo")] use parking_lot::RwLock;
use properties::ComputedValues;
-use properties::longhands::display::computed_value as display;
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, SnapshotMap};
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
use shared_lock::StylesheetGuards;
use sharing::{ValidationData, StyleSharingCandidateCache};
use std::fmt;
use std::ops::Add;
#[cfg(feature = "servo")] use std::sync::Mutex;
@@ -139,29 +138,28 @@ pub struct SharedStyleContext<'a> {
impl<'a> SharedStyleContext<'a> {
/// Return a suitable viewport size in order to be used for viewport units.
pub fn viewport_size(&self) -> Size2D<Au> {
self.stylist.device().au_viewport_size()
}
}
-/// The structure that represents the result of style computation. This is
-/// effectively a tuple of rules and computed values, that is, the rule node,
-/// and the result of computing that rule node's rules, the `ComputedValues`.
+/// The structure holds various intermediate inputs that are eventually used by
+/// by the cascade.
+///
+/// The matching and cascading process stores them in this format temporarily
+/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
+/// down into the main `ComputedValues` to reduce memory usage per element while
+/// still remaining accessible.
#[derive(Clone)]
-pub struct ComputedStyle {
+pub struct CascadeInputs {
/// The rule node representing the ordered list of rules matched for this
/// node.
- pub rules: StrongRuleNode,
-
- /// The computed values for each property obtained by cascading the
- /// matched rules. This can only be none during a transient interval of
- /// the styling algorithm, and callers can safely unwrap it.
- pub values: Option<Arc<ComputedValues>>,
+ rules: Option<StrongRuleNode>,
/// The rule node representing the ordered list of rules matched for this
/// node if visited, only computed if there's a relevant link for this
/// element. A element's "relevant link" is the element being matched if it
/// is a link or the nearest ancestor link.
visited_rules: Option<StrongRuleNode>,
/// The element's computed values if visited, only computed if there's a
@@ -169,42 +167,78 @@ pub struct ComputedStyle {
/// element being matched if it is a link or the nearest ancestor link.
///
/// We also store a reference to this inside the regular ComputedValues to
/// avoid refactoring all APIs to become aware of multiple ComputedValues
/// objects.
visited_values: Option<Arc<ComputedValues>>,
}
-impl ComputedStyle {
- /// Trivially construct a new `ComputedStyle`.
- pub fn new(rules: StrongRuleNode, values: Arc<ComputedValues>) -> Self {
- ComputedStyle {
- rules: rules,
- values: Some(values),
+impl Default for CascadeInputs {
+ fn default() -> Self {
+ CascadeInputs {
+ rules: None,
visited_rules: None,
visited_values: None,
}
}
+}
+
+impl CascadeInputs {
+ /// Construct inputs from previous cascade results, if any.
+ fn new_from_values(values: &Arc<ComputedValues>) -> Self {
+ CascadeInputs {
+ rules: values.rules.clone(),
+ visited_rules: values.get_visited_style().and_then(|v| v.rules.clone()),
+ // Values will be re-cascaded if necessary, so this can be None.
+ visited_values: None,
+ }
+ }
- /// Constructs a partial ComputedStyle, whose ComputedVaues will be filled
- /// in later.
- pub fn new_partial(rules: StrongRuleNode) -> Self {
- ComputedStyle {
- rules: rules,
- values: None,
- visited_rules: None,
- visited_values: None,
+ /// Whether there are any rules.
+ pub fn has_rules(&self) -> bool {
+ self.rules.is_some()
+ }
+
+ /// Gets a mutable reference to the rule node, if any.
+ pub fn get_rules_mut(&mut self) -> Option<&mut StrongRuleNode> {
+ self.rules.as_mut()
+ }
+
+ /// Gets a reference to the rule node. Panic if the element does not have
+ /// rule node.
+ pub fn rules(&self) -> &StrongRuleNode {
+ self.rules.as_ref().unwrap()
+ }
+
+ /// Sets the rule node depending on visited mode.
+ /// Returns whether the rules changed.
+ pub fn set_rules(&mut self,
+ visited_handling: VisitedHandlingMode,
+ rules: StrongRuleNode)
+ -> bool {
+ match visited_handling {
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
+ unreachable!("We should never try to selector match with \
+ AllLinksVisitedAndUnvisited");
+ },
+ VisitedHandlingMode::AllLinksUnvisited => self.set_unvisited_rules(rules),
+ VisitedHandlingMode::RelevantLinkVisited => self.set_visited_rules(rules),
}
}
- /// Returns a reference to the ComputedValues. The values can only be null during
- /// the styling algorithm, so this is safe to call elsewhere.
- pub fn values(&self) -> &Arc<ComputedValues> {
- self.values.as_ref().unwrap()
+ /// Sets the unvisited rule node, and returns whether it changed.
+ fn set_unvisited_rules(&mut self, rules: StrongRuleNode) -> bool {
+ if let Some(ref old_rules) = self.rules {
+ if *old_rules == rules {
+ return false
+ }
+ }
+ self.rules = Some(rules);
+ true
}
/// Whether there are any visited rules.
pub fn has_visited_rules(&self) -> bool {
self.visited_rules.is_some()
}
/// Gets a reference to the visited rule node, if any.
@@ -215,21 +249,21 @@ impl ComputedStyle {
/// Gets a mutable reference to the visited rule node, if any.
pub fn get_visited_rules_mut(&mut self) -> Option<&mut StrongRuleNode> {
self.visited_rules.as_mut()
}
/// Gets a reference to the visited rule node. Panic if the element does not
/// have visited rule node.
pub fn visited_rules(&self) -> &StrongRuleNode {
- self.get_visited_rules().unwrap()
+ self.visited_rules.as_ref().unwrap()
}
/// Sets the visited rule node, and returns whether it changed.
- pub fn set_visited_rules(&mut self, rules: StrongRuleNode) -> bool {
+ fn set_visited_rules(&mut self, rules: StrongRuleNode) -> bool {
if let Some(ref old_rules) = self.visited_rules {
if *old_rules == rules {
return false
}
}
self.visited_rules = Some(rules);
true
}
@@ -257,62 +291,91 @@ impl ComputedStyle {
/// Clone the visited computed values Arc. Used to store a reference to the
/// visited values inside the regular values.
pub fn clone_visited_values(&self) -> Option<Arc<ComputedValues>> {
self.visited_values.clone()
}
}
-// We manually implement Debug for ComputedStyle so that we can avoid the
+// We manually implement Debug for CascadeInputs so that we can avoid the
// verbose stringification of ComputedValues for normal logging.
-impl fmt::Debug for ComputedStyle {
+impl fmt::Debug for CascadeInputs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "ComputedStyle {{ rules: {:?}, values: {{..}} }}", self.rules)
+ write!(f, "CascadeInputs {{ rules: {:?}, visited_rules: {:?}, .. }}",
+ self.rules, self.visited_rules)
}
}
-/// A list of styles for eagerly-cascaded pseudo-elements. Lazily-allocated.
-#[derive(Clone, Debug)]
-pub struct EagerPseudoStyles(Option<Box<[Option<ComputedStyle>]>>);
+/// A list of cascade inputs for eagerly-cascaded pseudo-elements.
+/// The list is stored inline.
+#[derive(Debug)]
+pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
-impl EagerPseudoStyles {
- /// Returns whether there are any pseudo styles.
+// Manually implement `Clone` here because the derived impl of `Clone` for
+// array types assumes the value inside is `Copy`.
+impl Clone for EagerPseudoCascadeInputs {
+ fn clone(&self) -> Self {
+ if self.0.is_none() {
+ return EagerPseudoCascadeInputs(None)
+ }
+ let self_inputs = self.0.as_ref().unwrap();
+ let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
+ for i in 0..EAGER_PSEUDO_COUNT {
+ inputs[i] = self_inputs[i].clone();
+ }
+ EagerPseudoCascadeInputs(Some(inputs))
+ }
+}
+
+impl EagerPseudoCascadeInputs {
+ /// Construct inputs from previous cascade results, if any.
+ fn new_from_values(values: &EagerPseudoValues) -> Self {
+ EagerPseudoCascadeInputs(values.0.as_ref().map(|values| {
+ let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
+ for i in 0..EAGER_PSEUDO_COUNT {
+ inputs[i] = values[i].as_ref().map(|v| CascadeInputs::new_from_values(v));
+ }
+ inputs
+ }))
+ }
+
+ /// Returns whether there are any pseudo inputs.
pub fn is_empty(&self) -> bool {
self.0.is_none()
}
- /// Returns a reference to the style for a given eager pseudo, if it exists.
- pub fn get(&self, pseudo: &PseudoElement) -> Option<&ComputedStyle> {
+ /// Returns a reference to the inputs for a given eager pseudo, if they exist.
+ pub fn get(&self, pseudo: &PseudoElement) -> Option<&CascadeInputs> {
debug_assert!(pseudo.is_eager());
self.0.as_ref().and_then(|p| p[pseudo.eager_index()].as_ref())
}
- /// Returns a mutable reference to the style for a given eager pseudo, if it exists.
- pub fn get_mut(&mut self, pseudo: &PseudoElement) -> Option<&mut ComputedStyle> {
+ /// Returns a mutable reference to the inputs for a given eager pseudo, if they exist.
+ pub fn get_mut(&mut self, pseudo: &PseudoElement) -> Option<&mut CascadeInputs> {
debug_assert!(pseudo.is_eager());
self.0.as_mut().and_then(|p| p[pseudo.eager_index()].as_mut())
}
- /// Returns true if the EagerPseudoStyles has a ComputedStyle for |pseudo|.
+ /// Returns true if the EagerPseudoCascadeInputs has a inputs for |pseudo|.
pub fn has(&self, pseudo: &PseudoElement) -> bool {
self.get(pseudo).is_some()
}
/// Inserts a pseudo-element. The pseudo-element must not already exist.
- pub fn insert(&mut self, pseudo: &PseudoElement, style: ComputedStyle) {
+ pub fn insert(&mut self, pseudo: &PseudoElement, value: CascadeInputs) {
debug_assert!(!self.has(pseudo));
if self.0.is_none() {
- self.0 = Some(vec![None; EAGER_PSEUDO_COUNT].into_boxed_slice());
+ self.0 = Some(Default::default());
}
- self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(style);
+ self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(value);
}
- /// Removes a pseudo-element style if it exists, and returns it.
- fn take(&mut self, pseudo: &PseudoElement) -> Option<ComputedStyle> {
+ /// Removes a pseudo-element inputs if they exist, and returns it.
+ pub fn take(&mut self, pseudo: &PseudoElement) -> Option<CascadeInputs> {
let result = match self.0.as_mut() {
None => return None,
Some(arr) => arr[pseudo.eager_index()].take(),
};
let empty = self.0.as_ref().unwrap().iter().all(|x| x.is_none());
if empty {
self.0 = None;
}
@@ -335,56 +398,58 @@ impl EagerPseudoStyles {
/// Adds the unvisited rule node for a given pseudo-element, which may or
/// may not exist.
///
/// Returns true if the pseudo-element is new.
fn add_unvisited_rules(&mut self,
pseudo: &PseudoElement,
rules: StrongRuleNode)
-> bool {
- if let Some(mut style) = self.get_mut(pseudo) {
- style.rules = rules;
+ if let Some(mut inputs) = self.get_mut(pseudo) {
+ inputs.set_unvisited_rules(rules);
return false
}
- self.insert(pseudo, ComputedStyle::new_partial(rules));
+ let mut inputs = CascadeInputs::default();
+ inputs.set_unvisited_rules(rules);
+ self.insert(pseudo, inputs);
true
}
/// Remove the unvisited rule node for a given pseudo-element, which may or
/// may not exist. Since removing the rule node implies we don't need any
/// other data for the pseudo, take the entire pseudo if found.
///
/// Returns true if the pseudo-element was removed.
fn remove_unvisited_rules(&mut self, pseudo: &PseudoElement) -> bool {
self.take(pseudo).is_some()
}
/// Adds the visited rule node for a given pseudo-element. It is assumed to
- /// already exist because unvisited styles should have been added first.
+ /// already exist because unvisited inputs should have been added first.
///
/// Returns true if the pseudo-element is new. (Always false, but returns a
/// bool for parity with `add_unvisited_rules`.)
fn add_visited_rules(&mut self,
pseudo: &PseudoElement,
rules: StrongRuleNode)
-> bool {
debug_assert!(self.has(pseudo));
- let mut style = self.get_mut(pseudo).unwrap();
- style.set_visited_rules(rules);
+ let mut inputs = self.get_mut(pseudo).unwrap();
+ inputs.set_visited_rules(rules);
false
}
/// Remove the visited rule node for a given pseudo-element, which may or
/// may not exist.
///
/// Returns true if the psuedo-element was removed. (Always false, but
/// returns a bool for parity with `remove_unvisited_rules`.)
fn remove_visited_rules(&mut self, pseudo: &PseudoElement) -> bool {
- if let Some(mut style) = self.get_mut(pseudo) {
- style.take_visited_rules();
+ if let Some(mut inputs) = self.get_mut(pseudo) {
+ inputs.take_visited_rules();
}
false
}
/// Adds a rule node for a given pseudo-element, which may or may not exist.
/// The type of rule node depends on the visited mode.
///
/// Returns true if the pseudo-element is new.
@@ -423,60 +488,77 @@ impl EagerPseudoStyles {
VisitedHandlingMode::AllLinksUnvisited => {
self.remove_unvisited_rules(&pseudo)
},
VisitedHandlingMode::RelevantLinkVisited => {
self.remove_visited_rules(&pseudo)
},
}
}
+}
- /// Returns whether this EagerPseudoStyles has the same set of
- /// pseudos as the given one.
- pub fn has_same_pseudos_as(&self, other: &EagerPseudoStyles) -> bool {
- // We could probably just compare self.keys() to other.keys(), but that
- // seems like it'll involve a bunch more moving stuff around and
- // whatnot.
- match (&self.0, &other.0) {
- (&Some(ref our_arr), &Some(ref other_arr)) => {
- for i in 0..EAGER_PSEUDO_COUNT {
- if our_arr[i].is_some() != other_arr[i].is_some() {
- return false
- }
- }
- true
- },
- (&None, &None) => true,
- _ => false,
+/// The cascade inputs associated with a node, including those for any
+/// pseudo-elements.
+///
+/// The matching and cascading process stores them in this format temporarily
+/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
+/// down into the main `ComputedValues` to reduce memory usage per element while
+/// still remaining accessible.
+#[derive(Clone, Debug)]
+pub struct ElementCascadeInputs {
+ /// The element's cascade inputs.
+ pub primary: Option<CascadeInputs>,
+ /// A list of the inputs for the element's eagerly-cascaded pseudo-elements.
+ pub pseudos: EagerPseudoCascadeInputs,
+}
+
+impl Default for ElementCascadeInputs {
+ /// Construct an empty `ElementCascadeInputs`.
+ fn default() -> Self {
+ ElementCascadeInputs {
+ primary: None,
+ pseudos: EagerPseudoCascadeInputs(None),
}
}
}
-/// The styles associated with a node, including the styles for any
-/// pseudo-elements.
-#[derive(Clone, Debug)]
-pub struct ElementStyles {
- /// The element's style.
- pub primary: ComputedStyle,
- /// A list of the styles for the element's eagerly-cascaded pseudo-elements.
- pub pseudos: EagerPseudoStyles,
-}
-
-impl ElementStyles {
- /// Trivially construct a new `ElementStyles`.
- pub fn new(primary: ComputedStyle) -> Self {
- ElementStyles {
- primary: primary,
- pseudos: EagerPseudoStyles(None),
+impl ElementCascadeInputs {
+ /// Construct inputs from previous cascade results, if any.
+ pub fn new_from_element_data(data: &ElementData) -> Self {
+ if !data.has_values() {
+ return ElementCascadeInputs::default()
+ }
+ ElementCascadeInputs {
+ primary: Some(CascadeInputs::new_from_values(data.values.primary())),
+ pseudos: EagerPseudoCascadeInputs::new_from_values(&data.values.pseudos),
}
}
- /// Whether this element `display` value is `none`.
- pub fn is_display_none(&self) -> bool {
- self.primary.values().get_box().clone_display() == display::T::none
+ /// Returns whether we have primary inputs.
+ pub fn has_primary(&self) -> bool {
+ self.primary.is_some()
+ }
+
+ /// Gets the primary inputs. Panic if unavailable.
+ pub fn primary(&self) -> &CascadeInputs {
+ self.primary.as_ref().unwrap()
+ }
+
+ /// Gets the mutable primary inputs. Panic if unavailable.
+ pub fn primary_mut(&mut self) -> &mut CascadeInputs {
+ self.primary.as_mut().unwrap()
+ }
+
+ /// Ensure primary inputs exist and create them if they do not.
+ /// Returns a mutable reference to the primary inputs.
+ pub fn ensure_primary(&mut self) -> &mut CascadeInputs {
+ if self.primary.is_none() {
+ self.primary = Some(CascadeInputs::default());
+ }
+ self.primary.as_mut().unwrap()
}
}
/// Information about the current element being processed. We group this
/// together into a single struct within ThreadLocalStyleContext so that we can
/// instantiate and destroy it easily at the beginning and end of element
/// processing.
pub struct CurrentElementInfo {
@@ -486,16 +568,21 @@ pub struct CurrentElementInfo {
element: OpaqueNode,
/// Whether the element is being styled for the first time.
is_initial_style: bool,
/// Lazy cache of the different data used for style sharing.
pub validation_data: ValidationData,
/// A Vec of possibly expired animations. Used only by Servo.
#[allow(dead_code)]
pub possibly_expired_animations: Vec<PropertyAnimation>,
+ /// Temporary storage for various intermediate inputs that are eventually
+ /// used by by the cascade. At the end of the cascade, they are folded down
+ /// into the main `ComputedValues` to reduce memory usage per element while
+ /// still remaining accessible.
+ pub cascade_inputs: ElementCascadeInputs,
}
/// Statistics gathered during the traversal. We gather statistics on each
/// thread and then combine them after the threads join via the Add
/// implementation below.
#[derive(Default)]
pub struct TraversalStatistics {
/// The total number of elements traversed.
@@ -780,19 +867,20 @@ impl<E: TElement> ThreadLocalStyleContex
}
}
/// Notes when the style system starts traversing an element.
pub fn begin_element(&mut self, element: E, data: &ElementData) {
debug_assert!(self.current_element_info.is_none());
self.current_element_info = Some(CurrentElementInfo {
element: element.as_node().opaque(),
- is_initial_style: !data.has_styles(),
+ is_initial_style: !data.has_values(),
validation_data: ValidationData::default(),
possibly_expired_animations: Vec::new(),
+ cascade_inputs: ElementCascadeInputs::default(),
});
}
/// Notes when the style system finishes traversing an element.
pub fn end_element(&mut self, element: E) {
debug_assert!(self.current_element_info.is_some());
debug_assert!(self.current_element_info.as_ref().unwrap().element ==
element.as_node().opaque());
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -7,352 +7,19 @@
use arrayvec::ArrayVec;
use context::SharedStyleContext;
use dom::TElement;
use invalidation::element::restyle_hints::RestyleHint;
use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
use properties::longhands::display::computed_value as display;
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
-use selectors::matching::VisitedHandlingMode;
use shared_lock::{Locked, StylesheetGuards};
-use std::fmt;
use stylearc::Arc;
-/// The structure that represents the result of style computation. This is
-/// effectively a tuple of rules and computed values, that is, the rule node,
-/// and the result of computing that rule node's rules, the `ComputedValues`.
-#[derive(Clone)]
-pub struct ComputedStyle {
- /// The rule node representing the ordered list of rules matched for this
- /// node.
- pub rules: StrongRuleNode,
-
- /// The computed values for each property obtained by cascading the
- /// matched rules. This can only be none during a transient interval of
- /// the styling algorithm, and callers can safely unwrap it.
- pub values: Option<Arc<ComputedValues>>,
-
- /// The rule node representing the ordered list of rules matched for this
- /// node if visited, only computed if there's a relevant link for this
- /// element. A element's "relevant link" is the element being matched if it
- /// is a link or the nearest ancestor link.
- visited_rules: Option<StrongRuleNode>,
-
- /// The element's computed values if visited, only computed if there's a
- /// relevant link for this element. A element's "relevant link" is the
- /// element being matched if it is a link or the nearest ancestor link.
- ///
- /// We also store a reference to this inside the regular ComputedValues to
- /// avoid refactoring all APIs to become aware of multiple ComputedValues
- /// objects.
- visited_values: Option<Arc<ComputedValues>>,
-}
-
-impl ComputedStyle {
- /// Trivially construct a new `ComputedStyle`.
- pub fn new(rules: StrongRuleNode, values: Arc<ComputedValues>) -> Self {
- ComputedStyle {
- rules: rules,
- values: Some(values),
- visited_rules: None,
- visited_values: None,
- }
- }
-
- /// Constructs a partial ComputedStyle, whose ComputedVaues will be filled
- /// in later.
- pub fn new_partial(rules: StrongRuleNode) -> Self {
- ComputedStyle {
- rules: rules,
- values: None,
- visited_rules: None,
- visited_values: None,
- }
- }
-
- /// Returns a reference to the ComputedValues. The values can only be null during
- /// the styling algorithm, so this is safe to call elsewhere.
- pub fn values(&self) -> &Arc<ComputedValues> {
- self.values.as_ref().unwrap()
- }
-
- /// Whether there are any visited rules.
- pub fn has_visited_rules(&self) -> bool {
- self.visited_rules.is_some()
- }
-
- /// Gets a reference to the visited rule node, if any.
- pub fn get_visited_rules(&self) -> Option<&StrongRuleNode> {
- self.visited_rules.as_ref()
- }
-
- /// Gets a mutable reference to the visited rule node, if any.
- pub fn get_visited_rules_mut(&mut self) -> Option<&mut StrongRuleNode> {
- self.visited_rules.as_mut()
- }
-
- /// Gets a reference to the visited rule node. Panic if the element does not
- /// have visited rule node.
- pub fn visited_rules(&self) -> &StrongRuleNode {
- self.get_visited_rules().unwrap()
- }
-
- /// Sets the visited rule node, and returns whether it changed.
- pub fn set_visited_rules(&mut self, rules: StrongRuleNode) -> bool {
- if let Some(ref old_rules) = self.visited_rules {
- if *old_rules == rules {
- return false
- }
- }
- self.visited_rules = Some(rules);
- true
- }
-
- /// Takes the visited rule node.
- pub fn take_visited_rules(&mut self) -> Option<StrongRuleNode> {
- self.visited_rules.take()
- }
-
- /// Gets a reference to the visited computed values. Panic if the element
- /// does not have visited computed values.
- pub fn visited_values(&self) -> &Arc<ComputedValues> {
- self.visited_values.as_ref().unwrap()
- }
-
- /// Sets the visited computed values.
- pub fn set_visited_values(&mut self, values: Arc<ComputedValues>) {
- self.visited_values = Some(values);
- }
-
- /// Take the visited computed values.
- pub fn take_visited_values(&mut self) -> Option<Arc<ComputedValues>> {
- self.visited_values.take()
- }
-
- /// Clone the visited computed values Arc. Used to store a reference to the
- /// visited values inside the regular values.
- pub fn clone_visited_values(&self) -> Option<Arc<ComputedValues>> {
- self.visited_values.clone()
- }
-}
-
-// We manually implement Debug for ComputedStyle so that we can avoid the
-// verbose stringification of ComputedValues for normal logging.
-impl fmt::Debug for ComputedStyle {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "ComputedStyle {{ rules: {:?}, values: {{..}} }}", self.rules)
- }
-}
-
-/// A list of styles for eagerly-cascaded pseudo-elements. Lazily-allocated.
-#[derive(Clone, Debug)]
-pub struct EagerPseudoStyles(Option<Box<[Option<ComputedStyle>]>>);
-
-impl EagerPseudoStyles {
- /// Returns whether there are any pseudo styles.
- pub fn is_empty(&self) -> bool {
- self.0.is_none()
- }
-
- /// Returns a reference to the style for a given eager pseudo, if it exists.
- pub fn get(&self, pseudo: &PseudoElement) -> Option<&ComputedStyle> {
- debug_assert!(pseudo.is_eager());
- self.0.as_ref().and_then(|p| p[pseudo.eager_index()].as_ref())
- }
-
- /// Returns a mutable reference to the style for a given eager pseudo, if it exists.
- pub fn get_mut(&mut self, pseudo: &PseudoElement) -> Option<&mut ComputedStyle> {
- debug_assert!(pseudo.is_eager());
- self.0.as_mut().and_then(|p| p[pseudo.eager_index()].as_mut())
- }
-
- /// Returns true if the EagerPseudoStyles has a ComputedStyle for |pseudo|.
- pub fn has(&self, pseudo: &PseudoElement) -> bool {
- self.get(pseudo).is_some()
- }
-
- /// Inserts a pseudo-element. The pseudo-element must not already exist.
- pub fn insert(&mut self, pseudo: &PseudoElement, style: ComputedStyle) {
- debug_assert!(!self.has(pseudo));
- if self.0.is_none() {
- self.0 = Some(vec![None; EAGER_PSEUDO_COUNT].into_boxed_slice());
- }
- self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(style);
- }
-
- /// Removes a pseudo-element style if it exists, and returns it.
- fn take(&mut self, pseudo: &PseudoElement) -> Option<ComputedStyle> {
- let result = match self.0.as_mut() {
- None => return None,
- Some(arr) => arr[pseudo.eager_index()].take(),
- };
- let empty = self.0.as_ref().unwrap().iter().all(|x| x.is_none());
- if empty {
- self.0 = None;
- }
- result
- }
-
- /// Returns a list of the pseudo-elements.
- pub fn keys(&self) -> ArrayVec<[PseudoElement; EAGER_PSEUDO_COUNT]> {
- let mut v = ArrayVec::new();
- if let Some(ref arr) = self.0 {
- for i in 0..EAGER_PSEUDO_COUNT {
- if arr[i].is_some() {
- v.push(PseudoElement::from_eager_index(i));
- }
- }
- }
- v
- }
-
- /// Adds the unvisited rule node for a given pseudo-element, which may or
- /// may not exist.
- ///
- /// Returns true if the pseudo-element is new.
- fn add_unvisited_rules(&mut self,
- pseudo: &PseudoElement,
- rules: StrongRuleNode)
- -> bool {
- if let Some(mut style) = self.get_mut(pseudo) {
- style.rules = rules;
- return false
- }
- self.insert(pseudo, ComputedStyle::new_partial(rules));
- true
- }
-
- /// Remove the unvisited rule node for a given pseudo-element, which may or
- /// may not exist. Since removing the rule node implies we don't need any
- /// other data for the pseudo, take the entire pseudo if found.
- ///
- /// Returns true if the pseudo-element was removed.
- fn remove_unvisited_rules(&mut self, pseudo: &PseudoElement) -> bool {
- self.take(pseudo).is_some()
- }
-
- /// Adds the visited rule node for a given pseudo-element. It is assumed to
- /// already exist because unvisited styles should have been added first.
- ///
- /// Returns true if the pseudo-element is new. (Always false, but returns a
- /// bool for parity with `add_unvisited_rules`.)
- fn add_visited_rules(&mut self,
- pseudo: &PseudoElement,
- rules: StrongRuleNode)
- -> bool {
- debug_assert!(self.has(pseudo));
- let mut style = self.get_mut(pseudo).unwrap();
- style.set_visited_rules(rules);
- false
- }
-
- /// Remove the visited rule node for a given pseudo-element, which may or
- /// may not exist.
- ///
- /// Returns true if the psuedo-element was removed. (Always false, but
- /// returns a bool for parity with `remove_unvisited_rules`.)
- fn remove_visited_rules(&mut self, pseudo: &PseudoElement) -> bool {
- if let Some(mut style) = self.get_mut(pseudo) {
- style.take_visited_rules();
- }
- false
- }
-
- /// Adds a rule node for a given pseudo-element, which may or may not exist.
- /// The type of rule node depends on the visited mode.
- ///
- /// Returns true if the pseudo-element is new.
- pub fn add_rules(&mut self,
- pseudo: &PseudoElement,
- visited_handling: VisitedHandlingMode,
- rules: StrongRuleNode)
- -> bool {
- match visited_handling {
- VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
- unreachable!("We should never try to selector match with \
- AllLinksVisitedAndUnvisited");
- },
- VisitedHandlingMode::AllLinksUnvisited => {
- self.add_unvisited_rules(&pseudo, rules)
- },
- VisitedHandlingMode::RelevantLinkVisited => {
- self.add_visited_rules(&pseudo, rules)
- },
- }
- }
-
- /// Removes a rule node for a given pseudo-element, which may or may not
- /// exist. The type of rule node depends on the visited mode.
- ///
- /// Returns true if the psuedo-element was removed.
- pub fn remove_rules(&mut self,
- pseudo: &PseudoElement,
- visited_handling: VisitedHandlingMode)
- -> bool {
- match visited_handling {
- VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
- unreachable!("We should never try to selector match with \
- AllLinksVisitedAndUnvisited");
- },
- VisitedHandlingMode::AllLinksUnvisited => {
- self.remove_unvisited_rules(&pseudo)
- },
- VisitedHandlingMode::RelevantLinkVisited => {
- self.remove_visited_rules(&pseudo)
- },
- }
- }
-
- /// Returns whether this EagerPseudoStyles has the same set of
- /// pseudos as the given one.
- pub fn has_same_pseudos_as(&self, other: &EagerPseudoStyles) -> bool {
- // We could probably just compare self.keys() to other.keys(), but that
- // seems like it'll involve a bunch more moving stuff around and
- // whatnot.
- match (&self.0, &other.0) {
- (&Some(ref our_arr), &Some(ref other_arr)) => {
- for i in 0..EAGER_PSEUDO_COUNT {
- if our_arr[i].is_some() != other_arr[i].is_some() {
- return false
- }
- }
- true
- },
- (&None, &None) => true,
- _ => false,
- }
- }
-}
-
-/// The styles associated with a node, including the styles for any
-/// pseudo-elements.
-#[derive(Clone, Debug)]
-pub struct ElementStyles {
- /// The element's style.
- pub primary: ComputedStyle,
- /// A list of the styles for the element's eagerly-cascaded pseudo-elements.
- pub pseudos: EagerPseudoStyles,
-}
-
-impl ElementStyles {
- /// Trivially construct a new `ElementStyles`.
- pub fn new(primary: ComputedStyle) -> Self {
- ElementStyles {
- primary: primary,
- pseudos: EagerPseudoStyles(None),
- }
- }
-
- /// Whether this element `display` value is `none`.
- pub fn is_display_none(&self) -> bool {
- self.primary.values().get_box().clone_display() == display::T::none
- }
-}
-
bitflags! {
flags RestyleFlags: u8 {
/// Whether the styles changed for this restyle.
const WAS_RESTYLED = 1 << 0,
/// Whether we reframed/reconstructed any ancestor or self.
const ANCESTOR_WAS_RECONSTRUCTED = 1 << 1,
}
}
@@ -369,16 +36,22 @@ pub struct RestyleData {
/// A few flags to have in mind.
flags: RestyleFlags,
/// The restyle damage, indicating what kind of layout changes are required
/// afte restyling.
pub damage: RestyleDamage,
}
+impl Default for RestyleData {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl RestyleData {
fn new() -> Self {
Self {
hint: RestyleHint::empty(),
flags: RestyleFlags::empty(),
damage: RestyleDamage::empty(),
}
}
@@ -420,25 +93,155 @@ impl RestyleData {
}
/// Returns whether this element has been part of a restyle.
pub fn contains_restyle_data(&self) -> bool {
self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
}
}
+/// A list of computed values for eagerly-cascaded pseudo-elements.
+/// Lazily-allocated.
+#[derive(Clone, Debug)]
+pub struct EagerPseudoValues(pub Option<Box<[Option<Arc<ComputedValues>>]>>);
+
+impl EagerPseudoValues {
+ /// Returns whether there are any pseudo values.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_none()
+ }
+
+ /// Returns a reference to the values for a given eager pseudo, if it exists.
+ pub fn get(&self, pseudo: &PseudoElement) -> Option<&Arc<ComputedValues>> {
+ debug_assert!(pseudo.is_eager());
+ self.0.as_ref().and_then(|p| p[pseudo.eager_index()].as_ref())
+ }
+
+ /// Returns a mutable reference to the values for a given eager pseudo, if it exists.
+ pub fn get_mut(&mut self, pseudo: &PseudoElement) -> Option<&mut Arc<ComputedValues>> {
+ debug_assert!(pseudo.is_eager());
+ self.0.as_mut().and_then(|p| p[pseudo.eager_index()].as_mut())
+ }
+
+ /// Returns true if the EagerPseudoStyles has the values for |pseudo|.
+ pub fn has(&self, pseudo: &PseudoElement) -> bool {
+ self.get(pseudo).is_some()
+ }
+
+ /// Sets the values for the eager pseudo.
+ pub fn set(&mut self, pseudo: &PseudoElement, value: Arc<ComputedValues>) {
+ if self.0.is_none() {
+ self.0 = Some(vec![None; EAGER_PSEUDO_COUNT].into_boxed_slice());
+ }
+ self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(value);
+ }
+
+ /// Inserts a pseudo-element. The pseudo-element must not already exist.
+ pub fn insert(&mut self, pseudo: &PseudoElement, value: Arc<ComputedValues>) {
+ debug_assert!(!self.has(pseudo));
+ self.set(pseudo, value);
+ }
+
+ /// Removes a pseudo-element values if it exists, and returns it.
+ pub fn take(&mut self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> {
+ let result = match self.0.as_mut() {
+ None => return None,
+ Some(arr) => arr[pseudo.eager_index()].take(),
+ };
+ let empty = self.0.as_ref().unwrap().iter().all(|x| x.is_none());
+ if empty {
+ self.0 = None;
+ }
+ result
+ }
+
+ /// Returns a list of the pseudo-elements.
+ pub fn keys(&self) -> ArrayVec<[PseudoElement; EAGER_PSEUDO_COUNT]> {
+ let mut v = ArrayVec::new();
+ if let Some(ref arr) = self.0 {
+ for i in 0..EAGER_PSEUDO_COUNT {
+ if arr[i].is_some() {
+ v.push(PseudoElement::from_eager_index(i));
+ }
+ }
+ }
+ v
+ }
+
+ /// Returns whether this map has the same set of pseudos as the given one.
+ pub fn has_same_pseudos_as(&self, other: &Self) -> bool {
+ // We could probably just compare self.keys() to other.keys(), but that
+ // seems like it'll involve a bunch more moving stuff around and
+ // whatnot.
+ match (&self.0, &other.0) {
+ (&Some(ref our_arr), &Some(ref other_arr)) => {
+ for i in 0..EAGER_PSEUDO_COUNT {
+ if our_arr[i].is_some() != other_arr[i].is_some() {
+ return false
+ }
+ }
+ true
+ },
+ (&None, &None) => true,
+ _ => false,
+ }
+ }
+}
+
+/// The values associated with a node, including the values for any
+/// pseudo-elements.
+#[derive(Clone, Debug)]
+pub struct ElementValues {
+ /// The element's computed values.
+ pub primary: Option<Arc<ComputedValues>>,
+ /// The computed values for the element's eagerly-cascaded pseudo-elements.
+ pub pseudos: EagerPseudoValues,
+}
+
+impl Default for ElementValues {
+ /// Construct an empty `ElementValues`.
+ fn default() -> Self {
+ ElementValues {
+ primary: None,
+ pseudos: EagerPseudoValues(None),
+ }
+ }
+}
+
+impl ElementValues {
+ /// Returns the primary computed values.
+ pub fn get_primary(&self) -> Option<&Arc<ComputedValues>> {
+ self.primary.as_ref()
+ }
+
+ /// Returns the mutable primary computed values.
+ pub fn get_primary_mut(&mut self) -> Option<&mut Arc<ComputedValues>> {
+ self.primary.as_mut()
+ }
+
+ /// Returns the primary computed values. Panic if no values available.
+ pub fn primary(&self) -> &Arc<ComputedValues> {
+ self.primary.as_ref().unwrap()
+ }
+
+ /// Whether this element `display` value is `none`.
+ pub fn is_display_none(&self) -> bool {
+ self.primary().get_box().clone_display() == display::T::none
+ }
+}
+
/// Style system data associated with an Element.
///
/// In Gecko, this hangs directly off the Element. Servo, this is embedded
/// inside of layout data, which itself hangs directly off the Element. In
/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub struct ElementData {
- /// The computed styles for the element and its pseudo-elements.
- styles: Option<ElementStyles>,
+ /// The computed values for the element and its pseudo-elements.
+ pub values: ElementValues,
/// Restyle state.
pub restyle: RestyleData,
}
/// The kind of restyle that a single element should do.
#[derive(Debug)]
pub enum RestyleKind {
@@ -449,21 +252,21 @@ pub enum RestyleKind {
/// attribute, or animation rules.
CascadeWithReplacements(RestyleHint),
/// We only need to recascade, for example, because only inherited
/// properties in the parent changed.
CascadeOnly,
}
impl ElementData {
- /// Borrows both styles and restyle mutably at the same time.
- pub fn styles_and_restyle_mut(
+ /// Borrows both values and restyle mutably at the same time.
+ pub fn values_and_restyle_mut(
&mut self
- ) -> (&mut ElementStyles, &mut RestyleData) {
- (self.styles.as_mut().unwrap(),
+ ) -> (&mut ElementValues, &mut RestyleData) {
+ (&mut self.values,
&mut self.restyle)
}
/// Invalidates style for this element, its descendants, and later siblings,
/// based on the snapshot of the element that we took when attributes or
/// state changed.
pub fn invalidate_style_if_needed<'a, E: TElement>(
&mut self,
@@ -487,43 +290,34 @@ impl ElementData {
shared_context,
);
invalidator.invalidate();
unsafe { element.set_handled_snapshot() }
debug_assert!(element.handled_snapshot());
}
}
-
- /// Trivially construct an ElementData.
- pub fn new(existing: Option<ElementStyles>) -> Self {
- ElementData {
- styles: existing,
- restyle: RestyleData::new(),
- }
- }
-
- /// Returns true if this element has a computed style.
- pub fn has_styles(&self) -> bool {
- self.styles.is_some()
+ /// Returns true if this element has computed values.
+ pub fn has_values(&self) -> bool {
+ self.values.primary.is_some()
}
/// Returns whether we have any outstanding style invalidation.
pub fn has_invalidations(&self) -> bool {
self.restyle.hint.has_self_invalidations()
}
/// Returns the kind of restyling that we're going to need to do on this
/// element, based of the stored restyle hint.
pub fn restyle_kind(&self,
shared_context: &SharedStyleContext)
-> RestyleKind {
- debug_assert!(!self.has_styles() || self.has_invalidations(),
+ debug_assert!(!self.has_values() || self.has_invalidations(),
"Should've stopped earlier");
- if !self.has_styles() {
+ if !self.has_values() {
debug_assert!(!shared_context.traversal_flags.for_animation_only(),
"Unstyled element shouldn't be traversed during \
animation-only traversal");
return RestyleKind::MatchAndCascade;
}
let hint = self.restyle.hint;
if shared_context.traversal_flags.for_animation_only() {
@@ -545,97 +339,56 @@ impl ElementData {
return RestyleKind::CascadeWithReplacements(hint & RestyleHint::replacements());
}
debug_assert!(hint.has_recascade_self(),
"We definitely need to do something!");
return RestyleKind::CascadeOnly;
}
- /// Gets the element styles, if any.
- pub fn get_styles(&self) -> Option<&ElementStyles> {
- self.styles.as_ref()
- }
-
- /// Gets the element styles. Panic if the element has never been styled.
- pub fn styles(&self) -> &ElementStyles {
- self.styles.as_ref().expect("Calling styles() on unstyled ElementData")
- }
-
- /// Gets a mutable reference to the element styles, if any.
- pub fn get_styles_mut(&mut self) -> Option<&mut ElementStyles> {
- self.styles.as_mut()
- }
-
- /// Gets a mutable reference to the element styles. Panic if the element has
- /// never been styled.
- pub fn styles_mut(&mut self) -> &mut ElementStyles {
- self.styles.as_mut().expect("Calling styles_mut() on unstyled ElementData")
- }
-
- /// Sets the computed element styles.
- pub fn set_styles(&mut self, styles: ElementStyles) {
- self.styles = Some(styles);
- }
-
- /// Sets the computed element rules, and returns whether the rules changed.
- pub fn set_primary_rules(&mut self, rules: StrongRuleNode) -> bool {
- if !self.has_styles() {
- self.set_styles(ElementStyles::new(ComputedStyle::new_partial(rules)));
- return true;
- }
-
- if self.styles().primary.rules == rules {
- return false;
- }
-
- self.styles_mut().primary.rules = rules;
- true
- }
-
/// Return true if important rules are different.
/// We use this to make sure the cascade of off-main thread animations is correct.
/// Note: Ignore custom properties for now because we only support opacity and transform
/// properties for animations running on compositor. Actually, we only care about opacity
/// and transform for now, but it's fine to compare all properties and let the user
/// the check which properties do they want.
/// If it costs too much, get_properties_overriding_animations() should return a set
/// containing only opacity and transform properties.
pub fn important_rules_are_different(&self,
rules: &StrongRuleNode,
guards: &StylesheetGuards) -> bool {
- debug_assert!(self.has_styles());
+ debug_assert!(self.has_values());
let (important_rules, _custom) =
- self.styles().primary.rules.get_properties_overriding_animations(&guards);
+ self.values.primary().rules().get_properties_overriding_animations(&guards);
let (other_important_rules, _custom) = rules.get_properties_overriding_animations(&guards);
important_rules != other_important_rules
}
/// Drops any restyle state from the element.
pub fn clear_restyle_state(&mut self) {
self.restyle.clear();
}
/// Returns SMIL overriden value if exists.
pub fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
if cfg!(feature = "servo") {
// Servo has no knowledge of a SMIL rule, so just avoid looking for it.
return None;
}
- match self.get_styles() {
- Some(s) => s.primary.rules.get_smil_animation_rule(),
+ match self.values.get_primary() {
+ Some(v) => v.rules().get_smil_animation_rule(),
None => None,
}
}
/// Returns AnimationRules that has processed during animation-only restyles.
pub fn get_animation_rules(&self) -> AnimationRules {
if cfg!(feature = "servo") {
return AnimationRules(None, None)
}
- match self.get_styles() {
- Some(s) => s.primary.rules.get_animation_rules(),
+ match self.values.get_primary() {
+ Some(v) => v.rules().get_animation_rules(),
None => AnimationRules(None, None),
}
}
}
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -212,18 +212,17 @@ fn fmt_with_data<N: TNode>(f: &mut fmt::
write!(f, "{:?}", n)
}
}
fn fmt_with_data_and_primary_values<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
if let Some(el) = n.as_element() {
let dd = el.has_dirty_descendants();
let data = el.borrow_data();
- let styles = data.as_ref().and_then(|d| d.get_styles());
- let values = styles.map(|s| s.primary.values());
+ let values = data.as_ref().and_then(|d| d.values.get_primary());
write!(f, "{:?} dd={} data={:?} values={:?}", el, dd, &data, values)
} else {
write!(f, "{:?}", n)
}
}
fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32)
-> fmt::Result
@@ -444,17 +443,17 @@ pub trait TElement : Eq + PartialEq + De
unsafe fn set_handled_snapshot(&self);
/// Returns whether the element's styles are up-to-date.
fn has_current_styles(&self, data: &ElementData) -> bool {
if self.has_snapshot() && !self.handled_snapshot() {
return false;
}
- data.has_styles() && !data.has_invalidations()
+ data.has_values() && !data.has_invalidations()
}
/// Flags an element and its ancestors with a given `DescendantsBit`.
///
/// TODO(emilio): We call this conservatively from restyle_element_internal
/// because we never flag unstyled stuff. A different setup for this may be
/// a bit cleaner, but it's probably not worth to invest on it right now
/// unless necessary.
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -510,34 +510,34 @@ impl<'le> GeckoElement<'le> {
///
/// Only safe to call with exclusive access to the element, given otherwise
/// it could race to allocate and leak.
pub unsafe fn ensure_data(&self) -> &AtomicRefCell<ElementData> {
match self.get_data() {
Some(x) => x,
None => {
debug!("Creating ElementData for {:?}", self);
- let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new(None))));
+ let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
self.0.mServoData.set(ptr);
unsafe { &* ptr }
},
}
}
/// Sets the specified element data, return any existing data.
///
/// Like `ensure_data`, only safe to call with exclusive access to the
/// element.
pub unsafe fn set_data(&self, replace_data: Option<ElementData>) -> Option<ElementData> {
match (self.get_data(), replace_data) {
(Some(old), Some(replace_data)) => {
Some(mem::replace(old.borrow_mut().deref_mut(), replace_data))
}
(Some(old), None) => {
- let old_data = mem::replace(old.borrow_mut().deref_mut(), ElementData::new(None));
+ let old_data = mem::replace(old.borrow_mut().deref_mut(), ElementData::default());
self.0.mServoData.set(ptr::null_mut());
Some(old_data)
}
(None, Some(replace_data)) => {
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(replace_data)));
self.0.mServoData.set(ptr);
None
}
@@ -975,17 +975,17 @@ impl<'le> TElement for GeckoElement<'le>
fn update_animations(&self,
before_change_style: Option<Arc<ComputedValues>>,
tasks: UpdateAnimationsTasks) {
// We have to update animations even if the element has no computed
// style since it means the element is in a display:none subtree, we
// should destroy all CSS animations in display:none subtree.
let computed_data = self.borrow_data();
let computed_values =
- computed_data.as_ref().map(|d| d.styles().primary.values());
+ computed_data.as_ref().map(|d| d.values.primary());
let computed_values_opt =
computed_values.map(|v| *HasArcFFI::arc_as_borrowed(v));
let before_change_values =
before_change_style.as_ref().map(|v| *HasArcFFI::arc_as_borrowed(v));
unsafe {
Gecko_UpdateAnimations(self.0,
before_change_values,
computed_values_opt,
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -156,17 +156,17 @@ impl StylesheetInvalidationSet {
fn process_invalidations_in_subtree<E>(&self, element: E) -> bool
where E: TElement,
{
let mut data = match element.mutate_data() {
Some(data) => data,
None => return false,
};
- if !data.has_styles() {
+ if !data.has_values() {
return false;
}
if data.restyle.hint.contains_subtree() {
debug!("process_invalidations_in_subtree: {:?} was already invalid",
element);
return false;
}
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -4,18 +4,18 @@
//! High-level interface to CSS selector matching.
#![allow(unsafe_code)]
#![deny(missing_docs)]
use applicable_declarations::ApplicableDeclarationList;
use cascade_info::CascadeInfo;
-use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
-use data::{ComputedStyle, ElementData, RestyleData};
+use context::{CascadeInputs, SelectorFlagsMap, SharedStyleContext, StyleContext};
+use data::{ElementData, ElementValues, RestyleData};
use dom::{TElement, TNode};
use font_metrics::FontMetricsProvider;
use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
use invalidation::element::restyle_hints::{RESTYLE_SMIL, RESTYLE_STYLE_ATTRIBUTE};
use invalidation::element::restyle_hints::RestyleHint;
use log::LogLevel::Trace;
use properties::{ALLOW_SET_ROOT_FONT_SIZE, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
use properties::{AnimationRules, CascadeFlags, ComputedValues};
@@ -137,69 +137,109 @@ pub enum CascadeVisitedMode {
/// Cascade the regular, unvisited styles.
Unvisited,
/// Cascade the styles used when an element's relevant link is visited. A
/// "relevant link" is the element being matched if it is a link or the
/// nearest ancestor link.
Visited,
}
-/// Various helper methods to ease navigating the style storage locations
+/// Various helper methods to ease navigating the input storage locations
/// depending on the current cascade mode.
impl CascadeVisitedMode {
/// Returns whether there is a rule node based on the cascade mode.
- fn has_rules(&self, style: &ComputedStyle) -> bool {
+ fn has_rules(&self, inputs: &CascadeInputs) -> bool {
match *self {
- CascadeVisitedMode::Unvisited => true,
- CascadeVisitedMode::Visited => style.has_visited_rules(),
+ CascadeVisitedMode::Unvisited => inputs.has_rules(),
+ CascadeVisitedMode::Visited => inputs.has_visited_rules(),
}
}
/// Returns the rule node based on the cascade mode.
- fn rules<'a>(&self, style: &'a ComputedStyle) -> &'a StrongRuleNode {
+ fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode {
match *self {
- CascadeVisitedMode::Unvisited => &style.rules,
- CascadeVisitedMode::Visited => style.visited_rules(),
+ CascadeVisitedMode::Unvisited => inputs.rules(),
+ CascadeVisitedMode::Visited => inputs.visited_rules(),
}
}
/// Returns a mutable rules node based on the cascade mode, if any.
- fn get_rules_mut<'a>(&self, style: &'a mut ComputedStyle) -> Option<&'a mut StrongRuleNode> {
+ fn get_rules_mut<'a>(&self, inputs: &'a mut CascadeInputs) -> Option<&'a mut StrongRuleNode> {
match *self {
- CascadeVisitedMode::Unvisited => Some(&mut style.rules),
- CascadeVisitedMode::Visited => style.get_visited_rules_mut(),
+ CascadeVisitedMode::Unvisited => inputs.get_rules_mut(),
+ CascadeVisitedMode::Visited => inputs.get_visited_rules_mut(),
}
}
/// Returns the computed values based on the cascade mode. In visited mode,
/// visited values are only returned if they already exist. If they don't,
/// we fallback to the regular, unvisited styles.
- fn values<'a>(&self, style: &'a ComputedStyle) -> &'a Arc<ComputedValues> {
- let mut values = style.values();
-
+ fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> {
if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() {
- values = values.visited_style();
+ return values.visited_style();
}
values
}
- /// Set the computed values based on the cascade mode.
- fn set_values(&self, style: &mut ComputedStyle, values: Arc<ComputedValues>) {
+ /// Set the primary computed values based on the cascade mode.
+ fn set_primary_values(&self,
+ el_values: &mut ElementValues,
+ inputs: &mut CascadeInputs,
+ values: Arc<ComputedValues>) {
+ // Unvisited values are stored in permanent storage on `ElementData`.
+ // Visited values are stored temporarily in `CascadeInputs` and then
+ // folded into the unvisited values when they cascade.
match *self {
- CascadeVisitedMode::Unvisited => style.values = Some(values),
- CascadeVisitedMode::Visited => style.set_visited_values(values),
+ CascadeVisitedMode::Unvisited => el_values.primary = Some(values),
+ CascadeVisitedMode::Visited => inputs.set_visited_values(values),
}
}
- /// Take the computed values based on the cascade mode.
- fn take_values(&self, style: &mut ComputedStyle) -> Option<Arc<ComputedValues>> {
+ /// Set the primary computed values based on the cascade mode.
+ fn set_pseudo_values(&self,
+ el_values: &mut ElementValues,
+ inputs: &mut CascadeInputs,
+ pseudo: &PseudoElement,
+ values: Arc<ComputedValues>) {
+ // Unvisited values are stored in permanent storage on `ElementData`.
+ // Visited values are stored temporarily in `CascadeInputs` and then
+ // folded into the unvisited values when they cascade.
match *self {
- CascadeVisitedMode::Unvisited => style.values.take(),
- CascadeVisitedMode::Visited => style.take_visited_values(),
+ CascadeVisitedMode::Unvisited => el_values.pseudos.set(pseudo, values),
+ CascadeVisitedMode::Visited => inputs.set_visited_values(values),
+ }
+ }
+
+ /// Take the primary computed values based on the cascade mode.
+ fn take_primary_values(&self,
+ el_values: &mut ElementValues,
+ inputs: &mut CascadeInputs)
+ -> Option<Arc<ComputedValues>> {
+ // Unvisited values are stored in permanent storage on `ElementData`.
+ // Visited values are stored temporarily in `CascadeInputs` and then
+ // folded into the unvisited values when they cascade.
+ match *self {
+ CascadeVisitedMode::Unvisited => el_values.primary.take(),
+ CascadeVisitedMode::Visited => inputs.take_visited_values(),
+ }
+ }
+
+ /// Take the pseudo computed values based on the cascade mode.
+ fn take_pseudo_values(&self,
+ el_values: &mut ElementValues,
+ inputs: &mut CascadeInputs,
+ pseudo: &PseudoElement)
+ -> Option<Arc<ComputedValues>> {
+ // Unvisited values are stored in permanent storage on `ElementData`.
+ // Visited values are stored temporarily in `CascadeInputs` and then
+ // folded into the unvisited values when they cascade.
+ match *self {
+ CascadeVisitedMode::Unvisited => el_values.pseudos.take(pseudo),
+ CascadeVisitedMode::Visited => inputs.take_visited_values(),
}
}
/// Returns whether there might be visited values that should be inserted
/// within the regular computed values based on the cascade mode.
fn visited_values_for_insertion(&self) -> bool {
*self == CascadeVisitedMode::Unvisited
}
@@ -241,29 +281,29 @@ trait PrivateMatchMethods: TElement {
let mut current = self.clone();
loop {
current = match current.traversal_parent() {
Some(el) => el,
None => return current,
};
let is_display_contents =
- current.borrow_data().unwrap().styles().primary.values().is_display_contents();
+ current.borrow_data().unwrap().values.primary().is_display_contents();
if !is_display_contents {
return current;
}
}
}
fn cascade_with_rules(&self,
shared_context: &SharedStyleContext,
font_metrics_provider: &FontMetricsProvider,
rule_node: &StrongRuleNode,
- primary_style: &ComputedStyle,
+ primary_values: Option<&Arc<ComputedValues>>,
cascade_target: CascadeTarget,
cascade_visited: CascadeVisitedMode,
visited_values_to_insert: Option<Arc<ComputedValues>>)
-> Arc<ComputedValues> {
let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
if self.skip_root_and_item_based_display_fixup() {
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
@@ -288,33 +328,33 @@ trait PrivateMatchMethods: TElement {
// Sometimes Gecko eagerly styles things without processing
// pending restyles first. In general we'd like to avoid this,
// but there can be good reasons (for example, needing to
// construct a frame for some small piece of newly-added
// content in order to do something specific with that frame,
// but not wanting to flush all of layout).
debug_assert!(cfg!(feature = "gecko") ||
parent_el.unwrap().has_current_styles(d));
- &d.styles().primary
+ d.values.primary()
});
parent_style.map(|s| cascade_visited.values(s))
}
CascadeTarget::EagerPseudo => {
parent_el = Some(self.clone());
- Some(cascade_visited.values(primary_style))
+ Some(cascade_visited.values(primary_values.unwrap()))
}
};
let mut layout_parent_el = parent_el.clone();
let layout_parent_data;
let mut layout_parent_style = style_to_inherit_from;
if style_to_inherit_from.map_or(false, |s| s.is_display_contents()) {
layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
- layout_parent_style = Some(cascade_visited.values(&layout_parent_data.styles().primary));
+ layout_parent_style = Some(cascade_visited.values(layout_parent_data.values.primary()));
}
let style_to_inherit_from = style_to_inherit_from.map(|x| &**x);
let layout_parent_style = layout_parent_style.map(|x| &**x);
// Propagate the "can be fragmented" bit. It would be nice to
// encapsulate this better.
//
@@ -345,22 +385,23 @@ trait PrivateMatchMethods: TElement {
shared_context.quirks_mode));
cascade_info.finish(&self.as_node());
values
}
fn cascade_internal(&self,
context: &StyleContext<Self>,
- primary_style: &ComputedStyle,
- eager_pseudo_style: Option<&ComputedStyle>,
+ primary_values: Option<&Arc<ComputedValues>>,
+ primary_inputs: &CascadeInputs,
+ eager_pseudo_inputs: Option<&CascadeInputs>,
cascade_visited: CascadeVisitedMode)
-> Arc<ComputedValues> {
if let Some(pseudo) = self.implemented_pseudo_element() {
- debug_assert!(eager_pseudo_style.is_none());
+ debug_assert!(eager_pseudo_inputs.is_none());
// This is an element-backed pseudo, just grab the styles from the
// parent if it's eager, and recascade otherwise.
//
// We also recascade if the eager pseudo-style has any animation
// rules, because we don't cascade those during the eager traversal.
//
// We could make that a bit better if the complexity cost is not too
@@ -371,100 +412,109 @@ trait PrivateMatchMethods: TElement {
// computing default styles, we aren't guaranteed the parent
// will have eagerly computed our styles, so we just handled it
// below like a lazy pseudo.
let only_default_rules = context.shared.traversal_flags.for_default_styles();
if pseudo.is_eager() && !only_default_rules {
debug_assert!(pseudo.is_before_or_after());
let parent = self.parent_element().unwrap();
if !parent.may_have_animations() ||
- primary_style.rules.get_animation_rules().is_empty() {
+ primary_inputs.rules().get_animation_rules().is_empty() {
let parent_data = parent.borrow_data().unwrap();
- let pseudo_style =
- parent_data.styles().pseudos.get(&pseudo).unwrap();
- let values = cascade_visited.values(pseudo_style);
+ let pseudo_values =
+ parent_data.values.pseudos.get(&pseudo).unwrap();
+ let values = cascade_visited.values(pseudo_values);
return values.clone()
}
}
}
// Find possible visited computed styles to insert within the regular
// computed values we are about to create.
let visited_values_to_insert = if cascade_visited.visited_values_for_insertion() {
- match eager_pseudo_style {
+ match eager_pseudo_inputs {
Some(ref s) => s.clone_visited_values(),
- None => primary_style.clone_visited_values(),
+ None => primary_inputs.clone_visited_values(),
}
} else {
None
};
// Grab the rule node.
- let style = eager_pseudo_style.unwrap_or(primary_style);
- let rule_node = cascade_visited.rules(style);
- let cascade_target = if eager_pseudo_style.is_some() {
+ let inputs = eager_pseudo_inputs.unwrap_or(primary_inputs);
+ let rule_node = cascade_visited.rules(inputs);
+ let cascade_target = if eager_pseudo_inputs.is_some() {
CascadeTarget::EagerPseudo
} else {
CascadeTarget::Normal
};
self.cascade_with_rules(context.shared,
&context.thread_local.font_metrics_provider,
rule_node,
- primary_style,
+ primary_values,
cascade_target,
cascade_visited,
visited_values_to_insert)
}
/// Computes values and damage for the primary style of an element, setting
/// them on the ElementData.
fn cascade_primary(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
important_rules_changed: bool,
cascade_visited: CascadeVisitedMode)
-> ChildCascadeRequirement {
debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited);
- // Collect some values.
- let (mut styles, restyle) = data.styles_and_restyle_mut();
- let mut primary_style = &mut styles.primary;
- // If there was no relevant link, we won't have any visited rules, so
- // there may not be anything do for the visited case. This early return
- // is especially important for the `cascade_primary_and_pseudos` path
- // since we rely on the state of some previous matching run.
- if !cascade_visited.has_rules(primary_style) {
- return ChildCascadeRequirement::CanSkipCascade
- }
- let mut old_values = cascade_visited.take_values(primary_style);
+ let mut old_values = cascade_visited.take_primary_values(
+ &mut data.values,
+ context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs.primary_mut()
+ );
+
+ let mut new_values = {
+ let primary_inputs = context.thread_local.current_element_info
+ .as_ref().unwrap()
+ .cascade_inputs.primary();
- // Compute the new values.
- let mut new_values = self.cascade_internal(context,
- primary_style,
- None,
- cascade_visited);
+ // If there was no relevant link, we won't have any visited rules, so
+ // there may not be anything do for the visited case. This early return
+ // is especially important for the `cascade_primary_and_pseudos` path
+ // since we rely on the state of some previous matching run.
+ if !cascade_visited.has_rules(primary_inputs) {
+ return ChildCascadeRequirement::CanSkipCascade
+ }
+
+ // Compute the new values.
+ self.cascade_internal(context,
+ None,
+ primary_inputs,
+ None,
+ cascade_visited)
+ };
// NB: Animations for pseudo-elements in Gecko are handled while
// traversing the pseudo-elements themselves.
if !context.shared.traversal_flags.for_animation_only() &&
cascade_visited.should_process_animations() {
self.process_animations(context,
&mut old_values,
&mut new_values,
- primary_style,
important_rules_changed);
}
let mut child_cascade_requirement =
ChildCascadeRequirement::CanSkipCascade;
if cascade_visited.should_accumulate_damage() {
child_cascade_requirement =
self.accumulate_damage(&context.shared,
- restyle,
+ &mut data.restyle,
old_values.as_ref().map(|v| v.as_ref()),
&new_values,
None);
// Handle root font-size changes.
if self.is_root() && !self.is_native_anonymous() {
// The new root font-size has already been updated on the Device
// in properties::apply_declarations.
@@ -477,80 +527,109 @@ trait PrivateMatchMethods: TElement {
if old_values.map_or(false, |v| v.get_font().clone_font_size() != new_font_size) &&
device.used_root_font_size() {
child_cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants;
}
}
}
// Set the new computed values.
- cascade_visited.set_values(primary_style, new_values);
+ let primary_inputs = context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs.primary_mut();
+ cascade_visited.set_primary_values(&mut data.values,
+ primary_inputs,
+ new_values);
// Return whether the damage indicates we must cascade new inherited
// values into children.
child_cascade_requirement
}
/// Computes values and damage for the eager pseudo-element styles of an
/// element, setting them on the ElementData.
fn cascade_eager_pseudo(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
pseudo: &PseudoElement,
cascade_visited: CascadeVisitedMode) {
debug_assert!(pseudo.is_eager());
- let (mut styles, restyle) = data.styles_and_restyle_mut();
- let mut pseudo_style = styles.pseudos.get_mut(pseudo).unwrap();
- // If there was no relevant link, we won't have any visited rules, so
- // there may not be anything do for the visited case. This early return
- // is especially important for the `cascade_primary_and_pseudos` path
- // since we rely on the state of some previous matching run.
- if !cascade_visited.has_rules(pseudo_style) {
- return
- }
- let old_values = cascade_visited.take_values(pseudo_style);
+
+ let old_values = cascade_visited.take_pseudo_values(
+ &mut data.values,
+ context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs.pseudos.get_mut(pseudo).unwrap(),
+ pseudo
+ );
+
+ let new_values = {
+ let info = context.thread_local.current_element_info
+ .as_ref().unwrap();
+ let pseudo_inputs = info.cascade_inputs.pseudos.get(pseudo).unwrap();
- let new_values = self.cascade_internal(context,
- &styles.primary,
- Some(pseudo_style),
- cascade_visited);
+ // If there was no relevant link, we won't have any visited rules, so
+ // there may not be anything do for the visited case. This early return
+ // is especially important for the `cascade_primary_and_pseudos` path
+ // since we rely on the state of some previous matching run.
+ if !cascade_visited.has_rules(pseudo_inputs) {
+ return
+ }
+
+ // Primary inputs should already have rules populated since it's
+ // always processed before eager pseudos.
+ debug_assert!(cascade_visited.has_rules(info.cascade_inputs.primary()));
+ let primary_inputs = info.cascade_inputs.primary();
+
+ self.cascade_internal(context,
+ data.values.get_primary(),
+ primary_inputs,
+ Some(pseudo_inputs),
+ cascade_visited)
+ };
if cascade_visited.should_accumulate_damage() {
self.accumulate_damage(&context.shared,
- restyle,
- old_values.as_ref().map(|v| &**v),
+ &mut data.restyle,
+ old_values.as_ref().map(|v| v.as_ref()),
&new_values,
Some(pseudo));
}
- cascade_visited.set_values(pseudo_style, new_values);
+ let pseudo_inputs = context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs.pseudos.get_mut(pseudo).unwrap();
+ cascade_visited.set_pseudo_values(&mut data.values,
+ pseudo_inputs,
+ pseudo,
+ new_values);
}
/// get_after_change_style removes the transition rules from the ComputedValues.
/// If there is no transition rule in the ComputedValues, it returns None.
#[cfg(feature = "gecko")]
fn get_after_change_style(&self,
context: &mut StyleContext<Self>,
- primary_style: &ComputedStyle)
+ primary_values: &Arc<ComputedValues>)
-> Option<Arc<ComputedValues>> {
- let rule_node = &primary_style.rules;
+ let rule_node = primary_values.rules();
let without_transition_rules =
context.shared.stylist.rule_tree().remove_transition_rule_if_applicable(rule_node);
if without_transition_rules == *rule_node {
// We don't have transition rule in this case, so return None to let the caller
// use the original ComputedValues.
return None;
}
// This currently ignores visited styles, which seems acceptable,
// as existing browsers don't appear to transition visited styles.
Some(self.cascade_with_rules(context.shared,
&context.thread_local.font_metrics_provider,
&without_transition_rules,
- primary_style,
+ Some(primary_values),
CascadeTarget::Normal,
CascadeVisitedMode::Unvisited,
None))
}
#[cfg(feature = "gecko")]
fn needs_animations_update(&self,
context: &mut StyleContext<Self>,
@@ -580,30 +659,29 @@ trait PrivateMatchMethods: TElement {
})
}
#[cfg(feature = "gecko")]
fn process_animations(&self,
context: &mut StyleContext<Self>,
old_values: &mut Option<Arc<ComputedValues>>,
new_values: &mut Arc<ComputedValues>,
- primary_style: &ComputedStyle,
important_rules_changed: bool) {
use context::{CASCADE_RESULTS, CSS_ANIMATIONS, CSS_TRANSITIONS, EFFECT_PROPERTIES};
use context::UpdateAnimationsTasks;
let mut tasks = UpdateAnimationsTasks::empty();
if self.needs_animations_update(context, old_values.as_ref(), new_values) {
tasks.insert(CSS_ANIMATIONS);
}
let before_change_style = if self.might_need_transitions_update(old_values.as_ref().map(|s| &**s),
new_values) {
let after_change_style = if self.has_css_transitions() {
- self.get_after_change_style(context, primary_style)
+ self.get_after_change_style(context, new_values)
} else {
None
};
// In order to avoid creating a SequentialTask for transitions which
// may not be updated, we check it per property to make sure Gecko
// side will really update transition.
let needs_transitions_update = {
@@ -646,17 +724,16 @@ trait PrivateMatchMethods: TElement {
}
}
#[cfg(feature = "servo")]
fn process_animations(&self,
context: &mut StyleContext<Self>,
old_values: &mut Option<Arc<ComputedValues>>,
new_values: &mut Arc<ComputedValues>,
- _primary_style: &ComputedStyle,
_important_rules_changed: bool) {
use animation;
let possibly_expired_animations =
&mut context.thread_local.current_element_info.as_mut().unwrap()
.possibly_expired_animations;
let shared_context = context.shared;
if let Some(ref mut old) = *old_values {
@@ -730,24 +807,24 @@ trait PrivateMatchMethods: TElement {
let difference = self.compute_style_difference(&old_values, &new_values, pseudo);
restyle.damage |= difference.damage;
difference.change.into()
}
#[cfg(feature = "servo")]
fn update_animations_for_cascade(&self,
context: &SharedStyleContext,
- style: &mut Arc<ComputedValues>,
+ values: &mut Arc<ComputedValues>,
possibly_expired_animations: &mut Vec<::animation::PropertyAnimation>,
font_metrics: &FontMetricsProvider) {
use animation::{self, Animation};
// Finish any expired transitions.
let this_opaque = self.as_node().opaque();
- animation::complete_expired_transitions(this_opaque, style, context);
+ animation::complete_expired_transitions(this_opaque, values, context);
// Merge any running transitions into the current style, and cancel them.
let had_running_animations = context.running_animations
.read()
.get(&this_opaque)
.is_some();
if had_running_animations {
let mut all_running_animations = context.running_animations.write();
@@ -761,17 +838,17 @@ trait PrivateMatchMethods: TElement {
// updated by layout, because other restyle due to script might
// be triggered by layout before the animation tick.
//
// See #12171 and the associated PR for an example where this
// happened while debugging other release panic.
if !running_animation.is_expired() {
animation::update_style_for_animation(context,
running_animation,
- style,
+ values,
font_metrics);
if let Animation::Transition(_, _, ref frame, _) = *running_animation {
possibly_expired_animations.push(frame.property_animation.clone())
}
}
}
}
}
@@ -851,17 +928,17 @@ pub trait MatchMethods : TElement {
}
// Cascade properties and compute primary values.
let child_cascade_requirement =
self.cascade_primary(context, data, important_rules_changed,
CascadeVisitedMode::Unvisited);
// Match and cascade eager pseudo-elements.
- if !data.styles().is_display_none() {
+ if !data.values.is_display_none() {
self.match_pseudos(context, data, VisitedHandlingMode::AllLinksUnvisited);
// If there's a relevant link involved, match and cascade eager
// pseudo-element styles as if the link is visited as well.
// This runs after matching for regular styles because matching adds
// each pseudo as needed to the PseudoMap, and this runs before
// cascade for regular styles because the visited ComputedValues
// are placed within the regular ComputedValues, which is immutable
@@ -870,17 +947,17 @@ pub trait MatchMethods : TElement {
self.match_pseudos(context, data, VisitedHandlingMode::RelevantLinkVisited);
self.cascade_pseudos(context, data, CascadeVisitedMode::Visited);
}
self.cascade_pseudos(context, data, CascadeVisitedMode::Unvisited);
}
// If we have any pseudo elements, indicate so in the primary StyleRelations.
- if !data.styles().pseudos.is_empty() {
+ if !data.values.pseudos.is_empty() {
primary_results.relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
// If the style is shareable, add it to the LRU cache.
if sharing == StyleSharingBehavior::Allow {
// If we previously tried to match this element against the cache,
// the revalidation match results will already be cached. Otherwise
// we'll have None, and compute them later on-demand.
@@ -893,17 +970,17 @@ pub trait MatchMethods : TElement {
.as_mut().unwrap()
.validation_data
.take();
let dom_depth = context.thread_local.bloom_filter.matching_depth();
context.thread_local
.style_sharing_candidate_cache
.insert_if_possible(self,
- data.styles().primary.values(),
+ data.values.primary(),
primary_results.relations,
validation_data,
dom_depth);
}
child_cascade_requirement
}
@@ -937,16 +1014,19 @@ pub trait MatchMethods : TElement {
fn match_primary(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
visited_handling: VisitedHandlingMode)
-> MatchingResults
{
debug!("Match primary for {:?}, visited: {:?}", self, visited_handling);
+ let mut info = context.thread_local.current_element_info.as_mut().unwrap();
+ let mut primary_inputs = info.cascade_inputs.ensure_primary();
+
let only_default_rules = context.shared.traversal_flags.for_default_styles();
let implemented_pseudo = self.implemented_pseudo_element();
if let Some(ref pseudo) = implemented_pseudo {
// We don't expect to match against a non-canonical pseudo-element.
debug_assert_eq!(*pseudo, pseudo.canonical());
if pseudo.is_eager() && !only_default_rules {
// If it's an eager element-backed pseudo, we can generally just
// grab the matched rules from the parent, and then update
@@ -957,19 +1037,19 @@ pub trait MatchMethods : TElement {
// any pseudo styles stored on the parent. For example, if
// document-level style sheets cause the element to exist, due
// to ::before rules, then those rules won't be found when
// computing default styles on the parent, so we won't have
// bothered to store pseudo styles there. In this case, we just
// treat it like a lazily computed pseudo.
let parent = self.parent_element().unwrap();
let parent_data = parent.borrow_data().unwrap();
- let pseudo_style =
- parent_data.styles().pseudos.get(&pseudo).unwrap();
- let mut rules = pseudo_style.rules.clone();
+ let pseudo_values =
+ parent_data.values.pseudos.get(&pseudo).unwrap();
+ let mut rules = pseudo_values.rules().clone();
if parent.may_have_animations() {
let animation_rules = data.get_animation_rules();
// Handle animations here.
if let Some(animation_rule) = animation_rules.0 {
let animation_rule_node =
context.shared.stylist.rule_tree()
.update_rule_at_level(CascadeLevel::Animations,
@@ -990,32 +1070,21 @@ pub trait MatchMethods : TElement {
&context.shared.guards);
if let Some(node) = animation_rule_node {
rules = node;
}
}
}
let important_rules_changed =
self.has_animations() &&
- data.has_styles() &&
+ data.has_values() &&
data.important_rules_are_different(&rules,
- &context.shared.guards);
+ &context.shared.guards);
- let rules_changed = match visited_handling {
- VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
- unreachable!("We should never try to selector match with \
- AllLinksVisitedAndUnvisited");
- },
- VisitedHandlingMode::AllLinksUnvisited => {
- data.set_primary_rules(rules)
- },
- VisitedHandlingMode::RelevantLinkVisited => {
- data.styles_mut().primary.set_visited_rules(rules)
- },
- };
+ let rules_changed = primary_inputs.set_rules(visited_handling, rules);
return MatchingResults::new(rules_changed, important_rules_changed)
}
}
let mut applicable_declarations = ApplicableDeclarationList::new();
let stylist = &context.shared.stylist;
@@ -1072,34 +1141,23 @@ pub trait MatchMethods : TElement {
if source.is_some() {
trace!(" > {:?}", source);
}
}
}
let important_rules_changed =
self.has_animations() &&
- data.has_styles() &&
+ data.has_values() &&
data.important_rules_are_different(
&primary_rule_node,
&context.shared.guards
);
- let rules_changed = match visited_handling {
- VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
- unreachable!("We should never try to selector match with \
- AllLinksVisitedAndUnvisited");
- },
- VisitedHandlingMode::AllLinksUnvisited => {
- data.set_primary_rules(primary_rule_node)
- },
- VisitedHandlingMode::RelevantLinkVisited => {
- data.styles_mut().primary.set_visited_rules(primary_rule_node)
- },
- };
+ let rules_changed = primary_inputs.set_rules(visited_handling, primary_rule_node);
MatchingResults::new_from_context(rules_changed,
important_rules_changed,
matching_context)
}
/// Runs selector matching to (re)compute eager pseudo-element rule nodes
/// for this element.
@@ -1112,68 +1170,73 @@ pub trait MatchMethods : TElement {
if self.implemented_pseudo_element().is_some() {
// Element pseudos can't have any other pseudo.
return;
}
let mut applicable_declarations = ApplicableDeclarationList::new();
- let map = &mut context.thread_local.selector_flags;
- let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
- self.apply_selector_flags(map, element, flags);
- };
-
// Borrow the stuff we need here so the borrow checker doesn't get mad
// at us later in the closure.
let stylist = &context.shared.stylist;
let guards = &context.shared.guards;
let rule_inclusion = if context.shared.traversal_flags.for_default_styles() {
RuleInclusion::DefaultOnly
} else {
RuleInclusion::All
};
- let bloom_filter = context.thread_local.bloom_filter.filter();
-
- let mut matching_context =
- MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
- Some(bloom_filter),
- visited_handling,
- context.shared.quirks_mode);
-
// Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
+ let bloom_filter = context.thread_local.bloom_filter.filter();
+
+ let mut matching_context =
+ MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
+ Some(bloom_filter),
+ visited_handling,
+ context.shared.quirks_mode);
+
// For pseudo-elements, we only try to match visited rules if there
// are also unvisited rules. (This matches Gecko's behavior.)
if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
- !data.styles().pseudos.has(&pseudo) {
+ !context.thread_local.current_element_info.as_ref().unwrap()
+ .cascade_inputs.pseudos.has(&pseudo) {
return
}
- if !self.may_generate_pseudo(&pseudo, data.styles().primary.values()) {
+ if !self.may_generate_pseudo(&pseudo, data.values.primary()) {
return;
}
- debug_assert!(applicable_declarations.is_empty());
- // NB: We handle animation rules for ::before and ::after when
- // traversing them.
- stylist.push_applicable_declarations(self,
- Some(&pseudo),
- None,
- None,
- AnimationRules(None, None),
- rule_inclusion,
- &mut applicable_declarations,
- &mut matching_context,
- &mut set_selector_flags);
+ {
+ let map = &mut context.thread_local.selector_flags;
+ let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
+ self.apply_selector_flags(map, element, flags);
+ };
- let pseudos = &mut data.styles_mut().pseudos;
+ debug_assert!(applicable_declarations.is_empty());
+ // NB: We handle animation rules for ::before and ::after when
+ // traversing them.
+ stylist.push_applicable_declarations(self,
+ Some(&pseudo),
+ None,
+ None,
+ AnimationRules(None, None),
+ rule_inclusion,
+ &mut applicable_declarations,
+ &mut matching_context,
+ &mut set_selector_flags);
+ }
+
+ let pseudos = &mut context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs.pseudos;
if !applicable_declarations.is_empty() {
let rules = stylist.rule_tree().compute_rule_node(
&mut applicable_declarations,
&guards
);
matches_different_pseudos |= pseudos.add_rules(
&pseudo,
visited_handling,
@@ -1286,57 +1349,60 @@ pub trait MatchMethods : TElement {
/// Updates the rule nodes without re-running selector matching, using just
/// the rule tree.
///
/// Returns true if an !important rule was replaced.
fn replace_rules(
&self,
replacements: RestyleHint,
- context: &StyleContext<Self>,
- data: &mut ElementData
+ context: &mut StyleContext<Self>,
) -> bool {
let mut result = false;
- result |= self.replace_rules_internal(replacements, context, data,
+ result |= self.replace_rules_internal(replacements, context,
CascadeVisitedMode::Unvisited);
if !context.shared.traversal_flags.for_animation_only() {
- result |= self.replace_rules_internal(replacements, context, data,
+ result |= self.replace_rules_internal(replacements, context,
CascadeVisitedMode::Visited);
}
result
}
/// Updates the rule nodes without re-running selector matching, using just
/// the rule tree, for a specific visited mode.
///
/// Returns true if an !important rule was replaced.
fn replace_rules_internal(
&self,
replacements: RestyleHint,
- context: &StyleContext<Self>,
- data: &mut ElementData,
+ context: &mut StyleContext<Self>,
cascade_visited: CascadeVisitedMode
) -> bool {
use properties::PropertyDeclarationBlock;
use shared_lock::Locked;
debug_assert!(replacements.intersects(RestyleHint::replacements()) &&
(replacements & !RestyleHint::replacements()).is_empty());
- let element_styles = &mut data.styles_mut();
- let primary_rules = match cascade_visited.get_rules_mut(&mut element_styles.primary) {
+ let stylist = &context.shared.stylist;
+ let guards = &context.shared.guards;
+
+ let mut primary_inputs = context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs.primary_mut();
+ let primary_rules = match cascade_visited.get_rules_mut(primary_inputs) {
Some(r) => r,
None => return false,
};
let replace_rule_node = |level: CascadeLevel,
pdb: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
path: &mut StrongRuleNode| -> bool {
- let new_node = context.shared.stylist.rule_tree()
- .update_rule_at_level(level, pdb, path, &context.shared.guards);
+ let new_node = stylist.rule_tree()
+ .update_rule_at_level(level, pdb, path, guards);
match new_node {
Some(n) => {
*path = n;
level.is_important()
},
None => false,
}
};
@@ -1449,45 +1515,48 @@ pub trait MatchMethods : TElement {
{
debug!("Cascade pseudos for {:?}, visited: {:?}", self,
cascade_visited);
// Note that we've already set up the map of matching pseudo-elements
// in match_pseudos (and handled the damage implications of changing
// which pseudos match), so now we can just iterate what we have. This
// does mean collecting owned pseudos, so that the borrow checker will
// let us pass the mutable |data| to the cascade function.
- let matched_pseudos = data.styles().pseudos.keys();
+ let matched_pseudos = context.thread_local.current_element_info
+ .as_ref().unwrap()
+ .cascade_inputs.pseudos.keys();
for pseudo in matched_pseudos {
+ debug!("Cascade pseudo for {:?} {:?}", self, pseudo);
self.cascade_eager_pseudo(context, data, &pseudo, cascade_visited);
}
}
/// Returns computed values without animation and transition rules.
fn get_base_style(&self,
shared_context: &SharedStyleContext,
font_metrics_provider: &FontMetricsProvider,
- primary_style: &ComputedStyle,
- pseudo_style: Option<&ComputedStyle>)
+ primary_values: &Arc<ComputedValues>,
+ pseudo_values: Option<&Arc<ComputedValues>>)
-> Arc<ComputedValues> {
- let relevant_style = pseudo_style.unwrap_or(primary_style);
- let rule_node = &relevant_style.rules;
+ let relevant_values = pseudo_values.unwrap_or(primary_values);
+ let rule_node = relevant_values.rules();
let without_animation_rules =
shared_context.stylist.rule_tree().remove_animation_rules(rule_node);
if without_animation_rules == *rule_node {
// Note that unwrapping here is fine, because the style is
// only incomplete during the styling process.
- return relevant_style.values.as_ref().unwrap().clone();
+ return relevant_values.clone();
}
// This currently ignores visited styles, which seems acceptable,
// as existing browsers don't appear to animate visited styles.
self.cascade_with_rules(shared_context,
font_metrics_provider,
&without_animation_rules,
- primary_style,
+ Some(primary_values),
CascadeTarget::Normal,
CascadeVisitedMode::Unvisited,
None)
}
}
impl<E: TElement> MatchMethods for E {}
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -53,16 +53,17 @@ use gecko::values::GeckoStyleCoordConver
use gecko::values::round_border_to_device_pixels;
use logical_geometry::WritingMode;
use media_queries::Device;
use properties::animated_properties::TransitionProperty;
use properties::longhands;
use properties:: FontComputationData;
use properties::{Importance, LonghandId};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
+use rule_tree::StrongRuleNode;
use std::fmt::{self, Debug};
use std::mem::{forget, transmute, zeroed};
use std::ptr;
use stylearc::Arc;
use std::cmp;
use values::{Auto, CustomIdent, Either, KeyframesName};
use values::computed::{Shadow, ToComputedValue};
use values::specified::length::Percentage;
@@ -70,65 +71,71 @@ use computed_values::border_style;
pub mod style_structs {
% for style_struct in data.style_structs:
pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
% endfor
}
-#[derive(Clone, Debug)]
+#[derive(Clone)]
pub struct ComputedValues {
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
custom_properties: Option<Arc<ComputedValuesMap>>,
pub writing_mode: WritingMode,
pub font_computation_data: FontComputationData,
+ /// The rule node representing the ordered list of rules matched for this
+ /// node. A value should always be present, except when asking for the
+ /// default values.
+ pub rules: Option<StrongRuleNode>,
/// The element's computed values if visited, only computed if there's a
/// relevant link for this element. A element's "relevant link" is the
/// element being matched if it is a link or the nearest ancestor link.
visited_style: Option<Arc<ComputedValues>>,
}
impl ComputedValues {
pub fn new(custom_properties: Option<Arc<ComputedValuesMap>>,
writing_mode: WritingMode,
font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
+ rules: Option<StrongRuleNode>,
visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> Self {
ComputedValues {
custom_properties: custom_properties,
writing_mode: writing_mode,
font_computation_data: FontComputationData::new(font_size_keyword),
+ rules: rules,
visited_style: visited_style,
% for style_struct in data.style_structs:
${style_struct.ident}: ${style_struct.ident},
% endfor
}
}
pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
Arc::new(ComputedValues {
custom_properties: None,
writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious
font_computation_data: FontComputationData::default_values(),
+ rules: None,
visited_style: None,
% for style_struct in data.style_structs:
${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context),
% endfor
})
}
-
#[inline]
pub fn is_display_contents(&self) -> bool {
self.get_box().clone_display() == longhands::display::computed_value::T::contents
}
/// Returns true if the value of the `content` property would make a
/// pseudo-element not rendered.
#[inline]
@@ -151,16 +158,22 @@ impl ComputedValues {
}
#[inline]
pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
Arc::make_mut(&mut self.${style_struct.ident})
}
% endfor
+ /// Gets a reference to the rule node. Panic if the element does not have a
+ /// rule node.
+ pub fn rules(&self) -> &StrongRuleNode {
+ self.rules.as_ref().unwrap()
+ }
+
/// Gets a reference to the visited computed values, if any.
pub fn get_visited_style(&self) -> Option<<&Arc<ComputedValues>> {
self.visited_style.as_ref()
}
/// Gets a reference to the visited computed values. Panic if the element
/// does not have visited computed values.
pub fn visited_style(&self) -> &Arc<ComputedValues> {
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1805,48 +1805,54 @@ pub type ServoComputedValues = ComputedV
/// The struct that Servo uses to represent computed values.
///
/// This struct contains an immutable atomically-reference-counted pointer to
/// every kind of style struct.
///
/// When needed, the structs may be copied in order to get mutated.
#[cfg(feature = "servo")]
-#[cfg_attr(feature = "servo", derive(Clone, Debug))]
+#[cfg_attr(feature = "servo", derive(Clone))]
pub struct ComputedValues {
% for style_struct in data.active_style_structs():
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
/// The writing mode of this computed values struct.
pub writing_mode: WritingMode,
/// The keyword behind the current font-size property, if any
pub font_computation_data: FontComputationData,
+ /// The rule node representing the ordered list of rules matched for this
+ /// node. A value should always be present, except when asking for the
+ /// default values.
+ rules: Option<StrongRuleNode>,
/// The element's computed values if visited, only computed if there's a
/// relevant link for this element. A element's "relevant link" is the
/// element being matched if it is a link or the nearest ancestor link.
visited_style: Option<Arc<ComputedValues>>,
}
#[cfg(feature = "servo")]
impl ComputedValues {
/// Construct a `ComputedValues` instance.
pub fn new(custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
writing_mode: WritingMode,
font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
+ rules: Option<StrongRuleNode>,
visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.active_style_structs():
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> Self {
ComputedValues {
custom_properties: custom_properties,
writing_mode: writing_mode,
font_computation_data: FontComputationData::new(font_size_keyword),
+ rules: rules,
visited_style: visited_style,
% for style_struct in data.active_style_structs():
${style_struct.ident}: ${style_struct.ident},
% endfor
}
}
/// Get the initial computed values.
@@ -1873,16 +1879,22 @@ impl ComputedValues {
/// Get a mutable reference to the ${style_struct.name} struct.
#[inline]
pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
Arc::make_mut(&mut self.${style_struct.ident})
}
% endfor
+ /// Gets a reference to the rule node. Panic if the element does not have a
+ /// rule node.
+ pub fn rules(&self) -> &StrongRuleNode {
+ self.rules.as_ref().unwrap()
+ }
+
/// Gets a reference to the visited computed values, if any.
pub fn get_visited_style(&self) -> Option<<&Arc<ComputedValues>> {
self.visited_style.as_ref()
}
/// Gets a reference to the visited computed values. Panic if the element
/// does not have visited computed values.
pub fn visited_style(&self) -> &Arc<ComputedValues> {
@@ -2133,16 +2145,24 @@ impl ComputedValues {
.and_then(|map| map.get(name))
.map(|value| value.to_css_string())
.unwrap_or(String::new())
}
}
}
}
+// We manually implement Debug for ComputedValues so that we can avoid the
+// verbose stringification of every property and instead focus on a few values.
+impl fmt::Debug for ComputedValues {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "ComputedValues {{ rules: {:?}, .. }}", self.rules)
+ }
+}
+
/// Return a WritingMode bitflags from the relevant CSS properties.
pub fn get_writing_mode(inheritedbox_style: &style_structs::InheritedBox) -> WritingMode {
use logical_geometry;
let mut flags = WritingMode::empty();
match inheritedbox_style.clone_direction() {
computed_values::direction::T::ltr => {},
computed_values::direction::T::rtl => {
flags.insert(logical_geometry::FLAG_RTL);
@@ -2272,16 +2292,19 @@ impl<'a, T: 'a> Deref for StyleStructRef
}
/// A type used to compute a struct with minimal overhead.
///
/// This allows holding references to the parent/default computed values without
/// actually cloning them, until we either build the style, or mutate the
/// inherited value.
pub struct StyleBuilder<'a> {
+ /// The rule node representing the ordered list of rules matched for this
+ /// node.
+ rules: Option<StrongRuleNode>,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
/// The writing mode flags.
///
/// TODO(emilio): Make private.
pub writing_mode: WritingMode,
/// The keyword behind the current font-size property, if any.
pub font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
/// The element's style if visited, only computed if there's a relevant link
@@ -2291,25 +2314,27 @@ pub struct StyleBuilder<'a> {
% for style_struct in data.active_style_structs():
${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
% endfor
}
impl<'a> StyleBuilder<'a> {
/// Trivially construct a `StyleBuilder`.
pub fn new(
+ rules: Option<StrongRuleNode>,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
writing_mode: WritingMode,
font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.active_style_structs():
${style_struct.ident}: &'a Arc<style_structs::${style_struct.name}>,
% endfor
) -> Self {
StyleBuilder {
+ rules: rules,
custom_properties: custom_properties,
writing_mode: writing_mode,
font_size_keyword: font_size_keyword,
visited_style: visited_style,
% for style_struct in data.active_style_structs():
${style_struct.ident}: StyleStructRef::Borrowed(${style_struct.ident}),
% endfor
}
@@ -2319,17 +2344,18 @@ impl<'a> StyleBuilder<'a> {
/// order to create a derived style.
pub fn for_derived_style(s: &'a ComputedValues) -> Self {
Self::for_inheritance(s, s)
}
/// Inherits style from the parent element, accounting for the default
/// computed values that need to be provided as well.
pub fn for_inheritance(parent: &'a ComputedValues, default: &'a ComputedValues) -> Self {
- Self::new(parent.custom_properties(),
+ Self::new(parent.rules.clone(),
+ parent.custom_properties(),
parent.writing_mode,
parent.font_computation_data.font_size_keyword,
parent.clone_visited_style(),
% for style_struct in data.active_style_structs():
% if style_struct.inherited:
parent.${style_struct.name_lower}_arc(),
% else:
default.${style_struct.name_lower}_arc(),
@@ -2395,16 +2421,17 @@ impl<'a> StyleBuilder<'a> {
}
/// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
pub fn build(self) -> ComputedValues {
ComputedValues::new(self.custom_properties,
self.writing_mode,
self.font_size_keyword,
+ self.rules,
self.visited_style,
% for style_struct in data.active_style_structs():
self.${style_struct.ident}.build(),
% endfor
)
}
/// Get the custom properties map if necessary.
@@ -2438,16 +2465,17 @@ mod lazy_static_module {
% if style_struct.name == "Font":
hash: 0,
% endif
}),
% endfor
custom_properties: None,
writing_mode: WritingMode::empty(),
font_computation_data: FontComputationData::default_values(),
+ rules: None,
visited_style: None,
};
}
}
/// A per-longhand function that performs the CSS cascade for that longhand.
pub type CascadePropertyFn =
extern "Rust" fn(declaration: &PropertyDeclaration,
@@ -2553,32 +2581,34 @@ pub fn cascade(device: &Device,
Some((declaration, cascade_level))
} else {
None
}
})
})
};
apply_declarations(device,
+ rule_node,
is_root_element,
iter_declarations,
inherited_style,
layout_parent_style,
visited_style,
cascade_info,
error_reporter,
font_metrics_provider,
flags,
quirks_mode)
}
/// NOTE: This function expects the declaration with more priority to appear
/// first.
#[allow(unused_mut)] // conditionally compiled code for "position"
pub fn apply_declarations<'a, F, I>(device: &Device,
+ rules: &StrongRuleNode,
is_root_element: bool,
iter_declarations: F,
inherited_style: &ComputedValues,
layout_parent_style: &ComputedValues,
visited_style: Option<Arc<ComputedValues>>,
mut cascade_info: Option<<&mut CascadeInfo>,
error_reporter: &ParseErrorReporter,
font_metrics_provider: &FontMetricsProvider,
@@ -2600,30 +2630,32 @@ pub fn apply_declarations<'a, F, I>(devi
}
}
let custom_properties =
::custom_properties::finish_cascade(
custom_properties, &inherited_custom_properties);
let builder = if !flags.contains(INHERIT_ALL) {
- StyleBuilder::new(custom_properties,
+ StyleBuilder::new(Some(rules.clone()),
+ custom_properties,
WritingMode::empty(),
inherited_style.font_computation_data.font_size_keyword,
visited_style,
% for style_struct in data.active_style_structs():
% if style_struct.inherited:
inherited_style.${style_struct.name_lower}_arc(),
% else:
default_style.${style_struct.name_lower}_arc(),
% endif
% endfor
)
} else {
- StyleBuilder::new(custom_properties,
+ StyleBuilder::new(Some(rules.clone()),
+ custom_properties,
WritingMode::empty(),
inherited_style.font_computation_data.font_size_keyword,
visited_style,
% for style_struct in data.active_style_structs():
inherited_style.${style_struct.name_lower}_arc(),
% endfor
)
};
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -1161,17 +1161,17 @@ impl StrongRuleNode {
// Continue to the parent element and search for the inherited properties.
element = match element.inheritance_parent() {
Some(parent) => parent,
None => break
};
let parent_data = element.mutate_data().unwrap();
- let parent_rule_node = parent_data.styles().primary.rules.clone();
+ let parent_rule_node = parent_data.values.primary().rules().clone();
element_rule_node = Cow::Owned(parent_rule_node);
properties = inherited_properties;
}
false
}
--- a/servo/components/style/sharing/checks.rs
+++ b/servo/components/style/sharing/checks.rs
@@ -23,18 +23,18 @@ use stylearc::Arc;
pub fn same_computed_values<E>(first: Option<E>, second: Option<E>) -> bool
where E: TElement,
{
let (a, b) = match (first, second) {
(Some(f), Some(s)) => (f, s),
_ => return false,
};
- let eq = Arc::ptr_eq(a.borrow_data().unwrap().styles().primary.values(),
- b.borrow_data().unwrap().styles().primary.values());
+ let eq = Arc::ptr_eq(a.borrow_data().unwrap().values.primary(),
+ b.borrow_data().unwrap().values.primary());
eq
}
/// Whether two elements have the same same style attribute (by pointer identity).
pub fn have_same_style_attribute<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>
) -> bool
--- a/servo/components/style/sharing/mod.rs
+++ b/servo/components/style/sharing/mod.rs
@@ -65,17 +65,17 @@
//! elements makes sense.
use Atom;
use applicable_declarations::ApplicableDeclarationBlock;
use bit_vec::BitVec;
use bloom::StyleBloom;
use cache::{LRUCache, LRUCacheMutIterator};
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
-use data::{ElementData, ElementStyles};
+use data::{ElementData, ElementValues};
use dom::{TElement, SendElement};
use matching::{ChildCascadeRequirement, MatchMethods};
use properties::ComputedValues;
use selector_parser::RestyleDamage;
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode, StyleRelations};
use smallvec::SmallVec;
use std::mem;
use std::ops::Deref;
@@ -357,65 +357,63 @@ impl<E: TElement> StyleSharingTarget<E>
context.thread_local.current_element_info.as_mut().unwrap().validation_data =
self.validation_data.take();
result
}
fn accumulate_damage_when_sharing(&self,
shared_context: &SharedStyleContext,
- shared_style: &ElementStyles,
+ shared_values: &ElementValues,
data: &mut ElementData) -> ChildCascadeRequirement {
// Accumulate restyle damage for the case when our sharing
// target managed to share style. This can come from several
// sources:
//
// 1) We matched a different set of eager pseudos (which
// should cause a reconstruct).
// 2) We have restyle damage from the eager pseudo computed
// styles.
// 3) We have restyle damage from our own computed styles.
- if data.has_styles() {
+ if data.has_values() {
// We used to have pseudos (because we had styles).
// Check for damage from the set of pseudos changing or
// pseudos being restyled.
- let (styles, mut restyle_data) = data.styles_and_restyle_mut();
- let old_pseudos = &styles.pseudos;
- let new_pseudos = &shared_style.pseudos;
+ let (values, mut restyle_data) = data.values_and_restyle_mut();
+ let old_pseudos = &values.pseudos;
+ let new_pseudos = &shared_values.pseudos;
if !old_pseudos.has_same_pseudos_as(new_pseudos) {
restyle_data.damage |= RestyleDamage::reconstruct();
} else {
// It's a bit unfortunate that we have to keep
// mapping PseudoElements back to indices
// here....
for pseudo in old_pseudos.keys() {
let old_values =
- old_pseudos.get(&pseudo).unwrap().values.as_ref().map(|v| &**v);
+ old_pseudos.get(&pseudo).map(|v| &**v);
let new_values =
- new_pseudos.get(&pseudo).unwrap().values();
+ new_pseudos.get(&pseudo).unwrap();
self.element.accumulate_damage(
&shared_context,
restyle_data,
old_values,
new_values,
Some(&pseudo)
);
}
}
}
- let old_values =
- data.get_styles_mut().and_then(|s| s.primary.values.take());
-
+ let old_values = data.values.primary.take();
self.element.accumulate_damage(
&shared_context,
&mut data.restyle,
old_values.as_ref().map(|v| &**v),
- shared_style.primary.values(),
+ shared_values.primary(),
None
)
}
}
/// A cache miss result.
#[derive(Clone, Debug)]
pub enum CacheMiss {
@@ -590,23 +588,23 @@ impl<E: TElement> StyleSharingCandidateC
target,
candidate,
&shared_context,
bloom_filter,
selector_flags_map
);
match sharing_result {
- Ok(shared_style) => {
+ Ok(shared_values) => {
// Yay, cache hit. Share the style.
let child_cascade_requirement =
target.accumulate_damage_when_sharing(shared_context,
- &shared_style,
+ &shared_values,
data);
- data.set_styles(shared_style);
+ data.values = shared_values;
return StyleSharingResult::StyleWasShared(i, child_cascade_requirement)
}
Err(miss) => {
debug!("Cache miss: {:?}", miss);
// Cache miss, let's see what kind of failure to decide
// whether we keep trying or not.
@@ -627,17 +625,17 @@ impl<E: TElement> StyleSharingCandidateC
StyleSharingResult::CannotShare
}
fn test_candidate(target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>,
shared: &SharedStyleContext,
bloom: &StyleBloom<E>,
selector_flags_map: &mut SelectorFlagsMap<E>)
- -> Result<ElementStyles, CacheMiss> {
+ -> Result<ElementValues, CacheMiss> {
macro_rules! miss {
($miss: ident) => {
return Err(CacheMiss::$miss);
}
}
// Check that we have the same parent, or at least the same pointer
// identity for parent computed style. The latter check allows us to
@@ -703,11 +701,11 @@ impl<E: TElement> StyleSharingCandidateC
miss!(Revalidation)
}
let data = candidate.element.borrow_data().unwrap();
debug_assert!(target.has_current_styles(&data));
debug!("Sharing style between {:?} and {:?}",
target.element, candidate.element);
- Ok(data.styles().clone())
+ Ok(data.values.clone())
}
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -3,17 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Selector matching.
use {Atom, LocalName, Namespace};
use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
use bit_vec::BitVec;
use context::QuirksMode;
-use data::ComputedStyle;
use dom::TElement;
use element_state::ElementState;
use error_reporting::create_error_reporter;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use invalidation::element::invalidation_map::InvalidationMap;
use invalidation::media_queries::EffectiveMediaQueryResults;
@@ -583,17 +582,17 @@ impl Stylist {
/// values. The flow constructor uses this flag when constructing anonymous
/// flows.
pub fn precomputed_values_for_pseudo(&self,
guards: &StylesheetGuards,
pseudo: &PseudoElement,
parent: Option<&Arc<ComputedValues>>,
cascade_flags: CascadeFlags,
font_metrics: &FontMetricsProvider)
- -> ComputedStyle {
+ -> Arc<ComputedValues> {
debug_assert!(pseudo.is_precomputed());
let rule_node = match self.precomputed_pseudo_element_decls.get(pseudo) {
Some(declarations) => {
// FIXME(emilio): When we've taken rid of the cascade we can just
// use into_iter.
self.rule_tree.insert_ordered_rules_with_important(
declarations.into_iter().map(|a| (a.source.clone(), a.level())),
@@ -623,17 +622,17 @@ impl Stylist {
parent.map(|p| &**p),
parent.map(|p| &**p),
None,
None,
&create_error_reporter(),
font_metrics,
cascade_flags,
self.quirks_mode);
- ComputedStyle::new(rule_node, Arc::new(computed))
+ Arc::new(computed)
}
/// Returns the style for an anonymous box of the given type.
#[cfg(feature = "servo")]
pub fn style_for_anonymous(&self,
guards: &StylesheetGuards,
pseudo: &PseudoElement,
parent_style: &Arc<ComputedValues>)
@@ -661,34 +660,33 @@ impl Stylist {
}
};
let mut cascade_flags = CascadeFlags::empty();
if inherit_all {
cascade_flags.insert(INHERIT_ALL);
}
self.precomputed_values_for_pseudo(guards, &pseudo, Some(parent_style), cascade_flags,
&ServoMetricsProvider)
- .values.unwrap()
}
/// Computes a pseudo-element style lazily during layout.
///
/// This can only be done for a certain set of pseudo-elements, like
/// :selection.
///
/// Check the documentation on lazy pseudo-elements in
/// docs/components/style.md
pub fn lazily_compute_pseudo_element_style<E>(&self,
guards: &StylesheetGuards,
element: &E,
pseudo: &PseudoElement,
rule_inclusion: RuleInclusion,
parent_style: &ComputedValues,
font_metrics: &FontMetricsProvider)
- -> Option<ComputedStyle>
+ -> Option<Arc<ComputedValues>>
where E: TElement,
{
let rule_node =
match self.lazy_pseudo_rules(guards, element, pseudo, rule_inclusion) {
Some(rule_node) => rule_node,
None => return None
};
@@ -705,17 +703,17 @@ impl Stylist {
Some(parent_style),
None,
None,
&create_error_reporter(),
font_metrics,
CascadeFlags::empty(),
self.quirks_mode);
- Some(ComputedStyle::new(rule_node, Arc::new(computed)))
+ Some(Arc::new(computed))
}
/// Computes the rule node for a lazily-cascaded pseudo-element.
///
/// See the documentation on lazy pseudo-elements in
/// docs/components/style.md
pub fn lazy_pseudo_rules<E>(&self,
guards: &StylesheetGuards,
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -1,17 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! Traversing the DOM tree; the bloom filter.
use atomic_refcell::AtomicRefCell;
+use context::ElementCascadeInputs;
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
-use data::{ElementData, ElementStyles};
+use data::{ElementData, ElementValues};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
use matching::{ChildCascadeRequirement, MatchMethods};
use sharing::{StyleSharingBehavior, StyleSharingTarget};
#[cfg(feature = "servo")] use servo_config::opts;
use smallvec::SmallVec;
use std::borrow::BorrowMut;
@@ -223,17 +224,17 @@ pub trait DomTraversal<E: TElement> : Sy
traversal_flags: TraversalFlags)
-> PreTraverseToken
{
debug_assert!(!(traversal_flags.for_reconstruct() &&
traversal_flags.for_unstyled_children_only()),
"must not specify FOR_RECONSTRUCT in combination with UNSTYLED_CHILDREN_ONLY");
if traversal_flags.for_unstyled_children_only() {
- if root.borrow_data().map_or(true, |d| d.has_styles() && d.styles().is_display_none()) {
+ if root.borrow_data().map_or(true, |d| d.has_values() && d.values.is_display_none()) {
return PreTraverseToken {
traverse: false,
unstyled_children_only: false,
};
}
return PreTraverseToken {
traverse: true,
unstyled_children_only: true,
@@ -298,17 +299,17 @@ pub trait DomTraversal<E: TElement> : Sy
let going_to_reframe =
parent_data.restyle.reconstructed_self_or_ancestor();
let mut is_before_or_after_pseudo = false;
if let Some(pseudo) = el.implemented_pseudo_element() {
if pseudo.is_before_or_after() {
is_before_or_after_pseudo = true;
let still_match =
- parent_data.styles().pseudos.get(&pseudo).is_some();
+ parent_data.values.pseudos.get(&pseudo).is_some();
if !still_match {
debug_assert!(going_to_reframe,
"We're removing a pseudo, so we \
should reframe!");
return false;
}
}
@@ -328,17 +329,17 @@ pub trait DomTraversal<E: TElement> : Sy
if traversal_flags.for_animation_only() {
// Skip elements that have no style data since animation-only
// restyle is not necessary for the elements.
let data = match el.borrow_data() {
Some(d) => d,
None => return false,
};
- if !data.has_styles() {
+ if !data.has_values() {
return false;
}
if el.has_animation_only_dirty_descendants() {
return true;
}
return data.restyle.hint.has_animation_hint() ||
@@ -354,17 +355,17 @@ pub trait DomTraversal<E: TElement> : Sy
// 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,
};
// If we don't have any style data, we need to visit the element.
- if !data.has_styles() {
+ if !data.has_values() {
return true;
}
// If we have a restyle hint or need to recascade, we need to
// visit the element.
//
// Note that this is different than checking has_current_styles(),
// since that can return true even if we have a restyle hint
@@ -399,17 +400,17 @@ pub trait DomTraversal<E: TElement> : Sy
parent: E,
parent_data: &ElementData,
log: LogBehavior) -> bool
{
// See the comment on `cascade_node` for why we allow this on Gecko.
debug_assert!(cfg!(feature = "gecko") || parent.has_current_styles(parent_data));
// If the parent computed display:none, we don't style the subtree.
- if parent_data.styles().is_display_none() {
+ if parent_data.values.is_display_none() {
if log.allow() { debug!("Parent {:?} is display:none, culling traversal", parent); }
return false;
}
// Gecko-only XBL handling.
//
// If we're computing initial styles and the parent has a Gecko XBL
// binding, that binding may inject anonymous children and remap the
@@ -426,17 +427,17 @@ pub trait DomTraversal<E: TElement> : Sy
// in the same way.
//
// We explicitly avoid handling restyles here (explicitly removing or
// changing bindings), since that adds complexity and is rarer. If it
// happens, we may just end up doing wasted work, since Gecko
// recursively drops Servo ElementData when the XBL insertion parent of
// an Element is changed.
if cfg!(feature = "gecko") && thread_local.is_initial_style() &&
- parent_data.styles().primary.values().has_moz_binding() {
+ parent_data.values.primary().has_moz_binding() {
if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); }
return false;
}
return true;
}
/// Helper for the traversal implementations to select the children that
@@ -456,17 +457,17 @@ pub trait DomTraversal<E: TElement> : Sy
for kid in parent.as_node().traversal_children() {
if Self::node_needs_traversal(kid, self.shared_context().traversal_flags) {
// If we are in a restyle for reconstruction, there is no need to
// perform a post-traversal, so we don't need to set the dirty
// descendants bit on the parent.
if !self.shared_context().traversal_flags.for_reconstruct() {
let el = kid.as_element();
if el.as_ref().and_then(|el| el.borrow_data())
- .map_or(false, |d| d.has_styles()) {
+ .map_or(false, |d| d.has_values()) {
if self.shared_context().traversal_flags.for_animation_only() {
unsafe { parent.set_animation_only_dirty_descendants(); }
} else {
unsafe { parent.set_dirty_descendants(); }
}
}
}
f(thread_local, kid);
@@ -510,17 +511,17 @@ fn resolve_style_internal<E, F>(context:
where E: TElement,
F: Fn(E),
{
ensure_data(element);
let mut data = element.mutate_data().unwrap();
let mut display_none_root = None;
// If the Element isn't styled, we need to compute its style.
- if data.get_styles().is_none() {
+ if !data.has_values() {
// Compute the parent style if necessary.
let parent = element.traversal_parent();
if let Some(p) = parent {
display_none_root = resolve_style_internal(context, p, ensure_data);
}
// Maintain the bloom filter. If it doesn't exist, we need to build it
// from scratch. Otherwise we just need to push the parent.
@@ -544,44 +545,44 @@ fn resolve_style_internal<E, F>(context:
// chain. No need to do this if we're computing default styles, since
// resolve_default_style will want the tree to be left as it is.
unsafe { element.note_descendants::<DirtyDescendants>() };
}
}
// If we're display:none and none of our ancestors are, we're the root
// of a display:none subtree.
- if display_none_root.is_none() && data.styles().is_display_none() {
+ if display_none_root.is_none() && data.values.is_display_none() {
display_none_root = Some(element);
}
return display_none_root
}
/// Manually resolve style by sequentially walking up the parent chain to the
/// first styled Element, ignoring pending restyles. The resolved style is
/// made available via a callback, and can be dropped by the time this function
/// returns in the display:none subtree case.
pub fn resolve_style<E, F, G, H>(context: &mut StyleContext<E>, element: E,
ensure_data: &F, clear_data: &G, callback: H)
where E: TElement,
F: Fn(E),
G: Fn(E),
- H: FnOnce(&ElementStyles)
+ H: FnOnce(&ElementValues)
{
// Clear the bloom filter, just in case the caller is reusing TLS.
context.thread_local.bloom_filter.clear();
// Resolve styles up the tree.
let display_none_root = resolve_style_internal(context, element, ensure_data);
// Make them available for the scope of the callback. The callee may use the
// argument, or perform any other processing that requires the styles to exist
// on the Element.
- callback(element.borrow_data().unwrap().styles());
+ callback(&element.borrow_data().unwrap().values);
// Clear any styles in display:none subtrees or subtrees not in the document,
// to leave the tree in a valid state. For display:none subtrees, we leave
// the styles on the display:none root, but for subtrees not in the document,
// we clear styles all the way up to the root of the disconnected subtree.
let in_doc = element.as_node().is_in_doc();
if !in_doc || display_none_root.is_some() {
let mut curr = element;
@@ -609,17 +610,17 @@ pub fn resolve_style<E, F, G, H>(context
pub fn resolve_default_style<E, F, G, H>(context: &mut StyleContext<E>,
element: E,
ensure_data: &F,
set_data: &G,
callback: H)
where E: TElement,
F: Fn(E),
G: Fn(E, Option<ElementData>) -> Option<ElementData>,
- H: FnOnce(&ElementStyles)
+ H: FnOnce(&ElementValues)
{
// Save and clear out element data from the element and its ancestors.
let mut old_data: SmallVec<[(E, Option<ElementData>); 8]> = SmallVec::new();
{
let mut e = element;
loop {
old_data.push((e, set_data(e, None)));
match e.traversal_parent() {
@@ -630,17 +631,17 @@ pub fn resolve_default_style<E, F, G, H>
}
// Resolve styles up the tree.
resolve_style_internal(context, element, ensure_data);
// Make them available for the scope of the callback. The callee may use the
// argument, or perform any other processing that requires the styles to exist
// on the Element.
- callback(element.borrow_data().unwrap().styles());
+ callback(&element.borrow_data().unwrap().values);
// Swap the old element data back into the element and its ancestors.
for entry in old_data {
set_data(entry.0, entry.1);
}
}
/// Calculates the style for a single node.
@@ -680,17 +681,17 @@ pub fn recalc_style_at<E, D>(traversal:
// We must always cascade native anonymous subtrees, since they inherit styles
// from their first non-NAC ancestor.
if element.is_native_anonymous() {
hint |= RECASCADE_SELF;
}
// If we're restyling this element to display:none, throw away all style
// data in the subtree, notify the caller to early-return.
- if data.styles().is_display_none() {
+ if data.values.is_display_none() {
debug!("{:?} style is display:none - clearing data from descendants.",
element);
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
}
}
// Now that matching and cascading is done, clear the bits corresponding to
// those operations and compute the propagated restyle hint.
@@ -704,17 +705,17 @@ pub fn recalc_style_at<E, D>(traversal:
// FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
propagated_hint.insert(hint);
trace!("propagated_hint={:?} \
is_display_none={:?}, implementing_pseudo={:?}",
propagated_hint,
- data.styles().is_display_none(),
+ data.values.is_display_none(),
element.implemented_pseudo_element());
debug_assert!(element.has_current_styles(data) ||
context.shared.traversal_flags.for_animation_only(),
"Should have computed style or haven't yet valid computed \
style in case of animation-only restyle");
let has_dirty_descendants_for_this_restyle =
if context.shared.traversal_flags.for_animation_only() {
@@ -764,17 +765,17 @@ pub fn recalc_style_at<E, D>(traversal:
// check display:none on the parent when inserting new children (which
// can be moderately expensive). Instead, DOM implementations can
// unconditionally set the dirty descendants bit on any styled parent,
// and let the traversal sort it out.
//
// The second case is when we are in a restyle for reconstruction,
// where we won't need to perform a post-traversal to pick up any
// change hints.
- if data.styles().is_display_none() ||
+ if data.values.is_display_none() ||
context.shared.traversal_flags.for_reconstruct() {
unsafe { element.unset_dirty_descendants(); }
}
}
fn compute_style<E, D>(_traversal: &D,
traversal_data: &PerLevelTraversalData,
context: &mut StyleContext<E>,
@@ -787,17 +788,17 @@ fn compute_style<E, D>(_traversal: &D,
use data::RestyleKind::*;
use sharing::StyleSharingResult::*;
context.thread_local.statistics.elements_styled += 1;
let kind = data.restyle_kind(context.shared);
debug!("compute_style: {:?} (kind={:?})", element, kind);
- if data.has_styles() {
+ if data.has_values() {
data.restyle.set_restyled();
}
match kind {
MatchAndCascade => {
debug_assert!(!context.shared.traversal_flags.for_animation_only(),
"MatchAndCascade shouldn't be processed during \
animation-only traversal");
@@ -824,24 +825,32 @@ fn compute_style<E, D>(_traversal: &D,
// Perform the matching and cascading.
element.match_and_cascade(
context,
data,
StyleSharingBehavior::Allow
)
}
CascadeWithReplacements(flags) => {
- let important_rules_changed = element.replace_rules(flags, context, data);
+ // Skipping full matching, load cascade inputs from previous values.
+ context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
+ let important_rules_changed = element.replace_rules(flags, context);
element.cascade_primary_and_pseudos(
context,
data,
important_rules_changed
)
}
CascadeOnly => {
+ // Skipping full matching, load cascade inputs from previous values.
+ context.thread_local.current_element_info
+ .as_mut().unwrap()
+ .cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
element.cascade_primary_and_pseudos(
context,
data,
/* important_rules_changed = */ false
)
}
}
}
@@ -862,17 +871,17 @@ where
for child in element.as_node().traversal_children() {
// FIXME(bholley): Add TElement::element_children instead of this.
let child = match child.as_element() {
Some(el) => el,
None => continue,
};
// If the child is unstyled, we don't need to set up any restyling.
- if child.borrow_data().map_or(true, |d| !d.has_styles()) {
+ if child.borrow_data().map_or(true, |d| !d.has_values()) {
continue;
}
let mut child_data =
unsafe { D::ensure_element_data(&child).borrow_mut() };
trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}",
child,
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -271,17 +271,17 @@ impl ToComputedValue for Color {
let pres_context = unsafe { &*_context.device.pres_context };
let body = unsafe {
Gecko_GetBody(pres_context)
};
if let Some(body) = body {
let wrap = GeckoElement(body);
let borrow = wrap.borrow_data();
ComputedColor::rgba(borrow.as_ref().unwrap()
- .styles().primary.values()
+ .values.primary()
.get_color()
.clone_color())
} else {
convert_nscolor_to_computedcolor(pres_context.mDefaultColor)
}
},
}
}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -7,17 +7,17 @@ use cssparser::{Parser, ParserInput};
use cssparser::ToCss as ParserToCss;
use env_logger::LogBuilder;
use selectors::Element;
use std::env;
use std::fmt::Write;
use std::ptr;
use style::context::{QuirksMode, SharedStyleContext, StyleContext};
use style::context::ThreadLocalStyleContext;
-use style::data::{ElementData, ElementStyles, RestyleData};
+use style::data::{ElementData, ElementValues, RestyleData};
use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants};
use style::dom::{ShowSubtreeData, TElement, TNode};
use style::element_state::ElementState;
use style::error_reporting::RustLogReporter;
use style::font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl};
use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData};
use style::gecko::restyle_damage::GeckoRestyleDamage;
@@ -194,17 +194,17 @@ fn create_shared_context<'a>(global_styl
fn traverse_subtree(element: GeckoElement,
raw_data: RawServoStyleSetBorrowed,
traversal_flags: TraversalFlags,
snapshots: &ServoElementSnapshotTable) {
// When new content is inserted in a display:none subtree, we will call into
// servo to try to style it. Detect that here and bail out.
if let Some(parent) = element.traversal_parent() {
- if parent.borrow_data().map_or(true, |d| d.styles().is_display_none()) {
+ if parent.borrow_data().map_or(true, |d| d.values.is_display_none()) {
debug!("{:?} has unstyled parent {:?} - ignoring call to traverse_subtree", element, parent);
return;
}
}
let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
debug_assert!(!per_doc_data.stylesheets.has_changed());
@@ -667,34 +667,34 @@ pub extern "C" fn Servo_StyleSet_GetBase
let guard = global_style_data.shared_lock.read();
let shared_context = create_shared_context(&global_style_data,
&guard,
&doc_data,
TraversalFlags::empty(),
unsafe { &*snapshots });
let element = GeckoElement(element);
let element_data = element.borrow_data().unwrap();
- let styles = element_data.styles();
+ let values = &element_data.values;
let pseudo = PseudoElement::from_pseudo_type(pseudo_type);
- let pseudos = &styles.pseudos;
- let pseudo_style = match pseudo {
+ let pseudos = &values.pseudos;
+ let pseudo_values = match pseudo {
Some(ref p) => {
- let style = pseudos.get(p);
- debug_assert!(style.is_some());
- style
+ let values = pseudos.get(p);
+ debug_assert!(values.is_some());
+ values
}
None => None,
};
let provider = get_metrics_provider_for_product();
element.get_base_style(&shared_context,
&provider,
- &styles.primary,
- pseudo_style)
+ values.primary(),
+ pseudo_values)
.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(computed_values: ServoComputedValuesBorrowed,
property_id: nsCSSPropertyID)
-> RawServoAnimationValueStrong
{
@@ -1423,104 +1423,106 @@ pub extern "C" fn Servo_ComputedValues_G
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
let mut cascade_flags = CascadeFlags::empty();
if skip_display_fixup {
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
}
let metrics = get_metrics_provider_for_product();
data.stylist.precomputed_values_for_pseudo(&guards, &pseudo, maybe_parent,
cascade_flags, &metrics)
- .values.unwrap()
.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
pseudo_type: CSSPseudoElementType,
is_probe: bool,
raw_data: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong
{
let element = GeckoElement(element);
let data = unsafe { element.ensure_data() }.borrow_mut();
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
+ debug!("Servo_ResolvePseudoStyle: {:?} {:?}", element,
+ PseudoElement::from_pseudo_type(pseudo_type));
+
// FIXME(bholley): Assert against this.
- if data.get_styles().is_none() {
+ if !data.has_values() {
warn!("Calling Servo_ResolvePseudoStyle on unstyled element");
return if is_probe {
Strong::null()
} else {
doc_data.borrow().default_computed_values().clone().into_strong()
};
}
let pseudo = PseudoElement::from_pseudo_type(pseudo_type)
.expect("ResolvePseudoStyle with a non-pseudo?");
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
match get_pseudo_style(&guard, element, &pseudo, RuleInclusion::All,
- data.styles(), &*doc_data.borrow()) {
+ &data.values, &*doc_data.borrow()) {
Some(values) => values.into_strong(),
// FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an
// empty style inheriting from the element?
- None if !is_probe => data.styles().primary.values().clone().into_strong(),
+ None if !is_probe => data.values.primary().clone().into_strong(),
None => Strong::null(),
}
}
#[no_mangle]
pub extern "C" fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed,
rule_type_mask: u32,
author_colors_allowed: bool)
-> bool
{
let element = GeckoElement(element);
let data = element.borrow_data().unwrap();
- let primary_style = &data.styles().primary;
+ let primary_values = data.values.primary();
let guard = (*GLOBAL_STYLE_DATA).shared_lock.read();
let guards = StylesheetGuards::same(&guard);
- primary_style.rules.has_author_specified_rules(element,
- &guards,
- rule_type_mask,
- author_colors_allowed)
+ primary_values.rules().has_author_specified_rules(element,
+ &guards,
+ rule_type_mask,
+ author_colors_allowed)
}
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
element: GeckoElement,
pseudo: &PseudoElement,
rule_inclusion: RuleInclusion,
- styles: &ElementStyles,
+ values: &ElementValues,
doc_data: &PerDocumentStyleDataImpl)
-> Option<Arc<ComputedValues>>
{
match pseudo.cascade_type() {
- PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()),
+ PseudoElementCascadeType::Eager => values.pseudos.get(&pseudo).map(|s| s.clone()),
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
PseudoElementCascadeType::Lazy => {
let base = if pseudo.inherits_from_default_values() {
doc_data.default_computed_values()
} else {
- styles.primary.values()
+ values.primary()
};
let guards = StylesheetGuards::same(guard);
let metrics = get_metrics_provider_for_product();
doc_data.stylist
.lazily_compute_pseudo_element_style(
&guards,
&element,
&pseudo,
rule_inclusion,
base,
&metrics)
- .map(|s| s.values().clone())
+ .map(|s| s.clone())
},
}
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_Inherit(
raw_data: RawServoStyleSetBorrowed,
parent_style: ServoComputedValuesBorrowedOrNull,
@@ -1682,17 +1684,17 @@ pub extern "C" fn Servo_GetProperties_Ov
let element_data = match element.borrow_data() {
Some(data) => data,
None => return
};
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let guards = StylesheetGuards::same(&guard);
let (overridden, custom) =
- element_data.styles().primary.rules.get_properties_overriding_animations(&guards);
+ element_data.values.primary().rules().get_properties_overriding_animations(&guards);
for p in list.iter() {
match PropertyId::from_nscsspropertyid(*p) {
Ok(property) => {
if let PropertyId::Longhand(id) = property {
if overridden.contains(id) {
unsafe { Gecko_AddPropertyToSet(set, *p) };
}
}
@@ -2454,17 +2456,17 @@ pub extern "C" fn Servo_CSSSupports(cond
/// Only safe to call on the main thread, with exclusive access to the element and
/// its ancestors.
unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
element: GeckoElement,
animation_only: bool)
-> Option<&'a mut RestyleData>
{
// Don't generate a useless RestyleData if the element hasn't been styled.
- if !data.has_styles() {
+ if !data.has_values() {
return None;
}
// Propagate the bit up the chain.
if let Some(p) = element.traversal_parent() {
if animation_only {
p.note_descendants::<AnimationOnlyDirtyDescendants>();
} else {
@@ -2481,22 +2483,22 @@ unsafe fn maybe_restyle<'a>(data: &'a mu
#[no_mangle]
pub extern "C" fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed,
rules: RawGeckoServoStyleRuleListBorrowedMut) {
let element = GeckoElement(element);
let data = match element.borrow_data() {
Some(element_data) => element_data,
None => return,
};
- let computed = match data.get_styles() {
- Some(styles) => &styles.primary,
+ let computed = match data.values.get_primary() {
+ Some(values) => values,
None => return,
};
let mut result = vec![];
- for rule_node in computed.rules.self_and_ancestors() {
+ for rule_node in computed.rules().self_and_ancestors() {
if let &StyleSource::Style(ref rule) = rule_node.style_source() {
result.push(Locked::<StyleRule>::arc_as_borrowed(&rule));
}
}
unsafe { rules.set_len(result.len() as u32) };
for (&src, dest) in result.into_iter().zip(rules.iter_mut()) {
*dest = src;
}
@@ -2554,29 +2556,29 @@ pub extern "C" fn Servo_ResolveStyle(ele
allow_stale: bool)
-> ServoComputedValuesStrong
{
let element = GeckoElement(element);
debug!("Servo_ResolveStyle: {:?}", element);
let data = unsafe { element.ensure_data() }.borrow();
let valid_styles = if allow_stale {
- data.has_styles()
+ data.has_values()
} else {
element.has_current_styles(&*data)
};
if !valid_styles {
debug_assert!(false, "Resolving style on element without current styles with lazy \
computation forbidden.");
let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
return per_doc_data.default_computed_values().clone().into_strong();
}
- data.styles().primary.values().clone().into_strong()
+ data.values.primary().clone().into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_type: CSSPseudoElementType,
rule_inclusion: StyleRuleInclusion,
snapshots: *const ServoElementSnapshotTable,
raw_data: RawServoStyleSetBorrowed)
@@ -2584,28 +2586,33 @@ pub extern "C" fn Servo_ResolveStyleLazi
{
debug_assert!(!snapshots.is_null());
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let element = GeckoElement(element);
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
let data = doc_data.borrow();
let rule_inclusion = RuleInclusion::from(rule_inclusion);
- let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
+ let finish = |values: &ElementValues| -> Arc<ComputedValues> {
PseudoElement::from_pseudo_type(pseudo_type).and_then(|ref pseudo| {
- get_pseudo_style(&guard, element, pseudo, rule_inclusion, styles, &*data)
- }).unwrap_or_else(|| styles.primary.values().clone())
+ get_pseudo_style(&guard, element, pseudo, rule_inclusion, values, &*data)
+ }).unwrap_or_else(|| values.primary().clone())
};
// In the common case we already have the style. Check that before setting
// up all the computation machinery. (Don't use it when we're getting
// default styles, though.)
if rule_inclusion == RuleInclusion::All {
- if let Some(result) = element.mutate_data()
- .and_then(|d| d.get_styles().map(&finish)) {
+ let styles = element.mutate_data().and_then(|d| {
+ match d.has_values() {
+ true => Some(finish(&d.values)),
+ false => None,
+ }
+ });
+ if let Some(result) = styles {
return result.into_strong();
}
}
let traversal_flags = match rule_inclusion {
RuleInclusion::All => TraversalFlags::empty(),
RuleInclusion::DefaultOnly => FOR_DEFAULT_STYLES,
};
@@ -2623,22 +2630,22 @@ pub extern "C" fn Servo_ResolveStyleLazi
thread_local: &mut tlc,
};
let ensure = |el: GeckoElement| { unsafe { el.ensure_data(); } };
match rule_inclusion {
RuleInclusion::All => {
let clear = |el: GeckoElement| el.clear_data();
resolve_style(&mut context, element, &ensure, &clear,
- |styles| result = Some(finish(styles)));
+ |values| result = Some(finish(values)));
}
RuleInclusion::DefaultOnly => {
let set_data = |el: GeckoElement, data| { unsafe { el.set_data(data) } };
resolve_default_style(&mut context, element, &ensure, &set_data,
- |styles| result = Some(finish(styles)));
+ |values| result = Some(finish(values)));
}
}
result.unwrap().into_strong()
}
#[cfg(feature = "gecko_debug")]
fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
@@ -2684,19 +2691,19 @@ pub extern "C" fn Servo_GetComputedKeyfr
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let metrics = get_metrics_provider_for_product();
let style = ComputedValues::as_arc(&style);
let element = GeckoElement(element);
let parent_element = element.inheritance_parent();
let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
- let parent_style = parent_data.as_ref().map(|d| d.styles().primary.values());
-
- let mut context = create_context(&data, &metrics, style, &parent_style);
+ let parent_values = parent_data.as_ref().map(|d| d.values.primary());
+
+ let mut context = create_context(&data, &metrics, style, &parent_values);
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let default_values = data.default_computed_values();
for (index, keyframe) in keyframes.iter().enumerate() {
let ref mut animation_values = computed_keyframes[index];
@@ -2741,19 +2748,19 @@ pub extern "C" fn Servo_GetAnimationValu
animation_values: RawGeckoServoAnimationValueListBorrowedMut) {
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let style = ComputedValues::as_arc(&style);
let metrics = get_metrics_provider_for_product();
let element = GeckoElement(element);
let parent_element = element.inheritance_parent();
let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
- let parent_style = parent_data.as_ref().map(|d| d.styles().primary.values());
-
- let mut context = create_context(&data, &metrics, style, &parent_style);
+ let parent_values = parent_data.as_ref().map(|d| d.values.primary());
+
+ let mut context = create_context(&data, &metrics, style, &parent_values);
let default_values = data.default_computed_values();
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
let guard = declarations.read_with(&guard);
for (index, anim) in guard.to_animation_value_iter(&mut context, &default_values).enumerate() {
@@ -2770,19 +2777,19 @@ pub extern "C" fn Servo_AnimationValue_C
-> RawServoAnimationValueStrong {
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let style = ComputedValues::as_arc(&style);
let metrics = get_metrics_provider_for_product();
let element = GeckoElement(element);
let parent_element = element.inheritance_parent();
let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
- let parent_style = parent_data.as_ref().map(|d| d.styles().primary.values());
-
- let mut context = create_context(&data, &metrics, style, &parent_style);
+ let parent_values = parent_data.as_ref().map(|d| d.values.primary());
+
+ let mut context = create_context(&data, &metrics, style, &parent_values);
let default_values = data.default_computed_values();
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
// We only compute the first element in declarations.
match declarations.read_with(&guard).declarations().first() {
Some(&(ref decl, imp)) if imp == Importance::Normal => {
--- a/servo/tests/unit/stylo/size_of.rs
+++ b/servo/tests/unit/stylo/size_of.rs
@@ -2,17 +2,17 @@
* 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/. */
use selectors::gecko_like_types as dummies;
use servo_arc::Arc;
use std::mem::{size_of, align_of};
use style;
use style::applicable_declarations::ApplicableDeclarationBlock;
-use style::data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
+use style::data::{ElementData, ElementValues, RestyleData};
use style::gecko::selector_parser as real;
use style::properties::ComputedValues;
use style::rule_tree::{RuleNode, StrongRuleNode};
#[test]
fn size_of_selectors_dummy_types() {
assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());
@@ -26,20 +26,20 @@ fn size_of_selectors_dummy_types() {
// The size of this is critical to performance on the bloom-basic microbenchmark.
// When iterating over a large Rule array, we want to be able to fast-reject
// selectors (with the inline hashes) with as few cache misses as possible.
size_of_test!(test_size_of_rule, style::stylist::Rule, 32);
size_of_test!(test_size_of_option_arc_cv, Option<Arc<ComputedValues>>, 8);
size_of_test!(test_size_of_option_rule_node, Option<StrongRuleNode>, 8);
-size_of_test!(test_size_of_computed_style, ComputedStyle, 32);
-size_of_test!(test_size_of_element_styles, ElementStyles, 48);
-size_of_test!(test_size_of_element_data, ElementData, 56);
+
+size_of_test!(test_size_of_element_values, ElementValues, 24);
size_of_test!(test_size_of_restyle_data, RestyleData, 8);
+size_of_test!(test_size_of_element_data, ElementData, 32);
size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);
size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 24);
// FIXME(bholley): This can shrink with a little bit of work.
// See https://github.com/servo/servo/issues/17280
size_of_test!(test_size_of_rule_node, RuleNode, 80);