layout/generic/nsLineLayout.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim:cindent:ts=2:et:sw=2:
     3  *
     4  * This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     7  * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
     8  * described herein are Copyright (c) International Business Machines Corporation, 2000.
     9  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
    10  *
    11  * Date             Modified by     Description of modification
    12  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
    13  */
    15 /* state and methods used while laying out a single line of a block frame */
    17 #ifndef nsLineLayout_h___
    18 #define nsLineLayout_h___
    20 #include "nsLineBox.h"
    21 #include "nsBlockReflowState.h"
    22 #include "plarena.h"
    23 #include "gfxTypes.h"
    24 #include "WritingModes.h"
    26 class nsFloatManager;
    27 struct nsStyleText;
    29 class nsLineLayout {
    30 public:
    31   nsLineLayout(nsPresContext* aPresContext,
    32                nsFloatManager* aFloatManager,
    33                const nsHTMLReflowState* aOuterReflowState,
    34                const nsLineList::iterator* aLine);
    35   ~nsLineLayout();
    37   void Init(nsBlockReflowState* aState, nscoord aMinLineBSize,
    38             int32_t aLineNumber) {
    39     mBlockRS = aState;
    40     mMinLineBSize = aMinLineBSize;
    41     mLineNumber = aLineNumber;
    42   }
    44   int32_t GetLineNumber() const {
    45     return mLineNumber;
    46   }
    48   void BeginLineReflow(nscoord aICoord, nscoord aBCoord,
    49                        nscoord aISize, nscoord aBSize,
    50                        bool aImpactedByFloats,
    51                        bool aIsTopOfPage,
    52                        mozilla::WritingMode aWritingMode,
    53                        nscoord aContainerWidth);
    55   void EndLineReflow();
    57   /**
    58    * Called when a float has been placed. This method updates the
    59    * inline frame and span data to account for any change in positions
    60    * due to available space for the line boxes changing.
    61    * @param aX/aY/aWidth/aHeight are the new available
    62    * space rectangle, relative to the containing block.
    63    * @param aFloatFrame the float frame that was placed.
    64    */
    65   void UpdateBand(const nsRect& aNewAvailableSpace,
    66                   nsIFrame* aFloatFrame);
    68   void BeginSpan(nsIFrame* aFrame, const nsHTMLReflowState* aSpanReflowState,
    69                  nscoord aLeftEdge, nscoord aRightEdge, nscoord* aBaseline);
    71   // Returns the width of the span
    72   nscoord EndSpan(nsIFrame* aFrame);
    74   int32_t GetCurrentSpanCount() const;
    76   void SplitLineTo(int32_t aNewCount);
    78   bool IsZeroBSize();
    80   // Reflows the frame and returns the reflow status. aPushedFrame is true
    81   // if the frame is pushed to the next line because it doesn't fit
    82   nsresult ReflowFrame(nsIFrame* aFrame,
    83                        nsReflowStatus& aReflowStatus,
    84                        nsHTMLReflowMetrics* aMetrics,
    85                        bool& aPushedFrame);
    87   void AddBulletFrame(nsIFrame* aFrame, const nsHTMLReflowMetrics& aMetrics);
    89   void RemoveBulletFrame(nsIFrame* aFrame) {
    90     PushFrame(aFrame);
    91   }
    93   void BlockDirAlignLine();
    95   bool TrimTrailingWhiteSpace();
    97   void InlineDirAlignFrames(nsLineBox* aLine, bool aIsLastLine);
    99   /**
   100    * Handle all the relative positioning in the line, compute the
   101    * combined area (== overflow area) for the line, and handle view
   102    * sizing/positioning and the setting of the overflow rect.
   103    */
   104   void RelativePositionFrames(nsOverflowAreas& aOverflowAreas);
   106   // Support methods for word-wrapping during line reflow
   108   void SetTextJustificationWeights(int32_t aNumSpaces, int32_t aNumLetters) {
   109     mTextJustificationNumSpaces = aNumSpaces;
   110     mTextJustificationNumLetters = aNumLetters;
   111   }
   113   /**
   114    * @return true if so far during reflow no non-empty content has been
   115    * placed in the line (according to nsIFrame::IsEmpty())
   116    */
   117   bool LineIsEmpty() const
   118   {
   119     return mLineIsEmpty;
   120   }
   122   /**
   123    * @return true if so far during reflow no non-empty leaf content
   124    * (non-collapsed whitespace, replaced element, inline-block, etc) has been
   125    * placed in the line
   126    */
   127   bool LineAtStart() const
   128   {
   129     return mLineAtStart;
   130   }
   132   bool LineIsBreakable() const;
   134   bool GetLineEndsInBR() const 
   135   { 
   136     return mLineEndsInBR;
   137   }
   139   void SetLineEndsInBR(bool aOn) 
   140   { 
   141     mLineEndsInBR = aOn;
   142   }
   144   //----------------------------------------
   145   // Inform the line-layout about the presence of a floating frame
   146   // XXX get rid of this: use get-frame-type?
   147   bool AddFloat(nsIFrame* aFloat, nscoord aAvailableWidth)
   148   {
   149     return mBlockRS->AddFloat(this, aFloat, aAvailableWidth);
   150   }
   152   void SetTrimmableWidth(nscoord aTrimmableWidth) {
   153     mTrimmableWidth = aTrimmableWidth;
   154   }
   156   //----------------------------------------
   158   bool GetFirstLetterStyleOK() const {
   159     return mFirstLetterStyleOK;
   160   }
   162   void SetFirstLetterStyleOK(bool aSetting) {
   163     mFirstLetterStyleOK = aSetting;
   164   }
   166   bool GetInFirstLetter() const {
   167     return mInFirstLetter;
   168   }
   170   void SetInFirstLetter(bool aSetting) {
   171     mInFirstLetter = aSetting;
   172   }
   174   bool GetInFirstLine() const {
   175     return mInFirstLine;
   176   }
   178   void SetInFirstLine(bool aSetting) {
   179     mInFirstLine = aSetting;
   180   }
   182   // Calling this during block reflow ensures that the next line of inlines
   183   // will be marked dirty, if there is one.
   184   void SetDirtyNextLine() {
   185     mDirtyNextLine = true;
   186   }
   187   bool GetDirtyNextLine() {
   188     return mDirtyNextLine;
   189   }
   191   //----------------------------------------
   193   nsPresContext* mPresContext;
   195   /**
   196    * Record where an optional break could have been placed. During line reflow,
   197    * frames containing optional break points (e.g., whitespace in text frames)
   198    * can call SetLastOptionalBreakPosition to record where a break could
   199    * have been made, but wasn't because we decided to place more content on
   200    * the line. For non-text frames, offset 0 means
   201    * before the content, offset INT32_MAX means after the content.
   202    * 
   203    * Currently this is used to handle cases where a single word comprises
   204    * multiple frames, and the first frame fits on the line but the whole word
   205    * doesn't. We look back to the last optional break position and
   206    * reflow the whole line again, forcing a break at that position. The last
   207    * optional break position could be in a text frame or else after a frame
   208    * that cannot be part of a text run, so those are the positions we record.
   209    * 
   210    * @param aFits set to true if the break position is within the available width.
   211    * 
   212    * @param aPriority the priority of the break opportunity. If we are
   213    * prioritizing break opportunities, we will not set a break if we have
   214    * already set a break with a higher priority. @see gfxBreakPriority.
   215    *
   216    * @return true if we are actually reflowing with forced break position and we
   217    * should break here
   218    */
   219   bool NotifyOptionalBreakPosition(nsIContent* aContent, int32_t aOffset,
   220                                      bool aFits, gfxBreakPriority aPriority) {
   221     NS_ASSERTION(!aFits || !mNeedBackup,
   222                   "Shouldn't be updating the break position with a break that fits after we've already flagged an overrun");
   223     // Remember the last break position that fits; if there was no break that fit,
   224     // just remember the first break
   225     if ((aFits && aPriority >= mLastOptionalBreakPriority) ||
   226         !mLastOptionalBreakContent) {
   227       mLastOptionalBreakContent = aContent;
   228       mLastOptionalBreakContentOffset = aOffset;
   229       mLastOptionalBreakPriority = aPriority;
   230     }
   231     return aContent && mForceBreakContent == aContent &&
   232       mForceBreakContentOffset == aOffset;
   233   }
   234   /**
   235    * Like NotifyOptionalBreakPosition, but here it's OK for mNeedBackup
   236    * to be set, because the caller is merely pruning some saved break position(s)
   237    * that are actually not feasible.
   238    */
   239   void RestoreSavedBreakPosition(nsIContent* aContent, int32_t aOffset,
   240                                  gfxBreakPriority aPriority) {
   241     mLastOptionalBreakContent = aContent;
   242     mLastOptionalBreakContentOffset = aOffset;
   243     mLastOptionalBreakPriority = aPriority;
   244   }
   245   /**
   246    * Signal that no backing up will be required after all.
   247    */
   248   void ClearOptionalBreakPosition() {
   249     mNeedBackup = false;
   250     mLastOptionalBreakContent = nullptr;
   251     mLastOptionalBreakContentOffset = -1;
   252     mLastOptionalBreakPriority = gfxBreakPriority::eNoBreak;
   253   }
   254   // Retrieve last set optional break position. When this returns null, no
   255   // optional break has been recorded (which means that the line can't break yet).
   256   nsIContent* GetLastOptionalBreakPosition(int32_t* aOffset,
   257                                            gfxBreakPriority* aPriority) {
   258     *aOffset = mLastOptionalBreakContentOffset;
   259     *aPriority = mLastOptionalBreakPriority;
   260     return mLastOptionalBreakContent;
   261   }
   263   /**
   264    * Check whether frames overflowed the available width and CanPlaceFrame
   265    * requested backing up to a saved break position.
   266    */  
   267   bool NeedsBackup() { return mNeedBackup; }
   269   // Line layout may place too much content on a line, overflowing its available
   270   // width. When that happens, if SetLastOptionalBreakPosition has been
   271   // used to record an optional break that wasn't taken, we can reflow the line
   272   // again and force the break to happen at that point (i.e., backtracking
   273   // to the last choice point).
   275   // Record that we want to break at the given content+offset (which
   276   // should have been previously returned by GetLastOptionalBreakPosition
   277   // from another nsLineLayout).
   278   void ForceBreakAtPosition(nsIContent* aContent, int32_t aOffset) {
   279     mForceBreakContent = aContent;
   280     mForceBreakContentOffset = aOffset;
   281   }
   282   bool HaveForcedBreakPosition() { return mForceBreakContent != nullptr; }
   283   int32_t GetForcedBreakPosition(nsIContent* aContent) {
   284     return mForceBreakContent == aContent ? mForceBreakContentOffset : -1;
   285   }
   287   /**
   288    * This can't be null. It usually returns a block frame but may return
   289    * some other kind of frame when inline frames are reflowed in a non-block
   290    * context (e.g. MathML or floating first-letter).
   291    */
   292   nsIFrame* LineContainerFrame() const { return mBlockReflowState->frame; }
   293   const nsHTMLReflowState* LineContainerRS() const { return mBlockReflowState; }
   294   const nsLineList::iterator* GetLine() const {
   295     return mGotLineBox ? &mLineBox : nullptr;
   296   }
   297   nsLineList::iterator* GetLine() {
   298     return mGotLineBox ? &mLineBox : nullptr;
   299   }
   301   /**
   302    * Returns the accumulated advance width of frames before the current frame
   303    * on the line, plus the line container's left border+padding.
   304    * This is always positive, the advance width is measured from
   305    * the right edge for RTL blocks and from the left edge for LTR blocks.
   306    * In other words, the current frame's distance from the line container's
   307    * start content edge is:
   308    * <code>GetCurrentFrameInlineDistanceFromBlock() - lineContainer->GetUsedBorderAndPadding().left</code>
   309    * Note the use of <code>.left</code> for both LTR and RTL line containers.
   310    */
   311   nscoord GetCurrentFrameInlineDistanceFromBlock();
   313 protected:
   314   // This state is constant for a given block frame doing line layout
   315   nsFloatManager* mFloatManager;
   316   const nsStyleText* mStyleText; // for the block
   317   const nsHTMLReflowState* mBlockReflowState;
   319   nsIContent* mLastOptionalBreakContent;
   320   nsIContent* mForceBreakContent;
   322   // XXX remove this when landing bug 154892 (splitting absolute positioned frames)
   323   friend class nsInlineFrame;
   325   nsBlockReflowState* mBlockRS;/* XXX hack! */
   327   nsLineList::iterator mLineBox;
   329   // Per-frame data recorded by the line-layout reflow logic. This
   330   // state is the state needed to post-process the line after reflow
   331   // has completed (block-direction alignment, inline-direction alignment,
   332   // justification and relative positioning).
   334   struct PerSpanData;
   335   struct PerFrameData;
   336   friend struct PerSpanData;
   337   friend struct PerFrameData;
   338   struct PerFrameData
   339   {
   340     PerFrameData(mozilla::WritingMode aWritingMode)
   341       : mBounds(aWritingMode)
   342       , mMargin(aWritingMode)
   343       , mBorderPadding(aWritingMode)
   344       , mOffsets(aWritingMode)
   345     {}
   347     // link to next/prev frame in same span
   348     PerFrameData* mNext;
   349     PerFrameData* mPrev;
   351     // pointer to child span data if this is an inline container frame
   352     PerSpanData* mSpan;
   354     // The frame
   355     nsIFrame* mFrame;
   357     // From metrics
   358     nscoord mAscent;
   359     // note that mBounds is a logical rect in the *line*'s writing mode.
   360     // When setting frame coordinates, we have to convert to the frame's
   361     //  writing mode
   362     mozilla::LogicalRect mBounds;
   363     nsOverflowAreas mOverflowAreas;
   365     // From reflow-state
   366     mozilla::LogicalMargin mMargin;
   367     mozilla::LogicalMargin mBorderPadding;
   368     mozilla::LogicalMargin mOffsets;
   370     // state for text justification
   371     int32_t mJustificationNumSpaces;
   372     int32_t mJustificationNumLetters;
   374     // Other state we use
   375     uint8_t mBlockDirAlign;
   377 // PerFrameData flags
   378 #define PFD_RELATIVEPOS                 0x00000001
   379 #define PFD_ISTEXTFRAME                 0x00000002
   380 #define PFD_ISNONEMPTYTEXTFRAME         0x00000004
   381 #define PFD_ISNONWHITESPACETEXTFRAME    0x00000008
   382 #define PFD_ISLETTERFRAME               0x00000010
   383 #define PFD_RECOMPUTEOVERFLOW           0x00000020
   384 #define PFD_ISBULLET                    0x00000040
   385 #define PFD_SKIPWHENTRIMMINGWHITESPACE  0x00000080
   386 #define PFD_LASTFLAG                    PFD_SKIPWHENTRIMMINGWHITESPACE
   388     uint8_t mFlags;
   390     void SetFlag(uint32_t aFlag, bool aValue)
   391     {
   392       NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
   393       NS_ASSERTION(aFlag<=UINT8_MAX, "bad flag");
   394       if (aValue) { // set flag
   395         mFlags |= aFlag;
   396       }
   397       else {        // unset flag
   398         mFlags &= ~aFlag;
   399       }
   400     }
   402     bool GetFlag(uint32_t aFlag) const
   403     {
   404       NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
   405       return !!(mFlags & aFlag);
   406     }
   409     PerFrameData* Last() {
   410       PerFrameData* pfd = this;
   411       while (pfd->mNext) {
   412         pfd = pfd->mNext;
   413       }
   414       return pfd;
   415     }
   416   };
   417   PerFrameData* mFrameFreeList;
   419   struct PerSpanData {
   420     union {
   421       PerSpanData* mParent;
   422       PerSpanData* mNextFreeSpan;
   423     };
   424     PerFrameData* mFrame;
   425     PerFrameData* mFirstFrame;
   426     PerFrameData* mLastFrame;
   428     const nsHTMLReflowState* mReflowState;
   429     bool mNoWrap;
   430     mozilla::WritingMode mWritingMode;
   431     bool mZeroEffectiveSpanBox;
   432     bool mContainsFloat;
   433     bool mHasNonemptyContent;
   435     nscoord mIStart;
   436     nscoord mICoord;
   437     nscoord mIEnd;
   439     nscoord mBStartLeading, mBEndLeading;
   440     nscoord mLogicalBSize;
   441     nscoord mMinBCoord, mMaxBCoord;
   442     nscoord* mBaseline;
   444     void AppendFrame(PerFrameData* pfd) {
   445       if (nullptr == mLastFrame) {
   446         mFirstFrame = pfd;
   447       }
   448       else {
   449         mLastFrame->mNext = pfd;
   450         pfd->mPrev = mLastFrame;
   451       }
   452       mLastFrame = pfd;
   453     }
   454   };
   455   PerSpanData* mSpanFreeList;
   456   PerSpanData* mRootSpan;
   457   PerSpanData* mCurrentSpan;
   459   gfxBreakPriority mLastOptionalBreakPriority;
   460   int32_t     mLastOptionalBreakContentOffset;
   461   int32_t     mForceBreakContentOffset;
   463   nscoord mMinLineBSize;
   465   // The amount of text indent that we applied to this line, needed for
   466   // max-element-size calculation.
   467   nscoord mTextIndent;
   469   // This state varies during the reflow of a line but is line
   470   // "global" state not span "local" state.
   471   int32_t mLineNumber;
   472   int32_t mTextJustificationNumSpaces;
   473   int32_t mTextJustificationNumLetters;
   475   int32_t mTotalPlacedFrames;
   477   nscoord mBStartEdge;
   478   nscoord mMaxStartBoxBSize;
   479   nscoord mMaxEndBoxBSize;
   481   nscoord mInflationMinFontSize;
   483   // Final computed line-bSize value after BlockDirAlignFrames for
   484   // the block has been called.
   485   nscoord mFinalLineBSize;
   487   // Amount of trimmable whitespace width for the trailing text frame, if any
   488   nscoord mTrimmableWidth;
   490   nscoord mContainerWidth;
   492   bool mFirstLetterStyleOK      : 1;
   493   bool mIsTopOfPage             : 1;
   494   bool mImpactedByFloats        : 1;
   495   bool mLastFloatWasLetterFrame : 1;
   496   bool mLineIsEmpty             : 1;
   497   bool mLineEndsInBR            : 1;
   498   bool mNeedBackup              : 1;
   499   bool mInFirstLine             : 1;
   500   bool mGotLineBox              : 1;
   501   bool mInFirstLetter           : 1;
   502   bool mHasBullet               : 1;
   503   bool mDirtyNextLine           : 1;
   504   bool mLineAtStart             : 1;
   506   int32_t mSpanDepth;
   507 #ifdef DEBUG
   508   int32_t mSpansAllocated, mSpansFreed;
   509   int32_t mFramesAllocated, mFramesFreed;
   510 #endif
   511   PLArenaPool mArena; // Per span and per frame data, 4 byte aligned
   513   /**
   514    * Allocate a PerFrameData from the mArena pool. The allocation is infallible.
   515    */
   516   PerFrameData* NewPerFrameData(nsIFrame* aFrame);
   518   /**
   519    * Allocate a PerSpanData from the mArena pool. The allocation is infallible.
   520    */
   521   PerSpanData* NewPerSpanData();
   523   void FreeSpan(PerSpanData* psd);
   525   bool InBlockContext() const {
   526     return mSpanDepth == 0;
   527   }
   529   void PushFrame(nsIFrame* aFrame);
   531   void AllowForStartMargin(PerFrameData* pfd,
   532                            nsHTMLReflowState& aReflowState);
   534   bool CanPlaceFrame(PerFrameData* pfd,
   535                        bool aNotSafeToBreak,
   536                        bool aFrameCanContinueTextRun,
   537                        bool aCanRollBackBeforeFrame,
   538                        nsHTMLReflowMetrics& aMetrics,
   539                        nsReflowStatus& aStatus,
   540                        bool* aOptionalBreakAfterFits);
   542   void PlaceFrame(PerFrameData* pfd,
   543                   nsHTMLReflowMetrics& aMetrics);
   545   void BlockDirAlignFrames(PerSpanData* psd);
   547   void PlaceStartEndFrames(PerSpanData* psd,
   548                            nscoord aDistanceFromStart,
   549                            nscoord aLineBSize);
   551   void RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas);
   553   bool TrimTrailingWhiteSpaceIn(PerSpanData* psd, nscoord* aDeltaISize);
   555   void ComputeJustificationWeights(PerSpanData* psd, int32_t* numSpaces, int32_t* numLetters);
   557   struct FrameJustificationState {
   558     int32_t mTotalNumSpaces;
   559     int32_t mTotalNumLetters;
   560     nscoord mTotalWidthForSpaces;
   561     nscoord mTotalWidthForLetters;
   562     int32_t mNumSpacesProcessed;
   563     int32_t mNumLettersProcessed;
   564     nscoord mWidthForSpacesProcessed;
   565     nscoord mWidthForLettersProcessed;
   566   };
   568   // Apply justification.  The return value is the amount by which the width of
   569   // the span corresponding to aPSD got increased due to justification.
   570   nscoord ApplyFrameJustification(PerSpanData* aPSD,
   571                                   FrameJustificationState* aState);
   574 #ifdef DEBUG
   575   void DumpPerSpanData(PerSpanData* psd, int32_t aIndent);
   576 #endif
   577 };
   579 #endif /* nsLineLayout_h___ */

mercurial