This commit is contained in:
Cameron Kaiser 2017-10-16 21:35:53 -07:00
parent b8ebae8398
commit 84bfb83b16
14 changed files with 198 additions and 87 deletions

View File

@ -451,6 +451,16 @@ protected:
RemoveStatesSilently(aStates); RemoveStatesSilently(aStates);
NotifyStateChange(aStates); NotifyStateChange(aStates);
} }
virtual void ToggleStates(EventStates aStates, bool aNotify)
{
NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES),
"Should only be removing ESM-managed states here");
mState ^= aStates;
if (aNotify) {
NotifyStateChange(aStates);
}
}
public: public:
virtual void UpdateEditableState(bool aNotify) override; virtual void UpdateEditableState(bool aNotify) override;

View File

@ -276,10 +276,13 @@ private:
#define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL) #define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL)
#define DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED)
#define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \ #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \ NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \
NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \ NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | \ NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | \
DISABLED_STATES | \
NS_EVENT_STATE_UNRESOLVED) NS_EVENT_STATE_UNRESOLVED)
#define INTRINSIC_STATES (~ESM_MANAGED_STATES) #define INTRINSIC_STATES (~ESM_MANAGED_STATES)

View File

@ -113,9 +113,14 @@ HTMLButtonElement::UpdateBarredFromConstraintValidation()
void void
HTMLButtonElement::FieldSetDisabledChanged(bool aNotify) HTMLButtonElement::FieldSetDisabledChanged(bool aNotify)
{ {
UpdateBarredFromConstraintValidation();
// FieldSetDisabledChanged *has* to be called *before*
// UpdateBarredFromConstraintValidation, because the latter depends on our
// disabled state.
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify); nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateBarredFromConstraintValidation();
UpdateState(aNotify);
} }
// nsIDOMHTMLButtonElement // nsIDOMHTMLButtonElement
@ -522,6 +527,12 @@ HTMLButtonElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
} }
if (aName == nsGkAtoms::type || aName == nsGkAtoms::disabled) { if (aName == nsGkAtoms::type || aName == nsGkAtoms::disabled) {
if (aName == nsGkAtoms::disabled) {
// This *has* to be called *before* validity state check because
// UpdateBarredFromConstraintValidation depends on our disabled state.
UpdateDisabledState(aNotify);
}
UpdateBarredFromConstraintValidation(); UpdateBarredFromConstraintValidation();
UpdateState(aNotify); UpdateState(aNotify);
} }

View File

@ -85,8 +85,12 @@ nsresult
HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) const nsAttrValue* aValue, bool aNotify)
{ {
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled && if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
nsINode::GetFirstChild()) { // This *has* to be called *before* calling FieldSetDisabledChanged on our
// controls, as they may depend on our disabled state.
UpdateDisabledState(aNotify);
if (nsINode::GetFirstChild()) {
if (!mElements) { if (!mElements) {
mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr, mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr,
true); true);
@ -98,6 +102,7 @@ HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
->FieldSetDisabledChanged(aNotify); ->FieldSetDisabledChanged(aNotify);
} }
} }
}
return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName, return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
aValue, aNotify); aValue, aNotify);

View File

@ -1142,6 +1142,13 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled || if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
aName == nsGkAtoms::readonly) { aName == nsGkAtoms::readonly) {
if (aName == nsGkAtoms::disabled) {
// This *has* to be called *before* validity state check because
// UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState depend on our disabled state.
UpdateDisabledState(aNotify);
}
UpdateValueMissingValidityState(); UpdateValueMissingValidityState();
// This *has* to be called *after* validity has changed. // This *has* to be called *after* validity has changed.
@ -7192,10 +7199,14 @@ HTMLInputElement::HasCachedSelection()
void void
HTMLInputElement::FieldSetDisabledChanged(bool aNotify) HTMLInputElement::FieldSetDisabledChanged(bool aNotify)
{ {
// This *has* to be called *before* UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState because these two functions depend on our
// disabled state.
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateValueMissingValidityState(); UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation(); UpdateBarredFromConstraintValidation();
UpdateState(aNotify);
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
} }
void void

View File

@ -64,8 +64,6 @@ public:
NS_IMETHOD Reset() override; NS_IMETHOD Reset() override;
NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission) override; NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission) override;
virtual bool IsDisabled() const override { return false; }
// nsIContent // nsIContent
virtual nsresult PostHandleEvent( virtual nsresult PostHandleEvent(
EventChainPostVisitor& aVisitor) override; EventChainPostVisitor& aVisitor) override;

View File

