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

Quelle  nsContainerFrame.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/. */


/* base class #1 for rendering objects that have child lists */

#include "nsContainerFrame.h"
#include "mozilla/widget/InitData.h"
#include "nsContainerFrameInlines.h"

#include "mozilla/ComputedStyle.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Types.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsFlexContainerFrame.h"
#include "nsFrameSelection.h"
#include "mozilla/dom/Document.h"
#include "nsPresContext.h"
#include "nsRect.h"
#include "nsPoint.h"
#include "nsStyleConsts.h"
#include "nsView.h"
#include "nsCOMPtr.h"
#include "nsGkAtoms.h"
#include "nsViewManager.h"
#include "nsIWidget.h"
#include "nsCanvasFrame.h"
#include "nsCSSRendering.h"
#include "nsError.h"
#include "nsDisplayList.h"
#include "nsIBaseWindow.h"
#include "nsCSSFrameConstructor.h"
#include "nsBlockFrame.h"
#include "nsPlaceholderFrame.h"
#include "mozilla/AutoRestore.h"
#include "nsIFrameInlines.h"
#include "nsPrintfCString.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include <algorithm>

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layout;

using mozilla::gfx::ColorPattern;
using mozilla::gfx::DeviceColor;
using mozilla::gfx::DrawTarget;
using mozilla::gfx::Rect;
using mozilla::gfx::sRGBColor;
using mozilla::gfx::ToDeviceColor;

nsContainerFrame::~nsContainerFrame() = default;

NS_QUERYFRAME_HEAD(nsContainerFrame)
  NS_QUERYFRAME_ENTRY(nsContainerFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)

void nsContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                            nsIFrame* aPrevInFlow) {
  nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
  if (aPrevInFlow) {
    // Make sure we copy bits from our prev-in-flow that will affect
    // us. A continuation for a container frame needs to know if it
    // has a child with a view so that we'll properly reposition it.
    if (aPrevInFlow->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) {
      AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
    }
  }
}

void nsContainerFrame::SetInitialChildList(ChildListID aListID,
                                           nsFrameList&& aChildList) {
#ifdef DEBUG
  nsIFrame::VerifyDirtyBitSet(aChildList);
  for (nsIFrame* f : aChildList) {
    MOZ_ASSERT(f->GetParent() == this"Unexpected parent");
  }
#endif
  if (aListID == FrameChildListID::Principal) {
    MOZ_ASSERT(mFrames.IsEmpty(),
               "unexpected second call to SetInitialChildList");
    mFrames = std::move(aChildList);
  } else if (aListID == FrameChildListID::Backdrop) {
    MOZ_ASSERT(StyleDisplay()->mTopLayer != StyleTopLayer::None,
               "Only top layer frames should have backdrop");
    MOZ_ASSERT(HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
               "Top layer frames should be out-of-flow");
    MOZ_ASSERT(!GetProperty(BackdropProperty()),
               "We shouldn't have setup backdrop frame list before");
#ifdef DEBUG
    {
      nsIFrame* placeholder = aChildList.FirstChild();
      MOZ_ASSERT(aChildList.OnlyChild(), "Should have only one backdrop");
      MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
                 "The frame to be stored should be a placeholder");
      MOZ_ASSERT(static_cast<nsPlaceholderFrame*>(placeholder)
                     ->GetOutOfFlowFrame()
                     ->IsBackdropFrame(),
                 "The placeholder should points to a backdrop frame");
    }
#endif
    nsFrameList* list = new (PresShell()) nsFrameList(std::move(aChildList));
    SetProperty(BackdropProperty(), list);
  } else {
    MOZ_ASSERT_UNREACHABLE("Unexpected child list");
  }
}

void nsContainerFrame::AppendFrames(ChildListID aListID,
                                    nsFrameList&& aFrameList) {
  MOZ_ASSERT(aListID == FrameChildListID::Principal ||
                 aListID == FrameChildListID::NoReflowPrincipal,
             "unexpected child list");

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

  DrainSelfOverflowList();  // ensure the last frame is in mFrames
  mFrames.AppendFrames(this, std::move(aFrameList));

  if (aListID != FrameChildListID::NoReflowPrincipal) {
    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
                                  NS_FRAME_HAS_DIRTY_CHILDREN);
  }
}

void nsContainerFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                                    const nsLineList::iterator* aPrevFrameLine,
                                    nsFrameList&& aFrameList) {
  MOZ_ASSERT(aListID == FrameChildListID::Principal ||
                 aListID == FrameChildListID::NoReflowPrincipal,
             "unexpected child list");
  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
               "inserting after sibling frame with different parent");

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

  DrainSelfOverflowList();  // ensure aPrevFrame is in mFrames
  mFrames.InsertFrames(this, aPrevFrame, std::move(aFrameList));

  if (aListID != FrameChildListID::NoReflowPrincipal) {
    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
                                  NS_FRAME_HAS_DIRTY_CHILDREN);
  }
}

void nsContainerFrame::RemoveFrame(DestroyContext& aContext,
                                   ChildListID aListID, nsIFrame* aOldFrame) {
  MOZ_ASSERT(aListID == FrameChildListID::Principal ||
                 aListID == FrameChildListID::NoReflowPrincipal,
             "unexpected child list");

  AutoTArray<nsIFrame*, 10> continuations;
  {
    nsIFrame* continuation = aOldFrame;
    while (continuation) {
      continuations.AppendElement(continuation);
      continuation = continuation->GetNextContinuation();
    }
  }

  mozilla::PresShell* presShell = PresShell();
  nsContainerFrame* lastParent = nullptr;

  // Loop and destroy aOldFrame and all of its continuations.
  //
  // Request a reflow on the parent frames involved unless we were explicitly
  // told not to (FrameChildListID::NoReflowPrincipal).
  const bool generateReflowCommand =
      aListID != FrameChildListID::NoReflowPrincipal;
  for (nsIFrame* continuation : Reversed(continuations)) {
    nsContainerFrame* parent = continuation->GetParent();

    // Please note that 'parent' may not actually be where 'continuation' lives.
    // We really MUST use StealFrame() and nothing else here.
    // @see nsInlineFrame::StealFrame for details.
    parent->StealFrame(continuation);
    continuation->Destroy(aContext);
    if (generateReflowCommand && parent != lastParent) {
      presShell->FrameNeedsReflow(parent, IntrinsicDirty::FrameAndAncestors,
                                  NS_FRAME_HAS_DIRTY_CHILDREN);
      lastParent = parent;
    }
  }
}

void nsContainerFrame::DestroyAbsoluteFrames(DestroyContext& aContext) {
  if (IsAbsoluteContainer()) {
    GetAbsoluteContainingBlock()->DestroyFrames(aContext);
    MarkAsNotAbsoluteContainingBlock();
  }
}

void nsContainerFrame::SafelyDestroyFrameListProp(
    DestroyContext& aContext, mozilla::PresShell* aPresShell,
    FrameListPropertyDescriptor aProp) {
  // Note that the last frame can be removed through another route and thus
  // delete the property -- that's why we fetch the property again before
  // removing each frame rather than fetching it once and iterating the list.
  while (nsFrameList* frameList = GetProperty(aProp)) {
    // Note: Similar to nsFrameList::DestroyFrames(), we remove the frames in
    // reverse order to avoid unnecessary updates to the first-continuation and
    // first-in-flow cache. If we delete them from front to back, updating the
    // cache has a O(n^2) time complexity.
    nsIFrame* frame = frameList->RemoveLastChild();
    if (MOZ_LIKELY(frame)) {
      frame->Destroy(aContext);
    } else {
      Unused << TakeProperty(aProp);
      frameList->Delete(aPresShell);
      return;
    }
  }
}

