1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/dom/HTMLCanvasElement.h"
8 
9 #include "ImageEncoder.h"
10 #include "ImageLayers.h"
11 #include "jsapi.h"
12 #include "jsfriendapi.h"
13 #include "Layers.h"
14 #include "MediaTrackGraph.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/Base64.h"
17 #include "mozilla/BasePrincipal.h"
18 #include "mozilla/CheckedInt.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/dom/CanvasCaptureMediaStream.h"
21 #include "mozilla/dom/CanvasRenderingContext2D.h"
22 #include "mozilla/dom/Document.h"
23 #include "mozilla/dom/GeneratePlaceholderCanvasData.h"
24 #include "mozilla/dom/Event.h"
25 #include "mozilla/dom/File.h"
26 #include "mozilla/dom/HTMLCanvasElementBinding.h"
27 #include "mozilla/dom/VideoStreamTrack.h"
28 #include "mozilla/dom/MouseEvent.h"
29 #include "mozilla/dom/OffscreenCanvas.h"
30 #include "mozilla/EventDispatcher.h"
31 #include "mozilla/gfx/Rect.h"
32 #include "mozilla/layers/CanvasRenderer.h"
33 #include "mozilla/layers/WebRenderCanvasRenderer.h"
34 #include "mozilla/layers/WebRenderUserData.h"
35 #include "mozilla/MouseEvents.h"
36 #include "mozilla/Preferences.h"
37 #include "mozilla/ProfilerLabels.h"
38 #include "mozilla/Telemetry.h"
39 #include "mozilla/webgpu/CanvasContext.h"
40 #include "nsAttrValueInlines.h"
41 #include "nsContentUtils.h"
42 #include "nsDisplayList.h"
43 #include "nsDOMJSUtils.h"
44 #include "nsITimer.h"
45 #include "nsJSUtils.h"
46 #include "nsLayoutUtils.h"
47 #include "nsMathUtils.h"
48 #include "nsNetUtil.h"
49 #include "nsRefreshDriver.h"
50 #include "nsStreamUtils.h"
51 #include "ActiveLayerTracker.h"
52 #include "CanvasUtils.h"
53 #include "VRManagerChild.h"
54 #include "ClientWebGLContext.h"
55 
56 using namespace mozilla::layers;
57 using namespace mozilla::gfx;
58 
59 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
60 
61 namespace mozilla::dom {
62 
63 class RequestedFrameRefreshObserver : public nsARefreshObserver {
64   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RequestedFrameRefreshObserver, override)
65 
66  public:
RequestedFrameRefreshObserver(HTMLCanvasElement * const aOwningElement,nsRefreshDriver * aRefreshDriver,bool aReturnPlaceholderData)67   RequestedFrameRefreshObserver(HTMLCanvasElement* const aOwningElement,
68                                 nsRefreshDriver* aRefreshDriver,
69                                 bool aReturnPlaceholderData)
70       : mRegistered(false),
71         mReturnPlaceholderData(aReturnPlaceholderData),
72         mOwningElement(aOwningElement),
73         mRefreshDriver(aRefreshDriver) {
74     MOZ_ASSERT(mOwningElement);
75   }
76 
CopySurface(const RefPtr<SourceSurface> & aSurface,bool aReturnPlaceholderData)77   static already_AddRefed<DataSourceSurface> CopySurface(
78       const RefPtr<SourceSurface>& aSurface, bool aReturnPlaceholderData) {
79     RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
80     if (!data) {
81       return nullptr;
82     }
83 
84     DataSourceSurface::ScopedMap read(data, DataSourceSurface::READ);
85     if (!read.IsMapped()) {
86       return nullptr;
87     }
88 
89     RefPtr<DataSourceSurface> copy = Factory::CreateDataSourceSurfaceWithStride(
90         data->GetSize(), data->GetFormat(), read.GetStride());
91     if (!copy) {
92       return nullptr;
93     }
94 
95     DataSourceSurface::ScopedMap write(copy, DataSourceSurface::WRITE);
96     if (!write.IsMapped()) {
97       return nullptr;
98     }
99 
100     MOZ_ASSERT(read.GetStride() == write.GetStride());
101     MOZ_ASSERT(data->GetSize() == copy->GetSize());
102     MOZ_ASSERT(data->GetFormat() == copy->GetFormat());
103 
104     if (aReturnPlaceholderData) {
105       auto size = write.GetStride() * copy->GetSize().height;
106       auto* data = write.GetData();
107       GeneratePlaceholderCanvasData(size, data);
108     } else {
109       memcpy(write.GetData(), read.GetData(),
110              write.GetStride() * copy->GetSize().height);
111     }
112 
113     return copy.forget();
114   }
115 
SetReturnPlaceholderData(bool aReturnPlaceholderData)116   void SetReturnPlaceholderData(bool aReturnPlaceholderData) {
117     mReturnPlaceholderData = aReturnPlaceholderData;
118   }
119 
WillRefresh(TimeStamp aTime)120   void WillRefresh(TimeStamp aTime) override {
121     MOZ_ASSERT(NS_IsMainThread());
122 
123     AUTO_PROFILER_LABEL("RequestedFrameRefreshObserver::WillRefresh", OTHER);
124 
125     if (!mOwningElement) {
126       return;
127     }
128 
129     if (mOwningElement->IsWriteOnly()) {
130       return;
131     }
132 
133     if (mOwningElement->IsContextCleanForFrameCapture()) {
134       return;
135     }
136 
137     mOwningElement->ProcessDestroyedFrameListeners();
138 
139     if (!mOwningElement->IsFrameCaptureRequested()) {
140       return;
141     }
142 
143     RefPtr<SourceSurface> snapshot;
144     {
145       AUTO_PROFILER_LABEL(
146           "RequestedFrameRefreshObserver::WillRefresh:GetSnapshot", OTHER);
147       snapshot = mOwningElement->GetSurfaceSnapshot(nullptr);
148       if (!snapshot) {
149         return;
150       }
151     }
152 
153     RefPtr<DataSourceSurface> copy;
154     {
155       AUTO_PROFILER_LABEL(
156           "RequestedFrameRefreshObserver::WillRefresh:CopySurface", OTHER);
157       copy = CopySurface(snapshot, mReturnPlaceholderData);
158       if (!copy) {
159         return;
160       }
161     }
162 
163     {
164       AUTO_PROFILER_LABEL("RequestedFrameRefreshObserver::WillRefresh:SetFrame",
165                           OTHER);
166       mOwningElement->SetFrameCapture(copy.forget(), aTime);
167       mOwningElement->MarkContextCleanForFrameCapture();
168     }
169   }
170 
DetachFromRefreshDriver()171   void DetachFromRefreshDriver() {
172     MOZ_ASSERT(mOwningElement);
173     MOZ_ASSERT(mRefreshDriver);
174 
175     Unregister();
176     mRefreshDriver = nullptr;
177   }
178 
Register()179   void Register() {
180     if (mRegistered) {
181       return;
182     }
183 
184     MOZ_ASSERT(mRefreshDriver);
185     if (mRefreshDriver) {
186       mRefreshDriver->AddRefreshObserver(this, FlushType::Display,
187                                          "Canvas frame capture listeners");
188       mRegistered = true;
189     }
190   }
191 
Unregister()192   void Unregister() {
193     if (!mRegistered) {
194       return;
195     }
196 
197     MOZ_ASSERT(mRefreshDriver);
198     if (mRefreshDriver) {
199       mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display);
200       mRegistered = false;
201     }
202   }
203 
204  private:
~RequestedFrameRefreshObserver()205   virtual ~RequestedFrameRefreshObserver() {
206     MOZ_ASSERT(!mRefreshDriver);
207     MOZ_ASSERT(!mRegistered);
208   }
209 
210   bool mRegistered;
211   bool mReturnPlaceholderData;
212   HTMLCanvasElement* const mOwningElement;
213   RefPtr<nsRefreshDriver> mRefreshDriver;
214 };
215 
216 // ---------------------------------------------------------------------------
217 
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLCanvasPrintState,mCanvas,mContext,mCallback)218 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLCanvasPrintState, mCanvas, mContext,
219                                       mCallback)
220 
221 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLCanvasPrintState, AddRef)
222 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLCanvasPrintState, Release)
223 
224 HTMLCanvasPrintState::HTMLCanvasPrintState(
225     HTMLCanvasElement* aCanvas, nsICanvasRenderingContextInternal* aContext,
226     nsITimerCallback* aCallback)
227     : mIsDone(false),
228       mPendingNotify(false),
229       mCanvas(aCanvas),
230       mContext(aContext),
231       mCallback(aCallback) {}
232 
233 HTMLCanvasPrintState::~HTMLCanvasPrintState() = default;
234 
235 /* virtual */
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)236 JSObject* HTMLCanvasPrintState::WrapObject(JSContext* aCx,
237                                            JS::Handle<JSObject*> aGivenProto) {
238   return MozCanvasPrintState_Binding::Wrap(aCx, this, aGivenProto);
239 }
240 
Context() const241 nsISupports* HTMLCanvasPrintState::Context() const { return mContext; }
242 
Done()243 void HTMLCanvasPrintState::Done() {
244   if (!mPendingNotify && !mIsDone) {
245     // The canvas needs to be invalidated for printing reftests on linux to
246     // work.
247     if (mCanvas) {
248       mCanvas->InvalidateCanvas();
249     }
250     RefPtr<nsRunnableMethod<HTMLCanvasPrintState>> doneEvent =
251         NewRunnableMethod("dom::HTMLCanvasPrintState::NotifyDone", this,
252                           &HTMLCanvasPrintState::NotifyDone);
253     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) {
254       mPendingNotify = true;
255     }
256   }
257 }
258 
NotifyDone()259 void HTMLCanvasPrintState::NotifyDone() {
260   mIsDone = true;
261   mPendingNotify = false;
262   if (mCallback) {
263     mCallback->Notify(nullptr);
264   }
265 }
266 
267 // ---------------------------------------------------------------------------
268 
HTMLCanvasElementObserver(HTMLCanvasElement * aElement)269 HTMLCanvasElementObserver::HTMLCanvasElementObserver(
270     HTMLCanvasElement* aElement)
271     : mElement(aElement) {
272   RegisterVisibilityChangeEvent();
273   RegisterObserverEvents();
274 }
275 
~HTMLCanvasElementObserver()276 HTMLCanvasElementObserver::~HTMLCanvasElementObserver() { Destroy(); }
277 
Destroy()278 void HTMLCanvasElementObserver::Destroy() {
279   UnregisterObserverEvents();
280   UnregisterVisibilityChangeEvent();
281   mElement = nullptr;
282 }
283 
RegisterVisibilityChangeEvent()284 void HTMLCanvasElementObserver::RegisterVisibilityChangeEvent() {
285   if (!mElement) {
286     return;
287   }
288 
289   Document* document = mElement->OwnerDoc();
290   document->AddSystemEventListener(u"visibilitychange"_ns, this, true, false);
291 }
292 
UnregisterVisibilityChangeEvent()293 void HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent() {
294   if (!mElement) {
295     return;
296   }
297 
298   Document* document = mElement->OwnerDoc();
299   document->RemoveSystemEventListener(u"visibilitychange"_ns, this, true);
300 }
301 
RegisterObserverEvents()302 void HTMLCanvasElementObserver::RegisterObserverEvents() {
303   if (!mElement) {
304     return;
305   }
306 
307   nsCOMPtr<nsIObserverService> observerService =
308       mozilla::services::GetObserverService();
309 
310   MOZ_ASSERT(observerService);
311 
312   if (observerService) {
313     observerService->AddObserver(this, "memory-pressure", false);
314     observerService->AddObserver(this, "canvas-device-reset", false);
315   }
316 }
317 
UnregisterObserverEvents()318 void HTMLCanvasElementObserver::UnregisterObserverEvents() {
319   if (!mElement) {
320     return;
321   }
322 
323   nsCOMPtr<nsIObserverService> observerService =
324       mozilla::services::GetObserverService();
325 
326   // Do not assert on observerService here. This might be triggered by
327   // the cycle collector at a late enough time, that XPCOM services are
328   // no longer available. See bug 1029504.
329   if (observerService) {
330     observerService->RemoveObserver(this, "memory-pressure");
331     observerService->RemoveObserver(this, "canvas-device-reset");
332   }
333 }
334 
335 NS_IMETHODIMP
Observe(nsISupports *,const char * aTopic,const char16_t *)336 HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic,
337                                    const char16_t*) {
338   if (!mElement) {
339     return NS_OK;
340   }
341 
342   if (strcmp(aTopic, "memory-pressure") == 0) {
343     mElement->OnMemoryPressure();
344   } else if (strcmp(aTopic, "canvas-device-reset") == 0) {
345     mElement->OnDeviceReset();
346   }
347 
348   return NS_OK;
349 }
350 
351 NS_IMETHODIMP
HandleEvent(Event * aEvent)352 HTMLCanvasElementObserver::HandleEvent(Event* aEvent) {
353   nsAutoString type;
354   aEvent->GetType(type);
355   if (!mElement || !type.EqualsLiteral("visibilitychange")) {
356     return NS_OK;
357   }
358 
359   mElement->OnVisibilityChange();
360 
361   return NS_OK;
362 }
363 
NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver,nsIObserver)364 NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
365 
366 // ---------------------------------------------------------------------------
367 
368 HTMLCanvasElement::HTMLCanvasElement(
369     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
370     : nsGenericHTMLElement(std::move(aNodeInfo)),
371       mResetLayer(true),
372       mMaybeModified(false),
373       mWriteOnly(false) {}
374 
~HTMLCanvasElement()375 HTMLCanvasElement::~HTMLCanvasElement() {
376   if (mContextObserver) {
377     mContextObserver->Destroy();
378     mContextObserver = nullptr;
379   }
380 
381   ResetPrintCallback();
382   if (mRequestedFrameRefreshObserver) {
383     mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
384   }
385 }
386 
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement,nsGenericHTMLElement,mCurrentContext,mPrintCallback,mPrintState,mOriginalCanvas,mOffscreenCanvas)387 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
388                                    mCurrentContext, mPrintCallback, mPrintState,
389                                    mOriginalCanvas, mOffscreenCanvas)
390 
391 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLCanvasElement,
392                                                nsGenericHTMLElement)
393 
394 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
395 
396 /* virtual */
397 JSObject* HTMLCanvasElement::WrapNode(JSContext* aCx,
398                                       JS::Handle<JSObject*> aGivenProto) {
399   return HTMLCanvasElement_Binding::Wrap(aCx, this, aGivenProto);
400 }
401 
402 already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType)403 HTMLCanvasElement::CreateContext(CanvasContextType aContextType) {
404   // Note that the compositor backend will be LAYERS_NONE if there is no widget.
405   RefPtr<nsICanvasRenderingContextInternal> ret =
406       CreateContextHelper(aContextType, GetCompositorBackendType());
407 
408   // Add Observer for webgl canvas.
409   if (aContextType == CanvasContextType::WebGL1 ||
410       aContextType == CanvasContextType::WebGL2 ||
411       aContextType == CanvasContextType::Canvas2D) {
412     if (!mContextObserver) {
413       mContextObserver = new HTMLCanvasElementObserver(this);
414     }
415   }
416 
417   ret->SetCanvasElement(this);
418   return ret.forget();
419 }
420 
GetWidthHeight()421 nsIntSize HTMLCanvasElement::GetWidthHeight() {
422   nsIntSize size(DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);
423   const nsAttrValue* value;
424 
425   if ((value = GetParsedAttr(nsGkAtoms::width)) &&
426       value->Type() == nsAttrValue::eInteger) {
427     size.width = value->GetIntegerValue();
428   }
429 
430   if ((value = GetParsedAttr(nsGkAtoms::height)) &&
431       value->Type() == nsAttrValue::eInteger) {
432     size.height = value->GetIntegerValue();
433   }
434 
435   MOZ_ASSERT(size.width >= 0 && size.height >= 0,
436              "we should've required <canvas> width/height attrs to be "
437              "unsigned (non-negative) values");
438 
439   return size;
440 }
441 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)442 nsresult HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
443                                          const nsAttrValue* aValue,
444                                          const nsAttrValue* aOldValue,
445                                          nsIPrincipal* aSubjectPrincipal,
446                                          bool aNotify) {
447   AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
448 
449   return nsGenericHTMLElement::AfterSetAttr(
450       aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
451 }
452 
OnAttrSetButNotChanged(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString & aValue,bool aNotify)453 nsresult HTMLCanvasElement::OnAttrSetButNotChanged(
454     int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
455     bool aNotify) {
456   AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
457 
458   return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
459                                                       aValue, aNotify);
460 }
461 
AfterMaybeChangeAttr(int32_t aNamespaceID,nsAtom * aName,bool aNotify)462 void HTMLCanvasElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
463                                              nsAtom* aName, bool aNotify) {
464   if (mCurrentContext && aNamespaceID == kNameSpaceID_None &&
465       (aName == nsGkAtoms::width || aName == nsGkAtoms::height ||
466        aName == nsGkAtoms::moz_opaque)) {
467     ErrorResult dummy;
468     UpdateContext(nullptr, JS::NullHandleValue, dummy);
469   }
470 }
471 
HandlePrintCallback(nsPresContext * aPresContext)472 void HTMLCanvasElement::HandlePrintCallback(nsPresContext* aPresContext) {
473   // Only call the print callback here if 1) we're in a print testing mode or
474   // print preview mode, 2) the canvas has a print callback and 3) the callback
475   // hasn't already been called. For real printing the callback is handled in
476   // nsSimplePageSequenceFrame::PrePrintNextPage.
477   if ((aPresContext->Type() == nsPresContext::eContext_PageLayout ||
478        aPresContext->Type() == nsPresContext::eContext_PrintPreview) &&
479       !mPrintState && GetMozPrintCallback()) {
480     DispatchPrintCallback(nullptr);
481   }
482 }
483 
DispatchPrintCallback(nsITimerCallback * aCallback)484 nsresult HTMLCanvasElement::DispatchPrintCallback(nsITimerCallback* aCallback) {
485   // For print reftests the context may not be initialized yet, so get a context
486   // so mCurrentContext is set.
487   if (!mCurrentContext) {
488     nsresult rv;
489     nsCOMPtr<nsISupports> context;
490     rv = GetContext(u"2d"_ns, getter_AddRefs(context));
491     NS_ENSURE_SUCCESS(rv, rv);
492   }
493   mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback);
494 
495   RefPtr<nsRunnableMethod<HTMLCanvasElement>> renderEvent =
496       NewRunnableMethod("dom::HTMLCanvasElement::CallPrintCallback", this,
497                         &HTMLCanvasElement::CallPrintCallback);
498   return OwnerDoc()->Dispatch(TaskCategory::Other, renderEvent.forget());
499 }
500 
501 MOZ_CAN_RUN_SCRIPT
CallPrintCallback()502 void HTMLCanvasElement::CallPrintCallback() {
503   if (!mPrintState) {
504     // `mPrintState` might have been destroyed by cancelling the previous
505     // printing (especially the canvas frame destruction) during processing
506     // event loops in the printing.
507     return;
508   }
509   RefPtr<PrintCallback> callback = GetMozPrintCallback();
510   RefPtr<HTMLCanvasPrintState> state = mPrintState;
511   callback->Call(*state);
512 }
513 
ResetPrintCallback()514 void HTMLCanvasElement::ResetPrintCallback() {
515   if (mPrintState) {
516     mPrintState = nullptr;
517   }
518 }
519 
IsPrintCallbackDone()520 bool HTMLCanvasElement::IsPrintCallbackDone() {
521   if (mPrintState == nullptr) {
522     return true;
523   }
524 
525   return mPrintState->mIsDone;
526 }
527 
GetOriginalCanvas()528 HTMLCanvasElement* HTMLCanvasElement::GetOriginalCanvas() {
529   return mOriginalCanvas ? mOriginalCanvas.get() : this;
530 }
531 
CopyInnerTo(HTMLCanvasElement * aDest)532 nsresult HTMLCanvasElement::CopyInnerTo(HTMLCanvasElement* aDest) {
533   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
534   NS_ENSURE_SUCCESS(rv, rv);
535   Document* destDoc = aDest->OwnerDoc();
536   if (destDoc->IsStaticDocument()) {
537     // The Firefox print preview code can create a static clone from an
538     // existing static clone, so we may not be the original 'canvas' element.
539     aDest->mOriginalCanvas = GetOriginalCanvas();
540 
541     if (GetMozPrintCallback()) {
542       destDoc->SetHasPrintCallbacks();
543     }
544 
545     // We make sure that the canvas is not zero sized since that would cause
546     // the DrawImage call below to return an error, which would cause printing
547     // to fail.
548     nsIntSize size = GetWidthHeight();
549     if (size.height > 0 && size.width > 0) {
550       nsCOMPtr<nsISupports> cxt;
551       aDest->GetContext(u"2d"_ns, getter_AddRefs(cxt));
552       RefPtr<CanvasRenderingContext2D> context2d =
553           static_cast<CanvasRenderingContext2D*>(cxt.get());
554       if (context2d && !mPrintCallback) {
555         CanvasImageSource source;
556         source.SetAsHTMLCanvasElement() = this;
557         ErrorResult err;
558         context2d->DrawImage(source, 0.0, 0.0, err);
559         rv = err.StealNSResult();
560       }
561     }
562   }
563   return rv;
564 }
565 
GetEventTargetParent(EventChainPreVisitor & aVisitor)566 void HTMLCanvasElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
567   if (aVisitor.mEvent->mClass == eMouseEventClass) {
568     WidgetMouseEventBase* evt = (WidgetMouseEventBase*)aVisitor.mEvent;
569     if (mCurrentContext) {
570       nsIFrame* frame = GetPrimaryFrame();
571       if (!frame) {
572         return;
573       }
574       nsPoint ptInRoot =
575           nsLayoutUtils::GetEventCoordinatesRelativeTo(evt, RelativeTo{frame});
576       nsRect paddingRect = frame->GetContentRectRelativeToSelf();
577       Point hitpoint;
578       hitpoint.x = (ptInRoot.x - paddingRect.x) / AppUnitsPerCSSPixel();
579       hitpoint.y = (ptInRoot.y - paddingRect.y) / AppUnitsPerCSSPixel();
580 
581       evt->mRegion = mCurrentContext->GetHitRegion(hitpoint);
582       aVisitor.mCanHandle = true;
583     }
584   }
585   nsGenericHTMLElement::GetEventTargetParent(aVisitor);
586 }
587 
GetAttributeChangeHint(const nsAtom * aAttribute,int32_t aModType) const588 nsChangeHint HTMLCanvasElement::GetAttributeChangeHint(const nsAtom* aAttribute,
589                                                        int32_t aModType) const {
590   nsChangeHint retval =
591       nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
592   if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
593     retval |= NS_STYLE_HINT_REFLOW;
594   } else if (aAttribute == nsGkAtoms::moz_opaque) {
595     retval |= NS_STYLE_HINT_VISUAL;
596   }
597   return retval;
598 }
599 
MapAttributesIntoRule(const nsMappedAttributes * aAttributes,MappedDeclarations & aDecls)600 void HTMLCanvasElement::MapAttributesIntoRule(
601     const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
602   MapAspectRatioInto(aAttributes, aDecls);
603   MapCommonAttributesInto(aAttributes, aDecls);
604 }
605 
GetAttributeMappingFunction() const606 nsMapRuleToAttributesFunc HTMLCanvasElement::GetAttributeMappingFunction()
607     const {
608   return &MapAttributesIntoRule;
609 }
610 
NS_IMETHODIMP_(bool)611 NS_IMETHODIMP_(bool)
612 HTMLCanvasElement::IsAttributeMapped(const nsAtom* aAttribute) const {
613   static const MappedAttributeEntry attributes[] = {
614       {nsGkAtoms::width}, {nsGkAtoms::height}, {nullptr}};
615   static const MappedAttributeEntry* const map[] = {attributes,
616                                                     sCommonAttributeMap};
617   return FindAttributeDependence(aAttribute, map);
618 }
619 
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)620 bool HTMLCanvasElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
621                                        const nsAString& aValue,
622                                        nsIPrincipal* aMaybeScriptedPrincipal,
623                                        nsAttrValue& aResult) {
624   if (aNamespaceID == kNameSpaceID_None &&
625       (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height)) {
626     return aResult.ParseNonNegativeIntValue(aValue);
627   }
628 
629   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
630                                               aMaybeScriptedPrincipal, aResult);
631 }
632 
ToDataURL(JSContext * aCx,const nsAString & aType,JS::Handle<JS::Value> aParams,nsAString & aDataURL,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)633 void HTMLCanvasElement::ToDataURL(JSContext* aCx, const nsAString& aType,
634                                   JS::Handle<JS::Value> aParams,
635                                   nsAString& aDataURL,
636                                   nsIPrincipal& aSubjectPrincipal,
637                                   ErrorResult& aRv) {
638   // mWriteOnly check is redundant, but optimizes for the common case.
639   if (mWriteOnly && !CallerCanRead(aCx)) {
640     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
641     return;
642   }
643 
644   nsresult rv = ToDataURLImpl(aCx, aSubjectPrincipal, aType, aParams, aDataURL);
645   if (NS_FAILED(rv)) {
646     aDataURL.AssignLiteral("data:,");
647   }
648 }
649 
SetMozPrintCallback(PrintCallback * aCallback)650 void HTMLCanvasElement::SetMozPrintCallback(PrintCallback* aCallback) {
651   mPrintCallback = aCallback;
652 }
653 
GetMozPrintCallback() const654 PrintCallback* HTMLCanvasElement::GetMozPrintCallback() const {
655   if (mOriginalCanvas) {
656     return mOriginalCanvas->GetMozPrintCallback();
657   }
658   return mPrintCallback;
659 }
660 
661 class CanvasCaptureTrackSource : public MediaStreamTrackSource {
662  public:
663   NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureTrackSource,MediaStreamTrackSource)664   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureTrackSource,
665                                            MediaStreamTrackSource)
666 
667   CanvasCaptureTrackSource(nsIPrincipal* aPrincipal,
668                            CanvasCaptureMediaStream* aCaptureStream)
669       : MediaStreamTrackSource(aPrincipal, nsString()),
670         mCaptureStream(aCaptureStream) {}
671 
GetMediaSource() const672   MediaSourceEnum GetMediaSource() const override {
673     return MediaSourceEnum::Other;
674   }
675 
HasAlpha() const676   bool HasAlpha() const override {
677     if (!mCaptureStream || !mCaptureStream->Canvas()) {
678       // In cycle-collection
679       return false;
680     }
681     return !mCaptureStream->Canvas()->GetIsOpaque();
682   }
683 
Stop()684   void Stop() override {
685     if (!mCaptureStream) {
686       NS_ERROR("No stream");
687       return;
688     }
689 
690     mCaptureStream->StopCapture();
691   }
692 
Disable()693   void Disable() override {}
694 
Enable()695   void Enable() override {}
696 
697  private:
698   virtual ~CanvasCaptureTrackSource() = default;
699 
700   RefPtr<CanvasCaptureMediaStream> mCaptureStream;
701 };
702 
NS_IMPL_ADDREF_INHERITED(CanvasCaptureTrackSource,MediaStreamTrackSource)703 NS_IMPL_ADDREF_INHERITED(CanvasCaptureTrackSource, MediaStreamTrackSource)
704 NS_IMPL_RELEASE_INHERITED(CanvasCaptureTrackSource, MediaStreamTrackSource)
705 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureTrackSource)
706 NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
707 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureTrackSource,
708                                    MediaStreamTrackSource, mCaptureStream)
709 
710 already_AddRefed<CanvasCaptureMediaStream> HTMLCanvasElement::CaptureStream(
711     const Optional<double>& aFrameRate, nsIPrincipal& aSubjectPrincipal,
712     ErrorResult& aRv) {
713   if (IsWriteOnly()) {
714     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
715     return nullptr;
716   }
717 
718   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
719   if (!window) {
720     aRv.Throw(NS_ERROR_FAILURE);
721     return nullptr;
722   }
723 
724   if (!mCurrentContext) {
725     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
726     return nullptr;
727   }
728 
729   auto stream = MakeRefPtr<CanvasCaptureMediaStream>(window, this);
730 
731   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
732   nsresult rv = stream->Init(aFrameRate, principal);
733   if (NS_FAILED(rv)) {
734     aRv.Throw(rv);
735     return nullptr;
736   }
737 
738   RefPtr<MediaStreamTrack> track =
739       new VideoStreamTrack(window, stream->GetSourceStream(),
740                            new CanvasCaptureTrackSource(principal, stream));
741   stream->AddTrackInternal(track);
742 
743   // Check site-specific permission and display prompt if appropriate.
744   // If no permission, arrange for the frame capture listener to return
745   // all-white, opaque image data.
746   bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
747       OwnerDoc(), nsContentUtils::GetCurrentJSContext(), aSubjectPrincipal);
748 
749   rv = RegisterFrameCaptureListener(stream->FrameCaptureListener(),
750                                     usePlaceholder);
751   if (NS_FAILED(rv)) {
752     aRv.Throw(rv);
753     return nullptr;
754   }
755 
756   return stream.forget();
757 }
758 
ExtractData(JSContext * aCx,nsIPrincipal & aSubjectPrincipal,nsAString & aType,const nsAString & aOptions,nsIInputStream ** aStream)759 nsresult HTMLCanvasElement::ExtractData(JSContext* aCx,
760                                         nsIPrincipal& aSubjectPrincipal,
761                                         nsAString& aType,
762                                         const nsAString& aOptions,
763                                         nsIInputStream** aStream) {
764   // Check site-specific permission and display prompt if appropriate.
765   // If no permission, return all-white, opaque image data.
766   bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
767       OwnerDoc(), aCx, aSubjectPrincipal);
768   return ImageEncoder::ExtractData(aType, aOptions, GetSize(), usePlaceholder,
769                                    mCurrentContext, mCanvasRenderer, aStream);
770 }
771 
ToDataURLImpl(JSContext * aCx,nsIPrincipal & aSubjectPrincipal,const nsAString & aMimeType,const JS::Value & aEncoderOptions,nsAString & aDataURL)772 nsresult HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
773                                           nsIPrincipal& aSubjectPrincipal,
774                                           const nsAString& aMimeType,
775                                           const JS::Value& aEncoderOptions,
776                                           nsAString& aDataURL) {
777   nsIntSize size = GetWidthHeight();
778   if (size.height == 0 || size.width == 0) {
779     aDataURL = u"data:,"_ns;
780     return NS_OK;
781   }
782 
783   nsAutoString type;
784   nsContentUtils::ASCIIToLower(aMimeType, type);
785 
786   nsAutoString params;
787   bool usingCustomParseOptions;
788   nsresult rv =
789       ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
790   if (NS_FAILED(rv)) {
791     return rv;
792   }
793 
794   nsCOMPtr<nsIInputStream> stream;
795   rv =
796       ExtractData(aCx, aSubjectPrincipal, type, params, getter_AddRefs(stream));
797 
798   // If there are unrecognized custom parse options, we should fall back to
799   // the default values for the encoder without any options at all.
800   if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
801     rv = ExtractData(aCx, aSubjectPrincipal, type, u""_ns,
802                      getter_AddRefs(stream));
803   }
804 
805   NS_ENSURE_SUCCESS(rv, rv);
806 
807   // build data URL string
808   aDataURL = u"data:"_ns + type + u";base64,"_ns;
809 
810   uint64_t count;
811   rv = stream->Available(&count);
812   NS_ENSURE_SUCCESS(rv, rv);
813   NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
814 
815   return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count,
816                                  aDataURL.Length());
817 }
818 
ToBlob(JSContext * aCx,BlobCallback & aCallback,const nsAString & aType,JS::Handle<JS::Value> aParams,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)819 void HTMLCanvasElement::ToBlob(JSContext* aCx, BlobCallback& aCallback,
820                                const nsAString& aType,
821                                JS::Handle<JS::Value> aParams,
822                                nsIPrincipal& aSubjectPrincipal,
823                                ErrorResult& aRv) {
824   // mWriteOnly check is redundant, but optimizes for the common case.
825   if (mWriteOnly && !CallerCanRead(aCx)) {
826     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
827     return;
828   }
829 
830   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
831   MOZ_ASSERT(global);
832 
833   nsIntSize elemSize = GetWidthHeight();
834   if (elemSize.width == 0 || elemSize.height == 0) {
835     // According to spec, blob should return null if either its horizontal
836     // dimension or its vertical dimension is zero. See link below.
837     // https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
838     OwnerDoc()->Dispatch(
839         TaskCategory::Other,
840         NewRunnableMethod<Blob*, const char*>(
841             "dom::HTMLCanvasElement::ToBlob", &aCallback,
842             static_cast<void (BlobCallback::*)(Blob*, const char*)>(
843                 &BlobCallback::Call),
844             nullptr, nullptr));
845     return;
846   }
847 
848   // Check site-specific permission and display prompt if appropriate.
849   // If no permission, return all-white, opaque image data.
850   bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
851       OwnerDoc(), aCx, aSubjectPrincipal);
852   CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType, aParams,
853                                        usePlaceholder, aRv);
854 }
855 #define DISABLE_OFFSCREEN_CANVAS 1
TransferControlToOffscreen(ErrorResult & aRv)856 OffscreenCanvas* HTMLCanvasElement::TransferControlToOffscreen(
857     ErrorResult& aRv) {
858   if (DISABLE_OFFSCREEN_CANVAS) {
859     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
860     return nullptr;
861   }
862   if (mCurrentContext) {
863     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
864     return nullptr;
865   }
866 
867   if (!mOffscreenCanvas) {
868     MOZ_CRASH("todo");
869 
870     nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
871     if (!win) {
872       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
873       return nullptr;
874     }
875 
876     // nsIntSize sz = GetWidthHeight();
877     // mOffscreenCanvas =
878     //    new OffscreenCanvas(win->AsGlobal(), sz.width, sz.height,
879     //                        GetCompositorBackendType(), renderer);
880     if (mWriteOnly) {
881       mOffscreenCanvas->SetWriteOnly();
882     }
883 
884     if (!mContextObserver) {
885       mContextObserver = new HTMLCanvasElementObserver(this);
886     }
887   } else {
888     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
889   }
890 
891   return mOffscreenCanvas;
892 }
893 
MozGetAsFile(const nsAString & aName,const nsAString & aType,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)894 already_AddRefed<File> HTMLCanvasElement::MozGetAsFile(
895     const nsAString& aName, const nsAString& aType,
896     nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
897   // do a trust check if this is a write-only canvas
898   if (mWriteOnly && !aSubjectPrincipal.IsSystemPrincipal()) {
899     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
900     return nullptr;
901   }
902 
903   RefPtr<File> file;
904   aRv = MozGetAsFileImpl(aName, aType, aSubjectPrincipal, getter_AddRefs(file));
905   if (NS_WARN_IF(aRv.Failed())) {
906     return nullptr;
907   }
908   return file.forget();
909 }
910 
MozGetAsFileImpl(const nsAString & aName,const nsAString & aType,nsIPrincipal & aSubjectPrincipal,File ** aResult)911 nsresult HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
912                                              const nsAString& aType,
913                                              nsIPrincipal& aSubjectPrincipal,
914                                              File** aResult) {
915   nsCOMPtr<nsIInputStream> stream;
916   nsAutoString type(aType);
917   nsresult rv =
918       ExtractData(nsContentUtils::GetCurrentJSContext(), aSubjectPrincipal,
919                   type, u""_ns, getter_AddRefs(stream));
920   NS_ENSURE_SUCCESS(rv, rv);
921 
922   uint64_t imgSize;
923   void* imgData = nullptr;
924   rv = NS_ReadInputStreamToBuffer(stream, &imgData, -1, &imgSize);
925   NS_ENSURE_SUCCESS(rv, rv);
926 
927   nsCOMPtr<nsPIDOMWindowInner> win =
928       do_QueryInterface(OwnerDoc()->GetScopeObject());
929 
930   // The File takes ownership of the buffer
931   RefPtr<File> file = File::CreateMemoryFileWithLastModifiedNow(
932       win->AsGlobal(), imgData, imgSize, aName, type);
933   if (NS_WARN_IF(!file)) {
934     return NS_ERROR_FAILURE;
935   }
936 
937   file.forget(aResult);
938   return NS_OK;
939 }
940 
GetContext(const nsAString & aContextId,nsISupports ** aContext)941 nsresult HTMLCanvasElement::GetContext(const nsAString& aContextId,
942                                        nsISupports** aContext) {
943   ErrorResult rv;
944   mMaybeModified = true;  // For FirstContentfulPaint
945   *aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
946   return rv.StealNSResult();
947 }
948 
GetContext(JSContext * aCx,const nsAString & aContextId,JS::Handle<JS::Value> aContextOptions,ErrorResult & aRv)949 already_AddRefed<nsISupports> HTMLCanvasElement::GetContext(
950     JSContext* aCx, const nsAString& aContextId,
951     JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv) {
952   if (mOffscreenCanvas) {
953     return nullptr;
954   }
955 
956   mMaybeModified = true;  // For FirstContentfulPaint
957   return CanvasRenderingContextHelper::GetContext(
958       aCx, aContextId,
959       aContextOptions.isObject() ? aContextOptions : JS::NullHandleValue, aRv);
960 }
961 
MozGetIPCContext(const nsAString & aContextId,ErrorResult & aRv)962 already_AddRefed<nsISupports> HTMLCanvasElement::MozGetIPCContext(
963     const nsAString& aContextId, ErrorResult& aRv) {
964   // Note that we're a [ChromeOnly] method, so from JS we can only be called by
965   // system code.
966 
967   // We only support 2d shmem contexts for now.
968   if (!aContextId.EqualsLiteral("2d")) {
969     aRv.Throw(NS_ERROR_INVALID_ARG);
970     return nullptr;
971   }
972 
973   CanvasContextType contextType = CanvasContextType::Canvas2D;
974 
975   if (!mCurrentContext) {
976     // This canvas doesn't have a context yet.
977 
978     RefPtr<nsICanvasRenderingContextInternal> context;
979     context = CreateContext(contextType);
980     if (!context) {
981       return nullptr;
982     }
983 
984     mCurrentContext = context;
985     mCurrentContext->SetIsIPC(true);
986     mCurrentContextType = contextType;
987 
988     ErrorResult dummy;
989     nsresult rv = UpdateContext(nullptr, JS::NullHandleValue, dummy);
990     if (NS_WARN_IF(NS_FAILED(rv))) {
991       aRv.Throw(rv);
992       return nullptr;
993     }
994   } else {
995     // We already have a context of some type.
996     if (contextType != mCurrentContextType) {
997       aRv.Throw(NS_ERROR_INVALID_ARG);
998       return nullptr;
999     }
1000   }
1001 
1002   nsCOMPtr<nsISupports> context(mCurrentContext);
1003   return context.forget();
1004 }
1005 
GetSize()1006 nsIntSize HTMLCanvasElement::GetSize() { return GetWidthHeight(); }
1007 
IsWriteOnly() const1008 bool HTMLCanvasElement::IsWriteOnly() const { return mWriteOnly; }
1009 
SetWriteOnly()1010 void HTMLCanvasElement::SetWriteOnly() {
1011   mExpandedReader = nullptr;
1012   mWriteOnly = true;
1013 }
1014 
SetWriteOnly(nsIPrincipal * aExpandedReader)1015 void HTMLCanvasElement::SetWriteOnly(nsIPrincipal* aExpandedReader) {
1016   mExpandedReader = aExpandedReader;
1017   mWriteOnly = true;
1018 }
1019 
CallerCanRead(JSContext * aCx)1020 bool HTMLCanvasElement::CallerCanRead(JSContext* aCx) {
1021   if (!mWriteOnly) {
1022     return true;
1023   }
1024 
1025   nsIPrincipal* prin = nsContentUtils::SubjectPrincipal(aCx);
1026 
1027   // If mExpandedReader is set, this canvas was tainted only by
1028   // mExpandedReader's resources. So allow reading if the subject
1029   // principal subsumes mExpandedReader.
1030   if (mExpandedReader && prin->Subsumes(mExpandedReader)) {
1031     return true;
1032   }
1033 
1034   return nsContentUtils::PrincipalHasPermission(*prin,
1035                                                 nsGkAtoms::all_urlsPermission);
1036 }
1037 
InvalidateCanvasContent(const gfx::Rect * damageRect)1038 void HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect) {
1039   // We don't need to flush anything here; if there's no frame or if
1040   // we plan to reframe we don't need to invalidate it anyway.
1041   nsIFrame* frame = GetPrimaryFrame();
1042   if (!frame) return;
1043 
1044   ActiveLayerTracker::NotifyContentChange(frame);
1045 
1046   // When using layers-free WebRender, we cannot invalidate the layer (because
1047   // there isn't one). Instead, we mark the CanvasRenderer dirty and scheduling
1048   // an empty transaction which is effectively equivalent.
1049   CanvasRenderer* renderer = nullptr;
1050   const auto key = static_cast<uint32_t>(DisplayItemType::TYPE_CANVAS);
1051   RefPtr<WebRenderLocalCanvasData> localData =
1052       GetWebRenderUserData<WebRenderLocalCanvasData>(frame, key);
1053   RefPtr<WebRenderCanvasData> data =
1054       GetWebRenderUserData<WebRenderCanvasData>(frame, key);
1055   if (data) {
1056     renderer = data->GetCanvasRenderer();
1057   }
1058 
1059   if (localData && wr::AsUint64(localData->mImageKey)) {
1060     localData->mDirty = true;
1061     frame->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
1062   } else if (renderer) {
1063     renderer->SetDirty();
1064     frame->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
1065   } else {
1066     Layer* layer = nullptr;
1067     if (damageRect) {
1068       nsIntSize size = GetWidthHeight();
1069       if (size.width != 0 && size.height != 0) {
1070         gfx::IntRect invalRect = gfx::IntRect::Truncate(*damageRect);
1071         layer =
1072             frame->InvalidateLayer(DisplayItemType::TYPE_CANVAS, &invalRect);
1073       }
1074     } else {
1075       layer = frame->InvalidateLayer(DisplayItemType::TYPE_CANVAS);
1076     }
1077 
1078     if (layer) {
1079       if (CanvasLayer* canvas = layer->AsCanvasLayer()) {
1080         canvas->Updated();
1081       } else {
1082         layer->SetInvalidRectToVisibleRegion();
1083       }
1084     } else {
1085       // This path is taken in two situations:
1086       // 1) WebRender is enabled and has not yet processed a display list.
1087       // 2) WebRender is disabled and layer invalidation failed.
1088       // In both cases, schedule a full paint to properly update canvas.
1089       frame->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
1090     }
1091   }
1092 
1093   /*
1094    * Treat canvas invalidations as animation activity for JS. Frequently
1095    * invalidating a canvas will feed into heuristics and cause JIT code to be
1096    * kept around longer, for smoother animations.
1097    */
1098   nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
1099 
1100   if (win) {
1101     if (JSObject* obj = win->AsGlobal()->GetGlobalJSObject()) {
1102       js::NotifyAnimationActivity(obj);
1103     }
1104   }
1105 }
1106 
InvalidateCanvas()1107 void HTMLCanvasElement::InvalidateCanvas() {
1108   // We don't need to flush anything here; if there's no frame or if
1109   // we plan to reframe we don't need to invalidate it anyway.
1110   nsIFrame* frame = GetPrimaryFrame();
1111   if (!frame) return;
1112 
1113   frame->InvalidateFrame();
1114 }
1115 
GetIsOpaque()1116 bool HTMLCanvasElement::GetIsOpaque() {
1117   if (mCurrentContext) {
1118     return mCurrentContext->GetIsOpaque();
1119   }
1120 
1121   return GetOpaqueAttr();
1122 }
1123 
GetOpaqueAttr()1124 bool HTMLCanvasElement::GetOpaqueAttr() {
1125   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
1126 }
1127 
GetCurrentContextType()1128 CanvasContextType HTMLCanvasElement::GetCurrentContextType() {
1129   return mCurrentContextType;
1130 }
1131 
GetCanvasLayer(nsDisplayListBuilder * aBuilder,Layer * aOldLayer,LayerManager * aManager)1132 already_AddRefed<Layer> HTMLCanvasElement::GetCanvasLayer(
1133     nsDisplayListBuilder* aBuilder, Layer* aOldLayer, LayerManager* aManager) {
1134   if (mCurrentContext) {
1135     return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
1136   }
1137 
1138   if (mOffscreenCanvas) {
1139     MOZ_CRASH("todo");
1140   }
1141 
1142   return nullptr;
1143 }
1144 
UpdateWebRenderCanvasData(nsDisplayListBuilder * aBuilder,WebRenderCanvasData * aCanvasData)1145 bool HTMLCanvasElement::UpdateWebRenderCanvasData(
1146     nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) {
1147   if (mCurrentContext) {
1148     return mCurrentContext->UpdateWebRenderCanvasData(aBuilder, aCanvasData);
1149   }
1150   if (mOffscreenCanvas) {
1151     MOZ_CRASH("todo");
1152   }
1153 
1154   // Clear CanvasRenderer of WebRenderCanvasData
1155   aCanvasData->ClearCanvasRenderer();
1156   return false;
1157 }
1158 
InitializeCanvasRenderer(nsDisplayListBuilder * aBuilder,CanvasRenderer * aRenderer)1159 bool HTMLCanvasElement::InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
1160                                                  CanvasRenderer* aRenderer) {
1161   if (mCurrentContext) {
1162     return mCurrentContext->InitializeCanvasRenderer(aBuilder, aRenderer);
1163   }
1164 
1165   if (mOffscreenCanvas) {
1166     MOZ_CRASH("todo");
1167   }
1168 
1169   return false;
1170 }
1171 
ShouldForceInactiveLayer(LayerManager * aManager)1172 bool HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager) {
1173   if (mCurrentContext) {
1174     return mCurrentContext->ShouldForceInactiveLayer(aManager);
1175   }
1176 
1177   if (mOffscreenCanvas) {
1178     // TODO: We should handle offscreen canvas case.
1179     return false;
1180   }
1181 
1182   return true;
1183 }
1184 
MarkContextClean()1185 void HTMLCanvasElement::MarkContextClean() {
1186   if (!mCurrentContext) return;
1187 
1188   mCurrentContext->MarkContextClean();
1189 }
1190 
MarkContextCleanForFrameCapture()1191 void HTMLCanvasElement::MarkContextCleanForFrameCapture() {
1192   if (!mCurrentContext) return;
1193 
1194   mCurrentContext->MarkContextCleanForFrameCapture();
1195 }
1196 
IsContextCleanForFrameCapture()1197 bool HTMLCanvasElement::IsContextCleanForFrameCapture() {
1198   return mCurrentContext && mCurrentContext->IsContextCleanForFrameCapture();
1199 }
1200 
RegisterFrameCaptureListener(FrameCaptureListener * aListener,bool aReturnPlaceholderData)1201 nsresult HTMLCanvasElement::RegisterFrameCaptureListener(
1202     FrameCaptureListener* aListener, bool aReturnPlaceholderData) {
1203   WeakPtr<FrameCaptureListener> listener = aListener;
1204 
1205   if (mRequestedFrameListeners.Contains(listener)) {
1206     return NS_OK;
1207   }
1208 
1209   if (!mRequestedFrameRefreshObserver) {
1210     Document* doc = OwnerDoc();
1211     if (!doc) {
1212       return NS_ERROR_FAILURE;
1213     }
1214 
1215     PresShell* shell = nsContentUtils::FindPresShellForDocument(doc);
1216     if (!shell) {
1217       return NS_ERROR_FAILURE;
1218     }
1219 
1220     nsPresContext* context = shell->GetPresContext();
1221     if (!context) {
1222       return NS_ERROR_FAILURE;
1223     }
1224 
1225     context = context->GetRootPresContext();
1226     if (!context) {
1227       return NS_ERROR_FAILURE;
1228     }
1229 
1230     nsRefreshDriver* driver = context->RefreshDriver();
1231     if (!driver) {
1232       return NS_ERROR_FAILURE;
1233     }
1234 
1235     mRequestedFrameRefreshObserver =
1236         new RequestedFrameRefreshObserver(this, driver, aReturnPlaceholderData);
1237   } else {
1238     mRequestedFrameRefreshObserver->SetReturnPlaceholderData(
1239         aReturnPlaceholderData);
1240   }
1241 
1242   mRequestedFrameListeners.AppendElement(listener);
1243   mRequestedFrameRefreshObserver->Register();
1244   return NS_OK;
1245 }
1246 
IsFrameCaptureRequested() const1247 bool HTMLCanvasElement::IsFrameCaptureRequested() const {
1248   for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1249     if (!listener) {
1250       continue;
1251     }
1252 
1253     if (listener->FrameCaptureRequested()) {
1254       return true;
1255     }
1256   }
1257   return false;
1258 }
1259 
ProcessDestroyedFrameListeners()1260 void HTMLCanvasElement::ProcessDestroyedFrameListeners() {
1261   // Remove destroyed listeners from the list.
1262   mRequestedFrameListeners.RemoveElementsBy(
1263       [](const auto& weakListener) { return !weakListener; });
1264 
1265   if (mRequestedFrameListeners.IsEmpty()) {
1266     mRequestedFrameRefreshObserver->Unregister();
1267   }
1268 }
1269 
SetFrameCapture(already_AddRefed<SourceSurface> aSurface,const TimeStamp & aTime)1270 void HTMLCanvasElement::SetFrameCapture(
1271     already_AddRefed<SourceSurface> aSurface, const TimeStamp& aTime) {
1272   RefPtr<SourceSurface> surface = aSurface;
1273   RefPtr<SourceSurfaceImage> image =
1274       new SourceSurfaceImage(surface->GetSize(), surface);
1275 
1276   for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1277     if (!listener) {
1278       continue;
1279     }
1280 
1281     RefPtr<Image> imageRefCopy = image.get();
1282     listener->NewFrame(imageRefCopy.forget(), aTime);
1283   }
1284 }
1285 
GetSurfaceSnapshot(gfxAlphaType * const aOutAlphaType)1286 already_AddRefed<SourceSurface> HTMLCanvasElement::GetSurfaceSnapshot(
1287     gfxAlphaType* const aOutAlphaType) {
1288   if (!mCurrentContext) return nullptr;
1289 
1290   return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
1291 }
1292 
GetCompositorBackendType() const1293 layers::LayersBackend HTMLCanvasElement::GetCompositorBackendType() const {
1294   nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
1295   if (docWidget) {
1296     layers::LayerManager* layerManager = docWidget->GetLayerManager();
1297     if (layerManager) {
1298       return layerManager->GetCompositorBackendType();
1299     }
1300   }
1301 
1302   return LayersBackend::LAYERS_NONE;
1303 }
1304 
OnVisibilityChange()1305 void HTMLCanvasElement::OnVisibilityChange() {
1306   if (OwnerDoc()->Hidden()) {
1307     return;
1308   }
1309 
1310   if (mOffscreenCanvas) {
1311     MOZ_CRASH("todo");
1312     // Dispatch to GetActiveEventTarget.
1313     return;
1314   }
1315 
1316   if (mCurrentContext) {
1317     mCurrentContext->OnVisibilityChange();
1318   }
1319 }
1320 
OnMemoryPressure()1321 void HTMLCanvasElement::OnMemoryPressure() {
1322   if (mOffscreenCanvas) {
1323     MOZ_CRASH("todo");
1324     // Dispatch to GetActiveEventTarget.
1325     return;
1326   }
1327 
1328   if (mCurrentContext) {
1329     mCurrentContext->OnMemoryPressure();
1330   }
1331 }
1332 
OnDeviceReset()1333 void HTMLCanvasElement::OnDeviceReset() {
1334   if (!mOffscreenCanvas && mCurrentContext) {
1335     mCurrentContext->Reset();
1336   }
1337 }
1338 
GetWebGLContext()1339 ClientWebGLContext* HTMLCanvasElement::GetWebGLContext() {
1340   if (GetCurrentContextType() != CanvasContextType::WebGL1 &&
1341       GetCurrentContextType() != CanvasContextType::WebGL2) {
1342     return nullptr;
1343   }
1344 
1345   return static_cast<ClientWebGLContext*>(GetCurrentContext());
1346 }
1347 
GetWebGPUContext()1348 webgpu::CanvasContext* HTMLCanvasElement::GetWebGPUContext() {
1349   if (GetCurrentContextType() != CanvasContextType::WebGPU) {
1350     return nullptr;
1351   }
1352 
1353   return static_cast<webgpu::CanvasContext*>(GetCurrentContext());
1354 }
1355 
1356 }  // namespace mozilla::dom
1357