Coverage Report

Created: 2018-09-25 14:53

/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_) */