LibWeb: Do not rely on the layout tree for collapsed line breaks

The editing command that relies the most on this, `insertLinebreak`,
did not perform a layout update after inserting a `<br>` which caused
this algorithm to always return false. But instead of actually building
the layout tree needlessly, we can check the DOM tree instead.
This commit is contained in:
Jelle Raaijmakers 2025-05-01 12:27:42 +02:00 committed by Alexander Kalenik
commit 295b78f7d3
Notes: github-actions[bot] 2025-05-01 12:45:22 +00:00
3 changed files with 17 additions and 9 deletions

View file

@ -1981,15 +1981,10 @@ bool is_collapsed_line_break(GC::Ref<DOM::Node> node)
return false; return false;
// that begins a line box which has nothing else in it, and therefore has zero height. // that begins a line box which has nothing else in it, and therefore has zero height.
auto layout_node = node->layout_node(); // NOTE: We check this on the DOM-level by seeing if the next node is neither a non-empty text node nor a <br>.
if (!layout_node) if (auto text_node = as_if<DOM::Text>(node->next_sibling()))
return false; return text_node->text_content().value_or({}).is_empty();
VERIFY(is<Layout::BreakNode>(*layout_node)); return !is<HTML::HTMLBRElement>(node->next_sibling());
// NOTE: We do not generate a TextNode for empty text after the break, so if we do not have a sibling or if that
// sibling is not a TextNode, we consider it a collapsed line break.
auto* next_layout_node = layout_node->next_sibling();
return !is<Layout::TextNode>(next_layout_node);
} }
// https://w3c.github.io/editing/docs/execCommand/#collapsed-whitespace-node // https://w3c.github.io/editing/docs/execCommand/#collapsed-whitespace-node

View file

@ -7,3 +7,5 @@ Before: "<p style="white-space: pre">foobar</p>"
After: "<p style="white-space: pre">foobar After: "<p style="white-space: pre">foobar
</p>" </p>"
Before: "foo"
After: "foo<br><br>"

View file

@ -3,11 +3,13 @@
<div id="a" contenteditable>foobar</div> <div id="a" contenteditable>foobar</div>
<div id="b" contenteditable><p style="white-space: pre">foobar</p></div> <div id="b" contenteditable><p style="white-space: pre">foobar</p></div>
<div id="c" contenteditable><p style="white-space: pre">foobar</p></div> <div id="c" contenteditable><p style="white-space: pre">foobar</p></div>
<div id="d" contenteditable>foo</div>
<script> <script>
test(() => { test(() => {
let divA = document.querySelector('div#a'); let divA = document.querySelector('div#a');
let divB = document.querySelector('div#b'); let divB = document.querySelector('div#b');
let divC = document.querySelector('div#c'); let divC = document.querySelector('div#c');
let divD = document.querySelector('div#d');
document.body.offsetWidth // Force a layout document.body.offsetWidth // Force a layout
@ -37,5 +39,14 @@
document.execCommand('insertLinebreak'); document.execCommand('insertLinebreak');
println(`After: "${divC.innerHTML}"`); println(`After: "${divC.innerHTML}"`);
getSelection().empty(); getSelection().empty();
// D: Insert linebreak after 'foo'
println(`Before: "${divD.innerHTML}"`);
var range = document.createRange();
range.setStart(divD.firstChild, 3);
getSelection().addRange(range);
document.execCommand('insertLinebreak');
println(`After: "${divD.innerHTML}"`);
getSelection().empty();
}); });
</script> </script>