layout/base/nsBidiPresUtils.cpp

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

mercurial