mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
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:
parent
cc17391557
commit
00e718c4d4
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user