Skip to content

Commit

Permalink
#438: M1375599
Browse files Browse the repository at this point in the history
  • Loading branch information
classilla committed Oct 17, 2017
1 parent b8ebae8 commit 84bfb83
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 87 deletions.
10 changes: 10 additions & 0 deletions dom/base/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@ class Element : public FragmentOrElement
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;

Expand Down
3 changes: 3 additions & 0 deletions dom/events/EventStates.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,13 @@ class EventStates

#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)
Expand Down
13 changes: 12 additions & 1 deletion dom/html/HTMLButtonElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
25 changes: 15 additions & 10 deletions dom/html/HTMLFieldSetElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

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);
uint32_t length = mElements->Length(true);
for (uint32_t i=0; i<length; ++i) {
static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
->FieldSetDisabledChanged(aNotify);
}
}
}

Expand Down
15 changes: 13 additions & 2 deletions dom/html/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions dom/html/HTMLLabelElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class HTMLLabelElement final : public nsGenericHTMLFormElement,
NS_IMETHOD Reset() override;
NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission) override;

virtual bool IsDisabled() const override { return false; }

// nsIContent
virtual nsresult PostHandleEvent(
EventChainPostVisitor& aVisitor) override;
Expand Down
44 changes: 21 additions & 23 deletions dom/html/HTMLOptGroupElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,35 +105,33 @@ 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;
}
}

return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
aNotify);
}
EventStates oldDisabledStates = State() & DISABLED_STATES;
EventStates changedStates = disabledStates ^ oldDisabledStates;

EventStates
HTMLOptGroupElement::IntrinsicState() const
{
EventStates state = nsGenericHTMLElement::IntrinsicState();
if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);

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;
// 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);
}
}
}
}

return state;
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
aNotify);
}

JSObject*
Expand Down
4 changes: 1 addition & 3 deletions dom/html/HTMLOptGroupElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ class HTMLOptGroupElement final : public nsGenericHTMLElement,
// 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,
Expand All @@ -47,7 +45,7 @@ class HTMLOptGroupElement final : public nsGenericHTMLElement,
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
Expand Down
75 changes: 48 additions & 27 deletions dom/html/HTMLOptionElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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();
}
}
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -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
Expand All @@ -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;
}

Expand Down
15 changes: 14 additions & 1 deletion dom/html/HTMLOptionElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ class HTMLOptionElement final : public nsGenericHTMLElement,

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;
Expand All @@ -68,7 +81,7 @@ class HTMLOptionElement final : public nsGenericHTMLElement,
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
Expand Down
15 changes: 13 additions & 2 deletions dom/html/HTMLSelectElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 84bfb83

Please sign in to comment.