Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / base / nsBidiPresUtils.cpp
blob711e56509ce3f333ac7e12c94b2d3a0e6a4e9d67
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>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #ifdef IBMBIDI
42 #include "nsBidiPresUtils.h"
43 #include "nsTextFragment.h"
44 #include "nsGkAtoms.h"
45 #include "nsPresContext.h"
46 #include "nsIRenderingContext.h"
47 #include "nsIServiceManager.h"
48 #include "nsFrameManager.h"
49 #include "nsBidiFrames.h"
50 #include "nsBidiUtils.h"
51 #include "nsCSSFrameConstructor.h"
52 #include "nsHTMLContainerFrame.h"
53 #include "nsInlineFrame.h"
54 #include "nsPlaceholderFrame.h"
56 static NS_DEFINE_IID(kInlineFrameCID, NS_INLINE_FRAME_CID);
58 static const PRUnichar kSpace = 0x0020;
59 static const PRUnichar kLineSeparator = 0x2028;
60 static const PRUnichar kObjectSubstitute = 0xFFFC;
61 static const PRUnichar kLRE = 0x202A;
62 static const PRUnichar kRLE = 0x202B;
63 static const PRUnichar kLRO = 0x202D;
64 static const PRUnichar kRLO = 0x202E;
65 static const PRUnichar kPDF = 0x202C;
66 static const PRUnichar ALEF = 0x05D0;
68 #define CHAR_IS_HEBREW(c) ((0x0590 <= (c)) && ((c)<= 0x05FF))
69 // Note: The above code are moved from gfx/src/windows/nsRenderingContextWin.cpp
71 nsIFrame*
72 NS_NewDirectionalFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUnichar aChar);
74 nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
75 mIndexMap(nsnull),
76 mLevels(nsnull),
77 mSuccess(NS_ERROR_FAILURE),
78 mBidiEngine(nsnull)
80 mBidiEngine = new nsBidi();
81 if (mBidiEngine && mContentToFrameIndex.Init()) {
82 mSuccess = NS_OK;
86 nsBidiPresUtils::~nsBidiPresUtils()
88 if (mLevels) {
89 delete[] mLevels;
91 if (mIndexMap) {
92 delete[] mIndexMap;
94 delete mBidiEngine;
97 PRBool
98 nsBidiPresUtils::IsSuccessful() const
100 return NS_SUCCEEDED(mSuccess);
103 /* Some helper methods for Resolve() */
105 // Should this frame be split between text runs?
106 PRBool
107 IsBidiSplittable(nsIFrame* aFrame) {
108 nsIAtom* frameType = aFrame->GetType();
109 // Bidi inline containers should be split, unless they're line frames.
110 return aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer)
111 && frameType != nsGkAtoms::lineFrame;
114 static nsresult
115 SplitInlineAncestors(nsIFrame* aFrame)
117 nsPresContext *presContext = aFrame->PresContext();
118 nsIPresShell *presShell = presContext->PresShell();
119 nsIFrame* frame = aFrame;
120 nsIFrame* parent = aFrame->GetParent();
121 nsIFrame* newFrame = aFrame->GetNextSibling();
122 nsIFrame* newParent;
124 while (IsBidiSplittable(parent)) {
125 nsIFrame* grandparent = parent->GetParent();
126 NS_ASSERTION(grandparent, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
128 nsresult rv = presShell->FrameConstructor()->
129 CreateContinuingFrame(presContext, parent, grandparent, &newParent, PR_FALSE);
130 if (NS_FAILED(rv)) {
131 return rv;
134 // The new parent adopts the new frame
135 frame->SetNextSibling(nsnull);
136 rv = newParent->InsertFrames(nsGkAtoms::nextBidi, nsnull, newFrame);
137 if (NS_FAILED(rv)) {
138 return rv;
141 // Reparent views as necessary
142 rv = nsHTMLContainerFrame::ReparentFrameViewList(presContext, newFrame, parent, newParent);
143 if (NS_FAILED(rv)) {
144 return rv;
147 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
148 rv = grandparent->InsertFrames(nsGkAtoms::nextBidi, parent, newParent);
149 if (NS_FAILED(rv)) {
150 return rv;
153 frame = parent;
154 newFrame = newParent;
155 parent = grandparent;
158 return NS_OK;
161 // Convert bidi continuations to fluid continuations for a frame and all of its
162 // inline ancestors.
163 static void
164 JoinInlineAncestors(nsIFrame* aFrame)
166 nsIFrame* frame = aFrame;
167 while (frame && IsBidiSplittable(frame)) {
168 nsIFrame* next = frame->GetNextContinuation();
169 if (next) {
170 NS_ASSERTION (!frame->GetNextInFlow() || frame->GetNextInFlow() == next,
171 "next-in-flow is not next continuation!");
172 frame->SetNextInFlow(next);
174 NS_ASSERTION (!next->GetPrevInFlow() || next->GetPrevInFlow() == frame,
175 "prev-in-flow is not prev continuation!");
176 next->SetPrevInFlow(frame);
178 // Join the parent only as long as we're its last child.
179 if (frame->GetNextSibling())
180 break;
181 frame = frame->GetParent();
185 static nsresult
186 CreateBidiContinuation(nsIFrame* aFrame,
187 nsIFrame** aNewFrame)
189 NS_PRECONDITION(aNewFrame, "null OUT ptr");
190 NS_PRECONDITION(aFrame, "null ptr");
192 *aNewFrame = nsnull;
194 nsPresContext *presContext = aFrame->PresContext();
195 nsIPresShell *presShell = presContext->PresShell();
196 NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
198 nsIFrame* parent = aFrame->GetParent();
199 NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
201 nsresult rv = presShell->FrameConstructor()->
202 CreateContinuingFrame(presContext, aFrame, parent, aNewFrame, PR_FALSE);
203 if (NS_FAILED(rv)) {
204 return rv;
207 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
208 rv = parent->InsertFrames(nsGkAtoms::nextBidi, aFrame, *aNewFrame);
209 if (NS_FAILED(rv)) {
210 return rv;
213 // Split inline ancestor frames
214 rv = SplitInlineAncestors(aFrame);
215 if (NS_FAILED(rv)) {
216 return rv;
219 return NS_OK;
222 static PRBool
223 IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
224 nsIFrame* aPrevFrame, nsIFrame* aFrame)
226 nsIFrame* endFrame = aLineIter->IsLastLineInList() ? nsnull :
227 aLineIter->GetLine().next()->mFirstChild;
228 nsIFrame* startFrame = aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
229 return nsFrameList(startFrame).ContainsFrameBefore(aFrame, endFrame);
232 static void
233 AdvanceLineIteratorToFrame(nsIFrame* aFrame,
234 nsBlockInFlowLineIterator* aLineIter,
235 nsIFrame*& aPrevFrame)
237 // Advance aLine to the line containing aFrame
238 nsIFrame* child = aFrame;
239 nsFrameManager* frameManager = aFrame->PresContext()->FrameManager();
240 nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, child);
241 while (parent && !nsLayoutUtils::GetAsBlock(parent)) {
242 child = parent;
243 parent = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, child);
245 NS_ASSERTION (parent, "aFrame is not a descendent of aBlockFrame");
246 while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
247 #ifdef DEBUG
248 PRBool hasNext =
249 #endif
250 aLineIter->Next();
251 NS_ASSERTION(hasNext, "Can't find frame in lines!");
252 aPrevFrame = nsnull;
254 aPrevFrame = child;
258 * Overview of the implementation of Resolve():
260 * Walk through the descendants of aBlockFrame and build:
261 * * mLogicalArray: an nsVoidArray of nsIFrame* pointers in logical order
262 * * mBuffer: an nsAutoString containing a representation of
263 * the content of the frames.
264 * In the case of text frames, this is the actual text context of the
265 * frames, but some other elements are represented in a symbolic form which
266 * will make the Unicode Bidi Algorithm give the correct results.
267 * Bidi embeddings and overrides set by CSS or <bdo> elements are
268 * represented by the corresponding Unicode control characters.
269 * <br> elements are represented by U+2028 LINE SEPARATOR
270 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
271 * CHARACTER
273 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
274 * by nsBidi::SetPara() and division into directional runs by
275 * nsBidi::CountRuns().
277 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
278 * correlate them with the frames indexed in mLogicalArray, setting the
279 * baseLevel, embeddingLevel, and charType properties according to the results
280 * returned by the Bidi engine and CalculateCharType().
282 * The rendering layer requires each text frame to contain text in only one
283 * direction and of only one character type, so we may need to call
284 * EnsureBidiContinuation() to split frames. We may also need to call
285 * RemoveBidiContinuation() to convert frames created by
286 * EnsureBidiContinuation() in previous reflows into fluid continuations.
288 nsresult
289 nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame,
290 PRBool aIsVisualFormControl)
292 mLogicalFrames.Clear();
293 mContentToFrameIndex.Clear();
295 nsPresContext *presContext = aBlockFrame->PresContext();
296 nsIPresShell* shell = presContext->PresShell();
297 nsStyleContext* styleContext = aBlockFrame->GetStyleContext();
299 // handle bidi-override being set on the block itself before calling
300 // InitLogicalArray.
301 const nsStyleVisibility* vis = aBlockFrame->GetStyleVisibility();
302 const nsStyleTextReset* text = aBlockFrame->GetStyleTextReset();
304 if (text->mUnicodeBidi == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
305 nsIFrame *directionalFrame = nsnull;
307 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
308 directionalFrame = NS_NewDirectionalFrame(shell, styleContext, kRLO);
310 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
311 directionalFrame = NS_NewDirectionalFrame(shell, styleContext, kLRO);
314 if (directionalFrame) {
315 mLogicalFrames.AppendElement(directionalFrame);
318 for (nsBlockFrame* block = aBlockFrame; block;
319 block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
320 block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
321 InitLogicalArray(block->GetFirstChild(nsnull));
324 if (text->mUnicodeBidi == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
325 nsIFrame* directionalFrame = NS_NewDirectionalFrame(shell, styleContext, kPDF);
326 if (directionalFrame) {
327 mLogicalFrames.AppendElement(directionalFrame);
331 CreateBlockBuffer();
333 PRInt32 bufferLength = mBuffer.Length();
335 if (bufferLength < 1) {
336 mSuccess = NS_OK;
337 return mSuccess;
339 PRInt32 runCount;
340 PRUint8 embeddingLevel;
342 nsBidiLevel paraLevel = embeddingLevel =
343 (NS_STYLE_DIRECTION_RTL == vis->mDirection)
344 ? NSBIDI_RTL : NSBIDI_LTR;
346 mSuccess = mBidiEngine->SetPara(mBuffer.get(), bufferLength, paraLevel, nsnull);
347 if (NS_FAILED(mSuccess) ) {
348 return mSuccess;
351 PRBool isVisual;
352 if (aIsVisualFormControl) {
353 isVisual = PR_FALSE;
354 } else {
355 isVisual = presContext->IsVisualMode();
357 mSuccess = mBidiEngine->CountRuns(&runCount);
358 if (NS_FAILED(mSuccess) ) {
359 return mSuccess;
361 PRInt32 runLength = 0;
362 PRInt32 fragmentLength = 0;
363 PRInt32 temp;
364 PRInt32 frameIndex = -1;
365 PRInt32 frameCount = mLogicalFrames.Count();
366 PRInt32 contentOffset = 0; // offset within current frame
367 PRInt32 lineOffset = 0; // offset within mBuffer
368 PRInt32 logicalLimit = 0;
369 PRInt32 numRun = -1;
370 PRUint8 charType;
371 PRUint8 prevType = eCharType_LeftToRight;
372 PRBool isTextFrame = PR_FALSE;
373 nsIFrame* frame = nsnull;
374 nsIFrame* nextBidi;
375 nsIContent* content = nsnull;
376 const nsTextFragment* fragment;
377 nsIAtom* frameType = nsnull;
379 nsPropertyTable *propTable = presContext->PropertyTable();
381 nsBlockInFlowLineIterator lineIter(aBlockFrame, aBlockFrame->begin_lines(), PR_FALSE);
382 if (lineIter.GetLine() == aBlockFrame->end_lines()) {
383 // Advance to first valid line (might be in a next-continuation)
384 lineIter.Next();
386 nsIFrame* prevFrame = nsnull;
387 PRBool lineNeedsUpdate = PR_FALSE;
389 for (; ;) {
390 if (fragmentLength <= 0) {
391 if (++frameIndex >= frameCount) {
392 break;
394 contentOffset = 0;
396 frame = (nsIFrame*) (mLogicalFrames[frameIndex]);
397 frameType = frame->GetType();
398 lineNeedsUpdate = PR_TRUE;
399 if (nsGkAtoms::textFrame == frameType) {
400 content = frame->GetContent();
401 if (!content) {
402 mSuccess = NS_OK;
403 break;
405 fragment = content->GetText();
406 if (!fragment) {
407 mSuccess = NS_ERROR_FAILURE;
408 break;
410 fragmentLength = fragment->GetLength();
411 isTextFrame = PR_TRUE;
412 } // if text frame
413 else {
414 isTextFrame = PR_FALSE;
415 fragmentLength = 1;
417 } // if (fragmentLength <= 0)
418 if (runLength <= 0) {
419 if (++numRun >= runCount) {
420 break;
422 lineOffset = logicalLimit;
423 if (NS_FAILED(mBidiEngine->GetLogicalRun(
424 lineOffset, &logicalLimit, &embeddingLevel) ) ) {
425 break;
427 runLength = logicalLimit - lineOffset;
428 if (isVisual) {
429 embeddingLevel = paraLevel;
431 } // if (runLength <= 0)
433 if (nsGkAtoms::directionalFrame == frameType) {
434 frame->Destroy();
435 frame = nsnull;
436 ++lineOffset;
438 else {
439 propTable->SetProperty(frame, nsGkAtoms::embeddingLevel,
440 NS_INT32_TO_PTR(embeddingLevel), nsnull, nsnull);
441 propTable->SetProperty(frame, nsGkAtoms::baseLevel,
442 NS_INT32_TO_PTR(paraLevel), nsnull, nsnull);
443 if (isTextFrame) {
444 PRInt32 typeLimit = PR_MIN(logicalLimit, lineOffset + fragmentLength);
445 CalculateCharType(lineOffset, typeLimit, logicalLimit, runLength,
446 runCount, charType, prevType);
447 // IBMBIDI - Egypt - Start
448 propTable->SetProperty(frame, nsGkAtoms::charType,
449 NS_INT32_TO_PTR(charType), nsnull, nsnull);
450 // IBMBIDI - Egypt - End
452 if ( (runLength > 0) && (runLength < fragmentLength) ) {
453 if (lineNeedsUpdate) {
454 AdvanceLineIteratorToFrame(frame, &lineIter, prevFrame);
455 lineNeedsUpdate = PR_FALSE;
457 lineIter.GetLine()->MarkDirty();
458 if (!EnsureBidiContinuation(frame, &nextBidi, frameIndex,
459 contentOffset,
460 contentOffset + runLength,
461 lineNeedsUpdate)) {
462 break;
464 frame = nextBidi;
465 contentOffset += runLength;
466 } // if (runLength < fragmentLength)
467 else {
468 PRInt32 newIndex = 0;
469 mContentToFrameIndex.Get(content, &newIndex);
470 if (newIndex > frameIndex) {
471 RemoveBidiContinuation(frame, frameIndex, newIndex, temp);
472 if (lineNeedsUpdate) {
473 AdvanceLineIteratorToFrame(frame, &lineIter, prevFrame);
474 lineNeedsUpdate = PR_FALSE;
476 lineIter.GetLine()->MarkDirty();
477 runLength -= temp;
478 fragmentLength -= temp;
479 lineOffset += temp;
480 frameIndex = newIndex;
482 frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
484 } // isTextFrame
485 else {
486 ++lineOffset;
488 } // not directionalFrame
489 temp = runLength;
490 runLength -= fragmentLength;
491 fragmentLength -= temp;
493 if (frame && fragmentLength <= 0) {
494 if (runLength <= 0) {
495 // If the frame is at the end of a run, split all ancestor inlines that need splitting.
496 nsIFrame* child = frame;
497 nsIFrame* parent = frame->GetParent();
498 // As long as we're on the last sibling, the parent doesn't have to be split.
499 while (parent &&
500 IsBidiSplittable(parent) &&
501 !child->GetNextSibling()) {
502 child = parent;
503 parent = child->GetParent();
505 if (parent && IsBidiSplittable(parent))
506 SplitInlineAncestors(child);
508 else if (!frame->GetNextSibling()) {
509 // We're not at an end of a run, and |frame| is the last child of its parent.
510 // If its ancestors happen to have bidi continuations, convert them into
511 // fluid continuations.
512 nsIFrame* parent = frame->GetParent();
513 JoinInlineAncestors(parent);
516 } // for
517 return mSuccess;
520 // Should this frame be treated as a leaf (e.g. when building mLogicalArray)?
521 PRBool IsBidiLeaf(nsIFrame* aFrame) {
522 nsIFrame* kid = aFrame->GetFirstChild(nsnull);
523 return !kid
524 || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
527 void
528 nsBidiPresUtils::InitLogicalArray(nsIFrame* aCurrentFrame)
530 if (!aCurrentFrame)
531 return;
533 nsIPresShell* shell = aCurrentFrame->PresContext()->PresShell();
534 nsStyleContext* styleContext;
536 for (nsIFrame* childFrame = aCurrentFrame; childFrame;
537 childFrame = childFrame->GetNextSibling()) {
539 // If the real frame for a placeholder is an inline container, we need to
540 // drill down into it and include its contents in Bidi resolution. If it
541 // isn't an inline container, we just use the placeholder.
542 nsIFrame* frame = childFrame;
543 if (nsGkAtoms::placeholderFrame == childFrame->GetType()) {
544 nsIFrame* realFrame =
545 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
546 if (realFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
547 frame = realFrame;
551 PRUnichar ch = 0;
552 if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
553 const nsStyleVisibility* vis = frame->GetStyleVisibility();
554 const nsStyleTextReset* text = frame->GetStyleTextReset();
555 switch (text->mUnicodeBidi) {
556 case NS_STYLE_UNICODE_BIDI_NORMAL:
557 break;
558 case NS_STYLE_UNICODE_BIDI_EMBED:
559 styleContext = frame->GetStyleContext();
561 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
562 ch = kRLE;
564 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
565 ch = kLRE;
567 break;
568 case NS_STYLE_UNICODE_BIDI_OVERRIDE:
569 styleContext = frame->GetStyleContext();
571 if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
572 ch = kRLO;
574 else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
575 ch = kLRO;
577 break;
580 // Create a directional frame before the first frame of an
581 // element specifying embedding or override
582 if (ch != 0 && !frame->GetPrevContinuation()) {
583 nsIFrame* dirFrame = NS_NewDirectionalFrame(shell, styleContext, ch);
584 if (dirFrame) {
585 mLogicalFrames.AppendElement(dirFrame);
590 if (IsBidiLeaf(frame)) {
591 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
592 * and add its index to the mContentToFrameIndex hashtable. This
593 * will be used in RemoveBidiContinuation() to identify the last
594 * frame in the array with a given content.
596 nsIContent* content = frame->GetContent();
597 if (content) {
598 mContentToFrameIndex.Put(content, mLogicalFrames.Count());
600 mLogicalFrames.AppendElement(frame);
602 else {
603 nsIFrame* kid = frame->GetFirstChild(nsnull);
604 InitLogicalArray(kid);
607 // If the element is attributed by dir, indicate direction pop (add PDF frame)
608 if (ch != 0 && !frame->GetNextContinuation()) {
609 // Create a directional frame after the last frame of an
610 // element specifying embedding or override
611 nsIFrame* dirFrame = NS_NewDirectionalFrame(shell, styleContext, kPDF);
612 if (dirFrame) {
613 mLogicalFrames.AppendElement(dirFrame);
616 } // for
619 void
620 nsBidiPresUtils::CreateBlockBuffer()
622 mBuffer.SetLength(0);
624 nsIFrame* frame;
625 nsIContent* prevContent = nsnull;
626 PRUint32 i;
627 PRUint32 count = mLogicalFrames.Count();
629 for (i = 0; i < count; i++) {
630 frame = (nsIFrame*) (mLogicalFrames[i]);
631 nsIAtom* frameType = frame->GetType();
633 if (nsGkAtoms::textFrame == frameType) {
634 nsIContent* content = frame->GetContent();
635 if (!content) {
636 mSuccess = NS_OK;
637 break;
639 if (content == prevContent) {
640 continue;
642 prevContent = content;
643 content->AppendTextTo(mBuffer);
645 else if (nsGkAtoms::brFrame == frameType) { // break frame
646 // Append line separator
647 mBuffer.Append(kLineSeparator);
649 else if (nsGkAtoms::directionalFrame == frameType) {
650 nsDirectionalFrame* dirFrame = static_cast<nsDirectionalFrame*>(frame);
651 mBuffer.Append(dirFrame->GetChar());
653 else { // not text frame
654 // See the Unicode Bidi Algorithm:
655 // "...inline objects (such as graphics) are treated as if they are ... U+FFFC"
656 mBuffer.Append(kObjectSubstitute);
659 // XXX: TODO: Handle preformatted text ('\n')
660 mBuffer.ReplaceChar("\t\r\n", kSpace);
663 void
664 nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine,
665 PRInt32 aNumFramesOnLine)
667 // If this line consists of a line frame, reorder the line frame's children.
668 if (aFirstFrameOnLine->GetType() == nsGkAtoms::lineFrame) {
669 aFirstFrameOnLine = aFirstFrameOnLine->GetFirstChild(nsnull);
670 if (!aFirstFrameOnLine)
671 return;
672 // All children of the line frame are on the first line. Setting aNumFramesOnLine
673 // to -1 makes InitLogicalArrayFromLine look at all of them.
674 aNumFramesOnLine = -1;
677 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
679 PRBool isReordered;
680 PRBool hasRTLFrames;
681 Reorder(isReordered, hasRTLFrames);
682 RepositionInlineFrames(aFirstFrameOnLine);
685 nsresult
686 nsBidiPresUtils::Reorder(PRBool& aReordered, PRBool& aHasRTLFrames)
688 aReordered = PR_FALSE;
689 aHasRTLFrames = PR_FALSE;
690 PRInt32 count = mLogicalFrames.Count();
692 if (mArraySize < count) {
693 mArraySize = count << 1;
694 if (mLevels) {
695 delete[] mLevels;
696 mLevels = nsnull;
698 if (mIndexMap) {
699 delete[] mIndexMap;
700 mIndexMap = nsnull;
703 if (!mLevels) {
704 mLevels = new PRUint8[mArraySize];
705 if (!mLevels) {
706 return NS_ERROR_OUT_OF_MEMORY;
709 memset(mLevels, 0, sizeof(PRUint8) * mArraySize);
711 nsIFrame* frame;
712 PRInt32 i;
714 for (i = 0; i < count; i++) {
715 frame = (nsIFrame*) (mLogicalFrames[i]);
716 mLevels[i] = GetFrameEmbeddingLevel(frame);
717 if (mLevels[i] & 1) {
718 aHasRTLFrames = PR_TRUE;
721 if (!mIndexMap) {
722 mIndexMap = new PRInt32[mArraySize];
724 if (!mIndexMap) {
725 mSuccess = NS_ERROR_OUT_OF_MEMORY;
727 else {
728 memset(mIndexMap, 0, sizeof(PRUint32) * mArraySize);
730 mSuccess = mBidiEngine->ReorderVisual(mLevels, count, mIndexMap);
732 if (NS_SUCCEEDED(mSuccess) ) {
733 mVisualFrames.Clear();
735 for (i = 0; i < count; i++) {
736 mVisualFrames.AppendElement(mLogicalFrames[mIndexMap[i]]);
737 if (i != mIndexMap[i]) {
738 aReordered = PR_TRUE;
741 } // NS_SUCCEEDED(mSuccess)
742 } // indexMap
744 if (NS_FAILED(mSuccess) ) {
745 aReordered = PR_FALSE;
747 return mSuccess;
750 nsBidiLevel
751 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
753 nsIFrame* firstLeaf = aFrame;
754 while (!IsBidiLeaf(firstLeaf)) {
755 firstLeaf = firstLeaf->GetFirstChild(nsnull);
757 return NS_GET_EMBEDDING_LEVEL(firstLeaf);
760 nsBidiLevel
761 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame)
763 nsIFrame* firstLeaf = aFrame;
764 while (!IsBidiLeaf(firstLeaf)) {
765 firstLeaf = firstLeaf->GetFirstChild(nsnull);
767 return NS_GET_BASE_LEVEL(firstLeaf);
770 void
771 nsBidiPresUtils::IsLeftOrRightMost(nsIFrame* aFrame,
772 nsContinuationStates* aContinuationStates,
773 PRBool& aIsLeftMost /* out */,
774 PRBool& aIsRightMost /* out */) const
776 const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
777 PRBool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
780 * Since we lay out frames from left to right (in both LTR and RTL), visiting a
781 * frame with 'mFirstVisualFrame == nsnull', means it's the first appearance of
782 * one of its continuation chain frames on the line.
783 * To determine if it's the last visual frame of its continuation chain on the line
784 * or not, we count the number of frames of the chain on the line, and then reduce
785 * it when we lay out a frame of the chain. If this value becomes 1 it means
786 * that it's the last visual frame of its continuation chain on this line.
789 nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
790 nsFrameContinuationState* firstFrameState;
792 if (!frameState->mFirstVisualFrame) {
793 // aFrame is the first visual frame of its continuation chain
794 nsFrameContinuationState* contState;
795 nsIFrame* frame;
797 frameState->mFrameCount = 1;
798 frameState->mFirstVisualFrame = aFrame;
801 * Traverse continuation chain of aFrame in both backward and forward
802 * directions while the frames are on this line. Count the frames and
803 * set their mFirstVisualFrame to aFrame.
805 // Traverse continuation chain backward
806 for (frame = aFrame->GetPrevContinuation();
807 frame && (contState = aContinuationStates->GetEntry(frame));
808 frame = frame->GetPrevContinuation()) {
809 frameState->mFrameCount++;
810 contState->mFirstVisualFrame = aFrame;
812 frameState->mHasContOnPrevLines = (frame != nsnull);
814 // Traverse continuation chain forward
815 for (frame = aFrame->GetNextContinuation();
816 frame && (contState = aContinuationStates->GetEntry(frame));
817 frame = frame->GetNextContinuation()) {
818 frameState->mFrameCount++;
819 contState->mFirstVisualFrame = aFrame;
821 frameState->mHasContOnNextLines = (frame != nsnull);
823 aIsLeftMost = isLTR ? !frameState->mHasContOnPrevLines
824 : !frameState->mHasContOnNextLines;
825 firstFrameState = frameState;
826 } else {
827 // aFrame is not the first visual frame of its continuation chain
828 aIsLeftMost = PR_FALSE;
829 firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
832 aIsRightMost = (firstFrameState->mFrameCount == 1) &&
833 (isLTR ? !firstFrameState->mHasContOnNextLines
834 : !firstFrameState->mHasContOnPrevLines);
836 // Reduce number of remaining frames of the continuation chain on the line.
837 firstFrameState->mFrameCount--;
840 void
841 nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
842 PRBool aIsOddLevel,
843 nscoord& aLeft,
844 nsContinuationStates* aContinuationStates) const
846 if (!aFrame)
847 return;
849 PRBool isLeftMost, isRightMost;
850 IsLeftOrRightMost(aFrame,
851 aContinuationStates,
852 isLeftMost /* out */,
853 isRightMost /* out */);
855 nsIFrame* testFrame;
856 aFrame->QueryInterface(kInlineFrameCID, (void**)&testFrame);
858 if (testFrame) {
859 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
861 if (isLeftMost)
862 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
863 else
864 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
866 if (isRightMost)
867 aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
868 else
869 aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
871 // This method is called from nsBlockFrame::PlaceLine via the call to
872 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
873 // have been reflowed, which is required for GetUsedMargin/Border/Padding
874 nsMargin margin = aFrame->GetUsedMargin();
875 if (isLeftMost)
876 aLeft += margin.left;
878 nscoord start = aLeft;
880 if (!IsBidiLeaf(aFrame))
882 nscoord x = 0;
883 nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
884 if (isLeftMost) {
885 x += borderPadding.left;
888 // If aIsOddLevel is true, so we need to traverse the child list
889 // in reverse order, to make it O(n) we store the list locally and
890 // iterate the list reversely
891 nsVoidArray childList;
892 nsIFrame *frame = aFrame->GetFirstChild(nsnull);
893 if (frame && aIsOddLevel) {
894 childList.AppendElement(nsnull);
895 while (frame) {
896 childList.AppendElement(frame);
897 frame = frame->GetNextSibling();
899 frame = (nsIFrame*)childList[childList.Count() - 1];
902 // Reposition the child frames
903 PRInt32 index = 0;
904 while (frame) {
905 RepositionFrame(frame,
906 aIsOddLevel,
908 aContinuationStates);
909 index++;
910 frame = aIsOddLevel ?
911 (nsIFrame*)childList[childList.Count() - index - 1] :
912 frame->GetNextSibling();
915 if (isRightMost) {
916 x += borderPadding.right;
918 aLeft += x;
919 } else {
920 aLeft += aFrame->GetSize().width;
922 nsRect rect = aFrame->GetRect();
923 aFrame->SetRect(nsRect(start, rect.y, aLeft - start, rect.height));
925 if (isRightMost)
926 aLeft += margin.right;
929 void
930 nsBidiPresUtils::InitContinuationStates(nsIFrame* aFrame,
931 nsContinuationStates* aContinuationStates) const
933 nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame);
934 state->mFirstVisualFrame = nsnull;
935 state->mFrameCount = 0;
937 if (!IsBidiLeaf(aFrame)) {
938 // Continue for child frames
939 nsIFrame* frame;
940 for (frame = aFrame->GetFirstChild(nsnull);
941 frame;
942 frame = frame->GetNextSibling()) {
943 InitContinuationStates(frame,
944 aContinuationStates);
949 void
950 nsBidiPresUtils::RepositionInlineFrames(nsIFrame* aFirstChild) const
952 const nsStyleVisibility* vis = aFirstChild->GetStyleVisibility();
953 PRBool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
954 nscoord leftSpace = 0;
956 // This method is called from nsBlockFrame::PlaceLine via the call to
957 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
958 // have been reflowed, which is required for GetUsedMargin/Border/Padding
959 nsMargin margin = aFirstChild->GetUsedMargin();
960 if (!aFirstChild->GetPrevContinuation())
961 leftSpace = isLTR ? margin.left : margin.right;
963 nscoord left = aFirstChild->GetPosition().x - leftSpace;
964 nsIFrame* frame;
965 PRInt32 count = mVisualFrames.Count();
966 PRInt32 index;
967 nsContinuationStates continuationStates;
969 continuationStates.Init();
971 // Initialize continuation states to (nsnull, 0) for
972 // each frame on the line.
973 for (index = 0; index < count; index++) {
974 InitContinuationStates((nsIFrame*)mVisualFrames[index],
975 &continuationStates);
978 // Reposition frames in visual order
979 for (index = 0; index < count; index++) {
980 frame = (nsIFrame*) (mVisualFrames[index]);
981 RepositionFrame(frame,
982 (mLevels[mIndexMap[index]] & 1),
983 left,
984 &continuationStates);
985 } // for
988 void
989 nsBidiPresUtils::InitLogicalArrayFromLine(nsIFrame* aFirstFrameOnLine,
990 PRInt32 aNumFramesOnLine) {
991 mLogicalFrames.Clear();
992 for (nsIFrame* frame = aFirstFrameOnLine;
993 frame && aNumFramesOnLine--;
994 frame = frame->GetNextSibling()) {
995 mLogicalFrames.AppendElement(frame);
999 PRBool
1000 nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
1001 PRInt32 aNumFramesOnLine,
1002 nsIFrame** aFirstVisual,
1003 nsIFrame** aLastVisual)
1005 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
1007 PRBool isReordered;
1008 PRBool hasRTLFrames;
1009 Reorder(isReordered, hasRTLFrames);
1010 PRInt32 count = mLogicalFrames.Count();
1012 if (aFirstVisual) {
1013 *aFirstVisual = (nsIFrame*)mVisualFrames[0];
1015 if (aLastVisual) {
1016 *aLastVisual = (nsIFrame*)mVisualFrames[count-1];
1019 // If there's an RTL frame, assume the line is reordered
1020 return isReordered || hasRTLFrames;
1023 nsIFrame*
1024 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
1025 nsIFrame* aFirstFrameOnLine,
1026 PRInt32 aNumFramesOnLine)
1028 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
1030 PRBool isReordered;
1031 PRBool hasRTLFrames;
1032 Reorder(isReordered, hasRTLFrames);
1033 PRInt32 count = mVisualFrames.Count();
1035 if (aFrame == nsnull)
1036 return (nsIFrame*)mVisualFrames[0];
1038 for (PRInt32 i = 0; i < count - 1; i++) {
1039 if ((nsIFrame*)mVisualFrames[i] == aFrame) {
1040 return (nsIFrame*)mVisualFrames[i+1];
1044 return nsnull;
1047 nsIFrame*
1048 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
1049 nsIFrame* aFirstFrameOnLine,
1050 PRInt32 aNumFramesOnLine)
1052 InitLogicalArrayFromLine(aFirstFrameOnLine, aNumFramesOnLine);
1054 PRBool isReordered;
1055 PRBool hasRTLFrames;
1056 Reorder(isReordered, hasRTLFrames);
1057 PRInt32 count = mVisualFrames.Count();
1059 if (aFrame == nsnull)
1060 return (nsIFrame*)mVisualFrames[count-1];
1062 for (PRInt32 i = 1; i < count; i++) {
1063 if ((nsIFrame*)mVisualFrames[i] == aFrame) {
1064 return (nsIFrame*)mVisualFrames[i-1];
1068 return nsnull;
1071 PRBool
1072 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
1073 nsIFrame** aNewFrame,
1074 PRInt32& aFrameIndex,
1075 PRInt32 aStart,
1076 PRInt32 aEnd,
1077 PRInt32& aLineNeedsUpdate)
1079 NS_PRECONDITION(aNewFrame, "null OUT ptr");
1080 NS_PRECONDITION(aFrame, "aFrame is null");
1081 NS_ASSERTION(!aFrame->GetPrevInFlow(),
1082 "Calling EnsureBidiContinuation on non-first-in-flow");
1084 *aNewFrame = nsnull;
1085 nsBidiLevel embeddingLevel = NS_GET_EMBEDDING_LEVEL(aFrame);
1086 nsBidiLevel baseLevel = NS_GET_BASE_LEVEL(aFrame);
1087 nsCharType charType = (nsCharType)NS_PTR_TO_INT32(aFrame->GetProperty(nsGkAtoms::charType));
1089 // Skip fluid continuations
1090 while (aFrameIndex + 1 < mLogicalFrames.Count()) {
1091 nsIFrame* frame = (nsIFrame*)mLogicalFrames[aFrameIndex + 1];
1092 if (frame->GetPrevInFlow() != aFrame) {
1093 // If we found a non-fluid continuation, use it
1094 if (frame->GetPrevContinuation() == aFrame) {
1095 *aNewFrame = frame;
1096 aFrameIndex++;
1097 // The frame we found might be on another line. If so, the line iterator
1098 // should be updated.
1099 aLineNeedsUpdate = PR_TRUE;
1101 break;
1103 frame->SetProperty(nsGkAtoms::embeddingLevel, NS_INT32_TO_PTR(embeddingLevel));
1104 frame->SetProperty(nsGkAtoms::baseLevel, NS_INT32_TO_PTR(baseLevel));
1105 frame->SetProperty(nsGkAtoms::charType, NS_INT32_TO_PTR(charType));
1106 frame->AddStateBits(NS_FRAME_IS_BIDI);
1107 aFrameIndex++;
1108 aFrame->AdjustOffsetsForBidi(aStart, aStart);
1109 aFrame = frame;
1112 aFrame->AdjustOffsetsForBidi(aStart, aEnd);
1113 if (!*aNewFrame) {
1114 mSuccess = CreateBidiContinuation(aFrame, aNewFrame);
1115 if (NS_FAILED(mSuccess) ) {
1116 return PR_FALSE;
1120 return PR_TRUE;
1123 void
1124 nsBidiPresUtils::RemoveBidiContinuation(nsIFrame* aFrame,
1125 PRInt32 aFirstIndex,
1126 PRInt32 aLastIndex,
1127 PRInt32& aOffset) const
1129 aOffset = 0;
1131 nsresult rv;
1132 nsBidiLevel embeddingLevel = (nsCharType)NS_PTR_TO_INT32(aFrame->GetProperty(nsGkAtoms::embeddingLevel, &rv));
1133 NS_ASSERTION(NS_SUCCEEDED(rv), "embeddingLevel attribute missing from aFrame");
1134 nsBidiLevel baseLevel = (nsCharType)NS_PTR_TO_INT32(aFrame->GetProperty(nsGkAtoms::baseLevel, &rv));
1135 NS_ASSERTION(NS_SUCCEEDED(rv), "baseLevel attribute missing from aFrame");
1136 nsCharType charType = (nsCharType)NS_PTR_TO_INT32(aFrame->GetProperty(nsGkAtoms::charType, &rv));
1137 NS_ASSERTION(NS_SUCCEEDED(rv), "charType attribute missing from aFrame");
1139 for (PRInt32 index = aFirstIndex + 1; index <= aLastIndex; index++) {
1140 nsIFrame* frame = (nsIFrame*) mLogicalFrames[index];
1141 if (nsGkAtoms::directionalFrame == frame->GetType()) {
1142 frame->Destroy();
1143 ++aOffset;
1145 else {
1146 // Make the frame and its continuation ancestors fluid,
1147 // so they can be reused or deleted by normal reflow code
1148 frame->SetProperty(nsGkAtoms::embeddingLevel, NS_INT32_TO_PTR(embeddingLevel));
1149 frame->SetProperty(nsGkAtoms::baseLevel, NS_INT32_TO_PTR(baseLevel));
1150 frame->SetProperty(nsGkAtoms::charType, NS_INT32_TO_PTR(charType));
1151 frame->AddStateBits(NS_FRAME_IS_BIDI);
1152 while (frame) {
1153 nsIFrame* prev = frame->GetPrevContinuation();
1154 if (prev) {
1155 NS_ASSERTION (!frame->GetPrevInFlow() || frame->GetPrevInFlow() == prev,
1156 "prev-in-flow is not prev continuation!");
1157 frame->SetPrevInFlow(prev);
1159 NS_ASSERTION (!prev->GetNextInFlow() || prev->GetNextInFlow() == frame,
1160 "next-in-flow is not next continuation!");
1161 prev->SetNextInFlow(frame);
1163 frame = frame->GetParent();
1164 } else {
1165 break;
1172 nsresult
1173 nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
1174 PRUnichar* aText,
1175 PRInt32& aTextLength,
1176 nsCharType aCharType,
1177 PRBool aIsOddLevel)
1179 NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
1180 nsresult rv = NS_OK;
1181 // ahmed
1182 //adjusted for correct numeral shaping
1183 PRUint32 bidiOptions = aPresContext->GetBidi();
1184 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
1186 case IBMBIDI_NUMERAL_HINDI:
1187 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1188 break;
1190 case IBMBIDI_NUMERAL_ARABIC:
1191 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1192 break;
1194 case IBMBIDI_NUMERAL_REGULAR:
1196 switch (aCharType) {
1198 case eCharType_EuropeanNumber:
1199 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1200 break;
1202 case eCharType_ArabicNumber:
1203 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1204 break;
1206 default:
1207 break;
1209 break;
1211 case IBMBIDI_NUMERAL_HINDICONTEXT:
1212 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
1213 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1214 else if (eCharType_EuropeanNumber == aCharType)
1215 HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1216 break;
1218 case IBMBIDI_NUMERAL_NOMINAL:
1219 default:
1220 break;
1223 StripBidiControlCharacters(aText, aTextLength);
1224 return rv;
1227 void
1228 nsBidiPresUtils::StripBidiControlCharacters(PRUnichar* aText,
1229 PRInt32& aTextLength) const
1231 if ( (nsnull == aText) || (aTextLength < 1) ) {
1232 return;
1235 PRInt32 stripLen = 0;
1237 for (PRInt32 i = 0; i < aTextLength; i++) {
1238 // XXX: This silently ignores surrogate characters.
1239 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1240 if (IsBidiControl((PRUint32)aText[i])) {
1241 ++stripLen;
1243 else {
1244 aText[i - stripLen] = aText[i];
1247 aTextLength -= stripLen;
1250 #if 0 // XXX: for the future use ???
1251 void
1252 RemoveDiacritics(PRUnichar* aText,
1253 PRInt32& aTextLength)
1255 if (aText && (aTextLength > 0) ) {
1256 PRInt32 offset = 0;
1258 for (PRInt32 i = 0; i < aTextLength && aText[i]; i++) {
1259 if (IS_BIDI_DIACRITIC(aText[i]) ) {
1260 ++offset;
1261 continue;
1263 aText[i - offset] = aText[i];
1265 aTextLength = i - offset;
1266 aText[aTextLength] = 0;
1269 #endif
1271 void
1272 nsBidiPresUtils::CalculateCharType(PRInt32& aOffset,
1273 PRInt32 aCharTypeLimit,
1274 PRInt32& aRunLimit,
1275 PRInt32& aRunLength,
1276 PRInt32& aRunCount,
1277 PRUint8& aCharType,
1278 PRUint8& aPrevCharType) const
1281 PRBool strongTypeFound = PR_FALSE;
1282 PRInt32 offset;
1283 nsCharType charType;
1285 aCharType = eCharType_OtherNeutral;
1287 for (offset = aOffset; offset < aCharTypeLimit; offset++) {
1288 // Make sure we give RTL chartype to all characters that would be classified
1289 // as Right-To-Left by a bidi platform.
1290 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1291 if (IS_HEBREW_CHAR(mBuffer[offset]) ) {
1292 charType = eCharType_RightToLeft;
1294 else if (IS_ARABIC_ALPHABETIC(mBuffer[offset]) ) {
1295 charType = eCharType_RightToLeftArabic;
1297 else {
1298 mBidiEngine->GetCharTypeAt(offset, &charType);
1301 if (!CHARTYPE_IS_WEAK(charType) ) {
1303 if (strongTypeFound
1304 && (charType != aPrevCharType)
1305 && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
1306 // Stop at this point to ensure uni-directionality of the text
1307 // (from platform's point of view).
1308 // Also, don't mix Arabic and Hebrew content (since platform may
1309 // provide BIDI support to one of them only).
1310 aRunLength = offset - aOffset;
1311 aRunLimit = offset;
1312 ++aRunCount;
1313 break;
1316 if ( (eCharType_RightToLeftArabic == aPrevCharType
1317 || eCharType_ArabicNumber == aPrevCharType)
1318 && eCharType_EuropeanNumber == charType) {
1319 charType = eCharType_ArabicNumber;
1322 // Set PrevCharType to the last strong type in this frame
1323 // (for correct numeric shaping)
1324 aPrevCharType = charType;
1326 strongTypeFound = PR_TRUE;
1327 aCharType = charType;
1330 aOffset = offset;
1333 nsresult nsBidiPresUtils::GetBidiEngine(nsBidi** aBidiEngine)
1335 nsresult rv = NS_ERROR_FAILURE;
1336 if (mBidiEngine) {
1337 *aBidiEngine = mBidiEngine;
1338 rv = NS_OK;
1340 return rv;
1343 nsresult nsBidiPresUtils::ProcessText(const PRUnichar* aText,
1344 PRInt32 aLength,
1345 nsBidiDirection aBaseDirection,
1346 nsPresContext* aPresContext,
1347 BidiProcessor& aprocessor,
1348 Mode aMode,
1349 nsBidiPositionResolve* aPosResolve,
1350 PRInt32 aPosResolveCount,
1351 nscoord* aWidth)
1353 NS_ASSERTION((aPosResolve == nsnull) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1355 PRInt32 runCount;
1357 mBuffer.Assign(aText, aLength);
1359 nsresult rv = mBidiEngine->SetPara(mBuffer.get(), aLength, aBaseDirection, nsnull);
1360 if (NS_FAILED(rv))
1361 return rv;
1363 rv = mBidiEngine->CountRuns(&runCount);
1364 if (NS_FAILED(rv))
1365 return rv;
1367 nscoord xOffset = 0;
1368 nscoord width, xEndRun;
1369 nscoord totalWidth = 0;
1370 PRInt32 i, start, limit, length;
1371 PRUint32 visualStart = 0;
1372 PRUint8 charType;
1373 PRUint8 prevType = eCharType_LeftToRight;
1374 nsBidiLevel level;
1376 for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
1378 aPosResolve[nPosResolve].visualIndex = kNotFound;
1379 aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
1382 for (i = 0; i < runCount; i++) {
1383 rv = mBidiEngine->GetVisualRun(i, &start, &length, &aBaseDirection);
1384 if (NS_FAILED(rv))
1385 return rv;
1387 rv = mBidiEngine->GetLogicalRun(start, &limit, &level);
1388 if (NS_FAILED(rv))
1389 return rv;
1391 PRInt32 subRunLength = limit - start;
1392 PRInt32 lineOffset = start;
1393 PRInt32 typeLimit = PR_MIN(limit, aLength);
1394 PRInt32 subRunCount = 1;
1395 PRInt32 subRunLimit = typeLimit;
1398 * If |level| is even, i.e. the direction of the run is left-to-right, we
1399 * render the subruns from left to right and increment the x-coordinate
1400 * |xOffset| by the width of each subrun after rendering.
1402 * If |level| is odd, i.e. the direction of the run is right-to-left, we
1403 * render the subruns from right to left. We begin by incrementing |xOffset| by
1404 * the width of the whole run, and then decrement it by the width of each
1405 * subrun before rendering. After rendering all the subruns, we restore the
1406 * x-coordinate of the end of the run for the start of the next run.
1409 if (level & 1) {
1410 aprocessor.SetText(aText + start, subRunLength, nsBidiDirection(level & 1));
1411 width = aprocessor.GetWidth();
1412 xOffset += width;
1413 xEndRun = xOffset;
1416 while (subRunCount > 0) {
1417 // CalculateCharType can increment subRunCount if the run
1418 // contains mixed character types
1419 CalculateCharType(lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
1421 nsAutoString runVisualText;
1422 runVisualText.Assign(aText + start, subRunLength);
1423 if (runVisualText.Length() < subRunLength)
1424 return NS_ERROR_OUT_OF_MEMORY;
1425 FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength,
1426 (nsCharType)charType, level & 1);
1428 aprocessor.SetText(runVisualText.get(), subRunLength, nsBidiDirection(level & 1));
1429 width = aprocessor.GetWidth();
1430 totalWidth += width;
1431 if (level & 1) {
1432 xOffset -= width;
1434 if (aMode == MODE_DRAW) {
1435 aprocessor.DrawText(xOffset, width);
1439 * The caller may request to calculate the visual position of one
1440 * or more characters.
1442 for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve)
1444 nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
1446 * Did we already resolve this position's visual metric? If so, skip.
1448 if (posResolve->visualLeftTwips != kNotFound)
1449 continue;
1452 * First find out if the logical position is within this run.
1454 if (start <= posResolve->logicalIndex &&
1455 start + subRunLength > posResolve->logicalIndex) {
1457 * If this run is only one character long, we have an easy case:
1458 * the visual position is the x-coord of the start of the run
1459 * less the x-coord of the start of the whole text.
1461 if (subRunLength == 1) {
1462 posResolve->visualIndex = visualStart;
1463 posResolve->visualLeftTwips = xOffset;
1466 * Otherwise, we need to measure the width of the run's part
1467 * which is to the visual left of the index.
1468 * In other words, the run is broken in two, around the logical index,
1469 * and we measure the part which is visually left.
1470 * If the run is right-to-left, this part will span from after the index
1471 * up to the end of the run; if it is left-to-right, this part will span
1472 * from the start of the run up to (and inclduing) the character before the index.
1474 else {
1475 nscoord subWidth;
1476 // The position in the text where this run's "left part" begins.
1477 const PRUnichar* visualLeftPart;
1478 if (level & 1) {
1479 // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
1480 posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
1481 // Skipping to the "left part".
1482 visualLeftPart = aText + posResolve->logicalIndex + 1;
1484 else {
1485 posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
1486 // Skipping to the "left part".
1487 visualLeftPart = aText + start;
1489 // The delta between the start of the run and the left part's end.
1490 PRInt32 visualLeftLength = posResolve->visualIndex - visualStart;
1491 aprocessor.SetText(visualLeftPart, visualLeftLength, nsBidiDirection(level & 1));
1492 subWidth = aprocessor.GetWidth();
1493 posResolve->visualLeftTwips = xOffset + subWidth;
1498 if (!(level & 1)) {
1499 xOffset += width;
1502 --subRunCount;
1503 start = lineOffset;
1504 subRunLimit = typeLimit;
1505 subRunLength = typeLimit - lineOffset;
1506 } // while
1507 if (level & 1) {
1508 xOffset = xEndRun;
1511 visualStart += length;
1512 } // for
1514 if (aWidth) {
1515 *aWidth = totalWidth;
1517 return NS_OK;
1520 class NS_STACK_CLASS nsIRenderingContextBidiProcessor : public nsBidiPresUtils::BidiProcessor {
1521 public:
1522 nsIRenderingContextBidiProcessor(nsIRenderingContext* aCtx,
1523 const nsPoint& aPt)
1524 : mCtx(aCtx), mPt(aPt) { }
1526 ~nsIRenderingContextBidiProcessor()
1528 mCtx->SetRightToLeftText(PR_FALSE);
1531 virtual void SetText(const PRUnichar* aText,
1532 PRInt32 aLength,
1533 nsBidiDirection aDirection)
1535 mCtx->SetTextRunRTL(aDirection==NSBIDI_RTL);
1536 mText = aText;
1537 mLength = aLength;
1540 virtual nscoord GetWidth()
1542 nscoord width;
1543 mCtx->GetWidth(mText, mLength, width, nsnull);
1544 return width;
1547 virtual void DrawText(nscoord aXOffset,
1548 nscoord)
1550 mCtx->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y);
1553 private:
1554 nsPoint mPt;
1555 nsIRenderingContext* mCtx;
1556 const PRUnichar* mText;
1557 PRInt32 mLength;
1558 nsBidiDirection mDirection;
1561 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar* aText,
1562 PRInt32 aLength,
1563 nsBidiDirection aBaseDirection,
1564 nsPresContext* aPresContext,
1565 nsIRenderingContext& aRenderingContext,
1566 Mode aMode,
1567 nscoord aX,
1568 nscoord aY,
1569 nsBidiPositionResolve* aPosResolve,
1570 PRInt32 aPosResolveCount,
1571 nscoord* aWidth)
1573 nsIRenderingContextBidiProcessor processor(&aRenderingContext, nsPoint(aX, aY));
1575 return ProcessText(aText, aLength, aBaseDirection, aPresContext, processor,
1576 aMode, aPosResolve, aPosResolveCount, aWidth);
1578 #endif // IBMBIDI