void nsContainerFrame::Destroy(DestroyContext& aContext) {
  // Prevent event dispatch during destruction.
  if (HasView()) {
    GetView()->SetFrame(nullptr);
  }

  DestroyAbsoluteFrames(aContext);

  // Destroy frames on the principal child list.
  mFrames.DestroyFrames(aContext);

  // If we have any IB split siblings, clear their references to us.
  if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
    // Delete previous sibling's reference to me.
    if (nsIFrame* prevSib = GetProperty(nsIFrame::IBSplitPrevSibling())) {
      NS_WARNING_ASSERTION(
          this == prevSib->GetProperty(nsIFrame::IBSplitSibling()),
          "IB sibling chain is inconsistent");
      prevSib->RemoveProperty(nsIFrame::IBSplitSibling());
    }

    // Delete next sibling's reference to me.
    if (nsIFrame* nextSib = GetProperty(nsIFrame::IBSplitSibling())) {
      NS_WARNING_ASSERTION(
          this == nextSib->GetProperty(nsIFrame::IBSplitPrevSibling()),
          "IB sibling chain is inconsistent");
      nextSib->RemoveProperty(nsIFrame::IBSplitPrevSibling());
    }

#ifdef DEBUG
    // This is just so we can assert it's not set in nsIFrame::DestroyFrom.
    RemoveStateBits(NS_FRAME_PART_OF_IBSPLIT);
#endif
  }

  if (MOZ_UNLIKELY(!mProperties.IsEmpty())) {
    using T = mozilla::FrameProperties::UntypedDescriptor;
    bool hasO = false, hasOC = false, hasEOC = false, hasBackdrop = false;
    mProperties.ForEach([&](const T& aProp, uint64_t) {
      if (aProp == OverflowProperty()) {
        hasO = true;
      } else if (aProp == OverflowContainersProperty()) {
        hasOC = true;
      } else if (aProp == ExcessOverflowContainersProperty()) {
        hasEOC = true;
      } else if (aProp == BackdropProperty()) {
        hasBackdrop = true;
      }
      return true;
    });

    // Destroy frames on the auxiliary frame lists and delete the lists.
    mozilla::PresShell* presShell = PresShell();
    if (hasO) {
      SafelyDestroyFrameListProp(aContext, presShell, OverflowProperty());
    }

    MOZ_ASSERT(CanContainOverflowContainers() || !(hasOC || hasEOC),
               "this type of frame shouldn't have overflow containers");
    if (hasOC) {
      SafelyDestroyFrameListProp(aContext, presShell,
                                 OverflowContainersProperty());
    }
    if (hasEOC) {
      SafelyDestroyFrameListProp(aContext, presShell,
                                 ExcessOverflowContainersProperty());
    }

    MOZ_ASSERT(!GetProperty(BackdropProperty()) ||
                   StyleDisplay()->mTopLayer != StyleTopLayer::None,
               "only top layer frame may have backdrop");
    if (hasBackdrop) {
      SafelyDestroyFrameListProp(aContext, presShell, BackdropProperty());
    }
  }

  nsSplittableFrame::Destroy(aContext);
}

/////////////////////////////////////////////////////////////////////////////
// Child frame enumeration

const nsFrameList& nsContainerFrame::GetChildList(ChildListID aListID) const {
  // We only know about the principal child list, the overflow lists,
  // and the backdrop list.
  switch (aListID) {
    case FrameChildListID::Principal:
      return mFrames;
    case FrameChildListID::Overflow: {
      nsFrameList* list = GetOverflowFrames();
      return list ? *list : nsFrameList::EmptyList();
    }
    case FrameChildListID::OverflowContainers: {
      nsFrameList* list = GetOverflowContainers();
      return list ? *list : nsFrameList::EmptyList();
    }
    case FrameChildListID::ExcessOverflowContainers: {
      nsFrameList* list = GetExcessOverflowContainers();
      return list ? *list : nsFrameList::EmptyList();
    }
    case FrameChildListID::Backdrop: {
      nsFrameList* list = GetProperty(BackdropProperty());
      return list ? *list : nsFrameList::EmptyList();
    }
    default:
      return nsSplittableFrame::GetChildList(aListID);
  }
}

void nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
  mFrames.AppendIfNonempty(aLists, FrameChildListID::Principal);

  using T = mozilla::FrameProperties::UntypedDescriptor;
  mProperties.ForEach([this, aLists](const T& aProp, uint64_t aValue) {
    typedef const nsFrameList* L;
    if (aProp == OverflowProperty()) {
      reinterpret_cast<L>(aValue)->AppendIfNonempty(aLists,
                                                    FrameChildListID::Overflow);
    } else if (aProp == OverflowContainersProperty()) {
      MOZ_ASSERT(CanContainOverflowContainers(),
                 "found unexpected OverflowContainersProperty");
      Unused << this;  // silence clang -Wunused-lambda-capture in opt builds
      reinterpret_cast<L>(aValue)->AppendIfNonempty(
          aLists, FrameChildListID::OverflowContainers);
    } else if (aProp == ExcessOverflowContainersProperty()) {
      MOZ_ASSERT(CanContainOverflowContainers(),
                 "found unexpected ExcessOverflowContainersProperty");
      Unused << this;  // silence clang -Wunused-lambda-capture in opt builds
      reinterpret_cast<L>(aValue)->AppendIfNonempty(
          aLists, FrameChildListID::ExcessOverflowContainers);
    } else if (aProp == BackdropProperty()) {
      reinterpret_cast<L>(aValue)->AppendIfNonempty(aLists,
                                                    FrameChildListID::Backdrop);
    }
    return true;
  });

  nsSplittableFrame::GetChildLists(aLists);
}

/////////////////////////////////////////////////////////////////////////////
// Painting/Events

void nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                        const nsDisplayListSet& aLists) {
  DisplayBorderBackgroundOutline(aBuilder, aLists);
  BuildDisplayListForNonBlockChildren(aBuilder, aLists);
}

void nsContainerFrame::BuildDisplayListForNonBlockChildren(
    nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
    DisplayChildFlags aFlags) {
  nsIFrame* kid = mFrames.FirstChild();
  // Put each child's background directly onto the content list
  nsDisplayListSet set(aLists, aLists.Content());
  // The children should be in content order
  while (kid) {
    BuildDisplayListForChild(aBuilder, kid, set, aFlags);
    kid = kid->GetNextSibling();
  }
}

class nsDisplaySelectionOverlay final : public nsPaintedDisplayItem {
 public:
  /**
   * @param aSelectionValue nsISelectionController::getDisplaySelection.
   */

  nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                            int16_t aSelectionValue)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mSelectionValue(aSelectionValue) {
    MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
  }

  MOZ_COUNTED_DTOR_FINAL(nsDisplaySelectionOverlay)

  virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  bool CreateWebRenderCommands(
      mozilla::wr::DisplayListBuilder& aBuilder,
      mozilla::wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      mozilla::layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
 private:
  DeviceColor ComputeColor() const;

  static DeviceColor ComputeColorFromSelectionStyle(ComputedStyle&);
  static DeviceColor ApplyTransparencyIfNecessary(nscolor);

  // nsISelectionController::getDisplaySelection.
  int16_t mSelectionValue;
};

