Coverage Report

Created: 2018-09-25 14:53

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