Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/layout/base/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 477 kB image not shown  

Quelle  nsCSSFrameConstructor.cpp   Sprache: C

 
/* -*- 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 "ActiveLayerTracker.h"
#include "ChildIterator.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ComputedStyleInlines.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/BindContext.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CharacterData.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/GeneratedImageContent.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLSelectElement.h"
#include "mozilla/dom/HTMLSharedListElement.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Likely.h"
#include "mozilla/LinkedList.h"
#include "mozilla/ManualNAC.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/RestyleManager.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoStyleSetInlines.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_mathml.h"
#include "mozilla/SVGGradientFrame.h"
#include "mozilla/Unused.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsAtom.h"
#include "nsAutoLayoutPhase.h"
#include "nsBackdropFrame.h"
#include "nsBlockFrame.h"
#include "nsCanvasFrame.h"
#include "nsCheckboxRadioFrame.h"
#include "nsComboboxControlFrame.h"
#include "nsContainerFrame.h"
#include "nsContentUtils.h"
#include "nsCRT.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsError.h"
#include "nsFieldSetFrame.h"
#include "nsFirstLetterFrame.h"
#include "nsFlexContainerFrame.h"
#include "nsGkAtoms.h"
#include "nsGridContainerFrame.h"
#include "nsHTMLParts.h"
#include "nsIAnonymousContentCreator.h"
#include "nsIFormControl.h"
#include "nsIFrameInlines.h"
#include "nsImageFrame.h"
#include "nsInlineFrame.h"
#include "nsIObjectLoadingContent.h"
#include "nsIPopupContainer.h"
#include "nsIScriptError.h"
#include "nsLayoutUtils.h"
#include "nsListControlFrame.h"
#include "nsMathMLParts.h"
#include "nsNameSpaceManager.h"
#include "nsPageContentFrame.h"
#include "nsPageFrame.h"
#include "nsPageSequenceFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsPresContext.h"
#include "nsRefreshDriver.h"
#include "nsRubyBaseContainerFrame.h"
#include "nsRubyBaseFrame.h"
#include "nsRubyFrame.h"
#include "nsRubyTextContainerFrame.h"
#include "nsRubyTextFrame.h"
#include "nsStyleConsts.h"
#include "nsStyleStructInlines.h"
#include "nsTableCellFrame.h"
#include "nsTableColFrame.h"
#include "nsTableFrame.h"
#include "nsTableRowFrame.h"
#include "nsTableRowGroupFrame.h"
#include "nsTableWrapperFrame.h"
#include "nsTArray.h"
#include "nsTextFragment.h"
#include "nsTextNode.h"
#include "nsTransitionManager.h"
#include "nsUnicharUtils.h"
#include "nsViewManager.h"
#include "nsXULElement.h"
#include "RetainedDisplayListBuilder.h"
#include "RubyUtils.h"
#include "StickyScrollContainer.h"

#ifdef XP_MACOSX
#  include "nsIDocShell.h"
#endif

#ifdef ACCESSIBILITY
#  include "nsAccessibilityService.h"
#endif

#undef NOISY_FIRST_LETTER

using namespace mozilla;
using namespace mozilla::dom;

nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewHTMLAudioFrame(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);
nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*);
nsIFrame* NS_NewComboboxLabelFrame(PresShell*, ComputedStyle*);
nsIFrame* NS_NewMiddleCroppingLabelFrame(PresShell*, ComputedStyle*);

#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

//------------------------------------------------------------------

nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle);

nsIFrame* NS_NewScrollbarButtonFrame(PresShell*, ComputedStyle*);
nsIFrame* NS_NewSimpleXULLeafFrame(PresShell*, ComputedStyle*);

nsIFrame* NS_NewXULImageFrame(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForViewTransitionOld(PresShell*, ComputedStyle*);

// Returns true if aFrame is an anonymous flex/grid item.
static inline bool 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.
static inline bool IsFlexContainerForLegacyWebKitBox(const nsIFrame* aFrame) {
  return aFrame->IsFlexContainerFrame() &&
         aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
}

#if DEBUG
static void 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

#define ToCreationFunc(_func)                              \
  [](PresShell* aPs, ComputedStyle* aStyle) -> nsIFrame* { \
    return _func(aPs, aStyle);                             \
  }

/**
 * 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->IsLineParticipant();
}

/**
 * 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->IsSVGFrame() || aFrame->IsInSVGTextSubtree();
}

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->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.
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_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.
    return true;
  }

  return false;
}

// 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 (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?");

  return current;
}

static bool InsertSeparatorBeforeAccessKey() {
  static bool sInitialized = false;
  static bool sValue = false;
  if (!sInitialized) {
    sInitialized = true;

    const char* prefName = "intl.menuitems.insertseparatorbeforeaccesskeys";
    nsAutoString val;
    Preferences::GetLocalizedString(prefName, val);
    sValue = val.EqualsLiteral("true");
  }
  return sValue;
}

static bool AlwaysAppendAccessKey() {
  static bool sInitialized = false;
  static bool sValue = false;
  if (!sInitialized) {
    sInitialized = true;
    const char* prefName = "intl.menuitems.alwaysappendaccesskeys";
    nsAutoString val;
    Preferences::GetLocalizedString(prefName, val);
    sValue = val.EqualsLiteral("true");
  }
  return sValue;
}

//----------------------------------------------------------------------

// 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");
  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;

  explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock = nullptr)
      : mContainingBlock(aContainingBlock) {}

  // 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;

  friend class 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;

  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
   */

  void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
                nsIContent* aContent, nsContainerFrame* aParentFrame,
                bool aCanBePositioned = truebool 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; }

 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,
                              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 ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
};

