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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Uri Bernstein <uriber@gmail.com>
24 : * Haamed Gheibi <gheibi@metanetworking.com>
25 : * Ehsan Akhgari <ehsan.akhgari@gmail.com>
26 : *
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.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifdef IBMBIDI
42 :
43 : #include "nsBidiPresUtils.h"
44 : #include "nsTextFragment.h"
45 : #include "nsGkAtoms.h"
46 : #include "nsPresContext.h"
47 : #include "nsRenderingContext.h"
48 : #include "nsIServiceManager.h"
49 : #include "nsFrameManager.h"
50 : #include "nsBidiUtils.h"
51 : #include "nsCSSFrameConstructor.h"
52 : #include "nsContainerFrame.h"
53 : #include "nsInlineFrame.h"
54 : #include "nsPlaceholderFrame.h"
55 : #include "nsFirstLetterFrame.h"
56 : #include "nsUnicodeProperties.h"
57 : #include "nsTextFrame.h"
58 :
59 : #undef NOISY_BIDI
60 : #undef REALLY_NOISY_BIDI
61 :
62 : using namespace mozilla;
63 :
64 : static const PRUnichar kSpace = 0x0020;
65 : static const PRUnichar kZWSP = 0x200B;
66 : static const PRUnichar kLineSeparator = 0x2028;
67 : static const PRUnichar kObjectSubstitute = 0xFFFC;
68 : static const PRUnichar kLRE = 0x202A;
69 : static const PRUnichar kRLE = 0x202B;
70 : static const PRUnichar kLRO = 0x202D;
71 : static const PRUnichar kRLO = 0x202E;
72 : static const PRUnichar kPDF = 0x202C;
73 :
74 : #define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
75 :
76 0 : struct BidiParagraphData {
77 : nsString mBuffer;
78 : nsAutoTArray<PRUnichar, 16> mEmbeddingStack;
79 : nsTArray<nsIFrame*> mLogicalFrames;
80 : nsTArray<nsLineBox*> mLinePerFrame;
81 : nsDataHashtable<nsISupportsHashKey, PRInt32> mContentToFrameIndex;
82 : bool mIsVisual;
83 : bool mReset;
84 : nsBidiLevel mParaLevel;
85 : nsIContent* mPrevContent;
86 : nsAutoPtr<nsBidi> mBidiEngine;
87 : nsIFrame* mPrevFrame;
88 : nsAutoPtr<BidiParagraphData> mSubParagraph;
89 :
90 0 : void Init(nsBlockFrame *aBlockFrame)
91 : {
92 0 : mContentToFrameIndex.Init();
93 0 : mBidiEngine = new nsBidi();
94 0 : mPrevContent = nsnull;
95 :
96 : bool styleDirectionIsRTL =
97 0 : (NS_STYLE_DIRECTION_RTL == aBlockFrame->GetStyleVisibility()->mDirection);
98 0 : if (aBlockFrame->GetStyleTextReset()->mUnicodeBidi &
99 : NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
100 : // unicode-bidi: plaintext: the Bidi algorithm will determine the
101 : // directionality of the paragraph according to the first strong
102 : // directional character, defaulting to LTR if there is none.
103 0 : mParaLevel = NSBIDI_DEFAULT_LTR;
104 : } else {
105 0 : mParaLevel = styleDirectionIsRTL ? NSBIDI_RTL : NSBIDI_LTR;
106 : }
107 :
108 0 : mIsVisual = aBlockFrame->PresContext()->IsVisualMode();
109 0 : if (mIsVisual) {
110 : /**
111 : * Drill up in content to detect whether this is an element that needs to
112 : * be rendered with logical order even on visual pages.
113 : *
114 : * We always use logical order on form controls, firstly so that text
115 : * entry will be in logical order, but also because visual pages were
116 : * written with the assumption that even if the browser had no support
117 : * for right-to-left text rendering, it would use native widgets with
118 : * bidi support to display form controls.
119 : *
120 : * We also use logical order in XUL elements, since we expect that if a
121 : * XUL element appears in a visual page, it will be generated by an XBL
122 : * binding and contain localized text which will be in logical order.
123 : */
124 0 : for (nsIContent* content = aBlockFrame->GetContent() ; content;
125 0 : content = content->GetParent()) {
126 0 : if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) ||
127 0 : content->IsXUL()) {
128 0 : mIsVisual = false;
129 0 : break;
130 : }
131 : }
132 : }
133 0 : }
134 :
135 0 : BidiParagraphData* GetSubParagraph()
136 : {
137 0 : if (!mSubParagraph) {
138 0 : mSubParagraph = new BidiParagraphData();
139 0 : mSubParagraph->Init(this);
140 : }
141 :
142 0 : return mSubParagraph;
143 : }
144 :
145 : // Initialise a sub-paragraph from its containing paragraph
146 0 : void Init(BidiParagraphData *aBpd)
147 : {
148 0 : mContentToFrameIndex.Init();
149 0 : mBidiEngine = new nsBidi();
150 0 : mPrevContent = nsnull;
151 0 : mIsVisual = aBpd->mIsVisual;
152 0 : mParaLevel = aBpd->mParaLevel;
153 :
154 : // If the containing paragraph has a level of NSBIDI_DEFAULT_LTR, set
155 : // the sub-paragraph to NSBIDI_LTR (we can't use GetParaLevel to find the
156 : // resolved paragraph level, because the containing paragraph hasn't yet
157 : // been through bidi resolution
158 0 : if (mParaLevel == NSBIDI_DEFAULT_LTR) {
159 0 : mParaLevel = NSBIDI_LTR;
160 : }
161 0 : mReset = false;
162 0 : }
163 :
164 0 : void Reset(nsIFrame* aFrame, BidiParagraphData *aBpd)
165 : {
166 0 : mReset = true;
167 0 : mLogicalFrames.Clear();
168 0 : mLinePerFrame.Clear();
169 0 : mContentToFrameIndex.Clear();
170 0 : mBuffer.SetLength(0);
171 0 : mPrevFrame = aBpd->mPrevFrame;
172 : // We need to copy in embeddings (but not overrides!) from the containing
173 : // paragraph so that the line(s) including this sub-paragraph will be
174 : // correctly reordered.
175 0 : for (PRUint32 i = 0; i < aBpd->mEmbeddingStack.Length(); ++i) {
176 0 : switch(aBpd->mEmbeddingStack[i]) {
177 : case kRLE:
178 : case kRLO:
179 0 : mParaLevel = NextOddLevel(mParaLevel);
180 0 : break;
181 :
182 : case kLRE:
183 : case kLRO:
184 0 : mParaLevel = NextEvenLevel(mParaLevel);
185 0 : break;
186 :
187 : default:
188 0 : break;
189 : }
190 : }
191 :
192 0 : nsIFrame* container = aFrame->GetParent();
193 : bool isRTL = (NS_STYLE_DIRECTION_RTL ==
194 0 : container->GetStyleVisibility()->mDirection);
195 0 : if ((isRTL & 1) != (mParaLevel & 1)) {
196 0 : mParaLevel = isRTL ? NextOddLevel(mParaLevel) : NextEvenLevel(mParaLevel);
197 : }
198 :
199 0 : if (container->GetStyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
200 0 : PushBidiControl(isRTL ? kRLO : kLRO);
201 : } else {
202 0 : PushBidiControl(isRTL ? kRLE : kLRE);
203 : }
204 0 : }
205 :
206 0 : nsresult SetPara()
207 : {
208 : return mBidiEngine->SetPara(mBuffer.get(), BufferLength(),
209 0 : mParaLevel, nsnull);
210 : }
211 :
212 : /**
213 : * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
214 : * GetParaLevel() returns the actual (resolved) paragraph level which is
215 : * always either NSBIDI_LTR or NSBIDI_RTL
216 : */
217 0 : nsBidiLevel GetParaLevel()
218 : {
219 0 : nsBidiLevel paraLevel = mParaLevel;
220 0 : if (IS_DEFAULT_LEVEL(paraLevel)) {
221 0 : mBidiEngine->GetParaLevel(¶Level);
222 : }
223 0 : return paraLevel;
224 : }
225 :
226 0 : nsresult CountRuns(PRInt32 *runCount){ return mBidiEngine->CountRuns(runCount); }
227 :
228 0 : nsresult GetLogicalRun(PRInt32 aLogicalStart,
229 : PRInt32* aLogicalLimit,
230 : nsBidiLevel* aLevel)
231 : {
232 : nsresult rv = mBidiEngine->GetLogicalRun(aLogicalStart,
233 0 : aLogicalLimit, aLevel);
234 0 : if (mIsVisual || NS_FAILED(rv))
235 0 : *aLevel = GetParaLevel();
236 0 : return rv;
237 : }
238 :
239 0 : void ResetData()
240 : {
241 0 : mLogicalFrames.Clear();
242 0 : mLinePerFrame.Clear();
243 0 : mContentToFrameIndex.Clear();
244 0 : mBuffer.SetLength(0);
245 0 : mPrevContent = nsnull;
246 0 : for (PRUint32 i = 0; i < mEmbeddingStack.Length(); ++i) {
247 0 : mBuffer.Append(mEmbeddingStack[i]);
248 0 : mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
249 0 : mLinePerFrame.AppendElement((nsLineBox*)nsnull);
250 : }
251 0 : }
252 :
253 0 : void AppendFrame(nsIFrame* aFrame,
254 : nsBlockInFlowLineIterator* aLineIter,
255 : nsIContent* aContent = nsnull)
256 : {
257 0 : if (aContent) {
258 0 : mContentToFrameIndex.Put(aContent, FrameCount());
259 : }
260 0 : mLogicalFrames.AppendElement(aFrame);
261 :
262 0 : AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame);
263 0 : mLinePerFrame.AppendElement(aLineIter->GetLine().get());
264 0 : }
265 :
266 0 : void AdvanceAndAppendFrame(nsIFrame** aFrame,
267 : nsBlockInFlowLineIterator* aLineIter,
268 : nsIFrame** aNextSibling)
269 : {
270 0 : nsIFrame* frame = *aFrame;
271 0 : nsIFrame* nextSibling = *aNextSibling;
272 :
273 0 : frame = frame->GetNextContinuation();
274 0 : if (frame) {
275 0 : AppendFrame(frame, aLineIter, nsnull);
276 :
277 : /*
278 : * If we have already overshot the saved next-sibling while
279 : * scanning the frame's continuations, advance it.
280 : */
281 0 : if (frame == nextSibling) {
282 0 : nextSibling = frame->GetNextSibling();
283 : }
284 : }
285 :
286 0 : *aFrame = frame;
287 0 : *aNextSibling = nextSibling;
288 0 : }
289 :
290 0 : PRInt32 GetLastFrameForContent(nsIContent *aContent)
291 : {
292 0 : PRInt32 index = 0;
293 0 : mContentToFrameIndex.Get(aContent, &index);
294 0 : return index;
295 : }
296 :
297 0 : PRInt32 FrameCount(){ return mLogicalFrames.Length(); }
298 :
299 0 : PRInt32 BufferLength(){ return mBuffer.Length(); }
300 :
301 0 : nsIFrame* FrameAt(PRInt32 aIndex){ return mLogicalFrames[aIndex]; }
302 :
303 0 : nsLineBox* GetLineForFrameAt(PRInt32 aIndex){ return mLinePerFrame[aIndex]; }
304 :
305 0 : void AppendUnichar(PRUnichar aCh){ mBuffer.Append(aCh); }
306 :
307 0 : void AppendString(const nsDependentSubstring& aString){ mBuffer.Append(aString); }
308 :
309 0 : void AppendControlChar(PRUnichar aCh)
310 : {
311 0 : mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
312 0 : mLinePerFrame.AppendElement((nsLineBox*)nsnull);
313 0 : AppendUnichar(aCh);
314 0 : }
315 :
316 0 : void PushBidiControl(PRUnichar aCh)
317 : {
318 0 : AppendControlChar(aCh);
319 0 : mEmbeddingStack.AppendElement(aCh);
320 0 : }
321 :
322 0 : void PopBidiControl()
323 : {
324 0 : AppendControlChar(kPDF);
325 0 : NS_ASSERTION(mEmbeddingStack.Length(), "embedding/override underflow");
326 0 : mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
327 0 : }
328 :
329 0 : void ClearBidiControls()
330 : {
331 0 : for (PRUint32 i = 0; i < mEmbeddingStack.Length(); ++i) {
332 0 : AppendControlChar(kPDF);
333 : }
334 0 : }
335 :
336 0 : nsBidiLevel NextOddLevel(nsBidiLevel aLevel)
337 : {
338 0 : return (aLevel + 1) | 1;
339 : }
340 :
341 0 : nsBidiLevel NextEvenLevel(nsBidiLevel aLevel)
342 : {
343 0 : return (aLevel + 2) & ~1;
344 : }
345 :
346 : static bool
347 0 : IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
348 : nsIFrame* aPrevFrame, nsIFrame* aFrame)
349 : {
350 0 : nsIFrame* endFrame = aLineIter->IsLastLineInList() ? nsnull :
351 0 : aLineIter->GetLine().next()->mFirstChild;
352 0 : nsIFrame* startFrame = aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
353 0 : for (nsIFrame* frame = startFrame; frame && frame != endFrame;
354 : frame = frame->GetNextSibling()) {
355 0 : if (frame == aFrame)
356 0 : return true;
357 : }
358 0 : return false;
359 : }
360 :
361 : static void
362 0 : AdvanceLineIteratorToFrame(nsIFrame* aFrame,
363 : nsBlockInFlowLineIterator* aLineIter,
364 : nsIFrame*& aPrevFrame)
365 : {
366 : // Advance aLine to the line containing aFrame
367 0 : nsIFrame* child = aFrame;
368 0 : nsFrameManager* frameManager = aFrame->PresContext()->FrameManager();
369 0 : nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, child);
370 0 : while (parent && !nsLayoutUtils::GetAsBlock(parent)) {
371 0 : child = parent;
372 0 : parent = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, child);
373 : }
374 0 : NS_ASSERTION (parent, "aFrame is not a descendent of aBlockFrame");
375 0 : while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
376 : #ifdef DEBUG
377 : bool hasNext =
378 : #endif
379 0 : aLineIter->Next();
380 0 : NS_ASSERTION(hasNext, "Can't find frame in lines!");
381 0 : aPrevFrame = nsnull;
382 : }
383 0 : aPrevFrame = child;
384 0 : }
385 :
386 : };
387 :
388 0 : struct BidiLineData {
389 : nsTArray<nsIFrame*> mLogicalFrames;
390 : nsTArray<nsIFrame*> mVisualFrames;
391 : nsTArray<PRInt32> mIndexMap;
392 : nsAutoTArray<PRUint8, 18> mLevels;
393 : bool mIsReordered;
394 :
395 0 : BidiLineData(nsIFrame* aFirstFrameOnLine, PRInt32 aNumFramesOnLine)
396 0 : {
397 : /**
398 : * Initialize the logically-ordered array of frames using the top-level
399 : * frames of a single line
400 : */
401 0 : mLogicalFrames.Clear();
402 :
403 0 : bool isReordered = false;
404 0 : bool hasRTLFrames = false;
405 :
406 0 : for (nsIFrame* frame = aFirstFrameOnLine;
407 : frame && aNumFramesOnLine--;
408 : frame = frame->GetNextSibling()) {
409 0 : AppendFrame(frame);
410 0 : PRUint8 level = nsBidiPresUtils::GetFrameEmbeddingLevel(frame);
411 0 : mLevels.AppendElement(level);
412 0 : mIndexMap.AppendElement(0);
413 0 : if (level & 1) {
414 0 : hasRTLFrames = true;
415 : }
416 : }
417 :
418 : // Reorder the line
419 0 : nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
420 0 : mIndexMap.Elements());
421 :
422 0 : for (PRInt32 i = 0; i < FrameCount(); i++) {
423 0 : mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
424 0 : if (i != mIndexMap[i]) {
425 0 : isReordered = true;
426 : }
427 : }
428 :
429 : // If there's an RTL frame, assume the line is reordered
430 0 : mIsReordered = isReordered || hasRTLFrames;
431 0 : }
432 :
433 0 : void AppendFrame(nsIFrame* aFrame)
434 : {
435 0 : mLogicalFrames.AppendElement(aFrame);
436 0 : }
437 :
438 0 : PRInt32 FrameCount(){ return mLogicalFrames.Length(); }
439 :
440 0 : nsIFrame* LogicalFrameAt(PRInt32 aIndex){ return mLogicalFrames[aIndex]; }
441 :
442 0 : nsIFrame* VisualFrameAt(PRInt32 aIndex){ return mVisualFrames[aIndex]; }
443 : };
444 :
445 : /* Some helper methods for Resolve() */
446 :
447 : // Should this frame be split between text runs?
448 : bool
449 0 : IsBidiSplittable(nsIFrame* aFrame) {
450 0 : nsIAtom* frameType = aFrame->GetType();
451 : // Bidi inline containers should be split, unless they're line frames.
452 0 : return aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer)
453 0 : && frameType != nsGkAtoms::lineFrame;
454 : }
455 :
456 : static nsresult
457 0 : SplitInlineAncestors(nsIFrame* aFrame)
458 : {
459 0 : nsPresContext *presContext = aFrame->PresContext();
460 0 : nsIPresShell *presShell = presContext->PresShell();
461 0 : nsIFrame* frame = aFrame;
462 0 : nsIFrame* parent = aFrame->GetParent();
463 : nsIFrame* newParent;
464 :
465 0 : while (IsBidiSplittable(parent)) {
466 0 : nsIFrame* grandparent = parent->GetParent();
467 0 : NS_ASSERTION(grandparent, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
468 :
469 : nsresult rv = presShell->FrameConstructor()->
470 0 : CreateContinuingFrame(presContext, parent, grandparent, &newParent, false);
471 0 : if (NS_FAILED(rv)) {
472 0 : return rv;
473 : }
474 :
475 : // Split the child list after |frame|.
476 0 : nsContainerFrame* container = do_QueryFrame(parent);
477 0 : nsFrameList tail = container->StealFramesAfter(frame);
478 :
479 : // Reparent views as necessary
480 0 : rv = nsContainerFrame::ReparentFrameViewList(presContext, tail, parent, newParent);
481 0 : if (NS_FAILED(rv)) {
482 0 : return rv;
483 : }
484 :
485 : // The parent's continuation adopts the siblings after the split.
486 0 : rv = newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nsnull, tail);
487 0 : if (NS_FAILED(rv)) {
488 0 : return rv;
489 : }
490 : // The list name kNoReflowPrincipalList would indicate we don't want reflow
491 0 : nsFrameList temp(newParent, newParent);
492 0 : rv = grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp);
493 0 : if (NS_FAILED(rv)) {
494 0 : return rv;
495 : }
496 :
497 0 : frame = parent;
498 0 : parent = grandparent;
499 : }
500 :
501 0 : return NS_OK;
502 : }
503 :
504 : static void
505 0 : MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext)
506 : {
507 0 : NS_ASSERTION (!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext,
508 : "next-in-flow is not next continuation!");
509 0 : aFrame->SetNextInFlow(aNext);
510 :
511 0 : NS_ASSERTION (!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame,
512 : "prev-in-flow is not prev continuation!");
513 0 : aNext->SetPrevInFlow(aFrame);
514 0 : }
515 :
516 : // If aFrame is the last child of its parent, convert bidi continuations to
517 : // fluid continuations for all of its inline ancestors.
518 : static void
519 0 : JoinInlineAncestors(nsIFrame* aFrame)
520 : {
521 0 : if (aFrame->GetNextSibling()) {
522 0 : return;
523 : }
524 0 : nsIFrame* frame = aFrame->GetParent();
525 0 : while (frame && IsBidiSplittable(frame)) {
526 0 : nsIFrame* next = frame->GetNextContinuation();
527 0 : if (next) {
528 0 : MakeContinuationFluid(frame, next);
529 : }
530 : // Join the parent only as long as we're its last child.
531 0 : if (frame->GetNextSibling())
532 0 : break;
533 0 : frame = frame->GetParent();
534 : }
535 : }
536 :
537 : static nsresult
538 0 : CreateContinuation(nsIFrame* aFrame,
539 : nsIFrame** aNewFrame,
540 : bool aIsFluid)
541 : {
542 0 : NS_PRECONDITION(aNewFrame, "null OUT ptr");
543 0 : NS_PRECONDITION(aFrame, "null ptr");
544 :
545 0 : *aNewFrame = nsnull;
546 :
547 0 : nsPresContext *presContext = aFrame->PresContext();
548 0 : nsIPresShell *presShell = presContext->PresShell();
549 0 : NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateContinuation");
550 :
551 0 : nsIFrame* parent = aFrame->GetParent();
552 0 : NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
553 :
554 0 : nsresult rv = NS_OK;
555 :
556 : // Have to special case floating first letter frames because the continuation
557 : // doesn't go in the first letter frame. The continuation goes with the rest
558 : // of the text that the first letter frame was made out of.
559 0 : if (parent->GetType() == nsGkAtoms::letterFrame &&
560 0 : parent->GetStyleDisplay()->IsFloating()) {
561 0 : nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
562 : rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
563 0 : aNewFrame, aIsFluid);
564 0 : return rv;
565 : }
566 :
567 : rv = presShell->FrameConstructor()->
568 0 : CreateContinuingFrame(presContext, aFrame, parent, aNewFrame, aIsFluid);
569 0 : if (NS_FAILED(rv)) {
570 0 : return rv;
571 : }
572 :
573 : // The list name kNoReflowPrincipalList would indicate we don't want reflow
574 : // XXXbz this needs higher-level framelist love
575 0 : nsFrameList temp(*aNewFrame, *aNewFrame);
576 0 : rv = parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp);
577 0 : if (NS_FAILED(rv)) {
578 0 : return rv;
579 : }
580 :
581 0 : if (!aIsFluid) {
582 : // Split inline ancestor frames
583 0 : rv = SplitInlineAncestors(aFrame);
584 0 : if (NS_FAILED(rv)) {
585 0 : return rv;
586 : }
587 : }
588 :
589 0 : return NS_OK;
590 : }
591 :
592 : /*
593 : * Overview of the implementation of Resolve():
594 : *
595 : * Walk through the descendants of aBlockFrame and build:
596 : * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
597 : * * mBuffer: an nsString containing a representation of
598 : * the content of the frames.
599 : * In the case of text frames, this is the actual text context of the
600 : * frames, but some other elements are represented in a symbolic form which
601 : * will make the Unicode Bidi Algorithm give the correct results.
602 : * Bidi embeddings and overrides set by CSS or <bdo> elements are
603 : * represented by the corresponding Unicode control characters.
604 : * <br> elements are represented by U+2028 LINE SEPARATOR
605 : * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
606 : * CHARACTER
607 : *
608 : * Then pass mBuffer to the Bidi engine for resolving of embedding levels
609 : * by nsBidi::SetPara() and division into directional runs by
610 : * nsBidi::CountRuns().
611 : *
612 : * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
613 : * correlate them with the frames indexed in mLogicalFrames, setting the
614 : * baseLevel and embeddingLevel properties according to the results returned
615 : * by the Bidi engine.
616 : *
617 : * The rendering layer requires each text frame to contain text in only one
618 : * direction, so we may need to call EnsureBidiContinuation() to split frames.
619 : * We may also need to call RemoveBidiContinuation() to convert frames created
620 : * by EnsureBidiContinuation() in previous reflows into fluid continuations.
621 : */
622 : nsresult
623 0 : nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame)
624 : {
625 0 : BidiParagraphData bpd;
626 0 : bpd.Init(aBlockFrame);
627 :
628 : // handle bidi-override being set on the block itself before calling
629 : // TraverseFrames.
630 0 : const nsStyleTextReset* text = aBlockFrame->GetStyleTextReset();
631 0 : PRUnichar ch = 0;
632 0 : if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
633 0 : const nsStyleVisibility* vis = aBlockFrame->GetStyleVisibility();
634 0 : if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
635 0 : ch = kRLO;
636 : }
637 0 : else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
638 0 : ch = kLRO;
639 : }
640 0 : if (ch != 0) {
641 0 : bpd.PushBidiControl(ch);
642 : }
643 : }
644 0 : for (nsBlockFrame* block = aBlockFrame; block;
645 0 : block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
646 0 : block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
647 0 : nsBlockInFlowLineIterator lineIter(block, block->begin_lines());
648 0 : bpd.mPrevFrame = nsnull;
649 0 : bpd.GetSubParagraph()->mPrevFrame = nsnull;
650 0 : TraverseFrames(aBlockFrame, &lineIter, block->GetFirstPrincipalChild(), &bpd);
651 : }
652 :
653 0 : if (ch != 0) {
654 0 : bpd.PopBidiControl();
655 : }
656 :
657 0 : return ResolveParagraph(aBlockFrame, &bpd);
658 : }
659 :
660 : nsresult
661 0 : nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
662 : BidiParagraphData* aBpd)
663 : {
664 0 : nsPresContext *presContext = aBlockFrame->PresContext();
665 :
666 0 : if (aBpd->BufferLength() < 1) {
667 0 : return NS_OK;
668 : }
669 0 : aBpd->mBuffer.ReplaceChar("\t\r\n", kSpace);
670 :
671 : PRInt32 runCount;
672 :
673 0 : nsresult rv = aBpd->SetPara();
674 0 : NS_ENSURE_SUCCESS(rv, rv);
675 :
676 0 : PRUint8 embeddingLevel = aBpd->GetParaLevel();
677 :
678 0 : rv = aBpd->CountRuns(&runCount);
679 0 : NS_ENSURE_SUCCESS(rv, rv);
680 :
681 0 : PRInt32 runLength = 0; // the length of the current run of text
682 0 : PRInt32 lineOffset = 0; // the start of the current run
683 0 : PRInt32 logicalLimit = 0; // the end of the current run + 1
684 0 : PRInt32 numRun = -1;
685 0 : PRInt32 fragmentLength = 0; // the length of the current text frame
686 0 : PRInt32 frameIndex = -1; // index to the frames in mLogicalFrames
687 0 : PRInt32 frameCount = aBpd->FrameCount();
688 0 : PRInt32 contentOffset = 0; // offset of current frame in its content node
689 0 : bool isTextFrame = false;
690 0 : nsIFrame* frame = nsnull;
691 0 : nsIContent* content = nsnull;
692 0 : PRInt32 contentTextLength = 0;
693 :
694 0 : FramePropertyTable *propTable = presContext->PropertyTable();
695 0 : nsLineBox* currentLine = nsnull;
696 :
697 : #ifdef DEBUG
698 : #ifdef NOISY_BIDI
699 : printf("Before Resolve(), aBlockFrame=0x%p, mBuffer='%s', frameCount=%d, runCount=%d\n",
700 : (void*)aBlockFrame, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(), frameCount, runCount);
701 : #ifdef REALLY_NOISY_BIDI
702 : printf(" block frame tree=:\n");
703 : aBlockFrame->List(stdout, 0);
704 : #endif
705 : #endif
706 : #endif
707 :
708 0 : for (; ;) {
709 0 : if (fragmentLength <= 0) {
710 : // Get the next frame from mLogicalFrames
711 0 : if (++frameIndex >= frameCount) {
712 0 : break;
713 : }
714 0 : frame = aBpd->FrameAt(frameIndex);
715 0 : if (frame == NS_BIDI_CONTROL_FRAME ||
716 0 : nsGkAtoms::textFrame != frame->GetType()) {
717 : /*
718 : * Any non-text frame corresponds to a single character in the text buffer
719 : * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE)
720 : */
721 0 : isTextFrame = false;
722 0 : fragmentLength = 1;
723 : }
724 : else {
725 0 : currentLine = aBpd->GetLineForFrameAt(frameIndex);
726 0 : content = frame->GetContent();
727 0 : if (!content) {
728 0 : rv = NS_OK;
729 0 : break;
730 : }
731 0 : contentTextLength = content->TextLength();
732 0 : if (contentTextLength == 0) {
733 0 : frame->AdjustOffsetsForBidi(0, 0);
734 : // Set the base level and embedding level of the current run even
735 : // on an empty frame. Otherwise frame reordering will not be correct.
736 : propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
737 0 : NS_INT32_TO_PTR(embeddingLevel));
738 : propTable->Set(frame, nsIFrame::BaseLevelProperty(),
739 0 : NS_INT32_TO_PTR(aBpd->GetParaLevel()));
740 0 : continue;
741 : }
742 : PRInt32 start, end;
743 0 : frame->GetOffsets(start, end);
744 0 : NS_ASSERTION(!(contentTextLength < end - start),
745 : "Frame offsets don't fit in content");
746 0 : fragmentLength = NS_MIN(contentTextLength, end - start);
747 0 : contentOffset = start;
748 0 : isTextFrame = true;
749 : }
750 : } // if (fragmentLength <= 0)
751 :
752 0 : if (runLength <= 0) {
753 : // Get the next run of text from the Bidi engine
754 0 : if (++numRun >= runCount) {
755 0 : break;
756 : }
757 0 : lineOffset = logicalLimit;
758 0 : if (NS_FAILED(aBpd->GetLogicalRun(
759 : lineOffset, &logicalLimit, &embeddingLevel) ) ) {
760 0 : break;
761 : }
762 0 : runLength = logicalLimit - lineOffset;
763 : } // if (runLength <= 0)
764 :
765 0 : if (frame == NS_BIDI_CONTROL_FRAME) {
766 0 : frame = nsnull;
767 0 : ++lineOffset;
768 : }
769 : else {
770 : propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
771 0 : NS_INT32_TO_PTR(embeddingLevel));
772 : propTable->Set(frame, nsIFrame::BaseLevelProperty(),
773 0 : NS_INT32_TO_PTR(aBpd->GetParaLevel()));
774 0 : if (isTextFrame) {
775 0 : if ( (runLength > 0) && (runLength < fragmentLength) ) {
776 : /*
777 : * The text in this frame continues beyond the end of this directional run.
778 : * Create a non-fluid continuation frame for the next directional run.
779 : */
780 0 : currentLine->MarkDirty();
781 : nsIFrame* nextBidi;
782 0 : PRInt32 runEnd = contentOffset + runLength;
783 : rv = EnsureBidiContinuation(frame, &nextBidi, frameIndex,
784 : contentOffset,
785 0 : runEnd);
786 0 : if (NS_FAILED(rv)) {
787 0 : break;
788 : }
789 : nextBidi->AdjustOffsetsForBidi(runEnd,
790 0 : contentOffset + fragmentLength);
791 0 : frame = nextBidi;
792 0 : contentOffset = runEnd;
793 : } // if (runLength < fragmentLength)
794 : else {
795 0 : if (contentOffset + fragmentLength == contentTextLength) {
796 : /*
797 : * We have finished all the text in this content node. Convert any
798 : * further non-fluid continuations to fluid continuations and advance
799 : * frameIndex to the last frame in the content node
800 : */
801 0 : PRInt32 newIndex = aBpd->GetLastFrameForContent(content);
802 0 : if (newIndex > frameIndex) {
803 : RemoveBidiContinuation(aBpd, frame,
804 0 : frameIndex, newIndex, lineOffset);
805 0 : frameIndex = newIndex;
806 : }
807 0 : } else if (fragmentLength > 0 && runLength > fragmentLength) {
808 : /*
809 : * There is more text that belongs to this directional run in the next
810 : * text frame: make sure it is a fluid continuation of the current frame.
811 : * Do not advance frameIndex, because the next frame may contain
812 : * multi-directional text and need to be split
813 : */
814 0 : PRInt32 newIndex = frameIndex;
815 0 : do {
816 : } while (++newIndex < frameCount &&
817 0 : aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME);
818 0 : if (newIndex < frameCount) {
819 : RemoveBidiContinuation(aBpd, frame,
820 0 : frameIndex, newIndex, lineOffset);
821 0 : }
822 0 : } else if (runLength == fragmentLength) {
823 : /*
824 : * If the directional run ends at the end of the frame, make sure
825 : * that any continuation is non-fluid
826 : */
827 0 : nsIFrame* next = frame->GetNextInFlow();
828 0 : if (next) {
829 0 : frame->SetNextContinuation(next);
830 0 : next->SetPrevContinuation(frame);
831 : }
832 : }
833 0 : frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
834 0 : currentLine->MarkDirty();
835 : }
836 : } // isTextFrame
837 : else {
838 0 : ++lineOffset;
839 : }
840 : } // not bidi control frame
841 0 : PRInt32 temp = runLength;
842 0 : runLength -= fragmentLength;
843 0 : fragmentLength -= temp;
844 :
845 0 : if (frame && fragmentLength <= 0) {
846 : // If the frame is at the end of a run, and this is not the end of our
847 : // paragrah, split all ancestor inlines that need splitting.
848 : // To determine whether we're at the end of the run, we check that we've
849 : // finished processing the current run, and that the current frame
850 : // doesn't have a fluid continuation (it could have a fluid continuation
851 : // of zero length, so testing runLength alone is not sufficient).
852 0 : if (runLength <= 0 && !frame->GetNextInFlow()) {
853 0 : if (numRun + 1 < runCount) {
854 0 : nsIFrame* child = frame;
855 0 : nsIFrame* parent = frame->GetParent();
856 : // As long as we're on the last sibling, the parent doesn't have to
857 : // be split.
858 : // However, if the parent has a fluid continuation, we do have to make
859 : // it non-fluid. This can happen e.g. when we have a first-letter
860 : // frame and the end of the first-letter coincides with the end of a
861 : // directional run.
862 0 : while (parent &&
863 0 : IsBidiSplittable(parent) &&
864 0 : !child->GetNextSibling()) {
865 0 : nsIFrame* next = parent->GetNextInFlow();
866 0 : if (next) {
867 0 : parent->SetNextContinuation(next);
868 0 : next->SetPrevContinuation(parent);
869 : }
870 0 : child = parent;
871 0 : parent = child->GetParent();
872 : }
873 0 : if (parent && IsBidiSplittable(parent))
874 0 : SplitInlineAncestors(child);
875 : }
876 : }
877 : else {
878 : // We're not at an end of a run. If |frame| is the last child of its
879 : // parent, and its ancestors happen to have bidi continuations, convert
880 : // them into fluid continuations.
881 0 : JoinInlineAncestors(frame);
882 : }
883 : }
884 : } // for
885 :
886 : #ifdef DEBUG
887 : #ifdef REALLY_NOISY_BIDI
888 : printf("---\nAfter Resolve(), frameTree =:\n");
889 : aBlockFrame->List(stdout, 0);
890 : printf("===\n");
891 : #endif
892 : #endif
893 :
894 0 : return rv;
895 : }
896 :
897 : // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
898 0 : bool IsBidiLeaf(nsIFrame* aFrame) {
899 0 : nsIFrame* kid = aFrame->GetFirstPrincipalChild();
900 : return !kid
901 0 : || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
902 : }
903 :
904 : void
905 0 : nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame,
906 : nsBlockInFlowLineIterator* aLineIter,
907 : nsIFrame* aCurrentFrame,
908 : BidiParagraphData* aBpd)
909 : {
910 0 : if (!aCurrentFrame)
911 0 : return;
912 :
913 0 : nsIFrame* childFrame = aCurrentFrame;
914 0 : do {
915 : /*
916 : * It's important to get the next sibling and next continuation *before*
917 : * handling the frame: If we encounter a forced paragraph break and call
918 : * ResolveParagraph within this loop, doing GetNextSibling and
919 : * GetNextContinuation after that could return a bidi continuation that had
920 : * just been split from the original childFrame and we would process it
921 : * twice.
922 : */
923 0 : nsIFrame* nextSibling = childFrame->GetNextSibling();
924 0 : bool isLastFrame = !childFrame->GetNextContinuation();
925 0 : bool isFirstFrame = !childFrame->GetPrevContinuation();
926 :
927 : // If the real frame for a placeholder is a first letter frame, we need to
928 : // drill down into it and include its contents in Bidi resolution.
929 : // If not, we just use the placeholder.
930 0 : nsIFrame* frame = childFrame;
931 0 : if (nsGkAtoms::placeholderFrame == childFrame->GetType()) {
932 : nsIFrame* realFrame =
933 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
934 0 : if (realFrame->GetType() == nsGkAtoms::letterFrame) {
935 0 : frame = realFrame;
936 : }
937 : }
938 :
939 0 : PRUnichar ch = 0;
940 0 : if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
941 0 : const nsStyleVisibility* vis = frame->GetStyleVisibility();
942 0 : const nsStyleTextReset* text = frame->GetStyleTextReset();
943 0 : if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
944 0 : if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
945 0 : ch = kRLO;
946 : }
947 0 : else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
948 0 : ch = kLRO;
949 : }
950 0 : } else if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_EMBED) {
951 0 : if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
952 0 : ch = kRLE;
953 : }
954 0 : else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
955 0 : ch = kLRE;
956 : }
957 : }
958 :
959 : // Add a dummy frame pointer representing a bidi control code before the
960 : // first frame of an element specifying embedding or override
961 0 : if (ch != 0 && isFirstFrame) {
962 0 : aBpd->PushBidiControl(ch);
963 : }
964 : }
965 :
966 0 : if (IsBidiLeaf(frame)) {
967 : /* Bidi leaf frame: add the frame to the mLogicalFrames array,
968 : * and add its index to the mContentToFrameIndex hashtable. This
969 : * will be used in RemoveBidiContinuation() to identify the last
970 : * frame in the array with a given content.
971 : */
972 0 : nsIContent* content = frame->GetContent();
973 0 : aBpd->AppendFrame(frame, aLineIter, content);
974 :
975 : // Append the content of the frame to the paragraph buffer
976 0 : nsIAtom* frameType = frame->GetType();
977 0 : if (nsGkAtoms::textFrame == frameType) {
978 0 : if (content != aBpd->mPrevContent) {
979 0 : aBpd->mPrevContent = content;
980 0 : if (!frame->GetStyleContext()->GetStyleText()->NewlineIsSignificant()) {
981 0 : content->AppendTextTo(aBpd->mBuffer);
982 : } else {
983 : /*
984 : * For preformatted text we have to do bidi resolution on each line
985 : * separately.
986 : */
987 0 : nsAutoString text;
988 0 : content->AppendTextTo(text);
989 : nsIFrame* next;
990 0 : do {
991 0 : next = nsnull;
992 :
993 : PRInt32 start, end;
994 0 : frame->GetOffsets(start, end);
995 0 : PRInt32 endLine = text.FindChar('\n', start);
996 0 : if (endLine == -1) {
997 : /*
998 : * If there is no newline in the text content, just save the
999 : * text from this frame and its continuations, and do bidi
1000 : * resolution later
1001 : */
1002 0 : aBpd->AppendString(Substring(text, start));
1003 0 : while (frame && nextSibling) {
1004 0 : aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1005 : }
1006 0 : break;
1007 : }
1008 :
1009 : /*
1010 : * If there is a newline in the frame, break the frame after the
1011 : * newline, do bidi resolution and repeat until the last sibling
1012 : */
1013 0 : ++endLine;
1014 :
1015 : /*
1016 : * If the frame ends before the new line, save the text and move
1017 : * into the next continuation
1018 : */
1019 : aBpd->AppendString(Substring(text, start,
1020 0 : NS_MIN(end, endLine) - start));
1021 0 : while (end < endLine && nextSibling) {
1022 0 : aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1023 0 : NS_ASSERTION(frame, "Premature end of continuation chain");
1024 0 : frame->GetOffsets(start, end);
1025 : aBpd->AppendString(Substring(text, start,
1026 0 : NS_MIN(end, endLine) - start));
1027 : }
1028 :
1029 0 : if (end < endLine) {
1030 0 : aBpd->mPrevContent = nsnull;
1031 0 : break;
1032 : }
1033 :
1034 0 : bool createdContinuation = false;
1035 0 : if (PRUint32(endLine) < text.Length()) {
1036 : /*
1037 : * Timing is everything here: if the frame already has a bidi
1038 : * continuation, we need to make the continuation fluid *before*
1039 : * resetting the length of the current frame. Otherwise
1040 : * nsTextFrame::SetLength won't set the continuation frame's
1041 : * text offsets correctly.
1042 : *
1043 : * On the other hand, if the frame doesn't have a continuation,
1044 : * we need to create one *after* resetting the length, or
1045 : * CreateContinuingFrame will complain that there is no more
1046 : * content for the continuation.
1047 : */
1048 0 : next = frame->GetNextInFlow();
1049 0 : if (!next) {
1050 : // If the frame already has a bidi continuation, make it fluid
1051 0 : next = frame->GetNextContinuation();
1052 0 : if (next) {
1053 0 : MakeContinuationFluid(frame, next);
1054 0 : JoinInlineAncestors(frame);
1055 : }
1056 : }
1057 :
1058 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1059 0 : textFrame->SetLength(endLine - start, nsnull);
1060 :
1061 0 : if (!next) {
1062 : // If the frame has no next in flow, create one.
1063 0 : CreateContinuation(frame, &next, true);
1064 0 : createdContinuation = true;
1065 : }
1066 : }
1067 0 : ResolveParagraphWithinBlock(aBlockFrame, aBpd);
1068 :
1069 0 : if (!nextSibling && !createdContinuation) {
1070 0 : break;
1071 0 : } else if (next) {
1072 0 : frame = next;
1073 0 : aBpd->AppendFrame(frame, aLineIter);
1074 : }
1075 :
1076 : /*
1077 : * If we have already overshot the saved next-sibling while
1078 : * scanning the frame's continuations, advance it.
1079 : */
1080 0 : if (frame && frame == nextSibling) {
1081 0 : nextSibling = frame->GetNextSibling();
1082 : }
1083 :
1084 : } while (next);
1085 : }
1086 : }
1087 0 : } else if (nsGkAtoms::brFrame == frameType) {
1088 : // break frame -- append line separator
1089 0 : aBpd->AppendUnichar(kLineSeparator);
1090 0 : ResolveParagraphWithinBlock(aBlockFrame, aBpd);
1091 : } else {
1092 : // other frame type -- see the Unicode Bidi Algorithm:
1093 : // "...inline objects (such as graphics) are treated as if they are ...
1094 : // U+FFFC"
1095 : // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
1096 : // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
1097 0 : aBpd->AppendUnichar(content->IsHTML(nsGkAtoms::wbr) ?
1098 0 : kZWSP : kObjectSubstitute);
1099 0 : if (!frame->GetStyleContext()->GetStyleDisplay()->IsInlineOutside()) {
1100 : // if it is not inline, end the paragraph
1101 0 : ResolveParagraphWithinBlock(aBlockFrame, aBpd);
1102 : }
1103 : }
1104 : }
1105 : else {
1106 : // For a non-leaf frame, recurse into TraverseFrames
1107 0 : nsIFrame* kid = frame->GetFirstPrincipalChild();
1108 0 : if (kid) {
1109 0 : const nsStyleTextReset* text = frame->GetStyleTextReset();
1110 0 : if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_ISOLATE) {
1111 : // css "unicode-bidi: isolate" and html5 bdi:
1112 : // resolve the element as a separate paragraph
1113 0 : BidiParagraphData* subParagraph = aBpd->GetSubParagraph();
1114 :
1115 : /*
1116 : * As at the beginning of the loop, it's important to check for
1117 : * next-continuations before handling the frame. If we do
1118 : * TraverseFrames and *then* do GetNextContinuation on the original
1119 : * first frame, it could return a bidi continuation that had only
1120 : * just been created, and we would skip doing bidi resolution on the
1121 : * last part of the sub-paragraph.
1122 : */
1123 0 : bool isLastContinuation = !frame->GetNextContinuation();
1124 0 : if (!frame->GetPrevContinuation() || !subParagraph->mReset) {
1125 0 : subParagraph->Reset(kid, aBpd);
1126 : }
1127 0 : TraverseFrames(aBlockFrame, aLineIter, kid, subParagraph);
1128 0 : if (isLastContinuation) {
1129 0 : ResolveParagraph(aBlockFrame, subParagraph);
1130 : }
1131 :
1132 : // Treat the element as a neutral character within its containing
1133 : // paragraph.
1134 0 : aBpd->AppendControlChar(kObjectSubstitute);
1135 : } else {
1136 0 : TraverseFrames(aBlockFrame, aLineIter, kid, aBpd);
1137 : }
1138 : }
1139 : }
1140 :
1141 : // If the element is attributed by dir, indicate direction pop (add PDF frame)
1142 0 : if (isLastFrame) {
1143 0 : if (ch) {
1144 : // Add a dummy frame pointer representing a bidi control code after the
1145 : // last frame of an element specifying embedding or override
1146 0 : aBpd->PopBidiControl();
1147 : }
1148 : }
1149 0 : childFrame = nextSibling;
1150 : } while (childFrame);
1151 : }
1152 :
1153 : void
1154 0 : nsBidiPresUtils::ResolveParagraphWithinBlock(nsBlockFrame* aBlockFrame,
1155 : BidiParagraphData* aBpd)
1156 : {
1157 0 : aBpd->ClearBidiControls();
1158 0 : ResolveParagraph(aBlockFrame, aBpd);
1159 0 : aBpd->ResetData();
1160 0 : }
1161 :
1162 : void
1163 0 : nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine,
1164 : PRInt32 aNumFramesOnLine)
1165 : {
1166 : // If this line consists of a line frame, reorder the line frame's children.
1167 0 : if (aFirstFrameOnLine->GetType() == nsGkAtoms::lineFrame) {
1168 0 : aFirstFrameOnLine = aFirstFrameOnLine->GetFirstPrincipalChild();
1169 0 : if (!aFirstFrameOnLine)
1170 0 : return;
1171 : // All children of the line frame are on the first line. Setting aNumFramesOnLine
1172 : // to -1 makes InitLogicalArrayFromLine look at all of them.
1173 0 : aNumFramesOnLine = -1;
1174 : }
1175 :
1176 0 : BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1177 0 : RepositionInlineFrames(&bld, aFirstFrameOnLine);
1178 : }
1179 :
1180 : nsBidiLevel
1181 0 : nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
1182 : {
1183 0 : nsIFrame* firstLeaf = aFrame;
1184 0 : while (!IsBidiLeaf(firstLeaf)) {
1185 0 : nsIFrame* firstChild = firstLeaf->GetFirstPrincipalChild();
1186 0 : nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild);
1187 0 : firstLeaf = (realFrame->GetType() == nsGkAtoms::letterFrame) ?
1188 0 : realFrame : firstChild;
1189 : }
1190 0 : return NS_GET_EMBEDDING_LEVEL(firstLeaf);
1191 : }
1192 :
1193 : nsBidiLevel
1194 0 : nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame)
1195 : {
1196 0 : nsIFrame* firstLeaf = aFrame;
1197 0 : while (!IsBidiLeaf(firstLeaf)) {
1198 0 : firstLeaf = firstLeaf->GetFirstPrincipalChild();
1199 : }
1200 0 : return NS_GET_BASE_LEVEL(firstLeaf);
1201 : }
1202 :
1203 : void
1204 0 : nsBidiPresUtils::IsLeftOrRightMost(nsIFrame* aFrame,
1205 : nsContinuationStates* aContinuationStates,
1206 : bool& aIsLeftMost /* out */,
1207 : bool& aIsRightMost /* out */)
1208 : {
1209 0 : const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
1210 0 : bool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
1211 :
1212 : /*
1213 : * Since we lay out frames from left to right (in both LTR and RTL), visiting a
1214 : * frame with 'mFirstVisualFrame == nsnull', means it's the first appearance of
1215 : * one of its continuation chain frames on the line.
1216 : * To determine if it's the last visual frame of its continuation chain on the line
1217 : * or not, we count the number of frames of the chain on the line, and then reduce
1218 : * it when we lay out a frame of the chain. If this value becomes 1 it means
1219 : * that it's the last visual frame of its continuation chain on this line.
1220 : */
1221 :
1222 0 : nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
1223 : nsFrameContinuationState* firstFrameState;
1224 :
1225 0 : if (!frameState->mFirstVisualFrame) {
1226 : // aFrame is the first visual frame of its continuation chain
1227 : nsFrameContinuationState* contState;
1228 : nsIFrame* frame;
1229 :
1230 0 : frameState->mFrameCount = 1;
1231 0 : frameState->mFirstVisualFrame = aFrame;
1232 :
1233 : /**
1234 : * Traverse continuation chain of aFrame in both backward and forward
1235 : * directions while the frames are on this line. Count the frames and
1236 : * set their mFirstVisualFrame to aFrame.
1237 : */
1238 : // Traverse continuation chain backward
1239 0 : for (frame = aFrame->GetPrevContinuation();
1240 : frame && (contState = aContinuationStates->GetEntry(frame));
1241 0 : frame = frame->GetPrevContinuation()) {
1242 0 : frameState->mFrameCount++;
1243 0 : contState->mFirstVisualFrame = aFrame;
1244 : }
1245 0 : frameState->mHasContOnPrevLines = (frame != nsnull);
1246 :
1247 : // Traverse continuation chain forward
1248 0 : for (frame = aFrame->GetNextContinuation();
1249 : frame && (contState = aContinuationStates->GetEntry(frame));
1250 0 : frame = frame->GetNextContinuation()) {
1251 0 : frameState->mFrameCount++;
1252 0 : contState->mFirstVisualFrame = aFrame;
1253 : }
1254 0 : frameState->mHasContOnNextLines = (frame != nsnull);
1255 :
1256 0 : aIsLeftMost = isLTR ? !frameState->mHasContOnPrevLines
1257 0 : : !frameState->mHasContOnNextLines;
1258 0 : firstFrameState = frameState;
1259 : } else {
1260 : // aFrame is not the first visual frame of its continuation chain
1261 0 : aIsLeftMost = false;
1262 0 : firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
1263 : }
1264 :
1265 : aIsRightMost = (firstFrameState->mFrameCount == 1) &&
1266 0 : (isLTR ? !firstFrameState->mHasContOnNextLines
1267 0 : : !firstFrameState->mHasContOnPrevLines);
1268 :
1269 0 : if ((aIsLeftMost || aIsRightMost) &&
1270 0 : (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
1271 : // For ib splits, don't treat anything except the last part as
1272 : // endmost or anything except the first part as startmost.
1273 : // As an optimization, only get the first continuation once.
1274 0 : nsIFrame* firstContinuation = aFrame->GetFirstContinuation();
1275 0 : if (nsLayoutUtils::FrameIsNonLastInIBSplit(firstContinuation)) {
1276 : // We are not endmost
1277 0 : if (isLTR) {
1278 0 : aIsRightMost = false;
1279 : } else {
1280 0 : aIsLeftMost = false;
1281 : }
1282 : }
1283 0 : if (nsLayoutUtils::FrameIsNonFirstInIBSplit(firstContinuation)) {
1284 : // We are not startmost
1285 0 : if (isLTR) {
1286 0 : aIsLeftMost = false;
1287 : } else {
1288 0 : aIsRightMost = false;
1289 : }
1290 : }
1291 : }
1292 :
1293 : // Reduce number of remaining frames of the continuation chain on the line.
1294 0 : firstFrameState->mFrameCount--;
1295 0 : }
1296 :
1297 : void
1298 0 : nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
1299 : bool aIsOddLevel,
1300 : nscoord& aLeft,
1301 : nsContinuationStates* aContinuationStates)
1302 : {
1303 0 : if (!aFrame)
1304 0 : return;
1305 :
1306 : bool isLeftMost, isRightMost;
1307 : IsLeftOrRightMost(aFrame,
1308 : aContinuationStates,
1309 : isLeftMost /* out */,
1310 0 : isRightMost /* out */);
1311 :
1312 0 : nsInlineFrame* testFrame = do_QueryFrame(aFrame);
1313 0 : if (testFrame) {
1314 0 : aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
1315 :
1316 0 : if (isLeftMost)
1317 0 : aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
1318 : else
1319 0 : aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
1320 :
1321 0 : if (isRightMost)
1322 0 : aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
1323 : else
1324 0 : aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
1325 : }
1326 : // This method is called from nsBlockFrame::PlaceLine via the call to
1327 : // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1328 : // have been reflowed, which is required for GetUsedMargin/Border/Padding
1329 0 : nsMargin margin = aFrame->GetUsedMargin();
1330 0 : if (isLeftMost)
1331 0 : aLeft += margin.left;
1332 :
1333 0 : nscoord start = aLeft;
1334 :
1335 0 : if (!IsBidiLeaf(aFrame))
1336 : {
1337 0 : nscoord x = 0;
1338 0 : nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
1339 0 : if (isLeftMost) {
1340 0 : x += borderPadding.left;
1341 : }
1342 :
1343 : // If aIsOddLevel is true, so we need to traverse the child list
1344 : // in reverse order, to make it O(n) we store the list locally and
1345 : // iterate the list reversely
1346 0 : nsTArray<nsIFrame*> childList;
1347 0 : nsIFrame *frame = aFrame->GetFirstPrincipalChild();
1348 0 : if (frame && aIsOddLevel) {
1349 0 : childList.AppendElement((nsIFrame*)nsnull);
1350 0 : while (frame) {
1351 0 : childList.AppendElement(frame);
1352 0 : frame = frame->GetNextSibling();
1353 : }
1354 0 : frame = childList[childList.Length() - 1];
1355 : }
1356 :
1357 : // Reposition the child frames
1358 0 : PRInt32 index = 0;
1359 0 : while (frame) {
1360 : RepositionFrame(frame,
1361 : aIsOddLevel,
1362 : x,
1363 0 : aContinuationStates);
1364 0 : index++;
1365 : frame = aIsOddLevel ?
1366 0 : childList[childList.Length() - index - 1] :
1367 0 : frame->GetNextSibling();
1368 : }
1369 :
1370 0 : if (isRightMost) {
1371 0 : x += borderPadding.right;
1372 : }
1373 0 : aLeft += x;
1374 : } else {
1375 0 : aLeft += aFrame->GetSize().width;
1376 : }
1377 0 : nsRect rect = aFrame->GetRect();
1378 0 : aFrame->SetRect(nsRect(start, rect.y, aLeft - start, rect.height));
1379 :
1380 0 : if (isRightMost)
1381 0 : aLeft += margin.right;
1382 : }
1383 :
1384 : void
1385 0 : nsBidiPresUtils::InitContinuationStates(nsIFrame* aFrame,
1386 : nsContinuationStates* aContinuationStates)
1387 : {
1388 0 : nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame);
1389 0 : state->mFirstVisualFrame = nsnull;
1390 0 : state->mFrameCount = 0;
1391 :
1392 0 : if (!IsBidiLeaf(aFrame)) {
1393 : // Continue for child frames
1394 : nsIFrame* frame;
1395 0 : for (frame = aFrame->GetFirstPrincipalChild();
1396 : frame;
1397 : frame = frame->GetNextSibling()) {
1398 : InitContinuationStates(frame,
1399 0 : aContinuationStates);
1400 : }
1401 : }
1402 0 : }
1403 :
1404 : void
1405 0 : nsBidiPresUtils::RepositionInlineFrames(BidiLineData *aBld,
1406 : nsIFrame* aFirstChild)
1407 : {
1408 0 : const nsStyleVisibility* vis = aFirstChild->GetStyleVisibility();
1409 0 : bool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
1410 0 : nscoord leftSpace = 0;
1411 :
1412 : // This method is called from nsBlockFrame::PlaceLine via the call to
1413 : // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1414 : // have been reflowed, which is required for GetUsedMargin/Border/Padding
1415 0 : nsMargin margin = aFirstChild->GetUsedMargin();
1416 0 : if (!aFirstChild->GetPrevContinuation() &&
1417 0 : !nsLayoutUtils::FrameIsNonFirstInIBSplit(aFirstChild))
1418 0 : leftSpace = isLTR ? margin.left : margin.right;
1419 :
1420 0 : nscoord left = aFirstChild->GetPosition().x - leftSpace;
1421 : nsIFrame* frame;
1422 0 : PRInt32 count = aBld->mVisualFrames.Length();
1423 : PRInt32 index;
1424 0 : nsContinuationStates continuationStates;
1425 :
1426 0 : continuationStates.Init();
1427 :
1428 : // Initialize continuation states to (nsnull, 0) for
1429 : // each frame on the line.
1430 0 : for (index = 0; index < count; index++) {
1431 0 : InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
1432 : }
1433 :
1434 : // Reposition frames in visual order
1435 0 : for (index = 0; index < count; index++) {
1436 0 : frame = aBld->VisualFrameAt(index);
1437 : RepositionFrame(frame,
1438 0 : (aBld->mLevels[aBld->mIndexMap[index]] & 1),
1439 : left,
1440 0 : &continuationStates);
1441 : } // for
1442 0 : }
1443 :
1444 : bool
1445 0 : nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
1446 : PRInt32 aNumFramesOnLine,
1447 : nsIFrame** aFirstVisual,
1448 : nsIFrame** aLastVisual)
1449 : {
1450 0 : BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1451 0 : PRInt32 count = bld.FrameCount();
1452 :
1453 0 : if (aFirstVisual) {
1454 0 : *aFirstVisual = bld.VisualFrameAt(0);
1455 : }
1456 0 : if (aLastVisual) {
1457 0 : *aLastVisual = bld.VisualFrameAt(count-1);
1458 : }
1459 :
1460 0 : return bld.mIsReordered;
1461 : }
1462 :
1463 : nsIFrame*
1464 0 : nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
1465 : nsIFrame* aFirstFrameOnLine,
1466 : PRInt32 aNumFramesOnLine)
1467 : {
1468 0 : BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1469 :
1470 0 : PRInt32 count = bld.mVisualFrames.Length();
1471 :
1472 0 : if (aFrame == nsnull && count)
1473 0 : return bld.VisualFrameAt(0);
1474 :
1475 0 : for (PRInt32 i = 0; i < count - 1; i++) {
1476 0 : if (bld.VisualFrameAt(i) == aFrame) {
1477 0 : return bld.VisualFrameAt(i+1);
1478 : }
1479 : }
1480 :
1481 0 : return nsnull;
1482 : }
1483 :
1484 : nsIFrame*
1485 0 : nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
1486 : nsIFrame* aFirstFrameOnLine,
1487 : PRInt32 aNumFramesOnLine)
1488 : {
1489 0 : BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1490 :
1491 0 : PRInt32 count = bld.mVisualFrames.Length();
1492 :
1493 0 : if (aFrame == nsnull && count)
1494 0 : return bld.VisualFrameAt(count-1);
1495 :
1496 0 : for (PRInt32 i = 1; i < count; i++) {
1497 0 : if (bld.VisualFrameAt(i) == aFrame) {
1498 0 : return bld.VisualFrameAt(i-1);
1499 : }
1500 : }
1501 :
1502 0 : return nsnull;
1503 : }
1504 :
1505 : inline nsresult
1506 0 : nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
1507 : nsIFrame** aNewFrame,
1508 : PRInt32& aFrameIndex,
1509 : PRInt32 aStart,
1510 : PRInt32 aEnd)
1511 : {
1512 0 : NS_PRECONDITION(aNewFrame, "null OUT ptr");
1513 0 : NS_PRECONDITION(aFrame, "aFrame is null");
1514 :
1515 0 : aFrame->AdjustOffsetsForBidi(aStart, aEnd);
1516 0 : return CreateContinuation(aFrame, aNewFrame, false);
1517 : }
1518 :
1519 : void
1520 0 : nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
1521 : nsIFrame* aFrame,
1522 : PRInt32 aFirstIndex,
1523 : PRInt32 aLastIndex,
1524 : PRInt32& aOffset)
1525 : {
1526 0 : FrameProperties props = aFrame->Properties();
1527 : nsBidiLevel embeddingLevel =
1528 0 : (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::EmbeddingLevelProperty()));
1529 : nsBidiLevel baseLevel =
1530 0 : (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::BaseLevelProperty()));
1531 :
1532 0 : for (PRInt32 index = aFirstIndex + 1; index <= aLastIndex; index++) {
1533 0 : nsIFrame* frame = aBpd->FrameAt(index);
1534 0 : if (frame == NS_BIDI_CONTROL_FRAME) {
1535 0 : ++aOffset;
1536 : }
1537 : else {
1538 : // Make the frame and its continuation ancestors fluid,
1539 : // so they can be reused or deleted by normal reflow code
1540 0 : FrameProperties frameProps = frame->Properties();
1541 : frameProps.Set(nsIFrame::EmbeddingLevelProperty(),
1542 0 : NS_INT32_TO_PTR(embeddingLevel));
1543 : frameProps.Set(nsIFrame::BaseLevelProperty(),
1544 0 : NS_INT32_TO_PTR(baseLevel));
1545 0 : frame->AddStateBits(NS_FRAME_IS_BIDI);
1546 0 : while (frame) {
1547 0 : nsIFrame* prev = frame->GetPrevContinuation();
1548 0 : if (prev) {
1549 0 : MakeContinuationFluid(prev, frame);
1550 0 : frame = frame->GetParent();
1551 : } else {
1552 0 : break;
1553 : }
1554 : }
1555 : }
1556 : }
1557 0 : }
1558 :
1559 : nsresult
1560 0 : nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
1561 : PRUnichar* aText,
1562 : PRInt32& aTextLength,
1563 : nsCharType aCharType,
1564 : bool aIsOddLevel)
1565 : {
1566 : NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
1567 0 : nsresult rv = NS_OK;
1568 : // ahmed
1569 : //adjusted for correct numeral shaping
1570 0 : PRUint32 bidiOptions = aPresContext->GetBidi();
1571 0 : switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
1572 :
1573 : case IBMBIDI_NUMERAL_HINDI:
1574 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1575 0 : break;
1576 :
1577 : case IBMBIDI_NUMERAL_ARABIC:
1578 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1579 0 : break;
1580 :
1581 : case IBMBIDI_NUMERAL_PERSIAN:
1582 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
1583 0 : break;
1584 :
1585 : case IBMBIDI_NUMERAL_REGULAR:
1586 :
1587 0 : switch (aCharType) {
1588 :
1589 : case eCharType_EuropeanNumber:
1590 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1591 0 : break;
1592 :
1593 : case eCharType_ArabicNumber:
1594 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1595 0 : break;
1596 :
1597 : default:
1598 0 : break;
1599 : }
1600 0 : break;
1601 :
1602 : case IBMBIDI_NUMERAL_HINDICONTEXT:
1603 0 : if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
1604 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
1605 0 : else if (eCharType_EuropeanNumber == aCharType)
1606 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1607 0 : break;
1608 :
1609 : case IBMBIDI_NUMERAL_PERSIANCONTEXT:
1610 0 : if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
1611 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
1612 0 : else if (eCharType_EuropeanNumber == aCharType)
1613 0 : HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
1614 0 : break;
1615 :
1616 : case IBMBIDI_NUMERAL_NOMINAL:
1617 : default:
1618 0 : break;
1619 : }
1620 :
1621 0 : StripBidiControlCharacters(aText, aTextLength);
1622 0 : return rv;
1623 : }
1624 :
1625 : void
1626 0 : nsBidiPresUtils::StripBidiControlCharacters(PRUnichar* aText,
1627 : PRInt32& aTextLength)
1628 : {
1629 0 : if ( (nsnull == aText) || (aTextLength < 1) ) {
1630 0 : return;
1631 : }
1632 :
1633 0 : PRInt32 stripLen = 0;
1634 :
1635 0 : for (PRInt32 i = 0; i < aTextLength; i++) {
1636 : // XXX: This silently ignores surrogate characters.
1637 : // As of Unicode 4.0, all Bidi control characters are within the BMP.
1638 0 : if (IsBidiControl((PRUint32)aText[i])) {
1639 0 : ++stripLen;
1640 : }
1641 : else {
1642 0 : aText[i - stripLen] = aText[i];
1643 : }
1644 : }
1645 0 : aTextLength -= stripLen;
1646 : }
1647 :
1648 : #if 0 // XXX: for the future use ???
1649 : void
1650 : RemoveDiacritics(PRUnichar* aText,
1651 : PRInt32& aTextLength)
1652 : {
1653 : if (aText && (aTextLength > 0) ) {
1654 : PRInt32 offset = 0;
1655 :
1656 : for (PRInt32 i = 0; i < aTextLength && aText[i]; i++) {
1657 : if (IS_BIDI_DIACRITIC(aText[i]) ) {
1658 : ++offset;
1659 : continue;
1660 : }
1661 : aText[i - offset] = aText[i];
1662 : }
1663 : aTextLength = i - offset;
1664 : aText[aTextLength] = 0;
1665 : }
1666 : }
1667 : #endif
1668 :
1669 : void
1670 0 : nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine,
1671 : const PRUnichar* aText,
1672 : PRInt32& aOffset,
1673 : PRInt32 aCharTypeLimit,
1674 : PRInt32& aRunLimit,
1675 : PRInt32& aRunLength,
1676 : PRInt32& aRunCount,
1677 : PRUint8& aCharType,
1678 : PRUint8& aPrevCharType)
1679 :
1680 : {
1681 0 : bool strongTypeFound = false;
1682 : PRInt32 offset;
1683 : nsCharType charType;
1684 :
1685 0 : aCharType = eCharType_OtherNeutral;
1686 :
1687 0 : for (offset = aOffset; offset < aCharTypeLimit; offset++) {
1688 : // Make sure we give RTL chartype to all characters that would be classified
1689 : // as Right-To-Left by a bidi platform.
1690 : // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1691 0 : if (IS_HEBREW_CHAR(aText[offset]) ) {
1692 0 : charType = eCharType_RightToLeft;
1693 : }
1694 0 : else if (IS_ARABIC_ALPHABETIC(aText[offset]) ) {
1695 0 : charType = eCharType_RightToLeftArabic;
1696 : }
1697 : else {
1698 0 : aBidiEngine->GetCharTypeAt(offset, &charType);
1699 : }
1700 :
1701 0 : if (!CHARTYPE_IS_WEAK(charType) ) {
1702 :
1703 0 : if (strongTypeFound
1704 : && (charType != aPrevCharType)
1705 : && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
1706 : // Stop at this point to ensure uni-directionality of the text
1707 : // (from platform's point of view).
1708 : // Also, don't mix Arabic and Hebrew content (since platform may
1709 : // provide BIDI support to one of them only).
1710 0 : aRunLength = offset - aOffset;
1711 0 : aRunLimit = offset;
1712 0 : ++aRunCount;
1713 0 : break;
1714 : }
1715 :
1716 0 : if ( (eCharType_RightToLeftArabic == aPrevCharType
1717 : || eCharType_ArabicNumber == aPrevCharType)
1718 : && eCharType_EuropeanNumber == charType) {
1719 0 : charType = eCharType_ArabicNumber;
1720 : }
1721 :
1722 : // Set PrevCharType to the last strong type in this frame
1723 : // (for correct numeric shaping)
1724 0 : aPrevCharType = charType;
1725 :
1726 0 : strongTypeFound = true;
1727 0 : aCharType = charType;
1728 : }
1729 : }
1730 0 : aOffset = offset;
1731 0 : }
1732 :
1733 0 : nsresult nsBidiPresUtils::ProcessText(const PRUnichar* aText,
1734 : PRInt32 aLength,
1735 : nsBidiDirection aBaseDirection,
1736 : nsPresContext* aPresContext,
1737 : BidiProcessor& aprocessor,
1738 : Mode aMode,
1739 : nsBidiPositionResolve* aPosResolve,
1740 : PRInt32 aPosResolveCount,
1741 : nscoord* aWidth,
1742 : nsBidi* aBidiEngine)
1743 : {
1744 0 : NS_ASSERTION((aPosResolve == nsnull) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1745 :
1746 : PRInt32 runCount;
1747 :
1748 0 : nsAutoString textBuffer(aText, aLength);
1749 :
1750 0 : nsresult rv = aBidiEngine->SetPara(aText, aLength, aBaseDirection, nsnull);
1751 0 : if (NS_FAILED(rv))
1752 0 : return rv;
1753 :
1754 0 : rv = aBidiEngine->CountRuns(&runCount);
1755 0 : if (NS_FAILED(rv))
1756 0 : return rv;
1757 :
1758 0 : nscoord xOffset = 0;
1759 0 : nscoord width, xEndRun = 0;
1760 0 : nscoord totalWidth = 0;
1761 : PRInt32 i, start, limit, length;
1762 0 : PRUint32 visualStart = 0;
1763 : PRUint8 charType;
1764 0 : PRUint8 prevType = eCharType_LeftToRight;
1765 : nsBidiLevel level;
1766 :
1767 0 : for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
1768 : {
1769 0 : aPosResolve[nPosResolve].visualIndex = kNotFound;
1770 0 : aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
1771 0 : aPosResolve[nPosResolve].visualWidth = kNotFound;
1772 : }
1773 :
1774 0 : for (i = 0; i < runCount; i++) {
1775 0 : rv = aBidiEngine->GetVisualRun(i, &start, &length, &aBaseDirection);
1776 0 : if (NS_FAILED(rv))
1777 0 : return rv;
1778 :
1779 0 : rv = aBidiEngine->GetLogicalRun(start, &limit, &level);
1780 0 : if (NS_FAILED(rv))
1781 0 : return rv;
1782 :
1783 0 : PRInt32 subRunLength = limit - start;
1784 0 : PRInt32 lineOffset = start;
1785 0 : PRInt32 typeLimit = NS_MIN(limit, aLength);
1786 0 : PRInt32 subRunCount = 1;
1787 0 : PRInt32 subRunLimit = typeLimit;
1788 :
1789 : /*
1790 : * If |level| is even, i.e. the direction of the run is left-to-right, we
1791 : * render the subruns from left to right and increment the x-coordinate
1792 : * |xOffset| by the width of each subrun after rendering.
1793 : *
1794 : * If |level| is odd, i.e. the direction of the run is right-to-left, we
1795 : * render the subruns from right to left. We begin by incrementing |xOffset| by
1796 : * the width of the whole run, and then decrement it by the width of each
1797 : * subrun before rendering. After rendering all the subruns, we restore the
1798 : * x-coordinate of the end of the run for the start of the next run.
1799 : */
1800 :
1801 0 : if (level & 1) {
1802 0 : aprocessor.SetText(aText + start, subRunLength, nsBidiDirection(level & 1));
1803 0 : width = aprocessor.GetWidth();
1804 0 : xOffset += width;
1805 0 : xEndRun = xOffset;
1806 : }
1807 :
1808 0 : while (subRunCount > 0) {
1809 : // CalculateCharType can increment subRunCount if the run
1810 : // contains mixed character types
1811 0 : CalculateCharType(aBidiEngine, aText, lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
1812 :
1813 0 : nsAutoString runVisualText;
1814 0 : runVisualText.Assign(aText + start, subRunLength);
1815 0 : if (PRInt32(runVisualText.Length()) < subRunLength)
1816 0 : return NS_ERROR_OUT_OF_MEMORY;
1817 : FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength,
1818 0 : (nsCharType)charType, level & 1);
1819 :
1820 0 : aprocessor.SetText(runVisualText.get(), subRunLength, nsBidiDirection(level & 1));
1821 0 : width = aprocessor.GetWidth();
1822 0 : totalWidth += width;
1823 0 : if (level & 1) {
1824 0 : xOffset -= width;
1825 : }
1826 0 : if (aMode == MODE_DRAW) {
1827 0 : aprocessor.DrawText(xOffset, width);
1828 : }
1829 :
1830 : /*
1831 : * The caller may request to calculate the visual position of one
1832 : * or more characters.
1833 : */
1834 0 : for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve)
1835 : {
1836 0 : nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
1837 : /*
1838 : * Did we already resolve this position's visual metric? If so, skip.
1839 : */
1840 0 : if (posResolve->visualLeftTwips != kNotFound)
1841 0 : continue;
1842 :
1843 : /*
1844 : * First find out if the logical position is within this run.
1845 : */
1846 0 : if (start <= posResolve->logicalIndex &&
1847 : start + subRunLength > posResolve->logicalIndex) {
1848 : /*
1849 : * If this run is only one character long, we have an easy case:
1850 : * the visual position is the x-coord of the start of the run
1851 : * less the x-coord of the start of the whole text.
1852 : */
1853 0 : if (subRunLength == 1) {
1854 0 : posResolve->visualIndex = visualStart;
1855 0 : posResolve->visualLeftTwips = xOffset;
1856 0 : posResolve->visualWidth = width;
1857 : }
1858 : /*
1859 : * Otherwise, we need to measure the width of the run's part
1860 : * which is to the visual left of the index.
1861 : * In other words, the run is broken in two, around the logical index,
1862 : * and we measure the part which is visually left.
1863 : * If the run is right-to-left, this part will span from after the index
1864 : * up to the end of the run; if it is left-to-right, this part will span
1865 : * from the start of the run up to (and inclduing) the character before the index.
1866 : */
1867 : else {
1868 : /*
1869 : * Here is a description of how the width of the current character
1870 : * (posResolve->visualWidth) is calculated:
1871 : *
1872 : * LTR (current char: "P"):
1873 : * S A M P L E (logical index: 3, visual index: 3)
1874 : * ^ (visualLeftPart)
1875 : * ^ (visualRightSide)
1876 : * visualLeftLength == 3
1877 : * ^^^^^^ (subWidth)
1878 : * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1879 : * ^^ (posResolve->visualWidth)
1880 : *
1881 : * RTL (current char: "M"):
1882 : * E L P M A S (logical index: 2, visual index: 3)
1883 : * ^ (visualLeftPart)
1884 : * ^ (visualRightSide)
1885 : * visualLeftLength == 3
1886 : * ^^^^^^ (subWidth)
1887 : * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1888 : * ^^ (posResolve->visualWidth)
1889 : */
1890 : nscoord subWidth;
1891 : // The position in the text where this run's "left part" begins.
1892 : const PRUnichar* visualLeftPart, *visualRightSide;
1893 0 : if (level & 1) {
1894 : // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
1895 0 : posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
1896 : // Skipping to the "left part".
1897 0 : visualLeftPart = aText + posResolve->logicalIndex + 1;
1898 : // Skipping to the right side of the current character
1899 0 : visualRightSide = visualLeftPart - 1;
1900 : }
1901 : else {
1902 0 : posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
1903 : // Skipping to the "left part".
1904 0 : visualLeftPart = aText + start;
1905 : // In LTR mode this is the same as visualLeftPart
1906 0 : visualRightSide = visualLeftPart;
1907 : }
1908 : // The delta between the start of the run and the left part's end.
1909 0 : PRInt32 visualLeftLength = posResolve->visualIndex - visualStart;
1910 0 : aprocessor.SetText(visualLeftPart, visualLeftLength, nsBidiDirection(level & 1));
1911 0 : subWidth = aprocessor.GetWidth();
1912 0 : aprocessor.SetText(visualRightSide, visualLeftLength + 1, nsBidiDirection(level & 1));
1913 0 : posResolve->visualLeftTwips = xOffset + subWidth;
1914 0 : posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
1915 : }
1916 : }
1917 : }
1918 :
1919 0 : if (!(level & 1)) {
1920 0 : xOffset += width;
1921 : }
1922 :
1923 0 : --subRunCount;
1924 0 : start = lineOffset;
1925 0 : subRunLimit = typeLimit;
1926 0 : subRunLength = typeLimit - lineOffset;
1927 : } // while
1928 0 : if (level & 1) {
1929 0 : xOffset = xEndRun;
1930 : }
1931 :
1932 0 : visualStart += length;
1933 : } // for
1934 :
1935 0 : if (aWidth) {
1936 0 : *aWidth = totalWidth;
1937 : }
1938 0 : return NS_OK;
1939 : }
1940 :
1941 : class NS_STACK_CLASS nsIRenderingContextBidiProcessor : public nsBidiPresUtils::BidiProcessor {
1942 : public:
1943 0 : nsIRenderingContextBidiProcessor(nsRenderingContext* aCtx,
1944 : nsRenderingContext* aTextRunConstructionContext,
1945 : const nsPoint& aPt)
1946 0 : : mCtx(aCtx), mTextRunConstructionContext(aTextRunConstructionContext), mPt(aPt) { }
1947 :
1948 0 : ~nsIRenderingContextBidiProcessor()
1949 0 : {
1950 0 : mCtx->SetTextRunRTL(false);
1951 0 : }
1952 :
1953 0 : virtual void SetText(const PRUnichar* aText,
1954 : PRInt32 aLength,
1955 : nsBidiDirection aDirection)
1956 : {
1957 0 : mTextRunConstructionContext->SetTextRunRTL(aDirection==NSBIDI_RTL);
1958 0 : mText = aText;
1959 0 : mLength = aLength;
1960 0 : }
1961 :
1962 0 : virtual nscoord GetWidth()
1963 : {
1964 0 : return mTextRunConstructionContext->GetWidth(mText, mLength);
1965 : }
1966 :
1967 0 : virtual void DrawText(nscoord aXOffset,
1968 : nscoord)
1969 : {
1970 : mCtx->FontMetrics()->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y,
1971 0 : mCtx, mTextRunConstructionContext);
1972 0 : }
1973 :
1974 : private:
1975 : nsRenderingContext* mCtx;
1976 : nsRenderingContext* mTextRunConstructionContext;
1977 : nsPoint mPt;
1978 : const PRUnichar* mText;
1979 : PRInt32 mLength;
1980 : nsBidiDirection mDirection;
1981 : };
1982 :
1983 0 : nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar* aText,
1984 : PRInt32 aLength,
1985 : nsBidiDirection aBaseDirection,
1986 : nsPresContext* aPresContext,
1987 : nsRenderingContext& aRenderingContext,
1988 : nsRenderingContext& aTextRunConstructionContext,
1989 : Mode aMode,
1990 : nscoord aX,
1991 : nscoord aY,
1992 : nsBidiPositionResolve* aPosResolve,
1993 : PRInt32 aPosResolveCount,
1994 : nscoord* aWidth)
1995 : {
1996 0 : nsIRenderingContextBidiProcessor processor(&aRenderingContext, &aTextRunConstructionContext, nsPoint(aX, aY));
1997 0 : nsBidi bidiEngine;
1998 : return ProcessText(aText, aLength, aBaseDirection, aPresContext, processor,
1999 0 : aMode, aPosResolve, aPosResolveCount, aWidth, &bidiEngine);
2000 : }
2001 :
2002 : /* static */
2003 0 : void nsBidiPresUtils::WriteReverse(const PRUnichar* aSrc,
2004 : PRUint32 aSrcLength,
2005 : PRUnichar* aDest)
2006 : {
2007 0 : PRUnichar* dest = aDest + aSrcLength;
2008 0 : mozilla::unicode::ClusterIterator iter(aSrc, aSrcLength);
2009 :
2010 0 : while (!iter.AtEnd()) {
2011 0 : iter.Next();
2012 0 : for (const PRUnichar *cp = iter; cp > aSrc; ) {
2013 : // Here we rely on the fact that there are no non-BMP mirrored pairs
2014 : // currently in Unicode, so we don't need to look for surrogates
2015 0 : *--dest = mozilla::unicode::GetMirroredChar(*--cp);
2016 : }
2017 0 : aSrc = iter;
2018 : }
2019 :
2020 0 : NS_ASSERTION(dest == aDest, "Whole string not copied");
2021 0 : }
2022 :
2023 : /* static */
2024 0 : bool nsBidiPresUtils::WriteLogicalToVisual(const PRUnichar* aSrc,
2025 : PRUint32 aSrcLength,
2026 : PRUnichar* aDest,
2027 : nsBidiLevel aBaseDirection,
2028 : nsBidi* aBidiEngine)
2029 : {
2030 0 : const PRUnichar* src = aSrc;
2031 0 : nsresult rv = aBidiEngine->SetPara(src, aSrcLength, aBaseDirection, nsnull);
2032 0 : if (NS_FAILED(rv)) {
2033 0 : return false;
2034 : }
2035 :
2036 : nsBidiDirection dir;
2037 0 : rv = aBidiEngine->GetDirection(&dir);
2038 : // NSBIDI_LTR returned from GetDirection means the whole text is LTR
2039 0 : if (NS_FAILED(rv) || dir == NSBIDI_LTR) {
2040 0 : return false;
2041 : }
2042 :
2043 : PRInt32 runCount;
2044 0 : rv = aBidiEngine->CountRuns(&runCount);
2045 0 : if (NS_FAILED(rv)) {
2046 0 : return false;
2047 : }
2048 :
2049 : PRInt32 runIndex, start, length;
2050 0 : PRUnichar* dest = aDest;
2051 :
2052 0 : for (runIndex = 0; runIndex < runCount; ++runIndex) {
2053 0 : rv = aBidiEngine->GetVisualRun(runIndex, &start, &length, &dir);
2054 0 : if (NS_FAILED(rv)) {
2055 0 : return false;
2056 : }
2057 :
2058 0 : src = aSrc + start;
2059 :
2060 0 : if (dir == NSBIDI_RTL) {
2061 0 : WriteReverse(src, length, dest);
2062 0 : dest += length;
2063 : } else {
2064 0 : do {
2065 0 : NS_ASSERTION(src >= aSrc && src < aSrc + aSrcLength,
2066 : "logical index out of range");
2067 0 : NS_ASSERTION(dest < aDest + aSrcLength, "visual index out of range");
2068 0 : *(dest++) = *(src++);
2069 : } while (--length);
2070 : }
2071 : }
2072 :
2073 0 : NS_ASSERTION(dest - aDest == aSrcLength, "whole string not copied");
2074 0 : return true;
2075 : }
2076 :
2077 0 : void nsBidiPresUtils::CopyLogicalToVisual(const nsAString& aSource,
2078 : nsAString& aDest,
2079 : nsBidiLevel aBaseDirection,
2080 : bool aOverride)
2081 : {
2082 0 : aDest.SetLength(0);
2083 0 : PRUint32 srcLength = aSource.Length();
2084 0 : if (srcLength == 0)
2085 0 : return;
2086 0 : if (!EnsureStringLength(aDest, srcLength)) {
2087 0 : return;
2088 : }
2089 0 : nsAString::const_iterator fromBegin, fromEnd;
2090 0 : nsAString::iterator toBegin;
2091 0 : aSource.BeginReading(fromBegin);
2092 0 : aSource.EndReading(fromEnd);
2093 0 : aDest.BeginWriting(toBegin);
2094 :
2095 0 : if (aOverride) {
2096 0 : if (aBaseDirection == NSBIDI_RTL) {
2097 : // no need to use the converter -- just copy the string in reverse order
2098 0 : WriteReverse(fromBegin.get(), srcLength, toBegin.get());
2099 : } else {
2100 : // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the
2101 : // simple copy
2102 0 : aDest.SetLength(0);
2103 : }
2104 : } else {
2105 0 : nsBidi bidiEngine;
2106 0 : if (!WriteLogicalToVisual(fromBegin.get(), srcLength, toBegin.get(),
2107 0 : aBaseDirection, &bidiEngine)) {
2108 0 : aDest.SetLength(0);
2109 : }
2110 : }
2111 :
2112 0 : if (aDest.IsEmpty()) {
2113 : // Either there was an error or the source is unidirectional
2114 : // left-to-right. In either case, just copy source to dest.
2115 0 : CopyUnicodeTo(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
2116 0 : aDest);
2117 : }
2118 : }
2119 : #endif // IBMBIDI
|