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"
59 using namespace mozilla
;
61 static const PRUnichar kSpace
= 0x0020;
62 static const PRUnichar kLineSeparator
= 0x2028;
63 static const PRUnichar kObjectSubstitute
= 0xFFFC;
64 static const PRUnichar kLRE
= 0x202A;
65 static const PRUnichar kRLE
= 0x202B;
66 static const PRUnichar kLRO
= 0x202D;
67 static const PRUnichar kRLO
= 0x202E;
68 static const PRUnichar kPDF
= 0x202C;
69 static const PRUnichar ALEF
= 0x05D0;
71 #define CHAR_IS_HEBREW(c) ((0x0590 <= (c)) && ((c)<= 0x05FF))
72 // Note: The above code are moved from gfx/src/windows/nsRenderingContextWin.cpp
75 NS_NewDirectionalFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRUnichar aChar
);
77 nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
80 mSuccess(NS_ERROR_FAILURE
),
83 mBidiEngine
= new nsBidi();
84 if (mBidiEngine
&& mContentToFrameIndex
.Init()) {
89 nsBidiPresUtils::~nsBidiPresUtils()
101 nsBidiPresUtils::IsSuccessful() const
103 return NS_SUCCEEDED(mSuccess
);
106 /* Some helper methods for Resolve() */
108 // Should this frame be split between text runs?
110 IsBidiSplittable(nsIFrame
* aFrame
) {
111 nsIAtom
* frameType
= aFrame
->GetType();
112 // Bidi inline containers should be split, unless they're line frames.
113 return aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)
114 && frameType
!= nsGkAtoms::lineFrame
;
118 SplitInlineAncestors(nsIFrame
* aFrame
)
120 nsPresContext
*presContext
= aFrame
->PresContext();
121 nsIPresShell
*presShell
= presContext
->PresShell();
122 nsIFrame
* frame
= aFrame
;
123 nsIFrame
* parent
= aFrame
->GetParent();
126 while (IsBidiSplittable(parent
)) {
127 nsIFrame
* grandparent
= parent
->GetParent();
128 NS_ASSERTION(grandparent
, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
130 nsresult rv
= presShell
->FrameConstructor()->
131 CreateContinuingFrame(presContext
, parent
, grandparent
, &newParent
, PR_FALSE
);
136 // Split the child list after |frame|.
137 nsContainerFrame
* container
= do_QueryFrame(parent
);
138 nsFrameList tail
= container
->StealFramesAfter(frame
);
140 // Reparent views as necessary
141 rv
= nsHTMLContainerFrame::ReparentFrameViewList(presContext
, tail
, parent
, newParent
);
146 // The parent's continuation adopts the siblings after the split.
147 rv
= newParent
->InsertFrames(nsGkAtoms::nextBidi
, nsnull
, tail
);
151 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
152 nsFrameList
temp(newParent
, newParent
);
153 rv
= grandparent
->InsertFrames(nsGkAtoms::nextBidi
, parent
, temp
);
159 parent
= grandparent
;
165 // Convert bidi continuations to fluid continuations for a frame and all of its
168 JoinInlineAncestors(nsIFrame
* aFrame
)
170 nsIFrame
* frame
= aFrame
;
171 while (frame
&& IsBidiSplittable(frame
)) {
172 nsIFrame
* next
= frame
->GetNextContinuation();
174 NS_ASSERTION (!frame
->GetNextInFlow() || frame
->GetNextInFlow() == next
,
175 "next-in-flow is not next continuation!");
176 frame
->SetNextInFlow(next
);
178 NS_ASSERTION (!next
->GetPrevInFlow() || next
->GetPrevInFlow() == frame
,
179 "prev-in-flow is not prev continuation!");
180 next
->SetPrevInFlow(frame
);
182 // Join the parent only as long as we're its last child.
183 if (frame
->GetNextSibling())
185 frame
= frame
->GetParent();
190 CreateBidiContinuation(nsIFrame
* aFrame
,
191 nsIFrame
** aNewFrame
)
193 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
194 NS_PRECONDITION(aFrame
, "null ptr");
198 nsPresContext
*presContext
= aFrame
->PresContext();
199 nsIPresShell
*presShell
= presContext
->PresShell();
200 NS_ASSERTION(presShell
, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
202 nsIFrame
* parent
= aFrame
->GetParent();
203 NS_ASSERTION(parent
, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
207 // Have to special case floating first letter frames because the continuation
208 // doesn't go in the first letter frame. The continuation goes with the rest
209 // of the text that the first letter frame was made out of.
210 if (parent
->GetType() == nsGkAtoms::letterFrame
&&
211 parent
->GetStyleDisplay()->IsFloating()) {
212 nsFirstLetterFrame
* letterFrame
= do_QueryFrame(parent
);
213 rv
= letterFrame
->CreateContinuationForFloatingParent(presContext
, aFrame
,
214 aNewFrame
, PR_FALSE
);
218 rv
= presShell
->FrameConstructor()->
219 CreateContinuingFrame(presContext
, aFrame
, parent
, aNewFrame
, PR_FALSE
);
224 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
225 // XXXbz this needs higher-level framelist love
226 nsFrameList
temp(*aNewFrame
, *aNewFrame
);
227 rv
= parent
->InsertFrames(nsGkAtoms::nextBidi
, aFrame
, temp
);
232 // Split inline ancestor frames
233 rv
= SplitInlineAncestors(aFrame
);
242 IsFrameInCurrentLine(nsBlockInFlowLineIterator
* aLineIter
,
243 nsIFrame
* aPrevFrame
, nsIFrame
* aFrame
)
245 nsIFrame
* endFrame
= aLineIter
->IsLastLineInList() ? nsnull
:
246 aLineIter
->GetLine().next()->mFirstChild
;
247 nsIFrame
* startFrame
= aPrevFrame
? aPrevFrame
: aLineIter
->GetLine()->mFirstChild
;
248 for (nsIFrame
* frame
= startFrame
; frame
&& frame
!= endFrame
;
249 frame
= frame
->GetNextSibling()) {
257 AdvanceLineIteratorToFrame(nsIFrame
* aFrame
,
258 nsBlockInFlowLineIterator
* aLineIter
,
259 nsIFrame
*& aPrevFrame
)
261 // Advance aLine to the line containing aFrame
262 nsIFrame
* child
= aFrame
;
263 nsFrameManager
* frameManager
= aFrame
->PresContext()->FrameManager();
264 nsIFrame
* parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, child
);
265 while (parent
&& !nsLayoutUtils::GetAsBlock(parent
)) {
267 parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, child
);
269 NS_ASSERTION (parent
, "aFrame is not a descendent of aBlockFrame");
270 while (!IsFrameInCurrentLine(aLineIter
, aPrevFrame
, child
)) {
275 NS_ASSERTION(hasNext
, "Can't find frame in lines!");
282 * Overview of the implementation of Resolve():
284 * Walk through the descendants of aBlockFrame and build:
285 * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
286 * * mBuffer: an nsAutoString containing a representation of
287 * the content of the frames.
288 * In the case of text frames, this is the actual text context of the
289 * frames, but some other elements are represented in a symbolic form which
290 * will make the Unicode Bidi Algorithm give the correct results.
291 * Bidi embeddings and overrides set by CSS or <bdo> elements are
292 * represented by the corresponding Unicode control characters.
293 * <br> elements are represented by U+2028 LINE SEPARATOR
294 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
297 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
298 * by nsBidi::SetPara() and division into directional runs by
299 * nsBidi::CountRuns().
301 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
302 * correlate them with the frames indexed in mLogicalFrames, setting the
303 * baseLevel and embeddingLevel properties according to the results returned
304 * by the Bidi engine.
306 * The rendering layer requires each text frame to contain text in only one
307 * direction, so we may need to call EnsureBidiContinuation() to split frames.
308 * We may also need to call RemoveBidiContinuation() to convert frames created
309 * by EnsureBidiContinuation() in previous reflows into fluid continuations.
312 nsBidiPresUtils::Resolve(nsBlockFrame
* aBlockFrame
)
314 mLogicalFrames
.Clear();
315 mContentToFrameIndex
.Clear();
317 nsPresContext
*presContext
= aBlockFrame
->PresContext();
318 nsIPresShell
* shell
= presContext
->PresShell();
319 nsStyleContext
* styleContext
= aBlockFrame
->GetStyleContext();
321 // handle bidi-override being set on the block itself before calling
323 const nsStyleVisibility
* vis
= aBlockFrame
->GetStyleVisibility();
324 const nsStyleTextReset
* text
= aBlockFrame
->GetStyleTextReset();
326 if (text
->mUnicodeBidi
== NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
327 nsIFrame
*directionalFrame
= nsnull
;
329 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
330 directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kRLO
);
332 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
333 directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kLRO
);
336 if (directionalFrame
) {
337 mLogicalFrames
.AppendElement(directionalFrame
);
340 for (nsBlockFrame
* block
= aBlockFrame
; block
;
341 block
= static_cast<nsBlockFrame
*>(block
->GetNextContinuation())) {
342 block
->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
343 InitLogicalArray(block
->GetFirstChild(nsnull
));
346 if (text
->mUnicodeBidi
== NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
347 nsIFrame
* directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kPDF
);
348 if (directionalFrame
) {
349 mLogicalFrames
.AppendElement(directionalFrame
);
355 PRInt32 bufferLength
= mBuffer
.Length();
357 if (bufferLength
< 1) {
362 PRUint8 embeddingLevel
;
364 nsBidiLevel paraLevel
= embeddingLevel
=
365 (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
)
366 ? NSBIDI_RTL
: NSBIDI_LTR
;
368 mSuccess
= mBidiEngine
->SetPara(mBuffer
.get(), bufferLength
, paraLevel
, nsnull
);
369 if (NS_FAILED(mSuccess
) ) {
373 mSuccess
= mBidiEngine
->CountRuns(&runCount
);
374 if (NS_FAILED(mSuccess
) ) {
377 PRInt32 runLength
= 0; // the length of the current run of text
378 PRInt32 lineOffset
= 0; // the start of the current run
379 PRInt32 logicalLimit
= 0; // the end of the current run + 1
381 PRInt32 fragmentLength
= 0; // the length of the current text frame
382 PRInt32 frameIndex
= -1; // index to the frames in mLogicalFrames
383 PRInt32 frameCount
= mLogicalFrames
.Length();
384 PRInt32 contentOffset
= 0; // offset of current frame in its content node
385 PRBool isTextFrame
= PR_FALSE
;
386 nsIFrame
* frame
= nsnull
;
387 nsIContent
* content
= nsnull
;
388 PRInt32 contentTextLength
;
389 nsIAtom
* frameType
= nsnull
;
391 FramePropertyTable
*propTable
= presContext
->PropertyTable();
393 nsBlockInFlowLineIterator
lineIter(aBlockFrame
, aBlockFrame
->begin_lines(), PR_FALSE
);
394 if (lineIter
.GetLine() == aBlockFrame
->end_lines()) {
395 // Advance to first valid line (might be in a next-continuation)
398 nsIFrame
* prevFrame
= nsnull
;
399 PRBool lineNeedsUpdate
= PR_FALSE
;
401 PRBool isVisual
= presContext
->IsVisualMode();
404 * Drill up in content to detect whether this is an element that needs to be
405 * rendered with logical order even on visual pages.
407 * We always use logical order on form controls, firstly so that text entry
408 * will be in logical order, but also because visual pages were written with
409 * the assumption that even if the browser had no support for right-to-left
410 * text rendering, it would use native widgets with bidi support to display
413 * We also use logical order in XUL elements, since we expect that if a XUL
414 * element appears in a visual page, it will be generated by an XBL binding
415 * and contain localized text which will be in logical order.
417 for (content
= aBlockFrame
->GetContent() ; content
; content
= content
->GetParent()) {
418 if (content
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
) || content
->IsXUL()) {
426 if (fragmentLength
<= 0) {
427 // Get the next frame from mLogicalFrames
428 if (++frameIndex
>= frameCount
) {
431 frame
= mLogicalFrames
[frameIndex
];
432 frameType
= frame
->GetType();
433 lineNeedsUpdate
= PR_TRUE
;
434 if (nsGkAtoms::textFrame
== frameType
) {
435 content
= frame
->GetContent();
440 contentTextLength
= content
->TextLength();
441 if (contentTextLength
== 0) {
442 frame
->AdjustOffsetsForBidi(0, 0);
443 // Set the base level and embedding level of the current run even
444 // on an empty frame. Otherwise frame reordering will not be correct.
445 propTable
->Set(frame
, nsIFrame::EmbeddingLevelProperty(),
446 NS_INT32_TO_PTR(embeddingLevel
));
447 propTable
->Set(frame
, nsIFrame::BaseLevelProperty(),
448 NS_INT32_TO_PTR(paraLevel
));
452 frame
->GetOffsets(start
, end
);
453 NS_ASSERTION(!(contentTextLength
< end
- start
),
454 "Frame offsets don't fit in content");
455 fragmentLength
= NS_MIN(contentTextLength
, end
- start
);
456 contentOffset
= start
;
457 isTextFrame
= PR_TRUE
;
461 * Any non-text frame corresponds to a single character in the text buffer
462 * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE)
464 isTextFrame
= PR_FALSE
;
467 } // if (fragmentLength <= 0)
469 if (runLength
<= 0) {
470 // Get the next run of text from the Bidi engine
471 if (++numRun
>= runCount
) {
474 lineOffset
= logicalLimit
;
475 if (NS_FAILED(mBidiEngine
->GetLogicalRun(
476 lineOffset
, &logicalLimit
, &embeddingLevel
) ) ) {
479 runLength
= logicalLimit
- lineOffset
;
481 embeddingLevel
= paraLevel
;
483 } // if (runLength <= 0)
485 if (nsGkAtoms::directionalFrame
== frameType
) {
491 propTable
->Set(frame
, nsIFrame::EmbeddingLevelProperty(),
492 NS_INT32_TO_PTR(embeddingLevel
));
493 propTable
->Set(frame
, nsIFrame::BaseLevelProperty(),
494 NS_INT32_TO_PTR(paraLevel
));
496 if ( (runLength
> 0) && (runLength
< fragmentLength
) ) {
498 * The text in this frame continues beyond the end of this directional run.
499 * Create a non-fluid continuation frame for the next directional run.
501 if (lineNeedsUpdate
) {
502 AdvanceLineIteratorToFrame(frame
, &lineIter
, prevFrame
);
503 lineNeedsUpdate
= PR_FALSE
;
505 lineIter
.GetLine()->MarkDirty();
507 PRInt32 runEnd
= contentOffset
+ runLength
;
508 EnsureBidiContinuation(frame
, &nextBidi
, frameIndex
,
511 if (NS_FAILED(mSuccess
)) {
514 nextBidi
->AdjustOffsetsForBidi(runEnd
,
515 contentOffset
+ fragmentLength
);
517 contentOffset
= runEnd
;
518 } // if (runLength < fragmentLength)
520 if (contentOffset
+ fragmentLength
== contentTextLength
) {
522 * We have finished all the text in this content node. Convert any
523 * further non-fluid continuations to fluid continuations and advance
524 * frameIndex to the last frame in the content node
526 PRInt32 newIndex
= 0;
527 mContentToFrameIndex
.Get(content
, &newIndex
);
528 if (newIndex
> frameIndex
) {
529 RemoveBidiContinuation(frame
, frameIndex
, newIndex
, lineOffset
);
530 frameIndex
= newIndex
;
532 } else if (fragmentLength
> 0 && runLength
> fragmentLength
) {
534 * There is more text that belongs to this directional run in the next
535 * text frame: make sure it is a fluid continuation of the current frame.
536 * Do not advance frameIndex, because the next frame may contain
537 * multi-directional text and need to be split
539 PRInt32 newIndex
= frameIndex
;
541 } while (mLogicalFrames
[++newIndex
]->GetType() == nsGkAtoms::directionalFrame
);
542 RemoveBidiContinuation(frame
, frameIndex
, newIndex
, lineOffset
);
543 } else if (runLength
== fragmentLength
) {
545 * The directional run ends at the end of the frame. Make sure that
546 * the next frame is a non-fluid continuation
548 nsIFrame
* next
= frame
->GetNextInFlow();
550 frame
->SetNextContinuation(next
);
551 next
->SetPrevContinuation(frame
);
554 frame
->AdjustOffsetsForBidi(contentOffset
, contentOffset
+ fragmentLength
);
555 if (lineNeedsUpdate
) {
556 AdvanceLineIteratorToFrame(frame
, &lineIter
, prevFrame
);
557 lineNeedsUpdate
= PR_FALSE
;
559 lineIter
.GetLine()->MarkDirty();
565 } // not directionalFrame
566 PRInt32 temp
= runLength
;
567 runLength
-= fragmentLength
;
568 fragmentLength
-= temp
;
570 if (frame
&& fragmentLength
<= 0) {
571 // If the frame is at the end of a run, split all ancestor inlines that
573 // To determine whether we're at the end of the run, we check that we've
574 // finished processing the current run, and that the current frame
575 // doesn't have a fluid continuation (it could have a fluid continuation
576 // of zero length, so testing runLength alone is not sufficient).
577 if (runLength
<= 0 && !frame
->GetNextInFlow()) {
578 nsIFrame
* child
= frame
;
579 nsIFrame
* parent
= frame
->GetParent();
580 // As long as we're on the last sibling, the parent doesn't have to be split.
581 // However, if the parent has a fluid continuation, we do have to make
582 // it non-fluid. This can happen e.g. when we have a first-letter frame
583 // and the end of the first-letter coincides with the end of a
586 IsBidiSplittable(parent
) &&
587 !child
->GetNextSibling()) {
588 nsIFrame
* next
= parent
->GetNextInFlow();
590 parent
->SetNextContinuation(next
);
591 next
->SetPrevContinuation(parent
);
594 parent
= child
->GetParent();
596 if (parent
&& IsBidiSplittable(parent
))
597 SplitInlineAncestors(child
);
599 else if (!frame
->GetNextSibling()) {
600 // We're not at an end of a run, and |frame| is the last child of its parent.
601 // If its ancestors happen to have bidi continuations, convert them into
602 // fluid continuations.
603 nsIFrame
* parent
= frame
->GetParent();
604 JoinInlineAncestors(parent
);
611 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
612 PRBool
IsBidiLeaf(nsIFrame
* aFrame
) {
613 nsIFrame
* kid
= aFrame
->GetFirstChild(nsnull
);
615 || !aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
);
619 nsBidiPresUtils::InitLogicalArray(nsIFrame
* aCurrentFrame
)
624 nsIPresShell
* shell
= aCurrentFrame
->PresContext()->PresShell();
625 nsStyleContext
* styleContext
;
627 for (nsIFrame
* childFrame
= aCurrentFrame
; childFrame
;
628 childFrame
= childFrame
->GetNextSibling()) {
630 // If the real frame for a placeholder is a first letter frame, we need to
631 // drill down into it and include its contents in Bidi resolution.
632 // If not, we just use the placeholder.
633 nsIFrame
* frame
= childFrame
;
634 if (nsGkAtoms::placeholderFrame
== childFrame
->GetType()) {
635 nsIFrame
* realFrame
=
636 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame
);
637 if (realFrame
->GetType() == nsGkAtoms::letterFrame
) {
643 if (frame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)) {
644 const nsStyleVisibility
* vis
= frame
->GetStyleVisibility();
645 const nsStyleTextReset
* text
= frame
->GetStyleTextReset();
646 switch (text
->mUnicodeBidi
) {
647 case NS_STYLE_UNICODE_BIDI_NORMAL
:
649 case NS_STYLE_UNICODE_BIDI_EMBED
:
650 styleContext
= frame
->GetStyleContext();
652 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
655 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
659 case NS_STYLE_UNICODE_BIDI_OVERRIDE
:
660 styleContext
= frame
->GetStyleContext();
662 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
665 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
671 // Create a directional frame before the first frame of an
672 // element specifying embedding or override
673 if (ch
!= 0 && !frame
->GetPrevContinuation()) {
674 nsIFrame
* dirFrame
= NS_NewDirectionalFrame(shell
, styleContext
, ch
);
676 mLogicalFrames
.AppendElement(dirFrame
);
681 if (IsBidiLeaf(frame
)) {
682 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
683 * and add its index to the mContentToFrameIndex hashtable. This
684 * will be used in RemoveBidiContinuation() to identify the last
685 * frame in the array with a given content.
687 nsIContent
* content
= frame
->GetContent();
689 mContentToFrameIndex
.Put(content
, mLogicalFrames
.Length());
691 mLogicalFrames
.AppendElement(frame
);
694 nsIFrame
* kid
= frame
->GetFirstChild(nsnull
);
695 InitLogicalArray(kid
);
698 // If the element is attributed by dir, indicate direction pop (add PDF frame)
699 if (ch
!= 0 && !frame
->GetNextContinuation()) {
700 // Create a directional frame after the last frame of an
701 // element specifying embedding or override
702 nsIFrame
* dirFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kPDF
);
704 mLogicalFrames
.AppendElement(dirFrame
);
711 nsBidiPresUtils::CreateBlockBuffer()
713 mBuffer
.SetLength(0);
716 nsIContent
* prevContent
= nsnull
;
718 PRUint32 count
= mLogicalFrames
.Length();
720 for (i
= 0; i
< count
; i
++) {
721 frame
= mLogicalFrames
[i
];
722 nsIAtom
* frameType
= frame
->GetType();
724 if (nsGkAtoms::textFrame
== frameType
) {
725 nsIContent
* content
= frame
->GetContent();
730 if (content
== prevContent
) {
733 prevContent
= content
;
734 content
->AppendTextTo(mBuffer
);
736 else if (nsGkAtoms::brFrame
== frameType
) { // break frame
737 // Append line separator
738 mBuffer
.Append(kLineSeparator
);
740 else if (nsGkAtoms::directionalFrame
== frameType
) {
741 nsDirectionalFrame
* dirFrame
= static_cast<nsDirectionalFrame
*>(frame
);
742 mBuffer
.Append(dirFrame
->GetChar());
744 else { // not text frame
745 // See the Unicode Bidi Algorithm:
746 // "...inline objects (such as graphics) are treated as if they are ... U+FFFC"
747 mBuffer
.Append(kObjectSubstitute
);
750 // XXX: TODO: Handle preformatted text ('\n')
751 mBuffer
.ReplaceChar("\t\r\n", kSpace
);
755 nsBidiPresUtils::ReorderFrames(nsIFrame
* aFirstFrameOnLine
,
756 PRInt32 aNumFramesOnLine
)
758 // If this line consists of a line frame, reorder the line frame's children.
759 if (aFirstFrameOnLine
->GetType() == nsGkAtoms::lineFrame
) {
760 aFirstFrameOnLine
= aFirstFrameOnLine
->GetFirstChild(nsnull
);
761 if (!aFirstFrameOnLine
)
763 // All children of the line frame are on the first line. Setting aNumFramesOnLine
764 // to -1 makes InitLogicalArrayFromLine look at all of them.
765 aNumFramesOnLine
= -1;
768 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
772 Reorder(isReordered
, hasRTLFrames
);
773 RepositionInlineFrames(aFirstFrameOnLine
);
777 nsBidiPresUtils::Reorder(PRBool
& aReordered
, PRBool
& aHasRTLFrames
)
779 aReordered
= PR_FALSE
;
780 aHasRTLFrames
= PR_FALSE
;
781 PRInt32 count
= mLogicalFrames
.Length();
783 if (mArraySize
< count
) {
784 mArraySize
= count
<< 1;
795 mLevels
= new PRUint8
[mArraySize
];
797 return NS_ERROR_OUT_OF_MEMORY
;
800 memset(mLevels
, 0, sizeof(PRUint8
) * mArraySize
);
805 for (i
= 0; i
< count
; i
++) {
806 frame
= mLogicalFrames
[i
];
807 mLevels
[i
] = GetFrameEmbeddingLevel(frame
);
808 if (mLevels
[i
] & 1) {
809 aHasRTLFrames
= PR_TRUE
;
813 mIndexMap
= new PRInt32
[mArraySize
];
816 mSuccess
= NS_ERROR_OUT_OF_MEMORY
;
819 memset(mIndexMap
, 0, sizeof(PRUint32
) * mArraySize
);
821 mSuccess
= mBidiEngine
->ReorderVisual(mLevels
, count
, mIndexMap
);
823 if (NS_SUCCEEDED(mSuccess
) ) {
824 mVisualFrames
.Clear();
826 for (i
= 0; i
< count
; i
++) {
827 mVisualFrames
.AppendElement(mLogicalFrames
[mIndexMap
[i
]]);
828 if (i
!= mIndexMap
[i
]) {
829 aReordered
= PR_TRUE
;
832 } // NS_SUCCEEDED(mSuccess)
835 if (NS_FAILED(mSuccess
) ) {
836 aReordered
= PR_FALSE
;
842 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame
* aFrame
)
844 nsIFrame
* firstLeaf
= aFrame
;
845 while (!IsBidiLeaf(firstLeaf
)) {
846 nsIFrame
* firstChild
= firstLeaf
->GetFirstChild(nsnull
);
847 nsIFrame
* realFrame
= nsPlaceholderFrame::GetRealFrameFor(firstChild
);
848 firstLeaf
= (realFrame
->GetType() == nsGkAtoms::letterFrame
) ?
849 realFrame
: firstChild
;
851 return NS_GET_EMBEDDING_LEVEL(firstLeaf
);
855 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame
* aFrame
)
857 nsIFrame
* firstLeaf
= aFrame
;
858 while (!IsBidiLeaf(firstLeaf
)) {
859 firstLeaf
= firstLeaf
->GetFirstChild(nsnull
);
861 return NS_GET_BASE_LEVEL(firstLeaf
);
865 nsBidiPresUtils::IsLeftOrRightMost(nsIFrame
* aFrame
,
866 nsContinuationStates
* aContinuationStates
,
867 PRBool
& aIsLeftMost
/* out */,
868 PRBool
& aIsRightMost
/* out */) const
870 const nsStyleVisibility
* vis
= aFrame
->GetStyleVisibility();
871 PRBool isLTR
= (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
);
874 * Since we lay out frames from left to right (in both LTR and RTL), visiting a
875 * frame with 'mFirstVisualFrame == nsnull', means it's the first appearance of
876 * one of its continuation chain frames on the line.
877 * To determine if it's the last visual frame of its continuation chain on the line
878 * or not, we count the number of frames of the chain on the line, and then reduce
879 * it when we lay out a frame of the chain. If this value becomes 1 it means
880 * that it's the last visual frame of its continuation chain on this line.
883 nsFrameContinuationState
* frameState
= aContinuationStates
->GetEntry(aFrame
);
884 nsFrameContinuationState
* firstFrameState
;
886 if (!frameState
->mFirstVisualFrame
) {
887 // aFrame is the first visual frame of its continuation chain
888 nsFrameContinuationState
* contState
;
891 frameState
->mFrameCount
= 1;
892 frameState
->mFirstVisualFrame
= aFrame
;
895 * Traverse continuation chain of aFrame in both backward and forward
896 * directions while the frames are on this line. Count the frames and
897 * set their mFirstVisualFrame to aFrame.
899 // Traverse continuation chain backward
900 for (frame
= aFrame
->GetPrevContinuation();
901 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
902 frame
= frame
->GetPrevContinuation()) {
903 frameState
->mFrameCount
++;
904 contState
->mFirstVisualFrame
= aFrame
;
906 frameState
->mHasContOnPrevLines
= (frame
!= nsnull
);
908 // Traverse continuation chain forward
909 for (frame
= aFrame
->GetNextContinuation();
910 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
911 frame
= frame
->GetNextContinuation()) {
912 frameState
->mFrameCount
++;
913 contState
->mFirstVisualFrame
= aFrame
;
915 frameState
->mHasContOnNextLines
= (frame
!= nsnull
);
917 aIsLeftMost
= isLTR
? !frameState
->mHasContOnPrevLines
918 : !frameState
->mHasContOnNextLines
;
919 firstFrameState
= frameState
;
921 // aFrame is not the first visual frame of its continuation chain
922 aIsLeftMost
= PR_FALSE
;
923 firstFrameState
= aContinuationStates
->GetEntry(frameState
->mFirstVisualFrame
);
926 aIsRightMost
= (firstFrameState
->mFrameCount
== 1) &&
927 (isLTR
? !firstFrameState
->mHasContOnNextLines
928 : !firstFrameState
->mHasContOnPrevLines
);
930 if ((aIsLeftMost
|| aIsRightMost
) &&
931 (aFrame
->GetStateBits() & NS_FRAME_IS_SPECIAL
)) {
932 // For ib splits, don't treat anything except the last part as
933 // endmost or anything except the first part as startmost.
934 // As an optimization, only get the first continuation once.
935 nsIFrame
* firstContinuation
= aFrame
->GetFirstContinuation();
936 if (nsLayoutUtils::FrameIsNonLastInIBSplit(firstContinuation
)) {
937 // We are not endmost
939 aIsRightMost
= PR_FALSE
;
941 aIsLeftMost
= PR_FALSE
;
944 if (nsLayoutUtils::FrameIsNonFirstInIBSplit(firstContinuation
)) {
945 // We are not startmost
947 aIsLeftMost
= PR_FALSE
;
949 aIsRightMost
= PR_FALSE
;
954 // Reduce number of remaining frames of the continuation chain on the line.
955 firstFrameState
->mFrameCount
--;
959 nsBidiPresUtils::RepositionFrame(nsIFrame
* aFrame
,
962 nsContinuationStates
* aContinuationStates
) const
967 PRBool isLeftMost
, isRightMost
;
968 IsLeftOrRightMost(aFrame
,
970 isLeftMost
/* out */,
971 isRightMost
/* out */);
973 nsInlineFrame
* testFrame
= do_QueryFrame(aFrame
);
975 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET
);
978 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST
);
980 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST
);
983 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST
);
985 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST
);
987 // This method is called from nsBlockFrame::PlaceLine via the call to
988 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
989 // have been reflowed, which is required for GetUsedMargin/Border/Padding
990 nsMargin margin
= aFrame
->GetUsedMargin();
992 aLeft
+= margin
.left
;
994 nscoord start
= aLeft
;
996 if (!IsBidiLeaf(aFrame
))
999 nsMargin borderPadding
= aFrame
->GetUsedBorderAndPadding();
1001 x
+= borderPadding
.left
;
1004 // If aIsOddLevel is true, so we need to traverse the child list
1005 // in reverse order, to make it O(n) we store the list locally and
1006 // iterate the list reversely
1007 nsTArray
<nsIFrame
*> childList
;
1008 nsIFrame
*frame
= aFrame
->GetFirstChild(nsnull
);
1009 if (frame
&& aIsOddLevel
) {
1010 childList
.AppendElement((nsIFrame
*)nsnull
);
1012 childList
.AppendElement(frame
);
1013 frame
= frame
->GetNextSibling();
1015 frame
= childList
[childList
.Length() - 1];
1018 // Reposition the child frames
1021 RepositionFrame(frame
,
1024 aContinuationStates
);
1026 frame
= aIsOddLevel
?
1027 childList
[childList
.Length() - index
- 1] :
1028 frame
->GetNextSibling();
1032 x
+= borderPadding
.right
;
1036 aLeft
+= aFrame
->GetSize().width
;
1038 nsRect rect
= aFrame
->GetRect();
1039 aFrame
->SetRect(nsRect(start
, rect
.y
, aLeft
- start
, rect
.height
));
1042 aLeft
+= margin
.right
;
1046 nsBidiPresUtils::InitContinuationStates(nsIFrame
* aFrame
,
1047 nsContinuationStates
* aContinuationStates
) const
1049 nsFrameContinuationState
* state
= aContinuationStates
->PutEntry(aFrame
);
1050 state
->mFirstVisualFrame
= nsnull
;
1051 state
->mFrameCount
= 0;
1053 if (!IsBidiLeaf(aFrame
)) {
1054 // Continue for child frames
1056 for (frame
= aFrame
->GetFirstChild(nsnull
);
1058 frame
= frame
->GetNextSibling()) {
1059 InitContinuationStates(frame
,
1060 aContinuationStates
);
1066 nsBidiPresUtils::RepositionInlineFrames(nsIFrame
* aFirstChild
) const
1068 const nsStyleVisibility
* vis
= aFirstChild
->GetStyleVisibility();
1069 PRBool isLTR
= (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
);
1070 nscoord leftSpace
= 0;
1072 // This method is called from nsBlockFrame::PlaceLine via the call to
1073 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1074 // have been reflowed, which is required for GetUsedMargin/Border/Padding
1075 nsMargin margin
= aFirstChild
->GetUsedMargin();
1076 if (!aFirstChild
->GetPrevContinuation() &&
1077 !nsLayoutUtils::FrameIsNonFirstInIBSplit(aFirstChild
))
1078 leftSpace
= isLTR
? margin
.left
: margin
.right
;
1080 nscoord left
= aFirstChild
->GetPosition().x
- leftSpace
;
1082 PRInt32 count
= mVisualFrames
.Length();
1084 nsContinuationStates continuationStates
;
1086 continuationStates
.Init();
1088 // Initialize continuation states to (nsnull, 0) for
1089 // each frame on the line.
1090 for (index
= 0; index
< count
; index
++) {
1091 InitContinuationStates(mVisualFrames
[index
], &continuationStates
);
1094 // Reposition frames in visual order
1095 for (index
= 0; index
< count
; index
++) {
1096 frame
= mVisualFrames
[index
];
1097 RepositionFrame(frame
,
1098 (mLevels
[mIndexMap
[index
]] & 1),
1100 &continuationStates
);
1105 nsBidiPresUtils::InitLogicalArrayFromLine(nsIFrame
* aFirstFrameOnLine
,
1106 PRInt32 aNumFramesOnLine
) {
1107 mLogicalFrames
.Clear();
1108 for (nsIFrame
* frame
= aFirstFrameOnLine
;
1109 frame
&& aNumFramesOnLine
--;
1110 frame
= frame
->GetNextSibling()) {
1111 mLogicalFrames
.AppendElement(frame
);
1116 nsBidiPresUtils::CheckLineOrder(nsIFrame
* aFirstFrameOnLine
,
1117 PRInt32 aNumFramesOnLine
,
1118 nsIFrame
** aFirstVisual
,
1119 nsIFrame
** aLastVisual
)
1121 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1124 PRBool hasRTLFrames
;
1125 Reorder(isReordered
, hasRTLFrames
);
1126 PRInt32 count
= mLogicalFrames
.Length();
1129 *aFirstVisual
= mVisualFrames
[0];
1132 *aLastVisual
= mVisualFrames
[count
-1];
1135 // If there's an RTL frame, assume the line is reordered
1136 return isReordered
|| hasRTLFrames
;
1140 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame
* aFrame
,
1141 nsIFrame
* aFirstFrameOnLine
,
1142 PRInt32 aNumFramesOnLine
)
1144 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1147 PRBool hasRTLFrames
;
1148 Reorder(isReordered
, hasRTLFrames
);
1149 PRInt32 count
= mVisualFrames
.Length();
1151 if (aFrame
== nsnull
)
1152 return mVisualFrames
[0];
1154 for (PRInt32 i
= 0; i
< count
- 1; i
++) {
1155 if (mVisualFrames
[i
] == aFrame
) {
1156 return mVisualFrames
[i
+1];
1164 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame
* aFrame
,
1165 nsIFrame
* aFirstFrameOnLine
,
1166 PRInt32 aNumFramesOnLine
)
1168 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1171 PRBool hasRTLFrames
;
1172 Reorder(isReordered
, hasRTLFrames
);
1173 PRInt32 count
= mVisualFrames
.Length();
1175 if (aFrame
== nsnull
)
1176 return mVisualFrames
[count
-1];
1178 for (PRInt32 i
= 1; i
< count
; i
++) {
1179 if (mVisualFrames
[i
] == aFrame
) {
1180 return mVisualFrames
[i
-1];
1188 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame
* aFrame
,
1189 nsIFrame
** aNewFrame
,
1190 PRInt32
& aFrameIndex
,
1194 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
1195 NS_PRECONDITION(aFrame
, "aFrame is null");
1197 aFrame
->AdjustOffsetsForBidi(aStart
, aEnd
);
1198 mSuccess
= CreateBidiContinuation(aFrame
, aNewFrame
);
1202 nsBidiPresUtils::RemoveBidiContinuation(nsIFrame
* aFrame
,
1203 PRInt32 aFirstIndex
,
1205 PRInt32
& aOffset
) const
1207 FrameProperties props
= aFrame
->Properties();
1208 nsBidiLevel embeddingLevel
=
1209 (nsBidiLevel
)NS_PTR_TO_INT32(props
.Get(nsIFrame::EmbeddingLevelProperty()));
1210 nsBidiLevel baseLevel
=
1211 (nsBidiLevel
)NS_PTR_TO_INT32(props
.Get(nsIFrame::BaseLevelProperty()));
1213 for (PRInt32 index
= aFirstIndex
+ 1; index
<= aLastIndex
; index
++) {
1214 nsIFrame
* frame
= mLogicalFrames
[index
];
1215 if (nsGkAtoms::directionalFrame
== frame
->GetType()) {
1220 // Make the frame and its continuation ancestors fluid,
1221 // so they can be reused or deleted by normal reflow code
1222 FrameProperties frameProps
= frame
->Properties();
1223 frameProps
.Set(nsIFrame::EmbeddingLevelProperty(),
1224 NS_INT32_TO_PTR(embeddingLevel
));
1225 frameProps
.Set(nsIFrame::BaseLevelProperty(),
1226 NS_INT32_TO_PTR(baseLevel
));
1227 frame
->AddStateBits(NS_FRAME_IS_BIDI
);
1229 nsIFrame
* prev
= frame
->GetPrevContinuation();
1231 NS_ASSERTION (!frame
->GetPrevInFlow() || frame
->GetPrevInFlow() == prev
,
1232 "prev-in-flow is not prev continuation!");
1233 frame
->SetPrevInFlow(prev
);
1235 NS_ASSERTION (!prev
->GetNextInFlow() || prev
->GetNextInFlow() == frame
,
1236 "next-in-flow is not next continuation!");
1237 prev
->SetNextInFlow(frame
);
1239 frame
= frame
->GetParent();
1249 nsBidiPresUtils::FormatUnicodeText(nsPresContext
* aPresContext
,
1251 PRInt32
& aTextLength
,
1252 nsCharType aCharType
,
1255 NS_ASSERTION(aIsOddLevel
== 0 || aIsOddLevel
== 1, "aIsOddLevel should be 0 or 1");
1256 nsresult rv
= NS_OK
;
1258 //adjusted for correct numeral shaping
1259 PRUint32 bidiOptions
= aPresContext
->GetBidi();
1260 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions
)) {
1262 case IBMBIDI_NUMERAL_HINDI
:
1263 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1266 case IBMBIDI_NUMERAL_ARABIC
:
1267 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1270 case IBMBIDI_NUMERAL_PERSIAN
:
1271 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_PERSIAN
);
1274 case IBMBIDI_NUMERAL_REGULAR
:
1276 switch (aCharType
) {
1278 case eCharType_EuropeanNumber
:
1279 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1282 case eCharType_ArabicNumber
:
1283 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1291 case IBMBIDI_NUMERAL_HINDICONTEXT
:
1292 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1293 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1294 else if (eCharType_EuropeanNumber
== aCharType
)
1295 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1298 case IBMBIDI_NUMERAL_PERSIANCONTEXT
:
1299 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1300 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_PERSIAN
);
1301 else if (eCharType_EuropeanNumber
== aCharType
)
1302 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1305 case IBMBIDI_NUMERAL_NOMINAL
:
1310 StripBidiControlCharacters(aText
, aTextLength
);
1315 nsBidiPresUtils::StripBidiControlCharacters(PRUnichar
* aText
,
1316 PRInt32
& aTextLength
) const
1318 if ( (nsnull
== aText
) || (aTextLength
< 1) ) {
1322 PRInt32 stripLen
= 0;
1324 for (PRInt32 i
= 0; i
< aTextLength
; i
++) {
1325 // XXX: This silently ignores surrogate characters.
1326 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1327 if (IsBidiControl((PRUint32
)aText
[i
])) {
1331 aText
[i
- stripLen
] = aText
[i
];
1334 aTextLength
-= stripLen
;
1337 #if 0 // XXX: for the future use ???
1339 RemoveDiacritics(PRUnichar
* aText
,
1340 PRInt32
& aTextLength
)
1342 if (aText
&& (aTextLength
> 0) ) {
1345 for (PRInt32 i
= 0; i
< aTextLength
&& aText
[i
]; i
++) {
1346 if (IS_BIDI_DIACRITIC(aText
[i
]) ) {
1350 aText
[i
- offset
] = aText
[i
];
1352 aTextLength
= i
- offset
;
1353 aText
[aTextLength
] = 0;
1359 nsBidiPresUtils::CalculateCharType(PRInt32
& aOffset
,
1360 PRInt32 aCharTypeLimit
,
1362 PRInt32
& aRunLength
,
1365 PRUint8
& aPrevCharType
) const
1368 PRBool strongTypeFound
= PR_FALSE
;
1370 nsCharType charType
;
1372 aCharType
= eCharType_OtherNeutral
;
1374 for (offset
= aOffset
; offset
< aCharTypeLimit
; offset
++) {
1375 // Make sure we give RTL chartype to all characters that would be classified
1376 // as Right-To-Left by a bidi platform.
1377 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1378 if (IS_HEBREW_CHAR(mBuffer
[offset
]) ) {
1379 charType
= eCharType_RightToLeft
;
1381 else if (IS_ARABIC_ALPHABETIC(mBuffer
[offset
]) ) {
1382 charType
= eCharType_RightToLeftArabic
;
1385 mBidiEngine
->GetCharTypeAt(offset
, &charType
);
1388 if (!CHARTYPE_IS_WEAK(charType
) ) {
1391 && (charType
!= aPrevCharType
)
1392 && (CHARTYPE_IS_RTL(charType
) || CHARTYPE_IS_RTL(aPrevCharType
) ) ) {
1393 // Stop at this point to ensure uni-directionality of the text
1394 // (from platform's point of view).
1395 // Also, don't mix Arabic and Hebrew content (since platform may
1396 // provide BIDI support to one of them only).
1397 aRunLength
= offset
- aOffset
;
1403 if ( (eCharType_RightToLeftArabic
== aPrevCharType
1404 || eCharType_ArabicNumber
== aPrevCharType
)
1405 && eCharType_EuropeanNumber
== charType
) {
1406 charType
= eCharType_ArabicNumber
;
1409 // Set PrevCharType to the last strong type in this frame
1410 // (for correct numeric shaping)
1411 aPrevCharType
= charType
;
1413 strongTypeFound
= PR_TRUE
;
1414 aCharType
= charType
;
1420 nsresult
nsBidiPresUtils::ProcessText(const PRUnichar
* aText
,
1422 nsBidiDirection aBaseDirection
,
1423 nsPresContext
* aPresContext
,
1424 BidiProcessor
& aprocessor
,
1426 nsBidiPositionResolve
* aPosResolve
,
1427 PRInt32 aPosResolveCount
,
1430 NS_ASSERTION((aPosResolve
== nsnull
) != (aPosResolveCount
> 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1434 mBuffer
.Assign(aText
, aLength
);
1436 nsresult rv
= mBidiEngine
->SetPara(mBuffer
.get(), aLength
, aBaseDirection
, nsnull
);
1440 rv
= mBidiEngine
->CountRuns(&runCount
);
1444 nscoord xOffset
= 0;
1445 nscoord width
, xEndRun
;
1446 nscoord totalWidth
= 0;
1447 PRInt32 i
, start
, limit
, length
;
1448 PRUint32 visualStart
= 0;
1450 PRUint8 prevType
= eCharType_LeftToRight
;
1453 for(int nPosResolve
=0; nPosResolve
< aPosResolveCount
; ++nPosResolve
)
1455 aPosResolve
[nPosResolve
].visualIndex
= kNotFound
;
1456 aPosResolve
[nPosResolve
].visualLeftTwips
= kNotFound
;
1457 aPosResolve
[nPosResolve
].visualWidth
= kNotFound
;
1460 for (i
= 0; i
< runCount
; i
++) {
1461 rv
= mBidiEngine
->GetVisualRun(i
, &start
, &length
, &aBaseDirection
);
1465 rv
= mBidiEngine
->GetLogicalRun(start
, &limit
, &level
);
1469 PRInt32 subRunLength
= limit
- start
;
1470 PRInt32 lineOffset
= start
;
1471 PRInt32 typeLimit
= NS_MIN(limit
, aLength
);
1472 PRInt32 subRunCount
= 1;
1473 PRInt32 subRunLimit
= typeLimit
;
1476 * If |level| is even, i.e. the direction of the run is left-to-right, we
1477 * render the subruns from left to right and increment the x-coordinate
1478 * |xOffset| by the width of each subrun after rendering.
1480 * If |level| is odd, i.e. the direction of the run is right-to-left, we
1481 * render the subruns from right to left. We begin by incrementing |xOffset| by
1482 * the width of the whole run, and then decrement it by the width of each
1483 * subrun before rendering. After rendering all the subruns, we restore the
1484 * x-coordinate of the end of the run for the start of the next run.
1488 aprocessor
.SetText(aText
+ start
, subRunLength
, nsBidiDirection(level
& 1));
1489 width
= aprocessor
.GetWidth();
1494 while (subRunCount
> 0) {
1495 // CalculateCharType can increment subRunCount if the run
1496 // contains mixed character types
1497 CalculateCharType(lineOffset
, typeLimit
, subRunLimit
, subRunLength
, subRunCount
, charType
, prevType
);
1499 nsAutoString runVisualText
;
1500 runVisualText
.Assign(aText
+ start
, subRunLength
);
1501 if (PRInt32(runVisualText
.Length()) < subRunLength
)
1502 return NS_ERROR_OUT_OF_MEMORY
;
1503 FormatUnicodeText(aPresContext
, runVisualText
.BeginWriting(), subRunLength
,
1504 (nsCharType
)charType
, level
& 1);
1506 aprocessor
.SetText(runVisualText
.get(), subRunLength
, nsBidiDirection(level
& 1));
1507 width
= aprocessor
.GetWidth();
1508 totalWidth
+= width
;
1512 if (aMode
== MODE_DRAW
) {
1513 aprocessor
.DrawText(xOffset
, width
);
1517 * The caller may request to calculate the visual position of one
1518 * or more characters.
1520 for(int nPosResolve
=0; nPosResolve
<aPosResolveCount
; ++nPosResolve
)
1522 nsBidiPositionResolve
* posResolve
= &aPosResolve
[nPosResolve
];
1524 * Did we already resolve this position's visual metric? If so, skip.
1526 if (posResolve
->visualLeftTwips
!= kNotFound
)
1530 * First find out if the logical position is within this run.
1532 if (start
<= posResolve
->logicalIndex
&&
1533 start
+ subRunLength
> posResolve
->logicalIndex
) {
1535 * If this run is only one character long, we have an easy case:
1536 * the visual position is the x-coord of the start of the run
1537 * less the x-coord of the start of the whole text.
1539 if (subRunLength
== 1) {
1540 posResolve
->visualIndex
= visualStart
;
1541 posResolve
->visualLeftTwips
= xOffset
;
1542 posResolve
->visualWidth
= width
;
1545 * Otherwise, we need to measure the width of the run's part
1546 * which is to the visual left of the index.
1547 * In other words, the run is broken in two, around the logical index,
1548 * and we measure the part which is visually left.
1549 * If the run is right-to-left, this part will span from after the index
1550 * up to the end of the run; if it is left-to-right, this part will span
1551 * from the start of the run up to (and inclduing) the character before the index.
1555 * Here is a description of how the width of the current character
1556 * (posResolve->visualWidth) is calculated:
1558 * LTR (current char: "P"):
1559 * S A M P L E (logical index: 3, visual index: 3)
1560 * ^ (visualLeftPart)
1561 * ^ (visualRightSide)
1562 * visualLeftLength == 3
1564 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1565 * ^^ (posResolve->visualWidth)
1567 * RTL (current char: "M"):
1568 * E L P M A S (logical index: 2, visual index: 3)
1569 * ^ (visualLeftPart)
1570 * ^ (visualRightSide)
1571 * visualLeftLength == 3
1573 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1574 * ^^ (posResolve->visualWidth)
1577 // The position in the text where this run's "left part" begins.
1578 const PRUnichar
* visualLeftPart
, *visualRightSide
;
1580 // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
1581 posResolve
->visualIndex
= visualStart
+ (subRunLength
- (posResolve
->logicalIndex
+ 1 - start
));
1582 // Skipping to the "left part".
1583 visualLeftPart
= aText
+ posResolve
->logicalIndex
+ 1;
1584 // Skipping to the right side of the current character
1585 visualRightSide
= visualLeftPart
- 1;
1588 posResolve
->visualIndex
= visualStart
+ (posResolve
->logicalIndex
- start
);
1589 // Skipping to the "left part".
1590 visualLeftPart
= aText
+ start
;
1591 // In LTR mode this is the same as visualLeftPart
1592 visualRightSide
= visualLeftPart
;
1594 // The delta between the start of the run and the left part's end.
1595 PRInt32 visualLeftLength
= posResolve
->visualIndex
- visualStart
;
1596 aprocessor
.SetText(visualLeftPart
, visualLeftLength
, nsBidiDirection(level
& 1));
1597 subWidth
= aprocessor
.GetWidth();
1598 aprocessor
.SetText(visualRightSide
, visualLeftLength
+ 1, nsBidiDirection(level
& 1));
1599 posResolve
->visualLeftTwips
= xOffset
+ subWidth
;
1600 posResolve
->visualWidth
= aprocessor
.GetWidth() - subWidth
;
1611 subRunLimit
= typeLimit
;
1612 subRunLength
= typeLimit
- lineOffset
;
1618 visualStart
+= length
;
1622 *aWidth
= totalWidth
;
1627 class NS_STACK_CLASS nsIRenderingContextBidiProcessor
: public nsBidiPresUtils::BidiProcessor
{
1629 nsIRenderingContextBidiProcessor(nsIRenderingContext
* aCtx
,
1631 : mCtx(aCtx
), mPt(aPt
) { }
1633 ~nsIRenderingContextBidiProcessor()
1635 mCtx
->SetRightToLeftText(PR_FALSE
);
1638 virtual void SetText(const PRUnichar
* aText
,
1640 nsBidiDirection aDirection
)
1642 mCtx
->SetTextRunRTL(aDirection
==NSBIDI_RTL
);
1647 virtual nscoord
GetWidth()
1650 mCtx
->GetWidth(mText
, mLength
, width
, nsnull
);
1654 virtual void DrawText(nscoord aXOffset
,
1657 mCtx
->DrawString(mText
, mLength
, mPt
.x
+ aXOffset
, mPt
.y
);
1661 nsIRenderingContext
* mCtx
;
1663 const PRUnichar
* mText
;
1665 nsBidiDirection mDirection
;
1668 nsresult
nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar
* aText
,
1670 nsBidiDirection aBaseDirection
,
1671 nsPresContext
* aPresContext
,
1672 nsIRenderingContext
& aRenderingContext
,
1676 nsBidiPositionResolve
* aPosResolve
,
1677 PRInt32 aPosResolveCount
,
1680 nsIRenderingContextBidiProcessor
processor(&aRenderingContext
, nsPoint(aX
, aY
));
1682 return ProcessText(aText
, aLength
, aBaseDirection
, aPresContext
, processor
,
1683 aMode
, aPosResolve
, aPosResolveCount
, aWidth
);
1686 PRUint32
nsBidiPresUtils::EstimateMemoryUsed()
1690 size
+= sizeof(nsBidiPresUtils
);
1691 size
+= mBuffer
.Length() * sizeof(PRUnichar
);
1692 size
+= moz_malloc_usable_size(mBidiEngine
->mDirPropsMemory
);
1693 size
+= moz_malloc_usable_size(mBidiEngine
->mLevelsMemory
);
1694 size
+= moz_malloc_usable_size(mBidiEngine
->mRunsMemory
);