aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTing-Yu Lin <tlin@mozilla.com>2017-06-05 14:17:18 +0800
committerTing-Yu Lin <tlin@mozilla.com>2017-06-08 11:18:44 +0800
commit2411749fcfc7f97c35b5cf375498499d59e3a19e (patch)
tree13a9629c1de03ca03dc24b8bf721fe432069abb9
parentad47d33511c318fe208158bb16deb5086979e0c7 (diff)
downloadservo-2411749fcfc7f97c35b5cf375498499d59e3a19e.tar.gz
servo-2411749fcfc7f97c35b5cf375498499d59e3a19e.zip
stylo: Get rules from Gecko XBL stylesheets in cascading (Bug 1290276)
-rw-r--r--components/style/dom.rs9
-rw-r--r--components/style/gecko/wrapper.rs88
-rw-r--r--components/style/rule_tree/mod.rs5
-rw-r--r--components/style/stylist.rs47
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 {