nsFrameConstructorState::nsFrameConstructorState(
    PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
    nsContainerFrame* aAbsoluteContainingBlock,
    nsContainerFrame* aFloatContainingBlock,
    already_AddRefed<nsILayoutHistoryState> aHistoryState)
    : mPresContext(aPresShell->GetPresContext()),
      mPresShell(aPresShell),
      mFrameConstructor(aPresShell->FrameConstructor()),
      mFloatedList(aFloatContainingBlock),
      mAbsoluteList(aAbsoluteContainingBlock),
      mTopLayerAbsoluteList(mFrameConstructor->GetCanvasFrame()),
      mAncestorFixedList(aFixedContainingBlock),
      mRealFixedList(
          static_cast<nsContainerFrame*>(mFrameConstructor->GetRootFrame())),
      // See PushAbsoluteContaningBlock below
      mFrameState(aHistoryState),
      mCreatingExtraFrames(false),
      mHasRenderedLegend(false) {
  MOZ_COUNT_CTOR(nsFrameConstructorState);
  mFixedList = [&] {
    if (aFixedContainingBlock == aAbsoluteContainingBlock) {
      return &mAbsoluteList;
    }
    if (aAbsoluteContainingBlock == mRealFixedList.mContainingBlock) {
      return &mRealFixedList;
    }
    return &mAncestorFixedList;
  }();
}

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(mFloatedList, FrameChildListID::Float);
  ProcessFrameInsertions(mAbsoluteList, FrameChildListID::Absolute);
  ProcessFrameInsertions(mTopLayerAbsoluteList, FrameChildListID::Absolute);
  ProcessFrameInsertions(*mFixedList, FrameChildListID::Fixed);
  ProcessFrameInsertions(mRealFixedList, FrameChildListID::Fixed);
}

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);
  } 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 = 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.IsFloatingStyle() && mFloatedList.mContainingBlock) {
    NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(),
                 "Absolutely positioned _and_ floating?");
    return mFloatedList.mContainingBlock;
  }

  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);
  }
}

AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList(
    nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
    nsFrameState* aPlaceholderType) {
  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 &mRealFixedList;
      }
      *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 mFixedList;
    }
  }
  return nullptr;
}

