Bug 1349651 - Implement has_author_rules for Servo RuleNode. r=bholley
MozReview-Commit-ID: 4SInOGSPjVX
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2229,16 +2229,17 @@ nsPresContext::HasAuthorSpecifiedRules(c
nsIAtom *pseudoTag = aFrame->StyleContext()->GetPseudo();
RefPtr<RawServoRuleNode> ruleNode;
ruleNode = mShell->StyleSet()->AsServo()->ResolveRuleNode(elem, pseudoTag);
if (!ruleNode) {
return false;
}
return Servo_HasAuthorSpecifiedRules(ruleNode,
+ elem,
ruleTypeMask,
UseDocumentColors());
}
}
gfxUserFontSet*
nsPresContext::GetUserFontSet(bool aFlushUserFontSet)
{
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -385,17 +385,18 @@ SERVO_BINDING_FUNC(Servo_ResolveStyle, S
bool allow_stale)
SERVO_BINDING_FUNC(Servo_ResolvePseudoStyle, ServoComputedValuesStrong,
RawGeckoElementBorrowed element, nsIAtom* pseudo_tag,
bool is_probe, RawServoStyleSetBorrowed set)
SERVO_BINDING_FUNC(Servo_ResolveRuleNode, RawServoRuleNodeStrong,
RawGeckoElementBorrowed element, nsIAtom* pseudo_tag,
RawServoStyleSetBorrowed set)
SERVO_BINDING_FUNC(Servo_HasAuthorSpecifiedRules, bool,
- RawServoRuleNodeBorrowed rules,
+ RawServoRuleNodeBorrowed rule_node,
+ RawGeckoElementBorrowed element,
uint32_t rule_type_mask,
bool author_colors_allowed)
// Resolves style for an element or pseudo-element without processing pending
// restyles first. The Element and its ancestors may be unstyled, have pending
// restyles, or be in a display:none subtree. Styles are cached when possible,
// though caching is not possible within display:none subtrees, and the styles
// may be invalidated by already-scheduled restyles.
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -787,21 +787,190 @@ impl StrongRuleNode {
unsafe fn maybe_gc(&self) {
debug_assert!(self.get().is_root(), "Can't call GC on a non-root node!");
if self.get().free_count.load(Ordering::Relaxed) > RULE_TREE_GC_INTERVAL {
self.gc();
}
}
- /// TODO: docs
- pub fn has_author_specified_rules(&self,
- _rule_type_mask: u32,
- _author_colors_allowed: bool) -> bool {
- true // TODO
+ /// Implementation of `nsRuleNode::HasAuthorSpecifiedRules` for Servo rule nodes.
+ ///
+ /// Returns true if any properties specified by `rule_type_mask` was set by an author rule.
+ #[cfg(feature = "gecko")]
+ pub fn has_author_specified_rules<E>(&self,
+ mut element: E,
+ guards: &StylesheetGuards,
+ rule_type_mask: u32,
+ _author_colors_allowed: bool)
+ -> bool
+ where E: ::dom::TElement
+ {
+ use properties::{CSSWideKeyword, LonghandId, LonghandIdSet, PropertyDeclarationId};
+ use std::borrow::Cow;
+ use gecko_bindings::structs::{NS_AUTHOR_SPECIFIED_BACKGROUND, NS_AUTHOR_SPECIFIED_BORDER};
+ use gecko_bindings::structs::{NS_AUTHOR_SPECIFIED_PADDING, NS_AUTHOR_SPECIFIED_TEXT_SHADOW};
+
+ // Reset properties:
+ const BACKGROUND_PROPS: &'static [LonghandId] = &[
+ LonghandId::BackgroundColor,
+ LonghandId::BackgroundImage,
+ ];
+
+ const BORDER_PROPS: &'static [LonghandId] = &[
+ LonghandId::BorderTopColor,
+ LonghandId::BorderTopStyle,
+ LonghandId::BorderTopWidth,
+ LonghandId::BorderRightColor,
+ LonghandId::BorderRightStyle,
+ LonghandId::BorderRightWidth,
+ LonghandId::BorderBottomColor,
+ LonghandId::BorderBottomStyle,
+ LonghandId::BorderBottomWidth,
+ LonghandId::BorderLeftColor,
+ LonghandId::BorderLeftStyle,
+ LonghandId::BorderLeftWidth,
+ LonghandId::BorderTopLeftRadius,
+ LonghandId::BorderTopRightRadius,
+ LonghandId::BorderBottomRightRadius,
+ LonghandId::BorderBottomLeftRadius,
+ ];
+
+ const PADDING_PROPS: &'static [LonghandId] = &[
+ LonghandId::PaddingTop,
+ LonghandId::PaddingRight,
+ LonghandId::PaddingBottom,
+ LonghandId::PaddingLeft,
+ ];
+
+ // Inherited properties:
+ const TEXT_SHADOW_PROPS: &'static [LonghandId] = &[
+ LonghandId::TextShadow,
+ ];
+
+ fn inherited(id: LonghandId) -> bool {
+ id == LonghandId::TextShadow
+ }
+
+ // Set of properties that we are currently interested in.
+ let mut properties = LonghandIdSet::new();
+
+ if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
+ for id in BACKGROUND_PROPS {
+ properties.insert(*id);
+ }
+ }
+ if rule_type_mask & NS_AUTHOR_SPECIFIED_BORDER != 0 {
+ for id in BORDER_PROPS {
+ properties.insert(*id);
+ }
+ }
+ if rule_type_mask & NS_AUTHOR_SPECIFIED_PADDING != 0 {
+ for id in PADDING_PROPS {
+ properties.insert(*id);
+ }
+ }
+ if rule_type_mask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW != 0 {
+ for id in TEXT_SHADOW_PROPS {
+ properties.insert(*id);
+ }
+ }
+
+ let mut element_rule_node = Cow::Borrowed(self);
+
+ loop {
+ // We need to be careful not to count styles covered up by user-important or
+ // UA-important declarations. But we do want to catch explicit inherit styling in
+ // those and check our parent element to see whether we have user styling for
+ // those properties. Note that we don't care here about inheritance due to lack of
+ // a specified value, since all the properties we care about are reset properties.
+ //
+ // FIXME: The above comment is copied from Gecko, but the last sentence is no longer
+ // correct since 'text-shadow' support was added. This is a bug in Gecko, replicated
+ // in Stylo for now: https://bugzilla.mozilla.org/show_bug.cgi?id=1363088
+
+ let mut inherited_properties = LonghandIdSet::new();
+ let mut have_explicit_ua_inherit = false;
+
+ for node in element_rule_node.self_and_ancestors() {
+ let declarations = match node.style_source() {
+ Some(source) => source.read(node.cascade_level().guard(guards)).declarations(),
+ None => continue
+ };
+
+ // Iterate over declarations of the longhands we care about.
+ let node_importance = node.importance();
+ let longhands = declarations.iter().rev()
+ .filter_map(|&(ref declaration, importance)| {
+ if importance != node_importance { return None }
+ match declaration.id() {
+ PropertyDeclarationId::Longhand(id) => {
+ Some((id, declaration))
+ }
+ _ => None
+ }
+ });
+
+ match node.cascade_level() {
+ // Non-author rules:
+ CascadeLevel::UANormal |
+ CascadeLevel::UAImportant |
+ CascadeLevel::UserNormal |
+ CascadeLevel::UserImportant => {
+ for (id, declaration) in longhands {
+ if properties.contains(id) {
+ // This property was set by a non-author rule. Stop looking for it in
+ // this element's rule nodes.
+ properties.remove(id);
+
+ // However, if it is inherited, then it might be inherited from an
+ // author rule from an ancestor element's rule nodes.
+ if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) ||
+ (declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Unset) &&
+ inherited(id))
+ {
+ have_explicit_ua_inherit = true;
+ inherited_properties.insert(id);
+ }
+ }
+ }
+ }
+ // Author rules:
+ CascadeLevel::PresHints |
+ CascadeLevel::AuthorNormal |
+ CascadeLevel::StyleAttributeNormal |
+ CascadeLevel::SMILOverride |
+ CascadeLevel::Animations |
+ CascadeLevel::AuthorImportant |
+ CascadeLevel::StyleAttributeImportant |
+ CascadeLevel::Transitions => {
+ for (id, _) in longhands {
+ if properties.contains(id) {
+ return true
+ }
+ }
+ }
+ }
+ }
+
+ if !have_explicit_ua_inherit { break }
+
+ // Continue to the parent element and search for the inherited properties.
+ element = match element.parent_element() {
+ Some(parent) => parent,
+ None => break
+ };
+ let parent_data = element.mutate_data().unwrap();
+ let parent_rule_node = parent_data.styles().primary.rules.clone();
+ element_rule_node = Cow::Owned(parent_rule_node);
+
+ properties = inherited_properties;
+ }
+
+ false
}
/// Returns true if there is either animation or transition level rule.
pub fn has_animation_or_transition_rules(&self) -> bool {
self.self_and_ancestors()
.take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride)
.any(|node| node.cascade_level().is_animation())
}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -996,22 +996,29 @@ pub extern "C" fn Servo_ResolvePseudoSty
Some(values) => values.into_strong(),
None if !is_probe => data.styles().primary.values().clone().into_strong(),
None => Strong::null(),
}
}
#[no_mangle]
pub extern "C" fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed,
+ element: RawGeckoElementBorrowed,
rule_type_mask: u32,
author_colors_allowed: bool)
-> bool
{
- StrongRuleNode::from_ffi(&rule_node)
- .has_author_specified_rules(rule_type_mask, author_colors_allowed)
+ let element = GeckoElement(element);
+ let guard = (*GLOBAL_STYLE_DATA).shared_lock.read();
+ let guards = StylesheetGuards::same(&guard);
+
+ StrongRuleNode::from_ffi(&rule_node).has_author_specified_rules(element,
+ &guards,
+ rule_type_mask,
+ author_colors_allowed)
}
fn get_pseudo_rule_node(guard: &SharedRwLockReadGuard,
element: GeckoElement,
pseudo_tag: *mut nsIAtom,
styles: &ElementStyles,
doc_data: &PerDocumentStyleData)
-> Option<StrongRuleNode>