michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef TextOverflow_h_ michael@0: #define TextOverflow_h_ michael@0: michael@0: #include "nsDisplayList.h" michael@0: #include "nsTHashtable.h" michael@0: #include "mozilla/Likely.h" michael@0: #include michael@0: michael@0: class nsIScrollableFrame; michael@0: class gfxTextRun; michael@0: class nsLineBox; michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: /** michael@0: * A class for rendering CSS3 text-overflow. michael@0: * Usage: michael@0: * 1. allocate an object using WillProcessLines michael@0: * 2. then call ProcessLine for each line you are building display lists for michael@0: */ michael@0: class TextOverflow { michael@0: public: michael@0: /** michael@0: * Allocate an object for text-overflow processing. michael@0: * @return nullptr if no processing is necessary. The caller owns the object. michael@0: */ michael@0: static TextOverflow* WillProcessLines(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aBlockFrame); michael@0: /** michael@0: * Analyze the display lists for text overflow and what kind of item is at michael@0: * the content edges. Add display items for text-overflow markers as needed michael@0: * and remove or clip items that would overlap a marker. michael@0: */ michael@0: void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine); michael@0: michael@0: /** michael@0: * Get the resulting text-overflow markers (the list may be empty). michael@0: * @return a DisplayList containing any text-overflow markers. michael@0: */ michael@0: nsDisplayList& GetMarkers() { return mMarkerList; } michael@0: michael@0: /** michael@0: * @return true if aBlockFrame needs analysis for text overflow. michael@0: */ michael@0: static bool CanHaveTextOverflow(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aBlockFrame); michael@0: michael@0: typedef nsTHashtable > FrameHashtable; michael@0: michael@0: protected: michael@0: TextOverflow() {} michael@0: void Init(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aBlockFrame); michael@0: michael@0: struct AlignmentEdges { michael@0: AlignmentEdges() : mAssigned(false) {} michael@0: void Accumulate(const nsRect& aRect) { michael@0: if (MOZ_LIKELY(mAssigned)) { michael@0: x = std::min(x, aRect.X()); michael@0: xmost = std::max(xmost, aRect.XMost()); michael@0: } else { michael@0: x = aRect.X(); michael@0: xmost = aRect.XMost(); michael@0: mAssigned = true; michael@0: } michael@0: } michael@0: nscoord Width() { return xmost - x; } michael@0: nscoord x; michael@0: nscoord xmost; michael@0: bool mAssigned; michael@0: }; michael@0: michael@0: struct InnerClipEdges { michael@0: InnerClipEdges() : mAssignedLeft(false), mAssignedRight(false) {} michael@0: void AccumulateLeft(const nsRect& aRect) { michael@0: if (MOZ_LIKELY(mAssignedLeft)) { michael@0: mLeft = std::max(mLeft, aRect.X()); michael@0: } else { michael@0: mLeft = aRect.X(); michael@0: mAssignedLeft = true; michael@0: } michael@0: } michael@0: void AccumulateRight(const nsRect& aRect) { michael@0: if (MOZ_LIKELY(mAssignedRight)) { michael@0: mRight = std::min(mRight, aRect.XMost()); michael@0: } else { michael@0: mRight = aRect.XMost(); michael@0: mAssignedRight = true; michael@0: } michael@0: } michael@0: nscoord mLeft; michael@0: nscoord mRight; michael@0: bool mAssignedLeft; michael@0: bool mAssignedRight; michael@0: }; michael@0: michael@0: /** michael@0: * Examines frames on the line to determine whether we should draw a left michael@0: * and/or right marker, and if so, which frames should be completely hidden michael@0: * and the bounds of what will be displayed between the markers. michael@0: * @param aLine the line we're processing michael@0: * @param aFramesToHide frames that should have their display items removed michael@0: * @param aAlignmentEdges the outermost edges of all text and atomic michael@0: * inline-level frames that are inside the area between the markers michael@0: */ michael@0: void ExamineLineFrames(nsLineBox* aLine, michael@0: FrameHashtable* aFramesToHide, michael@0: AlignmentEdges* aAlignmentEdges); michael@0: michael@0: /** michael@0: * LineHasOverflowingText calls this to analyze edges, both the block's michael@0: * content edges and the hypothetical marker edges aligned at the block edges. michael@0: * @param aFrame the descendant frame of mBlock that we're analyzing michael@0: * @param aContentArea the block's content area michael@0: * @param aInsideMarkersArea the rectangle between the markers michael@0: * @param aFramesToHide frames that should have their display items removed michael@0: * @param aAlignmentEdges the outermost edges of all text and atomic michael@0: * inline-level frames that are inside the area between the markers michael@0: * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic michael@0: * inline-level frame is visible between the marker edges michael@0: * @param aClippedMarkerEdges the innermost edges of all text and atomic michael@0: * inline-level frames that are clipped by the current marker width michael@0: */ michael@0: void ExamineFrameSubtree(nsIFrame* aFrame, michael@0: const nsRect& aContentArea, michael@0: const nsRect& aInsideMarkersArea, michael@0: FrameHashtable* aFramesToHide, michael@0: AlignmentEdges* aAlignmentEdges, michael@0: bool* aFoundVisibleTextOrAtomic, michael@0: InnerClipEdges* aClippedMarkerEdges); michael@0: michael@0: /** michael@0: * ExamineFrameSubtree calls this to analyze a frame against the hypothetical michael@0: * marker edges (aInsideMarkersArea) for text frames and atomic inline-level michael@0: * elements. A text frame adds its extent inside aInsideMarkersArea where michael@0: * grapheme clusters are fully visible. An atomic adds its border box if michael@0: * it's fully inside aInsideMarkersArea, otherwise the frame is added to michael@0: * aFramesToHide. michael@0: * @param aFrame the descendant frame of mBlock that we're analyzing michael@0: * @param aFrameType aFrame's frame type michael@0: * @param aInsideMarkersArea the rectangle between the markers michael@0: * @param aFramesToHide frames that should have their display items removed michael@0: * @param aAlignmentEdges the outermost edges of all text and atomic michael@0: * inline-level frames that are inside the area between the markers michael@0: * inside aInsideMarkersArea michael@0: * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic michael@0: * inline-level frame is visible between the marker edges michael@0: * @param aClippedMarkerEdges the innermost edges of all text and atomic michael@0: * inline-level frames that are clipped by the current marker width michael@0: */ michael@0: void AnalyzeMarkerEdges(nsIFrame* aFrame, michael@0: const nsIAtom* aFrameType, michael@0: const nsRect& aInsideMarkersArea, michael@0: FrameHashtable* aFramesToHide, michael@0: AlignmentEdges* aAlignmentEdges, michael@0: bool* aFoundVisibleTextOrAtomic, michael@0: InnerClipEdges* aClippedMarkerEdges); michael@0: michael@0: /** michael@0: * Clip or remove items given the final marker edges. ("clip" here just means michael@0: * assigning mLeftEdge/mRightEdge for any nsCharClipDisplayItem that needs it, michael@0: * see nsDisplayList.h for a description of that item). michael@0: * @param aFramesToHide remove display items for these frames michael@0: * @param aInsideMarkersArea is the area inside the markers michael@0: */ michael@0: void PruneDisplayListContents(nsDisplayList* aList, michael@0: const FrameHashtable& aFramesToHide, michael@0: const nsRect& aInsideMarkersArea); michael@0: michael@0: /** michael@0: * ProcessLine calls this to create display items for the markers and insert michael@0: * them into mMarkerList. michael@0: * @param aLine the line we're processing michael@0: * @param aCreateLeft if true, create a marker on the left side michael@0: * @param aCreateRight if true, create a marker on the right side michael@0: * @param aInsideMarkersArea is the area inside the markers michael@0: */ michael@0: void CreateMarkers(const nsLineBox* aLine, michael@0: bool aCreateLeft, michael@0: bool aCreateRight, michael@0: const nsRect& aInsideMarkersArea); michael@0: michael@0: nsRect mContentArea; michael@0: nsDisplayListBuilder* mBuilder; michael@0: nsIFrame* mBlock; michael@0: nsIScrollableFrame* mScrollableFrame; michael@0: nsDisplayList mMarkerList; michael@0: bool mBlockIsRTL; michael@0: bool mCanHaveHorizontalScrollbar; michael@0: bool mAdjustForPixelSnapping; michael@0: michael@0: class Marker { michael@0: public: michael@0: void Init(const nsStyleTextOverflowSide& aStyle) { michael@0: mInitialized = false; michael@0: mWidth = 0; michael@0: mStyle = &aStyle; michael@0: } michael@0: michael@0: /** michael@0: * Setup the marker string and calculate its size, if not done already. michael@0: */ michael@0: void SetupString(nsIFrame* aFrame); michael@0: michael@0: bool IsNeeded() const { michael@0: return mHasOverflow; michael@0: } michael@0: void Reset() { michael@0: mHasOverflow = false; michael@0: } michael@0: michael@0: // The current width of the marker, the range is [0 .. mIntrinsicWidth]. michael@0: nscoord mWidth; michael@0: // The intrinsic width of the marker. michael@0: nscoord mIntrinsicWidth; michael@0: // The style for this side. michael@0: const nsStyleTextOverflowSide* mStyle; michael@0: // True if there is visible overflowing inline content on this side. michael@0: bool mHasOverflow; michael@0: // True if mMarkerString and mWidth have been setup from style. michael@0: bool mInitialized; michael@0: // True if the style is text-overflow:clip on this side and the marker michael@0: // won't cause the line to become empty. michael@0: bool mActive; michael@0: }; michael@0: michael@0: Marker mLeft; // the horizontal left marker michael@0: Marker mRight; // the horizontal right marker michael@0: }; michael@0: michael@0: } // namespace css michael@0: } // namespace mozilla michael@0: michael@0: #endif /* !defined(TextOverflow_h_) */