Bug 1838250 - Avoid O(n^2) behavior in nsTextFrame::GetCharacterRectsInRange. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D183382
This commit is contained in:
Jonathan Kew 2023-07-13 09:47:59 +00:00
Родитель 52c561897c
Коммит 90a0aae39c
1 изменённых файлов: 39 добавлений и 19 удалений

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

@ -7450,27 +7450,31 @@ nsresult nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
// place if it's positioned there
properties.InitializeForDisplay(false);
// Initialize iter; this will call FindClusterStart if necessary to align
// iter to a cluster boundary.
UpdateIteratorFromOffset(properties, aInOffset, iter);
nsPoint point = GetPointFromIterator(iter, properties);
const int32_t kContentEnd = GetContentEnd();
const int32_t kEndOffset = std::min(aInOffset + aLength, kContentEnd);
while (aInOffset < kEndOffset) {
if (!iter.IsOriginalCharSkipped() &&
!mTextRun->IsClusterStart(iter.GetSkippedOffset())) {
FindClusterStart(mTextRun,
properties.GetStart().GetOriginalOffset() +
properties.GetOriginalLength(),
&iter);
}
nsPoint point = GetPointFromIterator(iter, properties);
nsRect rect;
rect.x = point.x;
rect.y = point.y;
if (aInOffset >= kEndOffset) {
return NS_OK;
}
if (!aRects.SetCapacity(aRects.Length() + kEndOffset - aInOffset,
mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
do {
MOZ_ASSERT(point == GetPointFromIterator(iter, properties),
"character position error!");
// Measure to the end of the cluster.
nscoord iSize = 0;
gfxSkipCharsIterator nextIter(iter);
if (aInOffset < kContentEnd) {
gfxSkipCharsIterator nextIter(iter);
nextIter.AdvanceOriginal(1);
if (!nextIter.IsOriginalCharSkipped() &&
!mTextRun->IsClusterStart(nextIter.GetSkippedOffset()) &&
@ -7484,6 +7488,12 @@ nsresult nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
iSize = NSToCoordCeilClamped(advance);
}
// Compute the cluster rect, depending on directionality, and update
// point to the origin we'll need for the next cluster.
nsRect rect;
rect.x = point.x;
rect.y = point.y;
if (mTextRun->IsVertical()) {
rect.width = mRect.width;
rect.height = iSize;
@ -7492,6 +7502,9 @@ nsresult nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
// bottom left instead of the top left. Move the origin to the top left
// by subtracting the character's height.
rect.y -= rect.height;
point.y -= iSize;
} else {
point.y += iSize;
}
} else {
rect.width = iSize;
@ -7505,15 +7518,22 @@ nsresult nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
// subtracting the character's width. This is intentionally done after
// GetTextCombineScaleFactor() so we use the final, scaled width.
rect.x -= rect.width;
point.x -= iSize;
} else {
point.x += iSize;
}
}
aRects.AppendElement(rect);
aInOffset++;
// Don't advance iter if we've reached the end
if (aInOffset < kEndOffset) {
iter.AdvanceOriginal(1);
// Set the rect for all characters in the cluster.
int32_t end = std::min(kEndOffset, nextIter.GetOriginalOffset());
while (aInOffset < end) {
aRects.AppendElement(rect);
aInOffset++;
}
}
// Advance iter for the next cluster.
iter = nextIter;
} while (aInOffset < kEndOffset);
return NS_OK;
}