servo: Merge #17839 - style: allow out-of-range opacity values for SMIL animations (from heycam:opacity-smil); r=hiro
authorCameron McCormack <cam@mcc.id.au>
Mon, 24 Jul 2017 19:13:08 -0700
changeset 419407 85a14d31d9d0028c0e0aaa9b68e9b77efe2d9010
parent 419406 13e18ee3e49a9d49fb66ce6e309fdc329526f96b
child 419408 26d556a0f07ccd041ff5e28a53a3c7e38b4c2802
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #17839 - style: allow out-of-range opacity values for SMIL animations (from heycam:opacity-smil); r=hiro From https://bugzilla.mozilla.org/show_bug.cgi?id=1371150. Source-Repo: https://github.com/servo/servo Source-Revision: bf16b146e8edbf3e3513188fe495354546160988
servo/components/style/gecko/media_queries.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/servo/media_queries.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/mod.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/parsing/mod.rs
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -645,16 +645,17 @@ impl Expression {
         let context = computed::Context {
             is_root_element: false,
             builder: StyleBuilder::for_derived_style(device, default_values, None, None),
             font_metrics_provider: &provider,
             cached_system_font: None,
             in_media_query: true,
             // TODO: pass the correct value here.
             quirks_mode: quirks_mode,
+            for_smil_animation: false,
         };
 
         let required_value = match self.value {
             Some(ref v) => v,
             None => {
                 // If there's no value, always match unless it's a zero length
                 // or a zero integer or boolean.
                 return match *actual_value {
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2991,16 +2991,17 @@ where
             inherited_style.font_computation_data.font_size_keyword,
             ComputedValueFlags::empty(),
             visited_style,
         ),
         font_metrics_provider: font_metrics_provider,
         cached_system_font: None,
         in_media_query: false,
         quirks_mode: quirks_mode,
+        for_smil_animation: false,
     };
 
     let ignore_colors = !device.use_document_colors();
     let default_background_color_decl = if ignore_colors {
         let color = device.default_background_color();
         Some(PropertyDeclaration::BackgroundColor(color.into()))
     } else {
         None
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -241,16 +241,17 @@ impl Range<specified::Length> {
             builder: StyleBuilder::for_derived_style(device, default_values, None, None),
             // Servo doesn't support font metrics
             // A real provider will be needed here once we do; since
             // ch units can exist in media queries.
             font_metrics_provider: &ServoMetricsProvider,
             in_media_query: true,
             cached_system_font: None,
             quirks_mode: quirks_mode,
+            for_smil_animation: false,
         };
 
         match *self {
             Range::Min(ref width) => Range::Min(width.to_computed_value(&context)),
             Range::Max(ref width) => Range::Max(width.to_computed_value(&context)),
             Range::Eq(ref width) => Range::Eq(width.to_computed_value(&context))
         }
     }
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -704,16 +704,17 @@ impl MaybeNew for ViewportConstraints {
 
         let context = Context {
             is_root_element: false,
             builder: StyleBuilder::for_derived_style(device, default_values, None, None),
             font_metrics_provider: &provider,
             cached_system_font: None,
             in_media_query: false,
             quirks_mode: quirks_mode,
+            for_smil_animation: false,
         };
 
         // DEVICE-ADAPT ยง 9.3 Resolving 'extend-to-zoom'
         let extend_width;
         let extend_height;
         if let Some(extend_zoom) = max!(initial_zoom, max_zoom) {
             let scale_factor = 1. / extend_zoom;
             extend_width = Some(initial_viewport.width.scale_by(scale_factor));
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -90,16 +90,22 @@ pub struct Context<'a> {
     /// font-relative units.
     pub font_metrics_provider: &'a FontMetricsProvider,
 
     /// Whether or not we are computing the media list in a media query
     pub in_media_query: bool,
 
     /// The quirks mode of this context.
     pub quirks_mode: QuirksMode,
+
+    /// Whether this computation is being done for a SMIL animation.
+    ///
+    /// This is used to allow certain properties to generate out-of-range
+    /// values, which SMIL allows.
+    pub for_smil_animation: bool,
 }
 
 impl<'a> Context<'a> {
     /// Whether the current element is the root element.
     pub fn is_root_element(&self) -> bool {
         self.is_root_element
     }
 
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -561,17 +561,24 @@ impl Parse for Opacity {
     }
 }
 
 impl ToComputedValue for Opacity {
     type ComputedValue = CSSFloat;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> CSSFloat {
-        self.0.to_computed_value(context).min(1.0).max(0.0)
+        let value = self.0.to_computed_value(context);
+        if context.for_smil_animation {
+            // SMIL expects to be able to interpolate between out-of-range
+            // opacity values.
+            value
+        } else {
+            value.min(1.0).max(0.0)
+        }
     }
 
     #[inline]
     fn from_computed_value(computed: &CSSFloat) -> Self {
         Opacity(Number::from_computed_value(computed))
     }
 }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -2902,29 +2902,31 @@ fn simulate_compute_values_failure(_: &P
 }
 
 fn create_context<'a>(
     per_doc_data: &'a PerDocumentStyleDataImpl,
     font_metrics_provider: &'a FontMetricsProvider,
     style: &'a ComputedValues,
     parent_style: Option<&'a ComputedValues>,
     pseudo: Option<&'a PseudoElement>,
+    for_smil_animation: bool,
 ) -> Context<'a> {
     Context {
         is_root_element: false,
         builder: StyleBuilder::for_derived_style(
             per_doc_data.stylist.device(),
             style,
             parent_style,
             pseudo,
         ),
         font_metrics_provider: font_metrics_provider,
         cached_system_font: None,
         in_media_query: false,
         quirks_mode: per_doc_data.stylist.quirks_mode(),
+        for_smil_animation,
     }
 }
 
 struct PropertyAndIndex {
     property: PropertyId,
     index: usize,
 }
 
@@ -2984,17 +2986,24 @@ pub extern "C" fn Servo_GetComputedKeyfr
     let metrics = get_metrics_provider_for_product();
 
     let element = GeckoElement(element);
     let parent_element = element.inheritance_parent();
     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
     let parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
 
     let pseudo = style.pseudo();
-    let mut context = create_context(&data, &metrics, &style, parent_style, pseudo.as_ref());
+    let mut context = create_context(
+        &data,
+        &metrics,
+        &style,
+        parent_style,
+        pseudo.as_ref(),
+        /* for_smil_animation = */ false,
+    );
 
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     let default_values = data.default_computed_values();
 
     for (index, keyframe) in keyframes.iter().enumerate() {
         let ref mut animation_values = computed_keyframes[index];
 
@@ -3064,17 +3073,24 @@ pub extern "C" fn Servo_GetAnimationValu
     let metrics = get_metrics_provider_for_product();
 
     let element = GeckoElement(element);
     let parent_element = element.inheritance_parent();
     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
     let parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
 
     let pseudo = style.pseudo();
-    let mut context = create_context(&data, &metrics, &style, parent_style, pseudo.as_ref());
+    let mut context = create_context(
+        &data,
+        &metrics,
+        &style,
+        parent_style,
+        pseudo.as_ref(),
+        /* for_smil_animation = */ true
+    );
 
     let default_values = data.default_computed_values();
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
 
     let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
     let guard = declarations.read_with(&guard);
     for (index, anim) in guard.to_animation_value_iter(&mut context, &default_values).enumerate() {
@@ -3093,17 +3109,24 @@ pub extern "C" fn Servo_AnimationValue_C
     let metrics = get_metrics_provider_for_product();
 
     let element = GeckoElement(element);
     let parent_element = element.inheritance_parent();
     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
     let parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
 
     let pseudo = style.pseudo();
-    let mut context = create_context(&data, &metrics, style, parent_style, pseudo.as_ref());
+    let mut context = create_context(
+        &data,
+        &metrics,
+        style,
+        parent_style,
+        pseudo.as_ref(),
+        /* for_smil_animation = */ false
+    );
 
     let default_values = data.default_computed_values();
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
     // We only compute the first element in declarations.
     match declarations.read_with(&guard).declarations().first() {
         Some(&(ref decl, imp)) if imp == Importance::Normal => {
--- a/servo/tests/unit/style/parsing/mod.rs
+++ b/servo/tests/unit/style/parsing/mod.rs
@@ -55,16 +55,17 @@ fn assert_computed_serialization<C, F, T
 
     let context = Context {
         is_root_element: true,
         builder: StyleBuilder::for_derived_style(&device, initial_style, None, None),
         cached_system_font: None,
         font_metrics_provider: &ServoMetricsProvider,
         in_media_query: false,
         quirks_mode: QuirksMode::NoQuirks,
+        for_smil_animation: false,
     };
 
     let parsed = parse(f, input).unwrap();
     let computed = parsed.to_computed_value(&context);
     let serialized = ToCss::to_css_string(&computed);
     assert_eq!(serialized, output);
 }