Bug 1104435 part 2 - Make AnimationPlayer derive from nsISupports; r=smaug
[gecko.git] / layout / base / nsBidiPresUtils.cpp
blob1207b0138c6349aa4e7a479b2b2eee8be07f991a
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/. */
6 #include "nsBidiPresUtils.h"
7 #include "nsFontMetrics.h"
8 #include "nsGkAtoms.h"
9 #include "nsPresContext.h"
10 #include "nsRenderingContext.h"
11 #include "nsBidiUtils.h"
12 #include "nsCSSFrameConstructor.h"
13 #include "nsContainerFrame.h"
14 #include "nsInlineFrame.h"
15 #include "nsPlaceholderFrame.h"
16 #include "nsFirstLetterFrame.h"
17 #include "nsUnicodeProperties.h"
18 #include "nsTextFrame.h"
19 #include "nsBlockFrame.h"
20 #include "nsIFrameInlines.h"
21 #include <algorithm>
23 #undef NOISY_BIDI
24 #undef REALLY_NOISY_BIDI
26 using namespace mozilla;
28 static const char16_t kSpace = 0x0020;
29 static const char16_t kZWSP = 0x200B;
30 static const char16_t kLineSeparator = 0x2028;
31 static const char16_t kObjectSubstitute = 0xFFFC;
32 static const char16_t kLRE = 0x202A;
33 static const char16_t kRLE = 0x202B;
34 static const char16_t kLRO = 0x202D;
35 static const char16_t kRLO = 0x202E;
36 static const char16_t kPDF = 0x202C;
37 static const char16_t kSeparators[] = {
38 // All characters with Bidi type Segment Separator or Block Separator
39 char16_t('\t'),
40 char16_t('\r'),
41 char16_t('\n'),
42 char16_t(0xb),
43 char16_t(0x1c),
44 char16_t(0x1d),
45 char16_t(0x1e),
46 char16_t(0x1f),
47 char16_t(0x85),
48 char16_t(0x2029),
49 char16_t(0)
52 #define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
54 struct BidiParagraphData {
55 nsString mBuffer;
56 nsAutoTArray<char16_t, 16> mEmbeddingStack;
57 nsTArray<nsIFrame*> mLogicalFrames;
58 nsTArray<nsLineBox*> mLinePerFrame;
59 nsDataHashtable<nsISupportsHashKey, int32_t> mContentToFrameIndex;
60 bool mIsVisual;
61 bool mReset;
62 nsBidiLevel mParaLevel;
63 nsIContent* mPrevContent;
64 nsAutoPtr<nsBidi> mBidiEngine;
65 nsIFrame* mPrevFrame;
66 nsAutoPtr<BidiParagraphData> mSubParagraph;
67 uint8_t mParagraphDepth;
69 void Init(nsBlockFrame *aBlockFrame)
71 mBidiEngine = new nsBidi();
72 mPrevContent = nullptr;
73 mParagraphDepth = 0;
75 mParaLevel = nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame->StyleContext());
77 mIsVisual = aBlockFrame->PresContext()->IsVisualMode();
78 if (mIsVisual) {
79 /**
80 * Drill up in content to detect whether this is an element that needs to
81 * be rendered with logical order even on visual pages.
83 * We always use logical order on form controls, firstly so that text
84 * entry will be in logical order, but also because visual pages were
85 * written with the assumption that even if the browser had no support
86 * for right-to-left text rendering, it would use native widgets with
87 * bidi support to display form controls.
89 * We also use logical order in XUL elements, since we expect that if a
90 * XUL element appears in a visual page, it will be generated by an XBL
91 * binding and contain localized text which will be in logical order.
93 for (nsIContent* content = aBlockFrame->GetContent() ; content;
94 content = content->GetParent()) {
95 if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) ||
96 content->IsXUL()) {
97 mIsVisual = false;
98 break;
104 BidiParagraphData* GetSubParagraph()
106 if (!mSubParagraph) {
107 mSubParagraph = new BidiParagraphData();
108 mSubParagraph->Init(this);
111 return mSubParagraph;
114 // Initialise a sub-paragraph from its containing paragraph
115 void Init(BidiParagraphData *aBpd)
117 mBidiEngine = new nsBidi();
118 mPrevContent = nullptr;
119 mIsVisual = aBpd->mIsVisual;
120 mReset = false;
123 void Reset(nsIFrame* aBDIFrame, BidiParagraphData *aBpd)
125 mReset = true;
126 mLogicalFrames.Clear();
127 mLinePerFrame.Clear();
128 mContentToFrameIndex.Clear();
129 mBuffer.SetLength(0);
130 mPrevFrame = aBpd->mPrevFrame;
131 mParagraphDepth = aBpd->mParagraphDepth + 1;
133 const nsStyleTextReset* text = aBDIFrame->StyleTextReset();
134 bool isRTL = (NS_STYLE_DIRECTION_RTL ==
135 aBDIFrame->StyleVisibility()->mDirection);
137 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
138 mParaLevel = NSBIDI_DEFAULT_LTR;
139 } else {
140 mParaLevel = mParagraphDepth * 2;
141 if (isRTL) ++mParaLevel;
144 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
145 PushBidiControl(isRTL ? kRLO : kLRO);
149 void EmptyBuffer()
151 mBuffer.SetLength(0);
154 nsresult SetPara()
156 return mBidiEngine->SetPara(mBuffer.get(), BufferLength(),
157 mParaLevel, nullptr);
161 * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
162 * GetParaLevel() returns the actual (resolved) paragraph level which is
163 * always either NSBIDI_LTR or NSBIDI_RTL
165 nsBidiLevel GetParaLevel()
167 nsBidiLevel paraLevel = mParaLevel;
168 if (IS_DEFAULT_LEVEL(paraLevel)) {
169 mBidiEngine->GetParaLevel(&paraLevel);
171 return paraLevel;
174 nsBidiDirection GetDirection()
176 nsBidiDirection dir;
177 mBidiEngine->GetDirection(&dir);
178 return dir;
181 nsresult CountRuns(int32_t *runCount){ return mBidiEngine->CountRuns(runCount); }
183 nsresult GetLogicalRun(int32_t aLogicalStart,
184 int32_t* aLogicalLimit,
185 nsBidiLevel* aLevel)
187 nsresult rv = mBidiEngine->GetLogicalRun(aLogicalStart,
188 aLogicalLimit, aLevel);
189 if (mIsVisual || NS_FAILED(rv))
190 *aLevel = GetParaLevel();
191 return rv;
194 void ResetData()
196 mLogicalFrames.Clear();
197 mLinePerFrame.Clear();
198 mContentToFrameIndex.Clear();
199 mBuffer.SetLength(0);
200 mPrevContent = nullptr;
201 for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
202 mBuffer.Append(mEmbeddingStack[i]);
203 mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
204 mLinePerFrame.AppendElement((nsLineBox*)nullptr);
208 void ResetForNewBlock()
210 for (BidiParagraphData* bpd = this; bpd; bpd = bpd->mSubParagraph) {
211 bpd->mPrevFrame = nullptr;
215 void AppendFrame(nsIFrame* aFrame,
216 nsBlockInFlowLineIterator* aLineIter,
217 nsIContent* aContent = nullptr)
219 if (aContent) {
220 mContentToFrameIndex.Put(aContent, FrameCount());
222 mLogicalFrames.AppendElement(aFrame);
224 AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame);
225 mLinePerFrame.AppendElement(aLineIter->GetLine().get());
228 void AdvanceAndAppendFrame(nsIFrame** aFrame,
229 nsBlockInFlowLineIterator* aLineIter,
230 nsIFrame** aNextSibling)
232 nsIFrame* frame = *aFrame;
233 nsIFrame* nextSibling = *aNextSibling;
235 frame = frame->GetNextContinuation();
236 if (frame) {
237 AppendFrame(frame, aLineIter, nullptr);
240 * If we have already overshot the saved next-sibling while
241 * scanning the frame's continuations, advance it.
243 if (frame == nextSibling) {
244 nextSibling = frame->GetNextSibling();
248 *aFrame = frame;
249 *aNextSibling = nextSibling;
252 int32_t GetLastFrameForContent(nsIContent *aContent)
254 int32_t index = 0;
255 mContentToFrameIndex.Get(aContent, &index);
256 return index;
259 int32_t FrameCount(){ return mLogicalFrames.Length(); }
261 int32_t BufferLength(){ return mBuffer.Length(); }
263 nsIFrame* FrameAt(int32_t aIndex){ return mLogicalFrames[aIndex]; }
265 nsLineBox* GetLineForFrameAt(int32_t aIndex){ return mLinePerFrame[aIndex]; }
267 void AppendUnichar(char16_t aCh){ mBuffer.Append(aCh); }
269 void AppendString(const nsDependentSubstring& aString){ mBuffer.Append(aString); }
271 void AppendControlChar(char16_t aCh)
273 mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
274 mLinePerFrame.AppendElement((nsLineBox*)nullptr);
275 AppendUnichar(aCh);
278 void PushBidiControl(char16_t aCh)
280 AppendControlChar(aCh);
281 mEmbeddingStack.AppendElement(aCh);
284 void PopBidiControl()
286 AppendControlChar(kPDF);
287 NS_ASSERTION(mEmbeddingStack.Length(), "embedding/override underflow");
288 mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
291 void ClearBidiControls()
293 for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
294 AppendControlChar(kPDF);
298 static bool
299 IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
300 nsIFrame* aPrevFrame, nsIFrame* aFrame)
302 nsIFrame* endFrame = aLineIter->IsLastLineInList() ? nullptr :
303 aLineIter->GetLine().next()->mFirstChild;
304 nsIFrame* startFrame = aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
305 for (nsIFrame* frame = startFrame; frame && frame != endFrame;
306 frame = frame->GetNextSibling()) {
307 if (frame == aFrame)
308 return true;
310 return false;
313 static void
314 AdvanceLineIteratorToFrame(nsIFrame* aFrame,
315 nsBlockInFlowLineIterator* aLineIter,
316 nsIFrame*& aPrevFrame)
318 // Advance aLine to the line containing aFrame
319 nsIFrame* child = aFrame;
320 nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
321 while (parent && !nsLayoutUtils::GetAsBlock(parent)) {
322 child = parent;
323 parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
325 NS_ASSERTION (parent, "aFrame is not a descendent of aBlockFrame");
326 while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
327 #ifdef DEBUG
328 bool hasNext =
329 #endif
330 aLineIter->Next();
331 NS_ASSERTION(hasNext, "Can't find frame in lines!");
332 aPrevFrame = nullptr;
334 aPrevFrame = child;
339 struct BidiLineData {
340 nsTArray<nsIFrame*> mLogicalFrames;
341 nsTArray<nsIFrame*> mVisualFrames;
342 nsTArray<int32_t> mIndexMap;
343 nsAutoTArray<uint8_t, 18> mLevels;
344 bool mIsReordered;
346 BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine)
349 * Initialize the logically-ordered array of frames using the top-level
350 * frames of a single line
352 mLogicalFrames.Clear();
354 bool isReordered = false;
355 bool hasRTLFrames = false;
357 for (nsIFrame* frame = aFirstFrameOnLine;
358 frame && aNumFramesOnLine--;
359 frame = frame->GetNextSibling()) {
360 AppendFrame(frame);
361 nsBidiLevel level = nsBidiPresUtils::GetFrameEmbeddingLevel(frame);
362 mLevels.AppendElement(level);
363 mIndexMap.AppendElement(0);
364 if (IS_LEVEL_RTL(level)) {
365 hasRTLFrames = true;
369 // Reorder the line
370 nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
371 mIndexMap.Elements());
373 for (int32_t i = 0; i < FrameCount(); i++) {
374 mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
375 if (i != mIndexMap[i]) {
376 isReordered = true;
380 // If there's an RTL frame, assume the line is reordered
381 mIsReordered = isReordered || hasRTLFrames;
384 void AppendFrame(nsIFrame* aFrame)
386 mLogicalFrames.AppendElement(aFrame);
389 int32_t FrameCount(){ return mLogicalFrames.Length(); }
391 nsIFrame* LogicalFrameAt(int32_t aIndex){ return mLogicalFrames[aIndex]; }
393 nsIFrame* VisualFrameAt(int32_t aIndex){ return mVisualFrames[aIndex]; }
396 /* Some helper methods for Resolve() */
398 // Should this frame be split between text runs?
399 static bool
400 IsBidiSplittable(nsIFrame* aFrame)
402 // Bidi inline containers should be split, unless they're line frames.
403 nsIAtom* frameType = aFrame->GetType();
404 return (aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) &&
405 frameType != nsGkAtoms::lineFrame) ||
406 frameType == nsGkAtoms::textFrame;
409 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
410 static bool
411 IsBidiLeaf(nsIFrame* aFrame)
413 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
414 return !kid || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
418 * Create non-fluid continuations for the ancestors of a given frame all the way
419 * up the frame tree until we hit a non-splittable frame (a line or a block).
421 * @param aParent the first parent frame to be split
422 * @param aFrame the child frames after this frame are reparented to the
423 * newly-created continuation of aParent.
424 * If aFrame is null, all the children of aParent are reparented.
426 static nsresult
427 SplitInlineAncestors(nsContainerFrame* aParent,
428 nsIFrame* aFrame)
430 nsPresContext* presContext = aParent->PresContext();
431 nsIPresShell* presShell = presContext->PresShell();
432 nsIFrame* frame = aFrame;
433 nsContainerFrame* parent = aParent;
434 nsContainerFrame* newParent;
436 while (IsBidiSplittable(parent)) {
437 nsContainerFrame* grandparent = parent->GetParent();
438 NS_ASSERTION(grandparent, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
440 // Split the child list after |frame|, unless it is the last child.
441 if (!frame || frame->GetNextSibling()) {
443 newParent = static_cast<nsContainerFrame*>(presShell->FrameConstructor()->
444 CreateContinuingFrame(presContext, parent, grandparent, false));
446 nsFrameList tail = parent->StealFramesAfter(frame);
448 // Reparent views as necessary
449 nsresult rv;
450 rv = nsContainerFrame::ReparentFrameViewList(tail, parent, newParent);
451 if (NS_FAILED(rv)) {
452 return rv;
455 // The parent's continuation adopts the siblings after the split.
456 newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nullptr, tail);
458 // The list name kNoReflowPrincipalList would indicate we don't want reflow
459 nsFrameList temp(newParent, newParent);
460 grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp);
463 frame = parent;
464 parent = grandparent;
467 return NS_OK;
470 static void
471 MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext)
473 NS_ASSERTION (!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext,
474 "next-in-flow is not next continuation!");
475 aFrame->SetNextInFlow(aNext);
477 NS_ASSERTION (!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame,
478 "prev-in-flow is not prev continuation!");
479 aNext->SetPrevInFlow(aFrame);
482 static void
483 MakeContinuationsNonFluidUpParentChain(nsIFrame* aFrame, nsIFrame* aNext)
485 nsIFrame* frame;
486 nsIFrame* next;
488 for (frame = aFrame, next = aNext;
489 frame && next &&
490 next != frame && next == frame->GetNextInFlow() &&
491 IsBidiSplittable(frame);
492 frame = frame->GetParent(), next = next->GetParent()) {
494 frame->SetNextContinuation(next);
495 next->SetPrevContinuation(frame);
499 // If aFrame is the last child of its parent, convert bidi continuations to
500 // fluid continuations for all of its inline ancestors.
501 // If it isn't the last child, make sure that its continuation is fluid.
502 static void
503 JoinInlineAncestors(nsIFrame* aFrame)
505 nsIFrame* frame = aFrame;
506 do {
507 nsIFrame* next = frame->GetNextContinuation();
508 if (next) {
509 // Don't join frames if they come from different paragraph depths (i.e.
510 // one is bidi isolated relative to the other
511 if (nsBidiPresUtils::GetParagraphDepth(frame) ==
512 nsBidiPresUtils::GetParagraphDepth(next)) {
513 MakeContinuationFluid(frame, next);
516 // Join the parent only as long as we're its last child.
517 if (frame->GetNextSibling())
518 break;
519 frame = frame->GetParent();
520 } while (frame && IsBidiSplittable(frame));
523 static nsresult
524 CreateContinuation(nsIFrame* aFrame,
525 nsIFrame** aNewFrame,
526 bool aIsFluid)
528 NS_PRECONDITION(aNewFrame, "null OUT ptr");
529 NS_PRECONDITION(aFrame, "null ptr");
531 *aNewFrame = nullptr;
533 nsPresContext *presContext = aFrame->PresContext();
534 nsIPresShell *presShell = presContext->PresShell();
535 NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateContinuation");
537 nsContainerFrame* parent = aFrame->GetParent();
538 NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
540 nsresult rv = NS_OK;
542 // Have to special case floating first letter frames because the continuation
543 // doesn't go in the first letter frame. The continuation goes with the rest
544 // of the text that the first letter frame was made out of.
545 if (parent->GetType() == nsGkAtoms::letterFrame &&
546 parent->IsFloating()) {
547 nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
548 rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
549 aNewFrame, aIsFluid);
550 return rv;
553 *aNewFrame = presShell->FrameConstructor()->
554 CreateContinuingFrame(presContext, aFrame, parent, aIsFluid);
556 // The list name kNoReflowPrincipalList would indicate we don't want reflow
557 // XXXbz this needs higher-level framelist love
558 nsFrameList temp(*aNewFrame, *aNewFrame);
559 parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp);
561 if (!aIsFluid) {
562 // Split inline ancestor frames
563 rv = SplitInlineAncestors(parent, aFrame);
564 if (NS_FAILED(rv)) {
565 return rv;
569 return NS_OK;
573 * Overview of the implementation of Resolve():
575 * Walk through the descendants of aBlockFrame and build:
576 * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
577 * * mBuffer: an nsString containing a representation of
578 * the content of the frames.
579 * In the case of text frames, this is the actual text context of the
580 * frames, but some other elements are represented in a symbolic form which
581 * will make the Unicode Bidi Algorithm give the correct results.
582 * Bidi embeddings and overrides set by CSS or <bdo> elements are
583 * represented by the corresponding Unicode control characters.
584 * <br> elements are represented by U+2028 LINE SEPARATOR
585 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
586 * CHARACTER
588 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
589 * by nsBidi::SetPara() and division into directional runs by
590 * nsBidi::CountRuns().
592 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
593 * correlate them with the frames indexed in mLogicalFrames, setting the
594 * baseLevel and embeddingLevel properties according to the results returned
595 * by the Bidi engine.
597 * The rendering layer requires each text frame to contain text in only one
598 * direction, so we may need to call EnsureBidiContinuation() to split frames.
599 * We may also need to call RemoveBidiContinuation() to convert frames created
600 * by EnsureBidiContinuation() in previous reflows into fluid continuations.
602 nsresult
603 nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame)
605 BidiParagraphData bpd;
606 bpd.Init(aBlockFrame);
608 // Handle bidi-override being set on the block itself before calling
609 // TraverseFrames.
610 const nsStyleTextReset* text = aBlockFrame->StyleTextReset();
611 char16_t ch = 0;
612 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
613 const nsStyleVisibility* vis = aBlockFrame->StyleVisibility();
614 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
615 ch = kRLO;
617 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
618 ch = kLRO;
620 if (ch != 0) {
621 bpd.PushBidiControl(ch);
624 for (nsBlockFrame* block = aBlockFrame; block;
625 block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
626 block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
627 nsBlockInFlowLineIterator lineIter(block, block->begin_lines());
628 bpd.ResetForNewBlock();
629 TraverseFrames(aBlockFrame, &lineIter, block->GetFirstPrincipalChild(), &bpd);
630 // XXX what about overflow lines?
633 if (ch != 0) {
634 bpd.PopBidiControl();
637 BidiParagraphData* subParagraph = bpd.GetSubParagraph();
638 if (subParagraph->BufferLength()) {
639 ResolveParagraph(aBlockFrame, subParagraph);
640 subParagraph->EmptyBuffer();
642 return ResolveParagraph(aBlockFrame, &bpd);
645 nsresult
646 nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
647 BidiParagraphData* aBpd)
649 nsPresContext *presContext = aBlockFrame->PresContext();
651 if (aBpd->BufferLength() < 1) {
652 return NS_OK;
654 aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
656 int32_t runCount;
658 nsresult rv = aBpd->SetPara();
659 NS_ENSURE_SUCCESS(rv, rv);
661 nsBidiLevel embeddingLevel = aBpd->GetParaLevel();
663 rv = aBpd->CountRuns(&runCount);
664 NS_ENSURE_SUCCESS(rv, rv);
666 int32_t runLength = 0; // the length of the current run of text
667 int32_t lineOffset = 0; // the start of the current run
668 int32_t logicalLimit = 0; // the end of the current run + 1
669 int32_t numRun = -1;
670 int32_t fragmentLength = 0; // the length of the current text frame
671 int32_t frameIndex = -1; // index to the frames in mLogicalFrames
672 int32_t frameCount = aBpd->FrameCount();
673 int32_t contentOffset = 0; // offset of current frame in its content node
674 bool isTextFrame = false;
675 nsIFrame* frame = nullptr;
676 nsIContent* content = nullptr;
677 int32_t contentTextLength = 0;
679 FramePropertyTable *propTable = presContext->PropertyTable();
680 nsLineBox* currentLine = nullptr;
682 #ifdef DEBUG
683 #ifdef NOISY_BIDI
684 printf("Before Resolve(), aBlockFrame=0x%p, mBuffer='%s', frameCount=%d, runCount=%d\n",
685 (void*)aBlockFrame, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(), frameCount, runCount);
686 #ifdef REALLY_NOISY_BIDI
687 printf(" block frame tree=:\n");
688 aBlockFrame->List(stdout, 0);
689 #endif
690 #endif
691 #endif
693 if (runCount == 1 && frameCount == 1 &&
694 aBpd->mParagraphDepth == 0 && aBpd->GetDirection() == NSBIDI_LTR &&
695 aBpd->GetParaLevel() == 0) {
696 // We have a single left-to-right frame in a left-to-right paragraph,
697 // without bidi isolation from the surrounding text.
698 // Make sure that the embedding level and base level frame properties aren't
699 // set (because if they are this frame used to have some other direction,
700 // so we can't do this optimization), and we're done.
701 nsIFrame* frame = aBpd->FrameAt(0);
702 if (frame != NS_BIDI_CONTROL_FRAME &&
703 !frame->Properties().Get(nsIFrame::EmbeddingLevelProperty()) &&
704 !frame->Properties().Get(nsIFrame::BaseLevelProperty())) {
705 #ifdef DEBUG
706 #ifdef NOISY_BIDI
707 printf("early return for single direction frame %p\n", (void*)frame);
708 #endif
709 #endif
710 frame->AddStateBits(NS_FRAME_IS_BIDI);
711 return NS_OK;
715 nsIFrame* firstFrame = nullptr;
716 nsIFrame* lastFrame = nullptr;
718 for (; ;) {
719 if (fragmentLength <= 0) {
720 // Get the next frame from mLogicalFrames
721 if (++frameIndex >= frameCount) {
722 break;
724 frame = aBpd->FrameAt(frameIndex);
725 if (frame == NS_BIDI_CONTROL_FRAME ||
726 nsGkAtoms::textFrame != frame->GetType()) {
728 * Any non-text frame corresponds to a single character in the text buffer
729 * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE)
731 isTextFrame = false;
732 fragmentLength = 1;
734 else {
735 if (!firstFrame) {
736 firstFrame = frame;
738 lastFrame = frame;
739 currentLine = aBpd->GetLineForFrameAt(frameIndex);
740 content = frame->GetContent();
741 if (!content) {
742 rv = NS_OK;
743 break;
745 contentTextLength = content->TextLength();
746 if (contentTextLength == 0) {
747 frame->AdjustOffsetsForBidi(0, 0);
748 // Set the base level and embedding level of the current run even
749 // on an empty frame. Otherwise frame reordering will not be correct.
750 propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
751 NS_INT32_TO_PTR(embeddingLevel));
752 propTable->Set(frame, nsIFrame::BaseLevelProperty(),
753 NS_INT32_TO_PTR(aBpd->GetParaLevel()));
754 propTable->Set(frame, nsIFrame::ParagraphDepthProperty(),
755 NS_INT32_TO_PTR(aBpd->mParagraphDepth));
756 continue;
758 int32_t start, end;
759 frame->GetOffsets(start, end);
760 NS_ASSERTION(!(contentTextLength < end - start),
761 "Frame offsets don't fit in content");
762 fragmentLength = std::min(contentTextLength, end - start);
763 contentOffset = start;
764 isTextFrame = true;
766 } // if (fragmentLength <= 0)
768 if (runLength <= 0) {
769 // Get the next run of text from the Bidi engine
770 if (++numRun >= runCount) {
771 break;
773 lineOffset = logicalLimit;
774 if (NS_FAILED(aBpd->GetLogicalRun(
775 lineOffset, &logicalLimit, &embeddingLevel) ) ) {
776 break;
778 runLength = logicalLimit - lineOffset;
779 } // if (runLength <= 0)
781 if (frame == NS_BIDI_CONTROL_FRAME) {
782 frame = nullptr;
783 ++lineOffset;
785 else {
786 propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
787 NS_INT32_TO_PTR(embeddingLevel));
788 propTable->Set(frame, nsIFrame::BaseLevelProperty(),
789 NS_INT32_TO_PTR(aBpd->GetParaLevel()));
790 propTable->Set(frame, nsIFrame::ParagraphDepthProperty(),
791 NS_INT32_TO_PTR(aBpd->mParagraphDepth));
792 if (isTextFrame) {
793 if ( (runLength > 0) && (runLength < fragmentLength) ) {
795 * The text in this frame continues beyond the end of this directional run.
796 * Create a non-fluid continuation frame for the next directional run.
798 currentLine->MarkDirty();
799 nsIFrame* nextBidi;
800 int32_t runEnd = contentOffset + runLength;
801 rv = EnsureBidiContinuation(frame, &nextBidi, frameIndex,
802 contentOffset,
803 runEnd);
804 if (NS_FAILED(rv)) {
805 break;
807 nextBidi->AdjustOffsetsForBidi(runEnd,
808 contentOffset + fragmentLength);
809 lastFrame = frame = nextBidi;
810 contentOffset = runEnd;
811 } // if (runLength < fragmentLength)
812 else {
813 if (contentOffset + fragmentLength == contentTextLength) {
815 * We have finished all the text in this content node. Convert any
816 * further non-fluid continuations to fluid continuations and advance
817 * frameIndex to the last frame in the content node
819 int32_t newIndex = aBpd->GetLastFrameForContent(content);
820 if (newIndex > frameIndex) {
821 currentLine->MarkDirty();
822 RemoveBidiContinuation(aBpd, frame,
823 frameIndex, newIndex, lineOffset);
824 frameIndex = newIndex;
825 lastFrame = frame = aBpd->FrameAt(frameIndex);
827 } else if (fragmentLength > 0 && runLength > fragmentLength) {
829 * There is more text that belongs to this directional run in the next
830 * text frame: make sure it is a fluid continuation of the current frame.
831 * Do not advance frameIndex, because the next frame may contain
832 * multi-directional text and need to be split
834 int32_t newIndex = frameIndex;
835 do {
836 } while (++newIndex < frameCount &&
837 aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME);
838 if (newIndex < frameCount) {
839 currentLine->MarkDirty();
840 RemoveBidiContinuation(aBpd, frame,
841 frameIndex, newIndex, lineOffset);
843 } else if (runLength == fragmentLength) {
845 * If the directional run ends at the end of the frame, make sure
846 * that any continuation is non-fluid, and do the same up the
847 * parent chain
849 nsIFrame* next = frame->GetNextInFlow();
850 if (next) {
851 currentLine->MarkDirty();
852 MakeContinuationsNonFluidUpParentChain(frame, next);
855 frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
857 } // isTextFrame
858 else {
859 ++lineOffset;
861 } // not bidi control frame
862 int32_t temp = runLength;
863 runLength -= fragmentLength;
864 fragmentLength -= temp;
866 if (frame && fragmentLength <= 0) {
867 // If the frame is at the end of a run, and this is not the end of our
868 // paragrah, split all ancestor inlines that need splitting.
869 // To determine whether we're at the end of the run, we check that we've
870 // finished processing the current run, and that the current frame
871 // doesn't have a fluid continuation (it could have a fluid continuation
872 // of zero length, so testing runLength alone is not sufficient).
873 if (runLength <= 0 && !frame->GetNextInFlow()) {
874 if (numRun + 1 < runCount) {
875 nsIFrame* child = frame;
876 nsContainerFrame* parent = frame->GetParent();
877 // As long as we're on the last sibling, the parent doesn't have to
878 // be split.
879 // However, if the parent has a fluid continuation, we do have to make
880 // it non-fluid. This can happen e.g. when we have a first-letter
881 // frame and the end of the first-letter coincides with the end of a
882 // directional run.
883 while (parent &&
884 IsBidiSplittable(parent) &&
885 !child->GetNextSibling()) {
886 nsIFrame* next = parent->GetNextInFlow();
887 if (next) {
888 parent->SetNextContinuation(next);
889 next->SetPrevContinuation(parent);
891 child = parent;
892 parent = child->GetParent();
894 if (parent && IsBidiSplittable(parent)) {
895 SplitInlineAncestors(parent, child);
899 else {
900 // We're not at an end of a run. If |frame| is the last child of its
901 // parent, and its ancestors happen to have bidi continuations, convert
902 // them into fluid continuations.
903 JoinInlineAncestors(frame);
906 } // for
908 if (aBpd->mParagraphDepth > 0) {
909 if (firstFrame) {
910 nsContainerFrame* child = firstFrame->GetParent();
911 if (child) {
912 nsContainerFrame* parent = child->GetParent();
913 if (parent && IsBidiSplittable(parent)) {
914 nsIFrame* prev = child->GetPrevSibling();
915 if (prev) {
916 SplitInlineAncestors(parent, prev);
921 if (lastFrame) {
922 nsContainerFrame* child = lastFrame->GetParent();
923 if (child) {
924 nsContainerFrame* parent = child->GetParent();
925 if (parent && IsBidiSplittable(parent)) {
926 SplitInlineAncestors(parent, child);
932 #ifdef DEBUG
933 #ifdef REALLY_NOISY_BIDI
934 printf("---\nAfter Resolve(), frameTree =:\n");
935 aBlockFrame->List(stdout, 0);
936 printf("===\n");
937 #endif
938 #endif
940 return rv;
943 void
944 nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame,
945 nsBlockInFlowLineIterator* aLineIter,
946 nsIFrame* aCurrentFrame,
947 BidiParagraphData* aBpd)
949 if (!aCurrentFrame)
950 return;
952 #ifdef DEBUG
953 nsBlockFrame* initialLineContainer = aLineIter->GetContainer();
954 #endif
956 nsIFrame* childFrame = aCurrentFrame;
957 do {
959 * It's important to get the next sibling and next continuation *before*
960 * handling the frame: If we encounter a forced paragraph break and call
961 * ResolveParagraph within this loop, doing GetNextSibling and
962 * GetNextContinuation after that could return a bidi continuation that had
963 * just been split from the original childFrame and we would process it
964 * twice.
966 nsIFrame* nextSibling = childFrame->GetNextSibling();
967 bool isLastFrame = !childFrame->GetNextContinuation();
968 bool isFirstFrame = !childFrame->GetPrevContinuation();
970 // If the real frame for a placeholder is a first letter frame, we need to
971 // drill down into it and include its contents in Bidi resolution.
972 // If not, we just use the placeholder.
973 nsIFrame* frame = childFrame;
974 if (nsGkAtoms::placeholderFrame == childFrame->GetType()) {
975 nsIFrame* realFrame =
976 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
977 if (realFrame->GetType() == nsGkAtoms::letterFrame) {
978 frame = realFrame;
982 char16_t ch = 0;
983 if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
984 if (!(frame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
985 nsContainerFrame* c = static_cast<nsContainerFrame*>(frame);
986 MOZ_ASSERT(c = do_QueryFrame(frame),
987 "eBidiInlineContainer must be a nsContainerFrame subclass");
988 c->DrainSelfOverflowList();
991 const nsStyleVisibility* vis = frame->StyleVisibility();
992 const nsStyleTextReset* text = frame->StyleTextReset();
993 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
994 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
995 ch = kRLO;
997 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
998 ch = kLRO;
1000 } else if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_EMBED) {
1001 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
1002 ch = kRLE;
1004 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
1005 ch = kLRE;
1009 // Add a dummy frame pointer representing a bidi control code before the
1010 // first frame of an element specifying embedding or override
1011 if (ch != 0 && isFirstFrame) {
1012 aBpd->PushBidiControl(ch);
1016 if (IsBidiLeaf(frame)) {
1017 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
1018 * and add its index to the mContentToFrameIndex hashtable. This
1019 * will be used in RemoveBidiContinuation() to identify the last
1020 * frame in the array with a given content.
1022 nsIContent* content = frame->GetContent();
1023 aBpd->AppendFrame(frame, aLineIter, content);
1025 // Append the content of the frame to the paragraph buffer
1026 nsIAtom* frameType = frame->GetType();
1027 if (nsGkAtoms::textFrame == frameType) {
1028 if (content != aBpd->mPrevContent) {
1029 aBpd->mPrevContent = content;
1030 if (!frame->StyleText()->NewlineIsSignificant()) {
1031 content->AppendTextTo(aBpd->mBuffer);
1032 } else {
1034 * For preformatted text we have to do bidi resolution on each line
1035 * separately.
1037 nsAutoString text;
1038 content->AppendTextTo(text);
1039 nsIFrame* next;
1040 do {
1041 next = nullptr;
1043 int32_t start, end;
1044 frame->GetOffsets(start, end);
1045 int32_t endLine = text.FindChar('\n', start);
1046 if (endLine == -1) {
1048 * If there is no newline in the text content, just save the
1049 * text from this frame and its continuations, and do bidi
1050 * resolution later
1052 aBpd->AppendString(Substring(text, start));
1053 while (frame && nextSibling) {
1054 aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1056 break;
1060 * If there is a newline in the frame, break the frame after the
1061 * newline, do bidi resolution and repeat until the last sibling
1063 ++endLine;
1066 * If the frame ends before the new line, save the text and move
1067 * into the next continuation
1069 aBpd->AppendString(Substring(text, start,
1070 std::min(end, endLine) - start));
1071 while (end < endLine && nextSibling) {
1072 aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1073 NS_ASSERTION(frame, "Premature end of continuation chain");
1074 frame->GetOffsets(start, end);
1075 aBpd->AppendString(Substring(text, start,
1076 std::min(end, endLine) - start));
1079 if (end < endLine) {
1080 aBpd->mPrevContent = nullptr;
1081 break;
1084 bool createdContinuation = false;
1085 if (uint32_t(endLine) < text.Length()) {
1087 * Timing is everything here: if the frame already has a bidi
1088 * continuation, we need to make the continuation fluid *before*
1089 * resetting the length of the current frame. Otherwise
1090 * nsTextFrame::SetLength won't set the continuation frame's
1091 * text offsets correctly.
1093 * On the other hand, if the frame doesn't have a continuation,
1094 * we need to create one *after* resetting the length, or
1095 * CreateContinuingFrame will complain that there is no more
1096 * content for the continuation.
1098 next = frame->GetNextInFlow();
1099 if (!next) {
1100 // If the frame already has a bidi continuation, make it fluid
1101 next = frame->GetNextContinuation();
1102 if (next) {
1103 MakeContinuationFluid(frame, next);
1104 JoinInlineAncestors(frame);
1108 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1109 textFrame->SetLength(endLine - start, nullptr);
1111 if (!next) {
1112 // If the frame has no next in flow, create one.
1113 CreateContinuation(frame, &next, true);
1114 createdContinuation = true;
1116 // Mark the line before the newline as dirty.
1117 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
1119 ResolveParagraphWithinBlock(aBlockFrame, aBpd);
1121 if (!nextSibling && !createdContinuation) {
1122 break;
1123 } else if (next) {
1124 frame = next;
1125 aBpd->AppendFrame(frame, aLineIter);
1126 // Mark the line after the newline as dirty.
1127 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
1131 * If we have already overshot the saved next-sibling while
1132 * scanning the frame's continuations, advance it.
1134 if (frame && frame == nextSibling) {
1135 nextSibling = frame->GetNextSibling();
1138 } while (next);
1141 } else if (nsGkAtoms::brFrame == frameType) {
1142 // break frame -- append line separator
1143 aBpd->AppendUnichar(kLineSeparator);
1144 ResolveParagraphWithinBlock(aBlockFrame, aBpd);
1145 } else {
1146 // other frame type -- see the Unicode Bidi Algorithm:
1147 // "...inline objects (such as graphics) are treated as if they are ...
1148 // U+FFFC"
1149 // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
1150 // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
1151 aBpd->AppendUnichar(content->IsHTML(nsGkAtoms::wbr) ?
1152 kZWSP : kObjectSubstitute);
1153 if (!frame->IsInlineOutside()) {
1154 // if it is not inline, end the paragraph
1155 ResolveParagraphWithinBlock(aBlockFrame, aBpd);
1158 } else {
1159 // For a non-leaf frame, recurse into TraverseFrames
1160 nsIFrame* kid = frame->GetFirstPrincipalChild();
1161 MOZ_ASSERT(!frame->GetFirstChild(nsIFrame::kOverflowList),
1162 "should have drained the overflow list above");
1163 if (kid) {
1164 const nsStyleTextReset* text = frame->StyleTextReset();
1165 if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_ISOLATE ||
1166 text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
1167 // css "unicode-bidi: isolate" and html5 bdi:
1168 // resolve the element as a separate paragraph
1169 BidiParagraphData* subParagraph = aBpd->GetSubParagraph();
1172 * As at the beginning of the loop, it's important to check for
1173 * next-continuations before handling the frame. If we do
1174 * TraverseFrames and *then* do GetNextContinuation on the original
1175 * first frame, it could return a bidi continuation that had only
1176 * just been created, and we would skip doing bidi resolution on the
1177 * last part of the sub-paragraph.
1179 bool isLastContinuation = !frame->GetNextContinuation();
1180 if (!frame->GetPrevContinuation() || !subParagraph->mReset) {
1181 if (subParagraph->BufferLength()) {
1182 ResolveParagraph(aBlockFrame, subParagraph);
1184 subParagraph->Reset(frame, aBpd);
1186 TraverseFrames(aBlockFrame, aLineIter, kid, subParagraph);
1187 if (isLastContinuation) {
1188 ResolveParagraph(aBlockFrame, subParagraph);
1189 subParagraph->EmptyBuffer();
1192 // Treat the element as a neutral character within its containing
1193 // paragraph.
1194 aBpd->AppendControlChar(kObjectSubstitute);
1195 } else {
1196 TraverseFrames(aBlockFrame, aLineIter, kid, aBpd);
1201 // If the element is attributed by dir, indicate direction pop (add PDF frame)
1202 if (isLastFrame) {
1203 if (ch) {
1204 // Add a dummy frame pointer representing a bidi control code after the
1205 // last frame of an element specifying embedding or override
1206 aBpd->PopBidiControl();
1209 childFrame = nextSibling;
1210 } while (childFrame);
1212 MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer());
1215 void
1216 nsBidiPresUtils::ResolveParagraphWithinBlock(nsBlockFrame* aBlockFrame,
1217 BidiParagraphData* aBpd)
1219 aBpd->ClearBidiControls();
1220 ResolveParagraph(aBlockFrame, aBpd);
1221 aBpd->ResetData();
1224 void
1225 nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine,
1226 int32_t aNumFramesOnLine,
1227 WritingMode aLineWM,
1228 nscoord aLineWidth,
1229 nscoord aStart)
1231 // If this line consists of a line frame, reorder the line frame's children.
1232 if (aFirstFrameOnLine->GetType() == nsGkAtoms::lineFrame) {
1233 aFirstFrameOnLine = aFirstFrameOnLine->GetFirstPrincipalChild();
1234 if (!aFirstFrameOnLine)
1235 return;
1236 // All children of the line frame are on the first line. Setting aNumFramesOnLine
1237 // to -1 makes InitLogicalArrayFromLine look at all of them.
1238 aNumFramesOnLine = -1;
1241 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1242 RepositionInlineFrames(&bld, aFirstFrameOnLine, aLineWM, aLineWidth, aStart);
1245 nsIFrame*
1246 nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame)
1248 nsIFrame* firstLeaf = aFrame;
1249 while (!IsBidiLeaf(firstLeaf)) {
1250 nsIFrame* firstChild = firstLeaf->GetFirstPrincipalChild();
1251 nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild);
1252 firstLeaf = (realFrame->GetType() == nsGkAtoms::letterFrame) ?
1253 realFrame : firstChild;
1255 return firstLeaf;
1258 nsBidiLevel
1259 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
1261 return NS_GET_EMBEDDING_LEVEL(nsBidiPresUtils::GetFirstLeaf(aFrame));
1264 uint8_t
1265 nsBidiPresUtils::GetParagraphDepth(nsIFrame* aFrame)
1267 return NS_GET_PARAGRAPH_DEPTH(nsBidiPresUtils::GetFirstLeaf(aFrame));
1271 nsBidiLevel
1272 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame)
1274 nsIFrame* firstLeaf = aFrame;
1275 while (!IsBidiLeaf(firstLeaf)) {
1276 firstLeaf = firstLeaf->GetFirstPrincipalChild();
1278 return NS_GET_BASE_LEVEL(firstLeaf);
1281 void
1282 nsBidiPresUtils::IsFirstOrLast(nsIFrame* aFrame,
1283 nsContinuationStates* aContinuationStates,
1284 bool aSpanDirMatchesLineDir,
1285 bool& aIsFirst /* out */,
1286 bool& aIsLast /* out */)
1289 * Since we lay out frames in the line's direction, visiting a frame with
1290 * 'mFirstVisualFrame == nullptr', means it's the first appearance of one
1291 * of its continuation chain frames on the line.
1292 * To determine if it's the last visual frame of its continuation chain on
1293 * the line or not, we count the number of frames of the chain on the line,
1294 * and then reduce it when we lay out a frame of the chain. If this value
1295 * becomes 1 it means that it's the last visual frame of its continuation
1296 * chain on this line.
1299 bool firstInLineOrder, lastInLineOrder;
1300 nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
1301 nsFrameContinuationState* firstFrameState;
1303 if (!frameState->mFirstVisualFrame) {
1304 // aFrame is the first visual frame of its continuation chain
1305 nsFrameContinuationState* contState;
1306 nsIFrame* frame;
1308 frameState->mFrameCount = 1;
1309 frameState->mFirstVisualFrame = aFrame;
1312 * Traverse continuation chain of aFrame in both backward and forward
1313 * directions while the frames are on this line. Count the frames and
1314 * set their mFirstVisualFrame to aFrame.
1316 // Traverse continuation chain backward
1317 for (frame = aFrame->GetPrevContinuation();
1318 frame && (contState = aContinuationStates->GetEntry(frame));
1319 frame = frame->GetPrevContinuation()) {
1320 frameState->mFrameCount++;
1321 contState->mFirstVisualFrame = aFrame;
1323 frameState->mHasContOnPrevLines = (frame != nullptr);
1325 // Traverse continuation chain forward
1326 for (frame = aFrame->GetNextContinuation();
1327 frame && (contState = aContinuationStates->GetEntry(frame));
1328 frame = frame->GetNextContinuation()) {
1329 frameState->mFrameCount++;
1330 contState->mFirstVisualFrame = aFrame;
1332 frameState->mHasContOnNextLines = (frame != nullptr);
1334 firstInLineOrder = true;
1335 firstFrameState = frameState;
1336 } else {
1337 // aFrame is not the first visual frame of its continuation chain
1338 firstInLineOrder = false;
1339 firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
1342 lastInLineOrder = (firstFrameState->mFrameCount == 1);
1344 if (aSpanDirMatchesLineDir) {
1345 aIsFirst = firstInLineOrder;
1346 aIsLast = lastInLineOrder;
1347 } else {
1348 aIsFirst = lastInLineOrder;
1349 aIsLast = firstInLineOrder;
1352 if (frameState->mHasContOnPrevLines) {
1353 aIsFirst = false;
1355 if (firstFrameState->mHasContOnNextLines) {
1356 aIsLast = false;
1359 if ((aIsFirst || aIsLast) &&
1360 (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1361 // For ib splits, don't treat anything except the last part as
1362 // endmost or anything except the first part as startmost.
1363 // As an optimization, only get the first continuation once.
1364 nsIFrame* firstContinuation = aFrame->FirstContinuation();
1365 if (firstContinuation->FrameIsNonLastInIBSplit()) {
1366 // We are not endmost
1367 aIsLast = false;
1369 if (firstContinuation->FrameIsNonFirstInIBSplit()) {
1370 // We are not startmost
1371 aIsFirst = false;
1375 // Reduce number of remaining frames of the continuation chain on the line.
1376 firstFrameState->mFrameCount--;
1378 nsInlineFrame* testFrame = do_QueryFrame(aFrame);
1380 if (testFrame) {
1381 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
1383 if (aIsFirst) {
1384 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
1385 } else {
1386 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
1389 if (aIsLast) {
1390 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
1391 } else {
1392 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
1397 void
1398 nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
1399 bool aIsEvenLevel,
1400 nscoord& aStart,
1401 nsContinuationStates* aContinuationStates,
1402 WritingMode aContainerWM,
1403 nscoord aContainerWidth)
1405 if (!aFrame)
1406 return;
1408 bool isFirst, isLast;
1409 WritingMode frameWM = aFrame->GetWritingMode();
1410 IsFirstOrLast(aFrame,
1411 aContinuationStates,
1412 aContainerWM.IsBidiLTR() == frameWM.IsBidiLTR(),
1413 isFirst /* out */,
1414 isLast /* out */);
1416 // We only need the margin if the frame is first or last in its own
1417 // writing mode, but we're traversing the frames in the order of the
1418 // container's writing mode. To get the right values, we set start and
1419 // end margins on a logical margin in the frame's writing mode, and
1420 // then convert the margin to the container's writing mode to set the
1421 // coordinates.
1423 // This method is called from nsBlockFrame::PlaceLine via the call to
1424 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1425 // have been reflowed, which is required for GetUsedMargin/Border/Padding
1426 LogicalMargin frameMargin = aFrame->GetLogicalUsedMargin(frameWM);
1427 LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(frameWM);
1428 if (!isFirst) {
1429 frameMargin.IStart(frameWM) = 0;
1430 borderPadding.IStart(frameWM) = 0;
1432 if (!isLast) {
1433 frameMargin.IEnd(frameWM) = 0;
1434 borderPadding.IEnd(frameWM) = 0;
1436 LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM);
1437 aStart += margin.IStart(aContainerWM);
1439 nscoord start = aStart;
1441 if (!IsBidiLeaf(aFrame)) {
1442 // If the resolved direction of the container is different from the
1443 // direction of the frame, we need to traverse the child list in reverse
1444 // order, to make it O(n) we store the list locally and iterate the list
1445 // in reverse
1446 bool reverseOrder = aIsEvenLevel != frameWM.IsBidiLTR();
1447 nsTArray<nsIFrame*> childList;
1448 nsIFrame *frame = aFrame->GetFirstPrincipalChild();
1449 if (frame && reverseOrder) {
1450 childList.AppendElement((nsIFrame*)nullptr);
1451 while (frame) {
1452 childList.AppendElement(frame);
1453 frame = frame->GetNextSibling();
1455 frame = childList[childList.Length() - 1];
1458 // Reposition the child frames
1459 int32_t index = 0;
1460 nscoord iCoord = borderPadding.IStart(frameWM);
1462 while (frame) {
1463 RepositionFrame(frame,
1464 aIsEvenLevel,
1465 iCoord,
1466 aContinuationStates,
1467 frameWM,
1468 aFrame->GetLogicalSize(aContainerWM).Width(aContainerWM));
1469 index++;
1470 frame = reverseOrder ?
1471 childList[childList.Length() - index - 1] :
1472 frame->GetNextSibling();
1475 aStart += iCoord + borderPadding.IEnd(frameWM);
1476 } else {
1477 aStart += aFrame->ISize(aContainerWM);
1480 LogicalRect logicalRect = aFrame->GetLogicalRect(aContainerWM,
1481 aContainerWidth);
1482 logicalRect.IStart(aContainerWM) = start;
1483 logicalRect.ISize(aContainerWM) = aStart - start;
1484 aFrame->SetRect(aContainerWM, logicalRect, aContainerWidth);
1486 aStart += margin.IEnd(aContainerWM);
1489 void
1490 nsBidiPresUtils::InitContinuationStates(nsIFrame* aFrame,
1491 nsContinuationStates* aContinuationStates)
1493 nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame);
1494 state->mFirstVisualFrame = nullptr;
1495 state->mFrameCount = 0;
1497 if (!IsBidiLeaf(aFrame)) {
1498 // Continue for child frames
1499 nsIFrame* frame;
1500 for (frame = aFrame->GetFirstPrincipalChild();
1501 frame;
1502 frame = frame->GetNextSibling()) {
1503 InitContinuationStates(frame,
1504 aContinuationStates);
1509 void
1510 nsBidiPresUtils::RepositionInlineFrames(BidiLineData *aBld,
1511 nsIFrame* aFirstChild,
1512 WritingMode aLineWM,
1513 nscoord aLineWidth,
1514 nscoord aStart)
1516 nscoord start = aStart;
1517 nsIFrame* frame;
1518 int32_t count = aBld->mVisualFrames.Length();
1519 int32_t index;
1520 nsContinuationStates continuationStates;
1522 // Initialize continuation states to (nullptr, 0) for
1523 // each frame on the line.
1524 for (index = 0; index < count; index++) {
1525 InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
1528 // Reposition frames in visual order
1529 int32_t step, limit;
1530 if (aLineWM.IsBidiLTR()) {
1531 index = 0;
1532 step = 1;
1533 limit = count;
1534 } else {
1535 index = count - 1;
1536 step = -1;
1537 limit = -1;
1539 for (; index != limit; index += step) {
1540 frame = aBld->VisualFrameAt(index);
1541 RepositionFrame(frame,
1542 !(IS_LEVEL_RTL(aBld->mLevels[aBld->mIndexMap[index]])),
1543 start,
1544 &continuationStates,
1545 aLineWM,
1546 aLineWidth);
1550 bool
1551 nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
1552 int32_t aNumFramesOnLine,
1553 nsIFrame** aFirstVisual,
1554 nsIFrame** aLastVisual)
1556 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1557 int32_t count = bld.FrameCount();
1559 if (aFirstVisual) {
1560 *aFirstVisual = bld.VisualFrameAt(0);
1562 if (aLastVisual) {
1563 *aLastVisual = bld.VisualFrameAt(count-1);
1566 return bld.mIsReordered;
1569 nsIFrame*
1570 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
1571 nsIFrame* aFirstFrameOnLine,
1572 int32_t aNumFramesOnLine)
1574 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1576 int32_t count = bld.mVisualFrames.Length();
1578 if (aFrame == nullptr && count)
1579 return bld.VisualFrameAt(0);
1581 for (int32_t i = 0; i < count - 1; i++) {
1582 if (bld.VisualFrameAt(i) == aFrame) {
1583 return bld.VisualFrameAt(i+1);
1587 return nullptr;
1590 nsIFrame*
1591 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
1592 nsIFrame* aFirstFrameOnLine,
1593 int32_t aNumFramesOnLine)
1595 BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1597 int32_t count = bld.mVisualFrames.Length();
1599 if (aFrame == nullptr && count)
1600 return bld.VisualFrameAt(count-1);
1602 for (int32_t i = 1; i < count; i++) {
1603 if (bld.VisualFrameAt(i) == aFrame) {
1604 return bld.VisualFrameAt(i-1);
1608 return nullptr;
1611 inline nsresult
1612 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
1613 nsIFrame** aNewFrame,
1614 int32_t& aFrameIndex,
1615 int32_t aStart,
1616 int32_t aEnd)
1618 NS_PRECONDITION(aNewFrame, "null OUT ptr");
1619 NS_PRECONDITION(aFrame, "aFrame is null");
1621 aFrame->AdjustOffsetsForBidi(aStart, aEnd);
1622 return CreateContinuation(aFrame, aNewFrame, false);
1625 void
1626 nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
1627 nsIFrame* aFrame,
1628 int32_t aFirstIndex,
1629 int32_t aLastIndex,
1630 int32_t& aOffset)
1632 FrameProperties props = aFrame->Properties();
1633 nsBidiLevel embeddingLevel =
1634 (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::EmbeddingLevelProperty()));
1635 nsBidiLevel baseLevel =
1636 (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::BaseLevelProperty()));
1637 uint8_t paragraphDepth =
1638 NS_PTR_TO_INT32(props.Get(nsIFrame::ParagraphDepthProperty()));
1640 for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
1641 nsIFrame* frame = aBpd->FrameAt(index);
1642 if (frame == NS_BIDI_CONTROL_FRAME) {
1643 ++aOffset;
1645 else {
1646 // Make the frame and its continuation ancestors fluid,
1647 // so they can be reused or deleted by normal reflow code
1648 FrameProperties frameProps = frame->Properties();
1649 frameProps.Set(nsIFrame::EmbeddingLevelProperty(),
1650 NS_INT32_TO_PTR(embeddingLevel));
1651 frameProps.Set(nsIFrame::BaseLevelProperty(),
1652 NS_INT32_TO_PTR(baseLevel));
1653 frameProps.Set(nsIFrame::ParagraphDepthProperty(),
1654 NS_INT32_TO_PTR(paragraphDepth));
1655 frame->AddStateBits(NS_FRAME_IS_BIDI);
1656 while (frame) {
1657 nsIFrame* prev = frame->GetPrevContinuation();
1658 if (prev) {
1659 MakeContinuationFluid(prev, frame);
1660 frame = frame->GetParent();
1661 } else {
1662 break;
1668 // Make sure that the last continuation we made fluid does not itself have a
1669 // fluid continuation (this can happen when re-resolving after dynamic changes
1670 // to content)
1671 nsIFrame* lastFrame = aBpd->FrameAt(aLastIndex);
1672 MakeContinuationsNonFluidUpParentChain(lastFrame, lastFrame->GetNextInFlow());
1675 nsresult
1676 nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
1677 char16_t* aText,
1678 int32_t& aTextLength,
1679 nsCharType aCharType,
1680 nsBidiDirection aDir)
1682 nsresult rv = NS_OK;
1683 // ahmed
1684 //adjusted for correct numeral shaping
1685 uint32_t bidiOptions = aPresContext->GetBidi();
1686 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
1688 case IBMBIDI_NUMERAL_HINDI:
1689 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1690 break;
1692 case IBMBIDI_NUMERAL_ARABIC:
1693 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1694 break;
1696 case IBMBIDI_NUMERAL_PERSIAN:
1697 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
1698 break;
1700 case IBMBIDI_NUMERAL_REGULAR:
1702 switch (aCharType) {
1704 case eCharType_EuropeanNumber:
1705 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1706 break;
1708 case eCharType_ArabicNumber:
1709 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1710 break;
1712 default:
1713 break;
1715 break;
1717 case IBMBIDI_NUMERAL_HINDICONTEXT:
1718 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
1719 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1720 else if (eCharType_EuropeanNumber == aCharType)
1721 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1722 break;
1724 case IBMBIDI_NUMERAL_PERSIANCONTEXT:
1725 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
1726 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
1727 else if (eCharType_EuropeanNumber == aCharType)
1728 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1729 break;
1731 case IBMBIDI_NUMERAL_NOMINAL:
1732 default:
1733 break;
1736 StripBidiControlCharacters(aText, aTextLength);
1737 return rv;
1740 void
1741 nsBidiPresUtils::StripBidiControlCharacters(char16_t* aText,
1742 int32_t& aTextLength)
1744 if ( (nullptr == aText) || (aTextLength < 1) ) {
1745 return;
1748 int32_t stripLen = 0;
1750 for (int32_t i = 0; i < aTextLength; i++) {
1751 // XXX: This silently ignores surrogate characters.
1752 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1753 if (IsBidiControl((uint32_t)aText[i])) {
1754 ++stripLen;
1756 else {
1757 aText[i - stripLen] = aText[i];
1760 aTextLength -= stripLen;
1763 #if 0 // XXX: for the future use ???
1764 void
1765 RemoveDiacritics(char16_t* aText,
1766 int32_t& aTextLength)
1768 if (aText && (aTextLength > 0) ) {
1769 int32_t offset = 0;
1771 for (int32_t i = 0; i < aTextLength && aText[i]; i++) {
1772 if (IS_BIDI_DIACRITIC(aText[i]) ) {
1773 ++offset;
1774 continue;
1776 aText[i - offset] = aText[i];
1778 aTextLength = i - offset;
1779 aText[aTextLength] = 0;
1782 #endif
1784 void
1785 nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine,
1786 const char16_t* aText,
1787 int32_t& aOffset,
1788 int32_t aCharTypeLimit,
1789 int32_t& aRunLimit,
1790 int32_t& aRunLength,
1791 int32_t& aRunCount,
1792 uint8_t& aCharType,
1793 uint8_t& aPrevCharType)
1796 bool strongTypeFound = false;
1797 int32_t offset;
1798 nsCharType charType;
1800 aCharType = eCharType_OtherNeutral;
1802 for (offset = aOffset; offset < aCharTypeLimit; offset++) {
1803 // Make sure we give RTL chartype to all characters that would be classified
1804 // as Right-To-Left by a bidi platform.
1805 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1806 if (IS_HEBREW_CHAR(aText[offset]) ) {
1807 charType = eCharType_RightToLeft;
1809 else if (IS_ARABIC_ALPHABETIC(aText[offset]) ) {
1810 charType = eCharType_RightToLeftArabic;
1812 else {
1813 aBidiEngine->GetCharTypeAt(offset, &charType);
1816 if (!CHARTYPE_IS_WEAK(charType) ) {
1818 if (strongTypeFound
1819 && (charType != aPrevCharType)
1820 && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
1821 // Stop at this point to ensure uni-directionality of the text
1822 // (from platform's point of view).
1823 // Also, don't mix Arabic and Hebrew content (since platform may
1824 // provide BIDI support to one of them only).
1825 aRunLength = offset - aOffset;
1826 aRunLimit = offset;
1827 ++aRunCount;
1828 break;
1831 if ( (eCharType_RightToLeftArabic == aPrevCharType
1832 || eCharType_ArabicNumber == aPrevCharType)
1833 && eCharType_EuropeanNumber == charType) {
1834 charType = eCharType_ArabicNumber;
1837 // Set PrevCharType to the last strong type in this frame
1838 // (for correct numeric shaping)
1839 aPrevCharType = charType;
1841 strongTypeFound = true;
1842 aCharType = charType;
1845 aOffset = offset;
1848 nsresult nsBidiPresUtils::ProcessText(const char16_t* aText,
1849 int32_t aLength,
1850 nsBidiLevel aBaseLevel,
1851 nsPresContext* aPresContext,
1852 BidiProcessor& aprocessor,
1853 Mode aMode,
1854 nsBidiPositionResolve* aPosResolve,
1855 int32_t aPosResolveCount,
1856 nscoord* aWidth,
1857 nsBidi* aBidiEngine)
1859 NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1861 int32_t runCount;
1863 nsAutoString textBuffer(aText, aLength);
1865 nsresult rv = aBidiEngine->SetPara(aText, aLength, aBaseLevel, nullptr);
1866 if (NS_FAILED(rv))
1867 return rv;
1869 rv = aBidiEngine->CountRuns(&runCount);
1870 if (NS_FAILED(rv))
1871 return rv;
1873 nscoord xOffset = 0;
1874 nscoord width, xEndRun = 0;
1875 nscoord totalWidth = 0;
1876 int32_t i, start, limit, length;
1877 uint32_t visualStart = 0;
1878 uint8_t charType;
1879 uint8_t prevType = eCharType_LeftToRight;
1881 for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
1883 aPosResolve[nPosResolve].visualIndex = kNotFound;
1884 aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
1885 aPosResolve[nPosResolve].visualWidth = kNotFound;
1888 for (i = 0; i < runCount; i++) {
1889 nsBidiDirection dir;
1890 rv = aBidiEngine->GetVisualRun(i, &start, &length, &dir);
1891 if (NS_FAILED(rv))
1892 return rv;
1894 nsBidiLevel level;
1895 rv = aBidiEngine->GetLogicalRun(start, &limit, &level);
1896 if (NS_FAILED(rv))
1897 return rv;
1899 dir = DIRECTION_FROM_LEVEL(level);
1900 int32_t subRunLength = limit - start;
1901 int32_t lineOffset = start;
1902 int32_t typeLimit = std::min(limit, aLength);
1903 int32_t subRunCount = 1;
1904 int32_t subRunLimit = typeLimit;
1907 * If |level| is even, i.e. the direction of the run is left-to-right, we
1908 * render the subruns from left to right and increment the x-coordinate
1909 * |xOffset| by the width of each subrun after rendering.
1911 * If |level| is odd, i.e. the direction of the run is right-to-left, we
1912 * render the subruns from right to left. We begin by incrementing |xOffset| by
1913 * the width of the whole run, and then decrement it by the width of each
1914 * subrun before rendering. After rendering all the subruns, we restore the
1915 * x-coordinate of the end of the run for the start of the next run.
1918 if (dir == NSBIDI_RTL) {
1919 aprocessor.SetText(aText + start, subRunLength, dir);
1920 width = aprocessor.GetWidth();
1921 xOffset += width;
1922 xEndRun = xOffset;
1925 while (subRunCount > 0) {
1926 // CalculateCharType can increment subRunCount if the run
1927 // contains mixed character types
1928 CalculateCharType(aBidiEngine, aText, lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
1930 nsAutoString runVisualText;
1931 runVisualText.Assign(aText + start, subRunLength);
1932 if (int32_t(runVisualText.Length()) < subRunLength)
1933 return NS_ERROR_OUT_OF_MEMORY;
1934 FormatUnicodeText(aPresContext, runVisualText.BeginWriting(),
1935 subRunLength, (nsCharType)charType, dir);
1937 aprocessor.SetText(runVisualText.get(), subRunLength, dir);
1938 width = aprocessor.GetWidth();
1939 totalWidth += width;
1940 if (dir == NSBIDI_RTL) {
1941 xOffset -= width;
1943 if (aMode == MODE_DRAW) {
1944 aprocessor.DrawText(xOffset, width);
1948 * The caller may request to calculate the visual position of one
1949 * or more characters.
1951 for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve)
1953 nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
1955 * Did we already resolve this position's visual metric? If so, skip.
1957 if (posResolve->visualLeftTwips != kNotFound)
1958 continue;
1961 * First find out if the logical position is within this run.
1963 if (start <= posResolve->logicalIndex &&
1964 start + subRunLength > posResolve->logicalIndex) {
1966 * If this run is only one character long, we have an easy case:
1967 * the visual position is the x-coord of the start of the run
1968 * less the x-coord of the start of the whole text.
1970 if (subRunLength == 1) {
1971 posResolve->visualIndex = visualStart;
1972 posResolve->visualLeftTwips = xOffset;
1973 posResolve->visualWidth = width;
1976 * Otherwise, we need to measure the width of the run's part
1977 * which is to the visual left of the index.
1978 * In other words, the run is broken in two, around the logical index,
1979 * and we measure the part which is visually left.
1980 * If the run is right-to-left, this part will span from after the index
1981 * up to the end of the run; if it is left-to-right, this part will span
1982 * from the start of the run up to (and inclduing) the character before the index.
1984 else {
1986 * Here is a description of how the width of the current character
1987 * (posResolve->visualWidth) is calculated:
1989 * LTR (current char: "P"):
1990 * S A M P L E (logical index: 3, visual index: 3)
1991 * ^ (visualLeftPart)
1992 * ^ (visualRightSide)
1993 * visualLeftLength == 3
1994 * ^^^^^^ (subWidth)
1995 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1996 * ^^ (posResolve->visualWidth)
1998 * RTL (current char: "M"):
1999 * E L P M A S (logical index: 2, visual index: 3)
2000 * ^ (visualLeftPart)
2001 * ^ (visualRightSide)
2002 * visualLeftLength == 3
2003 * ^^^^^^ (subWidth)
2004 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
2005 * ^^ (posResolve->visualWidth)
2007 nscoord subWidth;
2008 // The position in the text where this run's "left part" begins.
2009 const char16_t* visualLeftPart;
2010 const char16_t* visualRightSide;
2011 if (dir == NSBIDI_RTL) {
2012 // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
2013 posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
2014 // Skipping to the "left part".
2015 visualLeftPart = aText + posResolve->logicalIndex + 1;
2016 // Skipping to the right side of the current character
2017 visualRightSide = visualLeftPart - 1;
2019 else {
2020 posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
2021 // Skipping to the "left part".
2022 visualLeftPart = aText + start;
2023 // In LTR mode this is the same as visualLeftPart
2024 visualRightSide = visualLeftPart;
2026 // The delta between the start of the run and the left part's end.
2027 int32_t visualLeftLength = posResolve->visualIndex - visualStart;
2028 aprocessor.SetText(visualLeftPart, visualLeftLength, dir);
2029 subWidth = aprocessor.GetWidth();
2030 aprocessor.SetText(visualRightSide, visualLeftLength + 1, dir);
2031 posResolve->visualLeftTwips = xOffset + subWidth;
2032 posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
2037 if (dir == NSBIDI_LTR) {
2038 xOffset += width;
2041 --subRunCount;
2042 start = lineOffset;
2043 subRunLimit = typeLimit;
2044 subRunLength = typeLimit - lineOffset;
2045 } // while
2046 if (dir == NSBIDI_RTL) {
2047 xOffset = xEndRun;
2050 visualStart += length;
2051 } // for
2053 if (aWidth) {
2054 *aWidth = totalWidth;
2056 return NS_OK;
2059 class MOZ_STACK_CLASS nsIRenderingContextBidiProcessor MOZ_FINAL
2060 : public nsBidiPresUtils::BidiProcessor
2062 public:
2063 nsIRenderingContextBidiProcessor(nsRenderingContext* aCtx,
2064 nsRenderingContext* aTextRunConstructionContext,
2065 nsFontMetrics* aFontMetrics,
2066 const nsPoint& aPt)
2067 : mCtx(aCtx)
2068 , mTextRunConstructionContext(aTextRunConstructionContext)
2069 , mFontMetrics(aFontMetrics)
2070 , mPt(aPt)
2073 ~nsIRenderingContextBidiProcessor()
2075 mFontMetrics->SetTextRunRTL(false);
2078 virtual void SetText(const char16_t* aText,
2079 int32_t aLength,
2080 nsBidiDirection aDirection) MOZ_OVERRIDE
2082 mFontMetrics->SetTextRunRTL(aDirection==NSBIDI_RTL);
2083 mText = aText;
2084 mLength = aLength;
2087 virtual nscoord GetWidth() MOZ_OVERRIDE
2089 return nsLayoutUtils::AppUnitWidthOfString(mText, mLength, *mFontMetrics,
2090 *mTextRunConstructionContext);
2093 virtual void DrawText(nscoord aXOffset,
2094 nscoord) MOZ_OVERRIDE
2096 mFontMetrics->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y,
2097 mCtx, mTextRunConstructionContext);
2100 private:
2101 nsRenderingContext* mCtx;
2102 nsRenderingContext* mTextRunConstructionContext;
2103 nsFontMetrics* mFontMetrics;
2104 nsPoint mPt;
2105 const char16_t* mText;
2106 int32_t mLength;
2109 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const char16_t* aText,
2110 int32_t aLength,
2111 nsBidiLevel aBaseLevel,
2112 nsPresContext* aPresContext,
2113 nsRenderingContext& aRenderingContext,
2114 nsRenderingContext& aTextRunConstructionContext,
2115 nsFontMetrics& aFontMetrics,
2116 Mode aMode,
2117 nscoord aX,
2118 nscoord aY,
2119 nsBidiPositionResolve* aPosResolve,
2120 int32_t aPosResolveCount,
2121 nscoord* aWidth)
2123 nsIRenderingContextBidiProcessor processor(&aRenderingContext,
2124 &aTextRunConstructionContext,
2125 &aFontMetrics,
2126 nsPoint(aX, aY));
2127 nsBidi bidiEngine;
2128 return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor,
2129 aMode, aPosResolve, aPosResolveCount, aWidth, &bidiEngine);
2132 /* static */
2133 void nsBidiPresUtils::WriteReverse(const char16_t* aSrc,
2134 uint32_t aSrcLength,
2135 char16_t* aDest)
2137 char16_t* dest = aDest + aSrcLength;
2138 mozilla::unicode::ClusterIterator iter(aSrc, aSrcLength);
2140 while (!iter.AtEnd()) {
2141 iter.Next();
2142 for (const char16_t *cp = iter; cp > aSrc; ) {
2143 // Here we rely on the fact that there are no non-BMP mirrored pairs
2144 // currently in Unicode, so we don't need to look for surrogates
2145 *--dest = mozilla::unicode::GetMirroredChar(*--cp);
2147 aSrc = iter;
2150 NS_ASSERTION(dest == aDest, "Whole string not copied");
2153 /* static */
2154 bool nsBidiPresUtils::WriteLogicalToVisual(const char16_t* aSrc,
2155 uint32_t aSrcLength,
2156 char16_t* aDest,
2157 nsBidiLevel aBaseDirection,
2158 nsBidi* aBidiEngine)
2160 const char16_t* src = aSrc;
2161 nsresult rv = aBidiEngine->SetPara(src, aSrcLength, aBaseDirection, nullptr);
2162 if (NS_FAILED(rv)) {
2163 return false;
2166 nsBidiDirection dir;
2167 rv = aBidiEngine->GetDirection(&dir);
2168 // NSBIDI_LTR returned from GetDirection means the whole text is LTR
2169 if (NS_FAILED(rv) || dir == NSBIDI_LTR) {
2170 return false;
2173 int32_t runCount;
2174 rv = aBidiEngine->CountRuns(&runCount);
2175 if (NS_FAILED(rv)) {
2176 return false;
2179 int32_t runIndex, start, length;
2180 char16_t* dest = aDest;
2182 for (runIndex = 0; runIndex < runCount; ++runIndex) {
2183 rv = aBidiEngine->GetVisualRun(runIndex, &start, &length, &dir);
2184 if (NS_FAILED(rv)) {
2185 return false;
2188 src = aSrc + start;
2190 if (dir == NSBIDI_RTL) {
2191 WriteReverse(src, length, dest);
2192 dest += length;
2193 } else {
2194 do {
2195 NS_ASSERTION(src >= aSrc && src < aSrc + aSrcLength,
2196 "logical index out of range");
2197 NS_ASSERTION(dest < aDest + aSrcLength, "visual index out of range");
2198 *(dest++) = *(src++);
2199 } while (--length);
2203 NS_ASSERTION(static_cast<uint32_t>(dest - aDest) == aSrcLength,
2204 "whole string not copied");
2205 return true;
2208 void nsBidiPresUtils::CopyLogicalToVisual(const nsAString& aSource,
2209 nsAString& aDest,
2210 nsBidiLevel aBaseDirection,
2211 bool aOverride)
2213 aDest.SetLength(0);
2214 uint32_t srcLength = aSource.Length();
2215 if (srcLength == 0)
2216 return;
2217 if (!aDest.SetLength(srcLength, fallible_t())) {
2218 return;
2220 nsAString::const_iterator fromBegin, fromEnd;
2221 nsAString::iterator toBegin;
2222 aSource.BeginReading(fromBegin);
2223 aSource.EndReading(fromEnd);
2224 aDest.BeginWriting(toBegin);
2226 if (aOverride) {
2227 if (aBaseDirection == NSBIDI_RTL) {
2228 // no need to use the converter -- just copy the string in reverse order
2229 WriteReverse(fromBegin.get(), srcLength, toBegin.get());
2230 } else {
2231 // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the
2232 // simple copy
2233 aDest.SetLength(0);
2235 } else {
2236 nsBidi bidiEngine;
2237 if (!WriteLogicalToVisual(fromBegin.get(), srcLength, toBegin.get(),
2238 aBaseDirection, &bidiEngine)) {
2239 aDest.SetLength(0);
2243 if (aDest.IsEmpty()) {
2244 // Either there was an error or the source is unidirectional
2245 // left-to-right. In either case, just copy source to dest.
2246 CopyUnicodeTo(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
2247 aDest);
2251 /* static */
2252 nsBidiLevel
2253 nsBidiPresUtils::BidiLevelFromStyle(nsStyleContext* aStyleContext)
2255 if (aStyleContext->StyleTextReset()->mUnicodeBidi &
2256 NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
2257 return NSBIDI_DEFAULT_LTR;
2260 if (aStyleContext->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
2261 return NSBIDI_RTL;
2264 return NSBIDI_LTR;