diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.cc b/third_party/blink/renderer/core/html/forms/html_button_element.cc index db7f9c4796555d..55b9e7115c058b 100644 --- a/third_party/blink/renderer/core/html/forms/html_button_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_button_element.cc @@ -141,7 +141,7 @@ Element* HTMLButtonElement::commandForElement() { } if (!IsInTreeScope() || IsDisabledFormControl() || - (Form() && CanBeSuccessfulSubmitButton())) { + (Form() && FastHasAttribute(html_names::kTypeAttr) && type_ == kSubmit)) { return nullptr; } @@ -248,24 +248,44 @@ CommandEventType HTMLButtonElement::GetCommandEventType() const { void HTMLButtonElement::DefaultEventHandler(Event& event) { if (event.type() == event_type_names::kDOMActivate) { + bool potentialCommand = (FastHasAttribute(html_names::kCommandforAttr) || + FastHasAttribute(html_names::kCommandAttr)); + bool implicitSubmit = + type_ == kSubmit && !FastHasAttribute(html_names::kTypeAttr); + if (!IsDisabledFormControl()) { if (Form() && type_ == kSubmit) { + if (implicitSubmit && potentialCommand) { + AddConsoleMessage(mojom::blink::ConsoleMessageSource::kOther, + mojom::blink::ConsoleMessageLevel::kWarning, + "Buttons associated with forms that include " + "command or commandfor attributes are " + "ambiguous, and require a type=button attribute. " + "No action will be taken."); + return; + } else if (potentialCommand) { + DCHECK(FastHasAttribute(html_names::kTypeAttr)); + AddConsoleMessage( + mojom::blink::ConsoleMessageSource::kOther, + mojom::blink::ConsoleMessageLevel::kWarning, + "Buttons with an explicit type=submit will always submit a form, " + "so command or commandfor attributes will be ignored."); + } Form()->PrepareForSubmission(&event, this); event.SetDefaultHandled(); return; } if (Form() && type_ == kReset) { + if (potentialCommand) { + AddConsoleMessage(mojom::blink::ConsoleMessageSource::kOther, + mojom::blink::ConsoleMessageLevel::kWarning, + "Buttons with a type of reset will ignore the " + "command or commandfor attributes."); + } Form()->reset(); event.SetDefaultHandled(); return; } - if (Form() && type_ != kButton && commandForElement()) { - AddConsoleMessage( - mojom::blink::ConsoleMessageSource::kOther, - mojom::blink::ConsoleMessageLevel::kWarning, - "commandfor is ignored on form buttons without type=button."); - return; - } } // Buttons with a commandfor will dispatch a CommandEvent on the diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-button-element/button-click-resets-with-commandfor.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-button-element/button-click-resets-with-commandfor.tentative.html new file mode 100644 index 00000000000000..a1e3669717e90c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-button-element/button-click-resets-with-commandfor.tentative.html @@ -0,0 +1,64 @@ + + +Clicking a button should submit the form + + + + + +
+ +
+ + diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-button-element/button-click-submits-with-commandfor.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-button-element/button-click-submits-with-commandfor.tentative.html new file mode 100644 index 00000000000000..ced0d9ef152d82 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-button-element/button-click-submits-with-commandfor.tentative.html @@ -0,0 +1,110 @@ + + +Clicking a button should submit the form + + + + + +
+ +
+ + diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html index ef870627a066f5..74148caa2107d3 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html @@ -37,7 +37,7 @@ assert_equals(event.isTrusted, true, "isTrusted"); assert_equals(event.command, "--custom-command", "command"); assert_equals(event.target, invokee, "target"); - assert_equals(event.source, invokerbutton, "invoker"); + assert_equals(event.source, invokerbutton, "source"); }, "event dispatches on click"); // valid custom invokeactions @@ -56,7 +56,7 @@ assert_equals(event.isTrusted, true, "isTrusted"); assert_equals(event.command, command, "command"); assert_equals(event.target, invokee, "target"); - assert_equals(event.source, invokerbutton, "invoker"); + assert_equals(event.source, invokerbutton, "source"); }, `setting custom command property to ${command} (must include dash) sets event command`); promise_test(async function (t) { @@ -72,7 +72,7 @@ assert_equals(event.isTrusted, true, "isTrusted"); assert_equals(event.command, command, "command"); assert_equals(event.target, invokee, "target"); - assert_equals(event.source, invokerbutton, "invoker"); + assert_equals(event.source, invokerbutton, "source"); }, `setting custom command attribute to ${command} (must include dash) sets event command`); }, ); @@ -139,19 +139,47 @@ let called = false; invokee.addEventListener("command", (e) => (called = true), { once: true }); invokerbutton.setAttribute("form", "aform"); + invokerbutton.removeAttribute("type"); await clickOn(invokerbutton); assert_false(called, "event was not called"); - }, "event does not dispatch if invoker is form associated without `type`"); + }, "event does NOT dispatch if button is form associated, with implicit type"); + + promise_test(async function (t) { + t.add_cleanup(resetState); + let event; + invokee.addEventListener("command", (e) => (event = e), { once: true }); + invokerbutton.setAttribute("form", "aform"); + invokerbutton.setAttribute("type", "button"); + await clickOn(invokerbutton); + assert_true(event instanceof CommandEvent, "event is CommandEvent"); + assert_equals(event.type, "command", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.command, "--custom-command", "command"); + assert_equals(event.target, invokee, "target"); + assert_equals(event.source, invokerbutton, "source"); + }, "event dispatches if button is form associated, with explicit type=button"); promise_test(async function (t) { t.add_cleanup(resetState); let called = false; invokee.addEventListener("command", (e) => (called = true), { once: true }); invokerbutton.setAttribute("form", "aform"); - invokerbutton.setAttribute("type", "button"); + invokerbutton.setAttribute("type", "submit"); + await clickOn(invokerbutton); + assert_false(called, "event was not called"); + }, "event does NOT dispatch if button is form associated, with explicit type=submit"); + + promise_test(async function (t) { + t.add_cleanup(resetState); + let called = false; + invokee.addEventListener("command", (e) => (called = true), { once: true }); + invokerbutton.setAttribute("form", "aform"); + invokerbutton.setAttribute("type", "reset"); await clickOn(invokerbutton); - assert_true(called, "event was not called"); - }, "event dispatches if invoker is form associated with `type=button`"); + assert_false(called, "event was not called"); + }, "event does NOT dispatch if button is form associated, with explicit type=reset"); promise_test(async function (t) { svgInvokee = document.createElementNS("http://www.w3.org/2000/svg", "svg");