Bug 1867925 - Mark some storage-access-api tests as intermittent after wpt-sync....
[gecko.git] / layout / generic / TextOverflow.h
blob7f0911b93db9801dd6129003b0848537511a9e86
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 TextOverflow_h_
8 #define TextOverflow_h_
10 #include "nsDisplayList.h"
11 #include "nsTHashSet.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Likely.h"
14 #include "mozilla/UniquePtr.h"
15 #include "mozilla/WritingModes.h"
16 #include <algorithm>
18 class nsIScrollableFrame;
19 class nsBlockFrame;
20 class nsLineBox;
22 namespace mozilla {
23 namespace css {
25 /**
26 * A class for rendering CSS3 text-overflow.
27 * Usage:
28 * 1. allocate an object using WillProcessLines
29 * 2. then call ProcessLine for each line you are building display lists for
31 * Note that this class is non-reassignable; we don't want to be making
32 * arbitrary copies. (But we do have a move constructor, since that's required
33 * in order to be stored in Maybe<>).
35 class TextOverflow final {
36 private:
37 /**
38 * Private constructor, for internal use only. Client code should call
39 * WillProcessLines(), which is basically the factory function for
40 * TextOverflow instances.
42 TextOverflow(nsDisplayListBuilder* aBuilder, nsBlockFrame*);
44 public:
45 ~TextOverflow() = default;
47 /**
48 * Allocate an object for text-overflow processing. (Factory function.)
49 * @return nullptr if no processing is necessary. The caller owns the object.
51 static Maybe<TextOverflow> WillProcessLines(nsDisplayListBuilder* aBuilder,
52 nsBlockFrame*);
54 /**
55 * This is a factory-constructed non-reassignable class, so we delete nearly
56 * all constructors and reassignment operators. We only provide a
57 * move-constructor, because that's required for Maybe<TextOverflow> to work
58 * (and that's what our factory method returns).
60 TextOverflow(TextOverflow&&) = default;
62 TextOverflow() = delete;
63 TextOverflow(const TextOverflow&) = delete;
64 TextOverflow& operator=(const TextOverflow&) = delete;
65 TextOverflow& operator=(TextOverflow&&) = delete;
67 /**
68 * Analyze the display lists for text overflow and what kind of item is at
69 * the content edges. Add display items for text-overflow markers as needed
70 * and remove or clip items that would overlap a marker.
72 void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine,
73 uint32_t aLineNumber);
75 /**
76 * Get the resulting text-overflow markers (the list may be empty).
77 * @return a DisplayList containing any text-overflow markers.
79 nsDisplayList& GetMarkers() { return mMarkerList; }
81 // Returns whether aBlockFrame has text-overflow:clip on both sides.
82 static bool HasClippedTextOverflow(nsIFrame* aBlockFrame);
84 // Returns whether aBlockFrame has a block ellipsis on one of its lines.
85 static bool HasBlockEllipsis(nsIFrame* aBlockFrame);
87 // Returns whether the given block frame needs analysis for text overflow.
88 // The BeforeReflow flag indicates whether we can be faster and more precise
89 // for line-clamp ellipsis (only returning true iff the block actually uses
90 // it).
91 enum class BeforeReflow : bool { No, Yes };
92 static bool CanHaveOverflowMarkers(nsBlockFrame*,
93 BeforeReflow = BeforeReflow::No);
95 typedef nsTHashSet<nsIFrame*> FrameHashtable;
97 private:
98 typedef mozilla::WritingMode WritingMode;
99 typedef mozilla::LogicalRect LogicalRect;
101 // Edges to align the IStart and IEnd markers to.
102 struct AlignmentEdges {
103 AlignmentEdges()
104 : mIStart(0), mIEnd(0), mIEndOuter(0), mAssignedInner(false) {}
105 void AccumulateInner(WritingMode aWM, const LogicalRect& aRect) {
106 if (MOZ_LIKELY(mAssignedInner)) {
107 mIStart = std::min(mIStart, aRect.IStart(aWM));
108 mIEnd = std::max(mIEnd, aRect.IEnd(aWM));
109 } else {
110 mIStart = aRect.IStart(aWM);
111 mIEnd = aRect.IEnd(aWM);
112 mAssignedInner = true;
115 void AccumulateOuter(WritingMode aWM, const LogicalRect& aRect) {
116 mIEndOuter = std::max(mIEndOuter, aRect.IEnd(aWM));
118 nscoord ISize() { return mIEnd - mIStart; }
120 // The outermost edges of all text and atomic inline-level frames that are
121 // inside the area between the markers.
122 nscoord mIStart;
123 nscoord mIEnd;
125 // The closest IEnd edge of all text and atomic inline-level frames that
126 // fall completely before the IStart edge of the content area. (Used to
127 // align a block ellipsis when there are no visible frames to align to.)
128 nscoord mIEndOuter;
130 bool mAssignedInner;
133 struct InnerClipEdges {
134 InnerClipEdges()
135 : mIStart(0), mIEnd(0), mAssignedIStart(false), mAssignedIEnd(false) {}
136 void AccumulateIStart(WritingMode aWM, const LogicalRect& aRect) {
137 if (MOZ_LIKELY(mAssignedIStart)) {
138 mIStart = std::max(mIStart, aRect.IStart(aWM));
139 } else {
140 mIStart = aRect.IStart(aWM);
141 mAssignedIStart = true;
144 void AccumulateIEnd(WritingMode aWM, const LogicalRect& aRect) {
145 if (MOZ_LIKELY(mAssignedIEnd)) {
146 mIEnd = std::min(mIEnd, aRect.IEnd(aWM));
147 } else {
148 mIEnd = aRect.IEnd(aWM);
149 mAssignedIEnd = true;
152 nscoord mIStart;
153 nscoord mIEnd;
154 bool mAssignedIStart;
155 bool mAssignedIEnd;
158 LogicalRect GetLogicalScrollableOverflowRectRelativeToBlock(
159 nsIFrame* aFrame) const {
160 return LogicalRect(
161 mBlockWM,
162 aFrame->ScrollableOverflowRect() + aFrame->GetOffsetTo(mBlock),
163 mBlockSize);
167 * Examines frames on the line to determine whether we should draw a left
168 * and/or right marker, and if so, which frames should be completely hidden
169 * and the bounds of what will be displayed between the markers.
170 * @param aLine the line we're processing
171 * @param aFramesToHide frames that should have their display items removed
172 * @param aAlignmentEdges edges the markers will be aligned to, including
173 * the outermost edges of all text and atomic inline-level frames that
174 * are inside the content area, and the closest IEnd edge of such a frame
175 * outside the content area
176 * @return the area inside which we should add any markers;
177 * this is the block's content area narrowed by any floats on this line.
179 LogicalRect ExamineLineFrames(nsLineBox* aLine, FrameHashtable* aFramesToHide,
180 AlignmentEdges* aAlignmentEdges);
183 * LineHasOverflowingText calls this to analyze edges, both the block's
184 * content edges and the hypothetical marker edges aligned at the block edges.
185 * @param aFrame the descendant frame of mBlock that we're analyzing
186 * @param aContentArea the block's content area
187 * @param aInsideMarkersArea the rectangle between the markers
188 * @param aFramesToHide frames that should have their display items removed
189 * @param aAlignmentEdges edges the markers will be aligned to, including
190 * the outermost edges of all text and atomic inline-level frames that
191 * are inside the content area, and the closest IEnd edge of such a frame
192 * outside the content area
193 * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
194 * inline-level frame is visible between the marker edges
195 * @param aClippedMarkerEdges the innermost edges of all text and atomic
196 * inline-level frames that are clipped by the current marker width
198 void ExamineFrameSubtree(nsIFrame* aFrame, const LogicalRect& aContentArea,
199 const LogicalRect& aInsideMarkersArea,
200 FrameHashtable* aFramesToHide,
201 AlignmentEdges* aAlignmentEdges,
202 bool* aFoundVisibleTextOrAtomic,
203 InnerClipEdges* aClippedMarkerEdges);
206 * ExamineFrameSubtree calls this to analyze a frame against the hypothetical
207 * marker edges (aInsideMarkersArea) for text frames and atomic inline-level
208 * elements. A text frame adds its extent inside aInsideMarkersArea where
209 * grapheme clusters are fully visible. An atomic adds its border box if
210 * it's fully inside aInsideMarkersArea, otherwise the frame is added to
211 * aFramesToHide.
212 * @param aFrame the descendant frame of mBlock that we're analyzing
213 * @param aFrameType aFrame's frame type
214 * @param aInsideMarkersArea the rectangle between the markers
215 * @param aFramesToHide frames that should have their display items removed
216 * @param aAlignmentEdges the outermost edges of all text and atomic
217 * inline-level frames that are inside the area between the markers
218 * inside aInsideMarkersArea
219 * @param aAlignmentEdges edges the markers will be aligned to, including
220 * the outermost edges of all text and atomic inline-level frames that
221 * are inside aInsideMarkersArea, and the closest IEnd edge of such a frame
222 * outside the content area
223 * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
224 * inline-level frame is visible between the marker edges
225 * @param aClippedMarkerEdges the innermost edges of all text and atomic
226 * inline-level frames that are clipped by the current marker width
228 void AnalyzeMarkerEdges(nsIFrame* aFrame, mozilla::LayoutFrameType aFrameType,
229 const LogicalRect& aInsideMarkersArea,
230 FrameHashtable* aFramesToHide,
231 AlignmentEdges* aAlignmentEdges,
232 bool* aFoundVisibleTextOrAtomic,
233 InnerClipEdges* aClippedMarkerEdges);
236 * Clip or remove items given the final marker edges. ("clip" here just means
237 * assigning mVisIStartEdge/mVisIEndEdge for any nsCharClipDisplayItem that
238 * needs it; see nsDisplayList.h for a description of that item).
239 * @param aFramesToHide remove display items for these frames
240 * @param aInsideMarkersArea is the area inside the markers
242 void PruneDisplayListContents(nsDisplayList* aList,
243 const FrameHashtable& aFramesToHide,
244 const LogicalRect& aInsideMarkersArea);
247 * ProcessLine calls this to create display items for the markers and insert
248 * them into mMarkerList.
249 * @param aLine the line we're processing
250 * @param aCreateIStart if true, create a marker on the inline start side
251 * @param aCreateIEnd if true, create a marker on the inline end side
252 * @param aInsideMarkersArea is the area inside the markers
253 * @param aContentArea is the area inside which we should add the markers;
254 * this is the block's content area narrowed by any floats on this line.
256 void CreateMarkers(const nsLineBox* aLine, bool aCreateIStart,
257 bool aCreateIEnd, const LogicalRect& aInsideMarkersArea,
258 const LogicalRect& aContentArea, uint32_t aLineNumber);
260 LogicalRect mContentArea;
261 nsDisplayListBuilder* mBuilder;
262 nsIFrame* mBlock;
263 nsIScrollableFrame* mScrollableFrame;
264 nsDisplayList mMarkerList;
265 nsSize mBlockSize;
266 WritingMode mBlockWM;
267 bool mCanHaveInlineAxisScrollbar;
268 // When we're in a -webkit-line-clamp context, we should ignore inline-end
269 // text-overflow markers. See nsBlockFrame::IsInLineClampContext.
270 const bool mInLineClampContext;
271 bool mAdjustForPixelSnapping;
273 class Marker {
274 public:
275 void Init(const StyleTextOverflowSide& aStyle) {
276 mInitialized = false;
277 mISize = 0;
278 mStyle = &aStyle;
279 mIntrinsicISize = 0;
280 mHasOverflow = false;
281 mHasBlockEllipsis = false;
282 mActive = false;
283 mEdgeAligned = false;
287 * Setup the marker string and calculate its size, if not done already.
289 void SetupString(nsIFrame* aFrame);
291 bool IsSuppressed(bool aInLineClampContext) const {
292 if (aInLineClampContext) {
293 return !mHasBlockEllipsis;
295 return mStyle->IsClip();
297 bool IsNeeded() const { return mHasOverflow || mHasBlockEllipsis; }
298 void Reset() {
299 mHasOverflow = false;
300 mHasBlockEllipsis = false;
301 mEdgeAligned = false;
304 // The current width of the marker, the range is [0 .. mIntrinsicISize].
305 nscoord mISize;
306 // The intrinsic width of the marker.
307 nscoord mIntrinsicISize;
308 // The text-overflow style for this side. Ignored if we're rendering a
309 // block ellipsis.
310 const StyleTextOverflowSide* mStyle;
311 // True if there is visible overflowing inline content on this side.
312 bool mHasOverflow;
313 // True if this side has a block ellipsis (from -webkit-line-clamp).
314 bool mHasBlockEllipsis;
315 // True if mISize and mIntrinsicISize have been setup from style.
316 bool mInitialized;
317 // True if the style is not text-overflow:clip on this side and the marker
318 // won't cause the line to become empty.
319 bool mActive;
320 // True if this marker is aligned to the edge of the content box, so that
321 // when scrolling the marker doesn't jump around.
322 bool mEdgeAligned;
325 Marker mIStart; // the inline start marker
326 Marker mIEnd; // the inline end marker
329 } // namespace css
330 } // namespace mozilla
332 #endif /* !defined(TextOverflow_h_) */