Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / html / HTMLCanvasElement.cpp
blob93a7bb378736e0bf470dcdfe2ced6852871aa509
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"
10 #include "jsapi.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"
45 #include "nsITimer.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)
68 public:
69 RequestedFrameRefreshObserver(HTMLCanvasElement* const aOwningElement,
70 nsRefreshDriver* aRefreshDriver,
71 bool aReturnPlaceholderData)
72 : mRegistered(false),
73 mWatching(false),
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();
85 if (!data) {
86 return nullptr;
89 DataSourceSurface::ScopedMap read(data, DataSourceSurface::READ);
90 if (!read.IsMapped()) {
91 return nullptr;
94 RefPtr<DataSourceSurface> copy = Factory::CreateDataSourceSurfaceWithStride(
95 data->GetSize(), data->GetFormat(), read.GetStride());
96 if (!copy) {
97 return nullptr;
100 DataSourceSurface::ScopedMap write(copy, DataSourceSurface::WRITE);
101 if (!write.IsMapped()) {
102 return nullptr;
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);
113 } else {
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) {
127 return;
130 if (!mOwningElement) {
131 return;
134 Watchable<FrameCaptureState>* captureState =
135 mOwningElement->GetFrameCaptureState();
136 if (!captureState) {
137 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
138 "Abort: No capture state"_ns);
139 return;
142 if (captureState->Ref() == FrameCaptureState::CLEAN) {
143 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
144 "Abort: CLEAN"_ns);
145 return;
148 if (!mRefreshDriver) {
149 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
150 "Abort: no refresh driver"_ns);
151 return;
154 if (!mRefreshDriver->IsThrottled()) {
155 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
156 "Abort: not throttled"_ns);
157 return;
160 TimeStamp now = TimeStamp::Now();
161 TimeStamp next =
162 mLastCaptureTime.IsNull()
163 ? now
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);
169 CaptureFrame(now);
170 return;
173 nsCString str;
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(
183 __func__,
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);
189 CaptureFrame(next);
191 // next >= now, so this is a guard for (next - now) flooring to 0.
192 std::max<uint32_t>(
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);
200 CaptureFrame(aTime);
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);
209 return;
212 if (mOwningElement->IsWriteOnly()) {
213 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
214 "Abort: write only"_ns);
215 return;
218 if (auto* captureStateWatchable = mOwningElement->GetFrameCaptureState();
219 captureStateWatchable &&
220 *captureStateWatchable == FrameCaptureState::CLEAN) {
221 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
222 "Abort: CLEAN"_ns);
223 return;
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);
237 return;
240 RefPtr<SourceSurface> snapshot;
242 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
243 "GetSnapshot"_ns);
244 snapshot = mOwningElement->GetSurfaceSnapshot(nullptr);
245 if (!snapshot) {
246 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
247 "Abort: snapshot failed"_ns);
248 return;
252 RefPtr<DataSourceSurface> copy;
254 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
255 "CopySurface"_ns);
256 copy = CopySurface(snapshot, mReturnPlaceholderData);
257 if (!copy) {
258 PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
259 "Abort: copy failed"_ns);
260 return;
264 nsCString str;
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);
285 Unregister();
286 mRefreshDriver = nullptr;
287 mWatchManager.Shutdown();
290 bool IsRegisteredAndWatching() { return mRegistered && mWatching; }
292 void Register() {
293 if (!mRegistered) {
294 MOZ_ASSERT(mRefreshDriver);
295 if (mRefreshDriver) {
296 mRefreshDriver->AddRefreshObserver(this, FlushType::Display,
297 "Canvas frame capture listeners");
298 mRegistered = true;
302 if (mWatching) {
303 return;
306 if (!mOwningElement) {
307 return;
310 if (Watchable<FrameCaptureState>* captureState =
311 mOwningElement->GetFrameCaptureState()) {
312 mWatchManager.Watch(
313 *captureState,
314 &RequestedFrameRefreshObserver::NotifyCaptureStateChange);
315 mWatching = true;
319 void Unregister() {
320 if (mRegistered) {
321 MOZ_ASSERT(mRefreshDriver);
322 if (mRefreshDriver) {
323 mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display);
324 mRegistered = false;
328 if (!mWatching) {
329 return;
332 if (!mOwningElement) {
333 return;
336 if (Watchable<FrameCaptureState>* captureState =
337 mOwningElement->GetFrameCaptureState()) {
338 mWatchManager.Unwatch(
339 *captureState,
340 &RequestedFrameRefreshObserver::NotifyCaptureStateChange);
341 mWatching = false;
345 private:
346 virtual ~RequestedFrameRefreshObserver() {
347 MOZ_ASSERT(!mRefreshDriver);
348 MOZ_ASSERT(!mRegistered);
349 MOZ_ASSERT(!mWatching);
352 bool mRegistered;
353 bool 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,
365 mCallback)
367 HTMLCanvasPrintState::HTMLCanvasPrintState(
368 HTMLCanvasElement* aCanvas, nsICanvasRenderingContextInternal* aContext,
369 nsITimerCallback* aCallback)
370 : mIsDone(false),
371 mPendingNotify(false),
372 mCanvas(aCanvas),
373 mContext(aContext),
374 mCallback(aCallback) {}
376 HTMLCanvasPrintState::~HTMLCanvasPrintState() = default;
378 /* virtual */
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
389 // work.
390 if (mCanvas) {
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() {
403 mIsDone = true;
404 mPendingNotify = false;
405 if (mCallback) {
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();
422 mElement = nullptr;
425 void HTMLCanvasElementObserver::RegisterObserverEvents() {
426 if (!mElement) {
427 return;
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() {
442 if (!mElement) {
443 return;
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");
458 NS_IMETHODIMP
459 HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic,
460 const char16_t*) {
461 if (!mElement) {
462 return NS_OK;
465 if (strcmp(aTopic, "memory-pressure") == 0) {
466 mElement->OnMemoryPressure();
467 } else if (strcmp(aTopic, "canvas-device-reset") == 0) {
468 mElement->OnDeviceReset();
471 return NS_OK;
474 NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
476 // ---------------------------------------------------------------------------
478 HTMLCanvasElement::HTMLCanvasElement(
479 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
480 : nsGenericHTMLElement(std::move(aNodeInfo)),
481 mResetLayer(true),
482 mMaybeModified(false),
483 mWriteOnly(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)
510 tmp->Destroy();
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,
519 mOffscreenCanvas)
520 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
522 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLCanvasElement,
523 nsGenericHTMLElement)
525 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
527 /* virtual */
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)) {
539 return nullptr;
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);
552 return ret.forget();
555 nsresult HTMLCanvasElement::UpdateContext(
556 JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions,
557 ErrorResult& aRvForDictionaryInit) {
558 nsresult rv = CanvasRenderingContextHelper::UpdateContext(
559 aCx, aNewContextOptions, aRvForDictionaryInit);
561 if (NS_FAILED(rv)) {
562 return rv;
565 // If we have a mRequestedFrameRefreshObserver that wasn't fully registered,
566 // retry that now.
567 if (mRequestedFrameRefreshObserver.get() &&
568 !mRequestedFrameRefreshObserver->IsRegisteredAndWatching()) {
569 mRequestedFrameRefreshObserver->Register();
572 return NS_OK;
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");
593 return size;
596 void HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
597 const nsAttrValue* aValue,
598 const nsAttrValue* aOldValue,
599 nsIPrincipal* aSubjectPrincipal,
600 bool aNotify) {
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,
609 bool aNotify) {
610 AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
612 return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
613 aValue, aNotify);
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)) {
621 ErrorResult dummy;
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) {
642 nsresult rv;
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);
658 if (!mPrintState) {
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.
662 return;
664 RefPtr<PrintCallback> callback = GetMozPrintCallback();
665 RefPtr<HTMLCanvasPrintState> state = mPrintState;
666 callback->Call(*state);
669 void HTMLCanvasElement::ResetPrintCallback() {
670 if (mPrintState) {
671 mPrintState = nullptr;
675 bool HTMLCanvasElement::IsPrintCallbackDone() {
676 if (mPrintState == nullptr) {
677 return true;
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
702 // to fail.
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;
712 ErrorResult err;
713 context2d->DrawImage(source, 0.0, 0.0, err);
714 rv = err.StealNSResult();
718 return rv;
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;
730 return retval;
733 void HTMLCanvasElement::MapAttributesIntoRule(
734 MappedDeclarationsBuilder& aBuilder) {
735 MapAspectRatioInto(aBuilder);
736 MapCommonAttributesInto(aBuilder);
739 nsMapRuleToAttributesFunc HTMLCanvasElement::GetAttributeMappingFunction()
740 const {
741 return &MapAttributesIntoRule;
744 NS_IMETHODIMP_(bool)
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,
768 nsAString& aDataURL,
769 nsIPrincipal& aSubjectPrincipal,
770 ErrorResult& aRv) {
771 // mWriteOnly check is redundant, but optimizes for the common case.
772 if (mWriteOnly && !CallerCanRead(aSubjectPrincipal)) {
773 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
774 return;
777 nsresult rv = ToDataURLImpl(aCx, aSubjectPrincipal, aType, aParams, aDataURL);
778 if (NS_FAILED(rv)) {
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 {
796 public:
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
816 return false;
818 return !mCaptureStream->Canvas()->GetIsOpaque();
821 void Stop() override {
822 if (!mCaptureStream) {
823 return;
826 mCaptureStream->StopCapture();
829 void Disable() override {}
831 void Enable() override {}
833 private:
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,
848 ErrorResult& aRv) {
849 if (IsWriteOnly()) {
850 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
851 return nullptr;
854 nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
855 if (!window) {
856 aRv.Throw(NS_ERROR_FAILURE);
857 return nullptr;
860 auto stream = MakeRefPtr<CanvasCaptureMediaStream>(window, this);
862 nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
863 nsresult rv = stream->Init(aFrameRate, principal);
864 if (NS_FAILED(rv)) {
865 aRv.Throw(rv);
866 return nullptr;
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(),
881 usePlaceholder);
882 if (NS_FAILED(rv)) {
883 aRv.Throw(rv);
884 return nullptr;
887 return stream.forget();
890 nsresult HTMLCanvasElement::ExtractData(JSContext* aCx,
891 nsIPrincipal& aSubjectPrincipal,
892 nsAString& aType,
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) {
905 if (auto ctx =
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;
927 return NS_OK;
930 nsAutoString type;
931 nsContentUtils::ASCIIToLower(aMimeType, type);
933 nsAutoString params;
934 bool usingCustomParseOptions;
935 nsresult rv =
936 ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
937 if (NS_FAILED(rv)) {
938 return rv;
941 nsCOMPtr<nsIInputStream> stream;
942 rv =
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;
957 uint64_t count;
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,
963 aDataURL.Length());
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);
974 return nullptr;
977 void HTMLCanvasElement::ToBlob(JSContext* aCx, BlobCallback& aCallback,
978 const nsAString& aType,
979 JS::Handle<JS::Value> aParams,
980 nsIPrincipal& aSubjectPrincipal,
981 ErrorResult& aRv) {
982 // mWriteOnly check is redundant, but optimizes for the common case.
983 if (mWriteOnly && !CallerCanRead(aSubjectPrincipal)) {
984 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
985 return;
988 nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
989 MOZ_ASSERT(global);
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),
1000 nullptr, nullptr));
1001 return;
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(
1013 ErrorResult& aRv) {
1014 if (mCurrentContext || mOffscreenCanvas) {
1015 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1016 return nullptr;
1019 MOZ_ASSERT(!mOffscreenDisplay);
1021 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
1022 if (!win) {
1023 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1024 return nullptr;
1027 LayersBackend backend = LayersBackend::LAYERS_NONE;
1028 TextureType textureType = TextureType::Unknown;
1029 nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
1030 if (docWidget) {
1031 WindowRenderer* renderer = docWidget->GetWindowRenderer();
1032 if (renderer) {
1033 backend = renderer->GetCompositorBackendType();
1034 textureType = TexTypeForWebgl(renderer->AsKnowsCompositor());
1038 nsIntSize sz = GetWidthHeight();
1039 mOffscreenDisplay =
1040 MakeRefPtr<OffscreenCanvasDisplayHelper>(this, sz.width, sz.height);
1041 mOffscreenCanvas =
1042 new OffscreenCanvas(win->AsGlobal(), sz.width, sz.height, backend,
1043 textureType, do_AddRef(mOffscreenDisplay));
1044 if (mWriteOnly) {
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) {
1057 ErrorResult rv;
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);
1068 return nullptr;
1071 mMaybeModified = true; // For FirstContentfulPaint
1072 return CanvasRenderingContextHelper::GetOrCreateContext(
1073 aCx, aContextId,
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;
1084 mWriteOnly = true;
1085 if (mOffscreenCanvas) {
1086 mOffscreenCanvas->SetWriteOnly(aExpandedReader);
1090 bool HTMLCanvasElement::CallerCanRead(nsIPrincipal& aPrincipal) const {
1091 if (!mWriteOnly) {
1092 return true;
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)) {
1099 return true;
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.");
1111 return;
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.");
1122 return;
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.");
1133 return;
1136 if (NS_WARN_IF(aSize.IsEmpty())) {
1137 aRv.ThrowRangeError("Canvas size is empty, must be non-empty.");
1138 return;
1141 SetUnsignedIntAttr(nsGkAtoms::width, aSize.width, DEFAULT_CANVAS_WIDTH, aRv);
1142 MOZ_ASSERT(!aRv.Failed());
1143 SetUnsignedIntAttr(nsGkAtoms::height, aSize.height, DEFAULT_CANVAS_HEIGHT,
1144 aRv);
1145 MOZ_ASSERT(!aRv.Failed());
1148 void HTMLCanvasElement::FlushOffscreenCanvas() {
1149 if (mOffscreenDisplay) {
1150 mOffscreenDisplay->FlushForDisplay();
1154 void HTMLCanvasElement::InvalidateCanvasPlaceholder(uint32_t aWidth,
1155 uint32_t aHeight) {
1156 ErrorResult rv;
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();
1172 if (!frame) return;
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);
1181 if (data) {
1182 renderer = data->GetCanvasRenderer();
1185 if (renderer) {
1186 renderer->SetDirty();
1187 frame->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
1188 } else {
1189 if (damageRect) {
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);
1195 } else {
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();
1213 if (win) {
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();
1224 if (!frame) return;
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();
1258 return nullptr;
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();
1271 return false;
1274 bool HTMLCanvasElement::InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
1275 CanvasRenderer* aRenderer) {
1276 MOZ_ASSERT(!mOffscreenDisplay);
1278 if (mCurrentContext) {
1279 return mCurrentContext->InitializeCanvasRenderer(aBuilder, aRenderer);
1282 return false;
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) {
1299 return nullptr;
1301 return mCurrentContext->GetFrameCaptureState();
1304 nsresult HTMLCanvasElement::RegisterFrameCaptureListener(
1305 FrameCaptureListener* aListener, bool aReturnPlaceholderData) {
1306 WeakPtr<FrameCaptureListener> listener = aListener;
1308 if (mRequestedFrameListeners.Contains(listener)) {
1309 return NS_OK;
1312 if (!mRequestedFrameRefreshObserver) {
1313 Document* doc = OwnerDoc();
1314 if (!doc) {
1315 return NS_ERROR_FAILURE;
1318 PresShell* shell = nsContentUtils::FindPresShellForDocument(doc);
1319 if (!shell) {
1320 return NS_ERROR_FAILURE;
1323 nsPresContext* context = shell->GetPresContext();
1324 if (!context) {
1325 return NS_ERROR_FAILURE;
1328 context = context->GetRootPresContext();
1329 if (!context) {
1330 return NS_ERROR_FAILURE;
1333 nsRefreshDriver* driver = context->RefreshDriver();
1334 if (!driver) {
1335 return NS_ERROR_FAILURE;
1338 mRequestedFrameRefreshObserver =
1339 new RequestedFrameRefreshObserver(this, driver, aReturnPlaceholderData);
1340 } else {
1341 mRequestedFrameRefreshObserver->SetReturnPlaceholderData(
1342 aReturnPlaceholderData);
1345 mRequestedFrameListeners.AppendElement(listener);
1346 mRequestedFrameRefreshObserver->Register();
1347 return NS_OK;
1350 bool HTMLCanvasElement::IsFrameCaptureRequested(const TimeStamp& aTime) const {
1351 for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1352 if (!listener) {
1353 continue;
1356 if (listener->FrameCaptureRequested(aTime)) {
1357 return true;
1360 return false;
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) {
1380 if (!listener) {
1381 continue;
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();
1396 return nullptr;
1399 layers::LayersBackend HTMLCanvasElement::GetCompositorBackendType() const {
1400 nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
1401 if (docWidget) {
1402 WindowRenderer* renderer = docWidget->GetWindowRenderer();
1403 if (renderer) {
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) {
1429 return nullptr;
1432 return static_cast<ClientWebGLContext*>(GetCurrentContext());
1435 webgpu::CanvasContext* HTMLCanvasElement::GetWebGPUContext() {
1436 if (GetCurrentContextType() != CanvasContextType::WebGPU) {
1437 return nullptr;
1440 return static_cast<webgpu::CanvasContext*>(GetCurrentContext());
1443 } // namespace mozilla::dom