void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
                                                        nsIFrame* aFrame) {
  MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Auto);
  nsContainerFrame* frame = do_QueryFrame(aFrame);
  if (!frame) {
    NS_WARNING("Cannot create backdrop frame for non-container frame");
    return;
  }

  ComputedStyle* parentStyle = nsLayoutUtils::GetStyleFrame(aFrame)->Style();
  RefPtr<ComputedStyle> style =
      mPresShell->StyleSet()->ResolvePseudoElementStyle(
          *aContent->AsElement(), PseudoStyleType::backdrop, nullptr,
          parentStyle);
  MOZ_ASSERT(style->StyleDisplay()->mTopLayer == StyleTopLayer::Auto);
  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, truetrue, &placeholderType);
  MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);

  nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
      mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
  frame->SetInitialChildList(FrameChildListID::Backdrop,
                             nsFrameList(placeholder, placeholder));

  frameList->AppendFrame(nullptr, backdropFrame);
}

void nsFrameConstructorState::AddChild(
    nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent,
    nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
    bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
  MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen");

  nsFrameState placeholderType;
  AbsoluteFrameList* outOfFlowFrameList = GetOutOfFlowFrameList(
      aNewFrame, aCanBePositioned, aCanBeFloated, &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->mContainingBlock) {
    MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->mContainingBlock,
               "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, FrameChildListID aChildListID) {
  MOZ_ASSERT(&aFrameList == &mFloatedList || &aFrameList == &mAbsoluteList ||
             &aFrameList == &mTopLayerAbsoluteList ||
             &aFrameList == &mAncestorFixedList || &aFrameList == mFixedList ||
             &aFrameList == &mRealFixedList);
  MOZ_ASSERT_IF(&aFrameList == &mFloatedList,
                aChildListID == FrameChildListID::Float);
  MOZ_ASSERT_IF(&aFrameList == &mAbsoluteList || &aFrameList == mFixedList,
                aChildListID == FrameChildListID::Absolute ||
                    aChildListID == FrameChildListID::Fixed);
  MOZ_ASSERT_IF(&aFrameList == &mTopLayerAbsoluteList,
                aChildListID == FrameChildListID::Absolute);
  MOZ_ASSERT_IF(&aFrameList == mFixedList && &aFrameList != &mAbsoluteList,
                aChildListID == FrameChildListID::Fixed);
  MOZ_ASSERT_IF(&aFrameList == &mAncestorFixedList,
                aChildListID == FrameChildListID::Fixed);
  MOZ_ASSERT_IF(&aFrameList == &mRealFixedList,
                aChildListID == FrameChildListID::Fixed);

  if (aFrameList.IsEmpty()) {
    return;
  }

  nsContainerFrame* containingBlock = aFrameList.mContainingBlock;

  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));
    }
  } else if (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;
        } 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;
        }
      }
      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);

    if (mList == &mState->mAbsoluteList) {
      mState->mAbsoluteList = std::move(mSavedList);
      mState->mFixedList = mSavedFixedList;
    } else {
      mState->mFloatedList = std::move(mSavedList);
    }

    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.
static void 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

  aFrameList.ApplySetParent(aNewParent);

  if (aNewParent->PrincipalChildList().IsEmpty() &&
      aNewParent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    aNewParent->SetInitialChildList(FrameChildListID::Principal,
                                    std::move(aFrameList));
  } else {
    aNewParent->AppendFrames(FrameChildListID::Principal,
                             std::move(aFrameList));
  }
}

static void 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

  EnsureAutoPageName(aState, aFrame->GetParent());
  mNameToRestore = aState.mAutoPageNameValue;

  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;
}

//----------------------------------------------------------------------

nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
                                             PresShell* aPresShell)
    : nsFrameManager(aPresShell),
      mDocument(aDocument),
      mFirstFreeFCItem(nullptr),
      mFCItemsInUse(0),
      mCurrentDepth(0),
      mQuotesDirty(false),
      mCountersDirty(false),
      mAlwaysCreateFramesForIgnorableWhitespace(false),
      mRemovingContent(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 (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();
  }

  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 nsAString& aString,
    UniquePtr<nsGenConInitializer> aInitializer) {
  RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager())
      nsTextNode(mDocument->NodeInfoManager());
  content->SetText(aString, false);
  if (aInitializer) {
    aInitializer->mNode->mText = content;
    content->SetProperty(nsGkAtoms::genConInitializerProperty,
                         aInitializer.release(),
                         nsINode::DeleteProperty<nsGenConInitializer>);
    aState.mGeneratedContentWithInitializer.AppendElement(content);
  }
  return content.forget();
}

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: {
      const auto string = aItem.AsString().AsString();
      if (string.IsEmpty()) {
        return;
      }
      RefPtr text =
          CreateGenConTextNode(aState, NS_ConvertUTF8toUTF16(string), nullptr);
      aAddChild(text);
      return;
    }

    case Type::Attr: {
      const auto& attr = aItem.AsAttr();
      RefPtr<nsAtom> attrName = attr.attribute.AsAtom();
      int32_t attrNameSpace = kNameSpaceID_None;
      RefPtr<nsAtom> ns = attr.namespace_url.AsAtom();
      if (!ns->IsEmpty()) {
        nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
            ns.forget(), attrNameSpace);
        NS_ENSURE_SUCCESS_VOID(rv);
      }

      if (mDocument->IsHTMLDocument() && aOriginatingElement.IsHTMLElement()) {
        ToLowerCaseASCII(attrName);
      }

      RefPtr<nsAtom> fallback = attr.fallback.AsAtom();

      nsCOMPtr<nsIContent> content;
      NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace,
                             attrName, fallback, getter_AddRefs(content));
      aAddChild(content);
      return;
    }

    case Type::Counter:
    case Type::Counters: {
      RefPtr<nsAtom> name;
      const StyleCounterStyle* style;
      nsString separator;
      if (type == Type::Counter) {
        const auto& counter = aItem.AsCounter();
        name = counter._0.AsAtom();
        style = &counter._1;
      } else {
        const auto& counters = aItem.AsCounters();
        name = counters._0.AsAtom();
        CopyUTF8toUTF16(counters._1.AsString(), separator);
        style = &counters._2;
      }

      auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
          aOriginatingElement, name);
      auto node = MakeUnique<nsCounterUseNode>(
          *style, std::move(separator), aContentIndex,
          /* aAllCounters = */ type == Type::Counters);

      auto initializer = MakeUnique<nsGenConInitializer>(
          std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
      RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
      aAddChild(c);
      return;
    }
    case Type::OpenQuote:
    case Type::CloseQuote:
    case Type::NoOpenQuote:
    case Type::NoCloseQuote: {
      auto node = MakeUnique<nsQuoteNode>(type, aContentIndex);
      auto* quoteList =
          mContainStyleScopeManager.QuoteListFor(aOriginatingElement);
      auto initializer = MakeUnique<nsGenConInitializer>(
          std::move(node), quoteList, &nsCSSFrameConstructor::QuotesDirty);
      RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
      aAddChild(c);
      return;
    }

    case Type::MozLabelContent: {
      nsAutoString accesskey;
      if (!aOriginatingElement.GetAttr(nsGkAtoms::accesskey, accesskey) ||
          accesskey.IsEmpty() || !LookAndFeel::GetMenuAccessKey()) {
        // Easy path: just return a regular value attribute content.
        nsCOMPtr<nsIContent> content;
        NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
                               nsGkAtoms::value, nsGkAtoms::_empty,
                               getter_AddRefs(content));
        aAddChild(content);
        return;
      }

      nsAutoString value;
      aOriginatingElement.GetAttr(nsGkAtoms::value, value);

      auto AppendAccessKeyLabel = [&] {
--> --------------------

--> maximum size reached

--> --------------------

93%


¤ Dauer der Verarbeitung: 0.61 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.