Bug 1352643 - Implement multi-position gradient color-stops syntax. r=emilio
☠☠ backed out by 1f808ca3a4b4 ☠ ☠
authorRyan Hunt <rhunt@eqrion.net>
Mon, 01 Oct 2018 14:26:23 -0500
changeset 495060 e85309ff67f47c2ee52944499f6ababe1278fabd
parent 495059 e1ac0b6c2ca0830009c059ef62d0aaf421b1a73d
child 495061 e6820b5308f7d9c008b79113983c9a975ea44d6f
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1352643
milestone64.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
Bug 1352643 - Implement multi-position gradient color-stops syntax. r=emilio This commit adds the multi-position gradient color-stops syntax. GradientItem::parse_comma_separated is extended to attempt to parse a LengthOrPercent after each color stop. If it succeeds, it appends an additional color stop with a duplicate color and the specified position. This change is only to the parsing, serialization is left unchanged as per [1]. [1] https://github.com/w3c/csswg-drafts/issues/2714 Differential Revision: https://phabricator.services.mozilla.com/D7380
layout/style/test/property_database.js
servo/components/style/values/specified/image.rs
testing/web-platform/meta/css/css-images/multiple-position-color-stop-linear.html.ini
testing/web-platform/meta/css/css-images/multiple-position-color-stop-radial.html.ini
testing/web-platform/tests/css/css-images/multiple-position-color-stop-linear-2-ref.html
testing/web-platform/tests/css/css-images/multiple-position-color-stop-linear-2.html
testing/web-platform/tests/css/css-images/multiple-position-color-stop-radial-2-ref.html
testing/web-platform/tests/css/css-images/multiple-position-color-stop-radial-2.html
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -125,16 +125,22 @@ var validGradientAndElementValues = [
   "linear-gradient(.414rad, red 50%, 50%, blue 50%)",
   "linear-gradient(.414rad, red 50%, 20%, blue 50%)",
   "linear-gradient(.414rad, red 50%, 30%, blue 10%)",
   "linear-gradient(to right bottom, red, 20%, green 50%, 65%, blue)",
   "linear-gradient(to right bottom, red, 20%, green 10%, blue)",
   "linear-gradient(to right bottom, red, 50%, green 50%, 50%, blue)",
   "linear-gradient(to right bottom, red, 0%, green 50%, 100%, blue)",
 
+  "linear-gradient(red 0% 100%)",
+  "linear-gradient(red 0% 50%, blue 50%)",
+  "linear-gradient(red 0% 50%, blue 50% 100%)",
+  "linear-gradient(red 0% 50%, 0%, blue 50%)",
+  "linear-gradient(red 0% 50%, 0%, blue 50% 100%)",
+
   /* Unitless 0 is valid as an <angle> */
   "linear-gradient(0, red, blue)",
 
   "radial-gradient(red, blue)",
   "radial-gradient(red, yellow, blue)",
   "radial-gradient(red 1px, yellow 20%, blue 24em, green)",
   "radial-gradient(red, yellow, green, blue 50%)",
   "radial-gradient(red -50%, yellow -25%, green, blue)",
@@ -178,16 +184,22 @@ var validGradientAndElementValues = [
   "radial-gradient(farthest-corner circle at 4em, red, blue)",
 
   "radial-gradient(30% 40% at top left, red, blue)",
   "radial-gradient(50px 60px at 15% 20%, red, blue)",
   "radial-gradient(7em 8em at 45px, red, blue)",
 
   "radial-gradient(circle at 15% 20%, red, blue)",
 
+  "radial-gradient(red 0% 100%)",
+  "radial-gradient(red 0% 50%, blue 50%)",
+  "radial-gradient(red 0% 50%, blue 50% 100%)",
+  "radial-gradient(red 0% 50%, 0%, blue 50%)",
+  "radial-gradient(red 0% 50%, 0%, blue 50% 100%)",
+
   "repeating-radial-gradient(red, blue)",
   "repeating-radial-gradient(red, yellow, blue)",
   "repeating-radial-gradient(red 1px, yellow 20%, blue 24em, green)",
   "repeating-radial-gradient(red, yellow, green, blue 50%)",
   "repeating-radial-gradient(red -50%, yellow -25%, green, blue)",
   "repeating-radial-gradient(red -99px, yellow, green, blue 120%)",
   "repeating-radial-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))",
   "repeating-radial-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)",
@@ -323,16 +335,26 @@ var invalidGradientAndElementValues = [
   "radial-gradient(399grad, ellipse closest-corner, red, blue)",
   "radial-gradient(399grad, farthest-side circle, red, blue)",
 
   "radial-gradient(top left 99deg, cover, red, blue)",
   "radial-gradient(15% 20% -1.2345rad, circle, red, blue)",
   "radial-gradient(45px 399grad, ellipse closest-corner, red, blue)",
   "radial-gradient(45px 399grad, farthest-side circle, red, blue)",
   "radial-gradient(circle red, blue)",
+
+  /* don't allow more than two positions with multi-position syntax */
+  "linear-gradient(red 0% 50% 100%)",
+  "linear-gradient(red 0% 50% 75%, blue 75%)",
+  "linear-gradient(to bottom, red 0% 50% 100%)",
+  "linear-gradient(to bottom, red 0% 50% 75%, blue 75%)",
+  "radial-gradient(red 0% 50% 100%)",
+  "radial-gradient(red 0% 50% 75%, blue 75%)",
+  "radial-gradient(center, red 0% 50% 100%)",
+  "radial-gradient(center, red 0% 50% 75%, blue 75%)",
 ];
 var unbalancedGradientAndElementValues = [
   "-moz-element(#a()",
 ];
 
 var basicShapeSVGBoxValues = [
   "fill-box",
   "stroke-box",
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! CSS handling for the specified value of
 //! [`image`][image]s
 //!
 //! [image]: https://drafts.csswg.org/css-images/#image-values
 
 use Atom;
-use cssparser::{Parser, Token};
+use cssparser::{Parser, Token, Delimiter};
 use custom_properties::SpecifiedValue;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")]
 use servo_url::ServoUrl;
 use std::cmp::Ordering;
 use std::f32::consts::PI;
 use std::fmt::{self, Write};
@@ -951,27 +951,53 @@ impl ShapeExtent {
     }
 }
 
 impl GradientItem {
     fn parse_comma_separated<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Vec<Self>, ParseError<'i>> {
+        let mut items = Vec::new();
         let mut seen_stop = false;
-        let items = input.parse_comma_separated(|input| {
-            if seen_stop {
-                if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) {
-                    seen_stop = false;
-                    return Ok(generic::GradientItem::InterpolationHint(hint));
+
+        loop {
+            input.parse_until_before(Delimiter::Comma, |input| {
+                if seen_stop {
+                    if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) {
+                        seen_stop = false;
+                        items.push(generic::GradientItem::InterpolationHint(hint));
+                        return Ok(());
+                    }
                 }
+
+                let stop = ColorStop::parse(context, input)?;
+
+                if let Ok(multi_position) = input.try(|i| LengthOrPercentage::parse(context, i)) {
+                    let stop_color = stop.color.clone();
+                    items.push(generic::GradientItem::ColorStop(stop));
+                    items.push(generic::GradientItem::ColorStop(ColorStop {
+                        color: stop_color,
+                        position: Some(multi_position),
+                    }));
+                } else {
+                    items.push(generic::GradientItem::ColorStop(stop));
+                }
+
+                seen_stop = true;
+                Ok(())
+            })?;
+
+            match input.next() {
+                Err(_) => break,
+                Ok(&Token::Comma) => continue,
+                Ok(_) => unreachable!(),
             }
-            seen_stop = true;
-            ColorStop::parse(context, input).map(generic::GradientItem::ColorStop)
-        })?;
+        }
+
         if !seen_stop || items.len() < 2 {
             return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         Ok(items)
     }
 }
 
 impl Parse for ColorStop {
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-images/multiple-position-color-stop-linear.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[multiple-position-color-stop-linear.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-images/multiple-position-color-stop-radial.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[multiple-position-color-stop-radial.html]
-  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-images/multiple-position-color-stop-linear-2-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+    <div style="background: linear-gradient(to bottom, red 0%, red 25%, blue 25%, blue 75%, red 75%, red 100%); width: 100px; height: 100px;"><br></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-images/multiple-position-color-stop-linear-2.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Linear gradient with a two position color stops</title>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#color-stop-syntax">
+<meta name="assert" content="Color stops with two positions are equivalent to two color stops with the same color">
+<link rel=match href=/css/css-images/multiple-position-color-stop-linear-2-ref.html>
+<body>
+    <div style="background: linear-gradient(to bottom, red 0% 25%, blue 25% 75%, red 75% 100%); width: 100px; height: 100px;"><br></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-images/multiple-position-color-stop-radial-2-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+    <div style="background: radial-gradient(center, red 0%, red 25%, blue 25%, blue 75%, red 75%, red 100%); width: 100px; height: 100px;"><br></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-images/multiple-position-color-stop-radial-2.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Radial gradient with a two position color stops</title>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#color-stop-syntax">
+<meta name="assert" content="Color stops with two positions are equivalent to two color stops with the same color">
+<link rel=match href=/css/css-images/multiple-position-color-stop-radial-2-ref.html>
+<body>
+    <div style="background: radial-gradient(center, red 0% 25%, blue 25% 75%, red 75% 100%); width: 100px; height: 100px;"><br></div>
+</body>