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);
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:
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 DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED)
#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 | \
NS_EVENT_STATE_UNRESOLVED)
#define INTRINSIC_STATES (~ESM_MANAGED_STATES)

View File

@ -113,9 +113,14 @@ HTMLButtonElement::UpdateBarredFromConstraintValidation()
void
HTMLButtonElement::FieldSetDisabledChanged(bool aNotify)
{
UpdateBarredFromConstraintValidation();
// FieldSetDisabledChanged *has* to be called *before*
// UpdateBarredFromConstraintValidation, because the latter depends on our
// disabled state.
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateBarredFromConstraintValidation();
UpdateState(aNotify);
}
// nsIDOMHTMLButtonElement
@ -522,6 +527,12 @@ HTMLButtonElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
}
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();
UpdateState(aNotify);
}

View File

@ -85,17 +85,22 @@ nsresult
HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled &&
nsINode::GetFirstChild()) {
if (!mElements) {
mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr,
true);
}
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
// This *has* to be called *before* calling FieldSetDisabledChanged on our
// controls, as they may depend on our disabled state.
UpdateDisabledState(aNotify);
uint32_t length = mElements->Length(true);
for (uint32_t i=0; i<length; ++i) {
static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
->FieldSetDisabledChanged(aNotify);
if (nsINode::GetFirstChild()) {
if (!mElements) {
mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr,
true);
}
uint32_t length = mElements->Length(true);
for (uint32_t i=0; i<length; ++i) {
static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
->FieldSetDisabledChanged(aNotify);
}
}
}

View File

@ -1142,6 +1142,13 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
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();
// This *has* to be called *after* validity has changed.
@ -7192,10 +7199,14 @@ HTMLInputElement::HasCachedSelection()
void
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();
UpdateBarredFromConstraintValidation();
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateState(aNotify);
}
void

View File

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

View File

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

View File

@ -37,8 +37,6 @@ public:
// nsIContent
virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override;
virtual EventStates IntrinsicState() const override;
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
@ -47,7 +45,7 @@ public:
virtual nsIDOMNode* AsDOMNode() override { return this; }
virtual bool IsDisabled() const override {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
return State().HasState(NS_EVENT_STATE_DISABLED);
}
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
HTMLOptionElement::GetSelected(bool* aValue)
{
@ -247,14 +280,19 @@ nsresult
HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None &&
aName == nsGkAtoms::value && Selected()) {
// Since this option is selected, changing value
// may have changed missing validity state of the
// Select element
HTMLSelectElement* select = GetSelect();
if (select) {
select->UpdateValueMissingValidityState();
if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::disabled) {
UpdateDisabledState(aNotify);
}
if (aName == nsGkAtoms::value && Selected()) {
// Since this option is selected, changing value
// may have changed missing validity state of the
// Select element
HTMLSelectElement* select = GetSelect();
if (select) {
select->UpdateValueMissingValidityState();
}
}
}
@ -305,7 +343,7 @@ HTMLOptionElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
NS_ENSURE_SUCCESS(rv, rv);
// Our new parent might change :disabled/:enabled state.
UpdateState(false);
UpdateDisabledState(false);
return NS_OK;
}
@ -316,7 +354,7 @@ HTMLOptionElement::UnbindFromTree(bool aDeep, bool aNullParent)
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
// Our previous parent could have been involved in :disabled/:enabled state.
UpdateState(false);
UpdateDisabledState(false);
}
EventStates
@ -330,23 +368,6 @@ HTMLOptionElement::IntrinsicState() const
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;
}

View File

@ -54,6 +54,19 @@ public:
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,
nsIContent* aBindingParent,
bool aCompileEventHandlers) override;
@ -68,7 +81,7 @@ public:
nsresult CopyInnerTo(mozilla::dom::Element* aDest);
virtual bool IsDisabled() const override {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
return State().HasState(NS_EVENT_STATE_DISABLED);
}
bool Disabled() const

View File

@ -1293,6 +1293,12 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
{
if (aNameSpaceID == kNameSpaceID_None) {
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();
} else if (aName == nsGkAtoms::required) {
UpdateValueMissingValidityState();
@ -1856,9 +1862,14 @@ HTMLSelectElement::UpdateBarredFromConstraintValidation()
void
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);
UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation();
UpdateState(aNotify);
}
void

View File

@ -1279,6 +1279,13 @@ HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
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();
// This *has* to be called *after* validity has changed.
@ -1542,10 +1549,14 @@ HTMLTextAreaElement::HasCachedSelection()
void
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();
UpdateBarredFromConstraintValidation();
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
UpdateState(aNotify);
}
JSObject*

View File

@ -2308,8 +2308,7 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
bool
nsGenericHTMLFormElement::IsDisabled() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) ||
(mFieldSet && mFieldSet->IsDisabled());
return State().HasState(NS_EVENT_STATE_DISABLED);
}
void
@ -2355,17 +2354,6 @@ nsGenericHTMLFormElement::IntrinsicState() const
// implement IntrinsicState() and are affected by that attribute.
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)) {
NS_ASSERTION(IsSubmitControl(),
"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
nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
{
UpdateState(aNotify);
UpdateDisabledState(aNotify);
}
bool

View File

@ -1345,6 +1345,12 @@ public:
*/
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) {
UpdateFieldSet(aNotify);
}