source: webkit/trunk/Source/WebCore/css/CSSGradientValue.h

Last change on this file was 288071, checked in by [email protected], 3 years ago

CSS Gradients: interpolation mode should default to OKLab if any non-legacy color syntax colors are used in the stops
https://bugs.webkit.org/show_bug.cgi?id=235071

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Update test to also include non-legacy color syntax stops and update results
to match new expected behavior.

  • web-platform-tests/css/css-images/parsing/gradient-interpolation-method-computed-expected.txt:
  • web-platform-tests/css/css-images/parsing/gradient-interpolation-method-computed.html:
  • web-platform-tests/css/css-images/parsing/gradient-interpolation-method-valid-expected.txt:
  • web-platform-tests/css/css-images/parsing/gradient-interpolation-method-valid.html:

Source/WebCore:

Change the default interpolation mode for CSS gradients that have any color stops specified using non-legacy
color syntax (e.g. color(...), lab(...), oklch(...), etc.) to OKLab from sRGB.

This is the current behavior specified (kind of, it could be clearer) by the spec, but I have concerns about
whether it is a good idea for the behavior to be dependent on what syntax style you use. This has been raised
with the editors at https://github.com/w3c/csswg-drafts/issues/6914.

To implement, we now compute the default color interpolation method from the parsed stop list and pass the
result to CSSGradientValue along with the stops and other data. The CSSGradientValue needs this information
avoid computing on serialization, since we need to conditionally exclude either "in srgb" or "in oklab"
depending on which one is computed.

To continue passing the interpolation color method to the CSSGradientValue constructor so it can be immutable,
we now need to consume all the color stops before we can create the CSSGradientValue. This also allows us to
pass the stops to the constructor and make them immutable as well.

  • css/CSSGradientValue.cpp:

(WebCore::CSSGradientValue::equals const):
(WebCore::appendColorInterpolationMethod):
(WebCore::CSSLinearGradientValue::customCSSText const):
(WebCore::CSSRadialGradientValue::customCSSText const):
(WebCore::CSSConicGradientValue::customCSSText const):

  • css/CSSGradientValue.h:

(WebCore::CSSGradientValue::setSecondY):
(WebCore::CSSGradientValue::CSSGradientValue):
(WebCore::CSSGradientValue::defaultColorInterpolationMethod const):
(WebCore::CSSGradientValue::addStop): Deleted.
(WebCore::CSSGradientValue::doneAddingStops): Deleted.
(WebCore::CSSGradientValue::hasAtLeastTwoStops const): Deleted.

  • css/parser/CSSPropertyParserHelpers.cpp:

(WebCore::CSSPropertyParserHelpers::consumeDeprecatedGradient):
(WebCore::CSSPropertyParserHelpers::consumeGradientColorStops):
(WebCore::CSSPropertyParserHelpers::consumePrefixedRadialGradient):
(WebCore::CSSPropertyParserHelpers::computeGradientColorInterpolationMethod):
(WebCore::CSSPropertyParserHelpers::consumeRadialGradient):
(WebCore::CSSPropertyParserHelpers::consumePrefixedLinearGradient):
(WebCore::CSSPropertyParserHelpers::consumeLinearGradient):
(WebCore::CSSPropertyParserHelpers::consumeConicGradient):

  • html/HTMLInputElement.cpp:

(WebCore::autoFillStrongPasswordMaskImage):

Source/WebKitLegacy/win:

Add support for tests enabling the CSSGradientInterpolationColorSpacesEnabled preference.

  • WebPreferences.cpp:

(WebPreferences::cssGradientInterpolationColorSpacesEnabled):

  • WebPreferences.h:
  • WebView.cpp:

(WebView::notifyPreferencesChanged):

Tools:

  • DumpRenderTree/TestOptions.cpp:

(WTR::TestOptions::defaults):
Add default for Windows WebKitLegacy testing which still requires it.

LayoutTests:

Update test to use an explicit interpolation method when using non-legacy colors to maintain srgb interpolation.

  • css3/color/gradients.html:
  • Property svn:eol-style set to native
