1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsBidiPresUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2246 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsBidiPresUtils.h" 1.10 +#include "nsGkAtoms.h" 1.11 +#include "nsPresContext.h" 1.12 +#include "nsRenderingContext.h" 1.13 +#include "nsBidiUtils.h" 1.14 +#include "nsCSSFrameConstructor.h" 1.15 +#include "nsContainerFrame.h" 1.16 +#include "nsInlineFrame.h" 1.17 +#include "nsPlaceholderFrame.h" 1.18 +#include "nsFirstLetterFrame.h" 1.19 +#include "nsUnicodeProperties.h" 1.20 +#include "nsTextFrame.h" 1.21 +#include "nsBlockFrame.h" 1.22 +#include "nsIFrameInlines.h" 1.23 +#include <algorithm> 1.24 + 1.25 +#undef NOISY_BIDI 1.26 +#undef REALLY_NOISY_BIDI 1.27 + 1.28 +using namespace mozilla; 1.29 + 1.30 +static const char16_t kSpace = 0x0020; 1.31 +static const char16_t kZWSP = 0x200B; 1.32 +static const char16_t kLineSeparator = 0x2028; 1.33 +static const char16_t kObjectSubstitute = 0xFFFC; 1.34 +static const char16_t kLRE = 0x202A; 1.35 +static const char16_t kRLE = 0x202B; 1.36 +static const char16_t kLRO = 0x202D; 1.37 +static const char16_t kRLO = 0x202E; 1.38 +static const char16_t kPDF = 0x202C; 1.39 +static const char16_t kSeparators[] = { 1.40 + // All characters with Bidi type Segment Separator or Block Separator 1.41 + char16_t('\t'), 1.42 + char16_t('\r'), 1.43 + char16_t('\n'), 1.44 + char16_t(0xb), 1.45 + char16_t(0x1c), 1.46 + char16_t(0x1d), 1.47 + char16_t(0x1e), 1.48 + char16_t(0x1f), 1.49 + char16_t(0x85), 1.50 + char16_t(0x2029), 1.51 + char16_t(0) 1.52 +}; 1.53 + 1.54 +#define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1) 1.55 + 1.56 +struct BidiParagraphData { 1.57 + nsString mBuffer; 1.58 + nsAutoTArray<char16_t, 16> mEmbeddingStack; 1.59 + nsTArray<nsIFrame*> mLogicalFrames; 1.60 + nsTArray<nsLineBox*> mLinePerFrame; 1.61 + nsDataHashtable<nsISupportsHashKey, int32_t> mContentToFrameIndex; 1.62 + bool mIsVisual; 1.63 + bool mReset; 1.64 + nsBidiLevel mParaLevel; 1.65 + nsIContent* mPrevContent; 1.66 + nsAutoPtr<nsBidi> mBidiEngine; 1.67 + nsIFrame* mPrevFrame; 1.68 + nsAutoPtr<BidiParagraphData> mSubParagraph; 1.69 + uint8_t mParagraphDepth; 1.70 + 1.71 + void Init(nsBlockFrame *aBlockFrame) 1.72 + { 1.73 + mBidiEngine = new nsBidi(); 1.74 + mPrevContent = nullptr; 1.75 + mParagraphDepth = 0; 1.76 + 1.77 + mParaLevel = nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame->StyleContext()); 1.78 + 1.79 + mIsVisual = aBlockFrame->PresContext()->IsVisualMode(); 1.80 + if (mIsVisual) { 1.81 + /** 1.82 + * Drill up in content to detect whether this is an element that needs to 1.83 + * be rendered with logical order even on visual pages. 1.84 + * 1.85 + * We always use logical order on form controls, firstly so that text 1.86 + * entry will be in logical order, but also because visual pages were 1.87 + * written with the assumption that even if the browser had no support 1.88 + * for right-to-left text rendering, it would use native widgets with 1.89 + * bidi support to display form controls. 1.90 + * 1.91 + * We also use logical order in XUL elements, since we expect that if a 1.92 + * XUL element appears in a visual page, it will be generated by an XBL 1.93 + * binding and contain localized text which will be in logical order. 1.94 + */ 1.95 + for (nsIContent* content = aBlockFrame->GetContent() ; content; 1.96 + content = content->GetParent()) { 1.97 + if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) || 1.98 + content->IsXUL()) { 1.99 + mIsVisual = false; 1.100 + break; 1.101 + } 1.102 + } 1.103 + } 1.104 + } 1.105 + 1.106 + BidiParagraphData* GetSubParagraph() 1.107 + { 1.108 + if (!mSubParagraph) { 1.109 + mSubParagraph = new BidiParagraphData(); 1.110 + mSubParagraph->Init(this); 1.111 + } 1.112 + 1.113 + return mSubParagraph; 1.114 + } 1.115 + 1.116 + // Initialise a sub-paragraph from its containing paragraph 1.117 + void Init(BidiParagraphData *aBpd) 1.118 + { 1.119 + mBidiEngine = new nsBidi(); 1.120 + mPrevContent = nullptr; 1.121 + mIsVisual = aBpd->mIsVisual; 1.122 + mReset = false; 1.123 + } 1.124 + 1.125 + void Reset(nsIFrame* aBDIFrame, BidiParagraphData *aBpd) 1.126 + { 1.127 + mReset = true; 1.128 + mLogicalFrames.Clear(); 1.129 + mLinePerFrame.Clear(); 1.130 + mContentToFrameIndex.Clear(); 1.131 + mBuffer.SetLength(0); 1.132 + mPrevFrame = aBpd->mPrevFrame; 1.133 + mParagraphDepth = aBpd->mParagraphDepth + 1; 1.134 + 1.135 + const nsStyleTextReset* text = aBDIFrame->StyleTextReset(); 1.136 + bool isRTL = (NS_STYLE_DIRECTION_RTL == 1.137 + aBDIFrame->StyleVisibility()->mDirection); 1.138 + 1.139 + if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) { 1.140 + mParaLevel = NSBIDI_DEFAULT_LTR; 1.141 + } else { 1.142 + mParaLevel = mParagraphDepth * 2; 1.143 + if (isRTL) ++mParaLevel; 1.144 + } 1.145 + 1.146 + if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) { 1.147 + PushBidiControl(isRTL ? kRLO : kLRO); 1.148 + } 1.149 + } 1.150 + 1.151 + void EmptyBuffer() 1.152 + { 1.153 + mBuffer.SetLength(0); 1.154 + } 1.155 + 1.156 + nsresult SetPara() 1.157 + { 1.158 + return mBidiEngine->SetPara(mBuffer.get(), BufferLength(), 1.159 + mParaLevel, nullptr); 1.160 + } 1.161 + 1.162 + /** 1.163 + * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL. 1.164 + * GetParaLevel() returns the actual (resolved) paragraph level which is 1.165 + * always either NSBIDI_LTR or NSBIDI_RTL 1.166 + */ 1.167 + nsBidiLevel GetParaLevel() 1.168 + { 1.169 + nsBidiLevel paraLevel = mParaLevel; 1.170 + if (IS_DEFAULT_LEVEL(paraLevel)) { 1.171 + mBidiEngine->GetParaLevel(¶Level); 1.172 + } 1.173 + return paraLevel; 1.174 + } 1.175 + 1.176 + nsBidiDirection GetDirection() 1.177 + { 1.178 + nsBidiDirection dir; 1.179 + mBidiEngine->GetDirection(&dir); 1.180 + return dir; 1.181 + } 1.182 + 1.183 + nsresult CountRuns(int32_t *runCount){ return mBidiEngine->CountRuns(runCount); } 1.184 + 1.185 + nsresult GetLogicalRun(int32_t aLogicalStart, 1.186 + int32_t* aLogicalLimit, 1.187 + nsBidiLevel* aLevel) 1.188 + { 1.189 + nsresult rv = mBidiEngine->GetLogicalRun(aLogicalStart, 1.190 + aLogicalLimit, aLevel); 1.191 + if (mIsVisual || NS_FAILED(rv)) 1.192 + *aLevel = GetParaLevel(); 1.193 + return rv; 1.194 + } 1.195 + 1.196 + void ResetData() 1.197 + { 1.198 + mLogicalFrames.Clear(); 1.199 + mLinePerFrame.Clear(); 1.200 + mContentToFrameIndex.Clear(); 1.201 + mBuffer.SetLength(0); 1.202 + mPrevContent = nullptr; 1.203 + for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) { 1.204 + mBuffer.Append(mEmbeddingStack[i]); 1.205 + mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME); 1.206 + mLinePerFrame.AppendElement((nsLineBox*)nullptr); 1.207 + } 1.208 + } 1.209 + 1.210 + void ResetForNewBlock() 1.211 + { 1.212 + for (BidiParagraphData* bpd = this; bpd; bpd = bpd->mSubParagraph) { 1.213 + bpd->mPrevFrame = nullptr; 1.214 + } 1.215 + } 1.216 + 1.217 + void AppendFrame(nsIFrame* aFrame, 1.218 + nsBlockInFlowLineIterator* aLineIter, 1.219 + nsIContent* aContent = nullptr) 1.220 + { 1.221 + if (aContent) { 1.222 + mContentToFrameIndex.Put(aContent, FrameCount()); 1.223 + } 1.224 + mLogicalFrames.AppendElement(aFrame); 1.225 + 1.226 + AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame); 1.227 + mLinePerFrame.AppendElement(aLineIter->GetLine().get()); 1.228 + } 1.229 + 1.230 + void AdvanceAndAppendFrame(nsIFrame** aFrame, 1.231 + nsBlockInFlowLineIterator* aLineIter, 1.232 + nsIFrame** aNextSibling) 1.233 + { 1.234 + nsIFrame* frame = *aFrame; 1.235 + nsIFrame* nextSibling = *aNextSibling; 1.236 + 1.237 + frame = frame->GetNextContinuation(); 1.238 + if (frame) { 1.239 + AppendFrame(frame, aLineIter, nullptr); 1.240 + 1.241 + /* 1.242 + * If we have already overshot the saved next-sibling while 1.243 + * scanning the frame's continuations, advance it. 1.244 + */ 1.245 + if (frame == nextSibling) { 1.246 + nextSibling = frame->GetNextSibling(); 1.247 + } 1.248 + } 1.249 + 1.250 + *aFrame = frame; 1.251 + *aNextSibling = nextSibling; 1.252 + } 1.253 + 1.254 + int32_t GetLastFrameForContent(nsIContent *aContent) 1.255 + { 1.256 + int32_t index = 0; 1.257 + mContentToFrameIndex.Get(aContent, &index); 1.258 + return index; 1.259 + } 1.260 + 1.261 + int32_t FrameCount(){ return mLogicalFrames.Length(); } 1.262 + 1.263 + int32_t BufferLength(){ return mBuffer.Length(); } 1.264 + 1.265 + nsIFrame* FrameAt(int32_t aIndex){ return mLogicalFrames[aIndex]; } 1.266 + 1.267 + nsLineBox* GetLineForFrameAt(int32_t aIndex){ return mLinePerFrame[aIndex]; } 1.268 + 1.269 + void AppendUnichar(char16_t aCh){ mBuffer.Append(aCh); } 1.270 + 1.271 + void AppendString(const nsDependentSubstring& aString){ mBuffer.Append(aString); } 1.272 + 1.273 + void AppendControlChar(char16_t aCh) 1.274 + { 1.275 + mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME); 1.276 + mLinePerFrame.AppendElement((nsLineBox*)nullptr); 1.277 + AppendUnichar(aCh); 1.278 + } 1.279 + 1.280 + void PushBidiControl(char16_t aCh) 1.281 + { 1.282 + AppendControlChar(aCh); 1.283 + mEmbeddingStack.AppendElement(aCh); 1.284 + } 1.285 + 1.286 + void PopBidiControl() 1.287 + { 1.288 + AppendControlChar(kPDF); 1.289 + NS_ASSERTION(mEmbeddingStack.Length(), "embedding/override underflow"); 1.290 + mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1); 1.291 + } 1.292 + 1.293 + void ClearBidiControls() 1.294 + { 1.295 + for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) { 1.296 + AppendControlChar(kPDF); 1.297 + } 1.298 + } 1.299 + 1.300 + static bool 1.301 + IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter, 1.302 + nsIFrame* aPrevFrame, nsIFrame* aFrame) 1.303 + { 1.304 + nsIFrame* endFrame = aLineIter->IsLastLineInList() ? nullptr : 1.305 + aLineIter->GetLine().next()->mFirstChild; 1.306 + nsIFrame* startFrame = aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild; 1.307 + for (nsIFrame* frame = startFrame; frame && frame != endFrame; 1.308 + frame = frame->GetNextSibling()) { 1.309 + if (frame == aFrame) 1.310 + return true; 1.311 + } 1.312 + return false; 1.313 + } 1.314 + 1.315 + static void 1.316 + AdvanceLineIteratorToFrame(nsIFrame* aFrame, 1.317 + nsBlockInFlowLineIterator* aLineIter, 1.318 + nsIFrame*& aPrevFrame) 1.319 + { 1.320 + // Advance aLine to the line containing aFrame 1.321 + nsIFrame* child = aFrame; 1.322 + nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child); 1.323 + while (parent && !nsLayoutUtils::GetAsBlock(parent)) { 1.324 + child = parent; 1.325 + parent = nsLayoutUtils::GetParentOrPlaceholderFor(child); 1.326 + } 1.327 + NS_ASSERTION (parent, "aFrame is not a descendent of aBlockFrame"); 1.328 + while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) { 1.329 +#ifdef DEBUG 1.330 + bool hasNext = 1.331 +#endif 1.332 + aLineIter->Next(); 1.333 + NS_ASSERTION(hasNext, "Can't find frame in lines!"); 1.334 + aPrevFrame = nullptr; 1.335 + } 1.336 + aPrevFrame = child; 1.337 + } 1.338 + 1.339 +}; 1.340 + 1.341 +struct BidiLineData { 1.342 + nsTArray<nsIFrame*> mLogicalFrames; 1.343 + nsTArray<nsIFrame*> mVisualFrames; 1.344 + nsTArray<int32_t> mIndexMap; 1.345 + nsAutoTArray<uint8_t, 18> mLevels; 1.346 + bool mIsReordered; 1.347 + 1.348 + BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine) 1.349 + { 1.350 + /** 1.351 + * Initialize the logically-ordered array of frames using the top-level 1.352 + * frames of a single line 1.353 + */ 1.354 + mLogicalFrames.Clear(); 1.355 + 1.356 + bool isReordered = false; 1.357 + bool hasRTLFrames = false; 1.358 + 1.359 + for (nsIFrame* frame = aFirstFrameOnLine; 1.360 + frame && aNumFramesOnLine--; 1.361 + frame = frame->GetNextSibling()) { 1.362 + AppendFrame(frame); 1.363 + uint8_t level = nsBidiPresUtils::GetFrameEmbeddingLevel(frame); 1.364 + mLevels.AppendElement(level); 1.365 + mIndexMap.AppendElement(0); 1.366 + if (level & 1) { 1.367 + hasRTLFrames = true; 1.368 + } 1.369 + } 1.370 + 1.371 + // Reorder the line 1.372 + nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(), 1.373 + mIndexMap.Elements()); 1.374 + 1.375 + for (int32_t i = 0; i < FrameCount(); i++) { 1.376 + mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i])); 1.377 + if (i != mIndexMap[i]) { 1.378 + isReordered = true; 1.379 + } 1.380 + } 1.381 + 1.382 + // If there's an RTL frame, assume the line is reordered 1.383 + mIsReordered = isReordered || hasRTLFrames; 1.384 + } 1.385 + 1.386 + void AppendFrame(nsIFrame* aFrame) 1.387 + { 1.388 + mLogicalFrames.AppendElement(aFrame); 1.389 + } 1.390 + 1.391 + int32_t FrameCount(){ return mLogicalFrames.Length(); } 1.392 + 1.393 + nsIFrame* LogicalFrameAt(int32_t aIndex){ return mLogicalFrames[aIndex]; } 1.394 + 1.395 + nsIFrame* VisualFrameAt(int32_t aIndex){ return mVisualFrames[aIndex]; } 1.396 +}; 1.397 + 1.398 +/* Some helper methods for Resolve() */ 1.399 + 1.400 +// Should this frame be split between text runs? 1.401 +static bool 1.402 +IsBidiSplittable(nsIFrame* aFrame) 1.403 +{ 1.404 + // Bidi inline containers should be split, unless they're line frames. 1.405 + nsIAtom* frameType = aFrame->GetType(); 1.406 + return (aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) && 1.407 + frameType != nsGkAtoms::lineFrame) || 1.408 + frameType == nsGkAtoms::textFrame; 1.409 +} 1.410 + 1.411 +// Should this frame be treated as a leaf (e.g. when building mLogicalFrames)? 1.412 +static bool 1.413 +IsBidiLeaf(nsIFrame* aFrame) 1.414 +{ 1.415 + nsIFrame* kid = aFrame->GetFirstPrincipalChild(); 1.416 + return !kid || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer); 1.417 +} 1.418 + 1.419 +/** 1.420 + * Create non-fluid continuations for the ancestors of a given frame all the way 1.421 + * up the frame tree until we hit a non-splittable frame (a line or a block). 1.422 + * 1.423 + * @param aParent the first parent frame to be split 1.424 + * @param aFrame the child frames after this frame are reparented to the 1.425 + * newly-created continuation of aParent. 1.426 + * If aFrame is null, all the children of aParent are reparented. 1.427 + */ 1.428 +static nsresult 1.429 +SplitInlineAncestors(nsIFrame* aParent, 1.430 + nsIFrame* aFrame) 1.431 +{ 1.432 + nsPresContext *presContext = aParent->PresContext(); 1.433 + nsIPresShell *presShell = presContext->PresShell(); 1.434 + nsIFrame* frame = aFrame; 1.435 + nsIFrame* parent = aParent; 1.436 + nsIFrame* newParent; 1.437 + 1.438 + while (IsBidiSplittable(parent)) { 1.439 + nsIFrame* grandparent = parent->GetParent(); 1.440 + NS_ASSERTION(grandparent, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors"); 1.441 + 1.442 + // Split the child list after |frame|, unless it is the last child. 1.443 + if (!frame || frame->GetNextSibling()) { 1.444 + 1.445 + newParent = presShell->FrameConstructor()-> 1.446 + CreateContinuingFrame(presContext, parent, grandparent, false); 1.447 + 1.448 + nsContainerFrame* container = do_QueryFrame(parent); 1.449 + nsFrameList tail = container->StealFramesAfter(frame); 1.450 + 1.451 + // Reparent views as necessary 1.452 + nsresult rv; 1.453 + rv = nsContainerFrame::ReparentFrameViewList(tail, parent, newParent); 1.454 + if (NS_FAILED(rv)) { 1.455 + return rv; 1.456 + } 1.457 + 1.458 + // The parent's continuation adopts the siblings after the split. 1.459 + rv = newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nullptr, tail); 1.460 + if (NS_FAILED(rv)) { 1.461 + return rv; 1.462 + } 1.463 + 1.464 + // The list name kNoReflowPrincipalList would indicate we don't want reflow 1.465 + nsFrameList temp(newParent, newParent); 1.466 + rv = grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp); 1.467 + if (NS_FAILED(rv)) { 1.468 + return rv; 1.469 + } 1.470 + } 1.471 + 1.472 + frame = parent; 1.473 + parent = grandparent; 1.474 + } 1.475 + 1.476 + return NS_OK; 1.477 +} 1.478 + 1.479 +static void 1.480 +MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext) 1.481 +{ 1.482 + NS_ASSERTION (!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext, 1.483 + "next-in-flow is not next continuation!"); 1.484 + aFrame->SetNextInFlow(aNext); 1.485 + 1.486 + NS_ASSERTION (!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame, 1.487 + "prev-in-flow is not prev continuation!"); 1.488 + aNext->SetPrevInFlow(aFrame); 1.489 +} 1.490 + 1.491 +static void 1.492 +MakeContinuationsNonFluidUpParentChain(nsIFrame* aFrame, nsIFrame* aNext) 1.493 +{ 1.494 + nsIFrame* frame; 1.495 + nsIFrame* next; 1.496 + 1.497 + for (frame = aFrame, next = aNext; 1.498 + frame && next && 1.499 + next != frame && next == frame->GetNextInFlow() && 1.500 + IsBidiSplittable(frame); 1.501 + frame = frame->GetParent(), next = next->GetParent()) { 1.502 + 1.503 + frame->SetNextContinuation(next); 1.504 + next->SetPrevContinuation(frame); 1.505 + } 1.506 +} 1.507 + 1.508 +// If aFrame is the last child of its parent, convert bidi continuations to 1.509 +// fluid continuations for all of its inline ancestors. 1.510 +// If it isn't the last child, make sure that its continuation is fluid. 1.511 +static void 1.512 +JoinInlineAncestors(nsIFrame* aFrame) 1.513 +{ 1.514 + nsIFrame* frame = aFrame; 1.515 + do { 1.516 + nsIFrame* next = frame->GetNextContinuation(); 1.517 + if (next) { 1.518 + // Don't join frames if they come from different paragraph depths (i.e. 1.519 + // one is bidi isolated relative to the other 1.520 + if (nsBidiPresUtils::GetParagraphDepth(frame) == 1.521 + nsBidiPresUtils::GetParagraphDepth(next)) { 1.522 + MakeContinuationFluid(frame, next); 1.523 + } 1.524 + } 1.525 + // Join the parent only as long as we're its last child. 1.526 + if (frame->GetNextSibling()) 1.527 + break; 1.528 + frame = frame->GetParent(); 1.529 + } while (frame && IsBidiSplittable(frame)); 1.530 +} 1.531 + 1.532 +static nsresult 1.533 +CreateContinuation(nsIFrame* aFrame, 1.534 + nsIFrame** aNewFrame, 1.535 + bool aIsFluid) 1.536 +{ 1.537 + NS_PRECONDITION(aNewFrame, "null OUT ptr"); 1.538 + NS_PRECONDITION(aFrame, "null ptr"); 1.539 + 1.540 + *aNewFrame = nullptr; 1.541 + 1.542 + nsPresContext *presContext = aFrame->PresContext(); 1.543 + nsIPresShell *presShell = presContext->PresShell(); 1.544 + NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateContinuation"); 1.545 + 1.546 + nsIFrame* parent = aFrame->GetParent(); 1.547 + NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation"); 1.548 + 1.549 + nsresult rv = NS_OK; 1.550 + 1.551 + // Have to special case floating first letter frames because the continuation 1.552 + // doesn't go in the first letter frame. The continuation goes with the rest 1.553 + // of the text that the first letter frame was made out of. 1.554 + if (parent->GetType() == nsGkAtoms::letterFrame && 1.555 + parent->IsFloating()) { 1.556 + nsFirstLetterFrame* letterFrame = do_QueryFrame(parent); 1.557 + rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame, 1.558 + aNewFrame, aIsFluid); 1.559 + return rv; 1.560 + } 1.561 + 1.562 + *aNewFrame = presShell->FrameConstructor()-> 1.563 + CreateContinuingFrame(presContext, aFrame, parent, aIsFluid); 1.564 + 1.565 + // The list name kNoReflowPrincipalList would indicate we don't want reflow 1.566 + // XXXbz this needs higher-level framelist love 1.567 + nsFrameList temp(*aNewFrame, *aNewFrame); 1.568 + rv = parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp); 1.569 + if (NS_FAILED(rv)) { 1.570 + return rv; 1.571 + } 1.572 + 1.573 + if (!aIsFluid) { 1.574 + // Split inline ancestor frames 1.575 + rv = SplitInlineAncestors(parent, aFrame); 1.576 + if (NS_FAILED(rv)) { 1.577 + return rv; 1.578 + } 1.579 + } 1.580 + 1.581 + return NS_OK; 1.582 +} 1.583 + 1.584 +/* 1.585 + * Overview of the implementation of Resolve(): 1.586 + * 1.587 + * Walk through the descendants of aBlockFrame and build: 1.588 + * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order 1.589 + * * mBuffer: an nsString containing a representation of 1.590 + * the content of the frames. 1.591 + * In the case of text frames, this is the actual text context of the 1.592 + * frames, but some other elements are represented in a symbolic form which 1.593 + * will make the Unicode Bidi Algorithm give the correct results. 1.594 + * Bidi embeddings and overrides set by CSS or <bdo> elements are 1.595 + * represented by the corresponding Unicode control characters. 1.596 + * <br> elements are represented by U+2028 LINE SEPARATOR 1.597 + * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT 1.598 + * CHARACTER 1.599 + * 1.600 + * Then pass mBuffer to the Bidi engine for resolving of embedding levels 1.601 + * by nsBidi::SetPara() and division into directional runs by 1.602 + * nsBidi::CountRuns(). 1.603 + * 1.604 + * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and 1.605 + * correlate them with the frames indexed in mLogicalFrames, setting the 1.606 + * baseLevel and embeddingLevel properties according to the results returned 1.607 + * by the Bidi engine. 1.608 + * 1.609 + * The rendering layer requires each text frame to contain text in only one 1.610 + * direction, so we may need to call EnsureBidiContinuation() to split frames. 1.611 + * We may also need to call RemoveBidiContinuation() to convert frames created 1.612 + * by EnsureBidiContinuation() in previous reflows into fluid continuations. 1.613 + */ 1.614 +nsresult 1.615 +nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) 1.616 +{ 1.617 + BidiParagraphData bpd; 1.618 + bpd.Init(aBlockFrame); 1.619 + 1.620 + // Handle bidi-override being set on the block itself before calling 1.621 + // TraverseFrames. 1.622 + const nsStyleTextReset* text = aBlockFrame->StyleTextReset(); 1.623 + char16_t ch = 0; 1.624 + if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) { 1.625 + const nsStyleVisibility* vis = aBlockFrame->StyleVisibility(); 1.626 + if (NS_STYLE_DIRECTION_RTL == vis->mDirection) { 1.627 + ch = kRLO; 1.628 + } 1.629 + else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) { 1.630 + ch = kLRO; 1.631 + } 1.632 + if (ch != 0) { 1.633 + bpd.PushBidiControl(ch); 1.634 + } 1.635 + } 1.636 + for (nsBlockFrame* block = aBlockFrame; block; 1.637 + block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) { 1.638 + block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); 1.639 + nsBlockInFlowLineIterator lineIter(block, block->begin_lines()); 1.640 + bpd.ResetForNewBlock(); 1.641 + TraverseFrames(aBlockFrame, &lineIter, block->GetFirstPrincipalChild(), &bpd); 1.642 + // XXX what about overflow lines? 1.643 + } 1.644 + 1.645 + if (ch != 0) { 1.646 + bpd.PopBidiControl(); 1.647 + } 1.648 + 1.649 + BidiParagraphData* subParagraph = bpd.GetSubParagraph(); 1.650 + if (subParagraph->BufferLength()) { 1.651 + ResolveParagraph(aBlockFrame, subParagraph); 1.652 + subParagraph->EmptyBuffer(); 1.653 + } 1.654 + return ResolveParagraph(aBlockFrame, &bpd); 1.655 +} 1.656 + 1.657 +nsresult 1.658 +nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame, 1.659 + BidiParagraphData* aBpd) 1.660 +{ 1.661 + nsPresContext *presContext = aBlockFrame->PresContext(); 1.662 + 1.663 + if (aBpd->BufferLength() < 1) { 1.664 + return NS_OK; 1.665 + } 1.666 + aBpd->mBuffer.ReplaceChar(kSeparators, kSpace); 1.667 + 1.668 + int32_t runCount; 1.669 + 1.670 + nsresult rv = aBpd->SetPara(); 1.671 + NS_ENSURE_SUCCESS(rv, rv); 1.672 + 1.673 + uint8_t embeddingLevel = aBpd->GetParaLevel(); 1.674 + 1.675 + rv = aBpd->CountRuns(&runCount); 1.676 + NS_ENSURE_SUCCESS(rv, rv); 1.677 + 1.678 + int32_t runLength = 0; // the length of the current run of text 1.679 + int32_t lineOffset = 0; // the start of the current run 1.680 + int32_t logicalLimit = 0; // the end of the current run + 1 1.681 + int32_t numRun = -1; 1.682 + int32_t fragmentLength = 0; // the length of the current text frame 1.683 + int32_t frameIndex = -1; // index to the frames in mLogicalFrames 1.684 + int32_t frameCount = aBpd->FrameCount(); 1.685 + int32_t contentOffset = 0; // offset of current frame in its content node 1.686 + bool isTextFrame = false; 1.687 + nsIFrame* frame = nullptr; 1.688 + nsIContent* content = nullptr; 1.689 + int32_t contentTextLength = 0; 1.690 + 1.691 + FramePropertyTable *propTable = presContext->PropertyTable(); 1.692 + nsLineBox* currentLine = nullptr; 1.693 + 1.694 +#ifdef DEBUG 1.695 +#ifdef NOISY_BIDI 1.696 + printf("Before Resolve(), aBlockFrame=0x%p, mBuffer='%s', frameCount=%d, runCount=%d\n", 1.697 + (void*)aBlockFrame, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(), frameCount, runCount); 1.698 +#ifdef REALLY_NOISY_BIDI 1.699 + printf(" block frame tree=:\n"); 1.700 + aBlockFrame->List(stdout, 0); 1.701 +#endif 1.702 +#endif 1.703 +#endif 1.704 + 1.705 + if (runCount == 1 && frameCount == 1 && 1.706 + aBpd->mParagraphDepth == 0 && aBpd->GetDirection() == NSBIDI_LTR && 1.707 + aBpd->GetParaLevel() == 0) { 1.708 + // We have a single left-to-right frame in a left-to-right paragraph, 1.709 + // without bidi isolation from the surrounding text. 1.710 + // Make sure that the embedding level and base level frame properties aren't 1.711 + // set (because if they are this frame used to have some other direction, 1.712 + // so we can't do this optimization), and we're done. 1.713 + nsIFrame* frame = aBpd->FrameAt(0); 1.714 + if (frame != NS_BIDI_CONTROL_FRAME && 1.715 + !frame->Properties().Get(nsIFrame::EmbeddingLevelProperty()) && 1.716 + !frame->Properties().Get(nsIFrame::BaseLevelProperty())) { 1.717 +#ifdef DEBUG 1.718 +#ifdef NOISY_BIDI 1.719 + printf("early return for single direction frame %p\n", (void*)frame); 1.720 +#endif 1.721 +#endif 1.722 + frame->AddStateBits(NS_FRAME_IS_BIDI); 1.723 + return NS_OK; 1.724 + } 1.725 + } 1.726 + 1.727 + nsIFrame* firstFrame = nullptr; 1.728 + nsIFrame* lastFrame = nullptr; 1.729 + 1.730 + for (; ;) { 1.731 + if (fragmentLength <= 0) { 1.732 + // Get the next frame from mLogicalFrames 1.733 + if (++frameIndex >= frameCount) { 1.734 + break; 1.735 + } 1.736 + frame = aBpd->FrameAt(frameIndex); 1.737 + if (frame == NS_BIDI_CONTROL_FRAME || 1.738 + nsGkAtoms::textFrame != frame->GetType()) { 1.739 + /* 1.740 + * Any non-text frame corresponds to a single character in the text buffer 1.741 + * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE) 1.742 + */ 1.743 + isTextFrame = false; 1.744 + fragmentLength = 1; 1.745 + } 1.746 + else { 1.747 + if (!firstFrame) { 1.748 + firstFrame = frame; 1.749 + } 1.750 + lastFrame = frame; 1.751 + currentLine = aBpd->GetLineForFrameAt(frameIndex); 1.752 + content = frame->GetContent(); 1.753 + if (!content) { 1.754 + rv = NS_OK; 1.755 + break; 1.756 + } 1.757 + contentTextLength = content->TextLength(); 1.758 + if (contentTextLength == 0) { 1.759 + frame->AdjustOffsetsForBidi(0, 0); 1.760 + // Set the base level and embedding level of the current run even 1.761 + // on an empty frame. Otherwise frame reordering will not be correct. 1.762 + propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(), 1.763 + NS_INT32_TO_PTR(embeddingLevel)); 1.764 + propTable->Set(frame, nsIFrame::BaseLevelProperty(), 1.765 + NS_INT32_TO_PTR(aBpd->GetParaLevel())); 1.766 + propTable->Set(frame, nsIFrame::ParagraphDepthProperty(), 1.767 + NS_INT32_TO_PTR(aBpd->mParagraphDepth)); 1.768 + continue; 1.769 + } 1.770 + int32_t start, end; 1.771 + frame->GetOffsets(start, end); 1.772 + NS_ASSERTION(!(contentTextLength < end - start), 1.773 + "Frame offsets don't fit in content"); 1.774 + fragmentLength = std::min(contentTextLength, end - start); 1.775 + contentOffset = start; 1.776 + isTextFrame = true; 1.777 + } 1.778 + } // if (fragmentLength <= 0) 1.779 + 1.780 + if (runLength <= 0) { 1.781 + // Get the next run of text from the Bidi engine 1.782 + if (++numRun >= runCount) { 1.783 + break; 1.784 + } 1.785 + lineOffset = logicalLimit; 1.786 + if (NS_FAILED(aBpd->GetLogicalRun( 1.787 + lineOffset, &logicalLimit, &embeddingLevel) ) ) { 1.788 + break; 1.789 + } 1.790 + runLength = logicalLimit - lineOffset; 1.791 + } // if (runLength <= 0) 1.792 + 1.793 + if (frame == NS_BIDI_CONTROL_FRAME) { 1.794 + frame = nullptr; 1.795 + ++lineOffset; 1.796 + } 1.797 + else { 1.798 + propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(), 1.799 + NS_INT32_TO_PTR(embeddingLevel)); 1.800 + propTable->Set(frame, nsIFrame::BaseLevelProperty(), 1.801 + NS_INT32_TO_PTR(aBpd->GetParaLevel())); 1.802 + propTable->Set(frame, nsIFrame::ParagraphDepthProperty(), 1.803 + NS_INT32_TO_PTR(aBpd->mParagraphDepth)); 1.804 + if (isTextFrame) { 1.805 + if ( (runLength > 0) && (runLength < fragmentLength) ) { 1.806 + /* 1.807 + * The text in this frame continues beyond the end of this directional run. 1.808 + * Create a non-fluid continuation frame for the next directional run. 1.809 + */ 1.810 + currentLine->MarkDirty(); 1.811 + nsIFrame* nextBidi; 1.812 + int32_t runEnd = contentOffset + runLength; 1.813 + rv = EnsureBidiContinuation(frame, &nextBidi, frameIndex, 1.814 + contentOffset, 1.815 + runEnd); 1.816 + if (NS_FAILED(rv)) { 1.817 + break; 1.818 + } 1.819 + nextBidi->AdjustOffsetsForBidi(runEnd, 1.820 + contentOffset + fragmentLength); 1.821 + lastFrame = frame = nextBidi; 1.822 + contentOffset = runEnd; 1.823 + } // if (runLength < fragmentLength) 1.824 + else { 1.825 + if (contentOffset + fragmentLength == contentTextLength) { 1.826 + /* 1.827 + * We have finished all the text in this content node. Convert any 1.828 + * further non-fluid continuations to fluid continuations and advance 1.829 + * frameIndex to the last frame in the content node 1.830 + */ 1.831 + int32_t newIndex = aBpd->GetLastFrameForContent(content); 1.832 + if (newIndex > frameIndex) { 1.833 + currentLine->MarkDirty(); 1.834 + RemoveBidiContinuation(aBpd, frame, 1.835 + frameIndex, newIndex, lineOffset); 1.836 + frameIndex = newIndex; 1.837 + lastFrame = frame = aBpd->FrameAt(frameIndex); 1.838 + } 1.839 + } else if (fragmentLength > 0 && runLength > fragmentLength) { 1.840 + /* 1.841 + * There is more text that belongs to this directional run in the next 1.842 + * text frame: make sure it is a fluid continuation of the current frame. 1.843 + * Do not advance frameIndex, because the next frame may contain 1.844 + * multi-directional text and need to be split 1.845 + */ 1.846 + int32_t newIndex = frameIndex; 1.847 + do { 1.848 + } while (++newIndex < frameCount && 1.849 + aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME); 1.850 + if (newIndex < frameCount) { 1.851 + currentLine->MarkDirty(); 1.852 + RemoveBidiContinuation(aBpd, frame, 1.853 + frameIndex, newIndex, lineOffset); 1.854 + } 1.855 + } else if (runLength == fragmentLength) { 1.856 + /* 1.857 + * If the directional run ends at the end of the frame, make sure 1.858 + * that any continuation is non-fluid, and do the same up the 1.859 + * parent chain 1.860 + */ 1.861 + nsIFrame* next = frame->GetNextInFlow(); 1.862 + if (next) { 1.863 + currentLine->MarkDirty(); 1.864 + MakeContinuationsNonFluidUpParentChain(frame, next); 1.865 + } 1.866 + } 1.867 + frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength); 1.868 + currentLine->MarkDirty(); 1.869 + } 1.870 + } // isTextFrame 1.871 + else { 1.872 + ++lineOffset; 1.873 + } 1.874 + } // not bidi control frame 1.875 + int32_t temp = runLength; 1.876 + runLength -= fragmentLength; 1.877 + fragmentLength -= temp; 1.878 + 1.879 + if (frame && fragmentLength <= 0) { 1.880 + // If the frame is at the end of a run, and this is not the end of our 1.881 + // paragrah, split all ancestor inlines that need splitting. 1.882 + // To determine whether we're at the end of the run, we check that we've 1.883 + // finished processing the current run, and that the current frame 1.884 + // doesn't have a fluid continuation (it could have a fluid continuation 1.885 + // of zero length, so testing runLength alone is not sufficient). 1.886 + if (runLength <= 0 && !frame->GetNextInFlow()) { 1.887 + if (numRun + 1 < runCount) { 1.888 + nsIFrame* child = frame; 1.889 + nsIFrame* parent = frame->GetParent(); 1.890 + // As long as we're on the last sibling, the parent doesn't have to 1.891 + // be split. 1.892 + // However, if the parent has a fluid continuation, we do have to make 1.893 + // it non-fluid. This can happen e.g. when we have a first-letter 1.894 + // frame and the end of the first-letter coincides with the end of a 1.895 + // directional run. 1.896 + while (parent && 1.897 + IsBidiSplittable(parent) && 1.898 + !child->GetNextSibling()) { 1.899 + nsIFrame* next = parent->GetNextInFlow(); 1.900 + if (next) { 1.901 + parent->SetNextContinuation(next); 1.902 + next->SetPrevContinuation(parent); 1.903 + } 1.904 + child = parent; 1.905 + parent = child->GetParent(); 1.906 + } 1.907 + if (parent && IsBidiSplittable(parent)) { 1.908 + SplitInlineAncestors(parent, child); 1.909 + } 1.910 + } 1.911 + } 1.912 + else { 1.913 + // We're not at an end of a run. If |frame| is the last child of its 1.914 + // parent, and its ancestors happen to have bidi continuations, convert 1.915 + // them into fluid continuations. 1.916 + JoinInlineAncestors(frame); 1.917 + } 1.918 + } 1.919 + } // for 1.920 + 1.921 + if (aBpd->mParagraphDepth > 0) { 1.922 + nsIFrame* child; 1.923 + nsIFrame* parent; 1.924 + if (firstFrame) { 1.925 + child = firstFrame->GetParent(); 1.926 + if (child) { 1.927 + parent = child->GetParent(); 1.928 + if (parent && IsBidiSplittable(parent)) { 1.929 + nsIFrame* prev = child->GetPrevSibling(); 1.930 + if (prev) { 1.931 + SplitInlineAncestors(parent, prev); 1.932 + } 1.933 + } 1.934 + } 1.935 + } 1.936 + if (lastFrame) { 1.937 + child = lastFrame->GetParent(); 1.938 + if (child) { 1.939 + parent = child->GetParent(); 1.940 + if (parent && IsBidiSplittable(parent)) { 1.941 + SplitInlineAncestors(parent, child); 1.942 + } 1.943 + } 1.944 + } 1.945 + } 1.946 + 1.947 +#ifdef DEBUG 1.948 +#ifdef REALLY_NOISY_BIDI 1.949 + printf("---\nAfter Resolve(), frameTree =:\n"); 1.950 + aBlockFrame->List(stdout, 0); 1.951 + printf("===\n"); 1.952 +#endif 1.953 +#endif 1.954 + 1.955 + return rv; 1.956 +} 1.957 + 1.958 +void 1.959 +nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame, 1.960 + nsBlockInFlowLineIterator* aLineIter, 1.961 + nsIFrame* aCurrentFrame, 1.962 + BidiParagraphData* aBpd) 1.963 +{ 1.964 + if (!aCurrentFrame) 1.965 + return; 1.966 + 1.967 +#ifdef DEBUG 1.968 + nsBlockFrame* initialLineContainer = aLineIter->GetContainer(); 1.969 +#endif 1.970 + 1.971 + nsIFrame* childFrame = aCurrentFrame; 1.972 + do { 1.973 + /* 1.974 + * It's important to get the next sibling and next continuation *before* 1.975 + * handling the frame: If we encounter a forced paragraph break and call 1.976 + * ResolveParagraph within this loop, doing GetNextSibling and 1.977 + * GetNextContinuation after that could return a bidi continuation that had 1.978 + * just been split from the original childFrame and we would process it 1.979 + * twice. 1.980 + */ 1.981 + nsIFrame* nextSibling = childFrame->GetNextSibling(); 1.982 + bool isLastFrame = !childFrame->GetNextContinuation(); 1.983 + bool isFirstFrame = !childFrame->GetPrevContinuation(); 1.984 + 1.985 + // If the real frame for a placeholder is a first letter frame, we need to 1.986 + // drill down into it and include its contents in Bidi resolution. 1.987 + // If not, we just use the placeholder. 1.988 + nsIFrame* frame = childFrame; 1.989 + if (nsGkAtoms::placeholderFrame == childFrame->GetType()) { 1.990 + nsIFrame* realFrame = 1.991 + nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame); 1.992 + if (realFrame->GetType() == nsGkAtoms::letterFrame) { 1.993 + frame = realFrame; 1.994 + } 1.995 + } 1.996 + 1.997 + char16_t ch = 0; 1.998 + if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) { 1.999 + if (!(frame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { 1.1000 + nsContainerFrame* c = static_cast<nsContainerFrame*>(frame); 1.1001 + MOZ_ASSERT(c = do_QueryFrame(frame), 1.1002 + "eBidiInlineContainer must be a nsContainerFrame subclass"); 1.1003 + c->DrainSelfOverflowList(); 1.1004 + } 1.1005 + 1.1006 + const nsStyleVisibility* vis = frame->StyleVisibility(); 1.1007 + const nsStyleTextReset* text = frame->StyleTextReset(); 1.1008 + if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) { 1.1009 + if (NS_STYLE_DIRECTION_RTL == vis->mDirection) { 1.1010 + ch = kRLO; 1.1011 + } 1.1012 + else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) { 1.1013 + ch = kLRO; 1.1014 + } 1.1015 + } else if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_EMBED) { 1.1016 + if (NS_STYLE_DIRECTION_RTL == vis->mDirection) { 1.1017 + ch = kRLE; 1.1018 + } 1.1019 + else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) { 1.1020 + ch = kLRE; 1.1021 + } 1.1022 + } 1.1023 + 1.1024 + // Add a dummy frame pointer representing a bidi control code before the 1.1025 + // first frame of an element specifying embedding or override 1.1026 + if (ch != 0 && isFirstFrame) { 1.1027 + aBpd->PushBidiControl(ch); 1.1028 + } 1.1029 + } 1.1030 + 1.1031 + if (IsBidiLeaf(frame)) { 1.1032 + /* Bidi leaf frame: add the frame to the mLogicalFrames array, 1.1033 + * and add its index to the mContentToFrameIndex hashtable. This 1.1034 + * will be used in RemoveBidiContinuation() to identify the last 1.1035 + * frame in the array with a given content. 1.1036 + */ 1.1037 + nsIContent* content = frame->GetContent(); 1.1038 + aBpd->AppendFrame(frame, aLineIter, content); 1.1039 + 1.1040 + // Append the content of the frame to the paragraph buffer 1.1041 + nsIAtom* frameType = frame->GetType(); 1.1042 + if (nsGkAtoms::textFrame == frameType) { 1.1043 + if (content != aBpd->mPrevContent) { 1.1044 + aBpd->mPrevContent = content; 1.1045 + if (!frame->StyleText()->NewlineIsSignificant()) { 1.1046 + content->AppendTextTo(aBpd->mBuffer); 1.1047 + } else { 1.1048 + /* 1.1049 + * For preformatted text we have to do bidi resolution on each line 1.1050 + * separately. 1.1051 + */ 1.1052 + nsAutoString text; 1.1053 + content->AppendTextTo(text); 1.1054 + nsIFrame* next; 1.1055 + do { 1.1056 + next = nullptr; 1.1057 + 1.1058 + int32_t start, end; 1.1059 + frame->GetOffsets(start, end); 1.1060 + int32_t endLine = text.FindChar('\n', start); 1.1061 + if (endLine == -1) { 1.1062 + /* 1.1063 + * If there is no newline in the text content, just save the 1.1064 + * text from this frame and its continuations, and do bidi 1.1065 + * resolution later 1.1066 + */ 1.1067 + aBpd->AppendString(Substring(text, start)); 1.1068 + while (frame && nextSibling) { 1.1069 + aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling); 1.1070 + } 1.1071 + break; 1.1072 + } 1.1073 + 1.1074 + /* 1.1075 + * If there is a newline in the frame, break the frame after the 1.1076 + * newline, do bidi resolution and repeat until the last sibling 1.1077 + */ 1.1078 + ++endLine; 1.1079 + 1.1080 + /* 1.1081 + * If the frame ends before the new line, save the text and move 1.1082 + * into the next continuation 1.1083 + */ 1.1084 + aBpd->AppendString(Substring(text, start, 1.1085 + std::min(end, endLine) - start)); 1.1086 + while (end < endLine && nextSibling) { 1.1087 + aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling); 1.1088 + NS_ASSERTION(frame, "Premature end of continuation chain"); 1.1089 + frame->GetOffsets(start, end); 1.1090 + aBpd->AppendString(Substring(text, start, 1.1091 + std::min(end, endLine) - start)); 1.1092 + } 1.1093 + 1.1094 + if (end < endLine) { 1.1095 + aBpd->mPrevContent = nullptr; 1.1096 + break; 1.1097 + } 1.1098 + 1.1099 + bool createdContinuation = false; 1.1100 + if (uint32_t(endLine) < text.Length()) { 1.1101 + /* 1.1102 + * Timing is everything here: if the frame already has a bidi 1.1103 + * continuation, we need to make the continuation fluid *before* 1.1104 + * resetting the length of the current frame. Otherwise 1.1105 + * nsTextFrame::SetLength won't set the continuation frame's 1.1106 + * text offsets correctly. 1.1107 + * 1.1108 + * On the other hand, if the frame doesn't have a continuation, 1.1109 + * we need to create one *after* resetting the length, or 1.1110 + * CreateContinuingFrame will complain that there is no more 1.1111 + * content for the continuation. 1.1112 + */ 1.1113 + next = frame->GetNextInFlow(); 1.1114 + if (!next) { 1.1115 + // If the frame already has a bidi continuation, make it fluid 1.1116 + next = frame->GetNextContinuation(); 1.1117 + if (next) { 1.1118 + MakeContinuationFluid(frame, next); 1.1119 + JoinInlineAncestors(frame); 1.1120 + } 1.1121 + } 1.1122 + 1.1123 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame); 1.1124 + textFrame->SetLength(endLine - start, nullptr); 1.1125 + 1.1126 + if (!next) { 1.1127 + // If the frame has no next in flow, create one. 1.1128 + CreateContinuation(frame, &next, true); 1.1129 + createdContinuation = true; 1.1130 + } 1.1131 + aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty(); 1.1132 + } 1.1133 + ResolveParagraphWithinBlock(aBlockFrame, aBpd); 1.1134 + 1.1135 + if (!nextSibling && !createdContinuation) { 1.1136 + break; 1.1137 + } else if (next) { 1.1138 + frame = next; 1.1139 + aBpd->AppendFrame(frame, aLineIter); 1.1140 + } 1.1141 + 1.1142 + /* 1.1143 + * If we have already overshot the saved next-sibling while 1.1144 + * scanning the frame's continuations, advance it. 1.1145 + */ 1.1146 + if (frame && frame == nextSibling) { 1.1147 + nextSibling = frame->GetNextSibling(); 1.1148 + } 1.1149 + 1.1150 + } while (next); 1.1151 + } 1.1152 + } 1.1153 + } else if (nsGkAtoms::brFrame == frameType) { 1.1154 + // break frame -- append line separator 1.1155 + aBpd->AppendUnichar(kLineSeparator); 1.1156 + ResolveParagraphWithinBlock(aBlockFrame, aBpd); 1.1157 + } else { 1.1158 + // other frame type -- see the Unicode Bidi Algorithm: 1.1159 + // "...inline objects (such as graphics) are treated as if they are ... 1.1160 + // U+FFFC" 1.1161 + // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See 1.1162 + // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1 1.1163 + aBpd->AppendUnichar(content->IsHTML(nsGkAtoms::wbr) ? 1.1164 + kZWSP : kObjectSubstitute); 1.1165 + if (!frame->IsInlineOutside()) { 1.1166 + // if it is not inline, end the paragraph 1.1167 + ResolveParagraphWithinBlock(aBlockFrame, aBpd); 1.1168 + } 1.1169 + } 1.1170 + } else { 1.1171 + // For a non-leaf frame, recurse into TraverseFrames 1.1172 + nsIFrame* kid = frame->GetFirstPrincipalChild(); 1.1173 + MOZ_ASSERT(!frame->GetFirstChild(nsIFrame::kOverflowList), 1.1174 + "should have drained the overflow list above"); 1.1175 + if (kid) { 1.1176 + const nsStyleTextReset* text = frame->StyleTextReset(); 1.1177 + if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_ISOLATE || 1.1178 + text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) { 1.1179 + // css "unicode-bidi: isolate" and html5 bdi: 1.1180 + // resolve the element as a separate paragraph 1.1181 + BidiParagraphData* subParagraph = aBpd->GetSubParagraph(); 1.1182 + 1.1183 + /* 1.1184 + * As at the beginning of the loop, it's important to check for 1.1185 + * next-continuations before handling the frame. If we do 1.1186 + * TraverseFrames and *then* do GetNextContinuation on the original 1.1187 + * first frame, it could return a bidi continuation that had only 1.1188 + * just been created, and we would skip doing bidi resolution on the 1.1189 + * last part of the sub-paragraph. 1.1190 + */ 1.1191 + bool isLastContinuation = !frame->GetNextContinuation(); 1.1192 + if (!frame->GetPrevContinuation() || !subParagraph->mReset) { 1.1193 + if (subParagraph->BufferLength()) { 1.1194 + ResolveParagraph(aBlockFrame, subParagraph); 1.1195 + } 1.1196 + subParagraph->Reset(frame, aBpd); 1.1197 + } 1.1198 + TraverseFrames(aBlockFrame, aLineIter, kid, subParagraph); 1.1199 + if (isLastContinuation) { 1.1200 + ResolveParagraph(aBlockFrame, subParagraph); 1.1201 + subParagraph->EmptyBuffer(); 1.1202 + } 1.1203 + 1.1204 + // Treat the element as a neutral character within its containing 1.1205 + // paragraph. 1.1206 + aBpd->AppendControlChar(kObjectSubstitute); 1.1207 + } else { 1.1208 + TraverseFrames(aBlockFrame, aLineIter, kid, aBpd); 1.1209 + } 1.1210 + } 1.1211 + } 1.1212 + 1.1213 + // If the element is attributed by dir, indicate direction pop (add PDF frame) 1.1214 + if (isLastFrame) { 1.1215 + if (ch) { 1.1216 + // Add a dummy frame pointer representing a bidi control code after the 1.1217 + // last frame of an element specifying embedding or override 1.1218 + aBpd->PopBidiControl(); 1.1219 + } 1.1220 + } 1.1221 + childFrame = nextSibling; 1.1222 + } while (childFrame); 1.1223 + 1.1224 + MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer()); 1.1225 +} 1.1226 + 1.1227 +void 1.1228 +nsBidiPresUtils::ResolveParagraphWithinBlock(nsBlockFrame* aBlockFrame, 1.1229 + BidiParagraphData* aBpd) 1.1230 +{ 1.1231 + aBpd->ClearBidiControls(); 1.1232 + ResolveParagraph(aBlockFrame, aBpd); 1.1233 + aBpd->ResetData(); 1.1234 +} 1.1235 + 1.1236 +void 1.1237 +nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine, 1.1238 + int32_t aNumFramesOnLine, 1.1239 + WritingMode aLineWM, 1.1240 + nscoord& aLineWidth) 1.1241 +{ 1.1242 + // If this line consists of a line frame, reorder the line frame's children. 1.1243 + if (aFirstFrameOnLine->GetType() == nsGkAtoms::lineFrame) { 1.1244 + aFirstFrameOnLine = aFirstFrameOnLine->GetFirstPrincipalChild(); 1.1245 + if (!aFirstFrameOnLine) 1.1246 + return; 1.1247 + // All children of the line frame are on the first line. Setting aNumFramesOnLine 1.1248 + // to -1 makes InitLogicalArrayFromLine look at all of them. 1.1249 + aNumFramesOnLine = -1; 1.1250 + } 1.1251 + 1.1252 + BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine); 1.1253 + RepositionInlineFrames(&bld, aFirstFrameOnLine, aLineWM, aLineWidth); 1.1254 +} 1.1255 + 1.1256 +nsIFrame* 1.1257 +nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame) 1.1258 +{ 1.1259 + nsIFrame* firstLeaf = aFrame; 1.1260 + while (!IsBidiLeaf(firstLeaf)) { 1.1261 + nsIFrame* firstChild = firstLeaf->GetFirstPrincipalChild(); 1.1262 + nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild); 1.1263 + firstLeaf = (realFrame->GetType() == nsGkAtoms::letterFrame) ? 1.1264 + realFrame : firstChild; 1.1265 + } 1.1266 + return firstLeaf; 1.1267 +} 1.1268 + 1.1269 +nsBidiLevel 1.1270 +nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame) 1.1271 +{ 1.1272 + return NS_GET_EMBEDDING_LEVEL(nsBidiPresUtils::GetFirstLeaf(aFrame)); 1.1273 +} 1.1274 + 1.1275 +uint8_t 1.1276 +nsBidiPresUtils::GetParagraphDepth(nsIFrame* aFrame) 1.1277 +{ 1.1278 + return NS_GET_PARAGRAPH_DEPTH(nsBidiPresUtils::GetFirstLeaf(aFrame)); 1.1279 +} 1.1280 + 1.1281 + 1.1282 +nsBidiLevel 1.1283 +nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame) 1.1284 +{ 1.1285 + nsIFrame* firstLeaf = aFrame; 1.1286 + while (!IsBidiLeaf(firstLeaf)) { 1.1287 + firstLeaf = firstLeaf->GetFirstPrincipalChild(); 1.1288 + } 1.1289 + return NS_GET_BASE_LEVEL(firstLeaf); 1.1290 +} 1.1291 + 1.1292 +void 1.1293 +nsBidiPresUtils::IsFirstOrLast(nsIFrame* aFrame, 1.1294 + nsContinuationStates* aContinuationStates, 1.1295 + bool& aIsFirst /* out */, 1.1296 + bool& aIsLast /* out */) 1.1297 +{ 1.1298 + /* 1.1299 + * Since we lay out frames in the line's direction, visiting a frame with 1.1300 + * 'mFirstVisualFrame == nullptr', means it's the first appearance of one 1.1301 + * of its continuation chain frames on the line. 1.1302 + * To determine if it's the last visual frame of its continuation chain on 1.1303 + * the line or not, we count the number of frames of the chain on the line, 1.1304 + * and then reduce it when we lay out a frame of the chain. If this value 1.1305 + * becomes 1 it means that it's the last visual frame of its continuation 1.1306 + * chain on this line. 1.1307 + */ 1.1308 + 1.1309 + nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame); 1.1310 + nsFrameContinuationState* firstFrameState; 1.1311 + 1.1312 + if (!frameState->mFirstVisualFrame) { 1.1313 + // aFrame is the first visual frame of its continuation chain 1.1314 + nsFrameContinuationState* contState; 1.1315 + nsIFrame* frame; 1.1316 + 1.1317 + frameState->mFrameCount = 1; 1.1318 + frameState->mFirstVisualFrame = aFrame; 1.1319 + 1.1320 + /** 1.1321 + * Traverse continuation chain of aFrame in both backward and forward 1.1322 + * directions while the frames are on this line. Count the frames and 1.1323 + * set their mFirstVisualFrame to aFrame. 1.1324 + */ 1.1325 + // Traverse continuation chain backward 1.1326 + for (frame = aFrame->GetPrevContinuation(); 1.1327 + frame && (contState = aContinuationStates->GetEntry(frame)); 1.1328 + frame = frame->GetPrevContinuation()) { 1.1329 + frameState->mFrameCount++; 1.1330 + contState->mFirstVisualFrame = aFrame; 1.1331 + } 1.1332 + frameState->mHasContOnPrevLines = (frame != nullptr); 1.1333 + 1.1334 + // Traverse continuation chain forward 1.1335 + for (frame = aFrame->GetNextContinuation(); 1.1336 + frame && (contState = aContinuationStates->GetEntry(frame)); 1.1337 + frame = frame->GetNextContinuation()) { 1.1338 + frameState->mFrameCount++; 1.1339 + contState->mFirstVisualFrame = aFrame; 1.1340 + } 1.1341 + frameState->mHasContOnNextLines = (frame != nullptr); 1.1342 + 1.1343 + aIsFirst = !frameState->mHasContOnPrevLines; 1.1344 + firstFrameState = frameState; 1.1345 + } else { 1.1346 + // aFrame is not the first visual frame of its continuation chain 1.1347 + aIsFirst = false; 1.1348 + firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame); 1.1349 + } 1.1350 + 1.1351 + aIsLast = (firstFrameState->mFrameCount == 1 && 1.1352 + !firstFrameState->mHasContOnNextLines); 1.1353 + 1.1354 + if ((aIsFirst || aIsLast) && 1.1355 + (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) { 1.1356 + // For ib splits, don't treat anything except the last part as 1.1357 + // endmost or anything except the first part as startmost. 1.1358 + // As an optimization, only get the first continuation once. 1.1359 + nsIFrame* firstContinuation = aFrame->FirstContinuation(); 1.1360 + if (firstContinuation->FrameIsNonLastInIBSplit()) { 1.1361 + // We are not endmost 1.1362 + aIsLast = false; 1.1363 + } 1.1364 + if (firstContinuation->FrameIsNonFirstInIBSplit()) { 1.1365 + // We are not startmost 1.1366 + aIsFirst = false; 1.1367 + } 1.1368 + } 1.1369 + 1.1370 + // Reduce number of remaining frames of the continuation chain on the line. 1.1371 + firstFrameState->mFrameCount--; 1.1372 +} 1.1373 + 1.1374 +void 1.1375 +nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame, 1.1376 + bool aIsEvenLevel, 1.1377 + nscoord& aStart, 1.1378 + nsContinuationStates* aContinuationStates, 1.1379 + WritingMode aLineWM, 1.1380 + nscoord& aLineWidth) 1.1381 +{ 1.1382 + if (!aFrame) 1.1383 + return; 1.1384 + 1.1385 + bool isFirst, isLast; 1.1386 + IsFirstOrLast(aFrame, 1.1387 + aContinuationStates, 1.1388 + isFirst /* out */, 1.1389 + isLast /* out */); 1.1390 + 1.1391 + WritingMode frameWM = aFrame->GetWritingMode(); 1.1392 + nsInlineFrame* testFrame = do_QueryFrame(aFrame); 1.1393 + 1.1394 + if (testFrame) { 1.1395 + aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET); 1.1396 + 1.1397 + if (isFirst) { 1.1398 + aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST); 1.1399 + } else { 1.1400 + aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST); 1.1401 + } 1.1402 + 1.1403 + if (isLast) { 1.1404 + aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST); 1.1405 + } else { 1.1406 + aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST); 1.1407 + } 1.1408 + } 1.1409 + // This method is called from nsBlockFrame::PlaceLine via the call to 1.1410 + // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines 1.1411 + // have been reflowed, which is required for GetUsedMargin/Border/Padding 1.1412 + LogicalMargin margin(frameWM, aFrame->GetUsedMargin()); 1.1413 + if (isFirst) { 1.1414 + aStart += margin.IStart(frameWM); 1.1415 + } 1.1416 + 1.1417 + nscoord start = aStart; 1.1418 + nscoord frameWidth = aFrame->GetSize().width; 1.1419 + 1.1420 + if (!IsBidiLeaf(aFrame)) 1.1421 + { 1.1422 + nscoord iCoord = 0; 1.1423 + LogicalMargin borderPadding(frameWM, aFrame->GetUsedBorderAndPadding()); 1.1424 + if (isFirst) { 1.1425 + iCoord += borderPadding.IStart(frameWM); 1.1426 + } 1.1427 + 1.1428 + // If the resolved direction of the container is different from the 1.1429 + // direction of the frame, we need to traverse the child list in reverse 1.1430 + // order, to make it O(n) we store the list locally and iterate the list 1.1431 + // in reverse 1.1432 + bool reverseOrder = aIsEvenLevel != frameWM.IsBidiLTR(); 1.1433 + nsTArray<nsIFrame*> childList; 1.1434 + nsIFrame *frame = aFrame->GetFirstPrincipalChild(); 1.1435 + if (frame && reverseOrder) { 1.1436 + childList.AppendElement((nsIFrame*)nullptr); 1.1437 + while (frame) { 1.1438 + childList.AppendElement(frame); 1.1439 + frame = frame->GetNextSibling(); 1.1440 + } 1.1441 + frame = childList[childList.Length() - 1]; 1.1442 + } 1.1443 + 1.1444 + // Reposition the child frames 1.1445 + int32_t index = 0; 1.1446 + while (frame) { 1.1447 + RepositionFrame(frame, 1.1448 + aIsEvenLevel, 1.1449 + iCoord, 1.1450 + aContinuationStates, 1.1451 + frameWM, 1.1452 + frameWidth); 1.1453 + index++; 1.1454 + frame = reverseOrder ? 1.1455 + childList[childList.Length() - index - 1] : 1.1456 + frame->GetNextSibling(); 1.1457 + } 1.1458 + 1.1459 + if (isLast) { 1.1460 + iCoord += borderPadding.IEnd(frameWM); 1.1461 + } 1.1462 + aStart += iCoord; 1.1463 + } else { 1.1464 + aStart += frameWidth; 1.1465 + } 1.1466 + 1.1467 + LogicalRect logicalRect(aLineWM, aFrame->GetRect(), aLineWidth); 1.1468 + logicalRect.IStart(aLineWM) = start; 1.1469 + logicalRect.ISize(aLineWM) = aStart - start; 1.1470 + aFrame->SetRect(aLineWM, logicalRect, aLineWidth); 1.1471 + 1.1472 + if (isLast) { 1.1473 + aStart += margin.IEnd(frameWM); 1.1474 + } 1.1475 +} 1.1476 + 1.1477 +void 1.1478 +nsBidiPresUtils::InitContinuationStates(nsIFrame* aFrame, 1.1479 + nsContinuationStates* aContinuationStates) 1.1480 +{ 1.1481 + nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame); 1.1482 + state->mFirstVisualFrame = nullptr; 1.1483 + state->mFrameCount = 0; 1.1484 + 1.1485 + if (!IsBidiLeaf(aFrame)) { 1.1486 + // Continue for child frames 1.1487 + nsIFrame* frame; 1.1488 + for (frame = aFrame->GetFirstPrincipalChild(); 1.1489 + frame; 1.1490 + frame = frame->GetNextSibling()) { 1.1491 + InitContinuationStates(frame, 1.1492 + aContinuationStates); 1.1493 + } 1.1494 + } 1.1495 +} 1.1496 + 1.1497 +void 1.1498 +nsBidiPresUtils::RepositionInlineFrames(BidiLineData *aBld, 1.1499 + nsIFrame* aFirstChild, 1.1500 + WritingMode aLineWM, 1.1501 + nscoord& aLineWidth) 1.1502 +{ 1.1503 + nscoord startSpace = 0; 1.1504 + 1.1505 + // This method is called from nsBlockFrame::PlaceLine via the call to 1.1506 + // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines 1.1507 + // have been reflowed, which is required for GetUsedMargin/Border/Padding 1.1508 + WritingMode frameWM = aFirstChild->GetWritingMode(); 1.1509 + LogicalMargin margin(frameWM, aFirstChild->GetUsedMargin()); 1.1510 + if (!aFirstChild->GetPrevContinuation() && 1.1511 + !aFirstChild->FrameIsNonFirstInIBSplit()) 1.1512 + startSpace = margin.IStart(frameWM); 1.1513 + 1.1514 + nscoord start = LogicalRect(aLineWM, aFirstChild->GetRect(), 1.1515 + aLineWidth).IStart(aLineWM) - startSpace; 1.1516 + nsIFrame* frame; 1.1517 + int32_t count = aBld->mVisualFrames.Length(); 1.1518 + int32_t index; 1.1519 + nsContinuationStates continuationStates; 1.1520 + 1.1521 + // Initialize continuation states to (nullptr, 0) for 1.1522 + // each frame on the line. 1.1523 + for (index = 0; index < count; index++) { 1.1524 + InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates); 1.1525 + } 1.1526 + 1.1527 + // Reposition frames in visual order 1.1528 + int32_t step, limit; 1.1529 + if (aLineWM.IsBidiLTR()) { 1.1530 + index = 0; 1.1531 + step = 1; 1.1532 + limit = count; 1.1533 + } else { 1.1534 + index = count - 1; 1.1535 + step = -1; 1.1536 + limit = -1; 1.1537 + } 1.1538 + for (; index != limit; index += step) { 1.1539 + frame = aBld->VisualFrameAt(index); 1.1540 + RepositionFrame(frame, 1.1541 + !(aBld->mLevels[aBld->mIndexMap[index]] & 1), 1.1542 + start, 1.1543 + &continuationStates, 1.1544 + aLineWM, 1.1545 + aLineWidth); 1.1546 + } 1.1547 +} 1.1548 + 1.1549 +bool 1.1550 +nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine, 1.1551 + int32_t aNumFramesOnLine, 1.1552 + nsIFrame** aFirstVisual, 1.1553 + nsIFrame** aLastVisual) 1.1554 +{ 1.1555 + BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine); 1.1556 + int32_t count = bld.FrameCount(); 1.1557 + 1.1558 + if (aFirstVisual) { 1.1559 + *aFirstVisual = bld.VisualFrameAt(0); 1.1560 + } 1.1561 + if (aLastVisual) { 1.1562 + *aLastVisual = bld.VisualFrameAt(count-1); 1.1563 + } 1.1564 + 1.1565 + return bld.mIsReordered; 1.1566 +} 1.1567 + 1.1568 +nsIFrame* 1.1569 +nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame, 1.1570 + nsIFrame* aFirstFrameOnLine, 1.1571 + int32_t aNumFramesOnLine) 1.1572 +{ 1.1573 + BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine); 1.1574 + 1.1575 + int32_t count = bld.mVisualFrames.Length(); 1.1576 + 1.1577 + if (aFrame == nullptr && count) 1.1578 + return bld.VisualFrameAt(0); 1.1579 + 1.1580 + for (int32_t i = 0; i < count - 1; i++) { 1.1581 + if (bld.VisualFrameAt(i) == aFrame) { 1.1582 + return bld.VisualFrameAt(i+1); 1.1583 + } 1.1584 + } 1.1585 + 1.1586 + return nullptr; 1.1587 +} 1.1588 + 1.1589 +nsIFrame* 1.1590 +nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame, 1.1591 + nsIFrame* aFirstFrameOnLine, 1.1592 + int32_t aNumFramesOnLine) 1.1593 +{ 1.1594 + BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine); 1.1595 + 1.1596 + int32_t count = bld.mVisualFrames.Length(); 1.1597 + 1.1598 + if (aFrame == nullptr && count) 1.1599 + return bld.VisualFrameAt(count-1); 1.1600 + 1.1601 + for (int32_t i = 1; i < count; i++) { 1.1602 + if (bld.VisualFrameAt(i) == aFrame) { 1.1603 + return bld.VisualFrameAt(i-1); 1.1604 + } 1.1605 + } 1.1606 + 1.1607 + return nullptr; 1.1608 +} 1.1609 + 1.1610 +inline nsresult 1.1611 +nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame, 1.1612 + nsIFrame** aNewFrame, 1.1613 + int32_t& aFrameIndex, 1.1614 + int32_t aStart, 1.1615 + int32_t aEnd) 1.1616 +{ 1.1617 + NS_PRECONDITION(aNewFrame, "null OUT ptr"); 1.1618 + NS_PRECONDITION(aFrame, "aFrame is null"); 1.1619 + 1.1620 + aFrame->AdjustOffsetsForBidi(aStart, aEnd); 1.1621 + return CreateContinuation(aFrame, aNewFrame, false); 1.1622 +} 1.1623 + 1.1624 +void 1.1625 +nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd, 1.1626 + nsIFrame* aFrame, 1.1627 + int32_t aFirstIndex, 1.1628 + int32_t aLastIndex, 1.1629 + int32_t& aOffset) 1.1630 +{ 1.1631 + FrameProperties props = aFrame->Properties(); 1.1632 + nsBidiLevel embeddingLevel = 1.1633 + (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::EmbeddingLevelProperty())); 1.1634 + nsBidiLevel baseLevel = 1.1635 + (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::BaseLevelProperty())); 1.1636 + uint8_t paragraphDepth = 1.1637 + NS_PTR_TO_INT32(props.Get(nsIFrame::ParagraphDepthProperty())); 1.1638 + 1.1639 + for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) { 1.1640 + nsIFrame* frame = aBpd->FrameAt(index); 1.1641 + if (frame == NS_BIDI_CONTROL_FRAME) { 1.1642 + ++aOffset; 1.1643 + } 1.1644 + else { 1.1645 + // Make the frame and its continuation ancestors fluid, 1.1646 + // so they can be reused or deleted by normal reflow code 1.1647 + FrameProperties frameProps = frame->Properties(); 1.1648 + frameProps.Set(nsIFrame::EmbeddingLevelProperty(), 1.1649 + NS_INT32_TO_PTR(embeddingLevel)); 1.1650 + frameProps.Set(nsIFrame::BaseLevelProperty(), 1.1651 + NS_INT32_TO_PTR(baseLevel)); 1.1652 + frameProps.Set(nsIFrame::ParagraphDepthProperty(), 1.1653 + NS_INT32_TO_PTR(paragraphDepth)); 1.1654 + frame->AddStateBits(NS_FRAME_IS_BIDI); 1.1655 + while (frame) { 1.1656 + nsIFrame* prev = frame->GetPrevContinuation(); 1.1657 + if (prev) { 1.1658 + MakeContinuationFluid(prev, frame); 1.1659 + frame = frame->GetParent(); 1.1660 + } else { 1.1661 + break; 1.1662 + } 1.1663 + } 1.1664 + } 1.1665 + } 1.1666 + 1.1667 + // Make sure that the last continuation we made fluid does not itself have a 1.1668 + // fluid continuation (this can happen when re-resolving after dynamic changes 1.1669 + // to content) 1.1670 + nsIFrame* lastFrame = aBpd->FrameAt(aLastIndex); 1.1671 + MakeContinuationsNonFluidUpParentChain(lastFrame, lastFrame->GetNextInFlow()); 1.1672 +} 1.1673 + 1.1674 +nsresult 1.1675 +nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext, 1.1676 + char16_t* aText, 1.1677 + int32_t& aTextLength, 1.1678 + nsCharType aCharType, 1.1679 + bool aIsOddLevel) 1.1680 +{ 1.1681 + nsresult rv = NS_OK; 1.1682 + // ahmed 1.1683 + //adjusted for correct numeral shaping 1.1684 + uint32_t bidiOptions = aPresContext->GetBidi(); 1.1685 + switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) { 1.1686 + 1.1687 + case IBMBIDI_NUMERAL_HINDI: 1.1688 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI); 1.1689 + break; 1.1690 + 1.1691 + case IBMBIDI_NUMERAL_ARABIC: 1.1692 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC); 1.1693 + break; 1.1694 + 1.1695 + case IBMBIDI_NUMERAL_PERSIAN: 1.1696 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN); 1.1697 + break; 1.1698 + 1.1699 + case IBMBIDI_NUMERAL_REGULAR: 1.1700 + 1.1701 + switch (aCharType) { 1.1702 + 1.1703 + case eCharType_EuropeanNumber: 1.1704 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC); 1.1705 + break; 1.1706 + 1.1707 + case eCharType_ArabicNumber: 1.1708 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI); 1.1709 + break; 1.1710 + 1.1711 + default: 1.1712 + break; 1.1713 + } 1.1714 + break; 1.1715 + 1.1716 + case IBMBIDI_NUMERAL_HINDICONTEXT: 1.1717 + if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) ) 1.1718 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI); 1.1719 + else if (eCharType_EuropeanNumber == aCharType) 1.1720 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC); 1.1721 + break; 1.1722 + 1.1723 + case IBMBIDI_NUMERAL_PERSIANCONTEXT: 1.1724 + if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) ) 1.1725 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN); 1.1726 + else if (eCharType_EuropeanNumber == aCharType) 1.1727 + HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC); 1.1728 + break; 1.1729 + 1.1730 + case IBMBIDI_NUMERAL_NOMINAL: 1.1731 + default: 1.1732 + break; 1.1733 + } 1.1734 + 1.1735 + StripBidiControlCharacters(aText, aTextLength); 1.1736 + return rv; 1.1737 +} 1.1738 + 1.1739 +void 1.1740 +nsBidiPresUtils::StripBidiControlCharacters(char16_t* aText, 1.1741 + int32_t& aTextLength) 1.1742 +{ 1.1743 + if ( (nullptr == aText) || (aTextLength < 1) ) { 1.1744 + return; 1.1745 + } 1.1746 + 1.1747 + int32_t stripLen = 0; 1.1748 + 1.1749 + for (int32_t i = 0; i < aTextLength; i++) { 1.1750 + // XXX: This silently ignores surrogate characters. 1.1751 + // As of Unicode 4.0, all Bidi control characters are within the BMP. 1.1752 + if (IsBidiControl((uint32_t)aText[i])) { 1.1753 + ++stripLen; 1.1754 + } 1.1755 + else { 1.1756 + aText[i - stripLen] = aText[i]; 1.1757 + } 1.1758 + } 1.1759 + aTextLength -= stripLen; 1.1760 +} 1.1761 + 1.1762 +#if 0 // XXX: for the future use ??? 1.1763 +void 1.1764 +RemoveDiacritics(char16_t* aText, 1.1765 + int32_t& aTextLength) 1.1766 +{ 1.1767 + if (aText && (aTextLength > 0) ) { 1.1768 + int32_t offset = 0; 1.1769 + 1.1770 + for (int32_t i = 0; i < aTextLength && aText[i]; i++) { 1.1771 + if (IS_BIDI_DIACRITIC(aText[i]) ) { 1.1772 + ++offset; 1.1773 + continue; 1.1774 + } 1.1775 + aText[i - offset] = aText[i]; 1.1776 + } 1.1777 + aTextLength = i - offset; 1.1778 + aText[aTextLength] = 0; 1.1779 + } 1.1780 +} 1.1781 +#endif 1.1782 + 1.1783 +void 1.1784 +nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine, 1.1785 + const char16_t* aText, 1.1786 + int32_t& aOffset, 1.1787 + int32_t aCharTypeLimit, 1.1788 + int32_t& aRunLimit, 1.1789 + int32_t& aRunLength, 1.1790 + int32_t& aRunCount, 1.1791 + uint8_t& aCharType, 1.1792 + uint8_t& aPrevCharType) 1.1793 + 1.1794 +{ 1.1795 + bool strongTypeFound = false; 1.1796 + int32_t offset; 1.1797 + nsCharType charType; 1.1798 + 1.1799 + aCharType = eCharType_OtherNeutral; 1.1800 + 1.1801 + for (offset = aOffset; offset < aCharTypeLimit; offset++) { 1.1802 + // Make sure we give RTL chartype to all characters that would be classified 1.1803 + // as Right-To-Left by a bidi platform. 1.1804 + // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.) 1.1805 + if (IS_HEBREW_CHAR(aText[offset]) ) { 1.1806 + charType = eCharType_RightToLeft; 1.1807 + } 1.1808 + else if (IS_ARABIC_ALPHABETIC(aText[offset]) ) { 1.1809 + charType = eCharType_RightToLeftArabic; 1.1810 + } 1.1811 + else { 1.1812 + aBidiEngine->GetCharTypeAt(offset, &charType); 1.1813 + } 1.1814 + 1.1815 + if (!CHARTYPE_IS_WEAK(charType) ) { 1.1816 + 1.1817 + if (strongTypeFound 1.1818 + && (charType != aPrevCharType) 1.1819 + && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) { 1.1820 + // Stop at this point to ensure uni-directionality of the text 1.1821 + // (from platform's point of view). 1.1822 + // Also, don't mix Arabic and Hebrew content (since platform may 1.1823 + // provide BIDI support to one of them only). 1.1824 + aRunLength = offset - aOffset; 1.1825 + aRunLimit = offset; 1.1826 + ++aRunCount; 1.1827 + break; 1.1828 + } 1.1829 + 1.1830 + if ( (eCharType_RightToLeftArabic == aPrevCharType 1.1831 + || eCharType_ArabicNumber == aPrevCharType) 1.1832 + && eCharType_EuropeanNumber == charType) { 1.1833 + charType = eCharType_ArabicNumber; 1.1834 + } 1.1835 + 1.1836 + // Set PrevCharType to the last strong type in this frame 1.1837 + // (for correct numeric shaping) 1.1838 + aPrevCharType = charType; 1.1839 + 1.1840 + strongTypeFound = true; 1.1841 + aCharType = charType; 1.1842 + } 1.1843 + } 1.1844 + aOffset = offset; 1.1845 +} 1.1846 + 1.1847 +nsresult nsBidiPresUtils::ProcessText(const char16_t* aText, 1.1848 + int32_t aLength, 1.1849 + nsBidiLevel aBaseLevel, 1.1850 + nsPresContext* aPresContext, 1.1851 + BidiProcessor& aprocessor, 1.1852 + Mode aMode, 1.1853 + nsBidiPositionResolve* aPosResolve, 1.1854 + int32_t aPosResolveCount, 1.1855 + nscoord* aWidth, 1.1856 + nsBidi* aBidiEngine) 1.1857 +{ 1.1858 + NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments"); 1.1859 + 1.1860 + int32_t runCount; 1.1861 + 1.1862 + nsAutoString textBuffer(aText, aLength); 1.1863 + 1.1864 + nsresult rv = aBidiEngine->SetPara(aText, aLength, aBaseLevel, nullptr); 1.1865 + if (NS_FAILED(rv)) 1.1866 + return rv; 1.1867 + 1.1868 + rv = aBidiEngine->CountRuns(&runCount); 1.1869 + if (NS_FAILED(rv)) 1.1870 + return rv; 1.1871 + 1.1872 + nscoord xOffset = 0; 1.1873 + nscoord width, xEndRun = 0; 1.1874 + nscoord totalWidth = 0; 1.1875 + int32_t i, start, limit, length; 1.1876 + uint32_t visualStart = 0; 1.1877 + uint8_t charType; 1.1878 + uint8_t prevType = eCharType_LeftToRight; 1.1879 + nsBidiLevel level; 1.1880 + 1.1881 + for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve) 1.1882 + { 1.1883 + aPosResolve[nPosResolve].visualIndex = kNotFound; 1.1884 + aPosResolve[nPosResolve].visualLeftTwips = kNotFound; 1.1885 + aPosResolve[nPosResolve].visualWidth = kNotFound; 1.1886 + } 1.1887 + 1.1888 + for (i = 0; i < runCount; i++) { 1.1889 + nsBidiDirection dir; 1.1890 + rv = aBidiEngine->GetVisualRun(i, &start, &length, &dir); 1.1891 + if (NS_FAILED(rv)) 1.1892 + return rv; 1.1893 + 1.1894 + rv = aBidiEngine->GetLogicalRun(start, &limit, &level); 1.1895 + if (NS_FAILED(rv)) 1.1896 + return rv; 1.1897 + 1.1898 + int32_t subRunLength = limit - start; 1.1899 + int32_t lineOffset = start; 1.1900 + int32_t typeLimit = std::min(limit, aLength); 1.1901 + int32_t subRunCount = 1; 1.1902 + int32_t subRunLimit = typeLimit; 1.1903 + 1.1904 + /* 1.1905 + * If |level| is even, i.e. the direction of the run is left-to-right, we 1.1906 + * render the subruns from left to right and increment the x-coordinate 1.1907 + * |xOffset| by the width of each subrun after rendering. 1.1908 + * 1.1909 + * If |level| is odd, i.e. the direction of the run is right-to-left, we 1.1910 + * render the subruns from right to left. We begin by incrementing |xOffset| by 1.1911 + * the width of the whole run, and then decrement it by the width of each 1.1912 + * subrun before rendering. After rendering all the subruns, we restore the 1.1913 + * x-coordinate of the end of the run for the start of the next run. 1.1914 + */ 1.1915 + 1.1916 + if (level & 1) { 1.1917 + aprocessor.SetText(aText + start, subRunLength, nsBidiDirection(level & 1)); 1.1918 + width = aprocessor.GetWidth(); 1.1919 + xOffset += width; 1.1920 + xEndRun = xOffset; 1.1921 + } 1.1922 + 1.1923 + while (subRunCount > 0) { 1.1924 + // CalculateCharType can increment subRunCount if the run 1.1925 + // contains mixed character types 1.1926 + CalculateCharType(aBidiEngine, aText, lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType); 1.1927 + 1.1928 + nsAutoString runVisualText; 1.1929 + runVisualText.Assign(aText + start, subRunLength); 1.1930 + if (int32_t(runVisualText.Length()) < subRunLength) 1.1931 + return NS_ERROR_OUT_OF_MEMORY; 1.1932 + FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength, 1.1933 + (nsCharType)charType, level & 1); 1.1934 + 1.1935 + aprocessor.SetText(runVisualText.get(), subRunLength, nsBidiDirection(level & 1)); 1.1936 + width = aprocessor.GetWidth(); 1.1937 + totalWidth += width; 1.1938 + if (level & 1) { 1.1939 + xOffset -= width; 1.1940 + } 1.1941 + if (aMode == MODE_DRAW) { 1.1942 + aprocessor.DrawText(xOffset, width); 1.1943 + } 1.1944 + 1.1945 + /* 1.1946 + * The caller may request to calculate the visual position of one 1.1947 + * or more characters. 1.1948 + */ 1.1949 + for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve) 1.1950 + { 1.1951 + nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve]; 1.1952 + /* 1.1953 + * Did we already resolve this position's visual metric? If so, skip. 1.1954 + */ 1.1955 + if (posResolve->visualLeftTwips != kNotFound) 1.1956 + continue; 1.1957 + 1.1958 + /* 1.1959 + * First find out if the logical position is within this run. 1.1960 + */ 1.1961 + if (start <= posResolve->logicalIndex && 1.1962 + start + subRunLength > posResolve->logicalIndex) { 1.1963 + /* 1.1964 + * If this run is only one character long, we have an easy case: 1.1965 + * the visual position is the x-coord of the start of the run 1.1966 + * less the x-coord of the start of the whole text. 1.1967 + */ 1.1968 + if (subRunLength == 1) { 1.1969 + posResolve->visualIndex = visualStart; 1.1970 + posResolve->visualLeftTwips = xOffset; 1.1971 + posResolve->visualWidth = width; 1.1972 + } 1.1973 + /* 1.1974 + * Otherwise, we need to measure the width of the run's part 1.1975 + * which is to the visual left of the index. 1.1976 + * In other words, the run is broken in two, around the logical index, 1.1977 + * and we measure the part which is visually left. 1.1978 + * If the run is right-to-left, this part will span from after the index 1.1979 + * up to the end of the run; if it is left-to-right, this part will span 1.1980 + * from the start of the run up to (and inclduing) the character before the index. 1.1981 + */ 1.1982 + else { 1.1983 + /* 1.1984 + * Here is a description of how the width of the current character 1.1985 + * (posResolve->visualWidth) is calculated: 1.1986 + * 1.1987 + * LTR (current char: "P"): 1.1988 + * S A M P L E (logical index: 3, visual index: 3) 1.1989 + * ^ (visualLeftPart) 1.1990 + * ^ (visualRightSide) 1.1991 + * visualLeftLength == 3 1.1992 + * ^^^^^^ (subWidth) 1.1993 + * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide) 1.1994 + * ^^ (posResolve->visualWidth) 1.1995 + * 1.1996 + * RTL (current char: "M"): 1.1997 + * E L P M A S (logical index: 2, visual index: 3) 1.1998 + * ^ (visualLeftPart) 1.1999 + * ^ (visualRightSide) 1.2000 + * visualLeftLength == 3 1.2001 + * ^^^^^^ (subWidth) 1.2002 + * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide) 1.2003 + * ^^ (posResolve->visualWidth) 1.2004 + */ 1.2005 + nscoord subWidth; 1.2006 + // The position in the text where this run's "left part" begins. 1.2007 + const char16_t* visualLeftPart, *visualRightSide; 1.2008 + if (level & 1) { 1.2009 + // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ... 1.2010 + posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start)); 1.2011 + // Skipping to the "left part". 1.2012 + visualLeftPart = aText + posResolve->logicalIndex + 1; 1.2013 + // Skipping to the right side of the current character 1.2014 + visualRightSide = visualLeftPart - 1; 1.2015 + } 1.2016 + else { 1.2017 + posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start); 1.2018 + // Skipping to the "left part". 1.2019 + visualLeftPart = aText + start; 1.2020 + // In LTR mode this is the same as visualLeftPart 1.2021 + visualRightSide = visualLeftPart; 1.2022 + } 1.2023 + // The delta between the start of the run and the left part's end. 1.2024 + int32_t visualLeftLength = posResolve->visualIndex - visualStart; 1.2025 + aprocessor.SetText(visualLeftPart, visualLeftLength, nsBidiDirection(level & 1)); 1.2026 + subWidth = aprocessor.GetWidth(); 1.2027 + aprocessor.SetText(visualRightSide, visualLeftLength + 1, nsBidiDirection(level & 1)); 1.2028 + posResolve->visualLeftTwips = xOffset + subWidth; 1.2029 + posResolve->visualWidth = aprocessor.GetWidth() - subWidth; 1.2030 + } 1.2031 + } 1.2032 + } 1.2033 + 1.2034 + if (!(level & 1)) { 1.2035 + xOffset += width; 1.2036 + } 1.2037 + 1.2038 + --subRunCount; 1.2039 + start = lineOffset; 1.2040 + subRunLimit = typeLimit; 1.2041 + subRunLength = typeLimit - lineOffset; 1.2042 + } // while 1.2043 + if (level & 1) { 1.2044 + xOffset = xEndRun; 1.2045 + } 1.2046 + 1.2047 + visualStart += length; 1.2048 + } // for 1.2049 + 1.2050 + if (aWidth) { 1.2051 + *aWidth = totalWidth; 1.2052 + } 1.2053 + return NS_OK; 1.2054 +} 1.2055 + 1.2056 +class MOZ_STACK_CLASS nsIRenderingContextBidiProcessor : public nsBidiPresUtils::BidiProcessor { 1.2057 +public: 1.2058 + nsIRenderingContextBidiProcessor(nsRenderingContext* aCtx, 1.2059 + nsRenderingContext* aTextRunConstructionContext, 1.2060 + const nsPoint& aPt) 1.2061 + : mCtx(aCtx), mTextRunConstructionContext(aTextRunConstructionContext), mPt(aPt) { } 1.2062 + 1.2063 + ~nsIRenderingContextBidiProcessor() 1.2064 + { 1.2065 + mCtx->SetTextRunRTL(false); 1.2066 + } 1.2067 + 1.2068 + virtual void SetText(const char16_t* aText, 1.2069 + int32_t aLength, 1.2070 + nsBidiDirection aDirection) MOZ_OVERRIDE 1.2071 + { 1.2072 + mTextRunConstructionContext->SetTextRunRTL(aDirection==NSBIDI_RTL); 1.2073 + mText = aText; 1.2074 + mLength = aLength; 1.2075 + } 1.2076 + 1.2077 + virtual nscoord GetWidth() MOZ_OVERRIDE 1.2078 + { 1.2079 + return mTextRunConstructionContext->GetWidth(mText, mLength); 1.2080 + } 1.2081 + 1.2082 + virtual void DrawText(nscoord aXOffset, 1.2083 + nscoord) MOZ_OVERRIDE 1.2084 + { 1.2085 + mCtx->FontMetrics()->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y, 1.2086 + mCtx, mTextRunConstructionContext); 1.2087 + } 1.2088 + 1.2089 +private: 1.2090 + nsRenderingContext* mCtx; 1.2091 + nsRenderingContext* mTextRunConstructionContext; 1.2092 + nsPoint mPt; 1.2093 + const char16_t* mText; 1.2094 + int32_t mLength; 1.2095 +}; 1.2096 + 1.2097 +nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const char16_t* aText, 1.2098 + int32_t aLength, 1.2099 + nsBidiLevel aBaseLevel, 1.2100 + nsPresContext* aPresContext, 1.2101 + nsRenderingContext& aRenderingContext, 1.2102 + nsRenderingContext& aTextRunConstructionContext, 1.2103 + Mode aMode, 1.2104 + nscoord aX, 1.2105 + nscoord aY, 1.2106 + nsBidiPositionResolve* aPosResolve, 1.2107 + int32_t aPosResolveCount, 1.2108 + nscoord* aWidth) 1.2109 +{ 1.2110 + nsIRenderingContextBidiProcessor processor(&aRenderingContext, &aTextRunConstructionContext, nsPoint(aX, aY)); 1.2111 + nsBidi bidiEngine; 1.2112 + return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor, 1.2113 + aMode, aPosResolve, aPosResolveCount, aWidth, &bidiEngine); 1.2114 +} 1.2115 + 1.2116 +/* static */ 1.2117 +void nsBidiPresUtils::WriteReverse(const char16_t* aSrc, 1.2118 + uint32_t aSrcLength, 1.2119 + char16_t* aDest) 1.2120 +{ 1.2121 + char16_t* dest = aDest + aSrcLength; 1.2122 + mozilla::unicode::ClusterIterator iter(aSrc, aSrcLength); 1.2123 + 1.2124 + while (!iter.AtEnd()) { 1.2125 + iter.Next(); 1.2126 + for (const char16_t *cp = iter; cp > aSrc; ) { 1.2127 + // Here we rely on the fact that there are no non-BMP mirrored pairs 1.2128 + // currently in Unicode, so we don't need to look for surrogates 1.2129 + *--dest = mozilla::unicode::GetMirroredChar(*--cp); 1.2130 + } 1.2131 + aSrc = iter; 1.2132 + } 1.2133 + 1.2134 + NS_ASSERTION(dest == aDest, "Whole string not copied"); 1.2135 +} 1.2136 + 1.2137 +/* static */ 1.2138 +bool nsBidiPresUtils::WriteLogicalToVisual(const char16_t* aSrc, 1.2139 + uint32_t aSrcLength, 1.2140 + char16_t* aDest, 1.2141 + nsBidiLevel aBaseDirection, 1.2142 + nsBidi* aBidiEngine) 1.2143 +{ 1.2144 + const char16_t* src = aSrc; 1.2145 + nsresult rv = aBidiEngine->SetPara(src, aSrcLength, aBaseDirection, nullptr); 1.2146 + if (NS_FAILED(rv)) { 1.2147 + return false; 1.2148 + } 1.2149 + 1.2150 + nsBidiDirection dir; 1.2151 + rv = aBidiEngine->GetDirection(&dir); 1.2152 + // NSBIDI_LTR returned from GetDirection means the whole text is LTR 1.2153 + if (NS_FAILED(rv) || dir == NSBIDI_LTR) { 1.2154 + return false; 1.2155 + } 1.2156 + 1.2157 + int32_t runCount; 1.2158 + rv = aBidiEngine->CountRuns(&runCount); 1.2159 + if (NS_FAILED(rv)) { 1.2160 + return false; 1.2161 + } 1.2162 + 1.2163 + int32_t runIndex, start, length; 1.2164 + char16_t* dest = aDest; 1.2165 + 1.2166 + for (runIndex = 0; runIndex < runCount; ++runIndex) { 1.2167 + rv = aBidiEngine->GetVisualRun(runIndex, &start, &length, &dir); 1.2168 + if (NS_FAILED(rv)) { 1.2169 + return false; 1.2170 + } 1.2171 + 1.2172 + src = aSrc + start; 1.2173 + 1.2174 + if (dir == NSBIDI_RTL) { 1.2175 + WriteReverse(src, length, dest); 1.2176 + dest += length; 1.2177 + } else { 1.2178 + do { 1.2179 + NS_ASSERTION(src >= aSrc && src < aSrc + aSrcLength, 1.2180 + "logical index out of range"); 1.2181 + NS_ASSERTION(dest < aDest + aSrcLength, "visual index out of range"); 1.2182 + *(dest++) = *(src++); 1.2183 + } while (--length); 1.2184 + } 1.2185 + } 1.2186 + 1.2187 + NS_ASSERTION(static_cast<uint32_t>(dest - aDest) == aSrcLength, 1.2188 + "whole string not copied"); 1.2189 + return true; 1.2190 +} 1.2191 + 1.2192 +void nsBidiPresUtils::CopyLogicalToVisual(const nsAString& aSource, 1.2193 + nsAString& aDest, 1.2194 + nsBidiLevel aBaseDirection, 1.2195 + bool aOverride) 1.2196 +{ 1.2197 + aDest.SetLength(0); 1.2198 + uint32_t srcLength = aSource.Length(); 1.2199 + if (srcLength == 0) 1.2200 + return; 1.2201 + if (!aDest.SetLength(srcLength, fallible_t())) { 1.2202 + return; 1.2203 + } 1.2204 + nsAString::const_iterator fromBegin, fromEnd; 1.2205 + nsAString::iterator toBegin; 1.2206 + aSource.BeginReading(fromBegin); 1.2207 + aSource.EndReading(fromEnd); 1.2208 + aDest.BeginWriting(toBegin); 1.2209 + 1.2210 + if (aOverride) { 1.2211 + if (aBaseDirection == NSBIDI_RTL) { 1.2212 + // no need to use the converter -- just copy the string in reverse order 1.2213 + WriteReverse(fromBegin.get(), srcLength, toBegin.get()); 1.2214 + } else { 1.2215 + // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the 1.2216 + // simple copy 1.2217 + aDest.SetLength(0); 1.2218 + } 1.2219 + } else { 1.2220 + nsBidi bidiEngine; 1.2221 + if (!WriteLogicalToVisual(fromBegin.get(), srcLength, toBegin.get(), 1.2222 + aBaseDirection, &bidiEngine)) { 1.2223 + aDest.SetLength(0); 1.2224 + } 1.2225 + } 1.2226 + 1.2227 + if (aDest.IsEmpty()) { 1.2228 + // Either there was an error or the source is unidirectional 1.2229 + // left-to-right. In either case, just copy source to dest. 1.2230 + CopyUnicodeTo(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd), 1.2231 + aDest); 1.2232 + } 1.2233 +} 1.2234 + 1.2235 +/* static */ 1.2236 +nsBidiLevel 1.2237 +nsBidiPresUtils::BidiLevelFromStyle(nsStyleContext* aStyleContext) 1.2238 +{ 1.2239 + if (aStyleContext->StyleTextReset()->mUnicodeBidi & 1.2240 + NS_STYLE_UNICODE_BIDI_PLAINTEXT) { 1.2241 + return NSBIDI_DEFAULT_LTR; 1.2242 + } 1.2243 + 1.2244 + if (aStyleContext->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { 1.2245 + return NSBIDI_RTL; 1.2246 + } 1.2247 + 1.2248 + return NSBIDI_LTR; 1.2249 +}