Bug 1597642 - Have scale function and scale property accept percentage value. r=emilio
authorenordin <enordin@mozilla.com>
Fri, 29 Nov 2019 04:40:03 +0000
changeset 504311 79c674504d23705095f572227f1f167dabede843
parent 504310 6293eb358d57465c23b90312af86938fbd75c8d3
child 504312 d60b79548cb59d13dd5ef9ed732c9c57ffa99411
push id101880
push userbtara@mozilla.com
push dateFri, 29 Nov 2019 04:51:24 +0000
treeherderautoland@79c674504d23 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1597642
milestone72.0a1
first release with
nightly linux32
79c674504d23 / 72.0a1 / 20191129094247 / files
nightly linux64
79c674504d23 / 72.0a1 / 20191129094247 / files
nightly mac
79c674504d23 / 72.0a1 / 20191129094247 / files
nightly win32
79c674504d23 / 72.0a1 / 20191129094247 / files
nightly win64
79c674504d23 / 72.0a1 / 20191129094247 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1597642 - Have scale function and scale property accept percentage value. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D55012
layout/reftests/transform/scale-percent-1-ref.html
layout/reftests/transform/scale-percent-1.html
layout/style/test/property_database.js
servo/components/style/values/specified/transform.rs
testing/web-platform/tests/css/css-transforms/parsing/scale-parsing-valid.html
testing/web-platform/tests/css/css-transforms/parsing/transform-invalid.html
testing/web-platform/tests/css/css-transforms/parsing/transform-valid.html
testing/web-platform/tests/css/css-transforms/transform-scale-percent-001.html
testing/web-platform/tests/css/css-transforms/transform-scale-percent-ref.html
--- a/layout/reftests/transform/scale-percent-1-ref.html
+++ b/layout/reftests/transform/scale-percent-1-ref.html
@@ -1,16 +1,17 @@
 <html>
 <head>
   <style>
     body { margin: 0px; }
     div {
       background: green;
       width: 100px;
       height: 100px;
+      -moz-transform: scale(0.5, 0.75);
     }
   </style>
 </head>
 <body>
   <div>
   </div>
 </body>
 </html>
--- a/layout/reftests/transform/scale-percent-1.html
+++ b/layout/reftests/transform/scale-percent-1.html
@@ -1,18 +1,18 @@
 <html>
 <head>
