mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 04:38:02 +00:00
Bug 1375599 - Change IsDisabled() to look at NS_EVENT_STATE_DISABLED instead of the "disabled" attribute. r=bz
In order to speed up IsDisabled(), instead of querying for the @disabled attribute, we're now using the NS_EVENT_STATE_DISABLED flag to know whether an element is disabled. It is safe to use the NS_EVENT_STATE_DISABLED flag for the following reasons: - For form elements, nsGenericHTMLFormElement::IsDisabled() is only called on form elements that can be disabled; form elements that can't be disabled overrides IsDisabled() to return false directly. And, before this patch, NS_EVENT_STATE_DISABLED flag is set by nsGenericHTMLFormElement::IntrinsicState() if and only if IsDisabled() in all cases when CanBeDisabled() is true, and when CanBeDisabled() is false then IsDisabled() is always false and the flag is not set. - For non form elements, optgroup and option have the flag matching IsDisabled(). Note that option's IsDisabled() should also refer to optgroup's (if it exists) disabled state, which was not done before this patch. For this to work correctly, we need to set NS_EVENT_STATE_DISABLED earlier, that is, in AfterSetAttr(), before any consumer of IsDisabled(). We also need to update the flag whenever the element's parent (e.g. fieldset or optgroup) disabled state changes and when moving into/out of a parent container. Note that NS_EVENT_STATE_DISABLED/ENABLED is now part of the EXTERNALLY_MANAGED_STATES. MozReview-Commit-ID: KSceikeqvvU
This commit is contained in:
parent
eeeda23e0a
commit
3342b6100d
@ -328,6 +328,8 @@ private:
|
||||
NS_EVENT_STATE_DIR_ATTR_RTL | \
|
||||
NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO)
|
||||
|
||||
#define DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED)
|
||||
|
||||
// Event states that can be added and removed through
|
||||
// Element::{Add,Remove}ManuallyManagedStates.
|
||||
//
|
||||
@ -347,6 +349,7 @@ private:
|
||||
#define EXTERNALLY_MANAGED_STATES ( \
|
||||
MANUALLY_MANAGED_STATES | \
|
||||
DIR_ATTR_STATES | \
|
||||
DISABLED_STATES | \
|
||||
NS_EVENT_STATE_ACTIVE | \
|
||||
NS_EVENT_STATE_DRAGOVER | \
|
||||
NS_EVENT_STATE_FOCUS | \
|
||||
|
@ -106,9 +106,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
|
||||
@ -436,6 +441,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();
|
||||
}
|
||||
}
|
||||
|
@ -83,17 +83,22 @@ HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
const nsAttrValue* aOldValue, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1446,6 +1446,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.
|
||||
@ -7463,10 +7470,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
|
||||
|
@ -59,8 +59,6 @@ public:
|
||||
using nsGenericHTMLElement::Focus;
|
||||
virtual void Focus(mozilla::ErrorResult& aError) override;
|
||||
|
||||
virtual bool IsDisabled() const override { return false; }
|
||||
|
||||
// nsIContent
|
||||
virtual nsresult PostHandleEvent(
|
||||
EventChainPostVisitor& aVisitor) override;
|
||||
|
@ -102,13 +102,27 @@ HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aOldValue, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,22 +131,6 @@ HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
aOldValue, 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)
|
||||
{
|
||||
|
@ -38,8 +38,6 @@ public:
|
||||
virtual nsresult GetEventTargetParent(
|
||||
EventChainPreVisitor& aVisitor) override;
|
||||
|
||||
virtual EventStates IntrinsicState() const override;
|
||||
|
||||
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult,
|
||||
bool aPreallocateChildren) const override;
|
||||
|
||||
@ -51,7 +49,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
|
||||
|
@ -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)
|
||||
{
|
||||
@ -235,14 +268,19 @@ HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
const nsAttrValue* aOldValue, 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +331,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;
|
||||
}
|
||||
@ -304,7 +342,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
|
||||
@ -318,23 +356,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;
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,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;
|
||||
@ -77,7 +90,7 @@ public:
|
||||
nsresult CopyInnerTo(mozilla::dom::Element* aDest, bool aPreallocateChildren);
|
||||
|
||||
virtual bool IsDisabled() const override {
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
|
||||
return State().HasState(NS_EVENT_STATE_DISABLED);
|
||||
}
|
||||
|
||||
bool Disabled() const
|
||||
|
@ -1325,6 +1325,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();
|
||||
@ -1869,9 +1875,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
|
||||
|
@ -1107,6 +1107,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.
|
||||
@ -1420,10 +1427,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*
|
||||
|
@ -2091,8 +2091,7 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainVisitor& aVisitor)
|
||||
bool
|
||||
nsGenericHTMLFormElement::IsDisabled() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) ||
|
||||
(mFieldSet && mFieldSet->IsDisabled());
|
||||
return State().HasState(NS_EVENT_STATE_DISABLED);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2137,17 +2136,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.");
|
||||
@ -2409,10 +2397,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
|
||||
|
@ -1097,6 +1097,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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user