Bug 1974970 - Make GetNextPrevLineBlockFrame() guarantee that it won't enter into anonymous subtree r=emilio a=RyanVM

It seems that `nsIFrame::GetLastLeaf()` and `nsIFrame::GetFirstLeaf()`
were designed for helper methods of `nsIFrame::IsVisuallyAtLineEdge()`
and `nsIFrame::IsLogicallyAtLineEdge()`.  Therefore, they require the
`nsTextFrame` which is generated by `::before` or `::after` to check
whether the frame is first or last of the line.  However, only
`nsIFrame::GetLastLeaf()` is used by `GetNextPrevLineBlockFrame()` too
and I changed the behavior only for `GetNextPrevLineBlockFrame()` in
D216371.  Then, that broke the `nsIFrame::IsVisuallyAtLineEdge()` and
`nsIFrame::IsLogicallyAtLineEdge()`.  Therefore, this patch moves the
new check to the caller side, i.e., into `GetNextPrevLineBlockFrame()`.

Differential Revision: https://phabricator.services.mozilla.com/D256211
This commit is contained in:
Masayuki Nakano
2025-07-07 07:58:21 +00:00
committed by rvandermeulen@mozilla.com
parent 982a6c591c
commit e7d692b3a1
2 changed files with 60 additions and 5 deletions

View File

@@ -9253,7 +9253,10 @@ static nsresult GetNextPrevLineFromBlockFrame(PeekOffsetStruct* aPos,
// must reach the editing host boundary.
return NS_ERROR_FAILURE;
}
nsIFrame::GetLastLeaf(&lastFrame);
// Don't enter into native anonymous subtrees.
if (!lastFrame->ContentIsRootOfNativeAnonymousSubtree()) {
nsIFrame::GetLastLeaf(&lastFrame);
}
if (aPos->mDirection == eDirNext) {
nearStoppingFrame = firstFrame;
@@ -11332,10 +11335,7 @@ ComputedStyle* nsIFrame::DoGetParentComputedStyle(
}
void nsIFrame::GetLastLeaf(nsIFrame** aFrame) {
if (!aFrame || !*aFrame ||
// Don't enter into native anoymous subtree from the root like <input> or
// <textarea>.
(*aFrame)->ContentIsRootOfNativeAnonymousSubtree()) {
if (!aFrame || !*aFrame) {
return;
}
for (nsIFrame* maybeLastLeaf = (*aFrame)->PrincipalChildList().LastChild();

View File

@@ -0,0 +1,55 @@
<!doctype html>
<html>
<meta charset="utf-8">
<title>Generated content should not connect the first/last word with adjacent line's last/first word</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<style>
p {
white-space: nowrap;
}
p#middleLine::before, p#middleLine::after {
content: "GeneratedText";
}
</style>
<script>
"use strict";
addEventListener("load", () => {
const middleLine = document.getElementById("middleLine");
test(() => {
getSelection().collapse(middleLine.firstChild, "mid".length);
getSelection().modify("extend", "backward", "word");
assert_in_array(
getSelection().getRangeAt(0).startContainer,
[middleLine, middleLine.firstChild],
"The start container should be in the middle line"
);
assert_equals(
getSelection().getRangeAt(0).startOffset,
0,
"The start offset should be 0"
);
}, "extending selection from middle of first word of the middle line shouldn't extend the range to the previous line");
test(() => {
getSelection().collapse(middleLine.firstChild, "middle li".length);
getSelection().modify("extend", "forward", "word");
assert_in_array(
getSelection().getRangeAt(0).endContainer,
[middleLine, middleLine.firstChild],
"The end container should be in the middle line"
);
assert_equals(
getSelection().getRangeAt(0).endOffset,
getSelection().getRangeAt(0).endContainer.length,
"The end offset should be the length of the container"
);
}, "extending selection from middle of last word of the middle line shouldn't extend the range to the next line");
}, {once: true});
</script>
<body>
<p id="previousLine">previous line</p>
<p id="middleLine">middle line</p>
<p id="nextLine">last line</p>
</body>
</html>