summaryrefslogtreecommitdiffstats
path: root/layout/base/nsBidiPresUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/nsBidiPresUtils.cpp')
-rw-r--r--layout/base/nsBidiPresUtils.cpp247
1 files changed, 134 insertions, 113 deletions
diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp
index b1215972c2..0331a7e5a0 100644
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -47,9 +47,10 @@ using BidiClass = intl::BidiClass;
using BidiDirection = intl::BidiDirection;
using BidiEmbeddingLevel = intl::BidiEmbeddingLevel;
-static const char16_t kSpace = 0x0020;
+static const char16_t kNextLine = 0x0085;
static const char16_t kZWSP = 0x200B;
static const char16_t kLineSeparator = 0x2028;
+static const char16_t kParagraphSeparator = 0x2029;
static const char16_t kObjectSubstitute = 0xFFFC;
static const char16_t kLRE = 0x202A;
static const char16_t kRLE = 0x202B;
@@ -60,11 +61,12 @@ static const char16_t kLRI = 0x2066;
static const char16_t kRLI = 0x2067;
static const char16_t kFSI = 0x2068;
static const char16_t kPDI = 0x2069;
-// All characters with Bidi type Segment Separator or Block Separator
+// All characters with Bidi type Segment Separator or Block Separator.
+// This should be kept in sync with the table in ReplaceSeparators.
static const char16_t kSeparators[] = {
- char16_t('\t'), char16_t('\r'), char16_t('\n'), char16_t(0xb),
- char16_t(0x1c), char16_t(0x1d), char16_t(0x1e), char16_t(0x1f),
- char16_t(0x85), char16_t(0x2029), char16_t(0)};
+ char16_t('\t'), char16_t('\r'), char16_t('\n'), char16_t(0xb),
+ char16_t(0x1c), char16_t(0x1d), char16_t(0x1e), char16_t(0x1f),
+ kNextLine, kParagraphSeparator, char16_t(0)};
#define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
@@ -478,115 +480,99 @@ struct MOZ_STACK_CLASS BidiParagraphData {
}
};
-struct MOZ_STACK_CLASS BidiLineData {
- AutoTArray<nsIFrame*, 16> mLogicalFrames;
- AutoTArray<nsIFrame*, 16> mVisualFrames;
- AutoTArray<int32_t, 16> mIndexMap;
- AutoTArray<BidiEmbeddingLevel, 16> mLevels;
- bool mIsReordered;
-
+class MOZ_STACK_CLASS BidiLineData {
+ public:
BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine) {
- /**
- * Initialize the logically-ordered array of frames using the top-level
- * frames of a single line
- */
- bool isReordered = false;
- bool hasRTLFrames = false;
- bool hasVirtualControls = false;
-
+ // Initialize the logically-ordered array of frames using the top-level
+ // frames of a single line
auto appendFrame = [&](nsIFrame* frame, BidiEmbeddingLevel level) {
mLogicalFrames.AppendElement(frame);
mLevels.AppendElement(level);
mIndexMap.AppendElement(0);
- if (level.IsRTL()) {
- hasRTLFrames = true;
- }
};
- bool firstFrame = true;
for (nsIFrame* frame = aFirstFrameOnLine; frame && aNumFramesOnLine--;
frame = frame->GetNextSibling()) {
FrameBidiData bidiData = nsBidiPresUtils::GetFrameBidiData(frame);
- // Ignore virtual control before the first frame. Doing so should
- // not affect the visual result, but could avoid running into the
- // stripping code below for many cases.
- if (!firstFrame && bidiData.precedingControl != kBidiLevelNone) {
+ if (bidiData.precedingControl != kBidiLevelNone) {
appendFrame(NS_BIDI_CONTROL_FRAME, bidiData.precedingControl);
- hasVirtualControls = true;
}
appendFrame(frame, bidiData.embeddingLevel);
- firstFrame = false;
}
// Reorder the line
- BidiEngine::ReorderVisual(mLevels.Elements(), FrameCount(),
+ BidiEngine::ReorderVisual(mLevels.Elements(), mLevels.Length(),
mIndexMap.Elements());
- // Strip virtual frames
- if (hasVirtualControls) {
- auto originalCount = mLogicalFrames.Length();
- AutoTArray<int32_t, 16> realFrameMap;
- realFrameMap.SetCapacity(originalCount);
- size_t count = 0;
- for (auto i : IntegerRange(originalCount)) {
- if (mLogicalFrames[i] == NS_BIDI_CONTROL_FRAME) {
- realFrameMap.AppendElement(-1);
- } else {
- mLogicalFrames[count] = mLogicalFrames[i];
- mLevels[count] = mLevels[i];
- realFrameMap.AppendElement(count);
- count++;
- }
+ // Collect the frames in visual order, omitting virtual controls
+ // and noting whether frames are reordered.
+ for (uint32_t i = 0; i < mIndexMap.Length(); i++) {
+ nsIFrame* frame = mLogicalFrames[mIndexMap[i]];
+ if (frame == NS_BIDI_CONTROL_FRAME) {
+ continue;
}
- // Only keep index map for real frames.
- for (size_t i = 0, j = 0; i < originalCount; ++i) {
- auto newIndex = realFrameMap[mIndexMap[i]];
- if (newIndex != -1) {
- mIndexMap[j] = newIndex;
- j++;
- }
+ mVisualFrameIndex.AppendElement(mIndexMap[i]);
+ if (int32_t(i) != mIndexMap[i]) {
+ mIsReordered = true;
}
- mLogicalFrames.TruncateLength(count);
- mLevels.TruncateLength(count);
- mIndexMap.TruncateLength(count);
}
+ }
- for (int32_t i = 0; i < FrameCount(); i++) {
- mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
- if (i != mIndexMap[i]) {
- isReordered = true;
- }
- }
+ uint32_t LogicalFrameCount() const { return mLogicalFrames.Length(); }
+ uint32_t VisualFrameCount() const { return mVisualFrameIndex.Length(); }
- // If there's an RTL frame, assume the line is reordered
- mIsReordered = isReordered || hasRTLFrames;
+ nsIFrame* LogicalFrameAt(uint32_t aIndex) const {
+ return mLogicalFrames[aIndex];
}
- int32_t FrameCount() const { return mLogicalFrames.Length(); }
+ nsIFrame* VisualFrameAt(uint32_t aIndex) const {
+ return mLogicalFrames[mVisualFrameIndex[aIndex]];
+ }
- nsIFrame* LogicalFrameAt(int32_t aIndex) const {
- return mLogicalFrames[aIndex];
+ std::pair<nsIFrame*, BidiEmbeddingLevel> VisualFrameAndLevelAt(
+ uint32_t aIndex) const {
+ int32_t index = mVisualFrameIndex[aIndex];
+ return std::pair(mLogicalFrames[index], mLevels[index]);
}
- nsIFrame* VisualFrameAt(int32_t aIndex) const {
- return mVisualFrames[aIndex];
+ bool IsReordered() const { return mIsReordered; }
+
+ void InitContinuationStates(nsContinuationStates* aContinuationStates) const {
+ for (auto* frame : mLogicalFrames) {
+ if (frame != NS_BIDI_CONTROL_FRAME) {
+ nsBidiPresUtils::InitContinuationStates(frame, aContinuationStates);
+ }
+ }
}
+
+ private:
+ AutoTArray<nsIFrame*, 16> mLogicalFrames;
+ AutoTArray<int32_t, 16> mVisualFrameIndex;
+ AutoTArray<int32_t, 16> mIndexMap;
+ AutoTArray<BidiEmbeddingLevel, 16> mLevels;
+ bool mIsReordered = false;
};
#ifdef DEBUG
extern "C" {
-void MOZ_EXPORT DumpFrameArray(const nsTArray<nsIFrame*>& aFrames) {
- for (nsIFrame* frame : aFrames) {
+void MOZ_EXPORT DumpBidiLine(BidiLineData* aData, bool aVisualOrder) {
+ auto dump = [](nsIFrame* frame) {
if (frame == NS_BIDI_CONTROL_FRAME) {
fprintf_stderr(stderr, "(Bidi control frame)\n");
} else {
frame->List();
}
- }
-}
+ };
-void MOZ_EXPORT DumpBidiLine(BidiLineData* aData, bool aVisualOrder) {
- DumpFrameArray(aVisualOrder ? aData->mVisualFrames : aData->mLogicalFrames);
+ if (aVisualOrder) {
+ for (uint32_t i = 0; i < aData->VisualFrameCount(); i++) {
+ dump(aData->VisualFrameAt(i));
+ }
+ } else {
+ for (uint32_t i = 0; i < aData->LogicalFrameCount(); i++) {
+ dump(aData->LogicalFrameAt(i));
+ }
+ }
}
}
#endif
@@ -870,11 +856,32 @@ nsresult nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) {
return ResolveParagraph(&bpd);
}
+// In ResolveParagraph, we previously used ReplaceChar(kSeparators, kSpace)
+// to convert separators to spaces, but this hard-coded implementation is
+// substantially faster than the general-purpose ReplaceChar function.
+// This must be kept in sync with the definition of kSeparators.
+static inline void ReplaceSeparators(nsString& aText, size_t aStartIndex = 0) {
+ for (char16_t* cp = aText.BeginWriting() + aStartIndex;
+ cp < aText.EndWriting(); cp++) {
+ if (MOZ_UNLIKELY(*cp < char16_t(' '))) {
+ static constexpr char16_t SeparatorToSpace[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ' ', ' ',
+ ' ', 0x0c, ' ', 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, ' ', ' ', ' ', ' ',
+ };
+ *cp = SeparatorToSpace[*cp];
+ } else if (MOZ_UNLIKELY(*cp == kNextLine || *cp == kParagraphSeparator)) {
+ *cp = ' ';
+ }
+ }
+}
+
nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
if (aBpd->BufferLength() < 1) {
return NS_OK;
}
- aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
+
+ ReplaceSeparators(aBpd->mBuffer);
int32_t runCount;
@@ -1506,8 +1513,19 @@ nscoord nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine,
aStart = 0;
}
+ // No need to bidi-reorder the line if there's only a single frame.
+ if (aNumFramesOnLine == 1) {
+ auto bidiData = nsBidiPresUtils::GetFrameBidiData(aFirstFrameOnLine);
+ nsContinuationStates continuationStates;
+ InitContinuationStates(aFirstFrameOnLine, &continuationStates);
+ return aStart + RepositionFrame(aFirstFrameOnLine,
+ bidiData.embeddingLevel.IsLTR(), aStart,
+ &continuationStates, aLineWM, false,
+ containerSize);
+ }
+
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- return RepositionInlineFrames(&bld, aLineWM, containerSize, aStart);
+ return RepositionInlineFrames(bld, aLineWM, containerSize, aStart);
}
nsIFrame* nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame) {
@@ -1857,40 +1875,30 @@ void nsBidiPresUtils::InitContinuationStates(
}
/* static */
-nscoord nsBidiPresUtils::RepositionInlineFrames(BidiLineData* aBld,
+nscoord nsBidiPresUtils::RepositionInlineFrames(const BidiLineData& aBld,
WritingMode aLineWM,
const nsSize& aContainerSize,
nscoord aStart) {
- nscoord start = aStart;
- nsIFrame* frame;
- int32_t count = aBld->mVisualFrames.Length();
- int32_t index;
nsContinuationStates continuationStates;
+ aBld.InitContinuationStates(&continuationStates);
- // Initialize continuation states to (nullptr, 0) for
- // each frame on the line.
- for (index = 0; index < count; index++) {
- InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
- }
-
- // Reposition frames in visual order
- int32_t step, limit;
if (aLineWM.IsBidiLTR()) {
- index = 0;
- step = 1;
- limit = count;
+ for (auto index : IntegerRange(aBld.VisualFrameCount())) {
+ auto [frame, level] = aBld.VisualFrameAndLevelAt(index);
+ aStart +=
+ RepositionFrame(frame, level.IsLTR(), aStart, &continuationStates,
+ aLineWM, false, aContainerSize);
+ }
} else {
- index = count - 1;
- step = -1;
- limit = -1;
- }
- for (; index != limit; index += step) {
- frame = aBld->VisualFrameAt(index);
- start += RepositionFrame(
- frame, !(aBld->mLevels[aBld->mIndexMap[index]].IsRTL()), start,
- &continuationStates, aLineWM, false, aContainerSize);
+ for (auto index : Reversed(IntegerRange(aBld.VisualFrameCount()))) {
+ auto [frame, level] = aBld.VisualFrameAndLevelAt(index);
+ aStart +=
+ RepositionFrame(frame, level.IsLTR(), aStart, &continuationStates,
+ aLineWM, false, aContainerSize);
+ }
}
- return start;
+
+ return aStart;
}
bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
@@ -1898,16 +1906,15 @@ bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
nsIFrame** aFirstVisual,
nsIFrame** aLastVisual) {
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- int32_t count = bld.FrameCount();
if (aFirstVisual) {
*aFirstVisual = bld.VisualFrameAt(0);
}
if (aLastVisual) {
- *aLastVisual = bld.VisualFrameAt(count - 1);
+ *aLastVisual = bld.VisualFrameAt(bld.VisualFrameCount() - 1);
}
- return bld.mIsReordered;
+ return bld.IsReordered();
}
nsIFrame* nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
@@ -1915,9 +1922,11 @@ nsIFrame* nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
int32_t aNumFramesOnLine) {
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- int32_t count = bld.mVisualFrames.Length();
+ int32_t count = bld.VisualFrameCount();
- if (aFrame == nullptr && count) return bld.VisualFrameAt(0);
+ if (!aFrame && count) {
+ return bld.VisualFrameAt(0);
+ }
for (int32_t i = 0; i < count - 1; i++) {
if (bld.VisualFrameAt(i) == aFrame) {
@@ -1933,9 +1942,11 @@ nsIFrame* nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
int32_t aNumFramesOnLine) {
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- int32_t count = bld.mVisualFrames.Length();
+ int32_t count = bld.VisualFrameCount();
- if (aFrame == nullptr && count) return bld.VisualFrameAt(count - 1);
+ if (!aFrame && count) {
+ return bld.VisualFrameAt(count - 1);
+ }
for (int32_t i = 1; i < count; i++) {
if (bld.VisualFrameAt(i) == aFrame) {
@@ -2476,9 +2487,19 @@ nsresult nsBidiPresUtils::ProcessTextForRenderingContext(
nsIRenderingContextBidiProcessor processor(&aRenderingContext,
aTextRunConstructionDrawTarget,
&aFontMetrics, nsPoint(aX, aY));
- nsAutoString text(aText, aLength);
- text.ReplaceChar(kSeparators, ' ');
- return ProcessText(text.BeginReading(), text.Length(), aBaseLevel,
+ nsDependentSubstring text(aText, aLength);
+ auto separatorIndex = text.FindCharInSet(kSeparators);
+ if (separatorIndex == kNotFound) {
+ return ProcessText(text.BeginReading(), text.Length(), aBaseLevel,
+ aPresContext, processor, aMode, aPosResolve,
+ aPosResolveCount, aWidth, aPresContext->BidiEngine());
+ }
+
+ // We need to replace any block or segment separators with space for bidi
+ // processing, so make a local copy.
+ nsAutoString localText(text);
+ ReplaceSeparators(localText, separatorIndex);
+ return ProcessText(localText.BeginReading(), localText.Length(), aBaseLevel,
aPresContext, processor, aMode, aPosResolve,
aPosResolveCount, aWidth, aPresContext->BidiEngine());
}