@ -105,13 +105,27 @@ HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) const nsAttrValue* aValue, bool aNotify)
{ {
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) { if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
EventStates disabledStates;
if (aValue) {
disabledStates |= NS_EVENT_STATE_DISABLED;
} else {
disabledStates |= NS_EVENT_STATE_ENABLED;
}
EventStates oldDisabledStates = State() & DISABLED_STATES;
EventStates changedStates = disabledStates ^ oldDisabledStates;
if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);
// All our children <option> have their :disabled state depending on our // All our children <option> have their :disabled state depending on our
// disabled attribute. We should make sure their state is updated. // disabled attribute. We should make sure their state is updated.
for (nsIContent* child = nsINode::GetFirstChild(); child; for (nsIContent* child = nsINode::GetFirstChild(); child;
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
if (child->IsHTMLElement(nsGkAtoms::option)) { if (auto optElement = HTMLOptionElement::FromContent(child)) {
// No need to call |IsElement()| because it's an HTML element. optElement->OptGroupDisabledChanged(true);
child->AsElement()->UpdateState(true); }
} }
} }
} }
@ -120,22 +134,6 @@ HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
aNotify); aNotify);
} }
EventStates
HTMLOptGroupElement::IntrinsicState() const
{
EventStates state = nsGenericHTMLElement::IntrinsicState();
if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
state &= ~NS_EVENT_STATE_DISABLED;
state |= NS_EVENT_STATE_ENABLED;
}
return state;
}
JSObject* JSObject*
HTMLOptGroupElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) HTMLOptGroupElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{ {

View File

@ -37,8 +37,6 @@ public:
// nsIContent // nsIContent
virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override;
virtual EventStates IntrinsicState() const override;
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
@ -47,7 +45,7 @@ public:
virtual nsIDOMNode* AsDOMNode() override { return this; } virtual nsIDOMNode* AsDOMNode() override { return this; }
virtual bool IsDisabled() const override { virtual bool IsDisabled() const override {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); return State().HasState(NS_EVENT_STATE_DISABLED);
} }
bool Disabled() const bool Disabled() const

View File

@ -84,6 +84,39 @@ HTMLOptionElement::SetSelectedInternal(bool aValue, bool aNotify)
} }
} }
void
HTMLOptionElement::OptGroupDisabledChanged(bool aNotify)
{
UpdateDisabledState(aNotify);
}
void
HTMLOptionElement::UpdateDisabledState(bool aNotify)
{
bool isDisabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
if (!isDisabled) {
nsIContent* parent = GetParent();
if (auto optGroupElement = HTMLOptGroupElement::FromContentOrNull(parent)) {
isDisabled = optGroupElement->IsDisabled();
}
}
EventStates disabledStates;
if (isDisabled) {
disabledStates |= NS_EVENT_STATE_DISABLED;
} else {
disabledStates |= NS_EVENT_STATE_ENABLED;
}
EventStates oldDisabledStates = State() & DISABLED_STATES;
EventStates changedStates = disabledStates ^ oldDisabledStates;
if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);
}
}
NS_IMETHODIMP NS_IMETHODIMP
HTMLOptionElement::GetSelected(bool* aValue) HTMLOptionElement::GetSelected(bool* aValue)
{ {
@ -247,8 +280,12 @@ nsresult
HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) const nsAttrValue* aValue, bool aNotify)
{ {
if (aNameSpaceID == kNameSpaceID_None && if (aNameSpaceID == kNameSpaceID_None) {
aName == nsGkAtoms::value && Selected()) { if (aName == nsGkAtoms::disabled) {
UpdateDisabledState(aNotify);
}
if (aName == nsGkAtoms::value && Selected()) {
// Since this option is selected, changing value // Since this option is selected, changing value
// may have changed missing validity state of the // may have changed missing validity state of the
// Select element // Select element
@ -257,6 +294,7 @@ HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
select->UpdateValueMissingValidityState(); select->UpdateValueMissingValidityState();
} }
} }
}
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
aValue, aNotify); aValue, aNotify);
@ -305,7 +343,7 @@ HTMLOptionElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Our new parent might change :disabled/:enabled state. // Our new parent might change :disabled/:enabled state.
UpdateState(false); UpdateDisabledState(false);
return NS_OK; return NS_OK;
} }
@ -316,7 +354,7 @@ HTMLOptionElement::UnbindFromTree(bool aDeep, bool aNullParent)
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
// Our previous parent could have been involved in :disabled/:enabled state. // Our previous parent could have been involved in :disabled/:enabled state.
UpdateState(false); UpdateDisabledState(false);
} }
EventStates EventStates
@ -330,23 +368,6 @@ HTMLOptionElement::IntrinsicState() const
state |= NS_EVENT_STATE_DEFAULT; state |= NS_EVENT_STATE_DEFAULT;
} }
// An <option> is disabled if it has @disabled set or if it's <optgroup> has
// @disabled set.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
nsIContent* parent = GetParent();
if (parent && parent->IsHTMLElement(nsGkAtoms::optgroup) &&
parent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
state &= ~NS_EVENT_STATE_DISABLED;
state |= NS_EVENT_STATE_ENABLED;
}
}
return state; return state;
} }

View File

