/* -*- 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
*/
#ifdef DEBUG // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or // more of the following flags (comma separated) for handy debug // output. staticbool gNoisyContentUpdates = false; staticbool gReallyNoisyContentUpdates = false; staticbool gNoisyInlineConstruction = false;
// Returns true if aFrame is an anonymous flex/grid item. staticinlinebool IsAnonymousItem(const nsIFrame* aFrame) { return aFrame->Style()->GetPseudoType() == PseudoStyleType::anonymousItem;
}
// Returns true IFF the given nsIFrame is a nsFlexContainerFrame and represents // a -webkit-{inline-}box container. staticinlinebool IsFlexContainerForLegacyWebKitBox(const nsIFrame* aFrame) { return aFrame->IsFlexContainerFrame() &&
aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
}
#if DEBUG staticvoid AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild, const nsIFrame* aParent) {
MOZ_ASSERT(IsAnonymousItem(aChild), "expected an anonymous item child frame");
MOZ_ASSERT(aParent, "expected a parent frame");
MOZ_ASSERT(aParent->IsFlexOrGridContainer(), "anonymous items should only exist as children of flex/grid " "container frames");
} #else # define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO #endif
/** * True if aFrame is an actual inline frame in the sense of non-replaced * display:inline CSS boxes. In other words, it can be affected by {ib} * splitting and can contain first-letter frames. Basically, this is either an * inline frame (positioned or otherwise) or an line frame (this last because * it can contain first-letter and because inserting blocks in the middle of it * needs to terminate it).
*/ staticbool IsInlineFrame(const nsIFrame* aFrame) { return aFrame->IsLineParticipant();
}
/** * True if aFrame is an instance of an SVG frame class or is an inline/block * frame being used for SVG text.
*/ staticbool IsFrameForSVG(const nsIFrame* aFrame) { return aFrame->IsSVGFrame() || aFrame->IsInSVGTextSubtree();
}
/** * Returns true iff aFrame explicitly prevents its descendants from floating * (at least, down to the level of descendants which themselves are * float-containing blocks -- those will manage the floating status of any * lower-level descendents inside them, of course).
*/ staticbool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) { return aFrame->IsFlexOrGridContainer() || aFrame->IsMathMLFrame();
}
// Return true if column-span descendants should be suppressed under aFrame's // subtree (until a multi-column container re-establishing a block formatting // context). Basically, this is testing whether aFrame establishes a new block // formatting context or not. staticbool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) { if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) { // Never suppress column-span under ::-moz-column-content frames. returnfalse;
}
if (aFrame->IsInlineFrame()) { // Allow inline frames to have column-span block children. returnfalse;
}
if (!aFrame->IsBlockFrameOrSubclass() ||
aFrame->HasAnyStateBits(NS_BLOCK_BFC | NS_FRAME_OUT_OF_FLOW) ||
aFrame->IsFixedPosContainingBlock()) { // Need to suppress column-span if we: // - Are a different block formatting context, // - Are an out-of-flow frame, OR // - Establish a containing block for fixed-position descendants // // For example, the children of a column-span never need to be further // processed even if there is a nested column-span child. Because a // column-span always creates its own block formatting context, a nested // column-span child won't be in the same block formatting context with the // nearest multi-column ancestor. This is the same case as if the // column-span is outside of a multi-column hierarchy. returntrue;
}
returnfalse;
}
// Reparent a frame into a wrapper frame that is a child of its old parent. staticvoid ReparentFrame(RestyleManager* aRestyleManager,
nsContainerFrame* aNewParentFrame, nsIFrame* aFrame, bool aForceStyleReparent) {
aFrame->SetParent(aNewParentFrame); // We reparent frames for two reasons: to put them inside ::first-line, and to // put them inside some wrapper anonymous boxes. if (aForceStyleReparent) {
aRestyleManager->ReparentComputedStyleForFirstLine(aFrame);
}
}
//---------------------------------------------------------------------- // // When inline frames get weird and have block frames in them, we // annotate them to help us respond to incremental content changes // more easily.
staticinlinebool IsFramePartOfIBSplit(nsIFrame* aFrame) { bool result = aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT);
MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) || static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)), "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT"); return result;
}
// We only store the "ib-split sibling" annotation with the first // frame in the continuation chain. Walk back to find that frame now. return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
}
// We only store the ib-split sibling annotation with the first // frame in the continuation chain. Walk back to find that frame now. return aFrame->FirstContinuation()->GetProperty(
nsIFrame::IBSplitPrevSibling());
}
static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) { for (nsIFrame *frame = aFrame, *next;; frame = next) {
next = GetIBSplitSibling(frame); if (!next) { returnstatic_cast<nsContainerFrame*>(frame);
}
}
MOZ_ASSERT_UNREACHABLE("unreachable code"); return nullptr;
}
// We should be the only continuation
NS_ASSERTION(!aFrame->GetPrevContinuation(), "assigning ib-split sibling to other than first continuation!");
NS_ASSERTION(!aFrame->GetNextContinuation() ||
IsFramePartOfIBSplit(aFrame->GetNextContinuation()), "should have no non-ib-split continuations here");
// Mark the frame as ib-split.
aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
if (aIBSplitSibling) {
NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(), "assigning something other than the first continuation as the " "ib-split sibling");
// Store the ib-split sibling (if we were given one) with the // first frame in the flow.
aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
}
}
static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) {
MOZ_ASSERT(
IsFramePartOfIBSplit(aFrame), "GetIBContainingBlockFor() should only be called on known IB frames");
// Get the first "normal" ancestor of the target frame.
nsIFrame* parentFrame; do {
parentFrame = aFrame->GetParent();
if (!parentFrame) {
NS_ERROR("no unsplit block frame in IB hierarchy"); return aFrame;
}
// Note that we ignore non-ib-split frames which have a pseudo on their // ComputedStyle -- they're not the frames we're looking for! In // particular, they may be hiding a real parent that _is_ in an ib-split. if (!IsFramePartOfIBSplit(parentFrame) &&
!parentFrame->Style()->IsPseudoOrAnonBox()) { break;
}
aFrame = parentFrame;
} while (true);
// post-conditions
NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame " "in GetIBContainingBlockFor");
NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
return parentFrame;
}
// Find the multicol containing block suitable for reframing. // // Note: this function may not return a ColumnSetWrapperFrame. For example, if // the multicol containing block has "overflow:scroll" style, // ScrollContainerFrame is returned because ColumnSetWrapperFrame is the // scrolled frame which has the -moz-scrolled-content pseudo style. We may walk // up "too far", but in terms of correctness of reframing, it's OK. static nsContainerFrame* GetMultiColumnContainingBlockFor(nsIFrame* aFrame) {
MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR), "Should only be called if the frame has a multi-column ancestor!");
nsContainerFrame* current = aFrame->GetParent(); while (current &&
(current->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
current->Style()->IsPseudoOrAnonBox())) {
current = current->GetParent();
}
MOZ_ASSERT(current, "No multicol containing block in a valid column hierarchy?");
// Block/inline frame construction logic. We maintain a few invariants here: // // 1. Block frames contain block and inline frames. // // 2. Inline frames only contain inline frames. If an inline parent has a block // child then the block child is migrated upward until it lands in a block // parent (the inline frames containing block is where it will end up).
inlinevoid SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list");
aParent->SetInitialChildList(FrameChildListID::Principal,
nsFrameList(aFrame, aFrame));
}
// Structure used when constructing formatting object trees. Contains // state information needed for absolutely positioned elements namespace mozilla { struct AbsoluteFrameList final : public nsFrameList { // Containing block for absolutely positioned elements.
nsContainerFrame* mContainingBlock;
// Transfer frames in aOther to this list. aOther becomes empty after this // operation.
AbsoluteFrameList(AbsoluteFrameList&& aOther) = default;
AbsoluteFrameList& operator=(AbsoluteFrameList&& aOther) = default;
#ifdef DEBUG // XXXbz Does this need a debug-only assignment operator that nulls out the // childList in the AbsoluteFrameList we're copying? Introducing a difference // between debug and non-debug behavior seems bad, so I guess not...
~AbsoluteFrameList() {
NS_ASSERTION(!FirstChild(), "Dangling child list. Someone forgot to insert it?");
} #endif
};
} // namespace mozilla
// Structure for saving the existing state when pushing/poping containing // blocks. The destructor restores the state to its previous state class MOZ_STACK_CLASS nsFrameConstructorSaveState { public:
~nsFrameConstructorSaveState();
private: // Pointer to struct whose data we save/restore.
AbsoluteFrameList* mList = nullptr;
// The saved pointer to the fixed list.
AbsoluteFrameList* mSavedFixedList = nullptr;
// Copy of original frame list. This can be the original absolute list or a // float list.
AbsoluteFrameList mSavedList;
// The name of the child list in which our frames would belong.
mozilla::FrameChildListID mChildListID = FrameChildListID::Principal;
nsFrameConstructorState* mState = nullptr;
friendclass nsFrameConstructorState;
};
// Structure used for maintaining state information during the // frame construction process class MOZ_STACK_CLASS nsFrameConstructorState { public:
nsPresContext* mPresContext;
PresShell* mPresShell;
nsCSSFrameConstructor* mFrameConstructor;
// Containing block information for out-of-flow frames. // // Floats are easy. Whatever is our float CB. // // Regular abspos elements are easy too. Its containing block can be the // nearest abspos element, or the ICB (the canvas frame). // // Top layer abspos elements are always children of the ICB, but we can get // away with having two different lists (mAbsoluteList and // mTopLayerAbsoluteList), because because top layer frames cause // non-top-layer frames to be contained inside (so any descendants of a top // layer abspos can never share containing block with it, unless they're also // in the top layer). // // Regular fixed elements however are trickier. Fixed elements can be // contained in one of three lists: // // * mAbsoluteList, if our abspos cb is also a fixpos cb (e.g., is // transformed or has a filter). // // * mAncestorFixedList, if the fixpos cb is an ancestor element other than // the viewport frame, (so, a transformed / filtered // ancestor). // // * mRealFixedList, which is also the fixed list used for the top layer // fixed items, which is the fixed list of the viewport // frame. // // It is important that mRealFixedList is shared between regular and top layer // fixpos elements, since no-top-layer descendants of top layer fixed elements // could share ICB and vice versa, so without that there would be no guarantee // of layout ordering between them.
AbsoluteFrameList mFloatedList;
AbsoluteFrameList mAbsoluteList;
AbsoluteFrameList mTopLayerAbsoluteList;
AbsoluteFrameList mAncestorFixedList;
AbsoluteFrameList mRealFixedList;
// Never null, always pointing to one of the lists documented above.
AbsoluteFrameList* mFixedList;
// What `page: auto` resolves to. This is the used page-name of the parent // frame. Updated by AutoFrameConstructionPageName. const nsAtom* mAutoPageNameValue = nullptr;
nsCOMPtr<nsILayoutHistoryState> mFrameState; // These bits will be added to the state bits of any frame we construct // using this state.
nsFrameState mAdditionalStateBits{0};
// If false (which is the default) then call SetPrimaryFrame() as needed // during frame construction. If true, don't make any SetPrimaryFrame() // calls, except for generated content which doesn't have a primary frame // yet. The mCreatingExtraFrames == true mode is meant to be used for // construction of random "extra" frames for elements via normal frame // construction APIs (e.g. replication of things across pages in paginated // mode). bool mCreatingExtraFrames;
// This keeps track of whether we have found a "rendered legend" for // the current FieldSetFrame. bool mHasRenderedLegend;
#ifdef DEBUG // Record the float containing block candidate passed into // MaybePushFloatContainingBlock() to keep track that we've call the method to // handle the float CB scope before processing the CB's children. It is reset // in ConstructFramesFromItemList().
nsContainerFrame* mFloatCBCandidate = nullptr; #endif
// Constructor // Use the passed-in history state.
nsFrameConstructorState(
PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock,
already_AddRefed<nsILayoutHistoryState> aHistoryState); // Get the history state from the pres context's pres shell.
nsFrameConstructorState(PresShell* aPresShell,
nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock);
~nsFrameConstructorState();
// Process the frame insertions for all the out-of-flow nsAbsoluteItems. void ProcessFrameInsertionsForAllLists();
// Function to push the existing absolute containing block state and // create a new scope. Code that uses this function should get matching // logic in GetAbsoluteContainingBlock. // Also makes aNewAbsoluteContainingBlock the containing block for // fixed-pos elements if necessary. // aPositionedFrame is the frame whose style actually makes // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable // element aPositionedFrame is the element's primary frame and // aNewAbsoluteContainingBlock is the scrolled frame. void PushAbsoluteContainingBlock(
nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
nsFrameConstructorSaveState& aSaveState);
// Function to forbid floats descendants under aFloatCBCandidate, or open a // new float containing block scope for aFloatCBCandidate. The current // state is saved in aSaveState if a new scope is pushed. void MaybePushFloatContainingBlock(nsContainerFrame* aFloatCBCandidate,
nsFrameConstructorSaveState& aSaveState);
// Helper function for MaybePushFloatContainingBlock(). void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock,
nsFrameConstructorSaveState& aSaveState);
// Function to return the proper geometric parent for a frame with display // struct given by aStyleDisplay and parent's frame given by // aContentParentFrame.
nsContainerFrame* GetGeometricParent( const nsStyleDisplay& aStyleDisplay,
nsContainerFrame* aContentParentFrame) const;
// Collect absolute frames in mAbsoluteList which are proper descendants // of aNewParent, and reparent them to aNewParent. // // Note: This function does something unusual that moves absolute items // after their frames are constructed under a column hierarchy which has // column-span elements. Do not use this if you're not dealing with // columns. void ReparentAbsoluteItems(nsContainerFrame* aNewParent);
// Collect floats in mFloatedList which are proper descendants of aNewParent, // and reparent them to aNewParent. // // Note: This function does something unusual that moves floats after their // frames are constructed under a column hierarchy which has column-span // elements. Do not use this if you're not dealing with columns. void ReparentFloats(nsContainerFrame* aNewParent);
/** * Function to add a new frame to the right frame list. This MUST be called * on frames before their children have been processed if the frames might * conceivably be out-of-flow; otherwise cleanup in error cases won't work * right. Also, this MUST be called on frames after they have been * initialized. * @param aNewFrame the frame to add * @param aFrameList the list to add in-flow frames to * @param aContent the content pointer for aNewFrame * @param aParentFrame the parent frame for the content if it were in-flow * @param aCanBePositioned pass false if the frame isn't allowed to be * positioned * @param aCanBeFloated pass false if the frame isn't allowed to be * floated
*/ void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
nsIContent* aContent, nsContainerFrame* aParentFrame, bool aCanBePositioned = true, bool aCanBeFloated = true, bool aInsertAfter = false,
nsIFrame* aInsertAfterFrame = nullptr);
/** * Function to return the fixed-pos element list. Normally this will just * hand back the fixed-pos element list, but in case we're dealing with a * transformed element that's acting as an abs-pos and fixed-pos container, * we'll hand back the abs-pos list. Callers should use this function if they * want to get the list acting as the fixed-pos item parent.
*/
AbsoluteFrameList& GetFixedList() { return *mFixedList; } const AbsoluteFrameList& GetFixedList() const { return *mFixedList; }
/** * ProcessFrameInsertions takes the frames in aFrameList and adds them as * kids to the aChildListID child list of |aFrameList.containingBlock|.
*/ void ProcessFrameInsertions(AbsoluteFrameList& aFrameList,
mozilla::FrameChildListID aChildListID);
/** * GetOutOfFlowFrameList selects the out-of-flow frame list the new * frame should be added to. If the frame shouldn't be added to any * out-of-flow list, it returns nullptr. The corresponding type of * placeholder is also returned via the aPlaceholderType parameter * if this method doesn't return nullptr. The caller should check * whether the returned list really has a containing block.
*/
AbsoluteFrameList* GetOutOfFlowFrameList(nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
nsFrameState* aPlaceholderType);
void nsFrameConstructorState::PushAbsoluteContainingBlock(
nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
nsFrameConstructorSaveState& aSaveState) {
MOZ_ASSERT(!!aNewAbsoluteContainingBlock == !!aPositionedFrame, "We should have both or none");
aSaveState.mList = &mAbsoluteList;
aSaveState.mChildListID = FrameChildListID::Absolute;
aSaveState.mState = this;
aSaveState.mSavedList = std::move(mAbsoluteList);
aSaveState.mSavedFixedList = mFixedList;
mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock);
mFixedList = [&] { if (!aPositionedFrame || aPositionedFrame->IsFixedPosContainingBlock()) { // See if we need to treat abspos and fixedpos the same. This happens if // we're a transformed/filtered/etc element, or if we force a null abspos // containing block (for mathml for example). return &mAbsoluteList;
} if (aPositionedFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Auto) { // If our new CB is in the top layer, and isn't a fixed CB itself, we also // escape the usual containment. return &mRealFixedList;
} if (mFixedList == &mAbsoluteList) { // If we were pointing to our old absolute list, keep pointing to it. return &aSaveState.mSavedList;
} // Otherwise keep pointing to the current thing (another ancestor's // absolute list, or the real fixed list, doesn't matter). return mFixedList;
}();
if (aNewAbsoluteContainingBlock) {
aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
}
}
void nsFrameConstructorState::MaybePushFloatContainingBlock(
nsContainerFrame* aFloatCBCandidate,
nsFrameConstructorSaveState& aSaveState) { // The logic here needs to match the logic in GetFloatContainingBlock(). if (ShouldSuppressFloatingOfDescendants(aFloatCBCandidate)) { // Pushing a null float containing block forbids any frames from being // floated until a new float containing block is pushed. See implementation // of nsFrameConstructorState::AddChild(). // // XXX we should get rid of null float containing blocks and teach the // various frame classes to deal with floats instead.
PushFloatContainingBlock(nullptr, aSaveState);
} elseif (aFloatCBCandidate->IsFloatContainingBlock()) {
PushFloatContainingBlock(aFloatCBCandidate, aSaveState);
}
void nsFrameConstructorState::PushFloatContainingBlock(
nsContainerFrame* aNewFloatContainingBlock,
nsFrameConstructorSaveState& aSaveState) {
MOZ_ASSERT(!aNewFloatContainingBlock ||
aNewFloatContainingBlock->IsFloatContainingBlock(), "Please push a real float containing block!");
NS_ASSERTION(
!aNewFloatContainingBlock ||
!ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock), "We should not push a frame that is supposed to _suppress_ " "floats as a float containing block!");
aSaveState.mList = &mFloatedList;
aSaveState.mSavedList = std::move(mFloatedList);
aSaveState.mChildListID = FrameChildListID::Float;
aSaveState.mState = this;
mFloatedList = AbsoluteFrameList(aNewFloatContainingBlock);
}
nsContainerFrame* nsFrameConstructorState::GetGeometricParent( const nsStyleDisplay& aStyleDisplay,
nsContainerFrame* aContentParentFrame) const { // If there is no container for a fixed, absolute, or floating root // frame, we will ignore the positioning. This hack is originally // brought to you by the letter T: tables, since other roots don't // even call into this code. See bug 178855. // // XXX Disabling positioning in this case is a hack. If one was so inclined, // one could support this either by (1) inserting a dummy block between the // table and the canvas or (2) teaching the canvas how to reflow positioned // elements. (1) has the usual problems when multiple frames share the same // content (notice all the special cases in this file dealing with inner // tables and table wrappers which share the same content). (2) requires some // work and possible factoring. // // XXXbz couldn't we just force position to "static" on roots and // float to "none"? That's OK per CSS 2.1, as far as I can tell.
if (aContentParentFrame && aContentParentFrame->IsInSVGTextSubtree()) { return aContentParentFrame;
}
if (aStyleDisplay.mTopLayer != StyleTopLayer::None) {
MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Auto, "-moz-top-layer should be either none or auto");
MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(), "Top layer items should always be absolutely positioned"); if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) {
MOZ_ASSERT(mRealFixedList.mContainingBlock, "No root frame?"); return mRealFixedList.mContainingBlock;
}
MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute);
MOZ_ASSERT(mTopLayerAbsoluteList.mContainingBlock); return mTopLayerAbsoluteList.mContainingBlock;
}
if (aStyleDisplay.mPosition == StylePositionProperty::Absolute &&
mAbsoluteList.mContainingBlock) { return mAbsoluteList.mContainingBlock;
}
if (aStyleDisplay.mPosition == StylePositionProperty::Fixed &&
mFixedList->mContainingBlock) { return mFixedList->mContainingBlock;
}
return aContentParentFrame;
}
void nsFrameConstructorState::ReparentAbsoluteItems(
nsContainerFrame* aNewParent) { // Bug 1491727: This function might not conform to the spec. See // https://github.com/w3c/csswg-drafts/issues/1894.
MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR), "Restrict the usage under column hierarchy.");
AbsoluteFrameList newAbsoluteItems(aNewParent);
nsIFrame* current = mAbsoluteList.FirstChild(); while (current) {
nsIFrame* placeholder = current->GetPlaceholderFrame();
if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
nsIFrame* next = current->GetNextSibling();
mAbsoluteList.RemoveFrame(current);
newAbsoluteItems.AppendFrame(aNewParent, current);
current = next;
} else {
current = current->GetNextSibling();
}
}
if (newAbsoluteItems.NotEmpty()) { // ~nsFrameConstructorSaveState() will move newAbsoluteItems to // aNewParent's absolute child list.
nsFrameConstructorSaveState absoluteSaveState;
// It doesn't matter whether aNewParent has position style or not. Caller // won't call us if we can't have absolute children.
PushAbsoluteContainingBlock(aNewParent, aNewParent, absoluteSaveState);
mAbsoluteList = std::move(newAbsoluteItems);
}
}
void nsFrameConstructorState::ReparentFloats(nsContainerFrame* aNewParent) {
MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR), "Restrict the usage under column hierarchy.");
MOZ_ASSERT(
aNewParent->IsFloatContainingBlock(), "Why calling this method if aNewParent is not a float containing block?");
// Gather floats that should reparent under aNewParent.
AbsoluteFrameList floats(aNewParent);
nsIFrame* current = mFloatedList.FirstChild(); while (current) {
nsIFrame* placeholder = current->GetPlaceholderFrame();
nsIFrame* next = current->GetNextSibling(); if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
mFloatedList.RemoveFrame(current);
floats.AppendFrame(aNewParent, current);
}
current = next;
}
if (floats.NotEmpty()) { // Make floats move into aNewParent's float child list in // ~nsFrameConstructorSaveState() when destructing floatSaveState.
nsFrameConstructorSaveState floatSaveState;
PushFloatContainingBlock(aNewParent, floatSaveState);
mFloatedList = std::move(floats);
}
}
// The comments in GetGeometricParent regarding root table frames // all apply here, unfortunately. Thus, we need to check whether // the returned frame items really has containing block.
nsFrameList* frameList; if (outOfFlowFrameList && outOfFlowFrameList->mContainingBlock) {
MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->mContainingBlock, "Parent of the frame is not the containing block?");
frameList = outOfFlowFrameList;
} else {
frameList = &aFrameList;
placeholderType = nsFrameState(0);
}
NS_ASSERTION(containingBlock, "Child list without containing block?");
if (aChildListID == FrameChildListID::Fixed) { // Put this frame on the transformed-frame's abs-pos list instead, if // it has abs-pos children instead of fixed-pos children.
aChildListID = containingBlock->GetAbsoluteListID();
}
// Insert the frames hanging out in aItems. We can use SetInitialChildList() // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW // is set) and doesn't have any frames in the aChildListID child list yet. const nsFrameList& childList = containingBlock->GetChildList(aChildListID); if (childList.IsEmpty() &&
containingBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { // If we're injecting absolutely positioned frames, inject them on the // absolute containing block if (aChildListID == containingBlock->GetAbsoluteListID()) {
containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList(
containingBlock, aChildListID, std::move(aFrameList));
} else {
containingBlock->SetInitialChildList(aChildListID, std::move(aFrameList));
}
} elseif (aChildListID == FrameChildListID::Fixed ||
aChildListID == FrameChildListID::Absolute) { // The order is not important for abs-pos/fixed-pos frame list, just // append the frame items to the list directly.
mFrameConstructor->AppendFrames(containingBlock, aChildListID,
std::move(aFrameList));
} else { // Note that whether the frame construction context is doing an append or // not is not helpful here, since it could be appending to some frame in // the middle of the document, which means we're not necessarily // appending to the children of the containing block. // // We need to make sure the 'append to the end of document' case is fast. // So first test the last child of the containing block
nsIFrame* lastChild = childList.LastChild();
// CompareTreePosition uses placeholder hierarchy for out of flow frames, // so this will make out-of-flows respect the ordering of placeholders, // which is great because it takes care of anonymous content.
nsIFrame* firstNewFrame = aFrameList.FirstChild();
// Cache the ancestor chain so that we can reuse it if needed.
AutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
nsIFrame* notCommonAncestor = nullptr; if (lastChild) {
notCommonAncestor = nsLayoutUtils::FillAncestors(
firstNewFrame, containingBlock, &firstNewFrameAncestors);
}
if (!lastChild || nsLayoutUtils::CompareTreePosition(
lastChild, firstNewFrame, firstNewFrameAncestors,
notCommonAncestor ? containingBlock : nullptr) < 0) { // no lastChild, or lastChild comes before the new children, so just // append
mFrameConstructor->AppendFrames(containingBlock, aChildListID,
std::move(aFrameList));
} else { // Try the other children. First collect them to an array so that a // reasonable fast binary search can be used to find the insertion point.
AutoTArray<nsIFrame*, 128> children; for (nsIFrame* f = childList.FirstChild(); f != lastChild;
f = f->GetNextSibling()) {
children.AppendElement(f);
}
nsIFrame* insertionPoint = nullptr;
int32_t imin = 0;
int32_t max = children.Length(); while (max > imin) {
int32_t imid = imin + ((max - imin) / 2);
nsIFrame* f = children[imid];
int32_t compare = nsLayoutUtils::CompareTreePosition(
f, firstNewFrame, firstNewFrameAncestors,
notCommonAncestor ? containingBlock : nullptr); if (compare > 0) { // f is after the new frame.
max = imid;
insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
} elseif (compare < 0) { // f is before the new frame.
imin = imid + 1;
insertionPoint = f;
} else { // This is for the old behavior. Should be removed once it is // guaranteed that CompareTreePosition can't return 0! // See bug 928645.
NS_WARNING("Something odd happening???");
insertionPoint = nullptr; for (uint32_t i = 0; i < children.Length(); ++i) {
nsIFrame* f = children[i]; if (nsLayoutUtils::CompareTreePosition(
f, firstNewFrame, firstNewFrameAncestors,
notCommonAncestor ? containingBlock : nullptr) > 0) { break;
}
insertionPoint = f;
} break;
}
}
mFrameConstructor->InsertFrames(containingBlock, aChildListID,
insertionPoint, std::move(aFrameList));
}
}
MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?");
}
nsFrameConstructorSaveState::~nsFrameConstructorSaveState() { // Restore the state if (mList) {
MOZ_ASSERT(mState, "Can't have mList set without having a state!");
mState->ProcessFrameInsertions(*mList, mChildListID);
MOZ_ASSERT(mSavedList.IsEmpty(), "Frames in mSavedList should've moved back into mState!");
MOZ_ASSERT(!mList->LastChild() || !mList->LastChild()->GetNextSibling(), "Something corrupted our list!");
}
}
/** * Moves aFrameList from aOldParent to aNewParent. This updates the parent * pointer of the frames in the list, and reparents their views as needed. * nsIFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its * ancestors as needed. Then it sets the list as the initial child list * on aNewParent, unless aNewParent either already has kids or has been * reflowed; in that case it appends the new frames. Note that this * method differs from ReparentFrames in that it doesn't change the kids' * style.
*/ // XXXbz Since this is only used for {ib} splits, could we just copy the view // bits from aOldParent to aNewParent and then use the // nsFrameList::ApplySetParent? That would still leave us doing two passes // over the list, of course; if we really wanted to we could factor out the // relevant part of ReparentFrameViewList, I suppose... Or just get rid of // views, which would make most of this function go away. staticvoid MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
nsFrameList& aFrameList) { #ifdef DEBUG bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) { // Move the frames into the new view
nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
} #endif
staticvoid EnsureAutoPageName(nsFrameConstructorState& aState, const nsContainerFrame* const aFrame) { // Check if we need to figure out our used page name. // When building the entire document, this should only happen for the // root, which will mean the loop will immediately end. Either way, this will // only happen once for each time the frame constructor is run. if (aState.mAutoPageNameValue) { return;
}
for (const nsContainerFrame* frame = aFrame; frame;
frame = frame->GetParent()) { if (const nsAtom* maybePageName = frame->GetStylePageName()) {
aState.mAutoPageNameValue = maybePageName; return;
}
} // Ensure that a root with `page: auto` gets an empty page name // https://drafts.csswg.org/css-page-3/#using-named-pages
aState.mAutoPageNameValue = nsGkAtoms::_empty;
}
nsCSSFrameConstructor::AutoFrameConstructionPageName::
AutoFrameConstructionPageName(nsFrameConstructorState& aState,
nsIFrame* const aFrame)
: mState(aState), mNameToRestore(nullptr) { if (!aState.mPresContext->IsPaginated()) {
MOZ_ASSERT(!aState.mAutoPageNameValue, "Page name should not have been set"); return;
} #ifdef DEBUG
MOZ_ASSERT(!aFrame->mWasVisitedByAutoFrameConstructionPageName, "Frame should only have been visited once");
aFrame->mWasVisitedByAutoFrameConstructionPageName = true; #endif
MOZ_ASSERT(mNameToRestore, "Page name should have been found by EnsureAutoPageName"); if (const nsAtom* maybePageName = aFrame->GetStylePageName()) {
aState.mAutoPageNameValue = maybePageName;
}
aFrame->SetAutoPageValue(aState.mAutoPageNameValue);
}
nsCSSFrameConstructor::AutoFrameConstructionPageName::
~AutoFrameConstructionPageName() { // This isn't actually useful when not in paginated layout, but it's very // likely cheaper to unconditionally write this pointer than to test for // paginated layout and then branch on the result.
mState.mAutoPageNameValue = mNameToRestore;
}
bool found = false;
FrameCtorDebugFlags* flag = gFlags;
FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS; while (flag < limit) { if (nsCRT::strcasecmp(flag->name, flags) == 0) {
*(flag->on) = true;
printf("nsCSSFrameConstructor: setting %s debug flag on\n",
flag->name);
found = true; break;
}
++flag;
}
if (!found) error = true;
if (!comma) break;
*comma = ',';
flags = comma + 1;
}
if (error) {
printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
FrameCtorDebugFlags* flag = gFlags;
FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS; while (flag < limit) {
printf(" %s\n", flag->name);
++flag;
}
printf( "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of " "flag\n");
printf("names (no whitespace)\n");
}
}
} #endif
}
void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) { if (aFrame->StyleDisplay()->IsContainStyle()) {
mContainStyleScopeManager.DestroyScopesFor(aFrame);
}
if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
mContainStyleScopeManager.DestroyQuoteNodesFor(aFrame)) {
QuotesDirty();
}
if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
mContainStyleScopeManager.DestroyCounterNodesFor(aFrame)) { // Technically we don't need to update anything if we destroyed only // USE nodes. However, this is unlikely to happen in the real world // since USE nodes generally go along with INCREMENT nodes.
CountersDirty();
}
void nsCSSFrameConstructor::CreateGeneratedContent(
nsFrameConstructorState& aState, Element& aOriginatingElement,
ComputedStyle& aPseudoStyle, const StyleContentItem& aItem,
size_t aContentIndex, const FunctionRef<void(nsIContent*)> aAddChild) { using Type = StyleContentItem::Tag; // Get the content value const Type type = aItem.tag;
switch (type) { case Type::Image: {
RefPtr c = GeneratedImageContent::Create(*mDocument, aContentIndex);
aAddChild(c); return;
}
case Type::String: { constauto string = aItem.AsString().AsString(); if (string.IsEmpty()) { return;
}
RefPtr text =
CreateGenConTextNode(aState, NS_ConvertUTF8toUTF16(string), nullptr);
aAddChild(text); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.