tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCanvasFrame.cpp (25677B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* rendering object that goes directly inside the document's scrollbars */
      8 
      9 #include "nsCanvasFrame.h"
     10 
     11 #include "gfxContext.h"
     12 #include "gfxPlatform.h"
     13 #include "gfxUtils.h"
     14 #include "mozilla/BasePrincipal.h"
     15 #include "mozilla/ComputedStyle.h"
     16 #include "mozilla/PresShell.h"
     17 #include "mozilla/ScrollContainerFrame.h"
     18 #include "mozilla/StaticPrefs_browser.h"
     19 #include "mozilla/StaticPrefs_layout.h"
     20 #include "mozilla/dom/AnonymousContent.h"
     21 #include "mozilla/layers/RenderRootStateManager.h"
     22 #include "mozilla/layers/StackingContextHelper.h"
     23 #include "nsCSSFrameConstructor.h"
     24 #include "nsCSSRendering.h"
     25 #include "nsContainerFrame.h"
     26 #include "nsContentCreatorFunctions.h"
     27 #include "nsDisplayList.h"
     28 #include "nsFrameManager.h"
     29 #include "nsGkAtoms.h"
     30 #include "nsIFrameInlines.h"
     31 #include "nsPresContext.h"
     32 
     33 using namespace mozilla;
     34 using namespace mozilla::dom;
     35 using namespace mozilla::layout;
     36 using namespace mozilla::gfx;
     37 using namespace mozilla::layers;
     38 
     39 nsCanvasFrame* NS_NewCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
     40  return new (aPresShell) nsCanvasFrame(aStyle, aPresShell->GetPresContext());
     41 }
     42 
     43 nsIPopupContainer* nsIPopupContainer::GetPopupContainer(PresShell* aPresShell) {
     44  return aPresShell ? aPresShell->GetCanvasFrame() : nullptr;
     45 }
     46 
     47 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
     48 
     49 NS_QUERYFRAME_HEAD(nsCanvasFrame)
     50  NS_QUERYFRAME_ENTRY(nsCanvasFrame)
     51  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
     52  NS_QUERYFRAME_ENTRY(nsIPopupContainer)
     53 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
     54 
     55 nsresult nsCanvasFrame::CreateAnonymousContent(
     56    nsTArray<ContentInfo>& aElements) {
     57  if (!mContent) {
     58    return NS_OK;
     59  }
     60 
     61  Document* doc = mContent->OwnerDoc();
     62 
     63  // Create a default tooltip element for system privileged documents.
     64  if (XRE_IsParentProcess() && doc->NodePrincipal()->IsSystemPrincipal()) {
     65    nsNodeInfoManager* nodeInfoManager = doc->NodeInfoManager();
     66    RefPtr<NodeInfo> nodeInfo = nodeInfoManager->GetNodeInfo(
     67        nsGkAtoms::tooltip, nullptr, kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
     68 
     69    nsresult rv = NS_NewXULElement(getter_AddRefs(mTooltipContent),
     70                                   nodeInfo.forget(), dom::NOT_FROM_PARSER);
     71    NS_ENSURE_SUCCESS(rv, rv);
     72 
     73    mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_default, u"true"_ns,
     74                             false);
     75    // Set the page attribute so XULTooltipElement::PostHandleEvent will find
     76    // the text for the tooltip from the currently hovered element.
     77    mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::page, u"true"_ns,
     78                             false);
     79 
     80    mTooltipContent->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
     81                                 reinterpret_cast<void*>(true));
     82 
     83    aElements.AppendElement(mTooltipContent);
     84  }
     85 
     86 #ifdef DEBUG
     87  for (auto& element : aElements) {
     88    MOZ_ASSERT(element.mContent->GetProperty(
     89                   nsGkAtoms::docLevelNativeAnonymousContent),
     90               "NAC from the canvas frame needs to be document-level, otherwise"
     91               " it (1) inherits from the document which is unexpected, and (2)"
     92               " StyleChildrenIterator won't be able to find it properly");
     93  }
     94 #endif
     95  return NS_OK;
     96 }
     97 
     98 void nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
     99                                             uint32_t aFilter) {
    100  if (mTooltipContent) {
    101    aElements.AppendElement(mTooltipContent);
    102  }
    103 }
    104 
    105 void nsCanvasFrame::Destroy(DestroyContext& aContext) {
    106  if (mTooltipContent) {
    107    aContext.AddAnonymousContent(mTooltipContent.forget());
    108  }
    109  nsContainerFrame::Destroy(aContext);
    110 }
    111 
    112 void nsCanvasFrame::SetInitialChildList(ChildListID aListID,
    113                                        nsFrameList&& aChildList) {
    114  NS_ASSERTION(aListID != FrameChildListID::Principal || aChildList.IsEmpty() ||
    115                   aChildList.OnlyChild(),
    116               "Primary child list can have at most one frame in it");
    117  nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList));
    118 }
    119 
    120 void nsCanvasFrame::AppendFrames(ChildListID aListID,
    121                                 nsFrameList&& aFrameList) {
    122 #ifdef DEBUG
    123  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
    124  if (!mFrames.IsEmpty()) {
    125    for (nsIFrame* f : aFrameList) {
    126      // We only allow native anonymous child frames to be in principal child
    127      // list in canvas frame.
    128      MOZ_ASSERT(f->GetContent()->IsInNativeAnonymousSubtree(),
    129                 "invalid child list");
    130    }
    131  }
    132  nsIFrame::VerifyDirtyBitSet(aFrameList);
    133 #endif
    134  nsContainerFrame::AppendFrames(aListID, std::move(aFrameList));
    135 }
    136 
    137 void nsCanvasFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
    138                                 const nsLineList::iterator* aPrevFrameLine,
    139                                 nsFrameList&& aFrameList) {
    140  // Because we only support a single child frame inserting is the same
    141  // as appending
    142  MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
    143  AppendFrames(aListID, std::move(aFrameList));
    144 }
    145 
    146 #ifdef DEBUG
    147 void nsCanvasFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID,
    148                                nsIFrame* aOldFrame) {
    149  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
    150  nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame);
    151 }
    152 #endif
    153 
    154 nsRect nsCanvasFrame::CanvasArea() const {
    155  // Not clear which overflow rect we want here, but it probably doesn't
    156  // matter.
    157  nsRect result(InkOverflowRect());
    158 
    159  if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(GetParent())) {
    160    nsRect portRect = scrollContainerFrame->GetScrollPortRect();
    161    result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size()));
    162  }
    163  return result;
    164 }
    165 
    166 Element* nsCanvasFrame::GetDefaultTooltip() { return mTooltipContent; }
    167 
    168 void nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
    169                                           gfxContext* aCtx) {
    170  auto* frame = static_cast<nsCanvasFrame*>(mFrame);
    171  nsPoint offset = ToReferenceFrame();
    172  nsRect bgClipRect = frame->CanvasArea() + offset;
    173 
    174  PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &bgClipRect);
    175 }
    176 
    177 bool nsDisplayCanvasBackgroundImage::IsSingleFixedPositionImage(
    178    nsDisplayListBuilder* aBuilder, const nsRect& aClipRect,
    179    gfxRect* aDestRect) {
    180  if (!mBackgroundStyle) {
    181    return false;
    182  }
    183 
    184  if (mBackgroundStyle->StyleBackground()->mImage.mLayers.Length() != 1) {
    185    return false;
    186  }
    187 
    188  nsPresContext* presContext = mFrame->PresContext();
    189  uint32_t flags = aBuilder->GetBackgroundPaintFlags();
    190  nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
    191  const nsStyleImageLayers::Layer& layer =
    192      mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
    193 
    194  if (layer.mAttachment != StyleImageLayerAttachment::Fixed) {
    195    return false;
    196  }
    197 
    198  nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
    199      presContext, mFrame, flags, borderArea, aClipRect, layer);
    200 
    201  // We only care about images here, not gradients.
    202  if (!mIsRasterImage) {
    203    return false;
    204  }
    205 
    206  int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
    207  *aDestRect =
    208      nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
    209 
    210  return true;
    211 }
    212 
    213 void nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    214                                     const nsDisplayListSet& aLists) {
    215  MOZ_ASSERT(IsVisibleForPainting(),
    216             "::-moz-{scrolled-,}canvas doesn't inherit from anything that can "
    217             "be invisible, and we don't specify visibility in UA sheets");
    218  MOZ_ASSERT(!IsThemed(),
    219             "::-moz-{scrolled-,}canvas doesn't have native appearance");
    220  if (GetPrevInFlow()) {
    221    DisplayOverflowContainers(aBuilder, aLists);
    222    DisplayPushedAbsoluteFrames(aBuilder, aLists);
    223  }
    224 
    225  // Force a background to be shown. We may have a background propagated to us,
    226  // in which case StyleBackground wouldn't have the right background
    227  // and the code in nsIFrame::DisplayBorderBackgroundOutline might not give us
    228  // a background.
    229  // We don't have any border or outline, and our background draws over
    230  // the overflow area, so just add nsDisplayCanvasBackground instead of
    231  // calling DisplayBorderBackgroundOutline.
    232  ComputedStyle* bg = nullptr;
    233  nsIFrame* dependentFrame = nsCSSRendering::FindBackgroundFrame(this);
    234  if (dependentFrame) {
    235    bg = dependentFrame->Style();
    236    if (dependentFrame == this) {
    237      dependentFrame = nullptr;
    238    }
    239  }
    240 
    241  if (!bg) {
    242    return;
    243  }
    244 
    245  const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
    246 
    247  bool needBlendContainerForBackgroundBlendMode = false;
    248  nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
    249 
    250  const bool suppressBackgroundImage = [&] {
    251    // Handle print settings.
    252    if (!ComputeShouldPaintBackground().mImage) {
    253      return true;
    254    }
    255    // In high-contrast-mode, we suppress background-image on the canvas frame
    256    // (even when backplating), because users expect site backgrounds to
    257    // conform to their HCM background color when a solid color is rendered,
    258    // and some websites use solid-color images instead of an overwritable
    259    // background color.
    260    if (PresContext()->ForcingColors() &&
    261        StaticPrefs::
    262            browser_display_suppress_canvas_background_image_on_forced_colors()) {
    263      return true;
    264    }
    265    return false;
    266  }();
    267 
    268  const bool isPage = GetParent()->IsPageContentFrame();
    269  const auto& canvasBg = PresShell()->GetCanvasBackground(isPage);
    270 
    271  // Note this list is important so that our blend container only captures our
    272  // own items.
    273  nsDisplayList list(aBuilder);
    274 
    275  // Put a scrolled background color item in place, at the bottom of the list.
    276  //
    277  // If the canvas background is specified by CSS, we must paint it. If it's
    278  // not, we don't need to paint it, but we still want to if we can without
    279  // compromising blending correctness.
    280  //
    281  // Painting this extra background used to be desirable for performance in the
    282  // FrameLayerBuilder era. It's unclear whether it still is (probably not), but
    283  // changing it causes a lot of fuzzy changes due to subpixel AA (not
    284  // necessarily regressions, tho?).
    285  //
    286  // NOTE(emilio): We used to have an optimization to try _not_ to draw it if
    287  // there was a fixed image (layers.mImageCount > 0 &&
    288  // layers.mLayers[0].mAttachment == StyleImageLayerAttachment::Fixed), but
    289  // it's unclear it was fully correct (didn't check for mix-blend-mode), and it
    290  // complicates quite a bit the logic. If it's useful for performance on real
    291  // world websites we could try to re-introduce it.
    292  nsDisplaySolidColor* backgroundColorItem = nullptr;
    293  if (NS_GET_A(canvasBg.mColor)) {
    294    // Note that if CSS didn't specify the background, it can't really be
    295    // semi-transparent.
    296    MOZ_ASSERT(
    297        canvasBg.mCSSSpecified || NS_GET_A(canvasBg.mColor) == 255,
    298        "Default canvas background should either be transparent or opaque");
    299    backgroundColorItem = MakeDisplayItem<nsDisplaySolidColor>(
    300        aBuilder, this,
    301        CanvasArea() + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
    302        canvasBg.mColor);
    303    list.AppendToTop(backgroundColorItem);
    304  }
    305 
    306  // Create separate items for each background layer.
    307  const nsStyleImageLayers& layers = bg->StyleBackground()->mImage;
    308  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
    309    if (layers.mLayers[i].mImage.IsNone() || suppressBackgroundImage) {
    310      continue;
    311    }
    312 
    313    nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
    314 
    315    const ActiveScrolledRoot* thisItemASR = asr;
    316    nsDisplayList thisItemList(aBuilder);
    317    nsDisplayBackgroundImage::InitData bgData =
    318        nsDisplayBackgroundImage::GetInitData(aBuilder, this, i, bgRect, bg);
    319 
    320    if (bgData.shouldFixToViewport) {
    321      auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
    322      nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
    323          aBuilder, this, aBuilder->GetVisibleRect(), aBuilder->GetDirtyRect());
    324 
    325      DisplayListClipState::AutoSaveRestore clipState(aBuilder);
    326      nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
    327          aBuilder);
    328      if (displayData) {
    329        const nsPoint offset = GetOffsetTo(PresShell()->GetRootFrame());
    330        aBuilder->SetVisibleRect(displayData->mVisibleRect + offset);
    331        aBuilder->SetDirtyRect(displayData->mDirtyRect + offset);
    332 
    333        clipState.SetClipChainForContainingBlockDescendants(
    334            displayData->mContainingBlockClipChain);
    335        asrSetter.SetCurrentActiveScrolledRoot(
    336            displayData->mContainingBlockActiveScrolledRoot);
    337        asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
    338        thisItemASR = displayData->mContainingBlockActiveScrolledRoot;
    339      }
    340      nsDisplayCanvasBackgroundImage* bgItem = nullptr;
    341      {
    342        DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
    343        bgImageClip.Clear();
    344        bgItem = MakeDisplayItemWithIndex<nsDisplayCanvasBackgroundImage>(
    345            aBuilder, this, /* aIndex = */ i, bgData);
    346        if (bgItem) {
    347          bgItem->SetDependentFrame(aBuilder, dependentFrame);
    348        }
    349      }
    350      if (bgItem) {
    351        const ActiveScrolledRoot* scrollTargetASR =
    352            asr ? asr->GetNearestScrollASR() : nullptr;
    353        thisItemList.AppendToTop(
    354            nsDisplayFixedPosition::CreateForFixedBackground(
    355                aBuilder, this, nullptr, bgItem, i, scrollTargetASR));
    356      }
    357 
    358    } else {
    359      nsDisplayCanvasBackgroundImage* bgItem =
    360          MakeDisplayItemWithIndex<nsDisplayCanvasBackgroundImage>(
    361              aBuilder, this, /* aIndex = */ i, bgData);
    362      if (bgItem) {
    363        bgItem->SetDependentFrame(aBuilder, dependentFrame);
    364        thisItemList.AppendToTop(bgItem);
    365      }
    366    }
    367 
    368    if (layers.mLayers[i].mBlendMode != StyleBlend::Normal) {
    369      DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
    370      thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
    371          aBuilder, this, i + 1, &thisItemList, layers.mLayers[i].mBlendMode,
    372          thisItemASR, nsDisplayItem::ContainerASRType::Constant, true);
    373      needBlendContainerForBackgroundBlendMode = true;
    374    }
    375    list.AppendToTop(&thisItemList);
    376  }
    377 
    378  if (needBlendContainerForBackgroundBlendMode) {
    379    const ActiveScrolledRoot* containerASR = contASRTracker.GetContainerASR();
    380    DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
    381    list.AppendToTop(nsDisplayBlendContainer::CreateForBackgroundBlendMode(
    382        aBuilder, this, nullptr, &list, containerASR,
    383        nsDisplayItem::ContainerASRType::AncestorOfContained));
    384  }
    385 
    386  aLists.BorderBackground()->AppendToTop(&list);
    387 
    388  for (nsIFrame* kid : PrincipalChildList()) {
    389    // Put our child into its own pseudo-stack.
    390    BuildDisplayListForChild(aBuilder, kid, aLists);
    391  }
    392 
    393  if (!canvasBg.mCSSSpecified && backgroundColorItem &&
    394      (needBlendContainerForBackgroundBlendMode ||
    395       aBuilder->ContainsBlendMode())) {
    396    // We can't draw the scrolled canvas background without compromising
    397    // correctness, since the non-CSS-specified background is not supposed to be
    398    // part of the blend group. Suppress it by making it transparent.
    399    backgroundColorItem->OverrideColor(NS_TRANSPARENT);
    400  }
    401 }
    402 
    403 nscoord nsCanvasFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    404                                      IntrinsicISizeType aType) {
    405  return mFrames.IsEmpty()
    406             ? 0
    407             : mFrames.FirstChild()->IntrinsicISize(aInput, aType);
    408 }
    409 
    410 void nsCanvasFrame::Reflow(nsPresContext* aPresContext,
    411                           ReflowOutput& aDesiredSize,
    412                           const ReflowInput& aReflowInput,
    413                           nsReflowStatus& aStatus) {
    414  MarkInReflow();
    415  DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
    416  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    417  NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
    418 
    419  auto* prevCanvasFrame = static_cast<nsCanvasFrame*>(GetPrevInFlow());
    420  if (prevCanvasFrame) {
    421    AutoFrameListPtr overflow(aPresContext,
    422                              prevCanvasFrame->StealOverflowFrames());
    423    if (overflow) {
    424      NS_ASSERTION(overflow->OnlyChild(),
    425                   "must have doc root as canvas frame's only child");
    426      // Prepend overflow to the our child list. There may already be
    427      // children placeholders for fixed-pos elements, which don't get
    428      // reflowed but must not be lost until the canvas frame is destroyed.
    429      mFrames.InsertFrames(this, nullptr, std::move(*overflow));
    430    }
    431  }
    432 
    433  // Set our size up front, since some parts of reflow depend on it
    434  // being already set.  Note that the computed height may be
    435  // unconstrained; that's ok.  Consumers should watch out for that.
    436  SetSize(aReflowInput.ComputedPhysicalSize());
    437 
    438  // Reflow our children.  Typically, we only have one child - the root
    439  // element's frame or a placeholder for that frame, if the root element
    440  // is abs-pos or fixed-pos.  Note that this child might be missing though
    441  // if that frame was Complete in one of our earlier continuations.  This
    442  // happens when we create additional pages purely to make room for painting
    443  // overflow (painted by BuildPreviousPageOverflow in nsPageFrame.cpp).
    444  // We may have additional children which are placeholders for continuations
    445  // of fixed-pos content, see nsCSSFrameConstructor::ReplicateFixedFrames.
    446  const WritingMode wm = aReflowInput.GetWritingMode();
    447  aDesiredSize.SetSize(wm, aReflowInput.ComputedSize());
    448  if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
    449    // Set the block-size to zero for now in case we don't have any non-
    450    // placeholder children that would update the size in the loop below.
    451    aDesiredSize.BSize(wm) = nscoord(0);
    452  }
    453  aDesiredSize.SetOverflowAreasToDesiredBounds();
    454  nsIFrame* nextKid = nullptr;
    455  for (auto* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = nextKid) {
    456    nextKid = kidFrame->GetNextSibling();
    457    ReflowOutput kidDesiredSize(aReflowInput);
    458    bool kidDirty = kidFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY);
    459    WritingMode kidWM = kidFrame->GetWritingMode();
    460    auto availableSize = aReflowInput.AvailableSize(kidWM);
    461    nscoord bOffset = 0;
    462    nscoord canvasBSizeSum = 0;
    463    if (prevCanvasFrame && availableSize.BSize(kidWM) != NS_UNCONSTRAINEDSIZE &&
    464        !kidFrame->IsPlaceholderFrame() &&
    465        StaticPrefs::layout_display_list_improve_fragmentation()) {
    466      for (auto* pif = prevCanvasFrame; pif;
    467           pif = static_cast<nsCanvasFrame*>(pif->GetPrevInFlow())) {
    468        canvasBSizeSum += pif->BSize(kidWM);
    469        auto* pifChild = pif->PrincipalChildList().FirstChild();
    470        if (pifChild) {
    471          nscoord layoutOverflow = pifChild->BSize(kidWM) - canvasBSizeSum;
    472          // A negative value means that the :root frame does not fill
    473          // the canvas.  In this case we can't determine the offset exactly
    474          // so we use the end edge of the scrollable overflow as the offset
    475          // instead.  This will likely push down the content below where it
    476          // should be placed, creating a gap.  That's preferred over making
    477          // content overlap which would otherwise occur.
    478          // See layout/reftests/pagination/inline-block-slice-7.html for an
    479          // example of this.
    480          if (layoutOverflow < 0) {
    481            LogicalRect so(kidWM, pifChild->ScrollableOverflowRect(),
    482                           pifChild->GetSize());
    483            layoutOverflow = so.BEnd(kidWM) - canvasBSizeSum;
    484          }
    485          bOffset = std::max(bOffset, layoutOverflow);
    486        }
    487      }
    488      availableSize.BSize(kidWM) -= bOffset;
    489    }
    490 
    491    if (MOZ_LIKELY(availableSize.BSize(kidWM) > 0)) {
    492      ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
    493                                 availableSize);
    494 
    495      if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
    496          kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
    497        // Tell our kid it's being block-dir resized too.  Bit of a
    498        // hack for framesets.
    499        kidReflowInput.SetBResize(true);
    500      }
    501 
    502      nsSize containerSize = aReflowInput.ComputedPhysicalSize();
    503      LogicalMargin margin = kidReflowInput.ComputedLogicalMargin(kidWM);
    504      LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
    505      (kidWM.IsOrthogonalTo(wm) ? kidPt.I(kidWM) : kidPt.B(kidWM)) += bOffset;
    506 
    507      nsReflowStatus kidStatus;
    508      ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput, kidWM,
    509                  kidPt, containerSize, ReflowChildFlags::Default, kidStatus);
    510 
    511      FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput,
    512                        kidWM, kidPt, containerSize,
    513                        ReflowChildFlags::ApplyRelativePositioning);
    514 
    515      if (!kidStatus.IsFullyComplete()) {
    516        nsIFrame* nextFrame = kidFrame->GetNextInFlow();
    517        NS_ASSERTION(nextFrame || kidStatus.NextInFlowNeedsReflow(),
    518                     "If it's incomplete and has no nif yet, it must flag a "
    519                     "nif reflow.");
    520        if (!nextFrame) {
    521          nextFrame = aPresContext->PresShell()
    522                          ->FrameConstructor()
    523                          ->CreateContinuingFrame(kidFrame, this);
    524          SetOverflowFrames(nsFrameList(nextFrame, nextFrame));
    525          // Root overflow containers will be normal children of
    526          // the canvas frame, but that's ok because there
    527          // aren't any other frames we need to isolate them from
    528          // during reflow.
    529        }
    530        if (kidStatus.IsOverflowIncomplete()) {
    531          nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
    532        }
    533      }
    534      aStatus.MergeCompletionStatusFrom(kidStatus);
    535 
    536      // If the child frame was just inserted, then we're responsible for making
    537      // sure it repaints
    538      if (kidDirty) {
    539        // But we have a new child, which will affect our background, so
    540        // invalidate our whole rect.
    541        // Note: Even though we request to be sized to our child's size, our
    542        // scroll frame ensures that we are always the size of the viewport.
    543        // Also note: GetPosition() on a CanvasFrame is always going to return
    544        // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect()
    545        // could also include overflow to our top and left (out of the viewport)
    546        // which doesn't need to be painted.
    547        nsIFrame* viewport = PresShell()->GetRootFrame();
    548        viewport->InvalidateFrame();
    549      }
    550 
    551      // Return our desired size. Normally it's what we're told, but sometimes
    552      // we can be given an unconstrained block-size (when a window is
    553      // sizing-to-content), and we should compute our desired block-size. This
    554      // is done by PresShell::ResizeReflow, when given the BSizeLimit flag.
    555      //
    556      // We do this here rather than at the viewport frame, because the canvas
    557      // is what draws the background, so it can extend a little bit more than
    558      // the real content without visual glitches, realistically.
    559      if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE &&
    560          !kidFrame->IsPlaceholderFrame()) {
    561        LogicalSize finalSize = aReflowInput.ComputedSize();
    562        finalSize.BSize(wm) = nsPresContext::RoundUpAppUnitsToCSSPixel(
    563            kidFrame->GetLogicalSize(wm).BSize(wm) +
    564            kidReflowInput.ComputedLogicalMargin(wm).BStartEnd(wm));
    565        aDesiredSize.SetSize(wm, finalSize);
    566        aDesiredSize.SetOverflowAreasToDesiredBounds();
    567      }
    568      aDesiredSize.mOverflowAreas.UnionWith(kidDesiredSize.mOverflowAreas +
    569                                            kidFrame->GetPosition());
    570    } else if (kidFrame->IsPlaceholderFrame()) {
    571      // Placeholders always fit even if there's no available block-size left.
    572    } else {
    573      // This only occurs in paginated mode.  There is no available space on
    574      // this page due to reserving space for overflow from a previous page,
    575      // so we push our child to the next page.  Note that we can have some
    576      // placeholders for fixed pos. frames in mFrames too, so we need to be
    577      // careful to only push `kidFrame`.
    578      mFrames.RemoveFrame(kidFrame);
    579      SetOverflowFrames(nsFrameList(kidFrame, kidFrame));
    580      aStatus.SetIncomplete();
    581    }
    582  }
    583 
    584  if (prevCanvasFrame) {
    585    ReflowOverflowContainerChildren(aPresContext, aReflowInput,
    586                                    aDesiredSize.mOverflowAreas,
    587                                    ReflowChildFlags::Default, aStatus);
    588  }
    589 
    590  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput,
    591                                 aStatus);
    592 
    593  NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
    594 }
    595 
    596 nsIContent* nsCanvasFrame::GetContentForEvent(const WidgetEvent* aEvent) const {
    597  if (nsIContent* content = nsIFrame::GetContentForEvent(aEvent)) {
    598    return content;
    599  }
    600  if (const nsIFrame* kid = mFrames.FirstChild()) {
    601    return kid->GetContentForEvent(aEvent);
    602  }
    603  return nullptr;
    604 }
    605 
    606 #ifdef DEBUG_FRAME_DUMP
    607 nsresult nsCanvasFrame::GetFrameName(nsAString& aResult) const {
    608  return MakeFrameName(u"Canvas"_ns, aResult);
    609 }
    610 #endif