Tracer build fixes. (b=588021, r=dvander)
[mozilla-central.git] / layout / base / nsBidiPresUtils.cpp
blob3823f017ced017dd6710ddeafb3e142edca1f6b1
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
41 #ifdef IBMBIDI
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
74 nsIFrame*
75 NS_NewDirectionalFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUnichar aChar);
77 nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
78 mIndexMap(nsnull),
79 mLevels(nsnull),
80 mSuccess(NS_ERROR_FAILURE),
81 mBidiEngine(nsnull)
83 mBidiEngine = new nsBidi();
84 if (mBidiEngine && mContentToFrameIndex.Init()) {
85 mSuccess = NS_OK;
89 nsBidiPresUtils::~nsBidiPresUtils()
91 if (mLevels) {
92 delete[] mLevels;
94 if (mIndexMap) {
95 delete[] mIndexMap;
97 delete mBidiEngine;
100 PRBool
101 nsBidiPresUtils::IsSuccessful() const
103 return NS_SUCCEEDED(mSuccess);
106 /* Some helper methods for Resolve() */
108 // Should this frame be split between text runs?
109 PRBool
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;
117 static nsresult
118 SplitInlineAncestors(nsIFrame* aFrame)
120 nsPresContext *presContext = aFrame->PresContext();
121 nsIPresShell *presShell = presContext->PresShell();
122 nsIFrame* frame = aFrame;
123 nsIFrame* parent = aFrame->GetParent();
124 nsIFrame* newParent;
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);
132 if (NS_FAILED(rv)) {
133 return rv;
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);
142 if (NS_FAILED(rv)) {
143 return rv;
146 // The parent's continuation adopts the siblings after the split.
147 rv = newParent->InsertFrames(nsGkAtoms::nextBidi, nsnull, tail);
148 if (NS_FAILED(rv)) {
149 return rv;
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);
154 if (NS_FAILED(rv)) {
155 return rv;
158 frame = parent;
159 parent = grandparent;
162 return NS_OK;
165 // Convert bidi continuations to fluid continuations for a frame and all of its
166 // inline ancestors.
167 static void
168 JoinInlineAncestors(nsIFrame* aFrame)
170 nsIFrame* frame = aFrame;
171 while (frame && IsBidiSplittable(frame)) {
172 nsIFrame* next = frame->GetNextContinuation();
173 if (next) {
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())
184 break;
185 frame = frame->GetParent();
189 static nsresult
190 CreateBidiContinuation(nsIFrame* aFrame,
191 nsIFrame** aNewFrame)
193 NS_PRECONDITION(aNewFrame, "null OUT ptr");
194 NS_PRECONDITION(aFrame, "null ptr");
196 *aNewFrame = nsnull;
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");
205 nsresult rv = NS_OK;
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);
215 return rv;
218 rv = presShell->FrameConstructor()->
219 CreateContinuingFrame(presContext, aFrame, parent, aNewFrame, PR_FALSE);
220 if (NS_FAILED(rv)) {
221 return rv;
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);
228 if (NS_FAILED(rv)) {
229 return rv;
232 // Split inline ancestor frames
233 rv = SplitInlineAncestors(aFrame);
234 if (NS_FAILED(rv)) {
235 return rv;
238 return NS_OK;
241 static PRBool
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()) {
250 if (frame == aFrame)
251 return PR_TRUE;
253 return PR_FALSE;
256 static void
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)) {
266 child = parent;
267 parent = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, child);
269 NS_ASSERTION (parent, "aFrame is not a descendent of aBlockFrame");
270 while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
271 #ifdef DEBUG
272 PRBool hasNext =
273 #endif
274 aLineIter->Next();
275 NS_ASSERTION(hasNext, "Can't find frame in lines!");
276 aPrevFrame = nsnull;
278 aPrevFrame = child;
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
295 * CHARACTER
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.
311 nsresult
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
322 // InitLogicalArray.
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);
353 CreateBlockBuffer();
355 PRInt32 bufferLength = mBuffer.Length();
357 if (bufferLength < 1) {
358 mSuccess = NS_OK;
359 return mSuccess;
361 PRInt32 runCount;
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) ) {
370 return mSuccess;
373 mSuccess = mBidiEngine->CountRuns(&runCount);
374 if (NS_FAILED(mSuccess) ) {
375 return 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
380 PRInt32 numRun = -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)
396 lineIter.Next();
398 nsIFrame* prevFrame = nsnull;
399 PRBool lineNeedsUpdate = PR_FALSE;
401 PRBool isVisual = presContext->IsVisualMode();
402 if (isVisual) {
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
411 * form controls.
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()) {
419 isVisual = PR_FALSE;
420 break;
425 for (; ;) {
426 if (fragmentLength <= 0) {
427 // Get the next frame from mLogicalFrames
428 if (++frameIndex >= frameCount) {
429 break;
431 frame = mLogicalFrames[frameIndex];
432 frameType = frame->GetType();
433 lineNeedsUpdate = PR_TRUE;
434 if (nsGkAtoms::textFrame == frameType) {
435 content = frame->GetContent();
436 if (!content) {
437 mSuccess = NS_OK;
438 break;
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));
449 continue;
451 PRInt32 start, end;
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;
459 else {
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;
465 fragmentLength = 1;
467 } // if (fragmentLength <= 0)
469 if (runLength <= 0) {
470 // Get the next run of text from the Bidi engine
471 if (++numRun >= runCount) {
472 break;
474 lineOffset = logicalLimit;
475 if (NS_FAILED(mBidiEngine->GetLogicalRun(
476 lineOffset, &logicalLimit, &embeddingLevel) ) ) {
477 break;
479 runLength = logicalLimit - lineOffset;
480 if (isVisual) {
481 embeddingLevel = paraLevel;
483 } // if (runLength <= 0)
485 if (nsGkAtoms::directionalFrame == frameType) {
486 frame->Destroy();
487 frame = nsnull;
488 ++lineOffset;
490 else {
491 propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
492 NS_INT32_TO_PTR(embeddingLevel));
493 propTable->Set(frame, nsIFrame::BaseLevelProperty(),
494 NS_INT32_TO_PTR(paraLevel));
495 if (isTextFrame) {
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();
506 nsIFrame* nextBidi;
507 PRInt32 runEnd = contentOffset + runLength;
508 EnsureBidiContinuation(frame, &nextBidi, frameIndex,
509 contentOffset,
510 runEnd);
511 if (NS_FAILED(mSuccess)) {
512 break;
514 nextBidi->AdjustOffsetsForBidi(runEnd,
515 contentOffset + fragmentLength);
516 frame = nextBidi;
517 contentOffset = runEnd;
518 } // if (runLength < fragmentLength)
519 else {
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;
540 do {
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();
549 if (next) {
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();
561 } // isTextFrame
562 else {
563 ++lineOffset;
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
572 // need splitting.
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
584 // directional run.
585 while (parent &&
586 IsBidiSplittable(parent) &&
587 !child->GetNextSibling()) {
588 nsIFrame* next = parent->GetNextInFlow();
589 if (next) {
590 parent->SetNextContinuation(next);
591 next->SetPrevContinuation(parent);
593 child = 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);
607 } // for
608 return mSuccess;
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);
614 return !kid
615 || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
618 void
619 nsBidiPresUtils::InitLogicalArray(nsIFrame* aCurrentFrame)
621 if (!aCurrentFrame)
622 return;
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) {
638 frame = realFrame;
642 PRUnichar ch = 0;
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:
648 break;
649 case NS_STYLE_UNICODE_BIDI_EMBED:
650 styleContext = frame->GetStyleContext();
652 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
653 ch = kRLE;
655 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
656 ch = kLRE;
658 break;
659 case NS_STYLE_UNICODE_BIDI_OVERRIDE:
660 styleContext = frame->GetStyleContext();
662 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
663 ch = kRLO;
665 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
666 ch = kLRO;
668 break;
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);
675 if (dirFrame) {
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();
688 if (content) {
689 mContentToFrameIndex.Put(content, mLogicalFrames.Length());
691 mLogicalFrames.AppendElement(frame);
693 else {
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);
703 if (dirFrame) {
704 mLogicalFrames.AppendElement(dirFrame);
707 } // for
710 void
711 nsBidiPresUtils::CreateBlockBuffer()
713 mBuffer.SetLength(0);
715 nsIFrame* frame;
716 nsIContent* prevContent = nsnull;
717 PRUint32 i;
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();
726 if (!content) {
727 mSuccess = NS_OK;
728 break;
730 if (content == prevContent) {
731 continue;
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);
754 void
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)
762 return;
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);
770 PRBool isReordered;
771 PRBool hasRTLFrames;
772 Reorder(isReordered, hasRTLFrames);
773 RepositionInlineFrames(aFirstFrameOnLine);
776 nsresult
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;
785 if (mLevels) {
786 delete[] mLevels;
787 mLevels = nsnull;
789 if (mIndexMap) {
790 delete[] mIndexMap;
791 mIndexMap = nsnull;
794 if (!mLevels) {
795 mLevels = new PRUint8[mArraySize];
796 if (!mLevels) {
797 return NS_ERROR_OUT_OF_MEMORY;
800 memset(mLevels, 0, sizeof(PRUint8) * mArraySize);
802 nsIFrame* frame;
803 PRInt32 i;
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;
812 if (!mIndexMap) {
813 mIndexMap = new PRInt32[mArraySize];
815 if (!mIndexMap) {
816 mSuccess = NS_ERROR_OUT_OF_MEMORY;
818 else {
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)
833 } // indexMap
835 if (NS_FAILED(mSuccess) ) {
836 aReordered = PR_FALSE;
838 return mSuccess;
841 nsBidiLevel
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);
854 nsBidiLevel
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);
864 void
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;
889 nsIFrame* frame;
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;
920 } else {
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
938 if (isLTR) {
939 aIsRightMost = PR_FALSE;
940 } else {
941 aIsLeftMost = PR_FALSE;
944 if (nsLayoutUtils::FrameIsNonFirstInIBSplit(firstContinuation)) {
945 // We are not startmost
946 if (isLTR) {
947 aIsLeftMost = PR_FALSE;
948 } else {
949 aIsRightMost = PR_FALSE;
954 // Reduce number of remaining frames of the continuation chain on the line.
955 firstFrameState->mFrameCount--;
958 void
959 nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
960 PRBool aIsOddLevel,
961 nscoord& aLeft,
962 nsContinuationStates* aContinuationStates) const
964 if (!aFrame)
965 return;
967 PRBool isLeftMost, isRightMost;
968 IsLeftOrRightMost(aFrame,
969 aContinuationStates,
970 isLeftMost /* out */,
971 isRightMost /* out */);
973 nsInlineFrame* testFrame = do_QueryFrame(aFrame);
974 if (testFrame) {
975 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
977 if (isLeftMost)
978 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
979 else
980 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
982 if (isRightMost)
983 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
984 else
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();
991 if (isLeftMost)
992 aLeft += margin.left;
994 nscoord start = aLeft;
996 if (!IsBidiLeaf(aFrame))
998 nscoord x = 0;
999 nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
1000 if (isLeftMost) {
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);
1011 while (frame) {
1012 childList.AppendElement(frame);
1013 frame = frame->GetNextSibling();
1015 frame = childList[childList.Length() - 1];
1018 // Reposition the child frames
1019 PRInt32 index = 0;
1020 while (frame) {
1021 RepositionFrame(frame,
1022 aIsOddLevel,
1024 aContinuationStates);
1025 index++;
1026 frame = aIsOddLevel ?
1027 childList[childList.Length() - index - 1] :
1028 frame->GetNextSibling();
1031 if (isRightMost) {
1032 x += borderPadding.right;
1034 aLeft += x;
1035 } else {
1036 aLeft += aFrame->GetSize().width;
1038 nsRect rect = aFrame->GetRect();
1039 aFrame->SetRect(nsRect(start, rect.y, aLeft - start, rect.height));
1041 if (isRightMost)
1042 aLeft += margin.right;
1045 void
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
1055 nsIFrame* frame;
1056 for (frame = aFrame->GetFirstChild(nsnull);
1057 frame;
1058 frame = frame->GetNextSibling()) {
1059 InitContinuationStates(frame,
1060 aContinuationStates);
1065 void
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;
1081 nsIFrame* frame;
1082 PRInt32 count = mVisualFrames.Length();
1083 PRInt32 index;
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),
1099 left,
1100 &continuationStates);
1101 } // for
1104 void
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);
1115 PRBool
1116 nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
1117 PRInt32 aNumFramesOnLine,
1118 nsIFrame** aFirstVisual,
1119 nsIFrame** aLastVisual)
1121 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
1123 PRBool isReordered;
1124 PRBool hasRTLFrames;
1125 Reorder(isReordered, hasRTLFrames);
1126 PRInt32 count = mLogicalFrames.Length();
1128 if (aFirstVisual) {
1129 *aFirstVisual = mVisualFrames[0];
1131 if (aLastVisual) {
1132 *aLastVisual = mVisualFrames[count-1];
1135 // If there's an RTL frame, assume the line is reordered
1136 return isReordered || hasRTLFrames;
1139 nsIFrame*
1140 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
1141 nsIFrame* aFirstFrameOnLine,
1142 PRInt32 aNumFramesOnLine)
1144 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
1146 PRBool isReordered;
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];
1160 return nsnull;
1163 nsIFrame*
1164 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
1165 nsIFrame* aFirstFrameOnLine,
1166 PRInt32 aNumFramesOnLine)
1168 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
1170 PRBool isReordered;
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];
1184 return nsnull;
1187 inline void
1188 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
1189 nsIFrame** aNewFrame,
1190 PRInt32& aFrameIndex,
1191 PRInt32 aStart,
1192 PRInt32 aEnd)
1194 NS_PRECONDITION(aNewFrame, "null OUT ptr");
1195 NS_PRECONDITION(aFrame, "aFrame is null");
1197 aFrame->AdjustOffsetsForBidi(aStart, aEnd);
1198 mSuccess = CreateBidiContinuation(aFrame, aNewFrame);
1201 void
1202 nsBidiPresUtils::RemoveBidiContinuation(nsIFrame* aFrame,
1203 PRInt32 aFirstIndex,
1204 PRInt32 aLastIndex,
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()) {
1216 frame->Destroy();
1217 ++aOffset;
1219 else {
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);
1228 while (frame) {
1229 nsIFrame* prev = frame->GetPrevContinuation();
1230 if (prev) {
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();
1240 } else {
1241 break;
1248 nsresult
1249 nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
1250 PRUnichar* aText,
1251 PRInt32& aTextLength,
1252 nsCharType aCharType,
1253 PRBool aIsOddLevel)
1255 NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
1256 nsresult rv = NS_OK;
1257 // ahmed
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);
1264 break;
1266 case IBMBIDI_NUMERAL_ARABIC:
1267 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1268 break;
1270 case IBMBIDI_NUMERAL_PERSIAN:
1271 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
1272 break;
1274 case IBMBIDI_NUMERAL_REGULAR:
1276 switch (aCharType) {
1278 case eCharType_EuropeanNumber:
1279 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1280 break;
1282 case eCharType_ArabicNumber:
1283 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1284 break;
1286 default:
1287 break;
1289 break;
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);
1296 break;
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);
1303 break;
1305 case IBMBIDI_NUMERAL_NOMINAL:
1306 default:
1307 break;
1310 StripBidiControlCharacters(aText, aTextLength);
1311 return rv;
1314 void
1315 nsBidiPresUtils::StripBidiControlCharacters(PRUnichar* aText,
1316 PRInt32& aTextLength) const
1318 if ( (nsnull == aText) || (aTextLength < 1) ) {
1319 return;
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])) {
1328 ++stripLen;
1330 else {
1331 aText[i - stripLen] = aText[i];
1334 aTextLength -= stripLen;
1337 #if 0 // XXX: for the future use ???
1338 void
1339 RemoveDiacritics(PRUnichar* aText,
1340 PRInt32& aTextLength)
1342 if (aText && (aTextLength > 0) ) {
1343 PRInt32 offset = 0;
1345 for (PRInt32 i = 0; i < aTextLength && aText[i]; i++) {
1346 if (IS_BIDI_DIACRITIC(aText[i]) ) {
1347 ++offset;
1348 continue;
1350 aText[i - offset] = aText[i];
1352 aTextLength = i - offset;
1353 aText[aTextLength] = 0;
1356 #endif
1358 void
1359 nsBidiPresUtils::CalculateCharType(PRInt32& aOffset,
1360 PRInt32 aCharTypeLimit,
1361 PRInt32& aRunLimit,
1362 PRInt32& aRunLength,
1363 PRInt32& aRunCount,
1364 PRUint8& aCharType,
1365 PRUint8& aPrevCharType) const
1368 PRBool strongTypeFound = PR_FALSE;
1369 PRInt32 offset;
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;
1384 else {
1385 mBidiEngine->GetCharTypeAt(offset, &charType);
1388 if (!CHARTYPE_IS_WEAK(charType) ) {
1390 if (strongTypeFound
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;
1398 aRunLimit = offset;
1399 ++aRunCount;
1400 break;
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;
1417 aOffset = offset;
1420 nsresult nsBidiPresUtils::ProcessText(const PRUnichar* aText,
1421 PRInt32 aLength,
1422 nsBidiDirection aBaseDirection,
1423 nsPresContext* aPresContext,
1424 BidiProcessor& aprocessor,
1425 Mode aMode,
1426 nsBidiPositionResolve* aPosResolve,
1427 PRInt32 aPosResolveCount,
1428 nscoord* aWidth)
1430 NS_ASSERTION((aPosResolve == nsnull) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1432 PRInt32 runCount;
1434 mBuffer.Assign(aText, aLength);
1436 nsresult rv = mBidiEngine->SetPara(mBuffer.get(), aLength, aBaseDirection, nsnull);
1437 if (NS_FAILED(rv))
1438 return rv;
1440 rv = mBidiEngine->CountRuns(&runCount);
1441 if (NS_FAILED(rv))
1442 return rv;
1444 nscoord xOffset = 0;
1445 nscoord width, xEndRun;
1446 nscoord totalWidth = 0;
1447 PRInt32 i, start, limit, length;
1448 PRUint32 visualStart = 0;
1449 PRUint8 charType;
1450 PRUint8 prevType = eCharType_LeftToRight;
1451 nsBidiLevel level;
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);
1462 if (NS_FAILED(rv))
1463 return rv;
1465 rv = mBidiEngine->GetLogicalRun(start, &limit, &level);
1466 if (NS_FAILED(rv))
1467 return rv;
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.
1487 if (level & 1) {
1488 aprocessor.SetText(aText + start, subRunLength, nsBidiDirection(level & 1));
1489 width = aprocessor.GetWidth();
1490 xOffset += width;
1491 xEndRun = xOffset;
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;
1509 if (level & 1) {
1510 xOffset -= 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)
1527 continue;
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.
1553 else {
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
1563 * ^^^^^^ (subWidth)
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
1572 * ^^^^^^ (subWidth)
1573 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1574 * ^^ (posResolve->visualWidth)
1576 nscoord subWidth;
1577 // The position in the text where this run's "left part" begins.
1578 const PRUnichar* visualLeftPart, *visualRightSide;
1579 if (level & 1) {
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;
1587 else {
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;
1605 if (!(level & 1)) {
1606 xOffset += width;
1609 --subRunCount;
1610 start = lineOffset;
1611 subRunLimit = typeLimit;
1612 subRunLength = typeLimit - lineOffset;
1613 } // while
1614 if (level & 1) {
1615 xOffset = xEndRun;
1618 visualStart += length;
1619 } // for
1621 if (aWidth) {
1622 *aWidth = totalWidth;
1624 return NS_OK;
1627 class NS_STACK_CLASS nsIRenderingContextBidiProcessor : public nsBidiPresUtils::BidiProcessor {
1628 public:
1629 nsIRenderingContextBidiProcessor(nsIRenderingContext* aCtx,
1630 const nsPoint& aPt)
1631 : mCtx(aCtx), mPt(aPt) { }
1633 ~nsIRenderingContextBidiProcessor()
1635 mCtx->SetRightToLeftText(PR_FALSE);
1638 virtual void SetText(const PRUnichar* aText,
1639 PRInt32 aLength,
1640 nsBidiDirection aDirection)
1642 mCtx->SetTextRunRTL(aDirection==NSBIDI_RTL);
1643 mText = aText;
1644 mLength = aLength;
1647 virtual nscoord GetWidth()
1649 nscoord width;
1650 mCtx->GetWidth(mText, mLength, width, nsnull);
1651 return width;
1654 virtual void DrawText(nscoord aXOffset,
1655 nscoord)
1657 mCtx->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y);
1660 private:
1661 nsIRenderingContext* mCtx;
1662 nsPoint mPt;
1663 const PRUnichar* mText;
1664 PRInt32 mLength;
1665 nsBidiDirection mDirection;
1668 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar* aText,
1669 PRInt32 aLength,
1670 nsBidiDirection aBaseDirection,
1671 nsPresContext* aPresContext,
1672 nsIRenderingContext& aRenderingContext,
1673 Mode aMode,
1674 nscoord aX,
1675 nscoord aY,
1676 nsBidiPositionResolve* aPosResolve,
1677 PRInt32 aPosResolveCount,
1678 nscoord* aWidth)
1680 nsIRenderingContextBidiProcessor processor(&aRenderingContext, nsPoint(aX, aY));
1682 return ProcessText(aText, aLength, aBaseDirection, aPresContext, processor,
1683 aMode, aPosResolve, aPosResolveCount, aWidth);
1686 PRUint32 nsBidiPresUtils::EstimateMemoryUsed()
1688 PRUint32 size = 0;
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);
1696 return size;
1700 #endif // IBMBIDI