@ -54,6 +54,19 @@ public:
void SetSelectedInternal(bool aValue, bool aNotify); void SetSelectedInternal(bool aValue, bool aNotify);
/**
* This callback is called by an optgroup on all its option elements whenever
* its disabled state is changed so that option elements can know their
* disabled state might have changed.
*/
void OptGroupDisabledChanged(bool aNotify);
/**
* Check our disabled content attribute and optgroup's (if it exists) disabled
* state to decide whether our disabled flag should be toggled.
*/
void UpdateDisabledState(bool aNotify);
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
@ -68,7 +81,7 @@ public:
nsresult CopyInnerTo(mozilla::dom::Element* aDest); nsresult CopyInnerTo(mozilla::dom::Element* aDest);
virtual bool IsDisabled() const override { virtual bool IsDisabled() const override {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); return State().HasState(NS_EVENT_STATE_DISABLED);
} }
bool Disabled() const bool Disabled() const

View File

@ -1293,6 +1293,12 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
{ {
if (aNameSpaceID == kNameSpaceID_None) { if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::disabled) { if (aName == nsGkAtoms::disabled) {
// This *has* to be called *before* validity state check because
// UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState depend on our disabled state.
UpdateDisabledState(aNotify);
UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation(); UpdateBarredFromConstraintValidation();
} else if (aName == nsGkAtoms::required) { } else if (aName == nsGkAtoms::required) {
UpdateValueMissingValidityState(); UpdateValueMissingValidityState();
@ -1856,9 +1862,14 @@ HTMLSelectElement::UpdateBarredFromConstraintValidation()
void void
HTMLSelectElement::FieldSetDisabledChanged(bool aNotify) HTMLSelectElement::FieldSetDisabledChanged(bool aNotify)
{ {
UpdateBarredFromConstraintValidation(); // This *has* to be called before UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState because these two functions depend on our
// disabled state.
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify); nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation();
UpdateState(aNotify);
} }
void void

View File

@ -1279,6 +1279,13 @@ HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
if (aNameSpaceID == kNameSpaceID_None) { if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled || if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
aName == nsGkAtoms::readonly) { aName == nsGkAtoms::readonly) {
if (aName == nsGkAtoms::disabled) {
// This *has* to be called *before* validity state check because
// UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState depend on our disabled state.
UpdateDisabledState(aNotify);
}
UpdateValueMissingValidityState(); UpdateValueMissingValidityState();
// This *has* to be called *after* validity has changed. // This *has* to be called *after* validity has changed.
@ -1542,10 +1549,14 @@ HTMLTextAreaElement::HasCachedSelection()
void void
HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify) HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify)
{ {
// This *has* to be called before UpdateBarredFromConstraintValidation and
// UpdateValueMissingValidityState because these two functions depend on our
// disabled state.
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateValueMissingValidityState(); UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation(); UpdateBarredFromConstraintValidation();
UpdateState(aNotify);
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
} }
JSObject* JSObject*

View File

@ -2308,8 +2308,7 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
bool bool
nsGenericHTMLFormElement::IsDisabled() const nsGenericHTMLFormElement::IsDisabled() const
{ {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) || return State().HasState(NS_EVENT_STATE_DISABLED);
(mFieldSet && mFieldSet->IsDisabled());
} }
void void
@ -2355,17 +2354,6 @@ nsGenericHTMLFormElement::IntrinsicState() const
// implement IntrinsicState() and are affected by that attribute. // implement IntrinsicState() and are affected by that attribute.
EventStates state = nsGenericHTMLElement::IntrinsicState(); EventStates state = nsGenericHTMLElement::IntrinsicState();
if (CanBeDisabled()) {
// :enabled/:disabled
if (IsDisabled()) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
state &= ~NS_EVENT_STATE_DISABLED;
state |= NS_EVENT_STATE_ENABLED;
}
}
if (mForm && mForm->IsDefaultSubmitElement(this)) { if (mForm && mForm->IsDefaultSubmitElement(this)) {
NS_ASSERTION(IsSubmitControl(), NS_ASSERTION(IsSubmitControl(),
"Default submit element that isn't a submit control."); "Default submit element that isn't a submit control.");
@ -2625,10 +2613,37 @@ nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify)
} }
} }
void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify)
{
if (!CanBeDisabled()) {
return;
}
bool isDisabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
if (!isDisabled && mFieldSet) {
isDisabled = mFieldSet->IsDisabled();
}
EventStates disabledStates;
if (isDisabled) {
disabledStates |= NS_EVENT_STATE_DISABLED;
} else {
disabledStates |= NS_EVENT_STATE_ENABLED;
}
EventStates oldDisabledStates = State() & DISABLED_STATES;
EventStates changedStates = disabledStates ^ oldDisabledStates;
if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);
}
}
void void
nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify) nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
{ {
UpdateState(aNotify); UpdateDisabledState(aNotify);
} }
bool bool

View File

@ -1345,6 +1345,12 @@ public:
*/ */
virtual void FieldSetDisabledChanged(bool aNotify); virtual void FieldSetDisabledChanged(bool aNotify);
/**
* Check our disabled content attribute and fieldset's (if it exists) disabled
* state to decide whether our disabled flag should be toggled.
*/
void UpdateDisabledState(bool aNotify);
void FieldSetFirstLegendChanged(bool aNotify) { void FieldSetFirstLegendChanged(bool aNotify) {
UpdateFieldSet(aNotify); UpdateFieldSet(aNotify);
} }