This is an automated email from the ASF dual-hosted git repository. reiern70 pushed a commit to branch firefox-triggered-submit in repository https://gitbox.apache.org/repos/asf/wicket.git
commit ef55f0ec92505528d3205ecb6a8294a533a240eb Author: reiern70 <[email protected]> AuthorDate: Mon Oct 5 10:59:49 2026 -0500 Keep Firefox from submitting a form for the triggered submit event An AjaxFormSubmitBehavior whose shouldTriggerJavaScriptSubmitEvent() returns true fires a submit event on the form before its Ajax request, so that submit handlers run - UploadProgressBar starts its bar from one. Since the jQuery-free engine, the precondition fired it with dispatchEvent(). Firefox still submits a form for a submit event fired by a script unless the event is cancelled, so an AjaxButton like the one of the upload example sent its Ajax request and submitted the form the normal way as well: the page was reloaded behind the Ajax response. Chrome does not submit for such an event. The precondition now calls the new Wicket.Event.triggerSubmit(form) of both engines. It fires the event, tells whether a submit handler cancelled it, and cancels it once it has bubbled up to the window, so no browser submits the form; the Ajax request does. A handler cancelling the event still stops the request. Applications see the Ajax request only, as in Chrome. Wicket 10.x is not affected: its precondition triggers the event through jQuery and cancels it. GitHub issue #1650 --- .../ajax/form/AjaxFormSubmitBehaviorTest.java | 27 ++++++++++ .../wicket/ajax/form/AjaxFormSubmitBehavior.java | 5 +- .../wicket/ajax/res/js/wicket-ajax-jquery.js | 26 ++++++++++ .../org/apache/wicket/ajax/res/js/wicket-ajax.js | 26 ++++++++++ wicket-core/src/test/js/event.js | 60 ++++++++++++++++++++++ 5 files changed, 143 insertions(+), 1 deletion(-) diff --git a/wicket-core-tests/src/test/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehaviorTest.java b/wicket-core-tests/src/test/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehaviorTest.java index 48d5007cc1..f7a85e844a 100644 --- a/wicket-core-tests/src/test/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehaviorTest.java +++ b/wicket-core-tests/src/test/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehaviorTest.java @@ -78,6 +78,33 @@ class AjaxFormSubmitBehaviorTest extends WicketTestCase formTester.submit("submit"); } + /** + * The submit event is triggered through {@code Wicket.Event.triggerSubmit()}, which keeps + * Firefox from submitting the form for it besides the Ajax request. + */ + @Test + void theSubmitEventIsTriggeredWithoutSubmittingTheForm() + { + AjaxFormSubmitBehaviorTestPage page = new AjaxFormSubmitBehaviorTestPage(); + page.getForm().getTextField().add(new AjaxFormSubmitBehavior("click") + { + private static final long serialVersionUID = 1L; + + @Override + protected boolean shouldTriggerJavaScriptSubmitEvent() + { + return true; + } + }); + + tester.startPage(page); + + String response = tester.getLastResponseAsString(); + assertTrue(response.contains("Wicket.Event.triggerSubmit(document.getElementById('" + + page.getForm().getMarkupId() + "'))"), response); + assertFalse(response.contains("dispatchEvent"), response); + } + public static class NestedFormTestPage extends NestedFormPage { diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java index 128ca2a514..aad70dbca2 100644 --- a/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java +++ b/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormSubmitBehavior.java @@ -143,6 +143,9 @@ public abstract class AjaxFormSubmitBehavior extends AjaxEventBehavior /** * Controls whether or not a JS <code>submit</code> should be triggered on the submitting form. * False by default. + * <p> + * The event runs the submit handlers of the form, and a handler cancelling it stops the Ajax + * request. The browser does not submit the form for this event; the Ajax request does. * * @return true if <code>submit</code> should be triggered, false otherwise */ @@ -184,7 +187,7 @@ public abstract class AjaxFormSubmitBehavior extends AjaxEventBehavior public CharSequence getPrecondition(Component component) { return String.format( - "return document.getElementById('%s').dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));", + "return Wicket.Event.triggerSubmit(document.getElementById('%s'));", form.getMarkupId()); } }); diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js b/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js index 288dcd004c..25c20d93c7 100644 --- a/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js +++ b/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js @@ -2505,6 +2505,32 @@ } }, + /** + * Fires a submit event on the given form, so that its submit handlers run, without + * submitting the form: an Ajax request submits it instead. Firefox submits a form for + * a submit event fired by a script unless the event is cancelled, so the event is + * cancelled once it has bubbled up to the window. + * + * @param form {HTMLFormElement} the form + * @returns {boolean} false if a submit handler cancelled the event + */ + triggerSubmit: function(form) { + let cancelledByHandler = null; + const cancel = function (event) { + cancelledByHandler = event.defaultPrevented; + event.preventDefault(); + }; + let notCancelled; + window.addEventListener('submit', cancel); + try { + notCancelled = form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true })); + } finally { + window.removeEventListener('submit', cancel); + } + // null when a handler stopped the propagation before the window + return cancelledByHandler === null ? notCancelled : !cancelledByHandler; + }, + /** * The names of the topics on which Wicket notifies */ diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax.js b/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax.js index befb85810a..4d86bf3833 100644 --- a/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax.js +++ b/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax.js @@ -2785,6 +2785,32 @@ } }, + /** + * Fires a submit event on the given form, so that its submit handlers run, without + * submitting the form: an Ajax request submits it instead. Firefox submits a form for + * a submit event fired by a script unless the event is cancelled, so the event is + * cancelled once it has bubbled up to the window. + * + * @param form {HTMLFormElement} the form + * @returns {boolean} false if a submit handler cancelled the event + */ + triggerSubmit: function(form) { + let cancelledByHandler = null; + const cancel = function (event) { + cancelledByHandler = event.defaultPrevented; + event.preventDefault(); + }; + let notCancelled; + window.addEventListener('submit', cancel); + try { + notCancelled = form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true })); + } finally { + window.removeEventListener('submit', cancel); + } + // null when a handler stopped the propagation before the window + return cancelledByHandler === null ? notCancelled : !cancelledByHandler; + }, + /** * The names of the topics on which Wicket notifies */ diff --git a/wicket-core/src/test/js/event.js b/wicket-core/src/test/js/event.js index e23d5c043d..9bfa8c0098 100644 --- a/wicket-core/src/test/js/event.js +++ b/wicket-core/src/test/js/event.js @@ -252,6 +252,66 @@ Wicket.Event.add(window, 'domready', function() { }); + module('Wicket.Event.triggerSubmit'); + + // a form posting into a hidden frame, so a submission the test fails to stop does not leave + // the test page + var createTestForm = function () { + var fixture = document.getElementById('qunit-fixture'); + var frame = document.createElement('iframe'); + frame.name = 'triggerSubmitSink'; + fixture.appendChild(frame); + var form = document.createElement('form'); + form.action = 'about:blank'; + form.target = frame.name; + fixture.appendChild(form); + return form; + }; + + test('runs the submit handlers and keeps the browser from submitting', assert => { + var form = createTestForm(); + var events = []; + form.addEventListener('submit', function (event) { + events.push(event); + }); + document.addEventListener('submit', function bubbled(event) { + document.removeEventListener('submit', bubbled); + assert.notOk(event.defaultPrevented, 'not cancelled while the handlers run'); + }); + + assert.ok(Wicket.Event.triggerSubmit(form), 'the Ajax request may go on'); + assert.equal(events.length, 1, 'the handler ran'); + assert.ok(events[0].defaultPrevented, 'the browser does not submit the form'); + }); + + test('a handler cancelling the event stops the Ajax request', assert => { + var form = createTestForm(); + form.onsubmit = function () { + return false; + }; + + assert.notOk(Wicket.Event.triggerSubmit(form)); + }); + + test('a listener cancelling the event stops the Ajax request', assert => { + var form = createTestForm(); + document.addEventListener('submit', function cancel(event) { + document.removeEventListener('submit', cancel); + event.preventDefault(); + }); + + assert.notOk(Wicket.Event.triggerSubmit(form)); + }); + + test('a handler stopping the propagation without cancelling lets the Ajax request go on', assert => { + var form = createTestForm(); + form.addEventListener('submit', function (event) { + event.stopPropagation(); + }); + + assert.ok(Wicket.Event.triggerSubmit(form)); + }); + module('Wicket.Event.pubsub'); test('specified topic', assert => {
