michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=78: */ 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: /* interface for all rendering objects */ michael@0: michael@0: #ifndef nsIFrame_h___ michael@0: #define nsIFrame_h___ michael@0: michael@0: #ifndef MOZILLA_INTERNAL_API michael@0: #error This header/class should only be used within Mozilla code. It should not be used by extensions. michael@0: #endif michael@0: michael@0: #define MAX_REFLOW_DEPTH 200 michael@0: michael@0: /* nsIFrame is in the process of being deCOMtaminated, i.e., this file is eventually michael@0: going to be eliminated, and all callers will use nsFrame instead. At the moment michael@0: we're midway through this process, so you will see inlined functions and member michael@0: variables in this file. -dwh */ michael@0: michael@0: #include michael@0: #include "nsQueryFrame.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleStruct.h" michael@0: #include "nsHTMLReflowMetrics.h" michael@0: #include "nsFrameList.h" michael@0: #include "mozilla/layout/FrameChildList.h" michael@0: #include "FramePropertyTable.h" michael@0: #include "mozilla/TypedEnum.h" michael@0: #include "nsDirection.h" michael@0: #include "WritingModes.h" michael@0: #include michael@0: #include "nsITheme.h" michael@0: #include "gfx3DMatrix.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsFrameState.h" michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: #include "mozilla/a11y/AccTypes.h" michael@0: #endif michael@0: michael@0: /** michael@0: * New rules of reflow: michael@0: * 1. you get a WillReflow() followed by a Reflow() followed by a DidReflow() in order michael@0: * (no separate pass over the tree) michael@0: * 2. it's the parent frame's responsibility to size/position the child's view (not michael@0: * the child frame's responsibility as it is today) during reflow (and before michael@0: * sending the DidReflow() notification) michael@0: * 3. positioning of child frames (and their views) is done on the way down the tree, michael@0: * and sizing of child frames (and their views) on the way back up michael@0: * 4. if you move a frame (outside of the reflow process, or after reflowing it), michael@0: * then you must make sure that its view (or its child frame's views) are re-positioned michael@0: * as well. It's reasonable to not position the view until after all reflowing the michael@0: * entire line, for example, but the frame should still be positioned and sized (and michael@0: * the view sized) during the reflow (i.e., before sending the DidReflow() notification) michael@0: * 5. the view system handles moving of widgets, i.e., it's not our problem michael@0: */ michael@0: michael@0: struct nsHTMLReflowState; michael@0: class nsHTMLReflowCommand; michael@0: michael@0: struct gfxMatrix; michael@0: class nsIAtom; michael@0: class nsPresContext; michael@0: class nsIPresShell; michael@0: class nsRenderingContext; michael@0: class nsView; michael@0: class nsIWidget; michael@0: class nsIDOMRange; michael@0: class nsISelectionController; michael@0: class nsBoxLayoutState; michael@0: class nsBoxLayout; michael@0: class nsILineIterator; michael@0: class nsDisplayListBuilder; michael@0: class nsDisplayListSet; michael@0: class nsDisplayList; michael@0: class gfxSkipChars; michael@0: class gfxSkipCharsIterator; michael@0: class gfxContext; michael@0: class nsLineList_iterator; michael@0: class nsAbsoluteContainingBlock; michael@0: class nsIContent; michael@0: michael@0: struct nsPeekOffsetStruct; michael@0: struct nsPoint; michael@0: struct nsRect; michael@0: struct nsSize; michael@0: struct nsMargin; michael@0: struct CharacterDataChangeInfo; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class EventStates; michael@0: michael@0: namespace layers { michael@0: class Layer; michael@0: } michael@0: michael@0: namespace gfx { michael@0: class Matrix; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Indication of how the frame can be split. This is used when doing runaround michael@0: * of floats, and when pulling up child frames from a next-in-flow. michael@0: * michael@0: * The choices are splittable, not splittable at all, and splittable in michael@0: * a non-rectangular fashion. This last type only applies to block-level michael@0: * elements, and indicates whether splitting can be used when doing runaround. michael@0: * If you can split across page boundaries, but you expect each continuing michael@0: * frame to be the same width then return frSplittable and not michael@0: * frSplittableNonRectangular. michael@0: * michael@0: * @see #GetSplittableType() michael@0: */ michael@0: typedef uint32_t nsSplittableType; michael@0: michael@0: #define NS_FRAME_NOT_SPLITTABLE 0 // Note: not a bit! michael@0: #define NS_FRAME_SPLITTABLE 0x1 michael@0: #define NS_FRAME_SPLITTABLE_NON_RECTANGULAR 0x3 michael@0: michael@0: #define NS_FRAME_IS_SPLITTABLE(type)\ michael@0: (0 != ((type) & NS_FRAME_SPLITTABLE)) michael@0: michael@0: #define NS_FRAME_IS_NOT_SPLITTABLE(type)\ michael@0: (0 == ((type) & NS_FRAME_SPLITTABLE)) michael@0: michael@0: #define NS_INTRINSIC_WIDTH_UNKNOWN nscoord_MIN michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: #define NS_SUBTREE_DIRTY(_frame) \ michael@0: (((_frame)->GetStateBits() & \ michael@0: (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0) michael@0: michael@0: /** michael@0: * Constant used to indicate an unconstrained size. michael@0: * michael@0: * @see #Reflow() michael@0: */ michael@0: #define NS_UNCONSTRAINEDSIZE NS_MAXSIZE michael@0: michael@0: #define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE michael@0: #define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE michael@0: #define NS_AUTOMARGIN NS_UNCONSTRAINEDSIZE michael@0: #define NS_AUTOOFFSET NS_UNCONSTRAINEDSIZE michael@0: // NOTE: there are assumptions all over that these have the same value, namely NS_UNCONSTRAINEDSIZE michael@0: // if any are changed to be a value other than NS_UNCONSTRAINEDSIZE michael@0: // at least update AdjustComputedHeight/Width and test ad nauseum michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: enum nsSelectionAmount { michael@0: eSelectCharacter = 0, // a single Unicode character; michael@0: // do not use this (prefer Cluster) unless you michael@0: // are really sure it's what you want michael@0: eSelectCluster = 1, // a grapheme cluster: this is usually the right michael@0: // choice for movement or selection by "character" michael@0: // as perceived by the user michael@0: eSelectWord = 2, michael@0: eSelectLine = 3, // previous drawn line in flow. michael@0: eSelectBeginLine = 4, michael@0: eSelectEndLine = 5, michael@0: eSelectNoAmount = 6, // just bounce back current offset. michael@0: eSelectParagraph = 7, // select a "paragraph" michael@0: eSelectWordNoSpace = 8 // select a "word" without selecting the following michael@0: // space, no matter what the default platform michael@0: // behavior is michael@0: }; michael@0: michael@0: enum nsSpread { michael@0: eSpreadNone = 0, michael@0: eSpreadAcross = 1, michael@0: eSpreadDown = 2 michael@0: }; michael@0: michael@0: // Carried out margin flags michael@0: #define NS_CARRIED_TOP_MARGIN_IS_AUTO 0x1 michael@0: #define NS_CARRIED_BOTTOM_MARGIN_IS_AUTO 0x2 michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * Reflow status returned by the reflow methods. There are three michael@0: * completion statuses, represented by two bit flags. michael@0: * michael@0: * NS_FRAME_COMPLETE means the frame is fully complete. michael@0: * michael@0: * NS_FRAME_NOT_COMPLETE bit flag means the frame does not map all its michael@0: * content, and that the parent frame should create a continuing frame. michael@0: * If this bit isn't set it means the frame does map all its content. michael@0: * This bit is mutually exclusive with NS_FRAME_OVERFLOW_INCOMPLETE. michael@0: * michael@0: * NS_FRAME_OVERFLOW_INCOMPLETE bit flag means that the frame has michael@0: * overflow that is not complete, but its own box is complete. michael@0: * (This happens when content overflows a fixed-height box.) michael@0: * The reflower should place and size the frame and continue its reflow, michael@0: * but needs to create an overflow container as a continuation for this michael@0: * frame. See nsContainerFrame.h for more information. michael@0: * This bit is mutually exclusive with NS_FRAME_NOT_COMPLETE. michael@0: * michael@0: * Please use the SET macro for handling michael@0: * NS_FRAME_NOT_COMPLETE and NS_FRAME_OVERFLOW_INCOMPLETE. michael@0: * michael@0: * NS_FRAME_REFLOW_NEXTINFLOW bit flag means that the next-in-flow is michael@0: * dirty, and also needs to be reflowed. This status only makes sense michael@0: * for a frame that is not complete, i.e. you wouldn't set both michael@0: * NS_FRAME_COMPLETE and NS_FRAME_REFLOW_NEXTINFLOW. michael@0: * michael@0: * The low 8 bits of the nsReflowStatus are reserved for future extensions; michael@0: * the remaining 24 bits are zero (and available for extensions; however michael@0: * API's that accept/return nsReflowStatus must not receive/return any michael@0: * extension bits). michael@0: * michael@0: * @see #Reflow() michael@0: */ michael@0: typedef uint32_t nsReflowStatus; michael@0: michael@0: #define NS_FRAME_COMPLETE 0 // Note: not a bit! michael@0: #define NS_FRAME_NOT_COMPLETE 0x1 michael@0: #define NS_FRAME_REFLOW_NEXTINFLOW 0x2 michael@0: #define NS_FRAME_OVERFLOW_INCOMPLETE 0x4 michael@0: michael@0: #define NS_FRAME_IS_COMPLETE(status) \ michael@0: (0 == ((status) & NS_FRAME_NOT_COMPLETE)) michael@0: michael@0: #define NS_FRAME_IS_NOT_COMPLETE(status) \ michael@0: (0 != ((status) & NS_FRAME_NOT_COMPLETE)) michael@0: michael@0: #define NS_FRAME_OVERFLOW_IS_INCOMPLETE(status) \ michael@0: (0 != ((status) & NS_FRAME_OVERFLOW_INCOMPLETE)) michael@0: michael@0: #define NS_FRAME_IS_FULLY_COMPLETE(status) \ michael@0: (NS_FRAME_IS_COMPLETE(status) && !NS_FRAME_OVERFLOW_IS_INCOMPLETE(status)) michael@0: michael@0: // These macros set or switch incomplete statuses without touching the michael@0: // NS_FRAME_REFLOW_NEXTINFLOW bit. michael@0: #define NS_FRAME_SET_INCOMPLETE(status) \ michael@0: status = (status & ~NS_FRAME_OVERFLOW_INCOMPLETE) | NS_FRAME_NOT_COMPLETE michael@0: michael@0: #define NS_FRAME_SET_OVERFLOW_INCOMPLETE(status) \ michael@0: status = (status & ~NS_FRAME_NOT_COMPLETE) | NS_FRAME_OVERFLOW_INCOMPLETE michael@0: michael@0: // This macro tests to see if an nsReflowStatus is an error value michael@0: // or just a regular return value michael@0: #define NS_IS_REFLOW_ERROR(_status) (int32_t(_status) < 0) michael@0: michael@0: /** michael@0: * Extensions to the reflow status bits defined by nsIFrameReflow michael@0: */ michael@0: michael@0: // This bit is set, when a break is requested. This bit is orthogonal michael@0: // to the nsIFrame::nsReflowStatus completion bits. michael@0: #define NS_INLINE_BREAK 0x0100 michael@0: michael@0: // When a break is requested, this bit when set indicates that the michael@0: // break should occur after the frame just reflowed; when the bit is michael@0: // clear the break should occur before the frame just reflowed. michael@0: #define NS_INLINE_BREAK_BEFORE 0x0000 michael@0: #define NS_INLINE_BREAK_AFTER 0x0200 michael@0: michael@0: // The type of break requested can be found in these bits. michael@0: #define NS_INLINE_BREAK_TYPE_MASK 0xF000 michael@0: michael@0: // Set when a break was induced by completion of a first-letter michael@0: #define NS_INLINE_BREAK_FIRST_LETTER_COMPLETE 0x10000 michael@0: michael@0: //---------------------------------------- michael@0: // Macros that use those bits michael@0: michael@0: #define NS_INLINE_IS_BREAK(_status) \ michael@0: (0 != ((_status) & NS_INLINE_BREAK)) michael@0: michael@0: #define NS_INLINE_IS_BREAK_AFTER(_status) \ michael@0: (0 != ((_status) & NS_INLINE_BREAK_AFTER)) michael@0: michael@0: #define NS_INLINE_IS_BREAK_BEFORE(_status) \ michael@0: (NS_INLINE_BREAK == ((_status) & (NS_INLINE_BREAK|NS_INLINE_BREAK_AFTER))) michael@0: michael@0: #define NS_INLINE_GET_BREAK_TYPE(_status) (((_status) >> 12) & 0xF) michael@0: michael@0: #define NS_INLINE_MAKE_BREAK_TYPE(_type) ((_type) << 12) michael@0: michael@0: // Construct a line-break-before status. Note that there is no michael@0: // completion status for a line-break before because we *know* that michael@0: // the frame will be reflowed later and hence its current completion michael@0: // status doesn't matter. michael@0: #define NS_INLINE_LINE_BREAK_BEFORE() \ michael@0: (NS_INLINE_BREAK | NS_INLINE_BREAK_BEFORE | \ michael@0: NS_INLINE_MAKE_BREAK_TYPE(NS_STYLE_CLEAR_LINE)) michael@0: michael@0: // Take a completion status and add to it the desire to have a michael@0: // line-break after. For this macro we do need the completion status michael@0: // because the user of the status will need to know whether to michael@0: // continue the frame or not. michael@0: #define NS_INLINE_LINE_BREAK_AFTER(_completionStatus) \ michael@0: ((_completionStatus) | NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER | \ michael@0: NS_INLINE_MAKE_BREAK_TYPE(NS_STYLE_CLEAR_LINE)) michael@0: michael@0: // A frame is "truncated" if the part of the frame before the first michael@0: // possible break point was unable to fit in the available vertical michael@0: // space. Therefore, the entire frame should be moved to the next page. michael@0: // A frame that begins at the top of the page must never be "truncated". michael@0: // Doing so would likely cause an infinite loop. michael@0: #define NS_FRAME_TRUNCATED 0x0010 michael@0: #define NS_FRAME_IS_TRUNCATED(status) \ michael@0: (0 != ((status) & NS_FRAME_TRUNCATED)) michael@0: #define NS_FRAME_SET_TRUNCATION(status, aReflowState, aMetrics) \ michael@0: aReflowState.SetTruncated(aMetrics, &status); michael@0: michael@0: // Merge the incompleteness, truncation and NS_FRAME_REFLOW_NEXTINFLOW michael@0: // status from aSecondary into aPrimary. michael@0: void NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, michael@0: nsReflowStatus aSecondary); michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * DidReflow status values. michael@0: */ michael@0: MOZ_BEGIN_ENUM_CLASS(nsDidReflowStatus, uint32_t) michael@0: NOT_FINISHED, michael@0: FINISHED michael@0: MOZ_END_ENUM_CLASS(nsDidReflowStatus) michael@0: michael@0: /** michael@0: * When there is no scrollable overflow rect, the visual overflow rect michael@0: * may be stored as four 1-byte deltas each strictly LESS THAN 0xff, for michael@0: * the four edges of the rectangle, or the four bytes may be read as a michael@0: * single 32-bit "overflow-rect type" value including at least one 0xff michael@0: * byte as an indicator that the value does NOT represent four deltas. michael@0: * If all four deltas are zero, this means that no overflow rect has michael@0: * actually been set (this is the initial state of newly-created frames). michael@0: */ michael@0: #define NS_FRAME_OVERFLOW_DELTA_MAX 0xfe // max delta we can store michael@0: michael@0: #define NS_FRAME_OVERFLOW_NONE 0x00000000 // there are no overflow rects; michael@0: // code relies on this being michael@0: // the all-zero value michael@0: michael@0: #define NS_FRAME_OVERFLOW_LARGE 0x000000ff // overflow is stored as a michael@0: // separate rect property michael@0: michael@0: namespace mozilla { michael@0: /* michael@0: * For replaced elements only. Gets the intrinsic dimensions of this element. michael@0: * The dimensions may only be one of the following two types: michael@0: * michael@0: * eStyleUnit_Coord - a length in app units michael@0: * eStyleUnit_None - the element has no intrinsic size in this dimension michael@0: */ michael@0: struct IntrinsicSize { michael@0: nsStyleCoord width, height; michael@0: michael@0: IntrinsicSize() michael@0: : width(eStyleUnit_None), height(eStyleUnit_None) michael@0: {} michael@0: IntrinsicSize(const IntrinsicSize& rhs) michael@0: : width(rhs.width), height(rhs.height) michael@0: {} michael@0: IntrinsicSize& operator=(const IntrinsicSize& rhs) { michael@0: width = rhs.width; height = rhs.height; return *this; michael@0: } michael@0: bool operator==(const IntrinsicSize& rhs) { michael@0: return width == rhs.width && height == rhs.height; michael@0: } michael@0: bool operator!=(const IntrinsicSize& rhs) { michael@0: return !(*this == rhs); michael@0: } michael@0: }; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * A frame in the layout model. This interface is supported by all frame michael@0: * objects. michael@0: * michael@0: * Frames can have multiple child lists: the default child list michael@0: * (referred to as the principal child list, and additional named michael@0: * child lists. There is an ordering of frames within a child list, but michael@0: * there is no order defined between frames in different child lists of michael@0: * the same parent frame. michael@0: * michael@0: * Frames are NOT reference counted. Use the Destroy() member function michael@0: * to destroy a frame. The lifetime of the frame hierarchy is bounded by the michael@0: * lifetime of the presentation shell which owns the frames. michael@0: * michael@0: * nsIFrame is a private Gecko interface. If you are not Gecko then you michael@0: * should not use it. If you're not in layout, then you won't be able to michael@0: * link to many of the functions defined here. Too bad. michael@0: * michael@0: * If you're not in layout but you must call functions in here, at least michael@0: * restrict yourself to calling virtual methods, which won't hurt you as badly. michael@0: */ michael@0: class nsIFrame : public nsQueryFrame michael@0: { michael@0: public: michael@0: typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor; michael@0: typedef mozilla::FrameProperties FrameProperties; michael@0: typedef mozilla::layers::Layer Layer; michael@0: typedef mozilla::layout::FrameChildList ChildList; michael@0: typedef mozilla::layout::FrameChildListID ChildListID; michael@0: typedef mozilla::layout::FrameChildListIDs ChildListIDs; michael@0: typedef mozilla::layout::FrameChildListIterator ChildListIterator; michael@0: typedef mozilla::layout::FrameChildListArrayIterator ChildListArrayIterator; michael@0: typedef mozilla::gfx::Matrix Matrix; michael@0: michael@0: NS_DECL_QUERYFRAME_TARGET(nsIFrame) michael@0: michael@0: nsPresContext* PresContext() const { michael@0: return StyleContext()->RuleNode()->PresContext(); michael@0: } michael@0: michael@0: /** michael@0: * Called to initialize the frame. This is called immediately after creating michael@0: * the frame. michael@0: * michael@0: * If the frame is a continuing frame, then aPrevInFlow indicates the previous michael@0: * frame (the frame that was split). michael@0: * michael@0: * If you want a view associated with your frame, you should create the view michael@0: * after Init() has returned. michael@0: * michael@0: * @param aContent the content object associated with the frame michael@0: * @param aParent the parent frame michael@0: * @param aPrevInFlow the prev-in-flow frame michael@0: */ michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) = 0; michael@0: michael@0: /** michael@0: * Destroys this frame and each of its child frames (recursively calls michael@0: * Destroy() for each child). If this frame is a first-continuation, this michael@0: * also removes the frame from the primary frame map and clears undisplayed michael@0: * content for its content node. michael@0: * If the frame is a placeholder, it also ensures the out-of-flow frame's michael@0: * removal and destruction. michael@0: */ michael@0: void Destroy() { DestroyFrom(this); } michael@0: michael@0: /** Flags for PeekOffsetCharacter, PeekOffsetNoAmount, PeekOffsetWord return values. michael@0: */ michael@0: enum FrameSearchResult { michael@0: // Peek found a appropriate offset within frame. michael@0: FOUND = 0x00, michael@0: // try next frame for offset. michael@0: CONTINUE = 0x1, michael@0: // offset not found because the frame was empty of text. michael@0: CONTINUE_EMPTY = 0x2 | CONTINUE, michael@0: // offset not found because the frame didn't contain any text that could be selected. michael@0: CONTINUE_UNSELECTABLE = 0x4 | CONTINUE, michael@0: }; michael@0: michael@0: protected: michael@0: /** michael@0: * Return true if the frame is part of a Selection. michael@0: * Helper method to implement the public IsSelected() API. michael@0: */ michael@0: virtual bool IsFrameSelected() const; michael@0: michael@0: /** michael@0: * Implements Destroy(). Do not call this directly except from within a michael@0: * DestroyFrom() implementation. michael@0: * michael@0: * @note This will always be called, so it is not necessary to override michael@0: * Destroy() in subclasses of nsFrame, just DestroyFrom(). michael@0: * michael@0: * @param aDestructRoot is the root of the subtree being destroyed michael@0: */ michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0; michael@0: friend class nsFrameList; // needed to pass aDestructRoot through to children michael@0: friend class nsLineBox; // needed to pass aDestructRoot through to children michael@0: friend class nsContainerFrame; // needed to pass aDestructRoot through to children michael@0: public: michael@0: michael@0: /** michael@0: * Called to set the initial list of frames. This happens after the frame michael@0: * has been initialized. michael@0: * michael@0: * This is only called once for a given child list, and won't be called michael@0: * at all for child lists with no initial list of frames. michael@0: * michael@0: * @param aListID the child list identifier. michael@0: * @param aChildList list of child frames. Each of the frames has its michael@0: * NS_FRAME_IS_DIRTY bit set. Must not be empty. michael@0: * This method cannot handle the child list returned by michael@0: * GetAbsoluteListID(). michael@0: * @return NS_ERROR_INVALID_ARG if there is no child list with the specified michael@0: * name, michael@0: * NS_ERROR_UNEXPECTED if the frame is an atomic frame or if the michael@0: * initial list of frames has already been set for that child list, michael@0: * NS_OK otherwise. In this case, SetInitialChildList empties out michael@0: * aChildList in the process of moving the frames over to its own michael@0: * child list. michael@0: * @see #Init() michael@0: */ michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) = 0; michael@0: michael@0: /** michael@0: * This method is responsible for appending frames to the frame michael@0: * list. The implementation should append the frames to the specified michael@0: * child list and then generate a reflow command. michael@0: * michael@0: * @param aListID the child list identifier. michael@0: * @param aFrameList list of child frames to append. Each of the frames has michael@0: * its NS_FRAME_IS_DIRTY bit set. Must not be empty. michael@0: * @return NS_ERROR_INVALID_ARG if there is no child list with the specified michael@0: * name, michael@0: * NS_ERROR_UNEXPECTED if the frame is an atomic frame, michael@0: * NS_OK otherwise. In this case, AppendFrames empties out michael@0: * aFrameList in the process of moving the frames over to its own michael@0: * child list. michael@0: */ michael@0: virtual nsresult AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) = 0; michael@0: michael@0: /** michael@0: * This method is responsible for inserting frames into the frame michael@0: * list. The implementation should insert the new frames into the specified michael@0: * child list and then generate a reflow command. michael@0: * michael@0: * @param aListID the child list identifier. michael@0: * @param aPrevFrame the frame to insert frames after michael@0: * @param aFrameList list of child frames to insert after aPrevFrame. michael@0: * Each of the frames has its NS_FRAME_IS_DIRTY bit set michael@0: * @return NS_ERROR_INVALID_ARG if there is no child list with the specified michael@0: * name, michael@0: * NS_ERROR_UNEXPECTED if the frame is an atomic frame, michael@0: * NS_OK otherwise. In this case, InsertFrames empties out michael@0: * aFrameList in the process of moving the frames over to its own michael@0: * child list. michael@0: */ michael@0: virtual nsresult InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) = 0; michael@0: michael@0: /** michael@0: * This method is responsible for removing a frame in the frame michael@0: * list. The implementation should do something with the removed frame michael@0: * and then generate a reflow command. The implementation is responsible michael@0: * for destroying aOldFrame (the caller mustn't destroy aOldFrame). michael@0: * michael@0: * @param aListID the child list identifier. michael@0: * @param aOldFrame the frame to remove michael@0: * @return NS_ERROR_INVALID_ARG if there is no child list with the specified michael@0: * name, michael@0: * NS_ERROR_FAILURE if the child frame is not in the specified michael@0: * child list, michael@0: * NS_ERROR_UNEXPECTED if the frame is an atomic frame, michael@0: * NS_OK otherwise michael@0: */ michael@0: virtual nsresult RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) = 0; michael@0: michael@0: /** michael@0: * Get the content object associated with this frame. Does not add a reference. michael@0: */ michael@0: nsIContent* GetContent() const { return mContent; } michael@0: michael@0: /** michael@0: * Get the frame that should be the parent for the frames of child elements michael@0: * May return nullptr during reflow michael@0: */ michael@0: virtual nsIFrame* GetContentInsertionFrame() { return this; } michael@0: michael@0: /** michael@0: * Move any frames on our overflow list to the end of our principal list. michael@0: * @return true if there were any overflow frames michael@0: */ michael@0: virtual bool DrainSelfOverflowList() { return false; } michael@0: michael@0: /** michael@0: * Get the frame that should be scrolled if the content associated michael@0: * with this frame is targeted for scrolling. For frames implementing michael@0: * nsIScrollableFrame this will return the frame itself. For frames michael@0: * like nsTextControlFrame that contain a scrollframe, will return michael@0: * that scrollframe. michael@0: */ michael@0: virtual nsIScrollableFrame* GetScrollTargetFrame() { return nullptr; } michael@0: michael@0: /** michael@0: * Get the offsets of the frame. most will be 0,0 michael@0: * michael@0: */ michael@0: virtual nsresult GetOffsets(int32_t &start, int32_t &end) const = 0; michael@0: michael@0: /** michael@0: * Reset the offsets when splitting frames during Bidi reordering michael@0: * michael@0: */ michael@0: virtual void AdjustOffsetsForBidi(int32_t aStart, int32_t aEnd) {} michael@0: michael@0: /** michael@0: * Get the style context associated with this frame. michael@0: */ michael@0: nsStyleContext* StyleContext() const { return mStyleContext; } michael@0: void SetStyleContext(nsStyleContext* aContext) michael@0: { michael@0: if (aContext != mStyleContext) { michael@0: nsStyleContext* oldStyleContext = mStyleContext; michael@0: mStyleContext = aContext; michael@0: aContext->AddRef(); michael@0: DidSetStyleContext(oldStyleContext); michael@0: oldStyleContext->Release(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * SetStyleContextWithoutNotification is for changes to the style michael@0: * context that should suppress style change processing, in other michael@0: * words, those that aren't really changes. This generally means only michael@0: * changes that happen during frame construction. michael@0: */ michael@0: void SetStyleContextWithoutNotification(nsStyleContext* aContext) michael@0: { michael@0: if (aContext != mStyleContext) { michael@0: mStyleContext->Release(); michael@0: mStyleContext = aContext; michael@0: aContext->AddRef(); michael@0: } michael@0: } michael@0: michael@0: // Style post processing hook michael@0: // Attention: the old style context is the one we're forgetting, michael@0: // and hence possibly completely bogus for GetStyle* purposes. michael@0: // Use PeekStyleData instead. michael@0: virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) = 0; michael@0: michael@0: /** michael@0: * Define typesafe getter functions for each style struct by michael@0: * preprocessing the list of style structs. These functions are the michael@0: * preferred way to get style data. The macro creates functions like: michael@0: * const nsStyleBorder* StyleBorder(); michael@0: * const nsStyleColor* StyleColor(); michael@0: * michael@0: * Callers outside of libxul should use nsIDOMWindow::GetComputedStyle() michael@0: * instead of these accessors. michael@0: */ michael@0: #define STYLE_STRUCT(name_, checkdata_cb_) \ michael@0: const nsStyle##name_ * Style##name_ () const { \ michael@0: NS_ASSERTION(mStyleContext, "No style context found!"); \ michael@0: return mStyleContext->Style##name_ (); \ michael@0: } michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: michael@0: /** Also forward GetVisitedDependentColor to the style context */ michael@0: nscolor GetVisitedDependentColor(nsCSSProperty aProperty) michael@0: { return mStyleContext->GetVisitedDependentColor(aProperty); } michael@0: michael@0: /** michael@0: * These methods are to access any additional style contexts that michael@0: * the frame may be holding. These are contexts that are children michael@0: * of the frame's primary context and are NOT used as style contexts michael@0: * for any child frames. These contexts also MUST NOT have any child michael@0: * contexts whatsoever. If you need to insert style contexts into the michael@0: * style tree, then you should create pseudo element frames to own them michael@0: * The indicies must be consecutive and implementations MUST return an michael@0: * NS_ERROR_INVALID_ARG if asked for an index that is out of range. michael@0: */ michael@0: virtual nsStyleContext* GetAdditionalStyleContext(int32_t aIndex) const = 0; michael@0: michael@0: virtual void SetAdditionalStyleContext(int32_t aIndex, michael@0: nsStyleContext* aStyleContext) = 0; michael@0: michael@0: /** michael@0: * Accessor functions for geometric parent michael@0: */ michael@0: nsIFrame* GetParent() const { return mParent; } michael@0: /** michael@0: * Set this frame's parent to aParent. michael@0: * If the frame may have moved into or out of a scrollframe's michael@0: * frame subtree, StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary michael@0: * must also be called. michael@0: */ michael@0: virtual void SetParent(nsIFrame* aParent) = 0; michael@0: michael@0: /** michael@0: * The frame's writing-mode, used for logical layout computations. michael@0: */ michael@0: mozilla::WritingMode GetWritingMode() const { michael@0: return mozilla::WritingMode(StyleVisibility()); michael@0: } michael@0: michael@0: /** michael@0: * Get the writing mode of this frame, but if it is styled with michael@0: * unicode-bidi: plaintext, reset the direction to the resolved paragraph michael@0: * level of the given subframe (typically the first frame on the line), michael@0: * not this frame's writing mode, because the container frame could be split michael@0: * by hard line breaks into multiple paragraphs with different base direction. michael@0: */ michael@0: mozilla::WritingMode GetWritingMode(nsIFrame* aSubFrame) const; michael@0: michael@0: /** michael@0: * Bounding rect of the frame. The values are in app units, and the origin is michael@0: * relative to the upper-left of the geometric parent. The size includes the michael@0: * content area, borders, and padding. michael@0: * michael@0: * Note: moving or sizing the frame does not affect the view's size or michael@0: * position. michael@0: */ michael@0: nsRect GetRect() const { return mRect; } michael@0: nsPoint GetPosition() const { return mRect.TopLeft(); } michael@0: nsSize GetSize() const { return mRect.Size(); } michael@0: nsRect GetRectRelativeToSelf() const { michael@0: return nsRect(nsPoint(0, 0), mRect.Size()); michael@0: } michael@0: /** michael@0: * Dimensions and position in logical coordinates in the frame's writing mode michael@0: * or another writing mode michael@0: */ michael@0: mozilla::LogicalRect GetLogicalRect(nscoord aContainerWidth) const { michael@0: return GetLogicalRect(GetWritingMode(), aContainerWidth); michael@0: } michael@0: mozilla::LogicalPoint GetLogicalPosition(nscoord aContainerWidth) const { michael@0: return GetLogicalPosition(GetWritingMode(), aContainerWidth); michael@0: } michael@0: mozilla::LogicalSize GetLogicalSize() const { michael@0: return GetLogicalSize(GetWritingMode()); michael@0: } michael@0: mozilla::LogicalRect GetLogicalRect(mozilla::WritingMode aWritingMode, michael@0: nscoord aContainerWidth) const { michael@0: return mozilla::LogicalRect(aWritingMode, GetRect(), aContainerWidth); michael@0: } michael@0: mozilla::LogicalPoint GetLogicalPosition(mozilla::WritingMode aWritingMode, michael@0: nscoord aContainerWidth) const { michael@0: return GetLogicalRect(aWritingMode, aContainerWidth).Origin(aWritingMode); michael@0: } michael@0: mozilla::LogicalSize GetLogicalSize(mozilla::WritingMode aWritingMode) const { michael@0: return mozilla::LogicalSize(aWritingMode, GetSize()); michael@0: } michael@0: nscoord IStart(nscoord aContainerWidth) const { michael@0: return IStart(GetWritingMode(), aContainerWidth); michael@0: } michael@0: nscoord IStart(mozilla::WritingMode aWritingMode, michael@0: nscoord aContainerWidth) const { michael@0: return GetLogicalPosition(aWritingMode, aContainerWidth).I(aWritingMode); michael@0: } michael@0: nscoord BStart(nscoord aContainerWidth) const { michael@0: return BStart(GetWritingMode(), aContainerWidth); michael@0: } michael@0: nscoord BStart(mozilla::WritingMode aWritingMode, michael@0: nscoord aContainerWidth) const { michael@0: return GetLogicalPosition(aWritingMode, aContainerWidth).B(aWritingMode); michael@0: } michael@0: nscoord ISize() const { return ISize(GetWritingMode()); } michael@0: nscoord ISize(mozilla::WritingMode aWritingMode) const { michael@0: return GetLogicalSize(aWritingMode).ISize(aWritingMode); michael@0: } michael@0: nscoord BSize() const { return BSize(GetWritingMode()); } michael@0: nscoord BSize(mozilla::WritingMode aWritingMode) const { michael@0: return GetLogicalSize(aWritingMode).BSize(aWritingMode); michael@0: } michael@0: michael@0: /** michael@0: * When we change the size of the frame's border-box rect, we may need to michael@0: * reset the overflow rect if it was previously stored as deltas. michael@0: * (If it is currently a "large" overflow and could be re-packed as deltas, michael@0: * we don't bother as the cost of the allocation has already been paid.) michael@0: */ michael@0: void SetRect(const nsRect& aRect) { michael@0: if (mOverflow.mType != NS_FRAME_OVERFLOW_LARGE && michael@0: mOverflow.mType != NS_FRAME_OVERFLOW_NONE) { michael@0: nsOverflowAreas overflow = GetOverflowAreas(); michael@0: mRect = aRect; michael@0: SetOverflowAreas(overflow); michael@0: } else { michael@0: mRect = aRect; michael@0: } michael@0: } michael@0: /** michael@0: * Set this frame's rect from a logical rect in its own writing direction michael@0: */ michael@0: void SetRect(const mozilla::LogicalRect& aRect, nscoord aContainerWidth) { michael@0: SetRect(GetWritingMode(), aRect, aContainerWidth); michael@0: } michael@0: /** michael@0: * Set this frame's rect from a logical rect in a different writing direction michael@0: * (GetPhysicalRect will assert if the writing mode doesn't match) michael@0: */ michael@0: void SetRect(mozilla::WritingMode aWritingMode, michael@0: const mozilla::LogicalRect& aRect, michael@0: nscoord aContainerWidth) { michael@0: SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerWidth)); michael@0: } michael@0: void SetSize(const nsSize& aSize) { michael@0: SetRect(nsRect(mRect.TopLeft(), aSize)); michael@0: } michael@0: void SetPosition(const nsPoint& aPt) { mRect.MoveTo(aPt); } michael@0: michael@0: /** michael@0: * Move the frame, accounting for relative positioning. Use this when michael@0: * adjusting the frame's position by a known amount, to properly update its michael@0: * saved normal position (see GetNormalPosition below). michael@0: * michael@0: * This must be used only when moving a frame *after* michael@0: * nsHTMLReflowState::ApplyRelativePositioning is called. When moving michael@0: * a frame during the reflow process prior to calling michael@0: * nsHTMLReflowState::ApplyRelativePositioning, the position should michael@0: * simply be adjusted directly (e.g., using SetPosition()). michael@0: */ michael@0: void MovePositionBy(const nsPoint& aTranslation); michael@0: michael@0: /** michael@0: * Return frame's position without relative positioning michael@0: */ michael@0: nsPoint GetNormalPosition() const; michael@0: mozilla::LogicalPoint michael@0: GetLogicalNormalPosition(mozilla::WritingMode aWritingMode, michael@0: nscoord aContainerWidth) const michael@0: { michael@0: return mozilla::LogicalPoint(aWritingMode, michael@0: GetNormalPosition(), aContainerWidth); michael@0: } michael@0: michael@0: virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild) michael@0: { return aChild->GetPosition(); } michael@0: michael@0: nsPoint GetPositionIgnoringScrolling() { michael@0: return mParent ? mParent->GetPositionOfChildIgnoringScrolling(this) michael@0: : GetPosition(); michael@0: } michael@0: michael@0: static void DestroyRegion(void* aPropertyValue); michael@0: michael@0: static void DestroyMargin(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: static void DestroyRect(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: static void DestroyPoint(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: static void DestroyOverflowAreas(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: static void DestroySurface(void* aPropertyValue); michael@0: static void DestroyDT(void* aPropertyValue); michael@0: michael@0: #ifdef _MSC_VER michael@0: // XXX Workaround MSVC issue by making the static FramePropertyDescriptor michael@0: // non-const. See bug 555727. michael@0: #define NS_PROPERTY_DESCRIPTOR_CONST michael@0: #else michael@0: #define NS_PROPERTY_DESCRIPTOR_CONST const michael@0: #endif michael@0: michael@0: #define NS_DECLARE_FRAME_PROPERTY(prop, dtor) \ michael@0: static const FramePropertyDescriptor* prop() { \ michael@0: static NS_PROPERTY_DESCRIPTOR_CONST FramePropertyDescriptor descriptor = { dtor, nullptr }; \ michael@0: return &descriptor; \ michael@0: } michael@0: // Don't use this unless you really know what you're doing! michael@0: #define NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(prop, dtor) \ michael@0: static const FramePropertyDescriptor* prop() { \ michael@0: static NS_PROPERTY_DESCRIPTOR_CONST FramePropertyDescriptor descriptor = { nullptr, dtor }; \ michael@0: return &descriptor; \ michael@0: } michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(IBSplitSibling, nullptr) michael@0: NS_DECLARE_FRAME_PROPERTY(IBSplitPrevSibling, nullptr) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(NormalPositionProperty, DestroyPoint) michael@0: NS_DECLARE_FRAME_PROPERTY(ComputedOffsetProperty, DestroyMargin) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(OutlineInnerRectProperty, DestroyRect) michael@0: NS_DECLARE_FRAME_PROPERTY(PreEffectsBBoxProperty, DestroyRect) michael@0: NS_DECLARE_FRAME_PROPERTY(PreTransformOverflowAreasProperty, michael@0: DestroyOverflowAreas) michael@0: michael@0: // The initial overflow area passed to FinishAndStoreOverflow. This is only set michael@0: // on frames that Preserve3D() or HasPerspective() or IsTransformed(), and michael@0: // when at least one of the overflow areas differs from the frame bound rect. michael@0: NS_DECLARE_FRAME_PROPERTY(InitialOverflowProperty, DestroyOverflowAreas) michael@0: michael@0: #ifdef DEBUG michael@0: // InitialOverflowPropertyDebug is added to the frame to indicate that either michael@0: // the InitialOverflowProperty has been stored or the InitialOverflowProperty michael@0: // has been suppressed due to being set to the default value (frame bounds) michael@0: NS_DECLARE_FRAME_PROPERTY(DebugInitialOverflowPropertyApplied, nullptr) michael@0: #endif michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(UsedMarginProperty, DestroyMargin) michael@0: NS_DECLARE_FRAME_PROPERTY(UsedPaddingProperty, DestroyMargin) michael@0: NS_DECLARE_FRAME_PROPERTY(UsedBorderProperty, DestroyMargin) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(ScrollLayerCount, nullptr) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(LineBaselineOffset, nullptr) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(CachedBackgroundImage, DestroySurface) michael@0: NS_DECLARE_FRAME_PROPERTY(CachedBackgroundImageDT, DestroyDT) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(InvalidationRect, DestroyRect) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(RefusedAsyncAnimation, nullptr) michael@0: michael@0: /** michael@0: * Return the distance between the border edge of the frame and the michael@0: * margin edge of the frame. Like GetRect(), returns the dimensions michael@0: * as of the most recent reflow. michael@0: * michael@0: * This doesn't include any margin collapsing that may have occurred. michael@0: * michael@0: * It also treats 'auto' margins as zero, and treats any margins that michael@0: * should have been turned into 'auto' because of overconstraint as michael@0: * having their original values. michael@0: */ michael@0: virtual nsMargin GetUsedMargin() const; michael@0: virtual mozilla::LogicalMargin michael@0: GetLogicalUsedMargin(mozilla::WritingMode aWritingMode) const { michael@0: return mozilla::LogicalMargin(aWritingMode, GetUsedMargin()); michael@0: } michael@0: michael@0: /** michael@0: * Return the distance between the border edge of the frame (which is michael@0: * its rect) and the padding edge of the frame. Like GetRect(), returns michael@0: * the dimensions as of the most recent reflow. michael@0: * michael@0: * Note that this differs from StyleBorder()->GetBorder() in that michael@0: * this describes region of the frame's box, and michael@0: * StyleBorder()->GetBorder() describes a border. They differ only michael@0: * for tables, particularly border-collapse tables. michael@0: */ michael@0: virtual nsMargin GetUsedBorder() const; michael@0: virtual mozilla::LogicalMargin michael@0: GetLogicalUsedBorder(mozilla::WritingMode aWritingMode) const { michael@0: return mozilla::LogicalMargin(aWritingMode, GetUsedBorder()); michael@0: } michael@0: michael@0: /** michael@0: * Return the distance between the padding edge of the frame and the michael@0: * content edge of the frame. Like GetRect(), returns the dimensions michael@0: * as of the most recent reflow. michael@0: */ michael@0: virtual nsMargin GetUsedPadding() const; michael@0: virtual mozilla::LogicalMargin michael@0: GetLogicalUsedPadding(mozilla::WritingMode aWritingMode) const { michael@0: return mozilla::LogicalMargin(aWritingMode, GetUsedPadding()); michael@0: } michael@0: michael@0: nsMargin GetUsedBorderAndPadding() const { michael@0: return GetUsedBorder() + GetUsedPadding(); michael@0: } michael@0: mozilla::LogicalMargin michael@0: GetLogicalUsedBorderAndPadding(mozilla::WritingMode aWritingMode) const { michael@0: return mozilla::LogicalMargin(aWritingMode, GetUsedBorderAndPadding()); michael@0: } michael@0: michael@0: /** michael@0: * Apply the result of GetSkipSides() on this frame to an nsMargin by michael@0: * setting to zero any sides that are skipped. michael@0: * michael@0: * @param aMargin The margin to apply the result of GetSkipSides() to. michael@0: * @param aReflowState An optional reflow state parameter, which is used if michael@0: * ApplySkipSides() is being called in the middle of reflow. michael@0: * michael@0: * @note (See also bug 743402, comment 11) GetSkipSides() and its sister michael@0: * method, ApplySkipSides() checks to see if this frame has a previous michael@0: * or next continuation to determine if a side should be skipped. michael@0: * Unfortunately, this only works after reflow has been completed. In michael@0: * lieu of this, during reflow, an nsHTMLReflowState parameter can be michael@0: * passed in, indicating that it should be used to determine if sides michael@0: * should be skipped during reflow. michael@0: */ michael@0: void ApplySkipSides(nsMargin& aMargin, michael@0: const nsHTMLReflowState* aReflowState = nullptr) const; michael@0: void ApplyLogicalSkipSides(mozilla::LogicalMargin& aMargin, michael@0: const nsHTMLReflowState* aReflowState = nullptr) const; michael@0: michael@0: /** michael@0: * Like the frame's rect (see |GetRect|), which is the border rect, michael@0: * other rectangles of the frame, in app units, relative to the parent. michael@0: */ michael@0: nsRect GetPaddingRect() const; michael@0: nsRect GetPaddingRectRelativeToSelf() const; michael@0: nsRect GetContentRect() const; michael@0: nsRect GetContentRectRelativeToSelf() const; michael@0: nsRect GetMarginRectRelativeToSelf() const; michael@0: michael@0: /** michael@0: * The area to paint box-shadows around. The default is the border rect. michael@0: * (nsFieldSetFrame overrides this). michael@0: */ michael@0: virtual nsRect VisualBorderRectRelativeToSelf() const { michael@0: return nsRect(0, 0, mRect.width, mRect.height); michael@0: } michael@0: michael@0: /** michael@0: * Get the size, in app units, of the border radii. It returns FALSE iff all michael@0: * returned radii == 0 (so no border radii), TRUE otherwise. michael@0: * For the aRadii indexes, use the NS_CORNER_* constants in nsStyleConsts.h michael@0: * If a side is skipped via aSkipSides, its corners are forced to 0. michael@0: * michael@0: * All corner radii are then adjusted so they do not require more michael@0: * space than aBorderArea, according to the algorithm in css3-background. michael@0: * michael@0: * aFrameSize is used as the basis for percentage widths and heights. michael@0: * aBorderArea is used for the adjustment of radii that might be too michael@0: * large. michael@0: * FIXME: In the long run, we can probably get away with only one of michael@0: * these, especially if we change the way we handle outline-radius (by michael@0: * removing it and inflating the border radius) michael@0: * michael@0: * Return whether any radii are nonzero. michael@0: */ michael@0: static bool ComputeBorderRadii(const nsStyleCorners& aBorderRadius, michael@0: const nsSize& aFrameSize, michael@0: const nsSize& aBorderArea, michael@0: int aSkipSides, michael@0: nscoord aRadii[8]); michael@0: michael@0: /* michael@0: * Given a set of border radii for one box (e.g., border box), convert michael@0: * it to the equivalent set of radii for another box (e.g., in to michael@0: * padding box, out to outline box) by reducing radii or increasing michael@0: * nonzero radii as appropriate. michael@0: * michael@0: * Indices into aRadii are the NS_CORNER_* constants in nsStyleConsts.h michael@0: * michael@0: * Note that InsetBorderRadii is lossy, since it can turn nonzero michael@0: * radii into zero, and OutsetBorderRadii does not inflate zero radii. michael@0: * Therefore, callers should always inset or outset directly from the michael@0: * original value coming from style. michael@0: */ michael@0: static void InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets); michael@0: static void OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets); michael@0: michael@0: /** michael@0: * Fill in border radii for this frame. Return whether any are michael@0: * nonzero. michael@0: * michael@0: * Indices into aRadii are the NS_CORNER_* constants in nsStyleConsts.h michael@0: */ michael@0: virtual bool GetBorderRadii(nscoord aRadii[8]) const; michael@0: michael@0: bool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const; michael@0: bool GetContentBoxBorderRadii(nscoord aRadii[8]) const; michael@0: michael@0: /** michael@0: * Get the position of the frame's baseline, relative to the top of michael@0: * the frame (its top border edge). Only valid when Reflow is not michael@0: * needed. michael@0: */ michael@0: virtual nscoord GetBaseline() const = 0; michael@0: michael@0: /** michael@0: * Get the position of the baseline on which the caret needs to be placed, michael@0: * relative to the top of the frame. This is mostly needed for frames michael@0: * which return a baseline from GetBaseline which is not useful for michael@0: * caret positioning. michael@0: */ michael@0: virtual nscoord GetCaretBaseline() const { michael@0: return GetBaseline(); michael@0: } michael@0: michael@0: /** michael@0: * Get the specified child list. michael@0: * michael@0: * @param aListID identifies the requested child list. michael@0: * @return the child list. If the requested list is unsupported by this michael@0: * frame type, an empty list will be returned. michael@0: */ michael@0: virtual const nsFrameList& GetChildList(ChildListID aListID) const = 0; michael@0: const nsFrameList& PrincipalChildList() { return GetChildList(kPrincipalList); } michael@0: virtual void GetChildLists(nsTArray* aLists) const = 0; michael@0: michael@0: /** michael@0: * Gets the child lists for this frame, including michael@0: * ones belong to a child document. michael@0: */ michael@0: void GetCrossDocChildLists(nsTArray* aLists); michael@0: michael@0: // XXXbz this method should go away michael@0: nsIFrame* GetFirstChild(ChildListID aListID) const { michael@0: return GetChildList(aListID).FirstChild(); michael@0: } michael@0: // XXXmats this method should also go away then michael@0: nsIFrame* GetLastChild(ChildListID aListID) const { michael@0: return GetChildList(aListID).LastChild(); michael@0: } michael@0: nsIFrame* GetFirstPrincipalChild() const { michael@0: return GetFirstChild(kPrincipalList); michael@0: } michael@0: michael@0: // The individual concrete child lists. michael@0: static const ChildListID kPrincipalList = mozilla::layout::kPrincipalList; michael@0: static const ChildListID kAbsoluteList = mozilla::layout::kAbsoluteList; michael@0: static const ChildListID kBulletList = mozilla::layout::kBulletList; michael@0: static const ChildListID kCaptionList = mozilla::layout::kCaptionList; michael@0: static const ChildListID kColGroupList = mozilla::layout::kColGroupList; michael@0: static const ChildListID kExcessOverflowContainersList = mozilla::layout::kExcessOverflowContainersList; michael@0: static const ChildListID kFixedList = mozilla::layout::kFixedList; michael@0: static const ChildListID kFloatList = mozilla::layout::kFloatList; michael@0: static const ChildListID kOverflowContainersList = mozilla::layout::kOverflowContainersList; michael@0: static const ChildListID kOverflowList = mozilla::layout::kOverflowList; michael@0: static const ChildListID kOverflowOutOfFlowList = mozilla::layout::kOverflowOutOfFlowList; michael@0: static const ChildListID kPopupList = mozilla::layout::kPopupList; michael@0: static const ChildListID kPushedFloatsList = mozilla::layout::kPushedFloatsList; michael@0: static const ChildListID kSelectPopupList = mozilla::layout::kSelectPopupList; michael@0: // A special alias for kPrincipalList that do not request reflow. michael@0: static const ChildListID kNoReflowPrincipalList = mozilla::layout::kNoReflowPrincipalList; michael@0: michael@0: /** michael@0: * Child frames are linked together in a doubly-linked list michael@0: */ michael@0: nsIFrame* GetNextSibling() const { return mNextSibling; } michael@0: void SetNextSibling(nsIFrame* aNextSibling) { michael@0: NS_ASSERTION(this != aNextSibling, "Creating a circular frame list, this is very bad."); michael@0: if (mNextSibling && mNextSibling->GetPrevSibling() == this) { michael@0: mNextSibling->mPrevSibling = nullptr; michael@0: } michael@0: mNextSibling = aNextSibling; michael@0: if (mNextSibling) { michael@0: mNextSibling->mPrevSibling = this; michael@0: } michael@0: } michael@0: michael@0: nsIFrame* GetPrevSibling() const { return mPrevSibling; } michael@0: michael@0: /** michael@0: * Builds the display lists for the content represented by this frame michael@0: * and its descendants. The background+borders of this element must michael@0: * be added first, before any other content. michael@0: * michael@0: * This should only be called by methods in nsFrame. Instead of calling this michael@0: * directly, call either BuildDisplayListForStackingContext or michael@0: * BuildDisplayListForChild. michael@0: * michael@0: * See nsDisplayList.h for more information about display lists. michael@0: * michael@0: * @param aDirtyRect content outside this rectangle can be ignored; the michael@0: * rectangle is in frame coordinates michael@0: */ michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) {} michael@0: /** michael@0: * Displays the caret onto the given display list builder. The caret is michael@0: * painted on top of the rest of the display list items. michael@0: * michael@0: * @param aDirtyRect is the dirty rectangle that we're repainting. michael@0: */ michael@0: void DisplayCaret(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: nsDisplayList* aList); michael@0: michael@0: /** michael@0: * Get the preferred caret color at the offset. michael@0: * michael@0: * @param aOffset is offset of the content. michael@0: */ michael@0: virtual nscolor GetCaretColorAt(int32_t aOffset); michael@0: michael@0: michael@0: bool IsThemed(nsITheme::Transparency* aTransparencyState = nullptr) const { michael@0: return IsThemed(StyleDisplay(), aTransparencyState); michael@0: } michael@0: bool IsThemed(const nsStyleDisplay* aDisp, michael@0: nsITheme::Transparency* aTransparencyState = nullptr) const { michael@0: nsIFrame* mutable_this = const_cast(this); michael@0: if (!aDisp->mAppearance) michael@0: return false; michael@0: nsPresContext* pc = PresContext(); michael@0: nsITheme *theme = pc->GetTheme(); michael@0: if(!theme || michael@0: !theme->ThemeSupportsWidget(pc, mutable_this, aDisp->mAppearance)) michael@0: return false; michael@0: if (aTransparencyState) { michael@0: *aTransparencyState = michael@0: theme->GetWidgetTransparency(mutable_this, aDisp->mAppearance); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Builds a display list for the content represented by this frame, michael@0: * treating this frame as the root of a stacking context. michael@0: * @param aDirtyRect content outside this rectangle can be ignored; the michael@0: * rectangle is in frame coordinates michael@0: */ michael@0: void BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: nsDisplayList* aList); michael@0: michael@0: enum { michael@0: DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT = 0x01, michael@0: DISPLAY_CHILD_FORCE_STACKING_CONTEXT = 0x02, michael@0: DISPLAY_CHILD_INLINE = 0x04 michael@0: }; michael@0: /** michael@0: * Adjusts aDirtyRect for the child's offset, checks that the dirty rect michael@0: * actually intersects the child (or its descendants), calls BuildDisplayList michael@0: * on the child if necessary, and puts things in the right lists if the child michael@0: * is positioned. michael@0: * michael@0: * @param aFlags combination of DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT, michael@0: * DISPLAY_CHILD_FORCE_STACKING_CONTEXT and DISPLAY_CHILD_INLINE michael@0: */ michael@0: void BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aChild, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists, michael@0: uint32_t aFlags = 0); michael@0: michael@0: /** michael@0: * Does this frame need a view? michael@0: */ michael@0: virtual bool NeedsView() { return false; } michael@0: michael@0: /** michael@0: * Returns true if this frame is transformed (e.g. has CSS or SVG transforms) michael@0: * or if its parent is an SVG frame that has children-only transforms (e.g. michael@0: * an SVG viewBox attribute). michael@0: */ michael@0: bool IsTransformed() const; michael@0: michael@0: /** michael@0: * Returns true if the frame is translucent for the purposes of creating a michael@0: * stacking context. michael@0: */ michael@0: bool HasOpacity() const michael@0: { michael@0: return HasOpacityInternal(1.0f); michael@0: } michael@0: /** michael@0: * Returns true if the frame is translucent for display purposes. michael@0: */ michael@0: bool HasVisualOpacity() const michael@0: { michael@0: // Treat an opacity value of 0.99 and above as opaque. This is an michael@0: // optimization aimed at Web content which use opacity:0.99 as a hint for michael@0: // creating a stacking context only. michael@0: return HasOpacityInternal(0.99f); michael@0: } michael@0: michael@0: /** michael@0: * Return true if this frame might be using a transform getter. michael@0: */ michael@0: virtual bool HasTransformGetter() const { return false; } michael@0: michael@0: /** michael@0: * Returns true if this frame is an SVG frame that has SVG transforms applied michael@0: * to it, or if its parent frame is an SVG frame that has children-only michael@0: * transforms (e.g. an SVG viewBox attribute). michael@0: * If aOwnTransforms is non-null and the frame has its own SVG transforms, michael@0: * aOwnTransforms will be set to these transforms. If aFromParentTransforms michael@0: * is non-null and the frame has an SVG parent with children-only transforms, michael@0: * then aFromParentTransforms will be set to these transforms. michael@0: */ michael@0: virtual bool IsSVGTransformed(Matrix *aOwnTransforms = nullptr, michael@0: Matrix *aFromParentTransforms = nullptr) const; michael@0: michael@0: /** michael@0: * Returns whether this frame will attempt to preserve the 3d transforms of its michael@0: * children. This requires transform-style: preserve-3d, as well as no clipping michael@0: * or svg effects. michael@0: */ michael@0: bool Preserves3DChildren() const; michael@0: michael@0: /** michael@0: * Returns whether this frame has a parent that Preserves3DChildren() and has michael@0: * its own transform (or hidden backface) to be combined with the parent's michael@0: * transform. michael@0: */ michael@0: bool Preserves3D() const; michael@0: michael@0: bool HasPerspective() const; michael@0: michael@0: bool ChildrenHavePerspective() const; michael@0: michael@0: // Calculate the overflow size of all child frames, taking preserve-3d into account michael@0: void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds); michael@0: michael@0: void RecomputePerspectiveChildrenOverflow(const nsStyleContext* aStartStyle, const nsRect* aBounds); michael@0: michael@0: /** michael@0: * Returns the number of ancestors between this and the root of our frame tree michael@0: */ michael@0: uint32_t GetDepthInFrameTree() { michael@0: uint32_t result = 0; michael@0: for (nsIFrame* ancestor = GetParent(); ancestor; michael@0: ancestor = ancestor->GetParent()) { michael@0: result++; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Event handling of GUI events. michael@0: * michael@0: * @param aEvent event structure describing the type of event and rge widget michael@0: * where the event originated michael@0: * The |point| member of this is in the coordinate system of the michael@0: * view returned by GetOffsetFromView. michael@0: * @param aEventStatus a return value indicating whether the event was handled michael@0: * and whether default processing should be done michael@0: * michael@0: * XXX From a frame's perspective it's unclear what the effect of the event status michael@0: * is. Does it cause the event to continue propagating through the frame hierarchy michael@0: * or is it just returned to the widgets? michael@0: * michael@0: * @see WidgetGUIEvent michael@0: * @see nsEventStatus michael@0: */ michael@0: virtual nsresult HandleEvent(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) = 0; michael@0: michael@0: virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent, michael@0: nsIContent** aContent) = 0; michael@0: michael@0: // This structure keeps track of the content node and offsets associated with michael@0: // a point; there is a primary and a secondary offset associated with any michael@0: // point. The primary and secondary offsets differ when the point is over a michael@0: // non-text object. The primary offset is the expected position of the michael@0: // cursor calculated from a point; the secondary offset, when it is different, michael@0: // indicates that the point is in the boundaries of some selectable object. michael@0: // Note that the primary offset can be after the secondary offset; for places michael@0: // that need the beginning and end of the object, the StartOffset and michael@0: // EndOffset helpers can be used. michael@0: struct MOZ_STACK_CLASS ContentOffsets { michael@0: ContentOffsets(); michael@0: ContentOffsets(const ContentOffsets&); michael@0: ~ContentOffsets(); michael@0: nsCOMPtr content; michael@0: bool IsNull() { return !content; } michael@0: int32_t offset; michael@0: int32_t secondaryOffset; michael@0: // Helpers for places that need the ends of the offsets and expect them in michael@0: // numerical order, as opposed to wanting the primary and secondary offsets michael@0: int32_t StartOffset() { return std::min(offset, secondaryOffset); } michael@0: int32_t EndOffset() { return std::max(offset, secondaryOffset); } michael@0: // This boolean indicates whether the associated content is before or after michael@0: // the offset; the most visible use is to allow the caret to know which line michael@0: // to display on. michael@0: bool associateWithNext; michael@0: }; michael@0: enum { michael@0: IGNORE_SELECTION_STYLE = 0x01, michael@0: // Treat visibility:hidden frames as non-selectable michael@0: SKIP_HIDDEN = 0x02 michael@0: }; michael@0: /** michael@0: * This function calculates the content offsets for selection relative to michael@0: * a point. Note that this should generally only be callled on the event michael@0: * frame associated with an event because this function does not account michael@0: * for frame lists other than the primary one. michael@0: * @param aPoint point relative to this frame michael@0: */ michael@0: ContentOffsets GetContentOffsetsFromPoint(nsPoint aPoint, michael@0: uint32_t aFlags = 0); michael@0: michael@0: virtual ContentOffsets GetContentOffsetsFromPointExternal(nsPoint aPoint, michael@0: uint32_t aFlags = 0) michael@0: { return GetContentOffsetsFromPoint(aPoint, aFlags); } michael@0: michael@0: /** michael@0: * Ensure that aImage gets notifed when the underlying image request loads michael@0: * or animates. michael@0: */ michael@0: void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext); michael@0: michael@0: /** michael@0: * This structure holds information about a cursor. mContainer represents a michael@0: * loaded image that should be preferred. If it is not possible to use it, or michael@0: * if it is null, mCursor should be used. michael@0: */ michael@0: struct MOZ_STACK_CLASS Cursor { michael@0: nsCOMPtr mContainer; michael@0: int32_t mCursor; michael@0: bool mHaveHotspot; michael@0: float mHotspotX, mHotspotY; michael@0: }; michael@0: /** michael@0: * Get the cursor for a given frame. michael@0: */ michael@0: virtual nsresult GetCursor(const nsPoint& aPoint, michael@0: Cursor& aCursor) = 0; michael@0: michael@0: /** michael@0: * Get a point (in the frame's coordinate space) given an offset into michael@0: * the content. This point should be on the baseline of text with michael@0: * the correct horizontal offset michael@0: */ michael@0: virtual nsresult GetPointFromOffset(int32_t inOffset, michael@0: nsPoint* outPoint) = 0; michael@0: michael@0: /** michael@0: * Get the child frame of this frame which contains the given michael@0: * content offset. outChildFrame may be this frame, or nullptr on return. michael@0: * outContentOffset returns the content offset relative to the start michael@0: * of the returned node. You can also pass a hint which tells the method michael@0: * to stick to the end of the first found frame or the beginning of the michael@0: * next in case the offset falls on a boundary. michael@0: */ michael@0: virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset, michael@0: bool inHint,//false stick left michael@0: int32_t* outFrameContentOffset, michael@0: nsIFrame** outChildFrame) = 0; michael@0: michael@0: /** michael@0: * Get the current frame-state value for this frame. aResult is michael@0: * filled in with the state bits. michael@0: */ michael@0: nsFrameState GetStateBits() const { return mState; } michael@0: michael@0: /** michael@0: * Update the current frame-state value for this frame. michael@0: */ michael@0: void AddStateBits(nsFrameState aBits) { mState |= aBits; } michael@0: void RemoveStateBits(nsFrameState aBits) { mState &= ~aBits; } michael@0: michael@0: /** michael@0: * Checks if the current frame-state includes all of the listed bits michael@0: */ michael@0: bool HasAllStateBits(nsFrameState aBits) const michael@0: { michael@0: return (mState & aBits) == aBits; michael@0: } michael@0: michael@0: /** michael@0: * Checks if the current frame-state includes any of the listed bits michael@0: */ michael@0: bool HasAnyStateBits(nsFrameState aBits) const michael@0: { michael@0: return mState & aBits; michael@0: } michael@0: michael@0: /** michael@0: * This call is invoked on the primary frame for a character data content michael@0: * node, when it is changed in the content tree. michael@0: */ michael@0: virtual nsresult CharacterDataChanged(CharacterDataChangeInfo* aInfo) = 0; michael@0: michael@0: /** michael@0: * This call is invoked when the value of a content objects's attribute michael@0: * is changed. michael@0: * The first frame that maps that content is asked to deal michael@0: * with the change by doing whatever is appropriate. michael@0: * michael@0: * @param aNameSpaceID the namespace of the attribute michael@0: * @param aAttribute the atom name of the attribute michael@0: * @param aModType Whether or not the attribute was added, changed, or removed. michael@0: * The constants are defined in nsIDOMMutationEvent.h. michael@0: */ michael@0: virtual nsresult AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) = 0; michael@0: michael@0: /** michael@0: * When the content states of a content object change, this method is invoked michael@0: * on the primary frame of that content object. michael@0: * michael@0: * @param aStates the changed states michael@0: */ michael@0: virtual void ContentStatesChanged(mozilla::EventStates aStates); michael@0: michael@0: /** michael@0: * Return how your frame can be split. michael@0: */ michael@0: virtual nsSplittableType GetSplittableType() const = 0; michael@0: michael@0: /** michael@0: * Continuation member functions michael@0: */ michael@0: virtual nsIFrame* GetPrevContinuation() const = 0; michael@0: virtual void SetPrevContinuation(nsIFrame*) = 0; michael@0: virtual nsIFrame* GetNextContinuation() const = 0; michael@0: virtual void SetNextContinuation(nsIFrame*) = 0; michael@0: virtual nsIFrame* FirstContinuation() const { michael@0: return const_cast(this); michael@0: } michael@0: virtual nsIFrame* LastContinuation() const { michael@0: return const_cast(this); michael@0: } michael@0: michael@0: /** michael@0: * GetTailContinuation gets the last non-overflow-container continuation michael@0: * in the continuation chain, i.e. where the next sibling element michael@0: * should attach). michael@0: */ michael@0: nsIFrame* GetTailContinuation(); michael@0: michael@0: /** michael@0: * Flow member functions michael@0: */ michael@0: virtual nsIFrame* GetPrevInFlowVirtual() const = 0; michael@0: nsIFrame* GetPrevInFlow() const { return GetPrevInFlowVirtual(); } michael@0: virtual void SetPrevInFlow(nsIFrame*) = 0; michael@0: michael@0: virtual nsIFrame* GetNextInFlowVirtual() const = 0; michael@0: nsIFrame* GetNextInFlow() const { return GetNextInFlowVirtual(); } michael@0: virtual void SetNextInFlow(nsIFrame*) = 0; michael@0: michael@0: /** michael@0: * Return the first frame in our current flow. michael@0: */ michael@0: virtual nsIFrame* FirstInFlow() const { michael@0: return const_cast(this); michael@0: } michael@0: michael@0: /** michael@0: * Return the last frame in our current flow. michael@0: */ michael@0: virtual nsIFrame* LastInFlow() const { michael@0: return const_cast(this); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Mark any stored intrinsic width information as dirty (requiring michael@0: * re-calculation). Note that this should generally not be called michael@0: * directly; nsPresShell::FrameNeedsReflow will call it instead. michael@0: */ michael@0: virtual void MarkIntrinsicWidthsDirty() = 0; michael@0: michael@0: /** michael@0: * Get the intrinsic minimum width of the frame. This must be less michael@0: * than or equal to the intrinsic width. michael@0: * michael@0: * This is *not* affected by the CSS 'min-width', 'width', and michael@0: * 'max-width' properties on this frame, but it is affected by the michael@0: * values of those properties on this frame's descendants. (It may be michael@0: * called during computation of the values of those properties, so it michael@0: * cannot depend on any values in the nsStylePosition for this frame.) michael@0: * michael@0: * The value returned should **NOT** include the space required for michael@0: * padding and border. michael@0: * michael@0: * Note that many frames will cache the result of this function call michael@0: * unless MarkIntrinsicWidthsDirty is called. michael@0: * michael@0: * It is not acceptable for a frame to mark itself dirty when this michael@0: * method is called. michael@0: * michael@0: * This method must not return a negative value. michael@0: */ michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) = 0; michael@0: michael@0: /** michael@0: * Get the intrinsic width of the frame. This must be greater than or michael@0: * equal to the intrinsic minimum width. michael@0: * michael@0: * Otherwise, all the comments for |GetMinWidth| above apply. michael@0: */ michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) = 0; michael@0: michael@0: /** michael@0: * |InlineIntrinsicWidth| represents the intrinsic width information michael@0: * in inline layout. Code that determines the intrinsic width of a michael@0: * region of inline layout accumulates the result into this structure. michael@0: * This pattern is needed because we need to maintain state michael@0: * information about whitespace (for both collapsing and trimming). michael@0: */ michael@0: struct InlineIntrinsicWidthData { michael@0: InlineIntrinsicWidthData() michael@0: : line(nullptr) michael@0: , lineContainer(nullptr) michael@0: , prevLines(0) michael@0: , currentLine(0) michael@0: , skipWhitespace(true) michael@0: , trailingWhitespace(0) michael@0: {} michael@0: michael@0: // The line. This may be null if the inlines are not associated with michael@0: // a block or if we just don't know the line. michael@0: const nsLineList_iterator* line; michael@0: michael@0: // The line container. michael@0: nsIFrame* lineContainer; michael@0: michael@0: // The maximum intrinsic width for all previous lines. michael@0: nscoord prevLines; michael@0: michael@0: // The maximum intrinsic width for the current line. At a line michael@0: // break (mandatory for preferred width; allowed for minimum width), michael@0: // the caller should call |Break()|. michael@0: nscoord currentLine; michael@0: michael@0: // True if initial collapsable whitespace should be skipped. This michael@0: // should be true at the beginning of a block, after hard breaks michael@0: // and when the last text ended with whitespace. michael@0: bool skipWhitespace; michael@0: michael@0: // This contains the width of the trimmable whitespace at the end of michael@0: // |currentLine|; it is zero if there is no such whitespace. michael@0: nscoord trailingWhitespace; michael@0: michael@0: // Floats encountered in the lines. michael@0: class FloatInfo { michael@0: public: michael@0: FloatInfo(const nsIFrame* aFrame, nscoord aWidth) michael@0: : mFrame(aFrame), mWidth(aWidth) michael@0: { } michael@0: const nsIFrame* Frame() const { return mFrame; } michael@0: nscoord Width() const { return mWidth; } michael@0: michael@0: private: michael@0: const nsIFrame* mFrame; michael@0: nscoord mWidth; michael@0: }; michael@0: michael@0: nsTArray floats; michael@0: }; michael@0: michael@0: struct InlineMinWidthData : public InlineIntrinsicWidthData { michael@0: InlineMinWidthData() michael@0: : trailingTextFrame(nullptr) michael@0: , atStartOfLine(true) michael@0: {} michael@0: michael@0: // We need to distinguish forced and optional breaks for cases where the michael@0: // current line total is negative. When it is, we need to ignore michael@0: // optional breaks to prevent min-width from ending up bigger than michael@0: // pref-width. michael@0: void ForceBreak(nsRenderingContext *aRenderingContext); michael@0: michael@0: // If the break here is actually taken, aHyphenWidth must be added to the michael@0: // width of the current line. michael@0: void OptionallyBreak(nsRenderingContext *aRenderingContext, michael@0: nscoord aHyphenWidth = 0); michael@0: michael@0: // The last text frame processed so far in the current line, when michael@0: // the last characters in that text frame are relevant for line michael@0: // break opportunities. michael@0: nsIFrame *trailingTextFrame; michael@0: michael@0: // Whether we're currently at the start of the line. If we are, we michael@0: // can't break (for example, between the text-indent and the first michael@0: // word). michael@0: bool atStartOfLine; michael@0: }; michael@0: michael@0: struct InlinePrefWidthData : public InlineIntrinsicWidthData { michael@0: void ForceBreak(nsRenderingContext *aRenderingContext); michael@0: }; michael@0: michael@0: /** michael@0: * Add the intrinsic minimum width of a frame in a way suitable for michael@0: * use in inline layout to an |InlineIntrinsicWidthData| object that michael@0: * represents the intrinsic width information of all the previous michael@0: * frames in the inline layout region. michael@0: * michael@0: * All *allowed* breakpoints within the frame determine what counts as michael@0: * a line for the |InlineIntrinsicWidthData|. This means that michael@0: * |aData->trailingWhitespace| will always be zero (unlike for michael@0: * AddInlinePrefWidth). michael@0: * michael@0: * All the comments for |GetMinWidth| apply, except that this function michael@0: * is responsible for adding padding, border, and margin and for michael@0: * considering the effects of 'width', 'min-width', and 'max-width'. michael@0: * michael@0: * This may be called on any frame. Frames that do not participate in michael@0: * line breaking can inherit the default implementation on nsFrame, michael@0: * which calls |GetMinWidth|. michael@0: */ michael@0: virtual void michael@0: AddInlineMinWidth(nsRenderingContext *aRenderingContext, michael@0: InlineMinWidthData *aData) = 0; michael@0: michael@0: /** michael@0: * Add the intrinsic preferred width of a frame in a way suitable for michael@0: * use in inline layout to an |InlineIntrinsicWidthData| object that michael@0: * represents the intrinsic width information of all the previous michael@0: * frames in the inline layout region. michael@0: * michael@0: * All the comments for |AddInlineMinWidth| and |GetPrefWidth| apply, michael@0: * except that this fills in an |InlineIntrinsicWidthData| structure michael@0: * based on using all *mandatory* breakpoints within the frame. michael@0: */ michael@0: virtual void michael@0: AddInlinePrefWidth(nsRenderingContext *aRenderingContext, michael@0: InlinePrefWidthData *aData) = 0; michael@0: michael@0: /** michael@0: * Return the horizontal components of padding, border, and margin michael@0: * that contribute to the intrinsic width that applies to the parent. michael@0: */ michael@0: struct IntrinsicWidthOffsetData { michael@0: nscoord hPadding, hBorder, hMargin; michael@0: float hPctPadding, hPctMargin; michael@0: michael@0: IntrinsicWidthOffsetData() michael@0: : hPadding(0), hBorder(0), hMargin(0) michael@0: , hPctPadding(0.0f), hPctMargin(0.0f) michael@0: {} michael@0: }; michael@0: virtual IntrinsicWidthOffsetData michael@0: IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) = 0; michael@0: michael@0: virtual mozilla::IntrinsicSize GetIntrinsicSize() = 0; michael@0: michael@0: /* michael@0: * Get the intrinsic ratio of this element, or nsSize(0,0) if it has michael@0: * no intrinsic ratio. The intrinsic ratio is the ratio of the michael@0: * height/width of a box with an intrinsic size or the intrinsic michael@0: * aspect ratio of a scalable vector image without an intrinsic size. michael@0: * michael@0: * Either one of the sides may be zero, indicating a zero or infinite michael@0: * ratio. michael@0: */ michael@0: virtual nsSize GetIntrinsicRatio() = 0; michael@0: michael@0: /** michael@0: * Bit-flags to pass to ComputeSize in |aFlags| parameter. michael@0: */ michael@0: enum { michael@0: /* Set if the frame is in a context where non-replaced blocks should michael@0: * shrink-wrap (e.g., it's floating, absolutely positioned, or michael@0: * inline-block). */ michael@0: eShrinkWrap = 1 << 0, michael@0: /* Set if we'd like to compute our 'auto' height, regardless of our actual michael@0: * computed value of 'height'. (e.g. to get an intrinsic height for flex michael@0: * items with "min-height: auto" to use during flexbox layout.) */ michael@0: eUseAutoHeight = 1 << 1 michael@0: }; michael@0: michael@0: /** michael@0: * Compute the size that a frame will occupy. Called while michael@0: * constructing the nsHTMLReflowState to be used to Reflow the frame, michael@0: * in order to fill its mComputedWidth and mComputedHeight member michael@0: * variables. michael@0: * michael@0: * The |height| member of the return value may be michael@0: * NS_UNCONSTRAINEDSIZE, but the |width| member must not be. michael@0: * michael@0: * Note that the reason that border and padding need to be passed michael@0: * separately is so that the 'box-sizing' property can be handled. michael@0: * Thus aMargin includes absolute positioning offsets as well. michael@0: * michael@0: * @param aCBSize The size of the element's containing block. (Well, michael@0: * the |height| component isn't really.) michael@0: * @param aAvailableWidth The available width for 'auto' widths. michael@0: * This is usually the same as aCBSize.width, michael@0: * but differs in cases such as block michael@0: * formatting context roots next to floats, or michael@0: * in some cases of float reflow in quirks michael@0: * mode. michael@0: * @param aMargin The sum of the vertical / horizontal margins michael@0: * ***AND*** absolute positioning offsets (top, right, michael@0: * bottom, left) of the frame, including actual values michael@0: * resulting from percentages and from the michael@0: * "hypothetical box" for absolute positioning, but michael@0: * not including actual values resulting from 'auto' michael@0: * margins or ignored 'auto' values in absolute michael@0: * positioning. michael@0: * @param aBorder The sum of the vertical / horizontal border widths michael@0: * of the frame. michael@0: * @param aPadding The sum of the vertical / horizontal margins of michael@0: * the frame, including actual values resulting from michael@0: * percentages. michael@0: * @param aFlags Flags to further customize behavior (definitions above). 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) = 0; michael@0: michael@0: /** michael@0: * Compute a tight bounding rectangle for the frame. This is a rectangle michael@0: * that encloses the pixels that are actually drawn. We're allowed to be michael@0: * conservative and currently we don't try very hard. The rectangle is michael@0: * in appunits and relative to the origin of this frame. michael@0: * michael@0: * This probably only needs to include frame bounds, glyph bounds, and michael@0: * text decorations, but today it sometimes includes other things that michael@0: * contribute to visual overflow. michael@0: * michael@0: * @param aContext a rendering context that can be used if we need michael@0: * to do measurement michael@0: */ michael@0: virtual nsRect ComputeTightBounds(gfxContext* aContext) const; michael@0: michael@0: /** michael@0: * This function is similar to GetPrefWidth and ComputeTightBounds: it michael@0: * computes the left and right coordinates of a preferred tight bounding michael@0: * rectangle for the frame. This is a rectangle that would enclose the pixels michael@0: * that are drawn if we lay out the element without taking any optional line michael@0: * breaks. The rectangle is in appunits and relative to the origin of this michael@0: * frame. Currently, this function is only implemented for nsBlockFrame and michael@0: * nsTextFrame and is used to determine intrinsic widths of MathML token michael@0: * elements. michael@0: michael@0: * @param aContext a rendering context that can be used if we need michael@0: * to do measurement michael@0: * @param aX computed left coordinate of the tight bounding rectangle michael@0: * @param aXMost computed intrinsic width of the tight bounding rectangle michael@0: * michael@0: */ michael@0: virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext, michael@0: nscoord* aX, michael@0: nscoord* aXMost); michael@0: michael@0: /** michael@0: * Pre-reflow hook. Before a frame is reflowed this method will be called. michael@0: * This call will always be invoked at least once before a subsequent Reflow michael@0: * and DidReflow call. It may be called more than once, In general you will michael@0: * receive on WillReflow notification before each Reflow request. michael@0: * michael@0: * XXX Is this really the semantics we want? Because we have the NS_FRAME_IN_REFLOW michael@0: * bit we can ensure we don't call it more than once... michael@0: */ michael@0: virtual nsresult WillReflow(nsPresContext* aPresContext) = 0; michael@0: michael@0: /** michael@0: * The frame is given an available size and asked for its desired michael@0: * size. This is the frame's opportunity to reflow its children. michael@0: * michael@0: * If the frame has the NS_FRAME_IS_DIRTY bit set then it is michael@0: * responsible for completely reflowing itself and all of its michael@0: * descendants. michael@0: * michael@0: * Otherwise, if the frame has the NS_FRAME_HAS_DIRTY_CHILDREN bit michael@0: * set, then it is responsible for reflowing at least those michael@0: * children that have NS_FRAME_HAS_DIRTY_CHILDREN or NS_FRAME_IS_DIRTY michael@0: * set. michael@0: * michael@0: * If a difference in available size from the previous reflow causes michael@0: * the frame's size to change, it should reflow descendants as needed. michael@0: * michael@0: * @param aReflowMetrics out parameter where you should return the michael@0: * desired size and ascent/descent info. You should include any michael@0: * space you want for border/padding in the desired size you return. michael@0: * michael@0: * It's okay to return a desired size that exceeds the avail michael@0: * size if that's the smallest you can be, i.e. it's your michael@0: * minimum size. michael@0: * michael@0: * For an incremental reflow you are responsible for invalidating michael@0: * any area within your frame that needs repainting (including michael@0: * borders). If your new desired size is different than your current michael@0: * size, then your parent frame is responsible for making sure that michael@0: * the difference between the two rects is repainted michael@0: * michael@0: * @param aReflowState information about your reflow including the reason michael@0: * for the reflow and the available space in which to lay out. Each michael@0: * dimension of the available space can either be constrained or michael@0: * unconstrained (a value of NS_UNCONSTRAINEDSIZE). michael@0: * michael@0: * Note that the available space can be negative. In this case you michael@0: * still must return an accurate desired size. If you're a container michael@0: * you must always reflow at least one frame regardless of the michael@0: * available space michael@0: * michael@0: * @param aStatus a return value indicating whether the frame is complete michael@0: * and whether the next-in-flow is dirty and needs to be reflowed michael@0: */ michael@0: virtual nsresult Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aReflowMetrics, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) = 0; michael@0: michael@0: /** michael@0: * Post-reflow hook. After a frame is reflowed this method will be called michael@0: * informing the frame that this reflow process is complete, and telling the michael@0: * frame the status returned by the Reflow member function. michael@0: * michael@0: * This call may be invoked many times, while NS_FRAME_IN_REFLOW is set, before michael@0: * it is finally called once with a NS_FRAME_REFLOW_COMPLETE value. When called michael@0: * with a NS_FRAME_REFLOW_COMPLETE value the NS_FRAME_IN_REFLOW bit in the michael@0: * frame state will be cleared. michael@0: * michael@0: * XXX This doesn't make sense. If the frame is reflowed but not complete, then michael@0: * the status should be NS_FRAME_NOT_COMPLETE and not NS_FRAME_COMPLETE michael@0: * XXX Don't we want the semantics to dictate that we only call this once for michael@0: * a given reflow? michael@0: */ michael@0: virtual nsresult DidReflow(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nsDidReflowStatus aStatus) = 0; michael@0: michael@0: // XXX Maybe these three should be a separate interface? michael@0: michael@0: /** michael@0: * Updates the overflow areas of the frame. This can be called if an michael@0: * overflow area of the frame's children has changed without reflowing. michael@0: * @return true if either of the overflow areas for this frame have changed. michael@0: */ michael@0: virtual bool UpdateOverflow() = 0; michael@0: michael@0: /** michael@0: * Helper method used by block reflow to identify runs of text so michael@0: * that proper word-breaking can be done. michael@0: * michael@0: * @return michael@0: * true if we can continue a "text run" through the frame. A michael@0: * text run is text that should be treated contiguously for line michael@0: * and word breaking. michael@0: */ michael@0: virtual bool CanContinueTextRun() const = 0; michael@0: michael@0: /** michael@0: * Append the rendered text to the passed-in string. michael@0: * The appended text will often not contain all the whitespace from source, michael@0: * depending on whether the CSS rule "white-space: pre" is active for this frame. michael@0: * if aStartOffset + aLength goes past end, or if aLength is not specified michael@0: * then use the text up to the string's end. michael@0: * Call this on the primary frame for a text node. michael@0: * @param aAppendToString String to append text to, or null if text should not be returned michael@0: * @param aSkipChars if aSkipIter is non-null, this must also be non-null. michael@0: * This gets used as backing data for the iterator so it should outlive the iterator. michael@0: * @param aSkipIter Where to fill in the gfxSkipCharsIterator info, or null if not needed by caller michael@0: * @param aStartOffset Skipped (rendered text) start offset michael@0: * @param aSkippedMaxLength Maximum number of characters to return michael@0: * The iterator can be used to map content offsets to offsets in the returned string, or vice versa. michael@0: */ michael@0: virtual nsresult GetRenderedText(nsAString* aAppendToString = nullptr, michael@0: gfxSkipChars* aSkipChars = nullptr, michael@0: gfxSkipCharsIterator* aSkipIter = nullptr, michael@0: uint32_t aSkippedStartOffset = 0, michael@0: uint32_t aSkippedMaxLength = UINT32_MAX) michael@0: { return NS_ERROR_NOT_IMPLEMENTED; } michael@0: michael@0: /** michael@0: * Returns true if the frame contains any non-collapsed characters. michael@0: * This method is only available for text frames, and it will return false michael@0: * for all other frame types. michael@0: */ michael@0: virtual bool HasAnyNoncollapsedCharacters() michael@0: { return false; } michael@0: michael@0: /** michael@0: * Accessor functions to get/set the associated view object michael@0: * michael@0: * GetView returns non-null if and only if |HasView| returns true. michael@0: */ michael@0: bool HasView() const { return !!(mState & NS_FRAME_HAS_VIEW); } michael@0: nsView* GetView() const; michael@0: virtual nsView* GetViewExternal() const; michael@0: nsresult SetView(nsView* aView); michael@0: michael@0: /** michael@0: * Find the closest view (on |this| or an ancestor). michael@0: * If aOffset is non-null, it will be set to the offset of |this| michael@0: * from the returned view. michael@0: */ michael@0: nsView* GetClosestView(nsPoint* aOffset = nullptr) const; michael@0: michael@0: /** michael@0: * Find the closest ancestor (excluding |this| !) that has a view michael@0: */ michael@0: nsIFrame* GetAncestorWithView() const; michael@0: virtual nsIFrame* GetAncestorWithViewExternal() const; michael@0: michael@0: /** michael@0: * Get the offset between the coordinate systems of |this| and aOther. michael@0: * Adding the return value to a point in the coordinate system of |this| michael@0: * will transform the point to the coordinate system of aOther. michael@0: * michael@0: * aOther must be non-null. michael@0: * michael@0: * This function is fastest when aOther is an ancestor of |this|. michael@0: * michael@0: * This function _DOES NOT_ work across document boundaries. michael@0: * Use this function only when |this| and aOther are in the same document. michael@0: * michael@0: * NOTE: this actually returns the offset from aOther to |this|, but michael@0: * that offset is added to transform _coordinates_ from |this| to michael@0: * aOther. michael@0: */ michael@0: nsPoint GetOffsetTo(const nsIFrame* aOther) const; michael@0: virtual nsPoint GetOffsetToExternal(const nsIFrame* aOther) const; michael@0: michael@0: /** michael@0: * Get the offset between the coordinate systems of |this| and aOther michael@0: * expressed in appunits per dev pixel of |this|' document. Adding the return michael@0: * value to a point that is relative to the origin of |this| will make the michael@0: * point relative to the origin of aOther but in the appunits per dev pixel michael@0: * ratio of |this|. michael@0: * michael@0: * aOther must be non-null. michael@0: * michael@0: * This function is fastest when aOther is an ancestor of |this|. michael@0: * michael@0: * This function works across document boundaries. michael@0: * michael@0: * Because this function may cross document boundaries that have different michael@0: * app units per dev pixel ratios it needs to be used very carefully. michael@0: * michael@0: * NOTE: this actually returns the offset from aOther to |this|, but michael@0: * that offset is added to transform _coordinates_ from |this| to michael@0: * aOther. michael@0: */ michael@0: nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther) const; michael@0: michael@0: /** michael@0: * Get the screen rect of the frame in pixels. michael@0: * @return the pixel rect of the frame in screen coordinates. michael@0: */ michael@0: nsIntRect GetScreenRect() const; michael@0: virtual nsIntRect GetScreenRectExternal() const; michael@0: michael@0: /** michael@0: * Get the screen rect of the frame in app units. michael@0: * @return the app unit rect of the frame in screen coordinates. michael@0: */ michael@0: nsRect GetScreenRectInAppUnits() const; michael@0: virtual nsRect GetScreenRectInAppUnitsExternal() const; michael@0: michael@0: /** michael@0: * Returns the offset from this frame to the closest geometric parent that michael@0: * has a view. Also returns the containing view or null in case of error michael@0: */ michael@0: void GetOffsetFromView(nsPoint& aOffset, nsView** aView) const; michael@0: michael@0: /** michael@0: * Returns the nearest widget containing this frame. If this frame has a michael@0: * view and the view has a widget, then this frame's widget is michael@0: * returned, otherwise this frame's geometric parent is checked michael@0: * recursively upwards. michael@0: * XXX virtual because gfx callers use it! (themes) michael@0: */ michael@0: virtual nsIWidget* GetNearestWidget() const; michael@0: michael@0: /** michael@0: * Same as GetNearestWidget() above but uses an outparam to return the offset michael@0: * of this frame to the returned widget expressed in appunits of |this| (the michael@0: * widget might be in a different document with a different zoom). michael@0: */ michael@0: virtual nsIWidget* GetNearestWidget(nsPoint& aOffset) const; michael@0: michael@0: /** michael@0: * Get the "type" of the frame. May return nullptr. michael@0: * michael@0: * @see nsGkAtoms michael@0: */ michael@0: virtual nsIAtom* GetType() const = 0; michael@0: michael@0: /** michael@0: * Returns a transformation matrix that converts points in this frame's michael@0: * coordinate space to points in some ancestor frame's coordinate space. michael@0: * The frame decides which ancestor it will use as a reference point. michael@0: * If this frame has no ancestor, aOutAncestor will be set to null. michael@0: * michael@0: * @param aStopAtAncestor don't look further than aStopAtAncestor. If null, michael@0: * all ancestors (including across documents) will be traversed. michael@0: * @param aOutAncestor [out] The ancestor frame the frame has chosen. If michael@0: * this frame has no ancestor, *aOutAncestor will be set to null. If michael@0: * this frame is not a root frame, then *aOutAncestor will be in the same michael@0: * document as this frame. If this frame IsTransformed(), then *aOutAncestor michael@0: * will be the parent frame (if not preserve-3d) or the nearest non-transformed michael@0: * ancestor (if preserve-3d). michael@0: * @return A gfxMatrix that converts points in this frame's coordinate space michael@0: * into points in aOutAncestor's coordinate space. michael@0: */ michael@0: gfx3DMatrix GetTransformMatrix(const nsIFrame* aStopAtAncestor, michael@0: nsIFrame **aOutAncestor); michael@0: michael@0: /** michael@0: * Bit-flags to pass to IsFrameOfType() michael@0: */ michael@0: enum { michael@0: eMathML = 1 << 0, michael@0: eSVG = 1 << 1, michael@0: eSVGForeignObject = 1 << 2, michael@0: eSVGContainer = 1 << 3, michael@0: eSVGGeometry = 1 << 4, michael@0: eSVGPaintServer = 1 << 5, michael@0: eBidiInlineContainer = 1 << 6, michael@0: // the frame is for a replaced element, such as an image michael@0: eReplaced = 1 << 7, michael@0: // Frame that contains a block but looks like a replaced element michael@0: // from the outside michael@0: eReplacedContainsBlock = 1 << 8, michael@0: // A frame that participates in inline reflow, i.e., one that michael@0: // requires nsHTMLReflowState::mLineLayout. michael@0: eLineParticipant = 1 << 9, michael@0: eXULBox = 1 << 10, michael@0: eCanContainOverflowContainers = 1 << 11, michael@0: eBlockFrame = 1 << 12, michael@0: eTablePart = 1 << 13, michael@0: // If this bit is set, the frame doesn't allow ignorable whitespace as michael@0: // children. For example, the whitespace between \n\n
michael@0: // will be excluded during the construction of children. michael@0: eExcludesIgnorableWhitespace = 1 << 14, michael@0: eSupportsCSSTransforms = 1 << 15, michael@0: michael@0: // These are to allow nsFrame::Init to assert that IsFrameOfType michael@0: // implementations all call the base class method. They are only michael@0: // meaningful in DEBUG builds. michael@0: eDEBUGAllFrames = 1 << 30, michael@0: eDEBUGNoFrames = 1 << 31 michael@0: }; michael@0: michael@0: /** michael@0: * API for doing a quick check if a frame is of a given michael@0: * type. Returns true if the frame matches ALL flags passed in. michael@0: * michael@0: * Implementations should always override with inline virtual michael@0: * functions that call the base class's IsFrameOfType method. michael@0: */ michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const michael@0: { michael@0: #ifdef DEBUG michael@0: return !(aFlags & ~(nsIFrame::eDEBUGAllFrames | nsIFrame::eSupportsCSSTransforms)); michael@0: #else michael@0: return !(aFlags & ~nsIFrame::eSupportsCSSTransforms); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the frame is a block wrapper. michael@0: */ michael@0: bool IsBlockWrapper() const; michael@0: michael@0: /** michael@0: * Get this frame's CSS containing block. michael@0: * michael@0: * The algorithm is defined in michael@0: * http://www.w3.org/TR/CSS2/visudet.html#containing-block-details. michael@0: * michael@0: * NOTE: This is guaranteed to return a non-null pointer when invoked on any michael@0: * frame other than the root frame. michael@0: */ michael@0: nsIFrame* GetContainingBlock() const; michael@0: michael@0: /** michael@0: * Is this frame a containing block for floating elements? michael@0: * Note that very few frames are, so default to false. michael@0: */ michael@0: virtual bool IsFloatContainingBlock() const { return false; } michael@0: michael@0: /** michael@0: * Is this a leaf frame? Frames that want the frame constructor to be able michael@0: * to construct kids for them should return false, all others should return michael@0: * true. Note that returning true here does not mean that the frame _can't_ michael@0: * have kids. It could still have kids created via michael@0: * nsIAnonymousContentCreator. Returning true indicates that "normal" michael@0: * (non-anonymous, XBL-bound, CSS generated content, etc) children should not michael@0: * be constructed. michael@0: */ michael@0: virtual bool IsLeaf() const; michael@0: michael@0: /** michael@0: * Marks all display items created by this frame as needing a repaint, michael@0: * and calls SchedulePaint() if requested and one is not already pending. michael@0: * michael@0: * This includes all display items created by this frame, including michael@0: * container types. michael@0: * michael@0: * @param aDisplayItemKey If specified, only issues an invalidate michael@0: * if this frame painted a display item of that type during the michael@0: * previous paint. SVG rendering observers are always notified. michael@0: */ michael@0: virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0); michael@0: michael@0: /** michael@0: * Same as InvalidateFrame(), but only mark a fixed rect as needing michael@0: * repainting. michael@0: * michael@0: * @param aRect The rect to invalidate, relative to the TopLeft of the michael@0: * frame's border box. michael@0: * @param aDisplayItemKey If specified, only issues an invalidate michael@0: * if this frame painted a display item of that type during the michael@0: * previous paint. SVG rendering observers are always notified. michael@0: */ michael@0: virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0); michael@0: michael@0: /** michael@0: * Calls InvalidateFrame() on all frames descendant frames (including michael@0: * this one). michael@0: * michael@0: * This function doesn't walk through placeholder frames to invalidate michael@0: * the out-of-flow frames. michael@0: * michael@0: * @param aDisplayItemKey If specified, only issues an invalidate michael@0: * if this frame painted a display item of that type during the michael@0: * previous paint. SVG rendering observers are always notified. michael@0: */ michael@0: void InvalidateFrameSubtree(uint32_t aDisplayItemKey = 0); michael@0: michael@0: /** michael@0: * Called when a frame is about to be removed and needs to be invalidated. michael@0: * Normally does nothing since DLBI handles removed frames. michael@0: */ michael@0: virtual void InvalidateFrameForRemoval() {} michael@0: michael@0: /** michael@0: * When HasUserData(frame->LayerIsPrerenderedDataKey()), then the michael@0: * entire overflow area of this frame has been rendered in its michael@0: * layer(s). michael@0: */ michael@0: static void* LayerIsPrerenderedDataKey() { michael@0: return &sLayerIsPrerenderedDataKey; michael@0: } michael@0: static uint8_t sLayerIsPrerenderedDataKey; michael@0: michael@0: /** michael@0: * Try to update this frame's transform without invalidating any michael@0: * content. Return true iff successful. If unsuccessful, the michael@0: * caller is responsible for scheduling an invalidating paint. michael@0: * michael@0: * If the result is true, aLayerResult will be filled in with the michael@0: * transform layer for the frame. michael@0: */ michael@0: bool TryUpdateTransformOnly(Layer** aLayerResult); michael@0: michael@0: /** michael@0: * Checks if a frame has had InvalidateFrame() called on it since the michael@0: * last paint. michael@0: * michael@0: * If true, then the invalid rect is returned in aRect, with an michael@0: * empty rect meaning all pixels drawn by this frame should be michael@0: * invalidated. michael@0: * If false, aRect is left unchanged. michael@0: */ michael@0: bool IsInvalid(nsRect& aRect); michael@0: michael@0: /** michael@0: * Check if any frame within the frame subtree (including this frame) michael@0: * returns true for IsInvalid(). michael@0: */ michael@0: bool HasInvalidFrameInSubtree() michael@0: { michael@0: return HasAnyStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT); michael@0: } michael@0: michael@0: /** michael@0: * Removes the invalid state from the current frame and all michael@0: * descendant frames. michael@0: */ michael@0: void ClearInvalidationStateBits(); michael@0: michael@0: /** michael@0: * Ensures that the refresh driver is running, and schedules a view michael@0: * manager flush on the next tick. michael@0: * michael@0: * The view manager flush will update the layer tree, repaint any michael@0: * invalid areas in the layer tree and schedule a layer tree michael@0: * composite operation to display the layer tree. michael@0: * michael@0: * In general it is not necessary for frames to call this when they change. michael@0: * For example, changes that result in a reflow will have this called for michael@0: * them by PresContext::DoReflow when the reflow begins. Style changes that michael@0: * do not trigger a reflow should have this called for them by michael@0: * DoApplyRenderingChangeToTree. michael@0: * michael@0: * @param aType PAINT_COMPOSITE_ONLY : No changes have been made michael@0: * that require a layer tree update, so only schedule a layer michael@0: * tree composite. michael@0: * PAINT_DELAYED_COMPRESS : Schedule a paint to be executed after a delay, and michael@0: * put FrameLayerBuilder in 'compressed' mode that avoids short cut optimizations. michael@0: */ michael@0: enum PaintType { michael@0: PAINT_DEFAULT = 0, michael@0: PAINT_COMPOSITE_ONLY, michael@0: PAINT_DELAYED_COMPRESS michael@0: }; michael@0: void SchedulePaint(PaintType aType = PAINT_DEFAULT); michael@0: michael@0: /** michael@0: * Checks if the layer tree includes a dedicated layer for this michael@0: * frame/display item key pair, and invalidates at least aDamageRect michael@0: * area within that layer. michael@0: * michael@0: * If no layer is found, calls InvalidateFrame() instead. michael@0: * michael@0: * @param aDamageRect Area of the layer to invalidate. michael@0: * @param aDisplayItemKey Display item type. michael@0: * @param aFlags UPDATE_IS_ASYNC : Will skip the invalidation michael@0: * if the found layer is being composited by a remote michael@0: * compositor. michael@0: * @return Layer, if found, nullptr otherwise. michael@0: */ michael@0: enum { michael@0: UPDATE_IS_ASYNC = 1 << 0 michael@0: }; michael@0: Layer* InvalidateLayer(uint32_t aDisplayItemKey, const nsIntRect* aDamageRect = nullptr, uint32_t aFlags = 0); michael@0: michael@0: /** michael@0: * Returns a rect that encompasses everything that might be painted by michael@0: * this frame. This includes this frame, all its descendent frames, this michael@0: * frame's outline, and descentant frames' outline, but does not include michael@0: * areas clipped out by the CSS "overflow" and "clip" properties. michael@0: * michael@0: * HasOverflowRects() (below) will return true when this overflow michael@0: * rect has been explicitly set, even if it matches mRect. michael@0: * XXX Note: because of a space optimization using the formula above, michael@0: * during reflow this function does not give accurate data if michael@0: * FinishAndStoreOverflow has been called but mRect hasn't yet been michael@0: * updated yet. FIXME: This actually isn't true, but it should be. michael@0: * michael@0: * The visual overflow rect should NEVER be used for things that michael@0: * affect layout. The scrollable overflow rect is permitted to affect michael@0: * layout. michael@0: * michael@0: * @return the rect relative to this frame's origin, but after michael@0: * CSS transforms have been applied (i.e. not really this frame's coordinate michael@0: * system, and may not contain the frame's border-box, e.g. if there michael@0: * is a CSS transform scaling it down) michael@0: */ michael@0: nsRect GetVisualOverflowRect() const { michael@0: return GetOverflowRect(eVisualOverflow); michael@0: } michael@0: michael@0: /** michael@0: * Returns a rect that encompasses the area of this frame that the michael@0: * user should be able to scroll to reach. This is similar to michael@0: * GetVisualOverflowRect, but does not include outline or shadows, and michael@0: * may in the future include more margins than visual overflow does. michael@0: * It does not include areas clipped out by the CSS "overflow" and michael@0: * "clip" properties. michael@0: * michael@0: * HasOverflowRects() (below) will return true when this overflow michael@0: * rect has been explicitly set, even if it matches mRect. michael@0: * XXX Note: because of a space optimization using the formula above, michael@0: * during reflow this function does not give accurate data if michael@0: * FinishAndStoreOverflow has been called but mRect hasn't yet been michael@0: * updated yet. michael@0: * michael@0: * @return the rect relative to this frame's origin, but after michael@0: * CSS transforms have been applied (i.e. not really this frame's coordinate michael@0: * system, and may not contain the frame's border-box, e.g. if there michael@0: * is a CSS transform scaling it down) michael@0: */ michael@0: nsRect GetScrollableOverflowRect() const { michael@0: return GetOverflowRect(eScrollableOverflow); michael@0: } michael@0: michael@0: nsRect GetOverflowRect(nsOverflowType aType) const; michael@0: michael@0: nsOverflowAreas GetOverflowAreas() const; michael@0: michael@0: /** michael@0: * Same as GetOverflowAreas, except in this frame's coordinate michael@0: * system (before transforms are applied). michael@0: * michael@0: * @return the overflow areas relative to this frame, before any CSS transforms have michael@0: * been applied, i.e. in this frame's coordinate system michael@0: */ michael@0: nsOverflowAreas GetOverflowAreasRelativeToSelf() const; michael@0: michael@0: /** michael@0: * Same as GetScrollableOverflowRect, except relative to the parent michael@0: * frame. michael@0: * michael@0: * @return the rect relative to the parent frame, in the parent frame's michael@0: * coordinate system michael@0: */ michael@0: nsRect GetScrollableOverflowRectRelativeToParent() const; michael@0: michael@0: /** michael@0: * Same as GetScrollableOverflowRect, except in this frame's coordinate michael@0: * system (before transforms are applied). michael@0: * michael@0: * @return the rect relative to this frame, before any CSS transforms have michael@0: * been applied, i.e. in this frame's coordinate system michael@0: */ michael@0: nsRect GetScrollableOverflowRectRelativeToSelf() const; michael@0: michael@0: /** michael@0: * Like GetVisualOverflowRect, except in this frame's michael@0: * coordinate system (before transforms are applied). michael@0: * michael@0: * @return the rect relative to this frame, before any CSS transforms have michael@0: * been applied, i.e. in this frame's coordinate system michael@0: */ michael@0: nsRect GetVisualOverflowRectRelativeToSelf() const; michael@0: michael@0: /** michael@0: * Returns this frame's visual overflow rect as it would be before taking michael@0: * account of SVG effects or transforms. The rect returned is relative to michael@0: * this frame. michael@0: */ michael@0: nsRect GetPreEffectsVisualOverflowRect() const; michael@0: michael@0: /** michael@0: * Store the overflow area in the frame's mOverflow.mVisualDeltas michael@0: * fields or as a frame property in the frame manager so that it can michael@0: * be retrieved later without reflowing the frame. Returns true if either of michael@0: * the overflow areas changed. michael@0: */ michael@0: bool FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, michael@0: nsSize aNewSize, nsSize* aOldSize = nullptr); michael@0: michael@0: bool FinishAndStoreOverflow(nsHTMLReflowMetrics* aMetrics) { michael@0: return FinishAndStoreOverflow(aMetrics->mOverflowAreas, michael@0: nsSize(aMetrics->Width(), aMetrics->Height())); michael@0: } michael@0: michael@0: /** michael@0: * Returns whether the frame has an overflow rect that is different from michael@0: * its border-box. michael@0: */ michael@0: bool HasOverflowAreas() const { michael@0: return mOverflow.mType != NS_FRAME_OVERFLOW_NONE; michael@0: } michael@0: michael@0: /** michael@0: * Removes any stored overflow rects (visual and scrollable) from the frame. michael@0: * Returns true if the overflow changed. michael@0: */ michael@0: bool ClearOverflowRects(); michael@0: michael@0: /** michael@0: * Determine whether borders should not be painted on certain sides of the michael@0: * frame. michael@0: * michael@0: * @note (See also bug 743402, comment 11) GetSkipSides() and its sister michael@0: * method, ApplySkipSides() checks to see if this frame has a previous michael@0: * or next continuation to determine if a side should be skipped. michael@0: * Unfortunately, this only works after reflow has been completed. In michael@0: * lieu of this, during reflow, an nsHTMLReflowState parameter can be michael@0: * passed in, indicating that it should be used to determine if sides michael@0: * should be skipped during reflow. michael@0: */ michael@0: #define LOGICAL_SIDE_B_START 1 michael@0: #define LOGICAL_SIDE_I_START 2 michael@0: #define LOGICAL_SIDE_B_END 4 michael@0: #define LOGICAL_SIDE_I_END 8 michael@0: #define LOGICAL_SIDES_I_BOTH (LOGICAL_SIDE_I_START | LOGICAL_SIDE_I_END) michael@0: #define LOGICAL_SIDES_B_BOTH (LOGICAL_SIDE_B_START | LOGICAL_SIDE_B_END) michael@0: #define LOGICAL_SIDES_ALL (LOGICAL_SIDE_I_START | LOGICAL_SIDE_I_END | \ michael@0: LOGICAL_SIDE_B_START | LOGICAL_SIDE_B_END) michael@0: int GetSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const; michael@0: virtual int michael@0: GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const { michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * @returns true if this frame is selected. michael@0: */ michael@0: bool IsSelected() const; michael@0: michael@0: /** michael@0: * called to discover where this frame, or a parent frame has user-select style michael@0: * applied, which affects that way that it is selected. michael@0: * michael@0: * @param aIsSelectable out param. Set to true if the frame can be selected michael@0: * (i.e. is not affected by user-select: none) michael@0: * @param aSelectStyle out param. Returns the type of selection style found michael@0: * (using values defined in nsStyleConsts.h). michael@0: */ michael@0: virtual nsresult IsSelectable(bool* aIsSelectable, uint8_t* aSelectStyle) const = 0; michael@0: michael@0: /** michael@0: * Called to retrieve the SelectionController associated with the frame. michael@0: * @param aSelCon will contain the selection controller associated with michael@0: * the frame. michael@0: */ michael@0: virtual nsresult GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) = 0; michael@0: michael@0: /** michael@0: * Call to get nsFrameSelection for this frame. michael@0: */ michael@0: already_AddRefed GetFrameSelection(); michael@0: michael@0: /** michael@0: * GetConstFrameSelection returns an object which methods are safe to use for michael@0: * example in nsIFrame code. michael@0: */ michael@0: const nsFrameSelection* GetConstFrameSelection() const; michael@0: michael@0: /** michael@0: * called to find the previous/next character, word, or line returns the actual michael@0: * nsIFrame and the frame offset. THIS DOES NOT CHANGE SELECTION STATE michael@0: * uses frame's begin selection state to start. if no selection on this frame will michael@0: * return NS_ERROR_FAILURE michael@0: * @param aPOS is defined in nsFrameSelection michael@0: */ michael@0: virtual nsresult PeekOffset(nsPeekOffsetStruct *aPos); michael@0: michael@0: /** michael@0: * called to find the previous/next selectable leaf frame. michael@0: * @param aDirection [in] the direction to move in (eDirPrevious or eDirNext) michael@0: * @param aVisual [in] whether bidi caret behavior is visual (true) or logical (false) michael@0: * @param aJumpLines [in] whether to allow jumping across line boundaries michael@0: * @param aScrollViewStop [in] whether to stop when reaching a scroll frame boundary michael@0: * @param aOutFrame [out] the previous/next selectable leaf frame michael@0: * @param aOutOffset [out] 0 indicates that we arrived at the beginning of the output frame; michael@0: * -1 indicates that we arrived at its end. michael@0: * @param aOutJumpedLine [out] whether this frame and the returned frame are on different lines michael@0: */ michael@0: nsresult GetFrameFromDirection(nsDirection aDirection, bool aVisual, michael@0: bool aJumpLines, bool aScrollViewStop, michael@0: nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine); michael@0: michael@0: /** michael@0: * called to see if the children of the frame are visible from indexstart to index end. michael@0: * this does not change any state. returns true only if the indexes are valid and any of michael@0: * the children are visible. for textframes this index is the character index. michael@0: * if aStart = aEnd result will be false michael@0: * @param aStart start index of first child from 0-N (number of children) michael@0: * @param aEnd end index of last child from 0-N michael@0: * @param aRecurse should this frame talk to siblings to get to the contents other children? michael@0: * @param aFinished did this frame have the aEndIndex? or is there more work to do michael@0: * @param _retval return value true or false. false = range is not rendered. michael@0: */ michael@0: virtual nsresult CheckVisibility(nsPresContext* aContext, int32_t aStartIndex, int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *_retval)=0; michael@0: michael@0: /** michael@0: * Called to tell a frame that one of its child frames is dirty (i.e., michael@0: * has the NS_FRAME_IS_DIRTY *or* NS_FRAME_HAS_DIRTY_CHILDREN bit michael@0: * set). This should always set the NS_FRAME_HAS_DIRTY_CHILDREN on michael@0: * the frame, and may do other work. michael@0: */ michael@0: virtual void ChildIsDirty(nsIFrame* aChild) = 0; michael@0: michael@0: /** michael@0: * Called to retrieve this frame's accessible. michael@0: * If this frame implements Accessibility return a valid accessible michael@0: * If not return NS_ERROR_NOT_IMPLEMENTED. michael@0: * Note: Accessible must be refcountable. Do not implement directly on your frame michael@0: * Use a mediatior of some kind. michael@0: */ michael@0: #ifdef ACCESSIBILITY michael@0: virtual mozilla::a11y::AccType AccessibleType() = 0; michael@0: #endif michael@0: michael@0: /** michael@0: * Get the frame whose style context should be the parent of this michael@0: * frame's style context (i.e., provide the parent style context). michael@0: * This frame must either be an ancestor of this frame or a child. If michael@0: * this returns a child frame, then the child frame must be sure to michael@0: * return a grandparent or higher! Furthermore, if a child frame is michael@0: * returned it must have the same GetContent() as this frame. michael@0: * michael@0: * @return The frame whose style context should be the parent of this frame's michael@0: * style context. Null is permitted, and means that this frame's michael@0: * style context should be the root of the style context tree. michael@0: */ michael@0: virtual nsIFrame* GetParentStyleContextFrame() const = 0; michael@0: michael@0: /** michael@0: * Determines whether a frame is visible for painting; michael@0: * taking into account whether it is painting a selection or printing. michael@0: */ michael@0: bool IsVisibleForPainting(nsDisplayListBuilder* aBuilder); michael@0: /** michael@0: * Determines whether a frame is visible for painting or collapsed; michael@0: * taking into account whether it is painting a selection or printing, michael@0: */ michael@0: bool IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder); michael@0: /** michael@0: * As above, but slower because we have to recompute some stuff that michael@0: * aBuilder already has. michael@0: */ michael@0: bool IsVisibleForPainting(); michael@0: /** michael@0: * Check whether this frame is visible in the current selection. Returns michael@0: * true if there is no current selection. michael@0: */ michael@0: bool IsVisibleInSelection(nsDisplayListBuilder* aBuilder); michael@0: michael@0: /** michael@0: * Overridable function to determine whether this frame should be considered michael@0: * "in" the given non-null aSelection for visibility purposes. michael@0: */ michael@0: virtual bool IsVisibleInSelection(nsISelection* aSelection); michael@0: michael@0: /** michael@0: * Determines whether this frame is a pseudo stacking context, looking michael@0: * only as style --- i.e., assuming that it's in-flow and not a replaced michael@0: * element and not an SVG element. michael@0: * XXX maybe check IsTransformed()? michael@0: */ michael@0: bool IsPseudoStackingContextFromStyle(); michael@0: michael@0: virtual bool HonorPrintBackgroundSettings() { return true; } michael@0: michael@0: /** michael@0: * Determine whether the frame is logically empty, which is roughly michael@0: * whether the layout would be the same whether or not the frame is michael@0: * present. Placeholder frames should return true. Block frames michael@0: * should be considered empty whenever margins collapse through them, michael@0: * even though those margins are relevant. Text frames containing michael@0: * only whitespace that does not contribute to the height of the line michael@0: * should return true. michael@0: */ michael@0: virtual bool IsEmpty() = 0; michael@0: /** michael@0: * Return the same as IsEmpty(). This may only be called after the frame michael@0: * has been reflowed and before any further style or content changes. michael@0: */ michael@0: virtual bool CachedIsEmpty(); michael@0: /** michael@0: * Determine whether the frame is logically empty, assuming that all michael@0: * its children are empty. michael@0: */ michael@0: virtual bool IsSelfEmpty() = 0; michael@0: michael@0: /** michael@0: * IsGeneratedContentFrame returns whether a frame corresponds to michael@0: * generated content michael@0: * michael@0: * @return whether the frame correspods to generated content michael@0: */ michael@0: bool IsGeneratedContentFrame() { michael@0: return (mState & NS_FRAME_GENERATED_CONTENT) != 0; michael@0: } michael@0: michael@0: /** michael@0: * IsPseudoFrame returns whether a frame is a pseudo frame (eg an michael@0: * anonymous table-row frame created for a CSS table-cell without an michael@0: * enclosing table-row. michael@0: * michael@0: * @param aParentContent the content node corresponding to the parent frame michael@0: * @return whether the frame is a pseudo frame michael@0: */ michael@0: bool IsPseudoFrame(nsIContent* aParentContent) { michael@0: return mContent == aParentContent; michael@0: } michael@0: michael@0: FrameProperties Properties() const { michael@0: return FrameProperties(PresContext()->PropertyTable(), this); michael@0: } michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(BaseLevelProperty, nullptr) michael@0: NS_DECLARE_FRAME_PROPERTY(EmbeddingLevelProperty, nullptr) michael@0: NS_DECLARE_FRAME_PROPERTY(ParagraphDepthProperty, nullptr) michael@0: michael@0: #define NS_GET_BASE_LEVEL(frame) \ michael@0: NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::BaseLevelProperty())) michael@0: michael@0: #define NS_GET_EMBEDDING_LEVEL(frame) \ michael@0: NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::EmbeddingLevelProperty())) michael@0: michael@0: #define NS_GET_PARAGRAPH_DEPTH(frame) \ michael@0: NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::ParagraphDepthProperty())) michael@0: michael@0: /** michael@0: * Return true if and only if this frame obeys visibility:hidden. michael@0: * if it does not, then nsContainerFrame will hide its view even though michael@0: * this means children can't be made visible again. michael@0: */ michael@0: virtual bool SupportsVisibilityHidden() { return true; } michael@0: michael@0: /** michael@0: * Returns true if the frame has a valid clip rect set via the 'clip' michael@0: * property, and the 'clip' property applies to this frame. The 'clip' michael@0: * property applies to HTML frames if they are absolutely positioned. The michael@0: * 'clip' property applies to SVG frames regardless of the value of the michael@0: * 'position' property. michael@0: * michael@0: * If this method returns true, then we also set aRect to the computed clip michael@0: * rect, with coordinates relative to this frame's origin. aRect must not be michael@0: * null! michael@0: */ michael@0: bool GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, michael@0: const nsSize& aSize) const; michael@0: michael@0: /** michael@0: * Check if this frame is focusable and in the current tab order. michael@0: * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable. michael@0: * For example, only the selected radio button in a group is in the michael@0: * tab order, unless the radio group has no selection in which case michael@0: * all of the visible, non-disabled radio buttons in the group are michael@0: * in the tab order. On the other hand, all of the visible, non-disabled michael@0: * radio buttons are always focusable via clicking or script. michael@0: * Also, depending on the pref accessibility.tabfocus some widgets may be michael@0: * focusable but removed from the tab order. This is the default on michael@0: * Mac OS X, where fewer items are focusable. michael@0: * @param [in, optional] aTabIndex the computed tab index michael@0: * < 0 if not tabbable michael@0: * == 0 if in normal tab order michael@0: * > 0 can be tabbed to in the order specified by this value michael@0: * @param [in, optional] aWithMouse, is this focus query for mouse clicking michael@0: * @return whether the frame is focusable via mouse, kbd or script. michael@0: */ michael@0: virtual bool IsFocusable(int32_t *aTabIndex = nullptr, bool aWithMouse = false); michael@0: michael@0: // BOX LAYOUT METHODS michael@0: // These methods have been migrated from nsIBox and are in the process of michael@0: // being refactored. DO NOT USE OUTSIDE OF XUL. michael@0: bool IsBoxFrame() const michael@0: { michael@0: return IsFrameOfType(nsIFrame::eXULBox); michael@0: } michael@0: bool IsBoxWrapped() const michael@0: { return (!IsBoxFrame() && mParent && mParent->IsBoxFrame()); } michael@0: michael@0: enum Halignment { michael@0: hAlign_Left, michael@0: hAlign_Right, michael@0: hAlign_Center michael@0: }; michael@0: michael@0: enum Valignment { michael@0: vAlign_Top, michael@0: vAlign_Middle, michael@0: vAlign_BaseLine, michael@0: vAlign_Bottom michael@0: }; michael@0: michael@0: /** michael@0: * This calculates the minimum size required for a box based on its state michael@0: * @param[in] aBoxLayoutState The desired state to calculate for michael@0: * @return The minimum size michael@0: */ michael@0: virtual nsSize GetMinSize(nsBoxLayoutState& aBoxLayoutState) = 0; michael@0: michael@0: /** michael@0: * This calculates the preferred size of a box based on its state michael@0: * @param[in] aBoxLayoutState The desired state to calculate for michael@0: * @return The preferred size michael@0: */ michael@0: virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState) = 0; michael@0: michael@0: /** michael@0: * This calculates the maximum size for a box based on its state michael@0: * @param[in] aBoxLayoutState The desired state to calculate for michael@0: * @return The maximum size michael@0: */ michael@0: virtual nsSize GetMaxSize(nsBoxLayoutState& aBoxLayoutState) = 0; michael@0: michael@0: /** michael@0: * This returns the minimum size for the scroll area if this frame is michael@0: * being scrolled. Usually it's (0,0). michael@0: */ michael@0: virtual nsSize GetMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) = 0; michael@0: michael@0: // Implemented in nsBox, used in nsBoxFrame michael@0: uint32_t GetOrdinal(); michael@0: michael@0: virtual nscoord GetFlex(nsBoxLayoutState& aBoxLayoutState) = 0; michael@0: virtual nscoord GetBoxAscent(nsBoxLayoutState& aBoxLayoutState) = 0; michael@0: virtual bool IsCollapsed() = 0; michael@0: // This does not alter the overflow area. If the caller is changing michael@0: // the box size, the caller is responsible for updating the overflow michael@0: // area. It's enough to just call Layout or SyncLayout on the michael@0: // box. You can pass true to aRemoveOverflowArea as a michael@0: // convenience. michael@0: virtual void SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect, michael@0: bool aRemoveOverflowAreas = false) = 0; michael@0: NS_HIDDEN_(nsresult) Layout(nsBoxLayoutState& aBoxLayoutState); michael@0: nsIFrame* GetChildBox() const michael@0: { michael@0: // box layout ends at box-wrapped frames, so don't allow these frames michael@0: // to report child boxes. michael@0: return IsBoxFrame() ? GetFirstPrincipalChild() : nullptr; michael@0: } michael@0: nsIFrame* GetNextBox() const michael@0: { michael@0: return (mParent && mParent->IsBoxFrame()) ? mNextSibling : nullptr; michael@0: } michael@0: nsIFrame* GetParentBox() const michael@0: { michael@0: return (mParent && mParent->IsBoxFrame()) ? mParent : nullptr; michael@0: } michael@0: // Box methods. Note that these do NOT just get the CSS border, padding, michael@0: // etc. They also talk to nsITheme. michael@0: virtual nsresult GetBorderAndPadding(nsMargin& aBorderAndPadding); michael@0: virtual nsresult GetBorder(nsMargin& aBorder)=0; michael@0: virtual nsresult GetPadding(nsMargin& aBorderAndPadding)=0; michael@0: virtual nsresult GetMargin(nsMargin& aMargin)=0; michael@0: virtual void SetLayoutManager(nsBoxLayout* aLayout) { } michael@0: virtual nsBoxLayout* GetLayoutManager() { return nullptr; } michael@0: NS_HIDDEN_(nsresult) GetClientRect(nsRect& aContentRect); michael@0: michael@0: // For nsSprocketLayout michael@0: virtual Valignment GetVAlign() const = 0; michael@0: virtual Halignment GetHAlign() const = 0; michael@0: michael@0: bool IsHorizontal() const { return (mState & NS_STATE_IS_HORIZONTAL) != 0; } michael@0: bool IsNormalDirection() const { return (mState & NS_STATE_IS_DIRECTION_NORMAL) != 0; } michael@0: michael@0: NS_HIDDEN_(nsresult) Redraw(nsBoxLayoutState& aState); michael@0: virtual nsresult RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIFrame* aChild)=0; michael@0: // XXX take this out after we've branched michael@0: virtual bool GetMouseThrough() const { return false; } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: virtual nsresult SetDebug(nsBoxLayoutState& aState, bool aDebug)=0; michael@0: virtual nsresult GetDebug(bool& aDebug)=0; michael@0: michael@0: virtual nsresult DumpBox(FILE* out)=0; michael@0: #endif michael@0: michael@0: /** michael@0: * @return true if this text frame ends with a newline character. It michael@0: * should return false if this is not a text frame. michael@0: */ michael@0: virtual bool HasSignificantTerminalNewline() const; michael@0: michael@0: static bool AddCSSPrefSize(nsIFrame* aBox, nsSize& aSize, bool& aWidth, bool& aHeightSet); michael@0: static bool AddCSSMinSize(nsBoxLayoutState& aState, nsIFrame* aBox, michael@0: nsSize& aSize, bool& aWidth, bool& aHeightSet); michael@0: static bool AddCSSMaxSize(nsIFrame* aBox, nsSize& aSize, bool& aWidth, bool& aHeightSet); michael@0: static bool AddCSSFlex(nsBoxLayoutState& aState, nsIFrame* aBox, nscoord& aFlex); michael@0: michael@0: // END OF BOX LAYOUT METHODS michael@0: // The above methods have been migrated from nsIBox and are in the process of michael@0: // being refactored. DO NOT USE OUTSIDE OF XUL. michael@0: michael@0: struct CaretPosition { michael@0: CaretPosition(); michael@0: ~CaretPosition(); michael@0: michael@0: nsCOMPtr mResultContent; michael@0: int32_t mContentOffset; michael@0: }; michael@0: michael@0: /** michael@0: * gets the first or last possible caret position within the frame michael@0: * michael@0: * @param [in] aStart michael@0: * true for getting the first possible caret position michael@0: * false for getting the last possible caret position michael@0: * @return The caret position in a CaretPosition. michael@0: * the returned value is a 'best effort' in case errors michael@0: * are encountered rummaging through the frame. michael@0: */ michael@0: CaretPosition GetExtremeCaretPosition(bool aStart); michael@0: michael@0: /** michael@0: * Get a line iterator for this frame, if supported. michael@0: * michael@0: * @return nullptr if no line iterator is supported. michael@0: * @note dispose the line iterator using nsILineIterator::DisposeLineIterator michael@0: */ michael@0: virtual nsILineIterator* GetLineIterator() = 0; michael@0: michael@0: /** michael@0: * If this frame is a next-in-flow, and its prev-in-flow has something on its michael@0: * overflow list, pull those frames into the child list of this one. michael@0: */ michael@0: virtual void PullOverflowsFromPrevInFlow() {} michael@0: michael@0: /** michael@0: * Clear the list of child PresShells generated during the last paint michael@0: * so that we can begin generating a new one. michael@0: */ michael@0: void ClearPresShellsFromLastPaint() { michael@0: PaintedPresShellList()->Clear(); michael@0: } michael@0: michael@0: /** michael@0: * Flag a child PresShell as painted so that it will get its paint count michael@0: * incremented during empty transactions. michael@0: */ michael@0: void AddPaintedPresShell(nsIPresShell* shell) { michael@0: nsWeakPtr weakShell = do_GetWeakReference(shell); michael@0: PaintedPresShellList()->AppendElement(weakShell); michael@0: } michael@0: michael@0: /** michael@0: * Increment the paint count of all child PresShells that were painted during michael@0: * the last repaint. michael@0: */ michael@0: void UpdatePaintCountForPaintedPresShells() { michael@0: nsTArray * list = PaintedPresShellList(); michael@0: for (int i = 0, l = list->Length(); i < l; i++) { michael@0: nsCOMPtr shell = do_QueryReferent(list->ElementAt(i)); michael@0: michael@0: if (shell) { michael@0: shell->IncrementPaintCount(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Accessors for the absolute containing block. michael@0: */ michael@0: bool IsAbsoluteContainer() const { return !!(mState & NS_FRAME_HAS_ABSPOS_CHILDREN); } michael@0: bool HasAbsolutelyPositionedChildren() const; michael@0: nsAbsoluteContainingBlock* GetAbsoluteContainingBlock() const; michael@0: void MarkAsAbsoluteContainingBlock(); michael@0: void MarkAsNotAbsoluteContainingBlock(); michael@0: // Child frame types override this function to select their own child list name michael@0: virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const { return kAbsoluteList; } michael@0: michael@0: // Checks if we (or any of our descendents) have NS_FRAME_PAINTED_THEBES set, and michael@0: // clears this bit if so. michael@0: bool CheckAndClearPaintedState(); michael@0: michael@0: // CSS visibility just doesn't cut it because it doesn't inherit through michael@0: // documents. Also if this frame is in a hidden card of a deck then it isn't michael@0: // visible either and that isn't expressed using CSS visibility. Also if it michael@0: // is in a hidden view (there are a few cases left and they are hopefully michael@0: // going away soon). michael@0: // If the VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY flag is passed then we michael@0: // ignore the chrome/content boundary, otherwise we stop looking when we michael@0: // reach it. michael@0: enum { michael@0: VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY = 0x01 michael@0: }; michael@0: bool IsVisibleConsideringAncestors(uint32_t aFlags = 0) const; michael@0: michael@0: struct FrameWithDistance michael@0: { michael@0: nsIFrame* mFrame; michael@0: nscoord mXDistance; michael@0: nscoord mYDistance; michael@0: }; michael@0: michael@0: /** michael@0: * Finds a frame that is closer to a specified point than a current michael@0: * distance. Distance is measured as for text selection -- a closer x michael@0: * distance beats a closer y distance. michael@0: * michael@0: * Normally, this function will only check the distance between this michael@0: * frame's rectangle and the specified point. SVGTextFrame overrides michael@0: * this so that it can manage all of its descendant frames and take michael@0: * into account any SVG text layout. michael@0: * michael@0: * If aPoint is closer to this frame's rectangle than aCurrentBestFrame michael@0: * indicates, then aCurrentBestFrame is updated with the distance between michael@0: * aPoint and this frame's rectangle, and with a pointer to this frame. michael@0: * If aPoint is not closer, then aCurrentBestFrame is left unchanged. michael@0: * michael@0: * @param aPoint The point to check for its distance to this frame. michael@0: * @param aCurrentBestFrame Pointer to a struct that will be updated with michael@0: * a pointer to this frame and its distance to aPoint, if this frame michael@0: * is indeed closer than the current distance in aCurrentBestFrame. michael@0: */ michael@0: virtual void FindCloserFrameForSelection(nsPoint aPoint, michael@0: FrameWithDistance* aCurrentBestFrame); michael@0: michael@0: /** michael@0: * Is this a flex item? (i.e. a non-abs-pos child of a flex container) michael@0: */ michael@0: inline bool IsFlexItem() const; michael@0: michael@0: inline bool IsBlockInside() const; michael@0: inline bool IsBlockOutside() const; michael@0: inline bool IsInlineOutside() const; michael@0: inline uint8_t GetDisplay() const; michael@0: inline bool IsFloating() const; michael@0: inline bool IsPositioned() const; michael@0: inline bool IsRelativelyPositioned() const; michael@0: inline bool IsAbsolutelyPositioned() const; michael@0: michael@0: /** michael@0: * Returns the vertical-align value to be used for layout, if it is one michael@0: * of the enumerated values. If this is an SVG text frame, it returns a value michael@0: * that corresponds to the value of dominant-baseline. If the michael@0: * vertical-align property has length or percentage value, this returns michael@0: * eInvalidVerticalAlign. michael@0: */ michael@0: uint8_t VerticalAlignEnum() const; michael@0: enum { eInvalidVerticalAlign = 0xFF }; michael@0: michael@0: bool IsSVGText() const { return mState & NS_FRAME_IS_SVG_TEXT; } michael@0: michael@0: void CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, nsDisplayList* aList); michael@0: michael@0: /** michael@0: * Adds the NS_FRAME_IN_POPUP state bit to aFrame, and michael@0: * all descendant frames (including cross-doc ones). michael@0: */ michael@0: static void AddInPopupStateBitToDescendants(nsIFrame* aFrame); michael@0: /** michael@0: * Removes the NS_FRAME_IN_POPUP state bit from aFrame and michael@0: * all descendant frames (including cross-doc ones), unless michael@0: * the frame is a popup itself. michael@0: */ michael@0: static void RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Sorts the given nsFrameList, so that for every two adjacent frames in the michael@0: * list, the former is less than or equal to the latter, according to the michael@0: * templated IsLessThanOrEqual method. michael@0: * michael@0: * Note: this method uses a stable merge-sort algorithm. michael@0: */ michael@0: template michael@0: static void SortFrameList(nsFrameList& aFrameList); michael@0: michael@0: /** michael@0: * Returns true if the given frame list is already sorted, according to the michael@0: * templated IsLessThanOrEqual function. michael@0: */ michael@0: template michael@0: static bool IsFrameListSorted(nsFrameList& aFrameList); michael@0: michael@0: /** michael@0: * Return true if aFrame is in an {ib} split and is NOT one of the michael@0: * continuations of the first inline in it. michael@0: */ michael@0: bool FrameIsNonFirstInIBSplit() const { michael@0: return (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) && michael@0: FirstContinuation()->Properties().Get(nsIFrame::IBSplitPrevSibling()); michael@0: } michael@0: michael@0: /** michael@0: * Return true if aFrame is in an {ib} split and is NOT one of the michael@0: * continuations of the last inline in it. michael@0: */ michael@0: bool FrameIsNonLastInIBSplit() const { michael@0: return (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) && michael@0: FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling()); michael@0: } michael@0: michael@0: /** michael@0: * Return whether this is a frame whose width is used when computing michael@0: * the font size inflation of its descendants. michael@0: */ michael@0: bool IsContainerForFontSizeInflation() const { michael@0: return GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER; michael@0: } michael@0: michael@0: /** michael@0: * Returns the content node within the anonymous content that this frame michael@0: * generated and which corresponds to the specified pseudo-element type, michael@0: * or nullptr if there is no such anonymous content. michael@0: */ michael@0: virtual mozilla::dom::Element* GetPseudoElement(nsCSSPseudoElements::Type aType); michael@0: michael@0: protected: michael@0: // Members michael@0: nsRect mRect; michael@0: nsIContent* mContent; michael@0: nsStyleContext* mStyleContext; michael@0: nsIFrame* mParent; michael@0: private: michael@0: nsIFrame* mNextSibling; // doubly-linked list of frames michael@0: nsIFrame* mPrevSibling; // Do not touch outside SetNextSibling! michael@0: michael@0: void MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect); michael@0: michael@0: static void DestroyPaintedPresShellList(void* propertyValue) { michael@0: nsTArray* list = static_cast*>(propertyValue); michael@0: list->Clear(); michael@0: delete list; michael@0: } michael@0: michael@0: // Stores weak references to all the PresShells that were painted during michael@0: // the last paint event so that we can increment their paint count during michael@0: // empty transactions michael@0: NS_DECLARE_FRAME_PROPERTY(PaintedPresShellsProperty, DestroyPaintedPresShellList) michael@0: michael@0: nsTArray* PaintedPresShellList() { michael@0: nsTArray* list = static_cast*>( michael@0: Properties().Get(PaintedPresShellsProperty()) michael@0: ); michael@0: michael@0: if (!list) { michael@0: list = new nsTArray(); michael@0: Properties().Set(PaintedPresShellsProperty(), list); michael@0: } michael@0: michael@0: return list; michael@0: } michael@0: michael@0: protected: michael@0: nsFrameState mState; michael@0: michael@0: // When there is an overflow area only slightly larger than mRect, michael@0: // we store a set of four 1-byte deltas from the edges of mRect michael@0: // rather than allocating a whole separate rectangle property. michael@0: // Note that these are unsigned values, all measured "outwards" michael@0: // from the edges of mRect, so /mLeft/ and /mTop/ are reversed from michael@0: // our normal coordinate system. michael@0: // If mOverflow.mType == NS_FRAME_OVERFLOW_LARGE, then the michael@0: // delta values are not meaningful and the overflow area is stored michael@0: // as a separate rect property. michael@0: struct VisualDeltas { michael@0: uint8_t mLeft; michael@0: uint8_t mTop; michael@0: uint8_t mRight; michael@0: uint8_t mBottom; michael@0: bool operator==(const VisualDeltas& aOther) const michael@0: { michael@0: return mLeft == aOther.mLeft && mTop == aOther.mTop && michael@0: mRight == aOther.mRight && mBottom == aOther.mBottom; michael@0: } michael@0: bool operator!=(const VisualDeltas& aOther) const michael@0: { michael@0: return !(*this == aOther); michael@0: } michael@0: }; michael@0: union { michael@0: uint32_t mType; michael@0: VisualDeltas mVisualDeltas; michael@0: } mOverflow; michael@0: michael@0: // Helpers michael@0: /** michael@0: * Can we stop inside this frame when we're skipping non-rendered whitespace? michael@0: * @param aForward [in] Are we moving forward (or backward) in content order. michael@0: * @param aOffset [in/out] At what offset into the frame to start looking. michael@0: * on output - what offset was reached (whether or not we found a place to stop). michael@0: * @return STOP: An appropriate offset was found within this frame, michael@0: * and is given by aOffset. michael@0: * CONTINUE: Not found within this frame, need to try the next frame. michael@0: * see enum FrameSearchResult for more details. michael@0: */ michael@0: virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) = 0; michael@0: michael@0: /** michael@0: * Search the frame for the next character michael@0: * @param aForward [in] Are we moving forward (or backward) in content order. michael@0: * @param aOffset [in/out] At what offset into the frame to start looking. michael@0: * on output - what offset was reached (whether or not we found a place to stop). michael@0: * @param aRespectClusters [in] Whether to restrict result to valid cursor locations michael@0: * (between grapheme clusters) - default TRUE maintains "normal" behavior, michael@0: * FALSE is used for selection by "code unit" (instead of "character") michael@0: * @return STOP: An appropriate offset was found within this frame, michael@0: * and is given by aOffset. michael@0: * CONTINUE: Not found within this frame, need to try the next frame. michael@0: * see enum FrameSearchResult for more details. michael@0: */ michael@0: virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset, michael@0: bool aRespectClusters = true) = 0; michael@0: michael@0: /** michael@0: * Search the frame for the next word boundary michael@0: * @param aForward [in] Are we moving forward (or backward) in content order. michael@0: * @param aWordSelectEatSpace [in] true: look for non-whitespace following michael@0: * whitespace (in the direction of movement). michael@0: * false: look for whitespace following non-whitespace (in the michael@0: * direction of movement). michael@0: * @param aIsKeyboardSelect [in] Was the action initiated by a keyboard operation? michael@0: * If true, punctuation immediately following a word is considered part michael@0: * of that word. Otherwise, a sequence of punctuation is always considered michael@0: * as a word on its own. michael@0: * @param aOffset [in/out] At what offset into the frame to start looking. michael@0: * on output - what offset was reached (whether or not we found a place to stop). michael@0: * @param aState [in/out] the state that is carried from frame to frame michael@0: * @return true: An appropriate offset was found within this frame, michael@0: * and is given by aOffset. michael@0: * false: Not found within this frame, need to try the next frame. michael@0: */ michael@0: struct PeekWordState { michael@0: // true when we're still at the start of the search, i.e., we can't return michael@0: // this point as a valid offset! michael@0: bool mAtStart; michael@0: // true when we've encountered at least one character of the pre-boundary type michael@0: // (whitespace if aWordSelectEatSpace is true, non-whitespace otherwise) michael@0: bool mSawBeforeType; michael@0: // true when the last character encountered was punctuation michael@0: bool mLastCharWasPunctuation; michael@0: // true when the last character encountered was whitespace michael@0: bool mLastCharWasWhitespace; michael@0: // true when we've seen non-punctuation since the last whitespace michael@0: bool mSeenNonPunctuationSinceWhitespace; michael@0: // text that's *before* the current frame when aForward is true, *after* michael@0: // the current frame when aForward is false. Only includes the text michael@0: // on the current line. michael@0: nsAutoString mContext; michael@0: michael@0: PeekWordState() : mAtStart(true), mSawBeforeType(false), michael@0: mLastCharWasPunctuation(false), mLastCharWasWhitespace(false), michael@0: mSeenNonPunctuationSinceWhitespace(false) {} michael@0: void SetSawBeforeType() { mSawBeforeType = true; } michael@0: void Update(bool aAfterPunctuation, bool aAfterWhitespace) { michael@0: mLastCharWasPunctuation = aAfterPunctuation; michael@0: mLastCharWasWhitespace = aAfterWhitespace; michael@0: if (aAfterWhitespace) { michael@0: mSeenNonPunctuationSinceWhitespace = false; michael@0: } else if (!aAfterPunctuation) { michael@0: mSeenNonPunctuationSinceWhitespace = true; michael@0: } michael@0: mAtStart = false; michael@0: } michael@0: }; michael@0: virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, michael@0: int32_t* aOffset, PeekWordState* aState) = 0; michael@0: michael@0: /** michael@0: * Search for the first paragraph boundary before or after the given position michael@0: * @param aPos See description in nsFrameSelection.h. The following fields are michael@0: * used by this method: michael@0: * Input: mDirection michael@0: * Output: mResultContent, mContentOffset michael@0: */ michael@0: nsresult PeekOffsetParagraph(nsPeekOffsetStruct *aPos); michael@0: michael@0: private: michael@0: nsOverflowAreas* GetOverflowAreasProperty(); michael@0: nsRect GetVisualOverflowFromDeltas() const { michael@0: NS_ABORT_IF_FALSE(mOverflow.mType != NS_FRAME_OVERFLOW_LARGE, michael@0: "should not be called when overflow is in a property"); michael@0: // Calculate the rect using deltas from the frame's border rect. michael@0: // Note that the mOverflow.mDeltas fields are unsigned, but we will often michael@0: // need to return negative values for the left and top, so take care michael@0: // to cast away the unsigned-ness. michael@0: return nsRect(-(int32_t)mOverflow.mVisualDeltas.mLeft, michael@0: -(int32_t)mOverflow.mVisualDeltas.mTop, michael@0: mRect.width + mOverflow.mVisualDeltas.mRight + michael@0: mOverflow.mVisualDeltas.mLeft, michael@0: mRect.height + mOverflow.mVisualDeltas.mBottom + michael@0: mOverflow.mVisualDeltas.mTop); michael@0: } michael@0: /** michael@0: * Returns true if any overflow changed. michael@0: */ michael@0: bool SetOverflowAreas(const nsOverflowAreas& aOverflowAreas); michael@0: nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const; michael@0: michael@0: // Helper-functions for SortFrameList(): michael@0: template michael@0: static nsIFrame* SortedMerge(nsIFrame *aLeft, nsIFrame *aRight); michael@0: michael@0: template michael@0: static nsIFrame* MergeSort(nsIFrame *aSource); michael@0: michael@0: bool HasOpacityInternal(float aThreshold) const; michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: public: michael@0: static void IndentBy(FILE* out, int32_t aIndent) { michael@0: while (--aIndent >= 0) fputs(" ", out); michael@0: } michael@0: void ListTag(FILE* out) const { michael@0: ListTag(out, this); michael@0: } michael@0: static void ListTag(FILE* out, const nsIFrame* aFrame) { michael@0: nsAutoCString t; michael@0: ListTag(t, aFrame); michael@0: fputs(t.get(), out); michael@0: } michael@0: void ListTag(nsACString& aTo) const; michael@0: static void ListTag(nsACString& aTo, const nsIFrame* aFrame); michael@0: void ListGeneric(nsACString& aTo, const char* aPrefix = "", uint32_t aFlags = 0) const; michael@0: enum { michael@0: TRAVERSE_SUBDOCUMENT_FRAMES = 0x01 michael@0: }; michael@0: virtual void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const; michael@0: /** michael@0: * lists the frames beginning from the root frame michael@0: * - calls root frame's List(...) michael@0: */ michael@0: static void RootFrameList(nsPresContext* aPresContext, michael@0: FILE* out = stderr, const char* aPrefix = ""); michael@0: virtual void DumpFrameTree(); michael@0: void DumpFrameTreeLimited(); michael@0: michael@0: virtual nsresult GetFrameName(nsAString& aResult) const = 0; michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: public: michael@0: virtual nsFrameState GetDebugStateBits() const = 0; michael@0: virtual nsresult DumpRegressionData(nsPresContext* aPresContext, michael@0: FILE* out, int32_t aIndent) = 0; michael@0: #endif michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * nsWeakFrame can be used to keep a reference to a nsIFrame in a safe way. michael@0: * Whenever an nsIFrame object is deleted, the nsWeakFrames pointing michael@0: * to it will be cleared. michael@0: * michael@0: * Create nsWeakFrame object when it is sure that nsIFrame object michael@0: * is alive and after some operations which may destroy the nsIFrame michael@0: * (for example any DOM modifications) use IsAlive() or GetFrame() methods to michael@0: * check whether it is safe to continue to use the nsIFrame object. michael@0: * michael@0: * @note The usage of this class should be kept to a minimum. michael@0: */ michael@0: michael@0: class nsWeakFrame { michael@0: public: michael@0: nsWeakFrame() : mPrev(nullptr), mFrame(nullptr) { } michael@0: michael@0: nsWeakFrame(const nsWeakFrame& aOther) : mPrev(nullptr), mFrame(nullptr) michael@0: { michael@0: Init(aOther.GetFrame()); michael@0: } michael@0: michael@0: nsWeakFrame(nsIFrame* aFrame) : mPrev(nullptr), mFrame(nullptr) michael@0: { michael@0: Init(aFrame); michael@0: } michael@0: michael@0: nsWeakFrame& operator=(nsWeakFrame& aOther) { michael@0: Init(aOther.GetFrame()); michael@0: return *this; michael@0: } michael@0: michael@0: nsWeakFrame& operator=(nsIFrame* aFrame) { michael@0: Init(aFrame); michael@0: return *this; michael@0: } michael@0: michael@0: nsIFrame* operator->() michael@0: { michael@0: return mFrame; michael@0: } michael@0: michael@0: operator nsIFrame*() michael@0: { michael@0: return mFrame; michael@0: } michael@0: michael@0: void Clear(nsIPresShell* aShell) { michael@0: if (aShell) { michael@0: aShell->RemoveWeakFrame(this); michael@0: } michael@0: mFrame = nullptr; michael@0: mPrev = nullptr; michael@0: } michael@0: michael@0: bool IsAlive() { return !!mFrame; } michael@0: michael@0: nsIFrame* GetFrame() const { return mFrame; } michael@0: michael@0: nsWeakFrame* GetPreviousWeakFrame() { return mPrev; } michael@0: michael@0: void SetPreviousWeakFrame(nsWeakFrame* aPrev) { mPrev = aPrev; } michael@0: michael@0: ~nsWeakFrame() michael@0: { michael@0: Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr); michael@0: } michael@0: private: michael@0: void Init(nsIFrame* aFrame); michael@0: michael@0: nsWeakFrame* mPrev; michael@0: nsIFrame* mFrame; michael@0: }; michael@0: michael@0: inline bool michael@0: nsFrameList::ContinueRemoveFrame(nsIFrame* aFrame) michael@0: { michael@0: MOZ_ASSERT(!aFrame->GetPrevSibling() || !aFrame->GetNextSibling(), michael@0: "Forgot to call StartRemoveFrame?"); michael@0: if (aFrame == mLastChild) { michael@0: MOZ_ASSERT(!aFrame->GetNextSibling(), "broken frame list"); michael@0: nsIFrame* prevSibling = aFrame->GetPrevSibling(); michael@0: if (!prevSibling) { michael@0: MOZ_ASSERT(aFrame == mFirstChild, "broken frame list"); michael@0: mFirstChild = mLastChild = nullptr; michael@0: return true; michael@0: } michael@0: MOZ_ASSERT(prevSibling->GetNextSibling() == aFrame, "Broken frame linkage"); michael@0: prevSibling->SetNextSibling(nullptr); michael@0: mLastChild = prevSibling; michael@0: return true; michael@0: } michael@0: if (aFrame == mFirstChild) { michael@0: MOZ_ASSERT(!aFrame->GetPrevSibling(), "broken frame list"); michael@0: mFirstChild = aFrame->GetNextSibling(); michael@0: aFrame->SetNextSibling(nullptr); michael@0: MOZ_ASSERT(mFirstChild, "broken frame list"); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: inline bool michael@0: nsFrameList::StartRemoveFrame(nsIFrame* aFrame) michael@0: { michael@0: if (aFrame->GetPrevSibling() && aFrame->GetNextSibling()) { michael@0: UnhookFrameFromSiblings(aFrame); michael@0: return true; michael@0: } michael@0: return ContinueRemoveFrame(aFrame); michael@0: } michael@0: michael@0: inline void michael@0: nsFrameList::Enumerator::Next() michael@0: { michael@0: NS_ASSERTION(!AtEnd(), "Should have checked AtEnd()!"); michael@0: mFrame = mFrame->GetNextSibling(); michael@0: } michael@0: michael@0: inline michael@0: nsFrameList::FrameLinkEnumerator:: michael@0: FrameLinkEnumerator(const nsFrameList& aList, nsIFrame* aPrevFrame) michael@0: : Enumerator(aList) michael@0: { michael@0: mPrev = aPrevFrame; michael@0: mFrame = aPrevFrame ? aPrevFrame->GetNextSibling() : aList.FirstChild(); michael@0: } michael@0: michael@0: inline void michael@0: nsFrameList::FrameLinkEnumerator::Next() michael@0: { michael@0: mPrev = mFrame; michael@0: Enumerator::Next(); michael@0: } michael@0: michael@0: // Helper-functions for nsIFrame::SortFrameList() michael@0: // --------------------------------------------------- michael@0: michael@0: template michael@0: /* static */ nsIFrame* michael@0: nsIFrame::SortedMerge(nsIFrame *aLeft, nsIFrame *aRight) michael@0: { michael@0: NS_PRECONDITION(aLeft && aRight, "SortedMerge must have non-empty lists"); michael@0: michael@0: nsIFrame *result; michael@0: // Unroll first iteration to avoid null-check 'result' inside the loop. michael@0: if (IsLessThanOrEqual(aLeft, aRight)) { michael@0: result = aLeft; michael@0: aLeft = aLeft->GetNextSibling(); michael@0: if (!aLeft) { michael@0: result->SetNextSibling(aRight); michael@0: return result; michael@0: } michael@0: } michael@0: else { michael@0: result = aRight; michael@0: aRight = aRight->GetNextSibling(); michael@0: if (!aRight) { michael@0: result->SetNextSibling(aLeft); michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: nsIFrame *last = result; michael@0: for (;;) { michael@0: if (IsLessThanOrEqual(aLeft, aRight)) { michael@0: last->SetNextSibling(aLeft); michael@0: last = aLeft; michael@0: aLeft = aLeft->GetNextSibling(); michael@0: if (!aLeft) { michael@0: last->SetNextSibling(aRight); michael@0: return result; michael@0: } michael@0: } michael@0: else { michael@0: last->SetNextSibling(aRight); michael@0: last = aRight; michael@0: aRight = aRight->GetNextSibling(); michael@0: if (!aRight) { michael@0: last->SetNextSibling(aLeft); michael@0: return result; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: template michael@0: /* static */ nsIFrame* michael@0: nsIFrame::MergeSort(nsIFrame *aSource) michael@0: { michael@0: NS_PRECONDITION(aSource, "MergeSort null arg"); michael@0: michael@0: nsIFrame *sorted[32] = { nullptr }; michael@0: nsIFrame **fill = &sorted[0]; michael@0: nsIFrame **left; michael@0: nsIFrame *rest = aSource; michael@0: michael@0: do { michael@0: nsIFrame *current = rest; michael@0: rest = rest->GetNextSibling(); michael@0: current->SetNextSibling(nullptr); michael@0: michael@0: // Merge it with sorted[0] if present; then merge the result with sorted[1] etc. michael@0: // sorted[0] is a list of length 1 (or nullptr). michael@0: // sorted[1] is a list of length 2 (or nullptr). michael@0: // sorted[2] is a list of length 4 (or nullptr). etc. michael@0: for (left = &sorted[0]; left != fill && *left; ++left) { michael@0: current = SortedMerge(*left, current); michael@0: *left = nullptr; michael@0: } michael@0: michael@0: // Fill the empty slot that we couldn't merge with the last result. michael@0: *left = current; michael@0: michael@0: if (left == fill) michael@0: ++fill; michael@0: } while (rest); michael@0: michael@0: // Collect and merge the results. michael@0: nsIFrame *result = nullptr; michael@0: for (left = &sorted[0]; left != fill; ++left) { michael@0: if (*left) { michael@0: result = result ? SortedMerge(*left, result) : *left; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: template michael@0: /* static */ void michael@0: nsIFrame::SortFrameList(nsFrameList& aFrameList) michael@0: { michael@0: nsIFrame* head = MergeSort(aFrameList.FirstChild()); michael@0: aFrameList = nsFrameList(head, nsLayoutUtils::GetLastSibling(head)); michael@0: MOZ_ASSERT(IsFrameListSorted(aFrameList), michael@0: "After we sort a frame list, it should be in sorted order..."); michael@0: } michael@0: michael@0: template michael@0: /* static */ bool michael@0: nsIFrame::IsFrameListSorted(nsFrameList& aFrameList) michael@0: { michael@0: if (aFrameList.IsEmpty()) { michael@0: // empty lists are trivially sorted. michael@0: return true; michael@0: } michael@0: michael@0: // We'll walk through the list with two iterators, one trailing behind the michael@0: // other. The list is sorted IFF trailingIter <= iter, across the whole list. michael@0: nsFrameList::Enumerator trailingIter(aFrameList); michael@0: nsFrameList::Enumerator iter(aFrameList); michael@0: iter.Next(); // Skip |iter| past first frame. (List is nonempty, so we can.) michael@0: michael@0: // Now, advance the iterators in parallel, comparing each adjacent pair. michael@0: while (!iter.AtEnd()) { michael@0: MOZ_ASSERT(!trailingIter.AtEnd(), "trailing iter shouldn't finish first"); michael@0: if (!IsLessThanOrEqual(trailingIter.get(), iter.get())) { michael@0: return false; michael@0: } michael@0: trailingIter.Next(); michael@0: iter.Next(); michael@0: } michael@0: michael@0: // We made it to the end without returning early, so the list is sorted. michael@0: return true; michael@0: } michael@0: michael@0: #endif /* nsIFrame_h___ */