diff options
author | Ting-Yu Lin <tlin@mozilla.com> | 2017-06-05 14:17:18 +0800 |
---|---|---|
committer | Ting-Yu Lin <tlin@mozilla.com> | 2017-06-08 11:18:44 +0800 |
commit | 2411749fcfc7f97c35b5cf375498499d59e3a19e (patch) | |
tree | 13a9629c1de03ca03dc24b8bf721fe432069abb9 | |
parent | ad47d33511c318fe208158bb16deb5086979e0c7 (diff) | |
download | servo-2411749fcfc7f97c35b5cf375498499d59e3a19e.tar.gz servo-2411749fcfc7f97c35b5cf375498499d59e3a19e.zip |
stylo: Get rules from Gecko XBL stylesheets in cascading (Bug 1290276)
-rw-r--r-- | components/style/dom.rs | 9 | ||||
-rw-r--r-- | components/style/gecko/wrapper.rs | 88 | ||||
-rw-r--r-- | components/style/rule_tree/mod.rs | 5 | ||||
-rw-r--r-- | components/style/stylist.rs | 47 |
4 files changed, 138 insertions, 11 deletions
diff --git a/components/style/dom.rs b/components/style/dom.rs index 64e11b2faf0..870690c2027 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -22,6 +22,7 @@ use selector_parser::{PseudoClassStringArg, PseudoElement}; use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode}; use shared_lock::Locked; use sink::Push; +use smallvec::VecLike; use std::fmt; #[cfg(feature = "gecko")] use std::collections::HashMap; use std::fmt::Debug; @@ -562,6 +563,14 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + .map_or(false, |r| r.hint.has_animation_hint()); } + /// Gets declarations from XBL bindings from the element. Only gecko element could have this. + fn get_declarations_from_xbl_bindings<V>(&self, + _: &mut V) + -> bool + where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> { + false + } + /// Gets the current existing CSS transitions, by |property, end value| pairs in a HashMap. #[cfg(feature = "gecko")] fn get_css_transitions_info(&self) diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index cc4411ffaf5..ba346d671c7 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -23,6 +23,7 @@ use dom::{OpaqueNode, PresentationalHintsSynthesizer}; use element_state::ElementState; use error_reporting::RustLogReporter; use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult}; +use gecko::data::PerDocumentStyleData; use gecko::global_style_data::GLOBAL_STYLE_DATA; use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement}; use gecko::snapshot_helpers; @@ -50,7 +51,7 @@ use gecko_bindings::bindings::Gecko_MatchStringArgPseudo; use gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr; use gecko_bindings::bindings::Gecko_UpdateAnimations; use gecko_bindings::structs; -use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode}; +use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode, RawGeckoXBLBinding}; use gecko_bindings::structs::{nsIAtom, nsIContent, nsINode_BooleanFlag, nsStyleContext}; use gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT; use gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO; @@ -59,7 +60,7 @@ use gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT; use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel; use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS; -use gecko_bindings::sugar::ownership::HasArcFFI; +use gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI}; use logical_geometry::WritingMode; use media_queries::Device; use properties::{ComputedValues, parse_style_attribute}; @@ -74,6 +75,7 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode}; use shared_lock::Locked; use sink::Push; +use smallvec::VecLike; use std::cell::RefCell; use std::collections::HashMap; use std::fmt; @@ -202,6 +204,12 @@ impl<'ln> GeckoNode<'ln> { true } + /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree. + fn is_root_of_native_anonymous_subtree(&self) -> bool { + use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT; + return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0 + } + fn contains_non_whitespace_content(&self) -> bool { unsafe { Gecko_IsSignificantChild(self.0, true, false) } } @@ -341,6 +349,37 @@ impl<'a> Iterator for GeckoChildrenIterator<'a> { } } +/// A Simple wrapper over a non-null Gecko `nsXBLBinding` pointer. +pub struct GeckoXBLBinding<'lb>(pub &'lb RawGeckoXBLBinding); + +impl<'lb> GeckoXBLBinding<'lb> { + fn base_binding(&self) -> Option<GeckoXBLBinding> { + unsafe { self.0.mNextBinding.mRawPtr.as_ref().map(GeckoXBLBinding) } + } + + fn inherits_style(&self) -> bool { + unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) } + } + + // Implements Gecko's nsXBLBinding::WalkRules(). + fn get_declarations_for<E, V>(&self, + element: &E, + applicable_declarations: &mut V) + where E: TElement, + V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> { + if let Some(base_binding) = self.base_binding() { + base_binding.get_declarations_for(element, applicable_declarations); + } + + let raw_data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyleSet(self.0) }; + if let Some(raw_data) = raw_data { + let data = PerDocumentStyleData::from_ffi(&*raw_data).borrow(); + data.stylist.push_applicable_declarations_as_xbl_only_stylist(element, + applicable_declarations); + } + } +} + /// A simple wrapper over a non-null Gecko `Element` pointer. #[derive(Clone, Copy)] pub struct GeckoElement<'le>(pub &'le RawGeckoElement); @@ -394,6 +433,14 @@ impl<'le> GeckoElement<'le> { unsafe { slots.as_ref() } } + fn get_xbl_binding(&self) -> Option<GeckoXBLBinding> { + unsafe { bindings::Gecko_GetXBLBinding(self.0).map(GeckoXBLBinding) } + } + + fn get_xbl_binding_parent(&self) -> Option<Self> { + unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) } + } + /// Clear the element data for a given element. pub fn clear_data(&self) { let ptr = self.0.mServoData.get(); @@ -857,6 +904,43 @@ impl<'le> TElement for GeckoElement<'le> { self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) } } + // Implements Gecko's nsBindingManager::WalkRules(). Returns whether to cut off the + // inheritance. + fn get_declarations_from_xbl_bindings<V>(&self, + applicable_declarations: &mut V) + -> bool + where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> { + // Walk the binding scope chain, starting with the binding attached to our content, up + // till we run out of scopes or we get cut off. + let mut current = Some(*self); + + while let Some(element) = current { + if let Some(binding) = element.get_xbl_binding() { + binding.get_declarations_for(self, applicable_declarations); + + // If we're not looking at our original element, allow the binding to cut off + // style inheritance. + if element != *self { + if !binding.inherits_style() { + // Go no further; we're not inheriting style from anything above here. + break; + } + } + } + + if element.as_node().is_root_of_native_anonymous_subtree() { + // Deliberately cut off style inheritance here. + break; + } + + current = element.get_xbl_binding_parent(); + } + + // If current has something, this means we cut off inheritance at some point in the + // loop. + current.is_some() + } + fn get_css_transitions_info(&self) -> HashMap<TransitionProperty, Arc<AnimationValue>> { use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt; diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 37e7334b9db..4e2a7ca61ec 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -406,6 +406,8 @@ pub enum CascadeLevel { PresHints, /// User normal rules. UserNormal, + /// XBL <stylesheet> rules. + XBL, /// Author normal rules. AuthorNormal, /// Style attribute normal rules. @@ -1049,7 +1051,8 @@ impl StrongRuleNode { CascadeLevel::UANormal | CascadeLevel::UAImportant | CascadeLevel::UserNormal | - CascadeLevel::UserImportant => { + CascadeLevel::UserImportant | + CascadeLevel::XBL => { for (id, declaration) in longhands { if properties.contains(id) { // This property was set by a non-author rule. diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 43b29c0275d..8844216e7fd 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -917,6 +917,26 @@ impl Stylist { self.quirks_mode = quirks_mode; } + /// Returns the applicable CSS declarations for the given element by + /// treating us as an XBL stylesheet-only stylist. + pub fn push_applicable_declarations_as_xbl_only_stylist<E, V>(&self, + element: &E, + applicable_declarations: &mut V) + where E: TElement, + V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>, + { + let mut matching_context = + MatchingContext::new(MatchingMode::Normal, None); + let mut dummy_flag_setter = |_: &E, _: ElementSelectorFlags| {}; + + self.element_map.author.get_all_matching_rules(element, + element, + applicable_declarations, + &mut matching_context, + &mut dummy_flag_setter, + CascadeLevel::XBL); + } + /// Returns the applicable CSS declarations for the given element. /// /// This corresponds to `ElementRuleCollector` in WebKit. @@ -1019,15 +1039,26 @@ impl Stylist { debug!("skipping user rules"); } + // Step 3b: XBL rules. + let cut_off_inheritance = + rule_hash_target.get_declarations_from_xbl_bindings(applicable_declarations); + debug!("XBL: {:?}", context.relations); + 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); + // Gecko skips author normal rules if cutting off inheritance. + // See nsStyleSet::FileRules(). + if !cut_off_inheritance { + // Step 3c: 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); + } else { + debug!("Skipping author normal rules due to cut off inheritance"); + } // Step 4: Normal style attributes. if let Some(sa) = style_attribute { |