Skip to content

Commit

Permalink
Enhance <input type=color>: infrastructure for alpha and colorspace a…
Browse files Browse the repository at this point in the history
…ttributes

https://bugs.webkit.org/show_bug.cgi?id=281213
rdar://137663314

Reviewed by NOBODY (OOPS!).

Add the necessary infrastructure for
whatwg/html#10456. In particular:

- Support the alpha and colorspace attributes behind a preference.
- When the preference is enabled vastly expand the CSS color parsing
  and serialization capabilities of <input type=color>.

A future change will adjust the UI accordingly and add the necessary
tests.

* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* Source/WebCore/html/ColorInputType.cpp:
(WebCore::colorParsingParameters):
(WebCore::parseColorValue):
(WebCore::serializeColorValue):
(WebCore::ColorInputType::fallbackValue const):
(WebCore::ColorInputType::sanitizeValue const):
(WebCore::ColorInputType::valueAsColor const):
(WebCore::ColorInputType::typeMismatchFor const):
(WebCore::ColorInputType::didChooseColor):
(WebCore::ColorInputType::suggestedColors const):
(WebCore::ColorInputType::selectColor):
* Source/WebCore/html/HTMLAttributeNames.in:
* Source/WebCore/html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::alpha):
(WebCore::HTMLInputElement::colorSpace):
(WebCore::HTMLInputElement::setColorSpace):
* Source/WebCore/html/HTMLInputElement.h:
* Source/WebCore/html/HTMLInputElement.idl:
  • Loading branch information
annevk committed Oct 10, 2024
1 parent 1146c56 commit 3cfc55f
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 14 deletions.
15 changes: 15 additions & 0 deletions Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3567,6 +3567,21 @@ InputTypeColorEnabled:
default: false
sharedPreferenceForWebProcess: true

InputTypeColorEnhancementsEnabled:
type: bool
status: unstable
category: html
condition: ENABLE(INPUT_TYPE_COLOR)
humanReadableName: "HTML alpha and colorspace attribute support for color inputs"
humanReadableDescription: "Enable HTML alpha and colorspace attribute support for input elements of type color"
defaultValue:
WebKitLegacy:
default: false
WebKit:
default: false
WebCore:
default: false

InputTypeDateEnabled:
type: bool
status: embedder
Expand Down
90 changes: 77 additions & 13 deletions Source/WebCore/html/ColorInputType.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
* Copyright (C) 2015-2020 Apple Inc. All rights reserved.
* Copyright (C) 2015-2024 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
Expand Down Expand Up @@ -37,6 +37,7 @@

#include "AXObjectCache.h"
#include "CSSPropertyNames.h"
#include "CSSPropertyParserConsumer+Color.h"
#include "Chrome.h"
#include "Color.h"
#include "ColorSerialization.h"
Expand Down Expand Up @@ -64,6 +65,12 @@ WTF_MAKE_TZONE_ALLOCATED_IMPL(ColorInputType);

using namespace HTMLNames;

using LazySlowPathColorParsingParameters = std::tuple<
CSSPropertyParserHelpers::CSSColorParsingOptions,
CSSUnresolvedColorResolutionState,
std::optional<CSSUnresolvedColorResolutionDelegate>
>;

// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-simple-colour
static bool isValidSimpleColor(StringView string)
{
Expand All @@ -86,6 +93,56 @@ static std::optional<SRGBA<uint8_t>> parseSimpleColorValue(StringView string)
return { { toASCIIHexValue(string[1], string[2]), toASCIIHexValue(string[3], string[4]), toASCIIHexValue(string[5], string[6]) } };
}

static LazySlowPathColorParsingParameters colorParsingParameters()
{
return {
CSSPropertyParserHelpers::CSSColorParsingOptions {
.allowedColorTypes = { StyleColor::CSSColorType::Absolute, StyleColor::CSSColorType::Current, StyleColor::CSSColorType::System }
},
CSSUnresolvedColorResolutionState {
.resolvedCurrentColor = Color::black
},
std::nullopt
};
}

static std::optional<Color> parseColorValue(StringView string, HTMLInputElement& context)
{
if (context.colorSpace().isNull())
return parseSimpleColorValue(string);

auto color = CSSPropertyParserHelpers::parseColorRaw(string.toString(), CSSParserContext(HTMLStandardMode), [&] {
return colorParsingParameters();
});

if (!color.isValid())
return { };

return color;
}

static String serializeColorValue(Color input, HTMLInputElement& context)
{
auto colorSpace = context.colorSpace();
auto alpha = !colorSpace.isNull() && context.alpha();

if (!alpha)
input = input.opaqueColor();

if (colorSpace.isNull() || colorSpace == "limited-srgb"_s) {
auto inputAsRGBA = input.toColorTypeLossy<SRGBA<uint8_t>>();
if (alpha)
input = { inputAsRGBA, { Color::Flags::UseColorFunctionSerialization } };
else
input = inputAsRGBA.resolved();
} else {
// FIXME: Ideally this would not be a lossy conversion and preserve colors such as color(display-p3 2 2 2).
input = input.toColorTypeLossy<DisplayP3<float>>().resolved();
}

return serializationForHTML(input);
}

