Bug 1745113 Part 5 - Make grapheme cluster break iterators implement SegmentIteratorUtf16, and adapt the callers. r=necko-reviewers,jfkthame,kershaw

This is the main patch for the bug. It aims to change the grapheme cluster
break's `Next()` API by implementing SegmentIteratorUtf16 interface, and adapt
the callers. It shouldn't change the behavior.

While rewriting the caller, one caveat worth mentioning is the loop termination
condition. If the old code relies on `!AtEnd()` as the loop termination
condition, and it advances the iterator at the end of the loop, it meant
to *skip* its logic when the break position is at the end of the string. For
example, see the `mozTXTToHTMLConv::NumberOfMatches`.

This patch also hooks grapheme cluster break iterator into
Segmenter::TryCreate() interface.

Existing test coverage for the file changed:
- netwerk/test/unit/test_mozTXTToHTMLConv.js
- layout/reftests/forms/input/file/dynamic-max-width.html

Differential Revision: https://phabricator.services.mozilla.com/D135643
This commit is contained in:
Ting-Yu Lin 2022-01-13 18:36:04 +00:00
Родитель 529d2dbd76
Коммит 653f4b3694
9 изменённых файлов: 224 добавлений и 225 удалений

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

@ -578,24 +578,26 @@ void gfxFontShaper::MergeFontFeatures(
void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
const char16_t* aString,
uint32_t aLength) {
CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
if (aLength == 0) {
return;
}
CompressedGlyph* const glyphs = GetCharacterGlyphs() + aOffset;
CompressedGlyph extendCluster = CompressedGlyph::MakeComplex(false, true);
const char16_t* const stringStart = aString;
intl::GraphemeClusterBreakIteratorUtf16 iter(aString, aLength);
// GraphemeClusterBreakIteratorUtf16 won't be able to tell us if the string
// _begins_ with a cluster-extender, so we handle that here
if (aLength) {
uint32_t ch = *aString;
if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
ch = SURROGATE_TO_UCS4(ch, aString[1]);
}
if (IsClusterExtender(ch)) {
*glyphs = extendCluster;
}
uint32_t ch = aString[0];
if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
ch = SURROGATE_TO_UCS4(ch, aString[1]);
}
if (IsClusterExtender(ch)) {
glyphs[0] = extendCluster;
}
intl::GraphemeClusterBreakIteratorUtf16 iter(
Span<const char16_t>(aString, aLength));
uint32_t pos = 0;
const char16_t kIdeographicSpace = 0x3000;
// Special case for Bengali: although Virama normally clusters with the
@ -604,25 +606,23 @@ void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
// preceding letter by any letter-spacing or justification.
const char16_t kBengaliVirama = 0x09CD;
const char16_t kBengaliYa = 0x09AF;
while (!iter.AtEnd()) {
if (*iter == char16_t(' ') || *iter == kIdeographicSpace) {
glyphs->SetIsSpace();
} else if (*iter == kBengaliYa) {
while (pos < aLength) {
const char16_t ch = aString[pos];
if (ch == char16_t(' ') || ch == kIdeographicSpace) {
glyphs[pos].SetIsSpace();
} else if (ch == kBengaliYa) {
// Unless we're at the start, check for a preceding virama.
if (aString > stringStart && *(aString - 1) == kBengaliVirama) {
*glyphs = extendCluster;
if (pos > 0 && aString[pos - 1] == kBengaliVirama) {
glyphs[pos] = extendCluster;
}
}
// advance iter to the next cluster-start (or end of text)
iter.Next();
const uint32_t nextPos = *iter.Next();
// step past the first char of the cluster
aString++;
glyphs++;
++pos;
// mark all the rest as cluster-continuations
while (aString < iter) {
*glyphs = extendCluster;
glyphs++;
aString++;
for (; pos < nextPos; ++pos) {
glyphs[pos] = extendCluster;
}
}
}

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

@ -1090,10 +1090,10 @@ void LineBreaker::ComputeBreakPositions(
if (aWordBreak == WordBreakRule::BreakAll) {
// For break-all, we don't need to run a dictionary-based breaking
// algorithm, we just allow breaks between all grapheme clusters.
GraphemeClusterBreakIteratorUtf16 ci(aChars + cur, end - cur);
while (!ci.AtEnd()) {
ci.Next();
aBreakBefore[ci - aChars] = true;
GraphemeClusterBreakIteratorUtf16 ci(
Span<const char16_t>(aChars + cur, end - cur));
while (Maybe<uint32_t> pos = ci.Next()) {
aBreakBefore[cur + *pos] = true;
}
} else {
ComplexBreaker::GetBreaks(aChars + cur, end - cur, aBreakBefore + cur);

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

@ -55,6 +55,10 @@ Maybe<uint32_t> WordBreakIteratorUtf16::Next() {
return Some(mPos);
}
GraphemeClusterBreakIteratorUtf16::GraphemeClusterBreakIteratorUtf16(
Span<const char16_t> aText)
: SegmentIteratorUtf16(aText) {}
enum HSType {
HST_NONE = U_HST_NOT_APPLICABLE,
HST_L = U_HST_LEADING_JAMO,
@ -69,22 +73,23 @@ static HSType GetHangulSyllableType(uint32_t aCh) {
aCh, UnicodeProperties::IntProperty::HangulSyllableType));
}
void GraphemeClusterBreakIteratorUtf16::Next() {
if (AtEnd()) {
NS_WARNING("ClusterIterator has already reached the end");
return;
Maybe<uint32_t> GraphemeClusterBreakIteratorUtf16::Next() {
const auto len = mText.Length();
if (mPos >= len) {
// The iterator has already reached the end.
return Nothing();
}
uint32_t ch = *mPos++;
uint32_t ch = mText[mPos++];
if (mPos < mLimit && NS_IS_SURROGATE_PAIR(ch, *mPos)) {
ch = SURROGATE_TO_UCS4(ch, *mPos++);
if (mPos < len && NS_IS_SURROGATE_PAIR(ch, mText[mPos])) {
ch = SURROGATE_TO_UCS4(ch, mText[mPos++]);
} else if ((ch & ~0xff) == 0x1100 || (ch >= 0xa960 && ch <= 0xa97f) ||
(ch >= 0xac00 && ch <= 0xd7ff)) {
// Handle conjoining Jamo that make Hangul syllables
HSType hangulState = GetHangulSyllableType(ch);
while (mPos < mLimit) {
ch = *mPos;
while (mPos < len) {
ch = mText[mPos];
HSType hangulType = GetHangulSyllableType(ch);
switch (hangulType) {
case HST_L:
@ -127,21 +132,21 @@ void GraphemeClusterBreakIteratorUtf16::Next() {
bool baseIsEmoji = (GetEmojiPresentation(ch) == EmojiDefault) ||
(GetEmojiPresentation(ch) == TextDefault &&
((mPos < mLimit && *mPos == kVS16) ||
(mPos + 1 < mLimit && *mPos == kFitzpatrickHigh &&
*(mPos + 1) >= kFitzpatrickLowFirst &&
*(mPos + 1) <= kFitzpatrickLowLast)));
((mPos < len && mText[mPos] == kVS16) ||
(mPos + 1 < len && mText[mPos] == kFitzpatrickHigh &&
mText[mPos + 1] >= kFitzpatrickLowFirst &&
mText[mPos + 1] <= kFitzpatrickLowLast)));
bool prevWasZwj = false;
while (mPos < mLimit) {
ch = *mPos;
while (mPos < len) {
ch = mText[mPos];
size_t chLen = 1;
// Check for surrogate pairs; note that isolated surrogates will just
// be treated as generic (non-cluster-extending) characters here,
// which is fine for cluster-iterating purposes
if (mPos < mLimit - 1 && NS_IS_SURROGATE_PAIR(ch, *(mPos + 1))) {
ch = SURROGATE_TO_UCS4(ch, *(mPos + 1));
if (mPos < len - 1 && NS_IS_SURROGATE_PAIR(ch, mText[mPos + 1])) {
ch = SURROGATE_TO_UCS4(ch, mText[mPos + 1]);
chLen = 2;
}
@ -149,8 +154,8 @@ void GraphemeClusterBreakIteratorUtf16::Next() {
IsClusterExtender(ch) ||
(baseIsEmoji && prevWasZwj &&
((GetEmojiPresentation(ch) == EmojiDefault) ||
(GetEmojiPresentation(ch) == TextDefault && mPos + chLen < mLimit &&
*(mPos + chLen) == kVS16)));
(GetEmojiPresentation(ch) == TextDefault && mPos + chLen < len &&
mText[mPos + chLen] == kVS16)));
if (!extendCluster) {
break;
}
@ -159,39 +164,49 @@ void GraphemeClusterBreakIteratorUtf16::Next() {
mPos += chLen;
}
NS_ASSERTION(mText < mPos && mPos <= mLimit,
"ClusterIterator::Next has overshot the string!");
MOZ_ASSERT(mPos <= len, "Next() has overshot the string!");
return Some(mPos);
}
void GraphemeClusterBreakReverseIteratorUtf16::Next() {
if (AtEnd()) {
NS_WARNING("ClusterReverseIterator has already reached the end");
return;
GraphemeClusterBreakReverseIteratorUtf16::
GraphemeClusterBreakReverseIteratorUtf16(Span<const char16_t> aText)
: SegmentIteratorUtf16(aText) {
mPos = mText.Length();
}
Maybe<uint32_t> GraphemeClusterBreakReverseIteratorUtf16::Next() {
if (mPos == 0) {
return Nothing();
}
uint32_t ch;
do {
ch = *--mPos;
ch = mText[--mPos];
if (mPos > mLimit && NS_IS_SURROGATE_PAIR(*(mPos - 1), ch)) {
ch = SURROGATE_TO_UCS4(*--mPos, ch);
if (mPos > 0 && NS_IS_SURROGATE_PAIR(mText[mPos - 1], ch)) {
ch = SURROGATE_TO_UCS4(mText[--mPos], ch);
}
if (!IsClusterExtender(ch)) {
break;
}
} while (mPos > mLimit);
} while (mPos > 0);
// XXX May need to handle conjoining Jamo
NS_ASSERTION(mPos >= mLimit,
"ClusterReverseIterator::Next has overshot the string!");
return Some(mPos);
}
Maybe<uint32_t> GraphemeClusterBreakReverseIteratorUtf16::Seek(uint32_t aPos) {
if (mPos > aPos) {
mPos = aPos;
}
return Next();
}
Result<UniquePtr<Segmenter>, ICUError> Segmenter::TryCreate(
Span<const char> aLocale, const SegmenterOptions& aOptions) {
if (aOptions.mGranularity == SegmenterGranularity::Grapheme ||
aOptions.mGranularity == SegmenterGranularity::Sentence) {
if (aOptions.mGranularity == SegmenterGranularity::Sentence) {
// Grapheme and Sentence iterator are not yet implemented.
return Err(ICUError::InternalError);
}
@ -202,6 +217,7 @@ UniquePtr<SegmentIteratorUtf16> Segmenter::Segment(
Span<const char16_t> aText) const {
switch (mOptions.mGranularity) {
case SegmenterGranularity::Grapheme:
return MakeUnique<GraphemeClusterBreakIteratorUtf16>(aText);
case SegmenterGranularity::Sentence:
MOZ_ASSERT_UNREACHABLE("Unimplemented yet!");
return nullptr;

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

@ -124,30 +124,11 @@ class WordBreakIteratorUtf16 final : public SegmentIteratorUtf16 {
/**
* Grapheme cluster break iterator for UTF-16 text.
*/
class GraphemeClusterBreakIteratorUtf16 {
class GraphemeClusterBreakIteratorUtf16 final : public SegmentIteratorUtf16 {
public:
GraphemeClusterBreakIteratorUtf16(const char16_t* aText, uint32_t aLength)
: mPos(aText),
mLimit(aText + aLength)
#ifdef DEBUG
,
mText(aText)
#endif
{
}
explicit GraphemeClusterBreakIteratorUtf16(Span<const char16_t> aText);
operator const char16_t*() const { return mPos; }
bool AtEnd() const { return mPos >= mLimit; }
void Next();
private:
const char16_t* mPos;
const char16_t* mLimit;
#ifdef DEBUG
const char16_t* mText;
#endif
Maybe<uint32_t> Next() override;
};
/**
@ -156,21 +137,13 @@ class GraphemeClusterBreakIteratorUtf16 {
* Note: The reverse iterator doesn't handle conjoining Jamo and emoji. Use it
* at your own risk.
*/
class GraphemeClusterBreakReverseIteratorUtf16 {
class GraphemeClusterBreakReverseIteratorUtf16 final
: public SegmentIteratorUtf16 {
public:
GraphemeClusterBreakReverseIteratorUtf16(const char16_t* aText,
uint32_t aLength)
: mPos(aText + aLength), mLimit(aText) {}
explicit GraphemeClusterBreakReverseIteratorUtf16(Span<const char16_t> aText);
operator const char16_t*() const { return mPos; }
bool AtEnd() const { return mPos <= mLimit; }
void Next();
private:
const char16_t* mPos;
const char16_t* mLimit;
Maybe<uint32_t> Next() override;
Maybe<uint32_t> Seek(uint32_t aPos) override;
};
/**

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

@ -51,11 +51,48 @@ TEST(IntlSegmenter, TestWordBreakIteratorUtf16)
ASSERT_EQ(segIter->Seek(0u), Nothing());
}
TEST(IntlSegmenter, TestGraphemeBreakIteratorUtf16)
TEST(IntlSegmenter, TestGraphemeClusterBreakIteratorUtf16)
{
SegmenterOptions options{SegmenterGranularity::Grapheme};
auto result = Segmenter::TryCreate("en", options);
ASSERT_TRUE(result.isErr());
ASSERT_TRUE(result.isOk());
auto graphemeClusterSegmenter = result.unwrap();
const char16_t text[] = u"hello world";
UniquePtr<SegmentIteratorUtf16> segIter =
graphemeClusterSegmenter->Segment(MakeStringSpan(text));
// Seek to the space between "hello" and "world"
ASSERT_EQ(segIter->Seek(5u), Some(6u));
ASSERT_EQ(segIter->Next(), Some(7u));
ASSERT_EQ(segIter->Next(), Some(8u));
ASSERT_EQ(segIter->Next(), Some(9u));
ASSERT_EQ(segIter->Next(), Some(10u));
ASSERT_EQ(segIter->Next(), Some(11u));
ASSERT_EQ(segIter->Next(), Nothing());
// Same as calling Next().
ASSERT_EQ(segIter->Seek(0u), Nothing());
}
TEST(IntlSegmenter, TestGraphemeClusterBreakReverseIteratorUtf16)
{
const char16_t text[] = u"hello world";
GraphemeClusterBreakReverseIteratorUtf16 segIter(MakeStringSpan(text));
// Seek to the space between "hello" and "world"
ASSERT_EQ(segIter.Seek(6u), Some(5u));
ASSERT_EQ(segIter.Next(), Some(4u));
ASSERT_EQ(segIter.Next(), Some(3u));
ASSERT_EQ(segIter.Next(), Some(2u));
ASSERT_EQ(segIter.Next(), Some(1u));
ASSERT_EQ(segIter.Next(), Some(0u));
ASSERT_EQ(segIter.Next(), Nothing());
// Same as calling Next().
ASSERT_EQ(segIter.Seek(0u), Nothing());
}
TEST(IntlSegmenter, TestSentenceBreakIteratorUtf16)

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

@ -168,12 +168,10 @@ bool IsClusterExtender(uint32_t aCh, uint8_t aCategory) {
}
uint32_t CountGraphemeClusters(Span<const char16_t> aText) {
intl::GraphemeClusterBreakIteratorUtf16 iter(aText.Elements(),
aText.Length());
intl::GraphemeClusterBreakIteratorUtf16 iter(aText);
uint32_t result = 0;
while (!iter.AtEnd()) {
while (iter.Next()) {
++result;
iter.Next();
}
return result;
}

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

@ -69,9 +69,9 @@ bool nsFileControlFrame::CropTextToWidth(gfxContext& aRenderingContext,
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
// see if the text will completely fit in the width given
nscoord textWidth = nsLayoutUtils::AppUnitWidthOfStringBidi(
aText, aFrame, *fm, aRenderingContext);
if (textWidth <= aWidth) {
if (const nscoord textWidth = nsLayoutUtils::AppUnitWidthOfStringBidi(
aText, aFrame, *fm, aRenderingContext);
textWidth <= aWidth) {
return false;
}
@ -80,54 +80,49 @@ bool nsFileControlFrame::CropTextToWidth(gfxContext& aRenderingContext,
// see if the width is even smaller than the ellipsis
fm->SetTextRunRTL(false);
textWidth = nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm, drawTarget);
if (textWidth >= aWidth) {
const nscoord ellipsisWidth =
nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm, drawTarget);
if (ellipsisWidth >= aWidth) {
aText = kEllipsis;
return true;
}
// determine how much of the string will fit in the max width
nscoord totalWidth = textWidth;
intl::GraphemeClusterBreakIteratorUtf16 leftIter(aText.Data(),
aText.Length());
intl::GraphemeClusterBreakReverseIteratorUtf16 rightIter(aText.Data(),
aText.Length());
const char16_t* leftPos = leftIter;
const char16_t* rightPos = rightIter;
const char16_t* pos;
ptrdiff_t length;
nscoord totalWidth = ellipsisWidth;
const Span text(aText);
intl::GraphemeClusterBreakIteratorUtf16 leftIter(text);
intl::GraphemeClusterBreakReverseIteratorUtf16 rightIter(text);
uint32_t leftPos = 0;
uint32_t rightPos = aText.Length();
nsAutoString leftString, rightString;
while (leftPos < rightPos) {
leftIter.Next();
pos = leftIter;
length = pos - leftPos;
textWidth =
nsLayoutUtils::AppUnitWidthOfString(leftPos, length, *fm, drawTarget);
if (totalWidth + textWidth > aWidth) {
Maybe<uint32_t> pos = leftIter.Next();
Span chars = text.FromTo(leftPos, *pos);
nscoord charWidth =
nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
if (totalWidth + charWidth > aWidth) {
break;
}
leftString.Append(leftPos, length);
leftPos = pos;
totalWidth += textWidth;
leftString.Append(chars);
leftPos = *pos;
totalWidth += charWidth;
if (leftPos >= rightPos) {
break;
}
rightIter.Next();
pos = rightIter;
length = rightPos - pos;
textWidth =
nsLayoutUtils::AppUnitWidthOfString(pos, length, *fm, drawTarget);
if (totalWidth + textWidth > aWidth) {
pos = rightIter.Next();
chars = text.FromTo(*pos, rightPos);
charWidth = nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
if (totalWidth + charWidth > aWidth) {
break;
}
rightString.Insert(pos, 0, length);
rightPos = pos;
totalWidth += textWidth;
rightString.Insert(chars, 0);
rightPos = *pos;
totalWidth += charWidth;
}
aText = leftString + kEllipsis + rightString;

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

@ -622,71 +622,59 @@ nscoord nsTextBoxFrame::CalculateTitleForWidth(gfxContext& aRenderingContext,
case CropAuto:
case CropNone:
case CropRight: {
GraphemeClusterBreakIteratorUtf16 iter(mTitle.Data(), mTitle.Length());
const char16_t* dataBegin = iter;
const char16_t* pos = dataBegin;
nscoord charWidth;
const Span title(mTitle);
GraphemeClusterBreakIteratorUtf16 iter(title);
uint32_t pos = 0;
nscoord totalWidth = 0;
while (!iter.AtEnd()) {
iter.Next();
const char16_t* nextPos = iter;
ptrdiff_t length = nextPos - pos;
charWidth =
nsLayoutUtils::AppUnitWidthOfString(pos, length, *fm, drawTarget);
while (Maybe<uint32_t> nextPos = iter.Next()) {
const nscoord charWidth = nsLayoutUtils::AppUnitWidthOfString(
title.FromTo(pos, *nextPos), *fm, drawTarget);
if (totalWidth + charWidth > aWidth) {
break;
}
if (UTF16_CODE_UNIT_IS_BIDI(*pos)) {
if (UTF16_CODE_UNIT_IS_BIDI(mTitle[pos])) {
AddStateBits(NS_FRAME_IS_BIDI);
}
pos = nextPos;
pos = *nextPos;
totalWidth += charWidth;
}
if (pos == dataBegin) {
if (pos == 0) {
return titleWidth;
}
// insert what character we can in.
nsAutoString title(mTitle);
title.Truncate(pos - dataBegin);
mCroppedTitle.Insert(title, 0);
mCroppedTitle.Insert(title.To(pos), 0);
} break;
case CropLeft: {
GraphemeClusterBreakReverseIteratorUtf16 iter(mTitle.Data(),
mTitle.Length());
const char16_t* dataEnd = iter;
const char16_t* prevPos = dataEnd;
nscoord charWidth;
const Span title(mTitle);
GraphemeClusterBreakReverseIteratorUtf16 iter(title);
uint32_t pos = title.Length();
nscoord totalWidth = 0;
while (!iter.AtEnd()) {
iter.Next();
const char16_t* pos = iter;
ptrdiff_t length = prevPos - pos;
charWidth =
nsLayoutUtils::AppUnitWidthOfString(pos, length, *fm, drawTarget);
// nextPos is decreasing since we use a reverse iterator.
while (Maybe<uint32_t> nextPos = iter.Next()) {
const nscoord charWidth = nsLayoutUtils::AppUnitWidthOfString(
title.FromTo(*nextPos, pos), *fm, drawTarget);
if (totalWidth + charWidth > aWidth) {
break;
}
if (UTF16_CODE_UNIT_IS_BIDI(*pos)) {
if (UTF16_CODE_UNIT_IS_BIDI(mTitle[*nextPos])) {
AddStateBits(NS_FRAME_IS_BIDI);
}
prevPos = pos;
pos = *nextPos;
totalWidth += charWidth;
}
if (prevPos == dataEnd) {
if (pos == title.Length()) {
return titleWidth;
}
nsAutoString copy;
mTitle.Right(copy, dataEnd - prevPos);
mCroppedTitle += copy;
mCroppedTitle.Append(title.From(pos));
} break;
case CropCenter: {
@ -699,57 +687,48 @@ nscoord nsTextBoxFrame::CalculateTitleForWidth(gfxContext& aRenderingContext,
}
// determine how much of the string will fit in the max width
nscoord charWidth = 0;
const Span title(mTitle);
nscoord totalWidth = 0;
GraphemeClusterBreakIteratorUtf16 leftIter(mTitle.Data(),
mTitle.Length());
GraphemeClusterBreakReverseIteratorUtf16 rightIter(mTitle.Data(),
mTitle.Length());
const char16_t* dataBegin = leftIter;
const char16_t* dataEnd = rightIter;
const char16_t* leftPos = dataBegin;
const char16_t* rightPos = dataEnd;
const char16_t* pos;
ptrdiff_t length;
GraphemeClusterBreakIteratorUtf16 leftIter(title);
GraphemeClusterBreakReverseIteratorUtf16 rightIter(title);
uint32_t leftPos = 0;
uint32_t rightPos = title.Length();
nsAutoString leftString, rightString;
while (leftPos < rightPos) {
leftIter.Next();
pos = leftIter;
length = pos - leftPos;
charWidth = nsLayoutUtils::AppUnitWidthOfString(leftPos, length, *fm,
drawTarget);
Maybe<uint32_t> nextPos = leftIter.Next();
Span chars = title.FromTo(leftPos, *nextPos);
nscoord charWidth =
nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
if (totalWidth + charWidth > aWidth) {
break;
}
if (UTF16_CODE_UNIT_IS_BIDI(*leftPos)) {
if (UTF16_CODE_UNIT_IS_BIDI(mTitle[leftPos])) {
AddStateBits(NS_FRAME_IS_BIDI);
}
leftString.Append(leftPos, length);
leftPos = pos;
leftString.Append(chars);
leftPos = *nextPos;
totalWidth += charWidth;
if (leftPos >= rightPos) {
break;
}
rightIter.Next();
pos = rightIter;
length = rightPos - pos;
charWidth =
nsLayoutUtils::AppUnitWidthOfString(pos, length, *fm, drawTarget);
nextPos = rightIter.Next();
chars = title.FromTo(*nextPos, rightPos);
charWidth = nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
if (totalWidth + charWidth > aWidth) {
break;
}
if (UTF16_CODE_UNIT_IS_BIDI(*pos)) {
if (UTF16_CODE_UNIT_IS_BIDI(mTitle[*nextPos])) {
AddStateBits(NS_FRAME_IS_BIDI);
}
rightString.Insert(pos, 0, length);
rightPos = pos;
rightString.Insert(chars, 0);
rightPos = *nextPos;
totalWidth += charWidth;
}

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

@ -6,6 +6,7 @@
#include "mozilla/TextUtils.h"
#include "mozTXTToHTMLConv.h"
#include "mozilla/intl/Segmenter.h"
#include "mozilla/Maybe.h"
#include "nsNetUtil.h"
#include "nsUnicharUtils.h"
#include "nsUnicodeProperties.h"
@ -23,6 +24,9 @@
using mozilla::IsAscii;
using mozilla::IsAsciiAlpha;
using mozilla::IsAsciiDigit;
using mozilla::Maybe;
using mozilla::Some;
using mozilla::Span;
using mozilla::intl::GraphemeClusterBreakIteratorUtf16;
using mozilla::intl::GraphemeClusterBreakReverseIteratorUtf16;
@ -560,9 +564,9 @@ bool mozTXTToHTMLConv::ItMatchesDelimited(const char16_t* aInString,
// find length of the char/cluster to be ignored
int32_t ignoreLen = before == LT_IGNORE ? 0 : 1;
if (ignoreLen) {
GraphemeClusterBreakIteratorUtf16 ci(aInString, aInLength);
ci.Next();
ignoreLen = ci - aInString;
GraphemeClusterBreakIteratorUtf16 ci(
Span<const char16_t>(aInString, aInLength));
ignoreLen = *ci.Next();
}
int32_t afterIndex = aRepLen + ignoreLen;
@ -593,10 +597,11 @@ uint32_t mozTXTToHTMLConv::NumberOfMatches(const char16_t* aInString,
LIMTYPE before, LIMTYPE after) {
uint32_t result = 0;
const char16_t* end = aInString + aInStringLength;
for (GraphemeClusterBreakIteratorUtf16 ci(aInString, aInStringLength);
!ci.AtEnd(); ci.Next()) {
if (ItMatchesDelimited(ci, end - ci, rep, aRepLen, before, after)) {
const uint32_t len = mozilla::AssertedCast<uint32_t>(aInStringLength);
GraphemeClusterBreakIteratorUtf16 ci(Span<const char16_t>(aInString, len));
for (uint32_t pos = 0; pos < len; pos = *ci.Next()) {
if (ItMatchesDelimited(aInString + pos, aInStringLength - pos, rep, aRepLen,
before, after)) {
result++;
}
}
@ -982,17 +987,15 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
const char16_t* rawInputString = aInString.BeginReading();
uint32_t inLength = aInString.Length();
for (GraphemeClusterBreakIteratorUtf16 ci(rawInputString, inLength);
!ci.AtEnd();) {
uint32_t i = ci - rawInputString;
const Span<const char16_t> inString(aInString);
GraphemeClusterBreakIteratorUtf16 ci(inString);
uint32_t i = 0;
while (i < inLength) {
if (doGlyphSubstitution) {
int32_t glyphTextLen;
if (GlyphHit(&rawInputString[i], inLength - i, i == 0, aOutString,
glyphTextLen)) {
i += glyphTextLen;
while (ci < rawInputString + i) {
ci.Next();
}
i = *ci.Seek(i + glyphTextLen - 1);
continue;
}
}
@ -1002,10 +1005,11 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
int32_t newLength = aInString.Length();
if (i > 0) // skip the first element?
{
GraphemeClusterBreakReverseIteratorUtf16 ri(rawInputString, i);
ri.Next();
newOffset = ri;
newLength = aInString.Length() - (ri - rawInputString);
GraphemeClusterBreakReverseIteratorUtf16 ri(
Span<const char16_t>(rawInputString, i));
Maybe<uint32_t> nextPos = ri.Next();
newOffset += *nextPos;
newLength -= *nextPos;
}
switch (aInString[i]) // Performance increase
@ -1014,7 +1018,7 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
if (StructPhraseHit(newOffset, newLength, i == 0, u"*", 1, "b",
"class=\"moz-txt-star\"", aOutString,
structPhrase_strong)) {
ci.Next();
i = *ci.Next();
continue;
}
break;
@ -1022,7 +1026,7 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
if (StructPhraseHit(newOffset, newLength, i == 0, u"/", 1, "i",
"class=\"moz-txt-slash\"", aOutString,
structPhrase_italic)) {
ci.Next();
i = *ci.Next();
continue;
}
break;
@ -1031,7 +1035,7 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
"span" /* <u> is deprecated */,
"class=\"moz-txt-underscore\"", aOutString,
structPhrase_underline)) {
ci.Next();
i = *ci.Next();
continue;
}
break;
@ -1039,7 +1043,7 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
if (StructPhraseHit(newOffset, newLength, i == 0, u"|", 1, "code",
"class=\"moz-txt-verticalline\"", aOutString,
structPhrase_code)) {
ci.Next();
i = *ci.Next();
continue;
}
break;
@ -1071,10 +1075,7 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
replaceBefore);
aOutString += outputHTML;
endOfLastURLOutput = aOutString.Length();
i += replaceAfter + 1;
while (ci < rawInputString + i) {
ci.Next();
}
i = *ci.Seek(i + replaceAfter);
continue;
}
}
@ -1088,13 +1089,13 @@ mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
case '>':
case '&':
EscapeChar(aInString[i], aOutString, false);
ci.Next();
i = *ci.Next();
break;
// Normal characters
default: {
const char16_t* start = ci;
ci.Next();
aOutString += Substring(start, (const char16_t*)ci);
const uint32_t oldIdx = i;
i = *ci.Next();
aOutString.Append(inString.FromTo(oldIdx, i));
break;
}
}