Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #ifndef TextOverflow_h_ |
michael@0 | 8 | #define TextOverflow_h_ |
michael@0 | 9 | |
michael@0 | 10 | #include "nsDisplayList.h" |
michael@0 | 11 | #include "nsTHashtable.h" |
michael@0 | 12 | #include "mozilla/Likely.h" |
michael@0 | 13 | #include <algorithm> |
michael@0 | 14 | |
michael@0 | 15 | class nsIScrollableFrame; |
michael@0 | 16 | class gfxTextRun; |
michael@0 | 17 | class nsLineBox; |
michael@0 | 18 | |
michael@0 | 19 | namespace mozilla { |
michael@0 | 20 | namespace css { |
michael@0 | 21 | |
michael@0 | 22 | /** |
michael@0 | 23 | * A class for rendering CSS3 text-overflow. |
michael@0 | 24 | * Usage: |
michael@0 | 25 | * 1. allocate an object using WillProcessLines |
michael@0 | 26 | * 2. then call ProcessLine for each line you are building display lists for |
michael@0 | 27 | */ |
michael@0 | 28 | class TextOverflow { |
michael@0 | 29 | public: |
michael@0 | 30 | /** |
michael@0 | 31 | * Allocate an object for text-overflow processing. |
michael@0 | 32 | * @return nullptr if no processing is necessary. The caller owns the object. |
michael@0 | 33 | */ |
michael@0 | 34 | static TextOverflow* WillProcessLines(nsDisplayListBuilder* aBuilder, |
michael@0 | 35 | nsIFrame* aBlockFrame); |
michael@0 | 36 | /** |
michael@0 | 37 | * Analyze the display lists for text overflow and what kind of item is at |
michael@0 | 38 | * the content edges. Add display items for text-overflow markers as needed |
michael@0 | 39 | * and remove or clip items that would overlap a marker. |
michael@0 | 40 | */ |
michael@0 | 41 | void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine); |
michael@0 | 42 | |
michael@0 | 43 | /** |
michael@0 | 44 | * Get the resulting text-overflow markers (the list may be empty). |
michael@0 | 45 | * @return a DisplayList containing any text-overflow markers. |
michael@0 | 46 | */ |
michael@0 | 47 | nsDisplayList& GetMarkers() { return mMarkerList; } |
michael@0 | 48 | |
michael@0 | 49 | /** |
michael@0 | 50 | * @return true if aBlockFrame needs analysis for text overflow. |
michael@0 | 51 | */ |
michael@0 | 52 | static bool CanHaveTextOverflow(nsDisplayListBuilder* aBuilder, |
michael@0 | 53 | nsIFrame* aBlockFrame); |
michael@0 | 54 | |
michael@0 | 55 | typedef nsTHashtable<nsPtrHashKey<nsIFrame> > FrameHashtable; |
michael@0 | 56 | |
michael@0 | 57 | protected: |
michael@0 | 58 | TextOverflow() {} |
michael@0 | 59 | void Init(nsDisplayListBuilder* aBuilder, |
michael@0 | 60 | nsIFrame* aBlockFrame); |
michael@0 | 61 | |
michael@0 | 62 | struct AlignmentEdges { |
michael@0 | 63 | AlignmentEdges() : mAssigned(false) {} |
michael@0 | 64 | void Accumulate(const nsRect& aRect) { |
michael@0 | 65 | if (MOZ_LIKELY(mAssigned)) { |
michael@0 | 66 | x = std::min(x, aRect.X()); |
michael@0 | 67 | xmost = std::max(xmost, aRect.XMost()); |
michael@0 | 68 | } else { |
michael@0 | 69 | x = aRect.X(); |
michael@0 | 70 | xmost = aRect.XMost(); |
michael@0 | 71 | mAssigned = true; |
michael@0 | 72 | } |
michael@0 | 73 | } |
michael@0 | 74 | nscoord Width() { return xmost - x; } |
michael@0 | 75 | nscoord x; |
michael@0 | 76 | nscoord xmost; |
michael@0 | 77 | bool mAssigned; |
michael@0 | 78 | }; |
michael@0 | 79 | |
michael@0 | 80 | struct InnerClipEdges { |
michael@0 | 81 | InnerClipEdges() : mAssignedLeft(false), mAssignedRight(false) {} |
michael@0 | 82 | void AccumulateLeft(const nsRect& aRect) { |
michael@0 | 83 | if (MOZ_LIKELY(mAssignedLeft)) { |
michael@0 | 84 | mLeft = std::max(mLeft, aRect.X()); |
michael@0 | 85 | } else { |
michael@0 | 86 | mLeft = aRect.X(); |
michael@0 | 87 | mAssignedLeft = true; |
michael@0 | 88 | } |
michael@0 | 89 | } |
michael@0 | 90 | void AccumulateRight(const nsRect& aRect) { |
michael@0 | 91 | if (MOZ_LIKELY(mAssignedRight)) { |
michael@0 | 92 | mRight = std::min(mRight, aRect.XMost()); |
michael@0 | 93 | } else { |
michael@0 | 94 | mRight = aRect.XMost(); |
michael@0 | 95 | mAssignedRight = true; |
michael@0 | 96 | } |
michael@0 | 97 | } |
michael@0 | 98 | nscoord mLeft; |
michael@0 | 99 | nscoord mRight; |
michael@0 | 100 | bool mAssignedLeft; |
michael@0 | 101 | bool mAssignedRight; |
michael@0 | 102 | }; |
michael@0 | 103 | |
michael@0 | 104 | /** |
michael@0 | 105 | * Examines frames on the line to determine whether we should draw a left |
michael@0 | 106 | * and/or right marker, and if so, which frames should be completely hidden |
michael@0 | 107 | * and the bounds of what will be displayed between the markers. |
michael@0 | 108 | * @param aLine the line we're processing |
michael@0 | 109 | * @param aFramesToHide frames that should have their display items removed |
michael@0 | 110 | * @param aAlignmentEdges the outermost edges of all text and atomic |
michael@0 | 111 | * inline-level frames that are inside the area between the markers |
michael@0 | 112 | */ |
michael@0 | 113 | void ExamineLineFrames(nsLineBox* aLine, |
michael@0 | 114 | FrameHashtable* aFramesToHide, |
michael@0 | 115 | AlignmentEdges* aAlignmentEdges); |
michael@0 | 116 | |
michael@0 | 117 | /** |
michael@0 | 118 | * LineHasOverflowingText calls this to analyze edges, both the block's |
michael@0 | 119 | * content edges and the hypothetical marker edges aligned at the block edges. |
michael@0 | 120 | * @param aFrame the descendant frame of mBlock that we're analyzing |
michael@0 | 121 | * @param aContentArea the block's content area |
michael@0 | 122 | * @param aInsideMarkersArea the rectangle between the markers |
michael@0 | 123 | * @param aFramesToHide frames that should have their display items removed |
michael@0 | 124 | * @param aAlignmentEdges the outermost edges of all text and atomic |
michael@0 | 125 | * inline-level frames that are inside the area between the markers |
michael@0 | 126 | * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic |
michael@0 | 127 | * inline-level frame is visible between the marker edges |
michael@0 | 128 | * @param aClippedMarkerEdges the innermost edges of all text and atomic |
michael@0 | 129 | * inline-level frames that are clipped by the current marker width |
michael@0 | 130 | */ |
michael@0 | 131 | void ExamineFrameSubtree(nsIFrame* aFrame, |
michael@0 | 132 | const nsRect& aContentArea, |
michael@0 | 133 | const nsRect& aInsideMarkersArea, |
michael@0 | 134 | FrameHashtable* aFramesToHide, |
michael@0 | 135 | AlignmentEdges* aAlignmentEdges, |
michael@0 | 136 | bool* aFoundVisibleTextOrAtomic, |
michael@0 | 137 | InnerClipEdges* aClippedMarkerEdges); |
michael@0 | 138 | |
michael@0 | 139 | /** |
michael@0 | 140 | * ExamineFrameSubtree calls this to analyze a frame against the hypothetical |
michael@0 | 141 | * marker edges (aInsideMarkersArea) for text frames and atomic inline-level |
michael@0 | 142 | * elements. A text frame adds its extent inside aInsideMarkersArea where |
michael@0 | 143 | * grapheme clusters are fully visible. An atomic adds its border box if |
michael@0 | 144 | * it's fully inside aInsideMarkersArea, otherwise the frame is added to |
michael@0 | 145 | * aFramesToHide. |
michael@0 | 146 | * @param aFrame the descendant frame of mBlock that we're analyzing |
michael@0 | 147 | * @param aFrameType aFrame's frame type |
michael@0 | 148 | * @param aInsideMarkersArea the rectangle between the markers |
michael@0 | 149 | * @param aFramesToHide frames that should have their display items removed |
michael@0 | 150 | * @param aAlignmentEdges the outermost edges of all text and atomic |
michael@0 | 151 | * inline-level frames that are inside the area between the markers |
michael@0 | 152 | * inside aInsideMarkersArea |
michael@0 | 153 | * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic |
michael@0 | 154 | * inline-level frame is visible between the marker edges |
michael@0 | 155 | * @param aClippedMarkerEdges the innermost edges of all text and atomic |
michael@0 | 156 | * inline-level frames that are clipped by the current marker width |
michael@0 | 157 | */ |
michael@0 | 158 | void AnalyzeMarkerEdges(nsIFrame* aFrame, |
michael@0 | 159 | const nsIAtom* aFrameType, |
michael@0 | 160 | const nsRect& aInsideMarkersArea, |
michael@0 | 161 | FrameHashtable* aFramesToHide, |
michael@0 | 162 | AlignmentEdges* aAlignmentEdges, |
michael@0 | 163 | bool* aFoundVisibleTextOrAtomic, |
michael@0 | 164 | InnerClipEdges* aClippedMarkerEdges); |
michael@0 | 165 | |
michael@0 | 166 | /** |
michael@0 | 167 | * Clip or remove items given the final marker edges. ("clip" here just means |
michael@0 | 168 | * assigning mLeftEdge/mRightEdge for any nsCharClipDisplayItem that needs it, |
michael@0 | 169 | * see nsDisplayList.h for a description of that item). |
michael@0 | 170 | * @param aFramesToHide remove display items for these frames |
michael@0 | 171 | * @param aInsideMarkersArea is the area inside the markers |
michael@0 | 172 | */ |
michael@0 | 173 | void PruneDisplayListContents(nsDisplayList* aList, |
michael@0 | 174 | const FrameHashtable& aFramesToHide, |
michael@0 | 175 | const nsRect& aInsideMarkersArea); |
michael@0 | 176 | |
michael@0 | 177 | /** |
michael@0 | 178 | * ProcessLine calls this to create display items for the markers and insert |
michael@0 | 179 | * them into mMarkerList. |
michael@0 | 180 | * @param aLine the line we're processing |
michael@0 | 181 | * @param aCreateLeft if true, create a marker on the left side |
michael@0 | 182 | * @param aCreateRight if true, create a marker on the right side |
michael@0 | 183 | * @param aInsideMarkersArea is the area inside the markers |
michael@0 | 184 | */ |
michael@0 | 185 | void CreateMarkers(const nsLineBox* aLine, |
michael@0 | 186 | bool aCreateLeft, |
michael@0 | 187 | bool aCreateRight, |
michael@0 | 188 | const nsRect& aInsideMarkersArea); |
michael@0 | 189 | |
michael@0 | 190 | nsRect mContentArea; |
michael@0 | 191 | nsDisplayListBuilder* mBuilder; |
michael@0 | 192 | nsIFrame* mBlock; |
michael@0 | 193 | nsIScrollableFrame* mScrollableFrame; |
michael@0 | 194 | nsDisplayList mMarkerList; |
michael@0 | 195 | bool mBlockIsRTL; |
michael@0 | 196 | bool mCanHaveHorizontalScrollbar; |
michael@0 | 197 | bool mAdjustForPixelSnapping; |
michael@0 | 198 | |
michael@0 | 199 | class Marker { |
michael@0 | 200 | public: |
michael@0 | 201 | void Init(const nsStyleTextOverflowSide& aStyle) { |
michael@0 | 202 | mInitialized = false; |
michael@0 | 203 | mWidth = 0; |
michael@0 | 204 | mStyle = &aStyle; |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | /** |
michael@0 | 208 | * Setup the marker string and calculate its size, if not done already. |
michael@0 | 209 | */ |
michael@0 | 210 | void SetupString(nsIFrame* aFrame); |
michael@0 | 211 | |
michael@0 | 212 | bool IsNeeded() const { |
michael@0 | 213 | return mHasOverflow; |
michael@0 | 214 | } |
michael@0 | 215 | void Reset() { |
michael@0 | 216 | mHasOverflow = false; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | // The current width of the marker, the range is [0 .. mIntrinsicWidth]. |
michael@0 | 220 | nscoord mWidth; |
michael@0 | 221 | // The intrinsic width of the marker. |
michael@0 | 222 | nscoord mIntrinsicWidth; |
michael@0 | 223 | // The style for this side. |
michael@0 | 224 | const nsStyleTextOverflowSide* mStyle; |
michael@0 | 225 | // True if there is visible overflowing inline content on this side. |
michael@0 | 226 | bool mHasOverflow; |
michael@0 | 227 | // True if mMarkerString and mWidth have been setup from style. |
michael@0 | 228 | bool mInitialized; |
michael@0 | 229 | // True if the style is text-overflow:clip on this side and the marker |
michael@0 | 230 | // won't cause the line to become empty. |
michael@0 | 231 | bool mActive; |
michael@0 | 232 | }; |
michael@0 | 233 | |
michael@0 | 234 | Marker mLeft; // the horizontal left marker |
michael@0 | 235 | Marker mRight; // the horizontal right marker |
michael@0 | 236 | }; |
michael@0 | 237 | |
michael@0 | 238 | } // namespace css |
michael@0 | 239 | } // namespace mozilla |
michael@0 | 240 | |
michael@0 | 241 | #endif /* !defined(TextOverflow_h_) */ |