DeviceColor nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary(
    nscolor aColor) {
  // If it has already alpha, leave it like that.
  if (NS_GET_A(aColor) != 255) {
    return ToDeviceColor(aColor);
  }

  // NOTE(emilio): Blink and WebKit do something slightly different here, and
  // blend the color with white instead, both for overlays and text backgrounds.
  auto color = sRGBColor::FromABGR(aColor);
  color.a = 0.5;
  return ToDeviceColor(color);
}

DeviceColor nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle(
    ComputedStyle& aStyle) {
  return ApplyTransparencyIfNecessary(
      aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor));
}

DeviceColor nsDisplaySelectionOverlay::ComputeColor() const {
  LookAndFeel::ColorID colorID;
  if (RefPtr<ComputedStyle> style =
          mFrame->ComputeSelectionStyle(mSelectionValue)) {
    return ComputeColorFromSelectionStyle(*style);
  }
  if (mSelectionValue == nsISelectionController::SELECTION_ON) {
    colorID = LookAndFeel::ColorID::Highlight;
  } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
    colorID = LookAndFeel::ColorID::TextSelectAttentionBackground;
  } else {
    colorID = LookAndFeel::ColorID::TextSelectDisabledBackground;
  }

  return ApplyTransparencyIfNecessary(
      LookAndFeel::Color(colorID, mFrame, NS_RGB(255, 255, 255)));
}

void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
                                      gfxContext* aCtx) {
  DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
  ColorPattern color(ComputeColor());

  nsIntRect pxRect =
      GetPaintRect(aBuilder, aCtx)
          .ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
  Rect rect(pxRect.x, pxRect.y, pxRect.width, pxRect.height);
  MaybeSnapToDevicePixels(rect, aDrawTarget, true);

  aDrawTarget.FillRect(rect, color);
}

bool nsDisplaySelectionOverlay::CreateWebRenderCommands(
    mozilla::wr::DisplayListBuilder& aBuilder,
    mozilla::wr::IpcResourceUpdateQueue& aResources,
    const StackingContextHelper& aSc,
    mozilla::layers::RenderRootStateManager* aManager,
    nsDisplayListBuilder* aDisplayListBuilder) {
  wr::LayoutRect bounds = wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
      nsRect(ToReferenceFrame(), Frame()->GetSize()),
      mFrame->PresContext()->AppUnitsPerDevPixel()));
  aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(), falsefalse,
                    wr::ToColorF(ComputeColor()));
  return true;
}

void nsContainerFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
                                               nsDisplayList* aList,
                                               uint16_t aContentType) {
  if (!IsSelected() || !IsVisibleForPainting()) {
    return;
  }

  int16_t displaySelection = PresShell()->GetSelectionFlags();
  if (!(displaySelection & aContentType)) {
    return;
  }

  const nsFrameSelection* frameSelection = GetConstFrameSelection();
  int16_t selectionValue = frameSelection->GetDisplaySelection();

  if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) {
    return;  // selection is hidden or off
  }

  nsIContent* newContent = mContent->GetParent();

  // check to see if we are anonymous content
  // XXXbz there has GOT to be a better way of determining this!
  int32_t offset =
      newContent ? newContent->ComputeIndexOf_Deprecated(mContent) : 0;

  // look up to see what selection(s) are on this frame
  UniquePtr<SelectionDetails> details =
      frameSelection->LookUpSelection(newContent, offset, 1, false);
  if (!details) {
    return;
  }

  bool normal = false;
  for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
    if (sd->mSelectionType == SelectionType::eNormal) {
      normal = true;
    }
  }

  if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
    // Don't overlay an image if it's not in the primary selection.
    return;
  }

  aList->AppendNewToTop<nsDisplaySelectionOverlay>(aBuilder, this,
                                                   selectionValue);
}

/* virtual */
void nsContainerFrame::ChildIsDirty(nsIFrame* aChild) {
  NS_ASSERTION(aChild->IsSubtreeDirty(), "child isn't actually dirty");

  AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
}

nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetNoAmount(
    bool aForward, int32_t* aOffset) {
  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
  // Don't allow the caret to stay in an empty (leaf) container frame.
  return CONTINUE_EMPTY;
}

nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetCharacter(
    bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
  // Don't allow the caret to stay in an empty (leaf) container frame.
  return CONTINUE_EMPTY;
}

/////////////////////////////////////////////////////////////////////////////
// Helper member functions

/**
 * Position the view associated with |aKidFrame|, if there is one. A
 * container frame should call this method after positioning a frame,
 * but before |Reflow|.
 */

void nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame) {
  nsIFrame* parentFrame = aKidFrame->GetParent();
  if (!aKidFrame->HasView() || !parentFrame) {
    return;
  }

  nsView* view = aKidFrame->GetView();
  nsViewManager* vm = view->GetViewManager();
  nsPoint pt;
  nsView* ancestorView = parentFrame->GetClosestView(&pt);

  if (ancestorView != view->GetParent()) {
    NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
                 "Allowed only one anonymous view between frames");
    // parentFrame is responsible for positioning aKidFrame's view
    // explicitly
    return;
  }

  pt += aKidFrame->GetPosition();
  vm->MoveViewTo(view, pt.x, pt.y);
}

void nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
                                         nsIFrame* aOldParentFrame,
                                         nsIFrame* aNewParentFrame) {
#ifdef DEBUG
  MOZ_ASSERT(aChildFrame, "null child frame pointer");
  MOZ_ASSERT(aOldParentFrame, "null old parent frame pointer");
  MOZ_ASSERT(aNewParentFrame, "null new parent frame pointer");
  MOZ_ASSERT(aOldParentFrame != aNewParentFrame,
             "same old and new parent frame");

  // See if either the old parent frame or the new parent frame have a view
  while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
    // Walk up both the old parent frame and the new parent frame nodes
    // stopping when we either find a common parent or views for one
    // or both of the frames.
    //
    // This works well in the common case where we push/pull and the old parent
    // frame and the new parent frame are part of the same flow. They will
    // typically be the same distance (height wise) from the
    aOldParentFrame = aOldParentFrame->GetParent();
    aNewParentFrame = aNewParentFrame->GetParent();

    // We should never walk all the way to the root frame without finding
    // a view
    NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");

    // See if we reached a common ancestor
    if (aOldParentFrame == aNewParentFrame) {
      break;
    }
  }

  // See if we found a common parent frame
  if (aOldParentFrame == aNewParentFrame) {
    // We found a common parent and there are no views between the old parent
    // and the common parent or the new parent frame and the common parent.
    // Because neither the old parent frame nor the new parent frame have views,
    // then any child views don't need reparenting
    return;
  }

  // We found views for one or both of the ancestor frames before we
  // found a common ancestor.
  nsView* oldParentView = aOldParentFrame->GetClosestView();
  nsView* newParentView = aNewParentFrame->GetClosestView();

  // See if the old parent frame and the new parent frame are in the
  // same view sub-hierarchy. If they are then we don't have to do
  // anything
  if (oldParentView != newParentView) {
    MOZ_ASSERT_UNREACHABLE("can't move frames between views");
    // They're not so we need to reparent any child views
    aChildFrame->ReparentFrameViewTo(oldParentView->GetViewManager(),
                                     newParentView);
  }
#endif
}

void nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
                                             nsIFrame* aOldParentFrame,
                                             nsIFrame* aNewParentFrame) {
#ifdef DEBUG
  MOZ_ASSERT(aChildFrameList.NotEmpty(), "empty child frame list");
  MOZ_ASSERT(aOldParentFrame, "null old parent frame pointer");
  MOZ_ASSERT(aNewParentFrame, "null new parent frame pointer");
  MOZ_ASSERT(aOldParentFrame != aNewParentFrame,
             "same old and new parent frame");

  // See if either the old parent frame or the new parent frame have a view
  while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
    // Walk up both the old parent frame and the new parent frame nodes
    // stopping when we either find a common parent or views for one
    // or both of the frames.
    //
    // This works well in the common case where we push/pull and the old parent
    // frame and the new parent frame are part of the same flow. They will
    // typically be the same distance (height wise) from the
    aOldParentFrame = aOldParentFrame->GetParent();
    aNewParentFrame = aNewParentFrame->GetParent();

    // We should never walk all the way to the root frame without finding
    // a view
    NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");

    // See if we reached a common ancestor
    if (aOldParentFrame == aNewParentFrame) {
      break;
    }
  }

  // See if we found a common parent frame
  if (aOldParentFrame == aNewParentFrame) {
    // We found a common parent and there are no views between the old parent
    // and the common parent or the new parent frame and the common parent.
    // Because neither the old parent frame nor the new parent frame have views,
    // then any child views don't need reparenting
    return;
  }

  // We found views for one or both of the ancestor frames before we
  // found a common ancestor.
  nsView* oldParentView = aOldParentFrame->GetClosestView();
  nsView* newParentView = aNewParentFrame->GetClosestView();

  // See if the old parent frame and the new parent frame are in the
  // same view sub-hierarchy. If they are then we don't have to do
  // anything
  if (oldParentView != newParentView) {
    MOZ_ASSERT_UNREACHABLE("can't move frames between views");
    nsViewManager* viewManager = oldParentView->GetViewManager();

    // They're not so we need to reparent any child views
    for (nsIFrame* f : aChildFrameList) {
      f->ReparentFrameViewTo(viewManager, newParentView);
    }
  }
#endif
}

void nsContainerFrame::ReparentFrame(nsIFrame* aFrame,
                                     nsContainerFrame* aOldParent,
                                     nsContainerFrame* aNewParent) {
  NS_ASSERTION(aOldParent == aFrame->GetParent(),
               "Parent not consistent with expectations");

  aFrame->SetParent(aNewParent);

  // When pushing and pulling frames we need to check for whether any
  // views need to be reparented
  ReparentFrameView(aFrame, aOldParent, aNewParent);
}

void nsContainerFrame::ReparentFrames(nsFrameList& aFrameList,
                                      nsContainerFrame* aOldParent,
                                      nsContainerFrame* aNewParent) {
  for (auto* f : aFrameList) {
    ReparentFrame(f, aOldParent, aNewParent);
  }
}

void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
                                          nsIWidget* aWidget,
                                          const nsSize& aMinSize,
                                          const nsSize& aMaxSize) {
  LayoutDeviceIntSize devMinSize(
      aPresContext->AppUnitsToDevPixels(aMinSize.width),
      aPresContext->AppUnitsToDevPixels(aMinSize.height));
  LayoutDeviceIntSize devMaxSize(
      aMaxSize.width == NS_UNCONSTRAINEDSIZE
          ? NS_MAXSIZE
          : aPresContext->AppUnitsToDevPixels(aMaxSize.width),
      aMaxSize.height == NS_UNCONSTRAINEDSIZE
          ? NS_MAXSIZE
          : aPresContext->AppUnitsToDevPixels(aMaxSize.height));

  // MinSize has a priority over MaxSize
  if (devMinSize.width > devMaxSize.width) {
    devMaxSize.width = devMinSize.width;
  }
  if (devMinSize.height > devMaxSize.height) {
    devMaxSize.height = devMinSize.height;
  }

  nsIWidget* rootWidget = aPresContext->GetNearestWidget();
  DesktopToLayoutDeviceScale constraintsScale(MOZ_WIDGET_INVALID_SCALE);
  if (rootWidget) {
    constraintsScale = rootWidget->GetDesktopToDeviceScale();
  }

  widget::SizeConstraints constraints(devMinSize, devMaxSize, constraintsScale);

  // The sizes are in inner window sizes, so convert them into outer window
  // sizes. Use a size of (200, 200) as only the difference between the inner
  // and outer size is needed.
  const LayoutDeviceIntSize sizeDiff = aWidget->ClientToWindowSizeDifference();
  if (constraints.mMinSize.width) {
    constraints.mMinSize.width += sizeDiff.width;
  }
  if (constraints.mMinSize.height) {
    constraints.mMinSize.height += sizeDiff.height;
  }
  if (constraints.mMaxSize.width != NS_MAXSIZE) {
    constraints.mMaxSize.width += sizeDiff.width;
  }
  if (constraints.mMaxSize.height != NS_MAXSIZE) {
    constraints.mMaxSize.height += sizeDiff.height;
  }

  aWidget->SetSizeConstraints(constraints);
}

void nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
                                                nsIFrame* aFrame, nsView* aView,
                                                const nsRect& aInkOverflowArea,
                                                ReflowChildFlags aFlags) {
  if (!aView) {
    return;
  }

  // Make sure the view is sized and positioned correctly
  if (!(aFlags & ReflowChildFlags::NoMoveView)) {
    PositionFrameView(aFrame);
  }

  if (!(aFlags & ReflowChildFlags::NoSizeView)) {
    nsViewManager* vm = aView->GetViewManager();

    vm->ResizeView(aView, aInkOverflowArea);
  }
}

void nsContainerFrame::DoInlineMinISize(const IntrinsicSizeInput& aInput,
                                        InlineMinISizeData* aData) {
  auto handleChildren = [&](auto frame, auto data) {
    for (nsIFrame* kid : frame->mFrames) {
      const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
                                        GetWritingMode());
      kid->AddInlineMinISize(kidInput, data);
    }
  };
  DoInlineIntrinsicISize(aData, handleChildren);
}

void nsContainerFrame::DoInlinePrefISize(const IntrinsicSizeInput& aInput,
                                         InlinePrefISizeData* aData) {
  auto handleChildren = [&](auto frame, auto data) {
    for (nsIFrame* kid : frame->mFrames) {
      const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
                                        GetWritingMode());
      kid->AddInlinePrefISize(kidInput, data);
    }
  };
  DoInlineIntrinsicISize(aData, handleChildren);
  aData->mLineIsEmpty = false;
}

/* virtual */
LogicalSize nsContainerFrame::ComputeAutoSize(
    gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
    nscoord aAvailableISize, const LogicalSize& aMargin,
    const mozilla::LogicalSize& aBorderPadding,
    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
  const bool isTableCaption = IsTableCaption();
  // Skip table caption, which requires special sizing - see bug 1109571.
  if (IsAbsolutelyPositionedWithDefiniteContainingBlock() && !isTableCaption) {
    return ComputeAbsolutePosAutoSize(aRenderingContext, aWM, aCBSize,
                                      aAvailableISize, aMargin, aBorderPadding,
                                      aSizeOverrides, aFlags);
  }
  LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
  if (aFlags.contains(ComputeSizeFlag::ShrinkWrap)) {
    // Delegate to nsIFrame::ComputeAutoSize() for computing the shrink-wrapping
    // size.
    result = nsIFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
                                       aAvailableISize, aMargin, aBorderPadding,
                                       aSizeOverrides, aFlags);
  } else {
    result.ISize(aWM) =
        aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM);
  }

  if (isTableCaption) {
    // If we're a container for font size inflation, then shrink
    // wrapping inside of us should not apply font size inflation.
    AutoMaybeDisableFontInflation an(this);

    WritingMode tableWM = GetParent()->GetWritingMode();
    const IntrinsicSizeInput input(
        aRenderingContext, Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
        Nothing());
    if (aWM.IsOrthogonalTo(tableWM)) {
      // For an orthogonal caption on a block-dir side of the table, shrink-wrap
      // to min-isize.
      result.ISize(aWM) = GetMinISize(input);
    } else {
      // The outer frame constrains our available isize to the isize of
      // the table.  Grow if our min-isize is bigger than that, but not
      // larger than the containing block isize.  (It would really be nice
      // to transmit that information another way, so we could grow up to
      // the table's available isize, but that's harder.)
      nscoord min = GetMinISize(input);
      if (min > aCBSize.ISize(aWM)) {
        min = aCBSize.ISize(aWM);
      }
      if (min > result.ISize(aWM)) {
        result.ISize(aWM) = min;
      }
    }
  }
  return result;
}

