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:
parent
79da6d48c1
commit
db24440403
Notes:
github-actions[bot]
2024-12-30 10:05:12 +00:00
Author: https://github.com/tcl3 Commit: https://github.com/LadybirdBrowser/ladybird/commit/db244404033 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3065 Reviewed-by: https://github.com/Gingeh ✅
8 changed files with 83 additions and 40 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
23
Tests/LibWeb/Text/input/HTMLInputElement-edit-value.html
Normal file
23
Tests/LibWeb/Text/input/HTMLInputElement-edit-value.html
Normal 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>
|
Loading…
Add table
Reference in a new issue