/src/mozilla-central/layout/generic/TextOverflow.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef TextOverflow_h_ |
8 | | #define TextOverflow_h_ |
9 | | |
10 | | #include "nsDisplayList.h" |
11 | | #include "nsTHashtable.h" |
12 | | #include "mozilla/Attributes.h" |
13 | | #include "mozilla/Likely.h" |
14 | | #include "mozilla/UniquePtr.h" |
15 | | #include "mozilla/WritingModes.h" |
16 | | #include <algorithm> |
17 | | |
18 | | class nsIScrollableFrame; |
19 | | class nsLineBox; |
20 | | |
21 | | namespace mozilla { |
22 | | namespace css { |
23 | | |
24 | | /** |
25 | | * A class for rendering CSS3 text-overflow. |
26 | | * Usage: |
27 | | * 1. allocate an object using WillProcessLines |
28 | | * 2. then call ProcessLine for each line you are building display lists for |
29 | | */ |
30 | | class TextOverflow final { |
31 | | public: |
32 | | /** |
33 | | * Allocate an object for text-overflow processing. |
34 | | * @return nullptr if no processing is necessary. The caller owns the object. |
35 | | */ |
36 | | static Maybe<TextOverflow> |
37 | | WillProcessLines(nsDisplayListBuilder* aBuilder, |
38 | | nsIFrame* aBlockFrame); |
39 | | |
40 | | /** |
41 | | * Constructor, which client code SHOULD NOT use directly. Instead, clients |
42 | | * should call WillProcessLines(), which is basically the factory function |
43 | | * for TextOverflow instances. |
44 | | */ |
45 | | TextOverflow(nsDisplayListBuilder* aBuilder, |
46 | | nsIFrame* aBlockFrame); |
47 | | |
48 | | TextOverflow() = default; |
49 | 0 | ~TextOverflow() = default; |
50 | 0 | TextOverflow(TextOverflow&&) = default; |
51 | | TextOverflow(const TextOverflow&) = delete; |
52 | | TextOverflow& operator=(TextOverflow&&) = default; |
53 | | TextOverflow& operator=(const TextOverflow&) = delete; |
54 | | |
55 | | /** |
56 | | * Analyze the display lists for text overflow and what kind of item is at |
57 | | * the content edges. Add display items for text-overflow markers as needed |
58 | | * and remove or clip items that would overlap a marker. |
59 | | */ |
60 | | void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine, uint32_t aLineNumber); |
61 | | |
62 | | /** |
63 | | * Get the resulting text-overflow markers (the list may be empty). |
64 | | * @return a DisplayList containing any text-overflow markers. |
65 | | */ |
66 | 0 | nsDisplayList& GetMarkers() { return mMarkerList; } |
67 | | |
68 | | /** |
69 | | * @return true if aBlockFrmae has text-overflow:clip on both sides. |
70 | | */ |
71 | | static bool HasClippedOverflow(nsIFrame* aBlockFrame); |
72 | | /** |
73 | | * @return true if aBlockFrame needs analysis for text overflow. |
74 | | */ |
75 | | static bool CanHaveTextOverflow(nsIFrame* aBlockFrame); |
76 | | |
77 | | typedef nsTHashtable<nsPtrHashKey<nsIFrame>> FrameHashtable; |
78 | | |
79 | | private: |
80 | | typedef mozilla::WritingMode WritingMode; |
81 | | typedef mozilla::LogicalRect LogicalRect; |
82 | | |
83 | | struct AlignmentEdges { |
84 | 0 | AlignmentEdges() : mIStart(0), mIEnd(0), mAssigned(false) {} |
85 | | void Accumulate(WritingMode aWM, const LogicalRect& aRect) |
86 | 0 | { |
87 | 0 | if (MOZ_LIKELY(mAssigned)) { |
88 | 0 | mIStart = std::min(mIStart, aRect.IStart(aWM)); |
89 | 0 | mIEnd = std::max(mIEnd, aRect.IEnd(aWM)); |
90 | 0 | } else { |
91 | 0 | mIStart = aRect.IStart(aWM); |
92 | 0 | mIEnd = aRect.IEnd(aWM); |
93 | 0 | mAssigned = true; |
94 | 0 | } |
95 | 0 | } |
96 | 0 | nscoord ISize() { return mIEnd - mIStart; } |
97 | | nscoord mIStart; |
98 | | nscoord mIEnd; |
99 | | bool mAssigned; |
100 | | }; |
101 | | |
102 | | struct InnerClipEdges { |
103 | | InnerClipEdges() |
104 | | : mIStart(0) |
105 | | , mIEnd(0) |
106 | | , mAssignedIStart(false) |
107 | 0 | , mAssignedIEnd(false) {} |
108 | | void AccumulateIStart(WritingMode aWM, const LogicalRect& aRect) |
109 | 0 | { |
110 | 0 | if (MOZ_LIKELY(mAssignedIStart)) { |
111 | 0 | mIStart = std::max(mIStart, aRect.IStart(aWM)); |
112 | 0 | } else { |
113 | 0 | mIStart = aRect.IStart(aWM); |
114 | 0 | mAssignedIStart = true; |
115 | 0 | } |
116 | 0 | } |
117 | | void AccumulateIEnd(WritingMode aWM, const LogicalRect& aRect) |
118 | 0 | { |
119 | 0 | if (MOZ_LIKELY(mAssignedIEnd)) { |
120 | 0 | mIEnd = std::min(mIEnd, aRect.IEnd(aWM)); |
121 | 0 | } else { |
122 | 0 | mIEnd = aRect.IEnd(aWM); |
123 | 0 | mAssignedIEnd = true; |
124 | 0 | } |
125 | 0 | } |
126 | | nscoord mIStart; |
127 | | nscoord mIEnd; |
128 | | bool mAssignedIStart; |
129 | | bool mAssignedIEnd; |
130 | | }; |
131 | | |
132 | | LogicalRect |
133 | | GetLogicalScrollableOverflowRectRelativeToBlock(nsIFrame* aFrame) const |
134 | 0 | { |
135 | 0 | return LogicalRect(mBlockWM, |
136 | 0 | aFrame->GetScrollableOverflowRect() + |
137 | 0 | aFrame->GetOffsetTo(mBlock), |
138 | 0 | mBlockSize); |
139 | 0 | } |
140 | | |
141 | | /** |
142 | | * Examines frames on the line to determine whether we should draw a left |
143 | | * and/or right marker, and if so, which frames should be completely hidden |
144 | | * and the bounds of what will be displayed between the markers. |
145 | | * @param aLine the line we're processing |
146 | | * @param aFramesToHide frames that should have their display items removed |
147 | | * @param aAlignmentEdges the outermost edges of all text and atomic |
148 | | * inline-level frames that are inside the area between the markers |
149 | | * @return the area inside which we should add any markers; |
150 | | * this is the block's content area narrowed by any floats on this line. |
151 | | */ |
152 | | LogicalRect ExamineLineFrames(nsLineBox* aLine, |
153 | | FrameHashtable* aFramesToHide, |
154 | | AlignmentEdges* aAlignmentEdges); |
155 | | |
156 | | /** |
157 | | * LineHasOverflowingText calls this to analyze edges, both the block's |
158 | | * content edges and the hypothetical marker edges aligned at the block edges. |
159 | | * @param aFrame the descendant frame of mBlock that we're analyzing |
160 | | * @param aContentArea the block's content area |
161 | | * @param aInsideMarkersArea the rectangle between the markers |
162 | | * @param aFramesToHide frames that should have their display items removed |
163 | | * @param aAlignmentEdges the outermost edges of all text and atomic |
164 | | * inline-level frames that are inside the area between the markers |
165 | | * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic |
166 | | * inline-level frame is visible between the marker edges |
167 | | * @param aClippedMarkerEdges the innermost edges of all text and atomic |
168 | | * inline-level frames that are clipped by the current marker width |
169 | | */ |
170 | | void ExamineFrameSubtree(nsIFrame* aFrame, |
171 | | const LogicalRect& aContentArea, |
172 | | const LogicalRect& aInsideMarkersArea, |
173 | | FrameHashtable* aFramesToHide, |
174 | | AlignmentEdges* aAlignmentEdges, |
175 | | bool* aFoundVisibleTextOrAtomic, |
176 | | InnerClipEdges* aClippedMarkerEdges); |
177 | | |
178 | | /** |
179 | | * ExamineFrameSubtree calls this to analyze a frame against the hypothetical |
180 | | * marker edges (aInsideMarkersArea) for text frames and atomic inline-level |
181 | | * elements. A text frame adds its extent inside aInsideMarkersArea where |
182 | | * grapheme clusters are fully visible. An atomic adds its border box if |
183 | | * it's fully inside aInsideMarkersArea, otherwise the frame is added to |
184 | | * aFramesToHide. |
185 | | * @param aFrame the descendant frame of mBlock that we're analyzing |
186 | | * @param aFrameType aFrame's frame type |
187 | | * @param aInsideMarkersArea the rectangle between the markers |
188 | | * @param aFramesToHide frames that should have their display items removed |
189 | | * @param aAlignmentEdges the outermost edges of all text and atomic |
190 | | * inline-level frames that are inside the area between the markers |
191 | | * inside aInsideMarkersArea |
192 | | * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic |
193 | | * inline-level frame is visible between the marker edges |
194 | | * @param aClippedMarkerEdges the innermost edges of all text and atomic |
195 | | * inline-level frames that are clipped by the current marker width |
196 | | */ |
197 | | void AnalyzeMarkerEdges(nsIFrame* aFrame, |
198 | | mozilla::LayoutFrameType aFrameType, |
199 | | const LogicalRect& aInsideMarkersArea, |
200 | | FrameHashtable* aFramesToHide, |
201 | | AlignmentEdges* aAlignmentEdges, |
202 | | bool* aFoundVisibleTextOrAtomic, |
203 | | InnerClipEdges* aClippedMarkerEdges); |
204 | | |
205 | | /** |
206 | | * Clip or remove items given the final marker edges. ("clip" here just means |
207 | | * assigning mVisIStartEdge/mVisIEndEdge for any nsCharClipDisplayItem that |
208 | | * needs it; see nsDisplayList.h for a description of that item). |
209 | | * @param aFramesToHide remove display items for these frames |
210 | | * @param aInsideMarkersArea is the area inside the markers |
211 | | */ |
212 | | void PruneDisplayListContents(nsDisplayList* aList, |
213 | | const FrameHashtable& aFramesToHide, |
214 | | const LogicalRect& aInsideMarkersArea); |
215 | | |
216 | | /** |
217 | | * ProcessLine calls this to create display items for the markers and insert |
218 | | * them into mMarkerList. |
219 | | * @param aLine the line we're processing |
220 | | * @param aCreateIStart if true, create a marker on the inline start side |
221 | | * @param aCreateIEnd if true, create a marker on the inline end side |
222 | | * @param aInsideMarkersArea is the area inside the markers |
223 | | * @param aContentArea is the area inside which we should add the markers; |
224 | | * this is the block's content area narrowed by any floats on this line. |
225 | | */ |
226 | | void CreateMarkers(const nsLineBox* aLine, |
227 | | bool aCreateIStart, bool aCreateIEnd, |
228 | | const LogicalRect& aInsideMarkersArea, |
229 | | const LogicalRect& aContentArea, |
230 | | uint32_t aLineNumber); |
231 | | |
232 | | LogicalRect mContentArea; |
233 | | nsDisplayListBuilder* mBuilder; |
234 | | nsIFrame* mBlock; |
235 | | nsIScrollableFrame* mScrollableFrame; |
236 | | nsDisplayList mMarkerList; |
237 | | nsSize mBlockSize; |
238 | | WritingMode mBlockWM; |
239 | | bool mCanHaveInlineAxisScrollbar; |
240 | | bool mAdjustForPixelSnapping; |
241 | | |
242 | | class Marker { |
243 | | public: |
244 | 0 | void Init(const nsStyleTextOverflowSide& aStyle) { |
245 | 0 | mInitialized = false; |
246 | 0 | mISize = 0; |
247 | 0 | mStyle = &aStyle; |
248 | 0 | mIntrinsicISize = 0; |
249 | 0 | mHasOverflow = false; |
250 | 0 | mActive = false; |
251 | 0 | } |
252 | | |
253 | | /** |
254 | | * Setup the marker string and calculate its size, if not done already. |
255 | | */ |
256 | | void SetupString(nsIFrame* aFrame); |
257 | | |
258 | 0 | bool IsNeeded() const { |
259 | 0 | return mHasOverflow; |
260 | 0 | } |
261 | 0 | void Reset() { |
262 | 0 | mHasOverflow = false; |
263 | 0 | } |
264 | | |
265 | | // The current width of the marker, the range is [0 .. mIntrinsicISize]. |
266 | | nscoord mISize; |
267 | | // The intrinsic width of the marker. |
268 | | nscoord mIntrinsicISize; |
269 | | // The style for this side. |
270 | | const nsStyleTextOverflowSide* mStyle; |
271 | | // True if there is visible overflowing inline content on this side. |
272 | | bool mHasOverflow; |
273 | | // True if mMarkerString and mWidth have been setup from style. |
274 | | bool mInitialized; |
275 | | // True if the style is text-overflow:clip on this side and the marker |
276 | | // won't cause the line to become empty. |
277 | | bool mActive; |
278 | | }; |
279 | | |
280 | | Marker mIStart; // the inline start marker |
281 | | Marker mIEnd; // the inline end marker |
282 | | }; |
283 | | |
284 | | } // namespace css |
285 | | } // namespace mozilla |
286 | | |
287 | | #endif /* !defined(TextOverflow_h_) */ |