#425: M1385478 IsRequired()

This commit is contained in:
Cameron Kaiser 2017-12-13 20:50:08 -08:00
parent faa6eb4c0c
commit 71abc4edc1
11 changed files with 101 additions and 41 deletions

View File

@ -8382,7 +8382,11 @@ nsDocument::AddToRadioGroup(const nsAString& aName,
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
HTMLInputElement* input = HTMLInputElement::FromContent(element);
NS_ASSERTION(input, "radio controls have to be input elements!");
if (input->IsRequired()) {
radioGroup->mRequiredRadioCount++;
}
}
@ -8396,7 +8400,11 @@ nsDocument::RemoveFromRadioGroup(const nsAString& aName,
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
HTMLInputElement* input = HTMLInputElement::FromContent(element);
NS_ASSERTION(input, "radio controls have to be input elements!");
if (input->IsRequired()) {
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
"mRequiredRadioCount about to wrap below 0!");
radioGroup->mRequiredRadioCount--;

View File

@ -278,11 +278,13 @@ private:
#define DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED)
#define REQUIRED_STATES (NS_EVENT_STATE_REQUIRED | NS_EVENT_STATE_OPTIONAL)
#define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \
NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | \
DISABLED_STATES | \
DISABLED_STATES | REQUIRED_STATES | \
NS_EVENT_STATE_UNRESOLVED)
#define INTRINSIC_STATES (~ESM_MANAGED_STATES)

View File

@ -2296,7 +2296,10 @@ HTMLFormElement::AddToRadioGroup(const nsAString& aName,
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements!");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
HTMLInputElement* input = HTMLInputElement::FromContent(element);
NS_ASSERTION(input, "radio controls have to be input elements!");
if (input->IsRequired()) {
mRequiredRadioButtonCounts.Put(aName,
mRequiredRadioButtonCounts.Get(aName)+1);
}
@ -2307,9 +2310,12 @@ HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
nsIFormControl* aRadio)
{
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements!");
NS_ASSERTION(element, "radio controls have to be content elements");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
HTMLInputElement* input = HTMLInputElement::FromContent(element);
NS_ASSERTION(input, "radio controls have to be input elements!");
if (input->IsRequired()) {
uint32_t requiredNb = mRequiredRadioButtonCounts.Get(aName);
NS_ASSERTION(requiredNb >= 1,
"At least one radio button has to be required!");

View File

@ -1149,6 +1149,13 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
UpdateDisabledState(aNotify);
}
if (aName == nsGkAtoms::required && DoesRequiredApply()) {
// This *has* to be called *before* UpdateValueMissingValidityState
// because UpdateValueMissingValidityState depends on our required
// state.
UpdateRequiredState(!!aValue, aNotify);
}
UpdateValueMissingValidityState();
// This *has* to be called *after* validity has changed.
@ -4371,6 +4378,15 @@ HTMLInputElement::HandleTypeChange(uint8_t aNewType)
mFocusedValue.Truncate();
}
// Update or clear our required states since we may have changed from a
// required input type to a non-required input type or viceversa.
if (DoesRequiredApply()) {
bool isRequired = HasAttr(kNameSpaceID_None, nsGkAtoms::required);
UpdateRequiredState(isRequired, false); // See below for why this is OK.
} else {
RemoveStatesSilently(REQUIRED_STATES);
}
UpdateHasRange();
// Do not notify, it will be done after if needed.
@ -5757,12 +5773,6 @@ HTMLInputElement::IntrinsicState() const
state |= nsImageLoadingContent::ImageState();
}
if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
state |= NS_EVENT_STATE_REQUIRED;
} else {
state |= NS_EVENT_STATE_OPTIONAL;
}
if (IsCandidateForConstraintValidation()) {
if (IsValid()) {
state |= NS_EVENT_STATE_VALID;
@ -6369,8 +6379,7 @@ HTMLInputElement::IsValueMissing() const
// Should use UpdateValueMissingValidityStateForRadio() for type radio.
MOZ_ASSERT(mType != NS_FORM_INPUT_RADIO);
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) ||
!DoesRequiredApply()) {
if (!IsRequired() || !DoesRequiredApply()) {
return false;
}
@ -6635,7 +6644,7 @@ HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
// If there is no selection, that might mean the radio is not in a group.
// In that case, we can look for the checked state of the radio.
bool selected = selection || (!aIgnoreSelf && mChecked);
bool required = !aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required);
bool required = !aIgnoreSelf && IsRequired();
bool valueMissing = false;
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
@ -6652,7 +6661,7 @@ HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
// If the current radio is required and not ignored, we can assume the entire
// group is required.
if (!required) {
required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
required = (aIgnoreSelf && IsRequired())
? container->GetRequiredRadioCount(name) - 1
: container->GetRequiredRadioCount(name);
}

View File