-  <!-- Percent values are not allowed for scale, so this shouldn't do anything -->
+  <!-- Percent values should function the same as their equivalent numeric values: e.g. (50% == 0.5) -->
   <style>
     body { margin: 0px; }
     div {
       background: green;
       width: 100px;
       height: 100px;
-      -moz-transform: scale(50%, 50%);
+      -moz-transform: scale(50%, 75%);
     }
   </style>
 </head>
 <body>
   <div>
   </div>
 </body>
 </html>
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -3621,19 +3621,33 @@ var gCSSProperties = {
       "translate(3px)",
       "translate(10px, -3px)",
       "rotate(45deg)",
       "rotate(45grad)",
       "rotate(45rad)",
       "rotate(0.25turn)",
       "rotate(0)",
       "scalex(10)",
+      "scalex(10%)",
+      "scalex(-10)",
+      "scalex(-10%)",
       "scaley(10)",
+      "scaley(10%)",
+      "scaley(-10)",
+      "scaley(-10%)",
       "scale(10)",
+      "scale(10%)",
       "scale(10, 20)",
+      "scale(10%, 20%)",
+      "scale(-10)",
+      "scale(-10%)",
+      "scale(-10, 20)",
+      "scale(10%, -20%)",
+      "scale(10, 20%)",
+      "scale(-10, 20%)",
       "skewx(30deg)",
       "skewx(0)",
       "skewy(0)",
       "skewx(30grad)",
       "skewx(30rad)",
       "skewx(0.08turn)",
       "skewy(30deg)",
       "skewy(30grad)",
@@ -3658,19 +3672,23 @@ var gCSSProperties = {
       "translatez(4em)",
       "translatez(-4px)",
       "translatez(0px)",
       "translatez(2px) translatez(5px)",
       "translate3d(3px, 4px, 5px)",
       "translate3d(2em, 3px, 1em)",
       "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)",
       "scale3d(4, 4, 4)",
+      "scale3d(4%, 4%, 4%)",
       "scale3d(-2, 3, -7)",
+      "scale3d(-2%, 3%, -7%)",
       "scalez(4)",
+      "scalez(4%)",
       "scalez(-6)",
+      "scalez(-6%)",
       "rotate3d(2, 3, 4, 45deg)",
       "rotate3d(-3, 7, 0, 12rad)",
       "rotatex(15deg)",
       "rotatey(-12grad)",
       "rotatez(72rad)",
       "rotatex(0.125turn)",
       "rotate3d(0, 0, 0, 0rad)",
       "perspective(0px)",
@@ -3687,17 +3705,16 @@ var gCSSProperties = {
       "translate(2)",
       "translate(-3, -4)",
       "translatex(1px 1px)",
       "translatex(translatex(1px))",
       "translatex(#0000ff)",
       "translatex(red)",
       "translatey()",
       "matrix(1px, 2px, 3px, 4px, 5px, 6px)",
-      "scale(150%)",
       "skewx(red)",
       "matrix(1%, 0, 0, 0, 0px, 0px)",
       "matrix(0, 1%, 2, 3, 4px,5px)",
       "matrix(0, 1, 2%, 3, 4px, 5px)",
       "matrix(0, 1, 2, 3%, 4%, 5%)",
       "matrix(1, 2, 3, 4, 5px, 6%)",
       "matrix(1, 2, 3, 4, 5%, 6px)",
       "matrix(1, 2, 3, 4, 5%, 6%)",
@@ -11479,25 +11496,35 @@ if (IsCSSPropertyPrefEnabled("layout.css
   };
   gCSSProperties.scale = {
     domProp: "scale",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: ["none"],
     other_values: [
       "10",
+      "10%",
       "10 20",
+      "10% 20%",
       "10 20 30",
+      "10% 20% 30%",
+      "10 20% 30",
+      "-10",
+      "-10%",
+      "-10 20",
+      "-10% 20%",
+      "-10 20 -30",
+      "-10% 20% -30%",
+      "-10 20% -30",
       "0 2.0",
       /* valid calc() values */
       "calc(1 + 2)",
       "calc(10) calc(20) 30",
     ],
     invalid_values: [
-      "150%",
       "10px",
       "10deg",
       "10, 20, 30",
       /* invalid calc() values */
       "calc(1 + 20%)",
       "10 calc(1 + 10px)",
     ],
   };
@@ -12561,19 +12588,33 @@ if (false) {
       "translate(3px)",
       "translate(10px, -3px)",
       "rotate(45deg)",
       "rotate(45grad)",
       "rotate(45rad)",
       "rotate(0.25turn)",
       "rotate(0)",
       "scalex(10)",
+      "scalex(10%)",
+      "scalex(-10)",
+      "scalex(-10%)",
       "scaley(10)",
+      "scaley(10%)",
+      "scaley(-10)",
+      "scaley(-10%)",
       "scale(10)",
+      "scale(10%)",
       "scale(10, 20)",
+      "scale(10%, 20%)",
+      "scale(-10)",
+      "scale(-10%)",
+      "scale(-10, 20)",
+      "scale(10%, -20%)",
+      "scale(10, 20%)",
+      "scale(-10, 20%)",
       "skewx(30deg)",
       "skewx(0)",
       "skewy(0)",
       "skewx(30grad)",
       "skewx(30rad)",
       "skewx(0.08turn)",
       "skewy(30deg)",
       "skewy(30grad)",
@@ -12598,19 +12639,23 @@ if (false) {
       "translatez(4em)",
       "translatez(-4px)",
       "translatez(0px)",
       "translatez(2px) translatez(5px)",
       "translate3d(3px, 4px, 5px)",
       "translate3d(2em, 3px, 1em)",
       "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)",
       "scale3d(4, 4, 4)",
+      "scale3d(4%, 4%, 4%)",
       "scale3d(-2, 3, -7)",
+      "scale3d(-2%, 3%, -7%)",
       "scalez(4)",
+      "scalez(4%)",
       "scalez(-6)",
+      "scalez(-6%)",
       "rotate3d(2, 3, 4, 45deg)",
       "rotate3d(-3, 7, 0, 12rad)",
       "rotatex(15deg)",
       "rotatey(-12grad)",
       "rotatez(72rad)",
       "rotatex(0.125turn)",
       "perspective(0px)",
       "perspective(1000px)",
@@ -12626,17 +12671,16 @@ if (false) {
       "translate(2)",
       "translate(-3, -4)",
       "translatex(1px 1px)",
       "translatex(translatex(1px))",
       "translatex(#0000ff)",
       "translatex(red)",
       "translatey()",
       "matrix(1px, 2px, 3px, 4px, 5px, 6px)",
-      "scale(150%)",
       "skewx(red)",
       "matrix(1%, 0, 0, 0, 0px, 0px)",
       "matrix(0, 1%, 2, 3, 4px,5px)",
       "matrix(0, 1, 2%, 3, 4px, 5px)",
       "matrix(0, 1, 2, 3%, 4%, 5%)",
       "matrix(1, 2, 3, 4, 5px, 6%)",
       "matrix(1, 2, 3, 4, 5%, 6px)",
       "matrix(1, 2, 3, 4, 5%, 6%)",
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -7,17 +7,17 @@
 use crate::parser::{Parse, ParserContext};
 use crate::values::computed::{Context, LengthPercentage as ComputedLengthPercentage};
 use crate::values::computed::{Percentage as ComputedPercentage, ToComputedValue};
 use crate::values::generics::transform as generic;
 use crate::values::generics::transform::{Matrix, Matrix3D};
 use crate::values::specified::position::{
     HorizontalPositionKeyword, Side, VerticalPositionKeyword,
 };
-use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number};
+use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage};
 use crate::Zero;
 use cssparser::Parser;
 use style_traits::{ParseError, StyleParseErrorKind};
 
 pub use crate::values::generics::transform::TransformStyle;
 
 /// A single operation in a specified CSS `transform`
 pub type TransformOperation =
@@ -158,42 +158,42 @@ impl Transform {
                             let tx = specified::LengthPercentage::parse(context, input)?;
                             input.expect_comma()?;
                             let ty = specified::LengthPercentage::parse(context, input)?;
                             input.expect_comma()?;
                             let tz = specified::Length::parse(context, input)?;
                             Ok(generic::TransformOperation::Translate3D(tx, ty, tz))
                         },
                         "scale" => {
-                            let sx = Number::parse(context, input)?;
+                            let sx = NumberOrPercentage::parse(context, input)?.to_number();
                             if input.try(|input| input.expect_comma()).is_ok() {
-                                let sy = Number::parse(context, input)?;
+                                let sy = NumberOrPercentage::parse(context, input)?.to_number();
                                 Ok(generic::TransformOperation::Scale(sx, sy))
                             } else {
                                 Ok(generic::TransformOperation::Scale(sx, sx))
                             }
                         },
                         "scalex" => {
-                            let sx = Number::parse(context, input)?;
+                            let sx = NumberOrPercentage::parse(context, input)?.to_number();
                             Ok(generic::TransformOperation::ScaleX(sx))
                         },
                         "scaley" => {
-                            let sy = Number::parse(context, input)?;
+                            let sy = NumberOrPercentage::parse(context, input)?.to_number();
                             Ok(generic::TransformOperation::ScaleY(sy))
                         },
                         "scalez" => {
-                            let sz = Number::parse(context, input)?;
+                            let sz = NumberOrPercentage::parse(context, input)?.to_number();
                             Ok(generic::TransformOperation::ScaleZ(sz))
                         },
                         "scale3d" => {
-                            let sx = Number::parse(context, input)?;
+                            let sx = NumberOrPercentage::parse(context, input)?.to_number();
                             input.expect_comma()?;
-                            let sy = Number::parse(context, input)?;
+                            let sy = NumberOrPercentage::parse(context, input)?.to_number();
                             input.expect_comma()?;
-                            let sz = Number::parse(context, input)?;
+                            let sz = NumberOrPercentage::parse(context, input)?.to_number();
                             Ok(generic::TransformOperation::Scale3D(sx, sy, sz))
                         },
                         "rotate" => {
                             let theta = specified::Angle::parse_with_unitless(context, input)?;
                             Ok(generic::TransformOperation::Rotate(theta))
                         },
                         "rotatex" => {
                             let theta = specified::Angle::parse_with_unitless(context, input)?;
@@ -440,29 +440,33 @@ impl Parse for Translate {
         ))
     }
 }
 
 /// A specified CSS `scale`
 pub type Scale = generic::Scale<Number>;
 
 impl Parse for Scale {
+    /// Scale accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
+    /// and then convert into an Number if it's a Percentage.
+    /// https://github.com/w3c/csswg-drafts/pull/4396
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         if input.try(|i| i.expect_ident_matching("none")).is_ok() {
             return Ok(generic::Scale::None);
         }
 
-        let sx = Number::parse(context, input)?;
-        if let Ok(sy) = input.try(|i| Number::parse(context, i)) {
-            if let Ok(sz) = input.try(|i| Number::parse(context, i)) {
+        let sx = NumberOrPercentage::parse(context, input)?.to_number();
+        if let Ok(sy) = input.try(|i| NumberOrPercentage::parse(context, i)) {
+            let sy = sy.to_number();
+            if let Ok(sz) = input.try(|i| NumberOrPercentage::parse(context, i)) {
                 // 'scale: <number> <number> <number>'
-                return Ok(generic::Scale::Scale(sx, sy, sz));
+                return Ok(generic::Scale::Scale(sx, sy, sz.to_number()));
             }
 
             // 'scale: <number> <number>'
             return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0)));
         }
 
         // 'scale: <number>'
         Ok(generic::Scale::Scale(sx, sx, Number::new(1.0)))
--- a/testing/web-platform/tests/css/css-transforms/parsing/scale-parsing-valid.html
+++ b/testing/web-platform/tests/css/css-transforms/parsing/scale-parsing-valid.html
@@ -9,19 +9,33 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 </head>
 <body>
 <script>
 test_valid_value("scale", "none");
 
 test_valid_value("scale", "1");
+test_valid_value("scale", "1%", "0.01");
 
 test_valid_value("scale", "100");
+test_valid_value("scale", "100%", "1");
 test_valid_value("scale", "100 100", "100");
+test_valid_value("scale", "100% 100%", "1");
 test_valid_value("scale", "100 100 1", "100");
+test_valid_value("scale", "100% 100% 1", "1");
+
+test_valid_value("scale", "-100");
+test_valid_value("scale", "-100%", "-1");
+test_valid_value("scale", "-100 -100", "-100");
+test_valid_value("scale", "-100% -100%", "-1");
+test_valid_value("scale", "-100 -100 1", "-100");
+test_valid_value("scale", "-100% -100% 1", "-1");
 
 test_valid_value("scale", "100 200");
+test_valid_value("scale", "100% 200%", "1 2");
 test_valid_value("scale", "100 200 1", "100 200");
+test_valid_value("scale", "100% 200% 1", "1 2");
 test_valid_value("scale", "100 200 300");
+
 </script>
 </body>
 </html>
--- a/testing/web-platform/tests/css/css-transforms/parsing/transform-invalid.html
+++ b/testing/web-platform/tests/css/css-transforms/parsing/transform-invalid.html
@@ -18,20 +18,23 @@ test_invalid_value("transform", "matrix(
 
 test_invalid_value("transform", "translate(1px, 2px, 3px)");
 
 test_invalid_value("transform", "translateX(-4px, 5px)");
 
 test_invalid_value("transform", "translateY(4%, 5%)");
 
 test_invalid_value("transform", "scale(6, 7, 8)");
+test_invalid_value("transform", "scale(6%, 7%, 8%)");
 
 test_invalid_value("transform", "scaleX(1, 2)");
+test_invalid_value("transform", "scaleX(1%, 2%)");
 
 test_invalid_value("transform", "scaleY(3, 4)");
+test_invalid_value("transform", "scaleY(3%, 4%)");
 
 test_invalid_value("transform", "rotate(0, 0)");
 test_invalid_value("transform", "rotate(0, 0, 0)");
 test_invalid_value("transform", "rotate(0, 0, 0, 0)");
 
 test_invalid_value("transform", "skew(0, 0, 0)");
 
 test_invalid_value("transform", "skewX(0, 0)");
--- a/testing/web-platform/tests/css/css-transforms/parsing/transform-valid.html
+++ b/testing/web-platform/tests/css/css-transforms/parsing/transform-valid.html
@@ -21,21 +21,37 @@ test_valid_value("transform", "translate
 test_valid_value("transform", "translate(2%, -3%)");
 
 test_valid_value("transform", "translateX(-4px)");
 
 test_valid_value("transform", "translateY(5%)");
 
 test_valid_value("transform", "scale(2)");
 test_valid_value("transform", "scale(3, 4)");
+
+test_valid_value("transform", "scale(-2)");
 test_valid_value("transform", "scale(-5, -6)");
 
+test_valid_value("transform", "scale(250%)", "scale(2.5)");
+test_valid_value("transform", "scale(325%, 475%)", "scale(3.25, 4.75)");
+
+test_valid_value("transform", "scale(-250%)", "scale(-2.5)");
+test_valid_value("transform", "scale(-500%, -620%)", "scale(-5, -6.2)");
+
 test_valid_value("transform", "scaleX(7)");
+test_valid_value("transform", "scaleX(720%)", "scaleX(7.2)");
 
 test_valid_value("transform", "scaleY(-8)");
+test_valid_value("transform", "scaleY(-85%)", "scaleY(-0.85)");
+
+test_valid_value("transform", "scale3d(0.5, 2.5, 3)");
+test_valid_value("transform", "scale3d(50%, 250%, 300%)", "scale3d(0.5, 2.5, 3)");
+
+test_valid_value("transform", "scale3d(-0.5, 2.5, -3)");
+test_valid_value("transform", "scale3d(-50%, 250%, -300%)", "scale3d(-0.5, 2.5, -3)");
 
 test_valid_value("transform", "rotate(0)", "rotate(0deg)");
 test_valid_value("transform", "rotate(90deg)");
 
 test_valid_value("transform", "skew(0)", "skew(0deg)");
 test_valid_value("transform", "skew(90deg)");
 test_valid_value("transform", "skew(0, -90deg)", "skew(0deg, -90deg)");
 test_valid_value("transform", "skew(90deg, 0)", ["skew(90deg)", "skew(90deg, 0deg)"]);
--- a/testing/web-platform/tests/css/css-transforms/transform-scale-percent-001.html
+++ b/testing/web-platform/tests/css/css-transforms/transform-scale-percent-001.html
@@ -1,24 +1,24 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>CSS Test (Transforms): scale(50%, 50%)</title>
     <link rel="author" title="Clint Talbert" href="mailto:ctalbert@mozilla.com">
     <link rel="author" title="Aryeh Gregor" href="mailto:ayg@aryeh.name">
     <link rel="help" href="http://www.w3.org/TR/css-transforms-1/#two-d-transform-functions">
     <link rel="help" href="http://www.w3.org/TR/css-transforms-1/#funcdef-scale">
-    <meta name="assert" content='This tests that scale(50%, 50%) does nothing,
-    because scale() is defined to take numbers and not percentages.'>
+    <meta name="assert" content='This tests that scale(50%, 75%) is equivalent to scale(0.5, 0.75),
+    because scale() is defined to accept both numbers and percentages.'>
     <link rel="match" href="transform-scale-percent-ref.html">
     <style>
       div {
         background: green;
         width: 100px;
         height: 100px;
-        transform: scale(50%, 50%);
+        transform: scale(50%, 75%);
       }
     </style>
   </head>
   <body>
     <div></div>
   </body>
 </html>
--- a/testing/web-platform/tests/css/css-transforms/transform-scale-percent-ref.html
+++ b/testing/web-platform/tests/css/css-transforms/transform-scale-percent-ref.html
@@ -4,15 +4,16 @@
     <title>CSS Reftest Reference</title>
     <link rel="author" title="Clint Talbert" href="mailto:ctalbert@mozilla.com">
     <link rel="author" title="Aryeh Gregor" href="mailto:ayg@aryeh.name">
     <style>
       div {
         background: green;
         width: 100px;
         height: 100px;
+        transform: scale(0.5, 0.75)
       }
     </style>
   </head>
   <body>
     <div></div>
   </body>
 </html>