Skip to content

Commit

Permalink
Bug 1349651 - stylo: Implement HasAuthorSpecifiedRules.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrubeck committed May 9, 2017
1 parent 121662a commit 32d3779
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 28 deletions.
1 change: 1 addition & 0 deletions components/style/build_gecko.rs
Expand Up @@ -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_.*",
Expand Down
28 changes: 28 additions & 0 deletions components/style/gecko/arc_types.rs
Expand Up @@ -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};
Expand All @@ -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};

Expand Down Expand Up @@ -75,3 +78,28 @@ impl_arc_ffi!(Locked<PageRule> => RawServoPageRule

impl_arc_ffi!(Locked<SupportsRule> => 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);
}
23 changes: 23 additions & 0 deletions components/style/gecko/generated/bindings.rs
Expand Up @@ -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<RawServoRuleNode>;
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<RawServoStyleSet>;
pub type RawServoStyleSetOwnedOrNull = ::gecko_bindings::sugar::ownership::OwnedOrNull<RawServoStyleSet>;
pub type RawServoStyleSetBorrowed<'a> = &'a RawServoStyleSet;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions components/style/gecko/generated/structs_debug.rs
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions components/style/gecko/generated/structs_release.rs
Expand Up @@ -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;
Expand Down
208 changes: 206 additions & 2 deletions components/style/rule_tree/mod.rs
Expand Up @@ -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<WeakRuleNode>,

Expand Down Expand Up @@ -648,7 +649,8 @@ impl StrongRuleNode {
}
}

fn ptr(&self) -> *mut RuleNode {
/// Raw pointer to the RuleNode
pub fn ptr(&self) -> *mut RuleNode {
self.ptr
}

Expand Down Expand Up @@ -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<E>(&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()
Expand Down

0 comments on commit 32d3779

Please sign in to comment.