File size: 11.1 KB
Line 
1/*
2 * Copyright (C) 2008-2021 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "CSSImageGeneratorValue.h"
29#include "CSSPrimitiveValue.h"
30#include "ColorInterpolationMethod.h"
31#include "Gradient.h"
32
33namespace WebCore {
34
35namespace Style {
36class BuilderState;
37}
38
39enum CSSGradientType {
40 CSSDeprecatedLinearGradient,
41 CSSDeprecatedRadialGradient,
42 CSSPrefixedLinearGradient,
43 CSSPrefixedRadialGradient,
44 CSSLinearGradient,
45 CSSRadialGradient,
46 CSSConicGradient
47};
48enum CSSGradientRepeat { NonRepeating, Repeating };
49
50struct CSSGradientColorStop {
51 RefPtr<CSSPrimitiveValue> color;
52 RefPtr<CSSPrimitiveValue> position; // percentage or length
53 Color resolvedColor;
54};
55
56inline bool operator==(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
57{
58 return compareCSSValuePtr(a.color, b.color) && compareCSSValuePtr(a.position, b.position);
59}
60
61struct CSSGradientColorInterpolationMethod {
62 enum class Default : bool { SRGB, OKLab };
63
64 ColorInterpolationMethod method;
65 Default defaultMethod;
66
67 static CSSGradientColorInterpolationMethod legacyMethod(AlphaPremultiplication alphaPremultiplication)
68 {
69 return { { ColorInterpolationMethod::SRGB { }, alphaPremultiplication }, Default::SRGB };
70 }
71};
72
73inline bool operator==(const CSSGradientColorInterpolationMethod& a, const CSSGradientColorInterpolationMethod& b)
74{
75 return a.method == b.method && a.defaultMethod == b.defaultMethod;
76}
77
78using CSSGradientColorStopList = Vector<CSSGradientColorStop, 2>;
79
80class CSSGradientValue : public CSSImageGeneratorValue {
81public:
82 void setFirstX(RefPtr<CSSPrimitiveValue>&& value) { m_firstX = WTFMove(value); }
83 void setFirstY(RefPtr<CSSPrimitiveValue>&& value) { m_firstY = WTFMove(value); }
84 void setSecondX(RefPtr<CSSPrimitiveValue>&& value) { m_secondX = WTFMove(value); }
85 void setSecondY(RefPtr<CSSPrimitiveValue>&& value) { m_secondY = WTFMove(value); }
86 void resolveRGBColors();
87
88 CSSGradientType gradientType() const { return m_gradientType; }
89
90 RefPtr<Image> image(RenderElement&, const FloatSize&);
91 bool knownToBeOpaque(const RenderElement&) const;
92
93 static constexpr bool isFixedSize() { return false; }
94 static FloatSize fixedSize(const RenderElement&) { return FloatSize(); }
95 static constexpr bool isPending() { return false; }
96 static void loadSubimages(CachedResourceLoader&, const ResourceLoaderOptions&) { }
97
98 Ref<CSSGradientValue> valueWithStylesResolved(Style::BuilderState&);
99
100protected:
101 CSSGradientValue(ClassType classType, CSSGradientRepeat repeat, CSSGradientType gradientType, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
102 : CSSImageGeneratorValue(classType)
103 , m_stops(WTFMove(stops))
104 , m_gradientType(gradientType)
105 , m_repeating(repeat == Repeating)
106 , m_colorInterpolationMethod(colorInterpolationMethod)
107 {
108 }
109
110 CSSGradientValue(const CSSGradientValue& other, ClassType classType)
111 : CSSImageGeneratorValue(classType)
112 , m_firstX(other.m_firstX)
113 , m_firstY(other.m_firstY)
114 , m_secondX(other.m_secondX)
115 , m_secondY(other.m_secondY)
116 , m_stops(other.m_stops)
117 , m_gradientType(other.m_gradientType)
118 , m_repeating(other.m_repeating)
119 , m_colorInterpolationMethod(other.m_colorInterpolationMethod)
120 , m_hasColorDerivedFromElement(other.m_hasColorDerivedFromElement)
121 {
122 }
123
124 template<typename GradientAdapter> GradientColorStops computeStops(GradientAdapter&, const CSSToLengthConversionData&, const RenderStyle&, float maxLengthForRepeat);
125
126 auto firstX() const { return m_firstX.get(); }
127 auto firstY() const { return m_firstY.get(); }
128 auto secondX() const { return m_secondX.get(); }
129 auto secondY() const { return m_secondY.get(); }
130 auto& stops() const { return m_stops; }
131 bool isRepeating() const { return m_repeating; }
132 auto colorInterpolationMethod() const { return m_colorInterpolationMethod; }
133
134 bool equals(const CSSGradientValue&) const;
135
136private:
137 bool hasColorDerivedFromElement() const;
138 bool isCacheable() const;
139
140 RefPtr<CSSPrimitiveValue> m_firstX;
141 RefPtr<CSSPrimitiveValue> m_firstY;
142 RefPtr<CSSPrimitiveValue> m_secondX;
143 RefPtr<CSSPrimitiveValue> m_secondY;
144 CSSGradientColorStopList m_stops;
145 CSSGradientType m_gradientType;
146 bool m_repeating { false };
147 CSSGradientColorInterpolationMethod m_colorInterpolationMethod;
148
149 mutable std::optional<bool> m_hasColorDerivedFromElement;
150};
151
152class CSSLinearGradientValue final : public CSSGradientValue {
153public:
154 static Ref<CSSLinearGradientValue> create(CSSGradientRepeat repeat, CSSGradientType gradientType, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
155 {
156 return adoptRef(*new CSSLinearGradientValue(repeat, gradientType, colorInterpolationMethod, WTFMove(stops)));
157 }
158
159 void setAngle(Ref<CSSPrimitiveValue>&& value) { m_angle = WTFMove(value); }
160
161 String customCSSText() const;
162
163 // Create the gradient for a given size.
164 Ref<Gradient> createGradient(RenderElement&, const FloatSize&);
165
166 Ref<CSSLinearGradientValue> clone() const
167 {
168 return adoptRef(*new CSSLinearGradientValue(*this));
169 }
170
171 bool equals(const CSSLinearGradientValue&) const;
172
173private:
174 CSSLinearGradientValue(CSSGradientRepeat repeat, CSSGradientType gradientType, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
175 : CSSGradientValue(LinearGradientClass, repeat, gradientType, colorInterpolationMethod, WTFMove(stops))
176 {
177 }
178
179 CSSLinearGradientValue(const CSSLinearGradientValue& other)
180 : CSSGradientValue(other, LinearGradientClass)
181 , m_angle(other.m_angle)
182 {
183 }
184
185 RefPtr<CSSPrimitiveValue> m_angle; // may be null.
186};
187
188class CSSRadialGradientValue final : public CSSGradientValue {
189public:
190 static Ref<CSSRadialGradientValue> create(CSSGradientRepeat repeat, CSSGradientType gradientType, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
191 {
192 return adoptRef(*new CSSRadialGradientValue(repeat, gradientType, colorInterpolationMethod, WTFMove(stops)));
193 }
194
195 Ref<CSSRadialGradientValue> clone() const
196 {
197 return adoptRef(*new CSSRadialGradientValue(*this));
198 }
199
200 String customCSSText() const;
201
202 void setFirstRadius(RefPtr<CSSPrimitiveValue>&& value) { m_firstRadius = WTFMove(value); }
203 void setSecondRadius(RefPtr<CSSPrimitiveValue>&& value) { m_secondRadius = WTFMove(value); }
204
205 void setShape(RefPtr<CSSPrimitiveValue>&& value) { m_shape = WTFMove(value); }
206 void setSizingBehavior(RefPtr<CSSPrimitiveValue>&& value) { m_sizingBehavior = WTFMove(value); }
207
208 void setEndHorizontalSize(RefPtr<CSSPrimitiveValue>&& value) { m_endHorizontalSize = WTFMove(value); }
209 void setEndVerticalSize(RefPtr<CSSPrimitiveValue>&& value) { m_endVerticalSize = WTFMove(value); }
210
211 // Create the gradient for a given size.
212 Ref<Gradient> createGradient(RenderElement&, const FloatSize&);
213
214 bool equals(const CSSRadialGradientValue&) const;
215
216private:
217 CSSRadialGradientValue(CSSGradientRepeat repeat, CSSGradientType gradientType, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
218 : CSSGradientValue(RadialGradientClass, repeat, gradientType, colorInterpolationMethod, WTFMove(stops))
219 {
220 }
221
222 CSSRadialGradientValue(const CSSRadialGradientValue& other)
223 : CSSGradientValue(other, RadialGradientClass)
224 , m_firstRadius(other.m_firstRadius)
225 , m_secondRadius(other.m_secondRadius)
226 , m_shape(other.m_shape)
227 , m_sizingBehavior(other.m_sizingBehavior)
228 , m_endHorizontalSize(other.m_endHorizontalSize)
229 , m_endVerticalSize(other.m_endVerticalSize)
230 {
231 }
232
233 // Resolve points/radii to front end values.
234 float resolveRadius(CSSPrimitiveValue&, const CSSToLengthConversionData&, float* widthOrHeight = 0);
235
236 // These may be null for non-deprecated gradients.
237 RefPtr<CSSPrimitiveValue> m_firstRadius;
238 RefPtr<CSSPrimitiveValue> m_secondRadius;
239
240 // The below are only used for non-deprecated gradients. Any of them may be null.
241 RefPtr<CSSPrimitiveValue> m_shape;
242 RefPtr<CSSPrimitiveValue> m_sizingBehavior;
243
244 RefPtr<CSSPrimitiveValue> m_endHorizontalSize;
245 RefPtr<CSSPrimitiveValue> m_endVerticalSize;
246};
247
248class CSSConicGradientValue final : public CSSGradientValue {
249public:
250 static Ref<CSSConicGradientValue> create(CSSGradientRepeat repeat, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
251 {
252 return adoptRef(*new CSSConicGradientValue(repeat, colorInterpolationMethod, WTFMove(stops)));
253 }
254
255 Ref<CSSConicGradientValue> clone() const
256 {
257 return adoptRef(*new CSSConicGradientValue(*this));
258 }
259
260 String customCSSText() const;
261
262 void setAngle(RefPtr<CSSPrimitiveValue>&& value) { m_angle = WTFMove(value); }
263
264 // Create the gradient for a given size.
265 Ref<Gradient> createGradient(RenderElement&, const FloatSize&);
266
267 bool equals(const CSSConicGradientValue&) const;
268
269private:
270 explicit CSSConicGradientValue(CSSGradientRepeat repeat, CSSGradientColorInterpolationMethod colorInterpolationMethod, CSSGradientColorStopList stops)
271 : CSSGradientValue(ConicGradientClass, repeat, CSSConicGradient, colorInterpolationMethod, WTFMove(stops))
272 {
273 }
274
275 CSSConicGradientValue(const CSSConicGradientValue& other)
276 : CSSGradientValue(other, ConicGradientClass)
277 , m_angle(other.m_angle)
278 {
279 }
280
281 RefPtr<CSSPrimitiveValue> m_angle; // may be null.
282};
283
284} // namespace WebCore
285
286SPECIALIZE_TYPE_TRAITS_CSS_VALUE(CSSGradientValue, isGradientValue())
287SPECIALIZE_TYPE_TRAITS_CSS_VALUE(CSSLinearGradientValue, isLinearGradientValue())
288SPECIALIZE_TYPE_TRAITS_CSS_VALUE(CSSRadialGradientValue, isRadialGradientValue())
289SPECIALIZE_TYPE_TRAITS_CSS_VALUE(CSSConicGradientValue, isConicGradientValue())
Note: See TracBrowser for help on using the repository browser.