Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1349651 - stylo: Implement HasAuthorSpecifiedRules. #16784

Merged
merged 1 commit into from May 9, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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_.*",
@@ -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<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);
}
@@ -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;
@@ -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,
@@ -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;
@@ -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;
@@ -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>,

@@ -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<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()
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.