tor-browser

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

commit 818d00dba473c2b3cf98caefc3b6ef5b873ff265
parent dee40b711de1d5ee0adcffb6158b0586b0f9687b
Author: Vincent Hilla <vhilla@mozilla.com>
Date:   Tue, 28 Oct 2025 12:26:34 +0000

Bug 1767228 - Add showPicker Support for Input with Data List. r=emilio,credential-management-reviewers,dimi

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

Diffstat:
Mdom/html/HTMLInputElement.cpp | 20++++++++++++++++++--
Mdom/html/HTMLInputElement.h | 2+-
Mdom/html/test/forms/mochitest.toml | 3+++
Adom/html/test/forms/test_input_text_show_picker.html | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/semantics/forms/the-input-element/show-picker-does-not-focus.html | 29+++++++++++++++++++++++++++++
Mtoolkit/components/satchel/nsFormFillController.cpp | 40++++++++++++++++++++++++----------------
Mtoolkit/components/satchel/nsFormFillController.h | 2+-
Mtoolkit/components/satchel/nsIFormFillController.idl | 2+-
8 files changed, 146 insertions(+), 21 deletions(-)

diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp @@ -65,6 +65,7 @@ #include "nsIEditor.h" #include "nsIFilePicker.h" #include "nsIFormControl.h" +#include "nsIFormFillController.h" #include "nsIFrame.h" #include "nsIMutationObserver.h" #include "nsIPromptCollection.h" @@ -5898,7 +5899,7 @@ void HTMLInputElement::ShowPicker(ErrorResult& aRv) { // InitFilePicker() and InitColorPicker() consume it themselves, // so only consume in this function if not those. - // Step 4. If element's type attribute is in the File Upload state, then run + // Step 5. If element's type attribute is in the File Upload state, then run // these steps in parallel: if (mType == FormControlType::InputFile) { FilePickerType type = FILE_PICKER_FILE; @@ -5910,9 +5911,11 @@ void HTMLInputElement::ShowPicker(ErrorResult& aRv) { return; } - // Step 5. Otherwise, the user agent should show any relevant user interface + // Step 6. Otherwise, the user agent should show any relevant user interface // for selecting a value for element, in the way it normally would when the // user interacts with the control + + // Step 6 for color if (mType == FormControlType::InputColor) { InitColorPicker(); return; @@ -5925,6 +5928,7 @@ void HTMLInputElement::ShowPicker(ErrorResult& aRv) { return; } + // Step 6 for date and time types if (IsDateTimeTypeSupported(mType)) { if (CreatesDateTimeWidget()) { if (RefPtr<Element> dateTimeBoxElement = GetDateTimeBoxElement()) { @@ -5939,6 +5943,18 @@ void HTMLInputElement::ShowPicker(ErrorResult& aRv) { GetDateTimeInputBoxValue(value); OpenDateTimePicker(value); } + return; + } + + // Step 6 for input elements with a suggestions source element. + // I.e. show the autocomplete dropdown based on the list attribute. + // XXX Form-fill support on android is bug 1535985. + if (IsSingleLineTextControl(true) && GetList()) { + if (nsCOMPtr<nsIFormFillController> controller = + do_GetService("@mozilla.org/satchel/form-fill-controller;1")) { + controller->SetControlledElement(this); + controller->ShowPopup(); + } } } diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h @@ -720,7 +720,7 @@ class HTMLInputElement final : public TextControlElement, SelectionMode aSelectMode, ErrorResult& aRv); - void ShowPicker(ErrorResult& aRv); + MOZ_CAN_RUN_SCRIPT void ShowPicker(ErrorResult& aRv); bool WebkitDirectoryAttr() const { return HasAttr(nsGkAtoms::webkitdirectory); diff --git a/dom/html/test/forms/mochitest.toml b/dom/html/test/forms/mochitest.toml @@ -135,6 +135,9 @@ skip-if = ["os == 'mac' && os_version == '10.15' && processor == 'x86_64' && deb ["test_input_setting_value.html"] +["test_input_text_show_picker.html"] +skip-if = ["os == 'android'"] # Bug 1535985 + ["test_input_textarea_set_value_no_scroll.html"] ["test_input_time_key_events.html"] diff --git a/dom/html/test/forms/test_input_text_show_picker.html b/dom/html/test/forms/test_input_text_show_picker.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>input.showPicker() with datalist shows the autocomplete popup</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + const controller = SpecialPowers.Cc["@mozilla.org/autocomplete/controller;1"].getService( + SpecialPowers.Ci.nsIAutoCompleteController + ); + + async function testWithInputType(aType) { + const inp = document.getElementById("inp-showPicker"); + inp.type = aType; + + ok(!controller.input?.popupOpen, `Initially no input popup`); + + SpecialPowers.wrap(document).notifyUserGestureActivation(); + try { + inp.showPicker(); + } catch (e) { + if (e.name == "SecurityError" && isXOrigin) { + ok(true, "showPicker was blocked in xorigin document"); + return; + } + throw e; + } + is(isXOrigin, false, "showPicker should be blocked in xorigin document"); + + await SimpleTest.promiseWaitForCondition( + () => controller.input?.popupOpen, + `input[type=${aType}] popup was not opened` + ); + ok(controller.input?.popupOpen, `input[type=${aType}] popup open`); + + // focus a different element + synthesizeMouseAtCenter(document.getElementById("inp-blur"), {}); + await SimpleTest.promiseWaitForCondition( + () => !controller.input?.popupOpen, + `input[type=${aType}] popup was closed` + ); + } + + SimpleTest.waitForExplicitFinish(); + window.onload = async function() { + for (const type of ["text", "search", "email", "url", "tel", "number"]) { + await testWithInputType(type); + } + SimpleTest.finish(); + }; + </script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> + +<input list="id-list" id="inp-showPicker" /> +<datalist id="id-list"> + <option value="Chocolate"></option> + <option value="Coconut"></option> +</datalist> + +<input id="inp-blur"> + +</body> +</html> diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-does-not-focus.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-does-not-focus.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Test showPicker() does not focus the element</title> +<meta name="timeout" content="long"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> +<script type=module> +import inputTypes from "./input-types.js"; + +function createElement(t, type) { + const input = document.createElement("input"); + input.type = type; + document.body.append(input); + t.add_cleanup(() => input.remove()); + return input; +} + +for (const inputType of inputTypes) { + promise_test(async (t) => { + const input = createElement(t, inputType); + await test_driver.bless('show picker'); + input.showPicker(); + assert_not_equals(input, document.activeElement, "Input element was not focused"); + }, `input[type=${inputType}].showPicker() does not focus the element`); +} +</script> diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp @@ -271,6 +271,27 @@ nsFormFillController::MarkAsAutoCompletableField(Element* aElement) { } NS_IMETHODIMP +nsFormFillController::SetControlledElement(Element* aElement) { + if (!aElement || + !aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { + return NS_OK; + } + + MaybeStartControllingInput(aElement); + + // Bail if we didn't start controlling the input. + if (!mControlledElement) { + return NS_OK; + } + + // if there is a delayed task to restart the controller after an attribute + // change, cancel it to prevent it overriding the controlled input + MaybeCancelAttributeChangeTask(); + + return NS_OK; +} + +NS_IMETHODIMP nsFormFillController::GetControlledElement(Element** aElement) { *aElement = mControlledElement; NS_IF_ADDREF(*aElement); @@ -864,21 +885,7 @@ void nsFormFillController::MaybeStartControllingInput(Element* aElement) { } nsresult nsFormFillController::HandleFocus(Element* aElement) { - if (!aElement || - !aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { - return NS_OK; - } - - MaybeStartControllingInput(aElement); - - // Bail if we didn't start controlling the input. - if (!mControlledElement) { - return NS_OK; - } - - // if there is a delayed task to restart the controller after an attribute - // change, cancel it to prevent it overriding the focused input - MaybeCancelAttributeChangeTask(); + MOZ_TRY(SetControlledElement(aElement)); // If this focus doesn't follow a right click within our specified // threshold then show the autocomplete popup for all password fields. @@ -1135,7 +1142,8 @@ void nsFormFillController::StartControllingInput(Element* aElement) { } bool nsFormFillController::IsFocusedInputControlled() const { - return mControlledElement && mController && !ReadOnly(mControlledElement); + return mControlledElement && mController && !ReadOnly(mControlledElement) && + nsFocusManager::GetFocusedElementStatic() == mControlledElement; } void nsFormFillController::StopControllingInput() { diff --git a/toolkit/components/satchel/nsFormFillController.h b/toolkit/components/satchel/nsFormFillController.h @@ -72,7 +72,7 @@ class nsFormFillController final : public nsIFormFillController, bool IsFocusedInputControlled() const; MOZ_CAN_RUN_SCRIPT - nsresult HandleFocus(mozilla::dom::Element* aInput); + nsresult HandleFocus(mozilla::dom::Element* aElement); void AttachListeners(mozilla::dom::EventTarget* aEventTarget); diff --git a/toolkit/components/satchel/nsIFormFillController.idl b/toolkit/components/satchel/nsIFormFillController.idl @@ -26,7 +26,7 @@ interface nsIFormFillController : nsISupports /* * The input or textarea element the form fill controller is currently bound to. */ - readonly attribute Element controlledElement; + [setter_can_run_script] attribute Element controlledElement; /* * Whether the autocomplete popup on a password field was automatically opened