Skip to content

Commit f360400

Browse files
committed
Bug 1965029 - Accept CSS color syntax in input type=color r=emilio
The alpha and colorspace part of the https://html.spec.whatwg.org/#serialize-a-color-well-control-color is largely skipped in this patch, as we don't support them yet in `<input type=color>`, and thus the value is always in the "HTML compatible" form. Changing the existing WPT color.tentative.html to color.html to specifically cover implementations without `alpha` and `colorspace` support, because otherwise reading the test result of `color.window.js` is overly confusing. (Ideally the two tests should share the same test data, but for now I'm skipping that as I'm not confident yet to fill the display-p3 fields. That can be done when we actually implement `colorspace` support.) Introducing Servo_ComputeColorWellControlColor so that the colorspace conversion can happen before losing number precision, following the spec. Differential Revision: https://phabricator.services.mozilla.com/D258654
1 parent 6628d1b commit f360400

File tree

11 files changed

+191
-143
lines changed

11 files changed

+191
-143
lines changed

dom/canvas/CanvasRenderingContext2D.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,9 +1309,10 @@ CanvasRenderingContext2D::ParseColorSlow(const nsACString& aString) {
13091309

13101310
PresShell* presShell = GetPresShell();
13111311
ServoStyleSet* set = presShell ? presShell->StyleSet() : nullptr;
1312+
const StylePerDocumentStyleData* data = set ? set->RawData() : nullptr;
13121313
bool wasCurrentColor = false;
13131314
nscolor color;
1314-
if (ServoCSSParser::ComputeColor(set, NS_RGB(0, 0, 0), aString, &color,
1315+
if (ServoCSSParser::ComputeColor(data, NS_RGB(0, 0, 0), aString, &color,
13151316
&wasCurrentColor, loader)) {
13161317
result.mWasCurrentColor = wasCurrentColor;
13171318
result.mColor.emplace(color);

dom/html/HTMLInputElement.cpp

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "mozilla/dom/BlobImpl.h"
1616
#include "mozilla/dom/CustomEvent.h"
1717
#include "mozilla/dom/Directory.h"
18+
#include "mozilla/dom/DocumentInlines.h"
1819
#include "mozilla/dom/DocumentOrShadowRoot.h"
1920
#include "mozilla/dom/ElementBinding.h"
2021
#include "mozilla/dom/FileSystemUtils.h"
@@ -33,6 +34,7 @@
3334
#include "mozilla/Maybe.h"
3435
#include "mozilla/MouseEvents.h"
3536
#include "mozilla/PresShell.h"
37+
#include "mozilla/ServoCSSParser.h"
3638
#include "mozilla/StaticPrefs_dom.h"
3739
#include "mozilla/StaticPrefs_signon.h"
3840
#include "mozilla/TextUtils.h"
@@ -724,6 +726,40 @@ static bool IsPickerBlocked(Document* aDoc) {
724726
return true;
725727
}
726728

729+
/**
730+
* Parse a CSS color string and convert it to the target colorspace, to match:
731+
* https://html.spec.whatwg.org/#update-a-color-well-control-color
732+
*
733+
* @param aValue the string to be parsed
734+
* @return the parsed result as a HTML compatible form
735+
*/
736+
static StyleAbsoluteColor ComputeColor(Document* aDocument,
737+
const nsAString& aValue) {
738+
// A few steps are ignored given we don't support alpha and colorspace. See
739+
// bug 1919718.
740+
return ServoCSSParser::ComputeColorWellControlColor(
741+
aDocument->EnsureStyleSet().RawData(), NS_ConvertUTF16toUTF8(aValue),
742+
StyleColorSpace::Srgb);
743+
}
744+
745+
/**
746+
* https://html.spec.whatwg.org/#serialize-a-color-well-control-color
747+
* https://drafts.csswg.org/css-color/#color-serialization-html-compatible-serialization-is-requested
748+
*
749+
* @param aColor The parsed color
750+
* @param aResult The result in the form of #ffffff.
751+
*/
752+
static void SerializeColorForHTMLCompatibility(const StyleAbsoluteColor& aColor,
753+
nsAString& aResult) {
754+
// Raw StyleAbsoluteColor can have floats outside of 0-1 range e.g. when
755+
// display-p3 color is converted to srgb, and ToColor guarantees to fit the
756+
// values within the range.
757+
nscolor color = aColor.ToColor();
758+
aResult.Truncate();
759+
aResult.AppendPrintf("#%02x%02x%02x", NS_GET_R(color), NS_GET_G(color),
760+
NS_GET_B(color));
761+
}
762+
727763
nsTArray<nsString> HTMLInputElement::GetColorsFromList() {
728764
RefPtr<HTMLDataListElement> dataList = GetList();
729765
if (!dataList) {
@@ -740,12 +776,15 @@ nsTArray<nsString> HTMLInputElement::GetColorsFromList() {
740776
continue;
741777
}
742778

743-
nsString value;
779+
nsAutoString value;
744780
option->GetValue(value);
745-
if (IsValidSimpleColor(value)) {
746-
ToLowerCase(value);
747-
colors.AppendElement(value);
748-
}
781+
// https://html.spec.whatwg.org/#update-a-color-well-control-color
782+
// https://html.spec.whatwg.org/#serialize-a-color-well-control-color
783+
StyleAbsoluteColor result = ComputeColor(OwnerDoc(), value);
784+
// Serialization step 6: If htmlCompatible is true, then do so with
785+
// HTML-compatible serialization requested.
786+
SerializeColorForHTMLCompatibility(result, value);
787+
colors.AppendElement(value);
749788
}
750789

751790
return colors;
@@ -4981,13 +5020,14 @@ void HTMLInputElement::SanitizeValue(nsAString& aValue,
49815020
}
49825021
} break;
49835022
case FormControlType::InputColor: {
4984-
if (IsValidSimpleColor(aValue)) {
4985-
ToLowerCase(aValue);
4986-
} else {
4987-
// Set default (black) color, if aValue wasn't parsed correctly.
4988-
aValue.AssignLiteral("#000000");
4989-
}
4990-
} break;
5023+
// https://html.spec.whatwg.org/#update-a-color-well-control-color
5024+
// https://html.spec.whatwg.org/#serialize-a-color-well-control-color
5025+
StyleAbsoluteColor color = ComputeColor(OwnerDoc(), aValue);
5026+
// Serialization step 6: If htmlCompatible is true, then do so with
5027+
// HTML-compatible serialization requested.
5028+
SerializeColorForHTMLCompatibility(color, aValue);
5029+
break;
5030+
}
49915031
default:
49925032
break;
49935033
}
@@ -5009,20 +5049,6 @@ Maybe<nscolor> HTMLInputElement::ParseSimpleColor(const nsAString& aColor) {
50095049
return Some(color);
50105050
}
50115051

5012-
bool HTMLInputElement::IsValidSimpleColor(const nsAString& aValue) const {
5013-
if (aValue.Length() != 7 || aValue.First() != '#') {
5014-
return false;
5015-
}
5016-
5017-
for (int i = 1; i < 7; ++i) {
5018-
if (!IsAsciiDigit(aValue[i]) && !(aValue[i] >= 'a' && aValue[i] <= 'f') &&
5019-
!(aValue[i] >= 'A' && aValue[i] <= 'F')) {
5020-
return false;
5021-
}
5022-
}
5023-
return true;
5024-
}
5025-
50265052
bool HTMLInputElement::IsLeapYear(uint32_t aYear) const {
50275053
if ((aYear % 4 == 0 && aYear % 100 != 0) || (aYear % 400 == 0)) {
50285054
return true;

dom/html/HTMLInputElement.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,14 +1127,6 @@ class HTMLInputElement final : public TextControlElement,
11271127
*/
11281128
RadioGroupContainer* FindTreeRadioGroupContainer() const;
11291129

1130-
/**
1131-
* Parse a color string of the form #XXXXXX where X should be hexa characters
1132-
* @param the string to be parsed.
1133-
* @return whether the string is a valid simple color.
1134-
* Note : this function does not consider the empty string as valid.
1135-
*/
1136-
bool IsValidSimpleColor(const nsAString& aValue) const;
1137-
11381130
/**
11391131
* Parse a week string of the form yyyy-Www
11401132
* @param the string to be parsed.

layout/inspector/InspectorUtils.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ void InspectorUtils::RgbToColorName(GlobalObject&, uint8_t aR, uint8_t aG,
749749
void InspectorUtils::ColorToRGBA(GlobalObject& aGlobal,
750750
const nsACString& aColorString,
751751
Nullable<InspectorRGBATuple>& aResult) {
752-
auto* styleSet = [&]() -> ServoStyleSet* {
752+
const auto* styleData = [&]() -> const StylePerDocumentStyleData* {
753753
nsCOMPtr<nsIGlobalObject> global =
754754
do_QueryInterface(aGlobal.GetAsSupports());
755755
if (!global) {
@@ -767,11 +767,11 @@ void InspectorUtils::ColorToRGBA(GlobalObject& aGlobal,
767767
if (!ps) {
768768
return nullptr;
769769
}
770-
return ps->StyleSet();
770+
return ps->StyleSet()->RawData();
771771
}();
772772

773773
nscolor color = NS_RGB(0, 0, 0);
774-
if (!ServoCSSParser::ComputeColor(styleSet, NS_RGB(0, 0, 0), aColorString,
774+
if (!ServoCSSParser::ComputeColor(styleData, NS_RGB(0, 0, 0), aColorString,
775775
&color)) {
776776
aResult.SetNull();
777777
return;

layout/style/ServoCSSParser.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,22 @@ bool ServoCSSParser::IsValidCSSColor(const nsACString& aValue) {
2222
}
2323

2424
/* static */
25-
bool ServoCSSParser::ComputeColor(ServoStyleSet* aStyleSet,
25+
bool ServoCSSParser::ComputeColor(const StylePerDocumentStyleData* aStyleData,
2626
nscolor aCurrentColor,
2727
const nsACString& aValue,
2828
nscolor* aResultColor, bool* aWasCurrentColor,
2929
css::Loader* aLoader) {
30-
return Servo_ComputeColor(aStyleSet ? aStyleSet->RawData() : nullptr,
31-
aCurrentColor, &aValue, aResultColor,
30+
return Servo_ComputeColor(aStyleData, aCurrentColor, &aValue, aResultColor,
3231
aWasCurrentColor, aLoader);
3332
}
3433

34+
/* static */
35+
StyleAbsoluteColor ServoCSSParser::ComputeColorWellControlColor(
36+
const StylePerDocumentStyleData* aStyleData, const nsACString& aValue,
37+
StyleColorSpace aToColorSpace) {
38+
return Servo_ComputeColorWellControlColor(aStyleData, &aValue, aToColorSpace);
39+
}
40+
3541
/* static */
3642
bool ServoCSSParser::ColorTo(const nsACString& aFromColor,
3743
const nsACString& aToColorSpace,

layout/style/ServoCSSParser.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ namespace mozilla {
2525
struct AnimatedPropertyID;
2626
class ServoStyleSet;
2727
struct URLExtraData;
28+
struct StyleAbsoluteColor;
2829
struct StyleFontFamilyList;
2930
struct StyleFontStretch;
3031
struct StyleFontWeight;
3132
struct StyleFontStyle;
3233
struct StyleLockedDeclarationBlock;
3334
struct StyleParsingMode;
35+
struct StylePerDocumentStyleData;
3436
union StyleComputedFontStyleDescriptor;
37+
enum class StyleColorSpace : uint8_t;
3538

3639
template <typename Integer, typename Number, typename LinearStops>
3740
struct StyleTimingFunction;
@@ -62,8 +65,8 @@ class ServoCSSParser {
6265
/**
6366
* Computes an nscolor from the given CSS <color> value.
6467
*
65-
* @param aStyleSet The style set whose nsPresContext will be used to
66-
* compute system colors and other special color values.
68+
* @param aStyleData The style data to compute system colors and other special
69+
* color values.
6770
* @param aCurrentColor The color value that currentcolor should compute to.
6871
* @param aValue The CSS <color> value.
6972
* @param aResultColor The resulting computed color value.
@@ -74,11 +77,28 @@ class ServoCSSParser {
7477
* won't be reported to the console.
7578
* @return Whether aValue was successfully parsed and aResultColor was set.
7679
*/
77-
static bool ComputeColor(ServoStyleSet* aStyleSet, nscolor aCurrentColor,
78-
const nsACString& aValue, nscolor* aResultColor,
80+
static bool ComputeColor(const StylePerDocumentStyleData* aStyleData,
81+
nscolor aCurrentColor, const nsACString& aValue,
82+
nscolor* aResultColor,
7983
bool* aWasCurrentColor = nullptr,
8084
css::Loader* aLoader = nullptr);
8185

86+
/**
87+
* Computes a StyleAbsoluteColor from the given CSS <color> value, following
88+
* the HTML spec:
89+
* https://html.spec.whatwg.org/#update-a-color-well-control-color
90+
*
91+
* @param aStyleData The style data to compute system colors and other special
92+
* color values.
93+
* @param aValue The CSS <color> value.
94+
* @param aToColorSpace The color space to convert the color into.
95+
* @return The resulting computed color value. For invalid color value, the
96+
* opaque black color will be returned.
97+
*/
98+
static StyleAbsoluteColor ComputeColorWellControlColor(
99+
const StylePerDocumentStyleData* aStyleData, const nsACString& aValue,
100+
StyleColorSpace aToColorSpace);
101+
82102
/**
83103
* Takes a CSS <color> and convert it to another color space.
84104
*

servo/ports/geckolib/glue.rs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8371,15 +8371,17 @@ pub unsafe extern "C" fn Servo_IsValidCSSColor(value: &nsACString) -> bool {
83718371
specified::Color::is_valid(&context, &mut input)
83728372
}
83738373

8374-
#[no_mangle]
8375-
pub unsafe extern "C" fn Servo_ComputeColor(
8374+
struct ComputeColorResult {
8375+
result_color: AbsoluteColor,
8376+
was_current_color: bool,
8377+
}
8378+
8379+
unsafe fn compute_color(
83768380
raw_data: Option<&PerDocumentStyleData>,
8377-
current_color: structs::nscolor,
8381+
current_color: &AbsoluteColor,
83788382
value: &nsACString,
8379-
result_color: &mut structs::nscolor,
8380-
was_current_color: *mut bool,
83818383
loader: *mut Loader,
8382-
) -> bool {
8384+
) -> Option<ComputeColorResult> {
83838385
let mut input = ParserInput::new(value.as_str_unchecked());
83848386
let mut input = Parser::new(&mut input);
83858387
let reporter = loader.as_mut().and_then(|loader| {
@@ -8407,21 +8409,51 @@ pub unsafe extern "C" fn Servo_ComputeColor(
84078409
None => None,
84088410
};
84098411

8410-
let computed = match specified::Color::parse_and_compute(&context, &mut input, device) {
8411-
Some(c) => c,
8412-
None => return false,
8413-
};
8412+
let computed = specified::Color::parse_and_compute(&context, &mut input, device)?;
8413+
8414+
let result_color = computed.resolve_to_absolute(current_color);
8415+
let was_current_color = computed.is_currentcolor();
8416+
8417+
Some(ComputeColorResult {
8418+
result_color,
8419+
was_current_color,
8420+
})
8421+
}
84148422

8423+
#[no_mangle]
8424+
pub unsafe extern "C" fn Servo_ComputeColor(
8425+
raw_data: Option<&PerDocumentStyleData>,
8426+
current_color: structs::nscolor,
8427+
value: &nsACString,
8428+
result_color: &mut structs::nscolor,
8429+
was_current_color: *mut bool,
8430+
loader: *mut Loader,
8431+
) -> bool {
84158432
let current_color = style::gecko::values::convert_nscolor_to_absolute_color(current_color);
8433+
let Some(result) = compute_color(raw_data, &current_color, value, loader) else {
8434+
return false;
8435+
};
8436+
8437+
*result_color = style::gecko::values::convert_absolute_color_to_nscolor(&result.result_color);
84168438
if !was_current_color.is_null() {
8417-
*was_current_color = computed.is_currentcolor();
8439+
*was_current_color = result.was_current_color
84188440
}
8419-
8420-
let rgba = computed.resolve_to_absolute(&current_color);
8421-
*result_color = style::gecko::values::convert_absolute_color_to_nscolor(&rgba);
84228441
true
84238442
}
84248443

8444+
// This implements https://html.spec.whatwg.org/#update-a-color-well-control-color,
8445+
// except the actual serialization steps in step 6 of "serialize a color well control color".
8446+
#[no_mangle]
8447+
pub unsafe extern "C" fn Servo_ComputeColorWellControlColor(
8448+
raw_data: Option<&PerDocumentStyleData>,
8449+
value: &nsACString,
8450+
to_color_space: ColorSpace,
8451+
) -> style::color::AbsoluteColor {
8452+
compute_color(raw_data, &AbsoluteColor::BLACK, value, ptr::null_mut())
8453+
.map_or(AbsoluteColor::BLACK, |res| res.result_color)
8454+
.to_color_space(to_color_space)
8455+
}
8456+
84258457
#[no_mangle]
84268458
pub unsafe extern "C" fn Servo_ColorTo(
84278459
from_color: &nsACString,

0 commit comments

Comments
 (0)