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.h1156
1 files changed, 1156 insertions, 0 deletions
diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h
new file mode 100644
index 000000000..c3ee3bdcb
--- /dev/null
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -0,0 +1,1156 @@
+/* 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 "mozilla/Attributes.h"
+#include <vector>
+#include "nsIDOMCanvasRenderingContext2D.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "mozilla/RefPtr.h"
+#include "nsColor.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/dom/HTMLVideoElement.h"
+#include "gfxTextRun.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/CanvasGradient.h"
+#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
+#include "mozilla/dom/CanvasPattern.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/UniquePtr.h"
+#include "gfx2DGlue.h"
+#include "imgIEncoder.h"
+#include "nsLayoutUtils.h"
+#include "mozilla/EnumeratedArray.h"
+#include "FilterSupport.h"
+#include "nsSVGEffects.h"
+#include "Layers.h"
+
+class nsGlobalWindow;
+class nsXULElement;
+
+namespace mozilla {
+namespace gl {
+class SourceSurface;
+} // namespace gl
+
+namespace dom {
+class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
+typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource;
+class ImageData;
+class StringOrCanvasGradientOrCanvasPattern;
+class OwningStringOrCanvasGradientOrCanvasPattern;
+class TextMetrics;
+class CanvasFilterChainObserver;
+class CanvasPath;
+
+extern const mozilla::gfx::Float SIGMA_MAX;
+
+template<typename T> class Optional;
+
+struct CanvasBidiProcessor;
+class CanvasRenderingContext2DUserData;
+class CanvasDrawObserver;
+class CanvasShutdownObserver;
+
+/**
+ ** CanvasRenderingContext2D
+ **/
+class CanvasRenderingContext2D final :
+ public nsICanvasRenderingContextInternal,
+ public nsWrapperCache
+{
+ 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 Save();
+ void Restore();
+ void Scale(double aX, double aY, mozilla::ErrorResult& aError);
+ void Rotate(double aAngle, mozilla::ErrorResult& aError);
+ void Translate(double aX, double aY, mozilla::ErrorResult& aError);
+ void Transform(double aM11, double aM12, double aM21, double aM22, double aDx,
+ double aDy, mozilla::ErrorResult& aError);
+ void SetTransform(double aM11, double aM12, double aM21, double aM22, double aDx,
+ double aDy, mozilla::ErrorResult& aError);
+ void ResetTransform(mozilla::ErrorResult& aError);
+
+ double GlobalAlpha()
+ {
+ return CurrentState().globalAlpha;
+ }
+
+ // Useful for silencing cast warnings
+ static mozilla::gfx::Float ToFloat(double aValue) { return mozilla::gfx::Float(aValue); }
+
+ void SetGlobalAlpha(double aGlobalAlpha)
+ {
+ if (aGlobalAlpha >= 0.0 && aGlobalAlpha <= 1.0) {
+ CurrentState().globalAlpha = ToFloat(aGlobalAlpha);
+ }
+ }
+
+ void GetGlobalCompositeOperation(nsAString& aOp, mozilla::ErrorResult& aError);
+ void SetGlobalCompositeOperation(const nsAString& aOp,
+ mozilla::ErrorResult& aError);
+
+ void GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue)
+ {
+ GetStyleAsUnion(aValue, Style::STROKE);
+ }
+
+ void SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& aValue)
+ {
+ SetStyleFromUnion(aValue, Style::STROKE);
+ }
+
+ void GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue)
+ {
+ GetStyleAsUnion(aValue, Style::FILL);
+ }
+
+ void SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& aValue)
+ {
+ SetStyleFromUnion(aValue, Style::FILL);
+ }
+
+ already_AddRefed<CanvasGradient>
+ CreateLinearGradient(double aX0, double aY0, double aX1, double aY1);
+ already_AddRefed<CanvasGradient>
+ CreateRadialGradient(double aX0, double aY0, double aR0, double aX1, double aY1,
+ double aR1, ErrorResult& aError);
+ already_AddRefed<CanvasPattern>
+ CreatePattern(const CanvasImageSource& aElement,
+ const nsAString& aRepeat, ErrorResult& aError);
+
+ double ShadowOffsetX()
+ {
+ return CurrentState().shadowOffset.x;
+ }
+
+ void SetShadowOffsetX(double aShadowOffsetX)
+ {
+ CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX);
+ }
+
+ double ShadowOffsetY()
+ {
+ return CurrentState().shadowOffset.y;
+ }
+
+ void SetShadowOffsetY(double aShadowOffsetY)
+ {
+ CurrentState().shadowOffset.y = ToFloat(aShadowOffsetY);
+ }
+
+ double ShadowBlur()
+ {
+ return CurrentState().shadowBlur;
+ }
+
+ void SetShadowBlur(double aShadowBlur)
+ {
+ if (aShadowBlur >= 0.0) {
+ CurrentState().shadowBlur = ToFloat(aShadowBlur);
+ }
+ }
+
+ void GetShadowColor(nsAString& aShadowColor)
+ {
+ StyleColorToString(CurrentState().shadowColor, aShadowColor);
+ }
+
+ void GetFilter(nsAString& aFilter)
+ {
+ aFilter = CurrentState().filterString;
+ }
+
+ void SetShadowColor(const nsAString& aShadowColor);
+ void SetFilter(const nsAString& aFilter, mozilla::ErrorResult& aError);
+ void ClearRect(double aX, double aY, double aW, double aH);
+ void FillRect(double aX, double aY, double aW, double aH);
+ void StrokeRect(double aX, double aY, double aW, double aH);
+ 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(double aX, double aY, const CanvasWindingRule& aWinding);
+ bool IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding);
+ bool IsPointInStroke(double aX, double aY);
+ bool IsPointInStroke(const CanvasPath& aPath, double aX, double aY);
+ 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 AddHitRegion(const HitRegionOptions& aOptions, mozilla::ErrorResult& aError);
+ void RemoveHitRegion(const nsAString& aId);
+ void ClearHitRegions();
+
+ void DrawImage(const CanvasImageSource& aImage,
+ double aDx, double aDy, mozilla::ErrorResult& aError)
+ {
+ 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)
+ {
+ 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)
+ {
+ DrawImage(aImage, aSx, aSy, aSw, aSh, aDx, aDy, aDw, aDh, 6, aError);
+ }
+
+ already_AddRefed<ImageData>
+ CreateImageData(JSContext* aCx, double aSw, double aSh,
+ mozilla::ErrorResult& aError);
+ already_AddRefed<ImageData>
+ CreateImageData(JSContext* aCx, ImageData& aImagedata,
+ mozilla::ErrorResult& aError);
+ already_AddRefed<ImageData>
+ GetImageData(JSContext* aCx, double aSx, double aSy, double aSw, double aSh,
+ mozilla::ErrorResult& aError);
+ void PutImageData(ImageData& aImageData,
+ double aDx, double aDy, mozilla::ErrorResult& aError);
+ void PutImageData(ImageData& aImageData,
+ double aDx, double aDy, double aDirtyX, double aDirtyY,
+ double aDirtyWidth, double aDirtyHeight,
+ mozilla::ErrorResult& aError);
+
+ double LineWidth()
+ {
+ return CurrentState().lineWidth;
+ }
+
+ void SetLineWidth(double aWidth)
+ {
+ if (aWidth > 0.0) {
+ CurrentState().lineWidth = ToFloat(aWidth);
+ }
+ }
+ void GetLineCap(nsAString& aLinecapStyle);
+ void SetLineCap(const nsAString& aLinecapStyle);
+ void GetLineJoin(nsAString& aLinejoinStyle, mozilla::ErrorResult& aError);
+ void SetLineJoin(const nsAString& aLinejoinStyle);
+
+ double MiterLimit()
+ {
+ return CurrentState().miterLimit;
+ }
+
+ void SetMiterLimit(double aMiter)
+ {
+ if (aMiter > 0.0) {
+ CurrentState().miterLimit = ToFloat(aMiter);
+ }
+ }
+
+ void GetFont(nsAString& aFont)
+ {
+ aFont = GetFont();
+ }
+
+ void SetFont(const nsAString& aFont, mozilla::ErrorResult& aError);
+ void GetTextAlign(nsAString& aTextAlign);
+ void SetTextAlign(const nsAString& aTextAlign);
+ void GetTextBaseline(nsAString& aTextBaseline);
+ void SetTextBaseline(const nsAString& aTextBaseline);
+
+ void ClosePath()
+ {
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->Close();
+ } else {
+ mDSPathBuilder->Close();
+ }
+ }
+
+ void MoveTo(double aX, double aY)
+ {
+ 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)
+ {
+ EnsureWritablePath();
+
+ LineTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
+ }
+
+ void QuadraticCurveTo(double aCpx, double aCpy, double aX, double aY)
+ {
+ 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)
+ {
+ 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);
+ void Rect(double aX, double aY, double aW, double aH);
+ void Arc(double aX, double aY, double aRadius, double aStartAngle,
+ double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError);
+ void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
+ double aRotation, double aStartAngle, double aEndAngle,
+ bool aAnticlockwise, ErrorResult& aError);
+
+ void GetMozCurrentTransform(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aResult,
+ mozilla::ErrorResult& aError);
+ void SetMozCurrentTransform(JSContext* aCx,
+ JS::Handle<JSObject*> aCurrentTransform,
+ mozilla::ErrorResult& aError);
+ void GetMozCurrentTransformInverse(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aResult,
+ mozilla::ErrorResult& aError);
+ void SetMozCurrentTransformInverse(JSContext* aCx,
+ JS::Handle<JSObject*> aCurrentTransform,
+ mozilla::ErrorResult& aError);
+ void GetFillRule(nsAString& aFillRule);
+ void SetFillRule(const nsAString& aFillRule);
+
+ void SetLineDash(const Sequence<double>& aSegments,
+ mozilla::ErrorResult& aRv);
+ void GetLineDash(nsTArray<double>& aSegments) const;
+
+ void SetLineDashOffset(double aOffset);
+ double LineDashOffset() const;
+
+ void GetMozTextStyle(nsAString& aMozTextStyle)
+ {
+ GetFont(aMozTextStyle);
+ }
+
+ void SetMozTextStyle(const nsAString& aMozTextStyle,
+ mozilla::ErrorResult& aError)
+ {
+ SetFont(aMozTextStyle, aError);
+ }
+
+ bool ImageSmoothingEnabled()
+ {
+ return CurrentState().imageSmoothingEnabled;
+ }
+
+ void SetImageSmoothingEnabled(bool aImageSmoothingEnabled)
+ {
+ if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) {
+ CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
+ }
+ }
+
+ void DrawWindow(nsGlobalWindow& aWindow, double aX, double aY,
+ double aW, double aH,
+ const nsAString& aBgColor, uint32_t aFlags,
+ mozilla::ErrorResult& aError);
+ void AsyncDrawXULElement(nsXULElement& aElem, double aX, double aY, double aW,
+ double aH, const nsAString& aBgColor, uint32_t aFlags,
+ mozilla::ErrorResult& aError);
+
+ enum RenderingMode {
+ SoftwareBackendMode,
+ OpenGLBackendMode,
+ DefaultBackendMode
+ };
+
+ bool SwitchRenderingMode(RenderingMode aRenderingMode);
+
+ // Eventually this should be deprecated. Keeping for now to keep the binding functional.
+ void Demote();
+
+ nsresult Redraw();
+
+ virtual int32_t GetWidth() const override;
+ virtual int32_t GetHeight() const override;
+ gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
+
+ // nsICanvasRenderingContextInternal
+ /**
+ * Gets the pres shell from either the canvas element or the doc shell
+ */
+ virtual nsIPresShell *GetPresShell() override {
+ if (mCanvasElement) {
+ return mCanvasElement->OwnerDoc()->GetShell();
+ }
+ if (mDocShell) {
+ return mDocShell->GetPresShell();
+ }
+ return nullptr;
+ }
+ 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 char16_t* aEncoderOptions,
+ nsIInputStream** aStream) override;
+
+ already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override
+ {
+ EnsureTarget();
+ if (aPremultAlpha) {
+ *aPremultAlpha = true;
+ }
+ return mTarget->Snapshot();
+ }
+
+ NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
+ bool GetIsOpaque() override { return mOpaque; }
+ NS_IMETHOD Reset() override;
+ already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+ Layer* aOldLayer,
+ LayerManager* aManager,
+ bool aMirror = false) override;
+ virtual bool ShouldForceInactiveLayer(LayerManager* aManager) override;
+ void MarkContextClean() override;
+ void MarkContextCleanForFrameCapture() override;
+ bool IsContextCleanForFrameCapture() override;
+ NS_IMETHOD SetIsIPC(bool aIsIPC) override;
+ // 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_SCRIPT_HOLDER_CLASS(CanvasRenderingContext2D)
+
+ enum class CanvasMultiGetterType : uint8_t {
+ STRING = 0,
+ PATTERN = 1,
+ GRADIENT = 2
+ };
+
+ enum class Style : uint8_t {
+ STROKE = 0,
+ FILL,
+ MAX
+ };
+
+ nsINode* GetParentObject()
+ {
+ return mCanvasElement;
+ }
+
+ 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));
+ }
+ }
+
+ friend class CanvasRenderingContext2DUserData;
+
+ virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
+
+
+ // Given a point, return hit region ID if it exists
+ nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
+
+
+ // return true and fills in the bound rect if element has a hit region.
+ bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
+
+ void OnShutdown();
+
+ // Check the global setup, as well as the compositor type:
+ bool AllowOpenGLCanvas() const;
+
+protected:
+ nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
+ uint32_t aWidth, uint32_t aHeight,
+ JSObject** aRetval);
+
+ nsresult PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
+ dom::Uint8ClampedArray* aArray,
+ bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
+ int32_t aDirtyWidth, int32_t aDirtyHeight);
+
+ /**
+ * 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 uintptr_t sNumLivingContexts;
+
+ static mozilla::gfx::DrawTarget* sErrorTarget;
+
+ void SetTransformInternal(const mozilla::gfx::Matrix& aTransform);
+
+ // Some helpers. Doesn't modify a color on failure.
+ void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
+ Style aWhichStyle);
+ void SetStyleFromString(const nsAString& 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(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
+ Style aWhichStyle);
+
+ // Returns whether a color was successfully parsed.
+ bool ParseColor(const nsAString& aString, nscolor* aColor);
+
+ static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
+
+ // Returns whether a filter was successfully parsed.
+ bool ParseFilter(const nsAString& aString,
+ nsTArray<nsStyleFilter>& aFilterChain,
+ ErrorResult& aError);
+
+ // Returns whether the font was successfully updated.
+ bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
+
+
+ /**
+ * 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 the actual rendering mode being used by the created target.
+ */
+ RenderingMode EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
+ RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+
+ void RestoreClipsAndTransformToTarget();
+
+ bool TrySkiaGLTarget(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.
+ */
+ void ClearTarget();
+
+ /*
+ * 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 (sErrorTarget == nullptr || mTarget != sErrorTarget) && (mBufferProvider != nullptr || mTarget);
+ }
+
+ /**
+ * 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.
+ */
+ bool PatternIsOpaque(Style aStyle) const;
+
+ /**
+ * Update CurrentState().filter with the filter description for
+ * CurrentState().filterChain.
+ * Flushes the PresShell, so the world can change if you call this function.
+ */
+ void UpdateFilter();
+
+ nsLayoutUtils::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 nsLayoutUtils::DirectDrawInfo& aImage,
+ mozilla::gfx::Rect* aBounds,
+ mozilla::gfx::Rect aDest,
+ mozilla::gfx::Rect aSrc,
+ gfx::IntSize aImgSize);
+
+ nsString& GetFont()
+ {
+ /* will initilize the value if not set, else does nothing */
+ GetCurrentFontStyle();
+
+ return CurrentState().font;
+ }
+
+ // This function maintains a list of raw pointers to cycle-collected
+ // objects. We need to ensure that no entries persist beyond unlink,
+ // since the objects are logically destructed at that point.
+ static std::vector<CanvasRenderingContext2D*>& DemotableContexts();
+ static void DemoteOldestContextIfNecessary();
+
+ static void AddDemotableContext(CanvasRenderingContext2D* aContext);
+ static void RemoveDemotableContext(CanvasRenderingContext2D* aContext);
+
+ RenderingMode mRenderingMode;
+
+ layers::LayersBackend mCompositorBackend;
+
+ // 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;
+
+ 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;
+ // True if the current DrawTarget is using skia-gl, used so we can avoid
+ // requesting the DT from mBufferProvider to check.
+ bool mIsSkiaGL;
+
+ bool mHasPendingStableStateCallback;
+
+ nsTArray<CanvasRenderingContext2DUserData*> mUserDatas;
+
+ // 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;
+
+ uint32_t SkiaGLTex() const;
+
+ // This observes our draw calls at the beginning of the canvas
+ // lifetime and switches to software or GPU mode depending on
+ // what it thinks is best
+ CanvasDrawObserver* mDrawObserver;
+ void RemoveDrawObserver();
+
+ RefPtr<CanvasShutdownObserver> mShutdownObserver;
+ void RemoveShutdownObserver();
+ 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.
+ */
+ bool mIsCapturedFrameInvalid;
+
+ /**
+ * 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;
+
+ /**
+ * State information for hit regions
+ */
+ struct RegionInfo
+ {
+ nsString mId;
+ // fallback element for a11y
+ RefPtr<Element> mElement;
+ // Path of the hit region in the 2d context coordinate space (not user space)
+ RefPtr<gfx::Path> mPath;
+ };
+
+ nsTArray<RegionInfo> mHitRegionsOptions;
+
+ /**
+ * 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();
+ }
+
+ mozilla::gfx::CompositionOp UsedOperation()
+ {
+ if (NeedToDrawShadow() || NeedToApplyFilter()) {
+ // In this case the shadow or filter rendering will use the operator.
+ return mozilla::gfx::CompositionOp::OP_OVER;
+ }
+
+ return CurrentState().op;
+ }
+
+ // 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
+ };
+
+protected:
+ gfxFontGroup *GetCurrentFontStyle();
+
+ /**
+ * Implementation of the fillText, strokeText, and measure functions with
+ * the operation abstracted to a flag.
+ */
+ nsresult DrawOrMeasureText(const nsAString& aText,
+ float aX,
+ float aY,
+ const Optional<double>& aMaxWidth,
+ TextDrawOperation aOp,
+ float* aWidth);
+
+ bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
+
+ // 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() : textAlign(TextAlign::START),
+ textBaseline(TextBaseline::ALPHABETIC),
+ shadowColor(0),
+ lineWidth(1.0f),
+ miterLimit(10.0f),
+ globalAlpha(1.0f),
+ shadowBlur(0.0),
+ dashOffset(0.0f),
+ op(mozilla::gfx::CompositionOp::OP_OVER),
+ fillRule(mozilla::gfx::FillRule::FILL_WINDING),
+ lineCap(mozilla::gfx::CapStyle::BUTT),
+ lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
+ filterString(u"none"),
+ filterSourceGraphicTainted(false),
+ imageSmoothingEnabled(true),
+ fontExplicitLanguage(false)
+ { }
+
+ ContextState(const ContextState& aOther)
+ : fontGroup(aOther.fontGroup),
+ fontLanguage(aOther.fontLanguage),
+ fontFont(aOther.fontFont),
+ gradientStyles(aOther.gradientStyles),
+ patternStyles(aOther.patternStyles),
+ colorStyles(aOther.colorStyles),
+ font(aOther.font),
+ textAlign(aOther.textAlign),
+ textBaseline(aOther.textBaseline),
+ shadowColor(aOther.shadowColor),
+ transform(aOther.transform),
+ shadowOffset(aOther.shadowOffset),
+ lineWidth(aOther.lineWidth),
+ miterLimit(aOther.miterLimit),
+ globalAlpha(aOther.globalAlpha),
+ shadowBlur(aOther.shadowBlur),
+ dash(aOther.dash),
+ dashOffset(aOther.dashOffset),
+ op(aOther.op),
+ fillRule(aOther.fillRule),
+ lineCap(aOther.lineCap),
+ lineJoin(aOther.lineJoin),
+ filterString(aOther.filterString),
+ filterChain(aOther.filterChain),
+ filterChainObserver(aOther.filterChainObserver),
+ filter(aOther.filter),
+ filterAdditionalImages(aOther.filterAdditionalImages),
+ filterSourceGraphicTainted(aOther.filterSourceGraphicTainted),
+ imageSmoothingEnabled(aOther.imageSmoothingEnabled),
+ fontExplicitLanguage(aOther.fontExplicitLanguage)
+ { }
+
+ void SetColorStyle(Style aWhichStyle, nscolor aColor)
+ {
+ colorStyles[aWhichStyle] = aColor;
+ gradientStyles[aWhichStyle] = nullptr;
+ patternStyles[aWhichStyle] = nullptr;
+ }
+
+ void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat)
+ {
+ gradientStyles[aWhichStyle] = nullptr;
+ patternStyles[aWhichStyle] = aPat;
+ }
+
+ void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad)
+ {
+ gradientStyles[aWhichStyle] = aGrad;
+ patternStyles[aWhichStyle] = nullptr;
+ }
+
+ /**
+ * 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);
+ }
+
+ nsTArray<ClipState> clipsAndTransforms;
+
+ RefPtr<gfxFontGroup> fontGroup;
+ nsCOMPtr<nsIAtom> fontLanguage;
+ nsFont fontFont;
+
+ EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
+ EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
+ EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
+
+ nsString font;
+ TextAlign textAlign;
+ TextBaseline textBaseline;
+
+ nscolor shadowColor;
+
+ mozilla::gfx::Matrix transform;
+ mozilla::gfx::Point shadowOffset;
+ mozilla::gfx::Float lineWidth;
+ mozilla::gfx::Float miterLimit;
+ mozilla::gfx::Float globalAlpha;
+ mozilla::gfx::Float shadowBlur;
+ nsTArray<mozilla::gfx::Float> dash;
+ mozilla::gfx::Float dashOffset;
+
+ mozilla::gfx::CompositionOp op;
+ mozilla::gfx::FillRule fillRule;
+ mozilla::gfx::CapStyle lineCap;
+ mozilla::gfx::JoinStyle lineJoin;
+
+ nsString filterString;
+ nsTArray<nsStyleFilter> filterChain;
+ RefPtr<nsSVGFilterChainObserver> filterChainObserver;
+ 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;
+
+ bool imageSmoothingEnabled;
+ bool fontExplicitLanguage;
+ };
+
+ AutoTArray<ContextState, 3> mStyleStack;
+
+ inline ContextState& CurrentState() {
+ return mStyleStack[mStyleStack.Length() - 1];
+ }
+
+ inline const ContextState& CurrentState() const {
+ return mStyleStack[mStyleStack.Length() - 1];
+ }
+
+ friend class CanvasGeneralPattern;
+ friend class CanvasFilterChainObserver;
+ friend class AdjustedTarget;
+ friend class AdjustedTargetForShadow;
+ friend class AdjustedTargetForFilter;
+
+ // other helpers
+ void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel)
+ {
+ // If we don't have a canvas element, we just return something generic.
+ int32_t devPixel = 60;
+ int32_t cssPixel = 60;
+
+ nsIPresShell *ps = GetPresShell();
+ nsPresContext *pc;
+
+ if (!ps) goto FINISH;
+ pc = ps->GetPresContext();
+ if (!pc) goto FINISH;
+ devPixel = pc->AppUnitsPerDevPixel();
+ cssPixel = pc->AppUnitsPerCSSPixel();
+
+ FINISH:
+ if (aPerDevPixel)
+ *aPerDevPixel = devPixel;
+ if (aPerCSSPixel)
+ *aPerCSSPixel = cssPixel;
+ }
+
+ friend struct CanvasBidiProcessor;
+ friend class CanvasDrawObserver;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* CanvasRenderingContext2D_h */