The default bug view has changed. See this FAQ.

CSS gradients should work on premultiplied colors

RESOLVED FIXED in mozilla36

Status

()

Core
CSS Parsing and Computation
RESOLVED FIXED
7 years ago
a year ago

People

(Reporter: dbaron, Assigned: Rik Cabanier)

Tracking

(Depends on: 1 bug, Blocks: 3 bugs, {css3, dev-doc-complete})

Trunk
mozilla36
css3, dev-doc-complete
Points:
---
Dependency tree / graph
Bug Flags:
in-testsuite +

Firefox Tracking Flags

(relnote-firefox 36+)

Details

(Whiteboard: [parity-chrome][parity-opera][parity-safari][parity-ie], URL)

Attachments

(3 attachments, 9 obsolete attachments)

(Reporter)

Description

7 years ago
Gradients to/from transparent look bad because they go through gray in the middle.  This is because we're not doing gradients on premultiplied colors, but I think we should be.

For example, see:
data:text/html;charset=utf-8,<!DOCTYPE html><body style%3D"background%3A-moz-linear-gradient(left%2C yellow%2C transparent)">

(This isn't an issue in the SVG spec because SVG doesn't have rgba colors, and it thus has separate stop-color and stop-opacity.)

Does this seem reasonable?
Yes.

Comment 2

7 years ago
Agreed; you should have the ability to go from #ffff00ff to #ffff0000 and get an attractive transition.

Does this mean I'll need to revise the spec and specify how to determine the color between stops myself?
(Reporter)

Comment 3

7 years ago
I think the spec should specify something; right now it looks like it doesn't say anything.  I think specifying an appropriately weighted average, in premultiplied RGBA space, of the colors at the adjacent stops should be sufficient, and not require a lot of text (although you might want to be a little more specific than "appropriately weighted").

Comment 4

7 years ago
At the moment it explicitly defers to SVG: "Between two color-stops, the colors are interpolated as SVG gradients."

I've just committed a change so that it says "Between two color-stops, the line's color is linearly interpolated between the colors of the two color-stops, with the interpolation taking place in premultiplied RGBA space."
(Reporter)

Comment 5

7 years ago
Though, actually, SVG also defines the 'color-interpolation' property, which should probably apply.  Except I'm really not sure if linearRGB makes sense along with premultiplication.

But I guess the spec probably should at least say that interpolation takes place in sRGB.
> At the moment it explicitly defers to SVG: "Between two color-stops, the colors
> are interpolated as SVG gradients."

Thats underdefined, since SVG doesn't allow alpha != 1 in colors and hence only defines gradient behavior for colors with alpha == 1, as mentioned in comment 0.  So in effect the CSS spec doesn't define the behavior here at all at the moment (modulo whatever changes happened in comment 5).

Comment 7

7 years ago
Ah, gotcha bz.  I didn't catch the implication of that statement in comment 0.  Well, it should be well-defined now, as long as "linearly interpolated in premultiplied sRGBA space" is sufficient.

