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 #if !defined(mozilla_dom_HTMLCanvasElement_h)
7 #  define mozilla_dom_HTMLCanvasElement_h
8 
9 #  include "mozilla/Attributes.h"
10 #  include "mozilla/StateWatching.h"
11 #  include "mozilla/WeakPtr.h"
12 #  include "nsIDOMEventListener.h"
13 #  include "nsIObserver.h"
14 #  include "nsGenericHTMLElement.h"
15 #  include "nsGkAtoms.h"
16 #  include "nsSize.h"
17 #  include "nsError.h"
18 
19 #  include "mozilla/dom/CanvasRenderingContextHelper.h"
20 #  include "mozilla/gfx/Rect.h"
21 #  include "mozilla/layers/LayersTypes.h"
22 
23 class nsICanvasRenderingContextInternal;
24 class nsIInputStream;
25 class nsITimerCallback;
26 enum class gfxAlphaType;
27 enum class FrameCaptureState : uint8_t;
28 
29 namespace mozilla {
30 
31 class nsDisplayListBuilder;
32 class ClientWebGLContext;
33 
34 namespace layers {
35 class CanvasRenderer;
36 class Image;
37 class ImageContainer;
38 class Layer;
39 class LayerManager;
40 class OOPCanvasRenderer;
41 class SharedSurfaceTextureClient;
42 class WebRenderCanvasData;
43 }  // namespace layers
44 namespace gfx {
45 class SourceSurface;
46 class VRLayerChild;
47 }  // namespace gfx
48 namespace webgpu {
49 class CanvasContext;
50 }  // namespace webgpu
51 
52 namespace dom {
53 class BlobCallback;
54 class CanvasCaptureMediaStream;
55 class File;
56 class HTMLCanvasPrintState;
57 class OffscreenCanvas;
58 class OffscreenCanvasDisplayHelper;
59 class PrintCallback;
60 class PWebGLChild;
61 class RequestedFrameRefreshObserver;
62 
63 // Listen visibilitychange and memory-pressure event and inform
64 // context when event is fired.
65 class HTMLCanvasElementObserver final : public nsIObserver {
66  public:
67   NS_DECL_ISUPPORTS
68   NS_DECL_NSIOBSERVER
69 
70   explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement);
71   void Destroy();
72 
73   void RegisterObserverEvents();
74   void UnregisterObserverEvents();
75 
76  private:
77   ~HTMLCanvasElementObserver();
78 
79   HTMLCanvasElement* mElement;
80 };
81 
82 /*
83  * FrameCaptureListener is used by captureStream() as a way of getting video
84  * frames from the canvas. On a refresh driver tick after something has been
85  * drawn to the canvas since the last such tick, all registered
86  * FrameCaptureListeners that report true for FrameCaptureRequested() will be
87  * given a copy of the just-painted canvas.
88  * All FrameCaptureListeners get the same copy.
89  */
90 class FrameCaptureListener : public SupportsWeakPtr {
91  public:
92   FrameCaptureListener() = default;
93 
94   /*
95    * Indicates to the canvas whether or not this listener has requested a frame.
96    */
97   virtual bool FrameCaptureRequested(const TimeStamp& aTime) const = 0;
98 
99   /*
100    * Interface through which new video frames will be provided while
101    * `mFrameCaptureRequested` is `true`.
102    */
103   virtual void NewFrame(already_AddRefed<layers::Image> aImage,
104                         const TimeStamp& aTime) = 0;
105 
106  protected:
107   virtual ~FrameCaptureListener() = default;
108 };
109 
110 class HTMLCanvasElement final : public nsGenericHTMLElement,
111                                 public CanvasRenderingContextHelper,
112                                 public SupportsWeakPtr {
113   enum { DEFAULT_CANVAS_WIDTH = 300, DEFAULT_CANVAS_HEIGHT = 150 };
114 
115   typedef layers::CanvasRenderer CanvasRenderer;
116   typedef layers::Layer Layer;
117   typedef layers::LayerManager LayerManager;
118   typedef layers::WebRenderCanvasData WebRenderCanvasData;
119 
120  public:
121   explicit HTMLCanvasElement(
122       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
123 
NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLCanvasElement,canvas)124   NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLCanvasElement, canvas)
125 
126   // nsISupports
127   NS_DECL_ISUPPORTS_INHERITED
128 
129   // CC
130   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLCanvasElement,
131                                            nsGenericHTMLElement)
132 
133   // WebIDL
134   uint32_t Height() {
135     return GetUnsignedIntAttr(nsGkAtoms::height, DEFAULT_CANVAS_HEIGHT);
136   }
Width()137   uint32_t Width() {
138     return GetUnsignedIntAttr(nsGkAtoms::width, DEFAULT_CANVAS_WIDTH);
139   }
140   void SetHeight(uint32_t aHeight, ErrorResult& aRv);
141   void SetWidth(uint32_t aWidth, ErrorResult& aRv);
142 
143   already_AddRefed<nsISupports> GetContext(
144       JSContext* aCx, const nsAString& aContextId,
145       JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv);
146 
147   void ToDataURL(JSContext* aCx, const nsAString& aType,
148                  JS::Handle<JS::Value> aParams, nsAString& aDataURL,
149                  nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
150 
151   void ToBlob(JSContext* aCx, BlobCallback& aCallback, const nsAString& aType,
152               JS::Handle<JS::Value> aParams, nsIPrincipal& aSubjectPrincipal,
153               ErrorResult& aRv);
154 
155   OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv);
156 
MozOpaque()157   bool MozOpaque() const { return GetBoolAttr(nsGkAtoms::moz_opaque); }
SetMozOpaque(bool aValue,ErrorResult & aRv)158   void SetMozOpaque(bool aValue, ErrorResult& aRv) {
159     if (mOffscreenCanvas) {
160       aRv.Throw(NS_ERROR_FAILURE);
161       return;
162     }
163 
164     SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
165   }
166   already_AddRefed<File> MozGetAsFile(const nsAString& aName,
167                                       const nsAString& aType,
168                                       nsIPrincipal& aSubjectPrincipal,
169                                       ErrorResult& aRv);
170   already_AddRefed<nsISupports> MozGetIPCContext(const nsAString& aContextId,
171                                                  ErrorResult& aRv);
172   PrintCallback* GetMozPrintCallback() const;
173   void SetMozPrintCallback(PrintCallback* aCallback);
174 
175   already_AddRefed<CanvasCaptureMediaStream> CaptureStream(
176       const Optional<double>& aFrameRate, nsIPrincipal& aSubjectPrincipal,
177       ErrorResult& aRv);
178 
179   /**
180    * Get the size in pixels of this canvas element
181    */
182   nsIntSize GetSize();
183 
184   /**
185    * Determine whether the canvas is write-only.
186    */
187   bool IsWriteOnly() const;
188 
189   /**
190    * Force the canvas to be write-only, except for readers from
191    * a specific extension's content script expanded principal, if
192    * available.
193    */
194   void SetWriteOnly(nsIPrincipal* aExpandedReader = nullptr);
195 
196   /**
197    * Notify the placeholder offscreen canvas of an updated size.
198    */
199   void InvalidateCanvasPlaceholder(uint32_t aWidth, uint32_t aHeight);
200 
201   /**
202    * Notify that some canvas content has changed and the window may
203    * need to be updated. aDamageRect is in canvas coordinates.
204    */
205   void InvalidateCanvasContent(const mozilla::gfx::Rect* aDamageRect);
206   /*
207    * Notify that we need to repaint the entire canvas, including updating of
208    * the layer tree.
209    */
210   void InvalidateCanvas();
211 
GetCurrentContext()212   nsICanvasRenderingContextInternal* GetCurrentContext() {
213     return mCurrentContext;
214   }
215 
216   /*
217    * Returns true if the canvas context content is guaranteed to be opaque
218    * across its entire area.
219    */
220   bool GetIsOpaque();
221   virtual bool GetOpaqueAttr() override;
222 
223   virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(
224       gfxAlphaType* aOutAlphaType = nullptr);
225 
226   /*
227    * Register a FrameCaptureListener with this canvas.
228    * The canvas hooks into the RefreshDriver while there are
229    * FrameCaptureListeners registered.
230    * The registered FrameCaptureListeners are stored as WeakPtrs, thus it's the
231    * caller's responsibility to keep them alive. Once a registered
232    * FrameCaptureListener is destroyed it will be automatically deregistered.
233    */
234   nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener,
235                                         bool aReturnPlaceholderData);
236 
237   /*
238    * Returns true when there is at least one registered FrameCaptureListener
239    * that has requested a frame capture.
240    */
241   bool IsFrameCaptureRequested(const TimeStamp& aTime) const;
242 
243   /*
244    * Processes destroyed FrameCaptureListeners and removes them if necessary.
245    * Should there be none left, the FrameRefreshObserver will be unregistered.
246    */
247   void ProcessDestroyedFrameListeners();
248 
249   /*
250    * Called by the RefreshDriver hook when a frame has been captured.
251    * Makes a copy of the provided surface and hands it to all
252    * FrameCaptureListeners having requested frame capture.
253    */
254   void SetFrameCapture(already_AddRefed<gfx::SourceSurface> aSurface,
255                        const TimeStamp& aTime);
256 
257   virtual bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
258                               const nsAString& aValue,
259                               nsIPrincipal* aMaybeScriptedPrincipal,
260                               nsAttrValue& aResult) override;
261   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
262   nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
263                                       int32_t aModType) const override;
264   nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override;
265 
266   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
267   nsresult CopyInnerTo(HTMLCanvasElement* aDest);
268 
269   void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
270 
271   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
272                                     MappedDeclarations&);
273 
274   /*
275    * Helpers called by various users of Canvas
276    */
277 
278   already_AddRefed<layers::Image> GetAsImage();
279   bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
280                                  WebRenderCanvasData* aCanvasData);
281   bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
282                                 CanvasRenderer* aRenderer);
283 
284   // Call this whenever we need future changes to the canvas
285   // to trigger fresh invalidation requests. This needs to be called
286   // whenever we render the canvas contents to the screen, or whenever we
287   // take a snapshot of the canvas that needs to be "live" (e.g. -moz-element).
288   void MarkContextClean();
289 
290   // Call this after capturing a frame, so we can avoid unnecessary surface
291   // copies for future frames when no drawing has occurred.
292   void MarkContextCleanForFrameCapture();
293 
294   // Returns non-null when the current context supports captureStream().
295   // The FrameCaptureState gets set to DIRTY when something is drawn.
296   Watchable<FrameCaptureState>* GetFrameCaptureState();
297 
298   nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);
299 
300   layers::LayersBackend GetCompositorBackendType() const;
301 
302   void OnMemoryPressure();
303   void OnDeviceReset();
304 
305   already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
306   void ClearVRFrame();
307 
MaybeModified()308   bool MaybeModified() const { return mMaybeModified; };
309 
310  protected:
311   virtual ~HTMLCanvasElement();
312   void Destroy();
313 
314   virtual JSObject* WrapNode(JSContext* aCx,
315                              JS::Handle<JSObject*> aGivenProto) override;
316 
317   virtual nsIntSize GetWidthHeight() override;
318 
319   virtual already_AddRefed<nsICanvasRenderingContextInternal> CreateContext(
320       CanvasContextType aContextType) override;
321 
322   nsresult ExtractData(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
323                        nsAString& aType, const nsAString& aOptions,
324                        nsIInputStream** aStream);
325   nsresult ToDataURLImpl(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
326                          const nsAString& aMimeType,
327                          const JS::Value& aEncoderOptions, nsAString& aDataURL);
328   nsresult MozGetAsFileImpl(const nsAString& aName, const nsAString& aType,
329                             nsIPrincipal& aSubjectPrincipal, File** aResult);
330   MOZ_CAN_RUN_SCRIPT void CallPrintCallback();
331 
332   virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
333                                 const nsAttrValue* aValue,
334                                 const nsAttrValue* aOldValue,
335                                 nsIPrincipal* aSubjectPrincipal,
336                                 bool aNotify) override;
337   virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
338                                           const nsAttrValueOrString& aValue,
339                                           bool aNotify) override;
340 
341  public:
342   ClientWebGLContext* GetWebGLContext();
343   webgpu::CanvasContext* GetWebGPUContext();
344 
IsOffscreen()345   bool IsOffscreen() const { return !!mOffscreenCanvas; }
GetOffscreenCanvas()346   OffscreenCanvas* GetOffscreenCanvas() const { return mOffscreenCanvas; }
347 
348   RefPtr<layers::ImageContainer> GetImageContainer();
349 
350  protected:
351   bool mResetLayer;
352   bool mMaybeModified;  // we fetched the context, so we may have written to the
353                         // canvas
354   RefPtr<HTMLCanvasElement> mOriginalCanvas;
355   RefPtr<PrintCallback> mPrintCallback;
356   RefPtr<HTMLCanvasPrintState> mPrintState;
357   nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners;
358   RefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver;
359   RefPtr<CanvasRenderer> mCanvasRenderer;
360   RefPtr<OffscreenCanvas> mOffscreenCanvas;
361   RefPtr<OffscreenCanvasDisplayHelper> mOffscreenDisplay;
362   RefPtr<HTMLCanvasElementObserver> mContextObserver;
363 
364  public:
365   // Record whether this canvas should be write-only or not.
366   // We set this when script paints an image from a different origin.
367   // We also transitively set it when script paints a canvas which
368   // is itself write-only.
369   bool mWriteOnly;
370 
371   // When this canvas is (only) tainted by an image from an extension
372   // content script, allow reads from the same extension afterwards.
373   RefPtr<nsIPrincipal> mExpandedReader;
374 
375   // Determines if the caller should be able to read the content.
376   bool CallerCanRead(JSContext* aCx);
377 
378   bool IsPrintCallbackDone();
379 
380   void HandlePrintCallback(nsPresContext*);
381 
382   nsresult DispatchPrintCallback(nsITimerCallback* aCallback);
383 
384   void ResetPrintCallback();
385 
386   HTMLCanvasElement* GetOriginalCanvas();
387 
388   CanvasContextType GetCurrentContextType();
389 
390  private:
391   /**
392    * This function is called by AfterSetAttr and OnAttrSetButNotChanged.
393    * This function will be called by AfterSetAttr whether the attribute is being
394    * set or unset.
395    *
396    * @param aNamespaceID the namespace of the attr being set
397    * @param aName the localname of the attribute being set
398    * @param aNotify Whether we plan to notify document observers.
399    */
400   void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName, bool aNotify);
401 };
402 
403 class HTMLCanvasPrintState final : public nsWrapperCache {
404  public:
405   HTMLCanvasPrintState(HTMLCanvasElement* aCanvas,
406                        nsICanvasRenderingContextInternal* aContext,
407                        nsITimerCallback* aCallback);
408 
409   nsISupports* Context() const;
410 
411   void Done();
412 
413   void NotifyDone();
414 
415   bool mIsDone;
416 
417   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(HTMLCanvasPrintState)
418   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(HTMLCanvasPrintState)
419 
420   virtual JSObject* WrapObject(JSContext* cx,
421                                JS::Handle<JSObject*> aGivenProto) override;
422 
GetParentObject()423   HTMLCanvasElement* GetParentObject() { return mCanvas; }
424 
425  private:
426   ~HTMLCanvasPrintState();
427   bool mPendingNotify;
428 
429  protected:
430   RefPtr<HTMLCanvasElement> mCanvas;
431   nsCOMPtr<nsICanvasRenderingContextInternal> mContext;
432   nsCOMPtr<nsITimerCallback> mCallback;
433 };
434 
435 }  // namespace dom
436 }  // namespace mozilla
437 
438 #endif /* mozilla_dom_HTMLCanvasElement_h */
439