forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			280 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
	
		
			10 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 "nsTHashtable.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/Likely.h"
 | |
| #include "mozilla/UniquePtr.h"
 | |
| #include "mozilla/WritingModes.h"
 | |
| #include <algorithm>
 | |
| 
 | |
| class nsIScrollableFrame;
 | |
| 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
 | |
|  */
 | |
| class MOZ_HEAP_CLASS TextOverflow final {
 | |
|  public:
 | |
|   /**
 | |
|    * Allocate an object for text-overflow processing.
 | |
|    * @return nullptr if no processing is necessary.  The caller owns the object.
 | |
|    */
 | |
|   static UniquePtr<TextOverflow>
 | |
|   WillProcessLines(nsDisplayListBuilder* aBuilder,
 | |
|                    nsIFrame*             aBlockFrame);
 | |
| 
 | |
|   /**
 | |
|    * Constructor, which client code SHOULD NOT use directly. Instead, clients
 | |
|    * should call WillProcessLines(), which is basically the factory function
 | |
|    * for TextOverflow instances.
 | |
|    */
 | |
|   TextOverflow(nsDisplayListBuilder* aBuilder,
 | |
|                nsIFrame* aBlockFrame);
 | |
| 
 | |
|   /**
 | |
|    * 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; }
 | |
| 
 | |
|   /**
 | |
|    * @return true if aBlockFrmae has text-overflow:clip on both sides.
 | |
|    */
 | |
|   static bool HasClippedOverflow(nsIFrame* aBlockFrame);
 | |
|   /**
 | |
|    * @return true if aBlockFrame needs analysis for text overflow.
 | |
|    */
 | |
|   static bool CanHaveTextOverflow(nsIFrame* aBlockFrame);
 | |
| 
 | |
|   typedef nsTHashtable<nsPtrHashKey<nsIFrame>> FrameHashtable;
 | |
| 
 | |
|  private:
 | |
|   typedef mozilla::WritingMode WritingMode;
 | |
|   typedef mozilla::LogicalRect LogicalRect;
 | |
| 
 | |
|   struct AlignmentEdges {
 | |
|     AlignmentEdges() : mIStart(0), mIEnd(0), mAssigned(false) {}
 | |
|     void Accumulate(WritingMode aWM, const LogicalRect& aRect)
 | |
|     {
 | |
|       if (MOZ_LIKELY(mAssigned)) {
 | |
|         mIStart = std::min(mIStart, aRect.IStart(aWM));
 | |
|         mIEnd = std::max(mIEnd, aRect.IEnd(aWM));
 | |
|       } else {
 | |
|         mIStart = aRect.IStart(aWM);
 | |
|         mIEnd = aRect.IEnd(aWM);
 | |
|         mAssigned = true;
 | |
|       }
 | |
|     }
 | |
|     nscoord ISize() { return mIEnd - mIStart; }
 | |
|     nscoord mIStart;
 | |
|     nscoord mIEnd;
 | |
|     bool mAssigned;
 | |
|   };
 | |
| 
 | |
|   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->GetScrollableOverflowRect() +
 | |
|                          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 the outermost edges of all text and atomic
 | |
|    *   inline-level frames that are inside the area between the markers
 | |
|    * @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 the outermost edges of all text and atomic
 | |
|    *   inline-level frames that are inside the area between the markers
 | |
|    * @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 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;
 | |
|   bool                   mAdjustForPixelSnapping;
 | |
| 
 | |
|   class Marker {
 | |
|   public:
 | |
|     void Init(const nsStyleTextOverflowSide& aStyle) {
 | |
|       mInitialized = false;
 | |
|       mISize = 0;
 | |
|       mStyle = &aStyle;
 | |
|       mIntrinsicISize = 0;
 | |
|       mHasOverflow = false;
 | |
|       mActive = false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Setup the marker string and calculate its size, if not done already.
 | |
|      */
 | |
|     void SetupString(nsIFrame* aFrame);
 | |
| 
 | |
|     bool IsNeeded() const {
 | |
|       return mHasOverflow;
 | |
|     }
 | |
|     void Reset() {
 | |
|       mHasOverflow = false;
 | |
|     }
 | |
| 
 | |
|     // The current width of the marker, the range is [0 .. mIntrinsicISize].
 | |
|     nscoord                        mISize;
 | |
|     // The intrinsic width of the marker.
 | |
|     nscoord                        mIntrinsicISize;
 | |
|     // The style for this side.
 | |
|     const nsStyleTextOverflowSide* mStyle;
 | |
|     // True if there is visible overflowing inline content on this side.
 | |
|     bool                           mHasOverflow;
 | |
|     // True if mMarkerString and mWidth have been setup from style.
 | |
|     bool                           mInitialized;
 | |
|     // True if the style is text-overflow:clip on this side and the marker
 | |
|     // won't cause the line to become empty.
 | |
|     bool                           mActive;
 | |
|   };
 | |
| 
 | |
|   Marker mIStart; // the inline start marker
 | |
|   Marker mIEnd; // the inline end marker
 | |
| };
 | |
| 
 | |
| } // namespace css
 | |
| } // namespace mozilla
 | |
| 
 | |
| #endif /* !defined(TextOverflow_h_) */
 | 
