зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1855633 - Part 2: Activation behavior method for button element. r=edgar
Differential Revision: https://phabricator.services.mozilla.com/D183989
This commit is contained in:
Родитель
b88dc29857
Коммит
3f852adf17
|
@ -995,7 +995,6 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget,
|
|||
if (preVisitor.mWantsActivationBehavior) {
|
||||
MOZ_ASSERT(&chain[0] == targetEtci);
|
||||
activationTargetItemIndex.emplace(0);
|
||||
preVisitor.mEvent->mFlags.mMultiplePreActionsPrevented = true;
|
||||
}
|
||||
|
||||
if (!preVisitor.mCanHandle) {
|
||||
|
@ -1065,7 +1064,6 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget,
|
|||
activationTargetItemIndex.isNothing() && aEvent->mFlags.mBubbles) {
|
||||
MOZ_ASSERT(&chain.LastElement() == parentEtci);
|
||||
activationTargetItemIndex.emplace(chain.Length() - 1);
|
||||
preVisitor.mEvent->mFlags.mMultiplePreActionsPrevented = true;
|
||||
}
|
||||
|
||||
if (preVisitor.mCanHandle) {
|
||||
|
|
|
@ -172,21 +172,25 @@ void HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
|||
|
||||
if (outerActivateEvent) {
|
||||
aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
|
||||
if (mType == FormControlType::ButtonSubmit && mForm &&
|
||||
!aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented) {
|
||||
aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented = true;
|
||||
aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
|
||||
aVisitor.mItemData = static_cast<Element*>(mForm);
|
||||
// tell the form that we are about to enter a click handler.
|
||||
// that means that if there are scripted submissions, the
|
||||
// latest one will be deferred until after the exit point of the handler.
|
||||
mForm->OnSubmitClickBegin(this);
|
||||
}
|
||||
aVisitor.mWantsActivationBehavior = true;
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::GetEventTargetParent(aVisitor);
|
||||
}
|
||||
|
||||
void HTMLButtonElement::LegacyPreActivationBehavior(
|
||||
EventChainVisitor& aVisitor) {
|
||||
// out-of-spec legacy pre-activation behavior needed because of bug 1803805
|
||||
if (mType == FormControlType::ButtonSubmit && mForm) {
|
||||
aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
|
||||
aVisitor.mItemData = static_cast<Element*>(mForm);
|
||||
// tell the form that we are about to enter a click handler.
|
||||
// that means that if there are scripted submissions, the
|
||||
// latest one will be deferred until after the exit point of the handler.
|
||||
mForm->OnSubmitClickBegin(this);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
||||
nsresult rv = NS_OK;
|
||||
if (!aVisitor.mPresContext) {
|
||||
|
@ -223,24 +227,19 @@ nsresult HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
|||
HandleKeyboardActivation(aVisitor);
|
||||
}
|
||||
|
||||
if (aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT) {
|
||||
if (mForm) {
|
||||
// Hold a strong ref while dispatching
|
||||
RefPtr<mozilla::dom::HTMLFormElement> form(mForm);
|
||||
if (mType == FormControlType::ButtonReset) {
|
||||
form->MaybeReset(this);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mType == FormControlType::ButtonSubmit) {
|
||||
form->MaybeSubmit(this);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-button-state
|
||||
// NS_FORM_BUTTON_BUTTON do nothing.
|
||||
}
|
||||
HandlePopoverTargetAction();
|
||||
// Bug 1459231: Temporarily needed till links respect activation target
|
||||
// Then also remove NS_OUTER_ACTIVATE_EVENT
|
||||
if ((aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT) && mForm &&
|
||||
(mType == FormControlType::ButtonReset ||
|
||||
mType == FormControlType::ButtonSubmit)) {
|
||||
aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void EndSubmitClick(EventChainVisitor& aVisitor) {
|
||||
if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK)) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mItemData));
|
||||
RefPtr<HTMLFormElement> form = HTMLFormElement::FromNodeOrNull(content);
|
||||
|
@ -257,8 +256,40 @@ nsresult HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
|||
// Note, NS_IN_SUBMIT_CLICK is set only when we're in outer activate event.
|
||||
form->FlushPendingSubmission();
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
void HTMLButtonElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
|
||||
if (!aVisitor.mPresContext) {
|
||||
// Should check whether EndSubmitClick is needed here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDisabled()) {
|
||||
if (mForm) {
|
||||
// Hold a strong ref while dispatching
|
||||
RefPtr<mozilla::dom::HTMLFormElement> form(mForm);
|
||||
if (mType == FormControlType::ButtonReset) {
|
||||
form->MaybeReset(this);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mType == FormControlType::ButtonSubmit) {
|
||||
form->MaybeSubmit(this);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-button-state
|
||||
// NS_FORM_BUTTON_BUTTON do nothing.
|
||||
}
|
||||
HandlePopoverTargetAction();
|
||||
}
|
||||
|
||||
EndSubmitClick(aVisitor);
|
||||
}
|
||||
|
||||
void HTMLButtonElement::LegacyCanceledActivationBehavior(
|
||||
EventChainPostVisitor& aVisitor) {
|
||||
// still need to end submission, see bug 1803805
|
||||
// e.g. when parent element of button has event handler preventing default
|
||||
// legacy canceled instead of activation behavior will be run
|
||||
EndSubmitClick(aVisitor);
|
||||
}
|
||||
|
||||
nsresult HTMLButtonElement::BindToTree(BindContext& aContext,
|
||||
|
|
|
@ -53,6 +53,11 @@ class HTMLButtonElement final : public nsGenericHTMLFormControlElementWithState,
|
|||
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override;
|
||||
void LegacyPreActivationBehavior(EventChainVisitor& aVisitor) override;
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void ActivationBehavior(EventChainPostVisitor& aVisitor) override;
|
||||
void LegacyCanceledActivationBehavior(
|
||||
EventChainPostVisitor& aVisitor) override;
|
||||
|
||||
// nsINode
|
||||
nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
|
||||
|
|
|
@ -3125,8 +3125,7 @@ bool HTMLInputElement::CheckActivationBehaviorPreconditions(
|
|||
if (outerActivateEvent) {
|
||||
aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
|
||||
}
|
||||
return outerActivateEvent &&
|
||||
!aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented;
|
||||
return outerActivateEvent;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[Event-dispatch-single-activation-behavior.html]
|
||||
[When clicking child <FORM><BUTTON type=reset></BUTTON></FORM> of parent <INPUT type=checkbox></INPUT>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><BUTTON type=reset></BUTTON></FORM> of parent <INPUT type=radio></INPUT>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><INPUT type=checkbox></INPUT><SPAN></SPAN></LABEL> of parent <INPUT type=checkbox></INPUT>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -38,47 +32,8 @@
|
|||
[When clicking child <LABEL><INPUT type=checkbox></INPUT><SPAN></SPAN></LABEL> of parent <FORM><BUTTON type=reset></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <INPUT type=checkbox></INPUT>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <FORM><INPUT type=submit></INPUT></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <FORM><INPUT type=image></INPUT></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <FORM><INPUT type=reset></INPUT></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <FORM><BUTTON type=submit></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <FORM><BUTTON type=reset></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <A></A>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <AREA></AREA>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <LABEL><BUTTON type=button></BUTTON></LABEL> of parent <INPUT type=radio></INPUT>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><INPUT type=submit></INPUT></FORM> of parent <FORM><BUTTON type=submit></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><INPUT type=submit></INPUT></FORM> of parent <FORM><BUTTON type=reset></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><INPUT type=image></INPUT></FORM> of parent <FORM><BUTTON type=submit></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><INPUT type=image></INPUT></FORM> of parent <FORM><BUTTON type=reset></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><INPUT type=reset></INPUT></FORM> of parent <FORM><BUTTON type=submit></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
||||
[When clicking child <FORM><INPUT type=reset></INPUT></FORM> of parent <FORM><BUTTON type=reset></BUTTON></FORM>, only child should be activated.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -198,7 +198,6 @@ async_test(t => {
|
|||
}));
|
||||
|
||||
span.addEventListener("click", t.step_func(ev => {
|
||||
ev.preventDefault();
|
||||
t.step_timeout(() => t.done(), 500);
|
||||
}));
|
||||
|
||||
|
@ -207,4 +206,27 @@ async_test(t => {
|
|||
|
||||
}, "clicking the child of a button by dispatching a non-bubbling event should not trigger submit");
|
||||
|
||||
async_test(t => {
|
||||
|
||||
const form = document.createElement("form");
|
||||
const button = document.createElement("button");
|
||||
button.disabled = true;
|
||||
const span = document.createElement("span");
|
||||
button.appendChild(span);
|
||||
form.appendChild(button);
|
||||
document.body.appendChild(form);
|
||||
|
||||
form.addEventListener("submit", t.step_func_done(ev => {
|
||||
ev.preventDefault();
|
||||
assert_unreached("Form should not be submitted");
|
||||
}));
|
||||
|
||||
span.addEventListener("click", t.step_func(ev => {
|
||||
assert_true(true, "span was clicked");
|
||||
t.step_timeout(() => t.done(), 500);
|
||||
}));
|
||||
|
||||
span.click();
|
||||
|
||||
}, "clicking the child of a disabled button with .click() should not trigger submit");
|
||||
</script>
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
<iframe name=frame1 id=frame1></iframe>
|
||||
<form id=form1 target=frame1 action="does_not_exist.html">
|
||||
<button id=submitbutton type=submit>
|
||||
<div id=buttonchilddiv>
|
||||
button child div text
|
||||
</div>
|
||||
</button>
|
||||
<div id=parentdiv>
|
||||
<button id=submitbutton type=submit>
|
||||
<div id=buttonchilddiv>
|
||||
button child div text
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
@ -31,4 +33,26 @@ async_test(t => {
|
|||
buttonChildDiv.click();
|
||||
});
|
||||
}, 'This test will pass if a form navigation successfully occurs when clicking a child element of a <button type=submit> element with a onclick event handler which prevents the default form submission and manually calls form.submit() instead.');
|
||||
|
||||
async_test(t => {
|
||||
window.addEventListener('load', () => {
|
||||
const frame1 = document.getElementById('frame1');
|
||||
frame1.addEventListener('load', t.step_func_done(() => {}));
|
||||
|
||||
const submitButton = document.getElementById('submitbutton');
|
||||
submitButton.addEventListener('click', event => {
|
||||
const form = document.getElementById('form1');
|
||||
form.submit();
|
||||
});
|
||||
|
||||
const parentDiv = document.getElementById("parentdiv");
|
||||
parentDiv.addEventListener("click", event => {
|
||||
// event was already handled for the button
|
||||
// but it's activation behavior won't have run yet and we prevent that now
|
||||
event.preventDefault();
|
||||
})
|
||||
|
||||
submitButton.click();
|
||||
});
|
||||
}, "clicking a submit button, which calls form.submit and has a parent calling e.prevenDefault() should still submit the form");
|
||||
</script>
|
||||
|
|
|
@ -99,8 +99,6 @@ struct BaseEventFlags {
|
|||
// the first <label> element is clicked, that one may set this true.
|
||||
// Then, the second <label> element won't handle the event.
|
||||
bool mMultipleActionsPrevented : 1;
|
||||
// Similar to above but expected to be used during PreHandleEvent phase.
|
||||
bool mMultiplePreActionsPrevented : 1;
|
||||
// If mIsBeingDispatched is true, the DOM event created from the event is
|
||||
// dispatching into the DOM tree and not completed.
|
||||
bool mIsBeingDispatched : 1;
|
||||
|
|
Загрузка…
Ссылка в новой задаче