Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Openjdk/test/jdk/java/time/test/java/time/   (Sun/Oracle ©)  Datei vom 13.11.2022 mit Größe 65 B image not shown  

Quelle  nsCSSFrameConstructor.cpp   Sprache: unbekannt

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

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

Messung V0.5
C=83 H=97 G=90

[ 0.50Quellennavigators  Projekt   ]