Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / layout / base / nsBidiPresUtils.h
blobc369a06f2d80ac3e9e2a333a693a2005660340da
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsBidiPresUtils_h___
8 #define nsBidiPresUtils_h___
10 #include "gfxContext.h"
11 #include "mozilla/intl/BidiClass.h"
12 #include "mozilla/intl/BidiEmbeddingLevel.h"
13 #include "nsBidiUtils.h"
14 #include "nsHashKeys.h"
15 #include "nsCoord.h"
16 #include "nsTArray.h"
17 #include "nsLineBox.h"
19 #ifdef DrawText
20 # undef DrawText
21 #endif
23 struct BidiParagraphData;
24 struct BidiLineData;
25 class gfxContext;
26 class nsFontMetrics;
27 class nsIFrame;
28 class nsBlockFrame;
29 class nsPresContext;
30 struct nsSize;
31 template <class T>
32 class nsTHashtable;
33 namespace mozilla {
34 namespace intl {
35 class Bidi;
37 class ComputedStyle;
38 class LogicalMargin;
39 class WritingMode;
40 } // namespace mozilla
42 /**
43 * A structure representing some continuation state for each frame on the line,
44 * used to determine the first and the last continuation frame for each
45 * continuation chain on the line.
47 struct nsFrameContinuationState : public nsVoidPtrHashKey {
48 explicit nsFrameContinuationState(const void* aFrame)
49 : nsVoidPtrHashKey(aFrame) {}
51 /**
52 * The first visual frame in the continuation chain containing this frame, or
53 * nullptr if this frame is the first visual frame in the chain.
55 nsIFrame* mFirstVisualFrame{nullptr};
57 /**
58 * The number of frames in the continuation chain containing this frame, if
59 * this frame is the first visual frame of the chain, or 0 otherwise.
61 uint32_t mFrameCount{0};
63 /**
64 * TRUE if this frame is the first visual frame of its continuation chain on
65 * this line and the chain has some frames on the previous lines.
67 bool mHasContOnPrevLines{false};
69 /**
70 * TRUE if this frame is the first visual frame of its continuation chain on
71 * this line and the chain has some frames left for next lines.
73 bool mHasContOnNextLines{false};
76 // A table of nsFrameContinuationState objects.
78 // This state is used between calls to nsBidiPresUtils::IsFirstOrLast.
79 struct nsContinuationStates {
80 static constexpr size_t kArrayMax = 32;
82 // We use the array to gather up all the continuation state objects. If in
83 // the end there are more than kArrayMax of them, we convert it to a hash
84 // table for faster lookup.
85 bool mUseTable = false;
86 AutoTArray<nsFrameContinuationState, kArrayMax> mValues;
87 nsTHashtable<nsFrameContinuationState> mTable;
89 void Insert(nsIFrame* aFrame) {
90 if (MOZ_UNLIKELY(mUseTable)) {
91 mTable.PutEntry(aFrame);
92 return;
94 if (MOZ_LIKELY(mValues.Length() < kArrayMax)) {
95 mValues.AppendElement(aFrame);
96 return;
98 for (const auto& entry : mValues) {
99 mTable.PutEntry(entry.GetKey());
101 mTable.PutEntry(aFrame);
102 mValues.Clear();
103 mUseTable = true;
106 nsFrameContinuationState* Get(nsIFrame* aFrame) {
107 MOZ_ASSERT(mValues.IsEmpty() != mTable.IsEmpty(),
108 "expect entries to either be in mValues or in mTable");
109 if (mUseTable) {
110 return mTable.GetEntry(aFrame);
112 for (size_t i = 0, len = mValues.Length(); i != len; ++i) {
113 if (mValues[i].GetKey() == aFrame) {
114 return &mValues[i];
117 return nullptr;
122 * A structure representing a logical position which should be resolved
123 * into its visual position during BiDi processing.
125 struct nsBidiPositionResolve {
126 // [in] Logical index within string.
127 int32_t logicalIndex;
128 // [out] Visual index within string.
129 // If the logical position was not found, set to kNotFound.
130 int32_t visualIndex;
131 // [out] Visual position of the character, from the left (on the X axis), in
132 // twips. Eessentially, this is the X position (relative to the rendering
133 // context) where the text was drawn + the font metric of the visual string to
134 // the left of the given logical position. If the logical position was not
135 // found, set to kNotFound.
136 int32_t visualLeftTwips;
137 // [out] Visual width of the character, in twips.
138 // If the logical position was not found, set to kNotFound.
139 int32_t visualWidth;
142 class nsBidiPresUtils {
143 public:
144 typedef mozilla::gfx::DrawTarget DrawTarget;
146 nsBidiPresUtils();
147 ~nsBidiPresUtils();
150 * Interface for the processor used by ProcessText. Used by process text to
151 * collect information about the width of subruns and to notify where each
152 * subrun should be rendered.
154 class BidiProcessor {
155 public:
156 virtual ~BidiProcessor() = default;
159 * Sets the current text with the given length and the given direction.
161 * @remark The reason that the function gives a string instead of an index
162 * is that ProcessText copies and modifies the string passed to it, so
163 * passing an index would be impossible.
165 * @param aText The string of text.
166 * @param aLength The length of the string of text.
167 * @param aDirection The direction of the text. The string will never have
168 * mixed direction.
170 virtual void SetText(const char16_t* aText, int32_t aLength,
171 mozilla::intl::BidiDirection aDirection) = 0;
174 * Returns the measured width of the text given in SetText. If SetText was
175 * not called with valid parameters, the result of this call is undefined.
176 * This call is guaranteed to only be called once between SetText calls.
177 * Will be invoked before DrawText.
179 virtual nscoord GetWidth() = 0;
182 * Draws the text given in SetText to a rendering context. If SetText was
183 * not called with valid parameters, the result of this call is undefined.
184 * This call is guaranteed to only be called once between SetText calls.
186 * @param aXOffset The offset of the left side of the substring to be drawn
187 * from the beginning of the overall string passed to ProcessText.
189 virtual void DrawText(nscoord aXOffset) = 0;
193 * Make Bidi engine calculate the embedding levels of the frames that are
194 * descendants of a given block frame.
196 * @param aBlockFrame The block frame
198 * @lina 06/18/2000
200 static nsresult Resolve(nsBlockFrame* aBlockFrame);
201 static nsresult ResolveParagraph(BidiParagraphData* aBpd);
202 static void ResolveParagraphWithinBlock(BidiParagraphData* aBpd);
205 * Reorder this line using Bidi engine.
206 * Update frame array, following the new visual sequence.
208 * @return total inline size
210 * @lina 05/02/2000
212 static nscoord ReorderFrames(nsIFrame* aFirstFrameOnLine,
213 int32_t aNumFramesOnLine,
214 mozilla::WritingMode aLineWM,
215 const nsSize& aContainerSize, nscoord aStart);
218 * Format Unicode text, taking into account bidi capabilities
219 * of the platform. The formatting includes: reordering, Arabic shaping,
220 * symmetric and numeric swapping, removing control characters.
222 * @lina 06/18/2000
224 static nsresult FormatUnicodeText(nsPresContext* aPresContext,
225 char16_t* aText, int32_t& aTextLength,
226 mozilla::intl::BidiClass aBidiClass);
229 * Reorder plain text using the Unicode Bidi algorithm and send it to
230 * a rendering context for rendering.
232 * @param[in] aText the string to be rendered (in logical order)
233 * @param aLength the number of characters in the string
234 * @param aBaseLevel the base embedding level of the string
235 * @param aPresContext the presentation context
236 * @param aRenderingContext the rendering context to render to
237 * @param aTextRunConstructionContext the rendering context to be used to
238 * construct the textrun (affects font hinting)
239 * @param aX the x-coordinate to render the string
240 * @param aY the y-coordinate to render the string
241 * @param[in,out] aPosResolve array of logical positions to resolve into
242 * visual positions; can be nullptr if this functionality is not required
243 * @param aPosResolveCount number of items in the aPosResolve array
245 static nsresult RenderText(const char16_t* aText, int32_t aLength,
246 mozilla::intl::BidiEmbeddingLevel aBaseLevel,
247 nsPresContext* aPresContext,
248 gfxContext& aRenderingContext,
249 DrawTarget* aTextRunConstructionDrawTarget,
250 nsFontMetrics& aFontMetrics, nscoord aX,
251 nscoord aY,
252 nsBidiPositionResolve* aPosResolve = nullptr,
253 int32_t aPosResolveCount = 0) {
254 return ProcessTextForRenderingContext(
255 aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
256 aTextRunConstructionDrawTarget, aFontMetrics, MODE_DRAW, aX, aY,
257 aPosResolve, aPosResolveCount, nullptr);
260 static nscoord MeasureTextWidth(const char16_t* aText, int32_t aLength,
261 mozilla::intl::BidiEmbeddingLevel aBaseLevel,
262 nsPresContext* aPresContext,
263 gfxContext& aRenderingContext,
264 nsFontMetrics& aFontMetrics) {
265 nscoord length;
266 nsresult rv = ProcessTextForRenderingContext(
267 aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
268 aRenderingContext.GetDrawTarget(), aFontMetrics, MODE_MEASURE, 0, 0,
269 nullptr, 0, &length);
270 return NS_SUCCEEDED(rv) ? length : 0;
274 * Check if a line is reordered, i.e., if the child frames are not
275 * all laid out left-to-right.
276 * @param aFirstFrameOnLine : first frame of the line to be tested
277 * @param aNumFramesOnLine : number of frames on this line
278 * @param[out] aLeftMost : leftmost frame on this line
279 * @param[out] aRightMost : rightmost frame on this line
281 static bool CheckLineOrder(nsIFrame* aFirstFrameOnLine,
282 int32_t aNumFramesOnLine, nsIFrame** aLeftmost,
283 nsIFrame** aRightmost);
286 * Get the frame to the right of the given frame, on the same line.
287 * @param aFrame : We're looking for the frame to the right of this frame.
288 * If null, return the leftmost frame on the line.
289 * @param aFirstFrameOnLine : first frame of the line to be tested
290 * @param aNumFramesOnLine : number of frames on this line
292 static nsIFrame* GetFrameToRightOf(const nsIFrame* aFrame,
293 nsIFrame* aFirstFrameOnLine,
294 int32_t aNumFramesOnLine);
297 * Get the frame to the left of the given frame, on the same line.
298 * @param aFrame : We're looking for the frame to the left of this frame.
299 * If null, return the rightmost frame on the line.
300 * @param aFirstFrameOnLine : first frame of the line to be tested
301 * @param aNumFramesOnLine : number of frames on this line
303 static nsIFrame* GetFrameToLeftOf(const nsIFrame* aFrame,
304 nsIFrame* aFirstFrameOnLine,
305 int32_t aNumFramesOnLine);
307 static nsIFrame* GetFirstLeaf(nsIFrame* aFrame);
310 * Get the bidi data of the given (inline) frame.
312 static mozilla::FrameBidiData GetFrameBidiData(nsIFrame* aFrame);
315 * Get the bidi embedding level of the given (inline) frame.
317 static mozilla::intl::BidiEmbeddingLevel GetFrameEmbeddingLevel(
318 nsIFrame* aFrame);
321 * Get the bidi base level of the given (inline) frame.
323 static mozilla::intl::BidiEmbeddingLevel GetFrameBaseLevel(
324 const nsIFrame* aFrame);
327 * Get a mozilla::intl::BidiDirection representing the direction implied by
328 * the bidi base level of the frame.
329 * @return mozilla::intl::BidiDirection
331 static mozilla::intl::BidiDirection ParagraphDirection(
332 const nsIFrame* aFrame) {
333 return GetFrameBaseLevel(aFrame).Direction();
337 * Get a mozilla::intl::BidiDirection representing the direction implied by
338 * the bidi embedding level of the frame.
339 * @return mozilla::intl::BidiDirection
341 static mozilla::intl::BidiDirection FrameDirection(nsIFrame* aFrame) {
342 return GetFrameEmbeddingLevel(aFrame).Direction();
345 static bool IsFrameInParagraphDirection(nsIFrame* aFrame) {
346 return ParagraphDirection(aFrame) == FrameDirection(aFrame);
349 // This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
350 // because it uses the frame pointer passed in without drilling down to
351 // the leaf frame.
352 static bool IsReversedDirectionFrame(const nsIFrame* aFrame) {
353 mozilla::FrameBidiData bidiData = aFrame->GetBidiData();
354 return !bidiData.embeddingLevel.IsSameDirection(bidiData.baseLevel);
357 enum Mode { MODE_DRAW, MODE_MEASURE };
360 * Reorder plain text using the Unicode Bidi algorithm and send it to
361 * a processor for rendering or measuring
363 * @param[in] aText the string to be processed (in logical order)
364 * @param aLength the number of characters in the string
365 * @param aBaseLevel the base embedding level of the string
366 * @param aPresContext the presentation context
367 * @param aprocessor the bidi processor
368 * @param aMode the operation to process
369 * MODE_DRAW - invokes DrawText on the processor for each substring
370 * MODE_MEASURE - does not invoke DrawText on the processor
371 * Note that the string is always measured, regardless of mode
372 * @param[in,out] aPosResolve array of logical positions to resolve into
373 * visual positions; can be nullptr if this functionality is not required
374 * @param aPosResolveCount number of items in the aPosResolve array
375 * @param[out] aWidth Pointer to where the width will be stored (may be null)
377 static nsresult ProcessText(const char16_t* aText, size_t aLength,
378 mozilla::intl::BidiEmbeddingLevel aBaseLevel,
379 nsPresContext* aPresContext,
380 BidiProcessor& aprocessor, Mode aMode,
381 nsBidiPositionResolve* aPosResolve,
382 int32_t aPosResolveCount, nscoord* aWidth,
383 mozilla::intl::Bidi& aBidiEngine);
386 * Use style attributes to determine the base paragraph level to pass to the
387 * bidi algorithm.
389 * If |unicode-bidi| is set to "[-moz-]plaintext", returns
390 * BidiEmbeddingLevel::DefaultLTR, in other words the direction is determined
391 * from the first strong character in the text according to rules P2 and P3 of
392 * the bidi algorithm, or LTR if there is no strong character.
394 * Otherwise returns BidiEmbeddingLevel::LTR or BidiEmbeddingLevel::RTL
395 * depending on the value of |direction|
397 static mozilla::intl::BidiEmbeddingLevel BidiLevelFromStyle(
398 mozilla::ComputedStyle* aComputedStyle);
400 private:
401 static nsresult ProcessTextForRenderingContext(
402 const char16_t* aText, int32_t aLength,
403 mozilla::intl::BidiEmbeddingLevel aBaseLevel, nsPresContext* aPresContext,
404 gfxContext& aRenderingContext, DrawTarget* aTextRunConstructionDrawTarget,
405 nsFontMetrics& aFontMetrics, Mode aMode,
406 nscoord aX, // DRAW only
407 nscoord aY, // DRAW only
408 nsBidiPositionResolve* aPosResolve, /* may be null */
409 int32_t aPosResolveCount, nscoord* aWidth /* may be null */);
412 * Simplified form of ProcessText body, used when aText is a single Unicode
413 * character (one UTF-16 codepoint, or a surrogate pair), or a run that is
414 * known to have no bidi content.
416 static void ProcessSimpleRun(const char16_t* aText, size_t aLength,
417 mozilla::intl::BidiEmbeddingLevel aBaseLevel,
418 nsPresContext* aPresContext,
419 BidiProcessor& aprocessor, Mode aMode,
420 nsBidiPositionResolve* aPosResolve,
421 int32_t aPosResolveCount, nscoord* aWidth);
424 * Traverse the child frames of the block element and:
425 * Set up an array of the frames in logical order
426 * Create a string containing the text content of all the frames
427 * If we encounter content that requires us to split the element into more
428 * than one paragraph for bidi resolution, resolve the paragraph up to that
429 * point.
431 static void TraverseFrames(nsIFrame* aCurrentFrame, BidiParagraphData* aBpd);
434 * Perform a recursive "pre-traversal" of the child frames of a block or
435 * inline container frame, to determine whether full bidi resolution is
436 * actually needed.
437 * This explores the same frames as TraverseFrames (above), but is less
438 * expensive and may allow us to avoid performing the full TraverseFrames
439 * operation.
440 * @param aFirstChild frame to start traversal from
441 * @param[in/out] aCurrContent the content node that we've most recently
442 * scanned for RTL characters (so that when descendant frames refer
443 * to the same content, we can avoid repeatedly scanning it).
444 * @return true if it finds that bidi is (or may be) required,
445 * false if no potentially-bidi content is present.
447 static bool ChildListMayRequireBidi(nsIFrame* aFirstChild,
448 nsIContent** aCurrContent);
451 * Position ruby content frames (ruby base/text frame).
452 * Called from RepositionRubyFrame.
454 static void RepositionRubyContentFrame(
455 nsIFrame* aFrame, mozilla::WritingMode aFrameWM,
456 const mozilla::LogicalMargin& aBorderPadding);
459 * Position ruby frames. Called from RepositionFrame.
461 static nscoord RepositionRubyFrame(
462 nsIFrame* aFrame, nsContinuationStates* aContinuationStates,
463 const mozilla::WritingMode aContainerWM,
464 const mozilla::LogicalMargin& aBorderPadding);
467 * Position aFrame and its descendants to their visual places. Also if aFrame
468 * is not leaf, resize it to embrace its children.
470 * @param aFrame The frame which itself and its children are
471 * going to be repositioned
472 * @param aIsEvenLevel TRUE means the embedding level of this frame
473 * is even (LTR)
474 * @param aStartOrEnd The distance to the start or the end of aFrame
475 * without considering its inline margin. If the
476 * container is reordering frames in reverse
477 * direction, it's the distance to the end,
478 * otherwise, it's the distance to the start.
479 * @param aContinuationStates A map from nsIFrame* to
480 * nsFrameContinuationState
481 * @return The isize aFrame takes, including margins.
483 static nscoord RepositionFrame(nsIFrame* aFrame, bool aIsEvenLevel,
484 nscoord aStartOrEnd,
485 nsContinuationStates* aContinuationStates,
486 mozilla::WritingMode aContainerWM,
487 bool aContainerReverseOrder,
488 const nsSize& aContainerSize);
491 * Initialize the continuation state(nsFrameContinuationState) to
492 * (nullptr, 0) for aFrame and its descendants.
494 * @param aFrame The frame which itself and its descendants will
495 * be initialized
496 * @param aContinuationStates A map from nsIFrame* to
497 * nsFrameContinuationState
499 static void InitContinuationStates(nsIFrame* aFrame,
500 nsContinuationStates* aContinuationStates);
503 * Determine if aFrame is first or last, and set aIsFirst and
504 * aIsLast values. Also set continuation states of
505 * aContinuationStates.
507 * A frame is first if it's the first appearance of its continuation
508 * chain on the line and the chain is on its first line.
509 * A frame is last if it's the last appearance of its continuation
510 * chain on the line and the chain is on its last line.
512 * N.B: "First appearance" and "Last appearance" in the previous
513 * paragraph refer to the frame's inline direction, not necessarily
514 * the line's.
516 * @param aContinuationStates A map from nsIFrame* to
517 * nsFrameContinuationState
518 * @param[in] aSpanDirMatchesLineDir TRUE means that the inline
519 * direction of aFrame is the same
520 * as its container
521 * @param[out] aIsFirst TRUE means aFrame is first frame
522 * or continuation
523 * @param[out] aIsLast TRUE means aFrame is last frame
524 * or continuation
526 static void IsFirstOrLast(nsIFrame* aFrame,
527 nsContinuationStates* aContinuationStates,
528 bool aSpanInLineOrder /* in */,
529 bool& aIsFirst /* out */, bool& aIsLast /* out */);
532 * Adjust frame positions following their visual order
534 * @param aFirstChild the first kid
535 * @return total inline size
537 * @lina 04/11/2000
539 static nscoord RepositionInlineFrames(BidiLineData* aBld,
540 mozilla::WritingMode aLineWM,
541 const nsSize& aContainerSize,
542 nscoord aStart);
545 * Helper method for Resolve()
546 * Truncate a text frame to the end of a single-directional run and possibly
547 * create a continuation frame for the remainder of its content.
549 * @param aFrame the original frame
550 * @param aLine the line box containing aFrame
551 * @param aNewFrame [OUT] the new frame that was created
552 * @param aStart [IN] the start of the content mapped by aFrame (and
553 * any fluid continuations)
554 * @param aEnd [IN] the offset of the end of the single-directional
555 * text run.
556 * @see Resolve()
557 * @see RemoveBidiContinuation()
559 static inline void EnsureBidiContinuation(nsIFrame* aFrame,
560 const nsLineList::iterator aLine,
561 nsIFrame** aNewFrame,
562 int32_t aStart, int32_t aEnd);
565 * Helper method for Resolve()
566 * Convert one or more bidi continuation frames created in a previous reflow
567 * by EnsureBidiContinuation() into fluid continuations.
568 * @param aFrame the frame whose continuations are to be removed
569 * @param aFirstIndex index of aFrame in mLogicalFrames
570 * @param aLastIndex index of the last frame to be removed
572 * @see Resolve()
573 * @see EnsureBidiContinuation()
575 static void RemoveBidiContinuation(BidiParagraphData* aBpd, nsIFrame* aFrame,
576 int32_t aFirstIndex, int32_t aLastIndex);
578 static void CalculateBidiClass(const char16_t* aText, int32_t& aOffset,
579 int32_t aBidiClassLimit, int32_t& aRunLimit,
580 int32_t& aRunLength, int32_t& aRunCount,
581 mozilla::intl::BidiClass& aBidiClass,
582 mozilla::intl::BidiClass& aPrevBidiClass);
584 static void StripBidiControlCharacters(char16_t* aText, int32_t& aTextLength);
587 #endif /* nsBidiPresUtils_h___ */