Skip to content

Commit

Permalink
readonly attribute should only apply on certain input types
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=240343
<rdar://93173726>

Reviewed by Chris Dumez.

We should only apply the readonly attribute on certain input types: https://html.spec.whatwg.org/multipage/input.html#do-not-apply
Except in the constraint validation case (willValidate) for compat reasons, since it affects the :in-range/:out-of-range pseudo-classes, the relevant test has been updated to reflect that.
This also was the resolution of whatwg/html#8133 in the HTML spec triage meeting.

Also introduce isMutable() which corresponds to the spec concept of mutable form control.

* LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-willValidate-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-willValidate.html:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/forms/the-input-element/show-picker-disabled-readonly-expected.txt:
* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/html/semantics/forms/constraints/form-validation-willValidate-expected.txt:
* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/html/semantics/forms/the-input-element/show-picker-disabled-readonly-expected.txt: Added.
* Source/WebCore/html/BaseDateAndTimeInputType.cpp:
(WebCore::BaseDateAndTimeInputType::valueMissing const):
(WebCore::BaseDateAndTimeInputType::handleDOMActivateEvent):
* Source/WebCore/html/ColorInputType.cpp:
(WebCore::ColorInputType::isKeyboardFocusable const):
* Source/WebCore/html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::HTMLFormControlElement):
(WebCore::HTMLFormControlElement::parseAttribute):
(WebCore::HTMLFormControlElement::computeWillValidate const):
* Source/WebCore/html/HTMLFormControlElement.h:
(WebCore::HTMLFormControlElement::supportsReadOnly const):
(WebCore::HTMLFormControlElement::isReadOnly const):
(WebCore::HTMLFormControlElement::isMutable const):
(WebCore::HTMLFormControlElement::isDisabledOrReadOnly const): Deleted.
* Source/WebCore/html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::updateType):
(WebCore::HTMLInputElement::supportsReadOnly const):
(WebCore::HTMLInputElement::showPicker):
(WebCore::HTMLInputElement::matchesReadWritePseudoClass const):
(WebCore::HTMLInputElement::createInnerTextStyle):
* Source/WebCore/html/HTMLInputElement.h:
* Source/WebCore/html/HTMLTextAreaElement.cpp:
(WebCore::HTMLTextAreaElement::matchesReadWritePseudoClass const):
* Source/WebCore/html/HTMLTextAreaElement.h:
* Source/WebCore/html/HTMLTextFormControlElement.cpp:
(WebCore::HTMLTextFormControlElement::isInnerTextElementEditable const):
* Source/WebCore/html/SearchInputType.cpp:
(WebCore::SearchInputType::handleKeydownEvent):
* Source/WebCore/html/TextFieldInputType.cpp:
(WebCore::TextFieldInputType::valueMissing const):
(WebCore::TextFieldInputType::handleKeydownEventForSpinButton):
(WebCore::TextFieldInputType::shouldSpinButtonRespondToMouseEvents const):
(WebCore::TextFieldInputType::shouldDrawCapsLockIndicator const):
(WebCore::TextFieldInputType::shouldDrawAutoFillButton const):
* Source/WebCore/html/shadow/SliderThumbElement.cpp:
(WebCore::SliderThumbElement::handleTouchEvent):
* Source/WebCore/html/shadow/TextControlInnerElements.cpp:
(WebCore::SearchFieldCancelButtonElement::defaultEventHandler):
(WebCore::SearchFieldCancelButtonElement::willRespondToMouseClickEventsWithEditability const):

Canonical link: https://commits.webkit.org/253321@main
  • Loading branch information
