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