/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #if !defined(mozilla_dom_HTMLCanvasElement_h) # define mozilla_dom_HTMLCanvasElement_h # include "mozilla/Attributes.h" # include "mozilla/StateWatching.h" # include "mozilla/WeakPtr.h" # include "nsIDOMEventListener.h" # include "nsIObserver.h" # include "nsGenericHTMLElement.h" # include "nsGkAtoms.h" # include "nsSize.h" # include "nsError.h" # include "mozilla/dom/CanvasRenderingContextHelper.h" # include "mozilla/gfx/Rect.h" # include "mozilla/layers/LayersTypes.h" class nsICanvasRenderingContextInternal; class nsIInputStream; class nsITimerCallback; enum class gfxAlphaType; enum class FrameCaptureState : uint8_t; namespace mozilla { class nsDisplayListBuilder; class ClientWebGLContext; namespace layers { class CanvasRenderer; class Image; class ImageContainer; class Layer; class LayerManager; class OOPCanvasRenderer; class SharedSurfaceTextureClient; class WebRenderCanvasData; } // namespace layers namespace gfx { class DrawTarget; class SourceSurface; class VRLayerChild; } // namespace gfx namespace webgpu { class CanvasContext; } // namespace webgpu namespace dom { class BlobCallback; class CanvasCaptureMediaStream; class File; class HTMLCanvasPrintState; class OffscreenCanvas; class OffscreenCanvasDisplayHelper; class PrintCallback; class PWebGLChild; class RequestedFrameRefreshObserver; // Listen visibilitychange and memory-pressure event and inform // context when event is fired. class HTMLCanvasElementObserver final : public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement); void Destroy(); void RegisterObserverEvents(); void UnregisterObserverEvents(); private: ~HTMLCanvasElementObserver(); HTMLCanvasElement* mElement; }; /* * FrameCaptureListener is used by captureStream() as a way of getting video * frames from the canvas. On a refresh driver tick after something has been * drawn to the canvas since the last such tick, all registered * FrameCaptureListeners that report true for FrameCaptureRequested() will be * given a copy of the just-painted canvas. * All FrameCaptureListeners get the same copy. */ class FrameCaptureListener : public SupportsWeakPtr { public: FrameCaptureListener() = default; /* * Indicates to the canvas whether or not this listener has requested a frame. */ virtual bool FrameCaptureRequested(const TimeStamp& aTime) const = 0; /* * Interface through which new video frames will be provided while * `mFrameCaptureRequested` is `true`. */ virtual void NewFrame(already_AddRefed aImage, const TimeStamp& aTime) = 0; protected: virtual ~FrameCaptureListener() = default; }; class HTMLCanvasElement final : public nsGenericHTMLElement, public CanvasRenderingContextHelper, public SupportsWeakPtr { enum { DEFAULT_CANVAS_WIDTH = 300, DEFAULT_CANVAS_HEIGHT = 150 }; typedef layers::CanvasRenderer CanvasRenderer; typedef layers::LayerManager LayerManager; typedef layers::WebRenderCanvasData WebRenderCanvasData; public: explicit HTMLCanvasElement( already_AddRefed&& aNodeInfo); NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLCanvasElement, canvas) // nsISupports NS_DECL_ISUPPORTS_INHERITED // CC NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLCanvasElement, nsGenericHTMLElement) // WebIDL uint32_t Height() { return GetUnsignedIntAttr(nsGkAtoms::height, DEFAULT_CANVAS_HEIGHT); } uint32_t Width() { return GetUnsignedIntAttr(nsGkAtoms::width, DEFAULT_CANVAS_WIDTH); } void SetHeight(uint32_t aHeight, ErrorResult& aRv); void SetWidth(uint32_t aWidth, ErrorResult& aRv); already_AddRefed GetContext( JSContext* aCx, const nsAString& aContextId, JS::Handle aContextOptions, ErrorResult& aRv); void ToDataURL(JSContext* aCx, const nsAString& aType, JS::Handle aParams, nsAString& aDataURL, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv); void ToBlob(JSContext* aCx, BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv); OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv); bool MozOpaque() const { return GetBoolAttr(nsGkAtoms::moz_opaque); } void SetMozOpaque(bool aValue, ErrorResult& aRv) { if (mOffscreenCanvas) { aRv.Throw(NS_ERROR_FAILURE); return; } SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv); } PrintCallback* GetMozPrintCallback() const; void SetMozPrintCallback(PrintCallback* aCallback); already_AddRefed CaptureStream( const Optional& aFrameRate, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv); /** * Get the size in pixels of this canvas element */ nsIntSize GetSize(); /** * Determine whether the canvas is write-only. */ bool IsWriteOnly() const; /** * Force the canvas to be write-only, except for readers from * a specific extension's content script expanded principal, if * available. */ void SetWriteOnly(nsIPrincipal* aExpandedReader = nullptr); /** * Notify the placeholder offscreen canvas of an updated size. */ void InvalidateCanvasPlaceholder(uint32_t aWidth, uint32_t aHeight); /** * Notify that some canvas content has changed and the window may * need to be updated. aDamageRect is in canvas coordinates. */ void InvalidateCanvasContent(const mozilla::gfx::Rect* aDamageRect); /* * Notify that we need to repaint the entire canvas, including updating of * the layer tree. */ void InvalidateCanvas(); nsICanvasRenderingContextInternal* GetCurrentContext() { return mCurrentContext; } /* * Returns true if the canvas context content is guaranteed to be opaque * across its entire area. */ bool GetIsOpaque(); virtual bool GetOpaqueAttr() override; /** * Retrieve a snapshot of the internal surface, returning the alpha type if * requested. An optional target may be supplied for which the snapshot will * be optimized for, if possible. */ virtual already_AddRefed GetSurfaceSnapshot( gfxAlphaType* aOutAlphaType = nullptr, gfx::DrawTarget* aTarget = nullptr); /* * Register a FrameCaptureListener with this canvas. * The canvas hooks into the RefreshDriver while there are * FrameCaptureListeners registered. * The registered FrameCaptureListeners are stored as WeakPtrs, thus it's the * caller's responsibility to keep them alive. Once a registered * FrameCaptureListener is destroyed it will be automatically deregistered. */ nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener, bool aReturnPlaceholderData); /* * Returns true when there is at least one registered FrameCaptureListener * that has requested a frame capture. */ bool IsFrameCaptureRequested(const TimeStamp& aTime) const; /* * Processes destroyed FrameCaptureListeners and removes them if necessary. * Should there be none left, the FrameRefreshObserver will be unregistered. */ void ProcessDestroyedFrameListeners(); /* * Called by the RefreshDriver hook when a frame has been captured. * Makes a copy of the provided surface and hands it to all * FrameCaptureListeners having requested frame capture. */ void SetFrameCapture(already_AddRefed aSurface, const TimeStamp& aTime); virtual bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, const nsAString& aValue, nsIPrincipal* aMaybeScriptedPrincipal, nsAttrValue& aResult) override; NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override; nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute, int32_t aModType) const override; nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; nsresult CopyInnerTo(HTMLCanvasElement* aDest); static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, MappedDeclarations&); /* * Helpers called by various users of Canvas */ already_AddRefed GetAsImage(); bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData); bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer); // Call this whenever we need future changes to the canvas // to trigger fresh invalidation requests. This needs to be called // whenever we render the canvas contents to the screen, or whenever we // take a snapshot of the canvas that needs to be "live" (e.g. -moz-element). void MarkContextClean(); // Call this after capturing a frame, so we can avoid unnecessary surface // copies for future frames when no drawing has occurred. void MarkContextCleanForFrameCapture(); // Returns non-null when the current context supports captureStream(). // The FrameCaptureState gets set to DIRTY when something is drawn. Watchable* GetFrameCaptureState(); nsresult GetContext(const nsAString& aContextId, nsISupports** aContext); layers::LayersBackend GetCompositorBackendType() const; void OnMemoryPressure(); void OnDeviceReset(); already_AddRefed GetVRFrame(); void ClearVRFrame(); bool MaybeModified() const { return mMaybeModified; }; protected: virtual ~HTMLCanvasElement(); void Destroy(); virtual JSObject* WrapNode(JSContext* aCx, JS::Handle aGivenProto) override; virtual nsIntSize GetWidthHeight() override; virtual already_AddRefed CreateContext( CanvasContextType aContextType) override; nsresult ExtractData(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, nsAString& aType, const nsAString& aOptions, nsIInputStream** aStream); nsresult ToDataURLImpl(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, const nsAString& aMimeType, const JS::Value& aEncoderOptions, nsAString& aDataURL); MOZ_CAN_RUN_SCRIPT void CallPrintCallback(); virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, nsIPrincipal* aSubjectPrincipal, bool aNotify) override; virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue, bool aNotify) override; public: ClientWebGLContext* GetWebGLContext(); webgpu::CanvasContext* GetWebGPUContext(); bool IsOffscreen() const { return !!mOffscreenCanvas; } OffscreenCanvas* GetOffscreenCanvas() const { return mOffscreenCanvas; } layers::ImageContainer* GetImageContainer() const { return mImageContainer; } protected: bool mResetLayer; bool mMaybeModified; // we fetched the context, so we may have written to the // canvas RefPtr mOriginalCanvas; RefPtr mPrintCallback; RefPtr mPrintState; nsTArray> mRequestedFrameListeners; RefPtr mRequestedFrameRefreshObserver; RefPtr mCanvasRenderer; RefPtr mOffscreenCanvas; RefPtr mOffscreenDisplay; RefPtr mImageContainer; RefPtr mContextObserver; public: // Record whether this canvas should be write-only or not. // We set this when script paints an image from a different origin. // We also transitively set it when script paints a canvas which // is itself write-only. bool mWriteOnly; // When this canvas is (only) tainted by an image from an extension // content script, allow reads from the same extension afterwards. RefPtr mExpandedReader; // Determines if the caller should be able to read the content. bool CallerCanRead(JSContext* aCx) const; bool IsPrintCallbackDone(); void HandlePrintCallback(nsPresContext*); nsresult DispatchPrintCallback(nsITimerCallback* aCallback); void ResetPrintCallback(); HTMLCanvasElement* GetOriginalCanvas(); CanvasContextType GetCurrentContextType(); private: /** * This function is called by AfterSetAttr and OnAttrSetButNotChanged. * This function will be called by AfterSetAttr whether the attribute is being * set or unset. * * @param aNamespaceID the namespace of the attr being set * @param aName the localname of the attribute being set * @param aNotify Whether we plan to notify document observers. */ void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName, bool aNotify); }; class HTMLCanvasPrintState final : public nsWrapperCache { public: HTMLCanvasPrintState(HTMLCanvasElement* aCanvas, nsICanvasRenderingContextInternal* aContext, nsITimerCallback* aCallback); nsISupports* Context() const; void Done(); void NotifyDone(); bool mIsDone; NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(HTMLCanvasPrintState) NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(HTMLCanvasPrintState) virtual JSObject* WrapObject(JSContext* cx, JS::Handle aGivenProto) override; HTMLCanvasElement* GetParentObject() { return mCanvas; } private: ~HTMLCanvasPrintState(); bool mPendingNotify; protected: RefPtr mCanvas; nsCOMPtr mContext; nsCOMPtr mCallback; }; } // namespace dom } // namespace mozilla #endif /* mozilla_dom_HTMLCanvasElement_h */