summaryrefslogtreecommitdiffstats
path: root/dom/canvas/CanvasRenderingContext2D.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/CanvasRenderingContext2D.h')
-rw-r--r--dom/canvas/CanvasRenderingContext2D.h1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h
new file mode 100644
index 0000000000..8cad1b0c97
--- /dev/null
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -0,0 +1,1118 @@
+/* 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/. */
+
+#ifndef CanvasRenderingContext2D_h
+#define CanvasRenderingContext2D_h
+
+#include <vector>
+#include "mozilla/dom/BasicRenderingContext2D.h"
+#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/intl/Bidi.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MruCache.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/SurfaceFromElementResult.h"
+#include "mozilla/ThreadLocal.h"
+#include "mozilla/UniquePtr.h"
+#include "FilterDescription.h"
+#include "gfx2DGlue.h"
+#include "gfxFontConstants.h"
+#include "gfxUtils.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsColor.h"
+#include "nsIFrame.h"
+
+class gfxFontGroup;
+class nsGlobalWindowInner;
+class nsXULElement;
+
+namespace mozilla {
+class ErrorResult;
+class PresShell;
+
+namespace gl {
+class SourceSurface;
+} // namespace gl
+
+namespace layers {
+class PersistentBufferProvider;
+enum class LayersBackend : int8_t;
+} // namespace layers
+
+namespace dom {
+class
+ HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmap;
+using CanvasImageSource =
+ HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmap;
+class ImageBitmap;
+class ImageData;
+class UTF8StringOrCanvasGradientOrCanvasPattern;
+class OwningUTF8StringOrCanvasGradientOrCanvasPattern;
+class TextMetrics;
+class CanvasGradient;
+class CanvasPath;
+class CanvasPattern;
+
+extern const mozilla::gfx::Float SIGMA_MAX;
+
+template <typename T>
+class Optional;
+
+struct CanvasBidiProcessor;
+class CanvasDrawObserver;
+class CanvasShutdownObserver;
+
+class DOMMatrix;
+class DOMMatrixReadOnly;
+struct DOMMatrix2DInit;
+
+/**
+ ** CanvasRenderingContext2D
+ **/
+class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
+ public nsWrapperCache,
+ public BasicRenderingContext2D {
+ protected:
+ virtual ~CanvasRenderingContext2D();
+
+ public:
+ explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend);
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ HTMLCanvasElement* GetCanvas() const {
+ if (!mCanvasElement || mCanvasElement->IsInNativeAnonymousSubtree()) {
+ return nullptr;
+ }
+
+ // corresponds to changes to the old bindings made in bug 745025
+ return mCanvasElement->GetOriginalCanvas();
+ }
+
+ void OnMemoryPressure() override;
+ void OnBeforePaintTransaction() override;
+ void OnDidPaintTransaction() override;
+ layers::PersistentBufferProvider* GetBufferProvider() override;
+
+ Maybe<layers::SurfaceDescriptor> GetFrontBuffer(
+ WebGLFramebufferJS*, const bool webvr = false) override;
+
+ void Save() override;
+ void Restore() override;
+
+ void Reset() {
+ // reset the rendering context to its default state
+ // Userland polyfill is `c2d.width = c2d.width;`
+ SetDimensions(GetWidth(), GetHeight());
+ }
+
+ void Scale(double aX, double aY, mozilla::ErrorResult& aError) override;
+ void Rotate(double aAngle, mozilla::ErrorResult& aError) override;
+ void Translate(double aX, double aY, mozilla::ErrorResult& aError) override;
+ void Transform(double aM11, double aM12, double aM21, double aM22, double aDx,
+ double aDy, mozilla::ErrorResult& aError) override;
+ already_AddRefed<DOMMatrix> GetTransform(
+ mozilla::ErrorResult& aError) override;
+ void SetTransform(double aM11, double aM12, double aM21, double aM22,
+ double aDx, double aDy,
+ mozilla::ErrorResult& aError) override;
+ void SetTransform(const DOMMatrix2DInit& aInit,
+ mozilla::ErrorResult& aError) override;
+ void ResetTransform(mozilla::ErrorResult& aError) override;
+
+ double GlobalAlpha() override { return CurrentState().globalAlpha; }
+
+ // Useful for silencing cast warnings
+ static mozilla::gfx::Float ToFloat(double aValue) {
+ return mozilla::gfx::Float(aValue);
+ }
+
+ void SetGlobalAlpha(double aGlobalAlpha) override {
+ if (aGlobalAlpha >= 0.0 && aGlobalAlpha <= 1.0) {
+ CurrentState().globalAlpha = ToFloat(aGlobalAlpha);
+ }
+ }
+
+ enum class ResolveCurrentColor : bool { No, Yes };
+ Maybe<nscolor> ParseColor(const nsACString&,
+ ResolveCurrentColor = ResolveCurrentColor::Yes);
+
+ void GetGlobalCompositeOperation(nsAString& aOp,
+ mozilla::ErrorResult& aError) override;
+ void SetGlobalCompositeOperation(const nsAString& aOp,
+ mozilla::ErrorResult& aError) override;
+
+ void GetStrokeStyle(
+ OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
+ GetStyleAsUnion(aValue, Style::STROKE);
+ }
+
+ void SetStrokeStyle(
+ const UTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
+ SetStyleFromUnion(aValue, Style::STROKE);
+ }
+
+ void GetFillStyle(
+ OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
+ GetStyleAsUnion(aValue, Style::FILL);
+ }
+
+ void SetFillStyle(
+ const UTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
+ SetStyleFromUnion(aValue, Style::FILL);
+ }
+
+ already_AddRefed<CanvasGradient> CreateLinearGradient(double aX0, double aY0,
+ double aX1,
+ double aY1) override;
+ already_AddRefed<CanvasGradient> CreateRadialGradient(
+ double aX0, double aY0, double aR0, double aX1, double aY1, double aR1,
+ ErrorResult& aError) override;
+ already_AddRefed<CanvasGradient> CreateConicGradient(double aAngle,
+ double aCx,
+ double aCy) override;
+ already_AddRefed<CanvasPattern> CreatePattern(
+ const CanvasImageSource& aElement, const nsAString& aRepeat,
+ ErrorResult& aError) override;
+
+ double ShadowOffsetX() override { return CurrentState().shadowOffset.x; }
+
+ void SetShadowOffsetX(double aShadowOffsetX) override {
+ CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX);
+ }
+
+ double ShadowOffsetY() override { return CurrentState().shadowOffset.y; }
+
+ void SetShadowOffsetY(double aShadowOffsetY) override {
+ CurrentState().shadowOffset.y = ToFloat(aShadowOffsetY);
+ }
+
+ double ShadowBlur() override { return CurrentState().shadowBlur; }
+
+ void SetShadowBlur(double aShadowBlur) override {
+ if (aShadowBlur >= 0.0) {
+ CurrentState().shadowBlur = ToFloat(aShadowBlur);
+ }
+ }
+
+ void GetShadowColor(nsACString& aShadowColor) override {
+ StyleColorToString(CurrentState().shadowColor, aShadowColor);
+ }
+
+ void GetFilter(nsACString& aFilter) { aFilter = CurrentState().filterString; }
+
+ void SetShadowColor(const nsACString& aShadowColor) override;
+ void SetFilter(const nsACString& aFilter, mozilla::ErrorResult& aError);
+ void ClearRect(double aX, double aY, double aW, double aH) override;
+ void FillRect(double aX, double aY, double aW, double aH) override;
+ void StrokeRect(double aX, double aY, double aW, double aH) override;
+ void BeginPath();
+ void Fill(const CanvasWindingRule& aWinding);
+ void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
+ void Stroke();
+ void Stroke(const CanvasPath& aPath);
+ void DrawFocusIfNeeded(mozilla::dom::Element& aElement, ErrorResult& aRv);
+ bool DrawCustomFocusRing(mozilla::dom::Element& aElement);
+ void Clip(const CanvasWindingRule& aWinding);
+ void Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
+ bool IsPointInPath(JSContext* aCx, double aX, double aY,
+ const CanvasWindingRule& aWinding,
+ nsIPrincipal& aSubjectPrincipal);
+ bool IsPointInPath(JSContext* aCx, double aX, double aY,
+ const CanvasWindingRule& aWinding,
+ Maybe<nsIPrincipal*> aSubjectPrincipal);
+ bool IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX,
+ double aY, const CanvasWindingRule& aWinding,
+ nsIPrincipal&);
+ bool IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX,
+ double aY, const CanvasWindingRule& aWinding,
+ Maybe<nsIPrincipal*>);
+ bool IsPointInStroke(JSContext* aCx, double aX, double aY,
+ nsIPrincipal& aSubjectPrincipal);
+ bool IsPointInStroke(JSContext* aCx, double aX, double aY,
+ Maybe<nsIPrincipal*> aSubjectPrincipal);
+ bool IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX,
+ double aY, nsIPrincipal&);
+ bool IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX,
+ double aY, Maybe<nsIPrincipal*>);
+ void FillText(const nsAString& aText, double aX, double aY,
+ const Optional<double>& aMaxWidth,
+ mozilla::ErrorResult& aError);
+ void StrokeText(const nsAString& aText, double aX, double aY,
+ const Optional<double>& aMaxWidth,
+ mozilla::ErrorResult& aError);
+ TextMetrics* MeasureText(const nsAString& aRawText,
+ mozilla::ErrorResult& aError);
+
+ void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
+ mozilla::ErrorResult& aError) override {
+ DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, 0.0, 0.0, 0, aError);
+ }
+
+ void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
+ double aDw, double aDh,
+ mozilla::ErrorResult& aError) override {
+ DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, aDw, aDh, 2, aError);
+ }
+
+ void DrawImage(const CanvasImageSource& aImage, double aSx, double aSy,
+ double aSw, double aSh, double aDx, double aDy, double aDw,
+ double aDh, mozilla::ErrorResult& aError) override {
+ DrawImage(aImage, aSx, aSy, aSw, aSh, aDx, aDy, aDw, aDh, 6, aError);
+ }
+
+ already_AddRefed<ImageData> CreateImageData(JSContext*, int32_t aSw,
+ int32_t aSh, ErrorResult&);
+ already_AddRefed<ImageData> CreateImageData(JSContext*, ImageData&,
+ ErrorResult&);
+ already_AddRefed<ImageData> GetImageData(JSContext*, int32_t aSx, int32_t aSy,
+ int32_t aSw, int32_t aSh,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult&);
+ already_AddRefed<ImageData> GetImageData(
+ JSContext*, int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
+ Maybe<nsIPrincipal*> aSubjectPrincipal, ErrorResult&);
+ void PutImageData(ImageData&, int32_t aDx, int32_t aDy, ErrorResult&);
+ void PutImageData(ImageData&, int32_t aDx, int32_t aDy, int32_t aDirtyX,
+ int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight,
+ ErrorResult&);
+
+ double LineWidth() override { return CurrentState().lineWidth; }
+
+ void SetLineWidth(double aWidth) override {
+ if (aWidth > 0.0) {
+ CurrentState().lineWidth = ToFloat(aWidth);
+ }
+ }
+ void GetLineCap(nsAString& aLinecapStyle) override;
+ void SetLineCap(const nsAString& aLinecapStyle) override;
+ void GetLineJoin(nsAString& aLinejoinStyle,
+ mozilla::ErrorResult& aError) override;
+ void SetLineJoin(const nsAString& aLinejoinStyle) override;
+
+ double MiterLimit() override { return CurrentState().miterLimit; }
+
+ void SetMiterLimit(double aMiter) override {
+ if (aMiter > 0.0) {
+ CurrentState().miterLimit = ToFloat(aMiter);
+ }
+ }
+
+ void GetFont(nsACString& aFont) { aFont = GetFont(); }
+
+ void SetFont(const nsACString& aFont, mozilla::ErrorResult& aError);
+ void GetTextAlign(nsAString& aTextAlign);
+ void SetTextAlign(const nsAString& aTextAlign);
+ void GetTextBaseline(nsAString& aTextBaseline);
+ void SetTextBaseline(const nsAString& aTextBaseline);
+ void GetDirection(nsAString& aDirection);
+ void SetDirection(const nsAString& aDirection);
+
+ void GetFontKerning(nsAString& aFontKerning);
+ void SetFontKerning(const nsAString& aFontKerning);
+
+ void GetLetterSpacing(nsACString& aLetterSpacing);
+ void SetLetterSpacing(const nsACString& aLetterSpacing);
+ void GetWordSpacing(nsACString& aWordSpacing);
+ void SetWordSpacing(const nsACString& aWordSpacing);
+
+ void ClosePath() override {
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->Close();
+ } else {
+ mDSPathBuilder->Close();
+ }
+ }
+
+ void MoveTo(double aX, double aY) override {
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->MoveTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
+ } else {
+ mDSPathBuilder->MoveTo(mTarget->GetTransform().TransformPoint(
+ mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
+ }
+ }
+
+ void LineTo(double aX, double aY) override {
+ EnsureWritablePath();
+
+ LineTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
+ }
+
+ void QuadraticCurveTo(double aCpx, double aCpy, double aX,
+ double aY) override {
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->QuadraticBezierTo(
+ mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy)),
+ mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
+ } else {
+ mozilla::gfx::Matrix transform = mTarget->GetTransform();
+ mDSPathBuilder->QuadraticBezierTo(
+ transform.TransformPoint(
+ mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy))),
+ transform.TransformPoint(
+ mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
+ }
+ }
+
+ void BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, double aCp2y,
+ double aX, double aY) override {
+ EnsureWritablePath();
+
+ BezierTo(mozilla::gfx::Point(ToFloat(aCp1x), ToFloat(aCp1y)),
+ mozilla::gfx::Point(ToFloat(aCp2x), ToFloat(aCp2y)),
+ mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
+ }
+
+ void ArcTo(double aX1, double aY1, double aX2, double aY2, double aRadius,
+ mozilla::ErrorResult& aError) override;
+ void Rect(double aX, double aY, double aW, double aH) override;
+ void RoundRect(
+ double aX, double aY, double aW, double aH,
+ const UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence&
+ aRadii,
+ ErrorResult& aError);
+ void Arc(double aX, double aY, double aRadius, double aStartAngle,
+ double aEndAngle, bool aAnticlockwise,
+ mozilla::ErrorResult& aError) override;
+ void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
+ double aRotation, double aStartAngle, double aEndAngle,
+ bool aAnticlockwise, ErrorResult& aError) override;
+
+ void GetFillRule(nsAString& aFillRule);
+ void SetFillRule(const nsAString& aFillRule);
+
+ void SetLineDash(const Sequence<double>& aSegments,
+ mozilla::ErrorResult& aRv) override;
+ void GetLineDash(nsTArray<double>& aSegments) const override;
+
+ void SetLineDashOffset(double aOffset) override;
+ double LineDashOffset() const override;
+
+ bool ImageSmoothingEnabled() override {
+ return CurrentState().imageSmoothingEnabled;
+ }
+
+ void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) override {
+ if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) {
+ CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
+ }
+ }
+
+ void DrawWindow(nsGlobalWindowInner& aWindow, double aX, double aY, double aW,
+ double aH, const nsACString& aBgColor, uint32_t aFlags,
+ nsIPrincipal& aSubjectPrincipal,
+ mozilla::ErrorResult& aError);
+
+ // Eventually this should be deprecated. Keeping for now to keep the binding
+ // functional.
+ void Demote();
+
+ nsresult Redraw();
+
+ gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
+ virtual int32_t GetWidth() override { return GetSize().width; }
+ virtual int32_t GetHeight() override { return GetSize().height; }
+
+ // nsICanvasRenderingContextInternal
+ /**
+ * Gets the pres shell from either the canvas element or the doc shell
+ */
+ PresShell* GetPresShell() final;
+ void Initialize() override;
+ NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
+ NS_IMETHOD InitializeWithDrawTarget(
+ nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) override;
+
+ NS_IMETHOD GetInputStream(const char* aMimeType,
+ const nsAString& aEncoderOptions,
+ nsIInputStream** aStream) override;
+
+ already_AddRefed<mozilla::gfx::SourceSurface> GetOptimizedSnapshot(
+ mozilla::gfx::DrawTarget* aTarget, gfxAlphaType* aOutAlphaType) override;
+
+ already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(
+ gfxAlphaType* aOutAlphaType = nullptr) override {
+ return GetOptimizedSnapshot(nullptr, aOutAlphaType);
+ }
+
+ virtual void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override;
+ bool GetIsOpaque() override { return mOpaque; }
+ void ResetBitmap(bool aFreeBuffer);
+ void ResetBitmap() override { ResetBitmap(true); }
+
+ bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
+ WebRenderCanvasData* aCanvasData) override;
+
+ bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
+ CanvasRenderer* aRenderer) override;
+ void MarkContextClean() override;
+ void MarkContextCleanForFrameCapture() override {
+ mFrameCaptureState = FrameCaptureState::CLEAN;
+ }
+ Watchable<FrameCaptureState>* GetFrameCaptureState() override {
+ return &mFrameCaptureState;
+ }
+ // this rect is in canvas device space
+ void Redraw(const mozilla::gfx::Rect& aR);
+ NS_IMETHOD Redraw(const gfxRect& aR) override {
+ Redraw(ToRect(aR));
+ return NS_OK;
+ }
+ NS_IMETHOD SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions,
+ ErrorResult& aRvForDictionaryInit) override;
+
+ /**
+ * An abstract base class to be implemented by callers wanting to be notified
+ * that a refresh has occurred. Callers must ensure an observer is removed
+ * before it is destroyed.
+ */
+ virtual void DidRefresh() override;
+
+ // this rect is in mTarget's current user space
+ void RedrawUser(const gfxRect& aR);
+
+ // nsISupports interface + CC
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS(
+ CanvasRenderingContext2D)
+
+ enum class CanvasMultiGetterType : uint8_t {
+ STRING = 0,
+ PATTERN = 1,
+ GRADIENT = 2
+ };
+
+ enum class Style : uint8_t { STROKE = 0, FILL, MAX };
+
+ void LineTo(const mozilla::gfx::Point& aPoint) {
+ if (mPathBuilder) {
+ mPathBuilder->LineTo(aPoint);
+ } else {
+ mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(aPoint));
+ }
+ }
+
+ void BezierTo(const mozilla::gfx::Point& aCP1,
+ const mozilla::gfx::Point& aCP2,
+ const mozilla::gfx::Point& aCP3) {
+ if (mPathBuilder) {
+ mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
+ } else {
+ mozilla::gfx::Matrix transform = mTarget->GetTransform();
+ mDSPathBuilder->BezierTo(transform.TransformPoint(aCP1),
+ transform.TransformPoint(aCP2),
+ transform.TransformPoint(aCP3));
+ }
+ }
+
+ virtual UniquePtr<uint8_t[]> GetImageBuffer(
+ int32_t* out_format, gfx::IntSize* out_imageSize) override;
+
+ virtual void OnShutdown();
+
+ /**
+ * Update CurrentState().filter with the filter description for
+ * CurrentState().filterChain.
+ * Flushes the PresShell, so the world can change if you call this function.
+ */
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void UpdateFilter();
+
+ protected:
+ /**
+ * Helper to parse a value for the letterSpacing or wordSpacing attribute.
+ * If successful, returns the result in aValue, and the whitespace-normalized
+ * value string in aNormalized; if unsuccessful these are left untouched.
+ */
+ void ParseSpacing(const nsACString& aSpacing, float* aValue,
+ nsACString& aNormalized);
+
+ already_AddRefed<const ComputedStyle> ResolveStyleForProperty(
+ nsCSSPropertyID aProperty, const nsACString& aValue);
+
+ nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
+ uint32_t aWidth, uint32_t aHeight,
+ Maybe<nsIPrincipal*> aSubjectPrincipal,
+ JSObject** aRetval);
+
+ void PutImageData_explicit(int32_t aX, int32_t aY, ImageData&,
+ bool aHasDirtyRect, int32_t aDirtyX,
+ int32_t aDirtyY, int32_t aDirtyWidth,
+ int32_t aDirtyHeight, ErrorResult&);
+
+ bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
+ gfx::DrawTarget& aTarget, gfx::IntRect aCopyRect);
+
+ /**
+ * Internal method to complete initialisation, expects mTarget to have been
+ * set
+ */
+ nsresult Initialize(int32_t aWidth, int32_t aHeight);
+
+ nsresult InitializeWithTarget(mozilla::gfx::DrawTarget* aSurface,
+ int32_t aWidth, int32_t aHeight);
+
+ /**
+ * The number of living nsCanvasRenderingContexts. When this goes down to
+ * 0, we free the premultiply and unpremultiply tables, if they exist.
+ */
+ static MOZ_THREAD_LOCAL(uintptr_t) sNumLivingContexts;
+
+ static MOZ_THREAD_LOCAL(mozilla::gfx::DrawTarget*) sErrorTarget;
+
+ void SetTransformInternal(const mozilla::gfx::Matrix& aTransform);
+
+ // Some helpers. Doesn't modify a color on failure.
+ void SetStyleFromUnion(
+ const UTF8StringOrCanvasGradientOrCanvasPattern& aValue,
+ Style aWhichStyle);
+ void SetStyleFromString(const nsACString& aStr, Style aWhichStyle);
+
+ void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle) {
+ CurrentState().SetGradientStyle(aWhichStyle, &aGradient);
+ }
+
+ void SetStyleFromPattern(CanvasPattern& aPattern, Style aWhichStyle) {
+ CurrentState().SetPatternStyle(aWhichStyle, &aPattern);
+ }
+
+ void GetStyleAsUnion(OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue,
+ Style aWhichStyle);
+
+ static void StyleColorToString(const nscolor& aColor, nsACString& aStr);
+
+ // Returns whether a filter was successfully parsed.
+ bool ParseFilter(const nsACString& aString,
+ StyleOwnedSlice<StyleFilter>& aFilterChain,
+ ErrorResult& aError);
+
+ // Returns whether the font was successfully updated.
+ bool SetFontInternal(const nsACString& aFont, mozilla::ErrorResult& aError);
+
+ // Helper for SetFontInternal in the case where we have no PresShell.
+ bool SetFontInternalDisconnected(const nsACString& aFont,
+ mozilla::ErrorResult& aError);
+
+ // Update the resolved values for letterSpacing and wordSpacing, if present,
+ // following a potential change to font-relative dimensions.
+ void UpdateSpacing();
+
+ // Clears the target and updates mOpaque based on mOpaqueAttrValue and
+ // mContextAttributesHasAlpha.
+ void UpdateIsOpaque();
+
+ /**
+ * Creates the error target, if it doesn't exist
+ */
+ static void EnsureErrorTarget();
+
+ /* This function ensures there is a writable pathbuilder available, this
+ * pathbuilder may be working in user space or in device space or
+ * device space.
+ * After calling this function mPathTransformWillUpdate will be false
+ */
+ void EnsureWritablePath();
+
+ // Ensures a path in UserSpace is available.
+ void EnsureUserSpacePath(
+ const CanvasWindingRule& aWinding = CanvasWindingRule::Nonzero);
+
+ /**
+ * Needs to be called before updating the transform. This makes a call to
+ * EnsureTarget() so you don't have to.
+ */
+ void TransformWillUpdate();
+
+ // Report the fillRule has changed.
+ void FillRuleChanged();
+
+ /**
+ * Create the backing surfacing, if it doesn't exist. If there is an error
+ * in creating the target then it will put sErrorTarget in place. If there
+ * is in turn an error in creating the sErrorTarget then they would both
+ * be null so IsTargetValid() would still return null.
+ *
+ * Returns true on success.
+ */
+ bool EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
+ bool aWillClear = false);
+ // Attempt to borrow a new target from an existing buffer provider.
+ bool BorrowTarget(const gfx::IntRect& aPersistedRect, bool aNeedsClear);
+
+ void RestoreClipsAndTransformToTarget();
+
+ bool TryAcceleratedTarget(
+ RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+
+ bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+
+ bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+
+ void RegisterAllocation();
+
+ void SetInitialState();
+
+ void SetErrorState();
+
+ /**
+ * This method is run at the end of the event-loop spin where
+ * ScheduleStableStateCallback was called.
+ *
+ * We use it to unlock resources that need to be locked while drawing.
+ */
+ void OnStableState();
+
+ /**
+ * Cf. OnStableState.
+ */
+ void ScheduleStableStateCallback();
+
+ /**
+ * Disposes an old target and prepares to lazily create a new target.
+ *
+ * Parameters are the new dimensions to be used, or if either is negative,
+ * existing dimensions will be left unchanged.
+ */
+ void ClearTarget(int32_t aWidth = -1, int32_t aHeight = -1);
+
+ /*
+ * Returns the target to the buffer provider. i.e. this will queue a frame for
+ * rendering.
+ */
+ void ReturnTarget(bool aForceReset = false);
+
+ /**
+ * Check if the target is valid after calling EnsureTarget.
+ */
+ bool IsTargetValid() const {
+ return !!mTarget && mTarget != sErrorTarget.get();
+ }
+
+ /**
+ * Returns the surface format this canvas should be allocated using. Takes
+ * into account mOpaque, platform requirements, etc.
+ */
+ mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
+
+ /**
+ * Returns true if we know for sure that the pattern for a given style is
+ * opaque. Usefull to know if we can discard the content below in certain
+ * situations. Optionally checks if the pattern is a color pattern.
+ */
+ bool PatternIsOpaque(Style aStyle, bool* aIsColor = nullptr) const;
+
+ SurfaceFromElementResult CachedSurfaceFromElement(Element* aElement);
+
+ void DrawImage(const CanvasImageSource& aImgElt, double aSx, double aSy,
+ double aSw, double aSh, double aDx, double aDy, double aDw,
+ double aDh, uint8_t aOptional_argc,
+ mozilla::ErrorResult& aError);
+
+ void DrawDirectlyToCanvas(const DirectDrawInfo& aImage,
+ mozilla::gfx::Rect* aBounds,
+ mozilla::gfx::Rect aDest, mozilla::gfx::Rect aSrc,
+ gfx::IntSize aImgSize);
+
+ nsCString& GetFont() {
+ /* will initilize the value if not set, else does nothing */
+ GetCurrentFontStyle();
+
+ return CurrentState().font;
+ }
+
+ // Member vars
+ int32_t mWidth, mHeight;
+
+ // This is true when the canvas is valid, but of zero size, this requires
+ // specific behavior on some operations.
+ bool mZero;
+
+ // The two ways to set the opaqueness of the canvas.
+ // mOpaqueAttrValue: Whether the <canvas> element has the moz-opaque attribute
+ // set. Can change during the lifetime of the context. Non-standard, should
+ // hopefully go away soon.
+ // mContextAttributesHasAlpha: The standard way of setting canvas opaqueness.
+ // Set at context initialization time and never changes.
+ bool mOpaqueAttrValue;
+ bool mContextAttributesHasAlpha;
+
+ // Determines the context's opaqueness. Is computed from mOpaqueAttrValue and
+ // mContextAttributesHasAlpha in UpdateIsOpaque().
+ bool mOpaque;
+
+ // This is true when the next time our layer is retrieved we need to
+ // recreate it (i.e. our backing surface changed)
+ bool mResetLayer;
+ // This is needed for drawing in drawAsyncXULElement
+ bool mIPC;
+
+ bool mHasPendingStableStateCallback;
+
+ // If mCanvasElement is not provided, then a docshell is
+ nsCOMPtr<nsIDocShell> mDocShell;
+
+ // This is created lazily so it is necessary to call EnsureTarget before
+ // accessing it. In the event of an error it will be equal to
+ // sErrorTarget.
+ RefPtr<mozilla::gfx::DrawTarget> mTarget;
+
+ RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
+ bool mBufferNeedsClear = false;
+
+ // Whether we should try to create an accelerated buffer provider.
+ bool mAllowAcceleration = true;
+ // Whether the application expects to use operations that perform poorly with
+ // acceleration.
+ bool mWillReadFrequently = false;
+
+ RefPtr<CanvasShutdownObserver> mShutdownObserver;
+ virtual void AddShutdownObserver();
+ virtual void RemoveShutdownObserver();
+ virtual bool AlreadyShutDown() const { return !mShutdownObserver; }
+
+ /**
+ * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
+ * Redraw is called, reset to false when Render is called.
+ */
+ bool mIsEntireFrameInvalid;
+ /**
+ * When this is set, the first call to Redraw(gfxRect) should set
+ * mIsEntireFrameInvalid since we expect it will be followed by
+ * many more Redraw calls.
+ */
+ bool mPredictManyRedrawCalls;
+
+ /**
+ * Flag to avoid unnecessary surface copies to FrameCaptureListeners in the
+ * case when the canvas is not currently being drawn into and not rendered
+ * but canvas capturing is still ongoing.
+ */
+ Watchable<FrameCaptureState> mFrameCaptureState;
+
+ /**
+ * We also have a device space pathbuilder. The reason for this is as
+ * follows, when a path is being built, but the transform changes, we
+ * can no longer keep a single path in userspace, considering there's
+ * several 'user spaces' now. We therefore transform the current path
+ * into device space, and add all operations to this path in device
+ * space.
+ *
+ * When then finally executing a render, the Azure drawing API expects
+ * the path to be in userspace. We could then set an identity transform
+ * on the DrawTarget and do all drawing in device space. This is
+ * undesirable because it requires transforming patterns, gradients,
+ * clips, etc. into device space and it would not work for stroking.
+ * What we do instead is convert the path back to user space when it is
+ * drawn, and draw it with the current transform. This makes all drawing
+ * occur correctly.
+ *
+ * There's never both a device space path builder and a user space path
+ * builder present at the same time. There is also never a path and a
+ * path builder present at the same time. When writing proceeds on an
+ * existing path the Path is cleared and a new builder is created.
+ *
+ * mPath is always in user-space.
+ */
+ RefPtr<mozilla::gfx::Path> mPath;
+ RefPtr<mozilla::gfx::PathBuilder> mDSPathBuilder;
+ RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
+ bool mPathTransformWillUpdate;
+ mozilla::gfx::Matrix mPathToDS;
+
+ /**
+ * Number of times we've invalidated before calling redraw
+ */
+ uint32_t mInvalidateCount;
+ static const uint32_t kCanvasMaxInvalidateCount = 100;
+
+ mozilla::intl::Bidi mBidiEngine;
+
+ /**
+ * Returns true if a shadow should be drawn along with a
+ * drawing operation.
+ */
+ bool NeedToDrawShadow() {
+ const ContextState& state = CurrentState();
+
+ // The spec says we should not draw shadows if the operator is OVER.
+ // If it's over and the alpha value is zero, nothing needs to be drawn.
+ return NS_GET_A(state.shadowColor) != 0 &&
+ (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f ||
+ state.shadowOffset.y != 0.f);
+ }
+
+ /**
+ * Returns true if the result of a drawing operation should be
+ * drawn with a filter.
+ */
+ bool NeedToApplyFilter() {
+ return EnsureUpdatedFilter().mPrimitives.Length() > 0;
+ }
+
+ /**
+ * Calls UpdateFilter if the canvas's WriteOnly state has changed between the
+ * last call to UpdateFilter and now.
+ */
+ const gfx::FilterDescription& EnsureUpdatedFilter() {
+ bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly();
+ if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
+ UpdateFilter();
+ EnsureTarget();
+ }
+ MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
+ return CurrentState().filter;
+ }
+
+ bool NeedToCalculateBounds() {
+ return NeedToDrawShadow() || NeedToApplyFilter();
+ }
+
+ // text
+
+ protected:
+ enum class TextAlign : uint8_t { START, END, LEFT, RIGHT, CENTER };
+
+ enum class TextBaseline : uint8_t {
+ TOP,
+ HANGING,
+ MIDDLE,
+ ALPHABETIC,
+ IDEOGRAPHIC,
+ BOTTOM
+ };
+
+ enum class TextDrawOperation : uint8_t { FILL, STROKE, MEASURE };
+
+ enum class TextDirection : uint8_t { LTR, RTL, INHERIT };
+
+ // Match values from the style system, so we don't have to bother mapping
+ // between them when setting up an nsFont.
+ enum class FontKerning : uint8_t {
+ AUTO = NS_FONT_KERNING_AUTO,
+ NORMAL = NS_FONT_KERNING_NORMAL,
+ NONE = NS_FONT_KERNING_NONE
+ };
+
+ protected:
+ gfxFontGroup* GetCurrentFontStyle();
+
+ /**
+ * Implementation of the fillText, strokeText, and measure functions with
+ * the operation abstracted to a flag.
+ * Returns a TextMetrics object _only_ if the operation is measure;
+ * drawing operations (fill or stroke) always return nullptr.
+ */
+ TextMetrics* DrawOrMeasureText(const nsAString& aText, float aX, float aY,
+ const Optional<double>& aMaxWidth,
+ TextDrawOperation aOp, ErrorResult& aError);
+
+ // A clip or a transform, recorded and restored in order.
+ struct ClipState {
+ explicit ClipState(mozilla::gfx::Path* aClip) : clip(aClip) {}
+
+ explicit ClipState(const mozilla::gfx::Matrix& aTransform)
+ : transform(aTransform) {}
+
+ bool IsClip() const { return !!clip; }
+
+ RefPtr<mozilla::gfx::Path> clip;
+ mozilla::gfx::Matrix transform;
+ };
+
+ // state stack handling
+ class ContextState {
+ public:
+ ContextState();
+ ContextState(const ContextState& aOther);
+ ~ContextState();
+
+ void SetColorStyle(Style aWhichStyle, nscolor aColor);
+ void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat);
+ void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad);
+
+ /**
+ * returns true iff the given style is a solid color.
+ */
+ bool StyleIsColor(Style aWhichStyle) const {
+ return !(patternStyles[aWhichStyle] || gradientStyles[aWhichStyle]);
+ }
+
+ int32_t ShadowBlurRadius() const {
+ static const gfxFloat GAUSSIAN_SCALE_FACTOR =
+ (3 * sqrt(2 * M_PI) / 4) * 1.5;
+ return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
+ }
+
+ mozilla::gfx::Float ShadowBlurSigma() const {
+ return std::min(SIGMA_MAX, shadowBlur / 2.0f);
+ }
+
+ ElementOrArray<ClipState> clipsAndTransforms;
+
+ RefPtr<gfxFontGroup> fontGroup;
+ RefPtr<nsAtom> fontLanguage;
+ nsFont fontFont;
+
+ EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
+ EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
+ EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
+
+ nsCString font;
+ TextAlign textAlign = TextAlign::START;
+ TextBaseline textBaseline = TextBaseline::ALPHABETIC;
+ TextDirection textDirection = TextDirection::INHERIT;
+ FontKerning fontKerning = FontKerning::AUTO;
+
+ gfx::Float letterSpacing = 0.0f;
+ gfx::Float wordSpacing = 0.0f;
+ nsCString letterSpacingStr;
+ nsCString wordSpacingStr;
+
+ nscolor shadowColor = 0;
+
+ mozilla::gfx::Matrix transform;
+ mozilla::gfx::Point shadowOffset;
+ mozilla::gfx::Float lineWidth = 1.0f;
+ mozilla::gfx::Float miterLimit = 10.0f;
+ mozilla::gfx::Float globalAlpha = 1.0f;
+ mozilla::gfx::Float shadowBlur = 0.0f;
+
+ nsTArray<mozilla::gfx::Float> dash;
+ mozilla::gfx::Float dashOffset = 0.0f;
+
+ mozilla::gfx::CompositionOp op = mozilla::gfx::CompositionOp::OP_OVER;
+ mozilla::gfx::FillRule fillRule = mozilla::gfx::FillRule::FILL_WINDING;
+ mozilla::gfx::CapStyle lineCap = mozilla::gfx::CapStyle::BUTT;
+ mozilla::gfx::JoinStyle lineJoin = mozilla::gfx::JoinStyle::MITER_OR_BEVEL;
+
+ nsCString filterString{"none"};
+ StyleOwnedSlice<StyleFilter> filterChain;
+ // RAII object that we obtain when we start to observer SVG filter elements
+ // for rendering changes. When released we stop observing the SVG elements.
+ nsCOMPtr<nsISupports> autoSVGFiltersObserver;
+ mozilla::gfx::FilterDescription filter;
+ nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
+
+ // This keeps track of whether the canvas was "tainted" or not when
+ // we last used a filter. This is a security measure, whereby the
+ // canvas is flipped to write-only if a cross-origin image is drawn to it.
+ // This is to stop bad actors from reading back data they shouldn't have
+ // access to.
+ //
+ // This also limits what filters we can apply to the context; in particular
+ // feDisplacementMap is restricted.
+ //
+ // We keep track of this to ensure that if this gets out of sync with the
+ // tainted state of the canvas itself, we update our filters accordingly.
+ bool filterSourceGraphicTainted = false;
+
+ bool imageSmoothingEnabled = true;
+ bool fontExplicitLanguage = false;
+ };
+
+ AutoTArray<ContextState, 3> mStyleStack;
+
+ inline ContextState& CurrentState() {
+ return mStyleStack[mStyleStack.Length() - 1];
+ }
+
+ inline const ContextState& CurrentState() const {
+ return mStyleStack[mStyleStack.Length() - 1];
+ }
+
+ struct FontStyleCacheKey {
+ FontStyleCacheKey() = default;
+ FontStyleCacheKey(const nsACString& aFont, uint64_t aGeneration)
+ : mFont(aFont), mGeneration(aGeneration) {}
+ nsCString mFont;
+ uint64_t mGeneration = 0;
+ };
+
+ struct FontStyleData {
+ FontStyleCacheKey mKey;
+ nsCString mUsedFont;
+ RefPtr<const ComputedStyle> mStyle;
+ };
+
+ class FontStyleCache
+ : public MruCache<FontStyleCacheKey, FontStyleData, FontStyleCache> {
+ public:
+ static HashNumber Hash(const FontStyleCacheKey& aKey) {
+ HashNumber hash = HashString(aKey.mFont);
+ return AddToHash(hash, aKey.mGeneration);
+ }
+ static bool Match(const FontStyleCacheKey& aKey,
+ const FontStyleData& aVal) {
+ return aVal.mKey.mGeneration == aKey.mGeneration &&
+ aVal.mKey.mFont == aKey.mFont;
+ }
+ };
+
+ FontStyleCache mFontStyleCache;
+
+ struct ColorStyleCacheEntry {
+ nsCString mKey;
+ Maybe<nscolor> mColor;
+ bool mWasCurrentColor = false;
+ };
+ class ColorStyleCache
+ : public MruCache<nsACString, ColorStyleCacheEntry, ColorStyleCache> {
+ public:
+ static HashNumber Hash(const nsACString& aKey) { return HashString(aKey); }
+ static bool Match(const nsACString& aKey,
+ const ColorStyleCacheEntry& aVal) {
+ return aVal.mKey == aKey;
+ }
+ };
+ ColorStyleCache mColorStyleCache;
+
+ ColorStyleCacheEntry ParseColorSlow(const nsACString&);
+
+ friend class CanvasGeneralPattern;
+ friend class AdjustedTarget;
+ friend class AdjustedTargetForShadow;
+ friend class AdjustedTargetForFilter;
+
+ // other helpers
+ void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel);
+
+ friend struct CanvasBidiProcessor;
+ friend class CanvasDrawObserver;
+ friend class ImageBitmap;
+
+ void SetWriteOnly();
+
+ bool IsWriteOnly() const { return mWriteOnly; }
+
+ bool mWriteOnly;
+ bool mClipsNeedConverting = false;
+
+ virtual void AddZoneWaitingForGC();
+ virtual void AddAssociatedMemory();
+ virtual void RemoveAssociatedMemory();
+};
+
+size_t BindingJSObjectMallocBytes(CanvasRenderingContext2D* aContext);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* CanvasRenderingContext2D_h */