diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 3b80bd33819..eacee6b3633 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -326,6 +326,7 @@ mod bindings { .constified_enum("UpdateAnimationsTasks") .parse_callbacks(Box::new(Callbacks)); let whitelist_vars = [ + "NS_AUTHOR_SPECIFIED_.*", "NS_THEME_.*", "NODE_.*", "NS_FONT_.*", diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index f9818ba0eb5..6e2e7d215ad 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -9,6 +9,7 @@ #![allow(non_snake_case, missing_docs)] use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule}; +use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong}; use gecko_bindings::bindings::{RawServoStyleSheet, RawServoImportRule, RawServoSupportsRule}; use gecko_bindings::bindings::{ServoComputedValues, ServoCssRules}; use gecko_bindings::structs::{RawServoDeclarationBlock, RawServoStyleRule}; @@ -17,7 +18,9 @@ use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI}; use media_queries::MediaList; use properties::{ComputedValues, PropertyDeclarationBlock}; use properties::animated_properties::AnimationValue; +use rule_tree::StrongRuleNode; use shared_lock::Locked; +use std::{mem, ptr}; use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, MediaRule}; use stylesheets::{NamespaceRule, PageRule, SupportsRule}; @@ -75,3 +78,28 @@ impl_arc_ffi!(Locked => RawServoPageRule impl_arc_ffi!(Locked => RawServoSupportsRule [Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]); + +// RuleNode is a Arc-like type but it does not use Arc. + +impl StrongRuleNode { + pub fn into_strong(self) -> RawServoRuleNodeStrong { + let ptr = self.ptr(); + mem::forget(self); + unsafe { mem::transmute(ptr) } + } + + pub fn from_ffi<'a>(ffi: &'a &RawServoRuleNode) -> &'a Self { + unsafe { &*(ffi as *const &RawServoRuleNode as *const StrongRuleNode) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn Servo_RuleNode_AddRef(obj: &RawServoRuleNode) { + mem::forget(StrongRuleNode::from_ffi(&obj).clone()); +} + +#[no_mangle] +pub unsafe extern "C" fn Servo_RuleNode_Release(obj: &RawServoRuleNode) { + let ptr = StrongRuleNode::from_ffi(&obj); + ptr::read(ptr as *const StrongRuleNode); +} diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index fdd7d7c7ac8..8545f3c8a77 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -250,6 +250,11 @@ pub type RawServoSupportsRuleBorrowed<'a> = &'a RawServoSupportsRule; pub type RawServoSupportsRuleBorrowedOrNull<'a> = Option<&'a RawServoSupportsRule>; enum RawServoSupportsRuleVoid { } pub struct RawServoSupportsRule(RawServoSupportsRuleVoid); +pub type RawServoRuleNodeStrong = ::gecko_bindings::sugar::ownership::Strong; +pub type RawServoRuleNodeBorrowed<'a> = &'a RawServoRuleNode; +pub type RawServoRuleNodeBorrowedOrNull<'a> = Option<&'a RawServoRuleNode>; +enum RawServoRuleNodeVoid { } +pub struct RawServoRuleNode(RawServoRuleNodeVoid); pub type RawServoStyleSetOwned = ::gecko_bindings::sugar::ownership::Owned; pub type RawServoStyleSetOwnedOrNull = ::gecko_bindings::sugar::ownership::OwnedOrNull; pub type RawServoStyleSetBorrowed<'a> = &'a RawServoStyleSet; @@ -411,6 +416,12 @@ extern "C" { extern "C" { pub fn Servo_SupportsRule_Release(ptr: RawServoSupportsRuleBorrowed); } +extern "C" { + pub fn Servo_RuleNode_AddRef(ptr: RawServoRuleNodeBorrowed); +} +extern "C" { + pub fn Servo_RuleNode_Release(ptr: RawServoRuleNodeBorrowed); +} extern "C" { pub fn Servo_StyleSet_Drop(ptr: RawServoStyleSetOwned); } @@ -2183,6 +2194,18 @@ extern "C" { set: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong; } +extern "C" { + pub fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed, + pseudo_tag: *mut nsIAtom, + set: RawServoStyleSetBorrowed) + -> RawServoRuleNodeStrong; +} +extern "C" { + pub fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed, + element: RawGeckoElementBorrowed, + rule_type_mask: u32, + author_colors_allowed: bool) -> bool; +} extern "C" { pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, pseudo_tag: *mut nsIAtom, diff --git a/components/style/gecko/generated/structs_debug.rs b/components/style/gecko/generated/structs_debug.rs index 7ac6548e1fd..4f5b7aa1533 100644 --- a/components/style/gecko/generated/structs_debug.rs +++ b/components/style/gecko/generated/structs_debug.rs @@ -1126,6 +1126,10 @@ pub mod root { pub const kNameSpaceID_disabled_SVG: ::std::os::raw::c_uint = 12; pub const kNameSpaceID_LastBuiltin: ::std::os::raw::c_uint = 12; pub const kNameSpaceID_Wildcard: ::std::os::raw::c_int = -2147483648; + pub const NS_AUTHOR_SPECIFIED_BACKGROUND: ::std::os::raw::c_uint = 1; + pub const NS_AUTHOR_SPECIFIED_BORDER: ::std::os::raw::c_uint = 2; + pub const NS_AUTHOR_SPECIFIED_PADDING: ::std::os::raw::c_uint = 4; + pub const NS_AUTHOR_SPECIFIED_TEXT_SHADOW: ::std::os::raw::c_uint = 8; pub const NS_STYLE_INHERIT_MASK: ::std::os::raw::c_uint = 16777215; pub const NS_STYLE_HAS_TEXT_DECORATION_LINES: ::std::os::raw::c_uint = 16777216; diff --git a/components/style/gecko/generated/structs_release.rs b/components/style/gecko/generated/structs_release.rs index 6abecf98e09..a79784bd970 100644 --- a/components/style/gecko/generated/structs_release.rs +++ b/components/style/gecko/generated/structs_release.rs @@ -1126,6 +1126,10 @@ pub mod root { pub const kNameSpaceID_disabled_SVG: ::std::os::raw::c_uint = 12; pub const kNameSpaceID_LastBuiltin: ::std::os::raw::c_uint = 12; pub const kNameSpaceID_Wildcard: ::std::os::raw::c_int = -2147483648; + pub const NS_AUTHOR_SPECIFIED_BACKGROUND: ::std::os::raw::c_uint = 1; + pub const NS_AUTHOR_SPECIFIED_BORDER: ::std::os::raw::c_uint = 2; + pub const NS_AUTHOR_SPECIFIED_PADDING: ::std::os::raw::c_uint = 4; + pub const NS_AUTHOR_SPECIFIED_TEXT_SHADOW: ::std::os::raw::c_uint = 8; pub const NS_STYLE_INHERIT_MASK: ::std::os::raw::c_uint = 16777215; pub const NS_STYLE_HAS_TEXT_DECORATION_LINES: ::std::os::raw::c_uint = 16777216; diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 0947198e2da..904bf0bb1a0 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -377,7 +377,8 @@ impl CascadeLevel { } } -struct RuleNode { +/// A node in the rule tree. +pub struct RuleNode { /// The root node. Only the root has no root pointer, for obvious reasons. root: Option, @@ -648,7 +649,8 @@ impl StrongRuleNode { } } - fn ptr(&self) -> *mut RuleNode { + /// Raw pointer to the RuleNode + pub fn ptr(&self) -> *mut RuleNode { self.ptr } @@ -790,6 +792,208 @@ impl StrongRuleNode { } } + /// 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(&self, + mut element: E, + guards: &StylesheetGuards, + rule_type_mask: u32, + author_colors_allowed: bool) + -> bool + where E: ::dom::TElement + { + use cssparser::RGBA; + 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}; + use properties::{CSSWideKeyword, LonghandId, LonghandIdSet}; + use properties::{PropertyDeclaration, PropertyDeclarationId}; + use std::borrow::Cow; + use values::specified::Color; + + // 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); + } + } + + // If author colors are not allowed, only claim to have author-specified rules if we're + // looking at a non-color property or if we're looking at the background color and it's + // set to transparent. + const IGNORED_WHEN_COLORS_DISABLED: &'static [LonghandId] = &[ + LonghandId::BackgroundImage, + LonghandId::BorderTopColor, + LonghandId::BorderRightColor, + LonghandId::BorderBottomColor, + LonghandId::BorderLeftColor, + LonghandId::TextShadow, + ]; + + if !author_colors_allowed { + for id in IGNORED_WHEN_COLORS_DISABLED { + properties.remove(*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, declaration) in longhands { + if properties.contains(id) { + if !author_colors_allowed { + if let PropertyDeclaration::BackgroundColor(ref color) = *declaration { + return color.parsed == Color::RGBA(RGBA::transparent()) + } + } + 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() diff --git a/components/style/stylist.rs b/components/style/stylist.rs index e693a1d24e7..28d05589bbf 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -491,14 +491,47 @@ impl Stylist { where E: TElement + fmt::Debug + PresentationalHintsSynthetizer + { + let rule_node = match self.lazy_pseudo_rules(guards, element, pseudo) { + 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"). + let computed = + properties::cascade(&self.device, + &rule_node, + guards, + Some(&**parent), + Some(&**parent), + None, + &RustLogReporter, + font_metrics, + CascadeFlags::empty(), + self.quirks_mode); + + Some(ComputedStyle::new(rule_node, Arc::new(computed))) + } + + /// 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(&self, + guards: &StylesheetGuards, + element: &E, + pseudo: &PseudoElement) + -> Option + where E: TElement + fmt::Debug + PresentationalHintsSynthetizer { debug_assert!(pseudo.is_lazy()); if self.pseudos_map.get(pseudo).is_none() { - return None; + return None } - let mut declarations = vec![]; - // Apply the selector flags. We should be in sequential mode // already, so we can directly apply the parent flags. let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| { @@ -524,6 +557,7 @@ impl Stylist { }; + let mut declarations = vec![]; self.push_applicable_declarations(element, None, None, @@ -533,32 +567,14 @@ impl Stylist { guards, &mut declarations, &mut set_selector_flags); - if declarations.is_empty() { return None } - let rule_node = - self.rule_tree.insert_ordered_rules( - declarations.into_iter().map(|a| (a.source, a.level))); - - // 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, - &rule_node, - guards, - Some(&**parent), - Some(&**parent), - None, - &RustLogReporter, - font_metrics, - CascadeFlags::empty(), - self.quirks_mode); - - Some(ComputedStyle::new(rule_node, Arc::new(computed))) + let rule_node = self.rule_tree.insert_ordered_rules(declarations.into_iter().map(|a| { + (a.source, a.level) + })); + Some(rule_node) } /// Set a given device, which may change the styles that apply to the diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index c0d41d03ca3..4601d31f683 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -32,6 +32,7 @@ use style::gecko_bindings::bindings::{RawServoMediaList, RawServoMediaListBorrow use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed}; +use style::gecko_bindings::bindings::{RawServoRuleNodeBorrowed, RawServoRuleNodeStrong}; use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned}; use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong}; @@ -77,7 +78,7 @@ use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP; use style::properties::animated_properties::{AnimationValue, ComputeDistance, Interpolate, TransitionProperty}; use style::properties::parse_one_declaration; use style::restyle_hints::{self, RestyleHint}; -use style::rule_tree::StyleSource; +use style::rule_tree::{StrongRuleNode, StyleSource}; use style::selector_parser::PseudoElementCascadeType; use style::sequential; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked}; @@ -938,6 +939,37 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: .into_strong() } +#[no_mangle] +pub extern "C" fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed, + pseudo_tag: *mut nsIAtom, + raw_data: RawServoStyleSetBorrowed) + -> RawServoRuleNodeStrong +{ + let element = GeckoElement(element); + let doc_data = PerDocumentStyleData::from_ffi(raw_data); + let guard = (*GLOBAL_STYLE_DATA).shared_lock.read(); + + let data = element.mutate_data().unwrap(); + let styles = match data.get_styles() { + Some(styles) => styles, + None => { + warn!("Calling Servo_ResolveRuleNode on unstyled element"); + return Strong::null() + } + }; + + let maybe_rules = if pseudo_tag.is_null() { + Some(styles.primary.rules.clone()) + } else { + get_pseudo_rule_node(&guard, element, pseudo_tag, styles, doc_data) + }; + + match maybe_rules { + Some(rule_node) => rule_node.into_strong(), + None => Strong::null(), + } +} + #[no_mangle] pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, pseudo_tag: *mut nsIAtom, is_probe: bool, @@ -967,6 +999,42 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, } } +#[no_mangle] +pub extern "C" fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed, + element: RawGeckoElementBorrowed, + rule_type_mask: u32, + author_colors_allowed: bool) + -> bool +{ + 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 +{ + let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false); + match pseudo.cascade_type() { + PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.rules.clone()), + PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"), + PseudoElementCascadeType::Lazy => { + let d = doc_data.borrow_mut(); + let guards = StylesheetGuards::same(guard); + d.stylist.lazy_pseudo_rules(&guards, &element, &pseudo) + }, + } +} + fn get_pseudo_style(guard: &SharedRwLockReadGuard, element: GeckoElement, pseudo_tag: *mut nsIAtom,