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 #include "mozilla/dom/HTMLCanvasElement.h"
9 #include "ImageEncoder.h"
11 #include "jsfriendapi.h"
12 #include "MediaTrackGraph.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Base64.h"
15 #include "mozilla/BasePrincipal.h"
16 #include "mozilla/CheckedInt.h"
17 #include "mozilla/PresShell.h"
18 #include "mozilla/dom/CanvasCaptureMediaStream.h"
19 #include "mozilla/dom/CanvasRenderingContext2D.h"
20 #include "mozilla/dom/Document.h"
21 #include "mozilla/dom/GeneratePlaceholderCanvasData.h"
22 #include "mozilla/dom/Event.h"
23 #include "mozilla/dom/File.h"
24 #include "mozilla/dom/HTMLCanvasElementBinding.h"
25 #include "mozilla/dom/VideoStreamTrack.h"
26 #include "mozilla/dom/MouseEvent.h"
27 #include "mozilla/dom/OffscreenCanvas.h"
28 #include "mozilla/dom/OffscreenCanvasDisplayHelper.h"
29 #include "mozilla/EventDispatcher.h"
30 #include "mozilla/gfx/Rect.h"
31 #include "mozilla/layers/CanvasRenderer.h"
32 #include "mozilla/layers/WebRenderCanvasRenderer.h"
33 #include "mozilla/layers/WebRenderUserData.h"
34 #include "mozilla/MouseEvents.h"
35 #include "mozilla/Preferences.h"
36 #include "mozilla/ProfilerLabels.h"
37 #include "mozilla/ProfilerMarkers.h"
38 #include "mozilla/StaticPrefs_privacy.h"
39 #include "mozilla/Telemetry.h"
40 #include "mozilla/webgpu/CanvasContext.h"
41 #include "nsAttrValueInlines.h"
42 #include "nsContentUtils.h"
43 #include "nsDisplayList.h"
44 #include "nsDOMJSUtils.h"
46 #include "nsJSUtils.h"
47 #include "nsLayoutUtils.h"
48 #include "nsMathUtils.h"
49 #include "nsNetUtil.h"
50 #include "nsRefreshDriver.h"
51 #include "nsStreamUtils.h"
52 #include "ActiveLayerTracker.h"
53 #include "CanvasUtils.h"
54 #include "VRManagerChild.h"
55 #include "ClientWebGLContext.h"
56 #include "WindowRenderer.h"
58 using namespace mozilla::layers
;
59 using namespace mozilla::gfx
;
61 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas
)
63 namespace mozilla::dom
{
65 class RequestedFrameRefreshObserver
: public nsARefreshObserver
{
66 NS_INLINE_DECL_REFCOUNTING(RequestedFrameRefreshObserver
, override
)
69 RequestedFrameRefreshObserver(HTMLCanvasElement
* const aOwningElement
,
70 nsRefreshDriver
* aRefreshDriver
,
71 bool aReturnPlaceholderData
)
74 mReturnPlaceholderData(aReturnPlaceholderData
),
75 mOwningElement(aOwningElement
),
76 mRefreshDriver(aRefreshDriver
),
77 mWatchManager(this, AbstractThread::MainThread()),
78 mPendingThrottledCapture(false) {
79 MOZ_ASSERT(mOwningElement
);
82 static already_AddRefed
<DataSourceSurface
> CopySurface(
83 const RefPtr
<SourceSurface
>& aSurface
, bool aReturnPlaceholderData
) {
84 RefPtr
<DataSourceSurface
> data
= aSurface
->GetDataSurface();
89 DataSourceSurface::ScopedMap
read(data
, DataSourceSurface::READ
);
90 if (!read
.IsMapped()) {
94 RefPtr
<DataSourceSurface
> copy
= Factory::CreateDataSourceSurfaceWithStride(
95 data
->GetSize(), data
->GetFormat(), read
.GetStride());
100 DataSourceSurface::ScopedMap
write(copy
, DataSourceSurface::WRITE
);
101 if (!write
.IsMapped()) {
105 MOZ_ASSERT(read
.GetStride() == write
.GetStride());
106 MOZ_ASSERT(data
->GetSize() == copy
->GetSize());
107 MOZ_ASSERT(data
->GetFormat() == copy
->GetFormat());
109 if (aReturnPlaceholderData
) {
110 auto size
= write
.GetStride() * copy
->GetSize().height
;
111 auto* data
= write
.GetData();
112 GeneratePlaceholderCanvasData(size
, data
);
114 memcpy(write
.GetData(), read
.GetData(),
115 write
.GetStride() * copy
->GetSize().height
);
118 return copy
.forget();
121 void SetReturnPlaceholderData(bool aReturnPlaceholderData
) {
122 mReturnPlaceholderData
= aReturnPlaceholderData
;
125 void NotifyCaptureStateChange() {
126 if (mPendingThrottledCapture
) {
130 if (!mOwningElement
) {
134 Watchable
<FrameCaptureState
>* captureState
=
135 mOwningElement
->GetFrameCaptureState();
137 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
138 "Abort: No capture state"_ns
);
142 if (captureState
->Ref() == FrameCaptureState::CLEAN
) {
143 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
148 if (!mRefreshDriver
) {
149 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
150 "Abort: no refresh driver"_ns
);
154 if (!mRefreshDriver
->IsThrottled()) {
155 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
156 "Abort: not throttled"_ns
);
160 TimeStamp now
= TimeStamp::Now();
162 mLastCaptureTime
.IsNull()
164 : mLastCaptureTime
+ TimeDuration::FromMilliseconds(
165 nsRefreshDriver::DefaultInterval());
166 if (mLastCaptureTime
.IsNull() || next
<= now
) {
167 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
168 "CaptureFrame direct while throttled"_ns
);
174 if (profiler_thread_is_being_profiled_for_markers()) {
175 str
.AppendPrintf("Delaying CaptureFrame by %.2fms",
176 (next
- now
).ToMilliseconds());
178 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {}, str
);
180 mPendingThrottledCapture
= true;
181 AbstractThread::MainThread()->DelayedDispatch(
182 NS_NewRunnableFunction(
184 [this, self
= RefPtr
<RequestedFrameRefreshObserver
>(this), next
] {
185 mPendingThrottledCapture
= false;
186 AUTO_PROFILER_MARKER_TEXT(
187 "Canvas CaptureStream", MEDIA_RT
, {},
188 "CaptureFrame after delay while throttled"_ns
);
191 // next >= now, so this is a guard for (next - now) flooring to 0.
193 1, static_cast<uint32_t>((next
- now
).ToMilliseconds())));
196 void WillRefresh(TimeStamp aTime
) override
{
197 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
198 "CaptureFrame by refresh driver"_ns
);
203 void CaptureFrame(TimeStamp aTime
) {
204 MOZ_ASSERT(NS_IsMainThread());
206 if (!mOwningElement
) {
207 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
208 "Abort: no owning element"_ns
);
212 if (mOwningElement
->IsWriteOnly()) {
213 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
214 "Abort: write only"_ns
);
218 if (auto* captureStateWatchable
= mOwningElement
->GetFrameCaptureState();
219 captureStateWatchable
&&
220 *captureStateWatchable
== FrameCaptureState::CLEAN
) {
221 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
226 // Mark the context already now, since if the frame capture state is DIRTY
227 // and we catch an early return below (not marking it CLEAN), the next draw
228 // will not trigger a capture state change from the
229 // Watchable<FrameCaptureState>.
230 mOwningElement
->MarkContextCleanForFrameCapture();
232 mOwningElement
->ProcessDestroyedFrameListeners();
234 if (!mOwningElement
->IsFrameCaptureRequested(aTime
)) {
235 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
236 "Abort: no capture requested"_ns
);
240 RefPtr
<SourceSurface
> snapshot
;
242 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
244 snapshot
= mOwningElement
->GetSurfaceSnapshot(nullptr);
246 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
247 "Abort: snapshot failed"_ns
);
252 RefPtr
<DataSourceSurface
> copy
;
254 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
256 copy
= CopySurface(snapshot
, mReturnPlaceholderData
);
258 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
259 "Abort: copy failed"_ns
);
265 if (profiler_thread_is_being_profiled_for_markers()) {
266 TimeDuration sinceLast
=
267 aTime
- (mLastCaptureTime
.IsNull() ? aTime
: mLastCaptureTime
);
268 str
.AppendPrintf("Forwarding captured frame %.2fms after last",
269 sinceLast
.ToMilliseconds());
271 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {}, str
);
273 if (!mLastCaptureTime
.IsNull() && aTime
<= mLastCaptureTime
) {
274 aTime
= mLastCaptureTime
+ TimeDuration::FromMilliseconds(1);
276 mLastCaptureTime
= aTime
;
278 mOwningElement
->SetFrameCapture(copy
.forget(), aTime
);
281 void DetachFromRefreshDriver() {
282 MOZ_ASSERT(mOwningElement
);
283 MOZ_ASSERT(mRefreshDriver
);
286 mRefreshDriver
= nullptr;
287 mWatchManager
.Shutdown();
290 bool IsRegisteredAndWatching() { return mRegistered
&& mWatching
; }
294 MOZ_ASSERT(mRefreshDriver
);
295 if (mRefreshDriver
) {
296 mRefreshDriver
->AddRefreshObserver(this, FlushType::Display
,
297 "Canvas frame capture listeners");
306 if (!mOwningElement
) {
310 if (Watchable
<FrameCaptureState
>* captureState
=
311 mOwningElement
->GetFrameCaptureState()) {
314 &RequestedFrameRefreshObserver::NotifyCaptureStateChange
);
321 MOZ_ASSERT(mRefreshDriver
);
322 if (mRefreshDriver
) {
323 mRefreshDriver
->RemoveRefreshObserver(this, FlushType::Display
);
332 if (!mOwningElement
) {
336 if (Watchable
<FrameCaptureState
>* captureState
=
337 mOwningElement
->GetFrameCaptureState()) {
338 mWatchManager
.Unwatch(
340 &RequestedFrameRefreshObserver::NotifyCaptureStateChange
);
346 virtual ~RequestedFrameRefreshObserver() {
347 MOZ_ASSERT(!mRefreshDriver
);
348 MOZ_ASSERT(!mRegistered
);
349 MOZ_ASSERT(!mWatching
);
354 bool mReturnPlaceholderData
;
355 const WeakPtr
<HTMLCanvasElement
> mOwningElement
;
356 RefPtr
<nsRefreshDriver
> mRefreshDriver
;
357 WatchManager
<RequestedFrameRefreshObserver
> mWatchManager
;
358 TimeStamp mLastCaptureTime
;
359 bool mPendingThrottledCapture
;
362 // ---------------------------------------------------------------------------
364 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLCanvasPrintState
, mCanvas
, mContext
,
367 HTMLCanvasPrintState::HTMLCanvasPrintState(
368 HTMLCanvasElement
* aCanvas
, nsICanvasRenderingContextInternal
* aContext
,
369 nsITimerCallback
* aCallback
)
371 mPendingNotify(false),
374 mCallback(aCallback
) {}
376 HTMLCanvasPrintState::~HTMLCanvasPrintState() = default;
379 JSObject
* HTMLCanvasPrintState::WrapObject(JSContext
* aCx
,
380 JS::Handle
<JSObject
*> aGivenProto
) {
381 return MozCanvasPrintState_Binding::Wrap(aCx
, this, aGivenProto
);
384 nsISupports
* HTMLCanvasPrintState::Context() const { return mContext
; }
386 void HTMLCanvasPrintState::Done() {
387 if (!mPendingNotify
&& !mIsDone
) {
388 // The canvas needs to be invalidated for printing reftests on linux to
391 mCanvas
->InvalidateCanvas();
393 RefPtr
<nsRunnableMethod
<HTMLCanvasPrintState
>> doneEvent
=
394 NewRunnableMethod("dom::HTMLCanvasPrintState::NotifyDone", this,
395 &HTMLCanvasPrintState::NotifyDone
);
396 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent
))) {
397 mPendingNotify
= true;
402 void HTMLCanvasPrintState::NotifyDone() {
404 mPendingNotify
= false;
406 mCallback
->Notify(nullptr);
410 // ---------------------------------------------------------------------------
412 HTMLCanvasElementObserver::HTMLCanvasElementObserver(
413 HTMLCanvasElement
* aElement
)
414 : mElement(aElement
) {
415 RegisterObserverEvents();
418 HTMLCanvasElementObserver::~HTMLCanvasElementObserver() { Destroy(); }
420 void HTMLCanvasElementObserver::Destroy() {
421 UnregisterObserverEvents();
425 void HTMLCanvasElementObserver::RegisterObserverEvents() {
430 nsCOMPtr
<nsIObserverService
> observerService
=
431 mozilla::services::GetObserverService();
433 MOZ_ASSERT(observerService
);
435 if (observerService
) {
436 observerService
->AddObserver(this, "memory-pressure", false);
437 observerService
->AddObserver(this, "canvas-device-reset", false);
441 void HTMLCanvasElementObserver::UnregisterObserverEvents() {
446 nsCOMPtr
<nsIObserverService
> observerService
=
447 mozilla::services::GetObserverService();
449 // Do not assert on observerService here. This might be triggered by
450 // the cycle collector at a late enough time, that XPCOM services are
451 // no longer available. See bug 1029504.
452 if (observerService
) {
453 observerService
->RemoveObserver(this, "memory-pressure");
454 observerService
->RemoveObserver(this, "canvas-device-reset");
459 HTMLCanvasElementObserver::Observe(nsISupports
*, const char* aTopic
,
465 if (strcmp(aTopic
, "memory-pressure") == 0) {
466 mElement
->OnMemoryPressure();
467 } else if (strcmp(aTopic
, "canvas-device-reset") == 0) {
468 mElement
->OnDeviceReset();
474 NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver
, nsIObserver
)
476 // ---------------------------------------------------------------------------
478 HTMLCanvasElement::HTMLCanvasElement(
479 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
480 : nsGenericHTMLElement(std::move(aNodeInfo
)),
482 mMaybeModified(false),
485 HTMLCanvasElement::~HTMLCanvasElement() { Destroy(); }
487 void HTMLCanvasElement::Destroy() {
488 if (mOffscreenDisplay
) {
489 mOffscreenDisplay
->DestroyElement();
490 mOffscreenDisplay
= nullptr;
491 mImageContainer
= nullptr;
494 if (mContextObserver
) {
495 mContextObserver
->Destroy();
496 mContextObserver
= nullptr;
499 ResetPrintCallback();
500 if (mRequestedFrameRefreshObserver
) {
501 mRequestedFrameRefreshObserver
->DetachFromRefreshDriver();
502 mRequestedFrameRefreshObserver
= nullptr;
506 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLCanvasElement
)
508 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLCanvasElement
,
509 nsGenericHTMLElement
)
511 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentContext
, mPrintCallback
, mPrintState
,
512 mOriginalCanvas
, mOffscreenCanvas
)
513 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
515 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLCanvasElement
,
516 nsGenericHTMLElement
)
517 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentContext
, mPrintCallback
,
518 mPrintState
, mOriginalCanvas
,
520 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
522 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLCanvasElement
,
523 nsGenericHTMLElement
)
525 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement
)
528 JSObject
* HTMLCanvasElement::WrapNode(JSContext
* aCx
,
529 JS::Handle
<JSObject
*> aGivenProto
) {
530 return HTMLCanvasElement_Binding::Wrap(aCx
, this, aGivenProto
);
533 already_AddRefed
<nsICanvasRenderingContextInternal
>
534 HTMLCanvasElement::CreateContext(CanvasContextType aContextType
) {
535 // Note that the compositor backend will be LAYERS_NONE if there is no widget.
536 RefPtr
<nsICanvasRenderingContextInternal
> ret
=
537 CreateContextHelper(aContextType
, GetCompositorBackendType());
538 if (NS_WARN_IF(!ret
)) {
542 // Add Observer for webgl canvas.
543 if (aContextType
== CanvasContextType::WebGL1
||
544 aContextType
== CanvasContextType::WebGL2
||
545 aContextType
== CanvasContextType::Canvas2D
) {
546 if (!mContextObserver
) {
547 mContextObserver
= new HTMLCanvasElementObserver(this);
551 ret
->SetCanvasElement(this);
555 nsresult
HTMLCanvasElement::UpdateContext(
556 JSContext
* aCx
, JS::Handle
<JS::Value
> aNewContextOptions
,
557 ErrorResult
& aRvForDictionaryInit
) {
558 nsresult rv
= CanvasRenderingContextHelper::UpdateContext(
559 aCx
, aNewContextOptions
, aRvForDictionaryInit
);
565 // If we have a mRequestedFrameRefreshObserver that wasn't fully registered,
567 if (mRequestedFrameRefreshObserver
.get() &&
568 !mRequestedFrameRefreshObserver
->IsRegisteredAndWatching()) {
569 mRequestedFrameRefreshObserver
->Register();
575 nsIntSize
HTMLCanvasElement::GetWidthHeight() {
576 nsIntSize
size(DEFAULT_CANVAS_WIDTH
, DEFAULT_CANVAS_HEIGHT
);
577 const nsAttrValue
* value
;
579 if ((value
= GetParsedAttr(nsGkAtoms::width
)) &&
580 value
->Type() == nsAttrValue::eInteger
) {
581 size
.width
= value
->GetIntegerValue();
584 if ((value
= GetParsedAttr(nsGkAtoms::height
)) &&
585 value
->Type() == nsAttrValue::eInteger
) {
586 size
.height
= value
->GetIntegerValue();
589 MOZ_ASSERT(size
.width
>= 0 && size
.height
>= 0,
590 "we should've required <canvas> width/height attrs to be "
591 "unsigned (non-negative) values");
596 void HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
597 const nsAttrValue
* aValue
,
598 const nsAttrValue
* aOldValue
,
599 nsIPrincipal
* aSubjectPrincipal
,
601 AfterMaybeChangeAttr(aNamespaceID
, aName
, aNotify
);
603 return nsGenericHTMLElement::AfterSetAttr(
604 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
607 void HTMLCanvasElement::OnAttrSetButNotChanged(
608 int32_t aNamespaceID
, nsAtom
* aName
, const nsAttrValueOrString
& aValue
,
610 AfterMaybeChangeAttr(aNamespaceID
, aName
, aNotify
);
612 return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID
, aName
,
616 void HTMLCanvasElement::AfterMaybeChangeAttr(int32_t aNamespaceID
,
617 nsAtom
* aName
, bool aNotify
) {
618 if (mCurrentContext
&& aNamespaceID
== kNameSpaceID_None
&&
619 (aName
== nsGkAtoms::width
|| aName
== nsGkAtoms::height
||
620 aName
== nsGkAtoms::moz_opaque
)) {
622 UpdateContext(nullptr, JS::NullHandleValue
, dummy
);
626 void HTMLCanvasElement::HandlePrintCallback(nsPresContext
* aPresContext
) {
627 // Only call the print callback here if 1) we're in a print testing mode or
628 // print preview mode, 2) the canvas has a print callback and 3) the callback
629 // hasn't already been called. For real printing the callback is handled in
630 // nsPageSequenceFrame::PrePrintNextSheet.
631 if ((aPresContext
->Type() == nsPresContext::eContext_PageLayout
||
632 aPresContext
->Type() == nsPresContext::eContext_PrintPreview
) &&
633 !mPrintState
&& GetMozPrintCallback()) {
634 DispatchPrintCallback(nullptr);
638 nsresult
HTMLCanvasElement::DispatchPrintCallback(nsITimerCallback
* aCallback
) {
639 // For print reftests the context may not be initialized yet, so get a context
640 // so mCurrentContext is set.
641 if (!mCurrentContext
) {
643 nsCOMPtr
<nsISupports
> context
;
644 rv
= GetContext(u
"2d"_ns
, getter_AddRefs(context
));
645 NS_ENSURE_SUCCESS(rv
, rv
);
647 mPrintState
= new HTMLCanvasPrintState(this, mCurrentContext
, aCallback
);
649 RefPtr
<nsRunnableMethod
<HTMLCanvasElement
>> renderEvent
=
650 NewRunnableMethod("dom::HTMLCanvasElement::CallPrintCallback", this,
651 &HTMLCanvasElement::CallPrintCallback
);
652 return OwnerDoc()->Dispatch(renderEvent
.forget());
655 void HTMLCanvasElement::CallPrintCallback() {
656 AUTO_PROFILER_MARKER_TEXT("HTMLCanvasElement Printing", LAYOUT_Printing
, {},
657 "HTMLCanvasElement::CallPrintCallback"_ns
);
659 // `mPrintState` might have been destroyed by cancelling the previous
660 // printing (especially the canvas frame destruction) during processing
661 // event loops in the printing.
664 RefPtr
<PrintCallback
> callback
= GetMozPrintCallback();
665 RefPtr
<HTMLCanvasPrintState
> state
= mPrintState
;
666 callback
->Call(*state
);
669 void HTMLCanvasElement::ResetPrintCallback() {
671 mPrintState
= nullptr;
675 bool HTMLCanvasElement::IsPrintCallbackDone() {
676 if (mPrintState
== nullptr) {
680 return mPrintState
->mIsDone
;
683 HTMLCanvasElement
* HTMLCanvasElement::GetOriginalCanvas() {
684 return mOriginalCanvas
? mOriginalCanvas
.get() : this;
687 nsresult
HTMLCanvasElement::CopyInnerTo(HTMLCanvasElement
* aDest
) {
688 nsresult rv
= nsGenericHTMLElement::CopyInnerTo(aDest
);
689 NS_ENSURE_SUCCESS(rv
, rv
);
690 Document
* destDoc
= aDest
->OwnerDoc();
691 if (destDoc
->IsStaticDocument()) {
692 // The Firefox print preview code can create a static clone from an
693 // existing static clone, so we may not be the original 'canvas' element.
694 aDest
->mOriginalCanvas
= GetOriginalCanvas();
696 if (GetMozPrintCallback()) {
697 destDoc
->SetHasPrintCallbacks();
700 // We make sure that the canvas is not zero sized since that would cause
701 // the DrawImage call below to return an error, which would cause printing
703 nsIntSize size
= GetWidthHeight();
704 if (size
.height
> 0 && size
.width
> 0) {
705 nsCOMPtr
<nsISupports
> cxt
;
706 aDest
->GetContext(u
"2d"_ns
, getter_AddRefs(cxt
));
707 RefPtr
<CanvasRenderingContext2D
> context2d
=
708 static_cast<CanvasRenderingContext2D
*>(cxt
.get());
709 if (context2d
&& !mPrintCallback
) {
710 CanvasImageSource source
;
711 source
.SetAsHTMLCanvasElement() = this;
713 context2d
->DrawImage(source
, 0.0, 0.0, err
);
714 rv
= err
.StealNSResult();
721 nsChangeHint
HTMLCanvasElement::GetAttributeChangeHint(const nsAtom
* aAttribute
,
722 int32_t aModType
) const {
723 nsChangeHint retval
=
724 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute
, aModType
);
725 if (aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
) {
726 retval
|= NS_STYLE_HINT_REFLOW
;
727 } else if (aAttribute
== nsGkAtoms::moz_opaque
) {
728 retval
|= NS_STYLE_HINT_VISUAL
;
733 void HTMLCanvasElement::MapAttributesIntoRule(
734 MappedDeclarationsBuilder
& aBuilder
) {
735 MapAspectRatioInto(aBuilder
);
736 MapCommonAttributesInto(aBuilder
);
739 nsMapRuleToAttributesFunc
HTMLCanvasElement::GetAttributeMappingFunction()
741 return &MapAttributesIntoRule
;
745 HTMLCanvasElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
746 static const MappedAttributeEntry attributes
[] = {
747 {nsGkAtoms::width
}, {nsGkAtoms::height
}, {nullptr}};
748 static const MappedAttributeEntry
* const map
[] = {attributes
,
749 sCommonAttributeMap
};
750 return FindAttributeDependence(aAttribute
, map
);
753 bool HTMLCanvasElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
754 const nsAString
& aValue
,
755 nsIPrincipal
* aMaybeScriptedPrincipal
,
756 nsAttrValue
& aResult
) {
757 if (aNamespaceID
== kNameSpaceID_None
&&
758 (aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
)) {
759 return aResult
.ParseNonNegativeIntValue(aValue
);
762 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
763 aMaybeScriptedPrincipal
, aResult
);
766 void HTMLCanvasElement::ToDataURL(JSContext
* aCx
, const nsAString
& aType
,
767 JS::Handle
<JS::Value
> aParams
,
769 nsIPrincipal
& aSubjectPrincipal
,
771 // mWriteOnly check is redundant, but optimizes for the common case.
772 if (mWriteOnly
&& !CallerCanRead(aSubjectPrincipal
)) {
773 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
777 nsresult rv
= ToDataURLImpl(aCx
, aSubjectPrincipal
, aType
, aParams
, aDataURL
);
779 aDataURL
.AssignLiteral("data:,");
783 void HTMLCanvasElement::SetMozPrintCallback(PrintCallback
* aCallback
) {
784 mPrintCallback
= aCallback
;
787 PrintCallback
* HTMLCanvasElement::GetMozPrintCallback() const {
788 if (mOriginalCanvas
) {
789 return mOriginalCanvas
->GetMozPrintCallback();
791 return mPrintCallback
;
794 static uint32_t sCaptureSourceId
= 0;
795 class CanvasCaptureTrackSource
: public MediaStreamTrackSource
{
797 NS_DECL_ISUPPORTS_INHERITED
798 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureTrackSource
,
799 MediaStreamTrackSource
)
801 CanvasCaptureTrackSource(nsIPrincipal
* aPrincipal
,
802 CanvasCaptureMediaStream
* aCaptureStream
)
803 : MediaStreamTrackSource(
804 aPrincipal
, nsString(),
805 TrackingId(TrackingId::Source::Canvas
, sCaptureSourceId
++,
806 TrackingId::TrackAcrossProcesses::Yes
)),
807 mCaptureStream(aCaptureStream
) {}
809 MediaSourceEnum
GetMediaSource() const override
{
810 return MediaSourceEnum::Other
;
813 bool HasAlpha() const override
{
814 if (!mCaptureStream
|| !mCaptureStream
->Canvas()) {
815 // In cycle-collection
818 return !mCaptureStream
->Canvas()->GetIsOpaque();
821 void Stop() override
{
822 if (!mCaptureStream
) {
826 mCaptureStream
->StopCapture();
829 void Disable() override
{}
831 void Enable() override
{}
834 virtual ~CanvasCaptureTrackSource() = default;
836 RefPtr
<CanvasCaptureMediaStream
> mCaptureStream
;
839 NS_IMPL_ADDREF_INHERITED(CanvasCaptureTrackSource
, MediaStreamTrackSource
)
840 NS_IMPL_RELEASE_INHERITED(CanvasCaptureTrackSource
, MediaStreamTrackSource
)
841 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureTrackSource
)
842 NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource
)
843 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureTrackSource
,
844 MediaStreamTrackSource
, mCaptureStream
)
846 already_AddRefed
<CanvasCaptureMediaStream
> HTMLCanvasElement::CaptureStream(
847 const Optional
<double>& aFrameRate
, nsIPrincipal
& aSubjectPrincipal
,
850 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
854 nsPIDOMWindowInner
* window
= OwnerDoc()->GetInnerWindow();
856 aRv
.Throw(NS_ERROR_FAILURE
);
860 auto stream
= MakeRefPtr
<CanvasCaptureMediaStream
>(window
, this);
862 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
863 nsresult rv
= stream
->Init(aFrameRate
, principal
);
869 RefPtr
<MediaStreamTrack
> track
=
870 new VideoStreamTrack(window
, stream
->GetSourceStream(),
871 new CanvasCaptureTrackSource(principal
, stream
));
872 stream
->AddTrackInternal(track
);
874 // Check site-specific permission and display prompt if appropriate.
875 // If no permission, arrange for the frame capture listener to return
876 // all-white, opaque image data.
877 bool usePlaceholder
= !CanvasUtils::IsImageExtractionAllowed(
878 OwnerDoc(), nsContentUtils::GetCurrentJSContext(), aSubjectPrincipal
);
880 rv
= RegisterFrameCaptureListener(stream
->FrameCaptureListener(),
887 return stream
.forget();
890 nsresult
HTMLCanvasElement::ExtractData(JSContext
* aCx
,
891 nsIPrincipal
& aSubjectPrincipal
,
893 const nsAString
& aOptions
,
894 nsIInputStream
** aStream
) {
895 // Check site-specific permission and display prompt if appropriate.
896 // If no permission, return all-white, opaque image data.
897 bool usePlaceholder
= !CanvasUtils::IsImageExtractionAllowed(
898 OwnerDoc(), aCx
, aSubjectPrincipal
);
900 if (!usePlaceholder
) {
901 auto size
= GetWidthHeight();
902 CanvasContextType type
= GetCurrentContextType();
903 CanvasFeatureUsage featureUsage
= CanvasFeatureUsage::None
;
904 if (type
== CanvasContextType::Canvas2D
) {
906 static_cast<CanvasRenderingContext2D
*>(GetCurrentContext())) {
907 featureUsage
= ctx
->FeatureUsage();
911 CanvasUsage
usage(size
, type
, featureUsage
);
912 OwnerDoc()->RecordCanvasUsage(usage
);
915 return ImageEncoder::ExtractData(aType
, aOptions
, GetSize(), usePlaceholder
,
916 mCurrentContext
, mOffscreenDisplay
, aStream
);
919 nsresult
HTMLCanvasElement::ToDataURLImpl(JSContext
* aCx
,
920 nsIPrincipal
& aSubjectPrincipal
,
921 const nsAString
& aMimeType
,
922 const JS::Value
& aEncoderOptions
,
923 nsAString
& aDataURL
) {
924 nsIntSize size
= GetWidthHeight();
925 if (size
.height
== 0 || size
.width
== 0) {
926 aDataURL
= u
"data:,"_ns
;
931 nsContentUtils::ASCIIToLower(aMimeType
, type
);
934 bool usingCustomParseOptions
;
936 ParseParams(aCx
, type
, aEncoderOptions
, params
, &usingCustomParseOptions
);
941 nsCOMPtr
<nsIInputStream
> stream
;
943 ExtractData(aCx
, aSubjectPrincipal
, type
, params
, getter_AddRefs(stream
));
945 // If there are unrecognized custom parse options, we should fall back to
946 // the default values for the encoder without any options at all.
947 if (rv
== NS_ERROR_INVALID_ARG
&& usingCustomParseOptions
) {
948 rv
= ExtractData(aCx
, aSubjectPrincipal
, type
, u
""_ns
,
949 getter_AddRefs(stream
));
952 NS_ENSURE_SUCCESS(rv
, rv
);
954 // build data URL string
955 aDataURL
= u
"data:"_ns
+ type
+ u
";base64,"_ns
;
958 rv
= stream
->Available(&count
);
959 NS_ENSURE_SUCCESS(rv
, rv
);
960 NS_ENSURE_TRUE(count
<= UINT32_MAX
, NS_ERROR_FILE_TOO_BIG
);
962 return Base64EncodeInputStream(stream
, aDataURL
, (uint32_t)count
,
966 UniquePtr
<uint8_t[]> HTMLCanvasElement::GetImageBuffer(
967 int32_t* aOutFormat
, gfx::IntSize
* aOutImageSize
) {
968 if (mCurrentContext
) {
969 return mCurrentContext
->GetImageBuffer(aOutFormat
, aOutImageSize
);
971 if (mOffscreenDisplay
) {
972 return mOffscreenDisplay
->GetImageBuffer(aOutFormat
, aOutImageSize
);
977 void HTMLCanvasElement::ToBlob(JSContext
* aCx
, BlobCallback
& aCallback
,
978 const nsAString
& aType
,
979 JS::Handle
<JS::Value
> aParams
,
980 nsIPrincipal
& aSubjectPrincipal
,
982 // mWriteOnly check is redundant, but optimizes for the common case.
983 if (mWriteOnly
&& !CallerCanRead(aSubjectPrincipal
)) {
984 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
988 nsCOMPtr
<nsIGlobalObject
> global
= OwnerDoc()->GetScopeObject();
991 nsIntSize elemSize
= GetWidthHeight();
992 if (elemSize
.width
== 0 || elemSize
.height
== 0) {
993 // According to spec, blob should return null if either its horizontal
994 // dimension or its vertical dimension is zero. See link below.
995 // https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
996 OwnerDoc()->Dispatch(NewRunnableMethod
<Blob
*, const char*>(
997 "dom::HTMLCanvasElement::ToBlob", &aCallback
,
998 static_cast<void (BlobCallback::*)(Blob
*, const char*)>(
999 &BlobCallback::Call
),
1004 // Check site-specific permission and display prompt if appropriate.
1005 // If no permission, return all-white, opaque image data.
1006 bool usePlaceholder
= !CanvasUtils::IsImageExtractionAllowed(
1007 OwnerDoc(), aCx
, aSubjectPrincipal
);
1008 CanvasRenderingContextHelper::ToBlob(aCx
, global
, aCallback
, aType
, aParams
,
1009 usePlaceholder
, aRv
);
1012 OffscreenCanvas
* HTMLCanvasElement::TransferControlToOffscreen(
1014 if (mCurrentContext
|| mOffscreenCanvas
) {
1015 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1019 MOZ_ASSERT(!mOffscreenDisplay
);
1021 nsPIDOMWindowInner
* win
= OwnerDoc()->GetInnerWindow();
1023 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1027 LayersBackend backend
= LayersBackend::LAYERS_NONE
;
1028 TextureType textureType
= TextureType::Unknown
;
1029 nsIWidget
* docWidget
= nsContentUtils::WidgetForDocument(OwnerDoc());
1031 WindowRenderer
* renderer
= docWidget
->GetWindowRenderer();
1033 backend
= renderer
->GetCompositorBackendType();
1034 textureType
= TexTypeForWebgl(renderer
->AsKnowsCompositor());
1038 nsIntSize sz
= GetWidthHeight();
1040 MakeRefPtr
<OffscreenCanvasDisplayHelper
>(this, sz
.width
, sz
.height
);
1042 new OffscreenCanvas(win
->AsGlobal(), sz
.width
, sz
.height
, backend
,
1043 textureType
, do_AddRef(mOffscreenDisplay
));
1045 mOffscreenCanvas
->SetWriteOnly(mExpandedReader
);
1048 if (!mContextObserver
) {
1049 mContextObserver
= new HTMLCanvasElementObserver(this);
1052 return mOffscreenCanvas
;
1055 nsresult
HTMLCanvasElement::GetContext(const nsAString
& aContextId
,
1056 nsISupports
** aContext
) {
1058 mMaybeModified
= true; // For FirstContentfulPaint
1059 *aContext
= GetContext(nullptr, aContextId
, JS::NullHandleValue
, rv
).take();
1060 return rv
.StealNSResult();
1063 already_AddRefed
<nsISupports
> HTMLCanvasElement::GetContext(
1064 JSContext
* aCx
, const nsAString
& aContextId
,
1065 JS::Handle
<JS::Value
> aContextOptions
, ErrorResult
& aRv
) {
1066 if (mOffscreenCanvas
) {
1067 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1071 mMaybeModified
= true; // For FirstContentfulPaint
1072 return CanvasRenderingContextHelper::GetOrCreateContext(
1074 aContextOptions
.isObject() ? aContextOptions
: JS::NullHandleValue
, aRv
);
1077 nsIntSize
HTMLCanvasElement::GetSize() { return GetWidthHeight(); }
1079 bool HTMLCanvasElement::IsWriteOnly() const { return mWriteOnly
; }
1081 void HTMLCanvasElement::SetWriteOnly(
1082 nsIPrincipal
* aExpandedReader
/* = nullptr */) {
1083 mExpandedReader
= aExpandedReader
;
1085 if (mOffscreenCanvas
) {
1086 mOffscreenCanvas
->SetWriteOnly(aExpandedReader
);
1090 bool HTMLCanvasElement::CallerCanRead(nsIPrincipal
& aPrincipal
) const {
1095 // If mExpandedReader is set, this canvas was tainted only by
1096 // mExpandedReader's resources. So allow reading if the subject
1097 // principal subsumes mExpandedReader.
1098 if (mExpandedReader
&& aPrincipal
.Subsumes(mExpandedReader
)) {
1102 return nsContentUtils::PrincipalHasPermission(aPrincipal
,
1103 nsGkAtoms::all_urlsPermission
);
1106 void HTMLCanvasElement::SetWidth(uint32_t aWidth
, ErrorResult
& aRv
) {
1107 if (mOffscreenCanvas
) {
1108 aRv
.ThrowInvalidStateError(
1109 "Cannot set width of placeholder canvas transferred to "
1110 "OffscreenCanvas.");
1114 SetUnsignedIntAttr(nsGkAtoms::width
, aWidth
, DEFAULT_CANVAS_WIDTH
, aRv
);
1117 void HTMLCanvasElement::SetHeight(uint32_t aHeight
, ErrorResult
& aRv
) {
1118 if (mOffscreenCanvas
) {
1119 aRv
.ThrowInvalidStateError(
1120 "Cannot set height of placeholder canvas transferred to "
1121 "OffscreenCanvas.");
1125 SetUnsignedIntAttr(nsGkAtoms::height
, aHeight
, DEFAULT_CANVAS_HEIGHT
, aRv
);
1128 void HTMLCanvasElement::SetSize(const nsIntSize
& aSize
, ErrorResult
& aRv
) {
1129 if (mOffscreenCanvas
) {
1130 aRv
.ThrowInvalidStateError(
1131 "Cannot set width of placeholder canvas transferred to "
1132 "OffscreenCanvas.");
1136 if (NS_WARN_IF(aSize
.IsEmpty())) {
1137 aRv
.ThrowRangeError("Canvas size is empty, must be non-empty.");
1141 SetUnsignedIntAttr(nsGkAtoms::width
, aSize
.width
, DEFAULT_CANVAS_WIDTH
, aRv
);
1142 MOZ_ASSERT(!aRv
.Failed());
1143 SetUnsignedIntAttr(nsGkAtoms::height
, aSize
.height
, DEFAULT_CANVAS_HEIGHT
,
1145 MOZ_ASSERT(!aRv
.Failed());
1148 void HTMLCanvasElement::FlushOffscreenCanvas() {
1149 if (mOffscreenDisplay
) {
1150 mOffscreenDisplay
->FlushForDisplay();
1154 void HTMLCanvasElement::InvalidateCanvasPlaceholder(uint32_t aWidth
,
1157 SetUnsignedIntAttr(nsGkAtoms::width
, aWidth
, DEFAULT_CANVAS_WIDTH
, rv
);
1158 MOZ_ASSERT(!rv
.Failed());
1159 SetUnsignedIntAttr(nsGkAtoms::height
, aHeight
, DEFAULT_CANVAS_HEIGHT
, rv
);
1160 MOZ_ASSERT(!rv
.Failed());
1163 void HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect
* damageRect
) {
1164 // Cache the current ImageContainer to avoid contention on the mutex.
1165 if (mOffscreenDisplay
) {
1166 mImageContainer
= mOffscreenDisplay
->GetImageContainer();
1169 // We don't need to flush anything here; if there's no frame or if
1170 // we plan to reframe we don't need to invalidate it anyway.
1171 nsIFrame
* frame
= GetPrimaryFrame();
1174 // When using layers-free WebRender, we cannot invalidate the layer (because
1175 // there isn't one). Instead, we mark the CanvasRenderer dirty and scheduling
1176 // an empty transaction which is effectively equivalent.
1177 CanvasRenderer
* renderer
= nullptr;
1178 const auto key
= static_cast<uint32_t>(DisplayItemType::TYPE_CANVAS
);
1179 RefPtr
<WebRenderCanvasData
> data
=
1180 GetWebRenderUserData
<WebRenderCanvasData
>(frame
, key
);
1182 renderer
= data
->GetCanvasRenderer();
1186 renderer
->SetDirty();
1187 frame
->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY
);
1190 nsIntSize size
= GetWidthHeight();
1191 if (size
.width
!= 0 && size
.height
!= 0) {
1192 gfx::IntRect invalRect
= gfx::IntRect::Truncate(*damageRect
);
1193 frame
->InvalidateLayer(DisplayItemType::TYPE_CANVAS
, &invalRect
);
1196 frame
->InvalidateLayer(DisplayItemType::TYPE_CANVAS
);
1199 // This path is taken in two situations:
1200 // 1) WebRender is enabled and has not yet processed a display list.
1201 // 2) WebRender is disabled and layer invalidation failed.
1202 // In both cases, schedule a full paint to properly update canvas.
1203 frame
->SchedulePaint(nsIFrame::PAINT_DEFAULT
, false);
1207 * Treat canvas invalidations as animation activity for JS. Frequently
1208 * invalidating a canvas will feed into heuristics and cause JIT code to be
1209 * kept around longer, for smoother animations.
1211 nsPIDOMWindowInner
* win
= OwnerDoc()->GetInnerWindow();
1214 if (JSObject
* obj
= win
->AsGlobal()->GetGlobalJSObject()) {
1215 js::NotifyAnimationActivity(obj
);
1220 void HTMLCanvasElement::InvalidateCanvas() {
1221 // We don't need to flush anything here; if there's no frame or if
1222 // we plan to reframe we don't need to invalidate it anyway.
1223 nsIFrame
* frame
= GetPrimaryFrame();
1226 frame
->InvalidateFrame();
1229 bool HTMLCanvasElement::GetIsOpaque() {
1230 if (mCurrentContext
) {
1231 return mCurrentContext
->GetIsOpaque();
1234 return GetOpaqueAttr();
1237 bool HTMLCanvasElement::GetOpaqueAttr() {
1238 return HasAttr(nsGkAtoms::moz_opaque
);
1241 CanvasContextType
HTMLCanvasElement::GetCurrentContextType() {
1242 if (mCurrentContextType
== CanvasContextType::NoContext
&&
1243 mOffscreenDisplay
) {
1244 mCurrentContextType
= mOffscreenDisplay
->GetContextType();
1246 return mCurrentContextType
;
1249 already_AddRefed
<Image
> HTMLCanvasElement::GetAsImage() {
1250 if (mOffscreenDisplay
) {
1251 return mOffscreenDisplay
->GetAsImage();
1254 if (mCurrentContext
) {
1255 return mCurrentContext
->GetAsImage();
1261 bool HTMLCanvasElement::UpdateWebRenderCanvasData(
1262 nsDisplayListBuilder
* aBuilder
, WebRenderCanvasData
* aCanvasData
) {
1263 MOZ_ASSERT(!mOffscreenDisplay
);
1265 if (mCurrentContext
) {
1266 return mCurrentContext
->UpdateWebRenderCanvasData(aBuilder
, aCanvasData
);
1269 // Clear CanvasRenderer of WebRenderCanvasData
1270 aCanvasData
->ClearCanvasRenderer();
1274 bool HTMLCanvasElement::InitializeCanvasRenderer(nsDisplayListBuilder
* aBuilder
,
1275 CanvasRenderer
* aRenderer
) {
1276 MOZ_ASSERT(!mOffscreenDisplay
);
1278 if (mCurrentContext
) {
1279 return mCurrentContext
->InitializeCanvasRenderer(aBuilder
, aRenderer
);
1285 void HTMLCanvasElement::MarkContextClean() {
1286 if (!mCurrentContext
) return;
1288 mCurrentContext
->MarkContextClean();
1291 void HTMLCanvasElement::MarkContextCleanForFrameCapture() {
1292 if (!mCurrentContext
) return;
1294 mCurrentContext
->MarkContextCleanForFrameCapture();
1297 Watchable
<FrameCaptureState
>* HTMLCanvasElement::GetFrameCaptureState() {
1298 if (!mCurrentContext
) {
1301 return mCurrentContext
->GetFrameCaptureState();
1304 nsresult
HTMLCanvasElement::RegisterFrameCaptureListener(
1305 FrameCaptureListener
* aListener
, bool aReturnPlaceholderData
) {
1306 WeakPtr
<FrameCaptureListener
> listener
= aListener
;
1308 if (mRequestedFrameListeners
.Contains(listener
)) {
1312 if (!mRequestedFrameRefreshObserver
) {
1313 Document
* doc
= OwnerDoc();
1315 return NS_ERROR_FAILURE
;
1318 PresShell
* shell
= nsContentUtils::FindPresShellForDocument(doc
);
1320 return NS_ERROR_FAILURE
;
1323 nsPresContext
* context
= shell
->GetPresContext();
1325 return NS_ERROR_FAILURE
;
1328 context
= context
->GetRootPresContext();
1330 return NS_ERROR_FAILURE
;
1333 nsRefreshDriver
* driver
= context
->RefreshDriver();
1335 return NS_ERROR_FAILURE
;
1338 mRequestedFrameRefreshObserver
=
1339 new RequestedFrameRefreshObserver(this, driver
, aReturnPlaceholderData
);
1341 mRequestedFrameRefreshObserver
->SetReturnPlaceholderData(
1342 aReturnPlaceholderData
);
1345 mRequestedFrameListeners
.AppendElement(listener
);
1346 mRequestedFrameRefreshObserver
->Register();
1350 bool HTMLCanvasElement::IsFrameCaptureRequested(const TimeStamp
& aTime
) const {
1351 for (WeakPtr
<FrameCaptureListener
> listener
: mRequestedFrameListeners
) {
1356 if (listener
->FrameCaptureRequested(aTime
)) {
1363 void HTMLCanvasElement::ProcessDestroyedFrameListeners() {
1364 // Remove destroyed listeners from the list.
1365 mRequestedFrameListeners
.RemoveElementsBy(
1366 [](const auto& weakListener
) { return !weakListener
; });
1368 if (mRequestedFrameListeners
.IsEmpty()) {
1369 mRequestedFrameRefreshObserver
->Unregister();
1373 void HTMLCanvasElement::SetFrameCapture(
1374 already_AddRefed
<SourceSurface
> aSurface
, const TimeStamp
& aTime
) {
1375 RefPtr
<SourceSurface
> surface
= aSurface
;
1376 RefPtr
<SourceSurfaceImage
> image
=
1377 new SourceSurfaceImage(surface
->GetSize(), surface
);
1379 for (WeakPtr
<FrameCaptureListener
> listener
: mRequestedFrameListeners
) {
1384 RefPtr
<Image
> imageRefCopy
= image
.get();
1385 listener
->NewFrame(imageRefCopy
.forget(), aTime
);
1389 already_AddRefed
<SourceSurface
> HTMLCanvasElement::GetSurfaceSnapshot(
1390 gfxAlphaType
* const aOutAlphaType
, DrawTarget
* aTarget
) {
1391 if (mCurrentContext
) {
1392 return mCurrentContext
->GetOptimizedSnapshot(aTarget
, aOutAlphaType
);
1393 } else if (mOffscreenDisplay
) {
1394 return mOffscreenDisplay
->GetSurfaceSnapshot();
1399 layers::LayersBackend
HTMLCanvasElement::GetCompositorBackendType() const {
1400 nsIWidget
* docWidget
= nsContentUtils::WidgetForDocument(OwnerDoc());
1402 WindowRenderer
* renderer
= docWidget
->GetWindowRenderer();
1404 return renderer
->GetCompositorBackendType();
1408 return LayersBackend::LAYERS_NONE
;
1411 void HTMLCanvasElement::OnMemoryPressure() {
1412 // FIXME(aosmond): We need to implement memory pressure handling for
1413 // OffscreenCanvas when it is on worker threads. See bug 1746260.
1415 if (mCurrentContext
) {
1416 mCurrentContext
->OnMemoryPressure();
1420 void HTMLCanvasElement::OnDeviceReset() {
1421 if (!mOffscreenCanvas
&& mCurrentContext
) {
1422 mCurrentContext
->ResetBitmap();
1426 ClientWebGLContext
* HTMLCanvasElement::GetWebGLContext() {
1427 if (GetCurrentContextType() != CanvasContextType::WebGL1
&&
1428 GetCurrentContextType() != CanvasContextType::WebGL2
) {
1432 return static_cast<ClientWebGLContext
*>(GetCurrentContext());
1435 webgpu::CanvasContext
* HTMLCanvasElement::GetWebGPUContext() {
1436 if (GetCurrentContextType() != CanvasContextType::WebGPU
) {
1440 return static_cast<webgpu::CanvasContext
*>(GetCurrentContext());
1443 } // namespace mozilla::dom