void nsContainerFrame::ReflowChild(
    nsIFrame* aKidFrame, nsPresContext* aPresContext,
    ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
    const WritingMode& aWM, const LogicalPoint& aPos,
    const nsSize& aContainerSize, ReflowChildFlags aFlags,
    nsReflowStatus& aStatus, nsOverflowContinuationTracker* aTracker) {
  MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input");
  if (aWM.IsPhysicalRTL()) {
    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
                 "ReflowChild with unconstrained container width!");
  }
  MOZ_ASSERT(aDesiredSize.InkOverflow() == nsRect(0, 0, 0, 0) &&
                 aDesiredSize.ScrollableOverflow() == nsRect(0, 0, 0, 0),
             "please reset the overflow areas before calling ReflowChild");

  // Position the child frame and its view if requested.
  if (ReflowChildFlags::NoMoveFrame !=
      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    aKidFrame->SetPosition(aWM, aPos, aContainerSize);
  }

  if (!(aFlags & ReflowChildFlags::NoMoveView)) {
    PositionFrameView(aKidFrame);
    PositionChildViews(aKidFrame);
  }

  // Reflow the child frame
  aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);

  // If the child frame is complete, delete any next-in-flows,
  // but only if the NoDeleteNextInFlowChild flag isn't set.
  if (!aStatus.IsInlineBreakBefore() && aStatus.IsFullyComplete() &&
      !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) {
    if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) {
      // Remove all of the childs next-in-flows. Make sure that we ask
      // the right parent to do the removal (it's possible that the
      // parent is not this because we are executing pullup code)
      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
      DestroyContext context(PresShell());
      kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow,
                                                        true);
    }
  }
}

// XXX temporary: hold on to a copy of the old physical version of
//    ReflowChild so that we can convert callers incrementally.
void nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
                                   nsPresContext* aPresContext,
                                   ReflowOutput& aDesiredSize,
                                   const ReflowInput& aReflowInput, nscoord aX,
                                   nscoord aY, ReflowChildFlags aFlags,
                                   nsReflowStatus& aStatus,
                                   nsOverflowContinuationTracker* aTracker) {
  MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input");

  // Position the child frame and its view if requested.
  if (ReflowChildFlags::NoMoveFrame !=
      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    aKidFrame->SetPosition(nsPoint(aX, aY));
  }

  if (!(aFlags & ReflowChildFlags::NoMoveView)) {
    PositionFrameView(aKidFrame);
    PositionChildViews(aKidFrame);
  }

  // Reflow the child frame
  aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);

  // If the child frame is complete, delete any next-in-flows,
  // but only if the NoDeleteNextInFlowChild flag isn't set.
  if (aStatus.IsFullyComplete() &&
      !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) {
    if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) {
      // Remove all of the childs next-in-flows. Make sure that we ask
      // the right parent to do the removal (it's possible that the
      // parent is not this because we are executing pullup code)
      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
      DestroyContext context(PresShell());
      kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow,
                                                        true);
    }
  }
}

/**
 * Position the views of |aFrame|'s descendants. A container frame
 * should call this method if it moves a frame after |Reflow|.
 */

void nsContainerFrame::PositionChildViews(nsIFrame* aFrame) {
  if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) {
    return;
  }

  // Recursively walk aFrame's child frames.
  // Process the additional child lists, but skip the popup list as the view for
  // popups is managed by the parent.
  // Currently only nsMenuFrame has a popupList and during layout will adjust
  // the view manually to position the popup.
  for (const auto& [list, listID] : aFrame->ChildLists()) {
    for (nsIFrame* childFrame : list) {
      // Position the frame's view (if it has one) otherwise recursively
      // process its children
      if (childFrame->HasView()) {
        PositionFrameView(childFrame);
      } else {
        PositionChildViews(childFrame);
      }
    }
  }
}

void nsContainerFrame::FinishReflowChild(
    nsIFrame* aKidFrame, nsPresContext* aPresContext,
    const ReflowOutput& aDesiredSize, const ReflowInput* aReflowInput,
    const WritingMode& aWM, const LogicalPoint& aPos,
    const nsSize& aContainerSize, nsIFrame::ReflowChildFlags aFlags) {
  MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == aKidFrame);
  MOZ_ASSERT(aReflowInput || aKidFrame->IsMathMLFrame() ||
                 aKidFrame->IsTableCellFrame(),
             "aReflowInput should be passed in almost all cases");

  if (aWM.IsPhysicalRTL()) {
    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
                 "FinishReflowChild with unconstrained container width!");
  }

  nsPoint curOrigin = aKidFrame->GetPosition();
  const LogicalSize convertedSize = aDesiredSize.Size(aWM);
  LogicalPoint pos(aPos);

  if (aFlags & ReflowChildFlags::ApplyRelativePositioning) {
    MOZ_ASSERT(aReflowInput, "caller must have passed reflow input");
    // ApplyRelativePositioning in right-to-left writing modes needs to know
    // the updated frame width to set the normal position correctly.
    aKidFrame->SetSize(aWM, convertedSize);

    const LogicalMargin offsets = aReflowInput->ComputedLogicalOffsets(aWM);
    ReflowInput::ApplyRelativePositioning(aKidFrame, aWM, offsets, &pos,
                                          aContainerSize);
  }

  if (ReflowChildFlags::NoMoveFrame !=
      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    aKidFrame->SetRect(aWM, LogicalRect(aWM, pos, convertedSize),
                       aContainerSize);
  } else {
    aKidFrame->SetSize(aWM, convertedSize);
  }

  if (aKidFrame->HasView()) {
    nsView* view = aKidFrame->GetView();
    // Make sure the frame's view is properly sized and positioned and has
    // things like opacity correct
    SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
                             aDesiredSize.InkOverflow(), aFlags);
  }

  nsPoint newOrigin = aKidFrame->GetPosition();
  if (!(aFlags & ReflowChildFlags::NoMoveView) && curOrigin != newOrigin) {
    if (!aKidFrame->HasView()) {
      // If the frame has moved, then we need to make sure any child views are
      // correctly positioned
      PositionChildViews(aKidFrame);
    }
  }

  aKidFrame->DidReflow(aPresContext, aReflowInput);
}
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64)
#  pragma optimize("", on)
#endif

