Source code

Revision control

Other Tools

/* -*- 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
*/
#include "nsCSSFrameConstructor.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ComputedStyleInlines.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/ManualNAC.h"
#include "mozilla/dom/BindContext.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/GeneratedImageContent.h"
#include "mozilla/dom/HTMLDetailsElement.h"
#include "mozilla/dom/HTMLSelectElement.h"
#include "mozilla/dom/HTMLSharedListElement.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/EventStates.h"
#include "mozilla/Likely.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/PrintedSheetFrame.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoStyleSetInlines.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_mathml.h"
#include "mozilla/Unused.h"
#include "RetainedDisplayListBuilder.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsCSSPseudoElements.h"
#include "nsAtom.h"
#include "nsIFrameInlines.h"
#include "nsGkAtoms.h"
#include "nsPresContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "nsTableFrame.h"
#include "nsTableColFrame.h"
#include "nsTableRowFrame.h"
#include "nsTableCellFrame.h"
#include "nsHTMLParts.h"
#include "nsUnicharUtils.h"
#include "nsViewManager.h"
#include "nsStyleConsts.h"
#ifdef MOZ_XUL
# include "nsXULElement.h"
#endif // MOZ_XUL
#include "nsContainerFrame.h"
#include "nsNameSpaceManager.h"
#include "nsComboboxControlFrame.h"
#include "nsListControlFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsTableRowGroupFrame.h"
#include "nsIFormControl.h"
#include "nsCSSAnonBoxes.h"
#include "nsTextFragment.h"
#include "nsIAnonymousContentCreator.h"
#include "nsContentUtils.h"
#include "nsIScriptError.h"
#ifdef XP_MACOSX
# include "nsIDocShell.h"
#endif
#include "ChildIterator.h"
#include "nsError.h"
#include "nsLayoutUtils.h"
#include "nsBoxFrame.h"
#include "nsBoxLayout.h"
#include "nsFlexContainerFrame.h"
#include "nsGridContainerFrame.h"
#include "RubyUtils.h"
#include "nsRubyFrame.h"
#include "nsRubyBaseFrame.h"
#include "nsRubyBaseContainerFrame.h"
#include "nsRubyTextFrame.h"
#include "nsRubyTextContainerFrame.h"
#include "nsImageFrame.h"
#include "nsIObjectLoadingContent.h"
#include "nsTArray.h"
#include "mozilla/dom/CharacterData.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "nsAutoLayoutPhase.h"
#include "nsStyleStructInlines.h"
#include "nsPageContentFrame.h"
#include "mozilla/RestyleManager.h"
#include "StickyScrollContainer.h"
#include "nsFieldSetFrame.h"
#include "nsInlineFrame.h"
#include "nsBlockFrame.h"
#include "nsCanvasFrame.h"
#include "nsFirstLetterFrame.h"
#include "nsGfxScrollFrame.h"
#include "nsPageFrame.h"
#include "nsPageSequenceFrame.h"
#include "nsTableWrapperFrame.h"
#include "nsIScrollableFrame.h"
#include "nsBackdropFrame.h"
#include "nsTransitionManager.h"
#include "DetailsFrame.h"
#ifdef MOZ_XUL
# include "nsIPopupContainer.h"
#endif
#ifdef ACCESSIBILITY
# include "nsAccessibilityService.h"
#endif
#undef NOISY_FIRST_LETTER
#include "nsMathMLParts.h"
#include "mozilla/dom/SVGAnimationElement.h"
#include "mozilla/dom/SVGFilters.h"
#include "mozilla/dom/SVGTests.h"
#include "mozilla/SVGGradientFrame.h"
#include "mozilla/SVGUtils.h"
#include "nsRefreshDriver.h"
#include "nsTextNode.h"
#include "ActiveLayerTracker.h"
using namespace mozilla;
using namespace mozilla::dom;
// An alias for convenience.
static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewSVGInnerSVGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGGeometryFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsContainerFrame* NS_NewSVGForeignObjectFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewSVGAFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGSwitchFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGSymbolFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGTextFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGContainerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGViewFrame(PresShell* aPresShell, ComputedStyle* aStyle);
extern nsIFrame* NS_NewSVGLinearGradientFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
extern nsIFrame* NS_NewSVGRadialGradientFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
extern nsIFrame* NS_NewSVGStopFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsContainerFrame* NS_NewSVGMarkerFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
extern nsIFrame* NS_NewSVGImageFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewSVGClipPathFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGFilterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGPatternFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGFEContainerFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
#include "mozilla/dom/NodeInfo.h"
#include "prenv.h"
#include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h"
#ifdef DEBUG
// Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
// more of the following flags (comma separated) for handy debug
// output.
static bool gNoisyContentUpdates = false;
static bool gReallyNoisyContentUpdates = false;
static bool gNoisyInlineConstruction = false;
struct FrameCtorDebugFlags {
const char* name;
bool* on;
};
static FrameCtorDebugFlags gFlags[] = {
{"content-updates", &gNoisyContentUpdates},
{"really-noisy-content-updates", &gReallyNoisyContentUpdates},
{"noisy-inline", &gNoisyInlineConstruction}};
# define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
#endif
#ifdef MOZ_XUL
# include "nsMenuFrame.h"
# include "nsPopupSetFrame.h"
# include "nsTreeColFrame.h"
//------------------------------------------------------------------
nsContainerFrame* NS_NewRootBoxFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsContainerFrame* NS_NewDocElementBoxFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewDeckFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewImageBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewButtonBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewPopupSetFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewMenuFrame(PresShell* aPresShell, ComputedStyle* aStyle,
uint32_t aFlags);
nsIFrame* NS_NewMenuBarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewTitleBarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewResizerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
#endif
nsHTMLScrollFrame* NS_NewHTMLScrollFrame(PresShell* aPresShell,
ComputedStyle* aStyle, bool aIsRoot);
nsXULScrollFrame* NS_NewXULScrollFrame(PresShell* aPresShell,
ComputedStyle* aStyle, bool aIsRoot);
nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewScrollbarButtonFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*);
// Returns true if aFrame is an anonymous flex/grid item.
static inline bool IsAnonymousFlexOrGridItem(const nsIFrame* aFrame) {
auto pseudoType = aFrame->Style()->GetPseudoType();
return pseudoType == PseudoStyleType::anonymousFlexItem ||
pseudoType == PseudoStyleType::anonymousGridItem;
}
// Returns true IFF the given nsIFrame is a nsFlexContainerFrame and
// represents a -webkit-{inline-}box or -moz-{inline-}box container.
static inline bool IsFlexContainerForLegacyBox(const nsIFrame* aFrame) {
return aFrame->IsFlexContainerFrame() &&
aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX);
}
#if DEBUG
static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
const nsIFrame* aParent) {
MOZ_ASSERT(IsAnonymousFlexOrGridItem(aChild),
"expected an anonymous flex or grid item child frame");
MOZ_ASSERT(aParent, "expected a parent frame");
auto pseudoType = aChild->Style()->GetPseudoType();
if (pseudoType == PseudoStyleType::anonymousFlexItem) {
MOZ_ASSERT(aParent->IsFlexContainerFrame(),
"anonymous flex items should only exist as children "
"of flex container frames");
} else {
MOZ_ASSERT(aParent->IsGridContainerFrame(),
"anonymous grid items should only exist as children "
"of grid container frames");
}
}
#else
# define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO
#endif
#define FCDATA_DECL(_flags, _func) \
{ _flags, {(FrameCreationFunc)_func}, nullptr, PseudoStyleType::NotPseudo }
#define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
{ \
_flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \
{(FrameCreationFunc)_func}, nullptr, _anon_box \
}
#define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
#define FULL_CTOR_FCDATA(_flags, _func) \
{ \
_flags | FCDATA_FUNC_IS_FULL_CTOR, {nullptr}, _func, \
PseudoStyleType::NotPseudo \
}
/**
* 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).
*/
static bool IsInlineFrame(const nsIFrame* aFrame) {
return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
}
/**
* True for display: contents elements.
*/
static inline bool IsDisplayContents(const Element* aElement) {
return aElement->IsDisplayContents();
}
static inline bool IsDisplayContents(const nsIContent* aContent) {
return aContent->IsElement() && IsDisplayContents(aContent->AsElement());
}
/**
* True if aFrame is an instance of an SVG frame class or is an inline/block
* frame being used for SVG text.
*/
static bool IsFrameForSVG(const nsIFrame* aFrame) {
return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
SVGUtils::IsInSVGTextSubtree(aFrame);
}
static bool IsLastContinuationForColumnContent(const nsIFrame* aFrame) {
MOZ_ASSERT(aFrame);
return aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
!aFrame->GetNextContinuation();
}
/**
* 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).
*/
static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) {
return aFrame->IsFlexOrGridContainer() || aFrame->IsXULBoxFrame() ||
aFrame->IsFrameOfType(nsIFrame::eMathML);
}
// 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.
static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) {
if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) {
// Never suppress column-span under ::-moz-column-content frames.
return false;
}
if (aFrame->IsInlineFrame()) {
// Allow inline frames to have column-span block children.
return false;
}
if (!aFrame->IsBlockFrameOrSubclass() ||
aFrame->HasAnyStateBits(NS_BLOCK_FLOAT_MGR | NS_FRAME_OUT_OF_FLOW)) {
// Need to suppress column-span under a different block formatting
// context or an out-of-flow frame.
//
// 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.
return true;
}
return false;
}
/**
* If any children require a block parent, return the first such child.
* Otherwise return null.
*/
static nsIContent* AnyKidsNeedBlockParent(nsIFrame* aFrameList) {
for (nsIFrame* k = aFrameList; k; k = k->GetNextSibling()) {
// Line participants, such as text and inline frames, can't be
// directly inside a XUL box; they must be wrapped in an
// intermediate block.
if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
return k->GetContent();
}
}
return nullptr;
}
// Reparent a frame into a wrapper frame that is a child of its old parent.
static void 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);
}
}
static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
nsContainerFrame* aNewParentFrame,
const nsFrameList& aFrameList,
bool aForceStyleReparent) {
RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
for (nsIFrame* f : aFrameList) {
ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent);
}
}
//----------------------------------------------------------------------
//
// When inline frames get weird and have block frames in them, we
// annotate them to help us respond to incremental content changes
// more easily.
static inline bool 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;
}
static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) {
MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
// 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());
}
static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) {
MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
// 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) {
return static_cast<nsContainerFrame*>(frame);
}
}
MOZ_ASSERT_UNREACHABLE("unreachable code");
return nullptr;
}
static void SetFrameIsIBSplit(nsContainerFrame* aFrame,
nsContainerFrame* aIBSplitSibling) {
MOZ_ASSERT(aFrame, "bad args!");
// 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 (1);
// 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, HTMLScrollFrame 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?");
return current;
}
// This is a bit slow, but sometimes we need it.
static bool ParentIsWrapperAnonBox(nsIFrame* aParent) {
nsIFrame* maybeAnonBox = aParent;
if (maybeAnonBox->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
// The thing that would maybe be a wrapper anon box is the cell.
maybeAnonBox = maybeAnonBox->GetParent();
}
return maybeAnonBox->Style()->IsWrapperAnonBox();
}
//----------------------------------------------------------------------
// 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).
inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list");
nsFrameList temp(aFrame, aFrame);
aParent->SetInitialChildList(kPrincipalList, temp);
}
// -----------------------------------------------------------
// Structure used when constructing formatting object trees. Contains
// state information needed for absolutely positioned elements
namespace mozilla {
struct AbsoluteFrameList : public nsFrameList {
// containing block for absolutely positioned elements
nsContainerFrame* containingBlock;
explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock)
: containingBlock(aContainingBlock) {}
#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:
typedef nsIFrame::ChildListID ChildListID;
nsFrameConstructorSaveState();
~nsFrameConstructorSaveState();
private:
AbsoluteFrameList* mList; // pointer to struct whose data we save/restore
AbsoluteFrameList mSavedList; // copy of original data
// The name of the child list in which our frames would belong
ChildListID mChildListID;
nsFrameConstructorState* mState;
// State used only when we're saving the abs-pos state for a transformed
// element.
AbsoluteFrameList mSavedFixedList;
bool mSavedFixedPosIsAbsPos;
friend class nsFrameConstructorState;
};
// Structure used for maintaining state information during the
// frame construction process
class MOZ_STACK_CLASS nsFrameConstructorState {
public:
typedef nsIFrame::ChildListID ChildListID;
nsPresContext* mPresContext;
PresShell* mPresShell;
nsFrameManager* mFrameManager;
#ifdef MOZ_XUL
// Frames destined for the kPopupList.
AbsoluteFrameList mPopupList;
#endif
// Containing block information for out-of-flow frames.
AbsoluteFrameList mFixedList;
AbsoluteFrameList mAbsoluteList;
AbsoluteFrameList mFloatedList;
// The containing block of a frame in the top layer is defined by the
// spec: fixed-positioned frames are children of the viewport frame,
// and absolutely-positioned frames are children of the initial
// containing block. They would not be caught by any other containing
// block, e.g. frames with transform or filter.
AbsoluteFrameList mTopLayerFixedList;
AbsoluteFrameList mTopLayerAbsoluteList;
nsCOMPtr<nsILayoutHistoryState> mFrameState;
// These bits will be added to the state bits of any frame we construct
// using this state.
nsFrameState mAdditionalStateBits;
// When working with transform / filter properties, we want to hook the
// abs-pos and fixed-pos lists together, since such elements are fixed-pos
// containing blocks.
//
// Similarly when restricting absolute positioning (for e.g. mathml).
//
// This flag determines whether or not we want to wire the fixed-pos and
// abs-pos lists together.
bool mFixedPosIsAbsPos;
// A boolean to indicate whether we have a "pending" popupgroup. That is, we
// have already created the FrameConstructionItem for the root popupgroup but
// we have not yet created the relevant frame.
bool mHavePendingPopupgroup;
// 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;
nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer;
#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
* @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
* (XUL-only)
*/
void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
nsIContent* aContent, nsContainerFrame* aParentFrame,
bool aCanBePositioned = true, bool aCanBeFloated = true,
bool aIsOutOfFlowPopup = false, 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 mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
}
const AbsoluteFrameList& GetFixedList() const {
return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
}
protected:
friend class nsFrameConstructorSaveState;
/**
* ProcessFrameInsertions takes the frames in aFrameList and adds them as
* kids to the aChildListID child list of |aFrameList.containingBlock|.
*/
void ProcessFrameInsertions(AbsoluteFrameList& aFrameList,
ChildListID 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,
bool aIsOutOfFlowPopup,
nsFrameState* aPlaceholderType);
void ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
};
nsFrameConstructorState::nsFrameConstructorState(
PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock,
already_AddRefed<nsILayoutHistoryState> aHistoryState)
: mPresContext(aPresShell->GetPresContext()),
mPresShell(aPresShell),
mFrameManager(aPresShell->FrameConstructor()),
#ifdef MOZ_XUL
mPopupList(nullptr),
#endif
mFixedList(aFixedContainingBlock),
mAbsoluteList(aAbsoluteContainingBlock),
mFloatedList(aFloatContainingBlock),
mTopLayerFixedList(
static_cast<nsContainerFrame*>(mFrameManager->GetRootFrame())),
mTopLayerAbsoluteList(
aPresShell->FrameConstructor()->GetDocElementContainingBlock()),
// See PushAbsoluteContaningBlock below
mFrameState(aHistoryState),
mAdditionalStateBits(nsFrameState(0)),
// If the fixed-pos containing block is equal to the abs-pos containing
// block, use the abs-pos containing block's abs-pos list for fixed-pos
// frames.
mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
mHavePendingPopupgroup(false),
mCreatingExtraFrames(false),
mHasRenderedLegend(false) {
#ifdef MOZ_XUL
nsIPopupContainer* popupContainer =
nsIPopupContainer::GetPopupContainer(aPresShell);
if (popupContainer) {
mPopupList.containingBlock = popupContainer->GetPopupSetFrame();
}
#endif
MOZ_COUNT_CTOR(nsFrameConstructorState);
}
nsFrameConstructorState::nsFrameConstructorState(
PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock)
: nsFrameConstructorState(
aPresShell, aFixedContainingBlock, aAbsoluteContainingBlock,
aFloatContainingBlock,
aPresShell->GetDocument()->GetLayoutHistoryState()) {}
nsFrameConstructorState::~nsFrameConstructorState() {
MOZ_COUNT_DTOR(nsFrameConstructorState);
ProcessFrameInsertionsForAllLists();
for (auto& content : Reversed(mGeneratedContentWithInitializer)) {
content->RemoveProperty(nsGkAtoms::genConInitializerProperty);
}
}
void nsFrameConstructorState::ProcessFrameInsertionsForAllLists() {
ProcessFrameInsertions(mTopLayerFixedList, nsIFrame::kFixedList);
ProcessFrameInsertions(mTopLayerAbsoluteList, nsIFrame::kAbsoluteList);
ProcessFrameInsertions(mFloatedList, nsIFrame::kFloatList);
ProcessFrameInsertions(mAbsoluteList, nsIFrame::kAbsoluteList);
ProcessFrameInsertions(mFixedList, nsIFrame::kFixedList);
#ifdef MOZ_XUL
ProcessFrameInsertions(mPopupList, nsIFrame::kPopupList);
#endif
}
void nsFrameConstructorState::PushAbsoluteContainingBlock(
nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
nsFrameConstructorSaveState& aSaveState) {
MOZ_ASSERT(!!aNewAbsoluteContainingBlock == !!aPositionedFrame,
"We should have both or none");
aSaveState.mList = &mAbsoluteList;
aSaveState.mSavedList = mAbsoluteList;
aSaveState.mChildListID = nsIFrame::kAbsoluteList;
aSaveState.mState = this;
aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
if (mFixedPosIsAbsPos) {
// Since we're going to replace mAbsoluteList, we need to save it into
// mFixedList now (and save the current value of mFixedList).
aSaveState.mSavedFixedList = mFixedList;
mFixedList = mAbsoluteList;
}
mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock);
/* See if we're wiring the fixed-pos and abs-pos lists together. This happens
* if we're a transformed/filtered/etc element, or if we force a null abspos
* containing block (for mathml for example).
*/
mFixedPosIsAbsPos =
!aPositionedFrame || aPositionedFrame->IsFixedPosContainingBlock();
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);
} else if (aFloatCBCandidate->IsFloatContainingBlock()) {
PushFloatContainingBlock(aFloatCBCandidate, aSaveState);
}
#ifdef DEBUG
mFloatCBCandidate = aFloatCBCandidate;
#endif
}
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 = mFloatedList;
aSaveState.mChildListID = nsIFrame::kFloatList;
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 &&
SVGUtils::IsInSVGTextSubtree(aContentParentFrame)) {
return aContentParentFrame;
}
if (aStyleDisplay.IsFloatingStyle() && mFloatedList.containingBlock) {
NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(),
"Absolutely positioned _and_ floating?");
return mFloatedList.containingBlock;
}
if (aStyleDisplay.mTopLayer != StyleTopLayer::None) {
MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Top,
"-moz-top-layer should be either none or top");
MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(),
"Top layer items should always be absolutely positioned");
if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) {
MOZ_ASSERT(mTopLayerFixedList.containingBlock, "No root frame?");
return mTopLayerFixedList.containingBlock;
}
MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute);
MOZ_ASSERT(mTopLayerAbsoluteList.containingBlock);
return mTopLayerAbsoluteList.containingBlock;
}
if (aStyleDisplay.mPosition == StylePositionProperty::Absolute &&
mAbsoluteList.containingBlock) {
return mAbsoluteList.containingBlock;
}
if (aStyleDisplay.mPosition == StylePositionProperty::Fixed &&
GetFixedList().containingBlock) {
return GetFixedList().containingBlock;
}
return aContentParentFrame;
}
void nsFrameConstructorState::ReparentAbsoluteItems(
nsContainerFrame* aNewParent) {
// Bug 1491727: This function might not conform to the spec. See
MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
"Restrict the usage under column hierarchy.");
nsFrameList newAbsoluteItems;
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.SetFrames(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.
nsFrameList floats;
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.SetFrames(floats);
}
}
AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList(
nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
bool aIsOutOfFlowPopup, nsFrameState* aPlaceholderType) {
#ifdef MOZ_XUL
if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
MOZ_ASSERT(mPopupList.containingBlock, "Must have a popup set frame!");
*aPlaceholderType = PLACEHOLDER_FOR_POPUP;
return &mPopupList;
}
#endif // MOZ_XUL
const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
if (aCanBeFloated && disp->IsFloatingStyle()) {
*aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
return &mFloatedList;
}
if (aCanBePositioned) {
if (disp->mTopLayer != StyleTopLayer::None) {
*aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER;
if (disp->mPosition == StylePositionProperty::Fixed) {
*aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS;
return &mTopLayerFixedList;
}
*aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS;
return &mTopLayerAbsoluteList;
}
if (disp->mPosition == StylePositionProperty::Absolute) {
*aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
return &mAbsoluteList;
}
if (disp->mPosition == StylePositionProperty::Fixed) {
*aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
return &GetFixedList();
}
}
return nullptr;
}
void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
nsIFrame* aFrame) {
MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
nsContainerFrame* frame = do_QueryFrame(aFrame);
if (!frame) {
NS_WARNING("Cannot create backdrop frame for non-container frame");
return;
}
RefPtr<ComputedStyle> style =
mPresShell->StyleSet()->ResolvePseudoElementStyle(
*aContent->AsElement(), PseudoStyleType::backdrop,
/* aParentStyle */ nullptr);
MOZ_ASSERT(style->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
nsContainerFrame* parentFrame =
GetGeometricParent(*style->StyleDisplay(), nullptr);
nsBackdropFrame* backdropFrame =
new (mPresShell) nsBackdropFrame(style, mPresShell->GetPresContext());
backdropFrame->Init(aContent, parentFrame, nullptr);
nsFrameState placeholderType;
AbsoluteFrameList* frameList =
GetOutOfFlowFrameList(backdropFrame, true, true, false, &placeholderType);
MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);
nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
nsFrameList temp(placeholder, placeholder);
frame->SetInitialChildList(nsIFrame::kBackdropList, temp);
frameList->AppendFrame(nullptr, backdropFrame);
}
void nsFrameConstructorState::AddChild(
nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent,
nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
bool aIsOutOfFlowPopup, bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen");
nsFrameState placeholderType;
AbsoluteFrameList* outOfFlowFrameList =
GetOutOfFlowFrameList(aNewFrame, aCanBePositioned, aCanBeFloated,
aIsOutOfFlowPopup, &placeholderType);
// 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->containingBlock) {
MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->containingBlock,
"Parent of the frame is not the containing block?");
frameList = outOfFlowFrameList;
} else {
frameList = &aFrameList;
placeholderType = nsFrameState(0);
}
if (placeholderType) {
NS_ASSERTION(frameList != &aFrameList,
"Putting frame in-flow _and_ want a placeholder?");
nsIFrame* placeholderFrame =
nsCSSFrameConstructor::CreatePlaceholderFrameFor(
mPresShell, aContent, aNewFrame, aParentFrame, nullptr,
placeholderType);
placeholderFrame->AddStateBits(mAdditionalStateBits);
// Add the placeholder frame to the flow
aFrameList.AppendFrame(nullptr, placeholderFrame);
if (placeholderType & PLACEHOLDER_FOR_TOPLAYER) {
ConstructBackdropFrameFor(aContent, aNewFrame);
}
}
#ifdef DEBUG
else {
NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
"In-flow frame has wrong parent");
}
#endif
if (aInsertAfter) {
frameList->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
} else {
frameList->AppendFrame(nullptr, aNewFrame);
}
}
// Some of this function's callers recurse 1000 levels deep in crashtests. On
// platforms where stack limits are low, we can't afford to incorporate this
// function's `AutoTArray`s into its callers' stack frames, so disable inlining.
MOZ_NEVER_INLINE void nsFrameConstructorState::ProcessFrameInsertions(
AbsoluteFrameList& aFrameList, ChildListID aChildListID) {
#define NS_NONXUL_LIST_TEST \
(&aFrameList == &mFloatedList && aChildListID == nsIFrame::kFloatList) || \
((&aFrameList == &mAbsoluteList || \
&aFrameList == &mTopLayerAbsoluteList) && \
aChildListID == nsIFrame::kAbsoluteList) || \
((&aFrameList == &mFixedList || &aFrameList == &mTopLayerFixedList) && \
aChildListID == nsIFrame::kFixedList)
#ifdef MOZ_XUL
MOZ_ASSERT(NS_NONXUL_LIST_TEST || (&aFrameList == &mPopupList &&
aChildListID == nsIFrame::kPopupList),
"Unexpected aFrameList/aChildListID combination");
#else
MOZ_ASSERT(NS_NONXUL_LIST_TEST,
"Unexpected aFrameList/aChildListID combination");
#endif
if (aFrameList.IsEmpty()) {
return;
}
nsContainerFrame* containingBlock = aFrameList.containingBlock;
NS_ASSERTION(containingBlock, "Child list without containing block?");
if (aChildListID == nsIFrame::kFixedList) {
// 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, aFrameList);
} else {
containingBlock->SetInitialChildList(aChildListID, aFrameList);
}
} else if (aChildListID == nsIFrame::kFixedList ||
aChildListID == nsIFrame::kAbsoluteList) {
// The order is not important for abs-pos/fixed-pos frame list, just
// append the frame items to the list directly.
mFrameManager->AppendFrames(containingBlock, aChildListID, 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
mFrameManager->AppendFrames(containingBlock, aChildListID, 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;
} else if (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;
}
}
mFrameManager->InsertFrames(containingBlock, aChildListID, insertionPoint,
aFrameList);
}
}
MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?");
}
nsFrameConstructorSaveState::nsFrameConstructorSaveState()
: mList(nullptr),
mSavedList(nullptr),
mChildListID(kPrincipalList),
mState(nullptr),
mSavedFixedList(nullptr),
mSavedFixedPosIsAbsPos(false) {}
nsFrameConstructorSaveState::~nsFrameConstructorSaveState() {
// Restore the state
if (mList) {
NS_ASSERTION(mState, "Can't have mList set without having a state!");
mState->ProcessFrameInsertions(*mList, mChildListID);
*mList = mSavedList;
#ifdef DEBUG
// We've transferred the child list, so drop the pointer we held to it.
// Note that this only matters for the assert in ~AbsoluteFrameList.
mSavedList.Clear();
#endif
if (mList == &mState->mAbsoluteList) {
mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
if (mSavedFixedPosIsAbsPos) {
// mAbsoluteList was moved to mFixedList, so move mFixedList back
// and repair the old mFixedList now.
mState->mAbsoluteList = mState->mFixedList;
mState->mFixedList = mSavedFixedList;
#ifdef DEBUG
mSavedFixedList.Clear();
#endif
}
}
NS_ASSERTION(!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.
static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
nsFrameList& aFrameList) {
bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
// Move the frames into the new view
nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
}
for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
e.get()->SetParent(aNewParent);
}
if (aNewParent->PrincipalChildList().IsEmpty() &&
aNewParent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
aNewParent->SetInitialChildList(kPrincipalList, aFrameList);
} else {
aNewParent->AppendFrames(kPrincipalList, aFrameList);
}
}
static bool ShouldCreateImageFrameForContent(const Element& aElement,
ComputedStyle& aStyle) {
if (aElement.IsRootOfNativeAnonymousSubtree()) {
return false;
}
auto& content = aStyle.StyleContent()->mContent;
if (!content.IsItems()) {
return false;
}
Span<const StyleContentItem> items = content.AsItems().AsSpan();
return items.Length() == 1 && items[0].IsImage();
}
//----------------------------------------------------------------------
nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
PresShell* aPresShell)
: nsFrameManager(aPresShell),
mDocument(aDocument),
mRootElementFrame(nullptr),
mRootElementStyleFrame(nullptr),
mDocElementContainingBlock(nullptr),
mPageSequenceFrame(nullptr),
mFirstFreeFCItem(nullptr),
mFCItemsInUse(0),
mCurrentDepth(0),
mQuotesDirty(false),
mCountersDirty(false),
mIsDestroyingFrameTree(false),
mHasRootAbsPosContainingBlock(false),
mAlwaysCreateFramesForIgnorableWhitespace(false) {
#ifdef DEBUG
static bool gFirstTime = true;
if (gFirstTime) {
gFirstTime = false;
char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
if (flags) {
bool error = false;
for (;;) {
char* comma = strchr(flags, ',');
if (comma) *comma = '\0';
bool found = false;
FrameCtorDebugFlags* flag = gFlags;
FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
while (flag < limit) {
if (PL_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->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT)) {
if (mQuoteList.DestroyNodesFor(aFrame)) QuotesDirty();
}
if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
mCounterManager.DestroyNodesFor(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();
}
RestyleManager()->NotifyDestroyingFrame(aFrame);
}
struct nsGenConInitializer {
UniquePtr<nsGenConNode> mNode;
nsGenConList* mList;
void (nsCSSFrameConstructor::*mDirtyAll)();
nsGenConInitializer(UniquePtr<nsGenConNode> aNode, nsGenConList* aList,
void (nsCSSFrameConstructor::*aDirtyAll)())
: mNode(std::move(aNode)), mList(aList), mDirtyAll(aDirtyAll) {}
};
already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
nsFrameConstructorState& aState, const nsString& aString,
UniquePtr<nsGenConInitializer> aInitializer) {
RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager())
nsTextNode(mDocument->NodeInfoManager());
content->SetText(aString, false);
if (aInit