diff options
Diffstat (limited to 'dom/html/HTMLCanvasElement.h')
-rw-r--r-- | dom/html/HTMLCanvasElement.h | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h new file mode 100644 index 0000000000..586a43fedc --- /dev/null +++ b/dom/html/HTMLCanvasElement.h @@ -0,0 +1,447 @@ +/* -*- 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<layers::Image> 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<mozilla::dom::NodeInfo>&& 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<nsISupports> GetContext( + JSContext* aCx, const nsAString& aContextId, + JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv); + + void ToDataURL(JSContext* aCx, const nsAString& aType, + JS::Handle<JS::Value> aParams, nsAString& aDataURL, + nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv); + + void ToBlob(JSContext* aCx, BlobCallback& aCallback, const nsAString& aType, + JS::Handle<JS::Value> 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<CanvasCaptureMediaStream> CaptureStream( + const Optional<double>& aFrameRate, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv); + + /** + * Get the size in pixels of this canvas element + */ + nsIntSize GetSize(); + + /** + * Set the size in pixels of this canvas element. + */ + void SetSize(const nsIntSize& aSize, ErrorResult& aRv); + + /** + * 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<gfx::SourceSurface> 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<gfx::SourceSurface> 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(MappedDeclarationsBuilder&); + + /* + * Helpers called by various users of Canvas + */ + + already_AddRefed<layers::Image> 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<FrameCaptureState>* GetFrameCaptureState(); + + nsresult GetContext(const nsAString& aContextId, nsISupports** aContext); + + layers::LayersBackend GetCompositorBackendType() const; + + void OnMemoryPressure(); + void OnDeviceReset(); + + already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame(); + void ClearVRFrame(); + + bool MaybeModified() const { return mMaybeModified; }; + + protected: + virtual ~HTMLCanvasElement(); + void Destroy(); + + virtual JSObject* WrapNode(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + virtual nsIntSize GetWidthHeight() override; + + virtual already_AddRefed<nsICanvasRenderingContextInternal> CreateContext( + CanvasContextType aContextType) override; + + nsresult UpdateContext(JSContext* aCx, + JS::Handle<JS::Value> aNewContextOptions, + ErrorResult& aRvForDictionaryInit) 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); + + UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aOutFormat, + gfx::IntSize* aOutImageSize) override; + + MOZ_CAN_RUN_SCRIPT void CallPrintCallback(); + + virtual void AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + nsIPrincipal* aSubjectPrincipal, + bool aNotify) override; + virtual void 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; } + void FlushOffscreenCanvas(); + + layers::ImageContainer* GetImageContainer() const { return mImageContainer; } + + protected: + bool mResetLayer; + bool mMaybeModified; // we fetched the context, so we may have written to the + // canvas + RefPtr<HTMLCanvasElement> mOriginalCanvas; + RefPtr<PrintCallback> mPrintCallback; + RefPtr<HTMLCanvasPrintState> mPrintState; + nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners; + RefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver; + RefPtr<OffscreenCanvas> mOffscreenCanvas; + RefPtr<OffscreenCanvasDisplayHelper> mOffscreenDisplay; + RefPtr<layers::ImageContainer> mImageContainer; + RefPtr<HTMLCanvasElementObserver> 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<nsIPrincipal> mExpandedReader; + + // Determines if the caller should be able to read the content. + bool CallerCanRead(nsIPrincipal& aPrincipal) 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<JSObject*> aGivenProto) override; + + HTMLCanvasElement* GetParentObject() { return mCanvas; } + + private: + ~HTMLCanvasPrintState(); + bool mPendingNotify; + + protected: + RefPtr<HTMLCanvasElement> mCanvas; + nsCOMPtr<nsICanvasRenderingContextInternal> mContext; + nsCOMPtr<nsITimerCallback> mCallback; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_HTMLCanvasElement_h */ |