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/. */
7 /* rendering object for the HTML <canvas> element */
9 #include "nsHTMLCanvasFrame.h"
11 #include "nsGkAtoms.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/dom/HTMLCanvasElement.h"
15 #include "mozilla/layers/ImageDataSerializer.h"
16 #include "mozilla/layers/WebRenderBridgeChild.h"
17 #include "mozilla/layers/WebRenderCanvasRenderer.h"
18 #include "mozilla/layers/RenderRootStateManager.h"
19 #include "BasicLayers.h"
20 #include "mozilla/webgpu/CanvasContext.h"
21 #include "nsDisplayList.h"
22 #include "nsLayoutUtils.h"
23 #include "nsStyleUtil.h"
24 #include "ImageLayers.h"
26 #include "ActiveLayerTracker.h"
30 using namespace mozilla
;
31 using namespace mozilla::dom
;
32 using namespace mozilla::layers
;
33 using namespace mozilla::gfx
;
35 /* Helper for our nsIFrame::GetIntrinsicSize() impl. Takes the result of
36 * "GetCanvasSize()" as a parameter, which may help avoid redundant
37 * indirect calls to GetCanvasSize().
39 * @param aCanvasSizeInPx The canvas's size in CSS pixels, as returned
41 * @return The canvas's intrinsic size, as an IntrinsicSize object.
43 static IntrinsicSize
IntrinsicSizeFromCanvasSize(
44 const nsIntSize
& aCanvasSizeInPx
) {
46 nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx
.width
),
47 nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx
.height
));
50 /* Helper for our nsIFrame::GetIntrinsicRatio() impl. Takes the result of
51 * "GetCanvasSize()" as a parameter, which may help avoid redundant
52 * indirect calls to GetCanvasSize().
54 * @return The canvas's intrinsic ratio.
56 static AspectRatio
IntrinsicRatioFromCanvasSize(
57 const nsIntSize
& aCanvasSizeInPx
) {
58 return AspectRatio::FromSize(aCanvasSizeInPx
.width
, aCanvasSizeInPx
.height
);
61 class nsDisplayCanvas final
: public nsPaintedDisplayItem
{
63 nsDisplayCanvas(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
64 : nsPaintedDisplayItem(aBuilder
, aFrame
) {
65 MOZ_COUNT_CTOR(nsDisplayCanvas
);
67 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCanvas
)
69 NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS
)
71 virtual nsRegion
GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
72 bool* aSnap
) const override
{
74 nsHTMLCanvasFrame
* f
= static_cast<nsHTMLCanvasFrame
*>(Frame());
75 HTMLCanvasElement
* canvas
= HTMLCanvasElement::FromNode(f
->GetContent());
77 if (canvas
->GetIsOpaque()) {
78 // OK, the entire region painted by the canvas is opaque. But what is
79 // that region? It's the canvas's "dest rect" (controlled by the
80 // object-fit/object-position CSS properties), clipped to the container's
81 // content box (which is what GetBounds() returns). So, we grab those
82 // rects and intersect them.
83 nsRect constraintRect
= GetBounds(aBuilder
, aSnap
);
85 // Need intrinsic size & ratio, for ComputeObjectDestRect:
86 nsIntSize canvasSize
= f
->GetCanvasSize();
87 IntrinsicSize intrinsicSize
= IntrinsicSizeFromCanvasSize(canvasSize
);
88 AspectRatio intrinsicRatio
= IntrinsicRatioFromCanvasSize(canvasSize
);
90 const nsRect destRect
= nsLayoutUtils::ComputeObjectDestRect(
91 constraintRect
, intrinsicSize
, intrinsicRatio
, f
->StylePosition());
92 return nsRegion(destRect
.Intersect(constraintRect
));
97 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
98 bool* aSnap
) const override
{
100 nsHTMLCanvasFrame
* f
= static_cast<nsHTMLCanvasFrame
*>(Frame());
101 return f
->GetInnerArea() + ToReferenceFrame();
104 virtual already_AddRefed
<Layer
> BuildLayer(
105 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
106 const ContainerLayerParameters
& aContainerParameters
) override
{
107 return static_cast<nsHTMLCanvasFrame
*>(mFrame
)->BuildLayer(
108 aBuilder
, aManager
, this, aContainerParameters
);
111 virtual bool CreateWebRenderCommands(
112 mozilla::wr::DisplayListBuilder
& aBuilder
,
113 wr::IpcResourceUpdateQueue
& aResources
, const StackingContextHelper
& aSc
,
114 mozilla::layers::RenderRootStateManager
* aManager
,
115 nsDisplayListBuilder
* aDisplayListBuilder
) override
{
116 HTMLCanvasElement
* element
=
117 static_cast<HTMLCanvasElement
*>(mFrame
->GetContent());
118 element
->HandlePrintCallback(mFrame
->PresContext());
120 switch (element
->GetCurrentContextType()) {
121 case CanvasContextType::Canvas2D
:
122 case CanvasContextType::WebGL1
:
123 case CanvasContextType::WebGL2
: {
125 RefPtr
<WebRenderCanvasData
> canvasData
=
126 aManager
->CommandBuilder()
127 .CreateOrRecycleWebRenderUserData
<WebRenderCanvasData
>(
129 nsHTMLCanvasFrame
* canvasFrame
=
130 static_cast<nsHTMLCanvasFrame
*>(mFrame
);
131 if (!canvasFrame
->UpdateWebRenderCanvasData(aDisplayListBuilder
,
135 WebRenderCanvasRendererAsync
* data
= canvasData
->GetCanvasRenderer();
137 data
->UpdateCompositableClient();
139 // Push IFrame for async image pipeline.
140 // XXX Remove this once partial display list update is supported.
142 nsIntSize canvasSizeInPx
= data
->GetSize();
143 IntrinsicSize intrinsicSize
=
144 IntrinsicSizeFromCanvasSize(canvasSizeInPx
);
145 AspectRatio intrinsicRatio
=
146 IntrinsicRatioFromCanvasSize(canvasSizeInPx
);
149 mFrame
->GetContentRectRelativeToSelf() + ToReferenceFrame();
150 nsRect dest
= nsLayoutUtils::ComputeObjectDestRect(
151 area
, intrinsicSize
, intrinsicRatio
, mFrame
->StylePosition());
153 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
154 dest
, mFrame
->PresContext()->AppUnitsPerDevPixel());
156 // We don't push a stacking context for this async image pipeline here.
157 // Instead, we do it inside the iframe that hosts the image. As a
158 // result, a bunch of the calculations normally done as part of that
159 // stacking context need to be done manually and pushed over to the
160 // parent side, where it will be done when we build the display list for
161 // the iframe. That happens in WebRenderCompositableHolder.
163 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
164 aBuilder
.PushIFrame(r
, !BackfaceIsHidden(), data
->GetPipelineId().ref(),
165 /*ignoreMissingPipelines*/ false);
167 LayoutDeviceRect
scBounds(LayoutDevicePoint(0, 0), bounds
.Size());
168 wr::ImageRendering filter
= wr::ToImageRendering(
169 nsLayoutUtils::GetSamplingFilterForFrame(mFrame
));
170 wr::MixBlendMode mixBlendMode
= wr::MixBlendMode::Normal
;
171 aManager
->WrBridge()->AddWebRenderParentCommand(
172 OpUpdateAsyncImagePipeline(data
->GetPipelineId().value(), scBounds
,
173 VideoInfo::Rotation::kDegree_0
, filter
,
177 case CanvasContextType::WebGPU
: {
178 nsHTMLCanvasFrame
* canvasFrame
=
179 static_cast<nsHTMLCanvasFrame
*>(mFrame
);
180 HTMLCanvasElement
* canvasElement
=
181 static_cast<HTMLCanvasElement
*>(canvasFrame
->GetContent());
182 webgpu::CanvasContext
* canvasContext
=
183 canvasElement
->GetWebGPUContext();
185 if (!canvasContext
) {
190 RefPtr
<WebRenderLocalCanvasData
> canvasData
=
191 aManager
->CommandBuilder()
192 .CreateOrRecycleWebRenderUserData
<WebRenderLocalCanvasData
>(
194 if (!canvasContext
->UpdateWebRenderLocalCanvasData(canvasData
)) {
198 nsIntSize canvasSizeInPx
= canvasFrame
->GetCanvasSize();
199 IntrinsicSize intrinsicSize
=
200 IntrinsicSizeFromCanvasSize(canvasSizeInPx
);
201 AspectRatio intrinsicRatio
=
202 IntrinsicRatioFromCanvasSize(canvasSizeInPx
);
204 mFrame
->GetContentRectRelativeToSelf() + ToReferenceFrame();
205 nsRect dest
= nsLayoutUtils::ComputeObjectDestRect(
206 area
, intrinsicSize
, intrinsicRatio
, mFrame
->StylePosition());
207 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
208 dest
, mFrame
->PresContext()->AppUnitsPerDevPixel());
210 const RGBDescriptor
rgbDesc(canvasSizeInPx
, canvasData
->mFormat
, false);
211 const auto targetStride
= ImageDataSerializer::GetRGBStride(rgbDesc
);
212 const bool preferCompositorSurface
= true;
213 const wr::ImageDescriptor
imageDesc(
214 canvasSizeInPx
, targetStride
, canvasData
->mFormat
,
215 wr::OpacityType::Opaque
, preferCompositorSurface
);
217 wr::ImageKey imageKey
;
218 auto imageKeyMaybe
= canvasContext
->GetImageKey();
219 // Check that the key exists, and its namespace matches the active
220 // bridge. It will mismatch if there was a GPU reset.
222 aManager
->WrBridge()->GetNamespace() == imageKeyMaybe
->mNamespace
) {
223 imageKey
= imageKeyMaybe
.value();
225 imageKey
= canvasContext
->CreateImageKey(aManager
);
226 aResources
.AddPrivateExternalImage(canvasContext
->mExternalImageId
,
227 imageKey
, imageDesc
);
230 mozilla::wr::ImageRendering rendering
= wr::ToImageRendering(
231 nsLayoutUtils::GetSamplingFilterForFrame(mFrame
));
232 aBuilder
.PushImage(wr::ToLayoutRect(bounds
), wr::ToLayoutRect(bounds
),
233 !BackfaceIsHidden(), rendering
, imageKey
);
235 canvasData
->mDescriptor
= imageDesc
;
236 canvasData
->mImageKey
= imageKey
;
237 canvasData
->RequestFrameReadback();
240 case CanvasContextType::ImageBitmap
: {
241 nsHTMLCanvasFrame
* canvasFrame
=
242 static_cast<nsHTMLCanvasFrame
*>(mFrame
);
243 nsIntSize canvasSizeInPx
= canvasFrame
->GetCanvasSize();
244 if (canvasSizeInPx
.width
<= 0 || canvasSizeInPx
.height
<= 0) {
248 RefPtr
<WebRenderCanvasData
> canvasData
=
249 aManager
->CommandBuilder()
250 .CreateOrRecycleWebRenderUserData
<WebRenderCanvasData
>(
252 if (!canvasFrame
->UpdateWebRenderCanvasData(aDisplayListBuilder
,
254 canvasData
->ClearImageContainer();
258 IntrinsicSize intrinsicSize
=
259 IntrinsicSizeFromCanvasSize(canvasSizeInPx
);
260 AspectRatio intrinsicRatio
=
261 IntrinsicRatioFromCanvasSize(canvasSizeInPx
);
264 mFrame
->GetContentRectRelativeToSelf() + ToReferenceFrame();
265 nsRect dest
= nsLayoutUtils::ComputeObjectDestRect(
266 area
, intrinsicSize
, intrinsicRatio
, mFrame
->StylePosition());
268 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
269 dest
, mFrame
->PresContext()->AppUnitsPerDevPixel());
271 aManager
->CommandBuilder().PushImage(
272 this, canvasData
->GetImageContainer(), aBuilder
, aResources
, aSc
,
276 case CanvasContextType::NoContext
:
279 MOZ_ASSERT_UNREACHABLE("unknown canvas context type");
284 virtual LayerState
GetLayerState(
285 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
286 const ContainerLayerParameters
& aParameters
) override
{
287 if (HTMLCanvasElement::FromNode(mFrame
->GetContent())
288 ->ShouldForceInactiveLayer(aManager
))
289 return LayerState::LAYER_INACTIVE
;
291 // If compositing is cheap, just do that
292 if (aManager
->IsCompositingCheap() ||
293 ActiveLayerTracker::IsContentActive(mFrame
))
294 return mozilla::LayerState::LAYER_ACTIVE
;
296 return LayerState::LAYER_INACTIVE
;
299 // FirstContentfulPaint is supposed to ignore "white" canvases. We use
300 // MaybeModified (if GetContext() was called on the canvas) as a standin for
302 virtual bool IsContentful() const override
{
303 nsHTMLCanvasFrame
* f
= static_cast<nsHTMLCanvasFrame
*>(Frame());
304 HTMLCanvasElement
* canvas
= HTMLCanvasElement::FromNode(f
->GetContent());
305 return canvas
->MaybeModified();
308 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
309 gfxContext
* aCtx
) override
{
310 // This currently uses BasicLayerManager to re-use the code for extracting
311 // the current CanvasRenderer/Image and generating DrawTarget rendering
313 // Ideally we'll factor out that code and use it directly soon.
314 RefPtr
<BasicLayerManager
> layerManager
=
315 new BasicLayerManager(BasicLayerManager::BLM_OFFSCREEN
);
317 layerManager
->BeginTransactionWithTarget(aCtx
);
318 RefPtr
<Layer
> layer
=
319 BuildLayer(aBuilder
, layerManager
, ContainerLayerParameters());
321 layerManager
->AbortTransaction();
325 layerManager
->SetRoot(layer
);
326 layerManager
->EndEmptyTransaction();
330 nsIFrame
* NS_NewHTMLCanvasFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
331 return new (aPresShell
)
332 nsHTMLCanvasFrame(aStyle
, aPresShell
->GetPresContext());
335 NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame
)
336 NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame
)
337 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
339 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame
)
341 void nsHTMLCanvasFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
342 nsIFrame
* aPrevInFlow
) {
343 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
345 // We can fill in the canvas before the canvas frame is created, in
346 // which case we never get around to marking the content as active. Therefore,
347 // we mark it active here when we create the frame.
348 ActiveLayerTracker::NotifyContentChange(this);
351 void nsHTMLCanvasFrame::DestroyFrom(nsIFrame
* aDestroyRoot
,
352 PostDestroyData
& aPostDestroyData
) {
353 if (IsPrimaryFrame()) {
354 HTMLCanvasElement::FromNode(*mContent
)->ResetPrintCallback();
356 nsContainerFrame::DestroyFrom(aDestroyRoot
, aPostDestroyData
);
359 nsHTMLCanvasFrame::~nsHTMLCanvasFrame() = default;
361 nsIntSize
nsHTMLCanvasFrame::GetCanvasSize() const {
362 nsIntSize
size(0, 0);
363 HTMLCanvasElement
* canvas
= HTMLCanvasElement::FromNodeOrNull(GetContent());
365 size
= canvas
->GetSize();
366 MOZ_ASSERT(size
.width
>= 0 && size
.height
>= 0,
367 "we should've required <canvas> width/height attrs to be "
368 "unsigned (non-negative) values");
370 MOZ_ASSERT_UNREACHABLE("couldn't get canvas size");
377 nscoord
nsHTMLCanvasFrame::GetMinISize(gfxContext
* aRenderingContext
) {
378 // XXX The caller doesn't account for constraints of the height,
379 // min-height, and max-height properties.
380 bool vertical
= GetWritingMode().IsVertical();
382 if (StyleDisplay()->IsContainSize()) {
385 result
= nsPresContext::CSSPixelsToAppUnits(
386 vertical
? GetCanvasSize().height
: GetCanvasSize().width
);
388 DISPLAY_MIN_INLINE_SIZE(this, result
);
393 nscoord
nsHTMLCanvasFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
394 // XXX The caller doesn't account for constraints of the height,
395 // min-height, and max-height properties.
396 bool vertical
= GetWritingMode().IsVertical();
398 if (StyleDisplay()->IsContainSize()) {
401 result
= nsPresContext::CSSPixelsToAppUnits(
402 vertical
? GetCanvasSize().height
: GetCanvasSize().width
);
404 DISPLAY_PREF_INLINE_SIZE(this, result
);
409 IntrinsicSize
nsHTMLCanvasFrame::GetIntrinsicSize() {
410 if (StyleDisplay()->IsContainSize()) {
411 return IntrinsicSize(0, 0);
413 return IntrinsicSizeFromCanvasSize(GetCanvasSize());
417 AspectRatio
nsHTMLCanvasFrame::GetIntrinsicRatio() const {
418 if (StyleDisplay()->IsContainSize()) {
419 return AspectRatio();
422 return IntrinsicRatioFromCanvasSize(GetCanvasSize());
426 nsIFrame::SizeComputationResult
nsHTMLCanvasFrame::ComputeSize(
427 gfxContext
* aRenderingContext
, WritingMode aWM
, const LogicalSize
& aCBSize
,
428 nscoord aAvailableISize
, const LogicalSize
& aMargin
,
429 const LogicalSize
& aBorderPadding
, const StyleSizeOverrides
& aSizeOverrides
,
430 ComputeSizeFlags aFlags
) {
431 return {ComputeSizeWithIntrinsicDimensions(
432 aRenderingContext
, aWM
, GetIntrinsicSize(), GetAspectRatio(),
433 aCBSize
, aMargin
, aBorderPadding
, aSizeOverrides
, aFlags
),
434 AspectRatioUsage::None
};
437 void nsHTMLCanvasFrame::Reflow(nsPresContext
* aPresContext
,
438 ReflowOutput
& aMetrics
,
439 const ReflowInput
& aReflowInput
,
440 nsReflowStatus
& aStatus
) {
442 DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
443 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aMetrics
, aStatus
);
444 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
446 NS_FRAME_TRACE_CALLS
,
447 ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
448 aReflowInput
.AvailableWidth(), aReflowInput
.AvailableHeight()));
450 MOZ_ASSERT(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
452 WritingMode wm
= aReflowInput
.GetWritingMode();
453 LogicalSize finalSize
= aReflowInput
.ComputedSize();
455 // stash this away so we can compute our inner area later
456 mBorderPadding
= aReflowInput
.ComputedLogicalBorderPadding(wm
);
458 finalSize
.ISize(wm
) += mBorderPadding
.IStartEnd(wm
);
459 finalSize
.BSize(wm
) += mBorderPadding
.BStartEnd(wm
);
461 if (GetPrevInFlow()) {
462 nscoord y
= GetContinuationOffset(&finalSize
.ISize(wm
));
463 finalSize
.BSize(wm
) -= y
+ mBorderPadding
.BStart(wm
);
464 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
467 aMetrics
.SetSize(wm
, finalSize
);
468 aMetrics
.SetOverflowAreasToDesiredBounds();
469 FinishAndStoreOverflow(&aMetrics
);
471 // Reflow the single anon block child.
472 nsReflowStatus childStatus
;
473 nsIFrame
* childFrame
= mFrames
.FirstChild();
474 WritingMode childWM
= childFrame
->GetWritingMode();
475 LogicalSize availSize
= aReflowInput
.ComputedSize(childWM
);
476 availSize
.BSize(childWM
) = NS_UNCONSTRAINEDSIZE
;
477 NS_ASSERTION(!childFrame
->GetNextSibling(), "HTML canvas should have 1 kid");
478 ReflowOutput
childDesiredSize(aReflowInput
.GetWritingMode());
479 ReflowInput
childReflowInput(aPresContext
, aReflowInput
, childFrame
,
481 ReflowChild(childFrame
, aPresContext
, childDesiredSize
, childReflowInput
, 0,
482 0, ReflowChildFlags::Default
, childStatus
, nullptr);
483 FinishReflowChild(childFrame
, aPresContext
, childDesiredSize
,
484 &childReflowInput
, 0, 0, ReflowChildFlags::Default
);
486 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
487 ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
488 aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)));
489 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowInput
, aMetrics
);
492 // FIXME taken from nsImageFrame, but then had splittable frame stuff
493 // removed. That needs to be fixed.
494 // XXXdholbert As in nsImageFrame, this function's clients should probably
495 // just be calling GetContentRectRelativeToSelf().
496 nsRect
nsHTMLCanvasFrame::GetInnerArea() const {
497 nsMargin bp
= mBorderPadding
.GetPhysicalMargin(GetWritingMode());
501 r
.width
= mRect
.width
- bp
.left
- bp
.right
;
502 r
.height
= mRect
.height
- bp
.top
- bp
.bottom
;
506 already_AddRefed
<Layer
> nsHTMLCanvasFrame::BuildLayer(
507 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
508 nsDisplayItem
* aItem
,
509 const ContainerLayerParameters
& aContainerParameters
) {
510 nsRect area
= GetContentRectRelativeToSelf() + aItem
->ToReferenceFrame();
511 HTMLCanvasElement
* element
= static_cast<HTMLCanvasElement
*>(GetContent());
512 nsIntSize canvasSizeInPx
= GetCanvasSize();
514 nsPresContext
* presContext
= PresContext();
515 element
->HandlePrintCallback(presContext
);
517 if (canvasSizeInPx
.width
<= 0 || canvasSizeInPx
.height
<= 0 || area
.IsEmpty())
521 aManager
->GetLayerBuilder()
522 ? aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, aItem
)
524 RefPtr
<Layer
> layer
= element
->GetCanvasLayer(aBuilder
, oldLayer
, aManager
);
525 if (!layer
) return nullptr;
527 IntrinsicSize intrinsicSize
= IntrinsicSizeFromCanvasSize(canvasSizeInPx
);
528 AspectRatio intrinsicRatio
= IntrinsicRatioFromCanvasSize(canvasSizeInPx
);
530 nsRect dest
= nsLayoutUtils::ComputeObjectDestRect(
531 area
, intrinsicSize
, intrinsicRatio
, StylePosition());
533 gfxRect destGFXRect
= presContext
->AppUnitsToGfxUnits(dest
);
535 // Transform the canvas into the right place
536 gfxPoint p
= destGFXRect
.TopLeft() + aContainerParameters
.mOffset
;
537 Matrix transform
= Matrix::Translation(p
.x
, p
.y
);
538 transform
.PreScale(destGFXRect
.Width() / canvasSizeInPx
.width
,
539 destGFXRect
.Height() / canvasSizeInPx
.height
);
540 layer
->SetBaseTransform(gfx::Matrix4x4::From2D(transform
));
541 if (layer
->GetType() == layers::Layer::TYPE_CANVAS
) {
542 RefPtr
<CanvasLayer
> canvasLayer
= static_cast<CanvasLayer
*>(layer
.get());
543 canvasLayer
->SetSamplingFilter(
544 nsLayoutUtils::GetSamplingFilterForFrame(this));
546 bounds
.SetRect(0, 0, canvasSizeInPx
.width
, canvasSizeInPx
.height
);
547 canvasLayer
->SetBounds(bounds
);
548 } else if (layer
->GetType() == layers::Layer::TYPE_IMAGE
) {
549 RefPtr
<ImageLayer
> imageLayer
= static_cast<ImageLayer
*>(layer
.get());
550 imageLayer
->SetSamplingFilter(
551 nsLayoutUtils::GetSamplingFilterForFrame(this));
554 return layer
.forget();
557 bool nsHTMLCanvasFrame::UpdateWebRenderCanvasData(
558 nsDisplayListBuilder
* aBuilder
, WebRenderCanvasData
* aCanvasData
) {
559 HTMLCanvasElement
* element
= static_cast<HTMLCanvasElement
*>(GetContent());
560 return element
->UpdateWebRenderCanvasData(aBuilder
, aCanvasData
);
563 void nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
564 const nsDisplayListSet
& aLists
) {
565 if (!IsVisibleForPainting()) return;
567 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
570 nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition())
572 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT
;
574 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
clip(
575 aBuilder
, this, clipFlags
);
577 aLists
.Content()->AppendNewToTop
<nsDisplayCanvas
>(aBuilder
, this);
579 DisplaySelectionOverlay(aBuilder
, aLists
.Content(),
580 nsISelectionDisplay::DISPLAY_IMAGES
);
583 // get the offset into the content area of the image where aImg starts if it is
584 // a continuation. from nsImageFrame
585 nscoord
nsHTMLCanvasFrame::GetContinuationOffset(nscoord
* aWidth
) const {
591 if (GetPrevInFlow()) {
592 for (nsIFrame
* prevInFlow
= GetPrevInFlow(); prevInFlow
;
593 prevInFlow
= prevInFlow
->GetPrevInFlow()) {
594 nsRect rect
= prevInFlow
->GetRect();
596 *aWidth
= rect
.width
;
598 offset
+= rect
.height
;
600 offset
-= mBorderPadding
.GetPhysicalMargin(GetWritingMode()).top
;
601 offset
= std::max(0, offset
);
606 void nsHTMLCanvasFrame::AppendDirectlyOwnedAnonBoxes(
607 nsTArray
<OwnedAnonBox
>& aResult
) {
608 MOZ_ASSERT(mFrames
.FirstChild(), "Must have our canvas content anon box");
609 MOZ_ASSERT(!mFrames
.FirstChild()->GetNextSibling(),
610 "Must only have our canvas content anon box");
611 aResult
.AppendElement(OwnedAnonBox(mFrames
.FirstChild()));
615 a11y::AccType
nsHTMLCanvasFrame::AccessibleType() {
616 return a11y::eHTMLCanvasType
;
620 #ifdef DEBUG_FRAME_DUMP
621 nsresult
nsHTMLCanvasFrame::GetFrameName(nsAString
& aResult
) const {
622 return MakeFrameName(u
"HTMLCanvas"_ns
, aResult
);