// XXX temporary: hold on to a copy of the old physical version of
//    FinishReflowChild so that we can convert callers incrementally.
void nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
                                         nsPresContext* aPresContext,
                                         const ReflowOutput& aDesiredSize,
                                         const ReflowInput* aReflowInput,
                                         nscoord aX, nscoord aY,
                                         ReflowChildFlags aFlags) {
  MOZ_ASSERT(!(aFlags & ReflowChildFlags::ApplyRelativePositioning),
             "only the logical version supports ApplyRelativePositioning "
             "since ApplyRelativePositioning requires the container size");

  nsPoint curOrigin = aKidFrame->GetPosition();
  nsPoint pos(aX, aY);
  nsSize size(aDesiredSize.PhysicalSize());

  if (ReflowChildFlags::NoMoveFrame !=
      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    aKidFrame->SetRect(nsRect(pos, size));
  } else {
    aKidFrame->SetSize(size);
  }

  if (aKidFrame->HasView()) {
    nsView* view = aKidFrame->GetView();
    // Make sure the frame's view is properly sized and positioned and has
    // things like opacity correct
    SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
                             aDesiredSize.InkOverflow(), aFlags);
  }

  if (!(aFlags & ReflowChildFlags::NoMoveView) && curOrigin != pos) {
    if (!aKidFrame->HasView()) {
      // If the frame has moved, then we need to make sure any child views are
      // correctly positioned
      PositionChildViews(aKidFrame);
    }
  }

  aKidFrame->DidReflow(aPresContext, aReflowInput);
}

void nsContainerFrame::ReflowOverflowContainerChildren(
    nsPresContext* aPresContext, const ReflowInput& aReflowInput,
    OverflowAreas& aOverflowRects, ReflowChildFlags aFlags,
    nsReflowStatus& aStatus, ChildFrameMerger aMergeFunc,
    Maybe<nsSize> aContainerSize) {
  MOZ_ASSERT(aPresContext, "null pointer");

  nsFrameList* overflowContainers =
      DrainExcessOverflowContainersList(aMergeFunc);
  if (!overflowContainers) {
    return;  // nothing to reflow
  }

  nsOverflowContinuationTracker tracker(thisfalsefalse);
  bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids();

  for (nsIFrame* frame : *overflowContainers) {
    if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
      // frame's prevInFlow has moved, skip reflowing this frame;
      // it will get reflowed once it's been placed
      if (GetNextInFlow()) {
        // We report OverflowIncomplete status in this case to avoid our parent
        // deleting our next-in-flows which might destroy non-empty frames.
        nsReflowStatus status;
        status.SetOverflowIncomplete();
        aStatus.MergeCompletionStatusFrom(status);
      }
      continue;
    }

    auto ScrollableOverflowExceedsAvailableBSize =
        [this, &aReflowInput](nsIFrame* aFrame) {
          if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
            return false;
          }
          const auto parentWM = GetWritingMode();
          const nscoord scrollableOverflowRectBEnd =
              LogicalRect(parentWM,
                          aFrame->ScrollableOverflowRectRelativeToParent(),
                          GetSize())
                  .BEnd(parentWM);
          return scrollableOverflowRectBEnd > aReflowInput.AvailableBSize();
        };

    // If the available block-size has changed, or the existing scrollable
    // overflow's block-end exceeds it, we need to reflow even if the frame
    // isn't dirty.
    if (shouldReflowAllKids || frame->IsSubtreeDirty() ||
        ScrollableOverflowExceedsAvailableBSize(frame)) {
      nsIFrame* prevInFlow = frame->GetPrevInFlow();
      NS_ASSERTION(prevInFlow,
                   "overflow container frame must have a prev-in-flow");
      NS_ASSERTION(
          frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
          "overflow container frame must have overflow container bit set");
      WritingMode wm = frame->GetWritingMode();
      // Note: aReflowInput's available inline-size is technically wrong for us
      // to hand off to children here, because it doesn't account for the space
      // that's been used for the container's margin/border/padding (and some
      // other space that a concrete container type, e.g. fieldset and grid [1],
      // might reserve before setting up the available space for their
      // children). Since we don't have a way to query the specific available
      // inline-size each container type used, nor do we know how the container
      // computes its non-overflow-container children's inline-size, we just
      // unconditionally override the frame's inline-size, so that the available
      // inline-size for the children doesn't really matter anyway.
      //
      // [1] For example, fieldset uses its computed inline-size with padding as
      // the available inline-size to reflow its inner child frame.
      // https://searchfox.org/mozilla-central/rev/04f7743d94691fa24212fb43099f9d84c3bfc890/layout/forms/nsFieldSetFrame.cpp#535-536
      const LogicalSize availSpace = aReflowInput.AvailableSize(wm);

      StyleSizeOverrides sizeOverride;
      // We override current continuation's inline-size by using the
      // prev-in-flow's inline-size since both should be the same.
      sizeOverride.mStyleISize.emplace(
          StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(
              frame->StylePosition()->mBoxSizing == StyleBoxSizing::Border
                  ? prevInFlow->ISize(wm)
                  : prevInFlow->ContentISize(wm))));

      if (frame->IsFlexItem()) {
        // An overflow container's block-size must be 0.
        sizeOverride.mStyleBSize.emplace(
            StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(0)));
      }
      ReflowOutput desiredSize(wm);
      ReflowInput reflowInput(aPresContext, aReflowInput, frame, availSpace,
                              Nothing(), {}, sizeOverride);
      const nsSize containerSize =
          aContainerSize ? *aContainerSize
                         : aReflowInput.AvailableSize(wm).GetPhysicalSize(wm);
      const LogicalPoint pos(wm, prevInFlow->IStart(wm, containerSize), 0);
      nsReflowStatus frameStatus;

      ReflowChild(frame, aPresContext, desiredSize, reflowInput, wm, pos,
                  containerSize, aFlags, frameStatus, &tracker);
      FinishReflowChild(frame, aPresContext, desiredSize, &reflowInput, wm, pos,
                        containerSize, aFlags);

      // Handle continuations
      if (!frameStatus.IsFullyComplete()) {
        if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
          // Abspos frames can't cause their parent to be incomplete,
          // only overflow incomplete.
          frameStatus.SetOverflowIncomplete();
        } else {
          NS_ASSERTION(frameStatus.IsComplete(),
                       "overflow container frames can't be incomplete, only "
                       "overflow-incomplete");
        }

        // Acquire a next-in-flow, creating it if necessary
        nsIFrame* nif = frame->GetNextInFlow();
        if (!nif) {
          NS_ASSERTION(frameStatus.NextInFlowNeedsReflow(),
                       "Someone forgot a NextInFlowNeedsReflow flag");
          nif = PresShell()->FrameConstructor()->CreateContinuingFrame(frame,
                                                                       this);
        } else if (!nif->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
          // used to be a normal next-in-flow; steal it from the child list
          nif->GetParent()->StealFrame(nif);
        }

        tracker.Insert(nif, frameStatus);
      }
      aStatus.MergeCompletionStatusFrom(frameStatus);
      // At this point it would be nice to assert
      // !frame->GetOverflowRect().IsEmpty(), but we have some unsplittable
      // frames that, when taller than availableHeight will push zero-height
      // content into a next-in-flow.
    } else {
      tracker.Skip(frame, aStatus);
      if (aReflowInput.mFloatManager) {
        nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager,
                                       aReflowInput.GetWritingMode(),
                                       aReflowInput.ComputedPhysicalSize());
      }
    }
    ConsiderChildOverflow(aOverflowRects, frame, /* aAsIfScrolled = */ false);
  }
}

void nsContainerFrame::DisplayOverflowContainers(
    nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
  nsFrameList* overflowconts = GetOverflowContainers();
  if (overflowconts) {
    for (nsIFrame* frame : *overflowconts) {
      BuildDisplayListForChild(aBuilder, frame, aLists);
    }
  }
}

bool nsContainerFrame::TryRemoveFrame(FrameListPropertyDescriptor aProp,
                                      nsIFrame* aChildToRemove) {
  nsFrameList* list = GetProperty(aProp);
  if (list && list->StartRemoveFrame(aChildToRemove)) {
    // aChildToRemove *may* have been removed from this list.
    if (list->IsEmpty()) {
      Unused << TakeProperty(aProp);
      list->Delete(PresShell());
    }
    return true;
  }
  return false;
}

