Bug 1615142 - Fix triple-click selection on pre-formatted text. r=mats

We need to check for terminal new-lines on ourselves before looking at our next
sibling frame.

Differential Revision: https://phabricator.services.mozilla.com/D62766

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Emilio Cobos Álvarez 2020-02-20 11:57:50 +00:00
Родитель 4108cae997
Коммит 712d386afe
2 изменённых файлов: 72 добавлений и 33 удалений

Просмотреть файл

@ -183,8 +183,8 @@ struct nsBoxLayoutMetrics {
};
struct nsContentAndOffset {
nsIContent* mContent;
int32_t mOffset;
nsIContent* mContent = nullptr;
int32_t mOffset = 0;
};
// Some Misc #defines
@ -8461,21 +8461,39 @@ nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart) {
return result;
}
// If this is a preformatted text frame, see if it ends with a newline
static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
nsDirection aDirection) {
nsContentAndOffset result;
if (aFrame->IsGeneratedContentFrame() ||
!aFrame->HasSignificantTerminalNewline()) {
return result;
}
int32_t startOffset, endOffset;
aFrame->GetOffsets(startOffset, endOffset);
result.mContent = aFrame->GetContent();
result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
return result;
}
// Find the first (or last) descendant of the given frame
// which is either a block-level frame or a BRFrame, or some other kind of break
// which stops the line.
static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
nsDirection aDirection) {
nsContentAndOffset result;
result.mContent = nullptr;
result.mOffset = 0;
if (aFrame->IsGeneratedContentFrame()) return result;
if (aFrame->IsGeneratedContentFrame()) {
return result;
}
// Treat form controls as inline leaves
// XXX we really need a way to determine whether a frame is inline-level
nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
if (fcf) return result;
if (nsIFormControlFrame* fcf = do_QueryFrame(aFrame)) {
return result;
}
// Check the frame itself
// Fall through block-in-inline split frames because their mContent is
@ -8497,12 +8515,8 @@ static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
return result;
}
// If this is a preformatted text frame, see if it ends with a newline
if (aFrame->HasSignificantTerminalNewline()) {
int32_t startOffset, endOffset;
aFrame->GetOffsets(startOffset, endOffset);
result.mContent = aFrame->GetContent();
result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
result = FindLineBreakInText(aFrame, aDirection);
if (result.mContent) {
return result;
}
@ -8570,6 +8584,10 @@ nsresult nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct* aPos) {
reachedBlockAncestor = true;
break;
}
// Try to find our own line-break before looking at our siblings.
blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
nsIFrame* sibling = frame->GetNextSibling();
while (sibling && !blockFrameOrBR.mContent) {
blockFrameOrBR = FindLineBreakingFrame(sibling, eDirNext);

Просмотреть файл

@ -10,31 +10,52 @@
<p id="b">Here's <span>some</span> <code><span>code</span><br><span>broken</span></code> with <span>text</span> with <code><span>code</span><br><span>broken</span></code> in <span>it and such</span> and so on</p>
<pre id="c">Here's some text and
some <span>more</span> code <span>with</span> pre-formatted
whitespace <span>that</span> should be selected
line <span>by</span> line</pre>
<script>
SimpleTest.waitForExplicitFinish();
function testTripleClick(elem, expectation) {
window.getSelection().removeAllRanges();
synthesizeMouse(elem, 5, 5, { clickCount: 3 });
is(window.getSelection().toString(), expectation);
}
SimpleTest.waitForFocus(function () {
for (let child of document.querySelectorAll("#a span, #a code")) {
synthesizeMouse(child, 5, 5, { clickCount: 3 });
is(window.getSelection().toString(), "Some code with text with code in it");
window.getSelection().removeAllRanges();
for (let child of document.querySelectorAll("#a span, #a code"))
testTripleClick(child, "Some code with text with code in it");
{
let spans = document.querySelectorAll("#b span");
let expectations = [
"Here's some code", // First span, at the beginning of the text.
"Here's some code", // Top span in the inline-block, before break.
"broken with text with code", // Bottom span in inline-block, after break
"broken with text with code", // Center of the text.
"broken with text with code", // Top span in the second inline-block, before break.
"broken in it and such and so on", // Bottom span in the second inline-block, after break.
"broken in it and such and so on", // Last span, at the end of the text.
];
is(spans.length, expectations.length);
for (let i = 0; i < expectations.length; ++i)
testTripleClick(spans[i], expectations[i]);
}
let spans = document.querySelectorAll("#b span");
let expectations = [
"Here's some code", // First span, at the beginning of the text.
"Here's some code", // Top span in the inline-block, before break.
"broken with text with code", // Bottom span in inline-block, after break
"broken with text with code", // Center of the text.
"broken with text with code", // Top span in the second inline-block, before break.
"broken in it and such and so on", // Bottom span in the second inline-block, after break.
"broken in it and such and so on", // Last span, at the end of the text.
];
is(spans.length, expectations.length);
for (let i = 0; i < expectations.length; ++i) {
synthesizeMouse(spans[i], 5, 5, { clickCount: 3 });
is(window.getSelection().toString(), expectations[i]);
window.getSelection().removeAllRanges();
{
testTripleClick(document.getElementById("c"), "Here's some text and");
let spans = document.querySelectorAll("#c span");
let expectations = [
"some more code with pre-formatted",
"some more code with pre-formatted",
"whitespace that should be selected",
"line by line",
];
is(spans.length, expectations.length);
for (let i = 0; i < expectations.length; ++i)
testTripleClick(spans[i], expectations[i]);
}
SimpleTest.finish();