From 71838e2f11b35d415f0ffc0db2f709a906748b47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?=
Date: Tue, 16 Jan 2024 11:09:01 +0000
Subject: [PATCH] Bug 1850295 - Update :user-{valid,invalid} to follow the
spec. r=smaug
(Modulo open spec issues linked in comments)
Differential Revision: https://phabricator.services.mozilla.com/D196986
---
dom/html/HTMLButtonElement.h | 2 +-
dom/html/HTMLElement.h | 2 +-
dom/html/HTMLFormElement.cpp | 68 ++++---------
dom/html/HTMLFormElement.h | 11 +--
dom/html/HTMLInputElement.cpp | 92 ++++++++----------
dom/html/HTMLInputElement.h | 50 +---------
dom/html/HTMLSelectElement.cpp | 95 ++++++++-----------
dom/html/HTMLSelectElement.h | 48 ++--------
dom/html/HTMLTextAreaElement.cpp | 62 ++++--------
dom/html/HTMLTextAreaElement.h | 40 ++------
dom/html/nsGenericHTMLElement.cpp | 6 --
dom/html/nsGenericHTMLElement.h | 7 +-
dom/html/nsIConstraintValidation.cpp | 7 --
dom/html/test/test_bug605124-1.html | 17 ++--
dom/html/test/test_bug605125-2.html | 25 ++---
dom/html/test/test_bug610687.html | 68 ++++++-------
dom/webidl/HTMLSelectElement.webidl | 7 +-
layout/forms/HTMLSelectEventListener.cpp | 10 +-
...put-checkbox-required-invalid-changed.html | 16 ----
.../input/input-customerror.html | 18 ----
.../input/input-dyn-not-disabled.html | 19 ----
.../input/input-dyn-not-readonly-changed.html | 11 ---
.../input/input-email-invalid-changed.html | 11 ---
.../input-file-required-invalid-changed.html | 11 ---
.../input/input-pattern-invalid-changed.html | 12 ---
.../input/input-radio-customerror.html | 18 ----
.../input-radio-nogroup-required-valid.html | 13 ---
.../input-radio-required-invalid-changed.html | 16 ----
.../input/input-radio-required.html | 18 ----
.../input/input-required-invalid-changed.html | 11 ---
.../input/input-type-invalid.html | 20 ----
.../input/input-url-invalid-changed.html | 12 ---
.../css-ui-invalid/input/reftest.list | 14 ---
.../css-ui-invalid/select/reftest.list | 5 -
.../select/select-dyn-not-disabled.html | 19 ----
.../select/select-fieldset-legend-ref.html | 10 --
.../select/select-fieldset-legend.html | 22 -----
.../css-ui-invalid/select/select-invalid.html | 10 --
.../select-required-invalid-changed-1.html | 13 ---
.../select-required-invalid-changed-2.html | 13 ---
.../css-ui-invalid/textarea/reftest.list | 4 -
.../textarea/textarea-customerror.html | 18 ----
.../textarea/textarea-dyn-not-disabled.html | 20 ----
.../textarea-dyn-not-readonly-changed.html | 19 ----
.../textarea-required-invalid-changed.html | 19 ----
.../input/input-checkbox-valid-changed.html | 15 ---
.../input/input-dyn-not-disabled-changed.html | 11 ---
.../input/input-dyn-not-readonly-changed.html | 11 ---
.../input/input-email-valid-changed.html | 11 ---
.../input/input-file-valid-changed.html | 10 --
.../input/input-pattern-valid-changed.html | 11 ---
.../input/input-radio-customerror.html | 16 ----
.../input/input-radio-dyn-valid-1.html | 15 ---
.../input/input-radio-dyn-valid-2.html | 16 ----
.../input-radio-nogroup-required-invalid.html | 13 ---
.../input/input-required-valid-changed.html | 11 ---
.../input/input-url-valid-changed.html | 11 ---
.../reftests/css-ui-valid/input/reftest.list | 12 ---
.../reftests/css-ui-valid/select/reftest.list | 3 -
...elect-required-multiple-valid-changed.html | 24 -----
.../select-required-valid-changed-1.html | 12 ---
.../select-required-valid-changed-2.html | 12 ---
.../css-ui-valid/textarea/reftest.list | 3 -
.../textarea-dyn-not-disabled-changed.html | 19 ----
.../textarea-dyn-not-readonly-changed.html | 19 ----
.../textarea-required-valid-changed.html | 11 ---
.../meta/css/selectors/user-invalid.html.ini | 13 ---
.../meta/css/selectors/user-valid.html.ini | 8 --
toolkit/actors/SelectChild.sys.mjs | 16 +---
69 files changed, 203 insertions(+), 1109 deletions(-)
delete mode 100644 layout/reftests/css-ui-invalid/input/input-checkbox-required-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-customerror.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-dyn-not-disabled.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-dyn-not-readonly-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-email-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-file-required-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-pattern-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-radio-customerror.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-radio-nogroup-required-valid.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-radio-required-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-radio-required.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-required-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-type-invalid.html
delete mode 100644 layout/reftests/css-ui-invalid/input/input-url-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/select/select-dyn-not-disabled.html
delete mode 100644 layout/reftests/css-ui-invalid/select/select-fieldset-legend-ref.html
delete mode 100644 layout/reftests/css-ui-invalid/select/select-fieldset-legend.html
delete mode 100644 layout/reftests/css-ui-invalid/select/select-invalid.html
delete mode 100644 layout/reftests/css-ui-invalid/select/select-required-invalid-changed-1.html
delete mode 100644 layout/reftests/css-ui-invalid/select/select-required-invalid-changed-2.html
delete mode 100644 layout/reftests/css-ui-invalid/textarea/textarea-customerror.html
delete mode 100644 layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-disabled.html
delete mode 100644 layout/reftests/css-ui-invalid/textarea/textarea-dyn-not-readonly-changed.html
delete mode 100644 layout/reftests/css-ui-invalid/textarea/textarea-required-invalid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-checkbox-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-dyn-not-disabled-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-dyn-not-readonly-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-email-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-file-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-pattern-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-radio-customerror.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-radio-dyn-valid-1.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-radio-dyn-valid-2.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-radio-nogroup-required-invalid.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-required-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/input/input-url-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/select/select-required-multiple-valid-changed.html
delete mode 100644 layout/reftests/css-ui-valid/select/select-required-valid-changed-1.html
delete mode 100644 layout/reftests/css-ui-valid/select/select-required-valid-changed-2.html
delete mode 100644 layout/reftests/css-ui-valid/textarea/textarea-dyn-not-disabled-changed.html
delete mode 100644 layout/reftests/css-ui-valid/textarea/textarea-dyn-not-readonly-changed.html
delete mode 100644 layout/reftests/css-ui-valid/textarea/textarea-required-valid-changed.html
delete mode 100644 testing/web-platform/meta/css/selectors/user-invalid.html.ini
delete mode 100644 testing/web-platform/meta/css/selectors/user-valid.html.ini
diff --git a/dom/html/HTMLButtonElement.h b/dom/html/HTMLButtonElement.h
index 75839df7227d..57bc51c05ce8 100644
--- a/dom/html/HTMLButtonElement.h
+++ b/dom/html/HTMLButtonElement.h
@@ -69,7 +69,7 @@ class HTMLButtonElement final : public nsGenericHTMLFormControlElementWithState,
void DoneCreatingElement() override;
void UpdateBarredFromConstraintValidation();
- void UpdateValidityElementStates(bool aNotify) final;
+ void UpdateValidityElementStates(bool aNotify);
/**
* Called when an attribute is about to be changed
*/
diff --git a/dom/html/HTMLElement.h b/dom/html/HTMLElement.h
index 455eb992f114..8fbbc6f2b9f4 100644
--- a/dom/html/HTMLElement.h
+++ b/dom/html/HTMLElement.h
@@ -49,7 +49,7 @@ class HTMLElement final : public nsGenericHTMLFormElement {
void AfterClearForm(bool aUnbindOrDelete) override;
void FieldSetDisabledChanged(bool aNotify) override;
void SaveState() override;
- void UpdateValidityElementStates(bool aNotify) final;
+ void UpdateValidityElementStates(bool aNotify);
void UpdateFormOwner();
diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp
index 6868da337bca..85472708057e 100644
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -121,7 +121,6 @@ HTMLFormElement::HTMLFormElement(
mDeferSubmission(false),
mNotifiedObservers(false),
mNotifiedObserversResult(false),
- mEverTriedInvalidSubmit(false),
mIsConstructingEntryList(false),
mIsFiringSubmissionEvents(false) {
// We start out valid.
@@ -261,16 +260,28 @@ void HTMLFormElement::MaybeSubmit(Element* aSubmitter) {
return;
}
- // 6.1. If form's firing submission events is true, then return.
+ // 5.1. If form's firing submission events is true, then return.
if (mIsFiringSubmissionEvents) {
return;
}
- // 6.2. Set form's firing submission events to true.
+ // 5.2. Set form's firing submission events to true.
AutoRestore resetFiringSubmissionEventsFlag(mIsFiringSubmissionEvents);
mIsFiringSubmissionEvents = true;
- // 6.3. If the submitter element's no-validate state is false, then
+ // Flag elements as user-interacted.
+ // FIXME: Should be specified, see:
+ // https://github.com/whatwg/html/issues/10066
+ {
+ for (nsGenericHTMLFormElement* el : mControls->mElements) {
+ el->SetUserInteracted(true);
+ }
+ for (nsGenericHTMLFormElement* el : mControls->mNotInElements) {
+ el->SetUserInteracted(true);
+ }
+ }
+
+ // 5.3. If the submitter element's no-validate state is false, then
// interactively validate the constraints of form and examine the result.
// If the result is negative (i.e., the constraint validation concluded
// that there were invalid fields and probably informed the user of this)
@@ -635,7 +646,6 @@ nsresult HTMLFormElement::DoReset() {
doc->FlushPendingNotifications(FlushType::ContentAndNotify);
}
- mEverTriedInvalidSubmit = false;
// JBK walk the elements[] array instead of form frame controls - bug 34297
uint32_t numElements = mControls->Length();
for (uint32_t elementX = 0; elementX < numElements; ++elementX) {
@@ -1051,15 +1061,11 @@ nsresult HTMLFormElement::ConstructEntryList(FormData* aFormData) {
nsresult rv = mControls->GetSortedControls(sortedControls);
NS_ENSURE_SUCCESS(rv, rv);
- uint32_t len = sortedControls.Length();
-
- //
// Walk the list of nodes and call SubmitNamesValues() on the controls
- //
- for (uint32_t i = 0; i < len; ++i) {
+ for (nsGenericHTMLFormElement* control : sortedControls) {
// Disabled elements don't submit
- if (!sortedControls[i]->IsDisabled()) {
- nsCOMPtr fc = do_QueryInterface(sortedControls[i]);
+ if (!control->IsDisabled()) {
+ nsCOMPtr fc = do_QueryInterface(control);
MOZ_ASSERT(fc);
// Tell the control to submit its name/value pairs to the submission
fc->SubmitNamesValues(aFormData);
@@ -1768,44 +1774,6 @@ bool HTMLFormElement::CheckValidFormSubmission() {
return true;
}
- // For the first invalid submission, we should update element states.
- // We have to do that _before_ calling the observers so we are sure they
- // will not interfere (like focusing the element).
- if (!mEverTriedInvalidSubmit) {
- mEverTriedInvalidSubmit = true;
-
- /*
- * We are going to call update states assuming elements want to
- * be notified because we can't know.
- * Submissions shouldn't happen during parsing so it _should_ be safe.
- */
-
- nsAutoScriptBlocker scriptBlocker;
-
- for (nsGenericHTMLFormElement* element : mControls->mElements) {
- // Input elements can trigger a form submission and we want to
- // update the style in that case.
- if (auto* input = HTMLInputElement::FromNode(*element)) {
- // We don't use nsContentUtils::IsFocusedContent here, because it
- // doesn't really do what we want for number controls: it's true
- // for the anonymous textnode inside, but not the number control
- // itself. We can use the focus state, though, because that gets
- // synced to the number control by the anonymous text control.
- if (input->State().HasState(ElementState::FOCUS)) {
- input->UpdateValidityUIBits(true);
- }
- }
- element->UpdateValidityElementStates(true);
- }
-
- // Because of backward compatibility, is not in
- // elements but can be invalid.
- // TODO: should probably be removed when bug 606491 will be fixed.
- for (nsGenericHTMLFormElement* element : mControls->mNotInElements) {
- element->UpdateValidityElementStates(true);
- }
- }
-
AutoJSAPI jsapi;
if (!jsapi.Init(GetOwnerGlobal())) {
return false;
diff --git a/dom/html/HTMLFormElement.h b/dom/html/HTMLFormElement.h
index 42593b4e9185..1820ebbfa4fa 100644
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -233,16 +233,7 @@ class HTMLFormElement final : public nsGenericHTMLElement,
* @param aFormData the form data object
*/
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
- MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ConstructEntryList(FormData* aFormData);
-
- /**
- * Whether the submission of this form has been ever prevented because of
- * being invalid.
- *
- * @return Whether the submission of this form has been prevented because of
- * being invalid.
- */
- bool HasEverTriedInvalidSubmit() const { return mEverTriedInvalidSubmit; }
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ConstructEntryList(FormData*);
/**
* Implements form[name]. Returns form controls in this form with the correct
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index e225748daa84..ae2c4af82c38 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -248,7 +248,7 @@ class DispatchChangeEventCallback final : public GetFilesCallback {
RefPtr inputElement(mInputElement);
nsresult rv = nsContentUtils::DispatchInputEvent(inputElement);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch input event");
-
+ mInputElement->SetUserInteracted(true);
rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
mInputElement, u"change"_ns,
CanBubble::eYes, Cancelable::eNo);
@@ -667,6 +667,7 @@ nsColorPickerShownCallback::Done(const nsAString& aColor) {
}
if (mValueChanged) {
+ mInput->SetUserInteracted(true);
rv = nsContentUtils::DispatchTrustedEvent(
mInput->OwnerDoc(), static_cast(mInput.get()), u"change"_ns,
CanBubble::eYes, Cancelable::eNo);
@@ -995,6 +996,7 @@ HTMLInputElement::HTMLInputElement(already_AddRefed&& aNodeInfo,
mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
mDisabledChanged(false),
mValueChanged(false),
+ mUserInteracted(false),
mLastValueChangeWasInteractive(false),
mCheckedChanged(false),
mChecked(false),
@@ -1006,8 +1008,6 @@ HTMLInputElement::HTMLInputElement(already_AddRefed&& aNodeInfo,
mCheckedIsToggled(false),
mIndeterminate(false),
mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT),
- mCanShowValidUI(true),
- mCanShowInvalidUI(true),
mHasRange(false),
mIsDraggingRange(false),
mNumberControlSpinnerIsSpinning(false),
@@ -2613,15 +2613,25 @@ void HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged) {
}
void HTMLInputElement::FireChangeEventIfNeeded() {
+ if (!MayFireChangeOnBlur()) {
+ return;
+ }
+
// We're not exposing the GetValue return value anywhere here, so it's safe to
// claim to be a system caller.
nsAutoString value;
GetValue(value, CallerType::System);
- if (!MayFireChangeOnBlur() || mFocusedValue.Equals(value)) {
+ // NOTE(emilio): Per spec we should not set this if we don't fire the change
+ // event, but that seems like a bug. Using mValueChanged seems reasonable to
+ // keep the expected behavior while
+ // https://github.com/whatwg/html/issues/10013 is resolved.
+ if (mValueChanged) {
+ SetUserInteracted(true);
+ }
+ if (mFocusedValue.Equals(value)) {
return;
}
-
// Dispatch the change event.
mFocusedValue = value;
nsContentUtils::DispatchTrustedEvent(
@@ -3146,9 +3156,9 @@ bool HTMLInputElement::CheckActivationBehaviorPreconditions(
// we're a DOMActivate dispatched from click handling, it will not be set.
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
bool outerActivateEvent =
- ((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
- (aVisitor.mEvent->mMessage == eLegacyDOMActivate &&
- !mInInternalActivate));
+ (mouseEvent && mouseEvent->IsLeftClickEvent()) ||
+ (aVisitor.mEvent->mMessage == eLegacyDOMActivate &&
+ !mInInternalActivate);
if (outerActivateEvent) {
aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
}
@@ -3505,11 +3515,9 @@ void HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection) {
// the user. (IsValid() can return false if the 'required' attribute is
// set and the value is the empty string.)
if (!IsValueEmpty()) {
- // We pass 'true' for UpdateValidityUIBits' aIsFocused argument
- // regardless because we need the UI to update _now_ or the user will
- // wonder why the step behavior isn't functioning.
- UpdateValidityUIBits(true);
- UpdateValidityElementStates(true);
+ // We pass 'true' for SetUserInteracted because we need the UI to update
+ // _now_ or the user will wonder why the step behavior isn't functioning.
+ SetUserInteracted(true);
return;
}
}
@@ -3664,18 +3672,12 @@ static bool ActivatesWithKeyboard(FormControlType aType, uint32_t aKeyCode) {
}
nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
- if (aVisitor.mEvent->mMessage == eFocus ||
- aVisitor.mEvent->mMessage == eBlur) {
- if (aVisitor.mEvent->mMessage == eBlur) {
- if (mIsDraggingRange) {
- FinishRangeThumbDrag();
- } else if (mNumberControlSpinnerIsSpinning) {
- StopNumberControlSpinnerSpin();
- }
+ if (aVisitor.mEvent->mMessage == eBlur) {
+ if (mIsDraggingRange) {
+ FinishRangeThumbDrag();
+ } else if (mNumberControlSpinnerIsSpinning) {
+ StopNumberControlSpinnerSpin();
}
-
- UpdateValidityUIBits(aVisitor.mEvent->mMessage == eFocus);
- UpdateValidityElementStates(true);
}
nsresult rv = NS_OK;
@@ -4076,11 +4078,14 @@ void HTMLInputElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
}
if (mCheckedIsToggled) {
+ SetUserInteracted(true);
+
// Fire input event and then change event.
DebugOnly rvIgnored = nsContentUtils::DispatchInputEvent(this);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Failed to dispatch input event");
+ // FIXME: Why is this different than every other change event?
nsContentUtils::DispatchTrustedEvent(
OwnerDoc(), static_cast(this), eFormChange, CanBubble::eYes,
Cancelable::eNo);
@@ -4094,8 +4099,7 @@ void HTMLInputElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
FireEventForAccessibility(this, eFormRadioStateChange);
// Fire event for the previous selected radio.
nsCOMPtr content = do_QueryInterface(aVisitor.mItemData);
- if (HTMLInputElement* previous =
- HTMLInputElement::FromNodeOrNull(content)) {
+ if (auto* previous = HTMLInputElement::FromNodeOrNull(content)) {
FireEventForAccessibility(previous, eFormRadioStateChange);
}
}
@@ -5927,6 +5931,7 @@ HTMLInputElement::Reset() {
SetCheckedChanged(false);
SetValueChanged(false);
SetLastValueChangeWasInteractive(false);
+ SetUserInteracted(false);
switch (GetValueMode()) {
case VALUE_MODE_VALUE: {
@@ -6222,25 +6227,15 @@ void HTMLInputElement::UpdateValidityElementStates(bool aNotify) {
ElementState state;
if (IsValid()) {
state |= ElementState::VALID;
+ if (mUserInteracted) {
+ state |= ElementState::USER_VALID;
+ }
} else {
state |= ElementState::INVALID;
- if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
- (mCanShowInvalidUI && ShouldShowValidityUI())) {
+ if (mUserInteracted) {
state |= ElementState::USER_INVALID;
}
}
- // :-moz-ui-valid applies if all of the following conditions are true:
- // 1. The element is not focused, or had either :-moz-ui-valid or
- // :-moz-ui-invalid applying before it was focused ;
- // 2. The element is either valid or isn't allowed to have
- // :-moz-ui-invalid applying ;
- // 3. The element has already been modified or the user tried to submit the
- // form owner while invalid.
- if (mCanShowValidUI && ShouldShowValidityUI() &&
- (IsValid() ||
- (!state.HasState(ElementState::USER_INVALID) && !mCanShowInvalidUI))) {
- state |= ElementState::USER_VALID;
- }
AddStatesSilently(state);
}
@@ -7251,19 +7246,12 @@ Decimal HTMLInputElement::GetDefaultStep() const {
}
}
-void HTMLInputElement::UpdateValidityUIBits(bool aIsFocused) {
- if (aIsFocused) {
- // If the invalid UI is shown, we should show it while focusing (and
- // update). Otherwise, we should not.
- mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
-
- // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
- // UI while typing.
- mCanShowValidUI = ShouldShowValidityUI();
- } else {
- mCanShowInvalidUI = true;
- mCanShowValidUI = true;
+void HTMLInputElement::SetUserInteracted(bool aInteracted) {
+ if (mUserInteracted == aInteracted) {
+ return;
}
+ mUserInteracted = aInteracted;
+ UpdateValidityElementStates(true);
}
void HTMLInputElement::UpdateInRange(bool aNotify) {
diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h
index 9e83938057bb..5f86e54542b0 100644
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -15,7 +15,6 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
#include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit()
#include "mozilla/dom/HTMLInputElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/UnionTypes.h"
@@ -36,6 +35,7 @@
#include "nsIContentPrefService2.h"
#include "nsContentUtils.h"
+class nsIEditor;
class nsIRadioVisitor;
namespace mozilla {
@@ -338,7 +338,7 @@ class HTMLInputElement final : public TextControlElement,
// as needed. aNotify controls whether the element state update
// needs to notify.
void UpdateAllValidityStates(bool aNotify);
- void UpdateValidityElementStates(bool aNotify) final;
+ void UpdateValidityElementStates(bool aNotify);
MOZ_CAN_RUN_SCRIPT
void MaybeUpdateAllValidityStates(bool aNotify) {
// If you need to add new type which supports validationMessage, you should
@@ -395,16 +395,7 @@ class HTMLInputElement final : public TextControlElement,
*/
void SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker);
- /**
- * The form might need to request an update of the UI bits
- * (BF_CAN_SHOW_INVALID_UI and BF_CAN_SHOW_VALID_UI) when an invalid form
- * submission is tried.
- *
- * @param aIsFocused Whether the element is currently focused.
- *
- * @note The caller is responsible to call ContentStatesChanged.
- */
- void UpdateValidityUIBits(bool aIsFocused);
+ void SetUserInteracted(bool) final;
/**
* Fires change event if mFocusedValue and current value held are unequal and
@@ -1115,37 +1106,6 @@ class HTMLInputElement final : public TextControlElement,
void SetAutoDirectionality(bool aNotify,
const nsAString* aKnownValue = nullptr);
- /**
- * Return if an element should have a specific validity UI
- * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
- *
- * @return Whether the element should have a validity UI.
- */
- bool ShouldShowValidityUI() const {
- /**
- * Always show the validity UI if the form has already tried to be submitted
- * but was invalid.
- *
- * Otherwise, show the validity UI if the element's value has been changed.
- */
- if (mForm && mForm->HasEverTriedInvalidSubmit()) {
- return true;
- }
-
- switch (GetValueMode()) {
- case VALUE_MODE_DEFAULT:
- return true;
- case VALUE_MODE_DEFAULT_ON:
- return GetCheckedChanged();
- case VALUE_MODE_VALUE:
- case VALUE_MODE_FILENAME:
- return mValueChanged;
- }
-
- MOZ_ASSERT_UNREACHABLE("We should not be there: there are no other modes.");
- return false;
- }
-
/**
* Returns the radio group container within the DOM tree that the element
* is currently a member of, if one exists.
@@ -1551,6 +1511,8 @@ class HTMLInputElement final : public TextControlElement,
// https://html.spec.whatwg.org/#concept-fe-dirty
// TODO: Maybe rename to match the spec?
bool mValueChanged : 1;
+ // https://html.spec.whatwg.org/#user-interacted
+ bool mUserInteracted : 1;
bool mLastValueChangeWasInteractive : 1;
bool mCheckedChanged : 1;
bool mChecked : 1;
@@ -1561,8 +1523,6 @@ class HTMLInputElement final : public TextControlElement,
bool mCheckedIsToggled : 1;
bool mIndeterminate : 1;
bool mInhibitRestoration : 1;
- bool mCanShowValidUI : 1;
- bool mCanShowInvalidUI : 1;
bool mHasRange : 1;
bool mIsDraggingRange : 1;
bool mNumberControlSpinnerIsSpinning : 1;
diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp
index 125f443f7ba6..18bf2b79b28f 100644
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -119,10 +119,8 @@ HTMLSelectElement::HTMLSelectElement(
mDisabledChanged(false),
mMutating(false),
mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
- mSelectionHasChanged(false),
+ mUserInteracted(false),
mDefaultSelectionSet(false),
- mCanShowInvalidUI(true),
- mCanShowValidUI(true),
mIsOpenInParentProcess(false),
mNonOptionChildren(0),
mOptGroupCount(0),
@@ -282,7 +280,7 @@ void HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
// Fix the currently selected index
if (aListIndex <= mSelectedIndex) {
mSelectedIndex += (insertIndex - aListIndex);
- SetSelectionChanged(true, aNotify);
+ OnSelectionChanged();
}
// Get the frame stuff for notification. No need to flush here
@@ -382,7 +380,7 @@ nsresult HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
// If this is a Combobox, no other Item will be selected.
if (IsCombobox()) {
mSelectedIndex = -1;
- SetSelectionChanged(true, aNotify);
+ OnSelectionChanged();
} else {
FindSelectedIndex(aListIndex, aNotify);
}
@@ -390,7 +388,7 @@ nsresult HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
// Shift the selected index if something in front of it was removed
// aListIndex+numRemoved <= mSelectedIndex
mSelectedIndex -= numRemoved;
- SetSelectionChanged(true, aNotify);
+ OnSelectionChanged();
}
}
@@ -701,7 +699,7 @@ void HTMLSelectElement::SetSelectedIndexInternal(int32_t aIndex, bool aNotify) {
selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
}
- SetSelectionChanged(true, aNotify);
+ OnSelectionChanged();
}
bool HTMLSelectElement::IsOptionSelectedByIndex(int32_t aIndex) const {
@@ -716,7 +714,7 @@ void HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
// Set the selected index
if (aSelected && (aIndex < mSelectedIndex || mSelectedIndex < 0)) {
mSelectedIndex = aIndex;
- SetSelectionChanged(true, aNotify);
+ OnSelectionChanged();
} else if (!aSelected && aIndex == mSelectedIndex) {
FindSelectedIndex(aIndex + 1, aNotify);
}
@@ -741,15 +739,14 @@ void HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
void HTMLSelectElement::FindSelectedIndex(int32_t aStartIndex, bool aNotify) {
mSelectedIndex = -1;
- SetSelectionChanged(true, aNotify);
uint32_t len = Length();
for (int32_t i = aStartIndex; i < int32_t(len); i++) {
if (IsOptionSelectedByIndex(i)) {
mSelectedIndex = i;
- SetSelectionChanged(true, aNotify);
break;
}
}
+ OnSelectionChanged();
}
// XXX Consider splitting this into two functions for ease of reading:
@@ -1270,27 +1267,6 @@ void HTMLSelectElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
nsGenericHTMLFormControlElementWithState::GetEventTargetParent(aVisitor);
}
-nsresult HTMLSelectElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
- if (aVisitor.mEvent->mMessage == eFocus) {
- // If the invalid UI is shown, we should show it while focused and
- // update the invalid/valid UI.
- mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
-
- // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
- // UI while focused.
- mCanShowValidUI = ShouldShowValidityUI();
-
- // We don't have to update ElementState::USER_INVALID nor
- // ElementState::USER_VALID given that the states should not change.
- } else if (aVisitor.mEvent->mMessage == eBlur) {
- mCanShowInvalidUI = true;
- mCanShowValidUI = true;
- UpdateValidityElementStates(true);
- }
-
- return nsGenericHTMLFormControlElementWithState::PostHandleEvent(aVisitor);
-}
-
void HTMLSelectElement::UpdateValidityElementStates(bool aNotify) {
AutoStateChangeNotifier notifier(*this, aNotify);
RemoveStatesSilently(ElementState::VALIDITY_STATES);
@@ -1301,28 +1277,16 @@ void HTMLSelectElement::UpdateValidityElementStates(bool aNotify) {
ElementState state;
if (IsValid()) {
state |= ElementState::VALID;
+ if (mUserInteracted) {
+ state |= ElementState::USER_VALID;
+ }
} else {
state |= ElementState::INVALID;
-
- if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
- (mCanShowInvalidUI && ShouldShowValidityUI())) {
+ if (mUserInteracted) {
state |= ElementState::USER_INVALID;
}
}
- // :-moz-ui-valid applies if all the following are true:
- // 1. The element is not focused, or had either :-moz-ui-valid or
- // :-moz-ui-invalid applying before it was focused ;
- // 2. The element is either valid or isn't allowed to have
- // :-moz-ui-invalid applying ;
- // 3. The element has already been modified or the user tried to submit the
- // form owner while invalid.
- if (mCanShowValidUI && ShouldShowValidityUI() &&
- (IsValid() ||
- (state.HasState(ElementState::USER_INVALID) && !mCanShowInvalidUI))) {
- state |= ElementState::USER_VALID;
- }
-
AddStatesSilently(state);
}
@@ -1453,9 +1417,9 @@ HTMLSelectElement::Reset() {
SelectSomething(true);
}
- SetSelectionChanged(false, true);
+ OnSelectionChanged();
+ SetUserInteracted(false);
- //
// Let the frame know we were reset
//
// Don't flush, if there's no frame yet it won't care about us being
@@ -1626,18 +1590,11 @@ void HTMLSelectElement::FieldSetDisabledChanged(bool aNotify) {
UpdateValidityElementStates(aNotify);
}
-void HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify) {
+void HTMLSelectElement::OnSelectionChanged() {
if (!mDefaultSelectionSet) {
return;
}
-
UpdateSelectedOptions();
-
- bool previousSelectionChangedValue = mSelectionHasChanged;
- mSelectionHasChanged = aValue;
- if (mSelectionHasChanged != previousSelectionChangedValue) {
- UpdateValidityElementStates(aNotify);
- }
}
void HTMLSelectElement::UpdateSelectedOptions() {
@@ -1646,6 +1603,14 @@ void HTMLSelectElement::UpdateSelectedOptions() {
}
}
+void HTMLSelectElement::SetUserInteracted(bool aInteracted) {
+ if (mUserInteracted == aInteracted) {
+ return;
+ }
+ mUserInteracted = aInteracted;
+ UpdateValidityElementStates(true);
+}
+
void HTMLSelectElement::SetPreviewValue(const nsAString& aValue) {
mPreviewValue = aValue;
nsContentUtils::RemoveNewlines(mPreviewValue);
@@ -1656,6 +1621,22 @@ void HTMLSelectElement::SetPreviewValue(const nsAString& aValue) {
}
}
+void HTMLSelectElement::UserFinishedInteracting(bool aChanged) {
+ SetUserInteracted(true);
+ if (!aChanged) {
+ return;
+ }
+
+ // Dispatch the input event.
+ DebugOnly rvIgnored = nsContentUtils::DispatchInputEvent(this);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
+ "Failed to dispatch input event");
+
+ // Dispatch the change event.
+ nsContentUtils::DispatchTrustedEvent(OwnerDoc(), this, u"change"_ns,
+ CanBubble::eYes, Cancelable::eNo);
+}
+
JSObject* HTMLSelectElement::WrapNode(JSContext* aCx,
JS::Handle aGivenProto) {
return HTMLSelectElement_Binding::Wrap(aCx, this, aGivenProto);
diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h
index fed70051def3..223da65c31fb 100644
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -128,6 +128,9 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
void GetAutocompleteInfo(AutocompleteInfo& aInfo);
+ // Sets the user interacted flag and fires input/change events if needed.
+ MOZ_CAN_RUN_SCRIPT void UserFinishedInteracting(bool aChanged);
+
bool Disabled() const { return GetBoolAttr(nsGkAtoms::disabled); }
void SetDisabled(bool aVal, ErrorResult& aRv) {
SetHTMLBoolAttr(nsGkAtoms::disabled, aVal, aRv);
@@ -194,8 +197,6 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
// nsIContent
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
- MOZ_CAN_RUN_SCRIPT
- nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override;
bool IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
int32_t* aTabIndex) override;
@@ -300,7 +301,7 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
ValidityStateType aType) override;
void UpdateValueMissingValidityState();
- void UpdateValidityElementStates(bool aNotify) final;
+ void UpdateValidityElementStates(bool aNotify);
/**
* Insert aElement before the node given by aBefore
*/
@@ -451,7 +452,7 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
void SetSelectedIndexInternal(int32_t aIndex, bool aNotify);
- void SetSelectionChanged(bool aValue, bool aNotify);
+ void OnSelectionChanged();
/**
* Marks the selectedOptions list as dirty, so that it'll populate itself
@@ -459,25 +460,7 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
*/
void UpdateSelectedOptions();
- /**
- * Return whether an element should have a validity UI.
- * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
- *
- * @return Whether the element should have a validity UI.
- */
- bool ShouldShowValidityUI() const {
- /**
- * Always show the validity UI if the form has already tried to be submitted
- * but was invalid.
- *
- * Otherwise, show the validity UI if the selection has been changed.
- */
- if (mForm && mForm->HasEverTriedInvalidSubmit()) {
- return true;
- }
-
- return mSelectionHasChanged;
- }
+ void SetUserInteracted(bool) final;
/** The options[] array */
RefPtr mOptions;
@@ -495,23 +478,10 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
* True if DoneAddingChildren will get called but shouldn't restore state.
*/
bool mInhibitStateRestoration : 1;
- /**
- * True if the selection has changed since the element's creation.
- */
- bool mSelectionHasChanged : 1;
- /**
- * True if the default selected option has been set.
- */
+ /** https://html.spec.whatwg.org/#user-interacted */
+ bool mUserInteracted : 1;
+ /** True if the default selected option has been set. */
bool mDefaultSelectionSet : 1;
- /**
- * True if :-moz-ui-invalid can be shown.
- */
- bool mCanShowInvalidUI : 1;
- /**
- * True if :-moz-ui-valid can be shown.
- */
- bool mCanShowValidUI : 1;
-
/** True if we're open in the parent process */
bool mIsOpenInParentProcess : 1;
diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp
index 2423f3bb6dd2..046655453859 100644
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -48,15 +48,8 @@ HTMLTextAreaElement::HTMLTextAreaElement(
FromParser aFromParser)
: TextControlElement(std::move(aNodeInfo), aFromParser,
FormControlType::Textarea),
- mValueChanged(false),
- mLastValueChangeWasInteractive(false),
- mHandlingSelect(false),
mDoneAddingChildren(!aFromParser),
mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
- mDisabledChanged(false),
- mCanShowInvalidUI(true),
- mCanShowValidUI(true),
- mIsPreviewEnabled(false),
mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
mState(TextControlState::Construct(this)) {
AddMutationObserver(this);
@@ -465,6 +458,13 @@ void HTMLTextAreaElement::FireChangeEventIfNeeded() {
nsString value;
GetValueInternal(value, true);
+ // NOTE(emilio): This is not quite on the spec, but matches , see
+ // https://github.com/whatwg/html/issues/10011 and
+ // https://github.com/whatwg/html/issues/10013
+ if (mValueChanged) {
+ SetUserInteracted(true);
+ }
+
if (mFocusedValue.Equals(value)) {
return;
}
@@ -480,24 +480,6 @@ nsresult HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
mHandlingSelect = false;
}
- if (aVisitor.mEvent->mMessage == eFocus ||
- aVisitor.mEvent->mMessage == eBlur) {
- if (aVisitor.mEvent->mMessage == eFocus) {
- // If the invalid UI is shown, we should show it while focusing (and
- // update). Otherwise, we should not.
- GetValueInternal(mFocusedValue, true);
- mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
-
- // If neither invalid UI nor valid UI is shown, we shouldn't show the
- // valid UI while typing.
- mCanShowValidUI = ShouldShowValidityUI();
- } else { // eBlur
- mCanShowInvalidUI = true;
- mCanShowValidUI = true;
- }
- UpdateValidityElementStates(true);
- }
-
return NS_OK;
}
@@ -654,6 +636,7 @@ nsresult HTMLTextAreaElement::Reset() {
nsAutoString resetVal;
GetDefaultValue(resetVal, IgnoreErrors());
SetValueChanged(false);
+ SetUserInteracted(false);
nsresult rv = SetValueInternal(resetVal, ValueSetterOption::ByInternalAPI);
NS_ENSURE_SUCCESS(rv, rv);
@@ -751,28 +734,15 @@ void HTMLTextAreaElement::UpdateValidityElementStates(bool aNotify) {
ElementState state;
if (IsValid()) {
state |= ElementState::VALID;
+ if (mUserInteracted) {
+ state |= ElementState::USER_VALID;
+ }
} else {
state |= ElementState::INVALID;
- // :-moz-ui-invalid always apply if the element suffers from a custom
- // error.
- if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
- (mCanShowInvalidUI && ShouldShowValidityUI())) {
+ if (mUserInteracted) {
state |= ElementState::USER_INVALID;
}
}
-
- // :-moz-ui-valid applies if all the following are true:
- // 1. The element is not focused, or had either :-moz-ui-valid or
- // :-moz-ui-invalid applying before it was focused ;
- // 2. The element is either valid or isn't allowed to have
- // :-moz-ui-invalid applying ;
- // 3. The element has already been modified or the user tried to submit the
- // form owner while invalid.
- if (mCanShowValidUI && ShouldShowValidityUI() &&
- (IsValid() ||
- (state.HasState(ElementState::USER_INVALID) && !mCanShowInvalidUI))) {
- state |= ElementState::USER_VALID;
- }
AddStatesSilently(state);
}
@@ -1156,6 +1126,14 @@ bool HTMLTextAreaElement::HasCachedSelection() {
return mState->IsSelectionCached();
}
+void HTMLTextAreaElement::SetUserInteracted(bool aInteracted) {
+ if (mUserInteracted == aInteracted) {
+ return;
+ }
+ mUserInteracted = aInteracted;
+ UpdateValidityElementStates(true);
+}
+
void HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify) {
// This *has* to be called before UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState because these two functions depend on our
diff --git a/dom/html/HTMLTextAreaElement.h b/dom/html/HTMLTextAreaElement.h
index 266be29b0f8b..ac3eb8bbf532 100644
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -288,25 +288,23 @@ class HTMLTextAreaElement final : public TextControlElement,
JSObject* WrapNode(JSContext*, JS::Handle aGivenProto) override;
nsCOMPtr mControllers;
+ /** https://html.spec.whatwg.org/#user-interacted */
+ bool mUserInteracted = false;
/** Whether or not the value has changed since its default value was given. */
- bool mValueChanged;
+ bool mValueChanged = false;
/** Whether or not the last change to the value was made interactively by the
* user. */
- bool mLastValueChangeWasInteractive;
+ bool mLastValueChangeWasInteractive = false;
/** Whether or not we are already handling select event. */
- bool mHandlingSelect;
+ bool mHandlingSelect = false;
/** Whether or not we are done adding children (always true if not
created by a parser */
bool mDoneAddingChildren;
/** Whether state restoration should be inhibited in DoneAddingChildren. */
bool mInhibitStateRestoration;
/** Whether our disabled state has changed from the default **/
- bool mDisabledChanged;
- /** Whether we should make :-moz-ui-invalid apply on the element. **/
- bool mCanShowInvalidUI;
- /** Whether we should make :-moz-ui-valid apply on the element. **/
- bool mCanShowValidUI;
- bool mIsPreviewEnabled;
+ bool mDisabledChanged = false;
+ bool mIsPreviewEnabled = false;
nsContentUtils::AutocompleteAttrState mAutocompleteAttrState;
@@ -350,27 +348,6 @@ class HTMLTextAreaElement final : public TextControlElement,
void SetDirectionFromValue(bool aNotify,
const nsAString* aKnownValue = nullptr);
- /**
- * Return if an element should have a specific validity UI
- * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
- *
- * @return Whether the element should have a validity UI.
- */
- bool ShouldShowValidityUI() const {
- /**
- * Always show the validity UI if the form has already tried to be submitted
- * but was invalid.
- *
- * Otherwise, show the validity UI if the element's value has been changed.
- */
-
- if (mForm && mForm->HasEverTriedInvalidSubmit()) {
- return true;
- }
-
- return mValueChanged;
- }
-
/**
* Get the mutable state of the element.
*/
@@ -392,7 +369,8 @@ class HTMLTextAreaElement final : public TextControlElement,
void GetSelectionRange(uint32_t* aSelectionStart, uint32_t* aSelectionEnd,
ErrorResult& aRv);
- void UpdateValidityElementStates(bool aNotify) final;
+ void SetUserInteracted(bool) final;
+ void UpdateValidityElementStates(bool aNotify);
private:
static void MapAttributesIntoRule(MappedDeclarationsBuilder&);
diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp
index 04dcfc184c03..145b30ea75c5 100644
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1795,7 +1795,6 @@ void nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm,
UnsetFlags(ADDED_TO_FORM);
SetFormInternal(nullptr, false);
AfterClearForm(aUnbindOrDelete);
- UpdateValidityElementStates(true);
}
nsresult nsGenericHTMLFormElement::BindToTree(BindContext& aContext,
@@ -2148,11 +2147,6 @@ void nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
form->AddElementToTable(this, idVal);
}
}
-
- if (form != oldForm) {
- // ui-valid / invalid depends on the form for some elements
- UpdateValidityElementStates(true);
- }
}
void nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify) {
diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h
index 3ca31e277701..b3790314208c 100644
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1022,10 +1022,9 @@ class nsGenericHTMLFormElement : public nsGenericHTMLElement {
*/
already_AddRefed GetLayoutHistory(bool aRead);
- // Form changes (in particular whether our current form has been submitted
- // invalidly) affect the user-valid/user-invalid pseudo-classes. Sub-classes
- // can override this to react to it.
- virtual void UpdateValidityElementStates(bool aNotify) {}
+ // Sets the user-interacted flag in
+ // https://html.spec.whatwg.org/#user-interacted, if it applies.
+ virtual void SetUserInteracted(bool aNotify) {}
protected:
virtual ~nsGenericHTMLFormElement() = default;
diff --git a/dom/html/nsIConstraintValidation.cpp b/dom/html/nsIConstraintValidation.cpp
index 11ef8b028efc..6ccda2ea7e4c 100644
--- a/dom/html/nsIConstraintValidation.cpp
+++ b/dom/html/nsIConstraintValidation.cpp
@@ -83,13 +83,6 @@ bool nsIConstraintValidation::ReportValidity() {
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
element->DispatchEvent(*event);
-
- auto* inputElement = HTMLInputElement::FromNode(element);
- if (inputElement && inputElement->State().HasState(ElementState::FOCUS)) {
- inputElement->UpdateValidityUIBits(true);
- inputElement->UpdateValidityElementStates(true);
- }
-
return false;
}
diff --git a/dom/html/test/test_bug605124-1.html b/dom/html/test/test_bug605124-1.html
index 8530b8d54465..d252987ee922 100644
--- a/dom/html/test/test_bug605124-1.html
+++ b/dom/html/test/test_bug605124-1.html
@@ -37,8 +37,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=605124
function checkPseudoClass(aElement, aExpected)
{
- is(aElement.matches(":-moz-ui-invalid"), aExpected,
- "matches(':-moz-ui-invalid') should return " + aExpected + " for " + aElement);
+ is(aElement.matches(":user-invalid"), aExpected,
+ "matches(':user-invalid') should return " + aExpected + " for " + aElement);
}
var content = document.getElementById('content');
@@ -62,9 +62,9 @@ checkPseudoClass(select, true);
content.appendChild(textarea);
content.appendChild(input);
content.appendChild(select);
-checkPseudoClass(textarea, false);
-checkPseudoClass(input, false);
-checkPseudoClass(select, false);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
// Back in the form.
form.appendChild(textarea);
@@ -89,10 +89,9 @@ checkPseudoClass(select, true);
// Remove the form.
document.getElementsByTagName('table')[0].removeChild(form);
-checkPseudoClass(textarea, false);
-checkPseudoClass(input, false);
-checkPseudoClass(select, false);
-
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);