mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-29 13:46:31 +00:00
LibWeb+WebContent: Port FormAssociatedTextControlElement APIs to UTF-16
This commit is contained in:
parent
cdf270a5e6
commit
017a6cc687
Notes:
github-actions[bot]
2025-07-25 22:42:01 +00:00
Author: https://github.com/trflynn89
Commit: 017a6cc687
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5608
Reviewed-by: https://github.com/gmta ✅
12 changed files with 58 additions and 64 deletions
|
@ -34,7 +34,7 @@ void EditingHostManager::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_active_contenteditable_element);
|
||||
}
|
||||
|
||||
void EditingHostManager::handle_insert(String const& value)
|
||||
void EditingHostManager::handle_insert(Utf16String const& value)
|
||||
{
|
||||
// https://w3c.github.io/editing/docs/execCommand/#additional-requirements
|
||||
// When the user instructs the user agent to insert text inside an editing host, such as by typing on the keyboard
|
||||
|
@ -43,7 +43,7 @@ void EditingHostManager::handle_insert(String const& value)
|
|||
// once or in quick succession, this specification does not define whether it is treated as one insertion or several
|
||||
// consecutive insertions.
|
||||
|
||||
auto editing_result = m_document->exec_command(Editing::CommandNames::insertText, false, value);
|
||||
auto editing_result = m_document->exec_command(Editing::CommandNames::insertText, false, value.to_utf8_but_should_be_ported_to_utf16());
|
||||
if (editing_result.is_exception())
|
||||
dbgln("handle_insert(): editing resulted in exception: {}", editing_result.exception());
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class EditingHostManager
|
|||
public:
|
||||
[[nodiscard]] static GC::Ref<EditingHostManager> create(JS::Realm&, GC::Ref<Document>);
|
||||
|
||||
virtual void handle_insert(String const&) override;
|
||||
virtual void handle_insert(Utf16String const&) override;
|
||||
virtual void handle_delete(DeleteDirection) override;
|
||||
virtual EventResult handle_return_key(FlyString const& ui_input_type) override;
|
||||
virtual void select_all() override;
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
|
||||
virtual GC::Ref<JS::Cell> as_cell() = 0;
|
||||
|
||||
virtual void handle_insert(String const&) = 0;
|
||||
virtual void handle_insert(Utf16String const&) = 0;
|
||||
virtual EventResult handle_return_key(FlyString const& ui_input_type) = 0;
|
||||
|
||||
enum class DeleteDirection {
|
||||
|
|
|
@ -397,7 +397,7 @@ bool FormAssociatedElement::suffering_from_a_custom_error() const
|
|||
void FormAssociatedTextControlElement::relevant_value_was_changed()
|
||||
{
|
||||
auto the_relevant_value = relevant_value();
|
||||
auto relevant_value_length = the_relevant_value.code_points().length();
|
||||
auto relevant_value_length = the_relevant_value.length_in_code_units();
|
||||
|
||||
// 1. If the element has a selection:
|
||||
if (m_selection_start < m_selection_end) {
|
||||
|
@ -592,13 +592,13 @@ 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_binding(String const& replacement)
|
||||
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text_binding(Utf16String const& replacement)
|
||||
{
|
||||
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)
|
||||
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text_binding(Utf16String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode selection_mode)
|
||||
{
|
||||
auto& html_element = form_associated_element_to_html_element();
|
||||
|
||||
|
@ -611,7 +611,7 @@ WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text_bindi
|
|||
}
|
||||
|
||||
// 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)
|
||||
WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text(Utf16String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode selection_mode)
|
||||
{
|
||||
auto& html_element = form_associated_element_to_html_element();
|
||||
|
||||
|
@ -628,7 +628,7 @@ WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text(Strin
|
|||
|
||||
// 5. If start is greater than the length of the relevant value of the text control, then set it to the length of the relevant value of the text control.
|
||||
auto the_relevant_value = relevant_value();
|
||||
auto relevant_value_length = the_relevant_value.code_points().length();
|
||||
auto relevant_value_length = the_relevant_value.length_in_code_units();
|
||||
if (start > relevant_value_length)
|
||||
start = relevant_value_length;
|
||||
|
||||
|
@ -645,26 +645,24 @@ WebIDL::ExceptionOr<void> FormAssociatedTextControlElement::set_range_text(Strin
|
|||
// 9. If start is less than end, delete the sequence of code units within the element's relevant value starting with
|
||||
// the code unit at the startth position and ending with the code unit at the (end-1)th position.
|
||||
if (start < end) {
|
||||
StringBuilder builder;
|
||||
auto before_removal_point_view = the_relevant_value.code_points().unicode_substring_view(0, start);
|
||||
builder.append(before_removal_point_view.as_string());
|
||||
auto after_removal_point_view = the_relevant_value.code_points().unicode_substring_view(end);
|
||||
builder.append(after_removal_point_view.as_string());
|
||||
the_relevant_value = MUST(builder.to_string());
|
||||
StringBuilder builder(StringBuilder::Mode::UTF16, the_relevant_value.length_in_code_units() - (end - start));
|
||||
builder.append(the_relevant_value.substring_view(0, start));
|
||||
builder.append(the_relevant_value.substring_view(end));
|
||||
|
||||
the_relevant_value = builder.to_utf16_string();
|
||||
}
|
||||
|
||||
// 10. Insert the value of the first argument into the text of the relevant value of the text control, immediately before the startth code unit.
|
||||
StringBuilder builder;
|
||||
auto before_insertion_point_view = the_relevant_value.code_points().unicode_substring_view(0, start);
|
||||
builder.append(before_insertion_point_view.as_string());
|
||||
StringBuilder builder(StringBuilder::Mode::UTF16, the_relevant_value.length_in_code_units() + replacement.length_in_code_units());
|
||||
builder.append(the_relevant_value.substring_view(0, start));
|
||||
builder.append(replacement);
|
||||
auto after_insertion_point_view = the_relevant_value.code_points().unicode_substring_view(start);
|
||||
builder.append(after_insertion_point_view.as_string());
|
||||
the_relevant_value = MUST(builder.to_string());
|
||||
builder.append(the_relevant_value.substring_view(start));
|
||||
|
||||
the_relevant_value = builder.to_utf16_string();
|
||||
TRY(set_relevant_value(the_relevant_value));
|
||||
|
||||
// 11. Let new length be the length of the value of the first argument.
|
||||
i64 new_length = replacement.code_points().length();
|
||||
auto new_length = replacement.length_in_code_units();
|
||||
|
||||
// 12. Let new end be the sum of start and new length.
|
||||
auto new_end = start + new_length;
|
||||
|
@ -755,7 +753,8 @@ void FormAssociatedTextControlElement::set_the_selection_range(Optional<WebIDL::
|
|||
// relevant value of the text control (including the special value infinity) must be treated
|
||||
// as pointing at the end of the text control.
|
||||
auto the_relevant_value = relevant_value();
|
||||
auto relevant_value_length = the_relevant_value.code_points().length();
|
||||
auto relevant_value_length = the_relevant_value.length_in_code_units();
|
||||
|
||||
auto new_selection_start = AK::min(start.value(), relevant_value_length);
|
||||
auto new_selection_end = AK::min(end.value(), relevant_value_length);
|
||||
|
||||
|
@ -797,20 +796,20 @@ void FormAssociatedTextControlElement::set_the_selection_range(Optional<WebIDL::
|
|||
}
|
||||
}
|
||||
|
||||
void FormAssociatedTextControlElement::handle_insert(String const& data)
|
||||
void FormAssociatedTextControlElement::handle_insert(Utf16String const& data)
|
||||
{
|
||||
auto text_node = form_associated_element_to_text_node();
|
||||
if (!text_node || !is_mutable())
|
||||
return;
|
||||
|
||||
String data_for_insertion = data;
|
||||
// FIXME: Cut by UTF-16 code units instead of raw bytes
|
||||
auto data_for_insertion = data;
|
||||
|
||||
if (auto max_length = text_node->max_length(); max_length.has_value()) {
|
||||
auto remaining_length = *max_length - text_node->data().length_in_code_points();
|
||||
if (remaining_length < data.code_points().length()) {
|
||||
data_for_insertion = MUST(data.substring_from_byte_offset(0, remaining_length));
|
||||
}
|
||||
auto remaining_length = *max_length - text_node->length_in_utf16_code_units();
|
||||
if (remaining_length < data.length_in_code_units())
|
||||
data_for_insertion = Utf16String::from_utf16_without_validation(data.substring_view(0, remaining_length));
|
||||
}
|
||||
|
||||
auto selection_start = this->selection_start();
|
||||
auto selection_end = this->selection_end();
|
||||
MUST(set_range_text(data_for_insertion, selection_start, selection_end, Bindings::SelectionMode::End));
|
||||
|
@ -824,21 +823,22 @@ void FormAssociatedTextControlElement::handle_delete(DeleteDirection direction)
|
|||
auto text_node = form_associated_element_to_text_node();
|
||||
if (!text_node || !is_mutable())
|
||||
return;
|
||||
|
||||
auto selection_start = this->selection_start();
|
||||
auto selection_end = this->selection_end();
|
||||
|
||||
if (selection_start == selection_end) {
|
||||
if (direction == DeleteDirection::Backward) {
|
||||
if (selection_start > 0) {
|
||||
MUST(set_range_text(String {}, selection_start - 1, selection_end, Bindings::SelectionMode::End));
|
||||
}
|
||||
if (selection_start > 0)
|
||||
MUST(set_range_text({}, selection_start - 1, selection_end, Bindings::SelectionMode::End));
|
||||
} else {
|
||||
if (selection_start < text_node->data().length_in_code_points()) {
|
||||
MUST(set_range_text(String {}, selection_start, selection_end + 1, Bindings::SelectionMode::End));
|
||||
}
|
||||
if (selection_start < text_node->length_in_utf16_code_units())
|
||||
MUST(set_range_text({}, selection_start, selection_end + 1, Bindings::SelectionMode::End));
|
||||
}
|
||||
return;
|
||||
}
|
||||
MUST(set_range_text(String {}, selection_start, selection_end, Bindings::SelectionMode::End));
|
||||
|
||||
MUST(set_range_text({}, selection_start, selection_end, Bindings::SelectionMode::End));
|
||||
}
|
||||
|
||||
EventResult FormAssociatedTextControlElement::handle_return_key(FlyString const&)
|
||||
|
|
|
@ -181,8 +181,8 @@ class FormAssociatedTextControlElement
|
|||
, public InputEventsTarget {
|
||||
public:
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
|
||||
virtual String relevant_value() = 0;
|
||||
virtual WebIDL::ExceptionOr<void> set_relevant_value(String const&) = 0;
|
||||
virtual Utf16String relevant_value() = 0;
|
||||
virtual WebIDL::ExceptionOr<void> set_relevant_value(Utf16String const&) = 0;
|
||||
|
||||
virtual void set_dirty_value_flag(bool flag) = 0;
|
||||
|
||||
|
@ -206,9 +206,9 @@ 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_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);
|
||||
WebIDL::ExceptionOr<void> set_range_text_binding(Utf16String const& replacement);
|
||||
WebIDL::ExceptionOr<void> set_range_text_binding(Utf16String const& replacement, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, Bindings::SelectionMode = Bindings::SelectionMode::Preserve);
|
||||
WebIDL::ExceptionOr<void> set_range_text(Utf16String 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
|
||||
void set_the_selection_range(Optional<WebIDL::UnsignedLong> start, Optional<WebIDL::UnsignedLong> end, SelectionDirection direction = SelectionDirection::None, SelectionSource source = SelectionSource::DOM);
|
||||
|
@ -228,7 +228,7 @@ public:
|
|||
virtual GC::Ptr<DOM::Text> form_associated_element_to_text_node() = 0;
|
||||
virtual GC::Ptr<DOM::Text const> form_associated_element_to_text_node() const { return const_cast<FormAssociatedTextControlElement&>(*this).form_associated_element_to_text_node(); }
|
||||
|
||||
virtual void handle_insert(String const&) override;
|
||||
virtual void handle_insert(Utf16String const&) override;
|
||||
virtual void handle_delete(DeleteDirection) override;
|
||||
virtual EventResult handle_return_key(FlyString const& ui_input_type) override;
|
||||
virtual void select_all() override;
|
||||
|
|
|
@ -84,8 +84,8 @@ public:
|
|||
WebIDL::ExceptionOr<void> set_value(String const&);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
|
||||
virtual String relevant_value() override { return value(); }
|
||||
WebIDL::ExceptionOr<void> set_relevant_value(String const& value) override { return set_value(value); }
|
||||
virtual Utf16String relevant_value() override { return Utf16String::from_utf8(value()); }
|
||||
WebIDL::ExceptionOr<void> set_relevant_value(Utf16String const& value) override { return set_value(value.to_utf8_but_should_be_ported_to_utf16()); }
|
||||
|
||||
virtual void set_dirty_value_flag(bool flag) override { m_dirty_value = flag; }
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ interface HTMLInputElement : HTMLElement {
|
|||
[ImplementedAs=selection_start_binding] attribute unsigned long? selectionStart;
|
||||
[ImplementedAs=selection_end_binding] attribute unsigned long? selectionEnd;
|
||||
[ImplementedAs=selection_direction_binding] attribute DOMString? selectionDirection;
|
||||
[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");
|
||||
[ImplementedAs=set_range_text_binding] undefined setRangeText(Utf16DOMString replacement);
|
||||
[ImplementedAs=set_range_text_binding] undefined setRangeText(Utf16DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
|
||||
undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
|
||||
|
||||
undefined showPicker();
|
||||
|
|
|
@ -219,9 +219,9 @@ String HTMLTextAreaElement::api_value() const
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
|
||||
WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_relevant_value(String const& value)
|
||||
WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_relevant_value(Utf16String const& value)
|
||||
{
|
||||
set_value(value);
|
||||
set_value(value.to_utf8_but_should_be_ported_to_utf16());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -87,8 +87,8 @@ public:
|
|||
String api_value() const;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
|
||||
virtual String relevant_value() override { return api_value(); }
|
||||
virtual WebIDL::ExceptionOr<void> set_relevant_value(String const& value) override;
|
||||
virtual Utf16String relevant_value() override { return Utf16String::from_utf8(api_value()); }
|
||||
virtual WebIDL::ExceptionOr<void> set_relevant_value(Utf16String const& value) override;
|
||||
|
||||
virtual void set_dirty_value_flag(bool flag) override { m_dirty_value = flag; }
|
||||
|
||||
|
|
|
@ -39,7 +39,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;
|
||||
[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");
|
||||
[ImplementedAs=set_range_text_binding] undefined setRangeText(Utf16DOMString replacement);
|
||||
[ImplementedAs=set_range_text_binding] undefined setRangeText(Utf16DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
|
||||
undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
|
||||
};
|
||||
|
|
|
@ -1288,7 +1288,7 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u
|
|||
|
||||
FIRE(input_event(UIEvents::EventNames::beforeinput, input_type, m_navigable, code_point));
|
||||
if (target->handle_return_key(input_type) != EventResult::Handled)
|
||||
target->handle_insert(String::from_code_point(code_point));
|
||||
target->handle_insert(Utf16String::from_code_point(code_point));
|
||||
|
||||
return EventResult::Handled;
|
||||
}
|
||||
|
@ -1296,7 +1296,7 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u
|
|||
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
|
||||
if (!should_ignore_keydown_event(code_point, modifiers)) {
|
||||
FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::insertText, m_navigable, code_point));
|
||||
target->handle_insert(String::from_code_point(code_point));
|
||||
target->handle_insert(Utf16String::from_code_point(code_point));
|
||||
return EventResult::Handled;
|
||||
}
|
||||
} else if (auto selection = document->get_selection(); selection && !selection->is_collapsed()) {
|
||||
|
@ -1386,7 +1386,7 @@ EventResult EventHandler::handle_paste(String const& text)
|
|||
return EventResult::Dropped;
|
||||
|
||||
FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::insertFromPaste, m_navigable, text));
|
||||
target->handle_insert(text);
|
||||
target->handle_insert(Utf16String::from_utf8(text));
|
||||
return EventResult::Handled;
|
||||
}
|
||||
|
||||
|
|
|
@ -1978,14 +1978,8 @@ Web::WebDriver::Response WebDriverConnection::element_send_keys_impl(StringView
|
|||
if (target.has_value()) {
|
||||
// 1. If element does not currently have focus, let current text length be the length of element's API value.
|
||||
Optional<Web::WebIDL::UnsignedLong> current_text_length;
|
||||
|
||||
if (!element->is_focused()) {
|
||||
auto api_value = target->relevant_value();
|
||||
|
||||
// FIXME: This should be a UTF-16 code unit length, but `set_the_selection_range` is also currently
|
||||
// implemented in terms of code point length.
|
||||
current_text_length = api_value.code_points().length();
|
||||
}
|
||||
if (!element->is_focused())
|
||||
current_text_length = target->relevant_value().length_in_code_units();
|
||||
|
||||
// 2. Set the text insertion caret using set selection range using current text length for both the start
|
||||
// and end parameters.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue