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