1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Uri Bernstein <uriber@gmail.com>
24 * Haamed Gheibi <gheibi@metanetworking.com>
25 * Ehsan Akhgari <ehsan.akhgari@gmail.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
43 #include "nsBidiPresUtils.h"
44 #include "nsTextFragment.h"
45 #include "nsGkAtoms.h"
46 #include "nsPresContext.h"
47 #include "nsIRenderingContext.h"
48 #include "nsIServiceManager.h"
49 #include "nsFrameManager.h"
50 #include "nsBidiFrames.h"
51 #include "nsBidiUtils.h"
52 #include "nsCSSFrameConstructor.h"
53 #include "nsHTMLContainerFrame.h"
54 #include "nsInlineFrame.h"
55 #include "nsPlaceholderFrame.h"
56 #include "nsContainerFrame.h"
57 #include "nsFirstLetterFrame.h"
58 #include "gfxUnicodeProperties.h"
59 #include "nsIThebesFontMetrics.h"
61 using namespace mozilla
;
63 static const PRUnichar kSpace
= 0x0020;
64 static const PRUnichar kLineSeparator
= 0x2028;
65 static const PRUnichar kObjectSubstitute
= 0xFFFC;
66 static const PRUnichar kLRE
= 0x202A;
67 static const PRUnichar kRLE
= 0x202B;
68 static const PRUnichar kLRO
= 0x202D;
69 static const PRUnichar kRLO
= 0x202E;
70 static const PRUnichar kPDF
= 0x202C;
71 static const PRUnichar ALEF
= 0x05D0;
73 #define CHAR_IS_HEBREW(c) ((0x0590 <= (c)) && ((c)<= 0x05FF))
74 // Note: The above code are moved from gfx/src/windows/nsRenderingContextWin.cpp
77 NS_NewDirectionalFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRUnichar aChar
);
79 nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
82 mSuccess(NS_ERROR_FAILURE
),
85 mBidiEngine
= new nsBidi();
86 if (mBidiEngine
&& mContentToFrameIndex
.Init()) {
91 nsBidiPresUtils::~nsBidiPresUtils()
103 nsBidiPresUtils::IsSuccessful() const
105 return NS_SUCCEEDED(mSuccess
);
108 /* Some helper methods for Resolve() */
110 // Should this frame be split between text runs?
112 IsBidiSplittable(nsIFrame
* aFrame
) {
113 nsIAtom
* frameType
= aFrame
->GetType();
114 // Bidi inline containers should be split, unless they're line frames.
115 return aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)
116 && frameType
!= nsGkAtoms::lineFrame
;
120 SplitInlineAncestors(nsIFrame
* aFrame
)
122 nsPresContext
*presContext
= aFrame
->PresContext();
123 nsIPresShell
*presShell
= presContext
->PresShell();
124 nsIFrame
* frame
= aFrame
;
125 nsIFrame
* parent
= aFrame
->GetParent();
128 while (IsBidiSplittable(parent
)) {
129 nsIFrame
* grandparent
= parent
->GetParent();
130 NS_ASSERTION(grandparent
, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
132 nsresult rv
= presShell
->FrameConstructor()->
133 CreateContinuingFrame(presContext
, parent
, grandparent
, &newParent
, PR_FALSE
);
138 // Split the child list after |frame|.
139 nsContainerFrame
* container
= do_QueryFrame(parent
);
140 nsFrameList tail
= container
->StealFramesAfter(frame
);
142 // Reparent views as necessary
143 rv
= nsHTMLContainerFrame::ReparentFrameViewList(presContext
, tail
, parent
, newParent
);
148 // The parent's continuation adopts the siblings after the split.
149 rv
= newParent
->InsertFrames(nsGkAtoms::nextBidi
, nsnull
, tail
);
153 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
154 nsFrameList
temp(newParent
, newParent
);
155 rv
= grandparent
->InsertFrames(nsGkAtoms::nextBidi
, parent
, temp
);
161 parent
= grandparent
;
167 // Convert bidi continuations to fluid continuations for a frame and all of its
170 JoinInlineAncestors(nsIFrame
* aFrame
)
172 nsIFrame
* frame
= aFrame
;
173 while (frame
&& IsBidiSplittable(frame
)) {
174 nsIFrame
* next
= frame
->GetNextContinuation();
176 NS_ASSERTION (!frame
->GetNextInFlow() || frame
->GetNextInFlow() == next
,
177 "next-in-flow is not next continuation!");
178 frame
->SetNextInFlow(next
);
180 NS_ASSERTION (!next
->GetPrevInFlow() || next
->GetPrevInFlow() == frame
,
181 "prev-in-flow is not prev continuation!");
182 next
->SetPrevInFlow(frame
);
184 // Join the parent only as long as we're its last child.
185 if (frame
->GetNextSibling())
187 frame
= frame
->GetParent();
192 CreateBidiContinuation(nsIFrame
* aFrame
,
193 nsIFrame
** aNewFrame
)
195 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
196 NS_PRECONDITION(aFrame
, "null ptr");
200 nsPresContext
*presContext
= aFrame
->PresContext();
201 nsIPresShell
*presShell
= presContext
->PresShell();
202 NS_ASSERTION(presShell
, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
204 nsIFrame
* parent
= aFrame
->GetParent();
205 NS_ASSERTION(parent
, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
209 // Have to special case floating first letter frames because the continuation
210 // doesn't go in the first letter frame. The continuation goes with the rest
211 // of the text that the first letter frame was made out of.
212 if (parent
->GetType() == nsGkAtoms::letterFrame
&&
213 parent
->GetStyleDisplay()->IsFloating()) {
214 nsFirstLetterFrame
* letterFrame
= do_QueryFrame(parent
);
215 rv
= letterFrame
->CreateContinuationForFloatingParent(presContext
, aFrame
,
216 aNewFrame
, PR_FALSE
);
220 rv
= presShell
->FrameConstructor()->
221 CreateContinuingFrame(presContext
, aFrame
, parent
, aNewFrame
, PR_FALSE
);
226 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
227 // XXXbz this needs higher-level framelist love
228 nsFrameList
temp(*aNewFrame
, *aNewFrame
);
229 rv
= parent
->InsertFrames(nsGkAtoms::nextBidi
, aFrame
, temp
);
234 // Split inline ancestor frames
235 rv
= SplitInlineAncestors(aFrame
);
244 IsFrameInCurrentLine(nsBlockInFlowLineIterator
* aLineIter
,
245 nsIFrame
* aPrevFrame
, nsIFrame
* aFrame
)
247 nsIFrame
* endFrame
= aLineIter
->IsLastLineInList() ? nsnull
:
248 aLineIter
->GetLine().next()->mFirstChild
;
249 nsIFrame
* startFrame
= aPrevFrame
? aPrevFrame
: aLineIter
->GetLine()->mFirstChild
;
250 for (nsIFrame
* frame
= startFrame
; frame
&& frame
!= endFrame
;
251 frame
= frame
->GetNextSibling()) {
259 AdvanceLineIteratorToFrame(nsIFrame
* aFrame
,
260 nsBlockInFlowLineIterator
* aLineIter
,
261 nsIFrame
*& aPrevFrame
)
263 // Advance aLine to the line containing aFrame
264 nsIFrame
* child
= aFrame
;
265 nsFrameManager
* frameManager
= aFrame
->PresContext()->FrameManager();
266 nsIFrame
* parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, child
);
267 while (parent
&& !nsLayoutUtils::GetAsBlock(parent
)) {
269 parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, child
);
271 NS_ASSERTION (parent
, "aFrame is not a descendent of aBlockFrame");
272 while (!IsFrameInCurrentLine(aLineIter
, aPrevFrame
, child
)) {
277 NS_ASSERTION(hasNext
, "Can't find frame in lines!");
284 * Overview of the implementation of Resolve():
286 * Walk through the descendants of aBlockFrame and build:
287 * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
288 * * mBuffer: an nsAutoString containing a representation of
289 * the content of the frames.
290 * In the case of text frames, this is the actual text context of the
291 * frames, but some other elements are represented in a symbolic form which
292 * will make the Unicode Bidi Algorithm give the correct results.
293 * Bidi embeddings and overrides set by CSS or <bdo> elements are
294 * represented by the corresponding Unicode control characters.
295 * <br> elements are represented by U+2028 LINE SEPARATOR
296 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
299 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
300 * by nsBidi::SetPara() and division into directional runs by
301 * nsBidi::CountRuns().
303 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
304 * correlate them with the frames indexed in mLogicalFrames, setting the
305 * baseLevel and embeddingLevel properties according to the results returned
306 * by the Bidi engine.
308 * The rendering layer requires each text frame to contain text in only one
309 * direction, so we may need to call EnsureBidiContinuation() to split frames.
310 * We may also need to call RemoveBidiContinuation() to convert frames created
311 * by EnsureBidiContinuation() in previous reflows into fluid continuations.
314 nsBidiPresUtils::Resolve(nsBlockFrame
* aBlockFrame
)
316 mLogicalFrames
.Clear();
317 mContentToFrameIndex
.Clear();
319 nsPresContext
*presContext
= aBlockFrame
->PresContext();
320 nsIPresShell
* shell
= presContext
->PresShell();
321 nsStyleContext
* styleContext
= aBlockFrame
->GetStyleContext();
323 // handle bidi-override being set on the block itself before calling
325 const nsStyleVisibility
* vis
= aBlockFrame
->GetStyleVisibility();
326 const nsStyleTextReset
* text
= aBlockFrame
->GetStyleTextReset();
328 if (text
->mUnicodeBidi
== NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
329 nsIFrame
*directionalFrame
= nsnull
;
331 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
332 directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kRLO
);
334 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
335 directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kLRO
);
338 if (directionalFrame
) {
339 mLogicalFrames
.AppendElement(directionalFrame
);
342 for (nsBlockFrame
* block
= aBlockFrame
; block
;
343 block
= static_cast<nsBlockFrame
*>(block
->GetNextContinuation())) {
344 block
->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
345 InitLogicalArray(block
->GetFirstChild(nsnull
));
348 if (text
->mUnicodeBidi
== NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
349 nsIFrame
* directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kPDF
);
350 if (directionalFrame
) {
351 mLogicalFrames
.AppendElement(directionalFrame
);
357 PRInt32 bufferLength
= mBuffer
.Length();
359 if (bufferLength
< 1) {
364 PRUint8 embeddingLevel
;
366 nsBidiLevel paraLevel
= embeddingLevel
=
367 (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
)
368 ? NSBIDI_RTL
: NSBIDI_LTR
;
370 mSuccess
= mBidiEngine
->SetPara(mBuffer
.get(), bufferLength
, paraLevel
, nsnull
);
371 if (NS_FAILED(mSuccess
) ) {
375 mSuccess
= mBidiEngine
->CountRuns(&runCount
);
376 if (NS_FAILED(mSuccess
) ) {
379 PRInt32 runLength
= 0; // the length of the current run of text
380 PRInt32 lineOffset
= 0; // the start of the current run
381 PRInt32 logicalLimit
= 0; // the end of the current run + 1
383 PRInt32 fragmentLength
= 0; // the length of the current text frame
384 PRInt32 frameIndex
= -1; // index to the frames in mLogicalFrames
385 PRInt32 frameCount
= mLogicalFrames
.Length();
386 PRInt32 contentOffset
= 0; // offset of current frame in its content node
387 PRBool isTextFrame
= PR_FALSE
;
388 nsIFrame
* frame
= nsnull
;
389 nsIContent
* content
= nsnull
;
390 PRInt32 contentTextLength
;
391 nsIAtom
* frameType
= nsnull
;
393 FramePropertyTable
*propTable
= presContext
->PropertyTable();
395 nsBlockInFlowLineIterator
lineIter(aBlockFrame
, aBlockFrame
->begin_lines(), PR_FALSE
);
396 if (lineIter
.GetLine() == aBlockFrame
->end_lines()) {
397 // Advance to first valid line (might be in a next-continuation)
400 nsIFrame
* prevFrame
= nsnull
;
401 PRBool lineNeedsUpdate
= PR_FALSE
;
403 PRBool isVisual
= presContext
->IsVisualMode();
406 * Drill up in content to detect whether this is an element that needs to be
407 * rendered with logical order even on visual pages.
409 * We always use logical order on form controls, firstly so that text entry
410 * will be in logical order, but also because visual pages were written with
411 * the assumption that even if the browser had no support for right-to-left
412 * text rendering, it would use native widgets with bidi support to display
415 * We also use logical order in XUL elements, since we expect that if a XUL
416 * element appears in a visual page, it will be generated by an XBL binding
417 * and contain localized text which will be in logical order.
419 for (content
= aBlockFrame
->GetContent() ; content
; content
= content
->GetParent()) {
420 if (content
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
) || content
->IsXUL()) {
428 if (fragmentLength
<= 0) {
429 // Get the next frame from mLogicalFrames
430 if (++frameIndex
>= frameCount
) {
433 frame
= mLogicalFrames
[frameIndex
];
434 frameType
= frame
->GetType();
435 lineNeedsUpdate
= PR_TRUE
;
436 if (nsGkAtoms::textFrame
== frameType
) {
437 content
= frame
->GetContent();
442 contentTextLength
= content
->TextLength();
443 if (contentTextLength
== 0) {
444 frame
->AdjustOffsetsForBidi(0, 0);
445 // Set the base level and embedding level of the current run even
446 // on an empty frame. Otherwise frame reordering will not be correct.
447 propTable
->Set(frame
, nsIFrame::EmbeddingLevelProperty(),
448 NS_INT32_TO_PTR(embeddingLevel
));
449 propTable
->Set(frame
, nsIFrame::BaseLevelProperty(),
450 NS_INT32_TO_PTR(paraLevel
));
454 frame
->GetOffsets(start
, end
);
455 NS_ASSERTION(!(contentTextLength
< end
- start
),
456 "Frame offsets don't fit in content");
457 fragmentLength
= NS_MIN(contentTextLength
, end
- start
);
458 contentOffset
= start
;
459 isTextFrame
= PR_TRUE
;
463 * Any non-text frame corresponds to a single character in the text buffer
464 * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE)
466 isTextFrame
= PR_FALSE
;
469 } // if (fragmentLength <= 0)
471 if (runLength
<= 0) {
472 // Get the next run of text from the Bidi engine
473 if (++numRun
>= runCount
) {
476 lineOffset
= logicalLimit
;
477 if (NS_FAILED(mBidiEngine
->GetLogicalRun(
478 lineOffset
, &logicalLimit
, &embeddingLevel
) ) ) {
481 runLength
= logicalLimit
- lineOffset
;
483 embeddingLevel
= paraLevel
;
485 } // if (runLength <= 0)
487 if (nsGkAtoms::directionalFrame
== frameType
) {
493 propTable
->Set(frame
, nsIFrame::EmbeddingLevelProperty(),
494 NS_INT32_TO_PTR(embeddingLevel
));
495 propTable
->Set(frame
, nsIFrame::BaseLevelProperty(),
496 NS_INT32_TO_PTR(paraLevel
));
498 if ( (runLength
> 0) && (runLength
< fragmentLength
) ) {
500 * The text in this frame continues beyond the end of this directional run.
501 * Create a non-fluid continuation frame for the next directional run.
503 if (lineNeedsUpdate
) {
504 AdvanceLineIteratorToFrame(frame
, &lineIter
, prevFrame
);
505 lineNeedsUpdate
= PR_FALSE
;
507 lineIter
.GetLine()->MarkDirty();
509 PRInt32 runEnd
= contentOffset
+ runLength
;
510 EnsureBidiContinuation(frame
, &nextBidi
, frameIndex
,
513 if (NS_FAILED(mSuccess
)) {
516 nextBidi
->AdjustOffsetsForBidi(runEnd
,
517 contentOffset
+ fragmentLength
);
519 contentOffset
= runEnd
;
520 } // if (runLength < fragmentLength)
522 if (contentOffset
+ fragmentLength
== contentTextLength
) {
524 * We have finished all the text in this content node. Convert any
525 * further non-fluid continuations to fluid continuations and advance
526 * frameIndex to the last frame in the content node
528 PRInt32 newIndex
= 0;
529 mContentToFrameIndex
.Get(content
, &newIndex
);
530 if (newIndex
> frameIndex
) {
531 RemoveBidiContinuation(frame
, frameIndex
, newIndex
, lineOffset
);
532 frameIndex
= newIndex
;
534 } else if (fragmentLength
> 0 && runLength
> fragmentLength
) {
536 * There is more text that belongs to this directional run in the next
537 * text frame: make sure it is a fluid continuation of the current frame.
538 * Do not advance frameIndex, because the next frame may contain
539 * multi-directional text and need to be split
541 PRInt32 newIndex
= frameIndex
;
543 } while (mLogicalFrames
[++newIndex
]->GetType() == nsGkAtoms::directionalFrame
);
544 RemoveBidiContinuation(frame
, frameIndex
, newIndex
, lineOffset
);
545 } else if (runLength
== fragmentLength
) {
547 * The directional run ends at the end of the frame. Make sure that
548 * the next frame is a non-fluid continuation
550 nsIFrame
* next
= frame
->GetNextInFlow();
552 frame
->SetNextContinuation(next
);
553 next
->SetPrevContinuation(frame
);
556 frame
->AdjustOffsetsForBidi(contentOffset
, contentOffset
+ fragmentLength
);
557 if (lineNeedsUpdate
) {
558 AdvanceLineIteratorToFrame(frame
, &lineIter
, prevFrame
);
559 lineNeedsUpdate
= PR_FALSE
;
561 lineIter
.GetLine()->MarkDirty();
567 } // not directionalFrame
568 PRInt32 temp
= runLength
;
569 runLength
-= fragmentLength
;
570 fragmentLength
-= temp
;
572 if (frame
&& fragmentLength
<= 0) {
573 // If the frame is at the end of a run, split all ancestor inlines that
575 // To determine whether we're at the end of the run, we check that we've
576 // finished processing the current run, and that the current frame
577 // doesn't have a fluid continuation (it could have a fluid continuation
578 // of zero length, so testing runLength alone is not sufficient).
579 if (runLength
<= 0 && !frame
->GetNextInFlow()) {
580 nsIFrame
* child
= frame
;
581 nsIFrame
* parent
= frame
->GetParent();
582 // As long as we're on the last sibling, the parent doesn't have to be split.
583 // However, if the parent has a fluid continuation, we do have to make
584 // it non-fluid. This can happen e.g. when we have a first-letter frame
585 // and the end of the first-letter coincides with the end of a
588 IsBidiSplittable(parent
) &&
589 !child
->GetNextSibling()) {
590 nsIFrame
* next
= parent
->GetNextInFlow();
592 parent
->SetNextContinuation(next
);
593 next
->SetPrevContinuation(parent
);
596 parent
= child
->GetParent();
598 if (parent
&& IsBidiSplittable(parent
))
599 SplitInlineAncestors(child
);
601 else if (!frame
->GetNextSibling()) {
602 // We're not at an end of a run, and |frame| is the last child of its parent.
603 // If its ancestors happen to have bidi continuations, convert them into
604 // fluid continuations.
605 nsIFrame
* parent
= frame
->GetParent();
606 JoinInlineAncestors(parent
);
613 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
614 PRBool
IsBidiLeaf(nsIFrame
* aFrame
) {
615 nsIFrame
* kid
= aFrame
->GetFirstChild(nsnull
);
617 || !aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
);
621 nsBidiPresUtils::InitLogicalArray(nsIFrame
* aCurrentFrame
)
626 nsIPresShell
* shell
= aCurrentFrame
->PresContext()->PresShell();
627 nsStyleContext
* styleContext
;
629 for (nsIFrame
* childFrame
= aCurrentFrame
; childFrame
;
630 childFrame
= childFrame
->GetNextSibling()) {
632 // If the real frame for a placeholder is a first letter frame, we need to
633 // drill down into it and include its contents in Bidi resolution.
634 // If not, we just use the placeholder.
635 nsIFrame
* frame
= childFrame
;
636 if (nsGkAtoms::placeholderFrame
== childFrame
->GetType()) {
637 nsIFrame
* realFrame
=
638 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame
);
639 if (realFrame
->GetType() == nsGkAtoms::letterFrame
) {
645 if (frame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)) {
646 const nsStyleVisibility
* vis
= frame
->GetStyleVisibility();
647 const nsStyleTextReset
* text
= frame
->GetStyleTextReset();
648 switch (text
->mUnicodeBidi
) {
649 case NS_STYLE_UNICODE_BIDI_NORMAL
:
651 case NS_STYLE_UNICODE_BIDI_EMBED
:
652 styleContext
= frame
->GetStyleContext();
654 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
657 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
661 case NS_STYLE_UNICODE_BIDI_OVERRIDE
:
662 styleContext
= frame
->GetStyleContext();
664 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
667 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
673 // Create a directional frame before the first frame of an
674 // element specifying embedding or override
675 if (ch
!= 0 && !frame
->GetPrevContinuation()) {
676 nsIFrame
* dirFrame
= NS_NewDirectionalFrame(shell
, styleContext
, ch
);
678 mLogicalFrames
.AppendElement(dirFrame
);
683 if (IsBidiLeaf(frame
)) {
684 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
685 * and add its index to the mContentToFrameIndex hashtable. This
686 * will be used in RemoveBidiContinuation() to identify the last
687 * frame in the array with a given content.
689 nsIContent
* content
= frame
->GetContent();
691 mContentToFrameIndex
.Put(content
, mLogicalFrames
.Length());
693 mLogicalFrames
.AppendElement(frame
);
696 nsIFrame
* kid
= frame
->GetFirstChild(nsnull
);
697 InitLogicalArray(kid
);
700 // If the element is attributed by dir, indicate direction pop (add PDF frame)
701 if (ch
!= 0 && !frame
->GetNextContinuation()) {
702 // Create a directional frame after the last frame of an
703 // element specifying embedding or override
704 nsIFrame
* dirFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kPDF
);
706 mLogicalFrames
.AppendElement(dirFrame
);
713 nsBidiPresUtils::CreateBlockBuffer()
715 mBuffer
.SetLength(0);
718 nsIContent
* prevContent
= nsnull
;
720 PRUint32 count
= mLogicalFrames
.Length();
722 for (i
= 0; i
< count
; i
++) {
723 frame
= mLogicalFrames
[i
];
724 nsIAtom
* frameType
= frame
->GetType();
726 if (nsGkAtoms::textFrame
== frameType
) {
727 nsIContent
* content
= frame
->GetContent();
732 if (content
== prevContent
) {
735 prevContent
= content
;
736 content
->AppendTextTo(mBuffer
);
738 else if (nsGkAtoms::brFrame
== frameType
) { // break frame
739 // Append line separator
740 mBuffer
.Append(kLineSeparator
);
742 else if (nsGkAtoms::directionalFrame
== frameType
) {
743 nsDirectionalFrame
* dirFrame
= static_cast<nsDirectionalFrame
*>(frame
);
744 mBuffer
.Append(dirFrame
->GetChar());
746 else { // not text frame
747 // See the Unicode Bidi Algorithm:
748 // "...inline objects (such as graphics) are treated as if they are ... U+FFFC"
749 mBuffer
.Append(kObjectSubstitute
);
752 // XXX: TODO: Handle preformatted text ('\n')
753 mBuffer
.ReplaceChar("\t\r\n", kSpace
);
757 nsBidiPresUtils::ReorderFrames(nsIFrame
* aFirstFrameOnLine
,
758 PRInt32 aNumFramesOnLine
)
760 // If this line consists of a line frame, reorder the line frame's children.
761 if (aFirstFrameOnLine
->GetType() == nsGkAtoms::lineFrame
) {
762 aFirstFrameOnLine
= aFirstFrameOnLine
->GetFirstChild(nsnull
);
763 if (!aFirstFrameOnLine
)
765 // All children of the line frame are on the first line. Setting aNumFramesOnLine
766 // to -1 makes InitLogicalArrayFromLine look at all of them.
767 aNumFramesOnLine
= -1;
770 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
774 Reorder(isReordered
, hasRTLFrames
);
775 RepositionInlineFrames(aFirstFrameOnLine
);
779 nsBidiPresUtils::Reorder(PRBool
& aReordered
, PRBool
& aHasRTLFrames
)
781 aReordered
= PR_FALSE
;
782 aHasRTLFrames
= PR_FALSE
;
783 PRInt32 count
= mLogicalFrames
.Length();
785 if (mArraySize
< count
) {
786 mArraySize
= count
<< 1;
797 mLevels
= new PRUint8
[mArraySize
];
799 return NS_ERROR_OUT_OF_MEMORY
;
802 memset(mLevels
, 0, sizeof(PRUint8
) * mArraySize
);
807 for (i
= 0; i
< count
; i
++) {
808 frame
= mLogicalFrames
[i
];
809 mLevels
[i
] = GetFrameEmbeddingLevel(frame
);
810 if (mLevels
[i
] & 1) {
811 aHasRTLFrames
= PR_TRUE
;
815 mIndexMap
= new PRInt32
[mArraySize
];
818 mSuccess
= NS_ERROR_OUT_OF_MEMORY
;
821 memset(mIndexMap
, 0, sizeof(PRUint32
) * mArraySize
);
823 mSuccess
= mBidiEngine
->ReorderVisual(mLevels
, count
, mIndexMap
);
825 if (NS_SUCCEEDED(mSuccess
) ) {
826 mVisualFrames
.Clear();
828 for (i
= 0; i
< count
; i
++) {
829 mVisualFrames
.AppendElement(mLogicalFrames
[mIndexMap
[i
]]);
830 if (i
!= mIndexMap
[i
]) {
831 aReordered
= PR_TRUE
;
834 } // NS_SUCCEEDED(mSuccess)
837 if (NS_FAILED(mSuccess
) ) {
838 aReordered
= PR_FALSE
;
844 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame
* aFrame
)
846 nsIFrame
* firstLeaf
= aFrame
;
847 while (!IsBidiLeaf(firstLeaf
)) {
848 nsIFrame
* firstChild
= firstLeaf
->GetFirstChild(nsnull
);
849 nsIFrame
* realFrame
= nsPlaceholderFrame::GetRealFrameFor(firstChild
);
850 firstLeaf
= (realFrame
->GetType() == nsGkAtoms::letterFrame
) ?
851 realFrame
: firstChild
;
853 return NS_GET_EMBEDDING_LEVEL(firstLeaf
);
857 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame
* aFrame
)
859 nsIFrame
* firstLeaf
= aFrame
;
860 while (!IsBidiLeaf(firstLeaf
)) {
861 firstLeaf
= firstLeaf
->GetFirstChild(nsnull
);
863 return NS_GET_BASE_LEVEL(firstLeaf
);
867 nsBidiPresUtils::IsLeftOrRightMost(nsIFrame
* aFrame
,
868 nsContinuationStates
* aContinuationStates
,
869 PRBool
& aIsLeftMost
/* out */,
870 PRBool
& aIsRightMost
/* out */) const
872 const nsStyleVisibility
* vis
= aFrame
->GetStyleVisibility();
873 PRBool isLTR
= (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
);
876 * Since we lay out frames from left to right (in both LTR and RTL), visiting a
877 * frame with 'mFirstVisualFrame == nsnull', means it's the first appearance of
878 * one of its continuation chain frames on the line.
879 * To determine if it's the last visual frame of its continuation chain on the line
880 * or not, we count the number of frames of the chain on the line, and then reduce
881 * it when we lay out a frame of the chain. If this value becomes 1 it means
882 * that it's the last visual frame of its continuation chain on this line.
885 nsFrameContinuationState
* frameState
= aContinuationStates
->GetEntry(aFrame
);
886 nsFrameContinuationState
* firstFrameState
;
888 if (!frameState
->mFirstVisualFrame
) {
889 // aFrame is the first visual frame of its continuation chain
890 nsFrameContinuationState
* contState
;
893 frameState
->mFrameCount
= 1;
894 frameState
->mFirstVisualFrame
= aFrame
;
897 * Traverse continuation chain of aFrame in both backward and forward
898 * directions while the frames are on this line. Count the frames and
899 * set their mFirstVisualFrame to aFrame.
901 // Traverse continuation chain backward
902 for (frame
= aFrame
->GetPrevContinuation();
903 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
904 frame
= frame
->GetPrevContinuation()) {
905 frameState
->mFrameCount
++;
906 contState
->mFirstVisualFrame
= aFrame
;
908 frameState
->mHasContOnPrevLines
= (frame
!= nsnull
);
910 // Traverse continuation chain forward
911 for (frame
= aFrame
->GetNextContinuation();
912 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
913 frame
= frame
->GetNextContinuation()) {
914 frameState
->mFrameCount
++;
915 contState
->mFirstVisualFrame
= aFrame
;
917 frameState
->mHasContOnNextLines
= (frame
!= nsnull
);
919 aIsLeftMost
= isLTR
? !frameState
->mHasContOnPrevLines
920 : !frameState
->mHasContOnNextLines
;
921 firstFrameState
= frameState
;
923 // aFrame is not the first visual frame of its continuation chain
924 aIsLeftMost
= PR_FALSE
;
925 firstFrameState
= aContinuationStates
->GetEntry(frameState
->mFirstVisualFrame
);
928 aIsRightMost
= (firstFrameState
->mFrameCount
== 1) &&
929 (isLTR
? !firstFrameState
->mHasContOnNextLines
930 : !firstFrameState
->mHasContOnPrevLines
);
932 if ((aIsLeftMost
|| aIsRightMost
) &&
933 (aFrame
->GetStateBits() & NS_FRAME_IS_SPECIAL
)) {
934 // For ib splits, don't treat anything except the last part as
935 // endmost or anything except the first part as startmost.
936 // As an optimization, only get the first continuation once.
937 nsIFrame
* firstContinuation
= aFrame
->GetFirstContinuation();
938 if (nsLayoutUtils::FrameIsNonLastInIBSplit(firstContinuation
)) {
939 // We are not endmost
941 aIsRightMost
= PR_FALSE
;
943 aIsLeftMost
= PR_FALSE
;
946 if (nsLayoutUtils::FrameIsNonFirstInIBSplit(firstContinuation
)) {
947 // We are not startmost
949 aIsLeftMost
= PR_FALSE
;
951 aIsRightMost
= PR_FALSE
;
956 // Reduce number of remaining frames of the continuation chain on the line.
957 firstFrameState
->mFrameCount
--;
961 nsBidiPresUtils::RepositionFrame(nsIFrame
* aFrame
,
964 nsContinuationStates
* aContinuationStates
) const
969 PRBool isLeftMost
, isRightMost
;
970 IsLeftOrRightMost(aFrame
,
972 isLeftMost
/* out */,
973 isRightMost
/* out */);
975 nsInlineFrame
* testFrame
= do_QueryFrame(aFrame
);
977 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET
);
980 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST
);
982 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST
);
985 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST
);
987 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST
);
989 // This method is called from nsBlockFrame::PlaceLine via the call to
990 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
991 // have been reflowed, which is required for GetUsedMargin/Border/Padding
992 nsMargin margin
= aFrame
->GetUsedMargin();
994 aLeft
+= margin
.left
;
996 nscoord start
= aLeft
;
998 if (!IsBidiLeaf(aFrame
))
1001 nsMargin borderPadding
= aFrame
->GetUsedBorderAndPadding();
1003 x
+= borderPadding
.left
;
1006 // If aIsOddLevel is true, so we need to traverse the child list
1007 // in reverse order, to make it O(n) we store the list locally and
1008 // iterate the list reversely
1009 nsTArray
<nsIFrame
*> childList
;
1010 nsIFrame
*frame
= aFrame
->GetFirstChild(nsnull
);
1011 if (frame
&& aIsOddLevel
) {
1012 childList
.AppendElement((nsIFrame
*)nsnull
);
1014 childList
.AppendElement(frame
);
1015 frame
= frame
->GetNextSibling();
1017 frame
= childList
[childList
.Length() - 1];
1020 // Reposition the child frames
1023 RepositionFrame(frame
,
1026 aContinuationStates
);
1028 frame
= aIsOddLevel
?
1029 childList
[childList
.Length() - index
- 1] :
1030 frame
->GetNextSibling();
1034 x
+= borderPadding
.right
;
1038 aLeft
+= aFrame
->GetSize().width
;
1040 nsRect rect
= aFrame
->GetRect();
1041 aFrame
->SetRect(nsRect(start
, rect
.y
, aLeft
- start
, rect
.height
));
1044 aLeft
+= margin
.right
;
1048 nsBidiPresUtils::InitContinuationStates(nsIFrame
* aFrame
,
1049 nsContinuationStates
* aContinuationStates
) const
1051 nsFrameContinuationState
* state
= aContinuationStates
->PutEntry(aFrame
);
1052 state
->mFirstVisualFrame
= nsnull
;
1053 state
->mFrameCount
= 0;
1055 if (!IsBidiLeaf(aFrame
)) {
1056 // Continue for child frames
1058 for (frame
= aFrame
->GetFirstChild(nsnull
);
1060 frame
= frame
->GetNextSibling()) {
1061 InitContinuationStates(frame
,
1062 aContinuationStates
);
1068 nsBidiPresUtils::RepositionInlineFrames(nsIFrame
* aFirstChild
) const
1070 const nsStyleVisibility
* vis
= aFirstChild
->GetStyleVisibility();
1071 PRBool isLTR
= (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
);
1072 nscoord leftSpace
= 0;
1074 // This method is called from nsBlockFrame::PlaceLine via the call to
1075 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1076 // have been reflowed, which is required for GetUsedMargin/Border/Padding
1077 nsMargin margin
= aFirstChild
->GetUsedMargin();
1078 if (!aFirstChild
->GetPrevContinuation() &&
1079 !nsLayoutUtils::FrameIsNonFirstInIBSplit(aFirstChild
))
1080 leftSpace
= isLTR
? margin
.left
: margin
.right
;
1082 nscoord left
= aFirstChild
->GetPosition().x
- leftSpace
;
1084 PRInt32 count
= mVisualFrames
.Length();
1086 nsContinuationStates continuationStates
;
1088 continuationStates
.Init();
1090 // Initialize continuation states to (nsnull, 0) for
1091 // each frame on the line.
1092 for (index
= 0; index
< count
; index
++) {
1093 InitContinuationStates(mVisualFrames
[index
], &continuationStates
);
1096 // Reposition frames in visual order
1097 for (index
= 0; index
< count
; index
++) {
1098 frame
= mVisualFrames
[index
];
1099 RepositionFrame(frame
,
1100 (mLevels
[mIndexMap
[index
]] & 1),
1102 &continuationStates
);
1107 nsBidiPresUtils::InitLogicalArrayFromLine(nsIFrame
* aFirstFrameOnLine
,
1108 PRInt32 aNumFramesOnLine
) {
1109 mLogicalFrames
.Clear();
1110 for (nsIFrame
* frame
= aFirstFrameOnLine
;
1111 frame
&& aNumFramesOnLine
--;
1112 frame
= frame
->GetNextSibling()) {
1113 mLogicalFrames
.AppendElement(frame
);
1118 nsBidiPresUtils::CheckLineOrder(nsIFrame
* aFirstFrameOnLine
,
1119 PRInt32 aNumFramesOnLine
,
1120 nsIFrame
** aFirstVisual
,
1121 nsIFrame
** aLastVisual
)
1123 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1126 PRBool hasRTLFrames
;
1127 Reorder(isReordered
, hasRTLFrames
);
1128 PRInt32 count
= mLogicalFrames
.Length();
1131 *aFirstVisual
= mVisualFrames
[0];
1134 *aLastVisual
= mVisualFrames
[count
-1];
1137 // If there's an RTL frame, assume the line is reordered
1138 return isReordered
|| hasRTLFrames
;
1142 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame
* aFrame
,
1143 nsIFrame
* aFirstFrameOnLine
,
1144 PRInt32 aNumFramesOnLine
)
1146 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1149 PRBool hasRTLFrames
;
1150 Reorder(isReordered
, hasRTLFrames
);
1151 PRInt32 count
= mVisualFrames
.Length();
1153 if (aFrame
== nsnull
)
1154 return mVisualFrames
[0];
1156 for (PRInt32 i
= 0; i
< count
- 1; i
++) {
1157 if (mVisualFrames
[i
] == aFrame
) {
1158 return mVisualFrames
[i
+1];
1166 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame
* aFrame
,
1167 nsIFrame
* aFirstFrameOnLine
,
1168 PRInt32 aNumFramesOnLine
)
1170 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1173 PRBool hasRTLFrames
;
1174 Reorder(isReordered
, hasRTLFrames
);
1175 PRInt32 count
= mVisualFrames
.Length();
1177 if (aFrame
== nsnull
)
1178 return mVisualFrames
[count
-1];
1180 for (PRInt32 i
= 1; i
< count
; i
++) {
1181 if (mVisualFrames
[i
] == aFrame
) {
1182 return mVisualFrames
[i
-1];
1190 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame
* aFrame
,
1191 nsIFrame
** aNewFrame
,
1192 PRInt32
& aFrameIndex
,
1196 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
1197 NS_PRECONDITION(aFrame
, "aFrame is null");
1199 aFrame
->AdjustOffsetsForBidi(aStart
, aEnd
);
1200 mSuccess
= CreateBidiContinuation(aFrame
, aNewFrame
);
1204 nsBidiPresUtils::RemoveBidiContinuation(nsIFrame
* aFrame
,
1205 PRInt32 aFirstIndex
,
1207 PRInt32
& aOffset
) const
1209 FrameProperties props
= aFrame
->Properties();
1210 nsBidiLevel embeddingLevel
=
1211 (nsBidiLevel
)NS_PTR_TO_INT32(props
.Get(nsIFrame::EmbeddingLevelProperty()));
1212 nsBidiLevel baseLevel
=
1213 (nsBidiLevel
)NS_PTR_TO_INT32(props
.Get(nsIFrame::BaseLevelProperty()));
1215 for (PRInt32 index
= aFirstIndex
+ 1; index
<= aLastIndex
; index
++) {
1216 nsIFrame
* frame
= mLogicalFrames
[index
];
1217 if (nsGkAtoms::directionalFrame
== frame
->GetType()) {
1222 // Make the frame and its continuation ancestors fluid,
1223 // so they can be reused or deleted by normal reflow code
1224 FrameProperties frameProps
= frame
->Properties();
1225 frameProps
.Set(nsIFrame::EmbeddingLevelProperty(),
1226 NS_INT32_TO_PTR(embeddingLevel
));
1227 frameProps
.Set(nsIFrame::BaseLevelProperty(),
1228 NS_INT32_TO_PTR(baseLevel
));
1229 frame
->AddStateBits(NS_FRAME_IS_BIDI
);
1231 nsIFrame
* prev
= frame
->GetPrevContinuation();
1233 NS_ASSERTION (!frame
->GetPrevInFlow() || frame
->GetPrevInFlow() == prev
,
1234 "prev-in-flow is not prev continuation!");
1235 frame
->SetPrevInFlow(prev
);
1237 NS_ASSERTION (!prev
->GetNextInFlow() || prev
->GetNextInFlow() == frame
,
1238 "next-in-flow is not next continuation!");
1239 prev
->SetNextInFlow(frame
);
1241 frame
= frame
->GetParent();
1251 nsBidiPresUtils::FormatUnicodeText(nsPresContext
* aPresContext
,
1253 PRInt32
& aTextLength
,
1254 nsCharType aCharType
,
1257 NS_ASSERTION(aIsOddLevel
== 0 || aIsOddLevel
== 1, "aIsOddLevel should be 0 or 1");
1258 nsresult rv
= NS_OK
;
1260 //adjusted for correct numeral shaping
1261 PRUint32 bidiOptions
= aPresContext
->GetBidi();
1262 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions
)) {
1264 case IBMBIDI_NUMERAL_HINDI
:
1265 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1268 case IBMBIDI_NUMERAL_ARABIC
:
1269 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1272 case IBMBIDI_NUMERAL_PERSIAN
:
1273 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_PERSIAN
);
1276 case IBMBIDI_NUMERAL_REGULAR
:
1278 switch (aCharType
) {
1280 case eCharType_EuropeanNumber
:
1281 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1284 case eCharType_ArabicNumber
:
1285 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1293 case IBMBIDI_NUMERAL_HINDICONTEXT
:
1294 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1295 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1296 else if (eCharType_EuropeanNumber
== aCharType
)
1297 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1300 case IBMBIDI_NUMERAL_PERSIANCONTEXT
:
1301 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1302 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_PERSIAN
);
1303 else if (eCharType_EuropeanNumber
== aCharType
)
1304 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1307 case IBMBIDI_NUMERAL_NOMINAL
:
1312 StripBidiControlCharacters(aText
, aTextLength
);
1317 nsBidiPresUtils::StripBidiControlCharacters(PRUnichar
* aText
,
1318 PRInt32
& aTextLength
) const
1320 if ( (nsnull
== aText
) || (aTextLength
< 1) ) {
1324 PRInt32 stripLen
= 0;
1326 for (PRInt32 i
= 0; i
< aTextLength
; i
++) {
1327 // XXX: This silently ignores surrogate characters.
1328 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1329 if (IsBidiControl((PRUint32
)aText
[i
])) {
1333 aText
[i
- stripLen
] = aText
[i
];
1336 aTextLength
-= stripLen
;
1339 #if 0 // XXX: for the future use ???
1341 RemoveDiacritics(PRUnichar
* aText
,
1342 PRInt32
& aTextLength
)
1344 if (aText
&& (aTextLength
> 0) ) {
1347 for (PRInt32 i
= 0; i
< aTextLength
&& aText
[i
]; i
++) {
1348 if (IS_BIDI_DIACRITIC(aText
[i
]) ) {
1352 aText
[i
- offset
] = aText
[i
];
1354 aTextLength
= i
- offset
;
1355 aText
[aTextLength
] = 0;
1361 nsBidiPresUtils::CalculateCharType(PRInt32
& aOffset
,
1362 PRInt32 aCharTypeLimit
,
1364 PRInt32
& aRunLength
,
1367 PRUint8
& aPrevCharType
) const
1370 PRBool strongTypeFound
= PR_FALSE
;
1372 nsCharType charType
;
1374 aCharType
= eCharType_OtherNeutral
;
1376 for (offset
= aOffset
; offset
< aCharTypeLimit
; offset
++) {
1377 // Make sure we give RTL chartype to all characters that would be classified
1378 // as Right-To-Left by a bidi platform.
1379 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1380 if (IS_HEBREW_CHAR(mBuffer
[offset
]) ) {
1381 charType
= eCharType_RightToLeft
;
1383 else if (IS_ARABIC_ALPHABETIC(mBuffer
[offset
]) ) {
1384 charType
= eCharType_RightToLeftArabic
;
1387 mBidiEngine
->GetCharTypeAt(offset
, &charType
);
1390 if (!CHARTYPE_IS_WEAK(charType
) ) {
1393 && (charType
!= aPrevCharType
)
1394 && (CHARTYPE_IS_RTL(charType
) || CHARTYPE_IS_RTL(aPrevCharType
) ) ) {
1395 // Stop at this point to ensure uni-directionality of the text
1396 // (from platform's point of view).
1397 // Also, don't mix Arabic and Hebrew content (since platform may
1398 // provide BIDI support to one of them only).
1399 aRunLength
= offset
- aOffset
;
1405 if ( (eCharType_RightToLeftArabic
== aPrevCharType
1406 || eCharType_ArabicNumber
== aPrevCharType
)
1407 && eCharType_EuropeanNumber
== charType
) {
1408 charType
= eCharType_ArabicNumber
;
1411 // Set PrevCharType to the last strong type in this frame
1412 // (for correct numeric shaping)
1413 aPrevCharType
= charType
;
1415 strongTypeFound
= PR_TRUE
;
1416 aCharType
= charType
;
1422 nsresult
nsBidiPresUtils::ProcessText(const PRUnichar
* aText
,
1424 nsBidiDirection aBaseDirection
,
1425 nsPresContext
* aPresContext
,
1426 BidiProcessor
& aprocessor
,
1428 nsBidiPositionResolve
* aPosResolve
,
1429 PRInt32 aPosResolveCount
,
1432 NS_ASSERTION((aPosResolve
== nsnull
) != (aPosResolveCount
> 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1436 mBuffer
.Assign(aText
, aLength
);
1438 nsresult rv
= mBidiEngine
->SetPara(mBuffer
.get(), aLength
, aBaseDirection
, nsnull
);
1442 rv
= mBidiEngine
->CountRuns(&runCount
);
1446 nscoord xOffset
= 0;
1447 nscoord width
, xEndRun
;
1448 nscoord totalWidth
= 0;
1449 PRInt32 i
, start
, limit
, length
;
1450 PRUint32 visualStart
= 0;
1452 PRUint8 prevType
= eCharType_LeftToRight
;
1455 for(int nPosResolve
=0; nPosResolve
< aPosResolveCount
; ++nPosResolve
)
1457 aPosResolve
[nPosResolve
].visualIndex
= kNotFound
;
1458 aPosResolve
[nPosResolve
].visualLeftTwips
= kNotFound
;
1459 aPosResolve
[nPosResolve
].visualWidth
= kNotFound
;
1462 for (i
= 0; i
< runCount
; i
++) {
1463 rv
= mBidiEngine
->GetVisualRun(i
, &start
, &length
, &aBaseDirection
);
1467 rv
= mBidiEngine
->GetLogicalRun(start
, &limit
, &level
);
1471 PRInt32 subRunLength
= limit
- start
;
1472 PRInt32 lineOffset
= start
;
1473 PRInt32 typeLimit
= NS_MIN(limit
, aLength
);
1474 PRInt32 subRunCount
= 1;
1475 PRInt32 subRunLimit
= typeLimit
;
1478 * If |level| is even, i.e. the direction of the run is left-to-right, we
1479 * render the subruns from left to right and increment the x-coordinate
1480 * |xOffset| by the width of each subrun after rendering.
1482 * If |level| is odd, i.e. the direction of the run is right-to-left, we
1483 * render the subruns from right to left. We begin by incrementing |xOffset| by
1484 * the width of the whole run, and then decrement it by the width of each
1485 * subrun before rendering. After rendering all the subruns, we restore the
1486 * x-coordinate of the end of the run for the start of the next run.
1490 aprocessor
.SetText(aText
+ start
, subRunLength
, nsBidiDirection(level
& 1));
1491 width
= aprocessor
.GetWidth();
1496 while (subRunCount
> 0) {
1497 // CalculateCharType can increment subRunCount if the run
1498 // contains mixed character types
1499 CalculateCharType(lineOffset
, typeLimit
, subRunLimit
, subRunLength
, subRunCount
, charType
, prevType
);
1501 nsAutoString runVisualText
;
1502 runVisualText
.Assign(aText
+ start
, subRunLength
);
1503 if (PRInt32(runVisualText
.Length()) < subRunLength
)
1504 return NS_ERROR_OUT_OF_MEMORY
;
1505 FormatUnicodeText(aPresContext
, runVisualText
.BeginWriting(), subRunLength
,
1506 (nsCharType
)charType
, level
& 1);
1508 aprocessor
.SetText(runVisualText
.get(), subRunLength
, nsBidiDirection(level
& 1));
1509 width
= aprocessor
.GetWidth();
1510 totalWidth
+= width
;
1514 if (aMode
== MODE_DRAW
) {
1515 aprocessor
.DrawText(xOffset
, width
);
1519 * The caller may request to calculate the visual position of one
1520 * or more characters.
1522 for(int nPosResolve
=0; nPosResolve
<aPosResolveCount
; ++nPosResolve
)
1524 nsBidiPositionResolve
* posResolve
= &aPosResolve
[nPosResolve
];
1526 * Did we already resolve this position's visual metric? If so, skip.
1528 if (posResolve
->visualLeftTwips
!= kNotFound
)
1532 * First find out if the logical position is within this run.
1534 if (start
<= posResolve
->logicalIndex
&&
1535 start
+ subRunLength
> posResolve
->logicalIndex
) {
1537 * If this run is only one character long, we have an easy case:
1538 * the visual position is the x-coord of the start of the run
1539 * less the x-coord of the start of the whole text.
1541 if (subRunLength
== 1) {
1542 posResolve
->visualIndex
= visualStart
;
1543 posResolve
->visualLeftTwips
= xOffset
;
1544 posResolve
->visualWidth
= width
;
1547 * Otherwise, we need to measure the width of the run's part
1548 * which is to the visual left of the index.
1549 * In other words, the run is broken in two, around the logical index,
1550 * and we measure the part which is visually left.
1551 * If the run is right-to-left, this part will span from after the index
1552 * up to the end of the run; if it is left-to-right, this part will span
1553 * from the start of the run up to (and inclduing) the character before the index.
1557 * Here is a description of how the width of the current character
1558 * (posResolve->visualWidth) is calculated:
1560 * LTR (current char: "P"):
1561 * S A M P L E (logical index: 3, visual index: 3)
1562 * ^ (visualLeftPart)
1563 * ^ (visualRightSide)
1564 * visualLeftLength == 3
1566 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1567 * ^^ (posResolve->visualWidth)
1569 * RTL (current char: "M"):
1570 * E L P M A S (logical index: 2, visual index: 3)
1571 * ^ (visualLeftPart)
1572 * ^ (visualRightSide)
1573 * visualLeftLength == 3
1575 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1576 * ^^ (posResolve->visualWidth)
1579 // The position in the text where this run's "left part" begins.
1580 const PRUnichar
* visualLeftPart
, *visualRightSide
;
1582 // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
1583 posResolve
->visualIndex
= visualStart
+ (subRunLength
- (posResolve
->logicalIndex
+ 1 - start
));
1584 // Skipping to the "left part".
1585 visualLeftPart
= aText
+ posResolve
->logicalIndex
+ 1;
1586 // Skipping to the right side of the current character
1587 visualRightSide
= visualLeftPart
- 1;
1590 posResolve
->visualIndex
= visualStart
+ (posResolve
->logicalIndex
- start
);
1591 // Skipping to the "left part".
1592 visualLeftPart
= aText
+ start
;
1593 // In LTR mode this is the same as visualLeftPart
1594 visualRightSide
= visualLeftPart
;
1596 // The delta between the start of the run and the left part's end.
1597 PRInt32 visualLeftLength
= posResolve
->visualIndex
- visualStart
;
1598 aprocessor
.SetText(visualLeftPart
, visualLeftLength
, nsBidiDirection(level
& 1));
1599 subWidth
= aprocessor
.GetWidth();
1600 aprocessor
.SetText(visualRightSide
, visualLeftLength
+ 1, nsBidiDirection(level
& 1));
1601 posResolve
->visualLeftTwips
= xOffset
+ subWidth
;
1602 posResolve
->visualWidth
= aprocessor
.GetWidth() - subWidth
;
1613 subRunLimit
= typeLimit
;
1614 subRunLength
= typeLimit
- lineOffset
;
1620 visualStart
+= length
;
1624 *aWidth
= totalWidth
;
1629 class NS_STACK_CLASS nsIRenderingContextBidiProcessor
: public nsBidiPresUtils::BidiProcessor
{
1631 nsIRenderingContextBidiProcessor(nsIRenderingContext
* aCtx
,
1632 nsIRenderingContext
* aTextRunConstructionContext
,
1634 : mCtx(aCtx
), mTextRunConstructionContext(aTextRunConstructionContext
), mPt(aPt
) { }
1636 ~nsIRenderingContextBidiProcessor()
1638 mCtx
->SetRightToLeftText(PR_FALSE
);
1641 virtual void SetText(const PRUnichar
* aText
,
1643 nsBidiDirection aDirection
)
1645 mTextRunConstructionContext
->SetTextRunRTL(aDirection
==NSBIDI_RTL
);
1650 virtual nscoord
GetWidth()
1653 mTextRunConstructionContext
->GetWidth(mText
, mLength
, width
, nsnull
);
1657 virtual void DrawText(nscoord aXOffset
,
1660 nsCOMPtr
<nsIFontMetrics
> metrics
;
1661 mCtx
->GetFontMetrics(*getter_AddRefs(metrics
));
1662 nsIThebesFontMetrics
* fm
= static_cast<nsIThebesFontMetrics
*>(metrics
.get());
1663 fm
->DrawString(mText
, mLength
, mPt
.x
+ aXOffset
, mPt
.y
,
1664 mCtx
, mTextRunConstructionContext
);
1668 nsIRenderingContext
* mCtx
;
1669 nsIRenderingContext
* mTextRunConstructionContext
;
1671 const PRUnichar
* mText
;
1673 nsBidiDirection mDirection
;
1676 nsresult
nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar
* aText
,
1678 nsBidiDirection aBaseDirection
,
1679 nsPresContext
* aPresContext
,
1680 nsIRenderingContext
& aRenderingContext
,
1681 nsIRenderingContext
& aTextRunConstructionContext
,
1685 nsBidiPositionResolve
* aPosResolve
,
1686 PRInt32 aPosResolveCount
,
1689 nsIRenderingContextBidiProcessor
processor(&aRenderingContext
, &aTextRunConstructionContext
, nsPoint(aX
, aY
));
1691 return ProcessText(aText
, aLength
, aBaseDirection
, aPresContext
, processor
,
1692 aMode
, aPosResolve
, aPosResolveCount
, aWidth
);
1696 void nsBidiPresUtils::WriteReverse(const PRUnichar
* aSrc
,
1697 PRUint32 aSrcLength
,
1700 const PRUnichar
* src
= aSrc
+ aSrcLength
;
1701 PRUnichar
* dest
= aDest
;
1704 while (--src
>= aSrc
) {
1705 if (NS_IS_LOW_SURROGATE(*src
)) {
1706 if (src
> aSrc
&& NS_IS_HIGH_SURROGATE(*(src
- 1))) {
1707 UTF32Char
= SURROGATE_TO_UCS4(*(src
- 1), *src
);
1710 UTF32Char
= UCS2_REPLACEMENT_CHAR
;
1712 } else if (NS_IS_HIGH_SURROGATE(*src
)) {
1713 // paired high surrogates are handled above, so this is a lone high surrogate
1714 UTF32Char
= UCS2_REPLACEMENT_CHAR
;
1719 UTF32Char
= gfxUnicodeProperties::GetMirroredChar(UTF32Char
);
1721 if (IS_IN_BMP(UTF32Char
)) {
1722 *(dest
++) = UTF32Char
;
1724 *(dest
++) = H_SURROGATE(UTF32Char
);
1725 *(dest
++) = L_SURROGATE(UTF32Char
);
1729 NS_ASSERTION(dest
- aDest
== aSrcLength
, "Whole string not copied");
1733 PRBool
nsBidiPresUtils::WriteLogicalToVisual(const PRUnichar
* aSrc
,
1734 PRUint32 aSrcLength
,
1736 nsBidiLevel aBaseDirection
,
1737 nsBidi
* aBidiEngine
)
1739 const PRUnichar
* src
= aSrc
;
1740 nsresult rv
= aBidiEngine
->SetPara(src
, aSrcLength
, aBaseDirection
, nsnull
);
1741 if (NS_FAILED(rv
)) {
1745 nsBidiDirection dir
;
1746 rv
= aBidiEngine
->GetDirection(&dir
);
1747 // NSBIDI_LTR returned from GetDirection means the whole text is LTR
1748 if (NS_FAILED(rv
) || dir
== NSBIDI_LTR
) {
1753 rv
= aBidiEngine
->CountRuns(&runCount
);
1754 if (NS_FAILED(rv
)) {
1758 PRInt32 runIndex
, start
, length
;
1759 PRUnichar
* dest
= aDest
;
1761 for (runIndex
= 0; runIndex
< runCount
; ++runIndex
) {
1762 rv
= aBidiEngine
->GetVisualRun(runIndex
, &start
, &length
, &dir
);
1763 if (NS_FAILED(rv
)) {
1769 if (dir
== NSBIDI_RTL
) {
1770 WriteReverse(src
, length
, dest
);
1774 NS_ASSERTION(src
>= aSrc
&& src
< aSrc
+ aSrcLength
,
1775 "logical index out of range");
1776 NS_ASSERTION(dest
< aDest
+ aSrcLength
, "visual index out of range");
1777 *(dest
++) = *(src
++);
1782 NS_ASSERTION(dest
- aDest
== aSrcLength
, "whole string not copied");
1786 void nsBidiPresUtils::CopyLogicalToVisual(const nsAString
& aSource
,
1788 nsBidiLevel aBaseDirection
,
1792 PRUint32 srcLength
= aSource
.Length();
1795 if (!EnsureStringLength(aDest
, srcLength
)) {
1798 nsAString::const_iterator fromBegin
, fromEnd
;
1799 nsAString::iterator toBegin
;
1800 aSource
.BeginReading(fromBegin
);
1801 aSource
.EndReading(fromEnd
);
1802 aDest
.BeginWriting(toBegin
);
1805 if (aBaseDirection
== NSBIDI_RTL
) {
1806 // no need to use the converter -- just copy the string in reverse order
1807 WriteReverse(fromBegin
.get(), srcLength
, toBegin
.get());
1809 // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the
1814 if (!WriteLogicalToVisual(fromBegin
.get(), srcLength
, toBegin
.get(),
1815 aBaseDirection
, mBidiEngine
)) {
1820 if (aDest
.IsEmpty()) {
1821 // Either there was an error or the source is unidirectional
1822 // left-to-right. In either case, just copy source to dest.
1823 CopyUnicodeTo(aSource
.BeginReading(fromBegin
), aSource
.EndReading(fromEnd
),
1828 PRUint32
nsBidiPresUtils::EstimateMemoryUsed()
1832 size
+= sizeof(nsBidiPresUtils
);
1833 size
+= mBuffer
.Length() * sizeof(PRUnichar
);
1834 size
+= moz_malloc_usable_size(mBidiEngine
->mDirPropsMemory
);
1835 size
+= moz_malloc_usable_size(mBidiEngine
->mLevelsMemory
);
1836 size
+= moz_malloc_usable_size(mBidiEngine
->mRunsMemory
);
1841 static PLDHashOperator
1842 TraverseKey(nsISupports
*aKey
, PRInt32 aData
, void *aUserArg
)
1844 nsCycleCollectionTraversalCallback
*cb
=
1845 static_cast<nsCycleCollectionTraversalCallback
*>(aUserArg
);
1846 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mContentToFrameIndex key");
1847 cb
->NoteXPCOMChild(aKey
);
1848 return PL_DHASH_NEXT
;
1851 void nsBidiPresUtils::Traverse(nsCycleCollectionTraversalCallback
&cb
) const
1853 mContentToFrameIndex
.EnumerateRead(TraverseKey
, &cb
);
1856 void nsBidiPresUtils::Unlink()
1858 mContentToFrameIndex
.Clear();