servo: Merge #16625 - SMIL support for Gecko (from birtles:smil-support); r=hiro,heycam

PR for [Gecko bug 1355348](https://bugzilla.mozilla.org/show_bug.cgi?id=1355348)

---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes do not require tests because they are tested on the Gecko side

Source-Repo: https://github.com/servo/servo
Source-Revision: 8824a68063aa4f3ca87454468f382e4d2be66487

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 936553a846e86eacae01601c1ff1f515550200c6
This commit is contained in:
Brian Birtles 2017-04-27 00:48:21 -05:00
parent cc17391557
commit 00e718c4d4
8 changed files with 89 additions and 30 deletions

View File

@ -319,6 +319,11 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
/// Get this element's style attribute.
fn style_attribute(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>>;
/// Get this element's SMIL override declarations.
fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
None
}
/// Get this element's animation rules.
fn get_animation_rules(&self, _pseudo: Option<&PseudoElement>) -> AnimationRules {
AnimationRules(None, None)

View File

@ -38,6 +38,7 @@ use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
use gecko_bindings::bindings::Gecko_GetAnimationRule;
use gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetSMILOverrideDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetStyleContext;
use gecko_bindings::bindings::Gecko_IsSignificantChild;
@ -525,6 +526,11 @@ impl<'le> TElement for GeckoElement<'le> {
declarations.map(|s| s.as_arc_opt()).unwrap_or(None)
}
fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
let declarations = unsafe { Gecko_GetSMILOverrideDeclarationBlock(self.0) };
declarations.map(|s| s.as_arc_opt()).unwrap_or(None)
}
fn get_animation_rules(&self, pseudo: Option<&PseudoElement>) -> AnimationRules {
AnimationRules(self.get_animation_rule(pseudo),
self.get_transition_rule(pseudo))

View File

@ -623,6 +623,11 @@ extern "C" {
RawServoAnimationValueMapBorrowed)
-> bool;
}
extern "C" {
pub fn Gecko_GetSMILOverrideDeclarationBlock(element:
RawGeckoElementBorrowed)
-> RawServoDeclarationBlockStrongBorrowedOrNull;
}
extern "C" {
pub fn Gecko_StyleAnimationsEquals(arg1:
RawGeckoStyleAnimationListBorrowed,

View File

@ -19,7 +19,8 @@ use dom::{AnimationRules, SendElement, TElement, TNode};
use font_metrics::FontMetricsProvider;
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint};
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint};
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::bloom::BloomFilter;
@ -882,6 +883,7 @@ pub trait MatchMethods : TElement {
let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute();
let smil_override = self.get_smil_override();
let animation_rules = self.get_animation_rules(None);
let mut rule_nodes_changed = false;
let bloom = context.thread_local.bloom_filter.filter();
@ -895,6 +897,7 @@ pub trait MatchMethods : TElement {
*relations = stylist.push_applicable_declarations(self,
Some(bloom),
style_attribute,
smil_override,
animation_rules,
None,
&context.shared.guards,
@ -952,7 +955,9 @@ pub trait MatchMethods : TElement {
};
stylist.push_applicable_declarations(self,
Some(bloom_filter),
None, pseudo_animation_rules,
None,
None,
pseudo_animation_rules,
Some(&pseudo),
&guards,
&mut applicable_declarations,
@ -1068,12 +1073,18 @@ pub trait MatchMethods : TElement {
}
};
// RESTYLE_CSS_ANIMATIONS or RESTYLE_CSS_TRANSITIONS is processed prior to other
// restyle hints in the name of animation-only traversal. Rest of restyle hints
// will be processed in a subsequent normal traversal.
// Animation restyle hints are processed prior to other restyle hints in the
// animation-only traversal. Non-animation restyle hints will be processed in
// a subsequent normal traversal.
if hint.intersects(RestyleHint::for_animations()) {
debug_assert!(context.shared.traversal_flags.for_animation_only());
if hint.contains(RESTYLE_SMIL) {
replace_rule_node(CascadeLevel::SMILOverride,
self.get_smil_override(),
primary_rules);
}
use data::EagerPseudoStyles;
let mut replace_rule_node_for_animation = |level: CascadeLevel,
primary_rules: &mut StrongRuleNode,
@ -1330,7 +1341,7 @@ pub trait MatchMethods : TElement {
let relevant_style = pseudo_style.unwrap_or(primary_style);
let rule_node = &relevant_style.rules;
let without_animation_rules =
shared_context.stylist.rule_tree.remove_animation_and_transition_rules(rule_node);
shared_context.stylist.rule_tree.remove_animation_rules(rule_node);
if without_animation_rules == *rule_node {
// Note that unwrapping here is fine, because the style is
// only incomplete during the styling process.

View File

@ -62,6 +62,11 @@ bitflags! {
/// attribute has changed, and this change didn't have any other
/// dependencies.
const RESTYLE_STYLE_ATTRIBUTE = 0x40,
/// Replace the style data coming from SMIL animations without updating
/// any other style data. This hint is only processed in animation-only
/// traversal which is prior to normal traversal.
const RESTYLE_SMIL = 0x80,
}
}
@ -95,19 +100,22 @@ pub fn assert_restyle_hints_match() {
nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
nsRestyleHint_eRestyle_StyleAttribute_Animations => RESTYLE_SMIL,
}
}
impl RestyleHint {
/// The subset hints that affect the styling of a single element during the
/// traversal.
#[inline]
pub fn for_self() -> Self {
RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
}
/// The subset hints that are used for animation restyle.
#[inline]
pub fn for_animations() -> Self {
RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
}
}

View File

@ -254,19 +254,19 @@ impl RuleTree {
path.parent().unwrap().clone()
}
/// Returns new rule node without Animations and Transitions level rules.
pub fn remove_animation_and_transition_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {
// Return a clone if there is neither animation nor transition level.
/// Returns new rule node without rules from declarative animations.
pub fn remove_animation_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {
// Return a clone if there are no animation rules.
if !path.has_animation_or_transition_rules() {
return path.clone();
}
let iter = path.self_and_ancestors().take_while(|node| node.cascade_level() >= CascadeLevel::Animations);
let iter = path.self_and_ancestors().take_while(
|node| node.cascade_level() >= CascadeLevel::SMILOverride);
let mut last = path;
let mut children = vec![];
for node in iter {
if node.cascade_level() != CascadeLevel::Animations &&
node.cascade_level() != CascadeLevel::Transitions {
if node.cascade_level().is_animation() {
children.push((node.get().source.clone().unwrap(), node.cascade_level()));
}
last = node;
@ -301,6 +301,8 @@ pub enum CascadeLevel {
AuthorNormal,
/// Style attribute normal rules.
StyleAttributeNormal,
/// SVG SMIL animations.
SMILOverride,
/// CSS animations and script-generated animations.
Animations,
/// Author-supplied important rules.
@ -333,6 +335,7 @@ impl CascadeLevel {
match *self {
CascadeLevel::Transitions |
CascadeLevel::Animations |
CascadeLevel::SMILOverride |
CascadeLevel::StyleAttributeNormal |
CascadeLevel::StyleAttributeImportant => true,
_ => false,
@ -362,6 +365,17 @@ impl CascadeLevel {
Importance::Normal
}
}
/// Returns whether this cascade level represents an animation rules.
#[inline]
pub fn is_animation(&self) -> bool {
match *self {
CascadeLevel::SMILOverride |
CascadeLevel::Animations |
CascadeLevel::Transitions => true,
_ => false,
}
}
}
struct RuleNode {
@ -780,8 +794,8 @@ impl StrongRuleNode {
/// 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::Animations)
.any(|node| matches!(node.cascade_level(), CascadeLevel::Animations | CascadeLevel::Transitions))
.take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride)
.any(|node| node.cascade_level().is_animation())
}
}

View File

@ -510,6 +510,7 @@ impl Stylist {
self.push_applicable_declarations(element,
None,
None,
None,
AnimationRules(None, None),
@ -631,6 +632,7 @@ impl Stylist {
element: &E,
parent_bf: Option<&BloomFilter>,
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules,
pseudo_element: Option<&PseudoElement>,
guards: &StylesheetGuards,
@ -711,7 +713,17 @@ impl Stylist {
debug!("style attr: {:?}", relations);
// Step 5: Animations.
// Step 5: SMIL override.
// Declarations from SVG SMIL animation elements.
if let Some(so) = smil_override {
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(so.clone(),
CascadeLevel::SMILOverride));
}
debug!("SMIL: {:?}", relations);
// Step 6: Animations.
// The animations sheet (CSS animations, script-generated animations,
// and CSS transitions that are no longer tied to CSS markup)
if let Some(anim) = animation_rules.0 {
@ -722,7 +734,7 @@ impl Stylist {
}
debug!("animation: {:?}", relations);
// Step 6: Author-supplied `!important` rules.
// Step 7: Author-supplied `!important` rules.
map.author.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
@ -732,7 +744,7 @@ impl Stylist {
debug!("author important: {:?}", relations);
// Step 7: `!important` style attributes.
// Step 8: `!important` style attributes.
if let Some(sa) = style_attribute {
if sa.read_with(guards.author).any_important() {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
@ -745,7 +757,7 @@ impl Stylist {
debug!("style attr important: {:?}", relations);
// Step 8: User `!important` rules.
// Step 9: User `!important` rules.
map.user.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
@ -758,7 +770,7 @@ impl Stylist {
debug!("skipping non-agent rules");
}
// Step 9: UA `!important` rules.
// Step 10: UA `!important` rules.
map.user_agent.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
@ -768,7 +780,7 @@ impl Stylist {
debug!("UA important: {:?}", relations);
// Step 10: Transitions.
// Step 11: Transitions.
// The transitions sheet (CSS transitions that are tied to CSS markup)
if let Some(anim) = animation_rules.1 {
Push::push(

View File

@ -1795,19 +1795,17 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
let damage = GeckoRestyleDamage::new(change_hint);
debug!("Servo_NoteExplicitHints: {:?}, restyle_hint={:?}, change_hint={:?}",
element, restyle_hint, change_hint);
debug_assert!(restyle_hint == structs::nsRestyleHint_eRestyle_CSSAnimations ||
restyle_hint == structs::nsRestyleHint_eRestyle_CSSTransitions ||
(restyle_hint.0 & (structs::nsRestyleHint_eRestyle_CSSAnimations.0 |
structs::nsRestyleHint_eRestyle_CSSTransitions.0)) == 0,
"eRestyle_CSSAnimations or eRestyle_CSSTransitions should only appear by itself");
let restyle_hint: RestyleHint = restyle_hint.into();
debug_assert!(RestyleHint::for_animations().contains(restyle_hint) ||
!RestyleHint::for_animations().intersects(restyle_hint),
"Animation restyle hints should not appear with non-animation restyle hints");
let mut maybe_data = element.mutate_data();
let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe {
maybe_restyle(d, element, restyle_hint == structs::nsRestyleHint_eRestyle_CSSAnimations ||
restyle_hint == structs::nsRestyleHint_eRestyle_CSSTransitions)
maybe_restyle(d, element, restyle_hint.intersects(RestyleHint::for_animations()))
});
if let Some(restyle_data) = maybe_restyle_data {
let restyle_hint: RestyleHint = restyle_hint.into();
restyle_data.hint.insert(&restyle_hint.into());
restyle_data.damage |= damage;
} else {