layout/base/nsBidiPresUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial