Remove -moz-user-input disabled to improve event handling.

This commit is contained in:
Fedor 2020-03-12 20:41:01 +03:00
parent af95d17221
commit 46d1844658
25 changed files with 68 additions and 78 deletions

View File

@ -14,7 +14,6 @@
a { a {
-moz-user-focus: normal; -moz-user-focus: normal;
-moz-user-input: enabled;
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
} }

View File

@ -1440,8 +1440,6 @@ exports.CSS_PROPERTIES = {
"supports": [], "supports": [],
"values": [ "values": [
"auto", "auto",
"disabled",
"enabled",
"inherit", "inherit",
"initial", "initial",
"none", "none",

View File

@ -59,8 +59,6 @@ public:
using nsGenericHTMLElement::Focus; using nsGenericHTMLElement::Focus;
virtual void Focus(mozilla::ErrorResult& aError) override; virtual void Focus(mozilla::ErrorResult& aError) override;
virtual bool IsDisabled() const override { return false; }
// nsIContent // nsIContent
virtual nsresult PostHandleEvent( virtual nsresult PostHandleEvent(
EventChainPostVisitor& aVisitor) override; EventChainPostVisitor& aVisitor) override;

View File

@ -77,8 +77,6 @@ public:
NS_IMETHOD Reset() override; NS_IMETHOD Reset() override;
NS_IMETHOD SubmitNamesValues(HTMLFormSubmission *aFormSubmission) override; NS_IMETHOD SubmitNamesValues(HTMLFormSubmission *aFormSubmission) override;
virtual bool IsDisabled() const override { return false; }
virtual void DoneAddingChildren(bool aHaveNotified) override; virtual void DoneAddingChildren(bool aHaveNotified) override;
virtual bool IsDoneAddingChildren() override; virtual bool IsDoneAddingChildren() override;

View File

@ -53,15 +53,14 @@ HTMLOptGroupElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
aVisitor.mCanHandle = false; aVisitor.mCanHandle = false;
// Do not process any DOM events if the element is disabled // Do not process any DOM events if the element is disabled
// XXXsmaug This is not the right thing to do. But what is? // XXXsmaug This is not the right thing to do. But what is?
if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) { if (IsDisabled()) {
return NS_OK; return NS_OK;
} }
nsIFrame* frame = GetPrimaryFrame(); if (nsIFrame* frame = GetPrimaryFrame()) {
if (frame) { // FIXME(emilio): This poking at the style of the frame is broken unless we
const nsStyleUserInterface* uiStyle = frame->StyleUserInterface(); // flush before every event handling, which we don't really want to.
if (uiStyle->mUserInput == StyleUserInput::None || if (frame->StyleUserInterface()->mUserInput == StyleUserInput::None) {
uiStyle->mUserInput == StyleUserInput::Disabled) {
return NS_OK; return NS_OK;
} }
} }

View File

@ -46,10 +46,6 @@ public:
virtual nsIDOMNode* AsDOMNode() override { return this; } virtual nsIDOMNode* AsDOMNode() override { return this; }
virtual bool IsDisabled() const override {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
}
bool Disabled() const bool Disabled() const
{ {
return GetBoolAttr(nsGkAtoms::disabled); return GetBoolAttr(nsGkAtoms::disabled);

View File

@ -67,10 +67,6 @@ public:
nsresult CopyInnerTo(mozilla::dom::Element* aDest); nsresult CopyInnerTo(mozilla::dom::Element* aDest);
virtual bool IsDisabled() const override {
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
}
bool Disabled() const bool Disabled() const
{ {
return GetBoolAttr(nsGkAtoms::disabled); return GetBoolAttr(nsGkAtoms::disabled);

View File

@ -35,8 +35,6 @@ public:
NS_IMETHOD Reset() override; NS_IMETHOD Reset() override;
NS_IMETHOD SubmitNamesValues(HTMLFormSubmission* aFormSubmission) override; NS_IMETHOD SubmitNamesValues(HTMLFormSubmission* aFormSubmission) override;
virtual bool IsDisabled() const override { return false; }
nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,

View File

@ -2109,14 +2109,6 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
return nsGenericHTMLElement::PreHandleEvent(aVisitor); return nsGenericHTMLElement::PreHandleEvent(aVisitor);
} }
/* virtual */
bool
nsGenericHTMLFormElement::IsDisabled() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) ||
(mFieldSet && mFieldSet->IsDisabled());
}
void void
nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset) nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset)
{ {
@ -2308,14 +2300,13 @@ nsGenericHTMLFormElement::IsElementDisabledForEvents(EventMessage aMessage,
break; break;
} }
bool disabled = IsDisabled(); // FIXME(emilio): This poking at the style of the frame is slightly bogus
if (!disabled && aFrame) { // unless we flush before every event, which we don't really want to do.
const nsStyleUserInterface* uiStyle = aFrame->StyleUserInterface(); if (aFrame &&
disabled = uiStyle->mUserInput == StyleUserInput::None || aFrame->StyleUserInterface()->mUserInput == StyleUserInput::None) {
uiStyle->mUserInput == StyleUserInput::Disabled; return true;
} }
return disabled; return IsDisabled();
} }
void void

