tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 592bf213da405cddeea55e72cd73a7f2d5d90231
parent 6de07a14b7c2a72f127a90bdbca27ae5bb0bd092
Author: Tim Giles <tgiles@mozilla.com>
Date:   Wed, 12 Nov 2025 16:30:20 +0000

Bug 1991743 - Allow moz-checkbox to work in a form. r=mkennedy

We need to override the public `setFormValue` and `formResetCallback`
methods of the base input element for moz-checkbox to behave as expected
in forms.

Differential Revision: https://phabricator.services.mozilla.com/D268050

Diffstat:
Mtoolkit/content/tests/widgets/test_moz_input_elems_in_form.html | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/widgets/moz-checkbox/moz-checkbox.mjs | 25+++++++++++++++++++++++++
2 files changed, 151 insertions(+), 0 deletions(-)

diff --git a/toolkit/content/tests/widgets/test_moz_input_elems_in_form.html b/toolkit/content/tests/widgets/test_moz_input_elems_in_form.html @@ -31,6 +31,11 @@ let templateFn = () => html` <form id="main-form"> <moz-fieldset> + <moz-checkbox + name="moz-checkbox" + value="checkbox_val" + checked="" + ></moz-checkbox> <moz-toggle name="moz-toggle" value="toggle_val" @@ -50,6 +55,12 @@ testHelpers.setupTests({ templateFn }); formAttributeTemplateFn = html`<form id="form-attribute-id"></form> <moz-fieldset> + <moz-checkbox + name="moz-checkbox" + value="checkbox_val" + checked="" + form="form-attribute-id" + ></moz-checkbox> <moz-toggle name="moz-toggle" value="toggle_val" @@ -77,6 +88,11 @@ event.preventDefault(); const formData = new FormData(form); is( + formData.get("moz-checkbox"), + "checkbox_val", + "moz-checkbox, when checked, should have a submitted value" + ); + is( formData.get("moz-toggle"), "toggle_val", "moz-toggle should have a submitted value" @@ -129,6 +145,26 @@ let childArray = Array.from(fieldset.children); await Promise.all(childArray.map(item => item.updateComplete)); await synthesizeMouseAtCenter(submit, {}); + + await synthesizeMouseAtCenter( + document.querySelector("moz-checkbox"), + {} + ); + let assertCheckboxValue = (event, form) => { + event.preventDefault(); + const formData = new FormData(form); + ok( + !formData.get("moz-checkbox"), + "moz-checkbox that is not checked should not submit a value" + ); + }; + form.addEventListener( + "submit", + event => assertCheckboxValue(event, form), + { once: true } + ); + + await synthesizeMouseAtCenter(submit, {}); }); add_task(async function test_form_reset() { @@ -138,6 +174,10 @@ for (let c of fieldset.children) { c.value = "non-default value"; } + let checkbox = document.querySelector("moz-checkbox"); + // Change the checked state so that we can assert later that + // it is restored to the defaultChecked value. + checkbox.checked = !checkbox.defaultChecked; // Assert that the value property of the input elements has // changed before we reset the form for (let c of fieldset.children) { @@ -148,10 +188,16 @@ ); } form.reset(); + let formData = new FormData(form); // Each tested element's value property should reset to its // value attribute. is( + formData.get("moz-checkbox"), + document.querySelector("moz-checkbox").getAttribute("value"), + "moz-checkbox should reset to default value" + ); + is( formData.get("moz-toggle"), document.querySelector("moz-toggle").getAttribute("value"), "moz-toggle should reset to default value" @@ -166,6 +212,43 @@ document.querySelector("moz-input-text").getAttribute("value"), "moz-input-text should reset to default value" ); + + // Assert that the checkbox checked is restored to the + // defaultChecked property + is( + checkbox.checked, + checkbox.defaultChecked, + "moz-checkbox should reset its checked state to the defaultChecked value" + ); + }); + + // Assert that moz-checkbox contributes its value to the form + // when it is checked, does not contribute its value when unchecked, + // and contributes its value when switching from unchecked to + // checked. + add_task(async function test_moz_checkbox() { + let renderTarget = await testHelpers.renderTemplate(); + let form = renderTarget.firstElementChild; + let formData = new FormData(form); + is( + formData.get("moz-checkbox"), + "checkbox_val", + "There should be a value for a checked moz-checkbox in the form data" + ); + let checkbox = document.querySelector("moz-checkbox"); + await synthesizeMouseAtCenter(checkbox, {}); + formData = new FormData(form); + ok( + !formData.get("moz-checkbox"), + "There should be no value for an unchecked moz-checkbox in the form data" + ); + await synthesizeMouseAtCenter(checkbox, {}); + formData = new FormData(form); + is( + formData.get("moz-checkbox"), + "checkbox_val", + "There should be a value for a checked moz-checkbox in the form data" + ); }); // Assert that the input elements submit their values as expected @@ -180,6 +263,11 @@ event.preventDefault(); const formData = new FormData(form); is( + formData.get("moz-checkbox"), + "checkbox_val", + "moz-checkbox, when checked, should have a submitted value" + ); + is( formData.get("moz-toggle"), "toggle_val", "moz-toggle should have a submitted value" @@ -232,6 +320,28 @@ let childArray = Array.from(fieldset.children); await Promise.all(childArray.map(item => item.updateComplete)); await synthesizeMouseAtCenter(submit, {}); + + // Assert that an unchecked moz-checkbox does not submit a value + // when a form is submitted. + await synthesizeMouseAtCenter( + document.querySelector("moz-checkbox"), + {} + ); + let assertCheckboxValue = (event, form) => { + event.preventDefault(); + const formData = new FormData(form); + ok( + !formData.get("moz-checkbox"), + "moz-checkbox that is not checked should not submit a value" + ); + }; + form.addEventListener( + "submit", + event => assertCheckboxValue(event, form), + { once: true } + ); + + await synthesizeMouseAtCenter(submit, {}); }); // Assert that the input elements reset their values as expected @@ -245,6 +355,10 @@ for (let c of fieldset.children) { c.value = "non-default value"; } + let checkbox = document.querySelector("moz-checkbox"); + // Change the checked state so that we can assert later that + // it is restored to the defaultChecked value. + checkbox.checked = !checkbox.defaultChecked; // Assert that the value property of the input elements has // changed before we reset the form @@ -261,6 +375,11 @@ // Each tested element's value property should reset to its // value attribute. is( + formData.get("moz-checkbox"), + document.querySelector("moz-checkbox").getAttribute("value"), + "moz-checkbox should reset to default value" + ); + is( formData.get("moz-toggle"), document.querySelector("moz-toggle").getAttribute("value"), "moz-toggle should reset to default value" @@ -275,6 +394,13 @@ document.querySelector("moz-input-text").getAttribute("value"), "moz-input-text should reset to default value" ); + // Assert that the checkbox checked is restored to the + // defaultChecked property + is( + checkbox.checked, + checkbox.defaultChecked, + "moz-checkbox should reset its checked state to the defaultChecked value" + ); }); </script> </head> diff --git a/toolkit/content/widgets/moz-checkbox/moz-checkbox.mjs b/toolkit/content/widgets/moz-checkbox/moz-checkbox.mjs @@ -39,6 +39,21 @@ export default class MozCheckbox extends MozBaseInputElement { this.checked = false; } + connectedCallback() { + super.connectedCallback(); + this.defaultChecked = this.getAttribute("checked") || this.checked; + this.checked = !!this.defaultChecked; + let val = this.getAttribute("value"); + if (!val) { + this.defaultValue = "on"; + this.value = "on"; + } else { + this.defaultValue = val; + this.value = val; + } + this.setFormValue(this.value); + } + /** * Handles click events and keeps the checkbox checked value in sync * @@ -47,6 +62,16 @@ export default class MozCheckbox extends MozBaseInputElement { */ handleStateChange(event) { this.checked = event.target.checked; + if (this.checked) { + this.setFormValue(this.value); + } else { + this.setFormValue(null); + } + } + + formResetCallback() { + this.checked = this.defaultChecked; + this.value = this.defaultValue; } inputTemplate() {