Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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; }
bool UsingCaptureStream() const { return !!mRequestedFrameRefreshObserver; }
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 */