View File

@ -817,8 +817,8 @@ public:
/** /**
* Returns the current disabled state of the element. * Returns the current disabled state of the element.
*/ */
virtual bool IsDisabled() const { bool IsDisabled() const {
return false; return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
} }
bool IsHidden() const bool IsHidden() const
@ -1222,8 +1222,6 @@ public:
virtual nsresult PreHandleEvent( virtual nsresult PreHandleEvent(
mozilla::EventChainPreVisitor& aVisitor) override; mozilla::EventChainPreVisitor& aVisitor) override;
virtual bool IsDisabled() const override;
/** /**
* This callback is called by a fieldest on all its elements whenever its * This callback is called by a fieldest on all its elements whenever its
* disabled attribute is changed so the element knows its disabled state * disabled attribute is changed so the element knows its disabled state

View File

@ -1786,7 +1786,7 @@ interface nsIDOMWindowUtils : nsISupports {
/** /**
* In certain cases the event handling of nodes, form controls in practice, * In certain cases the event handling of nodes, form controls in practice,
* may be disabled. Such cases are for example the existence of disabled * may be disabled. Such cases are for example the existence of disabled
* attribute or -moz-user-input: none/disabled. * attribute or -moz-user-input: none.
*/ */
boolean isNodeDisabledForEvents(in nsIDOMNode aNode); boolean isNodeDisabledForEvents(in nsIDOMNode aNode);

View File

@ -511,7 +511,7 @@ nsCaret::GetPaintGeometry(nsRect* aRect)
CheckSelectionLanguageChange(); CheckSelectionLanguageChange();
int32_t frameOffset; int32_t frameOffset;
nsIFrame *frame = GetFrameAndOffset(GetSelectionInternal(), nsIFrame* frame = GetFrameAndOffset(GetSelectionInternal(),
mOverrideContent, mOverrideOffset, &frameOffset); mOverrideContent, mOverrideOffset, &frameOffset);
if (!frame) { if (!frame) {
return nullptr; return nullptr;
@ -521,8 +521,7 @@ nsCaret::GetPaintGeometry(nsRect* aRect)
const nsStyleUserInterface* userinterface = frame->StyleUserInterface(); const nsStyleUserInterface* userinterface = frame->StyleUserInterface();
if ((!mIgnoreUserModify && if ((!mIgnoreUserModify &&
userinterface->mUserModify == StyleUserModify::ReadOnly) || userinterface->mUserModify == StyleUserModify::ReadOnly) ||
userinterface->mUserInput == StyleUserInput::None || frame->IsContentDisabled()){
userinterface->mUserInput == StyleUserInput::Disabled) {
return nullptr; return nullptr;
} }

View File

@ -1165,9 +1165,7 @@ nsComboboxControlFrame::HandleEvent(nsPresContext* aPresContext,
// If we have style that affects how we are selected, feed event down to // If we have style that affects how we are selected, feed event down to
// nsFrame::HandleEvent so that selection takes place when appropriate. // nsFrame::HandleEvent so that selection takes place when appropriate.
const nsStyleUserInterface* uiStyle = StyleUserInterface(); if (IsContentDisabled()) {
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsBlockFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return nsBlockFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
} }
return NS_OK; return NS_OK;

View File

@ -183,10 +183,8 @@ nsFormControlFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent, WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) nsEventStatus* aEventStatus)
{ {
// Check for user-input:none style // Check for disabled content so that selection works properly (?).
const nsStyleUserInterface* uiStyle = StyleUserInterface(); if (IsContentDisabled()) {
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
} }
return NS_OK; return NS_OK;

View File

@ -227,10 +227,7 @@ nsGfxButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
// from being called. The nsFrame::HandleEvent causes the button label // from being called. The nsFrame::HandleEvent causes the button label
// to be selected (Drawn with an XOR rectangle over the label) // to be selected (Drawn with an XOR rectangle over the label)
// do we have user-input style? if (IsContentDisabled()) {
const nsStyleUserInterface* uiStyle = StyleUserInterface();
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
} }
return NS_OK; return NS_OK;

View File

@ -150,15 +150,9 @@ nsImageControlFrame::HandleEvent(nsPresContext* aPresContext,
return NS_OK; return NS_OK;
} }
// do we have user-input style? if (IsContentDisabled()) {
const nsStyleUserInterface* uiStyle = StyleUserInterface();
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
} }
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) { // XXX cache disabled
return NS_OK;
}
*aEventStatus = nsEventStatus_eIgnore; *aEventStatus = nsEventStatus_eIgnore;

View File

