Skip to content

Commit

Permalink
LibWeb: Use default paragraph separator in delete command
Browse files Browse the repository at this point in the history
  • Loading branch information
gmta committed Nov 30, 2024
1 parent 52517b9 commit 43edfdc
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 3 deletions.
20 changes: 17 additions & 3 deletions Libraries/LibWeb/Editing/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ bool command_delete_action(DOM::Document& document, String const&)
&& Array { HTML::TagNames::li, HTML::TagNames::dt, HTML::TagNames::dd }.contains_slow(node_element.local_name())) {
// 1. Let items be a list of all lis that are ancestors of node.
auto items = Vector<DOM::Element*>();
auto* ancestor = node->parent();
DOM::Node* ancestor = node->parent();
do {
auto* ancestor_element = static_cast<DOM::Element*>(ancestor);
if (ancestor_element->local_name() == HTML::TagNames::li)
Expand All @@ -164,11 +164,25 @@ bool command_delete_action(DOM::Document& document, String const&)
// 5. Restore the values from values.
restore_the_values_of_nodes(values);

// FIXME: 6. If node is a dd or dt, and it is not an allowed child of any of its ancestors in the
// 6. If node is a dd or dt, and it is not an allowed child of any of its ancestors in the
// same editing host, set the tag name of node to the default single-line container name
// and let node be the result.
if (Array { HTML::TagNames::dd, HTML::TagNames::dt }.contains_slow(node_element.local_name())) {
ancestor = node->parent();
bool allowed_child_of_any_ancestor = false;
do {
if (is_in_same_editing_host(*node, *ancestor) && is_allowed_child_of_node(node, ancestor)) {
allowed_child_of_any_ancestor = true;
break;
}
ancestor = ancestor->parent();
} while (ancestor);
if (!allowed_child_of_any_ancestor)
node = &set_the_tag_name(node_element, document.default_single_line_container_name());
}

// FIXME: 7. Fix disallowed ancestors of node.
// 7. Fix disallowed ancestors of node.
fix_disallowed_ancestors_of_node(node);

// 8. Return true.
return true;
Expand Down
76 changes: 76 additions & 0 deletions Libraries/LibWeb/Editing/Internal/Algorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,75 @@ DOM::Node const* editing_host_of_node(DOM::Node const& node)
return nullptr;
}

// https://w3c.github.io/editing/docs/execCommand/#fix-disallowed-ancestors
void fix_disallowed_ancestors_of_node(DOM::Node* node)
{
// 1. If node is not editable, abort these steps.
if (!node->is_editable())
return;

// 2. If node is not an allowed child of any of its ancestors in the same editing host:
bool allowed_child_of_any_ancestor = false;
auto* ancestor = node->parent();
do {
if (is_in_same_editing_host(*ancestor, *node) && is_allowed_child_of_node(node, ancestor)) {
allowed_child_of_any_ancestor = true;
break;
}
ancestor = ancestor->parent();
} while (ancestor);
if (!allowed_child_of_any_ancestor) {
// FIXME: 1. If node is a dd or dt, wrap the one-node list consisting of node, with sibling criteria returning true for
// any dl with no attributes and false otherwise, and new parent instructions returning the result of calling
// createElement("dl") on the context object. Then abort these steps.

// 2. If "p" is not an allowed child of the editing host of node, abort these steps.
if (!is_allowed_child_of_node(HTML::TagNames::p, editing_host_of_node(*node)))
return;

// 3. If node is not a prohibited paragraph child, abort these steps.
if (!is_prohibited_paragraph_child(*node))
return;

// 4. Set the tag name of node to the default single-line container name, and let node be the result.
node = &set_the_tag_name(*static_cast<DOM::Element*>(node), node->document().default_single_line_container_name());

// 5. Fix disallowed ancestors of node.
fix_disallowed_ancestors_of_node(node);

// 6. Let children be node's children.
// 7. For each child in children, if child is a prohibited paragraph child:
node->for_each_child([](DOM::Node& child) {
if (!is_prohibited_paragraph_child(child))
return IterationDecision::Continue;

// 1. Record the values of the one-node list consisting of child, and let values be the result.
auto values = record_the_values_of_nodes({ &child });

// 2. Split the parent of the one-node list consisting of child.
split_the_parent_of_nodes({ &child });

// 3. Restore the values from values.
restore_the_values_of_nodes(values);

return IterationDecision::Continue;
});

// 8. Abort these steps.
return;
}

// 3. Record the values of the one-node list consisting of node, and let values be the result.
auto values = record_the_values_of_nodes({ node });

// 4. While node is not an allowed child of its parent, split the parent of the one-node list consisting of node.
while (!is_allowed_child_of_node(node, node->parent()))
split_the_parent_of_nodes({ node });

// 5. Restore the values from values.
restore_the_values_of_nodes(values);
}

// https://w3c.github.io/editing/docs/execCommand/#follows-a-line-break
bool follows_a_line_break(DOM::Node const* node)
{
Expand Down Expand Up @@ -870,6 +939,13 @@ bool is_name_of_an_element_with_inline_contents(FlyString const& local_name)
return element_with_inline_contents_names.contains_slow(local_name);
}

// https://w3c.github.io/editing/docs/execCommand/#prohibited-paragraph-child
bool is_prohibited_paragraph_child(DOM::Node const& node)
{
// A prohibited paragraph child is an HTML element whose local name is a prohibited paragraph child name.
return node.is_html_element() && is_prohibited_paragraph_child_name(static_cast<DOM::Element const&>(node).local_name());
}

// https://w3c.github.io/editing/docs/execCommand/#prohibited-paragraph-child-name
bool is_prohibited_paragraph_child_name(FlyString const& local_name)
{
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Editing/Internal/Algorithms.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ String canonical_space_sequence(u32 length, bool non_breaking_start, bool non_br
void canonicalize_whitespace(DOM::Node&, u32 offset, bool fix_collapsed_space = true);
void delete_the_selection(Selection::Selection const&);
DOM::Node const* editing_host_of_node(DOM::Node const&);
void fix_disallowed_ancestors_of_node(DOM::Node*);
bool follows_a_line_break(DOM::Node const*);
bool is_allowed_child_of_node(Variant<DOM::Node const*, FlyString> child, Variant<DOM::Node const*, FlyString> parent);
bool is_block_boundary_point(DOM::Node const&, u32 offset);
Expand All @@ -40,6 +41,7 @@ bool is_in_same_editing_host(DOM::Node const&, DOM::Node const&);
bool is_inline_node(DOM::Node const&);
bool is_invisible_node(DOM::Node const&);
bool is_name_of_an_element_with_inline_contents(FlyString const&);
bool is_prohibited_paragraph_child(DOM::Node const&);
bool is_prohibited_paragraph_child_name(FlyString const&);
bool is_visible_node(DOM::Node const&);
bool is_whitespace_node(DOM::Node const&);
Expand Down

0 comments on commit 43edfdc

Please sign in to comment.