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