diff --git a/Libraries/LibWeb/Editing/Commands.cpp b/Libraries/LibWeb/Editing/Commands.cpp index 6c82757209c..2cdb6d32ded 100644 --- a/Libraries/LibWeb/Editing/Commands.cpp +++ b/Libraries/LibWeb/Editing/Commands.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace Web::Editing { @@ -374,6 +375,81 @@ bool command_delete_action(DOM::Document& document, String const&) return true; } +// https://w3c.github.io/editing/docs/execCommand/#the-insertlinebreak-command +bool command_insert_linebreak_action(DOM::Document& document, String const&) +{ + // 1. Delete the selection, with strip wrappers false. + auto& selection = *document.get_selection(); + delete_the_selection(selection, true, false); + + // 2. If the active range's start node is neither editable nor an editing host, return true. + auto& active_range = *selection.range(); + auto start_node = active_range.start_container(); + if (!start_node->is_editable_or_editing_host()) + return true; + + // 3. If the active range's start node is an Element, and "br" is not an allowed child of it, return true. + if (is(*start_node) && !is_allowed_child_of_node(HTML::TagNames::br, start_node)) + return true; + + // 4. If the active range's start node is not an Element, and "br" is not an allowed child of the active range's + // start node's parent, return true. + if (!is(*start_node) && start_node->parent() && !is_allowed_child_of_node(HTML::TagNames::br, GC::Ref { *start_node->parent() })) + return true; + + // 5. If the active range's start node is a Text node and its start offset is zero, call collapse() on the context + // object's selection, with first argument equal to the active range's start node's parent and second argument + // equal to the active range's start node's index. + if (is(*start_node) && active_range.start_offset() == 0) + MUST(selection.collapse(start_node->parent(), start_node->index())); + + // 6. If the active range's start node is a Text node and its start offset is the length of its start node, call + // collapse() on the context object's selection, with first argument equal to the active range's start node's + // parent and second argument equal to one plus the active range's start node's index. + if (is(*start_node) && active_range.start_offset() == start_node->length()) + MUST(selection.collapse(start_node->parent(), start_node->index() + 1)); + + // AD-HOC: If the active range's start node is a Text node and its resolved value for "white-space" is one of "pre", + // "pre-line" or "pre-wrap": + // * Insert a newline (\n) character at the active range's start offset; + // * Collapse the selection with active range's start node as the first argument and one plus active range's + // start offset as the second argument + // * Insert another newline (\n) character if the active range's start offset is equal to the length of the + // active range's start node. + // * Return true. + if (is(*start_node) && start_node->layout_node()) { + auto& text_node = static_cast(*start_node); + auto white_space = text_node.layout_node()->computed_values().white_space(); + if (first_is_one_of(white_space, CSS::WhiteSpace::Pre, CSS::WhiteSpace::PreLine, CSS::WhiteSpace::PreWrap)) { + MUST(text_node.insert_data(active_range.start_offset(), "\n"_string)); + MUST(selection.collapse(start_node, active_range.start_offset() + 1)); + if (selection.range()->start_offset() == start_node->length()) + MUST(text_node.insert_data(active_range.start_offset(), "\n"_string)); + return true; + } + } + + // 7. Let br be the result of calling createElement("br") on the context object. + auto br = MUST(DOM::create_element(document, HTML::TagNames::br, Namespace::HTML)); + + // 8. Call insertNode(br) on the active range. + MUST(active_range.insert_node(br)); + + // 9. Call collapse() on the context object's selection, with br's parent as the first argument and one plus br's + // index as the second argument. + MUST(selection.collapse(br->parent(), br->index() + 1)); + + // 10. If br is a collapsed line break, call createElement("br") on the context object and let extra br be the + // result, then call insertNode(extra br) on the active range. + if (is_collapsed_line_break(br)) { + auto extra_br = MUST(DOM::create_element(document, HTML::TagNames::br, Namespace::HTML)); + MUST(active_range.insert_node(extra_br)); + } + + // 11. Return true. + return true; +} + // https://w3c.github.io/editing/docs/execCommand/#the-insertparagraph-command bool command_insert_paragraph_action(DOM::Document& document, String const&) { @@ -705,6 +781,7 @@ bool command_style_with_css_state(DOM::Document const& document) static Array const commands { CommandDefinition { CommandNames::delete_, command_delete_action, {}, {}, {} }, CommandDefinition { CommandNames::defaultParagraphSeparator, command_default_paragraph_separator_action, {}, {}, command_default_paragraph_separator_value }, + CommandDefinition { CommandNames::insertLineBreak, command_insert_linebreak_action, {}, {}, {} }, CommandDefinition { CommandNames::insertParagraph, command_insert_paragraph_action, {}, {}, {} }, CommandDefinition { CommandNames::styleWithCSS, command_style_with_css_action, {}, command_style_with_css_state, {} }, }; diff --git a/Libraries/LibWeb/Editing/Commands.h b/Libraries/LibWeb/Editing/Commands.h index 6eb3f8385d3..9dbe90d5ee6 100644 --- a/Libraries/LibWeb/Editing/Commands.h +++ b/Libraries/LibWeb/Editing/Commands.h @@ -24,6 +24,7 @@ Optional find_command_definition(FlyString const&); bool command_default_paragraph_separator_action(DOM::Document&, String const&); String command_default_paragraph_separator_value(DOM::Document const&); bool command_delete_action(DOM::Document&, String const&); +bool command_insert_linebreak_action(DOM::Document&, String const&); bool command_insert_paragraph_action(DOM::Document&, String const&); bool command_style_with_css_action(DOM::Document&, String const&); bool command_style_with_css_state(DOM::Document const&); diff --git a/Tests/LibWeb/Text/expected/Editing/execCommand-insertLinebreak.txt b/Tests/LibWeb/Text/expected/Editing/execCommand-insertLinebreak.txt new file mode 100644 index 00000000000..1ec240902d4 --- /dev/null +++ b/Tests/LibWeb/Text/expected/Editing/execCommand-insertLinebreak.txt @@ -0,0 +1,9 @@ +Before: "foobar" +After: "foo
bar" +Before: "

foobar

" +After: "

foo +bar

" +Before: "

foobar

" +After: "

foobar + +

" diff --git a/Tests/LibWeb/Text/input/Editing/execCommand-insertLinebreak.html b/Tests/LibWeb/Text/input/Editing/execCommand-insertLinebreak.html new file mode 100644 index 00000000000..81bc658b50c --- /dev/null +++ b/Tests/LibWeb/Text/input/Editing/execCommand-insertLinebreak.html @@ -0,0 +1,40 @@ + +
foobar
+

foobar

+

foobar

+