mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibWeb: Implement Selection.modify
This commit is contained in:
parent
bd67a5afaa
commit
2eb44229b4
Notes:
github-actions[bot]
2025-06-30 09:45:38 +00:00
Author: https://github.com/Calme1709
Commit: 2eb44229b4
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4738
Reviewed-by: https://github.com/tcl3 ✅
5 changed files with 106 additions and 0 deletions
|
@ -399,6 +399,70 @@ WebIDL::ExceptionOr<void> Selection::select_all_children(GC::Ref<DOM::Node> node
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/selection-api/#dom-selection-modify
|
||||||
|
WebIDL::ExceptionOr<void> Selection::modify(Optional<String> alter, Optional<String> direction, Optional<String> granularity)
|
||||||
|
{
|
||||||
|
auto anchor_node = this->anchor_node();
|
||||||
|
if (!anchor_node || !is<DOM::Text>(*anchor_node))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto& text_node = static_cast<DOM::Text&>(*anchor_node);
|
||||||
|
|
||||||
|
// 1. If alter is not ASCII case-insensitive match with "extend" or "move", abort these steps.
|
||||||
|
if (!alter.has_value() || !alter.value().bytes_as_string_view().is_one_of_ignoring_ascii_case("extend"sv, "move"sv))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 2. If direction is not ASCII case-insensitive match with "forward", "backward", "left", or "right", abort these steps.
|
||||||
|
if (!direction.has_value() || !direction.value().bytes_as_string_view().is_one_of_ignoring_ascii_case("forward"sv, "backward"sv, "left"sv, "right"sv))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 3. If granularity is not ASCII case-insensitive match with "character", "word", "sentence", "line", "paragraph",
|
||||||
|
// "lineboundary", "sentenceboundary", "paragraphboundary", "documentboundary", abort these steps.
|
||||||
|
if (!granularity.has_value() || !granularity.value().bytes_as_string_view().is_one_of_ignoring_ascii_case("character"sv, "word"sv, "sentence"sv, "line"sv, "paragraph"sv, "lineboundary"sv, "sentenceboundary"sv, "paragraphboundary"sv, "documentboundary"sv))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 4. If this selection is empty, abort these steps.
|
||||||
|
if (is_empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 5. Let effectiveDirection be backwards.
|
||||||
|
auto effective_direction = Direction::Backwards;
|
||||||
|
|
||||||
|
// 6. If direction is ASCII case-insensitive match with "forward", set effectiveDirection to forwards.
|
||||||
|
if (direction.value().equals_ignoring_ascii_case("forward"sv))
|
||||||
|
effective_direction = Direction::Forwards;
|
||||||
|
|
||||||
|
// 7. If direction is ASCII case-insensitive match with "right" and inline base direction of this selection's focus is ltr, set effectiveDirection to forwards.
|
||||||
|
if (direction.value().equals_ignoring_ascii_case("right"sv) && text_node.directionality() == DOM::Element::Directionality::Ltr)
|
||||||
|
effective_direction = Direction::Forwards;
|
||||||
|
|
||||||
|
// 8. If direction is ASCII case-insensitive match with "left" and inline base direction of this selection's focus is rtl, set effectiveDirection to forwards.
|
||||||
|
if (direction.value().equals_ignoring_ascii_case("left"sv) && text_node.directionality() == DOM::Element::Directionality::Rtl)
|
||||||
|
effective_direction = Direction::Forwards;
|
||||||
|
|
||||||
|
// 9. Set this selection's direction to effectiveDirection.
|
||||||
|
// NOTE: This is handled by calls to move_offset_to_* later on
|
||||||
|
|
||||||
|
// 10. If alter is ASCII case-insensitive match with "extend", set this selection's focus to the location as if the user had requested to extend selection by granularity.
|
||||||
|
// 11. Otherwise, set this selection's focus and anchor to the location as if the user had requested to move selection by granularity.
|
||||||
|
auto collapse_selection = alter.value().equals_ignoring_ascii_case("move"sv);
|
||||||
|
|
||||||
|
// TODO: Implement the other granularity options.
|
||||||
|
if (effective_direction == Direction::Forwards) {
|
||||||
|
if (granularity.value().equals_ignoring_ascii_case("character"sv))
|
||||||
|
move_offset_to_next_character(collapse_selection);
|
||||||
|
if (granularity.value().equals_ignoring_ascii_case("word"sv))
|
||||||
|
move_offset_to_next_word(collapse_selection);
|
||||||
|
} else {
|
||||||
|
if (granularity.value().equals_ignoring_ascii_case("character"sv))
|
||||||
|
move_offset_to_previous_character(collapse_selection);
|
||||||
|
if (granularity.value().equals_ignoring_ascii_case("word"sv))
|
||||||
|
move_offset_to_previous_word(collapse_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/selection-api/#dom-selection-deletefromdocument
|
// https://w3c.github.io/selection-api/#dom-selection-deletefromdocument
|
||||||
WebIDL::ExceptionOr<void> Selection::delete_from_document()
|
WebIDL::ExceptionOr<void> Selection::delete_from_document()
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
WebIDL::ExceptionOr<void> extend(GC::Ref<DOM::Node>, unsigned offset);
|
WebIDL::ExceptionOr<void> extend(GC::Ref<DOM::Node>, unsigned offset);
|
||||||
WebIDL::ExceptionOr<void> set_base_and_extent(GC::Ref<DOM::Node> anchor_node, unsigned anchor_offset, GC::Ref<DOM::Node> focus_node, unsigned focus_offset);
|
WebIDL::ExceptionOr<void> set_base_and_extent(GC::Ref<DOM::Node> anchor_node, unsigned anchor_offset, GC::Ref<DOM::Node> focus_node, unsigned focus_offset);
|
||||||
WebIDL::ExceptionOr<void> select_all_children(GC::Ref<DOM::Node>);
|
WebIDL::ExceptionOr<void> select_all_children(GC::Ref<DOM::Node>);
|
||||||
|
WebIDL::ExceptionOr<void> modify(Optional<String> alter, Optional<String> direction, Optional<String> granularity);
|
||||||
WebIDL::ExceptionOr<void>
|
WebIDL::ExceptionOr<void>
|
||||||
delete_from_document();
|
delete_from_document();
|
||||||
bool contains_node(GC::Ref<DOM::Node>, bool allow_partial_containment) const;
|
bool contains_node(GC::Ref<DOM::Node>, bool allow_partial_containment) const;
|
||||||
|
|
|
@ -25,6 +25,7 @@ interface Selection {
|
||||||
undefined extend(Node node, optional unsigned long offset = 0);
|
undefined extend(Node node, optional unsigned long offset = 0);
|
||||||
undefined setBaseAndExtent(Node anchorNode, unsigned long anchorOffset, Node focusNode, unsigned long focusOffset);
|
undefined setBaseAndExtent(Node anchorNode, unsigned long anchorOffset, Node focusNode, unsigned long focusOffset);
|
||||||
undefined selectAllChildren(Node node);
|
undefined selectAllChildren(Node node);
|
||||||
|
undefined modify(optional DOMString alter, optional DOMString direction, optional DOMString granularity);
|
||||||
[CEReactions] undefined deleteFromDocument();
|
[CEReactions] undefined deleteFromDocument();
|
||||||
boolean containsNode(Node node, optional boolean allowPartialContainment = false);
|
boolean containsNode(Node node, optional boolean allowPartialContainment = false);
|
||||||
stringifier;
|
stringifier;
|
||||||
|
|
1
Tests/LibWeb/Text/expected/selection-modify.txt
Normal file
1
Tests/LibWeb/Text/expected/selection-modify.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
PASS
|
39
Tests/LibWeb/Text/input/selection-modify.html
Normal file
39
Tests/LibWeb/Text/input/selection-modify.html
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<!doctype html>
|
||||||
|
<script src="include.js"></script>
|
||||||
|
<p id="a">Well Hello Friends</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
var selection = window.getSelection();
|
||||||
|
selection.setBaseAndExtent(a.firstChild, 0, a.firstChild, 4);
|
||||||
|
|
||||||
|
if (selection.toString() !== "Well") {
|
||||||
|
println("FAIL: selection is not what we expected initially");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.modify("extend", "forward", "character");
|
||||||
|
if (selection.toString() !== "Well ") {
|
||||||
|
println("FAIL: selection is not what we expected after extending by character");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.modify("extend", "forward", "word");
|
||||||
|
if (selection.toString() !== "Well Hello") {
|
||||||
|
println("FAIL: selection is not what we expected after extending by word");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.modify("move", "backward", "character");
|
||||||
|
if (
|
||||||
|
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 9 ||
|
||||||
|
selection.focusNode !== a.firstChild || selection.focusOffset !== 9 ||
|
||||||
|
!selection.isCollapsed
|
||||||
|
) {
|
||||||
|
println("FAIL: selection is not what we expected after moving backward by character");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println("PASS");
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue