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