diff options
Diffstat (limited to '')
-rw-r--r-- | layout/base/nsCSSFrameConstructor.h | 2168 |
1 files changed, 2168 insertions, 0 deletions
diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h new file mode 100644 index 0000000000..b302543a42 --- /dev/null +++ b/layout/base/nsCSSFrameConstructor.h @@ -0,0 +1,2168 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/ArenaAllocator.h" +#include "mozilla/Attributes.h" +#include "mozilla/ContainStyleScopeManager.h" +#include "mozilla/FunctionRef.h" +#include "mozilla/LinkedList.h" +#include "mozilla/Maybe.h" +#include "mozilla/ScrollStyles.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/PresShell.h" + +#include "nsCOMPtr.h" +#include "nsILayoutHistoryState.h" +#include "nsIAnonymousContentCreator.h" +#include "nsFrameManager.h" +#include "nsIFrame.h" + +struct nsStyleDisplay; +struct nsGenConInitializer; + +class nsBlockFrame; +class nsContainerFrame; +class nsFirstLineFrame; +class nsFirstLetterFrame; +class nsCSSAnonBoxPseudoStaticAtom; +class nsPageSequenceFrame; + +class nsPageContentFrame; + +class nsFrameConstructorState; + +namespace mozilla { + +class ComputedStyle; +class PresShell; +class PrintedSheetFrame; +class RestyleManager; + +namespace dom { + +class CharacterData; +class Text; +class FlattenedChildIterator; + +} // namespace dom +} // namespace mozilla + +class nsCSSFrameConstructor final : public nsFrameManager { + public: + using ComputedStyle = mozilla::ComputedStyle; + using PseudoStyleType = mozilla::PseudoStyleType; + using PresShell = mozilla::PresShell; + using Element = mozilla::dom::Element; + using Text = mozilla::dom::Text; + + // FIXME(emilio): Is this really needed? + friend class mozilla::RestyleManager; + + nsCSSFrameConstructor(mozilla::dom::Document* aDocument, + PresShell* aPresShell); + ~nsCSSFrameConstructor() { MOZ_ASSERT(mFCItemsInUse == 0); } + + static void GetAlternateTextFor(const Element&, nsAString& aAltText); + + private: + nsCSSFrameConstructor(const nsCSSFrameConstructor& aCopy) = delete; + nsCSSFrameConstructor& operator=(const nsCSSFrameConstructor& aCopy) = delete; + + public: + /** + * Whether insertion should be done synchronously or asynchronously. + * + * Generally, insertion is synchronous if we're entering frame construction + * from restyle processing, and async if we're removing stuff, or need to + * reconstruct some ancestor. + * + * Note that constructing async from frame construction will post a restyle + * event, but won't need another whole refresh driver tick to go in. Instead + * change hint processing will keep going as long as there are changes in the + * queue. + */ + enum class InsertionKind { + Sync, + Async, + }; + + mozilla::RestyleManager* RestyleManager() const { + return mPresShell->GetPresContext()->RestyleManager(); + } + + nsIFrame* ConstructRootFrame(); + + private: + enum Operation { CONTENTAPPEND, CONTENTINSERT }; + + // aChild is the child being inserted for inserts, and the first + // child being appended for appends. + void ConstructLazily(Operation aOperation, nsIContent* aChild); + +#ifdef DEBUG + void CheckBitsForLazyFrameConstruction(nsIContent* aParent); +#else + void CheckBitsForLazyFrameConstruction(nsIContent*) {} +#endif + + // Issues a single ContentInserted for each child in the range + // [aStartChild, aEndChild). + void IssueSingleInsertNofications(nsIContent* aStartChild, + nsIContent* aEndChild, InsertionKind); + + /** + * Data that represents an insertion point for some child content. + */ + struct InsertionPoint { + InsertionPoint() : mParentFrame(nullptr), mContainer(nullptr) {} + + InsertionPoint(nsContainerFrame* aParentFrame, nsIContent* aContainer) + : mParentFrame(aParentFrame), mContainer(aContainer) {} + + /** + * 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; + + /** + * Whether it is required to insert children one-by-one instead of as a + * range. + */ + bool IsMultiple() const; + }; + + /** + * Checks if the children 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* aStartChild, + nsIContent* aEndChild, InsertionKind); + + // Returns true if parent was recreated due to frameset child, false + // otherwise. + bool MaybeRecreateForFrameset(nsIFrame* aParentFrame, nsIContent* aStartChild, + nsIContent* aEndChild); + + /** + * For each child in the aStartChild/aEndChild range, calls + * NoteDirtyDescendantsForServo on their flattened tree parents. This is + * used when content is inserted into the document and we decide that + * we can do lazy frame construction. It handles children being rebound to + * different insertion points by calling NoteDirtyDescendantsForServo on each + * child's flattened tree parent. Only used when we are styled by Servo. + */ + void LazilyStyleNewChildRange(nsIContent* aStartChild, nsIContent* aEndChild); + + /** + * For each child in the aStartChild/aEndChild range, calls StyleNewChildren + * on their flattened tree parents. This is used when content is inserted + * into the document and we decide that we cannot do lazy frame construction. + * It handles children being rebound to different insertion points by calling + * StyleNewChildren on each child's flattened tree parent. Only used when we + * are styled by Servo. + */ + void StyleNewChildRange(nsIContent* aStartChild, nsIContent* aEndChild); + + public: + /** + * Lazy frame construction is controlled by the InsertionKind 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. + * + * If we construct lazily, then we add 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 RestyleManager walks the dirty nodes during + * ProcessPostTraversal, and ends up calling Content{Appended,Inserted} with + * InsertionKind::Sync in ProcessRestyledFrames. + * + * 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 + * reconstruct frames. (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 the insertion kind is Async then frame construction of the new children + // can be done lazily. + void ContentAppended(nsIContent* aFirstNewContent, InsertionKind); + + // If the insertion kind is Async then frame construction of the new child + // can be done lazily. + void ContentInserted(nsIContent* aChild, InsertionKind); + + // Like ContentInserted but handles inserting the children in the range + // [aStartChild, aEndChild). aStartChild must be non-null. aEndChild may be + // null to indicate the range includes all kids after aStartChild. + // + // If aInsertionKind is Async then frame construction of the new children can + // be done lazily. It is only allowed to be Async when inserting a single + // node. + void ContentRangeInserted(nsIContent* aStartChild, nsIContent* aEndChild, + InsertionKind aInsertionKind); + + enum RemoveFlags { + REMOVE_CONTENT, + REMOVE_FOR_RECONSTRUCTION, + }; + + /** + * Recreate or destroy frames for aChild. + * + * 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 + * frames on some ancestor of aChild. This can happen regardless of the value + * of aFlags. + * + * The return value indicates whether this "reconstruct an ancestor" action + * took place. If true is returned, that means that the frame subtree rooted + * at some ancestor of aChild's frame was destroyed and will be reconstructed + * async. + */ + bool ContentRemoved(nsIContent* aChild, nsIContent* aOldNextSibling, + RemoveFlags aFlags); + + void CharacterDataChanged(nsIContent* aContent, + const 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), ensure that + // a frame for it is created the next time frames are flushed, if it can + // possibly have a frame at all. + // + // Returns whether there are chances for the frame to be unsuppressed. + bool EnsureFrameForTextNodeIsCreatedAfterFlush( + mozilla::dom::CharacterData* aContent); + + // Should be called when a frame is going to be destroyed and + // WillDestroyFrameTree hasn't been called yet. + void NotifyDestroyingFrame(nsIFrame* aFrame); + + 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. + * + * Returns whether a reconstruct was posted for any ancestor. + */ + bool DestroyFramesFor(nsIContent* aContent); + + // Request to create a continuing frame. This method never returns null. + nsIFrame* CreateContinuingFrame(nsIFrame* aFrame, + nsContainerFrame* aParentFrame, + bool aIsFluid = true); + + void SetNextPageContentFramePageName(const nsAtom* aAtom) { + MOZ_ASSERT(!mNextPageContentFramePageName, + "PageContentFrame page name was already set"); + mNextPageContentFramePageName = aAtom; + } + + // Copy over fixed frames from aParentFrame's prev-in-flow + nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame); + + /** + * Get the insertion point for aChild. + */ + InsertionPoint GetInsertionPoint(nsIContent* aChild); + + /** + * Return the insertion frame of the primary frame of aContent, or its nearest + * ancestor that isn't display:contents. + */ + nsContainerFrame* GetContentInsertionFrameFor(nsIContent* aContent); + + // 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; } + nsPageSequenceFrame* GetPageSequenceFrame() { return mPageSequenceFrame; } + + // Get the frame that is the parent of the root element. + nsContainerFrame* GetDocElementContainingBlock() { + return mDocElementContainingBlock; + } + + void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const; + +#if defined(ACCESSIBILITY) || defined(MOZ_LAYOUT_DEBUGGER) + // Exposed only for nsLayoutUtils::GetMarkerSpokenText and + // nsLayoutDebuggingTools to use. + mozilla::ContainStyleScopeManager& GetContainStyleScopeManager() { + return mContainStyleScopeManager; + } +#endif + + private: + struct FrameConstructionItem; + class FrameConstructionItemList; + + // Set the root element frame, and create frames for anonymous content if + // there is a canvas frame. + // + // It's important to do this _before_ constructing the children of the root + // element, because XUL popups depend on the anonymous root popupgroup being + // constructed already. + void SetRootElementFrameAndConstructCanvasAnonContent( + nsContainerFrame* aRootElementFrame, nsFrameConstructorState&, + nsFrameList&); + + mozilla::PrintedSheetFrame* ConstructPrintedSheetFrame( + PresShell* aPresShell, nsContainerFrame* aParentFrame, + nsIFrame* aPrevSheetFrame); + + nsContainerFrame* ConstructPageFrame(PresShell* aPresShell, + nsContainerFrame* aParentFrame, + nsIFrame* aPrevPageFrame, + nsContainerFrame*& aCanvasFrame); + + void InitAndRestoreFrame(const nsFrameConstructorState& aState, + nsIContent* aContent, nsContainerFrame* aParentFrame, + nsIFrame* aNewFrame, bool aAllowCounters = true); + + already_AddRefed<ComputedStyle> ResolveComputedStyle(nsIContent* aContent); + + enum class ItemFlag : uint8_t { + // Allow page-break before and after items to be created if the + // style asks for them. + AllowPageBreak, + IsGeneratedContent, + IsWithinSVGText, + // The item allows items to be created for SVG <textPath> children. + AllowTextPathChild, + // The item is content created by an nsIAnonymousContentCreator frame. + IsAnonymousContentCreatorContent, + // The item will be the rendered legend of a <fieldset>. + IsForRenderedLegend, + // This will be an outside ::marker. + IsForOutsideMarker, + }; + + using ItemFlags = mozilla::EnumSet<ItemFlag>; + + // 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 ComputedStyle& aParentStyle, + const InsertionPoint& aInsertion, + FrameConstructionItemList& aItems, + ItemFlags = {}); + + // 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); + + // Construct the frames for the document element. This can return null if the + // document element is display:none, or if it's an SVG element that's not + // <svg>, etc. + nsIFrame* ConstructDocElementFrame(Element* aDocElement); + + // 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 (that + * is, the originating element). + * @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 aComputedStyle the style 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 + */ + void CreateAttributeContent(const Element& aParentContent, + nsIFrame* aParentFrame, int32_t aAttrNamespace, + nsAtom* aAttrName, ComputedStyle* aComputedStyle, + 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 nsAString& aString, + mozilla::UniquePtr<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 aOriginatingElement is the node that has the before/after style. + * @param aComputedStyle is the 'before' or 'after' pseudo-element style. + * @param aContentIndex is the index of the content item to create. + * @param aAddChild callback to be called for each generated content child. + */ + void CreateGeneratedContent( + nsFrameConstructorState& aState, Element& aOriginatingElement, + ComputedStyle& aPseudoStyle, uint32_t aContentIndex, + const mozilla::FunctionRef<void(nsIContent*)> aAddChild); + + /** + * Create child content nodes for a ::marker from its 'list-style-*' values. + */ + void CreateGeneratedContentFromListStyle( + nsFrameConstructorState& aState, Element& aOriginatingElement, + const ComputedStyle& aPseudoStyle, + const mozilla::FunctionRef<void(nsIContent*)> aAddChild); + /** + * Create child content nodes for a ::marker from its 'list-style-type'. + */ + void CreateGeneratedContentFromListStyleType( + nsFrameConstructorState& aState, Element& aOriginatingElement, + const ComputedStyle& aPseudoStyle, + const mozilla::FunctionRef<void(nsIContent*)> aAddChild); + + // aParentFrame may be null; this method doesn't use it directly in any case. + void CreateGeneratedContentItem(nsFrameConstructorState& aState, + nsContainerFrame* aParentFrame, + Element& aOriginatingElement, ComputedStyle&, + PseudoStyleType aPseudoElement, + FrameConstructionItemList& aItems, + ItemFlags aExtraFlags = {}); + + // This method is called by ContentAppended() and ContentRangeInserted() when + // appending flowed frames to a parent's principal child list. It handles the + // case where the parent is the trailing inline of an ib-split or is the last + // continuation of a ::-moz-column-content in an nsColumnSetFrame. + // + // This method can change aFrameList: it can chop off the beginning and put it + // in aParentFrame while either putting the remainder into an ib-split sibling + // of aParentFrame or creating aParentFrame's column-span siblings for the + // remainder. + // + // 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. + void AppendFramesToParent(nsFrameConstructorState& aState, + nsContainerFrame* aParentFrame, + nsFrameList& 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, + nsFrameList& aFrameList); + + /** + * FrameConstructionData callback for constructing table rows and row groups. + */ + nsIFrame* ConstructTableRowOrRowGroup(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + /** + * FrameConstructionData callback used for constructing table columns. + */ + nsIFrame* ConstructTableCol(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + /** + * FrameConstructionData callback used for constructing table cells. + */ + nsIFrame* ConstructTableCell(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + 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->Type()); + } + + /* Get the parent type for the given LayoutFrameType */ + static ParentType GetParentType(mozilla::LayoutFrameType 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 PresShell the presshell whose arena should be used to allocate + the frame. + @param ComputedStyle the style to use for the frame. */ + using FrameCreationFunc = nsIFrame* (*)(PresShell*, ComputedStyle*); + using ContainerFrameCreationFunc = nsContainerFrame* (*)(PresShell*, + ComputedStyle*); + using BlockFrameCreationFunc = nsBlockFrame* (*)(PresShell*, ComputedStyle*); + + /* 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 ComputedStyle the style to be used for the frame. + */ + struct FrameConstructionData; + using FrameConstructionDataGetter = + const FrameConstructionData* (*)(const Element&, ComputedStyle&); + + /* 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 aFrameList (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 aFrameList? 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 mComputedStyle + @param aFrameList 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. + */ + using FrameFullConstructor = + nsIFrame* (nsCSSFrameConstructor::*)(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + /* 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 + /* 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 + /* 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 of a block (i.e. allow ::first-letter/line). + 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 + /** + * If FCDATA_ALLOW_GRID_FLEX_COLUMN is set, then we should create a + * grid/flex/column container instead of a block wrapper when the styles says + * so. This bit is meaningful only if FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS + * is also set. + */ +#define FCDATA_ALLOW_GRID_FLEX_COLUMN 0x200000 + /** + * Whether the kids of this FrameConstructionData should be flagged as having + * a wrapper anon box parent. This should only be set if + * FCDATA_USE_CHILD_ITEMS is set. + */ +#define FCDATA_IS_WRAPPER_ANON_BOX 0x400000 + + /* Structure representing information about how a frame should be + constructed. */ + struct FrameConstructionData { + // We have exactly one of three types of functions, so use a union for + // better cache locality. + union Func { + FrameCreationFunc mCreationFunc; + FrameConstructionDataGetter mDataGetter; + FrameFullConstructor mFullConstructor; + + explicit constexpr Func(FrameCreationFunc aFunc) : mCreationFunc(aFunc) {} + explicit constexpr Func(FrameConstructionDataGetter aDataGetter) + : mDataGetter(aDataGetter) {} + explicit constexpr Func(FrameFullConstructor aCtor) + : mFullConstructor(aCtor) {} + } mFunc; + // Flag bits that can modify the way the construction happens + const uint32_t mBits = 0; + // For cases when FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, the + // anonymous box type to use for that wrapper. + PseudoStyleType const mAnonBoxPseudo = PseudoStyleType::NotPseudo; + + constexpr FrameConstructionData() : FrameConstructionData(nullptr) {} + + MOZ_IMPLICIT constexpr FrameConstructionData(std::nullptr_t, + uint32_t aBits = 0) + : mFunc(static_cast<FrameCreationFunc>(nullptr)), mBits(aBits) {} + + MOZ_IMPLICIT constexpr FrameConstructionData( + FrameCreationFunc aCreationFunc, uint32_t aBits = 0) + : mFunc(aCreationFunc), mBits(aBits) {} + constexpr FrameConstructionData(FrameCreationFunc aCreationFunc, + uint32_t aBits, + PseudoStyleType aAnonBoxPseudo) + : mFunc(aCreationFunc), + mBits(aBits | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS), + mAnonBoxPseudo(aAnonBoxPseudo) {} + MOZ_IMPLICIT constexpr FrameConstructionData( + FrameConstructionDataGetter aDataGetter, uint32_t aBits = 0) + : mFunc(aDataGetter), + mBits(aBits | FCDATA_FUNC_IS_DATA_GETTER), + mAnonBoxPseudo(PseudoStyleType::NotPseudo) {} + MOZ_IMPLICIT constexpr FrameConstructionData(FrameFullConstructor aCtor, + uint32_t aBits = 0) + : mFunc(aCtor), + mBits(aBits | FCDATA_FUNC_IS_FULL_CTOR), + mAnonBoxPseudo(PseudoStyleType::NotPseudo) {} + }; + + /* Structure representing a mapping of an atom to a FrameConstructionData. + This can be used with non-static atoms, assuming that the nsAtom* is + stored somewhere that this struct can point to (that is, a static + nsAtom*) and that it's allocated before the struct is ever used. */ + struct FrameConstructionDataByTag { + const nsStaticAtom* 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; + }; + + /* Structure that has a FrameConstructionData and style pseudo-type + for a table pseudo-frame */ + struct PseudoParentData { + const FrameConstructionData mFCData; + mozilla::PseudoStyleType const mPseudoType; + }; + /* Array of such structures that we use to properly construct table + pseudo-frames as needed */ + static const PseudoParentData sPseudoParentData[eParentTypeCount]; + + const FrameConstructionData* FindDataForContent(nsIContent&, ComputedStyle&, + nsIFrame* aParentFrame, + ItemFlags aFlags); + + // aParentFrame might be null. If it is, that means it was an inline frame. + static const FrameConstructionData* FindTextData(const Text&, + nsIFrame* aParentFrame); + const FrameConstructionData* FindElementData(const Element&, ComputedStyle&, + nsIFrame* aParentFrame, + ItemFlags aFlags); + const FrameConstructionData* FindElementTagData(const Element&, + ComputedStyle&, + nsIFrame* aParentFrame, + ItemFlags aFlags); + + /* A function that takes an integer, content, style, 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, const Element&, ComputedStyle&, + const FrameConstructionDataByInt* aDataPtr, uint32_t aDataLength); + + /** + * A function that takes a tag, content, style, 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. In the case that the tags + * actually match, aTagFound will be true, even if the return value is null. + */ + static const FrameConstructionData* FindDataByTag( + const Element& aElement, ComputedStyle& aComputedStyle, + const FrameConstructionDataByTag* aDataPtr, uint32_t aDataLength); + + /* A class representing a list of FrameConstructionItems. Instances of this + class are only created as AutoFrameConstructionItemList, or as a member + of FrameConstructionItem. */ + class FrameConstructionItemList { + public: + void Reset(nsCSSFrameConstructor* aFCtor) { + Destroy(aFCtor); + this->~FrameConstructionItemList(); + new (this) FrameConstructionItemList(); + } + + void SetLineBoundaryAtStart(bool aBoundary) { + mLineBoundaryAtStart = aBoundary; + } + void SetLineBoundaryAtEnd(bool aBoundary) { + mLineBoundaryAtEnd = aBoundary; + } + void SetParentHasNoShadowDOM(bool aValue) { + mParentHasNoShadowDOM = aValue; + } + bool HasLineBoundaryAtStart() { return mLineBoundaryAtStart; } + bool HasLineBoundaryAtEnd() { return mLineBoundaryAtEnd; } + bool ParentHasNoShadowDOM() { return mParentHasNoShadowDOM; } + bool IsEmpty() const { return mItems.isEmpty(); } + 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( + nsCSSFrameConstructor* aFCtor, const FrameConstructionData* aFCData, + nsIContent* aContent, already_AddRefed<ComputedStyle>&& aComputedStyle, + bool aSuppressWhiteSpaceOptimizations) { + FrameConstructionItem* item = new (aFCtor) + FrameConstructionItem(aFCData, aContent, std::move(aComputedStyle), + aSuppressWhiteSpaceOptimizations); + mItems.insertBack(item); + ++mItemCount; + ++mDesiredParentCounts[item->DesiredParentType()]; + return item; + } + + // Arguments are the same as AppendItem(). + FrameConstructionItem* PrependItem( + nsCSSFrameConstructor* aFCtor, const FrameConstructionData* aFCData, + nsIContent* aContent, already_AddRefed<ComputedStyle>&& aComputedStyle, + bool aSuppressWhiteSpaceOptimizations) { + FrameConstructionItem* item = new (aFCtor) + FrameConstructionItem(aFCData, aContent, std::move(aComputedStyle), + aSuppressWhiteSpaceOptimizations); + mItems.insertFront(item); + ++mItemCount; + ++mDesiredParentCounts[item->DesiredParentType()]; + return item; + } + + void InlineItemAdded() { ++mInlineCount; } + void BlockItemAdded() { ++mBlockCount; } + + class Iterator { + public: + explicit Iterator(FrameConstructionItemList& aList) + : mCurrent(aList.mItems.getFirst()), mList(aList) {} + Iterator(const Iterator& aOther) = default; + + 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(nsCSSFrameConstructor* aFCtor, + 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(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd); + + private: + FrameConstructionItem* mCurrent; + FrameConstructionItemList& mList; + }; + + protected: + FrameConstructionItemList() + : mInlineCount(0), + mBlockCount(0), + mItemCount(0), + mLineBoundaryAtStart(false), + mLineBoundaryAtEnd(false), + mParentHasNoShadowDOM(false) { + MOZ_COUNT_CTOR(FrameConstructionItemList); + memset(mDesiredParentCounts, 0, sizeof(mDesiredParentCounts)); + } + + void Destroy(nsCSSFrameConstructor* aFCtor) { + while (FrameConstructionItem* item = mItems.popFirst()) { + item->Delete(aFCtor); + } + } + + // Prevent stack instances (except as AutoFrameConstructionItemList). + friend struct FrameConstructionItem; + ~FrameConstructionItemList() { + MOZ_COUNT_DTOR(FrameConstructionItemList); + MOZ_ASSERT(mItems.isEmpty(), "leaking"); + } + + private: + // Not allocated from the heap! + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; +#ifdef _MSC_VER /* Visual Studio */ + void operator delete(void*) { MOZ_CRASH("FrameConstructionItemList::del"); } +#else + void operator delete(void*) = delete; +#endif + void operator delete[](void*) = delete; + // Placement new is used by Reset(). + void* operator new(size_t, void* aPtr) { return aPtr; } + + struct UndisplayedItem { + UndisplayedItem(nsIContent* aContent, ComputedStyle* aComputedStyle) + : mContent(aContent), mComputedStyle(aComputedStyle) {} + + nsIContent* const mContent; + RefPtr<ComputedStyle> mComputedStyle; + }; + + // 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 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 shadow tree. + bool mParentHasNoShadowDOM; + }; + + /* A struct representing a list of FrameConstructionItems on the stack. */ + struct MOZ_RAII AutoFrameConstructionItemList final + : public FrameConstructionItemList { + template <typename... Args> + explicit AutoFrameConstructionItemList(nsCSSFrameConstructor* aFCtor, + Args&&... args) + : FrameConstructionItemList(std::forward<Args>(args)...), + mFCtor(aFCtor) { + MOZ_ASSERT(mFCtor); + } + ~AutoFrameConstructionItemList() { Destroy(mFCtor); } + + private: + nsCSSFrameConstructor* const mFCtor; + }; + + 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. You probably want to use + * AutoFrameConstructionItem instead of this struct. */ + struct FrameConstructionItem final + : public mozilla::LinkedListElement<FrameConstructionItem> { + FrameConstructionItem(const FrameConstructionData* aFCData, + nsIContent* aContent, + already_AddRefed<ComputedStyle>&& aComputedStyle, + bool aSuppressWhiteSpaceOptimizations) + : mFCData(aFCData), + mContent(aContent), + mComputedStyle(std::move(aComputedStyle)), + mSuppressWhiteSpaceOptimizations(aSuppressWhiteSpaceOptimizations), + mIsText(false), + mIsGeneratedContent(false), + mIsAllInline(false), + mIsBlock(false), + mIsPopup(false), + mIsLineParticipant(false), + mIsRenderedLegend(false) { + MOZ_COUNT_CTOR(FrameConstructionItem); + } + + void* operator new(size_t, nsCSSFrameConstructor* aFCtor) { + return aFCtor->AllocateFCItem(); + } + + void Delete(nsCSSFrameConstructor* aFCtor) { + mChildItems.Destroy(aFCtor); + if (mIsGeneratedContent) { + mContent->UnbindFromTree(); + NS_RELEASE(mContent); + } + this->~FrameConstructionItem(); + aFCtor->FreeFCItem(this); + } + + 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; + + // The FrameConstructionData to use. + const FrameConstructionData* mFCData; + // The nsIContent node to use when initializing the new frame. + nsIContent* mContent; + // The style to use for creating the new frame. + RefPtr<ComputedStyle> mComputedStyle; + // 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 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 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 the rendered legend of a <fieldset> + bool mIsRenderedLegend : 1; + + private: + // Not allocated from the general heap - instead, use the new/Delete APIs + // that take a nsCSSFrameConstructor* (which manages our arena allocation). + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; +#ifdef _MSC_VER /* Visual Studio */ + void operator delete(void*) { MOZ_CRASH("FrameConstructionItem::delete"); } +#else + void operator delete(void*) = delete; +#endif + void operator delete[](void*) = delete; + FrameConstructionItem(const FrameConstructionItem& aOther) = delete; + // Not allocated from the stack! + ~FrameConstructionItem() { + MOZ_COUNT_DTOR(FrameConstructionItem); + MOZ_ASSERT(mChildItems.IsEmpty(), "leaking"); + } + }; + + /** + * Convenience struct to assist in managing a temporary FrameConstructionItem + * using a local variable. Castable to FrameConstructionItem so that it can + * be passed transparently to functions that expect that type. + * (This struct exists because FrameConstructionItem is arena-allocated, and + * it's nice to abstract away its allocation/deallocation.) + */ + struct MOZ_RAII AutoFrameConstructionItem final { + template <typename... Args> + explicit AutoFrameConstructionItem(nsCSSFrameConstructor* aFCtor, + Args&&... args) + : mFCtor(aFCtor), + mItem(new(aFCtor) + FrameConstructionItem(std::forward<Args>(args)...)) { + MOZ_ASSERT(mFCtor); + } + ~AutoFrameConstructionItem() { mItem->Delete(mFCtor); } + operator FrameConstructionItem&() { return *mItem; } + + private: + nsCSSFrameConstructor* const mFCtor; + FrameConstructionItem* const mItem; + }; + + /** + * Updates the nsFrameConstructorState auto page-name value, and restores the + * previous value on destruction. + * See https://drafts.csswg.org/css-page-3/#using-named-pages + * + * To track this, this will automatically add PageValuesProperty to + * the frame. + * + * Note that this does not add PageValuesProperty to the frame when not in a + * paginated context, or if layout.css.named_pages.enabled is set to false. + */ + class MOZ_RAII AutoFrameConstructionPageName final { + nsFrameConstructorState& mState; + const nsAtom* mNameToRestore; + + public: + AutoFrameConstructionPageName(const AutoFrameConstructionPageName&) = + delete; + AutoFrameConstructionPageName(AutoFrameConstructionPageName&&) = delete; + AutoFrameConstructionPageName(nsFrameConstructorState& aState, + nsIFrame* const aFrame); + ~AutoFrameConstructionPageName(); + }; + + /** + * 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, + ComputedStyle* aParentStyle, + nsIContent* aParentContent); + + /** + * Function to wrap consecutive misparented items + * into a ruby level container. + */ + inline void WrapItemsInPseudoRubyLevelContainer( + nsFrameConstructorState& aState, FCItemIterator& aIter, + ComputedStyle* 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, + ComputedStyle* 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); + + // END TABLE SECTION + + protected: + static nsIFrame* CreatePlaceholderFrameFor(PresShell* aPresShell, + nsIContent* aContent, + nsIFrame* aFrame, + nsContainerFrame* aParentFrame, + nsIFrame* aPrevInFlow, + nsFrameState aTypeBit); + + private: + // ConstructSelectFrame puts the new frame in aFrameList and + // handles the kids of the select. + nsIFrame* ConstructSelectFrame(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + // ConstructFieldSetFrame puts the new frame in aFrameList and + // handles the kids of the fieldset + nsIFrame* ConstructFieldSetFrame(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + // <details> always creates a block per spec. + nsIFrame* ConstructDetails(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + // Creates a block frame wrapping an anonymous ruby frame. + nsIFrame* ConstructBlockRubyFrame(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aStyleDisplay, + nsFrameList& aFrameList); + + void ConstructTextFrame(const FrameConstructionData* aData, + nsFrameConstructorState& aState, nsIContent* aContent, + nsContainerFrame* aParentFrame, + ComputedStyle* aComputedStyle, + nsFrameList& aFrameList); + + // 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 ComputedStyle& aParentStyle, + const InsertionPoint& aInsertion, + nsIContent* aPossibleTextContent, + FrameConstructionItemList& aItems); + + // If aContent is a text node and doesn't have a frame, try to create a frame + // for it. + void ReframeTextIfNeeded(nsIContent* aContent); + + enum InsertPageBreakLocation { eBefore, eAfter }; + inline void AppendPageBreakItem(nsIContent* aContent, + FrameConstructionItemList& aItems) { + InsertPageBreakItem(aContent, aItems, InsertPageBreakLocation::eAfter); + } + inline void PrependPageBreakItem(nsIContent* aContent, + FrameConstructionItemList& aItems) { + InsertPageBreakItem(aContent, aItems, InsertPageBreakLocation::eBefore); + } + void InsertPageBreakItem(nsIContent* aContent, + FrameConstructionItemList& aItems, + InsertPageBreakLocation location); + + // 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(const Element&, + nsIFrame* aParentFrame, + ComputedStyle&); + // HTML data-finding helper functions + static const FrameConstructionData* FindImgData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindGeneratedImageData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindImgControlData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindSearchControlData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindInputData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindObjectData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindCanvasData(const Element&, + ComputedStyle&); + + /* 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 aFrameList the frame list to add the new frame (or its + placeholder) to. + */ + void ConstructFrameFromItemInternal(FrameConstructionItem& aItem, + nsFrameConstructorState& aState, + nsContainerFrame* aParentFrame, + nsFrameList& aFrameList); + + // 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, + bool aSuppressWhiteSpaceOptimizations, + ComputedStyle*, ItemFlags, + FrameConstructionItemList& aItems); + + /** + * Construct frames for the given item list and parent frame, and put the + * resulting frames in aFrameList. + */ + void ConstructFramesFromItemList(nsFrameConstructorState& aState, + FrameConstructionItemList& aItems, + nsContainerFrame* aParentFrame, + bool aParentIsWrapperAnonBox, + nsFrameList& aFrameList); + void ConstructFramesFromItem(nsFrameConstructorState& aState, + FCItemIterator& aItem, + nsContainerFrame* aParentFrame, + nsFrameList& aFrameList); + static bool AtLineBoundary(FCItemIterator& aIter); + + nsresult GetAnonymousContent( + nsIContent* aParent, nsIFrame* aParentFrame, + nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonContent); + + // MathML Mod - RBS + /** + * Takes the frames in aBlockList 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 aNewList and aBlockList is cleared. + */ + void FlushAccumulatedBlock(nsFrameConstructorState& aState, + nsIContent* aContent, + nsContainerFrame* aParentFrame, + nsFrameList& aBlockList, nsFrameList& aNewList); + + // Function to find FrameConstructionData for an element. Will return + // null if the element is not MathML. + static const FrameConstructionData* FindMathMLData(const Element&, + ComputedStyle&); + + // Function to find FrameConstructionData for an element. Will return + // null if the element is not XUL. + static const FrameConstructionData* FindXULTagData(const Element&, + ComputedStyle&); + // XUL data-finding helper functions and structures + static const FrameConstructionData* FindPopupGroupData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindXULButtonData(const Element&, + ComputedStyle&); + static const FrameConstructionData* FindXULLabelOrDescriptionData( + const Element&, ComputedStyle&); +#ifdef XP_MACOSX + static const FrameConstructionData* FindXULMenubarData(const Element&, + ComputedStyle&); +#endif /* XP_MACOSX */ + + /** + * 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, nsFrameList& aFrameList, + ContainerFrameCreationFunc aConstructor, + ContainerFrameCreationFunc aInnerConstructor, + mozilla::PseudoStyleType aInnerPseudo, bool aCandidateRootFrame); + + /** + * Construct an SVGOuterSVGFrame. + */ + nsIFrame* ConstructOuterSVG(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aDisplay, + nsFrameList& aFrameList); + + /** + * Construct an SVGMarkerFrame. + */ + nsIFrame* ConstructMarker(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aDisplay, + nsFrameList& aFrameList); + + static const FrameConstructionData* FindSVGData(const Element&, + nsIFrame* aParentFrame, + bool aIsWithinSVGText, + bool aAllowsTextPathChild, + ComputedStyle&); + + // Not static because it does PropagateScrollToViewport. If this + // changes, make this static. + const FrameConstructionData* FindDisplayData(const nsStyleDisplay&, + mozilla::StyleMozBoxLayout, + const Element&); + + /** + * Construct a scrollable block frame + */ + nsIFrame* ConstructScrollableBlock(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aDisplay, + nsFrameList& aFrameList); + + /** + * Construct a non-scrollable block frame + */ + nsIFrame* ConstructNonScrollableBlock(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aDisplay, + nsFrameList& aFrameList); + + /** + * This adds FrameConstructionItem objects to aItemsToConstruct for the + * anonymous content returned by an nsIAnonymousContentCreator:: + * CreateAnonymousContent implementation. + * This includes an AutoFrameConstructionPageName argument as it is always + * the caller's responsibility to handle page-name tracking before calling + * this function. + */ + void AddFCItemsForAnonymousContent( + nsFrameConstructorState& aState, nsContainerFrame* aFrame, + const nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems, + FrameConstructionItemList& aItemsToConstruct, + const AutoFrameConstructionPageName& aUnusedPageNameTracker); + + /** + * 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": + * + * https://drafts.csswg.org/css-scoping/#flat-tree + * + * 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 aComputedStyle the style 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 for the kids' style + * (not necessary aFrame's style). + * @param aCanHaveGeneratedContent Whether to allow :before and + * :after styles on the parent. + * @param aFrameList 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 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, + ComputedStyle* aComputedStyle, + nsContainerFrame* aParentFrame, + const bool aCanHaveGeneratedContent, + nsFrameList& aFrameList, const bool aAllowBlockStyles, + 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, + ComputedStyle* aContentStyle, nsIFrame* aScrolledFrame, + nsContainerFrame* aParentFrame, + nsContainerFrame*& aNewFrame); + + // Builds the initial ScrollFrame + already_AddRefed<ComputedStyle> BeginBuildingScrollFrame( + nsFrameConstructorState& aState, nsIContent* aContent, + ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame, + mozilla::PseudoStyleType 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); + + void InitializeListboxSelect(nsFrameConstructorState& aState, + nsContainerFrame* aScrollFrame, + nsContainerFrame* aScrolledFrame, + nsIContent* aContent, + nsContainerFrame* aParentFrame, + ComputedStyle* aComputedStyle, + nsFrameList& aFrameList); + + /** + * Recreate frames for aContent. + * @param aContent the content to recreate frames for + * @param aFlags normally you want to pass REMOVE_FOR_RECONSTRUCTION here + */ + void RecreateFramesForContent(nsIContent* aContent, + InsertionKind aInsertionKind); + + /** + * Handles change of rowspan and colspan attributes on table cells. + */ + void UpdateTableCellSpans(nsIContent* aContent); + + // 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. aFrame must be the result of a + // GetPrimaryFrame() call on a content node (which means its parent is also + // not null). + bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame); + + nsIFrame* CreateContinuingOuterTableFrame(nsIFrame* aFrame, + nsContainerFrame* aParentFrame, + nsIContent* aContent, + ComputedStyle* aComputedStyle); + + nsIFrame* CreateContinuingTableFrame(nsIFrame* aFrame, + nsContainerFrame* aParentFrame, + nsIContent* aContent, + ComputedStyle* aComputedStyle); + + //---------------------------------------- + + // Methods support creating block frames and their children + + already_AddRefed<ComputedStyle> GetFirstLetterStyle( + nsIContent* aContent, ComputedStyle* aComputedStyle); + + already_AddRefed<ComputedStyle> GetFirstLineStyle( + nsIContent* aContent, ComputedStyle* aComputedStyle); + + bool ShouldHaveFirstLetterStyle(nsIContent* aContent, + ComputedStyle* aComputedStyle); + + // 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, + ComputedStyle* aComputedStyle); + + void ShouldHaveSpecialBlockStyle(nsIContent* aContent, + ComputedStyle* aComputedStyle, + bool* aHaveFirstLetterStyle, + bool* aHaveFirstLineStyle); + + // |aContentParentFrame| should be null if it's really the same as + // |aParentFrame|. + // @param aFrameList 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. + void ConstructBlock(nsFrameConstructorState& aState, nsIContent* aContent, + nsContainerFrame* aParentFrame, + nsContainerFrame* aContentParentFrame, + ComputedStyle* aComputedStyle, + nsContainerFrame** aNewFrame, nsFrameList& aFrameList, + nsIFrame* aPositionedFrameForAbsPosContainer); + + // Build the initial column hierarchy around aColumnContent. This function + // should be called before constructing aColumnContent's children. + // + // Before calling FinishBuildingColumns(), we need to create column-span + // siblings for aColumnContent's children. Caller can use helpers + // MayNeedToCreateColumnSpanSiblings() and CreateColumnSpanSiblings() to + // check whether column-span siblings might need to be created and to do + // the actual work of creating them if they're needed. + // + // @param aColumnContent the block that we're wrapping in a ColumnSet. On + // entry to this function it has aComputedStyle as its style. After + // this function returns, aColumnContent has a ::-moz-column-content + // anonymous box style. + // @param aParentFrame the parent frame we want to use for the + // ColumnSetWrapperFrame (which would have been the parent of + // aColumnContent if we were not creating a column hierarchy). + // @param aContent is the content of the aColumnContent. + // @return the outermost ColumnSetWrapperFrame. + nsBlockFrame* BeginBuildingColumns(nsFrameConstructorState& aState, + nsIContent* aContent, + nsContainerFrame* aParentFrame, + nsContainerFrame* aColumnContent, + ComputedStyle* aComputedStyle); + + // Complete building the column hierarchy by first wrapping each + // non-column-span child in aChildList in a ColumnSetFrame (skipping + // column-span children), and reparenting them to have aColumnSetWrapper + // as their parent. + // + // @param aColumnSetWrapper is the frame returned by + // BeginBuildingColumns(), and is the grandparent of aColumnContent. + // @param aColumnContent is the block frame passed into + // BeginBuildingColumns() + // @param aColumnContentSiblings contains the aColumnContent's siblings, which + // are the column spanners and aColumnContent's continuations returned + // by CreateColumnSpanSiblings(). It'll become empty after this call. + void FinishBuildingColumns(nsFrameConstructorState& aState, + nsContainerFrame* aColumnSetWrapper, + nsContainerFrame* aColumnContent, + nsFrameList& aColumnContentSiblings); + + // Return whether aBlockFrame's children in aChildList, which might + // contain column-span, may need to be wrapped in + // ::moz-column-span-wrapper and promoted as aBlockFrame's siblings. + // + // @param aBlockFrame is the parent of the frames in aChildList. + // + // Note: This a check without actually looking into each frame in the + // child list, so it may return false positive. + bool MayNeedToCreateColumnSpanSiblings(nsContainerFrame* aBlockFrame, + const nsFrameList& aChildList); + + // Wrap consecutive runs of column-span kids and runs of non-column-span + // kids in blocks for aInitialBlock's children. + // + // @param aInitialBlock is the parent of those frames in aChildList. + // @param aChildList must begin with a column-span kid. It becomes empty + // after this call. + // @param aPositionedFrame if non-null, it's the frame whose style is making + // aInitialBlock an abs-pos container. + // + // Return those wrapping blocks in nsFrameList. + nsFrameList CreateColumnSpanSiblings(nsFrameConstructorState& aState, + nsContainerFrame* aInitialBlock, + nsFrameList& aChildList, + nsIFrame* aPositionedFrame); + + // Reconstruct the multi-column containing block of aParentFrame when we want + // to insert aFrameList into aParentFrame immediately after aPrevSibling but + // cannot fix the frame tree because aFrameList contains some column-spans. + // + // Note: This method is intended to be called as a helper in ContentAppended() + // and ContentRangeInserted(). It assumes aState was set up locally and wasn't + // used to construct any ancestors of aParentFrame in aFrameList. + // + // @param aParentFrame the to-be parent frame for aFrameList. + // @param aFrameList the frames to be inserted. It will be cleared if we need + // reconstruction. + // @param aPrevSibling the position where the frames in aFrameList are going + // to be inserted. Nullptr means aFrameList is being inserted at + // the beginning. + // @return true if the multi-column containing block of aParentFrame is + // reconstructed; false otherwise. + bool MaybeRecreateForColumnSpan(nsFrameConstructorState& aState, + nsContainerFrame* aParentFrame, + nsFrameList& aFrameList, + nsIFrame* aPrevSibling); + + nsIFrame* ConstructInline(nsFrameConstructorState& aState, + FrameConstructionItem& aItem, + nsContainerFrame* aParentFrame, + const nsStyleDisplay* aDisplay, + nsFrameList& aFrameList); + + /** + * Create any additional {ib} siblings needed to contain aChildList 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 aChildList 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 aChildList + * will be empty. + * @param aSiblings the nsFrameList 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, + nsFrameList& aChildList, nsFrameList& 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 aFrame (the insertion parent) and + // rebuild the entire subtree when we insert or append new content under + // aFrame. + // + // This is similar to WipeContainingBlock(), but is called before constructing + // any frame construction items. Any container frames which need reframing + // regardless of the content inserted or appended can add a check in this + // method. + // + // @return true if we reconstructed the insertion parent frame; false + // otherwise + bool WipeInsertionParent(nsContainerFrame* aFrame); + + // 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); + + void ReframeContainingBlock(nsIFrame* aFrame); + + //---------------------------------------- + + // Methods support :first-letter style + + nsFirstLetterFrame* CreateFloatingLetterFrame( + nsFrameConstructorState& aState, mozilla::dom::Text* aTextContent, + nsIFrame* aTextFrame, nsContainerFrame* aParentFrame, + ComputedStyle* aParentStyle, ComputedStyle* aComputedStyle, + nsFrameList& aResult); + + void CreateLetterFrame(nsContainerFrame* aBlockFrame, + nsContainerFrame* aBlockContinuation, + mozilla::dom::Text* aTextContent, + nsContainerFrame* aParentFrame, nsFrameList& aResult); + + void WrapFramesInFirstLetterFrame(nsContainerFrame* aBlockFrame, + nsFrameList& 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, nsFrameList& aLetterFrames, bool* aStopLooking); + + void RecoverLetterFrames(nsContainerFrame* aBlockFrame); + + void RemoveLetterFrames(PresShell* aPresShell, nsContainerFrame* aBlockFrame); + + // Recursive helper for RemoveLetterFrames + void RemoveFirstLetterFrames(PresShell* aPresShell, nsContainerFrame* aFrame, + nsContainerFrame* aBlockFrame, + bool* aStopLooking); + + // Special remove method for those pesky floating first-letter frames + void RemoveFloatingFirstLetterFrames(PresShell* 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 aFrameList. + // 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 + // aFrameList. In both cases, the kids are reparented to the line frame. + // After this call, aFrameList 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, + nsFrameList& aFrameList); + + // 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, + nsFrameList& aFrameList); + + /** + * When aFrameList is being inserted into aParentFrame, and aParentFrame has + * pseudo-element-affected styles, it's possible that we're inserting under a + * ::first-line frame. In that case, with servo's style system, the styles we + * resolved for aFrameList are wrong (they don't take ::first-line into + * account), and we should fix them up, which is what this method does. + * + * This method does not mutate aFrameList. + */ + void CheckForFirstLineInsertion(nsIFrame* aParentFrame, + nsFrameList& aFrameList); + + /** + * Find the next frame for appending to a given insertion point. + * + * We're appending, so this is almost always null, except for a few edge + * cases. + */ + nsIFrame* FindNextSiblingForAppend(const InsertionPoint&); + + // The direction in which we should look for siblings. + enum class SiblingDirection { + Forward, + Backward, + }; + + /** + * Find the frame for the content immediately next to the one aIter points to, + * in the direction SiblingDirection indicates, following continuations if + * necessary. + * + * aIter is passed by const reference 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 aTargetContentDisplay the CSS display enum for the content aIter + * points to if already known. It will be filled in if needed. + */ + template <SiblingDirection> + nsIFrame* FindSibling( + const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::Maybe<mozilla::StyleDisplay>& aTargetContentDisplay); + + // Helper for the implementation of FindSibling. + // + // Beware that this function does mutate the iterator. + template <SiblingDirection> + nsIFrame* FindSiblingInternal( + mozilla::dom::FlattenedChildIterator&, nsIContent* aTargetContent, + mozilla::Maybe<mozilla::StyleDisplay>& aTargetContentDisplay); + + // An alias of FindSibling<SiblingDirection::Forward>. + nsIFrame* FindNextSibling( + const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::Maybe<mozilla::StyleDisplay>& aTargetContentDisplay); + // An alias of FindSibling<SiblingDirection::Backwards>. + nsIFrame* FindPreviousSibling( + const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::Maybe<mozilla::StyleDisplay>& aTargetContentDisplay); + + // Given a potential first-continuation sibling frame for aTargetContent, + // verify that it is an actual valid sibling for it, and return the + // appropriate continuation the new frame for aTargetContent should be + // inserted next to. + nsIFrame* AdjustSiblingFrame( + nsIFrame* aSibling, nsIContent* aTargetContent, + mozilla::Maybe<mozilla::StyleDisplay>& aTargetContentDisplay, + SiblingDirection aDirection); + + // 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 shadow DOM, 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); + + // 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::Maybe<mozilla::StyleDisplay>& aDisplay); + + void QuotesDirty(); + void CountersDirty(); + + void ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState, + nsContainerFrame* aFrame, + nsIContent* aDocElement, + nsFrameList&); + + public: + friend class nsFrameConstructorState; + + private: + // For allocating FrameConstructionItems from the mFCItemPool arena. + friend struct FrameConstructionItem; + void* AllocateFCItem(); + void FreeFCItem(FrameConstructionItem*); + + mozilla::dom::Document* 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; + nsPageSequenceFrame* mPageSequenceFrame; + + // FrameConstructionItem arena + list of freed items available for re-use. + mozilla::ArenaAllocator<4096, 8> mFCItemPool; + + // This indicates what page name to use for the next nsPageContentFrame. + // Set when CSS named pages cause a breakpoint. + // This does not apply to the first page content frame, which has its name + // set by nsPageContentFrame::EnsurePageName() during first reflow. + RefPtr<const nsAtom> mNextPageContentFramePageName; + + struct FreeFCItemLink { + FreeFCItemLink* mNext; + }; + FreeFCItemLink* mFirstFreeFCItem; + size_t mFCItemsInUse; + + mozilla::ContainStyleScopeManager mContainStyleScopeManager; + + // Current ProcessChildren depth. + uint16_t mCurrentDepth; + bool mQuotesDirty : 1; + bool mCountersDirty : 1; + bool mAlwaysCreateFramesForIgnorableWhitespace : 1; + + // The layout state from our history entry (to restore scroll positions and + // such from history), or a new one if there was none (so we can store scroll + // positions and such during reframe). + // + // FIXME(bug 1397239): This can leak some state sometimes for the lifetime of + // the frame constructor, which is not great. + nsCOMPtr<nsILayoutHistoryState> mFrameTreeState; +}; + +#endif /* nsCSSFrameConstructor_h___ */ |