diff --git a/Tests/LibWeb/Text/expected/input-selection-start-selection-end.txt b/Tests/LibWeb/Text/expected/input-selection-start-selection-end.txt new file mode 100644 index 00000000000..f9fa806ddf6 --- /dev/null +++ b/Tests/LibWeb/Text/expected/input-selection-start-selection-end.txt @@ -0,0 +1,7 @@ +Well hello friends text selectionStart: 0 +text selectionEnd: 0 +date selectionStart: null +date selectionEnd: null +text selectionStart: 18 +text selectionEnd: 18 +date input setting selectionStart error: InvalidStateError: setSelectionStart does not apply to this input type diff --git a/Tests/LibWeb/Text/input/input-selection-start-selection-end.html b/Tests/LibWeb/Text/input/input-selection-start-selection-end.html new file mode 100644 index 00000000000..6d01fafaaa9 --- /dev/null +++ b/Tests/LibWeb/Text/input/input-selection-start-selection-end.html @@ -0,0 +1,24 @@ + + + + diff --git a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp index cd9888c046a..d90b520803b 100644 --- a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp @@ -155,7 +155,8 @@ void FormAssociatedElement::reset_form_owner() // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionstart WebIDL::UnsignedLong FormAssociatedElement::selection_start() const { - // FIXME: 1. If this element is an input element, and selectionStart does not apply to this element, return null. + // 1. If this element is an input element, and selectionStart does not apply to this element, return null. + // NOTE: This is done by HTMLInputElement before calling this function // 2. If there is no selection, return the code unit offset within the relevant value to the character that // immediately follows the text entry cursor. @@ -170,10 +171,11 @@ WebIDL::UnsignedLong FormAssociatedElement::selection_start() const } // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionstart-2 -WebIDL::ExceptionOr FormAssociatedElement::set_selection_start(WebIDL::UnsignedLong) +WebIDL::ExceptionOr FormAssociatedElement::set_selection_start(Optional const&) { - // FIXME: 1. If this element is an input element, and selectionStart does not apply to this element, throw an + // 1. If this element is an input element, and selectionStart does not apply to this element, throw an // "InvalidStateError" DOMException. + // NOTE: This is done by HTMLInputElement before calling this function // FIXME: 2. Let end be the value of this element's selectionEnd attribute. // FIXME: 3. If end is less than the given value, set end to the given value. @@ -184,7 +186,8 @@ WebIDL::ExceptionOr FormAssociatedElement::set_selection_start(WebIDL::Uns // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionend WebIDL::UnsignedLong FormAssociatedElement::selection_end() const { - // FIXME: 1. If this element is an input element, and selectionEnd does not apply to this element, return null. + // 1. If this element is an input element, and selectionEnd does not apply to this element, return null. + // NOTE: This is done by HTMLInputElement before calling this function // 2. If there is no selection, return the code unit offset within the relevant value to the character that // immediately follows the text entry cursor. @@ -199,10 +202,11 @@ WebIDL::UnsignedLong FormAssociatedElement::selection_end() const } // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionend-3 -WebIDL::ExceptionOr FormAssociatedElement::set_selection_end(WebIDL::UnsignedLong) +WebIDL::ExceptionOr FormAssociatedElement::set_selection_end(Optional const&) { - // FIXME: 1. If this element is an input element, and selectionEnd does not apply to this element, throw an + // 1. If this element is an input element, and selectionEnd does not apply to this element, throw an // "InvalidStateError" DOMException. + // NOTE: This is done by HTMLInputElement before calling this function // FIXME: 2. Set the selection range with the value of this element's selectionStart attribute, the given value, and the // value of this element's selectionDirection attribute. diff --git a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h index 08579b4ed4d..8a480473234 100644 --- a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h +++ b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h @@ -90,10 +90,10 @@ public: virtual void reset_algorithm() {}; WebIDL::UnsignedLong selection_start() const; - WebIDL::ExceptionOr set_selection_start(WebIDL::UnsignedLong); + WebIDL::ExceptionOr set_selection_start(Optional const&); WebIDL::UnsignedLong selection_end() const; - WebIDL::ExceptionOr set_selection_end(WebIDL::UnsignedLong); + WebIDL::ExceptionOr set_selection_end(Optional const&); protected: FormAssociatedElement() = default; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index c8348e756f1..e0a0c04e0dc 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2018-2023, Andreas Kling * Copyright (c) 2022, Adam Hodgen * Copyright (c) 2022, Andrew Kaster - * Copyright (c) 2023, Shannon Booth + * Copyright (c) 2023-2024, Shannon Booth * Copyright (c) 2023, Bastiaan van der Plaat * * SPDX-License-Identifier: BSD-2-Clause @@ -2070,6 +2070,52 @@ WebIDL::ExceptionOr HTMLInputElement::set_selection_range(u32 start, u32 e return {}; } +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionstart-2 +WebIDL::ExceptionOr HTMLInputElement::set_selection_start_for_bindings(Optional const& value) +{ + // 1. If this element is an input element, and selectionStart does not apply to this element, throw an + // "InvalidStateError" DOMException. + if (!selection_or_range_applies()) + return WebIDL::InvalidStateError::create(realm(), "setSelectionStart does not apply to this input type"_fly_string); + + // NOTE: Steps continued below: + return set_selection_start(value); +} + +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionstart +Optional HTMLInputElement::selection_start_for_bindings() const +{ + // 1. If this element is an input element, and selectionStart does not apply to this element, return null. + if (!selection_or_range_applies()) + return {}; + + // NOTE: Steps continued below: + return selection_start(); +} + +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionend-3 +WebIDL::ExceptionOr HTMLInputElement::set_selection_end_for_bindings(Optional const& value) +{ + // 1. If this element is an input element, and selectionEnd does not apply to this element, throw an + // "InvalidStateError" DOMException. + if (!selection_or_range_applies()) + return WebIDL::InvalidStateError::create(realm(), "setSelectionEnd does not apply to this input type"_fly_string); + + // NOTE: Steps continued below: + return set_selection_end(value); +} + +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionend +Optional HTMLInputElement::selection_end_for_bindings() const +{ + // 1. If this element is an input element, and selectionEnd does not apply to this element, return null. + if (!selection_or_range_applies()) + return {}; + + // NOTE: Steps continued below: + return selection_end(); +} + Optional HTMLInputElement::default_role() const { // https://www.w3.org/TR/html-aria/#el-input-button @@ -2198,6 +2244,21 @@ bool HTMLInputElement::has_input_activation_behavior() const } } +// https://html.spec.whatwg.org/multipage/input.html#do-not-apply +bool HTMLInputElement::selection_or_range_applies() const +{ + switch (type_state()) { + case TypeAttributeState::Text: + case TypeAttributeState::Search: + case TypeAttributeState::Telephone: + case TypeAttributeState::URL: + case TypeAttributeState::Password: + return true; + default: + return false; + } +} + // https://html.spec.whatwg.org/multipage/input.html#the-input-element:event-change-2 bool HTMLInputElement::change_event_applies() const { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index f0289685d2f..f56cfafed48 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -196,6 +196,13 @@ public: bool value_as_number_applies() const; bool step_applies() const; bool step_up_or_down_applies() const; + bool selection_or_range_applies() const; + + WebIDL::ExceptionOr set_selection_start_for_bindings(Optional const&); + Optional selection_start_for_bindings() const; + + WebIDL::ExceptionOr set_selection_end_for_bindings(Optional const&); + Optional selection_end_for_bindings() const; private: HTMLInputElement(DOM::Document&, DOM::QualifiedName); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl index f50216a72f7..9ff4c9e5660 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl @@ -58,8 +58,8 @@ interface HTMLInputElement : HTMLElement { readonly attribute NodeList? labels; undefined select(); - [FIXME] attribute unsigned long? selectionStart; - [FIXME] attribute unsigned long? selectionEnd; + [ImplementedAs=selection_start_for_bindings] attribute unsigned long? selectionStart; + [ImplementedAs=selection_end_for_bindings] attribute unsigned long? selectionEnd; [FIXME] attribute DOMString? selectionDirection; [FIXME] undefined setRangeText(DOMString replacement); [FIXME] undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");