michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * construction of a frame tree that is nearly isomorphic to the content michael@0: * tree and updating of that tree in response to dynamic changes michael@0: */ michael@0: michael@0: #ifndef nsCSSFrameConstructor_h___ michael@0: #define nsCSSFrameConstructor_h___ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsILayoutHistoryState.h" michael@0: #include "nsQuoteList.h" michael@0: #include "nsCounterManager.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "nsIAnonymousContentCreator.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: struct nsFrameItems; michael@0: struct nsAbsoluteItems; michael@0: class nsStyleContext; michael@0: struct nsStyleDisplay; michael@0: class nsIDOMHTMLSelectElement; michael@0: struct nsGenConInitializer; michael@0: michael@0: class nsICSSAnonBoxPseudo; michael@0: class nsPageContentFrame; michael@0: struct PendingBinding; michael@0: class nsGenericDOMDataNode; michael@0: michael@0: class nsFrameConstructorState; michael@0: class nsFrameConstructorSaveState; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class RestyleManager; michael@0: michael@0: namespace dom { michael@0: michael@0: class FlattenedChildIterator; michael@0: michael@0: } michael@0: } michael@0: michael@0: class nsCSSFrameConstructor : public nsFrameManager michael@0: { michael@0: public: michael@0: typedef mozilla::dom::Element Element; michael@0: michael@0: friend class mozilla::RestyleManager; michael@0: michael@0: nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell, michael@0: nsStyleSet* aStyleSet); michael@0: ~nsCSSFrameConstructor(void) { michael@0: NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?"); michael@0: } michael@0: michael@0: // get the alternate text for a content node michael@0: static void GetAlternateTextFor(nsIContent* aContent, michael@0: nsIAtom* aTag, // content object's tag michael@0: nsXPIDLString& aAltText); michael@0: michael@0: private: michael@0: nsCSSFrameConstructor(const nsCSSFrameConstructor& aCopy) MOZ_DELETE; michael@0: nsCSSFrameConstructor& operator=(const nsCSSFrameConstructor& aCopy) MOZ_DELETE; michael@0: michael@0: public: michael@0: mozilla::RestyleManager* RestyleManager() const michael@0: { return mPresShell->GetPresContext()->RestyleManager(); } michael@0: michael@0: nsIFrame* ConstructRootFrame(); michael@0: michael@0: nsresult ReconstructDocElementHierarchy(); michael@0: michael@0: // Create frames for content nodes that are marked as needing frames. This michael@0: // should be called before ProcessPendingRestyles. michael@0: // Note: It's the caller's responsibility to make sure to wrap a michael@0: // CreateNeededFrames call in a view update batch and a script blocker. michael@0: void CreateNeededFrames(); michael@0: michael@0: private: michael@0: void CreateNeededFrames(nsIContent* aContent); michael@0: michael@0: enum Operation { michael@0: CONTENTAPPEND, michael@0: CONTENTINSERT michael@0: }; michael@0: michael@0: // aChild is the child being inserted for inserts, and the first michael@0: // child being appended for appends. michael@0: bool MaybeConstructLazily(Operation aOperation, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild); michael@0: michael@0: // Issues a single ContentInserted for each child of aContainer in the range michael@0: // [aStartChild, aEndChild). michael@0: void IssueSingleInsertNofications(nsIContent* aContainer, michael@0: nsIContent* aStartChild, michael@0: nsIContent* aEndChild, michael@0: bool aAllowLazyConstruction); michael@0: michael@0: // Checks if the children of aContainer in the range [aStartChild, aEndChild) michael@0: // can be inserted/appended to one insertion point together. If so, returns michael@0: // that insertion point. If not, returns null and issues single michael@0: // ContentInserted calls for each child. aEndChild = nullptr indicates that we michael@0: // are dealing with an append. michael@0: nsIFrame* GetRangeInsertionPoint(nsIContent* aContainer, michael@0: nsIContent* aStartChild, michael@0: nsIContent* aEndChild, michael@0: bool aAllowLazyConstruction); michael@0: michael@0: // Returns true if parent was recreated due to frameset child, false otherwise. michael@0: bool MaybeRecreateForFrameset(nsIFrame* aParentFrame, michael@0: nsIContent* aStartChild, michael@0: nsIContent* aEndChild); michael@0: michael@0: public: michael@0: /** michael@0: * Lazy frame construction is controlled by the aAllowLazyConstruction bool michael@0: * parameter of nsCSSFrameConstructor::ContentAppended/Inserted. It is true michael@0: * for all inserts/appends as passed from the presshell, except for the michael@0: * insert of the root element, which is always non-lazy. Even if the michael@0: * aAllowLazyConstruction passed to ContentAppended/Inserted is true we still michael@0: * may not be able to construct lazily, so we call MaybeConstructLazily. michael@0: * MaybeConstructLazily does not allow lazy construction if any of the michael@0: * following are true: michael@0: * -we are in chrome michael@0: * -the container is in a native anonymous subtree michael@0: * -the container is XUL michael@0: * -is any of the appended/inserted nodes are XUL or editable michael@0: * -(for inserts) the child is anonymous. In the append case this function michael@0: * must not be called with anonymous children. michael@0: * The XUL and chrome checks are because XBL bindings only get applied at michael@0: * frame construction time and some things depend on the bindings getting michael@0: * attached synchronously. The editable checks are because the editor seems michael@0: * to expect frames to be constructed synchronously. michael@0: * michael@0: * If MaybeConstructLazily returns false we construct as usual, but if it michael@0: * returns true then it adds NODE_NEEDS_FRAME bits to the newly michael@0: * inserted/appended nodes and adds NODE_DESCENDANTS_NEED_FRAMES bits to the michael@0: * container and up along the parent chain until it hits the root or another michael@0: * node with that bit set. Then it posts a restyle event to ensure that a michael@0: * flush happens to construct those frames. michael@0: * michael@0: * When the flush happens the presshell calls michael@0: * nsCSSFrameConstructor::CreateNeededFrames. CreateNeededFrames follows any michael@0: * nodes with NODE_DESCENDANTS_NEED_FRAMES set down the content tree looking michael@0: * for nodes with NODE_NEEDS_FRAME set. It calls ContentAppended for any runs michael@0: * of nodes with NODE_NEEDS_FRAME set that are at the end of their childlist, michael@0: * and ContentRangeInserted for any other runs that aren't. michael@0: * michael@0: * If a node is removed from the document then we don't bother unsetting any michael@0: * of the lazy bits that might be set on it, its descendants, or any of its michael@0: * ancestor nodes because that is a slow operation, the work might be wasted michael@0: * if another node gets inserted in its place, and we can clear the bits michael@0: * quicker by processing the content tree from top down the next time we call michael@0: * CreateNeededFrames. (We do clear the bits when BindToTree is called on any michael@0: * nsIContent; so any nodes added to the document will not have any lazy bits michael@0: * set.) michael@0: */ michael@0: michael@0: // If aAllowLazyConstruction is true then frame construction of the new michael@0: // children can be done lazily. michael@0: nsresult ContentAppended(nsIContent* aContainer, michael@0: nsIContent* aFirstNewContent, michael@0: bool aAllowLazyConstruction); michael@0: michael@0: // If aAllowLazyConstruction is true then frame construction of the new child michael@0: // can be done lazily. michael@0: nsresult ContentInserted(nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: nsILayoutHistoryState* aFrameState, michael@0: bool aAllowLazyConstruction); michael@0: michael@0: // Like ContentInserted but handles inserting the children of aContainer in michael@0: // the range [aStartChild, aEndChild). aStartChild must be non-null. michael@0: // aEndChild may be null to indicate the range includes all kids after michael@0: // aStartChild. If aAllowLazyConstruction is true then frame construction of michael@0: // the new children can be done lazily. It is only allowed to be true when michael@0: // inserting a single node. michael@0: nsresult ContentRangeInserted(nsIContent* aContainer, michael@0: nsIContent* aStartChild, michael@0: nsIContent* aEndChild, michael@0: nsILayoutHistoryState* aFrameState, michael@0: bool aAllowLazyConstruction); michael@0: michael@0: enum RemoveFlags { REMOVE_CONTENT, REMOVE_FOR_RECONSTRUCTION }; michael@0: nsresult ContentRemoved(nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: nsIContent* aOldNextSibling, michael@0: RemoveFlags aFlags, michael@0: bool* aDidReconstruct); michael@0: michael@0: nsresult CharacterDataChanged(nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo); michael@0: michael@0: // If aContent is a text node that has been optimized away due to being michael@0: // whitespace next to a block boundary (or for some other reason), stop michael@0: // doing that and create a frame for it if it should have one. This recreates michael@0: // frames so be careful (although this should not change actual layout). michael@0: // Returns the frame for aContent if there is one. michael@0: nsIFrame* EnsureFrameForTextNode(nsGenericDOMDataNode* aContent); michael@0: michael@0: // generate the child frames and process bindings michael@0: nsresult GenerateChildFrames(nsIFrame* aFrame); michael@0: michael@0: // Should be called when a frame is going to be destroyed and michael@0: // WillDestroyFrameTree hasn't been called yet. michael@0: void NotifyDestroyingFrame(nsIFrame* aFrame); michael@0: michael@0: void BeginUpdate(); michael@0: void EndUpdate(); michael@0: void RecalcQuotesAndCounters(); michael@0: michael@0: // Gets called when the presshell is destroying itself and also michael@0: // when we tear down our frame tree to reconstruct it michael@0: void WillDestroyFrameTree(); michael@0: michael@0: // Request to create a continuing frame. This method never returns null. michael@0: nsIFrame* CreateContinuingFrame(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aParentFrame, michael@0: bool aIsFluid = true); michael@0: michael@0: // Copy over fixed frames from aParentFrame's prev-in-flow michael@0: nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame); michael@0: michael@0: // Get the XBL insertion point for a child michael@0: nsIFrame* GetInsertionPoint(nsIContent* aContainer, michael@0: nsIContent* aChildContent, michael@0: bool* aMultiple = nullptr); michael@0: michael@0: nsresult CreateListBoxContent(nsPresContext* aPresContext, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aPrevFrame, michael@0: nsIContent* aChild, michael@0: nsIFrame** aResult, michael@0: bool aIsAppend, michael@0: bool aIsScrollbar, michael@0: nsILayoutHistoryState* aFrameState); michael@0: michael@0: // GetInitialContainingBlock() is deprecated in favor of GetRootElementFrame(); michael@0: // nsIFrame* GetInitialContainingBlock() { return mRootElementFrame; } michael@0: // This returns the outermost frame for the root element michael@0: nsIFrame* GetRootElementFrame() { return mRootElementFrame; } michael@0: // This returns the frame for the root element that does not michael@0: // have a psuedo-element style michael@0: nsIFrame* GetRootElementStyleFrame() { return mRootElementStyleFrame; } michael@0: nsIFrame* GetPageSequenceFrame() { return mPageSequenceFrame; } michael@0: michael@0: // Get the frame that is the parent of the root element. michael@0: nsIFrame* GetDocElementContainingBlock() michael@0: { return mDocElementContainingBlock; } michael@0: michael@0: private: michael@0: struct FrameConstructionItem; michael@0: class FrameConstructionItemList; michael@0: michael@0: nsIFrame* ConstructPageFrame(nsIPresShell* aPresShell, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aPrevPageFrame, michael@0: nsIFrame*& aCanvasFrame); michael@0: michael@0: void InitAndRestoreFrame (const nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aNewFrame, michael@0: bool aAllowCounters = true); michael@0: michael@0: // aState can be null if not available; it's used as an optimization. michael@0: // XXXbz IsValidSibling is the only caller that doesn't pass a state here! michael@0: already_AddRefed michael@0: ResolveStyleContext(nsIFrame* aParentFrame, michael@0: nsIContent* aContent, michael@0: nsFrameConstructorState* aState); michael@0: already_AddRefed michael@0: ResolveStyleContext(nsStyleContext* aParentStyleContext, michael@0: nsIContent* aContent, michael@0: nsFrameConstructorState* aState); michael@0: michael@0: // Add the frame construction items for the given aContent and aParentFrame michael@0: // to the list. This might add more than one item in some rare cases. michael@0: // If aSuppressWhiteSpaceOptimizations is true, optimizations that michael@0: // may suppress the construction of white-space-only text frames michael@0: // must be skipped for these items and items around them. michael@0: void AddFrameConstructionItems(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: bool aSuppressWhiteSpaceOptimizations, michael@0: nsIFrame* aParentFrame, michael@0: FrameConstructionItemList& aItems); michael@0: michael@0: // Construct the frames for the document element. This can return null if the michael@0: // document element is display:none, or if the document element has a michael@0: // not-yet-loaded XBL binding, or if it's an SVG element that's not . michael@0: nsIFrame* ConstructDocElementFrame(Element* aDocElement, michael@0: nsILayoutHistoryState* aFrameState); michael@0: michael@0: // Set up our mDocElementContainingBlock correctly for the given root michael@0: // content. michael@0: void SetUpDocElementContainingBlock(nsIContent* aDocElement); michael@0: michael@0: /** michael@0: * CreateAttributeContent creates a single content/frame combination for an michael@0: * |attr(foo)| generated content. michael@0: * michael@0: * @param aParentContent the parent content for the generated content michael@0: * @param aParentFrame the parent frame for the generated frame michael@0: * @param aAttrNamespace the namespace of the attribute in question michael@0: * @param aAttrName the localname of the attribute michael@0: * @param aStyleContext the style context to use michael@0: * @param aGeneratedContent the array of generated content to append the michael@0: * created content to. michael@0: * @param [out] aNewContent the content node we create michael@0: * @param [out] aNewFrame the new frame we create michael@0: */ michael@0: nsresult CreateAttributeContent(nsIContent* aParentContent, michael@0: nsIFrame* aParentFrame, michael@0: int32_t aAttrNamespace, michael@0: nsIAtom* aAttrName, michael@0: nsStyleContext* aStyleContext, michael@0: nsCOMArray& aGeneratedContent, michael@0: nsIContent** aNewContent, michael@0: nsIFrame** aNewFrame); michael@0: michael@0: /** michael@0: * Create a text node containing the given string. If aText is non-null michael@0: * then we also set aText to the returned node. michael@0: */ michael@0: already_AddRefed CreateGenConTextNode(nsFrameConstructorState& aState, michael@0: const nsString& aString, michael@0: nsCOMPtr* aText, michael@0: nsGenConInitializer* aInitializer); michael@0: michael@0: /** michael@0: * Create a content node for the given generated content style. michael@0: * The caller takes care of making it SetIsNativeAnonymousRoot, binding it michael@0: * to the document, and creating frames for it. michael@0: * @param aParentContent is the node that has the before/after style michael@0: * @param aStyleContext is the 'before' or 'after' pseudo-element michael@0: * style context michael@0: * @param aContentIndex is the index of the content item to create michael@0: */ michael@0: already_AddRefed CreateGeneratedContent(nsFrameConstructorState& aState, michael@0: nsIContent* aParentContent, michael@0: nsStyleContext* aStyleContext, michael@0: uint32_t aContentIndex); michael@0: michael@0: // aFrame may be null; this method doesn't use it directly in any case. michael@0: void CreateGeneratedContentItem(nsFrameConstructorState& aState, michael@0: nsIFrame* aFrame, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: nsCSSPseudoElements::Type aPseudoElement, michael@0: FrameConstructionItemList& aItems); michael@0: michael@0: // This method can change aFrameList: it can chop off the beginning and put michael@0: // it in aParentFrame while putting the remainder into a ib-split sibling of michael@0: // aParentFrame. aPrevSibling must be the frame after which aFrameList is to michael@0: // be placed on aParentFrame's principal child list. It may be null if michael@0: // aFrameList is being added at the beginning of the child list. michael@0: nsresult AppendFramesToParent(nsFrameConstructorState& aState, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aFrameList, michael@0: nsIFrame* aPrevSibling, michael@0: bool aIsRecursiveCall = false); michael@0: michael@0: // BEGIN TABLE SECTION michael@0: /** michael@0: * Construct an outer table frame. This is the FrameConstructionData michael@0: * callback used for the job. michael@0: */ michael@0: nsIFrame* ConstructTable(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * FrameConstructionData callback for constructing table rows and row groups. michael@0: */ michael@0: nsIFrame* ConstructTableRowOrRowGroup(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * FrameConstructionData callback used for constructing table columns. michael@0: */ michael@0: nsIFrame* ConstructTableCol(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * FrameConstructionData callback used for constructing table cells. michael@0: */ michael@0: nsIFrame* ConstructTableCell(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: private: michael@0: /* An enum of possible parent types for anonymous table object construction */ michael@0: enum ParentType { michael@0: eTypeBlock = 0, /* This includes all non-table-related frames */ michael@0: eTypeRow, michael@0: eTypeRowGroup, michael@0: eTypeColGroup, michael@0: eTypeTable, michael@0: eParentTypeCount michael@0: }; michael@0: michael@0: /* 3 bits is enough to handle our ParentType values */ michael@0: #define FCDATA_PARENT_TYPE_OFFSET 29 michael@0: /* Macro to get the desired parent type out of an mBits member of michael@0: FrameConstructionData */ michael@0: #define FCDATA_DESIRED_PARENT_TYPE(_bits) \ michael@0: ParentType((_bits) >> FCDATA_PARENT_TYPE_OFFSET) michael@0: /* Macro to create FrameConstructionData bits out of a desired parent type */ michael@0: #define FCDATA_DESIRED_PARENT_TYPE_TO_BITS(_type) \ michael@0: (((uint32_t)(_type)) << FCDATA_PARENT_TYPE_OFFSET) michael@0: michael@0: /* Get the parent type that aParentFrame has. */ michael@0: static ParentType GetParentType(nsIFrame* aParentFrame) { michael@0: return GetParentType(aParentFrame->GetType()); michael@0: } michael@0: michael@0: /* Get the parent type for the given nsIFrame type atom */ michael@0: static ParentType GetParentType(nsIAtom* aFrameType); michael@0: michael@0: /* A constructor function that just creates an nsIFrame object. The caller michael@0: is responsible for initializing the object, adding it to frame lists, michael@0: constructing frames for the children, etc. michael@0: michael@0: @param nsIPresShell the presshell whose arena should be used to allocate michael@0: the frame. michael@0: @param nsStyleContext the style context to use for the frame. */ michael@0: typedef nsIFrame* (* FrameCreationFunc)(nsIPresShell*, nsStyleContext*); michael@0: michael@0: /* A function that can be used to get a FrameConstructionData. Such michael@0: a function is allowed to return null. michael@0: michael@0: @param nsIContent the node for which the frame is being constructed. michael@0: @param nsStyleContext the style context to be used for the frame. michael@0: */ michael@0: struct FrameConstructionData; michael@0: typedef const FrameConstructionData* michael@0: (* FrameConstructionDataGetter)(Element*, nsStyleContext*); michael@0: michael@0: /* A constructor function that's used for complicated construction tasks. michael@0: This is expected to create the new frame, initialize it, add whatever michael@0: needs to be added to aFrameItems (XXXbz is that really necessary? Could michael@0: caller add? Might there be cases when the returned frame or its michael@0: placeholder is not the thing that ends up in aFrameItems? If not, would michael@0: it be safe to do the add into the frame construction state after michael@0: processing kids? Look into this as a followup!), process children as michael@0: needed, etc. It is NOT expected to deal with setting the frame on the michael@0: content. michael@0: michael@0: @param aState the frame construction state to use. michael@0: @param aItem the frame construction item to use michael@0: @param aParentFrame the frame to set as the parent of the michael@0: newly-constructed frame. michael@0: @param aStyleDisplay the display struct from aItem's mStyleContext michael@0: @param aFrameItems the frame list to add the new frame (or its michael@0: placeholder) to. michael@0: @return the frame that was constructed. This frame is what the caller michael@0: will set as the frame on the content. Guaranteed non-null. michael@0: */ michael@0: typedef nsIFrame* michael@0: (nsCSSFrameConstructor::* FrameFullConstructor)(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /* Bits that modify the way a FrameConstructionData is handled */ michael@0: michael@0: /* If the FCDATA_SKIP_FRAMESET bit is set, then the frame created should not michael@0: be set as the primary frame on the content node. This should only be used michael@0: in very rare cases when we create more than one frame for a given content michael@0: node. */ michael@0: #define FCDATA_SKIP_FRAMESET 0x1 michael@0: /* If the FCDATA_FUNC_IS_DATA_GETTER bit is set, then the mFunc of the michael@0: FrameConstructionData is a getter function that can be used to get the michael@0: actual FrameConstructionData to use. */ michael@0: #define FCDATA_FUNC_IS_DATA_GETTER 0x2 michael@0: /* If the FCDATA_FUNC_IS_FULL_CTOR bit is set, then the FrameConstructionData michael@0: has an mFullConstructor. In this case, there is no relevant mData or michael@0: mFunc */ michael@0: #define FCDATA_FUNC_IS_FULL_CTOR 0x4 michael@0: /* If FCDATA_DISALLOW_OUT_OF_FLOW is set, do not allow the frame to michael@0: float or be absolutely positioned. This can also be used with michael@0: FCDATA_FUNC_IS_FULL_CTOR to indicate what the full-constructor michael@0: function will do. */ michael@0: #define FCDATA_DISALLOW_OUT_OF_FLOW 0x8 michael@0: /* If FCDATA_FORCE_NULL_ABSPOS_CONTAINER is set, make sure to push a michael@0: null absolute containing block before processing children for this michael@0: frame. If this is not set, the frame will be pushed as the michael@0: absolute containing block as needed, based on its style */ michael@0: #define FCDATA_FORCE_NULL_ABSPOS_CONTAINER 0x10 michael@0: /* If FCDATA_WRAP_KIDS_IN_BLOCKS is set, the inline kids of the frame michael@0: will be wrapped in blocks. This is only usable for MathML at the michael@0: moment. */ michael@0: #define FCDATA_WRAP_KIDS_IN_BLOCKS 0x20 michael@0: /* If FCDATA_SUPPRESS_FRAME is set, no frame should be created for the michael@0: content. If this bit is set, nothing else in the struct needs to be michael@0: set. */ michael@0: #define FCDATA_SUPPRESS_FRAME 0x40 michael@0: /* If FCDATA_MAY_NEED_SCROLLFRAME is set, the new frame should be wrapped in michael@0: a scrollframe if its overflow type so requires. */ michael@0: #define FCDATA_MAY_NEED_SCROLLFRAME 0x80 michael@0: #ifdef MOZ_XUL michael@0: /* If FCDATA_IS_POPUP is set, the new frame is a XUL popup frame. These need michael@0: some really weird special handling. */ michael@0: #define FCDATA_IS_POPUP 0x100 michael@0: #endif /* MOZ_XUL */ michael@0: /* If FCDATA_SKIP_ABSPOS_PUSH is set, don't push this frame as an michael@0: absolute containing block, no matter what its style says. */ michael@0: #define FCDATA_SKIP_ABSPOS_PUSH 0x200 michael@0: /* If FCDATA_DISALLOW_GENERATED_CONTENT is set, then don't allow generated michael@0: content when processing kids of this frame. This should not be used with michael@0: FCDATA_FUNC_IS_FULL_CTOR */ michael@0: #define FCDATA_DISALLOW_GENERATED_CONTENT 0x400 michael@0: /* If FCDATA_IS_TABLE_PART is set, then the frame is some sort of michael@0: table-related thing and we should not attempt to fetch a table-cell parent michael@0: for it if it's inside another table-related frame. */ michael@0: #define FCDATA_IS_TABLE_PART 0x800 michael@0: /* If FCDATA_IS_INLINE is set, then the frame is a non-replaced CSS michael@0: inline box. */ michael@0: #define FCDATA_IS_INLINE 0x1000 michael@0: /* If FCDATA_IS_LINE_PARTICIPANT is set, the frame is something that will michael@0: return true for IsFrameOfType(nsIFrame::eLineParticipant) */ michael@0: #define FCDATA_IS_LINE_PARTICIPANT 0x2000 michael@0: /* If FCDATA_IS_LINE_BREAK is set, the frame is something that will michael@0: induce a line break boundary before and after itself. */ michael@0: #define FCDATA_IS_LINE_BREAK 0x4000 michael@0: /* If FCDATA_ALLOW_BLOCK_STYLES is set, allow block styles when processing michael@0: children. This should not be used with FCDATA_FUNC_IS_FULL_CTOR. */ michael@0: #define FCDATA_ALLOW_BLOCK_STYLES 0x8000 michael@0: /* If FCDATA_USE_CHILD_ITEMS is set, then use the mChildItems in the relevant michael@0: FrameConstructionItem instead of trying to process the content's children. michael@0: This can be used with or without FCDATA_FUNC_IS_FULL_CTOR. michael@0: The child items might still need table pseudo processing. */ michael@0: #define FCDATA_USE_CHILD_ITEMS 0x10000 michael@0: /* If FCDATA_FORCED_NON_SCROLLABLE_BLOCK is set, then this block michael@0: would have been scrollable but has been forced to be michael@0: non-scrollable due to being in a paginated context. */ michael@0: #define FCDATA_FORCED_NON_SCROLLABLE_BLOCK 0x20000 michael@0: /* If FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, then create a michael@0: block formatting context wrapper around the kids of this frame michael@0: using the FrameConstructionData's mPseudoAtom for its anonymous michael@0: box type. */ michael@0: #define FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS 0x40000 michael@0: /* If FCDATA_IS_SVG_TEXT is set, then this text frame is a descendant of michael@0: an SVG text frame. */ michael@0: #define FCDATA_IS_SVG_TEXT 0x80000 michael@0: michael@0: /* Structure representing information about how a frame should be michael@0: constructed. */ michael@0: struct FrameConstructionData { michael@0: // Flag bits that can modify the way the construction happens michael@0: uint32_t mBits; michael@0: // We have exactly one of three types of functions, so use a union for michael@0: // better cache locality for the ones that aren't pointer-to-member. That michael@0: // one needs to be separate, because we can't cast between it and the michael@0: // others and hence wouldn't be able to initialize the union without a michael@0: // constructor and all the resulting generated code. See documentation michael@0: // above for FrameCreationFunc, FrameConstructionDataGetter, and michael@0: // FrameFullConstructor to see what the functions would do. michael@0: union Func { michael@0: FrameCreationFunc mCreationFunc; michael@0: FrameConstructionDataGetter mDataGetter; michael@0: } mFunc; michael@0: FrameFullConstructor mFullConstructor; michael@0: // For cases when FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, the michael@0: // anonymous box type to use for that wrapper. michael@0: nsICSSAnonBoxPseudo * const * const mAnonBoxPseudo; michael@0: }; michael@0: michael@0: /* Structure representing a mapping of an atom to a FrameConstructionData. michael@0: This can be used with non-static atoms, assuming that the nsIAtom* is michael@0: stored somewhere that this struct can point to (that is, a static michael@0: nsIAtom*) and that it's allocated before the struct is ever used. */ michael@0: struct FrameConstructionDataByTag { michael@0: // Pointer to nsIAtom* is used because we want to initialize this michael@0: // statically, so before our atom tables are set up. michael@0: const nsIAtom * const * const mTag; michael@0: const FrameConstructionData mData; michael@0: }; michael@0: michael@0: /* Structure representing a mapping of an integer to a michael@0: FrameConstructionData. There are no magic integer values here. */ michael@0: struct FrameConstructionDataByInt { michael@0: /* Could be used for display or whatever else */ michael@0: const int32_t mInt; michael@0: const FrameConstructionData mData; michael@0: }; michael@0: michael@0: /* Structure that has a FrameConstructionData and style context pseudo-type michael@0: for a table pseudo-frame */ michael@0: struct PseudoParentData { michael@0: const FrameConstructionData mFCData; michael@0: nsICSSAnonBoxPseudo * const * const mPseudoType; michael@0: }; michael@0: /* Array of such structures that we use to properly construct table michael@0: pseudo-frames as needed */ michael@0: static const PseudoParentData sPseudoParentData[eParentTypeCount]; michael@0: michael@0: /* A function that takes an integer, content, style context, and array of michael@0: FrameConstructionDataByInts and finds the appropriate frame construction michael@0: data to use and returns it. This can return null if none of the integers michael@0: match or if the matching integer has a FrameConstructionDataGetter that michael@0: returns null. */ michael@0: static const FrameConstructionData* michael@0: FindDataByInt(int32_t aInt, Element* aElement, michael@0: nsStyleContext* aStyleContext, michael@0: const FrameConstructionDataByInt* aDataPtr, michael@0: uint32_t aDataLength); michael@0: michael@0: /* A function that takes a tag, content, style context, and array of michael@0: FrameConstructionDataByTags and finds the appropriate frame construction michael@0: data to use and returns it. This can return null if none of the tags michael@0: match or if the matching tag has a FrameConstructionDataGetter that michael@0: returns null. */ michael@0: static const FrameConstructionData* michael@0: FindDataByTag(nsIAtom* aTag, Element* aElement, michael@0: nsStyleContext* aStyleContext, michael@0: const FrameConstructionDataByTag* aDataPtr, michael@0: uint32_t aDataLength); michael@0: michael@0: /* A class representing a list of FrameConstructionItems */ michael@0: class FrameConstructionItemList { michael@0: public: michael@0: FrameConstructionItemList() : michael@0: mInlineCount(0), michael@0: mBlockCount(0), michael@0: mLineParticipantCount(0), michael@0: mItemCount(0), michael@0: mLineBoundaryAtStart(false), michael@0: mLineBoundaryAtEnd(false), michael@0: mParentHasNoXBLChildren(false), michael@0: mTriedConstructingFrames(false) michael@0: { michael@0: PR_INIT_CLIST(&mItems); michael@0: memset(mDesiredParentCounts, 0, sizeof(mDesiredParentCounts)); michael@0: } michael@0: michael@0: ~FrameConstructionItemList() { michael@0: PRCList* cur = PR_NEXT_LINK(&mItems); michael@0: while (cur != &mItems) { michael@0: PRCList* next = PR_NEXT_LINK(cur); michael@0: delete ToItem(cur); michael@0: cur = next; michael@0: } michael@0: michael@0: // Leaves our mItems pointing to deleted memory in both directions, michael@0: // but that's OK at this point. michael@0: michael@0: // Create the undisplayed entries for our mUndisplayedItems, if any, but michael@0: // only if we have tried constructing frames for this item list. If we michael@0: // haven't, then we're just throwing it away and will probably try again. michael@0: if (!mUndisplayedItems.IsEmpty() && mTriedConstructingFrames) { michael@0: // We could store the frame manager in a member, but just michael@0: // getting it off the style context is not too bad. michael@0: nsFrameManager *mgr = michael@0: mUndisplayedItems[0].mStyleContext->PresContext()->FrameManager(); michael@0: for (uint32_t i = 0; i < mUndisplayedItems.Length(); ++i) { michael@0: UndisplayedItem& item = mUndisplayedItems[i]; michael@0: mgr->SetUndisplayedContent(item.mContent, item.mStyleContext); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SetLineBoundaryAtStart(bool aBoundary) { mLineBoundaryAtStart = aBoundary; } michael@0: void SetLineBoundaryAtEnd(bool aBoundary) { mLineBoundaryAtEnd = aBoundary; } michael@0: void SetParentHasNoXBLChildren(bool aHasNoXBLChildren) { michael@0: mParentHasNoXBLChildren = aHasNoXBLChildren; michael@0: } michael@0: void SetTriedConstructingFrames() { mTriedConstructingFrames = true; } michael@0: bool HasLineBoundaryAtStart() { return mLineBoundaryAtStart; } michael@0: bool HasLineBoundaryAtEnd() { return mLineBoundaryAtEnd; } michael@0: bool ParentHasNoXBLChildren() { return mParentHasNoXBLChildren; } michael@0: bool IsEmpty() const { return PR_CLIST_IS_EMPTY(&mItems); } michael@0: bool AnyItemsNeedBlockParent() const { return mLineParticipantCount != 0; } michael@0: bool AreAllItemsInline() const { return mInlineCount == mItemCount; } michael@0: bool AreAllItemsBlock() const { return mBlockCount == mItemCount; } michael@0: bool AllWantParentType(ParentType aDesiredParentType) const { michael@0: return mDesiredParentCounts[aDesiredParentType] == mItemCount; michael@0: } michael@0: michael@0: // aSuppressWhiteSpaceOptimizations is true if optimizations that michael@0: // skip constructing whitespace frames for this item or items michael@0: // around it cannot be performed. michael@0: FrameConstructionItem* AppendItem(const FrameConstructionData* aFCData, michael@0: nsIContent* aContent, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: PendingBinding* aPendingBinding, michael@0: already_AddRefed&& aStyleContext, michael@0: bool aSuppressWhiteSpaceOptimizations, michael@0: nsTArray* aAnonChildren) michael@0: { michael@0: FrameConstructionItem* item = michael@0: new FrameConstructionItem(aFCData, aContent, aTag, aNameSpaceID, michael@0: aPendingBinding, aStyleContext, michael@0: aSuppressWhiteSpaceOptimizations, michael@0: aAnonChildren); michael@0: PR_APPEND_LINK(item, &mItems); michael@0: ++mItemCount; michael@0: ++mDesiredParentCounts[item->DesiredParentType()]; michael@0: return item; michael@0: } michael@0: michael@0: void AppendUndisplayedItem(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) { michael@0: mUndisplayedItems.AppendElement(UndisplayedItem(aContent, aStyleContext)); michael@0: } michael@0: michael@0: void InlineItemAdded() { ++mInlineCount; } michael@0: void BlockItemAdded() { ++mBlockCount; } michael@0: void LineParticipantItemAdded() { ++mLineParticipantCount; } michael@0: michael@0: class Iterator; michael@0: friend class Iterator; michael@0: michael@0: class Iterator { michael@0: public: michael@0: Iterator(FrameConstructionItemList& list) : michael@0: mCurrent(PR_NEXT_LINK(&list.mItems)), michael@0: mEnd(&list.mItems), michael@0: mList(list) michael@0: {} michael@0: Iterator(const Iterator& aOther) : michael@0: mCurrent(aOther.mCurrent), michael@0: mEnd(aOther.mEnd), michael@0: mList(aOther.mList) michael@0: {} michael@0: michael@0: bool operator==(const Iterator& aOther) const { michael@0: NS_ASSERTION(mEnd == aOther.mEnd, "Iterators for different lists?"); michael@0: return mCurrent == aOther.mCurrent; michael@0: } michael@0: bool operator!=(const Iterator& aOther) const { michael@0: return !(*this == aOther); michael@0: } michael@0: Iterator& operator=(const Iterator& aOther) { michael@0: NS_ASSERTION(mEnd == aOther.mEnd, "Iterators for different lists?"); michael@0: mCurrent = aOther.mCurrent; michael@0: return *this; michael@0: } michael@0: michael@0: FrameConstructionItemList* List() { michael@0: return &mList; michael@0: } michael@0: michael@0: operator FrameConstructionItem& () { michael@0: return item(); michael@0: } michael@0: michael@0: FrameConstructionItem& item() { michael@0: return *FrameConstructionItemList::ToItem(mCurrent); michael@0: } michael@0: bool IsDone() const { return mCurrent == mEnd; } michael@0: bool AtStart() const { return mCurrent == PR_NEXT_LINK(mEnd); } michael@0: void Next() { michael@0: NS_ASSERTION(!IsDone(), "Should have checked IsDone()!"); michael@0: mCurrent = PR_NEXT_LINK(mCurrent); michael@0: } michael@0: void Prev() { michael@0: NS_ASSERTION(!AtStart(), "Should have checked AtStart()!"); michael@0: mCurrent = PR_PREV_LINK(mCurrent); michael@0: } michael@0: void SetToEnd() { mCurrent = mEnd; } michael@0: michael@0: // Skip over all items that want a parent type different from the given michael@0: // one. Return whether the iterator is done after doing that. The michael@0: // iterator must not be done when this is called. michael@0: inline bool SkipItemsWantingParentType(ParentType aParentType); michael@0: michael@0: // Skip over non-replaced inline frames and positioned frames. michael@0: // Return whether the iterator is done after doing that. michael@0: // The iterator must not be done when this is called. michael@0: inline bool SkipItemsThatNeedAnonFlexItem( michael@0: const nsFrameConstructorState& aState); michael@0: michael@0: // Skip to the first frame that is a non-replaced inline or is michael@0: // positioned. Return whether the iterator is done after doing that. michael@0: // The iterator must not be done when this is called. michael@0: inline bool SkipItemsThatDontNeedAnonFlexItem( michael@0: const nsFrameConstructorState& aState); michael@0: michael@0: // Skip over whitespace. Return whether the iterator is done after doing michael@0: // that. The iterator must not be done, and must be pointing to a michael@0: // whitespace item when this is called. michael@0: inline bool SkipWhitespace(nsFrameConstructorState& aState); michael@0: michael@0: // Remove the item pointed to by this iterator from its current list and michael@0: // Append it to aTargetList. This iterator is advanced to point to the michael@0: // next item in its list. aIter must not be done. aOther must not be michael@0: // the list this iterator is iterating over.. michael@0: void AppendItemToList(FrameConstructionItemList& aTargetList); michael@0: michael@0: // As above, but moves all items starting with this iterator until we michael@0: // get to aEnd; the item pointed to by aEnd is not stolen. This method michael@0: // might have optimizations over just looping and doing StealItem for michael@0: // some special cases. After this method returns, this iterator will michael@0: // point to the item aEnd points to now; aEnd is not modified. michael@0: // aTargetList must not be the list this iterator is iterating over. michael@0: void AppendItemsToList(const Iterator& aEnd, michael@0: FrameConstructionItemList& aTargetList); michael@0: michael@0: // Insert aItem in this iterator's list right before the item pointed to michael@0: // by this iterator. After the insertion, this iterator will continue to michael@0: // point to the item it now points to (the one just after the michael@0: // newly-inserted item). This iterator is allowed to be done; in that michael@0: // case this call just appends the given item to the list. michael@0: void InsertItem(FrameConstructionItem* aItem); michael@0: michael@0: // Delete the items between this iterator and aEnd, including the item michael@0: // this iterator currently points to but not including the item pointed michael@0: // to by aEnd. When this returns, this iterator will point to the same michael@0: // item as aEnd. This iterator must not equal aEnd when this method is michael@0: // called. michael@0: void DeleteItemsTo(const Iterator& aEnd); michael@0: michael@0: private: michael@0: PRCList* mCurrent; michael@0: PRCList* mEnd; michael@0: FrameConstructionItemList& mList; michael@0: }; michael@0: michael@0: private: michael@0: static FrameConstructionItem* ToItem(PRCList* item) { michael@0: return static_cast(item); michael@0: } michael@0: michael@0: struct UndisplayedItem { michael@0: UndisplayedItem(nsIContent* aContent, nsStyleContext* aStyleContext) : michael@0: mContent(aContent), mStyleContext(aStyleContext) michael@0: {} michael@0: michael@0: nsIContent * const mContent; michael@0: nsRefPtr mStyleContext; michael@0: }; michael@0: michael@0: // Adjust our various counts for aItem being added or removed. aDelta michael@0: // should be either +1 or -1 depending on which is happening. michael@0: void AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta); michael@0: michael@0: PRCList mItems; michael@0: uint32_t mInlineCount; michael@0: uint32_t mBlockCount; michael@0: uint32_t mLineParticipantCount; michael@0: uint32_t mItemCount; michael@0: uint32_t mDesiredParentCounts[eParentTypeCount]; michael@0: // True if there is guaranteed to be a line boundary before the michael@0: // frames created by these items michael@0: bool mLineBoundaryAtStart; michael@0: // True if there is guaranteed to be a line boundary after the michael@0: // frames created by these items michael@0: bool mLineBoundaryAtEnd; michael@0: // True if the parent is guaranteed to have no XBL anonymous children michael@0: bool mParentHasNoXBLChildren; michael@0: // True if we have tried constructing frames from this list michael@0: bool mTriedConstructingFrames; michael@0: michael@0: nsTArray mUndisplayedItems; michael@0: }; michael@0: michael@0: typedef FrameConstructionItemList::Iterator FCItemIterator; michael@0: michael@0: /* A struct representing an item for which frames might need to be michael@0: * constructed. This contains all the information needed to construct the michael@0: * frame other than the parent frame and whatever would be stored in the michael@0: * frame constructor state. */ michael@0: struct FrameConstructionItem : public PRCList { michael@0: // No need to PR_INIT_CLIST in the constructor because the only michael@0: // place that creates us immediately appends us. michael@0: FrameConstructionItem(const FrameConstructionData* aFCData, michael@0: nsIContent* aContent, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: PendingBinding* aPendingBinding, michael@0: already_AddRefed& aStyleContext, michael@0: bool aSuppressWhiteSpaceOptimizations, michael@0: nsTArray* aAnonChildren) : michael@0: mFCData(aFCData), mContent(aContent), mTag(aTag), michael@0: mNameSpaceID(aNameSpaceID), michael@0: mPendingBinding(aPendingBinding), mStyleContext(aStyleContext), michael@0: mSuppressWhiteSpaceOptimizations(aSuppressWhiteSpaceOptimizations), michael@0: mIsText(false), mIsGeneratedContent(false), michael@0: mIsAnonymousContentCreatorContent(false), michael@0: mIsRootPopupgroup(false), mIsAllInline(false), mIsBlock(false), michael@0: mHasInlineEnds(false), mIsPopup(false), michael@0: mIsLineParticipant(false), mIsForSVGAElement(false) michael@0: { michael@0: if (aAnonChildren) { michael@0: NS_ASSERTION(!(mFCData->mBits & FCDATA_FUNC_IS_FULL_CTOR) || michael@0: mFCData->mFullConstructor == michael@0: &nsCSSFrameConstructor::ConstructInline, michael@0: "This is going to fail"); michael@0: NS_ASSERTION(!(mFCData->mBits & FCDATA_USE_CHILD_ITEMS), michael@0: "nsIAnonymousContentCreator::CreateAnonymousContent " michael@0: "implementations should not output a list where the " michael@0: "items have children in this case"); michael@0: mAnonChildren.SwapElements(*aAnonChildren); michael@0: } michael@0: } michael@0: ~FrameConstructionItem() { michael@0: if (mIsGeneratedContent) { michael@0: mContent->UnbindFromTree(); michael@0: NS_RELEASE(mContent); michael@0: } michael@0: } michael@0: michael@0: ParentType DesiredParentType() { michael@0: return FCDATA_DESIRED_PARENT_TYPE(mFCData->mBits); michael@0: } michael@0: michael@0: // Indicates whether (when in a flexbox container) this item needs to be michael@0: // wrapped in an anonymous block. michael@0: bool NeedsAnonFlexItem(const nsFrameConstructorState& aState); michael@0: michael@0: // Don't call this unless the frametree really depends on the answer! michael@0: // Especially so for generated content, where we don't want to reframe michael@0: // things. michael@0: bool IsWhitespace(nsFrameConstructorState& aState) const; michael@0: michael@0: bool IsLineBoundary() const { michael@0: return mIsBlock || (mFCData->mBits & FCDATA_IS_LINE_BREAK); michael@0: } michael@0: michael@0: // The FrameConstructionData to use. michael@0: const FrameConstructionData* mFCData; michael@0: // The nsIContent node to use when initializing the new frame. michael@0: nsIContent* mContent; michael@0: // The XBL-resolved tag name to use for frame construction. michael@0: nsIAtom* mTag; michael@0: // The XBL-resolved namespace to use for frame construction. michael@0: int32_t mNameSpaceID; michael@0: // The PendingBinding for this frame construction item, if any. May be michael@0: // null. We maintain a list of PendingBindings in the frame construction michael@0: // state in the order in which AddToAttachedQueue should be called on them: michael@0: // depth-first, post-order traversal order. Since we actually traverse the michael@0: // DOM in a mix of breadth-first and depth-first, it is the responsibility michael@0: // of whoever constructs FrameConstructionItem kids of a given michael@0: // FrameConstructionItem to push its mPendingBinding as the current michael@0: // insertion point before doing so and pop it afterward. michael@0: PendingBinding* mPendingBinding; michael@0: // The style context to use for creating the new frame. michael@0: nsRefPtr mStyleContext; michael@0: // Whether optimizations to skip constructing textframes around michael@0: // this content need to be suppressed. michael@0: bool mSuppressWhiteSpaceOptimizations; michael@0: // Whether this is a text content item. michael@0: bool mIsText; michael@0: // Whether this is a generated content container. michael@0: // If it is, mContent is a strong pointer. michael@0: bool mIsGeneratedContent; michael@0: // Whether this is an item for nsIAnonymousContentCreator content. michael@0: bool mIsAnonymousContentCreatorContent; michael@0: // Whether this is an item for the root popupgroup. michael@0: bool mIsRootPopupgroup; michael@0: // Whether construction from this item will create only frames that are michael@0: // IsInlineOutside() in the principal child list. This is not precise, but michael@0: // conservative: if true the frames will really be inline, whereas if false michael@0: // they might still all be inline. michael@0: bool mIsAllInline; michael@0: // Whether construction from this item will create only frames that are michael@0: // IsBlockOutside() in the principal child list. This is not precise, but michael@0: // conservative: if true the frames will really be blocks, whereas if false michael@0: // they might still be blocks (and in particular, out-of-flows that didn't michael@0: // find a containing block). michael@0: bool mIsBlock; michael@0: // Whether construction from this item will give leading and trailing michael@0: // inline frames. This is equal to mIsAllInline, except for inline frame michael@0: // items, where it's always true, whereas mIsAllInline might be false due michael@0: // to {ib} splits. michael@0: bool mHasInlineEnds; michael@0: // Whether construction from this item will create a popup that needs to michael@0: // go into the global popup items. michael@0: bool mIsPopup; michael@0: // Whether this item should be treated as a line participant michael@0: bool mIsLineParticipant; michael@0: // Whether this item is for an SVG element michael@0: bool mIsForSVGAElement; michael@0: michael@0: // Child frame construction items. michael@0: FrameConstructionItemList mChildItems; michael@0: michael@0: // ContentInfo list for children that have yet to have michael@0: // FrameConstructionItem objects created for them. This exists because michael@0: // AddFrameConstructionItemsInternal needs a valid frame, but in the case michael@0: // that nsIAnonymousContentCreator::CreateAnonymousContent returns items michael@0: // that have their own children (so we have a tree of ContentInfo objects michael@0: // rather than a flat list) we don't yet have a frame to provide to michael@0: // AddFrameConstructionItemsInternal in order to create the items for the michael@0: // grandchildren. That prevents FrameConstructionItems from being created michael@0: // for these grandchildren (and any descendants that they may have), michael@0: // otherwise they could have been added to the mChildItems member of their michael@0: // parent FrameConstructionItem. As it is, the grandchildren ContentInfo michael@0: // list has to be stored in this mAnonChildren member in order to delay michael@0: // construction of the FrameConstructionItems for the grandchildren until michael@0: // a frame has been created for their parent item. michael@0: nsTArray mAnonChildren; michael@0: michael@0: private: michael@0: FrameConstructionItem(const FrameConstructionItem& aOther) MOZ_DELETE; /* not implemented */ michael@0: }; michael@0: michael@0: /** michael@0: * Function to create the anonymous flex items that we need. michael@0: * If aParentFrame is not a nsFlexContainerFrame then this method is a NOP. michael@0: * @param aItems the child frame construction items before pseudo creation michael@0: * @param aParentFrame the parent frame michael@0: */ michael@0: void CreateNeededAnonFlexItems(nsFrameConstructorState& aState, michael@0: FrameConstructionItemList& aItems, michael@0: nsIFrame* aParentFrame); michael@0: michael@0: /** michael@0: * Function to create the table pseudo items we need. michael@0: * @param aItems the child frame construction items before pseudo creation michael@0: * @param aParentFrame the parent frame we're creating pseudos for michael@0: */ michael@0: inline void CreateNeededTablePseudos(nsFrameConstructorState& aState, michael@0: FrameConstructionItemList& aItems, michael@0: nsIFrame* aParentFrame); michael@0: michael@0: /** michael@0: * Function to adjust aParentFrame to deal with captions. michael@0: * @param aParentFrame the frame we think should be the parent. This will be michael@0: * adjusted to point to the right parent frame. michael@0: * @param aFCData the FrameConstructionData that would be used for frame michael@0: * construction. michael@0: * @param aStyleContext the style context for aChildContent michael@0: */ michael@0: // XXXbz this function should really go away once we rework pseudo-frame michael@0: // handling to be better. This should simply be part of the job of michael@0: // GetGeometricParent, and stuff like the frameitems and parent frame should michael@0: // be kept track of in the state... michael@0: void AdjustParentFrame(nsIFrame* & aParentFrame, michael@0: const FrameConstructionData* aFCData, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: // END TABLE SECTION michael@0: michael@0: protected: michael@0: static nsIFrame* CreatePlaceholderFrameFor(nsIPresShell* aPresShell, michael@0: nsIContent* aContent, michael@0: nsIFrame* aFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aPrevInFlow, michael@0: nsFrameState aTypeBit); michael@0: michael@0: private: michael@0: // ConstructSelectFrame puts the new frame in aFrameItems and michael@0: // handles the kids of the select. michael@0: nsIFrame* ConstructSelectFrame(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: // ConstructFieldSetFrame puts the new frame in aFrameItems and michael@0: // handles the kids of the fieldset michael@0: nsIFrame* ConstructFieldSetFrame(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: // aParentFrame might be null. If it is, that means it was an michael@0: // inline frame. michael@0: static const FrameConstructionData* FindTextData(nsIFrame* aParentFrame); michael@0: michael@0: void ConstructTextFrame(const FrameConstructionData* aData, michael@0: nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: // If aPossibleTextContent is a text node and doesn't have a frame, append a michael@0: // frame construction item for it to aItems. michael@0: void AddTextItemIfNeeded(nsFrameConstructorState& aState, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aPossibleTextContent, michael@0: FrameConstructionItemList& aItems); michael@0: michael@0: // If aParentContent's child aContent is a text node and michael@0: // doesn't have a frame, try to create a frame for it. michael@0: void ReframeTextIfNeeded(nsIContent* aParentContent, michael@0: nsIContent* aContent); michael@0: michael@0: void AddPageBreakItem(nsIContent* aContent, michael@0: nsStyleContext* aMainStyleContext, michael@0: FrameConstructionItemList& aItems); michael@0: michael@0: // Function to find FrameConstructionData for aElement. Will return michael@0: // null if aElement is not HTML. michael@0: // aParentFrame might be null. If it is, that means it was an michael@0: // inline frame. michael@0: static const FrameConstructionData* FindHTMLData(Element* aContent, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext); michael@0: // HTML data-finding helper functions michael@0: static const FrameConstructionData* michael@0: FindImgData(Element* aElement, nsStyleContext* aStyleContext); michael@0: static const FrameConstructionData* michael@0: FindImgControlData(Element* aElement, nsStyleContext* aStyleContext); michael@0: static const FrameConstructionData* michael@0: FindInputData(Element* aElement, nsStyleContext* aStyleContext); michael@0: static const FrameConstructionData* michael@0: FindObjectData(Element* aElement, nsStyleContext* aStyleContext); michael@0: static const FrameConstructionData* michael@0: FindCanvasData(Element* aElement, nsStyleContext* aStyleContext); michael@0: michael@0: /* Construct a frame from the given FrameConstructionItem. This function michael@0: will handle adding the frame to frame lists, processing children, setting michael@0: the frame as the primary frame for the item's content, and so forth. michael@0: michael@0: @param aItem the FrameConstructionItem to use. michael@0: @param aState the frame construction state to use. michael@0: @param aParentFrame the frame to set as the parent of the michael@0: newly-constructed frame. michael@0: @param aFrameItems the frame list to add the new frame (or its michael@0: placeholder) to. michael@0: */ michael@0: void ConstructFrameFromItemInternal(FrameConstructionItem& aItem, michael@0: nsFrameConstructorState& aState, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: // possible flags for AddFrameConstructionItemInternal's aFlags argument michael@0: /* Allow xbl:base to affect the tag/namespace used. */ michael@0: #define ITEM_ALLOW_XBL_BASE 0x1 michael@0: /* Allow page-break before and after items to be created if the michael@0: style asks for them. */ michael@0: #define ITEM_ALLOW_PAGE_BREAK 0x2 michael@0: /* The item is a generated content item. */ michael@0: #define ITEM_IS_GENERATED_CONTENT 0x4 michael@0: /* The item is within an SVG text block frame. */ michael@0: #define ITEM_IS_WITHIN_SVG_TEXT 0x8 michael@0: /* The item allows items to be created for SVG children. */ michael@0: #define ITEM_ALLOWS_TEXT_PATH_CHILD 0x10 michael@0: /* The item is content created by an nsIAnonymousContentCreator frame */ michael@0: #define ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT 0x20 michael@0: // The guts of AddFrameConstructionItems michael@0: // aParentFrame might be null. If it is, that means it was an michael@0: // inline frame. michael@0: void AddFrameConstructionItemsInternal(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: bool aSuppressWhiteSpaceOptimizations, michael@0: nsStyleContext* aStyleContext, michael@0: uint32_t aFlags, michael@0: nsTArray* aAnonChildren, michael@0: FrameConstructionItemList& aItems); michael@0: michael@0: /** michael@0: * Construct frames for the given item list and parent frame, and put the michael@0: * resulting frames in aFrameItems. michael@0: */ michael@0: void ConstructFramesFromItemList(nsFrameConstructorState& aState, michael@0: FrameConstructionItemList& aItems, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aFrameItems); michael@0: void ConstructFramesFromItem(nsFrameConstructorState& aState, michael@0: FCItemIterator& aItem, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aFrameItems); michael@0: static bool AtLineBoundary(FCItemIterator& aIter); michael@0: michael@0: nsresult CreateAnonymousFrames(nsFrameConstructorState& aState, michael@0: nsIContent* aParent, michael@0: nsIFrame* aParentFrame, michael@0: PendingBinding * aPendingBinding, michael@0: nsFrameItems& aChildItems); michael@0: michael@0: nsresult GetAnonymousContent(nsIContent* aParent, michael@0: nsIFrame* aParentFrame, michael@0: nsTArray& aAnonContent); michael@0: michael@0: //MathML Mod - RBS michael@0: /** michael@0: * Takes the frames in aBlockItems and wraps them in a new anonymous block michael@0: * frame whose content is aContent and whose parent will be aParentFrame. michael@0: * The anonymous block is added to aNewItems and aBlockItems is cleared. michael@0: */ michael@0: void FlushAccumulatedBlock(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aBlockItems, michael@0: nsFrameItems& aNewItems); michael@0: michael@0: // Function to find FrameConstructionData for aContent. Will return michael@0: // null if aContent is not MathML. michael@0: static const FrameConstructionData* FindMathMLData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: // Function to find FrameConstructionData for aContent. Will return michael@0: // null if aContent is not XUL. michael@0: static const FrameConstructionData* FindXULTagData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsStyleContext* aStyleContext); michael@0: // XUL data-finding helper functions and structures michael@0: #ifdef MOZ_XUL michael@0: static const FrameConstructionData* michael@0: FindPopupGroupData(Element* aElement, nsStyleContext* aStyleContext); michael@0: // sXULTextBoxData used for both labels and descriptions michael@0: static const FrameConstructionData sXULTextBoxData; michael@0: static const FrameConstructionData* michael@0: FindXULLabelData(Element* aElement, nsStyleContext* aStyleContext); michael@0: static const FrameConstructionData* michael@0: FindXULDescriptionData(Element* aElement, nsStyleContext* aStyleContext); michael@0: #ifdef XP_MACOSX michael@0: static const FrameConstructionData* michael@0: FindXULMenubarData(Element* aElement, nsStyleContext* aStyleContext); michael@0: #endif /* XP_MACOSX */ michael@0: static const FrameConstructionData* michael@0: FindXULListBoxBodyData(Element* aElement, nsStyleContext* aStyleContext); michael@0: static const FrameConstructionData* michael@0: FindXULListItemData(Element* aElement, nsStyleContext* aStyleContext); michael@0: #endif /* MOZ_XUL */ michael@0: michael@0: // Function to find FrameConstructionData for aContent using one of the XUL michael@0: // display types. Will return null if aDisplay doesn't have a XUL display michael@0: // type. This function performs no other checks, so should only be called if michael@0: // we know for sure that the content is not something that should get a frame michael@0: // constructed by tag. michael@0: static const FrameConstructionData* michael@0: FindXULDisplayData(const nsStyleDisplay* aDisplay, michael@0: Element* aElement, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: /** michael@0: * Constructs an outer frame, an anonymous child that wraps its real michael@0: * children, and its descendant frames. This is used by both ConstructOuterSVG michael@0: * and ConstructMarker, which both want an anonymous block child for their michael@0: * children to go in to. michael@0: */ michael@0: nsIFrame* ConstructFrameWithAnonymousChild( michael@0: nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems, michael@0: FrameCreationFunc aConstructor, michael@0: FrameCreationFunc aInnerConstructor, michael@0: nsICSSAnonBoxPseudo* aInnerPseudo, michael@0: bool aCandidateRootFrame); michael@0: michael@0: /** michael@0: * Construct an nsSVGOuterSVGFrame. michael@0: */ michael@0: nsIFrame* ConstructOuterSVG(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * Construct an nsSVGMarkerFrame. michael@0: */ michael@0: nsIFrame* ConstructMarker(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: static const FrameConstructionData* FindSVGData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsIFrame* aParentFrame, michael@0: bool aIsWithinSVGText, michael@0: bool aAllowsTextPathChild, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: /* Not static because it does PropagateScrollToViewport. If this michael@0: changes, make this static */ michael@0: const FrameConstructionData* michael@0: FindDisplayData(const nsStyleDisplay* aDisplay, Element* aElement, michael@0: nsIFrame* aParentFrame, nsStyleContext* aStyleContext); michael@0: michael@0: /** michael@0: * Construct a scrollable block frame michael@0: */ michael@0: nsIFrame* ConstructScrollableBlock(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * Construct a non-scrollable block frame michael@0: */ michael@0: nsIFrame* ConstructNonScrollableBlock(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * This adds FrameConstructionItem objects to aItemsToConstruct for the michael@0: * anonymous content returned by an nsIAnonymousContentCreator:: michael@0: * CreateAnonymousContent implementation. michael@0: */ michael@0: void AddFCItemsForAnonymousContent( michael@0: nsFrameConstructorState& aState, michael@0: nsIFrame* aFrame, michael@0: nsTArray& aAnonymousItems, michael@0: FrameConstructionItemList& aItemsToConstruct, michael@0: uint32_t aExtraFlags = 0); michael@0: michael@0: /** michael@0: * Construct the frames for the children of aContent. "children" is defined michael@0: * as "whatever FlattenedChildIterator returns for aContent". This means we're michael@0: * basically operating on children in the "flattened tree" per sXBL/XBL2. michael@0: * This method will also handle constructing ::before, ::after, michael@0: * ::first-letter, and ::first-line frames, as needed and if allowed. michael@0: * michael@0: * If the parent is a float containing block, this method will handle pushing michael@0: * it as the float containing block in aState (so there's no need for callers michael@0: * to push it themselves). michael@0: * michael@0: * @param aState the frame construction state michael@0: * @param aContent the content node whose children need frames michael@0: * @param aStyleContext the style context for aContent michael@0: * @param aFrame the frame to use as the parent frame for the new in-flow michael@0: * kids. Note that this must be its own content insertion frame, but michael@0: * need not be be the primary frame for aContent. This frame will be michael@0: * pushed as the float containing block, as needed. aFrame is also michael@0: * used to find the parent style context for the kids' style contexts michael@0: * (not necessary aFrame's style context). michael@0: * @param aCanHaveGeneratedContent Whether to allow :before and michael@0: * :after styles on the parent. michael@0: * @param aFrameItems the list in which we should place the in-flow children michael@0: * @param aAllowBlockStyles Whether to allow first-letter and first-line michael@0: * styles on the parent. michael@0: * @param aPendingBinding Make sure to push this into aState before doing any michael@0: * child item construction. michael@0: * @param aPossiblyLeafFrame if non-null, this should be used for the isLeaf michael@0: * test and the anonymous content creation. If null, aFrame will be michael@0: * used. michael@0: */ michael@0: void ProcessChildren(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aFrame, michael@0: const bool aCanHaveGeneratedContent, michael@0: nsFrameItems& aFrameItems, michael@0: const bool aAllowBlockStyles, michael@0: PendingBinding* aPendingBinding, michael@0: nsIFrame* aPossiblyLeafFrame = nullptr); michael@0: michael@0: nsIFrame* GetFrameFor(nsIContent* aContent); michael@0: michael@0: /** michael@0: * These two functions are used when we start frame creation from a non-root michael@0: * element. They should recreate the same state that we would have michael@0: * arrived at if we had built frames from the root frame to aFrame. michael@0: * Therefore, any calls to PushFloatContainingBlock and michael@0: * PushAbsoluteContainingBlock during frame construction should get michael@0: * corresponding logic in these functions. michael@0: */ michael@0: public: michael@0: enum ContainingBlockType { michael@0: ABS_POS, michael@0: FIXED_POS michael@0: }; michael@0: nsIFrame* GetAbsoluteContainingBlock(nsIFrame* aFrame, ContainingBlockType aType); michael@0: nsIFrame* GetFloatContainingBlock(nsIFrame* aFrame); michael@0: michael@0: private: michael@0: nsIContent* PropagateScrollToViewport(); michael@0: michael@0: // Build a scroll frame: michael@0: // Calls BeginBuildingScrollFrame, InitAndRestoreFrame, and then FinishBuildingScrollFrame. michael@0: // @param aNewFrame the created scrollframe --- output only michael@0: // @param aParentFrame the geometric parent that the scrollframe will have. michael@0: nsresult michael@0: BuildScrollFrame(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aContentStyle, michael@0: nsIFrame* aScrolledFrame, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame*& aNewFrame); michael@0: michael@0: // Builds the initial ScrollFrame michael@0: already_AddRefed michael@0: BeginBuildingScrollFrame(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aContentStyle, michael@0: nsIFrame* aParentFrame, michael@0: nsIAtom* aScrolledPseudo, michael@0: bool aIsRoot, michael@0: nsIFrame*& aNewFrame); michael@0: michael@0: // Completes the building of the scrollframe: michael@0: // Creates a view for the scrolledframe and makes it the child of the scrollframe. michael@0: void michael@0: FinishBuildingScrollFrame(nsIFrame* aScrollFrame, michael@0: nsIFrame* aScrolledFrame); michael@0: michael@0: // InitializeSelectFrame puts scrollFrame in aFrameItems if aBuildCombobox is false michael@0: // aBuildCombobox indicates if we are building a combobox that has a dropdown michael@0: // popup widget or not. michael@0: nsresult michael@0: InitializeSelectFrame(nsFrameConstructorState& aState, michael@0: nsIFrame* scrollFrame, michael@0: nsIFrame* scrolledFrame, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: bool aBuildCombobox, michael@0: PendingBinding* aPendingBinding, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: nsresult MaybeRecreateFramesForElement(Element* aElement); michael@0: michael@0: // If aAsyncInsert is true then a restyle event will be posted to handle the michael@0: // required ContentInserted call instead of doing it immediately. michael@0: nsresult RecreateFramesForContent(nsIContent* aContent, bool aAsyncInsert); michael@0: michael@0: // If removal of aFrame from the frame tree requires reconstruction of some michael@0: // containing block (either of aFrame or of its parent) due to {ib} splits or michael@0: // table pseudo-frames, recreate the relevant frame subtree. The return value michael@0: // indicates whether this happened. If this method returns true, *aResult is michael@0: // the return value of ReframeContainingBlock or RecreateFramesForContent. If michael@0: // this method returns false, the value of *aResult is not affected. aFrame michael@0: // and aResult must not be null. aFrame must be the result of a michael@0: // GetPrimaryFrame() call on a content node (which means its parent is also michael@0: // not null). michael@0: bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, michael@0: nsresult* aResult); michael@0: michael@0: nsIFrame* CreateContinuingOuterTableFrame(nsIPresShell* aPresShell, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: nsIFrame* CreateContinuingTableFrame(nsIPresShell* aPresShell, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: //---------------------------------------- michael@0: michael@0: // Methods support creating block frames and their children michael@0: michael@0: already_AddRefed michael@0: GetFirstLetterStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: already_AddRefed michael@0: GetFirstLineStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: bool ShouldHaveFirstLetterStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: // Check whether a given block has first-letter style. Make sure to michael@0: // only pass in blocks! And don't pass in null either. michael@0: bool HasFirstLetterStyle(nsIFrame* aBlockFrame); michael@0: michael@0: bool ShouldHaveFirstLineStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: void ShouldHaveSpecialBlockStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: bool* aHaveFirstLetterStyle, michael@0: bool* aHaveFirstLineStyle); michael@0: michael@0: // |aContentParentFrame| should be null if it's really the same as michael@0: // |aParentFrame|. michael@0: // @param aFrameItems where we want to put the block in case it's in-flow. michael@0: // @param aNewFrame an in/out parameter. On input it is the block to be michael@0: // constructed. On output it is reset to the outermost michael@0: // frame constructed (e.g. if we need to wrap the block in an michael@0: // nsColumnSetFrame. michael@0: // @param aParentFrame is the desired parent for the (possibly wrapped) michael@0: // block michael@0: // @param aContentParent is the parent the block would have if it michael@0: // were in-flow michael@0: // @param aPositionedFrameForAbsPosContainer if non-null, then the new michael@0: // block should be an abs-pos container and aPositionedFrameForAbsPosContainer michael@0: // is the frame whose style is making this block an abs-pos container. michael@0: // @param aPendingBinding the pending binding from this block's frame michael@0: // construction item. michael@0: void ConstructBlock(nsFrameConstructorState& aState, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aContentParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame** aNewFrame, michael@0: nsFrameItems& aFrameItems, michael@0: nsIFrame* aPositionedFrameForAbsPosContainer, michael@0: PendingBinding* aPendingBinding); michael@0: michael@0: nsIFrame* ConstructInline(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: /** michael@0: * Create any additional {ib} siblings needed to contain aChildItems and put michael@0: * them in aSiblings. michael@0: * michael@0: * @param aState the frame constructor state michael@0: * @param aInitialInline is an already-existing inline frame that will be michael@0: * part of this {ib} split and come before everything michael@0: * in aSiblings. michael@0: * @param aIsPositioned true if aInitialInline is positioned. michael@0: * @param aChildItems is a child list starting with a block; this method michael@0: * assumes that the inline has already taken all the michael@0: * children it wants. When the method returns aChildItems michael@0: * will be empty. michael@0: * @param aSiblings the nsFrameItems to put the newly-created siblings into. michael@0: * michael@0: * This method is responsible for making any SetFrameIsIBSplit calls that are michael@0: * needed. michael@0: */ michael@0: void CreateIBSiblings(nsFrameConstructorState& aState, michael@0: nsIFrame* aInitialInline, michael@0: bool aIsPositioned, michael@0: nsFrameItems& aChildItems, michael@0: nsFrameItems& aSiblings); michael@0: michael@0: /** michael@0: * For an inline aParentItem, construct its list of child michael@0: * FrameConstructionItems and set its mIsAllInline flag appropriately. michael@0: */ michael@0: void BuildInlineChildItems(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aParentItem, michael@0: bool aItemIsWithinSVGText, michael@0: bool aItemAllowsTextPathChild); michael@0: michael@0: // Determine whether we need to wipe out what we just did and start over michael@0: // because we're doing something like adding block kids to an inline frame michael@0: // (and therefore need an {ib} split). aPrevSibling must be correct, even in michael@0: // aIsAppend cases. Passing aIsAppend false even when an append is happening michael@0: // is ok in terms of correctness, but can lead to unnecessary reframing. If michael@0: // aIsAppend is true, then the caller MUST call michael@0: // nsCSSFrameConstructor::AppendFrames (as opposed to michael@0: // nsFrameManager::InsertFrames directly) to add the new frames. michael@0: // @return true if we reconstructed the containing block, false michael@0: // otherwise michael@0: bool WipeContainingBlock(nsFrameConstructorState& aState, michael@0: nsIFrame* aContainingBlock, michael@0: nsIFrame* aFrame, michael@0: FrameConstructionItemList& aItems, michael@0: bool aIsAppend, michael@0: nsIFrame* aPrevSibling); michael@0: michael@0: nsresult ReframeContainingBlock(nsIFrame* aFrame); michael@0: michael@0: //---------------------------------------- michael@0: michael@0: // Methods support :first-letter style michael@0: michael@0: void CreateFloatingLetterFrame(nsFrameConstructorState& aState, michael@0: nsIFrame* aBlockFrame, michael@0: nsIContent* aTextContent, michael@0: nsIFrame* aTextFrame, michael@0: nsIContent* aBlockContent, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsFrameItems& aResult); michael@0: michael@0: void CreateLetterFrame(nsIFrame* aBlockFrame, michael@0: nsIFrame* aBlockContinuation, michael@0: nsIContent* aTextContent, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aResult); michael@0: michael@0: void WrapFramesInFirstLetterFrame(nsIContent* aBlockContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsFrameItems& aBlockFrames); michael@0: michael@0: /** michael@0: * Looks in the block aBlockFrame for a text frame that contains the michael@0: * first-letter of the block and creates the necessary first-letter frames michael@0: * and returns them in aLetterFrames. michael@0: * michael@0: * @param aBlockFrame the (first-continuation of) the block we are creating a michael@0: * first-letter frame for michael@0: * @param aBlockContinuation the current continuation of the block that we michael@0: * are looking in for a textframe with suitable michael@0: * contents for first-letter michael@0: * @param aParentFrame the current frame whose children we are looking at for michael@0: * a suitable first-letter textframe michael@0: * @param aParentFrameList the first child of aParentFrame michael@0: * @param aModifiedParent returns the parent of the textframe that contains michael@0: * the first-letter michael@0: * @param aTextFrame returns the textframe that had the first-letter michael@0: * @param aPrevFrame returns the previous sibling of aTextFrame michael@0: * @param aLetterFrames returns the frames that were created michael@0: * @param aStopLooking returns whether we should stop looking for a michael@0: * first-letter either because it was found or won't be michael@0: * found michael@0: */ michael@0: void WrapFramesInFirstLetterFrame(nsIFrame* aBlockFrame, michael@0: nsIFrame* aBlockContinuation, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aParentFrameList, michael@0: nsIFrame** aModifiedParent, michael@0: nsIFrame** aTextFrame, michael@0: nsIFrame** aPrevFrame, michael@0: nsFrameItems& aLetterFrames, michael@0: bool* aStopLooking); michael@0: michael@0: void RecoverLetterFrames(nsIFrame* aBlockFrame); michael@0: michael@0: // michael@0: nsresult RemoveLetterFrames(nsPresContext* aPresContext, michael@0: nsIPresShell* aPresShell, michael@0: nsIFrame* aBlockFrame); michael@0: michael@0: // Recursive helper for RemoveLetterFrames michael@0: nsresult RemoveFirstLetterFrames(nsPresContext* aPresContext, michael@0: nsIPresShell* aPresShell, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aBlockFrame, michael@0: bool* aStopLooking); michael@0: michael@0: // Special remove method for those pesky floating first-letter frames michael@0: nsresult RemoveFloatingFirstLetterFrames(nsPresContext* aPresContext, michael@0: nsIPresShell* aPresShell, michael@0: nsIFrame* aBlockFrame, michael@0: bool* aStopLooking); michael@0: michael@0: // Capture state for the frame tree rooted at the frame associated with the michael@0: // content object, aContent michael@0: void CaptureStateForFramesOf(nsIContent* aContent, michael@0: nsILayoutHistoryState* aHistoryState); michael@0: michael@0: //---------------------------------------- michael@0: michael@0: // Methods support :first-line style michael@0: michael@0: // This method chops the initial inline-outside frames out of aFrameItems. michael@0: // If aLineFrame is non-null, it appends them to that frame. Otherwise, it michael@0: // creates a new line frame, sets the inline frames as its initial child michael@0: // list, and inserts that line frame at the front of what's left of michael@0: // aFrameItems. In both cases, the kids are reparented to the line frame. michael@0: // After this call, aFrameItems holds the frames that need to become kids of michael@0: // the block (possibly including line frames). michael@0: void WrapFramesInFirstLineFrame(nsFrameConstructorState& aState, michael@0: nsIContent* aBlockContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsIFrame* aLineFrame, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: // Handle the case when a block with first-line style is appended to (by michael@0: // possibly calling WrapFramesInFirstLineFrame as needed). michael@0: void AppendFirstLineFrames(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: nsresult InsertFirstLineFrames(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsIFrame** aParentFrame, michael@0: nsIFrame* aPrevSibling, michael@0: nsFrameItems& aFrameItems); michael@0: michael@0: // Find the right frame to use for aContent when looking for sibling michael@0: // frames for aTargetContent. If aPrevSibling is true, this michael@0: // will look for last continuations, etc, as necessary. This calls michael@0: // IsValidSibling as needed; if that returns false it returns null. michael@0: // michael@0: // @param aTargetContentDisplay the CSS display enum for aTargetContent if michael@0: // already known, UNSET_DISPLAY otherwise. It will be filled in if needed. michael@0: nsIFrame* FindFrameForContentSibling(nsIContent* aContent, michael@0: nsIContent* aTargetContent, michael@0: uint8_t& aTargetContentDisplay, michael@0: bool aPrevSibling); michael@0: michael@0: // Find the ``rightmost'' frame for the content immediately preceding the one michael@0: // aIter points to, following continuations if necessary. aIter is passed by michael@0: // value on purpose, so as not to modify the caller's iterator. michael@0: nsIFrame* FindPreviousSibling(mozilla::dom::FlattenedChildIterator aIter, michael@0: uint8_t& aTargetContentDisplay); michael@0: michael@0: // Find the frame for the content node immediately following the one aIter michael@0: // points to, following continuations if necessary. aIter is passed by value michael@0: // on purpose, so as not to modify the caller's iterator. michael@0: nsIFrame* FindNextSibling(mozilla::dom::FlattenedChildIterator aIter, michael@0: uint8_t& aTargetContentDisplay); michael@0: michael@0: // Find the right previous sibling for an insertion. This also updates the michael@0: // parent frame to point to the correct continuation of the parent frame to michael@0: // use, and returns whether this insertion is to be treated as an append. michael@0: // aChild is the child being inserted. michael@0: // aIsRangeInsertSafe returns whether it is safe to do a range insert with michael@0: // aChild being the first child in the range. It is the callers' michael@0: // responsibility to check whether a range insert is safe with regards to michael@0: // fieldsets. michael@0: // The skip parameters are used to ignore a range of children when looking michael@0: // for a sibling. All nodes starting from aStartSkipChild and up to but not michael@0: // including aEndSkipChild will be skipped over when looking for sibling michael@0: // frames. Skipping a range can deal with XBL but not when there are multiple michael@0: // insertion points. michael@0: nsIFrame* GetInsertionPrevSibling(nsIFrame*& aParentFrame, /* inout */ michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: bool* aIsAppend, michael@0: bool* aIsRangeInsertSafe, michael@0: nsIContent* aStartSkipChild = nullptr, michael@0: nsIContent *aEndSkipChild = nullptr); michael@0: michael@0: // see if aContent and aSibling are legitimate siblings due to restrictions michael@0: // imposed by table columns michael@0: // XXXbz this code is generally wrong, since the frame for aContent michael@0: // may be constructed based on tag, not based on aDisplay! michael@0: bool IsValidSibling(nsIFrame* aSibling, michael@0: nsIContent* aContent, michael@0: uint8_t& aDisplay); michael@0: michael@0: void QuotesDirty() { michael@0: NS_PRECONDITION(mUpdateCount != 0, "Instant quote updates are bad news"); michael@0: mQuotesDirty = true; michael@0: mDocument->SetNeedLayoutFlush(); michael@0: } michael@0: michael@0: void CountersDirty() { michael@0: NS_PRECONDITION(mUpdateCount != 0, "Instant counter updates are bad news"); michael@0: mCountersDirty = true; michael@0: mDocument->SetNeedLayoutFlush(); michael@0: } michael@0: michael@0: /** michael@0: * Add the pair (aContent, aStyleContext) to the undisplayed items michael@0: * in aList as needed. This method enforces the invariant that all michael@0: * style contexts in the undisplayed content map must be non-pseudo michael@0: * contexts and also handles unbinding undisplayed generated content michael@0: * as needed. michael@0: */ michael@0: static void SetAsUndisplayedContent(FrameConstructionItemList& aList, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: bool aIsGeneratedContent); michael@0: michael@0: public: michael@0: michael@0: friend class nsFrameConstructorState; michael@0: michael@0: private: michael@0: michael@0: nsIDocument* mDocument; // Weak ref michael@0: michael@0: // See the comment at the start of ConstructRootFrame for more details michael@0: // about the following frames. michael@0: michael@0: // This is just the outermost frame for the root element. michael@0: nsIFrame* mRootElementFrame; michael@0: // This is the frame for the root element that has no pseudo-element style. michael@0: nsIFrame* mRootElementStyleFrame; michael@0: // This is the containing block for fixed-pos frames --- the michael@0: // viewport or page frame michael@0: nsIFrame* mFixedContainingBlock; michael@0: // This is the containing block that contains the root element --- michael@0: // the real "initial containing block" according to CSS 2.1. michael@0: nsIFrame* mDocElementContainingBlock; michael@0: nsIFrame* mGfxScrollFrame; michael@0: nsIFrame* mPageSequenceFrame; michael@0: nsQuoteList mQuoteList; michael@0: nsCounterManager mCounterManager; michael@0: // Current ProcessChildren depth. michael@0: uint16_t mCurrentDepth; michael@0: uint16_t mUpdateCount; michael@0: bool mQuotesDirty : 1; michael@0: bool mCountersDirty : 1; michael@0: bool mIsDestroyingFrameTree : 1; michael@0: // This is true if mDocElementContainingBlock supports absolute positioning michael@0: bool mHasRootAbsPosContainingBlock : 1; michael@0: bool mAlwaysCreateFramesForIgnorableWhitespace : 1; michael@0: michael@0: nsCOMPtr mTempFrameTreeState; michael@0: }; michael@0: michael@0: #endif /* nsCSSFrameConstructor_h___ */