Bug 1726704 [wpt PR 30103] - Add more extensive tests for .canShare(), a=testonly
[gecko.git] / layout / base / nsBidiPresUtils.h
blob09fec0a7608ca17de16c0ff2b376bf9bfa5733fc
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 "nsBidi.h"
12 #include "nsBidiUtils.h"
13 #include "nsHashKeys.h"
14 #include "nsCoord.h"
15 #include "nsTArray.h"
16 #include "nsLineBox.h"
18 #ifdef DrawText
19 # undef DrawText
20 #endif
22 struct BidiParagraphData;
23 struct BidiLineData;
24 class gfxContext;
25 class nsFontMetrics;
26 class nsIFrame;
27 class nsBlockFrame;
28 class nsPresContext;
29 struct nsSize;
30 template <class T>
31 class nsTHashtable;
32 namespace mozilla {
33 class ComputedStyle;
34 class LogicalMargin;
35 class WritingMode;
36 } // namespace mozilla
38 /**
39 * A structure representing some continuation state for each frame on the line,
40 * used to determine the first and the last continuation frame for each
41 * continuation chain on the line.
43 struct nsFrameContinuationState : public nsVoidPtrHashKey {
44 explicit nsFrameContinuationState(const void* aFrame)
45 : nsVoidPtrHashKey(aFrame) {}
47 /**
48 * The first visual frame in the continuation chain containing this frame, or
49 * nullptr if this frame is the first visual frame in the chain.
51 nsIFrame* mFirstVisualFrame{nullptr};
53 /**
54 * The number of frames in the continuation chain containing this frame, if
55 * this frame is the first visual frame of the chain, or 0 otherwise.
57 uint32_t mFrameCount{0};
59 /**
60 * TRUE if this frame is the first visual frame of its continuation chain on
61 * this line and the chain has some frames on the previous lines.
63 bool mHasContOnPrevLines{false};
65 /**
66 * TRUE if this frame is the first visual frame of its continuation chain on
67 * this line and the chain has some frames left for next lines.
69 bool mHasContOnNextLines{false};
72 // A table of nsFrameContinuationState objects.
74 // This state is used between calls to nsBidiPresUtils::IsFirstOrLast.
75 struct nsContinuationStates {
76 static constexpr size_t kArrayMax = 32;
78 // We use the array to gather up all the continuation state objects. If in
79 // the end there are more than kArrayMax of them, we convert it to a hash
80 // table for faster lookup.
81 bool mUseTable = false;
82 AutoTArray<nsFrameContinuationState, kArrayMax> mValues;
83 nsTHashtable<nsFrameContinuationState> mTable;
85 void Insert(nsIFrame* aFrame) {
86 if (MOZ_UNLIKELY(mUseTable)) {
87 mTable.PutEntry(aFrame);
88 return;
90 if (MOZ_LIKELY(mValues.Length() < kArrayMax)) {
91 mValues.AppendElement(aFrame);
92 return;
94 for (const auto& entry : mValues) {
95 mTable.PutEntry(entry.GetKey());
97 mTable.PutEntry(aFrame);
98 mValues.Clear();
99 mUseTable = true;
102 nsFrameContinuationState* Get(nsIFrame* aFrame) {
103 MOZ_ASSERT(mValues.IsEmpty() != mTable.IsEmpty(),
104 "expect entries to either be in mValues or in mTable");
105 if (mUseTable) {
106 return mTable.GetEntry(aFrame);
108 for (size_t i = 0, len = mValues.Length(); i != len; ++i) {
109 if (mValues[i].GetKey() == aFrame) {
110 return &mValues[i];
113 return nullptr;
118 * A structure representing a logical position which should be resolved
119 * into its visual position during BiDi processing.
121 struct nsBidiPositionResolve {
122 // [in] Logical index within string.
123 int32_t logicalIndex;
124 // [out] Visual index within string.
125 // If the logical position was not found, set to kNotFound.
126 int32_t visualIndex;
127 // [out] Visual position of the character, from the left (on the X axis), in
128 // twips. Eessentially, this is the X position (relative to the rendering
129 // context) where the text was drawn + the font metric of the visual string to
130 // the left of the given logical position. If the logical position was not
131 // found, set to kNotFound.
132 int32_t visualLeftTwips;
133 // [out] Visual width of the character, in twips.
134 // If the logical position was not found, set to kNotFound.
135 int32_t visualWidth;
138 class nsBidiPresUtils {
139 public:
140 typedef mozilla::gfx::DrawTarget DrawTarget;
142 nsBidiPresUtils();
143 ~nsBidiPresUtils();
146 * Interface for the processor used by ProcessText. Used by process text to
147 * collect information about the width of subruns and to notify where each
148 * subrun should be rendered.
150 class BidiProcessor {
151 public:
152 virtual ~BidiProcessor() = default;
155 * Sets the current text with the given length and the given direction.
157 * @remark The reason that the function gives a string instead of an index
158 * is that ProcessText copies and modifies the string passed to it, so
159 * passing an index would be impossible.
161 * @param aText The string of text.
162 * @param aLength The length of the string of text.
163 * @param aDirection The direction of the text. The string will never have
164 * mixed direction.
166 virtual void SetText(const char16_t* aText, int32_t aLength,
167 nsBidiDirection aDirection) = 0;
170 * Returns the measured width of the text given in SetText. If SetText was
171 * not called with valid parameters, the result of this call is undefined.
172 * This call is guaranteed to only be called once between SetText calls.
173 * Will be invoked before DrawText.
175 virtual nscoord GetWidth() = 0;
178 * Draws the text given in SetText to a rendering context. If SetText was
179 * not called with valid parameters, the result of this call is undefined.
180 * This call is guaranteed to only be called once between SetText calls.
182 * @param aXOffset The offset of the left side of the substring to be drawn
183 * from the beginning of the overall string passed to ProcessText.
184 * @param aWidth The width returned by GetWidth.
186 virtual void DrawText(nscoord aXOffset, nscoord aWidth) = 0;
190 * Make Bidi engine calculate the embedding levels of the frames that are
191 * descendants of a given block frame.
193 * @param aBlockFrame The block frame
195 * @lina 06/18/2000
197 static nsresult Resolve(nsBlockFrame* aBlockFrame);
198 static nsresult ResolveParagraph(BidiParagraphData* aBpd);
199 static void ResolveParagraphWithinBlock(BidiParagraphData* aBpd);
202 * Reorder this line using Bidi engine.
203 * Update frame array, following the new visual sequence.
205 * @return total inline size
207 * @lina 05/02/2000
209 static nscoord ReorderFrames(nsIFrame* aFirstFrameOnLine,
210 int32_t aNumFramesOnLine,
211 mozilla::WritingMode aLineWM,
212 const nsSize& aContainerSize, nscoord aStart);
215 * Format Unicode text, taking into account bidi capabilities
216 * of the platform. The formatting includes: reordering, Arabic shaping,
217 * symmetric and numeric swapping, removing control characters.
219 * @lina 06/18/2000
221 static nsresult FormatUnicodeText(nsPresContext* aPresContext,
222 char16_t* aText, int32_t& aTextLength,
223 nsCharType aCharType);
226 * Reorder plain text using the Unicode Bidi algorithm and send it to
227 * a rendering context for rendering.
229 * @param[in] aText the string to be rendered (in logical order)
230 * @param aLength the number of characters in the string
231 * @param aBaseLevel the base embedding level of the string
232 * odd values are right-to-left; even values are left-to-right, plus special
233 * constants as follows (defined in nsBidi.h)
234 * NSBIDI_LTR - left-to-right string
235 * NSBIDI_RTL - right-to-left string
236 * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
237 * default is left-to-right
238 * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
239 * default is right-to-left
241 * @param aPresContext the presentation context
242 * @param aRenderingContext the rendering context to render to
243 * @param aTextRunConstructionContext the rendering context to be used to
244 * construct the textrun (affects font hinting)
245 * @param aX the x-coordinate to render the string
246 * @param aY the y-coordinate to render the string
247 * @param[in,out] aPosResolve array of logical positions to resolve into
248 * visual positions; can be nullptr if this functionality is not required
249 * @param aPosResolveCount number of items in the aPosResolve array
251 static nsresult RenderText(
252 const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
253 nsPresContext* aPresContext, gfxContext& aRenderingContext,
254 DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
255 nscoord aX, nscoord aY, nsBidiPositionResolve* aPosResolve = nullptr,
256 int32_t aPosResolveCount = 0) {
257 return ProcessTextForRenderingContext(
258 aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
259 aTextRunConstructionDrawTarget, aFontMetrics, MODE_DRAW, aX, aY,
260 aPosResolve, aPosResolveCount, nullptr);
263 static nscoord MeasureTextWidth(const char16_t* aText, int32_t aLength,
264 nsBidiLevel aBaseLevel,
265 nsPresContext* aPresContext,
266 gfxContext& aRenderingContext,
267 nsFontMetrics& aFontMetrics) {
268 nscoord length;
269 nsresult rv = ProcessTextForRenderingContext(
270 aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
271 aRenderingContext.GetDrawTarget(), aFontMetrics, MODE_MEASURE, 0, 0,
272 nullptr, 0, &length);
273 return NS_SUCCEEDED(rv) ? length : 0;
277 * Check if a line is reordered, i.e., if the child frames are not
278 * all laid out left-to-right.
279 * @param aFirstFrameOnLine : first frame of the line to be tested
280 * @param aNumFramesOnLine : number of frames on this line
281 * @param[out] aLeftMost : leftmost frame on this line
282 * @param[out] aRightMost : rightmost frame on this line
284 static bool CheckLineOrder(nsIFrame* aFirstFrameOnLine,
285 int32_t aNumFramesOnLine, nsIFrame** aLeftmost,
286 nsIFrame** aRightmost);
289 * Get the frame to the right of the given frame, on the same line.
290 * @param aFrame : We're looking for the frame to the right of this frame.
291 * If null, return the leftmost frame on the line.
292 * @param aFirstFrameOnLine : first frame of the line to be tested
293 * @param aNumFramesOnLine : number of frames on this line
295 static nsIFrame* GetFrameToRightOf(const nsIFrame* aFrame,
296 nsIFrame* aFirstFrameOnLine,
297 int32_t aNumFramesOnLine);
300 * Get the frame to the left of the given frame, on the same line.
301 * @param aFrame : We're looking for the frame to the left of this frame.
302 * If null, return the rightmost frame on the line.
303 * @param aFirstFrameOnLine : first frame of the line to be tested
304 * @param aNumFramesOnLine : number of frames on this line
306 static nsIFrame* GetFrameToLeftOf(const nsIFrame* aFrame,
307 nsIFrame* aFirstFrameOnLine,
308 int32_t aNumFramesOnLine);
310 static nsIFrame* GetFirstLeaf(nsIFrame* aFrame);
313 * Get the bidi data of the given (inline) frame.
315 static mozilla::FrameBidiData GetFrameBidiData(nsIFrame* aFrame);
318 * Get the bidi embedding level of the given (inline) frame.
320 static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame);
323 * Get the bidi base level of the given (inline) frame.
325 static nsBidiLevel GetFrameBaseLevel(const nsIFrame* aFrame);
328 * Get an nsBidiDirection representing the direction implied by the
329 * bidi base level of the frame.
330 * @return NSBIDI_LTR (left-to-right) or NSBIDI_RTL (right-to-left)
331 * NSBIDI_MIXED will never be returned.
333 static nsBidiDirection ParagraphDirection(const nsIFrame* aFrame) {
334 return DIRECTION_FROM_LEVEL(GetFrameBaseLevel(aFrame));
338 * Get an nsBidiDirection representing the direction implied by the
339 * bidi embedding level of the frame.
340 * @return NSBIDI_LTR (left-to-right) or NSBIDI_RTL (right-to-left)
341 * NSBIDI_MIXED will never be returned.
343 static nsBidiDirection FrameDirection(nsIFrame* aFrame) {
344 return DIRECTION_FROM_LEVEL(GetFrameEmbeddingLevel(aFrame));
347 static bool IsFrameInParagraphDirection(nsIFrame* aFrame) {
348 return ParagraphDirection(aFrame) == FrameDirection(aFrame);
351 // This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
352 // because it uses the frame pointer passed in without drilling down to
353 // the leaf frame.
354 static bool IsReversedDirectionFrame(const nsIFrame* aFrame) {
355 mozilla::FrameBidiData bidiData = aFrame->GetBidiData();
356 return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
359 enum Mode { MODE_DRAW, MODE_MEASURE };
362 * Reorder plain text using the Unicode Bidi algorithm and send it to
363 * a processor for rendering or measuring
365 * @param[in] aText the string to be processed (in logical order)
366 * @param aLength the number of characters in the string
367 * @param aBaseLevel the base embedding level of the string
368 * odd values are right-to-left; even values are left-to-right, plus special
369 * constants as follows (defined in nsBidi.h)
370 * NSBIDI_LTR - left-to-right string
371 * NSBIDI_RTL - right-to-left string
372 * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
373 * default is left-to-right
374 * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
375 * default is right-to-left
377 * @param aPresContext the presentation context
378 * @param aprocessor the bidi processor
379 * @param aMode the operation to process
380 * MODE_DRAW - invokes DrawText on the processor for each substring
381 * MODE_MEASURE - does not invoke DrawText on the processor
382 * Note that the string is always measured, regardless of mode
383 * @param[in,out] aPosResolve array of logical positions to resolve into
384 * visual positions; can be nullptr if this functionality is not required
385 * @param aPosResolveCount number of items in the aPosResolve array
386 * @param[out] aWidth Pointer to where the width will be stored (may be null)
388 static nsresult ProcessText(const char16_t* aText, int32_t aLength,
389 nsBidiLevel aBaseLevel,
390 nsPresContext* aPresContext,
391 BidiProcessor& aprocessor, Mode aMode,
392 nsBidiPositionResolve* aPosResolve,
393 int32_t aPosResolveCount, nscoord* aWidth,
394 nsBidi* aBidiEngine);
397 * Use style attributes to determine the base paragraph level to pass to the
398 * bidi algorithm.
400 * If |unicode-bidi| is set to "[-moz-]plaintext", returns NSBIDI_DEFAULT_LTR,
401 * in other words the direction is determined from the first strong character
402 * in the text according to rules P2 and P3 of the bidi algorithm, or LTR if
403 * there is no strong character.
405 * Otherwise returns NSBIDI_LTR or NSBIDI_RTL depending on the value of
406 * |direction|
408 static nsBidiLevel BidiLevelFromStyle(mozilla::ComputedStyle* aComputedStyle);
410 private:
411 static nsresult ProcessTextForRenderingContext(
412 const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
413 nsPresContext* aPresContext, gfxContext& aRenderingContext,
414 DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
415 Mode aMode,
416 nscoord aX, // DRAW only
417 nscoord aY, // DRAW only
418 nsBidiPositionResolve* aPosResolve, /* may be null */
419 int32_t aPosResolveCount, nscoord* aWidth /* may be null */);
422 * Traverse the child frames of the block element and:
423 * Set up an array of the frames in logical order
424 * Create a string containing the text content of all the frames
425 * If we encounter content that requires us to split the element into more
426 * than one paragraph for bidi resolution, resolve the paragraph up to that
427 * point.
429 static void TraverseFrames(nsIFrame* aCurrentFrame, BidiParagraphData* aBpd);
432 * Perform a recursive "pre-traversal" of the child frames of a block or
433 * inline container frame, to determine whether full bidi resolution is
434 * actually needed.
435 * This explores the same frames as TraverseFrames (above), but is less
436 * expensive and may allow us to avoid performing the full TraverseFrames
437 * operation.
438 * @param aFirstChild frame to start traversal from
439 * @param[in/out] aCurrContent the content node that we've most recently
440 * scanned for RTL characters (so that when descendant frames refer
441 * to the same content, we can avoid repeatedly scanning it).
442 * @return true if it finds that bidi is (or may be) required,
443 * false if no potentially-bidi content is present.
445 static bool ChildListMayRequireBidi(nsIFrame* aFirstChild,
446 nsIContent** aCurrContent);
449 * Position ruby content frames (ruby base/text frame).
450 * Called from RepositionRubyFrame.
452 static void RepositionRubyContentFrame(
453 nsIFrame* aFrame, mozilla::WritingMode aFrameWM,
454 const mozilla::LogicalMargin& aBorderPadding);
457 * Position ruby frames. Called from RepositionFrame.
459 static nscoord RepositionRubyFrame(
460 nsIFrame* aFrame, nsContinuationStates* aContinuationStates,
461 const mozilla::WritingMode aContainerWM,
462 const mozilla::LogicalMargin& aBorderPadding);
465 * Position aFrame and its descendants to their visual places. Also if aFrame
466 * is not leaf, resize it to embrace its children.
468 * @param aFrame The frame which itself and its children are
469 * going to be repositioned
470 * @param aIsEvenLevel TRUE means the embedding level of this frame
471 * is even (LTR)
472 * @param aStartOrEnd The distance to the start or the end of aFrame
473 * without considering its inline margin. If the
474 * container is reordering frames in reverse
475 * direction, it's the distance to the end,
476 * otherwise, it's the distance to the start.
477 * @param aContinuationStates A map from nsIFrame* to
478 * nsFrameContinuationState
479 * @return The isize aFrame takes, including margins.
481 static nscoord RepositionFrame(nsIFrame* aFrame, bool aIsEvenLevel,
482 nscoord aStartOrEnd,
483 nsContinuationStates* aContinuationStates,
484 mozilla::WritingMode aContainerWM,
485 bool aContainerReverseOrder,
486 const nsSize& aContainerSize);
489 * Initialize the continuation state(nsFrameContinuationState) to
490 * (nullptr, 0) for aFrame and its descendants.
492 * @param aFrame The frame which itself and its descendants will
493 * be initialized
494 * @param aContinuationStates A map from nsIFrame* to
495 * nsFrameContinuationState
497 static void InitContinuationStates(nsIFrame* aFrame,
498 nsContinuationStates* aContinuationStates);
501 * Determine if aFrame is first or last, and set aIsFirst and
502 * aIsLast values. Also set continuation states of
503 * aContinuationStates.
505 * A frame is first if it's the first appearance of its continuation
506 * chain on the line and the chain is on its first line.
507 * A frame is last if it's the last appearance of its continuation
508 * chain on the line and the chain is on its last line.
510 * N.B: "First appearance" and "Last appearance" in the previous
511 * paragraph refer to the frame's inline direction, not necessarily
512 * the line's.
514 * @param aContinuationStates A map from nsIFrame* to
515 * nsFrameContinuationState
516 * @param[in] aSpanDirMatchesLineDir TRUE means that the inline
517 * direction of aFrame is the same
518 * as its container
519 * @param[out] aIsFirst TRUE means aFrame is first frame
520 * or continuation
521 * @param[out] aIsLast TRUE means aFrame is last frame
522 * or continuation
524 static void IsFirstOrLast(nsIFrame* aFrame,
525 nsContinuationStates* aContinuationStates,
526 bool aSpanInLineOrder /* in */,
527 bool& aIsFirst /* out */, bool& aIsLast /* out */);
530 * Adjust frame positions following their visual order
532 * @param aFirstChild the first kid
533 * @return total inline size
535 * @lina 04/11/2000
537 static nscoord RepositionInlineFrames(BidiLineData* aBld,
538 mozilla::WritingMode aLineWM,
539 const nsSize& aContainerSize,
540 nscoord aStart);
543 * Helper method for Resolve()
544 * Truncate a text frame to the end of a single-directional run and possibly
545 * create a continuation frame for the remainder of its content.
547 * @param aFrame the original frame
548 * @param aLine the line box containing aFrame
549 * @param aNewFrame [OUT] the new frame that was created
550 * @param aStart [IN] the start of the content mapped by aFrame (and
551 * any fluid continuations)
552 * @param aEnd [IN] the offset of the end of the single-directional
553 * text run.
554 * @see Resolve()
555 * @see RemoveBidiContinuation()
557 static inline void EnsureBidiContinuation(nsIFrame* aFrame,
558 const nsLineList::iterator aLine,
559 nsIFrame** aNewFrame,
560 int32_t aStart, int32_t aEnd);
563 * Helper method for Resolve()
564 * Convert one or more bidi continuation frames created in a previous reflow
565 * by EnsureBidiContinuation() into fluid continuations.
566 * @param aFrame the frame whose continuations are to be removed
567 * @param aFirstIndex index of aFrame in mLogicalFrames
568 * @param aLastIndex index of the last frame to be removed
570 * @see Resolve()
571 * @see EnsureBidiContinuation()
573 static void RemoveBidiContinuation(BidiParagraphData* aBpd, nsIFrame* aFrame,
574 int32_t aFirstIndex, int32_t aLastIndex);
575 static void CalculateCharType(nsBidi* aBidiEngine, const char16_t* aText,
576 int32_t& aOffset, int32_t aCharTypeLimit,
577 int32_t& aRunLimit, int32_t& aRunLength,
578 int32_t& aRunCount, uint8_t& aCharType,
579 uint8_t& aPrevCharType);
581 static void StripBidiControlCharacters(char16_t* aText, int32_t& aTextLength);
584 #endif /* nsBidiPresUtils_h___ */