@ -774,6 +774,22 @@ public:
*/
static Decimal StringToDecimal(const nsAString& aValue);
/**
* Returns if the required attribute applies for the current type.
*/
bool DoesRequiredApply() const;
/**
* Returns the current required state of the element. This function differs
* from Required() in that this function only returns true for input types
* that @required attribute applies and the attribute is set; in contrast,
* Required() returns true whenever @required attribute is set.
*/
bool IsRequired() const
{
return State().HasState(NS_EVENT_STATE_REQUIRED);
}
protected:
virtual ~HTMLInputElement();
@ -964,11 +980,6 @@ protected:
*/
bool DoesReadOnlyApply() const;
/**
* Returns if the required attribute applies for the current type.
*/
bool DoesRequiredApply() const;
/**
* Returns if the pattern attribute applies for the current type.
*/

View File

@ -1070,7 +1070,7 @@ HTMLSelectElement::IsOptionDisabled(int32_t aIndex, bool* aIsDisabled)
}
bool
HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption)
HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption) const
{
MOZ_ASSERT(aOption);
if (aOption->Disabled()) {
@ -1301,6 +1301,11 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation();
} else if (aName == nsGkAtoms::required) {
// This *has* to be called *before* UpdateValueMissingValidityState
// because UpdateValueMissingValidityState depends on our required
// state.
UpdateRequiredState(!!aValue, aNotify);
UpdateValueMissingValidityState();
} else if (aName == nsGkAtoms::autocomplete) {
// Clear the cached @autocomplete attribute state
@ -1523,12 +1528,6 @@ HTMLSelectElement::IntrinsicState() const
}
}
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
state |= NS_EVENT_STATE_REQUIRED;
} else {
state |= NS_EVENT_STATE_OPTIONAL;
}
return state;
}
@ -1771,7 +1770,7 @@ HTMLSelectElement::RebuildOptionsArray(bool aNotify)
}
bool
HTMLSelectElement::IsValueMissing()
HTMLSelectElement::IsValueMissing() const
{
if (!Required()) {
return false;

View File

@ -198,7 +198,7 @@ public:
}
bool Required() const
{
return GetBoolAttr(nsGkAtoms::required);
return State().HasState(NS_EVENT_STATE_REQUIRED);
}
void SetRequired(bool aVal, ErrorResult& aRv)
{
@ -332,7 +332,7 @@ public:
*/
NS_IMETHOD IsOptionDisabled(int32_t aIndex,
bool* aIsDisabled);
bool IsOptionDisabled(HTMLOptionElement* aOption);
bool IsOptionDisabled(HTMLOptionElement* aOption) const;
/**
* Sets multiple options (or just sets startIndex if select is single)
@ -505,7 +505,7 @@ protected:
// nsIConstraintValidation
void UpdateBarredFromConstraintValidation();
bool IsValueMissing();
bool IsValueMissing() const;
/**
* Get the index of the first option at, under or following the content in

View File

@ -1136,12 +1136,6 @@ HTMLTextAreaElement::IntrinsicState() const
{
EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
state |= NS_EVENT_STATE_REQUIRED;
} else {
state |= NS_EVENT_STATE_OPTIONAL;
}
if (IsCandidateForConstraintValidation()) {
if (IsValid()) {
state |= NS_EVENT_STATE_VALID;
@ -1286,6 +1280,13 @@ HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
UpdateDisabledState(aNotify);
}
if (aName == nsGkAtoms::required) {
// This *has* to be called *before* UpdateValueMissingValidityState
// because UpdateValueMissingValidityState depends on our required
// state.
UpdateRequiredState(!!aValue, aNotify);
}
UpdateValueMissingValidityState();
// This *has* to be called *after* validity has changed.
@ -1368,7 +1369,7 @@ HTMLTextAreaElement::IsTooLong()
bool
HTMLTextAreaElement::IsValueMissing() const
{
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
if (!Required() || !IsMutable()) {
return false;
}

View File

@ -222,9 +222,9 @@ public:
{
SetHTMLBoolAttr(nsGkAtoms::readonly, aReadOnly, aError);
}
bool Required()
bool Required() const
{
return GetBoolAttr(nsGkAtoms::required);
return State().HasState(NS_EVENT_STATE_REQUIRED);
}
void SetRangeText(const nsAString& aReplacement, ErrorResult& aRv);

View File

@ -107,6 +107,7 @@
#include "mozilla/dom/HTMLBodyElement.h"
#include "imgIContainer.h"
#include "nsComputedDOMStyle.h"
#include "mozilla/dom/HTMLInputElement.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -2640,6 +2641,24 @@ void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify)
}
}
void
nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired, bool aNotify)
{
EventStates requiredStates;
if (aIsRequired) {
requiredStates |= NS_EVENT_STATE_REQUIRED;
} else {
requiredStates |= NS_EVENT_STATE_OPTIONAL;
}
EventStates oldRequiredStates = State() & REQUIRED_STATES;
EventStates changedStates = requiredStates ^ oldRequiredStates;
if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);
}
}
void
nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
{

View File

@ -1351,6 +1351,11 @@ public:
*/
void UpdateDisabledState(bool aNotify);
/**
* Update our required/optional flags to match the given aIsRequired boolean.
*/
void UpdateRequiredState(bool aIsRequired, bool aNotify);
void FieldSetFirstLegendChanged(bool aNotify) {
UpdateFieldSet(aNotify);
}