--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -2,30 +2,29 @@
* 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/. */
//! 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::{EagerPseudoStyles, ElementData};
use dom::{OpaqueNode, TNode, TElement, SendElement};
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 rule_tree::StrongRuleNode;
-use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, SnapshotMap};
-use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
+use selector_parser::{EAGER_PSEUDO_COUNT, SnapshotMap};
+use selectors::matching::ElementSelectorFlags;
use shared_lock::StylesheetGuards;
use sharing::{ValidationData, StyleSharingCandidateCache};
use std::fmt;
use std::ops::Add;
#[cfg(feature = "servo")] use std::sync::Mutex;
#[cfg(feature = "servo")] use std::sync::mpsc::Sender;
use stylearc::Arc;
use stylist::Stylist;
@@ -157,173 +156,37 @@ impl<'a> SharedStyleContext<'a> {
/// 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)]
+#[derive(Clone, Default)]
pub struct CascadeInputs {
/// The rule node representing the ordered list of rules matched for this
/// node.
- rules: Option<StrongRuleNode>,
+ pub 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
- /// 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 Default for CascadeInputs {
- fn default() -> Self {
- CascadeInputs {
- rules: None,
- visited_rules: None,
- visited_values: None,
- }
- }
+ pub visited_rules: Option<StrongRuleNode>,
}
impl CascadeInputs {
/// Construct inputs from previous cascade results, if any.
pub fn new_from_style(style: &Arc<ComputedValues>) -> Self {
CascadeInputs {
rules: style.rules.clone(),
visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),
- // Values will be re-cascaded if necessary, so this can be None.
- visited_values: None,
}
}
-
- /// Whether there are any rules. Rules will be present after unvisited
- /// matching or pulled from a previous cascade if no matching is expected.
- 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, if any.
- pub fn get_rules(&self) -> Option<&StrongRuleNode> {
- self.rules.as_ref()
- }
-
- /// 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),
- }
- }
-
- /// 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. Visited rules will be present
- /// after visited matching or pulled from a previous cascade (assuming there
- /// was a relevant link at the time) if no matching is expected.
- 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.visited_rules.as_ref().unwrap()
- }
-
- /// Sets the visited rule node, and returns whether it changed.
- 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()
- }
-
- /// Whether there are any visited values.
- pub fn has_visited_values(&self) -> bool {
- self.visited_values.is_some()
- }
-
- /// 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 CascadeInputs so that we can avoid the
// verbose stringification of ComputedValues for normal logging.
impl fmt::Debug for CascadeInputs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CascadeInputs {{ rules: {:?}, visited_rules: {:?}, .. }}",
self.rules, self.visited_rules)
@@ -358,233 +221,46 @@ impl EagerPseudoCascadeInputs {
let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
for i in 0..EAGER_PSEUDO_COUNT {
inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
}
inputs
}))
}
- /// Returns whether there are any pseudo inputs.
- pub fn is_empty(&self) -> bool {
- self.0.is_none()
- }
-
- /// 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 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 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, inputs: CascadeInputs) {
- debug_assert!(!self.has(pseudo));
- if self.0.is_none() {
- self.0 = Some(Default::default());
- }
- self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(inputs);
- }
-
- /// 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;
- }
- 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 inputs) = self.get_mut(pseudo) {
- inputs.set_unvisited_rules(rules);
- return false
- }
- 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 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 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 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.
- 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 the list of rules, if they exist.
+ pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
+ self.0
}
}
/// 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>,
+ pub primary: 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),
- }
- }
-}
-
impl ElementCascadeInputs {
/// Construct inputs from previous cascade results, if any.
pub fn new_from_element_data(data: &ElementData) -> Self {
- if !data.has_styles() {
- return ElementCascadeInputs::default()
- }
+ debug_assert!(data.has_styles());
ElementCascadeInputs {
- primary: Some(CascadeInputs::new_from_style(data.styles.primary())),
+ primary: CascadeInputs::new_from_style(data.styles.primary()),
pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
}
}
-
- /// 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 {
/// The element being processed. Currently we use an OpaqueNode since we
@@ -593,21 +269,16 @@ 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.
@@ -901,17 +572,16 @@ 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(),
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());
@@ -946,34 +616,16 @@ impl<E: TElement> Drop for ThreadLocalSt
/// shared style context, and a mutable reference to a local one.
pub struct StyleContext<'a, E: TElement + 'a> {
/// The shared style context reference.
pub shared: &'a SharedStyleContext<'a>,
/// The thread-local style context (mutable) reference.
pub thread_local: &'a mut ThreadLocalStyleContext<E>,
}
-impl<'a, E: TElement + 'a> StyleContext<'a, E> {
- /// Returns a reference to the cascade inputs. Panics if there is no
- /// `CurrentElementInfo`.
- pub fn cascade_inputs(&self) -> &ElementCascadeInputs {
- &self.thread_local.current_element_info
- .as_ref().unwrap()
- .cascade_inputs
- }
-
- /// Returns a mutable reference to the cascade inputs. Panics if there is
- /// no `CurrentElementInfo`.
- pub fn cascade_inputs_mut(&mut self) -> &mut ElementCascadeInputs {
- &mut self.thread_local.current_element_info
- .as_mut().unwrap()
- .cascade_inputs
- }
-}
-
/// Why we're doing reflow.
#[derive(PartialEq, Copy, Clone, Debug)]
pub enum ReflowGoal {
/// We're reflowing in order to send a display list to the screen.
ForDisplay,
/// We're reflowing in order to satisfy a script query. No display list will be created.
ForScriptQuery,
}
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -2,57 +2,30 @@
* 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/. */
//! High-level interface to CSS selector matching.
#![allow(unsafe_code)]
#![deny(missing_docs)]
-use applicable_declarations::ApplicableDeclarationList;
-use cascade_info::CascadeInfo;
-use context::{CascadeInputs, SelectorFlagsMap, SharedStyleContext, StyleContext};
+use context::{CascadeInputs, ElementCascadeInputs, SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ElementData, ElementStyles, RestyleData};
-use dom::{TElement, TNode};
-use font_metrics::FontMetricsProvider;
+use dom::TElement;
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::{AnimationRules, CascadeFlags, ComputedValues};
-use properties::{IS_ROOT_ELEMENT, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
-use properties::{VISITED_DEPENDENT_ONLY, cascade};
+use properties::ComputedValues;
use properties::longhands::display::computed_value as display;
use rule_tree::{CascadeLevel, StrongRuleNode};
-use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
-use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations};
-use selectors::matching::VisitedHandlingMode;
-use sharing::StyleSharingBehavior;
+use selector_parser::{PseudoElement, RestyleDamage};
+use selectors::matching::ElementSelectorFlags;
use stylearc::Arc;
use stylist::RuleInclusion;
-/// Whether we are cascading for an eager pseudo-element or something else.
-///
-/// Controls where we inherit styles from, and whether display:contents is
-/// prohibited.
-#[derive(PartialEq, Copy, Clone, Debug)]
-enum CascadeTarget {
- /// Inherit from the parent element, as normal CSS dictates, _or_ from the
- /// closest non-Native Anonymous element in case this is Native Anonymous
- /// Content. display:contents is allowed.
- Normal,
- /// Inherit from the primary style, this is used while computing eager
- /// pseudos, like ::before and ::after when we're traversing the parent.
- /// Also prohibits display:contents from having an effect.
- ///
- /// TODO(emilio) display:contents really should apply to ::before/::after.
- /// https://github.com/w3c/csswg-drafts/issues/1345
- EagerPseudo,
-}
-
/// Represents the result of comparing an element's old and new style.
pub struct StyleDifference {
/// The resulting damage.
pub damage: RestyleDamage,
/// Whether any styles changed.
pub change: StyleChange,
}
@@ -73,34 +46,35 @@ pub enum StyleChange {
/// The style hasn't changed.
Unchanged,
/// The style has changed.
Changed,
}
/// Whether or not newly computed values for an element need to be cascade
/// to children.
+#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
pub enum ChildCascadeRequirement {
/// Old and new computed values were the same, or we otherwise know that
/// we won't bother recomputing style for children, so we can skip cascading
/// the new values into child elements.
- CanSkipCascade,
+ CanSkipCascade = 0,
/// Old and new computed values were different, so we must cascade the
/// new values to children.
///
/// FIXME(heycam) Although this is "must" cascade, in the future we should
/// track whether child elements rely specifically on inheriting particular
/// property values. When we do that, we can treat `MustCascadeChildren` as
/// "must cascade unless we know that changes to these properties can be
/// ignored".
- MustCascadeChildren,
+ MustCascadeChildren = 1,
/// The same as `MustCascadeChildren`, but for the entire subtree. This is
/// used to handle root font-size updates needing to recascade the whole
/// document.
- MustCascadeDescendants,
+ MustCascadeDescendants = 2,
}
bitflags! {
/// Flags that represent the result of replace_rules.
pub flags RulesChanged: u8 {
/// Normal rules are changed.
const NORMAL_RULES_CHANGED = 0x01,
/// Important rules are changed.
@@ -128,560 +102,56 @@ 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
-/// depending on the current cascade mode.
impl CascadeVisitedMode {
- /// Returns whether there is a rule node based on the cascade mode.
- /// Rules will be present after matching or pulled from a previous cascade
- /// if no matching is expected. For visited, this means rules exist only
- /// if a revelant link existed when matching was last done.
- fn has_rules(&self, inputs: &CascadeInputs) -> bool {
- match *self {
- CascadeVisitedMode::Unvisited => inputs.has_rules(),
- CascadeVisitedMode::Visited => inputs.has_visited_rules(),
- }
- }
-
- /// Returns the rule node based on the cascade mode.
- fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode {
- match *self {
- CascadeVisitedMode::Unvisited => inputs.rules(),
- CascadeVisitedMode::Visited => match inputs.get_visited_rules() {
- Some(rules) => rules,
- None => inputs.rules(),
- }
- }
- }
-
- /// Returns a mutable rules node based on the cascade mode, if any.
- fn get_rules_mut<'a>(&self, inputs: &'a mut CascadeInputs) -> Option<&'a mut StrongRuleNode> {
- match *self {
- 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.
- pub fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> {
- if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() {
- return values.visited_style();
- }
-
- values
- }
-
- /// Set the primary computed values based on the cascade mode.
- fn set_primary_values(&self,
- styles: &mut ElementStyles,
- 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 => styles.primary = Some(values),
- CascadeVisitedMode::Visited => inputs.set_visited_values(values),
- }
- }
-
- /// Set the primary computed values based on the cascade mode.
- fn set_pseudo_values(&self,
- styles: &mut ElementStyles,
- 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 => styles.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,
- styles: &mut ElementStyles,
- 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 => styles.primary.take(),
- CascadeVisitedMode::Visited => inputs.take_visited_values(),
- }
- }
-
- /// Take the pseudo computed values based on the cascade mode.
- fn take_pseudo_values(&self,
- styles: &mut ElementStyles,
- 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 => styles.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.
- pub fn visited_values_for_insertion(&self) -> bool {
- *self == CascadeVisitedMode::Unvisited
- }
-
- /// Returns whether animations should be processed based on the cascade
- /// mode. At the moment, it appears we don't need to support animating
- /// visited styles.
- fn should_process_animations(&self) -> bool {
- *self == CascadeVisitedMode::Unvisited
- }
-
- /// Returns whether we should accumulate restyle damage based on the cascade
- /// mode. At the moment, it appears we don't need to do so for visited
- /// styles. TODO: Verify this is correct as part of
- /// https://bugzilla.mozilla.org/show_bug.cgi?id=1364484.
- fn should_accumulate_damage(&self) -> bool {
- *self == CascadeVisitedMode::Unvisited
- }
-
/// Returns whether the cascade should filter to only visited dependent
/// properties based on the cascade mode.
pub fn visited_dependent_only(&self) -> bool {
*self == CascadeVisitedMode::Visited
}
}
trait PrivateMatchMethods: TElement {
- /// Returns the closest parent element that doesn't have a display: contents
- /// style (and thus generates a box).
- ///
- /// This is needed to correctly handle blockification of flex and grid
- /// items.
- ///
- /// Returns itself if the element has no parent. In practice this doesn't
- /// happen because the root element is blockified per spec, but it could
- /// happen if we decide to not blockify for roots of disconnected subtrees,
- /// which is a kind of dubious beahavior.
- fn layout_parent(&self) -> Self {
- 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().is_display_contents();
-
- if !is_display_contents {
- return current;
- }
- }
- }
-
- /// Get the ComputedValues (if any) for our inheritance parent.
- fn get_inherited_style_and_parent(&self) -> ParentElementAndStyle<Self> {
- let parent_el = self.inheritance_parent();
- let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
- let parent_style = parent_data.as_ref().map(|d| {
- // Sometimes Gecko eagerly styles things without processing
- // pending restyles first. In general we'd like to avoid this,
- // but there can be good reasons (for example, needing to
- // construct a frame for some small piece of newly-added
- // content in order to do something specific with that frame,
- // but not wanting to flush all of layout).
- debug_assert!(cfg!(feature = "gecko") ||
- parent_el.unwrap().has_current_styles(d));
- d.styles.primary()
- });
-
- ParentElementAndStyle {
- element: parent_el,
- style: parent_style.cloned(),
- }
- }
-
- /// A common path for the cascade used by both primary elements and eager
- /// pseudo-elements after collecting the appropriate rules to use.
- ///
- /// `primary_style` is expected to be Some for eager pseudo-elements.
- ///
- /// `parent_info` is our style parent and its primary style, if
- /// it's already been computed.
- fn cascade_with_rules(&self,
- shared_context: &SharedStyleContext,
- font_metrics_provider: &FontMetricsProvider,
- rule_node: &StrongRuleNode,
- primary_style: Option<&Arc<ComputedValues>>,
- cascade_target: CascadeTarget,
- cascade_visited: CascadeVisitedMode,
- parent_info: Option<&ParentElementAndStyle<Self>>,
- 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)
- }
- if cascade_visited.visited_dependent_only() {
- cascade_flags.insert(VISITED_DEPENDENT_ONLY);
- }
- if self.is_native_anonymous() || cascade_target == CascadeTarget::EagerPseudo {
- cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
- } else if self.is_root() {
- cascade_flags.insert(IS_ROOT_ELEMENT);
- }
-
- // Grab the inherited values.
- let parent_el;
- let element_and_style; // So parent_el and style_to_inherit_from are known live.
- let style_to_inherit_from = match cascade_target {
- CascadeTarget::Normal => {
- let info = match parent_info {
- Some(element_and_style) => element_and_style,
- None => {
- element_and_style = self.get_inherited_style_and_parent();
- &element_and_style
- }
- };
- parent_el = info.element;
- info.style.as_ref().map(|s| cascade_visited.values(s))
- }
- CascadeTarget::EagerPseudo => {
- parent_el = Some(self.clone());
- Some(cascade_visited.values(primary_style.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()));
- }
-
- 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.
- //
- // Note that this is technically not needed for pseudos since we already
- // do that when we resolve the non-pseudo style, but it doesn't hurt
- // anyway.
- //
- // TODO(emilio): This is servo-only, move somewhere else?
- if let Some(ref p) = layout_parent_style {
- let can_be_fragmented =
- p.is_multicol() ||
- layout_parent_el.as_ref().unwrap().as_node().can_be_fragmented();
- unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
- }
-
- // Invoke the cascade algorithm.
- let values =
- Arc::new(cascade(shared_context.stylist.device(),
- rule_node,
- &shared_context.guards,
- style_to_inherit_from,
- layout_parent_style,
- visited_values_to_insert,
- Some(&mut cascade_info),
- font_metrics_provider,
- cascade_flags,
- shared_context.quirks_mode));
-
- cascade_info.finish(&self.as_node());
- values
- }
-
- /// A common path for the cascade used by both primary elements and eager
- /// pseudo-elements.
- ///
- /// `primary_style` is expected to be Some for eager pseudo-elements.
- ///
- /// `parent_info` is our style parent and its primary style, if
- /// it's already been computed.
- fn cascade_internal(&self,
- context: &StyleContext<Self>,
- primary_style: Option<&Arc<ComputedValues>>,
- primary_inputs: &CascadeInputs,
- eager_pseudo_inputs: Option<&CascadeInputs>,
- parent_info: Option<&ParentElementAndStyle<Self>>,
- cascade_visited: CascadeVisitedMode)
- -> Arc<ComputedValues> {
- if let Some(pseudo) = self.implemented_pseudo_element() {
- 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
- // big, but given further restyles are posted directly to
- // pseudo-elements, it doesn't seem worth the effort at a glance.
- //
- // For the same reason as described in match_primary, if we are
- // 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() ||
- self.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);
- 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_inputs {
- Some(ref s) => s.clone_visited_values(),
- None => primary_inputs.clone_visited_values(),
- }
- } else {
- None
- };
-
- // Grab the rule node.
- let inputs = eager_pseudo_inputs.unwrap_or(primary_inputs);
- // We'd really like to take the rules here to avoid refcount traffic,
- // but animation's usage of `apply_declarations` make this tricky.
- // See bug 1375525.
- 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,
- cascade_target,
- cascade_visited,
- parent_info,
- visited_values_to_insert)
- }
-
- /// Computes values and damage for the primary style of an element, setting
- /// them on the ElementData.
- ///
- /// `parent_info` is our style parent and its primary style.
- fn cascade_primary(&self,
- context: &mut StyleContext<Self>,
- data: &mut ElementData,
- important_rules_changed: bool,
- parent_info: &ParentElementAndStyle<Self>,
- cascade_visited: CascadeVisitedMode)
- -> ChildCascadeRequirement {
- debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited);
-
- let mut old_values = cascade_visited.take_primary_values(
- &mut data.styles,
- context.cascade_inputs_mut().primary_mut()
- );
-
- let mut new_values = {
- let primary_inputs = context.cascade_inputs().primary();
-
- // If there was no relevant link at the time of matching, 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.
- //
- // Note that we cannot take this early return if our parent has
- // visited style, because then we too have visited style.
- if !cascade_visited.has_rules(primary_inputs) && !parent_info.has_visited_style() {
- return ChildCascadeRequirement::CanSkipCascade
- }
-
- // Compute the new values.
- self.cascade_internal(context,
- None,
- primary_inputs,
- None,
- /* parent_info = */ 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,
- important_rules_changed);
- }
-
- let mut child_cascade_requirement =
- ChildCascadeRequirement::CanSkipCascade;
- if cascade_visited.should_accumulate_damage() {
- child_cascade_requirement =
- self.accumulate_damage(&context.shared,
- &mut data.restyle,
- old_values.as_ref().map(|v| v.as_ref()),
- &new_values,
- None);
-
- // Handle root font-size changes.
- //
- // TODO(emilio): This should arguably be outside of the path for
- // getComputedStyle/getDefaultComputedStyle, but it's unclear how to
- // do it without duplicating a bunch of code.
- if self.is_root() && !self.is_native_anonymous() &&
- !context.shared.traversal_flags.for_default_styles() {
- let device = context.shared.stylist.device();
- let new_font_size = new_values.get_font().clone_font_size();
-
- // If the root font-size changed since last time, and something
- // in the document did use rem units, ensure we recascade the
- // entire tree.
- if old_values.map_or(true, |v| v.get_font().clone_font_size() != new_font_size) {
- // FIXME(emilio): This can fire when called from a document
- // from the bfcache (bug 1376897).
- debug_assert!(self.owner_doc_matches_for_testing(device));
- device.set_root_font_size(new_font_size);
- if device.used_root_font_size() {
- child_cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants;
- }
- }
- }
- }
-
- // If there were visited values to insert, ensure they do in fact exist
- // inside the new values.
- debug_assert!(!cascade_visited.visited_values_for_insertion() ||
- context.cascade_inputs().primary().has_visited_values() ==
- new_values.has_visited_style());
-
- // Set the new computed values.
- let primary_inputs = context.cascade_inputs_mut().primary_mut();
- cascade_visited.set_primary_values(&mut data.styles,
- 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 old_values = cascade_visited.take_pseudo_values(
- &mut data.styles,
- context.cascade_inputs_mut().pseudos.get_mut(pseudo).unwrap(),
- pseudo
- );
-
- let new_values = {
- let pseudo_inputs = context.cascade_inputs().pseudos
- .get(pseudo).unwrap();
-
- // If there was no relevant link at the time of matching, 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.
- let primary_inputs = context.cascade_inputs().primary();
- debug_assert!(cascade_visited.has_rules(primary_inputs));
-
- self.cascade_internal(context,
- data.styles.get_primary(),
- primary_inputs,
- Some(pseudo_inputs),
- /* parent_info = */ None,
- cascade_visited)
- };
-
- if cascade_visited.should_accumulate_damage() {
- self.accumulate_damage(&context.shared,
- &mut data.restyle,
- old_values.as_ref().map(|v| v.as_ref()),
- &new_values,
- Some(pseudo));
- }
-
- let pseudo_inputs = context.cascade_inputs_mut().pseudos
- .get_mut(pseudo).unwrap();
- cascade_visited.set_pseudo_values(&mut data.styles,
- 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: &Arc<ComputedValues>)
- -> Option<Arc<ComputedValues>> {
+ fn get_after_change_style(
+ &self,
+ context: &mut StyleContext<Self>,
+ primary_style: &Arc<ComputedValues>
+ ) -> Option<Arc<ComputedValues>> {
+ use style_resolver::StyleResolverForElement;
+
let rule_node = primary_style.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.
+ // We don't have transition rule in this case, so return None to let
+ // the caller use the original ComputedValues.
return None;
}
- // This currently passes through visited styles, if they exist.
- // When fixing bug 868975, compute after change for visited styles as
- // well, along with updating the rest of the animation processing.
- Some(self.cascade_with_rules(context.shared,
- &context.thread_local.font_metrics_provider,
- &without_transition_rules,
- Some(primary_style),
- CascadeTarget::Normal,
- CascadeVisitedMode::Unvisited,
- /* parent_info = */ None,
- primary_style.get_visited_style().cloned()))
+ // FIXME(bug 868975): We probably need to transition visited style as
+ // well.
+ let inputs =
+ CascadeInputs {
+ rules: Some(without_transition_rules),
+ visited_rules: primary_style.get_visited_style().and_then(|s| s.rules.clone()),
+ };
+
+ let style =
+ StyleResolverForElement::new(*self, context, RuleInclusion::All)
+ .cascade_style_and_visited_with_default_parents(inputs);
+
+ Some(style)
}
#[cfg(feature = "gecko")]
fn needs_animations_update(&self,
context: &mut StyleContext<Self>,
old_values: Option<&Arc<ComputedValues>>,
new_values: &ComputedValues)
-> bool {
@@ -814,27 +284,27 @@ trait PrivateMatchMethods: TElement {
this_opaque,
&**values,
new_values,
&shared_context.timer,
&possibly_expired_animations);
}
}
+
/// Computes and applies non-redundant damage.
#[cfg(feature = "gecko")]
fn accumulate_damage_for(&self,
shared_context: &SharedStyleContext,
restyle: &mut RestyleData,
old_values: &ComputedValues,
new_values: &Arc<ComputedValues>,
pseudo: Option<&PseudoElement>)
-> ChildCascadeRequirement {
use properties::computed_value_flags::*;
-
// Don't accumulate damage if we're in a restyle for reconstruction.
if shared_context.traversal_flags.for_reconstruct() {
return ChildCascadeRequirement::MustCascadeChildren;
}
// If an ancestor is already getting reconstructed by Gecko's top-down
// frame constructor, no need to apply damage. Similarly if we already
// have an explicitly stored ReconstructFrame hint.
@@ -926,467 +396,177 @@ trait PrivateMatchMethods: TElement {
}
}
}
}
}
impl<E: TElement> PrivateMatchMethods for E {}
-/// A struct that holds an element we inherit from and its ComputedValues.
-#[derive(Debug)]
-struct ParentElementAndStyle<E: TElement> {
- /// Our style parent element.
- element: Option<E>,
- /// Element's primary ComputedValues. Not a borrow because we can't prove
- /// that the thing hanging off element won't change while we're passing this
- /// struct around.
- style: Option<Arc<ComputedValues>>,
-}
-
-impl<E: TElement> ParentElementAndStyle<E> {
- fn has_visited_style(&self) -> bool {
- self.style.as_ref().map_or(false, |v| { v.get_visited_style().is_some() })
- }
-}
+/// The public API that elements expose for selector matching.
+pub trait MatchMethods : TElement {
+ /// Returns the closest parent element that doesn't have a display: contents
+ /// style (and thus generates a box).
+ ///
+ /// This is needed to correctly handle blockification of flex and grid
+ /// items.
+ ///
+ /// Returns itself if the element has no parent. In practice this doesn't
+ /// happen because the root element is blockified per spec, but it could
+ /// happen if we decide to not blockify for roots of disconnected subtrees,
+ /// which is a kind of dubious beahavior.
+ fn layout_parent(&self) -> Self {
+ let mut current = self.clone();
+ loop {
+ current = match current.traversal_parent() {
+ Some(el) => el,
+ None => return current,
+ };
-/// Collects the outputs of the primary matching process, including the rule
-/// node and other associated data.
-#[derive(Debug)]
-pub struct MatchingResults {
- /// Whether the rules changed.
- rules_changed: bool,
- /// Whether there are any changes of important rules overriding animations.
- important_rules_overriding_animation_changed: bool,
- /// Records certains relations between elements noticed during matching (and
- /// also extended after matching).
- relations: StyleRelations,
- /// Whether we encountered a "relevant link" while matching _any_ selector
- /// for this element. (This differs from `RelevantLinkStatus` which tracks
- /// the status for the _current_ selector only.)
- relevant_link_found: bool,
-}
+ let is_display_contents =
+ current.borrow_data().unwrap().styles.primary().is_display_contents();
-impl MatchingResults {
- /// Create `MatchingResults` with only the basic required outputs.
- fn new(rules_changed: bool, important_rules: bool) -> Self {
- Self {
- rules_changed: rules_changed,
- important_rules_overriding_animation_changed: important_rules,
- relations: StyleRelations::default(),
- relevant_link_found: false,
+ if !is_display_contents {
+ return current;
+ }
}
}
- /// Create `MatchingResults` from the output fields of `MatchingContext`.
- fn new_from_context(rules_changed: bool,
- important_rules: bool,
- context: MatchingContext)
- -> Self {
- Self {
- rules_changed: rules_changed,
- important_rules_overriding_animation_changed: important_rules,
- relations: context.relations,
- relevant_link_found: context.relevant_link_found,
- }
- }
-}
-
-/// The public API that elements expose for selector matching.
-pub trait MatchMethods : TElement {
- /// Performs selector matching and property cascading on an element and its
- /// eager pseudos.
- fn match_and_cascade(
+ /// Updates the styles with the new ones, diffs them, and stores the restyle
+ /// damage.
+ fn finish_restyle(
&self,
context: &mut StyleContext<Self>,
- data: &mut ElementData,
- sharing: StyleSharingBehavior
+ mut data: &mut ElementData,
+ mut new_styles: ElementStyles,
+ important_rules_changed: bool,
) -> ChildCascadeRequirement {
- debug!("Match and cascade for {:?}", self);
-
- // Perform selector matching for the primary style.
- let primary_results =
- self.match_primary(context, data, VisitedHandlingMode::AllLinksUnvisited);
- let important_rules_changed =
- primary_results.important_rules_overriding_animation_changed;
+ use dom::TNode;
+ use std::mem;
+ use std::cmp;
- // If there's a relevant link involved, match and cascade primary styles
- // as if the link is visited as well. This is done before the regular
- // cascade because the visited ComputedValues are placed within the
- // regular ComputedValues, which is immutable after the cascade.
- let relevant_link_found = primary_results.relevant_link_found;
- if relevant_link_found {
- self.match_primary(context, data, VisitedHandlingMode::RelevantLinkVisited);
- }
+ debug_assert!(new_styles.primary.is_some(), "How did that happen?");
- // Even if there is no relevant link, we need to cascade visited styles
- // if our parent has visited styles.
- let parent_and_styles = self.get_inherited_style_and_parent();
- if relevant_link_found || parent_and_styles.has_visited_style() {
- self.cascade_primary(
+ if !context.shared.traversal_flags.for_animation_only() {
+ self.process_animations(
context,
- data,
+ &mut data.styles.primary,
+ &mut new_styles.primary.as_mut().unwrap(),
important_rules_changed,
- &parent_and_styles,
- CascadeVisitedMode::Visited
);
}
- // Cascade properties and compute primary values.
- let child_cascade_requirement =
- self.cascade_primary(
- context,
- data,
- important_rules_changed,
- &parent_and_styles,
- CascadeVisitedMode::Unvisited
- );
-
- // Match and cascade eager pseudo-elements.
- if !data.styles.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
- // after the cascade.
- if relevant_link_found {
- self.match_pseudos(context, data, VisitedHandlingMode::RelevantLinkVisited);
- self.cascade_pseudos(context, data, CascadeVisitedMode::Visited);
- }
-
- self.cascade_pseudos(context, data, CascadeVisitedMode::Unvisited);
- }
-
- // 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.
- //
- // If we do have the results, grab them here to satisfy the borrow
- // checker.
- let validation_data =
- context.thread_local
- .current_element_info
- .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(),
- primary_results.relations,
- validation_data,
- dom_depth);
- }
-
- child_cascade_requirement
- }
+ // First of all, update the styles.
+ let old_styles = mem::replace(&mut data.styles, new_styles);
- /// Performs the cascade, without matching.
- fn cascade_primary_and_pseudos(&self,
- context: &mut StyleContext<Self>,
- mut data: &mut ElementData,
- important_rules_changed: bool)
- -> ChildCascadeRequirement
- {
- // If there's a relevant link involved, cascade styles as if the link is
- // visited as well. This is done before the regular cascade because the
- // visited ComputedValues are placed within the regular ComputedValues,
- // which is immutable after the cascade. If there aren't any visited
- // rules, these calls will return without cascading.
- let parent_and_styles = self.get_inherited_style_and_parent();
- self.cascade_primary(context, &mut data, important_rules_changed,
- &parent_and_styles,
- CascadeVisitedMode::Visited);
- let child_cascade_requirement =
- self.cascade_primary(context, &mut data, important_rules_changed,
- &parent_and_styles,
- CascadeVisitedMode::Unvisited);
- self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Visited);
- self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Unvisited);
- child_cascade_requirement
- }
-
- /// Runs selector matching to (re)compute the primary rule node for this
- /// element.
- ///
- /// Returns `MatchingResults` with the new rules and other associated data
- /// from the matching process.
- 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 primary_inputs = context.thread_local.current_element_info
- .as_mut().unwrap()
- .cascade_inputs.ensure_primary();
+ // Propagate the "can be fragmented" bit. It would be nice to
+ // encapsulate this better.
+ //
+ // Note that this is technically not needed for pseudos since we already
+ // do that when we resolve the non-pseudo style, but it doesn't hurt
+ // anyway.
+ if cfg!(feature = "servo") {
+ let layout_parent =
+ self.inheritance_parent().map(|e| e.layout_parent());
+ let layout_parent_data =
+ layout_parent.as_ref().and_then(|e| e.borrow_data());
+ let layout_parent_style =
+ layout_parent_data.as_ref().map(|d| d.styles.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
- // animations.
- //
- // However, if we're computing default styles, then we might
- // have traversed to this pseudo-implementing element without
- // 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();
- if parent.may_have_animations() {
- let animation_rules = self.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,
- Some(&animation_rule),
- &mut rules,
- &context.shared.guards);
- if let Some(node) = animation_rule_node {
- rules = node;
- }
- }
-
- if let Some(animation_rule) = animation_rules.1 {
- let animation_rule_node =
- context.shared.stylist.rule_tree()
- .update_rule_at_level(CascadeLevel::Transitions,
- Some(&animation_rule),
- &mut rules,
- &context.shared.guards);
- if let Some(node) = animation_rule_node {
- rules = node;
- }
- }
- }
- let important_rules_changed =
- self.has_animations() &&
- data.has_styles() &&
- data.important_rules_are_different(&rules,
- &context.shared.guards);
-
- let rules_changed = primary_inputs.set_rules(visited_handling, rules);
-
- return MatchingResults::new(rules_changed, important_rules_changed)
+ if let Some(ref p) = layout_parent_style {
+ let can_be_fragmented =
+ p.is_multicol() ||
+ layout_parent.as_ref().unwrap().as_node().can_be_fragmented();
+ unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
}
}
- let mut applicable_declarations = ApplicableDeclarationList::new();
-
- let stylist = &context.shared.stylist;
- let style_attribute = self.style_attribute();
-
- let map = &mut context.thread_local.selector_flags;
- let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
- self.apply_selector_flags(map, element, flags);
- };
+ // Don't accumulate damage if we're in a restyle for reconstruction.
+ if context.shared.traversal_flags.for_reconstruct() {
+ return ChildCascadeRequirement::MustCascadeChildren;
+ }
- let rule_inclusion = if only_default_rules {
- RuleInclusion::DefaultOnly
- } else {
- RuleInclusion::All
- };
-
- let bloom_filter = context.thread_local.bloom_filter.filter();
- let mut matching_context =
- MatchingContext::new_for_visited(MatchingMode::Normal,
- Some(bloom_filter),
- visited_handling,
- context.shared.quirks_mode);
+ let new_primary_style = data.styles.primary.as_ref().unwrap();
- {
- let smil_override = self.get_smil_override();
- let animation_rules = self.get_animation_rules();
+ let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
+ if self.is_root() && !self.is_native_anonymous() {
+ let device = context.shared.stylist.device();
+ let new_font_size = new_primary_style.get_font().clone_font_size();
- // Compute the primary rule node.
- stylist.push_applicable_declarations(self,
- implemented_pseudo.as_ref(),
- style_attribute,
- smil_override,
- animation_rules,
- rule_inclusion,
- &mut applicable_declarations,
- &mut matching_context,
- &mut set_selector_flags);
- }
- self.unset_dirty_style_attribute();
-
- let primary_rule_node = stylist.rule_tree().compute_rule_node(
- &mut applicable_declarations,
- &context.shared.guards
- );
-
- if log_enabled!(Trace) {
- trace!("Matched rules:");
- for rn in primary_rule_node.self_and_ancestors() {
- let source = rn.style_source();
- if source.is_some() {
- trace!(" > {:?}", source);
+ if old_styles.primary.as_ref().map_or(true, |s| s.get_font().clone_font_size() != new_font_size) {
+ debug_assert!(self.owner_doc_matches_for_testing(device));
+ device.set_root_font_size(new_font_size);
+ // If the root font-size changed since last time, and something
+ // in the document did use rem units, ensure we recascade the
+ // entire tree.
+ if device.used_root_font_size() {
+ cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants;
}
}
}
- let important_rules_changed =
- self.has_animations() &&
- data.has_styles() &&
- data.important_rules_are_different(
- &primary_rule_node,
- &context.shared.guards
- );
-
- 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.
- fn match_pseudos(&self,
- context: &mut StyleContext<Self>,
- data: &mut ElementData,
- visited_handling: VisitedHandlingMode)
- {
- debug!("Match pseudos for {:?}, visited: {:?}", self, visited_handling);
-
- if self.implemented_pseudo_element().is_some() {
- // Element pseudos can't have any other pseudo.
- return;
- }
-
- let mut applicable_declarations = ApplicableDeclarationList::new();
-
- // 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
+ // Also, don't do anything if there was no style.
+ let old_primary_style = match old_styles.primary {
+ Some(s) => s,
+ None => return ChildCascadeRequirement::MustCascadeChildren,
};
- // Compute rule nodes for eagerly-cascaded pseudo-elements.
- let mut matches_different_pseudos = false;
- SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
- // For eager pseudo-elements, we only try to match visited rules if
- // there are also unvisited rules. (This matches Gecko's behavior
- // for probing pseudo-elements, and for eager pseudo-elements Gecko
- // does not try to resolve style if the probe says there isn't any.)
- if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
- !context.cascade_inputs().pseudos.has(&pseudo) {
- return
- }
+ cascade_requirement = cmp::max(
+ cascade_requirement,
+ self.accumulate_damage_for(
+ context.shared,
+ &mut data.restyle,
+ &old_primary_style,
+ new_primary_style,
+ None,
+ )
+ );
- if self.may_generate_pseudo(&pseudo, data.styles.primary()) {
- let bloom_filter = context.thread_local.bloom_filter.filter();
+ if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
+ return cascade_requirement;
+ }
- let mut matching_context =
- MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
- Some(bloom_filter),
- visited_handling,
- context.shared.quirks_mode);
-
- let map = &mut context.thread_local.selector_flags;
- let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
- self.apply_selector_flags(map, element, flags);
- };
+ // If it matched a different number of pseudos, reconstruct.
+ if data.styles.pseudos.is_empty() != old_styles.pseudos.is_empty() {
+ data.restyle.damage |= RestyleDamage::reconstruct();
+ return cascade_requirement;
+ }
- 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 pseudo_styles =
+ old_styles.pseudos.as_array().unwrap().iter().zip(
+ data.styles.pseudos.as_array().unwrap().iter());
- 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 |= !data.styles.pseudos.has(&pseudo);
- pseudos.add_rules(
- &pseudo,
- visited_handling,
- rules
- );
- } else {
- matches_different_pseudos |= data.styles.pseudos.has(&pseudo);
- pseudos.remove_rules(
- &pseudo,
- visited_handling
- );
- data.styles.pseudos.take(&pseudo);
+ for (i, (old, new)) in pseudo_styles.enumerate() {
+ match (old, new) {
+ (&Some(ref old), &Some(ref new)) => {
+ self.accumulate_damage_for(
+ context.shared,
+ &mut data.restyle,
+ old,
+ new,
+ Some(&PseudoElement::from_eager_index(i)),
+ );
+ }
+ (&None, &None) => {},
+ _ => {
+ data.restyle.damage |= RestyleDamage::reconstruct();
+ return cascade_requirement;
+ }
}
- });
+ }
- if matches_different_pseudos && data.restyle.is_restyle() {
- // Any changes to the matched pseudo-elements trigger
- // reconstruction.
- data.restyle.damage |= RestyleDamage::reconstruct();
- }
+ cascade_requirement
}
+
/// Applies selector flags to an element, deferring mutations of the parent
/// until after the traversal.
///
- /// TODO(emilio): This is somewhat inefficient, because of a variety of
- /// reasons:
- ///
- /// * It doesn't coalesce flags.
- /// * It doesn't look at flags already sent in a task for the main
- /// thread to process.
- /// * It doesn't take advantage of us knowing that the traversal is
- /// sequential.
- ///
- /// I suspect (need to measure!) that we don't use to set flags on
- /// a lot of different elements, but we could end up posting the same
- /// flag over and over with this approach.
- ///
- /// If the number of elements is low, perhaps a small cache with the
- /// flags already sent would be appropriate.
- ///
- /// The sequential task business for this is kind of sad :(.
- ///
- /// Anyway, let's do the obvious thing for now.
+ /// TODO(emilio): This is somewhat inefficient, because it doesn't take
+ /// advantage of us knowing that the traversal is sequential.
fn apply_selector_flags(&self,
map: &mut SelectorFlagsMap<Self>,
element: &Self,
flags: ElementSelectorFlags) {
// Handle flags that apply to the element.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
if element == self {
@@ -1453,48 +633,63 @@ 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: &mut StyleContext<Self>,
+ cascade_inputs: &mut ElementCascadeInputs,
) -> bool {
let mut result = false;
- result |= self.replace_rules_internal(replacements, context,
- CascadeVisitedMode::Unvisited);
+ result |= self.replace_rules_internal(
+ replacements,
+ context,
+ CascadeVisitedMode::Unvisited,
+ cascade_inputs,
+ );
if !context.shared.traversal_flags.for_animation_only() {
- result |= self.replace_rules_internal(replacements, context,
- CascadeVisitedMode::Visited);
+ result |= self.replace_rules_internal(
+ replacements,
+ context,
+ CascadeVisitedMode::Visited,
+ cascade_inputs
+ );
}
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: &mut StyleContext<Self>,
- cascade_visited: CascadeVisitedMode
+ cascade_visited: CascadeVisitedMode,
+ cascade_inputs: &mut ElementCascadeInputs,
) -> bool {
use properties::PropertyDeclarationBlock;
use shared_lock::Locked;
debug_assert!(replacements.intersects(RestyleHint::replacements()) &&
(replacements & !RestyleHint::replacements()).is_empty());
let stylist = &context.shared.stylist;
let guards = &context.shared.guards;
- let mut primary_inputs = context.cascade_inputs_mut().primary_mut();
- let primary_rules = match cascade_visited.get_rules_mut(primary_inputs) {
+ let primary_rules =
+ match cascade_visited {
+ CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
+ CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
+ };
+
+ let primary_rules = match primary_rules {
Some(r) => r,
None => return false,
};
let replace_rule_node = |level: CascadeLevel,
pdb: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
path: &mut StrongRuleNode| -> bool {
let new_node = stylist.rule_tree()
@@ -1513,16 +708,17 @@ pub trait MatchMethods : TElement {
if replacements.contains(RESTYLE_STYLE_ATTRIBUTE) {
let style_attribute = self.style_attribute();
result |= replace_rule_node(CascadeLevel::StyleAttributeNormal,
style_attribute,
primary_rules);
result |= replace_rule_node(CascadeLevel::StyleAttributeImportant,
style_attribute,
primary_rules);
+ // FIXME(emilio): Still a hack!
self.unset_dirty_style_attribute();
}
return result;
}
// Animation restyle hints are processed prior to other restyle
// hints in the animation-only traversal.
//
@@ -1559,22 +755,22 @@ pub trait MatchMethods : TElement {
}
false
}
/// Given the old and new style of this element, and whether it's a
/// pseudo-element, compute the restyle damage used to determine which
/// kind of layout or painting operations we'll need.
- fn compute_style_difference(&self,
- old_values: &ComputedValues,
- new_values: &Arc<ComputedValues>,
- pseudo: Option<&PseudoElement>)
- -> StyleDifference
- {
+ fn compute_style_difference(
+ &self,
+ old_values: &ComputedValues,
+ new_values: &Arc<ComputedValues>,
+ pseudo: Option<&PseudoElement>
+ ) -> StyleDifference {
debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
if let Some(source) = self.existing_style_for_restyle_damage(old_values, pseudo) {
return RestyleDamage::compute_style_difference(source, new_values)
}
let new_display = new_values.get_box().clone_display();
let old_display = old_values.get_box().clone_display();
@@ -1637,60 +833,41 @@ pub trait MatchMethods : TElement {
RestyleDamage::empty()
};
// We don't really know if there was a change in any style (since we
// didn't actually call compute_style_difference) but we return
// StyleChange::Changed conservatively.
StyleDifference::new(damage, StyleChange::Changed)
}
- /// Performs the cascade for the element's eager pseudos.
- fn cascade_pseudos(&self,
- context: &mut StyleContext<Self>,
- mut data: &mut ElementData,
- cascade_visited: CascadeVisitedMode)
- {
- 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 = context.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,
+ context: &mut StyleContext<Self>,
+ style: &Arc<ComputedValues>,
+ ) -> Arc<ComputedValues> {
+ use style_resolver::StyleResolverForElement;
- /// Returns computed values without animation and transition rules.
- fn get_base_style(&self,
- shared_context: &SharedStyleContext,
- font_metrics_provider: &FontMetricsProvider,
- primary_style: &Arc<ComputedValues>,
- pseudo_style: Option<&Arc<ComputedValues>>)
- -> Arc<ComputedValues> {
- let relevant_style = pseudo_style.unwrap_or(primary_style);
- let rule_node = relevant_style.rules();
+ let rule_node = style.rules.as_ref().unwrap();
let without_animation_rules =
- shared_context.stylist.rule_tree().remove_animation_rules(rule_node);
+ context.shared.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.clone();
+ // Note that unwrapping here is fine, because the style is only
+ // incomplete during the styling process.
+ return style.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,
- Some(primary_style),
- CascadeTarget::Normal,
- CascadeVisitedMode::Unvisited,
- /* parent_info = */ None,
- None)
+ let inputs =
+ CascadeInputs {
+ rules: Some(without_animation_rules),
+ visited_rules: None,
+ };
+
+ let style =
+ StyleResolverForElement::new(*self, context, RuleInclusion::All)
+ .cascade_style_and_visited_with_default_parents(inputs);
+ style
}
-
}
impl<E: TElement> MatchMethods for E {}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2571,21 +2571,21 @@ pub fn cascade(device: &Device,
parent_style: Option<<&ComputedValues>,
layout_parent_style: Option<<&ComputedValues>,
visited_style: Option<Arc<ComputedValues>>,
cascade_info: Option<<&mut CascadeInfo>,
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags,
quirks_mode: QuirksMode)
-> ComputedValues {
- debug_assert_eq!(parent_style.is_some(), layout_parent_style.is_some());
+ debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
let (inherited_style, layout_parent_style) = match parent_style {
Some(parent_style) => {
(parent_style,
- layout_parent_style.unwrap())
+ layout_parent_style.unwrap_or(parent_style))
},
None => {
(device.default_computed_values(),
device.default_computed_values())
}
};
let iter_declarations = || {
--- a/servo/components/style/sharing/mod.rs
+++ b/servo/components/style/sharing/mod.rs
@@ -70,17 +70,17 @@ use bit_vec::BitVec;
use bloom::StyleBloom;
use cache::{LRUCache, LRUCacheMutIterator};
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ElementData, ElementStyles};
use dom::{TElement, SendElement};
use matching::{ChildCascadeRequirement, MatchMethods};
use properties::ComputedValues;
use selector_parser::RestyleDamage;
-use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
use smallvec::SmallVec;
use std::mem;
use std::ops::Deref;
use stylist::Stylist;
mod checks;
/// The amount of nodes that the style sharing candidate cache should hold at
@@ -489,21 +489,18 @@ impl<E: TElement> StyleSharingCandidateC
}
/// Tries to insert an element in the style sharing cache.
///
/// Fails if we know it should never be in the cache.
pub fn insert_if_possible(&mut self,
element: &E,
style: &ComputedValues,
- relations: StyleRelations,
- mut validation_data: ValidationData,
+ validation_data: ValidationData,
dom_depth: usize) {
- use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS;
-
let parent = match element.traversal_parent() {
Some(element) => element,
None => {
debug!("Failing to insert to the cache: no parent element");
return;
}
};
@@ -520,23 +517,16 @@ impl<E: TElement> StyleSharingCandidateC
return;
}
if box_style.specifies_animations() {
debug!("Failing to insert to the cache: animations");
return;
}
- // Take advantage of the information we've learned during
- // selector-matching.
- if !relations.intersects(AFFECTED_BY_PRESENTATIONAL_HINTS) {
- debug_assert!(validation_data.pres_hints.as_ref().map_or(true, |v| v.is_empty()));
- validation_data.pres_hints = Some(SmallVec::new());
- }
-
debug!("Inserting into cache: {:?} with parent {:?}", element, parent);
if self.dom_depth != dom_depth {
debug!("Clearing cache because depth changed from {:?} to {:?}, element: {:?}",
self.dom_depth, dom_depth, element);
self.clear();
self.dom_depth = dom_depth;
}
--- a/servo/components/style/style_resolver.rs
+++ b/servo/components/style/style_resolver.rs
@@ -1,17 +1,17 @@
/* 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/. */
//! Style resolution for a given element or pseudo-element.
use applicable_declarations::ApplicableDeclarationList;
use cascade_info::CascadeInfo;
-use context::StyleContext;
+use context::{CascadeInputs, ElementCascadeInputs, StyleContext};
use data::{ElementStyles, EagerPseudoStyles};
use dom::TElement;
use log::LogLevel::Trace;
use matching::{CascadeVisitedMode, MatchMethods};
use properties::{AnimationRules, CascadeFlags, ComputedValues};
use properties::{IS_ROOT_ELEMENT, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
use properties::{VISITED_DEPENDENT_ONLY, cascade};
use rule_tree::StrongRuleNode;
@@ -37,21 +37,47 @@ struct MatchingResults {
rule_node: StrongRuleNode,
relevant_link_found: bool,
}
/// The primary style of an element or an element-backed pseudo-element.
pub struct PrimaryStyle {
/// The style per se.
pub style: Arc<ComputedValues>,
+}
- /// Whether a relevant link was found while computing this style.
- ///
- /// FIXME(emilio): Slightly out of place?
- pub relevant_link_found: bool,
+fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
+where
+ E: TElement,
+ F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
+{
+ let parent_el = element.inheritance_parent();
+ let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
+ let parent_style = parent_data.as_ref().map(|d| {
+ // Sometimes Gecko eagerly styles things without processing
+ // pending restyles first. In general we'd like to avoid this,
+ // but there can be good reasons (for example, needing to
+ // construct a frame for some small piece of newly-added
+ // content in order to do something specific with that frame,
+ // but not wanting to flush all of layout).
+ debug_assert!(cfg!(feature = "gecko") ||
+ parent_el.unwrap().has_current_styles(d));
+ d.styles.primary()
+ });
+
+ let mut layout_parent_el = parent_el.clone();
+ let layout_parent_data;
+ let mut layout_parent_style = parent_style;
+ if parent_style.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(layout_parent_data.styles.primary());
+ }
+
+ f(parent_style.map(|s| &**s), layout_parent_style.map(|s| &**s))
}
impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
where
'ctx: 'a,
'le: 'ctx,
E: TElement + MatchMethods + 'le,
{
@@ -90,35 +116,35 @@ where
let mut visited_style = None;
let should_compute_visited_style =
relevant_link_found ||
parent_style.and_then(|s| s.get_visited_style()).is_some();
if should_compute_visited_style {
visited_style = Some(self.cascade_style(
- visited_rules.as_ref().unwrap_or(&primary_results.rule_node),
+ visited_rules.as_ref(),
/* style_if_visited = */ None,
parent_style,
layout_parent_style,
CascadeVisitedMode::Visited,
/* pseudo = */ None,
));
}
let style = self.cascade_style(
- &primary_results.rule_node,
+ Some(&primary_results.rule_node),
visited_style,
parent_style,
layout_parent_style,
CascadeVisitedMode::Unvisited,
/* pseudo = */ None,
);
- PrimaryStyle { style, relevant_link_found, }
+ PrimaryStyle { style, }
}
/// Resolve the style of a given element, and all its eager pseudo-elements.
pub fn resolve_style(
&mut self,
parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
@@ -132,17 +158,17 @@ where
if primary_style.style.get_box().clone_display() == display::none {
return ElementStyles {
// FIXME(emilio): Remove the Option<>.
primary: Some(primary_style.style),
pseudos: pseudo_styles,
}
}
- {
+ if self.element.implemented_pseudo_element().is_none() {
let layout_parent_style_for_pseudo =
if primary_style.style.is_display_contents() {
layout_parent_style
} else {
Some(&*primary_style.style)
};
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
let pseudo_style = self.resolve_pseudo_style(
@@ -158,59 +184,158 @@ where
ElementStyles {
// FIXME(emilio): Remove the Option<>.
primary: Some(primary_style.style),
pseudos: pseudo_styles,
}
}
+ /// Resolve an element's styles with the default inheritance parent/layout
+ /// parents.
+ pub fn resolve_style_with_default_parents(&mut self) -> ElementStyles {
+ with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
+ self.resolve_style(parent_style, layout_parent_style)
+ })
+ }
+
+ /// Cascade a set of rules, using the default parent for inheritance.
+ pub fn cascade_style_and_visited_with_default_parents(
+ &mut self,
+ inputs: CascadeInputs,
+ ) -> Arc<ComputedValues> {
+ with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
+ self.cascade_style_and_visited(
+ inputs,
+ parent_style,
+ layout_parent_style,
+ /* pseudo = */ None
+ )
+ })
+ }
+
+ fn cascade_style_and_visited(
+ &mut self,
+ inputs: CascadeInputs,
+ parent_style: Option<&ComputedValues>,
+ layout_parent_style: Option<&ComputedValues>,
+ pseudo: Option<&PseudoElement>,
+ ) -> Arc<ComputedValues> {
+ let mut style_if_visited = None;
+ if parent_style.map_or(false, |s| s.get_visited_style().is_some()) ||
+ inputs.visited_rules.is_some() {
+ style_if_visited = Some(self.cascade_style(
+ inputs.visited_rules.as_ref(),
+ /* style_if_visited = */ None,
+ parent_style,
+ layout_parent_style,
+ CascadeVisitedMode::Visited,
+ pseudo,
+ ));
+ }
+ self.cascade_style(
+ inputs.rules.as_ref(),
+ style_if_visited,
+ parent_style,
+ layout_parent_style,
+ CascadeVisitedMode::Unvisited,
+ pseudo,
+ )
+ }
+
+ /// Cascade the element and pseudo-element styles with the default parents.
+ pub fn cascade_styles_with_default_parents(
+ &mut self,
+ inputs: ElementCascadeInputs,
+ ) -> ElementStyles {
+ use properties::longhands::display::computed_value::T as display;
+ with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
+ let primary_style = PrimaryStyle {
+ style: self.cascade_style_and_visited(
+ inputs.primary,
+ parent_style,
+ layout_parent_style,
+ /* pseudo = */ None,
+ ),
+ };
+
+ let mut pseudo_styles = EagerPseudoStyles::default();
+ let pseudo_array = inputs.pseudos.into_array();
+ if pseudo_array.is_none() ||
+ primary_style.style.get_box().clone_display() == display::none {
+ return ElementStyles {
+ primary: Some(primary_style.style),
+ pseudos: pseudo_styles,
+ }
+ }
+
+ {
+ let layout_parent_style_for_pseudo =
+ if primary_style.style.is_display_contents() {
+ layout_parent_style
+ } else {
+ Some(&*primary_style.style)
+ };
+
+ for (i, mut inputs) in pseudo_array.unwrap().iter_mut().enumerate() {
+ if let Some(inputs) = inputs.take() {
+ let pseudo = PseudoElement::from_eager_index(i);
+ pseudo_styles.set(
+ &pseudo,
+ self.cascade_style_and_visited(
+ inputs,
+ Some(&*primary_style.style),
+ layout_parent_style_for_pseudo,
+ Some(&pseudo),
+ )
+ )
+ }
+ }
+ }
+
+ ElementStyles {
+ primary: Some(primary_style.style),
+ pseudos: pseudo_styles,
+ }
+ })
+ }
+
fn resolve_pseudo_style(
&mut self,
pseudo: &PseudoElement,
originating_element_style: &PrimaryStyle,
layout_parent_style: Option<&ComputedValues>,
) -> Option<Arc<ComputedValues>> {
let rules = self.match_pseudo(
&originating_element_style.style,
pseudo,
VisitedHandlingMode::AllLinksUnvisited
);
let rules = match rules {
Some(rules) => rules,
None => return None,
};
- let mut visited_style = None;
- if originating_element_style.relevant_link_found {
- let visited_rules = self.match_pseudo(
+ let mut visited_rules = None;
+ if originating_element_style.style.get_visited_style().is_some() {
+ visited_rules = self.match_pseudo(
&originating_element_style.style,
pseudo,
VisitedHandlingMode::RelevantLinkVisited,
);
-
- if let Some(ref rules) = visited_rules {
- visited_style = Some(self.cascade_style(
- rules,
- /* style_if_visited = */ None,
- Some(&originating_element_style.style),
- layout_parent_style,
- CascadeVisitedMode::Visited,
- Some(pseudo),
- ));
- }
}
- Some(self.cascade_style(
- &rules,
- visited_style,
+ Some(self.cascade_style_and_visited(
+ CascadeInputs {
+ rules: Some(rules),
+ visited_rules
+ },
Some(&originating_element_style.style),
layout_parent_style,
- CascadeVisitedMode::Unvisited,
- Some(pseudo)
+ Some(pseudo),
))
}
fn match_primary(
&mut self,
visited_handling: VisitedHandlingMode,
) -> MatchingResults {
debug!("Match primary for {:?}, visited: {:?}",
@@ -330,48 +455,50 @@ where
&self.context.shared.guards
);
Some(rule_node)
}
fn cascade_style(
&mut self,
- rules: &StrongRuleNode,
+ rules: Option<&StrongRuleNode>,
style_if_visited: Option<Arc<ComputedValues>>,
- parent_style: Option<&ComputedValues>,
+ mut parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
cascade_visited: CascadeVisitedMode,
pseudo: Option<&PseudoElement>,
) -> Arc<ComputedValues> {
let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
if self.element.skip_root_and_item_based_display_fixup() {
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
}
if cascade_visited.visited_dependent_only() {
+ parent_style = parent_style.map(|s| {
+ s.get_visited_style().map(|s| &**s).unwrap_or(s)
+ });
cascade_flags.insert(VISITED_DEPENDENT_ONLY);
}
if self.element.is_native_anonymous() || pseudo.is_some() {
cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
} else if self.element.is_root() {
cascade_flags.insert(IS_ROOT_ELEMENT);
}
let values =
Arc::new(cascade(
self.context.shared.stylist.device(),
- rules,
+ rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
&self.context.shared.guards,
parent_style,
layout_parent_style,
style_if_visited,
Some(&mut cascade_info),
- &*self.context.shared.error_reporter,
&self.context.thread_local.font_metrics_provider,
cascade_flags,
self.context.shared.quirks_mode
));
cascade_info.finish(&self.element.as_node());
values
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -10,17 +10,16 @@ use bit_vec::BitVec;
use context::{CascadeInputs, QuirksMode};
use dom::TElement;
use element_state::ElementState;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use invalidation::element::invalidation_map::InvalidationMap;
use invalidation::media_queries::{EffectiveMediaQueryResults, ToMediaListKey};
-use matching::CascadeVisitedMode;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock};
#[cfg(feature = "servo")]
use properties::INHERIT_ALL;
use rule_tree::{CascadeLevel, RuleTree, StyleSource};
use selector_map::{SelectorMap, SelectorMapEntry};
use selector_parser::{SelectorImpl, PseudoElement};
@@ -714,34 +713,40 @@ impl Stylist {
inputs: &CascadeInputs,
guards: &StylesheetGuards,
parent_style: &Arc<ComputedValues>,
font_metrics: &FontMetricsProvider)
-> Option<Arc<ComputedValues>>
{
// We may have only visited rules in cases when we are actually
// resolving, not probing, pseudo-element style.
- if !inputs.has_rules() && !inputs.has_visited_rules() {
+ if inputs.rules.is_none() && inputs.visited_rules.is_none() {
return None
}
// We need to compute visited values if we have visited rules or if our
// parent has visited values.
- let visited_values = if inputs.has_visited_rules() || parent_style.get_visited_style().is_some() {
+ let visited_values = if inputs.visited_rules.is_some() || parent_style.get_visited_style().is_some() {
// Slightly annoying: we know that inputs has either rules or
// visited rules, but we can't do inputs.rules() up front because
// maybe it just has visited rules, so can't unwrap_or.
- let rule_node = match inputs.get_visited_rules() {
+ let rule_node = match inputs.visited_rules.as_ref() {
Some(rules) => rules,
- None => inputs.rules()
+ None => inputs.rules.as_ref().unwrap(),
};
// We want to use the visited bits (if any) from our parent style as
// our parent.
- let mode = CascadeVisitedMode::Visited;
- let inherited_style = mode.values(parent_style);
+ let inherited_style =
+ parent_style.get_visited_style().unwrap_or(&*parent_style);
+
+ // FIXME(emilio): The lack of layout_parent_style here could be
+ // worrying, but we're probably dropping the display fixup for
+ // pseudos other than before and after, so it's probably ok.
+ //
+ // (Though the flags don't indicate so!)
let computed =
properties::cascade(&self.device,
rule_node,
guards,
Some(inherited_style),
Some(inherited_style),
None,
None,
@@ -751,17 +756,17 @@ impl Stylist {
Some(Arc::new(computed))
} else {
None
};
// We may not have non-visited rules, if we only had visited ones. In
// that case we want to use the root rulenode for our non-visited rules.
- let rules = inputs.get_rules().unwrap_or(self.rule_tree.root());
+ let rules = inputs.rules.as_ref().unwrap_or(self.rule_tree.root());
// Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the
// computed value is still "contents").
let computed =
properties::cascade(&self.device,
rules,
@@ -844,20 +849,20 @@ impl Stylist {
&mut matching_context,
&mut set_selector_flags
);
if !declarations.is_empty() {
let rule_node =
self.rule_tree.compute_rule_node(&mut declarations, guards);
debug_assert!(rule_node != *self.rule_tree.root());
- inputs.set_rules(VisitedHandlingMode::AllLinksUnvisited, rule_node);
+ inputs.rules = Some(rule_node);
}
- if is_probe && !inputs.has_rules() {
+ if is_probe && inputs.rules.is_none() {
// When probing, don't compute visited styles if we have no
// unvisited styles.
return inputs;
}
if matching_context.relevant_link_found {
let mut declarations = ApplicableDeclarationList::new();
let mut matching_context =
@@ -875,18 +880,17 @@ impl Stylist {
&mut matching_context,
&mut set_selector_flags);
if !declarations.is_empty() {
let rule_node =
self.rule_tree.insert_ordered_rules_with_important(
declarations.into_iter().map(|a| a.order_and_level()),
guards);
if rule_node != *self.rule_tree.root() {
- inputs.set_rules(VisitedHandlingMode::RelevantLinkVisited,
- rule_node);
+ inputs.visited_rules = Some(rule_node);
}
}
}
inputs
}
/// Set a given device, which may change the styles that apply to the
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -5,18 +5,19 @@
//! Traversing the DOM tree; the bloom filter.
use atomic_refcell::AtomicRefCell;
use context::{ElementCascadeInputs, StyleContext, SharedStyleContext};
use data::{ElementData, ElementStyles};
use dom::{NodeInfo, OpaqueNode, TElement, TNode};
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
use matching::{ChildCascadeRequirement, MatchMethods};
-use sharing::{StyleSharingBehavior, StyleSharingTarget};
+use sharing::StyleSharingTarget;
use smallvec::SmallVec;
+use style_resolver::StyleResolverForElement;
use stylist::RuleInclusion;
/// A per-traversal-level chunk of data. This is sent down by the traversal, and
/// currently only holds the dom depth for the bloom filter.
///
/// NB: Keep this as small as possible, please!
#[derive(Clone, Debug)]
pub struct PerLevelTraversalData {
@@ -760,17 +761,18 @@ where
let kind = data.restyle_kind(context.shared);
debug!("compute_style: {:?} (kind={:?})", element, kind);
if data.has_styles() {
data.restyle.set_restyled();
}
- match kind {
+ let mut important_rules_changed = false;
+ let new_styles = match kind {
MatchAndCascade => {
debug_assert!(!context.shared.traversal_flags.for_animation_only(),
"MatchAndCascade shouldn't be processed during \
animation-only traversal");
// Ensure the bloom filter is up to date.
context.thread_local.bloom_filter
.insert_parents_recovering(element,
traversal_data.current_dom_depth);
@@ -785,45 +787,72 @@ where
if let StyleWasShared(index, had_damage) = sharing_result {
context.thread_local.statistics.styles_shared += 1;
context.thread_local.style_sharing_candidate_cache.touch(index);
return had_damage;
}
context.thread_local.statistics.elements_matched += 1;
+ important_rules_changed = true;
+
// Perform the matching and cascading.
- element.match_and_cascade(
- context,
- data,
- StyleSharingBehavior::Allow
- )
+ let new_styles =
+ StyleResolverForElement::new(element, context, RuleInclusion::All)
+ .resolve_style_with_default_parents();
+
+ // 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.
+ //
+ // If we do have the results, grab them here to satisfy the borrow
+ // checker.
+ let validation_data =
+ context.thread_local
+ .current_element_info
+ .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(
+ &element,
+ new_styles.primary(),
+ validation_data,
+ dom_depth
+ );
+
+ new_styles
}
CascadeWithReplacements(flags) => {
// Skipping full matching, load cascade inputs from previous values.
- *context.cascade_inputs_mut() =
+ let mut 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
- )
+ important_rules_changed =
+ element.replace_rules(flags, context, &mut cascade_inputs);
+ StyleResolverForElement::new(element, context, RuleInclusion::All)
+ .cascade_styles_with_default_parents(cascade_inputs)
}
CascadeOnly => {
// Skipping full matching, load cascade inputs from previous values.
- *context.cascade_inputs_mut() =
+ let cascade_inputs =
ElementCascadeInputs::new_from_element_data(data);
- element.cascade_primary_and_pseudos(
- context,
- data,
- /* important_rules_changed = */ false
- )
+ StyleResolverForElement::new(element, context, RuleInclusion::All)
+ .cascade_styles_with_default_parents(cascade_inputs)
}
- }
+ };
+
+ element.finish_restyle(
+ context,
+ data,
+ new_styles,
+ important_rules_changed
+ )
}
fn preprocess_children<E, D>(
context: &mut StyleContext<E>,
element: E,
propagated_hint: RestyleHint,
reconstructed_ancestor: bool,
)
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -659,42 +659,45 @@ pub extern "C" fn Servo_StyleSet_GetBase
-> ServoComputedValuesStrong
{
use style::matching::MatchMethods;
debug_assert!(!snapshots.is_null());
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let global_style_data = &*GLOBAL_STYLE_DATA;
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 pseudo = PseudoElement::from_pseudo_type(pseudo_type);
- let pseudos = &styles.pseudos;
- let pseudo_style = match pseudo {
- Some(ref p) => {
- let style = pseudos.get(p);
- debug_assert!(style.is_some());
- style
- }
- None => None,
+ if let Some(pseudo) = PseudoElement::from_pseudo_type(pseudo_type) {
+ // This style already doesn't have animations.
+ return styles
+ .pseudos
+ .get(&pseudo)
+ .expect("GetBaseComputedValuesForElement for an unexisting pseudo?")
+ .clone().into_strong();
+ }
+
+ let shared = create_shared_context(&global_style_data,
+ &guard,
+ &doc_data,
+ TraversalFlags::empty(),
+ unsafe { &*snapshots });
+ let mut tlc = ThreadLocalStyleContext::new(&shared);
+ let mut context = StyleContext {
+ shared: &shared,
+ thread_local: &mut tlc,
};
- let provider = get_metrics_provider_for_product();
- element.get_base_style(&shared_context,
- &provider,
- styles.primary(),
- pseudo_style)
- .into_strong()
+ element.get_base_style(
+ &mut context,
+ styles.primary(),
+ ).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(computed_values: ServoComputedValuesBorrowed,
property_id: nsCSSPropertyID)
-> RawServoAnimationValueStrong
{
let property = match AnimatableLonghand::from_nscsspropertyid(property_id) {