ColorInputType::~ColorInputType()
{
endColorChooser();
Expand Down Expand Up @@ -124,21 +181,27 @@ bool ColorInputType::supportsRequired() const

String ColorInputType::fallbackValue() const
{
return "#000000"_s;
ASSERT(element());
return serializeColorValue(Color::black, *element());
}

String ColorInputType::sanitizeValue(const String& proposedValue) const
{
if (!isValidSimpleColor(proposedValue))
ASSERT(element());
auto color = parseColorValue(proposedValue, *element());

if (!color)
return fallbackValue();

return proposedValue.convertToASCIILowercase();
return serializeColorValue(*color, *element());
}

Color ColorInputType::valueAsColor() const
{
ASSERT(element());
return parseSimpleColorValue(element()->value()).value();
auto color = parseColorValue(element()->value(), *element());
ASSERT(!!color);
return *color;
}

void ColorInputType::createShadowSubtree()
Expand Down Expand Up @@ -231,7 +294,8 @@ bool ColorInputType::shouldRespectListAttribute()

bool ColorInputType::typeMismatchFor(const String& value) const
{
return !isValidSimpleColor(value);
ASSERT(element());
return !!parseColorValue(value, *element());
}

bool ColorInputType::shouldResetOnDocumentActivation()
Expand All @@ -246,13 +310,12 @@ void ColorInputType::didChooseColor(const Color& color)
if (element()->isDisabledFormControl())
return;

auto sRGBAColor = color.toColorTypeLossy<SRGBA<uint8_t>>().resolved();
auto sRGBColor = sRGBAColor.colorWithAlphaByte(255);
if (sRGBColor == valueAsColor())
auto serializedColor = serializeColorValue(color, *element());
if (serializedColor == element()->value())
return;

EventQueueScope scope;
element()->setValueFromRenderer(serializationForHTML(sRGBColor));
element()->setValueFromRenderer(serializedColor);
updateColorSwatch();
element()->dispatchFormControlChangeEvent();

Expand Down Expand Up @@ -312,7 +375,7 @@ Vector<Color> ColorInputType::suggestedColors() const
ASSERT(element());
if (auto dataList = element()->dataList()) {
for (auto& option : dataList->suggestions()) {
if (auto color = parseSimpleColorValue(option.value()))
if (auto color = parseColorValue(option.value(), *element()))
suggestions.append(*color);
}
}
Expand All @@ -322,8 +385,9 @@ Vector<Color> ColorInputType::suggestedColors() const

void ColorInputType::selectColor(StringView string)
{
if (auto color = parseSimpleColorValue(string))
didChooseColor(color.value());
ASSERT(element());
if (auto color = parseColorValue(string, *element()))
didChooseColor(*color);
}

} // namespace WebCore
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/html/HTMLAttributeNames.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ align
alink
allow
allowfullscreen
alpha
alt
archive
aria-activedescendant
Expand Down Expand Up @@ -103,6 +104,7 @@ code
codebase
codetype
color
colorspace
cols
colspan
command
Expand Down
21 changes: 21 additions & 0 deletions Source/WebCore/html/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,27 @@ void HTMLInputElement::setShowAutoFillButton(AutoFillButtonType autoFillButtonTy
cache->autofillTypeChanged(*this);
}

bool HTMLInputElement::alpha()
{
return hasAttributeWithoutSynchronization(alphaAttr);
}

String HTMLInputElement::colorSpace()
{
if (!document().settings().inputTypeColorEnhancementsEnabled())
return nullString();

if (equalLettersIgnoringASCIICase(attributeWithoutSynchronization(colorspaceAttr), "display-p3"_s))
return "display-p3"_s;
return "limited-srgb"_s;
}

void HTMLInputElement::setColorSpace(const AtomString& value)
{
ASSERT(document().settings().inputTypeColorEnhancementsEnabled());
setAttributeWithoutSynchronization(colorspaceAttr, value);
}

FileList* HTMLInputElement::files()
{
if (auto* fileInputType = dynamicDowncast<FileInputType>(*m_inputType))
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/html/HTMLInputElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ class HTMLInputElement final : public HTMLTextFormControlElement {
static Ref<HTMLInputElement> create(const QualifiedName&, Document&, HTMLFormElement*, bool createdByParser);
virtual ~HTMLInputElement();

bool alpha();
bool checked() const { return m_isChecked; }
WEBCORE_EXPORT void setChecked(bool, WasSetByJavaScript = WasSetByJavaScript::Yes);
String colorSpace();
void setColorSpace(const AtomString&);
WEBCORE_EXPORT FileList* files();
WEBCORE_EXPORT void setFiles(RefPtr<FileList>&&, WasSetByJavaScript = WasSetByJavaScript::No);
FileList* filesForBindings() { return files(); }
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/html/HTMLInputElement.idl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2006-2024 Apple Inc. All rights reserved.
* Copyright (C) 2006 Samuel Weinig <[email protected]>
* Copyright (C) 2012 Samsung Electronics. All rights reserved.
*
Expand All @@ -25,10 +25,12 @@
Exposed=Window
] interface HTMLInputElement : HTMLElement {
[CEReactions=NotNeeded, Reflect] attribute DOMString accept;
[EnabledBySetting=InputTypeColorEnhancementsEnabled, CEReactions=NotNeeded, Reflect] attribute boolean alpha;
[CEReactions=NotNeeded, Reflect] attribute DOMString alt;
[CEReactions=NotNeeded] attribute [AtomString] DOMString autocomplete;
[CEReactions=NotNeeded, Reflect=checked] attribute boolean defaultChecked;
attribute boolean checked;
[EnabledBySetting=InputTypeColorEnhancementsEnabled, CEReactions=NotNeeded] attribute [AtomString] DOMString colorSpace;
[CEReactions=NotNeeded, Reflect] attribute DOMString dirName;
[CEReactions=NotNeeded, Reflect] attribute boolean disabled;
readonly attribute HTMLFormElement form;
Expand Down

0 comments on commit 3cfc55f

Please sign in to comment.