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");