mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWeb: Extend logic for extraneous line breaks in block elements
While editing, we need to consider whether removing a <br> has any effect on layout to determine whether its extraneous. This new condition finds most cases for extraneous <br>s inside block elements.
This commit is contained in:
parent
f77f169824
commit
b9da7baac4
|
|
@ -1944,7 +1944,7 @@ bool is_collapsed_line_break(GC::Ref<DOM::Node> node)
|
|||
return false;
|
||||
|
||||
// that begins a line box which has nothing else in it, and therefore has zero height.
|
||||
// NOTE: We check this on the DOM-level by seeing if the next node is neither a non-empty text node nor a <br>.
|
||||
// AD-HOC: We check this on the DOM level by seeing if the next node is neither a non-empty text node nor a <br>.
|
||||
if (auto text_node = as_if<DOM::Text>(node->next_sibling()))
|
||||
return text_node->text_content().value_or({}).is_empty();
|
||||
return !is<HTML::HTMLBRElement>(node->next_sibling());
|
||||
|
|
@ -2088,8 +2088,18 @@ bool is_extraneous_line_break(GC::Ref<DOM::Node> node)
|
|||
if (is<HTML::HTMLLIElement>(parent.ptr()) && parent->child_count() == 1)
|
||||
return false;
|
||||
|
||||
// FIXME: ...that has no visual effect, in that removing it from the DOM
|
||||
// would not change layout,
|
||||
// ...that has no visual effect, in that removing it from the DOM would not change layout,
|
||||
|
||||
// AD-HOC: If node's parent is a block node, and node either has no next sibling or its next sibling is a block
|
||||
// node, and its previous sibling is a visible inline node but not a <br>, node is extraneous.
|
||||
if (parent && is_block_node(*parent) && node->previous_sibling()
|
||||
&& (!node->next_sibling() || is_block_node(*node->next_sibling()))
|
||||
&& is_visible_node(*node->previous_sibling()) && is_inline_node(*node->previous_sibling())
|
||||
&& !is<HTML::HTMLBRElement>(*node->previous_sibling())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: implement more cases that would cause removing a <br> not to have any effect on the layout.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,3 +7,12 @@ After: foobar
|
|||
--- c ---
|
||||
Before: foo<div contenteditable="">bar</div>
|
||||
After: foobar
|
||||
--- d ---
|
||||
Before: foo<br><br><div>bar<br>baz</div>
|
||||
After: foo<br><div>bar<br>baz</div>
|
||||
--- e ---
|
||||
Before: <p>foo</p><br><p>bar</p>
|
||||
After: <p>foo</p><p>bar</p>
|
||||
--- f ---
|
||||
Before: <p><span>abc</span><br></p>
|
||||
After: <p><br></p>
|
||||
|
|
|
|||
|
|
@ -22,3 +22,6 @@ After: b
|
|||
--- h ---
|
||||
Before: foo👩🏼❤️👨🏻bar
|
||||
After: foobar
|
||||
--- i ---
|
||||
Before: foo<div>bar<br>baz</div>
|
||||
After: foobar<div>baz</div>
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@
|
|||
<div id="a" contenteditable>foobar</div>
|
||||
<div id="b" contenteditable>foo👩🏼❤️👨🏻bar</div>
|
||||
<div id="c" contenteditable>foo<div contenteditable>bar</div></div>
|
||||
<div id="d" contenteditable>foo<br><br><div>bar<br>baz</div></div>
|
||||
<div id="e" contenteditable><p>foo</p><br><p>bar</p></div>
|
||||
<div id="f" contenteditable><p><span>abc</span><br></p></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const testDelete = function (divId, anchorExpression, position) {
|
||||
const testDelete = function (divId, anchorExpression, start, end = start) {
|
||||
println(`--- ${divId} ---`);
|
||||
const divElm = document.querySelector(`div#${divId}`);
|
||||
println(`Before: ${divElm.innerHTML}`);
|
||||
|
||||
// Place cursor
|
||||
const anchor = anchorExpression(divElm);
|
||||
getSelection().setBaseAndExtent(anchor, position, anchor, position);
|
||||
getSelection().setBaseAndExtent(anchor, start, anchor, end);
|
||||
|
||||
// Press backspace
|
||||
document.execCommand("delete");
|
||||
|
|
@ -23,5 +26,8 @@
|
|||
testDelete("a", (node) => node.firstChild, 3);
|
||||
testDelete("b", (node) => node.firstChild, 15);
|
||||
testDelete("c", (node) => node.childNodes[1].firstChild, 0);
|
||||
testDelete("d", (node) => node.childNodes[3].firstChild, 0);
|
||||
testDelete("e", (node) => node.childNodes[2].firstChild, 0);
|
||||
testDelete("f", (node) => node.firstChild.firstChild.firstChild, 0, 3);
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,37 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<div id="a" contenteditable="true">foobar</div>
|
||||
<div id="b" contenteditable="true">a </div>
|
||||
<div id="c" contenteditable="true">a b</div>
|
||||
<div id="d" contenteditable="true">a b</div>
|
||||
<div id="e" contenteditable="true">a b</div>
|
||||
<div id="f" contenteditable="true">a b</div>
|
||||
<div id="g" contenteditable="true"> b</div>
|
||||
<div id="h" contenteditable="true">foo👩🏼❤️👨🏻bar</div>
|
||||
<div id="a" contenteditable>foobar</div>
|
||||
<div id="b" contenteditable>a </div>
|
||||
<div id="c" contenteditable>a b</div>
|
||||
<div id="d" contenteditable>a b</div>
|
||||
<div id="e" contenteditable>a b</div>
|
||||
<div id="f" contenteditable>a b</div>
|
||||
<div id="g" contenteditable> b</div>
|
||||
<div id="h" contenteditable>foo👩🏼❤️👨🏻bar</div>
|
||||
<div id="i" contenteditable>foo<div>bar<br>baz</div></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const testForwardDelete = function(divId, position) {
|
||||
const testForwardDelete = function(divId, anchorExpression, start, end = start) {
|
||||
println(`--- ${divId} ---`);
|
||||
const divElm = document.querySelector(`div#${divId}`);
|
||||
println(`Before: ${divElm.innerHTML}`);
|
||||
|
||||
// Place cursor
|
||||
const node = divElm.childNodes[0];
|
||||
getSelection().setBaseAndExtent(node, position, node, position);
|
||||
const anchor = anchorExpression(divElm);
|
||||
getSelection().setBaseAndExtent(anchor, start, anchor, end);
|
||||
|
||||
// Press delete
|
||||
document.execCommand('forwardDelete');
|
||||
document.execCommand("forwardDelete");
|
||||
|
||||
println(`After: ${divElm.innerHTML}`);
|
||||
};
|
||||
|
||||
testForwardDelete('a', 3);
|
||||
testForwardDelete('b', 1);
|
||||
testForwardDelete('c', 1);
|
||||
testForwardDelete('d', 1);
|
||||
testForwardDelete('e', 1);
|
||||
testForwardDelete('f', 1);
|
||||
testForwardDelete('g', 0);
|
||||
testForwardDelete('h', 3);
|
||||
testForwardDelete("a", (node) => node.firstChild, 3);
|
||||
testForwardDelete("b", (node) => node.firstChild, 1);
|
||||
testForwardDelete("c", (node) => node.firstChild, 1);
|
||||
testForwardDelete("d", (node) => node.firstChild, 1);
|
||||
testForwardDelete("e", (node) => node.firstChild, 1);
|
||||
testForwardDelete("f", (node) => node.firstChild, 1);
|
||||
testForwardDelete("g", (node) => node.firstChild, 0);
|
||||
testForwardDelete("h", (node) => node.firstChild, 3);
|
||||
testForwardDelete("i", (node) => node.firstChild, 3);
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user