bool nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild) {
  bool removed = false;
  if (MOZ_UNLIKELY(aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER))) {
    // Try removing from the overflow container list.
    removed = TryRemoveFrame(OverflowContainersProperty(), aChild);
    if (!removed) {
      // It might be in the excess overflow container list.
      removed = TryRemoveFrame(ExcessOverflowContainersProperty(), aChild);
    }
  }
  return removed;
}

void nsContainerFrame::StealFrame(nsIFrame* aChild) {
#ifdef DEBUG
  if (!mFrames.ContainsFrame(aChild)) {
    nsFrameList* list = GetOverflowFrames();
    if (!list || !list->ContainsFrame(aChild)) {
      list = GetOverflowContainers();
      if (!list || !list->ContainsFrame(aChild)) {
        list = GetExcessOverflowContainers();
        MOZ_ASSERT(list && list->ContainsFrame(aChild),
                   "aChild isn't our child"
                   " or on a frame list not supported by StealFrame");
      }
    }
  }
#endif

  if (MaybeStealOverflowContainerFrame(aChild)) {
    return;
  }

  // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers
  // on the normal lists so we might get here also if the frame bit
  // NS_FRAME_IS_OVERFLOW_CONTAINER is set.
  if (mFrames.StartRemoveFrame(aChild)) {
    return;
  }

  // We didn't find the child in our principal child list.
  // Maybe it's on the overflow list?
  nsFrameList* frameList = GetOverflowFrames();
  if (frameList && frameList->ContinueRemoveFrame(aChild)) {
    if (frameList->IsEmpty()) {
      DestroyOverflowList();
    }
    return;
  }

  MOZ_ASSERT_UNREACHABLE("StealFrame: can't find aChild");
}

nsFrameList nsContainerFrame::StealFramesAfter(nsIFrame* aChild) {
  NS_ASSERTION(
      !aChild || !aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
      "StealFramesAfter doesn't handle overflow containers");
  NS_ASSERTION(!IsBlockFrame(), "unexpected call");

  if (!aChild) {
    return std::move(mFrames);
  }

  for (nsIFrame* f : mFrames) {
    if (f == aChild) {
      return mFrames.TakeFramesAfter(f);
    }
  }

  // We didn't find the child in the principal child list.
  // Maybe it's on the overflow list?
  if (nsFrameList* overflowFrames = GetOverflowFrames()) {
    for (nsIFrame* f : *overflowFrames) {
      if (f == aChild) {
        return mFrames.TakeFramesAfter(f);
      }
    }
  }

  NS_ERROR("StealFramesAfter: can't find aChild");
  return nsFrameList();
}

/*
 * Create a next-in-flow for aFrame. Will return the newly created
 * frame <b>if and only if</b> a new frame is created; otherwise
 * nullptr is returned.
 */

nsIFrame* nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame) {
  MOZ_ASSERT(
      !IsBlockFrame(),
      "you should have called nsBlockFrame::CreateContinuationFor instead");
  MOZ_ASSERT(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");

  nsIFrame* nextInFlow = aFrame->GetNextInFlow();
  if (nullptr == nextInFlow) {
    // Create a continuation frame for the child frame and insert it
    // into our child list.
    nextInFlow =
        PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame, this);
    mFrames.InsertFrame(nullptr, aFrame, nextInFlow);

    NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
                 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
                  aFrame, nextInFlow));

    return nextInFlow;
  }
  return nullptr;
}

/**
 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and
 * flow pointers
 */

void nsContainerFrame::DeleteNextInFlowChild(DestroyContext& aContext,
                                             nsIFrame* aNextInFlow,
                                             bool aDeletingEmptyFrames) {
#ifdef DEBUG
  nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
#endif
  MOZ_ASSERT(prevInFlow, "bad prev-in-flow");

  // If the next-in-flow has a next-in-flow then delete it, too (and
  // delete it first).
  // Do this in a loop so we don't overflow the stack for frames
  // with very many next-in-flows
  nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
  if (nextNextInFlow) {
    AutoTArray<nsIFrame*, 8> frames;
    for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
      frames.AppendElement(f);
    }
    for (nsIFrame* delFrame : Reversed(frames)) {
      nsContainerFrame* parent = delFrame->GetParent();
      parent->DeleteNextInFlowChild(aContext, delFrame, aDeletingEmptyFrames);
    }
  }

  // Take the next-in-flow out of the parent's child list
  StealFrame(aNextInFlow);

#ifdef DEBUG
  if (aDeletingEmptyFrames) {
    nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
  }
#endif

  // Delete the next-in-flow frame and its descendants. This will also
  // remove it from its next-in-flow/prev-in-flow chain.
  aNextInFlow->Destroy(aContext);

  MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
}

void nsContainerFrame::PushChildrenToOverflow(nsIFrame* aFromChild,
                                              nsIFrame* aPrevSibling) {
  MOZ_ASSERT(aFromChild, "null pointer");
  MOZ_ASSERT(aPrevSibling, "pushing first child");
  MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");

  // Add the frames to our overflow list (let our next in flow drain
  // our overflow list when it is ready)
  SetOverflowFrames(mFrames.TakeFramesAfter(aPrevSibling));
}

