LibWeb: Allow keyboard input to alter email inputs

Previously, the`HTMLInputElement.selectinStart` and
`HTMLInputElement.selectionEnd` IDL setters, and the
`setRangeText()` IDL method were used when updating an input's value
on keyboard input. These methods can't be used for this purpose,
since selection doesn't apply to email type inputs. Therefore, this
change introduces internal-use only methods that don't check whether
selection applies to the given input.
This commit is contained in:
Tim Ledbetter 2024-12-27 17:03:16 +00:00 committed by Andreas Kling
parent 79da6d48c1
commit db24440403
Notes: github-actions[bot] 2024-12-30 10:05:12 +00:00
8 changed files with 83 additions and 40 deletions

View file

@ -252,7 +252,7 @@ WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::select()
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionstart
Optional<WebIDL::UnsignedLong> FormAssociatedTextControlElement::selection_start() const
Optional<WebIDL::UnsignedLong> FormAssociatedTextControlElement::selection_start_binding() const
{
// 1. If this element is an input element, and selectionStart does not apply to this element, return null.
auto const& html_element = form_associated_element_to_html_element();
@ -273,8 +273,13 @@ Optional<WebIDL::UnsignedLong> FormAssociatedTextControlElement::selection_start
return m_selection_start < m_selection_end ? m_selection_start : m_selection_end;
}
WebIDL::UnsignedLong FormAssociatedTextControlElement::selection_start() const
{
return m_selection_start < m_selection_end ? m_selection_start : m_selection_end;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionstart-2
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_selection_start(Optional<WebIDL::UnsignedLong> const& value)
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_selection_start_binding(Optional<WebIDL::UnsignedLong> const& value)
{
// 1. If this element is an input element, and selectionStart does not apply to this element,
// throw an "InvalidStateError" DOMException.
@ -299,7 +304,7 @@ WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_selection_start(
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionend
Optional<WebIDL::UnsignedLong> FormAssociatedTextControlElement::selection_end() const
Optional<WebIDL::UnsignedLong> FormAssociatedTextControlElement::selection_end_binding() const
{
// 1. If this element is an input element, and selectionEnd does not apply to this element, return
// null.
@ -321,8 +326,13 @@ Optional<WebIDL::UnsignedLong> FormAssociatedTextControlElement::selection_end()
return m_selection_start < m_selection_end ? m_selection_end : m_selection_start;
}
WebIDL::UnsignedLong FormAssociatedTextControlElement::selection_end() const
{
return m_selection_start < m_selection_end ? m_selection_end : m_selection_start;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionend-3
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_selection_end(Optional<WebIDL::UnsignedLong> const& value)
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_selection_end_binding(Optional<WebIDL::UnsignedLong> const& value)
{
// 1. If this element is an input element, and selectionEnd does not apply to this element,
// throw an "InvalidStateError" DOMException.
@ -391,19 +401,28 @@ WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_selection_direct
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-setrangetext
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text(String const& replacement)
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text_binding(String const& replacement)
{
return set_range_text(replacement, m_selection_start, m_selection_end);
return set_range_text_binding(replacement, m_selection_start, m_selection_end);
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-setrangetext
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text_binding(String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode selection_mode)
{
auto& html_element = form_associated_element_to_html_element();
// 1. If this element is an input element, and setRangeText() does not apply to this element,
// throw an "InvalidStateError" DOMException.
if (is<HTMLInputElement>(html_element) && !static_cast<HTMLInputElement&>(html_element).selection_or_range_applies())
return WebIDL::InvalidStateError::create(html_element.realm(), "setRangeText does not apply to this input type"_string);
return set_range_text(replacement, start, end, selection_mode);
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-setrangetext
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text(String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode selection_mode)
{
// 1. If this element is an input element, and setRangeText() does not apply to this element,
// throw an "InvalidStateError" DOMException.
auto& html_element = form_associated_element_to_html_element();
if (is<HTMLInputElement>(html_element) && !static_cast<HTMLInputElement&>(html_element).selection_or_range_applies())
return WebIDL::InvalidStateError::create(html_element.realm(), "setRangeText does not apply to this input type"_string);
// 2. Set this element's dirty value flag to true.
set_dirty_value_flag(true);
@ -603,10 +622,7 @@ void FormAssociatedTextControlElement::handle_insert(String const& data)
}
auto selection_start = this->selection_start();
auto selection_end = this->selection_end();
if (!selection_start.has_value() || !selection_end.has_value()) {
return;
}
MUST(set_range_text(data_for_insertion, selection_start.value(), selection_end.value(), Bindings::SelectionMode::End));
MUST(set_range_text(data_for_insertion, selection_start, selection_end, Bindings::SelectionMode::End));
text_node->invalidate_style(DOM::StyleInvalidationReason::EditingInsertion);
did_edit_text_node();
@ -619,22 +635,19 @@ void FormAssociatedTextControlElement::handle_delete(DeleteDirection direction)
return;
auto selection_start = this->selection_start();
auto selection_end = this->selection_end();
if (!selection_start.has_value() || !selection_end.has_value()) {
return;
}
if (selection_start == selection_end) {
if (direction == DeleteDirection::Backward) {
if (selection_start.value() > 0) {
MUST(set_range_text(String {}, selection_start.value() - 1, selection_end.value(), Bindings::SelectionMode::End));
if (selection_start > 0) {
MUST(set_range_text(String {}, selection_start - 1, selection_end, Bindings::SelectionMode::End));
}
} else {
if (selection_start.value() < text_node->data().code_points().length()) {
MUST(set_range_text(String {}, selection_start.value(), selection_end.value() + 1, Bindings::SelectionMode::End));
if (selection_start < text_node->data().code_points().length()) {
MUST(set_range_text(String {}, selection_start, selection_end + 1, Bindings::SelectionMode::End));
}
}
return;
}
MUST(set_range_text(String {}, selection_start.value(), selection_end.value(), Bindings::SelectionMode::End));
MUST(set_range_text(String {}, selection_start, selection_end, Bindings::SelectionMode::End));
}
void FormAssociatedTextControlElement::handle_return_key()

View file

@ -143,12 +143,14 @@ public:
WebIDL::ExceptionOr<void> select();
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionstart
Optional<WebIDL::UnsignedLong> selection_start() const;
WebIDL::ExceptionOr<void> set_selection_start(Optional<WebIDL::UnsignedLong> const&);
Optional<WebIDL::UnsignedLong> selection_start_binding() const;
WebIDL::ExceptionOr<void> set_selection_start_binding(Optional<WebIDL::UnsignedLong> const&);
WebIDL::UnsignedLong selection_start() const;
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionend
Optional<WebIDL::UnsignedLong> selection_end() const;
WebIDL::ExceptionOr<void> set_selection_end(Optional<WebIDL::UnsignedLong> const&);
Optional<WebIDL::UnsignedLong> selection_end_binding() const;
WebIDL::ExceptionOr<void> set_selection_end_binding(Optional<WebIDL::UnsignedLong> const&);
WebIDL::UnsignedLong selection_end() const;
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectiondirection
Optional<String> selection_direction() const;
@ -157,7 +159,8 @@ public:
SelectionDirection selection_direction_state() const { return m_selection_direction; }
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-setrangetext
WebIDL::ExceptionOr<void> set_range_text(String const& replacement);
WebIDL::ExceptionOr<void> set_range_text_binding(String const& replacement);
WebIDL::ExceptionOr<void> set_range_text_binding(String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode = Bindings::SelectionMode::Preserve);
WebIDL::ExceptionOr<void> set_range_text(String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode = Bindings::SelectionMode::Preserve);
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-setselectionrange

View file

@ -60,11 +60,11 @@ interface HTMLInputElement : HTMLElement {
readonly attribute NodeList? labels;
undefined select();
attribute unsigned long? selectionStart;
attribute unsigned long? selectionEnd;
[ImplementedAs=selection_start_binding] attribute unsigned long? selectionStart;
[ImplementedAs=selection_end_binding] attribute unsigned long? selectionEnd;
[ImplementedAs=selection_direction_binding] attribute DOMString? selectionDirection;
undefined setRangeText(DOMString replacement);
undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
[ImplementedAs=set_range_text_binding] undefined setRangeText(DOMString replacement);
[ImplementedAs=set_range_text_binding] undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
undefined showPicker();

View file

@ -328,22 +328,22 @@ WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_rows(WebIDL::UnsignedLong row
WebIDL::UnsignedLong HTMLTextAreaElement::selection_start_binding() const
{
return selection_start().value();
return FormAssociatedTextControlElement::selection_start_binding().value();
}
WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_selection_start_binding(WebIDL::UnsignedLong const& value)
{
return set_selection_start(value);
return FormAssociatedTextControlElement::set_selection_start_binding(value);
}
WebIDL::UnsignedLong HTMLTextAreaElement::selection_end_binding() const
{
return selection_end().value();
return FormAssociatedTextControlElement::selection_end_binding().value();
}
WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_selection_end_binding(WebIDL::UnsignedLong const& value)
{
return set_selection_end(value);
return FormAssociatedTextControlElement::set_selection_end_binding(value);
}
String HTMLTextAreaElement::selection_direction_binding() const

View file

@ -38,7 +38,7 @@ interface HTMLTextAreaElement : HTMLElement {
[ImplementedAs=selection_start_binding] attribute unsigned long selectionStart;
[ImplementedAs=selection_end_binding] attribute unsigned long selectionEnd;
[ImplementedAs=selection_direction_binding] attribute DOMString selectionDirection;
undefined setRangeText(DOMString replacement);
undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
[ImplementedAs=set_range_text_binding] undefined setRangeText(DOMString replacement);
[ImplementedAs=set_range_text_binding] undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
};

View file

@ -200,9 +200,7 @@ CSSPixelRect PaintableFragment::selection_rect() const
}
auto selection_start = text_control_element->selection_start();
auto selection_end = text_control_element->selection_end();
if (!selection_start.has_value() || !selection_end.has_value())
return {};
return range_rect(selection_start.value(), selection_end.value());
return range_rect(selection_start, selection_end);
}
auto selection = paintable().document().get_selection();
if (!selection)

View file

@ -0,0 +1,6 @@
input[type=text] value: PASS
input[type=search] value: PASS
input[type=tel] value: PASS
input[type=url] value: PASS
input[type=email] value: PASS
input[type=password] value: PASS

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="include.js"></script>
<script>
test(() => {
const inputTypes = [
"text",
"search",
"tel",
"url",
"email",
"password",
];
inputTypes.forEach(type => {
const input = document.createElement("input");
input.type = type;
document.body.appendChild(input);
internals.sendText(input, "PASS");
println(`input[type=${type}] value: ${input.value}`);
input.remove();
});
});
</script>