Bug 1494333 - index crons just like artifacts r=Callek
[gecko.git] / dom / html / HTMLCanvasElement.cpp
blobe7894cf6f9f4b5a6f4a57d9f7abf4554374d6b28
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 "gfxPrefs.h"
10 #include "ImageEncoder.h"
11 #include "jsapi.h"
12 #include "jsfriendapi.h"
13 #include "Layers.h"
14 #include "MediaSegment.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/Base64.h"
17 #include "mozilla/CheckedInt.h"
18 #include "mozilla/dom/CanvasCaptureMediaStream.h"
19 #include "mozilla/dom/CanvasRenderingContext2D.h"
20 #include "mozilla/dom/Event.h"
21 #include "mozilla/dom/File.h"
22 #include "mozilla/dom/HTMLCanvasElementBinding.h"
23 #include "mozilla/dom/MediaStreamTrack.h"
24 #include "mozilla/dom/MouseEvent.h"
25 #include "mozilla/dom/OffscreenCanvas.h"
26 #include "mozilla/EventDispatcher.h"
27 #include "mozilla/gfx/Rect.h"
28 #include "mozilla/layers/AsyncCanvasRenderer.h"
29 #include "mozilla/layers/WebRenderCanvasRenderer.h"
30 #include "mozilla/layers/WebRenderUserData.h"
31 #include "mozilla/MouseEvents.h"
32 #include "mozilla/Preferences.h"
33 #include "mozilla/Telemetry.h"
34 #include "nsAttrValueInlines.h"
35 #include "nsContentUtils.h"
36 #include "nsDisplayList.h"
37 #include "nsDOMJSUtils.h"
38 #include "nsIScriptSecurityManager.h"
39 #include "nsITimer.h"
40 #include "nsIWritablePropertyBag2.h"
41 #include "nsIXPConnect.h"
42 #include "nsJSUtils.h"
43 #include "nsLayoutUtils.h"
44 #include "nsMathUtils.h"
45 #include "nsNetUtil.h"
46 #include "nsRefreshDriver.h"
47 #include "nsStreamUtils.h"
48 #include "ActiveLayerTracker.h"
49 #include "CanvasUtils.h"
50 #include "VRManagerChild.h"
51 #include "WebGL1Context.h"
52 #include "WebGL2Context.h"
54 using namespace mozilla::layers;
55 using namespace mozilla::gfx;
57 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
59 namespace mozilla {
60 namespace dom {
62 class RequestedFrameRefreshObserver : public nsARefreshObserver
64 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RequestedFrameRefreshObserver, override)
66 public:
67 RequestedFrameRefreshObserver(HTMLCanvasElement* const aOwningElement,
68 nsRefreshDriver* aRefreshDriver,
69 bool aReturnPlaceholderData)
70 : mRegistered(false),
71 mReturnPlaceholderData(aReturnPlaceholderData),
72 mOwningElement(aOwningElement),
73 mRefreshDriver(aRefreshDriver)
75 MOZ_ASSERT(mOwningElement);
78 static already_AddRefed<DataSourceSurface>
79 CopySurface(const RefPtr<SourceSurface>& aSurface,
80 bool aReturnPlaceholderData)
82 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
83 if (!data) {
84 return nullptr;
87 DataSourceSurface::ScopedMap read(data, DataSourceSurface::READ);
88 if (!read.IsMapped()) {
89 return nullptr;
92 RefPtr<DataSourceSurface> copy =
93 Factory::CreateDataSourceSurfaceWithStride(data->GetSize(),
94 data->GetFormat(),
95 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 // If returning placeholder data, fill the frame copy with white pixels.
111 memset(write.GetData(), 0xFF,
112 write.GetStride() * copy->GetSize().height);
113 } else {
114 memcpy(write.GetData(), read.GetData(),
115 write.GetStride() * copy->GetSize().height);
118 return copy.forget();
121 void SetReturnPlaceholderData(bool aReturnPlaceholderData)
123 mReturnPlaceholderData = aReturnPlaceholderData;
126 void WillRefresh(TimeStamp aTime) override
128 MOZ_ASSERT(NS_IsMainThread());
130 AUTO_PROFILER_LABEL("RequestedFrameRefreshObserver::WillRefresh", OTHER);
132 if (!mOwningElement) {
133 return;
136 if (mOwningElement->IsWriteOnly()) {
137 return;
140 if (mOwningElement->IsContextCleanForFrameCapture()) {
141 return;
144 mOwningElement->ProcessDestroyedFrameListeners();
146 if (!mOwningElement->IsFrameCaptureRequested()) {
147 return;
150 RefPtr<SourceSurface> snapshot;
152 AUTO_PROFILER_LABEL(
153 "RequestedFrameRefreshObserver::WillRefresh:GetSnapshot", OTHER);
154 snapshot = mOwningElement->GetSurfaceSnapshot(nullptr);
155 if (!snapshot) {
156 return;
160 RefPtr<DataSourceSurface> copy;
162 AUTO_PROFILER_LABEL(
163 "RequestedFrameRefreshObserver::WillRefresh:CopySurface", OTHER);
164 copy = CopySurface(snapshot, mReturnPlaceholderData);
165 if (!copy) {
166 return;
171 AUTO_PROFILER_LABEL(
172 "RequestedFrameRefreshObserver::WillRefresh:SetFrame", OTHER);
173 mOwningElement->SetFrameCapture(copy.forget(), aTime);
174 mOwningElement->MarkContextCleanForFrameCapture();
178 void DetachFromRefreshDriver()
180 MOZ_ASSERT(mOwningElement);
181 MOZ_ASSERT(mRefreshDriver);
183 Unregister();
184 mRefreshDriver = nullptr;
187 void Register()
189 if (mRegistered) {
190 return;
193 MOZ_ASSERT(mRefreshDriver);
194 if (mRefreshDriver) {
195 mRefreshDriver->AddRefreshObserver(this, FlushType::Display);
196 mRegistered = true;
200 void Unregister()
202 if (!mRegistered) {
203 return;
206 MOZ_ASSERT(mRefreshDriver);
207 if (mRefreshDriver) {
208 mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display);
209 mRegistered = false;
213 private:
214 virtual ~RequestedFrameRefreshObserver()
216 MOZ_ASSERT(!mRefreshDriver);
217 MOZ_ASSERT(!mRegistered);
220 bool mRegistered;
221 bool mReturnPlaceholderData;
222 HTMLCanvasElement* const mOwningElement;
223 RefPtr<nsRefreshDriver> mRefreshDriver;
226 // ---------------------------------------------------------------------------
228 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLCanvasPrintState, mCanvas,
229 mContext, mCallback)
231 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLCanvasPrintState, AddRef)
232 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLCanvasPrintState, Release)
234 HTMLCanvasPrintState::HTMLCanvasPrintState(HTMLCanvasElement* aCanvas,
235 nsICanvasRenderingContextInternal* aContext,
236 nsITimerCallback* aCallback)
237 : mIsDone(false), mPendingNotify(false), mCanvas(aCanvas),
238 mContext(aContext), mCallback(aCallback)
242 HTMLCanvasPrintState::~HTMLCanvasPrintState()
246 /* virtual */ JSObject*
247 HTMLCanvasPrintState::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
249 return MozCanvasPrintState_Binding::Wrap(aCx, this, aGivenProto);
252 nsISupports*
253 HTMLCanvasPrintState::Context() const
255 return mContext;
258 void
259 HTMLCanvasPrintState::Done()
261 if (!mPendingNotify && !mIsDone) {
262 // The canvas needs to be invalidated for printing reftests on linux to
263 // work.
264 if (mCanvas) {
265 mCanvas->InvalidateCanvas();
267 RefPtr<nsRunnableMethod<HTMLCanvasPrintState>> doneEvent =
268 NewRunnableMethod("dom::HTMLCanvasPrintState::NotifyDone",
269 this,
270 &HTMLCanvasPrintState::NotifyDone);
271 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) {
272 mPendingNotify = true;
277 void
278 HTMLCanvasPrintState::NotifyDone()
280 mIsDone = true;
281 mPendingNotify = false;
282 if (mCallback) {
283 mCallback->Notify(nullptr);
287 // ---------------------------------------------------------------------------
289 HTMLCanvasElementObserver::HTMLCanvasElementObserver(HTMLCanvasElement* aElement)
290 : mElement(aElement)
292 RegisterVisibilityChangeEvent();
293 RegisterMemoryPressureEvent();
296 HTMLCanvasElementObserver::~HTMLCanvasElementObserver()
298 Destroy();
301 void
302 HTMLCanvasElementObserver::Destroy()
304 UnregisterMemoryPressureEvent();
305 UnregisterVisibilityChangeEvent();
306 mElement = nullptr;
309 void
310 HTMLCanvasElementObserver::RegisterVisibilityChangeEvent()
312 if (!mElement) {
313 return;
316 nsIDocument* document = mElement->OwnerDoc();
317 document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
318 this, true, false);
321 void
322 HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent()
324 if (!mElement) {
325 return;
328 nsIDocument* document = mElement->OwnerDoc();
329 document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
330 this, true);
333 void
334 HTMLCanvasElementObserver::RegisterMemoryPressureEvent()
336 if (!mElement) {
337 return;
340 nsCOMPtr<nsIObserverService> observerService =
341 mozilla::services::GetObserverService();
343 MOZ_ASSERT(observerService);
345 if (observerService)
346 observerService->AddObserver(this, "memory-pressure", false);
349 void
350 HTMLCanvasElementObserver::UnregisterMemoryPressureEvent()
352 if (!mElement) {
353 return;
356 nsCOMPtr<nsIObserverService> observerService =
357 mozilla::services::GetObserverService();
359 // Do not assert on observerService here. This might be triggered by
360 // the cycle collector at a late enough time, that XPCOM services are
361 // no longer available. See bug 1029504.
362 if (observerService)
363 observerService->RemoveObserver(this, "memory-pressure");
366 NS_IMETHODIMP
367 HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*)
369 if (!mElement || strcmp(aTopic, "memory-pressure")) {
370 return NS_OK;
373 mElement->OnMemoryPressure();
375 return NS_OK;
378 NS_IMETHODIMP
379 HTMLCanvasElementObserver::HandleEvent(Event* aEvent)
381 nsAutoString type;
382 aEvent->GetType(type);
383 if (!mElement || !type.EqualsLiteral("visibilitychange")) {
384 return NS_OK;
387 mElement->OnVisibilityChange();
389 return NS_OK;
392 NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
394 // ---------------------------------------------------------------------------
396 HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
397 : nsGenericHTMLElement(std::move(aNodeInfo)),
398 mResetLayer(true) ,
399 mWriteOnly(false)
402 HTMLCanvasElement::~HTMLCanvasElement()
404 if (mContextObserver) {
405 mContextObserver->Destroy();
406 mContextObserver = nullptr;
409 ResetPrintCallback();
410 if (mRequestedFrameRefreshObserver) {
411 mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
414 if (mAsyncCanvasRenderer) {
415 mAsyncCanvasRenderer->mHTMLCanvasElement = nullptr;
419 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
420 mCurrentContext, mPrintCallback,
421 mPrintState, mOriginalCanvas,
422 mOffscreenCanvas)
424 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLCanvasElement, nsGenericHTMLElement)
426 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
428 /* virtual */ JSObject*
429 HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
431 return HTMLCanvasElement_Binding::Wrap(aCx, this, aGivenProto);
434 already_AddRefed<nsICanvasRenderingContextInternal>
435 HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
437 // Note that the compositor backend will be LAYERS_NONE if there is no widget.
438 RefPtr<nsICanvasRenderingContextInternal> ret =
439 CreateContextHelper(aContextType, GetCompositorBackendType());
441 // Add Observer for webgl canvas.
442 if (aContextType == CanvasContextType::WebGL1 ||
443 aContextType == CanvasContextType::WebGL2) {
444 if (!mContextObserver) {
445 mContextObserver = new HTMLCanvasElementObserver(this);
449 ret->SetCanvasElement(this);
450 return ret.forget();
453 nsIntSize
454 HTMLCanvasElement::GetWidthHeight()
456 nsIntSize size(DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);
457 const nsAttrValue* value;
459 if ((value = GetParsedAttr(nsGkAtoms::width)) &&
460 value->Type() == nsAttrValue::eInteger)
462 size.width = value->GetIntegerValue();
465 if ((value = GetParsedAttr(nsGkAtoms::height)) &&
466 value->Type() == nsAttrValue::eInteger)
468 size.height = value->GetIntegerValue();
471 MOZ_ASSERT(size.width >= 0 && size.height >= 0,
472 "we should've required <canvas> width/height attrs to be "
473 "unsigned (non-negative) values");
475 return size;
478 nsresult
479 HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
480 const nsAttrValue* aValue,
481 const nsAttrValue* aOldValue,
482 nsIPrincipal* aSubjectPrincipal,
483 bool aNotify)
485 AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
487 return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
488 aOldValue, aSubjectPrincipal, aNotify);
491 nsresult
492 HTMLCanvasElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
493 const nsAttrValueOrString& aValue,
494 bool aNotify)
496 AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
498 return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
499 aValue, aNotify);
502 void
503 HTMLCanvasElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName,
504 bool aNotify)
506 if (mCurrentContext && aNamespaceID == kNameSpaceID_None &&
507 (aName == nsGkAtoms::width || aName == nsGkAtoms::height ||
508 aName == nsGkAtoms::moz_opaque)) {
509 ErrorResult dummy;
510 UpdateContext(nullptr, JS::NullHandleValue, dummy);
514 void
515 HTMLCanvasElement::HandlePrintCallback(nsPresContext::nsPresContextType aType)
517 // Only call the print callback here if 1) we're in a print testing mode or
518 // print preview mode, 2) the canvas has a print callback and 3) the callback
519 // hasn't already been called. For real printing the callback is handled in
520 // nsSimplePageSequenceFrame::PrePrintNextPage.
521 if ((aType == nsPresContext::eContext_PageLayout ||
522 aType == nsPresContext::eContext_PrintPreview) &&
523 !mPrintState && GetMozPrintCallback()) {
524 DispatchPrintCallback(nullptr);
528 nsresult
529 HTMLCanvasElement::DispatchPrintCallback(nsITimerCallback* aCallback)
531 // For print reftests the context may not be initialized yet, so get a context
532 // so mCurrentContext is set.
533 if (!mCurrentContext) {
534 nsresult rv;
535 nsCOMPtr<nsISupports> context;
536 rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
537 NS_ENSURE_SUCCESS(rv, rv);
539 mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback);
541 RefPtr<nsRunnableMethod<HTMLCanvasElement>> renderEvent =
542 NewRunnableMethod("dom::HTMLCanvasElement::CallPrintCallback",
543 this,
544 &HTMLCanvasElement::CallPrintCallback);
545 return OwnerDoc()->Dispatch(TaskCategory::Other, renderEvent.forget());
548 void
549 HTMLCanvasElement::CallPrintCallback()
551 ErrorResult rv;
552 GetMozPrintCallback()->Call(*mPrintState, rv);
555 void
556 HTMLCanvasElement::ResetPrintCallback()
558 if (mPrintState) {
559 mPrintState = nullptr;
563 bool
564 HTMLCanvasElement::IsPrintCallbackDone()
566 if (mPrintState == nullptr) {
567 return true;
570 return mPrintState->mIsDone;
573 HTMLCanvasElement*
574 HTMLCanvasElement::GetOriginalCanvas()
576 return mOriginalCanvas ? mOriginalCanvas.get() : this;
579 nsresult
580 HTMLCanvasElement::CopyInnerTo(HTMLCanvasElement* aDest)
582 nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
583 NS_ENSURE_SUCCESS(rv, rv);
584 if (aDest->OwnerDoc()->IsStaticDocument()) {
585 aDest->mOriginalCanvas = this;
587 // We make sure that the canvas is not zero sized since that would cause
588 // the DrawImage call below to return an error, which would cause printing
589 // to fail.
590 nsIntSize size = GetWidthHeight();
591 if (size.height > 0 && size.width > 0) {
592 nsCOMPtr<nsISupports> cxt;
593 aDest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt));
594 RefPtr<CanvasRenderingContext2D> context2d =
595 static_cast<CanvasRenderingContext2D*>(cxt.get());
596 if (context2d && !mPrintCallback) {
597 CanvasImageSource source;
598 source.SetAsHTMLCanvasElement() = this;
599 ErrorResult err;
600 context2d->DrawImage(source, 0.0, 0.0, err);
601 rv = err.StealNSResult();
605 return rv;
608 void
609 HTMLCanvasElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
611 if (aVisitor.mEvent->mClass == eMouseEventClass) {
612 WidgetMouseEventBase* evt = (WidgetMouseEventBase*)aVisitor.mEvent;
613 if (mCurrentContext) {
614 nsIFrame *frame = GetPrimaryFrame();
615 if (!frame) {
616 return;
618 nsPoint ptInRoot = nsLayoutUtils::GetEventCoordinatesRelativeTo(evt, frame);
619 nsRect paddingRect = frame->GetContentRectRelativeToSelf();
620 Point hitpoint;
621 hitpoint.x = (ptInRoot.x - paddingRect.x) / AppUnitsPerCSSPixel();
622 hitpoint.y = (ptInRoot.y - paddingRect.y) / AppUnitsPerCSSPixel();
624 evt->region = mCurrentContext->GetHitRegion(hitpoint);
625 aVisitor.mCanHandle = true;
628 nsGenericHTMLElement::GetEventTargetParent(aVisitor);
631 nsChangeHint
632 HTMLCanvasElement::GetAttributeChangeHint(const nsAtom* aAttribute,
633 int32_t aModType) const
635 nsChangeHint retval =
636 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
637 if (aAttribute == nsGkAtoms::width ||
638 aAttribute == nsGkAtoms::height)
640 retval |= NS_STYLE_HINT_REFLOW;
641 } else if (aAttribute == nsGkAtoms::moz_opaque)
643 retval |= NS_STYLE_HINT_VISUAL;
645 return retval;
648 bool
649 HTMLCanvasElement::ParseAttribute(int32_t aNamespaceID,
650 nsAtom* aAttribute,
651 const nsAString& aValue,
652 nsIPrincipal* aMaybeScriptedPrincipal,
653 nsAttrValue& aResult)
655 if (aNamespaceID == kNameSpaceID_None &&
656 (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height)) {
657 return aResult.ParseNonNegativeIntValue(aValue);
660 return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
661 aMaybeScriptedPrincipal, aResult);
666 void
667 HTMLCanvasElement::ToDataURL(JSContext* aCx, const nsAString& aType,
668 JS::Handle<JS::Value> aParams,
669 nsAString& aDataURL,
670 nsIPrincipal& aSubjectPrincipal,
671 ErrorResult& aRv)
673 // mWriteOnly check is redundant, but optimizes for the common case.
674 if (mWriteOnly && !CallerCanRead(aCx)) {
675 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
676 return;
679 aRv = ToDataURLImpl(aCx, aSubjectPrincipal, aType, aParams, aDataURL);
682 void
683 HTMLCanvasElement::SetMozPrintCallback(PrintCallback* aCallback)
685 mPrintCallback = aCallback;
688 PrintCallback*
689 HTMLCanvasElement::GetMozPrintCallback() const
691 if (mOriginalCanvas) {
692 return mOriginalCanvas->GetMozPrintCallback();
694 return mPrintCallback;
697 class CanvasCaptureTrackSource : public MediaStreamTrackSource
699 public:
700 NS_DECL_ISUPPORTS_INHERITED
701 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureTrackSource,
702 MediaStreamTrackSource)
704 CanvasCaptureTrackSource(nsIPrincipal* aPrincipal,
705 CanvasCaptureMediaStream* aCaptureStream)
706 : MediaStreamTrackSource(aPrincipal, nsString())
707 , mCaptureStream(aCaptureStream) {}
709 MediaSourceEnum GetMediaSource() const override
711 return MediaSourceEnum::Other;
714 void Stop() override
716 if (!mCaptureStream) {
717 NS_ERROR("No stream");
718 return;
721 mCaptureStream->StopCapture();
724 void Disable() override
728 void Enable() override
732 private:
733 virtual ~CanvasCaptureTrackSource() {}
735 RefPtr<CanvasCaptureMediaStream> mCaptureStream;
738 NS_IMPL_ADDREF_INHERITED(CanvasCaptureTrackSource,
739 MediaStreamTrackSource)
740 NS_IMPL_RELEASE_INHERITED(CanvasCaptureTrackSource,
741 MediaStreamTrackSource)
742 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureTrackSource)
743 NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
744 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureTrackSource,
745 MediaStreamTrackSource,
746 mCaptureStream)
748 already_AddRefed<CanvasCaptureMediaStream>
749 HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
750 nsIPrincipal& aSubjectPrincipal,
751 ErrorResult& aRv)
753 if (IsWriteOnly()) {
754 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
755 return nullptr;
758 nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
759 if (!window) {
760 aRv.Throw(NS_ERROR_FAILURE);
761 return nullptr;
764 if (!mCurrentContext) {
765 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
766 return nullptr;
769 RefPtr<CanvasCaptureMediaStream> stream =
770 CanvasCaptureMediaStream::CreateSourceStream(window, this);
771 if (!stream) {
772 aRv.Throw(NS_ERROR_FAILURE);
773 return nullptr;
776 TrackID videoTrackId = 1;
777 nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
778 nsresult rv =
779 stream->Init(aFrameRate, videoTrackId, principal);
780 if (NS_FAILED(rv)) {
781 aRv.Throw(rv);
782 return nullptr;
785 RefPtr<MediaStreamTrack> track =
786 stream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO,
787 new CanvasCaptureTrackSource(principal, stream));
788 stream->AddTrackInternal(track);
790 // Check site-specific permission and display prompt if appropriate.
791 // If no permission, arrange for the frame capture listener to return
792 // all-white, opaque image data.
793 bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
794 OwnerDoc(),
795 nsContentUtils::GetCurrentJSContext(),
796 aSubjectPrincipal);
798 rv = RegisterFrameCaptureListener(stream->FrameCaptureListener(), usePlaceholder);
799 if (NS_FAILED(rv)) {
800 aRv.Throw(rv);
801 return nullptr;
804 return stream.forget();
807 nsresult
808 HTMLCanvasElement::ExtractData(JSContext* aCx,
809 nsIPrincipal& aSubjectPrincipal,
810 nsAString& aType,
811 const nsAString& aOptions,
812 nsIInputStream** aStream)
814 // Check site-specific permission and display prompt if appropriate.
815 // If no permission, return all-white, opaque image data.
816 bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
817 OwnerDoc(), aCx, aSubjectPrincipal);
818 return ImageEncoder::ExtractData(aType,
819 aOptions,
820 GetSize(),
821 usePlaceholder,
822 mCurrentContext,
823 mAsyncCanvasRenderer,
824 aStream);
827 nsresult
828 HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
829 nsIPrincipal& aSubjectPrincipal,
830 const nsAString& aMimeType,
831 const JS::Value& aEncoderOptions,
832 nsAString& aDataURL)
834 nsIntSize size = GetWidthHeight();
835 if (size.height == 0 || size.width == 0) {
836 aDataURL = NS_LITERAL_STRING("data:,");
837 return NS_OK;
840 nsAutoString type;
841 nsContentUtils::ASCIIToLower(aMimeType, type);
843 nsAutoString params;
844 bool usingCustomParseOptions;
845 nsresult rv =
846 ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
847 if (NS_FAILED(rv)) {
848 return rv;
851 nsCOMPtr<nsIInputStream> stream;
852 rv = ExtractData(aCx, aSubjectPrincipal, type, params,
853 getter_AddRefs(stream));
855 // If there are unrecognized custom parse options, we should fall back to
856 // the default values for the encoder without any options at all.
857 if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
858 rv = ExtractData(aCx, aSubjectPrincipal, type, EmptyString(),
859 getter_AddRefs(stream));
862 NS_ENSURE_SUCCESS(rv, rv);
864 // build data URL string
865 aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
867 uint64_t count;
868 rv = stream->Available(&count);
869 NS_ENSURE_SUCCESS(rv, rv);
870 NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
872 return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
875 void
876 HTMLCanvasElement::ToBlob(JSContext* aCx,
877 BlobCallback& aCallback,
878 const nsAString& aType,
879 JS::Handle<JS::Value> aParams,
880 nsIPrincipal& aSubjectPrincipal,
881 ErrorResult& aRv)
883 // mWriteOnly check is redundant, but optimizes for the common case.
884 if (mWriteOnly && !CallerCanRead(aCx)) {
885 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
886 return;
889 nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
890 MOZ_ASSERT(global);
892 nsIntSize elemSize = GetWidthHeight();
893 if (elemSize.width == 0 || elemSize.height == 0) {
894 // According to spec, blob should return null if either its horizontal
895 // dimension or its vertical dimension is zero. See link below.
896 // https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
897 OwnerDoc()->Dispatch(
898 TaskCategory::Other,
899 NewRunnableMethod<Blob*, const char*>(
900 "dom::HTMLCanvasElement::ToBlob",
901 &aCallback,
902 static_cast<void (BlobCallback::*)(Blob*, const char*)>(
903 &BlobCallback::Call),
904 nullptr,
905 nullptr));
906 return;
909 // Check site-specific permission and display prompt if appropriate.
910 // If no permission, return all-white, opaque image data.
911 bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
912 OwnerDoc(), aCx, aSubjectPrincipal);
913 CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
914 aParams, usePlaceholder, aRv);
918 OffscreenCanvas*
919 HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
921 if (mCurrentContext) {
922 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
923 return nullptr;
926 if (!mOffscreenCanvas) {
927 nsIntSize sz = GetWidthHeight();
928 RefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
929 renderer->SetWidth(sz.width);
930 renderer->SetHeight(sz.height);
932 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
933 if (!win) {
934 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
935 return nullptr;
938 mOffscreenCanvas = new OffscreenCanvas(win->AsGlobal(),
939 sz.width,
940 sz.height,
941 GetCompositorBackendType(),
942 renderer);
943 if (mWriteOnly) {
944 mOffscreenCanvas->SetWriteOnly();
947 if (!mContextObserver) {
948 mContextObserver = new HTMLCanvasElementObserver(this);
950 } else {
951 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
954 return mOffscreenCanvas;
957 already_AddRefed<File>
958 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
959 const nsAString& aType,
960 nsIPrincipal& aSubjectPrincipal,
961 ErrorResult& aRv)
963 OwnerDoc()->WarnOnceAbout(nsIDocument::eMozGetAsFile);
965 // do a trust check if this is a write-only canvas
966 if (mWriteOnly && !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
967 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
968 return nullptr;
972 RefPtr<File> file;
973 aRv = MozGetAsFileImpl(aName, aType, aSubjectPrincipal, getter_AddRefs(file));
974 if (NS_WARN_IF(aRv.Failed())) {
975 return nullptr;
977 return file.forget();
980 nsresult
981 HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
982 const nsAString& aType,
983 nsIPrincipal& aSubjectPrincipal,
984 File** aResult)
986 nsCOMPtr<nsIInputStream> stream;
987 nsAutoString type(aType);
988 nsresult rv = ExtractData(nsContentUtils::GetCurrentJSContext(),
989 aSubjectPrincipal, type, EmptyString(),
990 getter_AddRefs(stream));
991 NS_ENSURE_SUCCESS(rv, rv);
993 uint64_t imgSize;
994 void* imgData = nullptr;
995 rv = NS_ReadInputStreamToBuffer(stream, &imgData, -1, &imgSize);
996 NS_ENSURE_SUCCESS(rv, rv);
998 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(OwnerDoc()->GetScopeObject());
1000 // The File takes ownership of the buffer
1001 RefPtr<File> file =
1002 File::CreateMemoryFile(win, imgData, imgSize, aName, type, PR_Now());
1004 file.forget(aResult);
1005 return NS_OK;
1008 nsresult
1009 HTMLCanvasElement::GetContext(const nsAString& aContextId,
1010 nsISupports** aContext)
1012 ErrorResult rv;
1013 *aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
1014 return rv.StealNSResult();
1017 already_AddRefed<nsISupports>
1018 HTMLCanvasElement::GetContext(JSContext* aCx,
1019 const nsAString& aContextId,
1020 JS::Handle<JS::Value> aContextOptions,
1021 ErrorResult& aRv)
1023 if (mOffscreenCanvas) {
1024 return nullptr;
1027 return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
1028 aContextOptions.isObject() ? aContextOptions : JS::NullHandleValue,
1029 aRv);
1032 already_AddRefed<nsISupports>
1033 HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
1034 ErrorResult& aRv)
1036 // Note that we're a [ChromeOnly] method, so from JS we can only be called by
1037 // system code.
1039 // We only support 2d shmem contexts for now.
1040 if (!aContextId.EqualsLiteral("2d")) {
1041 aRv.Throw(NS_ERROR_INVALID_ARG);
1042 return nullptr;
1045 CanvasContextType contextType = CanvasContextType::Canvas2D;
1047 if (!mCurrentContext) {
1048 // This canvas doesn't have a context yet.
1050 RefPtr<nsICanvasRenderingContextInternal> context;
1051 context = CreateContext(contextType);
1052 if (!context) {
1053 return nullptr;
1056 mCurrentContext = context;
1057 mCurrentContext->SetIsIPC(true);
1058 mCurrentContextType = contextType;
1060 ErrorResult dummy;
1061 nsresult rv = UpdateContext(nullptr, JS::NullHandleValue, dummy);
1062 if (NS_WARN_IF(NS_FAILED(rv))) {
1063 aRv.Throw(rv);
1064 return nullptr;
1066 } else {
1067 // We already have a context of some type.
1068 if (contextType != mCurrentContextType) {
1069 aRv.Throw(NS_ERROR_INVALID_ARG);
1070 return nullptr;
1074 nsCOMPtr<nsISupports> context(mCurrentContext);
1075 return context.forget();
1079 nsIntSize
1080 HTMLCanvasElement::GetSize()
1082 return GetWidthHeight();
1085 bool
1086 HTMLCanvasElement::IsWriteOnly()
1088 return mWriteOnly;
1091 void
1092 HTMLCanvasElement::SetWriteOnly()
1094 mExpandedReader = nullptr;
1095 mWriteOnly = true;
1098 void
1099 HTMLCanvasElement::SetWriteOnly(nsIPrincipal* aExpandedReader)
1101 mExpandedReader = aExpandedReader;
1102 mWriteOnly = true;
1105 bool
1106 HTMLCanvasElement::CallerCanRead(JSContext* aCx)
1108 if (!mWriteOnly) {
1109 return true;
1112 nsIPrincipal* prin = nsContentUtils::SubjectPrincipal(aCx);
1114 // If mExpandedReader is set, this canvas was tainted only by
1115 // mExpandedReader's resources. So allow reading if the subject
1116 // principal subsumes mExpandedReader.
1117 if (mExpandedReader && prin->Subsumes(mExpandedReader)) {
1118 return true;
1121 return nsContentUtils::PrincipalHasPermission(prin, nsGkAtoms::all_urlsPermission);
1124 void
1125 HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect)
1127 // We don't need to flush anything here; if there's no frame or if
1128 // we plan to reframe we don't need to invalidate it anyway.
1129 nsIFrame *frame = GetPrimaryFrame();
1130 if (!frame)
1131 return;
1133 ActiveLayerTracker::NotifyContentChange(frame);
1135 // When using layers-free WebRender, we cannot invalidate the layer (because there isn't one).
1136 // Instead, we mark the CanvasRenderer dirty and scheduling an empty transaction
1137 // which is effectively equivalent.
1138 CanvasRenderer* renderer = nullptr;
1139 RefPtr<WebRenderCanvasData> data = GetWebRenderUserData<WebRenderCanvasData>(frame, static_cast<uint32_t>(DisplayItemType::TYPE_CANVAS));
1140 if (data) {
1141 renderer = data->GetCanvasRenderer();
1144 if (renderer) {
1145 renderer->SetDirty();
1146 frame->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
1147 } else {
1148 Layer* layer = nullptr;
1149 if (damageRect) {
1150 nsIntSize size = GetWidthHeight();
1151 if (size.width != 0 && size.height != 0) {
1152 gfx::IntRect invalRect = gfx::IntRect::Truncate(*damageRect);
1153 layer = frame->InvalidateLayer(DisplayItemType::TYPE_CANVAS, &invalRect);
1155 } else {
1156 layer = frame->InvalidateLayer(DisplayItemType::TYPE_CANVAS);
1159 if (layer) {
1160 static_cast<CanvasLayer*>(layer)->Updated();
1165 * Treat canvas invalidations as animation activity for JS. Frequently
1166 * invalidating a canvas will feed into heuristics and cause JIT code to be
1167 * kept around longer, for smoother animations.
1169 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
1171 if (win) {
1172 if (JSObject *obj = win->AsGlobal()->GetGlobalJSObject()) {
1173 js::NotifyAnimationActivity(obj);
1178 void
1179 HTMLCanvasElement::InvalidateCanvas()
1181 // We don't need to flush anything here; if there's no frame or if
1182 // we plan to reframe we don't need to invalidate it anyway.
1183 nsIFrame *frame = GetPrimaryFrame();
1184 if (!frame)
1185 return;
1187 frame->InvalidateFrame();
1190 int32_t
1191 HTMLCanvasElement::CountContexts()
1193 if (mCurrentContext)
1194 return 1;
1196 return 0;
1199 nsICanvasRenderingContextInternal *
1200 HTMLCanvasElement::GetContextAtIndex(int32_t index)
1202 if (mCurrentContext && index == 0)
1203 return mCurrentContext;
1205 return nullptr;
1208 bool
1209 HTMLCanvasElement::GetIsOpaque()
1211 if (mCurrentContext) {
1212 return mCurrentContext->GetIsOpaque();
1215 return GetOpaqueAttr();
1218 bool
1219 HTMLCanvasElement::GetOpaqueAttr()
1221 return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
1224 already_AddRefed<Layer>
1225 HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
1226 Layer *aOldLayer,
1227 LayerManager *aManager)
1229 // The address of sOffscreenCanvasLayerUserDataDummy is used as the user
1230 // data key for retained LayerManagers managed by FrameLayerBuilder.
1231 // We don't much care about what value in it, so just assign a dummy
1232 // value for it.
1233 static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
1235 if (mCurrentContext) {
1236 return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
1239 if (mOffscreenCanvas) {
1240 if (!mResetLayer &&
1241 aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
1242 RefPtr<Layer> ret = aOldLayer;
1243 return ret.forget();
1246 RefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
1247 if (!layer) {
1248 NS_WARNING("CreateCanvasLayer failed!");
1249 return nullptr;
1252 LayerUserData* userData = nullptr;
1253 layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
1255 CanvasRenderer* canvasRenderer = layer->CreateOrGetCanvasRenderer();
1257 if (!InitializeCanvasRenderer(aBuilder, canvasRenderer)) {
1258 return nullptr;
1261 layer->Updated();
1262 return layer.forget();
1265 return nullptr;
1268 bool
1269 HTMLCanvasElement::UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
1270 WebRenderCanvasData* aCanvasData)
1272 if (mCurrentContext) {
1273 return mCurrentContext->UpdateWebRenderCanvasData(aBuilder, aCanvasData);
1275 if (mOffscreenCanvas) {
1276 CanvasRenderer* renderer = aCanvasData->GetCanvasRenderer();
1278 if(!mResetLayer && renderer) {
1279 return true;
1282 renderer = aCanvasData->CreateCanvasRenderer();
1283 if (!InitializeCanvasRenderer(aBuilder, renderer)) {
1284 // Clear CanvasRenderer of WebRenderCanvasData
1285 aCanvasData->ClearCanvasRenderer();
1286 return false;
1289 MOZ_ASSERT(renderer);
1290 mResetLayer = false;
1291 return true;
1294 // Clear CanvasRenderer of WebRenderCanvasData
1295 aCanvasData->ClearCanvasRenderer();
1296 return false;
1299 bool
1300 HTMLCanvasElement::InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
1301 CanvasRenderer* aRenderer)
1303 if (mCurrentContext) {
1304 return mCurrentContext->InitializeCanvasRenderer(aBuilder, aRenderer);
1307 if (mOffscreenCanvas) {
1308 CanvasInitializeData data;
1309 data.mRenderer = GetAsyncCanvasRenderer();
1310 data.mSize = GetWidthHeight();
1311 aRenderer->Initialize(data);
1312 return true;
1315 return false;
1318 bool
1319 HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager)
1321 if (mCurrentContext) {
1322 return mCurrentContext->ShouldForceInactiveLayer(aManager);
1325 if (mOffscreenCanvas) {
1326 // TODO: We should handle offscreen canvas case.
1327 return false;
1330 return true;
1333 void
1334 HTMLCanvasElement::MarkContextClean()
1336 if (!mCurrentContext)
1337 return;
1339 mCurrentContext->MarkContextClean();
1342 void
1343 HTMLCanvasElement::MarkContextCleanForFrameCapture()
1345 if (!mCurrentContext)
1346 return;
1348 mCurrentContext->MarkContextCleanForFrameCapture();
1351 bool
1352 HTMLCanvasElement::IsContextCleanForFrameCapture()
1354 return mCurrentContext && mCurrentContext->IsContextCleanForFrameCapture();
1357 nsresult
1358 HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener,
1359 bool aReturnPlaceholderData)
1361 WeakPtr<FrameCaptureListener> listener = aListener;
1363 if (mRequestedFrameListeners.Contains(listener)) {
1364 return NS_OK;
1367 if (!mRequestedFrameRefreshObserver) {
1368 nsIDocument* doc = OwnerDoc();
1369 if (!doc) {
1370 return NS_ERROR_FAILURE;
1373 while (doc->GetParentDocument()) {
1374 doc = doc->GetParentDocument();
1377 nsPresContext* context = doc->GetPresContext();
1378 if (!context) {
1379 return NS_ERROR_FAILURE;
1382 context = context->GetRootPresContext();
1383 if (!context) {
1384 return NS_ERROR_FAILURE;
1387 nsRefreshDriver* driver = context->RefreshDriver();
1388 if (!driver) {
1389 return NS_ERROR_FAILURE;
1392 mRequestedFrameRefreshObserver =
1393 new RequestedFrameRefreshObserver(this, driver, aReturnPlaceholderData);
1394 } else {
1395 mRequestedFrameRefreshObserver->SetReturnPlaceholderData(aReturnPlaceholderData);
1398 mRequestedFrameListeners.AppendElement(listener);
1399 mRequestedFrameRefreshObserver->Register();
1400 return NS_OK;
1403 bool
1404 HTMLCanvasElement::IsFrameCaptureRequested() const
1406 for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1407 if (!listener) {
1408 continue;
1411 if (listener->FrameCaptureRequested()) {
1412 return true;
1415 return false;
1418 void
1419 HTMLCanvasElement::ProcessDestroyedFrameListeners()
1421 // Loop backwards to allow removing elements in the loop.
1422 for (int i = mRequestedFrameListeners.Length() - 1; i >= 0; --i) {
1423 WeakPtr<FrameCaptureListener> listener = mRequestedFrameListeners[i];
1424 if (!listener) {
1425 // listener was destroyed. Remove it from the list.
1426 mRequestedFrameListeners.RemoveElementAt(i);
1427 continue;
1431 if (mRequestedFrameListeners.IsEmpty()) {
1432 mRequestedFrameRefreshObserver->Unregister();
1436 void
1437 HTMLCanvasElement::SetFrameCapture(already_AddRefed<SourceSurface> aSurface,
1438 const TimeStamp& aTime)
1440 RefPtr<SourceSurface> surface = aSurface;
1441 RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), surface);
1443 for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1444 if (!listener) {
1445 continue;
1448 RefPtr<Image> imageRefCopy = image.get();
1449 listener->NewFrame(imageRefCopy.forget(), aTime);
1453 already_AddRefed<SourceSurface>
1454 HTMLCanvasElement::GetSurfaceSnapshot(gfxAlphaType* const aOutAlphaType)
1456 if (!mCurrentContext)
1457 return nullptr;
1459 return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
1462 AsyncCanvasRenderer*
1463 HTMLCanvasElement::GetAsyncCanvasRenderer()
1465 if (!mAsyncCanvasRenderer) {
1466 mAsyncCanvasRenderer = new AsyncCanvasRenderer();
1467 mAsyncCanvasRenderer->mHTMLCanvasElement = this;
1470 return mAsyncCanvasRenderer;
1473 layers::LayersBackend
1474 HTMLCanvasElement::GetCompositorBackendType() const
1476 nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
1477 if (docWidget) {
1478 layers::LayerManager* layerManager = docWidget->GetLayerManager();
1479 if (layerManager) {
1480 return layerManager->GetCompositorBackendType();
1484 return LayersBackend::LAYERS_NONE;
1487 void
1488 HTMLCanvasElement::OnVisibilityChange()
1490 if (OwnerDoc()->Hidden()) {
1491 return;
1494 if (mOffscreenCanvas) {
1495 class Runnable final : public CancelableRunnable
1497 public:
1498 explicit Runnable(AsyncCanvasRenderer* aRenderer)
1499 : mozilla::CancelableRunnable("Runnable")
1500 , mRenderer(aRenderer)
1503 NS_IMETHOD Run() override
1505 if (mRenderer && mRenderer->mContext) {
1506 mRenderer->mContext->OnVisibilityChange();
1509 return NS_OK;
1512 private:
1513 RefPtr<AsyncCanvasRenderer> mRenderer;
1516 RefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
1517 nsCOMPtr<nsIEventTarget> activeTarget = mAsyncCanvasRenderer->GetActiveEventTarget();
1518 if (activeTarget) {
1519 activeTarget->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
1521 return;
1524 if (mCurrentContext) {
1525 mCurrentContext->OnVisibilityChange();
1529 void
1530 HTMLCanvasElement::OnMemoryPressure()
1532 if (mOffscreenCanvas) {
1533 class Runnable final : public CancelableRunnable
1535 public:
1536 explicit Runnable(AsyncCanvasRenderer* aRenderer)
1537 : mozilla::CancelableRunnable("Runnable")
1538 , mRenderer(aRenderer)
1541 NS_IMETHOD Run() override
1543 if (mRenderer && mRenderer->mContext) {
1544 mRenderer->mContext->OnMemoryPressure();
1547 return NS_OK;
1550 private:
1551 RefPtr<AsyncCanvasRenderer> mRenderer;
1554 RefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
1555 nsCOMPtr<nsIEventTarget> activeTarget = mAsyncCanvasRenderer->GetActiveEventTarget();
1556 if (activeTarget) {
1557 activeTarget->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
1559 return;
1562 if (mCurrentContext) {
1563 mCurrentContext->OnMemoryPressure();
1567 /* static */ void
1568 HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
1570 HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
1571 if (!element) {
1572 return;
1575 if (element->GetWidthHeight() == aRenderer->GetSize()) {
1576 return;
1579 gfx::IntSize asyncCanvasSize = aRenderer->GetSize();
1581 ErrorResult rv;
1582 element->SetUnsignedIntAttr(nsGkAtoms::width, asyncCanvasSize.width,
1583 DEFAULT_CANVAS_WIDTH, rv);
1584 if (rv.Failed()) {
1585 NS_WARNING("Failed to set width attribute to a canvas element asynchronously.");
1588 element->SetUnsignedIntAttr(nsGkAtoms::height, asyncCanvasSize.height,
1589 DEFAULT_CANVAS_HEIGHT, rv);
1590 if (rv.Failed()) {
1591 NS_WARNING("Failed to set height attribute to a canvas element asynchronously.");
1594 element->mResetLayer = true;
1597 /* static */ void
1598 HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
1600 HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
1601 if (!element) {
1602 return;
1605 element->InvalidateCanvasContent(nullptr);
1608 already_AddRefed<layers::SharedSurfaceTextureClient>
1609 HTMLCanvasElement::GetVRFrame()
1611 if (GetCurrentContextType() != CanvasContextType::WebGL1 &&
1612 GetCurrentContextType() != CanvasContextType::WebGL2) {
1613 return nullptr;
1616 WebGLContext* webgl = static_cast<WebGLContext*>(GetContextAtIndex(0));
1617 if (!webgl) {
1618 return nullptr;
1621 return webgl->GetVRFrame();
1624 } // namespace dom
1625 } // namespace mozilla