nt1m committed Aug 11, 2022
1 parent b3a5c93 commit af43ca3
Show file tree
Hide file tree
Showing 18 changed files with 112 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ PASS [INPUT in TIME status] Must be barred from the constraint validation if it
PASS [INPUT in TIME status] The willValidate attribute must be false if it has a datalist ancestor
PASS [INPUT in COLOR status] Must be barred from the constraint validation if it is disabled
PASS [INPUT in COLOR status] The willValidate attribute must be true if an element is mutable
FAIL [INPUT in COLOR status] Must be not barred from the constraint validation even if it is readonly assert_true: The willValidate attribute should be true. expected true got false
PASS [INPUT in COLOR status] Must be barred from the constraint validation if it is readonly
PASS [INPUT in COLOR status] The willValidate attribute must be false if it has a datalist ancestor
PASS [INPUT in FILE status] Must be barred from the constraint validation if it is disabled
PASS [INPUT in FILE status] The willValidate attribute must be true if an element is mutable
FAIL [INPUT in FILE status] Must be not barred from the constraint validation even if it is readonly assert_true: The willValidate attribute should be true. expected true got false
PASS [INPUT in FILE status] Must be barred from the constraint validation if it is readonly
PASS [INPUT in FILE status] The willValidate attribute must be false if it has a datalist ancestor
PASS [INPUT in SUBMIT status] Must be barred from the constraint validation if it is disabled
PASS [INPUT in SUBMIT status] The willValidate attribute must be true if an element is mutable
FAIL [INPUT in SUBMIT status] Must be not barred from the constraint validation even if it is readonly assert_true: The willValidate attribute should be true. expected true got false
PASS [INPUT in SUBMIT status] Must be barred from the constraint validation if it is readonly
PASS [INPUT in SUBMIT status] The willValidate attribute must be false if it has a datalist ancestor
PASS [BUTTON in SUBMIT status] Must be barred from the constraint validation
PASS [BUTTON in SUBMIT status] The willValidate attribute must be true if an element is mutable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@
},
//If an element is disabled, it is barred from constraint validation.
//The willValidate attribute must be true if an element is mutable
//If the readonly attribute is specified on an INPUT element, the element is barred from constraint validation
//(with the assumption that the readonly attribute applies).
//If the readonly attribute is specified on an INPUT element, the element is barred from constraint validation.
{
tag: "input",
types: ["text", "search", "tel", "url", "email", "password", "datetime-local", "date", "month", "week", "time"],
Expand All @@ -53,14 +52,14 @@
{conditions: {disabled: false, readOnly: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"},
]
},
//In the following cases, the readonly attribute does not apply.
//In the following cases, the readonly attribute does not apply, however we should still bar the element from constraint validation.
{
tag: "input",
types: ["color", "file", "submit"],
testData: [
{conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is disabled"},
{conditions: {disabled: false, readOnly: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"},
{conditions: {readOnly: true}, expected: true, name: "[target] Must be not barred from the constraint validation even if it is readonly"},
{conditions: {readOnly: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is readonly"},
{conditions: {disabled: false, readOnly: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"},
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ PASS input[type=text] showPicker() throws when disabled
PASS input[type=time] showPicker() throws when disabled
PASS input[type=url] showPicker() throws when disabled
PASS input[type=week] showPicker() throws when disabled
FAIL input[type=button] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
FAIL input[type=checkbox] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
FAIL input[type=color] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
PASS input[type=button] showPicker() doesn't throw when readonly
PASS input[type=checkbox] showPicker() doesn't throw when readonly
PASS input[type=color] showPicker() doesn't throw when readonly
PASS input[type=date] showPicker() throws when readonly
PASS input[type=datetime-local] showPicker() throws when readonly
PASS input[type=email] showPicker() throws when readonly
FAIL input[type=file] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
FAIL input[type=hidden] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
FAIL input[type=image] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
PASS input[type=file] showPicker() doesn't throw when readonly
PASS input[type=hidden] showPicker() doesn't throw when readonly
PASS input[type=image] showPicker() doesn't throw when readonly
PASS input[type=month] showPicker() throws when readonly
PASS input[type=number] showPicker() throws when readonly
PASS input[type=password] showPicker() throws when readonly
FAIL input[type=radio] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
FAIL input[type=range] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
FAIL input[type=reset] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
PASS input[type=radio] showPicker() doesn't throw when readonly
PASS input[type=range] showPicker() doesn't throw when readonly
PASS input[type=reset] showPicker() doesn't throw when readonly
PASS input[type=search] showPicker() throws when readonly
FAIL input[type=submit] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
PASS input[type=submit] showPicker() doesn't throw when readonly
PASS input[type=tel] showPicker() throws when readonly
PASS input[type=text] showPicker() throws when readonly
PASS input[type=time] showPicker() throws when readonly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ PASS [INPUT in TIME status] Must be barred from the constraint validation if it
FAIL [INPUT in TIME status] The willValidate attribute must be false if it has a datalist ancestor assert_false: The willValidate attribute should be false. expected false got true
PASS [INPUT in COLOR status] Must be barred from the constraint validation if it is disabled
PASS [INPUT in COLOR status] The willValidate attribute must be true if an element is mutable
FAIL [INPUT in COLOR status] Must be not barred from the constraint validation even if it is readonly assert_true: The willValidate attribute should be true. expected true got false
PASS [INPUT in COLOR status] Must be barred from the constraint validation if it is readonly
FAIL [INPUT in COLOR status] The willValidate attribute must be false if it has a datalist ancestor assert_false: The willValidate attribute should be false. expected false got true
PASS [INPUT in FILE status] Must be barred from the constraint validation if it is disabled
PASS [INPUT in FILE status] The willValidate attribute must be true if an element is mutable
FAIL [INPUT in FILE status] Must be not barred from the constraint validation even if it is readonly assert_true: The willValidate attribute should be true. expected true got false
PASS [INPUT in FILE status] Must be barred from the constraint validation if it is readonly
FAIL [INPUT in FILE status] The willValidate attribute must be false if it has a datalist ancestor assert_false: The willValidate attribute should be false. expected false got true
PASS [INPUT in SUBMIT status] Must be barred from the constraint validation if it is disabled
PASS [INPUT in SUBMIT status] The willValidate attribute must be true if an element is mutable
FAIL [INPUT in SUBMIT status] Must be not barred from the constraint validation even if it is readonly assert_true: The willValidate attribute should be true. expected true got false
PASS [INPUT in SUBMIT status] Must be barred from the constraint validation if it is readonly
FAIL [INPUT in SUBMIT status] The willValidate attribute must be false if it has a datalist ancestor assert_false: The willValidate attribute should be false. expected false got true
PASS [BUTTON in SUBMIT status] Must be barred from the constraint validation
PASS [BUTTON in SUBMIT status] The willValidate attribute must be true if an element is mutable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

PASS input[type=button] showPicker() throws when disabled
PASS input[type=checkbox] showPicker() throws when disabled
PASS input[type=color] showPicker() throws when disabled
PASS input[type=date] showPicker() throws when disabled
PASS input[type=datetime-local] showPicker() throws when disabled
PASS input[type=email] showPicker() throws when disabled
PASS input[type=file] showPicker() throws when disabled
PASS input[type=hidden] showPicker() throws when disabled
PASS input[type=image] showPicker() throws when disabled
PASS input[type=month] showPicker() throws when disabled
PASS input[type=number] showPicker() throws when disabled
PASS input[type=password] showPicker() throws when disabled
PASS input[type=radio] showPicker() throws when disabled
PASS input[type=range] showPicker() throws when disabled
PASS input[type=reset] showPicker() throws when disabled
PASS input[type=search] showPicker() throws when disabled
PASS input[type=submit] showPicker() throws when disabled
PASS input[type=tel] showPicker() throws when disabled
PASS input[type=text] showPicker() throws when disabled
PASS input[type=time] showPicker() throws when disabled
PASS input[type=url] showPicker() throws when disabled
PASS input[type=week] showPicker() throws when disabled
PASS input[type=button] showPicker() doesn't throw when readonly
PASS input[type=checkbox] showPicker() doesn't throw when readonly
FAIL input[type=color] showPicker() doesn't throw when readonly assert_throws_dom: function "() => { input.showPicker(); }" threw object "InvalidStateError: Input showPicker() cannot be used on immutable controls." that is not a DOMException NotAllowedError: property "code" is equal to 11, expected 0
PASS input[type=date] showPicker() throws when readonly
PASS input[type=datetime-local] showPicker() throws when readonly
PASS input[type=email] showPicker() throws when readonly
PASS input[type=file] showPicker() doesn't throw when readonly
PASS input[type=hidden] showPicker() doesn't throw when readonly
PASS input[type=image] showPicker() doesn't throw when readonly
PASS input[type=month] showPicker() throws when readonly
PASS input[type=number] showPicker() throws when readonly
PASS input[type=password] showPicker() throws when readonly
PASS input[type=radio] showPicker() doesn't throw when readonly
PASS input[type=range] showPicker() doesn't throw when readonly
PASS input[type=reset] showPicker() doesn't throw when readonly
PASS input[type=search] showPicker() throws when readonly
PASS input[type=submit] showPicker() doesn't throw when readonly
PASS input[type=tel] showPicker() throws when readonly
PASS input[type=text] showPicker() throws when readonly
PASS input[type=time] showPicker() throws when readonly
PASS input[type=url] showPicker() throws when readonly
PASS input[type=week] showPicker() throws when readonly

4 changes: 2 additions & 2 deletions Source/WebCore/html/BaseDateAndTimeInputType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ bool BaseDateAndTimeInputType::shouldRespectListAttribute()
bool BaseDateAndTimeInputType::valueMissing(const String& value) const
{
ASSERT(element());
return !element()->isDisabledOrReadOnly() && element()->isRequired() && value.isEmpty();
return element()->isMutable() && element()->isRequired() && value.isEmpty();
}

bool BaseDateAndTimeInputType::isKeyboardFocusable(KeyboardEvent*) const
Expand Down Expand Up @@ -288,7 +288,7 @@ void BaseDateAndTimeInputType::setValue(const String& value, bool valueChanged,
void BaseDateAndTimeInputType::handleDOMActivateEvent(Event&)
{
ASSERT(element());
if (element()->isDisabledOrReadOnly() || !element()->renderer() || !UserGestureIndicator::processingUserGesture())
if (!element()->isMutable() || !element()->renderer() || !UserGestureIndicator::processingUserGesture())
return;

if (m_dateTimeChooser)
Expand Down
3 changes: 0 additions & 3 deletions Source/WebCore/html/ColorInputType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,6 @@ bool ColorInputType::isKeyboardFocusable(KeyboardEvent*) const
{
ASSERT(element());
#if PLATFORM(IOS_FAMILY)
if (element()->isReadOnly())
return false;

return element()->isTextFormControlFocusable();
#else
return false;
Expand Down
14 changes: 8 additions & 6 deletions Source/WebCore/html/HTMLFormControlElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Doc
: LabelableElement(tagName, document)
, FormAssociatedElement(form)
, m_disabled(false)
, m_isReadOnly(false)
, m_hasReadOnlyAttribute(false)
, m_isRequired(false)
, m_valueMatchesRenderer(false)
, m_disabledByAncestorFieldset(false)
Expand Down Expand Up @@ -167,10 +167,11 @@ void HTMLFormControlElement::parseAttribute(const QualifiedName& name, const Ato
}
}
} else if (name == readonlyAttr) {
bool newReadOnly = !value.isNull();
if (m_isReadOnly != newReadOnly) {
Style::PseudoClassChangeInvalidation readOnlyInvalidation(*this, { { CSSSelector::PseudoClassReadOnly, newReadOnly }, { CSSSelector::PseudoClassReadWrite, !newReadOnly } });
m_isReadOnly = newReadOnly;
bool newHasReadOnlyAttribute = !value.isNull();
if (m_hasReadOnlyAttribute != newHasReadOnlyAttribute) {
bool newMatchesReadWrite = supportsReadOnly() && !newHasReadOnlyAttribute;
Style::PseudoClassChangeInvalidation readWriteInvalidation(*this, { { CSSSelector::PseudoClassReadWrite, newMatchesReadWrite }, { CSSSelector::PseudoClassReadOnly, !newMatchesReadWrite } });
m_hasReadOnlyAttribute = newHasReadOnlyAttribute;
readOnlyStateChanged();
}
} else if (name == requiredAttr) {
Expand Down Expand Up @@ -377,7 +378,8 @@ bool HTMLFormControlElement::computeWillValidate() const
m_dataListAncestorState = NotInsideDataList;
#endif
}
return m_dataListAncestorState == NotInsideDataList && !isDisabledOrReadOnly();
// readonly bars constraint validation for *all* <input> elements, regardless of the <input> type, for compat reasons.
return m_dataListAncestorState == NotInsideDataList && !isDisabledFormControl() && !m_hasReadOnlyAttribute;
}

bool HTMLFormControlElement::willValidate() const
Expand Down
8 changes: 5 additions & 3 deletions Source/WebCore/html/HTMLFormControlElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ class HTMLFormControlElement : public LabelableElement, public FormAssociatedEle
void updateValidity();
void setCustomValidity(const String&) override;

bool isReadOnly() const { return m_isReadOnly; }
bool isDisabledOrReadOnly() const { return isDisabledFormControl() || m_isReadOnly; }
virtual bool supportsReadOnly() const { return false; }
bool isReadOnly() const { return supportsReadOnly() && m_hasReadOnlyAttribute; }
bool isMutable() const { return !isDisabledFormControl() && !isReadOnly(); }
void updateReadOnlyState();

WEBCORE_EXPORT String autocomplete() const;
WEBCORE_EXPORT void setAutocomplete(const AtomString&);
Expand Down Expand Up @@ -197,7 +199,7 @@ class HTMLFormControlElement : public LabelableElement, public FormAssociatedEle
bool m_isFocusingWithValidationMessage { false };

unsigned m_disabled : 1;
unsigned m_isReadOnly : 1;
unsigned m_hasReadOnlyAttribute : 1;
unsigned m_isRequired : 1;
unsigned m_valueMatchesRenderer : 1;
unsigned m_disabledByAncestorFieldset : 1;
Expand Down

0 comments on commit af43ca3

Please sign in to comment.