@ -920,16 +920,11 @@ nsListControlFrame::HandleEvent(nsPresContext* aPresContext,
if (nsEventStatus_eConsumeNoDefault == *aEventStatus) if (nsEventStatus_eConsumeNoDefault == *aEventStatus)
return NS_OK; return NS_OK;
// do we have style that affects how we are selected? // disabled state affects how we're selected, but we don't want to go through
// do we have user-input style? // nsHTMLScrollFrame if we're disabled.
const nsStyleUserInterface* uiStyle = StyleUserInterface(); if (IsContentDisabled()) {
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
} }
EventStates eventStates = mContent->AsElement()->State();
if (eventStates.HasState(NS_EVENT_STATE_DISABLED))
return NS_OK;
return nsHTMLScrollFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return nsHTMLScrollFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
} }

View File

@ -5557,6 +5557,19 @@ nsFrame::Reflow(nsPresContext* aPresContext,
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
} }
bool
nsIFrame::IsContentDisabled() const
{
// FIXME(emilio): Doing this via CSS means callers must ensure the style is up
// to date, and they don't!
if (StyleUserInterface()->mUserInput == StyleUserInput::None) {
return true;
}
auto* element = nsGenericHTMLElement::FromContentOrNull(GetContent());
return element && element->IsDisabled();
}
nsresult nsresult
nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
{ {

View File

@ -2435,6 +2435,11 @@ public:
*/ */
nsIWidget* GetNearestWidget(nsPoint& aOffset) const; nsIWidget* GetNearestWidget(nsPoint& aOffset) const;
/**
* Whether the content for this frame is disabled, used for event handling.
*/
bool IsContentDisabled() const;
/** /**
* Get the "type" of the frame. May return nullptr. * Get the "type" of the frame. May return nullptr.
* *

View File

@ -2189,8 +2189,6 @@ const KTableEntry nsCSSProps::kUserFocusKTable[] = {
const KTableEntry nsCSSProps::kUserInputKTable[] = { const KTableEntry nsCSSProps::kUserInputKTable[] = {
{ eCSSKeyword_none, StyleUserInput::None }, { eCSSKeyword_none, StyleUserInput::None },
{ eCSSKeyword_enabled, StyleUserInput::Enabled },
{ eCSSKeyword_disabled, StyleUserInput::Disabled },
{ eCSSKeyword_auto, StyleUserInput::Auto }, { eCSSKeyword_auto, StyleUserInput::Auto },
{ eCSSKeyword_UNKNOWN, -1 } { eCSSKeyword_UNKNOWN, -1 }
}; };

View File

@ -241,8 +241,6 @@ enum class StyleUserSelect : uint8_t {
// user-input // user-input
enum class StyleUserInput : uint8_t { enum class StyleUserInput : uint8_t {
None, None,
Enabled,
Disabled,
Auto, Auto,
}; };

View File

@ -425,7 +425,6 @@ optgroup:disabled,
select:disabled:disabled /* Need the pseudo-class twice to have the specificity select:disabled:disabled /* Need the pseudo-class twice to have the specificity
be at least the same as select[size][multiple] above */ be at least the same as select[size][multiple] above */
{ {
-moz-user-input: disabled;
color: GrayText; color: GrayText;
background-color: ThreeDLightShadow; background-color: ThreeDLightShadow;
cursor: inherit; cursor: inherit;

View File

@ -2203,7 +2203,7 @@ var gCSSProperties = {
inherited: true, inherited: true,
type: CSS_TYPE_LONGHAND, type: CSS_TYPE_LONGHAND,
initial_values: [ "auto" ], initial_values: [ "auto" ],
other_values: [ "none", "enabled", "disabled" ], other_values: [ "none" ],
invalid_values: [] invalid_values: []
}, },
"-moz-user-modify": { "-moz-user-modify": {

View File

@ -15393,6 +15393,10 @@
"path": "dom/events/EventTarget-removeEventListener.html", "path": "dom/events/EventTarget-removeEventListener.html",
"url": "/dom/events/EventTarget-removeEventListener.html" "url": "/dom/events/EventTarget-removeEventListener.html"
}, },
{
"path": "dom/events/event-disabled-dynamic.html",
"url": "/dom/events/event-disabled-dynamic.html"
},
{ {
"path": "dom/events/ProgressEvent.html", "path": "dom/events/ProgressEvent.html",
"url": "/dom/events/ProgressEvent.html" "url": "/dom/events/ProgressEvent.html"

View File

@ -0,0 +1,21 @@
<!doctype html>
<meta charset=utf-8>
<title>Test that disabled is honored immediately in presence of dynamic changes</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Andreas Farre" href="mailto:afarre@mozilla.com">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#enabling-and-disabling-form-controls:-the-disabled-attribute">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1405087">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<input type="button" value="Click" disabled>
<script>
async_test(t => {
window.addEventListener('load', t.step_func(() => {
+
let e = document.querySelector('input');
e.disabled = false;
e.onclick = t.step_func_done(() => {});
e.click();
}));
}, "disabled is honored properly in presence of dynamic changes");
</script>