зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1659218 - Prevent activating disabled inputs via dispatchEvent r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D87151
This commit is contained in:
Родитель
17accc12da
Коммит
34dff9ebd1
|
@ -3107,7 +3107,8 @@ void HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
|||
// we've already toggled the state from onclick since the user could
|
||||
// explicitly dispatch DOMActivate on the element.
|
||||
//
|
||||
// This is a compatibility hack.
|
||||
// These are compatibility hacks and are defined as legacy-pre-activation
|
||||
// and legacy-canceled-activation behavior in HTML.
|
||||
//
|
||||
|
||||
// Track whether we're in the outermost Dispatch invocation that will
|
||||
|
@ -3639,7 +3640,7 @@ nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
|||
// tell the form that we are about to exit a click handler
|
||||
// so the form knows not to defer subsequent submissions
|
||||
// the pending ones that were created during the handler
|
||||
// will be flushed or forgoten.
|
||||
// will be flushed or forgotten.
|
||||
mForm->OnSubmitClickEnd();
|
||||
break;
|
||||
default:
|
||||
|
@ -3647,12 +3648,23 @@ nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
|||
}
|
||||
}
|
||||
|
||||
// now check to see if the event was "cancelled"
|
||||
bool preventDefault =
|
||||
aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault;
|
||||
if (IsDisabled() && oldType != NS_FORM_INPUT_CHECKBOX &&
|
||||
oldType != NS_FORM_INPUT_RADIO) {
|
||||
// Behave as if defaultPrevented when the element becomes disabled by event
|
||||
// listeners. Checkboxes and radio buttons should still process clicks for
|
||||
// web compat. See:
|
||||
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:activation-behaviour
|
||||
preventDefault = true;
|
||||
}
|
||||
|
||||
// now check to see if the event was canceled
|
||||
if (mCheckedIsToggled && outerActivateEvent) {
|
||||
if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
|
||||
// if it was cancelled and a radio button, then set the old
|
||||
if (preventDefault) {
|
||||
// if it was canceled and a radio button, then set the old
|
||||
// selected btn to TRUE. if it is a checkbox then set it to its
|
||||
// original value
|
||||
// original value (legacy-canceled-activation)
|
||||
if (oldType == NS_FORM_INPUT_RADIO) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aVisitor.mItemData);
|
||||
HTMLInputElement* selectedRadioButton =
|
||||
|
@ -3704,7 +3716,7 @@ nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
|||
StepNumberControlForUserEvent(keyEvent->mKeyCode == NS_VK_UP ? 1 : -1);
|
||||
FireChangeEventIfNeeded();
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
|
||||
} else if (!preventDefault) {
|
||||
switch (aVisitor.mEvent->mMessage) {
|
||||
case eFocus: {
|
||||
// see if we should select the contents of the textbox. This happens
|
||||
|
|
|
@ -302,4 +302,50 @@ async_test(function(t) {
|
|||
assert_false(didSubmit)
|
||||
t.done()
|
||||
}, "disconnected form should not submit")
|
||||
|
||||
async_test(t => {
|
||||
const form = document.createElement("form");
|
||||
form.onsubmit = t.step_func(ev => {
|
||||
ev.preventDefault();
|
||||
assert_unreached("The form is unexpectedly submitted.");
|
||||
});
|
||||
dump.append(form);
|
||||
const input = form.appendChild(document.createElement("input"));
|
||||
input.type = "submit"
|
||||
input.disabled = true;
|
||||
input.dispatchEvent(new MouseEvent("click", { cancelable: true }));
|
||||
t.done();
|
||||
}, "disabled submit button should not activate");
|
||||
|
||||
async_test(t => {
|
||||
const form = document.createElement("form");
|
||||
form.onsubmit = t.step_func(ev => {
|
||||
ev.preventDefault();
|
||||
assert_unreached("The form is unexpectedly submitted.");
|
||||
});
|
||||
dump.append(form);
|
||||
const input = form.appendChild(document.createElement("input"));
|
||||
input.onclick = t.step_func(() => {
|
||||
input.disabled = true;
|
||||
});
|
||||
input.type = "submit"
|
||||
input.dispatchEvent(new MouseEvent("click", { cancelable: true }));
|
||||
t.done();
|
||||
}, "submit button should not activate if the event listener disables it");
|
||||
|
||||
async_test(t => {
|
||||
const form = document.createElement("form");
|
||||
form.onsubmit = t.step_func(ev => {
|
||||
ev.preventDefault();
|
||||
assert_unreached("The form is unexpectedly submitted.");
|
||||
});
|
||||
dump.append(form);
|
||||
const input = form.appendChild(document.createElement("input"));
|
||||
input.onclick = t.step_func(() => {
|
||||
input.type = "submit"
|
||||
input.disabled = true;
|
||||
});
|
||||
input.click();
|
||||
t.done();
|
||||
}, "submit button that morphed from checkbox should not activate");
|
||||
</script>
|
||||
|
|
|
@ -49,4 +49,30 @@ test(t => {
|
|||
label.dispatchEvent(new MouseEvent("click"));
|
||||
assert_false(input.checked);
|
||||
}, "disabled radio should not be checked from label click by dispatchEvent");
|
||||
|
||||
test(t => {
|
||||
const checkbox = dump.appendChild(document.createElement("input"));
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.onclick = ev => {
|
||||
checkbox.type = "date";
|
||||
ev.preventDefault();
|
||||
};
|
||||
checkbox.dispatchEvent(new MouseEvent("click", { cancelable: true }));
|
||||
assert_false(checkbox.checked);
|
||||
}, "checkbox morphed into another type should not mutate checked state");
|
||||
|
||||
test(t => {
|
||||
const radio1 = dump.appendChild(document.createElement("input"));
|
||||
const radio2 = dump.appendChild(radio1.cloneNode());
|
||||
radio1.type = radio2.type = "radio";
|
||||
radio1.name = radio2.name = "foo";
|
||||
radio2.checked = true;
|
||||
radio1.onclick = ev => {
|
||||
radio1.type = "date";
|
||||
ev.preventDefault();
|
||||
};
|
||||
radio1.dispatchEvent(new MouseEvent("click", { cancelable: true }));
|
||||
assert_false(radio1.checked);
|
||||
assert_true(radio2.checked);
|
||||
}, "radio morphed into another type should not steal the existing checked state");
|
||||
</script>
|
||||
|
|
Загрузка…
Ссылка в новой задаче