michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: /* base class of all rendering objects */ michael@0: michael@0: #ifndef nsFrame_h___ michael@0: #define nsFrame_h___ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/EventForwards.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "nsBox.h" michael@0: #include "prlog.h" michael@0: michael@0: #include "nsIPresShell.h" michael@0: #include "nsHTMLReflowState.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsISelectionDisplay.h" michael@0: michael@0: /** michael@0: * nsFrame logging constants. We redefine the nspr michael@0: * PRLogModuleInfo.level field to be a bitfield. Each bit controls a michael@0: * specific type of logging. Each logging operation has associated michael@0: * inline methods defined below. michael@0: */ michael@0: #define NS_FRAME_TRACE_CALLS 0x1 michael@0: #define NS_FRAME_TRACE_PUSH_PULL 0x2 michael@0: #define NS_FRAME_TRACE_CHILD_REFLOW 0x4 michael@0: #define NS_FRAME_TRACE_NEW_FRAMES 0x8 michael@0: michael@0: #define NS_FRAME_LOG_TEST(_lm,_bit) (int((_lm)->level) & (_bit)) michael@0: michael@0: #ifdef DEBUG michael@0: #define NS_FRAME_LOG(_bit,_args) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (NS_FRAME_LOG_TEST(nsFrame::GetLogModuleInfo(),_bit)) { \ michael@0: PR_LogPrint _args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: #else michael@0: #define NS_FRAME_LOG(_bit,_args) michael@0: #endif michael@0: michael@0: // XXX Need to rework this so that logging is free when it's off michael@0: #ifdef DEBUG michael@0: #define NS_FRAME_TRACE_IN(_method) Trace(_method, true) michael@0: michael@0: #define NS_FRAME_TRACE_OUT(_method) Trace(_method, false) michael@0: michael@0: // XXX remove me michael@0: #define NS_FRAME_TRACE_MSG(_bit,_args) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (NS_FRAME_LOG_TEST(nsFrame::GetLogModuleInfo(),_bit)) { \ michael@0: TraceMsg _args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define NS_FRAME_TRACE(_bit,_args) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (NS_FRAME_LOG_TEST(nsFrame::GetLogModuleInfo(),_bit)) { \ michael@0: TraceMsg _args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define NS_FRAME_TRACE_REFLOW_IN(_method) Trace(_method, true) michael@0: michael@0: #define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) \ michael@0: Trace(_method, false, _status) michael@0: michael@0: #else michael@0: #define NS_FRAME_TRACE(_bits,_args) michael@0: #define NS_FRAME_TRACE_IN(_method) michael@0: #define NS_FRAME_TRACE_OUT(_method) michael@0: #define NS_FRAME_TRACE_MSG(_bits,_args) michael@0: #define NS_FRAME_TRACE_REFLOW_IN(_method) michael@0: #define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) michael@0: #endif michael@0: michael@0: // Frame allocation boilerplate macros. Every subclass of nsFrame michael@0: // must define its own operator new and GetAllocatedSize. If they do michael@0: // not, the per-frame recycler lists in nsPresArena will not work michael@0: // correctly, with potentially catastrophic consequences (not enough michael@0: // memory is allocated for a frame object). michael@0: michael@0: #define NS_DECL_FRAMEARENA_HELPERS \ michael@0: void* operator new(size_t, nsIPresShell*) MOZ_MUST_OVERRIDE; \ michael@0: virtual nsQueryFrame::FrameIID GetFrameId() MOZ_MUST_OVERRIDE; michael@0: michael@0: #define NS_IMPL_FRAMEARENA_HELPERS(class) \ michael@0: void* class::operator new(size_t sz, nsIPresShell* aShell) \ michael@0: { return aShell->AllocateFrame(nsQueryFrame::class##_id, sz); } \ michael@0: nsQueryFrame::FrameIID class::GetFrameId() \ michael@0: { return nsQueryFrame::class##_id; } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: struct nsBoxLayoutMetrics; michael@0: class nsDisplayBackgroundImage; michael@0: struct nsRect; michael@0: michael@0: /** michael@0: * Implementation of a simple frame that's not splittable and has no michael@0: * child frames. michael@0: * michael@0: * Sets the NS_FRAME_SYNCHRONIZE_FRAME_AND_VIEW bit, so the default michael@0: * behavior is to keep the frame and view position and size in sync. michael@0: */ michael@0: class nsFrame : public nsBox michael@0: { michael@0: public: michael@0: /** michael@0: * Create a new "empty" frame that maps a given piece of content into a michael@0: * 0,0 area. michael@0: */ michael@0: friend nsIFrame* NS_NewEmptyFrame(nsIPresShell* aShell, michael@0: nsStyleContext* aContext); michael@0: michael@0: private: michael@0: // Left undefined; nsFrame objects are never allocated from the heap. michael@0: void* operator new(size_t sz) CPP_THROW_NEW; michael@0: michael@0: protected: michael@0: // Overridden to prevent the global delete from being called, since michael@0: // the memory came out of an arena instead of the heap. michael@0: // michael@0: // Ideally this would be private and undefined, like the normal michael@0: // operator new. Unfortunately, the C++ standard requires an michael@0: // overridden operator delete to be accessible to any subclass that michael@0: // defines a virtual destructor, so we can only make it protected; michael@0: // worse, some C++ compilers will synthesize calls to this function michael@0: // from the "deleting destructors" that they emit in case of michael@0: // delete-expressions, so it can't even be undefined. michael@0: void operator delete(void* aPtr, size_t sz); michael@0: michael@0: public: michael@0: michael@0: // nsQueryFrame michael@0: NS_DECL_QUERYFRAME michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: // nsIFrame michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* asPrevInFlow) MOZ_OVERRIDE; michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) MOZ_OVERRIDE; michael@0: virtual nsresult AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: virtual nsresult InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: virtual nsresult RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) MOZ_OVERRIDE; michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: virtual nsStyleContext* GetAdditionalStyleContext(int32_t aIndex) const MOZ_OVERRIDE; michael@0: virtual void SetAdditionalStyleContext(int32_t aIndex, michael@0: nsStyleContext* aStyleContext) MOZ_OVERRIDE; michael@0: virtual void SetParent(nsIFrame* aParent) MOZ_OVERRIDE; michael@0: virtual nscoord GetBaseline() const MOZ_OVERRIDE; michael@0: virtual const nsFrameList& GetChildList(ChildListID aListID) const MOZ_OVERRIDE; michael@0: virtual void GetChildLists(nsTArray* aLists) const MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult HandleEvent(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent, michael@0: nsIContent** aContent) MOZ_OVERRIDE; michael@0: virtual nsresult GetCursor(const nsPoint& aPoint, michael@0: nsIFrame::Cursor& aCursor) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetPointFromOffset(int32_t inOffset, michael@0: nsPoint* outPoint) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset, michael@0: bool inHint, michael@0: int32_t* outFrameContentOffset, michael@0: nsIFrame** outChildFrame) MOZ_OVERRIDE; michael@0: michael@0: static nsresult GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext, michael@0: nsPeekOffsetStruct *aPos, michael@0: nsIFrame *aBlockFrame, michael@0: int32_t aLineStart, michael@0: int8_t aOutSideLimit michael@0: ); michael@0: michael@0: virtual nsresult CharacterDataChanged(CharacterDataChangeInfo* aInfo) MOZ_OVERRIDE; michael@0: virtual nsresult AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) MOZ_OVERRIDE; michael@0: virtual nsSplittableType GetSplittableType() const MOZ_OVERRIDE; michael@0: virtual nsIFrame* GetPrevContinuation() const MOZ_OVERRIDE; michael@0: virtual void SetPrevContinuation(nsIFrame*) MOZ_OVERRIDE; michael@0: virtual nsIFrame* GetNextContinuation() const MOZ_OVERRIDE; michael@0: virtual void SetNextContinuation(nsIFrame*) MOZ_OVERRIDE; michael@0: virtual nsIFrame* GetPrevInFlowVirtual() const MOZ_OVERRIDE; michael@0: virtual void SetPrevInFlow(nsIFrame*) MOZ_OVERRIDE; michael@0: virtual nsIFrame* GetNextInFlowVirtual() const MOZ_OVERRIDE; michael@0: virtual void SetNextInFlow(nsIFrame*) MOZ_OVERRIDE; michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult IsSelectable(bool* aIsSelectable, uint8_t* aSelectStyle) const MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) MOZ_OVERRIDE; michael@0: michael@0: virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) MOZ_OVERRIDE; michael@0: virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset, michael@0: bool aRespectClusters = true) MOZ_OVERRIDE; michael@0: virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, michael@0: int32_t* aOffset, PeekWordState *aState) MOZ_OVERRIDE; michael@0: /** michael@0: * Check whether we should break at a boundary between punctuation and michael@0: * non-punctuation. Only call it at a punctuation boundary michael@0: * (i.e. exactly one of the previous and next characters are punctuation). michael@0: * @param aForward true if we're moving forward in content order michael@0: * @param aPunctAfter true if the next character is punctuation michael@0: * @param aWhitespaceAfter true if the next character is whitespace michael@0: */ michael@0: bool BreakWordBetweenPunctuation(const PeekWordState* aState, michael@0: bool aForward, michael@0: bool aPunctAfter, bool aWhitespaceAfter, michael@0: bool aIsKeyboardSelect); michael@0: michael@0: virtual nsresult CheckVisibility(nsPresContext* aContext, int32_t aStartIndex, int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *_retval) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetOffsets(int32_t &aStart, int32_t &aEnd) const MOZ_OVERRIDE; michael@0: virtual void ChildIsDirty(nsIFrame* aChild) MOZ_OVERRIDE; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: virtual nsIFrame* GetParentStyleContextFrame() const MOZ_OVERRIDE { michael@0: return DoGetParentStyleContextFrame(); michael@0: } michael@0: michael@0: /** michael@0: * Do the work for getting the parent style context frame so that michael@0: * other frame's |GetParentStyleContextFrame| methods can call this michael@0: * method on *another* frame. (This function handles out-of-flow michael@0: * frames by using the frame manager's placeholder map and it also michael@0: * handles block-within-inline and generated content wrappers.) michael@0: */ michael@0: nsIFrame* DoGetParentStyleContextFrame() const; michael@0: michael@0: virtual bool IsEmpty() MOZ_OVERRIDE; michael@0: virtual bool IsSelfEmpty() MOZ_OVERRIDE; michael@0: michael@0: virtual void MarkIntrinsicWidthsDirty() MOZ_OVERRIDE; michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual void AddInlineMinWidth(nsRenderingContext *aRenderingContext, michael@0: InlineMinWidthData *aData) MOZ_OVERRIDE; michael@0: virtual void AddInlinePrefWidth(nsRenderingContext *aRenderingContext, michael@0: InlinePrefWidthData *aData) MOZ_OVERRIDE; michael@0: virtual IntrinsicWidthOffsetData michael@0: IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE; michael@0: virtual mozilla::IntrinsicSize GetIntrinsicSize() MOZ_OVERRIDE; michael@0: virtual nsSize GetIntrinsicRatio() MOZ_OVERRIDE; michael@0: michael@0: virtual nsSize ComputeSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, nsSize aPadding, michael@0: uint32_t aFlags) MOZ_OVERRIDE; michael@0: michael@0: // Compute tight bounds assuming this frame honours its border, background michael@0: // and outline, its children's tight bounds, and nothing else. michael@0: nsRect ComputeSimpleTightBounds(gfxContext* aContext) const; michael@0: michael@0: /** michael@0: * A helper, used by |nsFrame::ComputeSize| (for frames that need to michael@0: * override only this part of ComputeSize), that computes the size michael@0: * that should be returned when 'width', 'height', and michael@0: * min/max-width/height are all 'auto' or equivalent. michael@0: * michael@0: * In general, frames that can accept any computed width/height should michael@0: * override only ComputeAutoSize, and frames that cannot do so need to michael@0: * override ComputeSize to enforce their width/height invariants. michael@0: * michael@0: * Implementations may optimize by returning a garbage width if michael@0: * StylePosition()->mWidth.GetUnit() != eStyleUnit_Auto, and michael@0: * likewise for height, since in such cases the result is guaranteed michael@0: * to be unused. michael@0: */ michael@0: virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap); michael@0: michael@0: /** michael@0: * Utility function for ComputeAutoSize implementations. Return michael@0: * max(GetMinWidth(), min(aWidthInCB, GetPrefWidth())) michael@0: */ michael@0: nscoord ShrinkWidthToFit(nsRenderingContext *aRenderingContext, michael@0: nscoord aWidthInCB); michael@0: michael@0: virtual nsresult WillReflow(nsPresContext* aPresContext) MOZ_OVERRIDE; michael@0: /** michael@0: * Calculates the size of this frame after reflowing (calling Reflow on, and michael@0: * updating the size and position of) its children, as necessary. The michael@0: * calculated size is returned to the caller via the nsHTMLReflowMetrics michael@0: * outparam. (The caller is responsible for setting the actual size and michael@0: * position of this frame.) michael@0: * michael@0: * A frame's children must _all_ be reflowed if the frame is dirty (the michael@0: * NS_FRAME_IS_DIRTY bit is set on it). Otherwise, individual children michael@0: * must be reflowed if they are dirty or have the NS_FRAME_HAS_DIRTY_CHILDREN michael@0: * bit set on them. Otherwise, whether children need to be reflowed depends michael@0: * on the frame's type (it's up to individual Reflow methods), and on what michael@0: * has changed. For example, a change in the width of the frame may require michael@0: * all of its children to be reflowed (even those without dirty bits set on michael@0: * them), whereas a change in its height might not. michael@0: * (nsHTMLReflowState::ShouldReflowAllKids may be helpful in deciding whether michael@0: * to reflow all the children, but for some frame types it might result in michael@0: * over-reflow.) michael@0: * michael@0: * Note: if it's only the overflow rect(s) of a frame that need to be michael@0: * updated, then UpdateOverflow should be called instead of Reflow. michael@0: */ michael@0: virtual nsresult Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) MOZ_OVERRIDE; michael@0: virtual nsresult DidReflow(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nsDidReflowStatus aStatus) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * NOTE: aStatus is assumed to be already-initialized. The reflow statuses of michael@0: * any reflowed absolute children will be merged into aStatus; aside from michael@0: * that, this method won't modify aStatus. michael@0: */ michael@0: void ReflowAbsoluteFrames(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: bool aConstrainHeight = true); michael@0: void FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: bool aConstrainHeight = true); michael@0: virtual bool CanContinueTextRun() const MOZ_OVERRIDE; michael@0: michael@0: virtual bool UpdateOverflow() MOZ_OVERRIDE; michael@0: michael@0: // Selection Methods michael@0: michael@0: NS_IMETHOD HandlePress(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus); michael@0: michael@0: NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus, michael@0: bool aControlHeld); michael@0: michael@0: NS_IMETHOD HandleDrag(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus); michael@0: michael@0: NS_IMETHOD HandleRelease(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus); michael@0: michael@0: enum { SELECT_ACCUMULATE = 0x01 }; michael@0: michael@0: nsresult PeekBackwardAndForward(nsSelectionAmount aAmountBack, michael@0: nsSelectionAmount aAmountForward, michael@0: int32_t aStartPos, michael@0: nsPresContext* aPresContext, michael@0: bool aJumpLines, michael@0: uint32_t aSelectFlags); michael@0: michael@0: nsresult SelectByTypeAtPoint(nsPresContext* aPresContext, michael@0: const nsPoint& aPoint, michael@0: nsSelectionAmount aBeginAmountType, michael@0: nsSelectionAmount aEndAmountType, michael@0: uint32_t aSelectFlags); michael@0: michael@0: // Helper for GetContentAndOffsetsFromPoint; calculation of content offsets michael@0: // in this function assumes there is no child frame that can be targeted. michael@0: virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint); michael@0: michael@0: // Box layout methods michael@0: virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; michael@0: virtual nsSize GetMinSize(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; michael@0: virtual nsSize GetMaxSize(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; michael@0: virtual nscoord GetFlex(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; michael@0: virtual nscoord GetBoxAscent(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; michael@0: michael@0: // We compute and store the HTML content's overflow area. So don't michael@0: // try to compute it in the box code. michael@0: virtual bool ComputesOwnOverflowArea() MOZ_OVERRIDE { return true; } michael@0: michael@0: //-------------------------------------------------- michael@0: // Additional methods michael@0: michael@0: // Helper function that tests if the frame tree is too deep; if it is michael@0: // it marks the frame as "unflowable", zeroes out the metrics, sets michael@0: // the reflow status, and returns true. Otherwise, the frame is michael@0: // unmarked "unflowable" and the metrics and reflow status are not michael@0: // touched and false is returned. michael@0: bool IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus& aStatus); michael@0: michael@0: // Incorporate the child overflow areas into aOverflowAreas. michael@0: // If the child does not have a overflow, use the child area. michael@0: void ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas, michael@0: nsIFrame* aChildFrame); michael@0: michael@0: /** michael@0: * @return true if we should avoid a page/column break in this frame. michael@0: */ michael@0: bool ShouldAvoidBreakInside(const nsHTMLReflowState& aReflowState) const { michael@0: return !aReflowState.mFlags.mIsTopOfPage && michael@0: NS_STYLE_PAGE_BREAK_AVOID == StyleDisplay()->mBreakInside && michael@0: !GetPrevInFlow(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /** michael@0: * Tracing method that writes a method enter/exit routine to the michael@0: * nspr log using the nsIFrame log module. The tracing is only michael@0: * done when the NS_FRAME_TRACE_CALLS bit is set in the log module's michael@0: * level field. michael@0: */ michael@0: void Trace(const char* aMethod, bool aEnter); michael@0: void Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus); michael@0: void TraceMsg(const char* fmt, ...); michael@0: michael@0: // Helper function that verifies that each frame in the list has the michael@0: // NS_FRAME_IS_DIRTY bit set michael@0: static void VerifyDirtyBitSet(const nsFrameList& aFrameList); michael@0: michael@0: static void XMLQuote(nsString& aString); michael@0: michael@0: /** michael@0: * Dump out the "base classes" regression data. This should dump michael@0: * out the interior data, not the "frame" XML container. And it michael@0: * should call the base classes same named method before doing michael@0: * anything specific in a derived class. This means that derived michael@0: * classes need not override DumpRegressionData unless they need michael@0: * some custom behavior that requires changing how the outer "frame" michael@0: * XML container is dumped. michael@0: */ michael@0: virtual void DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent); michael@0: michael@0: // Display Reflow Debugging michael@0: static void* DisplayReflowEnter(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: const nsHTMLReflowState& aReflowState); michael@0: static void* DisplayLayoutEnter(nsIFrame* aFrame); michael@0: static void* DisplayIntrinsicWidthEnter(nsIFrame* aFrame, michael@0: const char* aType); michael@0: static void* DisplayIntrinsicSizeEnter(nsIFrame* aFrame, michael@0: const char* aType); michael@0: static void DisplayReflowExit(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: uint32_t aStatus, michael@0: void* aFrameTreeNode); michael@0: static void DisplayLayoutExit(nsIFrame* aFrame, michael@0: void* aFrameTreeNode); michael@0: static void DisplayIntrinsicWidthExit(nsIFrame* aFrame, michael@0: const char* aType, michael@0: nscoord aResult, michael@0: void* aFrameTreeNode); michael@0: static void DisplayIntrinsicSizeExit(nsIFrame* aFrame, michael@0: const char* aType, michael@0: nsSize aResult, michael@0: void* aFrameTreeNode); michael@0: michael@0: static void DisplayReflowStartup(); michael@0: static void DisplayReflowShutdown(); michael@0: #endif michael@0: michael@0: /** michael@0: * Adds display items for standard CSS background if necessary. michael@0: * Does not check IsVisibleForPainting. michael@0: * @param aForceBackground draw the background even if the frame michael@0: * background style appears to have no background --- this is useful michael@0: * for frames that might receive a propagated background via michael@0: * nsCSSRendering::FindBackground michael@0: * @return whether a themed background item was created. michael@0: */ michael@0: bool DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists, michael@0: bool aForceBackground); michael@0: /** michael@0: * Adds display items for standard CSS borders, background and outline for michael@0: * for this frame, as necessary. Checks IsVisibleForPainting and won't michael@0: * display anything if the frame is not visible. michael@0: * @param aForceBackground draw the background even if the frame michael@0: * background style appears to have no background --- this is useful michael@0: * for frames that might receive a propagated background via michael@0: * nsCSSRendering::FindBackground michael@0: */ michael@0: void DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists, michael@0: bool aForceBackground = false); michael@0: /** michael@0: * Add a display item for the CSS outline. Does not check visibility. michael@0: */ michael@0: void DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists); michael@0: /** michael@0: * Add a display item for the CSS outline, after calling michael@0: * IsVisibleForPainting to confirm we are visible. michael@0: */ michael@0: void DisplayOutline(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists); michael@0: michael@0: /** michael@0: * Adjust the given parent frame to the right style context parent frame for michael@0: * the child, given the pseudo-type of the prospective child. This handles michael@0: * things like walking out of table pseudos and so forth. michael@0: * michael@0: * @param aProspectiveParent what GetParent() on the child returns. michael@0: * Must not be null. michael@0: * @param aChildPseudo the child's pseudo type, if any. michael@0: */ michael@0: static nsIFrame* michael@0: CorrectStyleParentFrame(nsIFrame* aProspectiveParent, nsIAtom* aChildPseudo); michael@0: michael@0: protected: michael@0: // Protected constructor and destructor michael@0: nsFrame(nsStyleContext* aContext); michael@0: virtual ~nsFrame(); michael@0: michael@0: /** michael@0: * To be called by |BuildDisplayLists| of this class or derived classes to add michael@0: * a translucent overlay if this frame's content is selected. michael@0: * @param aContentType an nsISelectionDisplay DISPLAY_ constant identifying michael@0: * which kind of content this is for michael@0: */ michael@0: void DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aList, uint16_t aContentType = nsISelectionDisplay::DISPLAY_FRAMES); michael@0: michael@0: int16_t DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn = false); michael@0: michael@0: // Style post processing hook michael@0: virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; michael@0: michael@0: public: michael@0: //given a frame five me the first/last leaf available michael@0: //XXX Robert O'Callahan wants to move these elsewhere michael@0: static void GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame); michael@0: static void GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame); michael@0: michael@0: // Return the line number of the aFrame, and (optionally) the containing block michael@0: // frame. michael@0: // If aScrollLock is true, don't break outside scrollframes when looking for a michael@0: // containing block frame. michael@0: static int32_t GetLineNumber(nsIFrame *aFrame, michael@0: bool aLockScroll, michael@0: nsIFrame** aContainingBlock = nullptr); michael@0: michael@0: /** michael@0: * Returns true if aFrame should apply overflow clipping. michael@0: */ michael@0: static bool ShouldApplyOverflowClipping(const nsIFrame* aFrame, michael@0: const nsStyleDisplay* aDisp) michael@0: { michael@0: // clip overflow:-moz-hidden-unscrollable ... michael@0: if (MOZ_UNLIKELY(aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP)) { michael@0: return true; michael@0: } michael@0: michael@0: // and overflow:hidden that we should interpret as -moz-hidden-unscrollable michael@0: if (aDisp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN && michael@0: aDisp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) { michael@0: // REVIEW: these are the frame types that set up clipping. michael@0: nsIAtom* type = aFrame->GetType(); michael@0: if (type == nsGkAtoms::tableFrame || michael@0: type == nsGkAtoms::tableCellFrame || michael@0: type == nsGkAtoms::bcTableCellFrame || michael@0: type == nsGkAtoms::svgOuterSVGFrame || michael@0: type == nsGkAtoms::svgInnerSVGFrame || michael@0: type == nsGkAtoms::svgForeignObjectFrame) { michael@0: return true; michael@0: } michael@0: if (aFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) { michael@0: if (type == nsGkAtoms::textInputFrame) { michael@0: // It always has an anonymous scroll frame that handles any overflow. michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { michael@0: return false; michael@0: } michael@0: michael@0: // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW michael@0: // set, then we want to clip our overflow. michael@0: return michael@0: (aFrame->GetStateBits() & NS_BLOCK_CLIP_PAGINATED_OVERFLOW) != 0 && michael@0: aFrame->PresContext()->IsPaginated() && michael@0: aFrame->GetType() == nsGkAtoms::blockFrame; michael@0: } michael@0: michael@0: protected: michael@0: michael@0: // Test if we are selecting a table object: michael@0: // Most table/cell selection requires that Ctrl (Cmd on Mac) key is down michael@0: // during a mouse click or drag. Exception is using Shift+click when michael@0: // already in "table/cell selection mode" to extend a block selection michael@0: // Get the parent content node and offset of the frame michael@0: // of the enclosing cell or table (if not inside a cell) michael@0: // aTarget tells us what table element to select (currently only cell and table supported) michael@0: // (enums for this are defined in nsIFrame.h) michael@0: NS_IMETHOD GetDataForTableSelection(const nsFrameSelection* aFrameSelection, michael@0: nsIPresShell* aPresShell, michael@0: mozilla::WidgetMouseEvent* aMouseEvent, michael@0: nsIContent** aParentContent, michael@0: int32_t* aContentOffset, michael@0: int32_t* aTarget); michael@0: michael@0: // Fills aCursor with the appropriate information from ui michael@0: static void FillCursorInformationFromStyle(const nsStyleUserInterface* ui, michael@0: nsIFrame::Cursor& aCursor); michael@0: NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: virtual void GetBoxName(nsAutoString& aName) MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: void InitBoxMetrics(bool aClear); michael@0: nsBoxLayoutMetrics* BoxMetrics() const; michael@0: michael@0: // Fire DOM event. If no aContent argument use frame's mContent. michael@0: void FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent = nullptr); michael@0: michael@0: private: michael@0: nsresult BoxReflow(nsBoxLayoutState& aState, michael@0: nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: nsRenderingContext* aRenderingContext, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: nscoord aWidth, michael@0: nscoord aHeight, michael@0: bool aMoveFrame = true); michael@0: michael@0: NS_IMETHODIMP RefreshSizeCache(nsBoxLayoutState& aState); michael@0: michael@0: virtual nsILineIterator* GetLineIterator() MOZ_OVERRIDE; michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: public: michael@0: /** michael@0: * Get a printable from of the name of the frame type. michael@0: * XXX This should be eliminated and we use GetType() instead... michael@0: */ michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: nsresult MakeFrameName(const nsAString& aKind, nsAString& aResult) const; michael@0: // Helper function to return the index in parent of the frame's content michael@0: // object. Returns -1 on error or if the frame doesn't have a content object michael@0: static int32_t ContentIndexInContainer(const nsIFrame* aFrame); michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: public: michael@0: /** michael@0: * Return the state bits that are relevant to regression tests (that michael@0: * is, those bits which indicate a real difference when they differ michael@0: */ michael@0: virtual nsFrameState GetDebugStateBits() const MOZ_OVERRIDE; michael@0: /** michael@0: * Called to dump out regression data that describes the layout michael@0: * of the frame and its children, and so on. The format of the michael@0: * data is dictated to be XML (using a specific DTD); the michael@0: * specific kind of data dumped is up to the frame itself, with michael@0: * the caveat that some base types are defined. michael@0: * For more information, see XXX. michael@0: */ michael@0: virtual nsresult DumpRegressionData(nsPresContext* aPresContext, michael@0: FILE* out, int32_t aIndent) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * See if style tree verification is enabled. To enable style tree michael@0: * verification add "styleverifytree:1" to your NSPR_LOG_MODULES michael@0: * environment variable (any non-zero debug level will work). Or, michael@0: * call SetVerifyStyleTreeEnable with true. michael@0: */ michael@0: static bool GetVerifyStyleTreeEnable(); michael@0: michael@0: /** michael@0: * Set the verify-style-tree enable flag. michael@0: */ michael@0: static void SetVerifyStyleTreeEnable(bool aEnabled); michael@0: michael@0: /** michael@0: * The frame class and related classes share an nspr log module michael@0: * for logging frame activity. michael@0: * michael@0: * Note: the log module is created during library initialization which michael@0: * means that you cannot perform logging before then. michael@0: */ michael@0: static PRLogModuleInfo* GetLogModuleInfo(); michael@0: michael@0: // Show frame borders when rendering michael@0: static void ShowFrameBorders(bool aEnable); michael@0: static bool GetShowFrameBorders(); michael@0: michael@0: // Show frame border of event target michael@0: static void ShowEventTargetFrameBorder(bool aEnable); michael@0: static bool GetShowEventTargetFrameBorder(); michael@0: michael@0: #endif michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: public: michael@0: michael@0: static void PrintDisplayItem(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem, michael@0: FILE* aFile = stdout, michael@0: bool aDumpSublist = false, michael@0: bool aDumpHtml = false); michael@0: michael@0: static void PrintDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayList& aList, michael@0: FILE* aFile = stdout, michael@0: bool aDumpHtml = false); michael@0: static void PrintDisplayListSet(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aList, michael@0: FILE* aFile = stdout, michael@0: bool aDumpHtml = false); michael@0: michael@0: #endif michael@0: }; michael@0: michael@0: // Start Display Reflow Debugging michael@0: #ifdef DEBUG michael@0: michael@0: struct DR_cookie { michael@0: DR_cookie(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus& aStatus); michael@0: ~DR_cookie(); michael@0: void Change() const; michael@0: michael@0: nsPresContext* mPresContext; michael@0: nsIFrame* mFrame; michael@0: const nsHTMLReflowState& mReflowState; michael@0: nsHTMLReflowMetrics& mMetrics; michael@0: nsReflowStatus& mStatus; michael@0: void* mValue; michael@0: }; michael@0: michael@0: struct DR_layout_cookie { michael@0: DR_layout_cookie(nsIFrame* aFrame); michael@0: ~DR_layout_cookie(); michael@0: michael@0: nsIFrame* mFrame; michael@0: void* mValue; michael@0: }; michael@0: michael@0: struct DR_intrinsic_width_cookie { michael@0: DR_intrinsic_width_cookie(nsIFrame* aFrame, const char* aType, michael@0: nscoord& aResult); michael@0: ~DR_intrinsic_width_cookie(); michael@0: michael@0: nsIFrame* mFrame; michael@0: const char* mType; michael@0: nscoord& mResult; michael@0: void* mValue; michael@0: }; michael@0: michael@0: struct DR_intrinsic_size_cookie { michael@0: DR_intrinsic_size_cookie(nsIFrame* aFrame, const char* aType, michael@0: nsSize& aResult); michael@0: ~DR_intrinsic_size_cookie(); michael@0: michael@0: nsIFrame* mFrame; michael@0: const char* mType; michael@0: nsSize& mResult; michael@0: void* mValue; michael@0: }; michael@0: michael@0: struct DR_init_constraints_cookie { michael@0: DR_init_constraints_cookie(nsIFrame* aFrame, nsHTMLReflowState* aState, michael@0: nscoord aCBWidth, nscoord aCBHeight, michael@0: const nsMargin* aBorder, michael@0: const nsMargin* aPadding); michael@0: ~DR_init_constraints_cookie(); michael@0: michael@0: nsIFrame* mFrame; michael@0: nsHTMLReflowState* mState; michael@0: void* mValue; michael@0: }; michael@0: michael@0: struct DR_init_offsets_cookie { michael@0: DR_init_offsets_cookie(nsIFrame* aFrame, nsCSSOffsetState* aState, michael@0: nscoord aHorizontalPercentBasis, michael@0: nscoord aVerticalPercentBasis, michael@0: const nsMargin* aBorder, michael@0: const nsMargin* aPadding); michael@0: ~DR_init_offsets_cookie(); michael@0: michael@0: nsIFrame* mFrame; michael@0: nsCSSOffsetState* mState; michael@0: void* mValue; michael@0: }; michael@0: michael@0: struct DR_init_type_cookie { michael@0: DR_init_type_cookie(nsIFrame* aFrame, nsHTMLReflowState* aState); michael@0: ~DR_init_type_cookie(); michael@0: michael@0: nsIFrame* mFrame; michael@0: nsHTMLReflowState* mState; michael@0: void* mValue; michael@0: }; michael@0: michael@0: #define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status) \ michael@0: DR_cookie dr_cookie(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status); michael@0: #define DISPLAY_REFLOW_CHANGE() \ michael@0: dr_cookie.Change(); michael@0: #define DISPLAY_LAYOUT(dr_frame) \ michael@0: DR_layout_cookie dr_cookie(dr_frame); michael@0: #define DISPLAY_MIN_WIDTH(dr_frame, dr_result) \ michael@0: DR_intrinsic_width_cookie dr_cookie(dr_frame, "Min", dr_result) michael@0: #define DISPLAY_PREF_WIDTH(dr_frame, dr_result) \ michael@0: DR_intrinsic_width_cookie dr_cookie(dr_frame, "Pref", dr_result) michael@0: #define DISPLAY_PREF_SIZE(dr_frame, dr_result) \ michael@0: DR_intrinsic_size_cookie dr_cookie(dr_frame, "Pref", dr_result) michael@0: #define DISPLAY_MIN_SIZE(dr_frame, dr_result) \ michael@0: DR_intrinsic_size_cookie dr_cookie(dr_frame, "Min", dr_result) michael@0: #define DISPLAY_MAX_SIZE(dr_frame, dr_result) \ michael@0: DR_intrinsic_size_cookie dr_cookie(dr_frame, "Max", dr_result) michael@0: #define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, \ michael@0: dr_bdr, dr_pad) \ michael@0: DR_init_constraints_cookie dr_cookie(dr_frame, dr_state, dr_cbw, dr_cbh, \ michael@0: dr_bdr, dr_pad) michael@0: #define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_hpb, dr_vpb, dr_bdr, dr_pad) \ michael@0: DR_init_offsets_cookie dr_cookie(dr_frame, dr_state, dr_hpb, dr_vpb, dr_bdr, dr_pad) michael@0: #define DISPLAY_INIT_TYPE(dr_frame, dr_result) \ michael@0: DR_init_type_cookie dr_cookie(dr_frame, dr_result) michael@0: michael@0: #else michael@0: michael@0: #define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status) michael@0: #define DISPLAY_REFLOW_CHANGE() michael@0: #define DISPLAY_LAYOUT(dr_frame) PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_MIN_WIDTH(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_PREF_WIDTH(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_PREF_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_MIN_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_MAX_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, \ michael@0: dr_bdr, dr_pad) \ michael@0: PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_hpb, dr_vpb, dr_bdr, dr_pad) \ michael@0: PR_BEGIN_MACRO PR_END_MACRO michael@0: #define DISPLAY_INIT_TYPE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO michael@0: michael@0: #endif michael@0: // End Display Reflow Debugging michael@0: michael@0: // similar to NS_ENSURE_TRUE but with no return value michael@0: #define ENSURE_TRUE(x) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (!(x)) { \ michael@0: NS_WARNING("ENSURE_TRUE(" #x ") failed"); \ michael@0: return; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: #endif /* nsFrame_h___ */