forked from mirrors/gecko-dev
		
	 99ab2bae3c
			
		
	
	
		99ab2bae3c
		
	
	
	
	
		
			
			This patch also reorders the destructor/constructor decls so that all the deleted ones are bunched together. Differential Revision: https://phabricator.services.mozilla.com/D160547
		
			
				
	
	
		
			332 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifndef TextOverflow_h_
 | |
| #define TextOverflow_h_
 | |
| 
 | |
| #include "nsDisplayList.h"
 | |
| #include "nsTHashSet.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/Likely.h"
 | |
| #include "mozilla/UniquePtr.h"
 | |
| #include "mozilla/WritingModes.h"
 | |
| #include <algorithm>
 | |
| 
 | |
| class nsIScrollableFrame;
 | |
| class nsBlockFrame;
 | |
| class nsLineBox;
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace css {
 | |
| 
 | |
| /**
 | |
|  * A class for rendering CSS3 text-overflow.
 | |
|  * Usage:
 | |
|  *  1. allocate an object using WillProcessLines
 | |
|  *  2. then call ProcessLine for each line you are building display lists for
 | |
|  *
 | |
|  * Note that this class is non-reassignable; we don't want to be making
 | |
|  * arbitrary copies. (But we do have a move constructor, since that's required
 | |
|  * in order to be stored in Maybe<>).
 | |
|  */
 | |
| class TextOverflow final {
 | |
|  private:
 | |
|   /**
 | |
|    * Private constructor, for internal use only. Client code should call
 | |
|    * WillProcessLines(), which is basically the factory function for
 | |
|    * TextOverflow instances.
 | |
|    */
 | |
|   TextOverflow(nsDisplayListBuilder* aBuilder, nsBlockFrame*);
 | |
| 
 | |
|  public:
 | |
|   ~TextOverflow() = default;
 | |
| 
 | |
|   /**
 | |
|    * Allocate an object for text-overflow processing. (Factory function.)
 | |
|    * @return nullptr if no processing is necessary.  The caller owns the object.
 | |
|    */
 | |
|   static Maybe<TextOverflow> WillProcessLines(nsDisplayListBuilder* aBuilder,
 | |
|                                               nsBlockFrame*);
 | |
| 
 | |
|   /**
 | |
|    * This is a factory-constructed non-reassignable class, so we delete nearly
 | |
|    * all constructors and reassignment operators.  We only provide a
 | |
|    * move-constructor, because that's required for Maybe<TextOverflow> to work
 | |
|    * (and that's what our factory method returns).
 | |
|    */
 | |
|   TextOverflow(TextOverflow&&) = default;
 | |
| 
 | |
|   TextOverflow() = delete;
 | |
|   TextOverflow(const TextOverflow&) = delete;
 | |
|   TextOverflow& operator=(const TextOverflow&) = delete;
 | |
|   TextOverflow& operator=(TextOverflow&&) = delete;
 | |
| 
 | |
|   /**
 | |
|    * Analyze the display lists for text overflow and what kind of item is at
 | |
|    * the content edges.  Add display items for text-overflow markers as needed
 | |
|    * and remove or clip items that would overlap a marker.
 | |
|    */
 | |
|   void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine,
 | |
|                    uint32_t aLineNumber);
 | |
| 
 | |
|   /**
 | |
|    * Get the resulting text-overflow markers (the list may be empty).
 | |
|    * @return a DisplayList containing any text-overflow markers.
 | |
|    */
 | |
|   nsDisplayList& GetMarkers() { return mMarkerList; }
 | |
| 
 | |
|   // Returns whether aBlockFrame has text-overflow:clip on both sides.
 | |
|   static bool HasClippedTextOverflow(nsIFrame* aBlockFrame);
 | |
| 
 | |
|   // Returns whether aBlockFrame has a block ellipsis on one of its lines.
 | |
|   static bool HasBlockEllipsis(nsIFrame* aBlockFrame);
 | |
| 
 | |
|   // Returns whether the given block frame needs analysis for text overflow.
 | |
|   // The BeforeReflow flag indicates whether we can be faster and more precise
 | |
|   // for line-clamp ellipsis (only returning true iff the block actually uses
 | |
|   // it).
 | |
|   enum class BeforeReflow : bool { No, Yes };
 | |
|   static bool CanHaveOverflowMarkers(nsBlockFrame*,
 | |
|                                      BeforeReflow = BeforeReflow::No);
 | |
| 
 | |
|   typedef nsTHashSet<nsIFrame*> FrameHashtable;
 | |
| 
 | |
|  private:
 | |
|   typedef mozilla::WritingMode WritingMode;
 | |
|   typedef mozilla::LogicalRect LogicalRect;
 | |
| 
 | |
|   // Edges to align the IStart and IEnd markers to.
 | |
|   struct AlignmentEdges {
 | |
|     AlignmentEdges()
 | |
|         : mIStart(0), mIEnd(0), mIEndOuter(0), mAssignedInner(false) {}
 | |
|     void AccumulateInner(WritingMode aWM, const LogicalRect& aRect) {
 | |
|       if (MOZ_LIKELY(mAssignedInner)) {
 | |
|         mIStart = std::min(mIStart, aRect.IStart(aWM));
 | |
|         mIEnd = std::max(mIEnd, aRect.IEnd(aWM));
 | |
|       } else {
 | |
|         mIStart = aRect.IStart(aWM);
 | |
|         mIEnd = aRect.IEnd(aWM);
 | |
|         mAssignedInner = true;
 | |
|       }
 | |
|     }
 | |
|     void AccumulateOuter(WritingMode aWM, const LogicalRect& aRect) {
 | |
|       mIEndOuter = std::max(mIEndOuter, aRect.IEnd(aWM));
 | |
|     }
 | |
|     nscoord ISize() { return mIEnd - mIStart; }
 | |
| 
 | |
|     // The outermost edges of all text and atomic inline-level frames that are
 | |
|     // inside the area between the markers.
 | |
|     nscoord mIStart;
 | |
|     nscoord mIEnd;
 | |
| 
 | |
|     // The closest IEnd edge of all text and atomic inline-level frames that
 | |
|     // fall completely before the IStart edge of the content area.  (Used to
 | |
|     // align a block ellipsis when there are no visible frames to align to.)
 | |
|     nscoord mIEndOuter;
 | |
| 
 | |
|     bool mAssignedInner;
 | |
|   };
 | |
| 
 | |
|   struct InnerClipEdges {
 | |
|     InnerClipEdges()
 | |
|         : mIStart(0), mIEnd(0), mAssignedIStart(false), mAssignedIEnd(false) {}
 | |
|     void AccumulateIStart(WritingMode aWM, const LogicalRect& aRect) {
 | |
|       if (MOZ_LIKELY(mAssignedIStart)) {
 | |
|         mIStart = std::max(mIStart, aRect.IStart(aWM));
 | |
|       } else {
 | |
|         mIStart = aRect.IStart(aWM);
 | |
|         mAssignedIStart = true;
 | |
|       }
 | |
|     }
 | |
|     void AccumulateIEnd(WritingMode aWM, const LogicalRect& aRect) {
 | |
|       if (MOZ_LIKELY(mAssignedIEnd)) {
 | |
|         mIEnd = std::min(mIEnd, aRect.IEnd(aWM));
 | |
|       } else {
 | |
|         mIEnd = aRect.IEnd(aWM);
 | |
|         mAssignedIEnd = true;
 | |
|       }
 | |
|     }
 | |
|     nscoord mIStart;
 | |
|     nscoord mIEnd;
 | |
|     bool mAssignedIStart;
 | |
|     bool mAssignedIEnd;
 | |
|   };
 | |
| 
 | |
|   LogicalRect GetLogicalScrollableOverflowRectRelativeToBlock(
 | |
|       nsIFrame* aFrame) const {
 | |
|     return LogicalRect(
 | |
|         mBlockWM,
 | |
|         aFrame->ScrollableOverflowRect() + aFrame->GetOffsetTo(mBlock),
 | |
|         mBlockSize);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Examines frames on the line to determine whether we should draw a left
 | |
|    * and/or right marker, and if so, which frames should be completely hidden
 | |
|    * and the bounds of what will be displayed between the markers.
 | |
|    * @param aLine the line we're processing
 | |
|    * @param aFramesToHide frames that should have their display items removed
 | |
|    * @param aAlignmentEdges edges the markers will be aligned to, including
 | |
|    *   the outermost edges of all text and atomic inline-level frames that
 | |
|    *   are inside the content area, and the closest IEnd edge of such a frame
 | |
|    *   outside the content area
 | |
|    * @return the area inside which we should add any markers;
 | |
|    *   this is the block's content area narrowed by any floats on this line.
 | |
|    */
 | |
|   LogicalRect ExamineLineFrames(nsLineBox* aLine, FrameHashtable* aFramesToHide,
 | |
|                                 AlignmentEdges* aAlignmentEdges);
 | |
| 
 | |
|   /**
 | |
|    * LineHasOverflowingText calls this to analyze edges, both the block's
 | |
|    * content edges and the hypothetical marker edges aligned at the block edges.
 | |
|    * @param aFrame the descendant frame of mBlock that we're analyzing
 | |
|    * @param aContentArea the block's content area
 | |
|    * @param aInsideMarkersArea the rectangle between the markers
 | |
|    * @param aFramesToHide frames that should have their display items removed
 | |
|    * @param aAlignmentEdges edges the markers will be aligned to, including
 | |
|    *   the outermost edges of all text and atomic inline-level frames that
 | |
|    *   are inside the content area, and the closest IEnd edge of such a frame
 | |
|    *   outside the content area
 | |
|    * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
 | |
|    *   inline-level frame is visible between the marker edges
 | |
|    * @param aClippedMarkerEdges the innermost edges of all text and atomic
 | |
|    *   inline-level frames that are clipped by the current marker width
 | |
|    */
 | |
|   void ExamineFrameSubtree(nsIFrame* aFrame, const LogicalRect& aContentArea,
 | |
|                            const LogicalRect& aInsideMarkersArea,
 | |
|                            FrameHashtable* aFramesToHide,
 | |
|                            AlignmentEdges* aAlignmentEdges,
 | |
|                            bool* aFoundVisibleTextOrAtomic,
 | |
|                            InnerClipEdges* aClippedMarkerEdges);
 | |
| 
 | |
|   /**
 | |
|    * ExamineFrameSubtree calls this to analyze a frame against the hypothetical
 | |
|    * marker edges (aInsideMarkersArea) for text frames and atomic inline-level
 | |
|    * elements.  A text frame adds its extent inside aInsideMarkersArea where
 | |
|    * grapheme clusters are fully visible.  An atomic adds its border box if
 | |
|    * it's fully inside aInsideMarkersArea, otherwise the frame is added to
 | |
|    * aFramesToHide.
 | |
|    * @param aFrame the descendant frame of mBlock that we're analyzing
 | |
|    * @param aFrameType aFrame's frame type
 | |
|    * @param aInsideMarkersArea the rectangle between the markers
 | |
|    * @param aFramesToHide frames that should have their display items removed
 | |
|    * @param aAlignmentEdges the outermost edges of all text and atomic
 | |
|    *   inline-level frames that are inside the area between the markers
 | |
|    *                       inside aInsideMarkersArea
 | |
|    * @param aAlignmentEdges edges the markers will be aligned to, including
 | |
|    *   the outermost edges of all text and atomic inline-level frames that
 | |
|    *   are inside aInsideMarkersArea, and the closest IEnd edge of such a frame
 | |
|    *   outside the content area
 | |
|    * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
 | |
|    *   inline-level frame is visible between the marker edges
 | |
|    * @param aClippedMarkerEdges the innermost edges of all text and atomic
 | |
|    *   inline-level frames that are clipped by the current marker width
 | |
|    */
 | |
|   void AnalyzeMarkerEdges(nsIFrame* aFrame, mozilla::LayoutFrameType aFrameType,
 | |
|                           const LogicalRect& aInsideMarkersArea,
 | |
|                           FrameHashtable* aFramesToHide,
 | |
|                           AlignmentEdges* aAlignmentEdges,
 | |
|                           bool* aFoundVisibleTextOrAtomic,
 | |
|                           InnerClipEdges* aClippedMarkerEdges);
 | |
| 
 | |
|   /**
 | |
|    * Clip or remove items given the final marker edges. ("clip" here just means
 | |
|    * assigning mVisIStartEdge/mVisIEndEdge for any nsCharClipDisplayItem that
 | |
|    * needs it; see nsDisplayList.h for a description of that item).
 | |
|    * @param aFramesToHide remove display items for these frames
 | |
|    * @param aInsideMarkersArea is the area inside the markers
 | |
|    */
 | |
|   void PruneDisplayListContents(nsDisplayList* aList,
 | |
|                                 const FrameHashtable& aFramesToHide,
 | |
|                                 const LogicalRect& aInsideMarkersArea);
 | |
| 
 | |
|   /**
 | |
|    * ProcessLine calls this to create display items for the markers and insert
 | |
|    * them into mMarkerList.
 | |
|    * @param aLine the line we're processing
 | |
|    * @param aCreateIStart if true, create a marker on the inline start side
 | |
|    * @param aCreateIEnd if true, create a marker on the inline end side
 | |
|    * @param aInsideMarkersArea is the area inside the markers
 | |
|    * @param aContentArea is the area inside which we should add the markers;
 | |
|    *   this is the block's content area narrowed by any floats on this line.
 | |
|    */
 | |
|   void CreateMarkers(const nsLineBox* aLine, bool aCreateIStart,
 | |
|                      bool aCreateIEnd, const LogicalRect& aInsideMarkersArea,
 | |
|                      const LogicalRect& aContentArea, uint32_t aLineNumber);
 | |
| 
 | |
|   LogicalRect mContentArea;
 | |
|   nsDisplayListBuilder* mBuilder;
 | |
|   nsIFrame* mBlock;
 | |
|   nsIScrollableFrame* mScrollableFrame;
 | |
|   nsDisplayList mMarkerList;
 | |
|   nsSize mBlockSize;
 | |
|   WritingMode mBlockWM;
 | |
|   bool mCanHaveInlineAxisScrollbar;
 | |
|   // When we're in a -webkit-line-clamp context, we should ignore inline-end
 | |
|   // text-overflow markers. See nsBlockFrame::IsInLineClampContext.
 | |
|   const bool mInLineClampContext;
 | |
|   bool mAdjustForPixelSnapping;
 | |
| 
 | |
|   class Marker {
 | |
|    public:
 | |
|     void Init(const StyleTextOverflowSide& aStyle) {
 | |
|       mInitialized = false;
 | |
|       mISize = 0;
 | |
|       mStyle = &aStyle;
 | |
|       mIntrinsicISize = 0;
 | |
|       mHasOverflow = false;
 | |
|       mHasBlockEllipsis = false;
 | |
|       mActive = false;
 | |
|       mEdgeAligned = false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Setup the marker string and calculate its size, if not done already.
 | |
|      */
 | |
|     void SetupString(nsIFrame* aFrame);
 | |
| 
 | |
|     bool IsSuppressed(bool aInLineClampContext) const {
 | |
|       if (aInLineClampContext) {
 | |
|         return !mHasBlockEllipsis;
 | |
|       }
 | |
|       return mStyle->IsClip();
 | |
|     }
 | |
|     bool IsNeeded() const { return mHasOverflow || mHasBlockEllipsis; }
 | |
|     void Reset() {
 | |
|       mHasOverflow = false;
 | |
|       mHasBlockEllipsis = false;
 | |
|       mEdgeAligned = false;
 | |
|     }
 | |
| 
 | |
|     // The current width of the marker, the range is [0 .. mIntrinsicISize].
 | |
|     nscoord mISize;
 | |
|     // The intrinsic width of the marker.
 | |
|     nscoord mIntrinsicISize;
 | |
|     // The text-overflow style for this side.  Ignored if we're rendering a
 | |
|     // block ellipsis.
 | |
|     const StyleTextOverflowSide* mStyle;
 | |
|     // True if there is visible overflowing inline content on this side.
 | |
|     bool mHasOverflow;
 | |
|     // True if this side has a block ellipsis (from -webkit-line-clamp).
 | |
|     bool mHasBlockEllipsis;
 | |
|     // True if mISize and mIntrinsicISize have been setup from style.
 | |
|     bool mInitialized;
 | |
|     // True if the style is not text-overflow:clip on this side and the marker
 | |
|     // won't cause the line to become empty.
 | |
|     bool mActive;
 | |
|     // True if this marker is aligned to the edge of the content box, so that
 | |
|     // when scrolling the marker doesn't jump around.
 | |
|     bool mEdgeAligned;
 | |
|   };
 | |
| 
 | |
|   Marker mIStart;  // the inline start marker
 | |
|   Marker mIEnd;    // the inline end marker
 | |
| };
 | |
| 
 | |
| }  // namespace css
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif /* !defined(TextOverflow_h_) */
 |