style: Add support for resolving default computed styles.
draft
style: Add support for resolving default computed styles.
MozReview-Commit-ID: 6lt7TDgTNuE
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -67,16 +67,18 @@ use selectors::Element;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use shared_lock::Locked;
use sink::Push;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
+use std::mem;
+use std::ops::DerefMut;
use std::ptr;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use stylearc::Arc;
use stylesheets::UrlExtraData;
use stylist::ApplicableDeclarationBlock;
/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
///
@@ -417,16 +419,39 @@ impl<'le> GeckoElement<'le> {
debug!("Creating ElementData for {:?}", self);
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new(None))));
self.0.mServoData.set(ptr);
unsafe { &* ptr }
},
}
}
+ /// Sets the specified element data, return any existing data.
+ ///
+ /// Like `ensure_data`, only safe to call with exclusive access to the
+ /// element.
+ pub unsafe fn set_data(&self, new_element_data: Option<ElementData>) -> Option<ElementData> {
+ match (self.get_data(), new_element_data) {
+ (Some(old_data), Some(new_element_data)) => {
+ Some(mem::replace(old_data.borrow_mut().deref_mut(), new_element_data))
+ }
+ (Some(old_data), None) => {
+ let old_element_data = mem::replace(old_data.borrow_mut().deref_mut(), ElementData::new(None));
+ self.0.mServoData.set(ptr::null_mut());
+ Some(old_element_data)
+ }
+ (None, Some(new_element_data)) => {
+ let ptr = Box::into_raw(Box::new(AtomicRefCell::new(new_element_data)));
+ self.0.mServoData.set(ptr);
+ None
+ }
+ (None, None) => None,
+ }
+ }
+
#[inline]
fn may_have_animations(&self) -> bool {
self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
}
#[inline]
fn has_id(&self) -> bool {
self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasID)
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -19,17 +19,17 @@ use restyle_hints::{RESTYLE_CSS_ANIMATIO
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations};
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use shared_lock::StylesheetGuards;
use sharing::{StyleSharingBehavior, StyleSharingResult};
use stylearc::Arc;
-use stylist::ApplicableDeclarationList;
+use stylist::{ApplicableDeclarationList, RuleInclusion};
/// The way a style should be inherited.
enum InheritMode {
/// Inherit from the parent element, as normal CSS dictates, _or_ from the
/// closest non-Native Anonymous element in case this is Native Anonymous
/// Content.
Normal,
/// Inherit from the primary style, this is used while computing eager
@@ -249,17 +249,23 @@ trait PrivateMatchMethods: TElement {
// 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.
- if pseudo.is_eager() && self.get_animation_rules().is_empty() {
+ //
+ // 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() && self.get_animation_rules().is_empty() && !only_default_rules {
let parent = self.parent_element().unwrap();
let parent_data = parent.borrow_data().unwrap();
let pseudo_style =
parent_data.styles().pseudos.get(&pseudo).unwrap();
return pseudo_style.values().clone()
}
}
@@ -677,21 +683,32 @@ pub trait MatchMethods : TElement {
/// Returns RulesMatchedResult which indicates whether the primary rule node changed
/// and whether the change includes important rules.
fn match_primary(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
relations: &mut StyleRelations)
-> RulesMatchedResult
{
+ 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 {
- if pseudo.is_eager() {
- // If it's an eager element-backed pseudo, just grab the matched
- // rules from the parent, and update animations.
+ 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();
let animation_rules = self.get_animation_rules();
// Handle animations here.
@@ -735,31 +752,37 @@ pub trait MatchMethods : TElement {
let mut applicable_declarations = ApplicableDeclarationList::new();
let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute();
let smil_override = self.get_smil_override();
let animation_rules = self.get_animation_rules();
let bloom = context.thread_local.bloom_filter.filter();
+ let rule_inclusion = if only_default_rules {
+ RuleInclusion::DefaultOnly
+ } else {
+ RuleInclusion::All
+ };
let map = &mut context.thread_local.selector_flags;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
self.apply_selector_flags(map, element, flags);
};
let mut matching_context =
MatchingContext::new(MatchingMode::Normal, Some(bloom));
// 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);
*relations = matching_context.relations;
let primary_rule_node =
compute_rule_node::<Self>(stylist.rule_tree(),
@@ -813,16 +836,22 @@ pub trait MatchMethods : TElement {
// 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_tree = stylist.rule_tree();
let bloom_filter = context.thread_local.bloom_filter.filter();
+ let rule_inclusion = if context.shared.traversal_flags.for_default_styles() {
+ RuleInclusion::DefaultOnly
+ } else {
+ RuleInclusion::All
+ };
+
let mut matching_context =
MatchingContext::new(MatchingMode::ForStatelessPseudoElement,
Some(bloom_filter));
// Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false;
let mut rule_nodes_changed = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
@@ -830,16 +859,17 @@ pub trait MatchMethods : TElement {
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);
if !applicable_declarations.is_empty() {
let new_rules =
compute_rule_node::<Self>(rule_tree,
&mut applicable_declarations,
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -8,17 +8,17 @@ use {Atom, LocalName, Namespace};
use bit_vec::BitVec;
use context::{QuirksMode, SharedStyleContext};
use data::ComputedStyle;
use dom::{AnimationRules, TElement};
use element_state::ElementState;
use error_reporting::RustLogReporter;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
-use gecko_bindings::structs::nsIAtom;
+use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use keyframes::KeyframesAnimation;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
#[cfg(feature = "servo")]
use properties::INHERIT_ALL;
use properties::PropertyDeclarationBlock;
use restyle_hints::{HintComputationContext, DependencySet, RestyleHint};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
@@ -200,16 +200,37 @@ pub struct ExtraStyleData<'a> {
pub marker: PhantomData<&'a usize>,
}
#[cfg(feature = "servo")]
impl<'a> ExtraStyleData<'a> {
fn clear(&mut self) {}
}
+/// What cascade levels to include when styling elements.
+#[derive(Copy, Clone, PartialEq)]
+pub enum RuleInclusion {
+ /// Include rules for style sheets at all cascade levels. This is the
+ /// normal rule inclusion mode.
+ All,
+ /// Only include rules from UA and user level sheets. Used to implement
+ /// `getDefaultComputedStyle`.
+ DefaultOnly,
+}
+
+#[cfg(feature = "gecko")]
+impl From<StyleRuleInclusion> for RuleInclusion {
+ fn from(value: StyleRuleInclusion) -> Self {
+ match value {
+ StyleRuleInclusion::All => RuleInclusion::All,
+ StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
+ }
+ }
+}
+
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
/// be reset in clear().
#[inline]
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
let mut stylist = Stylist {
viewport_constraints: None,
@@ -619,23 +640,24 @@ impl Stylist {
/// :selection.
///
/// Check the documentation on lazy pseudo-elements in
/// docs/components/style.md
pub fn lazily_compute_pseudo_element_style<E>(&self,
guards: &StylesheetGuards,
element: &E,
pseudo: &PseudoElement,
+ rule_inclusion: RuleInclusion,
parent_style: &ComputedValues,
font_metrics: &FontMetricsProvider)
-> Option<ComputedStyle>
where E: TElement,
{
let rule_node =
- match self.lazy_pseudo_rules(guards, element, pseudo) {
+ match self.lazy_pseudo_rules(guards, element, pseudo, rule_inclusion) {
Some(rule_node) => rule_node,
None => return None
};
// 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").
@@ -656,17 +678,18 @@ impl Stylist {
/// Computes the rule node for a lazily-cascaded pseudo-element.
///
/// See the documentation on lazy pseudo-elements in
/// docs/components/style.md
pub fn lazy_pseudo_rules<E>(&self,
guards: &StylesheetGuards,
element: &E,
- pseudo: &PseudoElement)
+ pseudo: &PseudoElement,
+ rule_inclusion: RuleInclusion)
-> Option<StrongRuleNode>
where E: TElement
{
debug_assert!(pseudo.is_lazy());
if self.pseudos_map.get(pseudo).is_none() {
return None
}
@@ -674,16 +697,22 @@ impl Stylist {
// already, so we can directly apply the parent flags.
let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
if cfg!(feature = "servo") {
// Servo calls this function from the worker, but only for internal
// pseudos, so we should never generate selector flags here.
unreachable!("internal pseudo generated slow selector flags?");
}
+ // No need to bother setting the selector flags when we're computing
+ // default styles.
+ if rule_inclusion == RuleInclusion::DefaultOnly {
+ return;
+ }
+
// Gecko calls this from sequential mode, so we can directly apply
// the flags.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { element.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
@@ -697,16 +726,17 @@ impl Stylist {
let mut declarations = ApplicableDeclarationList::new();
let mut matching_context =
MatchingContext::new(MatchingMode::ForStatelessPseudoElement, None);
self.push_applicable_declarations(element,
Some(pseudo),
None,
None,
AnimationRules(None, None),
+ rule_inclusion,
&mut declarations,
&mut matching_context,
&mut set_selector_flags);
if declarations.is_empty() {
return None
}
let rule_node =
@@ -826,16 +856,17 @@ impl Stylist {
/// which kind of rules have matched.
pub fn push_applicable_declarations<E, V, F>(
&self,
element: &E,
pseudo_element: Option<&PseudoElement>,
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules,
+ rule_inclusion: RuleInclusion,
applicable_declarations: &mut V,
context: &mut MatchingContext,
flags_setter: &mut F)
where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + ::std::fmt::Debug,
F: FnMut(&E, ElementSelectorFlags),
{
debug_assert!(!self.is_device_dirty);
@@ -861,26 +892,28 @@ impl Stylist {
element.closest_non_native_anonymous_ancestor().unwrap()
} else {
*element
};
debug!("Determining if style is shareable: pseudo: {}",
pseudo_element.is_some());
+ let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
+
// Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
flags_setter,
CascadeLevel::UANormal);
debug!("UA normal: {:?}", context.relations);
- if pseudo_element.is_none() {
+ if pseudo_element.is_none() && !only_default_rules {
// Step 2: Presentational hints.
let length_before_preshints = applicable_declarations.len();
element.synthesize_presentational_hints_for_legacy_attributes(applicable_declarations);
if applicable_declarations.len() != length_before_preshints {
if cfg!(debug_assertions) {
for declaration in &applicable_declarations[length_before_preshints..] {
assert_eq!(declaration.level, CascadeLevel::PresHints);
}
@@ -895,24 +928,30 @@ impl Stylist {
// inaccurate, would be equivalent to something like:
//
// element.matches_user_and_author_rules() ||
// (is_implemented_pseudo &&
// rule_hash_target.matches_user_and_author_rules())
//
// Which may be more what you would probably expect.
if rule_hash_target.matches_user_and_author_rules() {
- // Step 3: User and author normal rules.
+ // Step 3a: User normal rules.
map.user.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
flags_setter,
CascadeLevel::UserNormal);
debug!("user normal: {:?}", context.relations);
+ } else {
+ debug!("skipping user rules");
+ }
+
+ if rule_hash_target.matches_user_and_author_rules() && !only_default_rules {
+ // Step 3b: Author normal rules.
map.author.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
flags_setter,
CascadeLevel::AuthorNormal);
debug!("author normal: {:?}", context.relations);
@@ -951,24 +990,29 @@ impl Stylist {
debug!("skipping non-agent rules");
}
//
// Steps 7-10 correspond to !important rules, and are handled during
// rule tree insertion.
//
- // Step 11: Transitions.
- // The transitions sheet (CSS transitions that are tied to CSS markup)
- if let Some(anim) = animation_rules.1 {
- Push::push(
- applicable_declarations,
- ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
+ if !only_default_rules {
+ // Step 11: Transitions.
+ // The transitions sheet (CSS transitions that are tied to CSS markup)
+ if let Some(anim) = animation_rules.1 {
+ Push::push(
+ applicable_declarations,
+ ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
+ }
+ debug!("transition: {:?}", context.relations);
+ } else {
+ debug!("skipping transition rules");
}
- debug!("transition: {:?}", context.relations);
+
debug!("push_applicable_declarations: shareable: {:?}", context.relations);
}
/// Return whether the device is dirty, that is, whether the screen size or
/// media type have changed (for now).
#[inline]
pub fn is_device_dirty(&self) -> bool {
self.is_device_dirty
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -36,16 +36,18 @@ bitflags! {
/// Traverse only elements for animation restyles.
const ANIMATION_ONLY = 0x02,
/// Traverse without generating any change hints.
const FOR_RECONSTRUCT = 0x04,
/// Traverse triggered by CSS rule changes.
/// Traverse and update all elements with CSS animations since
/// @keyframes rules may have changed
const FOR_CSS_RULE_CHANGES = 0x08,
+ /// Only include user agent style sheets when selector matching.
+ const FOR_DEFAULT_STYLES = 0x10,
}
}
impl TraversalFlags {
/// Returns true if the traversal is for animation-only restyles.
pub fn for_animation_only(&self) -> bool {
self.contains(ANIMATION_ONLY)
}
@@ -59,16 +61,22 @@ impl TraversalFlags {
pub fn for_reconstruct(&self) -> bool {
self.contains(FOR_RECONSTRUCT)
}
/// Returns true if the traversal is triggered by CSS rule changes.
pub fn for_css_rule_changes(&self) -> bool {
self.contains(FOR_CSS_RULE_CHANGES)
}
+
+ /// Returns true if the traversal is to compute the default computed
+ /// styles for an element.
+ pub fn for_default_styles(&self) -> bool {
+ self.contains(FOR_DEFAULT_STYLES)
+ }
}
/// This structure exists to enforce that callers invoke pre_traverse, and also
/// to pass information from the pre-traversal into the primary traversal.
pub struct PreTraverseToken {
traverse: bool,
unstyled_children_only: bool,
}
@@ -532,20 +540,23 @@ fn resolve_style_internal<E, F>(context:
// Compute our style.
context.thread_local.begin_element(element, &data);
element.match_and_cascade(context,
&mut data,
StyleSharingBehavior::Disallow);
context.thread_local.end_element(element);
- // Conservatively mark us as having dirty descendants, since there might
- // be other unstyled siblings we miss when walking straight up the parent
- // chain.
- unsafe { element.note_descendants::<DirtyDescendants>() };
+ if !context.shared.traversal_flags.for_default_styles() {
+ // Conservatively mark us as having dirty descendants, since there might
+ // be other unstyled siblings we miss when walking straight up the parent
+ // chain. No need to do this if we're computing default styles, since
+ // resolve_default_style will want the tree to be left as it is.
+ unsafe { element.note_descendants::<DirtyDescendants>() };
+ }
}
// If we're display:none and none of our ancestors are, we're the root
// of a display:none subtree.
if display_none_root.is_none() && data.styles().is_display_none() {
display_none_root = Some(element);
}
@@ -593,16 +604,57 @@ pub fn resolve_style<E, F, G, H>(context
curr = match curr.parent_element() {
Some(parent) => parent,
None => break,
};
}
}
}
+/// Manually resolve default styles for the given Element, which are the styles
+/// only taking into account user agent and user cascade levels. The resolved
+/// style is made available via a callback, and will be dropped by the time this
+/// function returns.
+pub fn resolve_default_style<E, F, G, H>(context: &mut StyleContext<E>,
+ element: E,
+ ensure_data: &F,
+ set_data: &G,
+ callback: H)
+ where E: TElement,
+ F: Fn(E),
+ G: Fn(E, Option<ElementData>) -> Option<ElementData>,
+ H: FnOnce(&ElementStyles)
+{
+ // Save and clear out element data from the element and its ancestors.
+ let mut old_data: Vec<(E, Option<ElementData>)> = vec![];
+ {
+ let mut e = element;
+ loop {
+ old_data.push((e, set_data(e, None)));
+ match e.parent_element() {
+ Some(parent) => e = parent,
+ None => break,
+ }
+ }
+ }
+
+ // Resolve styles up the tree.
+ resolve_style_internal(context, element, ensure_data);
+
+ // Make them available for the scope of the callback. The callee may use the
+ // argument, or perform any other processing that requires the styles to exist
+ // on the Element.
+ callback(element.borrow_data().unwrap().styles());
+
+ // Swap the old element data back into the element and its ancestors.
+ for entry in old_data {
+ set_data(entry.0, entry.1);
+ }
+}
+
/// Calculates the style for a single node.
#[inline]
#[allow(unsafe_code)]
pub fn recalc_style_at<E, D>(traversal: &D,
traversal_data: &PerLevelTraversalData,
context: &mut StyleContext<E>,
element: E,
data: &mut ElementData)
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -66,16 +66,17 @@ use style::gecko_bindings::structs;
use style::gecko_bindings::structs::{CSSPseudoElementType, CompositeOperation};
use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint};
use style::gecko_bindings::structs::Loader;
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
use style::gecko_bindings::structs::ServoElementSnapshotTable;
+use style::gecko_bindings::structs::StyleRuleInclusion;
use style::gecko_bindings::structs::URLExtraData;
use style::gecko_bindings::structs::nsCSSValueSharedList;
use style::gecko_bindings::structs::nsCompatibility;
use style::gecko_bindings::structs::nsresult;
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::gecko_properties::{self, style_structs};
@@ -95,21 +96,23 @@ use style::sequential;
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
use style::string_cache::Atom;
use style::style_adjuster::StyleAdjuster;
use style::stylearc::Arc;
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers};
use style::stylesheets::{ImportRule, KeyframesRule, MediaRule, NamespaceRule, Origin};
use style::stylesheets::{PageRule, Stylesheet, StyleRule, SupportsRule, DocumentRule};
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
+use style::stylist::RuleInclusion;
use style::supports::parse_condition_or_declaration;
use style::thread_state;
use style::timer::Timer;
-use style::traversal::{ANIMATION_ONLY, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY};
-use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
+use style::traversal::{ANIMATION_ONLY, DomTraversal, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT};
+use style::traversal::{FOR_DEFAULT_STYLES, TraversalDriver, TraversalFlags, UNSTYLED_CHILDREN_ONLY};
+use style::traversal::{resolve_style, resolve_default_style};
use style::values::{CustomIdent, KeyframesName};
use style_traits::ToCss;
use super::stylesheet_loader::StylesheetLoader;
/*
* For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
* the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
* those signatures as well, giving us a second declaration of all the Servo_* functions in this
@@ -1157,17 +1160,18 @@ pub extern "C" fn Servo_ResolvePseudoSty
};
}
let pseudo = PseudoElement::from_pseudo_type(pseudo_type)
.expect("ResolvePseudoStyle with a non-pseudo?");
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
- match get_pseudo_style(&guard, element, &pseudo, data.styles(), doc_data) {
+ match get_pseudo_style(&guard, element, &pseudo, RuleInclusion::All,
+ data.styles(), doc_data) {
Some(values) => values.into_strong(),
// FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an
// empty style inheriting from the element?
None if !is_probe => data.styles().primary.values().clone().into_strong(),
None => Strong::null(),
}
}
@@ -1189,16 +1193,17 @@ pub extern "C" fn Servo_HasAuthorSpecifi
&guards,
rule_type_mask,
author_colors_allowed)
}
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
element: GeckoElement,
pseudo: &PseudoElement,
+ rule_inclusion: RuleInclusion,
styles: &ElementStyles,
doc_data: &PerDocumentStyleData)
-> Option<Arc<ComputedValues>>
{
match pseudo.cascade_type() {
PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()),
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
PseudoElementCascadeType::Lazy => {
@@ -1208,16 +1213,17 @@ fn get_pseudo_style(guard: &SharedRwLock
} else {
styles.primary.values()
};
let guards = StylesheetGuards::same(guard);
let metrics = get_metrics_provider_for_product();
d.stylist.lazily_compute_pseudo_element_style(&guards,
&element,
&pseudo,
+ rule_inclusion,
base,
&metrics)
.map(|s| s.values().clone())
},
}
}
#[no_mangle]
@@ -2220,55 +2226,75 @@ pub extern "C" fn Servo_ResolveStyle(ele
}
data.styles().primary.values().clone().into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_type: CSSPseudoElementType,
+ rule_inclusion: StyleRuleInclusion,
snapshots: *const ServoElementSnapshotTable,
raw_data: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong
{
debug_assert!(!snapshots.is_null());
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let element = GeckoElement(element);
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
+ let rule_inclusion = RuleInclusion::from(rule_inclusion);
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
PseudoElement::from_pseudo_type(pseudo_type).and_then(|ref pseudo| {
- get_pseudo_style(&guard, element, pseudo, styles, doc_data)
+ get_pseudo_style(&guard, element, pseudo, rule_inclusion, styles, doc_data)
}).unwrap_or_else(|| styles.primary.values().clone())
};
// In the common case we already have the style. Check that before setting
- // up all the computation machinery.
- let mut result = element.mutate_data()
- .and_then(|d| d.get_styles().map(&finish));
- if result.is_some() {
- return result.unwrap().into_strong();
+ // up all the computation machinery. (Don't use it when we're getting
+ // default styles, though.)
+ if rule_inclusion == RuleInclusion::All {
+ if let Some(result) = element.mutate_data()
+ .and_then(|d| d.get_styles().map(&finish)) {
+ return result.into_strong();
+ }
}
+ let traversal_flags = match rule_inclusion {
+ RuleInclusion::All => TraversalFlags::empty(),
+ RuleInclusion::DefaultOnly => FOR_DEFAULT_STYLES,
+ };
+
// We don't have the style ready. Go ahead and compute it as necessary.
+ let mut result = None;
let data = doc_data.borrow();
let shared = create_shared_context(&global_style_data,
&guard,
&data,
- TraversalFlags::empty(),
+ traversal_flags,
unsafe { &*snapshots });
let mut tlc = ThreadLocalStyleContext::new(&shared);
let mut context = StyleContext {
shared: &shared,
thread_local: &mut tlc,
};
let ensure = |el: GeckoElement| { unsafe { el.ensure_data(); } };
- let clear = |el: GeckoElement| el.clear_data();
- resolve_style(&mut context, element, &ensure, &clear,
- |styles| result = Some(finish(styles)));
+
+ match rule_inclusion {
+ RuleInclusion::All => {
+ let clear = |el: GeckoElement| el.clear_data();
+ resolve_style(&mut context, element, &ensure, &clear,
+ |styles| result = Some(finish(styles)));
+ }
+ RuleInclusion::DefaultOnly => {
+ let set_data = |el: GeckoElement, data| { unsafe { el.set_data(data) } };
+ resolve_default_style(&mut context, element, &ensure, &set_data,
+ |styles| result = Some(finish(styles)));
+ }
+ }
result.unwrap().into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeListBorrowed,
style: ServoComputedValuesBorrowed,
parent_style: ServoComputedValuesBorrowedOrNull,