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