bool nsContainerFrame::PushIncompleteChildren(
    const FrameHashtable& aPushedItems, const FrameHashtable& aIncompleteItems,
    const FrameHashtable& aOverflowIncompleteItems) {
  MOZ_ASSERT(IsFlexOrGridContainer(),
             "Only Grid / Flex containers can call this!");

  if (aPushedItems.IsEmpty() && aIncompleteItems.IsEmpty() &&
      aOverflowIncompleteItems.IsEmpty()) {
    return false;
  }

  // Iterate the children in normal document order and append them (or a NIF)
  // to one of the following frame lists according to their status.
  nsFrameList pushedList;
  nsFrameList incompleteList;
  nsFrameList overflowIncompleteList;
  auto* fc = PresShell()->FrameConstructor();
  for (nsIFrame* child = PrincipalChildList().FirstChild(); child;) {
    MOZ_ASSERT((aPushedItems.Contains(child) ? 1 : 0) +
                       (aIncompleteItems.Contains(child) ? 1 : 0) +
                       (aOverflowIncompleteItems.Contains(child) ? 1 : 0) <=
                   1,
               "child should only be in one of these sets");
    // Save the next-sibling so we can continue the loop if |child| is moved.
    nsIFrame* next = child->GetNextSibling();
    if (aPushedItems.Contains(child)) {
      MOZ_ASSERT(child->GetParent() == this);
      StealFrame(child);
      pushedList.AppendFrame(nullptr, child);
    } else if (aIncompleteItems.Contains(child)) {
      nsIFrame* childNIF = child->GetNextInFlow();
      if (!childNIF) {
        childNIF = fc->CreateContinuingFrame(child, this);
        incompleteList.AppendFrame(nullptr, childNIF);
      } else {
        auto* parent = childNIF->GetParent();
        MOZ_ASSERT(parent != this || !mFrames.ContainsFrame(childNIF),
                   "child's NIF shouldn't be in the same principal list");
        // If child's existing NIF is an overflow container, convert it to an
        // actual NIF, since now |child| has non-overflow stuff to give it.
        // Or, if it's further away then our next-in-flow, then pull it up.
        if (childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) ||
            (parent != this && parent != GetNextInFlow())) {
          parent->StealFrame(childNIF);
          childNIF->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
          if (parent == this) {
            incompleteList.AppendFrame(nullptr, childNIF);
          } else {
            // If childNIF already lives on the next fragment, then we
            // don't need to reparent it, since we know it's destined to end
            // up there anyway.  Just move it to its parent's overflow list.
            if (parent == GetNextInFlow()) {
              nsFrameList toMove(childNIF, childNIF);
              parent->MergeSortedOverflow(toMove);
            } else {
              ReparentFrame(childNIF, parent, this);
              incompleteList.AppendFrame(nullptr, childNIF);
            }
          }
        }
      }
    } else if (aOverflowIncompleteItems.Contains(child)) {
      nsIFrame* childNIF = child->GetNextInFlow();
      if (!childNIF) {
        childNIF = fc->CreateContinuingFrame(child, this);
        childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
        overflowIncompleteList.AppendFrame(nullptr, childNIF);
      } else {
        DebugOnly<nsContainerFrame*> lastParent = this;
        auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
        // If child has any non-overflow-container NIFs, convert them to
        // overflow containers, since that's all |child| needs now.
        while (childNIF &&
               !childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
          auto* parent = childNIF->GetParent();
          parent->StealFrame(childNIF);
          childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
          if (parent == this) {
            overflowIncompleteList.AppendFrame(nullptr, childNIF);
          } else {
            if (!nif || parent == nif) {
              nsFrameList toMove(childNIF, childNIF);
              parent->MergeSortedExcessOverflowContainers(toMove);
            } else {
              ReparentFrame(childNIF, parent, nif);
              nsFrameList toMove(childNIF, childNIF);
              nif->MergeSortedExcessOverflowContainers(toMove);
            }
            // We only need to reparent the first childNIF (or not at all if
            // its parent is our NIF).
            nif = nullptr;
          }
          lastParent = parent;
          childNIF = childNIF->GetNextInFlow();
        }
      }
    }
    child = next;
  }

  // Merge the results into our respective overflow child lists.
  if (!pushedList.IsEmpty()) {
    MergeSortedOverflow(pushedList);
  }
  if (!incompleteList.IsEmpty()) {
    MergeSortedOverflow(incompleteList);
  }
  if (!overflowIncompleteList.IsEmpty()) {
    // If our next-in-flow already has overflow containers list, merge the
    // overflowIncompleteList into that list. Otherwise, merge it into our
    // excess overflow containers list, to be drained by our next-in-flow.
    auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
    nsFrameList* oc = nif ? nif->GetOverflowContainers() : nullptr;
    if (oc) {
      ReparentFrames(overflowIncompleteList, this, nif);
      MergeSortedFrameLists(*oc, overflowIncompleteList, GetContent());
    } else {
      MergeSortedExcessOverflowContainers(overflowIncompleteList);
    }
  }
  return true;
}

void nsContainerFrame::NormalizeChildLists() {
  MOZ_ASSERT(IsFlexOrGridContainer(),
             "Only Flex / Grid containers can call this!");

  // Note: the following description uses grid container as an example. Flex
  // container is similar.
  //
  // First we gather child frames we should include in our reflow/placement,
  // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow
  // children (that might now fit). It's important to note that these children
  // can be in arbitrary order vis-a-vis the current children in our lists.
  // E.g. grid items in the document order: A, B, C may be placed in the rows
  // 3, 2, 1.  Assume each row goes in a separate grid container fragment,
  // and we reflow the second fragment.  Now if C (in fragment 1) overflows,
  // we can't just prepend it to our mFrames like we usually do because that
  // would violate the document order invariant that other code depends on.
  // Similarly if we pull up child A (from fragment 3) we can't just append
  // that for the same reason.  Instead, we must sort these children into
  // our child lists.  (The sorting is trivial given that both lists are
  // already fully sorted individually - it's just a merge.)
  //
  // The invariants that we maintain are that each grid container child list
  // is sorted in the normal document order at all times, but that children
  // in different grid container continuations may be in arbitrary order.

  const auto didPushItemsBit = IsFlexContainerFrame()
                                   ? NS_STATE_FLEX_DID_PUSH_ITEMS
                                   : NS_STATE_GRID_DID_PUSH_ITEMS;
  const auto hasChildNifBit = IsFlexContainerFrame()
                                  ? NS_STATE_FLEX_HAS_CHILD_NIFS
                                  : NS_STATE_GRID_HAS_CHILD_NIFS;

  auto* prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow());
  // Merge overflow frames from our prev-in-flow into our principal child list.
  if (prevInFlow) {
    AutoFrameListPtr overflow(PresContext(), prevInFlow->StealOverflowFrames());
    if (overflow) {
      ReparentFrames(*overflow, prevInFlow, this);
      MergeSortedFrameLists(mFrames, *overflow, GetContent());

      // Move trailing next-in-flows into our overflow list.
      nsFrameList continuations;
      for (nsIFrame* f = mFrames.FirstChild(); f;) {
        nsIFrame* next = f->GetNextSibling();
        nsIFrame* pif = f->GetPrevInFlow();
        if (pif && pif->GetParent() == this) {
          mFrames.RemoveFrame(f);
          continuations.AppendFrame(nullptr, f);
        }
        f = next;
      }
      MergeSortedOverflow(continuations);

      // Move prev-in-flow's excess overflow containers list into our own
      // overflow containers list. If we already have an excess overflow
      // containers list, any child in that list which doesn't have a
      // prev-in-flow in this frame is also merged into our overflow container
      // list.
      nsFrameList* overflowContainers =
          DrainExcessOverflowContainersList(MergeSortedFrameListsFor);

      // Move trailing OC next-in-flows into our excess overflow containers
      // list.
      if (overflowContainers) {
        nsFrameList moveToEOC;
        for (nsIFrame* f = overflowContainers->FirstChild(); f;) {
          nsIFrame* next = f->GetNextSibling();
          nsIFrame* pif = f->GetPrevInFlow();
          if (pif && pif->GetParent() == this) {
            overflowContainers->RemoveFrame(f);
            moveToEOC.AppendFrame(nullptr, f);
          }
          f = next;
        }
        if (overflowContainers->IsEmpty()) {
          DestroyOverflowContainers();
        }
        MergeSortedExcessOverflowContainers(moveToEOC);
      }
    }
  }

  // For each item in aItems, pull up its next-in-flow (if any), and reparent it
  // to our next-in-flow, unless its parent is already ourselves or our
  // next-in-flow (to avoid leaving a hole there).
  auto PullItemsNextInFlow = [this](const nsFrameList& aItems) {
    auto* firstNIF = static_cast<nsContainerFrame*>(GetNextInFlow());
    if (!firstNIF) {
      return;
    }
    nsFrameList childNIFs;
    nsFrameList childOCNIFs;
    for (auto* child : aItems) {
      if (auto* childNIF = child->GetNextInFlow()) {
        if (auto* parent = childNIF->GetParent();
            parent != this && parent != firstNIF) {
          parent->StealFrame(childNIF);
          ReparentFrame(childNIF, parent, firstNIF);
          if (childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
            childOCNIFs.AppendFrame(nullptr, childNIF);
          } else {
            childNIFs.AppendFrame(nullptr, childNIF);
          }
        }
      }
    }
    // Merge aItems' NIFs into our NIF's respective overflow child lists.
    firstNIF->MergeSortedOverflow(childNIFs);
--> --------------------

--> maximum size reached

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

Messung V0.5
C=89 H=97 G=93

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.19Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






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 und die Messung sind noch experimentell.