mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1829874 - Allow events on disabled fieldsets r=edgar
The HTML spec does not allow fieldset to be "disabled as a form control". The spec does not explicitly define whether fieldset is a form control or not (and thus can be disabled as a child of another fieldset), but here we assume it does not as: * It's not a "control", right? * It would be confusing if it could not be disabled by its own but could be disabled as a child. Differential Revision: https://phabricator.services.mozilla.com/D179986
This commit is contained in:
parent
b84df1a557
commit
d753b20dd1
@ -16,12 +16,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=816340
|
||||
window.arguments[0].ok(val, msg);
|
||||
}
|
||||
|
||||
var elems =
|
||||
var elems =
|
||||
[
|
||||
"input",
|
||||
"textarea",
|
||||
"select",
|
||||
"fieldset",
|
||||
"button",
|
||||
];
|
||||
|
||||
@ -49,7 +48,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=816340
|
||||
ok(contentDidGetEvent == contentShouldGetEvent, "content: " + el + (disabled ? " disabled" : ""));
|
||||
ok(chromeDidGetEvent, "chrome: " + el + (disabled ? " disabled" : ""));
|
||||
}
|
||||
|
||||
|
||||
function start() {
|
||||
// Test common element.
|
||||
testElement("div", false, true);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/dom/CustomElementRegistry.h"
|
||||
#include "mozilla/dom/HTMLFieldSetElement.h"
|
||||
#include "mozilla/dom/HTMLFieldSetElementBinding.h"
|
||||
@ -49,6 +50,9 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLFieldSetElement,
|
||||
NS_IMPL_ELEMENT_CLONE(HTMLFieldSetElement)
|
||||
|
||||
bool HTMLFieldSetElement::IsDisabledForEvents(WidgetEvent* aEvent) {
|
||||
if (StaticPrefs::dom_forms_fieldset_disable_only_descendants_enabled()) {
|
||||
return false;
|
||||
}
|
||||
return IsElementDisabledForEvents(aEvent, nullptr);
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,6 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/DeclarationBlock.h"
|
||||
#include "mozilla/EditorBase.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
@ -15,29 +13,24 @@
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "mozilla/MappedDeclarations.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/StaticPrefs_html5.h"
|
||||
#include "mozilla/StaticPrefs_layout.h"
|
||||
#include "mozilla/StaticPrefs_accessibility.h"
|
||||
|
||||
#include "mozilla/dom/FormData.h"
|
||||
#include "nscore.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAtom.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include "mozilla/dom/BindContext.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsMappedAttributes.h"
|
||||
#include "nsHTMLStyleSheet.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsIFrameInlines.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsView.h"
|
||||
@ -45,7 +38,6 @@
|
||||
#include "nsIWidget.h"
|
||||
#include "nsRange.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsContainerFrame.h"
|
||||
@ -60,7 +52,6 @@
|
||||
#include "mozilla/dom/DirectionalityUtils.h"
|
||||
#include "mozilla/dom/DocumentOrShadowRoot.h"
|
||||
#include "nsString.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsDOMCSSDeclaration.h"
|
||||
#include "nsITextControlFrame.h"
|
||||
@ -68,15 +59,11 @@
|
||||
#include "mozilla/dom/HTMLFormElement.h"
|
||||
#include "nsFocusManager.h"
|
||||
|
||||
#include "mozilla/InternalMutationEvent.h"
|
||||
#include "nsDOMStringMap.h"
|
||||
#include "nsDOMString.h"
|
||||
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "nsHtml5Module.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/ElementInlines.h"
|
||||
#include "HTMLFieldSetElement.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "HTMLBRElement.h"
|
||||
@ -84,20 +71,16 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/FromParser.h"
|
||||
#include "mozilla/dom/Link.h"
|
||||
#include "mozilla/BloomFilter.h"
|
||||
#include "mozilla/dom/ScriptLoader.h"
|
||||
|
||||
#include "nsVariant.h"
|
||||
#include "nsDOMTokenList.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTextFragment.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/MouseEventBinding.h"
|
||||
#include "mozilla/dom/ToggleEvent.h"
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsHTMLDocument.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
@ -2260,7 +2243,22 @@ void nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify) {
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void nsGenericHTMLElement::Click(CallerType aCallerType) {
|
||||
if (IsDisabled() || HandlingClick()) {
|
||||
if (HandlingClick()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There are two notions of disabled.
|
||||
// "disabled":
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
|
||||
// "actually disabled":
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled
|
||||
// click() reads the former but IsDisabled() is for the latter. <fieldset> is
|
||||
// included only in the latter, so we exclude it here.
|
||||
// XXX(krosylight): What about <optgroup>? And should we add a separate method
|
||||
// for this?
|
||||
if (IsDisabled() &&
|
||||
!(mNodeInfo->Equals(nsGkAtoms::fieldset) &&
|
||||
StaticPrefs::dom_forms_fieldset_disable_only_descendants_enabled())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ function checkClickEvent(aFieldset)
|
||||
|
||||
sendMouseEvent({type:'click'}, aFieldset);
|
||||
SimpleTest.executeSoon(function() {
|
||||
ok(!clickHandled, "When disabled, fieldset should prevent click events");
|
||||
ok(clickHandled, "When disabled, fieldset should not prevent click events");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=557087
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var elementsPreventingClick = [ "input", "button", "select", "textarea", "fieldset" ];
|
||||
var elementsWithClick = [ "option", "optgroup", "output", "label", "object" ];
|
||||
var elementsPreventingClick = [ "input", "button", "select", "textarea" ];
|
||||
var elementsWithClick = [ "option", "optgroup", "output", "label", "object", "fieldset" ];
|
||||
var gHandled = 0;
|
||||
|
||||
function clickShouldNotHappenHandler(aEvent)
|
||||
|
@ -3018,6 +3018,13 @@
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
# Whether to disable only the descendants or the parent fieldset element too
|
||||
# Note that this still allows it to be selected by `:disable`.
|
||||
- name: dom.forms.fieldset_disable_only_descendants.enabled
|
||||
type: bool
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
# Is support for HTMLInputElement.showPicker enabled?
|
||||
- name: dom.input.showPicker
|
||||
type: bool
|
||||
|
@ -1,6 +0,0 @@
|
||||
[fieldset-event-propagation.tentative.html]
|
||||
[Disabled fieldset elements should not prevent click event propagation.]
|
||||
expected: FAIL
|
||||
|
||||
[Disabled fieldset elements should not block click events.]
|
||||
expected: FAIL
|
@ -92,13 +92,28 @@
|
||||
return { target, observedEvents };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} element
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isFormControl(element) {
|
||||
if (["button", "input", "select", "textarea"].includes(element.localName)) {
|
||||
return true;
|
||||
}
|
||||
return element.constructor.formAssociated;
|
||||
}
|
||||
|
||||
function isDisabledFormControl(element) {
|
||||
return isFormControl(element) && element.disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} target
|
||||
* @param {*} observedEvent
|
||||
*/
|
||||
function shouldNotBubble(target, observedEvent) {
|
||||
return (
|
||||
target.disabled &&
|
||||
isDisabledFormControl(target) &&
|
||||
observedEvent.isTrusted &&
|
||||
["mousedown", "mouseup", "click"].includes(observedEvent.type)
|
||||
);
|
||||
@ -136,7 +151,7 @@
|
||||
await t.step_func(clickerFn)(target);
|
||||
await new Promise(resolve => t.step_timeout(resolve, 0));
|
||||
|
||||
const expected = element.disabled ? expectedEvents : nonDisabledExpectedEvents;
|
||||
const expected = isDisabledFormControl(element) ? expectedEvents : nonDisabledExpectedEvents;
|
||||
assert_array_equals(observedEvents.map(e => e.type), expected, "Observed events");
|
||||
|
||||
for (const observed of observedEvents) {
|
||||
|
@ -15,34 +15,41 @@
|
||||
</div>
|
||||
|
||||
<div id=target2parent>
|
||||
<fieldset disabled id=target2fieldset>hello world</div>
|
||||
<fieldset disabled id=target2fieldset>hello world</fieldset>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
let target1parentClicked = false;
|
||||
let target1childClicked = false;
|
||||
let target1fieldsetClicked = false;
|
||||
target1parent.onclick = () => target1parentClicked = true;
|
||||
target1child.onclick = () => target1childClicked = true;
|
||||
target1fieldset.onclick = () => target1fieldsetClicked = true;
|
||||
const clickers = {
|
||||
"native click": target => test_driver.click(target),
|
||||
"click()": target => target.click(),
|
||||
};
|
||||
|
||||
await test_driver.click(target1child);
|
||||
for (const [clickerName, clicker] of Object.entries(clickers)) {
|
||||
promise_test(async () => {
|
||||
let target1parentClicked = false;
|
||||
let target1childClicked = false;
|
||||
let target1fieldsetClicked = false;
|
||||
target1parent.onclick = () => target1parentClicked = true;
|
||||
target1child.onclick = () => target1childClicked = true;
|
||||
target1fieldset.onclick = () => target1fieldsetClicked = true;
|
||||
|
||||
assert_true(target1parentClicked, 'The parent of the fieldset should receive a click event.');
|
||||
assert_true(target1childClicked, 'The child of the fieldset should receive a click event.');
|
||||
assert_true(target1fieldsetClicked, 'The fieldset element should receive a click event.');
|
||||
}, 'Disabled fieldset elements should not prevent click event propagation.');
|
||||
await clicker(target1child);
|
||||
|
||||
promise_test(async () => {
|
||||
let target2parentClicked = false;
|
||||
let target2fieldsetClicked = false;
|
||||
target2parent.onclick = () => target2parentClicked = true;
|
||||
target2fieldset.onclick = () => target2fieldsetClicked = true;
|
||||
assert_true(target1parentClicked, 'The parent of the fieldset should receive a click event.');
|
||||
assert_true(target1childClicked, 'The child of the fieldset should receive a click event.');
|
||||
assert_true(target1fieldsetClicked, 'The fieldset element should receive a click event.');
|
||||
}, `Disabled fieldset elements should not prevent click event propagation from ${clickerName}`);
|
||||
|
||||
await test_driver.click(target2fieldset);
|
||||
promise_test(async () => {
|
||||
let target2parentClicked = false;
|
||||
let target2fieldsetClicked = false;
|
||||
target2parent.onclick = () => target2parentClicked = true;
|
||||
target2fieldset.onclick = () => target2fieldsetClicked = true;
|
||||
|
||||
assert_true(target2parentClicked, 'The parent of the fieldset should receive a click event.');
|
||||
assert_true(target2fieldsetClicked, 'The fieldset element should receive a click event.');
|
||||
}, 'Disabled fieldset elements should not block click events.');
|
||||
await clicker(target2fieldset);
|
||||
|
||||
assert_true(target2parentClicked, 'The parent of the fieldset should receive a click event.');
|
||||
assert_true(target2fieldsetClicked, 'The fieldset element should receive a click event.');
|
||||
}, `Disabled fieldset elements should not block click events from ${clickerName}.`);
|
||||
}
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user