(Let me know if the term "sRGBA space" is nonsensical, and I'll split out the color and opacity language explicitly.)
(Reporter)

Comment 8

7 years ago
This affects:
 * InterpolateColor in nsCSSRendering (for endpoints)
 * something inside cairo (we call cairo_pattern_add_color_stop_rgba)
(Reporter)

Updated

7 years ago
Duplicate of this bug: 607859
(Reporter)

Updated

6 years ago
Blocks: 619834

Updated

5 years ago
Blocks: 752187

Comment 10

5 years ago
Example 23 in Image Values spec Gradient Color Stops section http://www.w3.org/TR/css3-images/#color-stop-syntax
is another example...might be a good one to use for a test case.
Created attachment 641710 [details] [diff] [review]
Add extra color-stop to simulate interpolation in premultiplied color space
Attachment #641710 - Flags: review?(dbaron)
Created attachment 641711 [details] [diff] [review]
Tests
Attachment #641711 - Flags: review?(dbaron)
(Reporter)

Comment 13

5 years ago
This seems to only fix it when the alpha component is 0, but it really matters for all cases where the alpha components differ.
Is there a difference when the alpha component is not 0?
I thought a rgba() value itself is an unpremultiplied color even if interpolation is calculated in premultiplied color space. Is it incorrect?

Comment 15

5 years ago
Yes, the transition is different between pre and unpre any time both the hue and the alpha change.

You are correct that the rgba() function specifies an unpremultiplied color.  That doesn't affect what I said above.
(Reporter)

Comment 16

5 years ago
For example, the gradient from rgb(0, 255, 0) to rgba(255, 0, 0, 0.01) should be basically indistinguishable from the gradient from the same start color to transparent, or to rgba(*, *, *, 0.01), since as premultiplied colors, all values of rgba(*, *, *, 0.01) are nearly the same for any values of *.
Comment on attachment 641710 [details] [diff] [review]
Add extra color-stop to simulate interpolation in premultiplied color space

I got it. Thank you.
Attachment #641710 - Flags: review?(dbaron)
Attachment #641711 - Flags: review?(dbaron)
Duplicate of this bug: 858988

Comment 19

4 years ago
Created attachment 736196 [details]
HTML Test Case

Added a clean HTML page demonstrating the problem, for quick eye checking

Comment 20

4 years ago
(In reply to David Marland from comment #19)
> Created attachment 736196 [details]
> HTML Test Case
> 
> Added a clean HTML page demonstrating the problem, for quick eye checking

Just as a note, the easiest possible no-way-to-misinterpret-or-mistake-it way to test this is to just put a gradient from transparent->white over a white background.  If you're premultiplied, you'll see nothing; if you're not, you'll see a gradient from white->light gray->white.

Comment 21

3 years ago
Anyone still working on this? 'transparent' is useless in this context and have to revert to workarounds using rgba().
Duplicate of this bug: 1068012
Whiteboard: [parity-chrome][parity-opera][parity-safari][parity-ie]
Duplicate of this bug: 1078344
(Assignee)

Updated

3 years ago
Assignee: nobody → cabanier
(Assignee)

Comment 24

3 years ago
Created attachment 8505936 [details] [diff] [review]
First implementation for premultiplied gradients
Attachment #641710 - Attachment is obsolete: true
(Assignee)

Comment 25

3 years ago
Created attachment 8505937 [details] [diff] [review]
First implementation for premultiplied gradients

How does this look?
The rendering is identical (to the naked eye) to Chrome and IE
Attachment #8505936 - Attachment is obsolete: true
Flags: needinfo?(dbaron)
(Assignee)

Comment 26

3 years ago
Created attachment 8506482 [details] [diff] [review]
Implementation for premultiplied gradients
Attachment #8505937 - Attachment is obsolete: true
Flags: needinfo?(dbaron)
(Assignee)

Comment 27

3 years ago
Created attachment 8506483 [details] [diff] [review]
Implementation for premultiplied gradients
Attachment #8506482 - Attachment is obsolete: true
(Assignee)

Comment 28

3 years ago
Comment on attachment 8506483 [details] [diff] [review]
Implementation for premultiplied gradients

try build: https://treeherder.mozilla.org/ui/#/jobs?repo=try&revision=0ba919361a0a
Attachment #8506483 - Flags: review?(dbaron)
(Reporter)

Comment 29

3 years ago
Comment on attachment 8506483 [details] [diff] [review]
Implementation for premultiplied gradients

I think this is mostly good (but I didn't look very closely), except I think you don't need to do anything interesting if two adjacent stops have the same alpha.  (And then I don't think you need any condition testing alpha != 1.0.)


Also, I think once you fix that, :mstange could probably review this.
Attachment #8506483 - Flags: review?(dbaron) → review-
(Reporter)

Comment 30

3 years ago
(Also, is this on top of the patch in bug 1074056?)
(Assignee)

Comment 31

3 years ago
(In reply to David Baron [:dbaron] (UTC-7) (needinfo? for questions) from comment #30)
> (Also, is this on top of the patch in bug 1074056?)

No. It just happened that those were in my queue. I will do another try before landing.
(Assignee)

Comment 32

3 years ago
(In reply to David Baron [:dbaron] (UTC-7) (needinfo? for questions) from comment #29)
> Comment on attachment 8506483 [details] [diff] [review]
> Implementation for premultiplied gradients
> 
> I think this is mostly good (but I didn't look very closely), except I think
> you don't need to do anything interesting if two adjacent stops have the
> same alpha.  (And then I don't think you need any condition testing alpha !=
> 1.0.)

This line already accomplishes that:
   float steps = fabs(leftStop.mColor.a - rightStop.mColor.a) * 10;

If the alpha is the same (or close) there are no additional stops.

> Also, I think once you fix that, :mstange could probably review this.

Do you still want me to update the patch?
(Assignee)

Updated

3 years ago
Flags: needinfo?(dbaron)
(Reporter)

Comment 33

3 years ago
Yes -- it would be nice to do that check much earlier, and definitely before you've done any floating point math.

Also, steps should almost certainly be an integral type, both for faster comparison and so you get evenly distributed steps.
Flags: needinfo?(dbaron)
(Reporter)

Comment 34

3 years ago
(Can you do the equal-alpha check before the 0-alpha checks?  If so, that would probably be good, since it's the common case.)
(Reporter)

Comment 35

3 years ago
(In reply to Rik Cabanier from comment #31)
> (In reply to David Baron [:dbaron] (UTC-7) (needinfo? for questions) from
> comment #30)
> > (Also, is this on top of the patch in bug 1074056?)
> 
> No. It just happened that those were in my queue. I will do another try
> before landing.

Will you need to merge the two?  Or should they be sharing code?
(Assignee)

Comment 36

3 years ago
(In reply to David Baron [:dbaron] (UTC-7) (needinfo? for questions) from comment #35)
> (In reply to Rik Cabanier from comment #31)
> > (In reply to David Baron [:dbaron] (UTC-7) (needinfo? for questions) from
> > comment #30)
> > > (Also, is this on top of the patch in bug 1074056?)
> > 
> > No. It just happened that those were in my queue. I will do another try
> > before landing.
> 
> Will you need to merge the two?  Or should they be sharing code?

I don't need to merge them as they don't share code. I need to make sure that midpoint calculation happens before the premultiply logic
(Assignee)

Comment 37

3 years ago
Created attachment 8506643 [details] [diff] [review]
Implementation for premultiplied gradients
Attachment #8506483 - Attachment is obsolete: true
Attachment #8506643 - Flags: review?(matt.woodrow)
I've added dev-doc-needed because I want to check, once this lands, that the terminology is coherent in the MDN and that we speak about the premultiplied space (also there likely will lead to a new comment in the browser compat section of gradient-related articles) [And a release note]
Keywords: dev-doc-needed
(Assignee)

Updated

3 years ago
Attachment #8506643 - Flags: review?(matt.woodrow) → review?(mstange)
Comment on attachment 8506643 [details] [diff] [review]
Implementation for premultiplied gradients

Review of attachment 8506643 [details] [diff] [review]:
-----------------------------------------------------------------

The approach looks good to me, but I'd like to see the revised version of this patch before I can r+ it.

It looks like Skia and Direct2D natively support premultiplied gradient interpolation. Do you have any plans for using that support in the future?

::: layout/base/nsCSSRendering.cpp
@@ +2219,5 @@
>  
>    return false;
>  }
>  
> +static void ResolvePremultipliedAlpha(nsTArray<ColorStop>& stops)

add a line break between void and ResolvePremultipliedAlpha, and call the argument aStops

Also, please add a comment above the function, something like "Adjusts and adds color stops in such a way that drawing the gradient with unpremultiplied interpolation looks nearly the same as if it were drawn with premultiplied interpolation."

@@ +2221,5 @@
>  }
>  
> +static void ResolvePremultipliedAlpha(nsTArray<ColorStop>& stops)
> +{
> +  ColorStop leftStop = stops[0];

Doing leftStop = stops[x - 1] at the start of every loop iteration would make the invariant clearer and not be noticeably slower.

@@ +2223,5 @@
> +static void ResolvePremultipliedAlpha(nsTArray<ColorStop>& stops)
> +{
> +  ColorStop leftStop = stops[0];
> +
> +  for (size_t x = 1; x < stops.Length();) {

I think putting the x++ in here, and additionally calling x++ whenever you do stops.Insert*, would make it easier to understand what's happening.

@@ +2224,5 @@
> +{
> +  ColorStop leftStop = stops[0];
> +
> +  for (size_t x = 1; x < stops.Length();) {
> +    ColorStop rightStop = stops[x];

Can you make leftStop and rightStop const? I get confused when these variables change their values halfway through.

@@ +2225,5 @@
> +  ColorStop leftStop = stops[0];
> +
> +  for (size_t x = 1; x < stops.Length();) {
> +    ColorStop rightStop = stops[x];
> +    

end-of-line whitespace

@@ +2227,5 @@
> +  for (size_t x = 1; x < stops.Length();) {
> +    ColorStop rightStop = stops[x];
> +    
> +    // if the left and right stop have the same alpha value, we don't need
> +    // to do anything

Comments that are whole sentences should start with a capital letter and end with a full stop.

@@ +2239,5 @@
> +    // of the right stop
> +    if (leftStop.mColor.a == 0) {
> +      leftStop.mColor = rightStop.mColor;
> +      leftStop.mColor.a = 0;
> +      stops[x - 1] = leftStop;

You could add a helper function TransparentColor and make this

stops[x - 1].mColor = TransparentColor(rightStop.mColor);

@@ +2245,5 @@
> +
> +    // Is the stop on the right completely transparent?
> +    // If so, duplicate it and assign it the color on the left.
> +    if (rightStop.mColor.a == 0) {
> +      ColorStop NewStop = rightStop;

newStop with lowercase n, please

@@ +2248,5 @@
> +    if (rightStop.mColor.a == 0) {
> +      ColorStop NewStop = rightStop;
> +      rightStop.mColor = leftStop.mColor;
> +      rightStop.mColor.a = 0;
> +      stops[x] = rightStop;

stops[x].mColor = TransparentColor(leftStop.mColor);

@@ +2253,5 @@
> +      x++;
> +
> +      // If the right stop is the last colorstop, don't add a new
> +      // one; we're done.
> +      if (x == stops.Length() + 1) {

Invert this if and put the stuff up to (and including) stops.InsertElementAt(x, newStop) inside it.

@@ +2258,5 @@
> +        continue;
> +      }
> +      leftStop = NewStop;
> +      // if the next stop is in the same position,
> +      // don't add a new stop

Why not?

@@ +2269,5 @@
> +      x++;
> +      continue;
> +    }
> +
> +    // Now handle cases where one of the stops in transparent

in -> is, and add a full stop at the end

@@ +2271,5 @@
> +    }
> +
> +    // Now handle cases where one of the stops in transparent
> +    if (leftStop.mColor.a != 1.0f || rightStop.mColor.a != 1.0f) {
> +      gfxRGBA premulLeftColor(leftStop.mColor.Packed(gfxRGBA::PACKED_ABGR_PREMULTIPLIED));

Understanding this requires the reader to check that the gfxRGBA constructor uses PACKED_ABGR as the default value for its colorType argument (and not PACKED_ARGB_PREMULTIPLIED). Please add a dedicated helper function for this, e.g. static gfxRGBA Premultiply(const gfxRGBA& aColor);.

@@ +2274,5 @@
> +    if (leftStop.mColor.a != 1.0f || rightStop.mColor.a != 1.0f) {
> +      gfxRGBA premulLeftColor(leftStop.mColor.Packed(gfxRGBA::PACKED_ABGR_PREMULTIPLIED));
> +      gfxRGBA premulRightColor(rightStop.mColor.Packed(gfxRGBA::PACKED_ABGR_PREMULTIPLIED));
> +      // calculate how many extra steps. We do a step per 10% transparency
> +      float steps = fabs(leftStop.mColor.a - rightStop.mColor.a) * 10;

Call this stepCount, make it a size_t and use NSToIntFloor. And instead of * 10, make it / 0.1, and put the 0.1 in a static const kAlphaIncrementPerGradientStep above this function.

@@ +2277,5 @@
> +      // calculate how many extra steps. We do a step per 10% transparency
> +      float steps = fabs(leftStop.mColor.a - rightStop.mColor.a) * 10;
> +      for (int y = 1; y < steps; y++) {
> +        ColorStop NewStop(Interpolate(leftStop.mPosition, rightStop.mPosition, y / steps),
> +                          gfxRGBA(InterpolateColor(premulLeftColor, premulRightColor, y / steps).Packed(), gfxRGBA::PACKED_ABGR_PREMULTIPLIED));

Use a dedicated Unpremultiply helper function.

@@ +2582,5 @@
>      }
>      stops.AppendElement(ColorStop(firstStop, lastColor));
>    }
>  
> +  // resolve premultiplied colors

This comment can go, once you add a more descriptive comment above the function definition.
Attachment #8506643 - Flags: review?(mstange)
(Assignee)

Comment 40

2 years ago
(In reply to Markus Stange [:mstange] from comment #39)
> Comment on attachment 8506643 [details] [diff] [review]
> Implementation for premultiplied gradients
> 
> Review of attachment 8506643 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> The approach looks good to me, but I'd like to see the revised version of
> this patch before I can r+ it.
> 
> It looks like Skia and Direct2D natively support premultiplied gradient
> interpolation. Do you have any plans for using that support in the future?

I didn't know that D2D support it. I will add follow up patches to enable it.
I believe I addressed your comments; the code ended up much simpler.
(Assignee)

Comment 41

2 years ago
Created attachment 8508451 [details] [diff] [review]
Implementation for premultiplied gradients
Attachment #8506643 - Attachment is obsolete: true
Attachment #8508451 - Flags: review?(mstange)
(Assignee)

Comment 42

2 years ago
Created attachment 8508453 [details] [diff] [review]
Implementation for premultiplied gradients
Attachment #8508451 - Attachment is obsolete: true
Attachment #8508451 - Flags: review?(mstange)
Attachment #8508453 - Flags: review?(mstange)
Comment on attachment 8508453 [details] [diff] [review]
Implementation for premultiplied gradients

Review of attachment 8508453 [details] [diff] [review]:
-----------------------------------------------------------------

::: layout/base/nsCSSRendering.cpp
@@ +2229,5 @@
> +
> +// Adjusts and adds color stops in such a way that drawing the gradient with
> +// unpremultiplied interpolation looks nearly the same as if it were drawn with
> +// premultiplied interpolation.
> +static const float kAlphaIncrementPerGradientStep = 0.1f;

In this location the comment looks like it describes the Premultiply function and not the ResolvePremultipliedAlpha function. Please move the comment + the constant down directly above ResolvePremultipliedAlpha.

@@ +2232,5 @@
> +// premultiplied interpolation.
> +static const float kAlphaIncrementPerGradientStep = 0.1f;
> +static gfxRGBA
> +Premultiply(const gfxRGBA& aColor) {
> +  return aColor.Packed(gfxRGBA::PACKED_ABGR_PREMULTIPLIED);

You don't need to pack+unpack the color just to premultiply it. Let's reimplement Premultiply and Unpremultiply in place.

static gfxRGBA
Premultiply(const gfxRGBA& aColor)
{
  gfxFloat a = aColor.a;
  return gfxRGBA(aColor.r * a, aColor.g * a, aColor.b * a, a);
}

static gfxRGBA
Unpremultiply(const gfxRGBA& aColor)
{
  gfxFloat a = aColor.a;
  return (a > 0.0) ? gfxRGBA(aColor.r / a, aColor.g / a, aColor.b / a, a)
                   : aColor;
}

@@ +2239,5 @@
> +static gfxRGBA Unpremultiply(uint32_t aColor) {
> +  return gfxRGBA(gfxRGBA(aColor).Packed(), gfxRGBA::PACKED_ABGR_PREMULTIPLIED);
> +}
> +
> +static gfxRGBA TransparentColor(gfxRGBA aColor) {

Mozilla coding style requires two more line breaks in this line.

@@ +2262,5 @@
> +    // of the right stop
> +    if (leftStop.mColor.a == 0) {
> +      ColorStop newStop = leftStop;
> +      newStop.mColor = TransparentColor(rightStop.mColor);
> +      aStops[x - 1] = newStop;

Do you need newStop? Can't you just change aStops[x - 1].mColor?

@@ +2283,5 @@
> +      gfxRGBA premulRightColor = Premultiply(rightStop.mColor);
> +      // Calculate how many extra steps. We do a step per 10% transparency.
> +      size_t stepCount = NSToIntFloor(fabs(leftStop.mColor.a - rightStop.mColor.a) / kAlphaIncrementPerGradientStep);
> +      for (size_t y = 1; y < stepCount; y++) {
> +        ColorStop newStop(Interpolate(leftStop.mPosition, rightStop.mPosition, static_cast<float>(y) / stepCount),

You could factor out "static_cast<float>(y) / stepCount" into a new variable, e.g. "frac", since you're using it in three places.

@@ +2586,5 @@
>      }
>      stops.AppendElement(ColorStop(firstStop, lastColor));
>    }
>  
> +  // resolve premultiplied colors

remove comment
Attachment #8508453 - Flags: review?(mstange)
(Assignee)

Comment 44

2 years ago
Created attachment 8508814 [details] [diff] [review]
Implementation for premultiplied gradients
Attachment #8508453 - Attachment is obsolete: true
Attachment #8508814 - Flags: review?(mstange)
Comment on attachment 8508814 [details] [diff] [review]
Implementation for premultiplied gradients

Review of attachment 8508814 [details] [diff] [review]:
-----------------------------------------------------------------

Do you have some tests you can add?

::: layout/base/nsCSSRendering.cpp
@@ +2239,5 @@
> +  gfxFloat a = aColor.a;
> +  return (a > 0.0) ? gfxRGBA(aColor.r / a, aColor.g / a, aColor.b / a, a) : aColor;
> +}
> +
> +static gfxRGBA TransparentColor(gfxRGBA aColor) {

Three lines:

static gfxRGBA
TransparentColor(gfxRGBA aColor)
{
Attachment #8508814 - Flags: review?(mstange) → review+
(Assignee)

Comment 46

2 years ago
(In reply to Markus Stange [:mstange] from comment #45)
> Comment on attachment 8508814 [details] [diff] [review]
> Implementation for premultiplied gradients
> 
> Review of attachment 8508814 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> Do you have some tests you can add?

yes. I'll add a couple simple gradient test that have a stop at 0% opacity.
(Assignee)

Comment 47

2 years ago
Created attachment 8508862 [details] [diff] [review]
Implementation for premultiplied gradients

try build: https://treeherder.mozilla.org/ui/#/jobs?repo=try&revision=688f2ac57044
Attachment #8508814 - Attachment is obsolete: true
(Assignee)

Comment 48

2 years ago
There was an unrelated bad patch in that push queue.
Better try build: https://treeherder.mozilla.org/ui/#/jobs?repo=try&revision=e28ff5e50f07
(Assignee)

Updated

2 years ago
Keywords: checkin-needed
(Assignee)

Updated

2 years ago
Blocks: 1087119
https://hg.mozilla.org/integration/mozilla-inbound/rev/32dac7dce97a
Flags: in-testsuite+
Keywords: checkin-needed
https://hg.mozilla.org/mozilla-central/rev/32dac7dce97a
Status: NEW → RESOLVED
Last Resolved: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla36

Updated

2 years ago
Attachment #736196 - Attachment mime type: text/plain → text/html
I can confirm that it's working. Great job!

Sebastian
Release Note Request (optional, but appreciated)
[Why is this notable]: Fixed an issue with CSS gradients web devs often run into
[Suggested wording]: CSS gradients work on premultiplied colors
[Links (documentation, blog post, etc)]: (MDN dev-doc-needed)

May also be worth noting in b2g, though as I don't use a Firefox OS device to try that out, I didn't request a release note for it.

Sebastian
relnote-firefox: --- → ?

Updated

2 years ago
Blocks: 1088578

Comment 53

2 years ago
>(This isn't an issue in the SVG spec because SVG doesn't have rgba colors, and it thus has separate stop-color and stop-opacity.)

It seems to be an issue with canvas gradients, since they do not support stop opacities, unlike SVG

change "white" to "transparent" in this demo:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.createLinearGradient

Comment 54

2 years ago
And it *is* a problem in SVG gradients anyway; while SVG was originally written assuming opaque colors, everyone just implements the full CSS color spec for it, so you can get partially-transparent colors in the stop-color attribute as well.

You can *avoid* the issue by using opaque colors and stop-opacity, but that doesn't mean the issue isn't still there.
Added to the 36 release notes as a known issue being fixed in 36 (and not as a new feature). Let me know if you disagree.
relnote-firefox: ? → 36+
(In reply to Sylvestre Ledru [:sylvestre] from comment #55)
> Added to the 36 release notes as a known issue being fixed in 36 (and not as
> a new feature).

That was my intention. :-) So that's totally fine.

Sebastian
Added a note in:
https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient
https://developer.mozilla.org/en-US/docs/Web/CSS/radial-gradient
https://developer.mozilla.org/en-US/docs/Web/CSS/repeating-linear-gradient
https://developer.mozilla.org/en-US/docs/Web/CSS/repeating-radial-gradient
and
https://developer.mozilla.org/en-US/Firefox/Releases/36#CSS
Keywords: dev-doc-needed → dev-doc-complete

Updated

a year ago
Blocks: 1241717

Updated

a year ago
Depends on: 1242145
You need to log in before you can comment on or make changes to this bug.