1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsBidiPresUtils.h"
8
9 #include "mozilla/IntegerRange.h"
10
11 #include "gfxContext.h"
12 #include "nsAutoPtr.h"
13 #include "nsFontMetrics.h"
14 #include "nsGkAtoms.h"
15 #include "nsPresContext.h"
16 #include "nsBidiUtils.h"
17 #include "nsCSSFrameConstructor.h"
18 #include "nsContainerFrame.h"
19 #include "nsInlineFrame.h"
20 #include "nsPlaceholderFrame.h"
21 #include "nsPointerHashKeys.h"
22 #include "nsFirstLetterFrame.h"
23 #include "nsUnicodeProperties.h"
24 #include "nsTextFrame.h"
25 #include "nsBlockFrame.h"
26 #include "nsIFrameInlines.h"
27 #include "nsStyleStructInlines.h"
28 #include "RubyUtils.h"
29 #include "nsRubyFrame.h"
30 #include "nsRubyBaseFrame.h"
31 #include "nsRubyTextFrame.h"
32 #include "nsRubyBaseContainerFrame.h"
33 #include "nsRubyTextContainerFrame.h"
34 #include <algorithm>
35
36 #undef NOISY_BIDI
37 #undef REALLY_NOISY_BIDI
38
39 using namespace mozilla;
40
41 static const char16_t kSpace = 0x0020;
42 static const char16_t kZWSP = 0x200B;
43 static const char16_t kLineSeparator = 0x2028;
44 static const char16_t kObjectSubstitute = 0xFFFC;
45 static const char16_t kLRE = 0x202A;
46 static const char16_t kRLE = 0x202B;
47 static const char16_t kLRO = 0x202D;
48 static const char16_t kRLO = 0x202E;
49 static const char16_t kPDF = 0x202C;
50 static const char16_t kLRI = 0x2066;
51 static const char16_t kRLI = 0x2067;
52 static const char16_t kFSI = 0x2068;
53 static const char16_t kPDI = 0x2069;
54 static const char16_t kSeparators[] = {
55 // All characters with Bidi type Segment Separator or Block Separator
56 char16_t('\t'), char16_t('\r'), char16_t('\n'), char16_t(0xb),
57 char16_t(0x1c), char16_t(0x1d), char16_t(0x1e), char16_t(0x1f),
58 char16_t(0x85), char16_t(0x2029), char16_t(0)};
59
60 #define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
61
IsIsolateControl(char16_t aChar)62 static bool IsIsolateControl(char16_t aChar) {
63 return aChar == kLRI || aChar == kRLI || aChar == kFSI;
64 }
65
66 // Given a style context, return any bidi control character necessary to
67 // implement style properties that override directionality (i.e. if it has
68 // unicode-bidi:bidi-override, or text-orientation:upright in vertical
69 // writing mode) when applying the bidi algorithm.
70 //
71 // Returns 0 if no override control character is implied by this style.
GetBidiOverride(nsStyleContext * aStyleContext)72 static char16_t GetBidiOverride(nsStyleContext* aStyleContext) {
73 const nsStyleVisibility* vis = aStyleContext->StyleVisibility();
74 if ((vis->mWritingMode == NS_STYLE_WRITING_MODE_VERTICAL_RL ||
75 vis->mWritingMode == NS_STYLE_WRITING_MODE_VERTICAL_LR) &&
76 vis->mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT) {
77 return kLRO;
78 }
79 const nsStyleTextReset* text = aStyleContext->StyleTextReset();
80 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
81 return NS_STYLE_DIRECTION_RTL == vis->mDirection ? kRLO : kLRO;
82 }
83 return 0;
84 }
85
86 // Given a style context, return any bidi control character necessary to
87 // implement style properties that affect bidi resolution (i.e. if it
88 // has unicode-bidiembed, isolate, or plaintext) when applying the bidi
89 // algorithm.
90 //
91 // Returns 0 if no control character is implied by the style.
92 //
93 // Note that GetBidiOverride and GetBidiControl need to be separate
94 // because in the case of unicode-bidi:isolate-override we need both
95 // FSI and LRO/RLO.
GetBidiControl(nsStyleContext * aStyleContext)96 static char16_t GetBidiControl(nsStyleContext* aStyleContext) {
97 const nsStyleVisibility* vis = aStyleContext->StyleVisibility();
98 const nsStyleTextReset* text = aStyleContext->StyleTextReset();
99 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_EMBED) {
100 return NS_STYLE_DIRECTION_RTL == vis->mDirection ? kRLE : kLRE;
101 }
102 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_ISOLATE) {
103 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
104 // isolate-override
105 return kFSI;
106 }
107 // <bdi> element already has its directionality set from content so
108 // we never need to return kFSI.
109 return NS_STYLE_DIRECTION_RTL == vis->mDirection ? kRLI : kLRI;
110 }
111 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
112 return kFSI;
113 }
114 return 0;
115 }
116
117 struct MOZ_STACK_CLASS BidiParagraphData {
118 nsAutoString mBuffer;
119 AutoTArray<char16_t, 16> mEmbeddingStack;
120 AutoTArray<nsIFrame*, 16> mLogicalFrames;
121 AutoTArray<nsLineBox*, 16> mLinePerFrame;
122 nsDataHashtable<nsPtrHashKey<const nsIContent>, int32_t> mContentToFrameIndex;
123 // Cached presentation context for the frames we're processing.
124 nsPresContext* mPresContext;
125 bool mIsVisual;
126 bool mRequiresBidi;
127 nsBidiLevel mParaLevel;
128 nsIContent* mPrevContent;
129 nsIFrame* mPrevFrame;
130 #ifdef DEBUG
131 // Only used for NOISY debug output.
132 nsBlockFrame* mCurrentBlock;
133 #endif
134
BidiParagraphDataBidiParagraphData135 explicit BidiParagraphData(nsBlockFrame* aBlockFrame)
136 : mPresContext(aBlockFrame->PresContext()),
137 mIsVisual(mPresContext->IsVisualMode()),
138 mRequiresBidi(false),
139 mParaLevel(
140 nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame->StyleContext())),
141 mPrevContent(nullptr)
142 #ifdef DEBUG
143 ,
144 mCurrentBlock(aBlockFrame)
145 #endif
146 {
147 if (mParaLevel > 0) {
148 mRequiresBidi = true;
149 }
150
151 if (mIsVisual) {
152 /**
153 * Drill up in content to detect whether this is an element that needs to
154 * be rendered with logical order even on visual pages.
155 *
156 * We always use logical order on form controls, firstly so that text
157 * entry will be in logical order, but also because visual pages were
158 * written with the assumption that even if the browser had no support
159 * for right-to-left text rendering, it would use native widgets with
160 * bidi support to display form controls.
161 *
162 * We also use logical order in XUL elements, since we expect that if a
163 * XUL element appears in a visual page, it will be generated by an XBL
164 * binding and contain localized text which will be in logical order.
165 */
166 for (nsIContent* content = aBlockFrame->GetContent(); content;
167 content = content->GetParent()) {
168 if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) ||
169 content->IsXULElement()) {
170 mIsVisual = false;
171 break;
172 }
173 }
174 }
175 }
176
SetParaBidiParagraphData177 nsresult SetPara() {
178 return mPresContext->GetBidiEngine().SetPara(mBuffer.get(), BufferLength(),
179 mParaLevel);
180 }
181
182 /**
183 * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
184 * GetParaLevel() returns the actual (resolved) paragraph level which is
185 * always either NSBIDI_LTR or NSBIDI_RTL
186 */
GetParaLevelBidiParagraphData187 nsBidiLevel GetParaLevel() {
188 nsBidiLevel paraLevel = mParaLevel;
189 if (paraLevel == NSBIDI_DEFAULT_LTR || paraLevel == NSBIDI_DEFAULT_RTL) {
190 paraLevel = mPresContext->GetBidiEngine().GetParaLevel();
191 }
192 return paraLevel;
193 }
194
GetDirectionBidiParagraphData195 nsBidiDirection GetDirection() {
196 return mPresContext->GetBidiEngine().GetDirection();
197 }
198
CountRunsBidiParagraphData199 nsresult CountRuns(int32_t* runCount) {
200 return mPresContext->GetBidiEngine().CountRuns(runCount);
201 }
202
GetLogicalRunBidiParagraphData203 void GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
204 nsBidiLevel* aLevel) {
205 mPresContext->GetBidiEngine().GetLogicalRun(aLogicalStart, aLogicalLimit,
206 aLevel);
207 if (mIsVisual) {
208 *aLevel = GetParaLevel();
209 }
210 }
211
ResetDataBidiParagraphData212 void ResetData() {
213 mLogicalFrames.Clear();
214 mLinePerFrame.Clear();
215 mContentToFrameIndex.Clear();
216 mBuffer.SetLength(0);
217 mPrevContent = nullptr;
218 for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
219 mBuffer.Append(mEmbeddingStack[i]);
220 mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
221 mLinePerFrame.AppendElement((nsLineBox*)nullptr);
222 }
223 }
224
AppendFrameBidiParagraphData225 void AppendFrame(nsIFrame* aFrame, nsBlockInFlowLineIterator* aLineIter,
226 nsIContent* aContent = nullptr) {
227 if (aContent) {
228 mContentToFrameIndex.Put(aContent, FrameCount());
229 }
230 mLogicalFrames.AppendElement(aFrame);
231
232 AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame);
233 mLinePerFrame.AppendElement(aLineIter->GetLine().get());
234 }
235
AdvanceAndAppendFrameBidiParagraphData236 void AdvanceAndAppendFrame(nsIFrame** aFrame,
237 nsBlockInFlowLineIterator* aLineIter,
238 nsIFrame** aNextSibling) {
239 nsIFrame* frame = *aFrame;
240 nsIFrame* nextSibling = *aNextSibling;
241
242 frame = frame->GetNextContinuation();
243 if (frame) {
244 AppendFrame(frame, aLineIter, nullptr);
245
246 /*
247 * If we have already overshot the saved next-sibling while
248 * scanning the frame's continuations, advance it.
249 */
250 if (frame == nextSibling) {
251 nextSibling = frame->GetNextSibling();
252 }
253 }
254
255 *aFrame = frame;
256 *aNextSibling = nextSibling;
257 }
258
GetLastFrameForContentBidiParagraphData259 int32_t GetLastFrameForContent(nsIContent* aContent) {
260 int32_t index = 0;
261 mContentToFrameIndex.Get(aContent, &index);
262 return index;
263 }
264
FrameCountBidiParagraphData265 int32_t FrameCount() { return mLogicalFrames.Length(); }
266
BufferLengthBidiParagraphData267 int32_t BufferLength() { return mBuffer.Length(); }
268
FrameAtBidiParagraphData269 nsIFrame* FrameAt(int32_t aIndex) { return mLogicalFrames[aIndex]; }
270
GetLineForFrameAtBidiParagraphData271 nsLineBox* GetLineForFrameAt(int32_t aIndex) { return mLinePerFrame[aIndex]; }
272
AppendUnicharBidiParagraphData273 void AppendUnichar(char16_t aCh) { mBuffer.Append(aCh); }
274
AppendStringBidiParagraphData275 void AppendString(const nsDependentSubstring& aString) {
276 mBuffer.Append(aString);
277 }
278
AppendControlCharBidiParagraphData279 void AppendControlChar(char16_t aCh) {
280 mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
281 mLinePerFrame.AppendElement((nsLineBox*)nullptr);
282 AppendUnichar(aCh);
283 }
284
PushBidiControlBidiParagraphData285 void PushBidiControl(char16_t aCh) {
286 AppendControlChar(aCh);
287 mEmbeddingStack.AppendElement(aCh);
288 }
289
AppendPopCharBidiParagraphData290 void AppendPopChar(char16_t aCh) {
291 AppendControlChar(IsIsolateControl(aCh) ? kPDI : kPDF);
292 }
293
PopBidiControlBidiParagraphData294 void PopBidiControl(char16_t aCh) {
295 MOZ_ASSERT(mEmbeddingStack.Length(), "embedding/override underflow");
296 MOZ_ASSERT(aCh == mEmbeddingStack.LastElement());
297 AppendPopChar(aCh);
298 mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
299 }
300
ClearBidiControlsBidiParagraphData301 void ClearBidiControls() {
302 for (char16_t c : Reversed(mEmbeddingStack)) {
303 AppendPopChar(c);
304 }
305 }
306
IsFrameInCurrentLineBidiParagraphData307 static bool IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
308 nsIFrame* aPrevFrame, nsIFrame* aFrame) {
309 nsIFrame* endFrame = aLineIter->IsLastLineInList()
310 ? nullptr
311 : aLineIter->GetLine().next()->mFirstChild;
312 nsIFrame* startFrame =
313 aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
314 for (nsIFrame* frame = startFrame; frame && frame != endFrame;
315 frame = frame->GetNextSibling()) {
316 if (frame == aFrame) return true;
317 }
318 return false;
319 }
320
AdvanceLineIteratorToFrameBidiParagraphData321 static void AdvanceLineIteratorToFrame(nsIFrame* aFrame,
322 nsBlockInFlowLineIterator* aLineIter,
323 nsIFrame*& aPrevFrame) {
324 // Advance aLine to the line containing aFrame
325 nsIFrame* child = aFrame;
326 nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
327 while (parent && !nsLayoutUtils::GetAsBlock(parent)) {
328 child = parent;
329 parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
330 }
331 NS_ASSERTION(parent, "aFrame is not a descendent of a block frame");
332 while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
333 #ifdef DEBUG
334 bool hasNext =
335 #endif
336 aLineIter->Next();
337 NS_ASSERTION(hasNext, "Can't find frame in lines!");
338 aPrevFrame = nullptr;
339 }
340 aPrevFrame = child;
341 }
342 };
343
344 struct MOZ_STACK_CLASS BidiLineData {
345 AutoTArray<nsIFrame*, 16> mLogicalFrames;
346 AutoTArray<nsIFrame*, 16> mVisualFrames;
347 AutoTArray<int32_t, 16> mIndexMap;
348 AutoTArray<uint8_t, 16> mLevels;
349 bool mIsReordered;
350
BidiLineDataBidiLineData351 BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine) {
352 /**
353 * Initialize the logically-ordered array of frames using the top-level
354 * frames of a single line
355 */
356 bool isReordered = false;
357 bool hasRTLFrames = false;
358 bool hasVirtualControls = false;
359
360 auto appendFrame = [&](nsIFrame* frame, nsBidiLevel level) {
361 mLogicalFrames.AppendElement(frame);
362 mLevels.AppendElement(level);
363 mIndexMap.AppendElement(0);
364 if (IS_LEVEL_RTL(level)) {
365 hasRTLFrames = true;
366 }
367 };
368
369 bool firstFrame = true;
370 for (nsIFrame* frame = aFirstFrameOnLine; frame && aNumFramesOnLine--;
371 frame = frame->GetNextSibling()) {
372 FrameBidiData bidiData = nsBidiPresUtils::GetFrameBidiData(frame);
373 // Ignore virtual control before the first frame. Doing so should
374 // not affect the visual result, but could avoid running into the
375 // stripping code below for many cases.
376 if (!firstFrame && bidiData.precedingControl != kBidiLevelNone) {
377 appendFrame(NS_BIDI_CONTROL_FRAME, bidiData.precedingControl);
378 hasVirtualControls = true;
379 }
380 appendFrame(frame, bidiData.embeddingLevel);
381 firstFrame = false;
382 }
383
384 // Reorder the line
385 nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
386 mIndexMap.Elements());
387
388 // Strip virtual frames
389 if (hasVirtualControls) {
390 auto originalCount = mLogicalFrames.Length();
391 AutoTArray<int32_t, 16> realFrameMap;
392 realFrameMap.SetCapacity(originalCount);
393 size_t count = 0;
394 for (auto i : IntegerRange(originalCount)) {
395 if (mLogicalFrames[i] == NS_BIDI_CONTROL_FRAME) {
396 realFrameMap.AppendElement(-1);
397 } else {
398 mLogicalFrames[count] = mLogicalFrames[i];
399 mLevels[count] = mLevels[i];
400 realFrameMap.AppendElement(count);
401 count++;
402 }
403 }
404 // Only keep index map for real frames.
405 for (size_t i = 0, j = 0; i < originalCount; ++i) {
406 auto newIndex = realFrameMap[mIndexMap[i]];
407 if (newIndex != -1) {
408 mIndexMap[j] = newIndex;
409 j++;
410 }
411 }
412 mLogicalFrames.TruncateLength(count);
413 mLevels.TruncateLength(count);
414 mIndexMap.TruncateLength(count);
415 }
416
417 for (int32_t i = 0; i < FrameCount(); i++) {
418 mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
419 if (i != mIndexMap[i]) {
420 isReordered = true;
421 }
422 }
423
424 // If there's an RTL frame, assume the line is reordered
425 mIsReordered = isReordered || hasRTLFrames;
426 }
427
FrameCountBidiLineData428 int32_t FrameCount() const { return mLogicalFrames.Length(); }
429
LogicalFrameAtBidiLineData430 nsIFrame* LogicalFrameAt(int32_t aIndex) const {
431 return mLogicalFrames[aIndex];
432 }
433
VisualFrameAtBidiLineData434 nsIFrame* VisualFrameAt(int32_t aIndex) const {
435 return mVisualFrames[aIndex];
436 }
437 };
438
439 #ifdef DEBUG
440 extern "C" {
DumpFrameArray(const nsTArray<nsIFrame * > & aFrames)441 void MOZ_EXPORT DumpFrameArray(const nsTArray<nsIFrame*>& aFrames) {
442 for (nsIFrame* frame : aFrames) {
443 if (frame == NS_BIDI_CONTROL_FRAME) {
444 fprintf_stderr(stderr, "(Bidi control frame)\n");
445 } else {
446 frame->List();
447 }
448 }
449 }
450
DumpBidiLine(BidiLineData * aData,bool aVisualOrder)451 void MOZ_EXPORT DumpBidiLine(BidiLineData* aData, bool aVisualOrder) {
452 DumpFrameArray(aVisualOrder ? aData->mVisualFrames : aData->mLogicalFrames);
453 }
454 }
455 #endif
456
457 /* Some helper methods for Resolve() */
458
459 // Should this frame be split between text runs?
IsBidiSplittable(nsIFrame * aFrame)460 static bool IsBidiSplittable(nsIFrame* aFrame) {
461 // Bidi inline containers should be split, unless they're line frames.
462 LayoutFrameType frameType = aFrame->Type();
463 return (aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) &&
464 frameType != LayoutFrameType::Line) ||
465 frameType == LayoutFrameType::Text;
466 }
467
468 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
IsBidiLeaf(nsIFrame * aFrame)469 static bool IsBidiLeaf(nsIFrame* aFrame) {
470 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
471 return !kid || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
472 }
473
474 /**
475 * Create non-fluid continuations for the ancestors of a given frame all the way
476 * up the frame tree until we hit a non-splittable frame (a line or a block).
477 *
478 * @param aParent the first parent frame to be split
479 * @param aFrame the child frames after this frame are reparented to the
480 * newly-created continuation of aParent.
481 * If aFrame is null, all the children of aParent are reparented.
482 */
SplitInlineAncestors(nsContainerFrame * aParent,nsIFrame * aFrame)483 static nsresult SplitInlineAncestors(nsContainerFrame* aParent,
484 nsIFrame* aFrame) {
485 nsPresContext* presContext = aParent->PresContext();
486 nsIPresShell* presShell = presContext->PresShell();
487 nsIFrame* frame = aFrame;
488 nsContainerFrame* parent = aParent;
489 nsContainerFrame* newParent;
490
491 while (IsBidiSplittable(parent)) {
492 nsContainerFrame* grandparent = parent->GetParent();
493 NS_ASSERTION(grandparent,
494 "Couldn't get parent's parent in "
495 "nsBidiPresUtils::SplitInlineAncestors");
496
497 // Split the child list after |frame|, unless it is the last child.
498 if (!frame || frame->GetNextSibling()) {
499 newParent = static_cast<nsContainerFrame*>(
500 presShell->FrameConstructor()->CreateContinuingFrame(
501 presContext, parent, grandparent, false));
502
503 nsFrameList tail = parent->StealFramesAfter(frame);
504
505 // Reparent views as necessary
506 nsresult rv;
507 rv = nsContainerFrame::ReparentFrameViewList(tail, parent, newParent);
508 if (NS_FAILED(rv)) {
509 return rv;
510 }
511
512 // The parent's continuation adopts the siblings after the split.
513 newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nullptr, tail);
514
515 // The list name kNoReflowPrincipalList would indicate we don't want
516 // reflow
517 nsFrameList temp(newParent, newParent);
518 grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp);
519 }
520
521 frame = parent;
522 parent = grandparent;
523 }
524
525 return NS_OK;
526 }
527
MakeContinuationFluid(nsIFrame * aFrame,nsIFrame * aNext)528 static void MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext) {
529 NS_ASSERTION(!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext,
530 "next-in-flow is not next continuation!");
531 aFrame->SetNextInFlow(aNext);
532
533 NS_ASSERTION(!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame,
534 "prev-in-flow is not prev continuation!");
535 aNext->SetPrevInFlow(aFrame);
536 }
537
MakeContinuationsNonFluidUpParentChain(nsIFrame * aFrame,nsIFrame * aNext)538 static void MakeContinuationsNonFluidUpParentChain(nsIFrame* aFrame,
539 nsIFrame* aNext) {
540 nsIFrame* frame;
541 nsIFrame* next;
542
543 for (frame = aFrame, next = aNext;
544 frame && next && next != frame && next == frame->GetNextInFlow() &&
545 IsBidiSplittable(frame);
546 frame = frame->GetParent(), next = next->GetParent()) {
547 frame->SetNextContinuation(next);
548 next->SetPrevContinuation(frame);
549 }
550 }
551
552 // If aFrame is the last child of its parent, convert bidi continuations to
553 // fluid continuations for all of its inline ancestors.
554 // If it isn't the last child, make sure that its continuation is fluid.
JoinInlineAncestors(nsIFrame * aFrame)555 static void JoinInlineAncestors(nsIFrame* aFrame) {
556 nsIFrame* frame = aFrame;
557 do {
558 nsIFrame* next = frame->GetNextContinuation();
559 if (next) {
560 MakeContinuationFluid(frame, next);
561 }
562 // Join the parent only as long as we're its last child.
563 if (frame->GetNextSibling()) break;
564 frame = frame->GetParent();
565 } while (frame && IsBidiSplittable(frame));
566 }
567
CreateContinuation(nsIFrame * aFrame,nsIFrame ** aNewFrame,bool aIsFluid)568 static nsresult CreateContinuation(nsIFrame* aFrame, nsIFrame** aNewFrame,
569 bool aIsFluid) {
570 NS_PRECONDITION(aNewFrame, "null OUT ptr");
571 NS_PRECONDITION(aFrame, "null ptr");
572
573 *aNewFrame = nullptr;
574
575 nsPresContext* presContext = aFrame->PresContext();
576 nsIPresShell* presShell = presContext->PresShell();
577 NS_ASSERTION(presShell,
578 "PresShell must be set on PresContext before calling "
579 "nsBidiPresUtils::CreateContinuation");
580
581 nsContainerFrame* parent = aFrame->GetParent();
582 NS_ASSERTION(
583 parent,
584 "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
585
586 nsresult rv = NS_OK;
587
588 // Have to special case floating first letter frames because the continuation
589 // doesn't go in the first letter frame. The continuation goes with the rest
590 // of the text that the first letter frame was made out of.
591 if (parent->IsLetterFrame() && parent->IsFloating()) {
592 nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
593 rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
594 aNewFrame, aIsFluid);
595 return rv;
596 }
597
598 *aNewFrame = presShell->FrameConstructor()->CreateContinuingFrame(
599 presContext, aFrame, parent, aIsFluid);
600
601 // The list name kNoReflowPrincipalList would indicate we don't want reflow
602 // XXXbz this needs higher-level framelist love
603 nsFrameList temp(*aNewFrame, *aNewFrame);
604 parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp);
605
606 if (!aIsFluid) {
607 // Split inline ancestor frames
608 rv = SplitInlineAncestors(parent, aFrame);
609 if (NS_FAILED(rv)) {
610 return rv;
611 }
612 }
613
614 return NS_OK;
615 }
616
617 /*
618 * Overview of the implementation of Resolve():
619 *
620 * Walk through the descendants of aBlockFrame and build:
621 * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
622 * * mBuffer: an nsString containing a representation of
623 * the content of the frames.
624 * In the case of text frames, this is the actual text context of the
625 * frames, but some other elements are represented in a symbolic form which
626 * will make the Unicode Bidi Algorithm give the correct results.
627 * Bidi isolates, embeddings, and overrides set by CSS, <bdi>, or <bdo>
628 * elements are represented by the corresponding Unicode control characters.
629 * <br> elements are represented by U+2028 LINE SEPARATOR
630 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
631 * CHARACTER
632 *
633 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
634 * by nsBidi::SetPara() and division into directional runs by
635 * nsBidi::CountRuns().
636 *
637 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
638 * correlate them with the frames indexed in mLogicalFrames, setting the
639 * baseLevel and embeddingLevel properties according to the results returned
640 * by the Bidi engine.
641 *
642 * The rendering layer requires each text frame to contain text in only one
643 * direction, so we may need to call EnsureBidiContinuation() to split frames.
644 * We may also need to call RemoveBidiContinuation() to convert frames created
645 * by EnsureBidiContinuation() in previous reflows into fluid continuations.
646 */
Resolve(nsBlockFrame * aBlockFrame)647 nsresult nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) {
648 BidiParagraphData bpd(aBlockFrame);
649
650 // Handle bidi-override being set on the block itself before calling
651 // TraverseFrames.
652 // No need to call GetBidiControl as well, because isolate and embed
653 // values of unicode-bidi property are redundant on block elements.
654 // unicode-bidi:plaintext on a block element is handled by block frame
655 // via using nsIFrame::GetWritingMode(nsIFrame*).
656 char16_t ch = GetBidiOverride(aBlockFrame->StyleContext());
657 if (ch != 0) {
658 bpd.PushBidiControl(ch);
659 bpd.mRequiresBidi = true;
660 } else {
661 // If there are no unicode-bidi properties and no RTL characters in the
662 // block's content, then it is pure LTR and we can skip the rest of bidi
663 // resolution.
664 nsIContent* currContent = nullptr;
665 for (nsBlockFrame* block = aBlockFrame; block;
666 block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
667 block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
668 if (!bpd.mRequiresBidi &&
669 ChildListMayRequireBidi(block->PrincipalChildList().FirstChild(),
670 &currContent)) {
671 bpd.mRequiresBidi = true;
672 }
673 if (!bpd.mRequiresBidi) {
674 nsBlockFrame::FrameLines* overflowLines = block->GetOverflowLines();
675 if (overflowLines) {
676 if (ChildListMayRequireBidi(overflowLines->mFrames.FirstChild(),
677 &currContent)) {
678 bpd.mRequiresBidi = true;
679 }
680 }
681 }
682 }
683 if (!bpd.mRequiresBidi) {
684 return NS_OK;
685 }
686 }
687
688 for (nsBlockFrame* block = aBlockFrame; block;
689 block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
690 #ifdef DEBUG
691 bpd.mCurrentBlock = block;
692 #endif
693 block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
694 nsBlockInFlowLineIterator it(block, block->LinesBegin());
695 bpd.mPrevFrame = nullptr;
696 TraverseFrames(&it, block->PrincipalChildList().FirstChild(), &bpd);
697 nsBlockFrame::FrameLines* overflowLines = block->GetOverflowLines();
698 if (overflowLines) {
699 nsBlockInFlowLineIterator it(block, overflowLines->mLines.begin(), true);
700 bpd.mPrevFrame = nullptr;
701 TraverseFrames(&it, overflowLines->mFrames.FirstChild(), &bpd);
702 }
703 }
704
705 if (ch != 0) {
706 bpd.PopBidiControl(ch);
707 }
708
709 return ResolveParagraph(&bpd);
710 }
711
ResolveParagraph(BidiParagraphData * aBpd)712 nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
713 if (aBpd->BufferLength() < 1) {
714 return NS_OK;
715 }
716 aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
717
718 int32_t runCount;
719
720 nsresult rv = aBpd->SetPara();
721 NS_ENSURE_SUCCESS(rv, rv);
722
723 nsBidiLevel embeddingLevel = aBpd->GetParaLevel();
724
725 rv = aBpd->CountRuns(&runCount);
726 NS_ENSURE_SUCCESS(rv, rv);
727
728 int32_t runLength = 0; // the length of the current run of text
729 int32_t logicalLimit = 0; // the end of the current run + 1
730 int32_t numRun = -1;
731 int32_t fragmentLength = 0; // the length of the current text frame
732 int32_t frameIndex = -1; // index to the frames in mLogicalFrames
733 int32_t frameCount = aBpd->FrameCount();
734 int32_t contentOffset = 0; // offset of current frame in its content node
735 bool isTextFrame = false;
736 nsIFrame* frame = nullptr;
737 nsIContent* content = nullptr;
738 int32_t contentTextLength = 0;
739
740 nsLineBox* currentLine = nullptr;
741
742 #ifdef DEBUG
743 #ifdef NOISY_BIDI
744 printf(
745 "Before Resolve(), mCurrentBlock=%p, mBuffer='%s', frameCount=%d, "
746 "runCount=%d\n",
747 (void*)aBpd->mCurrentBlock, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(),
748 frameCount, runCount);
749 #ifdef REALLY_NOISY_BIDI
750 printf(" block frame tree=:\n");
751 aBpd->mCurrentBlock->List(stdout, 0);
752 #endif
753 #endif
754 #endif
755
756 if (runCount == 1 && frameCount == 1 && aBpd->GetDirection() == NSBIDI_LTR &&
757 aBpd->GetParaLevel() == 0) {
758 // We have a single left-to-right frame in a left-to-right paragraph,
759 // without bidi isolation from the surrounding text.
760 // Make sure that the embedding level and base level frame properties aren't
761 // set (because if they are this frame used to have some other direction,
762 // so we can't do this optimization), and we're done.
763 nsIFrame* frame = aBpd->FrameAt(0);
764 if (frame != NS_BIDI_CONTROL_FRAME) {
765 FrameBidiData bidiData = frame->GetBidiData();
766 if (!bidiData.embeddingLevel && !bidiData.baseLevel) {
767 #ifdef DEBUG
768 #ifdef NOISY_BIDI
769 printf("early return for single direction frame %p\n", (void*)frame);
770 #endif
771 #endif
772 frame->AddStateBits(NS_FRAME_IS_BIDI);
773 return NS_OK;
774 }
775 }
776 }
777
778 nsIFrame* lastRealFrame = nullptr;
779 nsBidiLevel lastEmbeddingLevel = kBidiLevelNone;
780 nsBidiLevel precedingControl = kBidiLevelNone;
781
782 auto storeBidiDataToFrame = [&]() {
783 FrameBidiData bidiData;
784 bidiData.embeddingLevel = embeddingLevel;
785 bidiData.baseLevel = aBpd->GetParaLevel();
786 // If a control character doesn't have a lower embedding level than
787 // both the preceding and the following frame, it isn't something
788 // needed for getting the correct result. This optimization should
789 // remove almost all of embeds and overrides, and some of isolates.
790 if (precedingControl >= embeddingLevel ||
791 precedingControl >= lastEmbeddingLevel) {
792 bidiData.precedingControl = kBidiLevelNone;
793 } else {
794 bidiData.precedingControl = precedingControl;
795 }
796 precedingControl = kBidiLevelNone;
797 lastEmbeddingLevel = embeddingLevel;
798 frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
799 };
800
801 for (;;) {
802 if (fragmentLength <= 0) {
803 // Get the next frame from mLogicalFrames
804 if (++frameIndex >= frameCount) {
805 break;
806 }
807 frame = aBpd->FrameAt(frameIndex);
808 if (frame == NS_BIDI_CONTROL_FRAME || !frame->IsTextFrame()) {
809 /*
810 * Any non-text frame corresponds to a single character in the text
811 * buffer (a bidi control character, LINE SEPARATOR, or OBJECT
812 * SUBSTITUTE)
813 */
814 isTextFrame = false;
815 fragmentLength = 1;
816 } else {
817 currentLine = aBpd->GetLineForFrameAt(frameIndex);
818 content = frame->GetContent();
819 if (!content) {
820 rv = NS_OK;
821 break;
822 }
823 contentTextLength = content->TextLength();
824 int32_t start, end;
825 frame->GetOffsets(start, end);
826 NS_ASSERTION(!(contentTextLength < end - start),
827 "Frame offsets don't fit in content");
828 fragmentLength = std::min(contentTextLength, end - start);
829 contentOffset = start;
830 isTextFrame = true;
831 }
832 } // if (fragmentLength <= 0)
833
834 if (runLength <= 0) {
835 // Get the next run of text from the Bidi engine
836 if (++numRun >= runCount) {
837 // We've run out of runs of text; but don't forget to store bidi data
838 // to the frame before breaking out of the loop (bug 1426042).
839 storeBidiDataToFrame();
840 if (isTextFrame) {
841 frame->AdjustOffsetsForBidi(contentOffset,
842 contentOffset + fragmentLength);
843 }
844 break;
845 }
846 int32_t lineOffset = logicalLimit;
847 aBpd->GetLogicalRun(lineOffset, &logicalLimit, &embeddingLevel);
848 runLength = logicalLimit - lineOffset;
849 } // if (runLength <= 0)
850
851 if (frame == NS_BIDI_CONTROL_FRAME) {
852 // In theory, we only need to do this for isolates. However, it is
853 // easier to do this for all here because we do not maintain the
854 // index to get corresponding character from buffer. Since we do
855 // have proper embedding level for all those characters, including
856 // them wouldn't affect the final result.
857 precedingControl = std::min(precedingControl, embeddingLevel);
858 } else {
859 storeBidiDataToFrame();
860 if (isTextFrame) {
861 if (contentTextLength == 0) {
862 // Set the base level and embedding level of the current run even
863 // on an empty frame. Otherwise frame reordering will not be correct.
864 frame->AdjustOffsetsForBidi(0, 0);
865 // Nothing more to do for an empty frame.
866 continue;
867 }
868 if ((runLength > 0) && (runLength < fragmentLength)) {
869 /*
870 * The text in this frame continues beyond the end of this directional
871 * run. Create a non-fluid continuation frame for the next directional
872 * run.
873 */
874 currentLine->MarkDirty();
875 nsIFrame* nextBidi;
876 int32_t runEnd = contentOffset + runLength;
877 rv = EnsureBidiContinuation(frame, &nextBidi, contentOffset, runEnd);
878 if (NS_FAILED(rv)) {
879 break;
880 }
881 nextBidi->AdjustOffsetsForBidi(runEnd,
882 contentOffset + fragmentLength);
883 frame = nextBidi;
884 contentOffset = runEnd;
885 } // if (runLength < fragmentLength)
886 else {
887 if (contentOffset + fragmentLength == contentTextLength) {
888 /*
889 * We have finished all the text in this content node. Convert any
890 * further non-fluid continuations to fluid continuations and
891 * advance frameIndex to the last frame in the content node
892 */
893 int32_t newIndex = aBpd->GetLastFrameForContent(content);
894 if (newIndex > frameIndex) {
895 currentLine->MarkDirty();
896 RemoveBidiContinuation(aBpd, frame, frameIndex, newIndex);
897 frameIndex = newIndex;
898 frame = aBpd->FrameAt(frameIndex);
899 }
900 } else if (fragmentLength > 0 && runLength > fragmentLength) {
901 /*
902 * There is more text that belongs to this directional run in the
903 * next text frame: make sure it is a fluid continuation of the
904 * current frame. Do not advance frameIndex, because the next frame
905 * may contain multi-directional text and need to be split
906 */
907 int32_t newIndex = frameIndex;
908 do {
909 } while (++newIndex < frameCount &&
910 aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME);
911 if (newIndex < frameCount) {
912 currentLine->MarkDirty();
913 RemoveBidiContinuation(aBpd, frame, frameIndex, newIndex);
914 }
915 } else if (runLength == fragmentLength) {
916 /*
917 * If the directional run ends at the end of the frame, make sure
918 * that any continuation is non-fluid, and do the same up the
919 * parent chain
920 */
921 nsIFrame* next = frame->GetNextInFlow();
922 if (next) {
923 currentLine->MarkDirty();
924 MakeContinuationsNonFluidUpParentChain(frame, next);
925 }
926 }
927 frame->AdjustOffsetsForBidi(contentOffset,
928 contentOffset + fragmentLength);
929 }
930 } // isTextFrame
931 } // not bidi control frame
932 int32_t temp = runLength;
933 runLength -= fragmentLength;
934 fragmentLength -= temp;
935
936 // Record last real frame so that we can do splitting properly even
937 // if a run ends after a virtual bidi control frame.
938 if (frame != NS_BIDI_CONTROL_FRAME) {
939 lastRealFrame = frame;
940 }
941 if (lastRealFrame && fragmentLength <= 0) {
942 // If the frame is at the end of a run, and this is not the end of our
943 // paragraph, split all ancestor inlines that need splitting.
944 // To determine whether we're at the end of the run, we check that we've
945 // finished processing the current run, and that the current frame
946 // doesn't have a fluid continuation (it could have a fluid continuation
947 // of zero length, so testing runLength alone is not sufficient).
948 if (runLength <= 0 && !lastRealFrame->GetNextInFlow()) {
949 if (numRun + 1 < runCount) {
950 nsIFrame* child = lastRealFrame;
951 nsContainerFrame* parent = child->GetParent();
952 // As long as we're on the last sibling, the parent doesn't have to
953 // be split.
954 // However, if the parent has a fluid continuation, we do have to make
955 // it non-fluid. This can happen e.g. when we have a first-letter
956 // frame and the end of the first-letter coincides with the end of a
957 // directional run.
958 while (parent && IsBidiSplittable(parent) &&
959 !child->GetNextSibling()) {
960 nsIFrame* next = parent->GetNextInFlow();
961 if (next) {
962 parent->SetNextContinuation(next);
963 next->SetPrevContinuation(parent);
964 }
965 child = parent;
966 parent = child->GetParent();
967 }
968 if (parent && IsBidiSplittable(parent)) {
969 SplitInlineAncestors(parent, child);
970 }
971 }
972 } else if (frame != NS_BIDI_CONTROL_FRAME) {
973 // We're not at an end of a run. If |frame| is the last child of its
974 // parent, and its ancestors happen to have bidi continuations, convert
975 // them into fluid continuations.
976 JoinInlineAncestors(frame);
977 }
978 }
979 } // for
980
981 #ifdef DEBUG
982 #ifdef REALLY_NOISY_BIDI
983 printf("---\nAfter Resolve(), frameTree =:\n");
984 aBpd->mCurrentBlock->List(stdout, 0);
985 printf("===\n");
986 #endif
987 #endif
988
989 return rv;
990 }
991
TraverseFrames(nsBlockInFlowLineIterator * aLineIter,nsIFrame * aCurrentFrame,BidiParagraphData * aBpd)992 void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
993 nsIFrame* aCurrentFrame,
994 BidiParagraphData* aBpd) {
995 if (!aCurrentFrame) return;
996
997 #ifdef DEBUG
998 nsBlockFrame* initialLineContainer = aLineIter->GetContainer();
999 #endif
1000
1001 nsIFrame* childFrame = aCurrentFrame;
1002 do {
1003 /*
1004 * It's important to get the next sibling and next continuation *before*
1005 * handling the frame: If we encounter a forced paragraph break and call
1006 * ResolveParagraph within this loop, doing GetNextSibling and
1007 * GetNextContinuation after that could return a bidi continuation that had
1008 * just been split from the original childFrame and we would process it
1009 * twice.
1010 */
1011 nsIFrame* nextSibling = childFrame->GetNextSibling();
1012
1013 // If the real frame for a placeholder is a first letter frame, we need to
1014 // drill down into it and include its contents in Bidi resolution.
1015 // If not, we just use the placeholder.
1016 nsIFrame* frame = childFrame;
1017 if (childFrame->IsPlaceholderFrame()) {
1018 nsIFrame* realFrame =
1019 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
1020 if (realFrame->IsLetterFrame()) {
1021 frame = realFrame;
1022 }
1023 }
1024
1025 auto DifferentBidiValues = [](nsStyleContext* aSC1, nsIFrame* aFrame2) {
1026 nsStyleContext* sc2 = aFrame2->StyleContext();
1027 return GetBidiControl(aSC1) != GetBidiControl(sc2) ||
1028 GetBidiOverride(aSC1) != GetBidiOverride(sc2);
1029 };
1030
1031 nsStyleContext* sc = frame->StyleContext();
1032 nsIFrame* nextContinuation = frame->GetNextContinuation();
1033 nsIFrame* prevContinuation = frame->GetPrevContinuation();
1034 bool isLastFrame =
1035 !nextContinuation || DifferentBidiValues(sc, nextContinuation);
1036 bool isFirstFrame =
1037 !prevContinuation || DifferentBidiValues(sc, prevContinuation);
1038
1039 char16_t controlChar = 0;
1040 char16_t overrideChar = 0;
1041 if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
1042 if (!(frame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1043 nsContainerFrame* c = static_cast<nsContainerFrame*>(frame);
1044 MOZ_ASSERT(c == do_QueryFrame(frame),
1045 "eBidiInlineContainer must be a nsContainerFrame subclass");
1046 c->DrainSelfOverflowList();
1047 }
1048
1049 controlChar = GetBidiControl(sc);
1050 overrideChar = GetBidiOverride(sc);
1051
1052 // Add dummy frame pointers representing bidi control codes before
1053 // the first frames of elements specifying override, isolation, or
1054 // plaintext.
1055 if (isFirstFrame) {
1056 if (controlChar != 0) {
1057 aBpd->PushBidiControl(controlChar);
1058 }
1059 if (overrideChar != 0) {
1060 aBpd->PushBidiControl(overrideChar);
1061 }
1062 }
1063 }
1064
1065 if (IsBidiLeaf(frame)) {
1066 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
1067 * and add its index to the mContentToFrameIndex hashtable. This
1068 * will be used in RemoveBidiContinuation() to identify the last
1069 * frame in the array with a given content.
1070 */
1071 nsIContent* content = frame->GetContent();
1072 aBpd->AppendFrame(frame, aLineIter, content);
1073
1074 // Append the content of the frame to the paragraph buffer
1075 LayoutFrameType frameType = frame->Type();
1076 if (LayoutFrameType::Text == frameType) {
1077 if (content != aBpd->mPrevContent) {
1078 aBpd->mPrevContent = content;
1079 if (!frame->StyleText()->NewlineIsSignificant(
1080 static_cast<nsTextFrame*>(frame))) {
1081 content->AppendTextTo(aBpd->mBuffer);
1082 } else {
1083 /*
1084 * For preformatted text we have to do bidi resolution on each line
1085 * separately.
1086 */
1087 nsAutoString text;
1088 content->AppendTextTo(text);
1089 nsIFrame* next;
1090 do {
1091 next = nullptr;
1092
1093 int32_t start, end;
1094 frame->GetOffsets(start, end);
1095 int32_t endLine = text.FindChar('\n', start);
1096 if (endLine == -1) {
1097 /*
1098 * If there is no newline in the text content, just save the
1099 * text from this frame and its continuations, and do bidi
1100 * resolution later
1101 */
1102 aBpd->AppendString(Substring(text, start));
1103 while (frame && nextSibling) {
1104 aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1105 }
1106 break;
1107 }
1108
1109 /*
1110 * If there is a newline in the frame, break the frame after the
1111 * newline, do bidi resolution and repeat until the last sibling
1112 */
1113 ++endLine;
1114
1115 /*
1116 * If the frame ends before the new line, save the text and move
1117 * into the next continuation
1118 */
1119 aBpd->AppendString(
1120 Substring(text, start, std::min(end, endLine) - start));
1121 while (end < endLine && nextSibling) {
1122 aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1123 NS_ASSERTION(frame, "Premature end of continuation chain");
1124 frame->GetOffsets(start, end);
1125 aBpd->AppendString(
1126 Substring(text, start, std::min(end, endLine) - start));
1127 }
1128
1129 if (end < endLine) {
1130 aBpd->mPrevContent = nullptr;
1131 break;
1132 }
1133
1134 bool createdContinuation = false;
1135 if (uint32_t(endLine) < text.Length()) {
1136 /*
1137 * Timing is everything here: if the frame already has a bidi
1138 * continuation, we need to make the continuation fluid *before*
1139 * resetting the length of the current frame. Otherwise
1140 * nsTextFrame::SetLength won't set the continuation frame's
1141 * text offsets correctly.
1142 *
1143 * On the other hand, if the frame doesn't have a continuation,
1144 * we need to create one *after* resetting the length, or
1145 * CreateContinuingFrame will complain that there is no more
1146 * content for the continuation.
1147 */
1148 next = frame->GetNextInFlow();
1149 if (!next) {
1150 // If the frame already has a bidi continuation, make it fluid
1151 next = frame->GetNextContinuation();
1152 if (next) {
1153 MakeContinuationFluid(frame, next);
1154 JoinInlineAncestors(frame);
1155 }
1156 }
1157
1158 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1159 textFrame->SetLength(endLine - start, nullptr);
1160
1161 if (!next) {
1162 // If the frame has no next in flow, create one.
1163 CreateContinuation(frame, &next, true);
1164 createdContinuation = true;
1165 }
1166 // Mark the line before the newline as dirty.
1167 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
1168 }
1169 ResolveParagraphWithinBlock(aBpd);
1170
1171 if (!nextSibling && !createdContinuation) {
1172 break;
1173 } else if (next) {
1174 frame = next;
1175 aBpd->AppendFrame(frame, aLineIter);
1176 // Mark the line after the newline as dirty.
1177 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
1178 }
1179
1180 /*
1181 * If we have already overshot the saved next-sibling while
1182 * scanning the frame's continuations, advance it.
1183 */
1184 if (frame && frame == nextSibling) {
1185 nextSibling = frame->GetNextSibling();
1186 }
1187
1188 } while (next);
1189 }
1190 }
1191 } else if (LayoutFrameType::Br == frameType) {
1192 // break frame -- append line separator
1193 aBpd->AppendUnichar(kLineSeparator);
1194 ResolveParagraphWithinBlock(aBpd);
1195 } else {
1196 // other frame type -- see the Unicode Bidi Algorithm:
1197 // "...inline objects (such as graphics) are treated as if they are ...
1198 // U+FFFC"
1199 // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
1200 // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
1201 aBpd->AppendUnichar(
1202 content->IsHTMLElement(nsGkAtoms::wbr) ? kZWSP : kObjectSubstitute);
1203 if (!frame->IsInlineOutside()) {
1204 // if it is not inline, end the paragraph
1205 ResolveParagraphWithinBlock(aBpd);
1206 }
1207 }
1208 } else {
1209 // For a non-leaf frame, recurse into TraverseFrames
1210 nsIFrame* kid = frame->PrincipalChildList().FirstChild();
1211 MOZ_ASSERT(!frame->GetChildList(nsIFrame::kOverflowList).FirstChild(),
1212 "should have drained the overflow list above");
1213 if (kid) {
1214 TraverseFrames(aLineIter, kid, aBpd);
1215 }
1216 }
1217
1218 // If the element is attributed by dir, indicate direction pop (add PDF
1219 // frame)
1220 if (isLastFrame) {
1221 // Add a dummy frame pointer representing a bidi control code after the
1222 // last frame of an element specifying embedding or override
1223 if (overrideChar != 0) {
1224 aBpd->PopBidiControl(overrideChar);
1225 }
1226 if (controlChar != 0) {
1227 aBpd->PopBidiControl(controlChar);
1228 }
1229 }
1230 childFrame = nextSibling;
1231 } while (childFrame);
1232
1233 MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer());
1234 }
1235
ChildListMayRequireBidi(nsIFrame * aFirstChild,nsIContent ** aCurrContent)1236 bool nsBidiPresUtils::ChildListMayRequireBidi(nsIFrame* aFirstChild,
1237 nsIContent** aCurrContent) {
1238 MOZ_ASSERT(!aFirstChild || !aFirstChild->GetPrevSibling(),
1239 "Expecting to traverse from the start of a child list");
1240
1241 for (nsIFrame* childFrame = aFirstChild; childFrame;
1242 childFrame = childFrame->GetNextSibling()) {
1243 nsIFrame* frame = childFrame;
1244
1245 // If the real frame for a placeholder is a first-letter frame, we need to
1246 // consider its contents for potential Bidi resolution.
1247 if (childFrame->IsPlaceholderFrame()) {
1248 nsIFrame* realFrame =
1249 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
1250 if (realFrame->IsLetterFrame()) {
1251 frame = realFrame;
1252 }
1253 }
1254
1255 // If unicode-bidi properties are present, we should do bidi resolution.
1256 nsStyleContext* sc = frame->StyleContext();
1257 if (GetBidiControl(sc) || GetBidiOverride(sc)) {
1258 return true;
1259 }
1260
1261 if (IsBidiLeaf(frame)) {
1262 if (frame->IsTextFrame()) {
1263 // If the frame already has a BidiDataProperty, we know we need to
1264 // perform bidi resolution (even if no bidi content is NOW present --
1265 // we might need to remove the property set by a previous reflow, if
1266 // content has changed; see bug 1366623).
1267 if (frame->HasProperty(nsIFrame::BidiDataProperty())) {
1268 return true;
1269 }
1270
1271 // Check whether the text frame has any RTL characters; if so, bidi
1272 // resolution will be needed.
1273 nsIContent* content = frame->GetContent();
1274 if (content != *aCurrContent) {
1275 *aCurrContent = content;
1276 const nsTextFragment* txt = content->GetText();
1277 if (txt->Is2b() &&
1278 HasRTLChars(MakeSpan(txt->Get2b(), txt->GetLength()))) {
1279 return true;
1280 }
1281 }
1282 }
1283 } else if (ChildListMayRequireBidi(frame->PrincipalChildList().FirstChild(),
1284 aCurrContent)) {
1285 return true;
1286 }
1287 }
1288
1289 return false;
1290 }
1291
ResolveParagraphWithinBlock(BidiParagraphData * aBpd)1292 void nsBidiPresUtils::ResolveParagraphWithinBlock(BidiParagraphData* aBpd) {
1293 aBpd->ClearBidiControls();
1294 ResolveParagraph(aBpd);
1295 aBpd->ResetData();
1296 }
1297
ReorderFrames(nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine,WritingMode aLineWM,const nsSize & aContainerSize,nscoord aStart)1298 /* static */ nscoord nsBidiPresUtils::ReorderFrames(
1299 nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine, WritingMode aLineWM,
1300 const nsSize& aContainerSize, nscoord aStart) {
1301 nsSize containerSize(aContainerSize);
1302
1303 // If this line consists of a line frame, reorder the line frame's children.
1304 if (aFirstFrameOnLine->IsLineFrame()) {
1305 // The line frame is positioned at the start-edge, so use its size
1306 // as the container size.
1307 containerSize = aFirstFrameOnLine->GetSize();
1308
1309 aFirstFrameOnLine = aFirstFrameOnLine->PrincipalChildList().FirstChild();
1310 if (!aFirstFrameOnLine) {
1311 return 0;
1312 }
1313 // All children of the line frame are on the first line. Setting
1314 // aNumFramesOnLine to -1 makes InitLogicalArrayFromLine look at all of
1315 // them.
1316 aNumFramesOnLine = -1;
1317 }
1318
1319 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1320 return RepositionInlineFrames(&bld, aLineWM, containerSize, aStart);
1321 }
1322
GetFirstLeaf(nsIFrame * aFrame)1323 nsIFrame* nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame) {
1324 nsIFrame* firstLeaf = aFrame;
1325 while (!IsBidiLeaf(firstLeaf)) {
1326 nsIFrame* firstChild = firstLeaf->PrincipalChildList().FirstChild();
1327 nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild);
1328 firstLeaf = (realFrame->IsLetterFrame()) ? realFrame : firstChild;
1329 }
1330 return firstLeaf;
1331 }
1332
GetFrameBidiData(nsIFrame * aFrame)1333 FrameBidiData nsBidiPresUtils::GetFrameBidiData(nsIFrame* aFrame) {
1334 return GetFirstLeaf(aFrame)->GetBidiData();
1335 }
1336
GetFrameEmbeddingLevel(nsIFrame * aFrame)1337 nsBidiLevel nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame) {
1338 return GetFirstLeaf(aFrame)->GetEmbeddingLevel();
1339 }
1340
GetFrameBaseLevel(nsIFrame * aFrame)1341 nsBidiLevel nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame) {
1342 nsIFrame* firstLeaf = aFrame;
1343 while (!IsBidiLeaf(firstLeaf)) {
1344 firstLeaf = firstLeaf->PrincipalChildList().FirstChild();
1345 }
1346 return firstLeaf->GetBaseLevel();
1347 }
1348
IsFirstOrLast(nsIFrame * aFrame,const nsContinuationStates * aContinuationStates,bool aSpanDirMatchesLineDir,bool & aIsFirst,bool & aIsLast)1349 void nsBidiPresUtils::IsFirstOrLast(
1350 nsIFrame* aFrame, const nsContinuationStates* aContinuationStates,
1351 bool aSpanDirMatchesLineDir, bool& aIsFirst /* out */,
1352 bool& aIsLast /* out */) {
1353 /*
1354 * Since we lay out frames in the line's direction, visiting a frame with
1355 * 'mFirstVisualFrame == nullptr', means it's the first appearance of one
1356 * of its continuation chain frames on the line.
1357 * To determine if it's the last visual frame of its continuation chain on
1358 * the line or not, we count the number of frames of the chain on the line,
1359 * and then reduce it when we lay out a frame of the chain. If this value
1360 * becomes 1 it means that it's the last visual frame of its continuation
1361 * chain on this line.
1362 */
1363
1364 bool firstInLineOrder, lastInLineOrder;
1365 nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
1366 nsFrameContinuationState* firstFrameState;
1367
1368 if (!frameState->mFirstVisualFrame) {
1369 // aFrame is the first visual frame of its continuation chain
1370 nsFrameContinuationState* contState;
1371 nsIFrame* frame;
1372
1373 frameState->mFrameCount = 1;
1374 frameState->mFirstVisualFrame = aFrame;
1375
1376 /**
1377 * Traverse continuation chain of aFrame in both backward and forward
1378 * directions while the frames are on this line. Count the frames and
1379 * set their mFirstVisualFrame to aFrame.
1380 */
1381 // Traverse continuation chain backward
1382 for (frame = aFrame->GetPrevContinuation();
1383 frame && (contState = aContinuationStates->GetEntry(frame));
1384 frame = frame->GetPrevContinuation()) {
1385 frameState->mFrameCount++;
1386 contState->mFirstVisualFrame = aFrame;
1387 }
1388 frameState->mHasContOnPrevLines = (frame != nullptr);
1389
1390 // Traverse continuation chain forward
1391 for (frame = aFrame->GetNextContinuation();
1392 frame && (contState = aContinuationStates->GetEntry(frame));
1393 frame = frame->GetNextContinuation()) {
1394 frameState->mFrameCount++;
1395 contState->mFirstVisualFrame = aFrame;
1396 }
1397 frameState->mHasContOnNextLines = (frame != nullptr);
1398
1399 firstInLineOrder = true;
1400 firstFrameState = frameState;
1401 } else {
1402 // aFrame is not the first visual frame of its continuation chain
1403 firstInLineOrder = false;
1404 firstFrameState =
1405 aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
1406 }
1407
1408 lastInLineOrder = (firstFrameState->mFrameCount == 1);
1409
1410 if (aSpanDirMatchesLineDir) {
1411 aIsFirst = firstInLineOrder;
1412 aIsLast = lastInLineOrder;
1413 } else {
1414 aIsFirst = lastInLineOrder;
1415 aIsLast = firstInLineOrder;
1416 }
1417
1418 if (frameState->mHasContOnPrevLines) {
1419 aIsFirst = false;
1420 }
1421 if (firstFrameState->mHasContOnNextLines) {
1422 aIsLast = false;
1423 }
1424
1425 if ((aIsFirst || aIsLast) &&
1426 (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1427 // For ib splits, don't treat anything except the last part as
1428 // endmost or anything except the first part as startmost.
1429 // As an optimization, only get the first continuation once.
1430 nsIFrame* firstContinuation = aFrame->FirstContinuation();
1431 if (firstContinuation->FrameIsNonLastInIBSplit()) {
1432 // We are not endmost
1433 aIsLast = false;
1434 }
1435 if (firstContinuation->FrameIsNonFirstInIBSplit()) {
1436 // We are not startmost
1437 aIsFirst = false;
1438 }
1439 }
1440
1441 // Reduce number of remaining frames of the continuation chain on the line.
1442 firstFrameState->mFrameCount--;
1443
1444 nsInlineFrame* testFrame = do_QueryFrame(aFrame);
1445
1446 if (testFrame) {
1447 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
1448
1449 if (aIsFirst) {
1450 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
1451 } else {
1452 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
1453 }
1454
1455 if (aIsLast) {
1456 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
1457 } else {
1458 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
1459 }
1460 }
1461 }
1462
RepositionRubyContentFrame(nsIFrame * aFrame,WritingMode aFrameWM,const LogicalMargin & aBorderPadding)1463 /* static */ void nsBidiPresUtils::RepositionRubyContentFrame(
1464 nsIFrame* aFrame, WritingMode aFrameWM,
1465 const LogicalMargin& aBorderPadding) {
1466 const nsFrameList& childList = aFrame->PrincipalChildList();
1467 if (childList.IsEmpty()) {
1468 return;
1469 }
1470
1471 // Reorder the children.
1472 // XXX It currently doesn't work properly because we do not
1473 // resolve frames inside ruby content frames.
1474 nscoord isize =
1475 ReorderFrames(childList.FirstChild(), childList.GetLength(), aFrameWM,
1476 aFrame->GetSize(), aBorderPadding.IStart(aFrameWM));
1477 isize += aBorderPadding.IEnd(aFrameWM);
1478
1479 if (aFrame->StyleText()->mRubyAlign == NS_STYLE_RUBY_ALIGN_START) {
1480 return;
1481 }
1482 nscoord residualISize = aFrame->ISize(aFrameWM) - isize;
1483 if (residualISize <= 0) {
1484 return;
1485 }
1486
1487 // When ruby-align is not "start", if the content does not fill this
1488 // frame, we need to center the children.
1489 const nsSize dummyContainerSize;
1490 for (nsIFrame* child : childList) {
1491 LogicalRect rect = child->GetLogicalRect(aFrameWM, dummyContainerSize);
1492 rect.IStart(aFrameWM) += residualISize / 2;
1493 child->SetRect(aFrameWM, rect, dummyContainerSize);
1494 }
1495 }
1496
RepositionRubyFrame(nsIFrame * aFrame,const nsContinuationStates * aContinuationStates,const WritingMode aContainerWM,const LogicalMargin & aBorderPadding)1497 /* static */ nscoord nsBidiPresUtils::RepositionRubyFrame(
1498 nsIFrame* aFrame, const nsContinuationStates* aContinuationStates,
1499 const WritingMode aContainerWM, const LogicalMargin& aBorderPadding) {
1500 LayoutFrameType frameType = aFrame->Type();
1501 MOZ_ASSERT(RubyUtils::IsRubyBox(frameType));
1502
1503 nscoord icoord = 0;
1504 WritingMode frameWM = aFrame->GetWritingMode();
1505 bool isLTR = frameWM.IsBidiLTR();
1506 nsSize frameSize = aFrame->GetSize();
1507 if (frameType == LayoutFrameType::Ruby) {
1508 icoord += aBorderPadding.IStart(frameWM);
1509 // Reposition ruby segments in a ruby container
1510 for (RubySegmentEnumerator e(static_cast<nsRubyFrame*>(aFrame)); !e.AtEnd();
1511 e.Next()) {
1512 nsRubyBaseContainerFrame* rbc = e.GetBaseContainer();
1513 AutoRubyTextContainerArray textContainers(rbc);
1514
1515 nscoord segmentISize = RepositionFrame(
1516 rbc, isLTR, icoord, aContinuationStates, frameWM, false, frameSize);
1517 for (nsRubyTextContainerFrame* rtc : textContainers) {
1518 nscoord isize = RepositionFrame(rtc, isLTR, icoord, aContinuationStates,
1519 frameWM, false, frameSize);
1520 segmentISize = std::max(segmentISize, isize);
1521 }
1522 icoord += segmentISize;
1523 }
1524 icoord += aBorderPadding.IEnd(frameWM);
1525 } else if (frameType == LayoutFrameType::RubyBaseContainer) {
1526 // Reposition ruby columns in a ruby segment
1527 auto rbc = static_cast<nsRubyBaseContainerFrame*>(aFrame);
1528 AutoRubyTextContainerArray textContainers(rbc);
1529
1530 for (RubyColumnEnumerator e(rbc, textContainers); !e.AtEnd(); e.Next()) {
1531 RubyColumn column;
1532 e.GetColumn(column);
1533
1534 nscoord columnISize =
1535 RepositionFrame(column.mBaseFrame, isLTR, icoord, aContinuationStates,
1536 frameWM, false, frameSize);
1537 for (nsRubyTextFrame* rt : column.mTextFrames) {
1538 nscoord isize = RepositionFrame(rt, isLTR, icoord, aContinuationStates,
1539 frameWM, false, frameSize);
1540 columnISize = std::max(columnISize, isize);
1541 }
1542 icoord += columnISize;
1543 }
1544 } else {
1545 if (frameType == LayoutFrameType::RubyBase ||
1546 frameType == LayoutFrameType::RubyText) {
1547 RepositionRubyContentFrame(aFrame, frameWM, aBorderPadding);
1548 }
1549 // Note that, ruby text container is not present in all conditions
1550 // above. It is intended, because the children of rtc are reordered
1551 // with the children of ruby base container simultaneously. We only
1552 // need to return its isize here, as it should not be changed.
1553 icoord += aFrame->ISize(aContainerWM);
1554 }
1555 return icoord;
1556 }
1557
RepositionFrame(nsIFrame * aFrame,bool aIsEvenLevel,nscoord aStartOrEnd,const nsContinuationStates * aContinuationStates,WritingMode aContainerWM,bool aContainerReverseDir,const nsSize & aContainerSize)1558 /* static */ nscoord nsBidiPresUtils::RepositionFrame(
1559 nsIFrame* aFrame, bool aIsEvenLevel, nscoord aStartOrEnd,
1560 const nsContinuationStates* aContinuationStates, WritingMode aContainerWM,
1561 bool aContainerReverseDir, const nsSize& aContainerSize) {
1562 nscoord lineSize =
1563 aContainerWM.IsVertical() ? aContainerSize.height : aContainerSize.width;
1564 NS_ASSERTION(lineSize != NS_UNCONSTRAINEDSIZE,
1565 "Unconstrained inline line size in bidi frame reordering");
1566 if (!aFrame) return 0;
1567
1568 bool isFirst, isLast;
1569 WritingMode frameWM = aFrame->GetWritingMode();
1570 IsFirstOrLast(aFrame, aContinuationStates,
1571 aContainerWM.IsBidiLTR() == frameWM.IsBidiLTR(),
1572 isFirst /* out */, isLast /* out */);
1573
1574 // We only need the margin if the frame is first or last in its own
1575 // writing mode, but we're traversing the frames in the order of the
1576 // container's writing mode. To get the right values, we set start and
1577 // end margins on a logical margin in the frame's writing mode, and
1578 // then convert the margin to the container's writing mode to set the
1579 // coordinates.
1580
1581 // This method is called from nsBlockFrame::PlaceLine via the call to
1582 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1583 // have been reflowed, which is required for GetUsedMargin/Border/Padding
1584 nscoord frameISize = aFrame->ISize();
1585 LogicalMargin frameMargin = aFrame->GetLogicalUsedMargin(frameWM);
1586 LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(frameWM);
1587 // Since the visual order of frame could be different from the continuation
1588 // order, we need to remove any inline border/padding [that is already applied
1589 // based on continuation order] and then add it back based on the visual order
1590 // (i.e. isFirst/isLast) to get the correct isize for the current frame.
1591 // We don't need to do that for 'box-decoration-break:clone' because then all
1592 // continuations have border/padding/margin applied.
1593 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
1594 StyleBoxDecorationBreak::Slice) {
1595 // First remove the border/padding that was applied based on logical order.
1596 if (!aFrame->GetPrevContinuation()) {
1597 frameISize -= borderPadding.IStart(frameWM);
1598 }
1599 if (!aFrame->GetNextContinuation()) {
1600 frameISize -= borderPadding.IEnd(frameWM);
1601 }
1602 // Set margin/border/padding based on visual order.
1603 if (!isFirst) {
1604 frameMargin.IStart(frameWM) = 0;
1605 borderPadding.IStart(frameWM) = 0;
1606 }
1607 if (!isLast) {
1608 frameMargin.IEnd(frameWM) = 0;
1609 borderPadding.IEnd(frameWM) = 0;
1610 }
1611 // Add the border/padding which is now based on visual order.
1612 frameISize += borderPadding.IStartEnd(frameWM);
1613 }
1614
1615 nscoord icoord = 0;
1616 if (!IsBidiLeaf(aFrame)) {
1617 bool reverseDir = aIsEvenLevel != frameWM.IsBidiLTR();
1618 icoord += reverseDir ? borderPadding.IEnd(frameWM)
1619 : borderPadding.IStart(frameWM);
1620 LogicalSize logicalSize(frameWM, frameISize, aFrame->BSize());
1621 nsSize frameSize = logicalSize.GetPhysicalSize(frameWM);
1622 // Reposition the child frames
1623 for (nsFrameList::Enumerator e(aFrame->PrincipalChildList()); !e.AtEnd();
1624 e.Next()) {
1625 icoord +=
1626 RepositionFrame(e.get(), aIsEvenLevel, icoord, aContinuationStates,
1627 frameWM, reverseDir, frameSize);
1628 }
1629 icoord += reverseDir ? borderPadding.IStart(frameWM)
1630 : borderPadding.IEnd(frameWM);
1631 } else if (RubyUtils::IsRubyBox(aFrame->Type())) {
1632 icoord += RepositionRubyFrame(aFrame, aContinuationStates, aContainerWM,
1633 borderPadding);
1634 } else {
1635 icoord +=
1636 frameWM.IsOrthogonalTo(aContainerWM) ? aFrame->BSize() : frameISize;
1637 }
1638
1639 // In the following variables, if aContainerReverseDir is true, i.e.
1640 // the container is positioning its children in reverse of its logical
1641 // direction, the "StartOrEnd" refers to the distance from the frame
1642 // to the inline end edge of the container, elsewise, it refers to the
1643 // distance to the inline start edge.
1644 const LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM);
1645 nscoord marginStartOrEnd = aContainerReverseDir ? margin.IEnd(aContainerWM)
1646 : margin.IStart(aContainerWM);
1647 nscoord frameStartOrEnd = aStartOrEnd + marginStartOrEnd;
1648
1649 LogicalRect rect = aFrame->GetLogicalRect(aContainerWM, aContainerSize);
1650 rect.ISize(aContainerWM) = icoord;
1651 rect.IStart(aContainerWM) = aContainerReverseDir
1652 ? lineSize - frameStartOrEnd - icoord
1653 : frameStartOrEnd;
1654 aFrame->SetRect(aContainerWM, rect, aContainerSize);
1655
1656 return icoord + margin.IStartEnd(aContainerWM);
1657 }
1658
InitContinuationStates(nsIFrame * aFrame,nsContinuationStates * aContinuationStates)1659 void nsBidiPresUtils::InitContinuationStates(
1660 nsIFrame* aFrame, nsContinuationStates* aContinuationStates) {
1661 aContinuationStates->PutEntry(aFrame);
1662 if (!IsBidiLeaf(aFrame) || RubyUtils::IsRubyBox(aFrame->Type())) {
1663 // Continue for child frames
1664 for (nsIFrame* frame : aFrame->PrincipalChildList()) {
1665 InitContinuationStates(frame, aContinuationStates);
1666 }
1667 }
1668 }
1669
RepositionInlineFrames(BidiLineData * aBld,WritingMode aLineWM,const nsSize & aContainerSize,nscoord aStart)1670 /* static */ nscoord nsBidiPresUtils::RepositionInlineFrames(
1671 BidiLineData* aBld, WritingMode aLineWM, const nsSize& aContainerSize,
1672 nscoord aStart) {
1673 nscoord start = aStart;
1674 nsIFrame* frame;
1675 int32_t count = aBld->mVisualFrames.Length();
1676 int32_t index;
1677 nsContinuationStates continuationStates;
1678
1679 // Initialize continuation states to (nullptr, 0) for
1680 // each frame on the line.
1681 for (index = 0; index < count; index++) {
1682 InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
1683 }
1684
1685 // Reposition frames in visual order
1686 int32_t step, limit;
1687 if (aLineWM.IsBidiLTR()) {
1688 index = 0;
1689 step = 1;
1690 limit = count;
1691 } else {
1692 index = count - 1;
1693 step = -1;
1694 limit = -1;
1695 }
1696 for (; index != limit; index += step) {
1697 frame = aBld->VisualFrameAt(index);
1698 start += RepositionFrame(
1699 frame, !(IS_LEVEL_RTL(aBld->mLevels[aBld->mIndexMap[index]])), start,
1700 &continuationStates, aLineWM, false, aContainerSize);
1701 }
1702 return start;
1703 }
1704
CheckLineOrder(nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine,nsIFrame ** aFirstVisual,nsIFrame ** aLastVisual)1705 bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
1706 int32_t aNumFramesOnLine,
1707 nsIFrame** aFirstVisual,
1708 nsIFrame** aLastVisual) {
1709 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1710 int32_t count = bld.FrameCount();
1711
1712 if (aFirstVisual) {
1713 *aFirstVisual = bld.VisualFrameAt(0);
1714 }
1715 if (aLastVisual) {
1716 *aLastVisual = bld.VisualFrameAt(count - 1);
1717 }
1718
1719 return bld.mIsReordered;
1720 }
1721
GetFrameToRightOf(const nsIFrame * aFrame,nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine)1722 nsIFrame* nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
1723 nsIFrame* aFirstFrameOnLine,
1724 int32_t aNumFramesOnLine) {
1725 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1726
1727 int32_t count = bld.mVisualFrames.Length();
1728
1729 if (aFrame == nullptr && count) return bld.VisualFrameAt(0);
1730
1731 for (int32_t i = 0; i < count - 1; i++) {
1732 if (bld.VisualFrameAt(i) == aFrame) {
1733 return bld.VisualFrameAt(i + 1);
1734 }
1735 }
1736
1737 return nullptr;
1738 }
1739
GetFrameToLeftOf(const nsIFrame * aFrame,nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine)1740 nsIFrame* nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
1741 nsIFrame* aFirstFrameOnLine,
1742 int32_t aNumFramesOnLine) {
1743 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1744
1745 int32_t count = bld.mVisualFrames.Length();
1746
1747 if (aFrame == nullptr && count) return bld.VisualFrameAt(count - 1);
1748
1749 for (int32_t i = 1; i < count; i++) {
1750 if (bld.VisualFrameAt(i) == aFrame) {
1751 return bld.VisualFrameAt(i - 1);
1752 }
1753 }
1754
1755 return nullptr;
1756 }
1757
EnsureBidiContinuation(nsIFrame * aFrame,nsIFrame ** aNewFrame,int32_t aStart,int32_t aEnd)1758 inline nsresult nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
1759 nsIFrame** aNewFrame,
1760 int32_t aStart,
1761 int32_t aEnd) {
1762 NS_PRECONDITION(aNewFrame, "null OUT ptr");
1763 NS_PRECONDITION(aFrame, "aFrame is null");
1764
1765 aFrame->AdjustOffsetsForBidi(aStart, aEnd);
1766 return CreateContinuation(aFrame, aNewFrame, false);
1767 }
1768
RemoveBidiContinuation(BidiParagraphData * aBpd,nsIFrame * aFrame,int32_t aFirstIndex,int32_t aLastIndex)1769 void nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData* aBpd,
1770 nsIFrame* aFrame,
1771 int32_t aFirstIndex,
1772 int32_t aLastIndex) {
1773 FrameBidiData bidiData = aFrame->GetBidiData();
1774 bidiData.precedingControl = kBidiLevelNone;
1775 for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
1776 nsIFrame* frame = aBpd->FrameAt(index);
1777 if (frame != NS_BIDI_CONTROL_FRAME) {
1778 // Make the frame and its continuation ancestors fluid,
1779 // so they can be reused or deleted by normal reflow code
1780 frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
1781 frame->AddStateBits(NS_FRAME_IS_BIDI);
1782 while (frame) {
1783 nsIFrame* prev = frame->GetPrevContinuation();
1784 if (prev) {
1785 MakeContinuationFluid(prev, frame);
1786 frame = frame->GetParent();
1787 } else {
1788 break;
1789 }
1790 }
1791 }
1792 }
1793
1794 // Make sure that the last continuation we made fluid does not itself have a
1795 // fluid continuation (this can happen when re-resolving after dynamic changes
1796 // to content)
1797 nsIFrame* lastFrame = aBpd->FrameAt(aLastIndex);
1798 MakeContinuationsNonFluidUpParentChain(lastFrame, lastFrame->GetNextInFlow());
1799 }
1800
FormatUnicodeText(nsPresContext * aPresContext,char16_t * aText,int32_t & aTextLength,nsCharType aCharType)1801 nsresult nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
1802 char16_t* aText,
1803 int32_t& aTextLength,
1804 nsCharType aCharType) {
1805 nsresult rv = NS_OK;
1806 // ahmed
1807 // adjusted for correct numeral shaping
1808 uint32_t bidiOptions = aPresContext->GetBidi();
1809 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
1810 case IBMBIDI_NUMERAL_HINDI:
1811 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
1812 break;
1813
1814 case IBMBIDI_NUMERAL_ARABIC:
1815 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1816 break;
1817
1818 case IBMBIDI_NUMERAL_PERSIAN:
1819 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_PERSIAN);
1820 break;
1821
1822 case IBMBIDI_NUMERAL_REGULAR:
1823
1824 switch (aCharType) {
1825 case eCharType_EuropeanNumber:
1826 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1827 break;
1828
1829 case eCharType_ArabicNumber:
1830 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
1831 break;
1832
1833 default:
1834 break;
1835 }
1836 break;
1837
1838 case IBMBIDI_NUMERAL_HINDICONTEXT:
1839 if (((GET_BIDI_OPTION_DIRECTION(bidiOptions) ==
1840 IBMBIDI_TEXTDIRECTION_RTL) &&
1841 (IS_ARABIC_DIGIT(aText[0]))) ||
1842 (eCharType_ArabicNumber == aCharType))
1843 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
1844 else if (eCharType_EuropeanNumber == aCharType)
1845 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1846 break;
1847
1848 case IBMBIDI_NUMERAL_PERSIANCONTEXT:
1849 if (((GET_BIDI_OPTION_DIRECTION(bidiOptions) ==
1850 IBMBIDI_TEXTDIRECTION_RTL) &&
1851 (IS_ARABIC_DIGIT(aText[0]))) ||
1852 (eCharType_ArabicNumber == aCharType))
1853 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_PERSIAN);
1854 else if (eCharType_EuropeanNumber == aCharType)
1855 HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1856 break;
1857
1858 case IBMBIDI_NUMERAL_NOMINAL:
1859 default:
1860 break;
1861 }
1862
1863 StripBidiControlCharacters(aText, aTextLength);
1864 return rv;
1865 }
1866
StripBidiControlCharacters(char16_t * aText,int32_t & aTextLength)1867 void nsBidiPresUtils::StripBidiControlCharacters(char16_t* aText,
1868 int32_t& aTextLength) {
1869 if ((nullptr == aText) || (aTextLength < 1)) {
1870 return;
1871 }
1872
1873 int32_t stripLen = 0;
1874
1875 for (int32_t i = 0; i < aTextLength; i++) {
1876 // XXX: This silently ignores surrogate characters.
1877 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1878 if (IsBidiControl((uint32_t)aText[i])) {
1879 ++stripLen;
1880 } else {
1881 aText[i - stripLen] = aText[i];
1882 }
1883 }
1884 aTextLength -= stripLen;
1885 }
1886
1887 #if 0 // XXX: for the future use ???
1888 void
1889 RemoveDiacritics(char16_t* aText,
1890 int32_t& aTextLength)
1891 {
1892 if (aText && (aTextLength > 0) ) {
1893 int32_t offset = 0;
1894
1895 for (int32_t i = 0; i < aTextLength && aText[i]; i++) {
1896 if (IS_BIDI_DIACRITIC(aText[i]) ) {
1897 ++offset;
1898 continue;
1899 }
1900 aText[i - offset] = aText[i];
1901 }
1902 aTextLength = i - offset;
1903 aText[aTextLength] = 0;
1904 }
1905 }
1906 #endif
1907
CalculateCharType(nsBidi * aBidiEngine,const char16_t * aText,int32_t & aOffset,int32_t aCharTypeLimit,int32_t & aRunLimit,int32_t & aRunLength,int32_t & aRunCount,uint8_t & aCharType,uint8_t & aPrevCharType)1908 void nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine,
1909 const char16_t* aText, int32_t& aOffset,
1910 int32_t aCharTypeLimit,
1911 int32_t& aRunLimit, int32_t& aRunLength,
1912 int32_t& aRunCount, uint8_t& aCharType,
1913 uint8_t& aPrevCharType)
1914
1915 {
1916 bool strongTypeFound = false;
1917 int32_t offset;
1918 nsCharType charType;
1919
1920 aCharType = eCharType_OtherNeutral;
1921
1922 int32_t charLen;
1923 for (offset = aOffset; offset < aCharTypeLimit; offset += charLen) {
1924 // Make sure we give RTL chartype to all characters that would be classified
1925 // as Right-To-Left by a bidi platform.
1926 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1927 charLen = 1;
1928 uint32_t ch = aText[offset];
1929 if (IS_HEBREW_CHAR(ch)) {
1930 charType = eCharType_RightToLeft;
1931 } else if (IS_ARABIC_ALPHABETIC(ch)) {
1932 charType = eCharType_RightToLeftArabic;
1933 } else {
1934 if (NS_IS_HIGH_SURROGATE(ch) && offset + 1 < aCharTypeLimit &&
1935 NS_IS_LOW_SURROGATE(aText[offset + 1])) {
1936 ch = SURROGATE_TO_UCS4(ch, aText[offset + 1]);
1937 charLen = 2;
1938 }
1939 charType = unicode::GetBidiCat(ch);
1940 }
1941
1942 if (!CHARTYPE_IS_WEAK(charType)) {
1943 if (strongTypeFound && (charType != aPrevCharType) &&
1944 (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType))) {
1945 // Stop at this point to ensure uni-directionality of the text
1946 // (from platform's point of view).
1947 // Also, don't mix Arabic and Hebrew content (since platform may
1948 // provide BIDI support to one of them only).
1949 aRunLength = offset - aOffset;
1950 aRunLimit = offset;
1951 ++aRunCount;
1952 break;
1953 }
1954
1955 if ((eCharType_RightToLeftArabic == aPrevCharType ||
1956 eCharType_ArabicNumber == aPrevCharType) &&
1957 eCharType_EuropeanNumber == charType) {
1958 charType = eCharType_ArabicNumber;
1959 }
1960
1961 // Set PrevCharType to the last strong type in this frame
1962 // (for correct numeric shaping)
1963 aPrevCharType = charType;
1964
1965 strongTypeFound = true;
1966 aCharType = charType;
1967 }
1968 }
1969 aOffset = offset;
1970 }
1971
ProcessText(const char16_t * aText,int32_t aLength,nsBidiLevel aBaseLevel,nsPresContext * aPresContext,BidiProcessor & aprocessor,Mode aMode,nsBidiPositionResolve * aPosResolve,int32_t aPosResolveCount,nscoord * aWidth,nsBidi * aBidiEngine)1972 nsresult nsBidiPresUtils::ProcessText(const char16_t* aText, int32_t aLength,
1973 nsBidiLevel aBaseLevel,
1974 nsPresContext* aPresContext,
1975 BidiProcessor& aprocessor, Mode aMode,
1976 nsBidiPositionResolve* aPosResolve,
1977 int32_t aPosResolveCount, nscoord* aWidth,
1978 nsBidi* aBidiEngine) {
1979 NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0),
1980 "Incorrect aPosResolve / aPosResolveCount arguments");
1981
1982 int32_t runCount;
1983
1984 nsAutoString textBuffer(aText, aLength);
1985 textBuffer.ReplaceChar(kSeparators, kSpace);
1986 const char16_t* text = textBuffer.get();
1987
1988 nsresult rv = aBidiEngine->SetPara(text, aLength, aBaseLevel);
1989 if (NS_FAILED(rv)) return rv;
1990
1991 rv = aBidiEngine->CountRuns(&runCount);
1992 if (NS_FAILED(rv)) return rv;
1993
1994 nscoord xOffset = 0;
1995 nscoord width, xEndRun = 0;
1996 nscoord totalWidth = 0;
1997 int32_t i, start, limit, length;
1998 uint32_t visualStart = 0;
1999 uint8_t charType;
2000 uint8_t prevType = eCharType_LeftToRight;
2001
2002 for (int nPosResolve = 0; nPosResolve < aPosResolveCount; ++nPosResolve) {
2003 aPosResolve[nPosResolve].visualIndex = kNotFound;
2004 aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
2005 aPosResolve[nPosResolve].visualWidth = kNotFound;
2006 }
2007
2008 for (i = 0; i < runCount; i++) {
2009 nsBidiDirection dir = aBidiEngine->GetVisualRun(i, &start, &length);
2010
2011 nsBidiLevel level;
2012 aBidiEngine->GetLogicalRun(start, &limit, &level);
2013
2014 dir = DIRECTION_FROM_LEVEL(level);
2015 int32_t subRunLength = limit - start;
2016 int32_t lineOffset = start;
2017 int32_t typeLimit = std::min(limit, aLength);
2018 int32_t subRunCount = 1;
2019 int32_t subRunLimit = typeLimit;
2020
2021 /*
2022 * If |level| is even, i.e. the direction of the run is left-to-right, we
2023 * render the subruns from left to right and increment the x-coordinate
2024 * |xOffset| by the width of each subrun after rendering.
2025 *
2026 * If |level| is odd, i.e. the direction of the run is right-to-left, we
2027 * render the subruns from right to left. We begin by incrementing |xOffset|
2028 * by the width of the whole run, and then decrement it by the width of each
2029 * subrun before rendering. After rendering all the subruns, we restore the
2030 * x-coordinate of the end of the run for the start of the next run.
2031 */
2032
2033 if (dir == NSBIDI_RTL) {
2034 aprocessor.SetText(text + start, subRunLength, dir);
2035 width = aprocessor.GetWidth();
2036 xOffset += width;
2037 xEndRun = xOffset;
2038 }
2039
2040 while (subRunCount > 0) {
2041 // CalculateCharType can increment subRunCount if the run
2042 // contains mixed character types
2043 CalculateCharType(aBidiEngine, text, lineOffset, typeLimit, subRunLimit,
2044 subRunLength, subRunCount, charType, prevType);
2045
2046 nsAutoString runVisualText;
2047 runVisualText.Assign(text + start, subRunLength);
2048 if (int32_t(runVisualText.Length()) < subRunLength)
2049 return NS_ERROR_OUT_OF_MEMORY;
2050 FormatUnicodeText(aPresContext, runVisualText.BeginWriting(),
2051 subRunLength, (nsCharType)charType);
2052
2053 aprocessor.SetText(runVisualText.get(), subRunLength, dir);
2054 width = aprocessor.GetWidth();
2055 totalWidth += width;
2056 if (dir == NSBIDI_RTL) {
2057 xOffset -= width;
2058 }
2059 if (aMode == MODE_DRAW) {
2060 aprocessor.DrawText(xOffset, width);
2061 }
2062
2063 /*
2064 * The caller may request to calculate the visual position of one
2065 * or more characters.
2066 */
2067 for (int nPosResolve = 0; nPosResolve < aPosResolveCount; ++nPosResolve) {
2068 nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
2069 /*
2070 * Did we already resolve this position's visual metric? If so, skip.
2071 */
2072 if (posResolve->visualLeftTwips != kNotFound) continue;
2073
2074 /*
2075 * First find out if the logical position is within this run.
2076 */
2077 if (start <= posResolve->logicalIndex &&
2078 start + subRunLength > posResolve->logicalIndex) {
2079 /*
2080 * If this run is only one character long, we have an easy case:
2081 * the visual position is the x-coord of the start of the run
2082 * less the x-coord of the start of the whole text.
2083 */
2084 if (subRunLength == 1) {
2085 posResolve->visualIndex = visualStart;
2086 posResolve->visualLeftTwips = xOffset;
2087 posResolve->visualWidth = width;
2088 }
2089 /*
2090 * Otherwise, we need to measure the width of the run's part
2091 * which is to the visual left of the index.
2092 * In other words, the run is broken in two, around the logical index,
2093 * and we measure the part which is visually left.
2094 * If the run is right-to-left, this part will span from after the
2095 * index up to the end of the run; if it is left-to-right, this part
2096 * will span from the start of the run up to (and inclduing) the
2097 * character before the index.
2098 */
2099 else {
2100 /*
2101 * Here is a description of how the width of the current character
2102 * (posResolve->visualWidth) is calculated:
2103 *
2104 * LTR (current char: "P"):
2105 * S A M P L E (logical index: 3, visual index: 3)
2106 * ^ (visualLeftPart)
2107 * ^ (visualRightSide)
2108 * visualLeftLength == 3
2109 * ^^^^^^ (subWidth)
2110 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
2111 * ^^ (posResolve->visualWidth)
2112 *
2113 * RTL (current char: "M"):
2114 * E L P M A S (logical index: 2, visual index: 3)
2115 * ^ (visualLeftPart)
2116 * ^ (visualRightSide)
2117 * visualLeftLength == 3
2118 * ^^^^^^ (subWidth)
2119 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
2120 * ^^ (posResolve->visualWidth)
2121 */
2122 nscoord subWidth;
2123 // The position in the text where this run's "left part" begins.
2124 const char16_t* visualLeftPart;
2125 const char16_t* visualRightSide;
2126 if (dir == NSBIDI_RTL) {
2127 // One day, son, this could all be replaced with
2128 // mPresContext->GetBidiEngine().GetVisualIndex() ...
2129 posResolve->visualIndex =
2130 visualStart +
2131 (subRunLength - (posResolve->logicalIndex + 1 - start));
2132 // Skipping to the "left part".
2133 visualLeftPart = text + posResolve->logicalIndex + 1;
2134 // Skipping to the right side of the current character
2135 visualRightSide = visualLeftPart - 1;
2136 } else {
2137 posResolve->visualIndex =
2138 visualStart + (posResolve->logicalIndex - start);
2139 // Skipping to the "left part".
2140 visualLeftPart = text + start;
2141 // In LTR mode this is the same as visualLeftPart
2142 visualRightSide = visualLeftPart;
2143 }
2144 // The delta between the start of the run and the left part's end.
2145 int32_t visualLeftLength = posResolve->visualIndex - visualStart;
2146 aprocessor.SetText(visualLeftPart, visualLeftLength, dir);
2147 subWidth = aprocessor.GetWidth();
2148 aprocessor.SetText(visualRightSide, visualLeftLength + 1, dir);
2149 posResolve->visualLeftTwips = xOffset + subWidth;
2150 posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
2151 }
2152 }
2153 }
2154
2155 if (dir == NSBIDI_LTR) {
2156 xOffset += width;
2157 }
2158
2159 --subRunCount;
2160 start = lineOffset;
2161 subRunLimit = typeLimit;
2162 subRunLength = typeLimit - lineOffset;
2163 } // while
2164 if (dir == NSBIDI_RTL) {
2165 xOffset = xEndRun;
2166 }
2167
2168 visualStart += length;
2169 } // for
2170
2171 if (aWidth) {
2172 *aWidth = totalWidth;
2173 }
2174 return NS_OK;
2175 }
2176
2177 class MOZ_STACK_CLASS nsIRenderingContextBidiProcessor final
2178 : public nsBidiPresUtils::BidiProcessor {
2179 public:
2180 typedef mozilla::gfx::DrawTarget DrawTarget;
2181
nsIRenderingContextBidiProcessor(gfxContext * aCtx,DrawTarget * aTextRunConstructionDrawTarget,nsFontMetrics * aFontMetrics,const nsPoint & aPt)2182 nsIRenderingContextBidiProcessor(gfxContext* aCtx,
2183 DrawTarget* aTextRunConstructionDrawTarget,
2184 nsFontMetrics* aFontMetrics,
2185 const nsPoint& aPt)
2186 : mCtx(aCtx),
2187 mTextRunConstructionDrawTarget(aTextRunConstructionDrawTarget),
2188 mFontMetrics(aFontMetrics),
2189 mPt(aPt) {}
2190
~nsIRenderingContextBidiProcessor()2191 ~nsIRenderingContextBidiProcessor() { mFontMetrics->SetTextRunRTL(false); }
2192
SetText(const char16_t * aText,int32_t aLength,nsBidiDirection aDirection)2193 virtual void SetText(const char16_t* aText, int32_t aLength,
2194 nsBidiDirection aDirection) override {
2195 mFontMetrics->SetTextRunRTL(aDirection == NSBIDI_RTL);
2196 mText = aText;
2197 mLength = aLength;
2198 }
2199
GetWidth()2200 virtual nscoord GetWidth() override {
2201 return nsLayoutUtils::AppUnitWidthOfString(mText, mLength, *mFontMetrics,
2202 mTextRunConstructionDrawTarget);
2203 }
2204
DrawText(nscoord aIOffset,nscoord)2205 virtual void DrawText(nscoord aIOffset, nscoord) override {
2206 nsPoint pt(mPt);
2207 if (mFontMetrics->GetVertical()) {
2208 pt.y += aIOffset;
2209 } else {
2210 pt.x += aIOffset;
2211 }
2212 mFontMetrics->DrawString(mText, mLength, pt.x, pt.y, mCtx,
2213 mTextRunConstructionDrawTarget);
2214 }
2215
2216 private:
2217 gfxContext* mCtx;
2218 DrawTarget* mTextRunConstructionDrawTarget;
2219 nsFontMetrics* mFontMetrics;
2220 nsPoint mPt;
2221 const char16_t* mText;
2222 int32_t mLength;
2223 };
2224
ProcessTextForRenderingContext(const char16_t * aText,int32_t aLength,nsBidiLevel aBaseLevel,nsPresContext * aPresContext,gfxContext & aRenderingContext,DrawTarget * aTextRunConstructionDrawTarget,nsFontMetrics & aFontMetrics,Mode aMode,nscoord aX,nscoord aY,nsBidiPositionResolve * aPosResolve,int32_t aPosResolveCount,nscoord * aWidth)2225 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(
2226 const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
2227 nsPresContext* aPresContext, gfxContext& aRenderingContext,
2228 DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
2229 Mode aMode, nscoord aX, nscoord aY, nsBidiPositionResolve* aPosResolve,
2230 int32_t aPosResolveCount, nscoord* aWidth) {
2231 nsIRenderingContextBidiProcessor processor(&aRenderingContext,
2232 aTextRunConstructionDrawTarget,
2233 &aFontMetrics, nsPoint(aX, aY));
2234 return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor, aMode,
2235 aPosResolve, aPosResolveCount, aWidth,
2236 &aPresContext->GetBidiEngine());
2237 }
2238
2239 /* static */
BidiLevelFromStyle(nsStyleContext * aStyleContext)2240 nsBidiLevel nsBidiPresUtils::BidiLevelFromStyle(nsStyleContext* aStyleContext) {
2241 if (aStyleContext->StyleTextReset()->mUnicodeBidi &
2242 NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
2243 return NSBIDI_DEFAULT_LTR;
2244 }
2245
2246 if (aStyleContext->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
2247 return NSBIDI_RTL;
2248 }
2249
2250 return NSBIDI_LTR;
2251 }
2252