1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #ifndef CanvasRenderingContext2D_h
6 #define CanvasRenderingContext2D_h
7 
8 #include <vector>
9 #include "mozilla/dom/BasicRenderingContext2D.h"
10 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
11 #include "mozilla/dom/HTMLCanvasElement.h"
12 #include "mozilla/gfx/Rect.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/EnumeratedArray.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/SurfaceFromElementResult.h"
18 #include "mozilla/UniquePtr.h"
19 #include "FilterDescription.h"
20 #include "gfx2DGlue.h"
21 #include "nsICanvasRenderingContextInternal.h"
22 #include "nsBidi.h"
23 #include "nsColor.h"
24 
25 class gfxFontGroup;
26 class nsGlobalWindowInner;
27 class nsXULElement;
28 
29 namespace mozilla {
30 class ErrorResult;
31 class PresShell;
32 
33 namespace gl {
34 class SourceSurface;
35 }  // namespace gl
36 
37 namespace layers {
38 class PersistentBufferProvider;
39 enum class LayersBackend : int8_t;
40 }  // namespace layers
41 
42 namespace dom {
43 class
44     HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
45 typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
46     CanvasImageSource;
47 class ImageBitmap;
48 class ImageData;
49 class UTF8StringOrCanvasGradientOrCanvasPattern;
50 class OwningUTF8StringOrCanvasGradientOrCanvasPattern;
51 class TextMetrics;
52 class CanvasGradient;
53 class CanvasPath;
54 class CanvasPattern;
55 
56 extern const mozilla::gfx::Float SIGMA_MAX;
57 
58 template <typename T>
59 class Optional;
60 
61 struct CanvasBidiProcessor;
62 class CanvasDrawObserver;
63 class CanvasShutdownObserver;
64 
65 class DOMMatrix;
66 class DOMMatrixReadOnly;
67 struct DOMMatrix2DInit;
68 
69 /**
70  ** CanvasRenderingContext2D
71  **/
72 class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
73                                        public nsWrapperCache,
74                                        public BasicRenderingContext2D {
75   virtual ~CanvasRenderingContext2D();
76 
77  public:
78   explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend);
79 
80   virtual JSObject* WrapObject(JSContext* aCx,
81                                JS::Handle<JSObject*> aGivenProto) override;
82 
GetCanvas()83   HTMLCanvasElement* GetCanvas() const {
84     if (!mCanvasElement || mCanvasElement->IsInNativeAnonymousSubtree()) {
85       return nullptr;
86     }
87 
88     // corresponds to changes to the old bindings made in bug 745025
89     return mCanvasElement->GetOriginalCanvas();
90   }
91 
92   void OnBeforePaintTransaction() override;
93   void OnDidPaintTransaction() override;
GetBufferProvider()94   layers::PersistentBufferProvider* GetBufferProvider() override {
95     return mBufferProvider;
96   }
97 
98   void Save() override;
99   void Restore() override;
100   void Scale(double aX, double aY, mozilla::ErrorResult& aError) override;
101   void Rotate(double aAngle, mozilla::ErrorResult& aError) override;
102   void Translate(double aX, double aY, mozilla::ErrorResult& aError) override;
103   void Transform(double aM11, double aM12, double aM21, double aM22, double aDx,
104                  double aDy, mozilla::ErrorResult& aError) override;
105   already_AddRefed<DOMMatrix> GetTransform(
106       mozilla::ErrorResult& aError) override;
107   void SetTransform(double aM11, double aM12, double aM21, double aM22,
108                     double aDx, double aDy,
109                     mozilla::ErrorResult& aError) override;
110   void SetTransform(const DOMMatrix2DInit& aInit,
111                     mozilla::ErrorResult& aError) override;
112   void ResetTransform(mozilla::ErrorResult& aError) override;
113 
GlobalAlpha()114   double GlobalAlpha() override { return CurrentState().globalAlpha; }
115 
116   // Useful for silencing cast warnings
ToFloat(double aValue)117   static mozilla::gfx::Float ToFloat(double aValue) {
118     return mozilla::gfx::Float(aValue);
119   }
120 
SetGlobalAlpha(double aGlobalAlpha)121   void SetGlobalAlpha(double aGlobalAlpha) override {
122     if (aGlobalAlpha >= 0.0 && aGlobalAlpha <= 1.0) {
123       CurrentState().globalAlpha = ToFloat(aGlobalAlpha);
124     }
125   }
126 
127   void GetGlobalCompositeOperation(nsAString& aOp,
128                                    mozilla::ErrorResult& aError) override;
129   void SetGlobalCompositeOperation(const nsAString& aOp,
130                                    mozilla::ErrorResult& aError) override;
131 
GetStrokeStyle(OwningUTF8StringOrCanvasGradientOrCanvasPattern & aValue)132   void GetStrokeStyle(
133       OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
134     GetStyleAsUnion(aValue, Style::STROKE);
135   }
136 
SetStrokeStyle(const UTF8StringOrCanvasGradientOrCanvasPattern & aValue)137   void SetStrokeStyle(
138       const UTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
139     SetStyleFromUnion(aValue, Style::STROKE);
140   }
141 
GetFillStyle(OwningUTF8StringOrCanvasGradientOrCanvasPattern & aValue)142   void GetFillStyle(
143       OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
144     GetStyleAsUnion(aValue, Style::FILL);
145   }
146 
SetFillStyle(const UTF8StringOrCanvasGradientOrCanvasPattern & aValue)147   void SetFillStyle(
148       const UTF8StringOrCanvasGradientOrCanvasPattern& aValue) override {
149     SetStyleFromUnion(aValue, Style::FILL);
150   }
151 
152   already_AddRefed<CanvasGradient> CreateLinearGradient(double aX0, double aY0,
153                                                         double aX1,
154                                                         double aY1) override;
155   already_AddRefed<CanvasGradient> CreateRadialGradient(
156       double aX0, double aY0, double aR0, double aX1, double aY1, double aR1,
157       ErrorResult& aError) override;
158   already_AddRefed<CanvasGradient> CreateConicGradient(double aAngle,
159                                                        double aCx,
160                                                        double aCy) override;
161   already_AddRefed<CanvasPattern> CreatePattern(
162       const CanvasImageSource& aElement, const nsAString& aRepeat,
163       ErrorResult& aError) override;
164 
ShadowOffsetX()165   double ShadowOffsetX() override { return CurrentState().shadowOffset.x; }
166 
SetShadowOffsetX(double aShadowOffsetX)167   void SetShadowOffsetX(double aShadowOffsetX) override {
168     CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX);
169   }
170 
ShadowOffsetY()171   double ShadowOffsetY() override { return CurrentState().shadowOffset.y; }
172 
SetShadowOffsetY(double aShadowOffsetY)173   void SetShadowOffsetY(double aShadowOffsetY) override {
174     CurrentState().shadowOffset.y = ToFloat(aShadowOffsetY);
175   }
176 
ShadowBlur()177   double ShadowBlur() override { return CurrentState().shadowBlur; }
178 
SetShadowBlur(double aShadowBlur)179   void SetShadowBlur(double aShadowBlur) override {
180     if (aShadowBlur >= 0.0) {
181       CurrentState().shadowBlur = ToFloat(aShadowBlur);
182     }
183   }
184 
GetShadowColor(nsACString & aShadowColor)185   void GetShadowColor(nsACString& aShadowColor) override {
186     StyleColorToString(CurrentState().shadowColor, aShadowColor);
187   }
188 
GetFilter(nsACString & aFilter)189   void GetFilter(nsACString& aFilter) { aFilter = CurrentState().filterString; }
190 
191   void SetShadowColor(const nsACString& aShadowColor) override;
192   void SetFilter(const nsACString& aFilter, mozilla::ErrorResult& aError);
193   void ClearRect(double aX, double aY, double aW, double aH) override;
194   void FillRect(double aX, double aY, double aW, double aH) override;
195   void StrokeRect(double aX, double aY, double aW, double aH) override;
196   void BeginPath();
197   void Fill(const CanvasWindingRule& aWinding);
198   void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
199   void Stroke();
200   void Stroke(const CanvasPath& aPath);
201   void DrawFocusIfNeeded(mozilla::dom::Element& aElement, ErrorResult& aRv);
202   bool DrawCustomFocusRing(mozilla::dom::Element& aElement);
203   void Clip(const CanvasWindingRule& aWinding);
204   void Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
205   bool IsPointInPath(JSContext* aCx, double aX, double aY,
206                      const CanvasWindingRule& aWinding,
207                      nsIPrincipal& aSubjectPrincipal);
208   bool IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX,
209                      double aY, const CanvasWindingRule& aWinding,
210                      nsIPrincipal&);
211   bool IsPointInStroke(JSContext* aCx, double aX, double aY,
212                        nsIPrincipal& aSubjectPrincipal);
213   bool IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX,
214                        double aY, nsIPrincipal&);
215   void FillText(const nsAString& aText, double aX, double aY,
216                 const Optional<double>& aMaxWidth,
217                 mozilla::ErrorResult& aError);
218   void StrokeText(const nsAString& aText, double aX, double aY,
219                   const Optional<double>& aMaxWidth,
220                   mozilla::ErrorResult& aError);
221   TextMetrics* MeasureText(const nsAString& aRawText,
222                            mozilla::ErrorResult& aError);
223 
224   void AddHitRegion(const HitRegionOptions& aOptions,
225                     mozilla::ErrorResult& aError);
226   void RemoveHitRegion(const nsAString& aId);
227   void ClearHitRegions();
228 
DrawImage(const CanvasImageSource & aImage,double aDx,double aDy,mozilla::ErrorResult & aError)229   void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
230                  mozilla::ErrorResult& aError) override {
231     DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, 0.0, 0.0, 0, aError);
232   }
233 
DrawImage(const CanvasImageSource & aImage,double aDx,double aDy,double aDw,double aDh,mozilla::ErrorResult & aError)234   void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
235                  double aDw, double aDh,
236                  mozilla::ErrorResult& aError) override {
237     DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, aDw, aDh, 2, aError);
238   }
239 
DrawImage(const CanvasImageSource & aImage,double aSx,double aSy,double aSw,double aSh,double aDx,double aDy,double aDw,double aDh,mozilla::ErrorResult & aError)240   void DrawImage(const CanvasImageSource& aImage, double aSx, double aSy,
241                  double aSw, double aSh, double aDx, double aDy, double aDw,
242                  double aDh, mozilla::ErrorResult& aError) override {
243     DrawImage(aImage, aSx, aSy, aSw, aSh, aDx, aDy, aDw, aDh, 6, aError);
244   }
245 
246   already_AddRefed<ImageData> CreateImageData(JSContext*, int32_t aSw,
247                                               int32_t aSh, ErrorResult&);
248   already_AddRefed<ImageData> CreateImageData(JSContext*, ImageData&,
249                                               ErrorResult&);
250   already_AddRefed<ImageData> GetImageData(JSContext*, int32_t aSx, int32_t aSy,
251                                            int32_t aSw, int32_t aSh,
252                                            nsIPrincipal& aSubjectPrincipal,
253                                            ErrorResult&);
254   void PutImageData(ImageData&, int32_t aDx, int32_t aDy, ErrorResult&);
255   void PutImageData(ImageData&, int32_t aDx, int32_t aDy, int32_t aDirtyX,
256                     int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight,
257                     ErrorResult&);
258 
LineWidth()259   double LineWidth() override { return CurrentState().lineWidth; }
260 
SetLineWidth(double aWidth)261   void SetLineWidth(double aWidth) override {
262     if (aWidth > 0.0) {
263       CurrentState().lineWidth = ToFloat(aWidth);
264     }
265   }
266   void GetLineCap(nsAString& aLinecapStyle) override;
267   void SetLineCap(const nsAString& aLinecapStyle) override;
268   void GetLineJoin(nsAString& aLinejoinStyle,
269                    mozilla::ErrorResult& aError) override;
270   void SetLineJoin(const nsAString& aLinejoinStyle) override;
271 
MiterLimit()272   double MiterLimit() override { return CurrentState().miterLimit; }
273 
SetMiterLimit(double aMiter)274   void SetMiterLimit(double aMiter) override {
275     if (aMiter > 0.0) {
276       CurrentState().miterLimit = ToFloat(aMiter);
277     }
278   }
279 
GetFont(nsACString & aFont)280   void GetFont(nsACString& aFont) { aFont = GetFont(); }
281 
282   void SetFont(const nsACString& aFont, mozilla::ErrorResult& aError);
283   void GetTextAlign(nsAString& aTextAlign);
284   void SetTextAlign(const nsAString& aTextAlign);
285   void GetTextBaseline(nsAString& aTextBaseline);
286   void SetTextBaseline(const nsAString& aTextBaseline);
287 
ClosePath()288   void ClosePath() override {
289     EnsureWritablePath();
290 
291     if (mPathBuilder) {
292       mPathBuilder->Close();
293     } else {
294       mDSPathBuilder->Close();
295     }
296   }
297 
MoveTo(double aX,double aY)298   void MoveTo(double aX, double aY) override {
299     EnsureWritablePath();
300 
301     if (mPathBuilder) {
302       mPathBuilder->MoveTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
303     } else {
304       mDSPathBuilder->MoveTo(mTarget->GetTransform().TransformPoint(
305           mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
306     }
307   }
308 
LineTo(double aX,double aY)309   void LineTo(double aX, double aY) override {
310     EnsureWritablePath();
311 
312     LineTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
313   }
314 
QuadraticCurveTo(double aCpx,double aCpy,double aX,double aY)315   void QuadraticCurveTo(double aCpx, double aCpy, double aX,
316                         double aY) override {
317     EnsureWritablePath();
318 
319     if (mPathBuilder) {
320       mPathBuilder->QuadraticBezierTo(
321           mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy)),
322           mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
323     } else {
324       mozilla::gfx::Matrix transform = mTarget->GetTransform();
325       mDSPathBuilder->QuadraticBezierTo(
326           transform.TransformPoint(
327               mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy))),
328           transform.TransformPoint(
329               mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
330     }
331   }
332 
BezierCurveTo(double aCp1x,double aCp1y,double aCp2x,double aCp2y,double aX,double aY)333   void BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, double aCp2y,
334                      double aX, double aY) override {
335     EnsureWritablePath();
336 
337     BezierTo(mozilla::gfx::Point(ToFloat(aCp1x), ToFloat(aCp1y)),
338              mozilla::gfx::Point(ToFloat(aCp2x), ToFloat(aCp2y)),
339              mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
340   }
341 
342   void ArcTo(double aX1, double aY1, double aX2, double aY2, double aRadius,
343              mozilla::ErrorResult& aError) override;
344   void Rect(double aX, double aY, double aW, double aH) override;
345   void Arc(double aX, double aY, double aRadius, double aStartAngle,
346            double aEndAngle, bool aAnticlockwise,
347            mozilla::ErrorResult& aError) override;
348   void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
349                double aRotation, double aStartAngle, double aEndAngle,
350                bool aAnticlockwise, ErrorResult& aError) override;
351 
352   void GetMozCurrentTransform(JSContext* aCx,
353                               JS::MutableHandle<JSObject*> aResult,
354                               mozilla::ErrorResult& aError);
355   void SetMozCurrentTransform(JSContext* aCx,
356                               JS::Handle<JSObject*> aCurrentTransform,
357                               mozilla::ErrorResult& aError);
358   void GetMozCurrentTransformInverse(JSContext* aCx,
359                                      JS::MutableHandle<JSObject*> aResult,
360                                      mozilla::ErrorResult& aError);
361   void SetMozCurrentTransformInverse(JSContext* aCx,
362                                      JS::Handle<JSObject*> aCurrentTransform,
363                                      mozilla::ErrorResult& aError);
364   void GetFillRule(nsAString& aFillRule);
365   void SetFillRule(const nsAString& aFillRule);
366 
367   void SetLineDash(const Sequence<double>& aSegments,
368                    mozilla::ErrorResult& aRv) override;
369   void GetLineDash(nsTArray<double>& aSegments) const override;
370 
371   void SetLineDashOffset(double aOffset) override;
372   double LineDashOffset() const override;
373 
GetMozTextStyle(nsACString & aMozTextStyle)374   void GetMozTextStyle(nsACString& aMozTextStyle) { GetFont(aMozTextStyle); }
375 
SetMozTextStyle(const nsACString & aMozTextStyle,mozilla::ErrorResult & aError)376   void SetMozTextStyle(const nsACString& aMozTextStyle,
377                        mozilla::ErrorResult& aError) {
378     SetFont(aMozTextStyle, aError);
379   }
380 
ImageSmoothingEnabled()381   bool ImageSmoothingEnabled() override {
382     return CurrentState().imageSmoothingEnabled;
383   }
384 
SetImageSmoothingEnabled(bool aImageSmoothingEnabled)385   void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) override {
386     if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) {
387       CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
388     }
389   }
390 
391   void DrawWindow(nsGlobalWindowInner& aWindow, double aX, double aY, double aW,
392                   double aH, const nsACString& aBgColor, uint32_t aFlags,
393                   nsIPrincipal& aSubjectPrincipal,
394                   mozilla::ErrorResult& aError);
395 
396   // Eventually this should be deprecated. Keeping for now to keep the binding
397   // functional.
398   void Demote();
399 
400   nsresult Redraw();
401 
GetSize()402   gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
GetWidth()403   virtual int32_t GetWidth() override { return GetSize().width; }
GetHeight()404   virtual int32_t GetHeight() override { return GetSize().height; }
405 
406   // nsICanvasRenderingContextInternal
407   /**
408    * Gets the pres shell from either the canvas element or the doc shell
409    */
410   PresShell* GetPresShell() final;
411   NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
412   NS_IMETHOD InitializeWithDrawTarget(
413       nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) override;
414 
415   NS_IMETHOD GetInputStream(const char* aMimeType,
416                             const nsAString& aEncoderOptions,
417                             nsIInputStream** aStream) override;
418 
419   already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(
420       gfxAlphaType* aOutAlphaType = nullptr) override;
421 
422   virtual void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override;
GetIsOpaque()423   bool GetIsOpaque() override { return mOpaque; }
424   NS_IMETHOD Reset() override;
425   already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
426                                          Layer* aOldLayer,
427                                          LayerManager* aManager) override;
428 
429   bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
430                                  WebRenderCanvasData* aCanvasData) override;
431 
432   bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
433                                 CanvasRenderer* aRenderer) override;
434   virtual bool ShouldForceInactiveLayer(LayerManager* aManager) override;
435   void MarkContextClean() override;
436   void MarkContextCleanForFrameCapture() override;
437   bool IsContextCleanForFrameCapture() override;
438   NS_IMETHOD SetIsIPC(bool aIsIPC) override;
439   // this rect is in canvas device space
440   void Redraw(const mozilla::gfx::Rect& aR);
Redraw(const gfxRect & aR)441   NS_IMETHOD Redraw(const gfxRect& aR) override {
442     Redraw(ToRect(aR));
443     return NS_OK;
444   }
445   NS_IMETHOD SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions,
446                                ErrorResult& aRvForDictionaryInit) override;
447 
448   /**
449    * An abstract base class to be implemented by callers wanting to be notified
450    * that a refresh has occurred. Callers must ensure an observer is removed
451    * before it is destroyed.
452    */
453   virtual void DidRefresh() override;
454 
455   // this rect is in mTarget's current user space
456   void RedrawUser(const gfxRect& aR);
457 
458   // nsISupports interface + CC
459   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
460 
461   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(
462       CanvasRenderingContext2D)
463 
464   enum class CanvasMultiGetterType : uint8_t {
465     STRING = 0,
466     PATTERN = 1,
467     GRADIENT = 2
468   };
469 
470   enum class Style : uint8_t { STROKE = 0, FILL, MAX };
471 
GetParentObject()472   nsINode* GetParentObject() { return mCanvasElement; }
473 
LineTo(const mozilla::gfx::Point & aPoint)474   void LineTo(const mozilla::gfx::Point& aPoint) {
475     if (mPathBuilder) {
476       mPathBuilder->LineTo(aPoint);
477     } else {
478       mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(aPoint));
479     }
480   }
481 
BezierTo(const mozilla::gfx::Point & aCP1,const mozilla::gfx::Point & aCP2,const mozilla::gfx::Point & aCP3)482   void BezierTo(const mozilla::gfx::Point& aCP1,
483                 const mozilla::gfx::Point& aCP2,
484                 const mozilla::gfx::Point& aCP3) {
485     if (mPathBuilder) {
486       mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
487     } else {
488       mozilla::gfx::Matrix transform = mTarget->GetTransform();
489       mDSPathBuilder->BezierTo(transform.TransformPoint(aCP1),
490                                transform.TransformPoint(aCP2),
491                                transform.TransformPoint(aCP3));
492     }
493   }
494 
495   virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
496 
497   // Given a point, return hit region ID if it exists
498   nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
499 
500   // return true and fills in the bound rect if element has a hit region.
501   bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
502 
503   void OnShutdown();
504 
505   /**
506    * Update CurrentState().filter with the filter description for
507    * CurrentState().filterChain.
508    * Flushes the PresShell, so the world can change if you call this function.
509    */
510   MOZ_CAN_RUN_SCRIPT_BOUNDARY void UpdateFilter();
511 
512  protected:
513   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
514                              uint32_t aWidth, uint32_t aHeight,
515                              nsIPrincipal& aSubjectPrincipal,
516                              JSObject** aRetval);
517 
518   void PutImageData_explicit(int32_t aX, int32_t aY, ImageData&,
519                              bool aHasDirtyRect, int32_t aDirtyX,
520                              int32_t aDirtyY, int32_t aDirtyWidth,
521                              int32_t aDirtyHeight, ErrorResult&);
522 
523   bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
524                           gfx::DrawTarget& aTarget, gfx::IntRect aCopyRect);
525 
526   /**
527    * Internal method to complete initialisation, expects mTarget to have been
528    * set
529    */
530   nsresult Initialize(int32_t aWidth, int32_t aHeight);
531 
532   nsresult InitializeWithTarget(mozilla::gfx::DrawTarget* aSurface,
533                                 int32_t aWidth, int32_t aHeight);
534 
535   /**
536    * The number of living nsCanvasRenderingContexts.  When this goes down to
537    * 0, we free the premultiply and unpremultiply tables, if they exist.
538    */
539   static uintptr_t sNumLivingContexts;
540 
541   static mozilla::gfx::DrawTarget* sErrorTarget;
542 
543   void SetTransformInternal(const mozilla::gfx::Matrix& aTransform);
544 
545   // Some helpers.  Doesn't modify a color on failure.
546   void SetStyleFromUnion(
547       const UTF8StringOrCanvasGradientOrCanvasPattern& aValue,
548       Style aWhichStyle);
549   void SetStyleFromString(const nsACString& aStr, Style aWhichStyle);
550 
SetStyleFromGradient(CanvasGradient & aGradient,Style aWhichStyle)551   void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle) {
552     CurrentState().SetGradientStyle(aWhichStyle, &aGradient);
553   }
554 
SetStyleFromPattern(CanvasPattern & aPattern,Style aWhichStyle)555   void SetStyleFromPattern(CanvasPattern& aPattern, Style aWhichStyle) {
556     CurrentState().SetPatternStyle(aWhichStyle, &aPattern);
557   }
558 
559   void GetStyleAsUnion(OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue,
560                        Style aWhichStyle);
561 
562   // Returns whether a color was successfully parsed.
563   bool ParseColor(const nsACString& aString, nscolor* aColor);
564 
565   static void StyleColorToString(const nscolor& aColor, nsACString& aStr);
566 
567   // Returns whether a filter was successfully parsed.
568   bool ParseFilter(const nsACString& aString,
569                    StyleOwnedSlice<StyleFilter>& aFilterChain,
570                    ErrorResult& aError);
571 
572   // Returns whether the font was successfully updated.
573   bool SetFontInternal(const nsACString& aFont, mozilla::ErrorResult& aError);
574 
575   // Clears the target and updates mOpaque based on mOpaqueAttrValue and
576   // mContextAttributesHasAlpha.
577   void UpdateIsOpaque();
578 
579   /**
580    * Creates the error target, if it doesn't exist
581    */
582   static void EnsureErrorTarget();
583 
584   /* This function ensures there is a writable pathbuilder available, this
585    * pathbuilder may be working in user space or in device space or
586    * device space.
587    * After calling this function mPathTransformWillUpdate will be false
588    */
589   void EnsureWritablePath();
590 
591   // Ensures a path in UserSpace is available.
592   void EnsureUserSpacePath(
593       const CanvasWindingRule& aWinding = CanvasWindingRule::Nonzero);
594 
595   /**
596    * Needs to be called before updating the transform. This makes a call to
597    * EnsureTarget() so you don't have to.
598    */
599   void TransformWillUpdate();
600 
601   // Report the fillRule has changed.
602   void FillRuleChanged();
603 
604   /**
605    * Create the backing surfacing, if it doesn't exist. If there is an error
606    * in creating the target then it will put sErrorTarget in place. If there
607    * is in turn an error in creating the sErrorTarget then they would both
608    * be null so IsTargetValid() would still return null.
609    *
610    * Returns true on success.
611    */
612   bool EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
613                     bool aWillClear = false);
614 
615   void RestoreClipsAndTransformToTarget();
616 
617   bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
618                        RefPtr<layers::PersistentBufferProvider>& aOutProvider);
619 
620   bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
621                       RefPtr<layers::PersistentBufferProvider>& aOutProvider);
622 
623   void RegisterAllocation();
624 
625   void SetInitialState();
626 
627   void SetErrorState();
628 
629   /**
630    * This method is run at the end of the event-loop spin where
631    * ScheduleStableStateCallback was called.
632    *
633    * We use it to unlock resources that need to be locked while drawing.
634    */
635   void OnStableState();
636 
637   /**
638    * Cf. OnStableState.
639    */
640   void ScheduleStableStateCallback();
641 
642   /**
643    * Disposes an old target and prepares to lazily create a new target.
644    *
645    * Parameters are the new dimensions to be used, or if either is negative,
646    * existing dimensions will be left unchanged.
647    */
648   void ClearTarget(int32_t aWidth = -1, int32_t aHeight = -1);
649 
650   /*
651    * Returns the target to the buffer provider. i.e. this will queue a frame for
652    * rendering.
653    */
654   void ReturnTarget(bool aForceReset = false);
655 
656   /**
657    * Check if the target is valid after calling EnsureTarget.
658    */
IsTargetValid()659   bool IsTargetValid() const { return !!mTarget && mTarget != sErrorTarget; }
660 
661   /**
662    * Returns the surface format this canvas should be allocated using. Takes
663    * into account mOpaque, platform requirements, etc.
664    */
665   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
666 
667   /**
668    * Returns true if we know for sure that the pattern for a given style is
669    * opaque. Usefull to know if we can discard the content below in certain
670    * situations. Optionally checks if the pattern is a color pattern.
671    */
672   bool PatternIsOpaque(Style aStyle, bool* aIsColor = nullptr) const;
673 
674   SurfaceFromElementResult CachedSurfaceFromElement(Element* aElement);
675 
676   void DrawImage(const CanvasImageSource& aImgElt, double aSx, double aSy,
677                  double aSw, double aSh, double aDx, double aDy, double aDw,
678                  double aDh, uint8_t aOptional_argc,
679                  mozilla::ErrorResult& aError);
680 
681   void DrawDirectlyToCanvas(const DirectDrawInfo& aImage,
682                             mozilla::gfx::Rect* aBounds,
683                             mozilla::gfx::Rect aDest, mozilla::gfx::Rect aSrc,
684                             gfx::IntSize aImgSize);
685 
GetFont()686   nsCString& GetFont() {
687     /* will initilize the value if not set, else does nothing */
688     GetCurrentFontStyle();
689 
690     return CurrentState().font;
691   }
692 
693   // Member vars
694   int32_t mWidth, mHeight;
695 
696   // This is true when the canvas is valid, but of zero size, this requires
697   // specific behavior on some operations.
698   bool mZero;
699 
700   // The two ways to set the opaqueness of the canvas.
701   // mOpaqueAttrValue: Whether the <canvas> element has the moz-opaque attribute
702   // set. Can change during the lifetime of the context. Non-standard, should
703   // hopefully go away soon.
704   // mContextAttributesHasAlpha: The standard way of setting canvas opaqueness.
705   // Set at context initialization time and never changes.
706   bool mOpaqueAttrValue;
707   bool mContextAttributesHasAlpha;
708 
709   // Determines the context's opaqueness. Is computed from mOpaqueAttrValue and
710   // mContextAttributesHasAlpha in UpdateIsOpaque().
711   bool mOpaque;
712 
713   // This is true when the next time our layer is retrieved we need to
714   // recreate it (i.e. our backing surface changed)
715   bool mResetLayer;
716   // This is needed for drawing in drawAsyncXULElement
717   bool mIPC;
718 
719   bool mHasPendingStableStateCallback;
720 
721   // If mCanvasElement is not provided, then a docshell is
722   nsCOMPtr<nsIDocShell> mDocShell;
723 
724   // This is created lazily so it is necessary to call EnsureTarget before
725   // accessing it. In the event of an error it will be equal to
726   // sErrorTarget.
727   RefPtr<mozilla::gfx::DrawTarget> mTarget;
728 
729   RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
730 
731   RefPtr<CanvasShutdownObserver> mShutdownObserver;
732   void RemoveShutdownObserver();
AlreadyShutDown()733   bool AlreadyShutDown() const { return !mShutdownObserver; }
734 
735   /**
736    * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
737    * Redraw is called, reset to false when Render is called.
738    */
739   bool mIsEntireFrameInvalid;
740   /**
741    * When this is set, the first call to Redraw(gfxRect) should set
742    * mIsEntireFrameInvalid since we expect it will be followed by
743    * many more Redraw calls.
744    */
745   bool mPredictManyRedrawCalls;
746 
747   /**
748    * Flag to avoid unnecessary surface copies to FrameCaptureListeners in the
749    * case when the canvas is not currently being drawn into and not rendered
750    * but canvas capturing is still ongoing.
751    */
752   bool mIsCapturedFrameInvalid;
753 
754   /**
755    * We also have a device space pathbuilder. The reason for this is as
756    * follows, when a path is being built, but the transform changes, we
757    * can no longer keep a single path in userspace, considering there's
758    * several 'user spaces' now. We therefore transform the current path
759    * into device space, and add all operations to this path in device
760    * space.
761    *
762    * When then finally executing a render, the Azure drawing API expects
763    * the path to be in userspace. We could then set an identity transform
764    * on the DrawTarget and do all drawing in device space. This is
765    * undesirable because it requires transforming patterns, gradients,
766    * clips, etc. into device space and it would not work for stroking.
767    * What we do instead is convert the path back to user space when it is
768    * drawn, and draw it with the current transform. This makes all drawing
769    * occur correctly.
770    *
771    * There's never both a device space path builder and a user space path
772    * builder present at the same time. There is also never a path and a
773    * path builder present at the same time. When writing proceeds on an
774    * existing path the Path is cleared and a new builder is created.
775    *
776    * mPath is always in user-space.
777    */
778   RefPtr<mozilla::gfx::Path> mPath;
779   RefPtr<mozilla::gfx::PathBuilder> mDSPathBuilder;
780   RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
781   bool mPathTransformWillUpdate;
782   mozilla::gfx::Matrix mPathToDS;
783 
784   /**
785    * Number of times we've invalidated before calling redraw
786    */
787   uint32_t mInvalidateCount;
788   static const uint32_t kCanvasMaxInvalidateCount = 100;
789 
790   /**
791    * State information for hit regions
792    */
793   struct RegionInfo {
794     nsString mId;
795     // fallback element for a11y
796     RefPtr<Element> mElement;
797     // Path of the hit region in the 2d context coordinate space (not user
798     // space)
799     RefPtr<gfx::Path> mPath;
800   };
801 
802   nsTArray<RegionInfo> mHitRegionsOptions;
803 
804   nsBidi mBidiEngine;
805 
806   /**
807    * Returns true if a shadow should be drawn along with a
808    * drawing operation.
809    */
NeedToDrawShadow()810   bool NeedToDrawShadow() {
811     const ContextState& state = CurrentState();
812 
813     // The spec says we should not draw shadows if the operator is OVER.
814     // If it's over and the alpha value is zero, nothing needs to be drawn.
815     return NS_GET_A(state.shadowColor) != 0 &&
816            (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f ||
817             state.shadowOffset.y != 0.f);
818   }
819 
820   /**
821    * Returns true if the result of a drawing operation should be
822    * drawn with a filter.
823    */
NeedToApplyFilter()824   bool NeedToApplyFilter() {
825     return EnsureUpdatedFilter().mPrimitives.Length() > 0;
826   }
827 
828   /**
829    * Calls UpdateFilter if the canvas's WriteOnly state has changed between the
830    * last call to UpdateFilter and now.
831    */
EnsureUpdatedFilter()832   const gfx::FilterDescription& EnsureUpdatedFilter() {
833     bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly();
834     if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
835       UpdateFilter();
836       EnsureTarget();
837     }
838     MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
839     return CurrentState().filter;
840   }
841 
NeedToCalculateBounds()842   bool NeedToCalculateBounds() {
843     return NeedToDrawShadow() || NeedToApplyFilter();
844   }
845 
UsedOperation()846   mozilla::gfx::CompositionOp UsedOperation() {
847     if (NeedToDrawShadow() || NeedToApplyFilter()) {
848       // In this case the shadow or filter rendering will use the operator.
849       return mozilla::gfx::CompositionOp::OP_OVER;
850     }
851 
852     return CurrentState().op;
853   }
854 
855   // text
856 
857  protected:
858   enum class TextAlign : uint8_t { START, END, LEFT, RIGHT, CENTER };
859 
860   enum class TextBaseline : uint8_t {
861     TOP,
862     HANGING,
863     MIDDLE,
864     ALPHABETIC,
865     IDEOGRAPHIC,
866     BOTTOM
867   };
868 
869   enum class TextDrawOperation : uint8_t { FILL, STROKE, MEASURE };
870 
871  protected:
872   gfxFontGroup* GetCurrentFontStyle();
873 
874   /**
875    * Implementation of the fillText, strokeText, and measure functions with
876    * the operation abstracted to a flag.
877    * Returns a TextMetrics object _only_ if the operation is measure;
878    * drawing operations (fill or stroke) always return nullptr.
879    */
880   TextMetrics* DrawOrMeasureText(const nsAString& aText, float aX, float aY,
881                                  const Optional<double>& aMaxWidth,
882                                  TextDrawOperation aOp, ErrorResult& aError);
883 
884   // A clip or a transform, recorded and restored in order.
885   struct ClipState {
ClipStateClipState886     explicit ClipState(mozilla::gfx::Path* aClip) : clip(aClip) {}
887 
ClipStateClipState888     explicit ClipState(const mozilla::gfx::Matrix& aTransform)
889         : transform(aTransform) {}
890 
IsClipClipState891     bool IsClip() const { return !!clip; }
892 
893     RefPtr<mozilla::gfx::Path> clip;
894     mozilla::gfx::Matrix transform;
895   };
896 
897   // state stack handling
898   class ContextState {
899    public:
900     ContextState();
901     ContextState(const ContextState& aOther);
902     ~ContextState();
903 
904     void SetColorStyle(Style aWhichStyle, nscolor aColor);
905     void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat);
906     void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad);
907 
908     /**
909      * returns true iff the given style is a solid color.
910      */
StyleIsColor(Style aWhichStyle)911     bool StyleIsColor(Style aWhichStyle) const {
912       return !(patternStyles[aWhichStyle] || gradientStyles[aWhichStyle]);
913     }
914 
ShadowBlurRadius()915     int32_t ShadowBlurRadius() const {
916       static const gfxFloat GAUSSIAN_SCALE_FACTOR =
917           (3 * sqrt(2 * M_PI) / 4) * 1.5;
918       return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
919     }
920 
ShadowBlurSigma()921     mozilla::gfx::Float ShadowBlurSigma() const {
922       return std::min(SIGMA_MAX, shadowBlur / 2.0f);
923     }
924 
925     nsTArray<ClipState> clipsAndTransforms;
926 
927     RefPtr<gfxFontGroup> fontGroup;
928     RefPtr<nsAtom> fontLanguage;
929     nsFont fontFont;
930 
931     EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
932     EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
933     EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
934 
935     nsCString font;
936     TextAlign textAlign = TextAlign::START;
937     TextBaseline textBaseline = TextBaseline::ALPHABETIC;
938 
939     nscolor shadowColor = 0;
940 
941     mozilla::gfx::Matrix transform;
942     mozilla::gfx::Point shadowOffset;
943     mozilla::gfx::Float lineWidth = 1.0f;
944     mozilla::gfx::Float miterLimit = 10.0f;
945     mozilla::gfx::Float globalAlpha = 1.0f;
946     mozilla::gfx::Float shadowBlur = 0.0f;
947 
948     nsTArray<mozilla::gfx::Float> dash;
949     mozilla::gfx::Float dashOffset = 0.0f;
950 
951     mozilla::gfx::CompositionOp op = mozilla::gfx::CompositionOp::OP_OVER;
952     mozilla::gfx::FillRule fillRule = mozilla::gfx::FillRule::FILL_WINDING;
953     mozilla::gfx::CapStyle lineCap = mozilla::gfx::CapStyle::BUTT;
954     mozilla::gfx::JoinStyle lineJoin = mozilla::gfx::JoinStyle::MITER_OR_BEVEL;
955 
956     nsCString filterString{"none"};
957     StyleOwnedSlice<StyleFilter> filterChain;
958     // RAII object that we obtain when we start to observer SVG filter elements
959     // for rendering changes.  When released we stop observing the SVG elements.
960     nsCOMPtr<nsISupports> autoSVGFiltersObserver;
961     mozilla::gfx::FilterDescription filter;
962     nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
963 
964     // This keeps track of whether the canvas was "tainted" or not when
965     // we last used a filter. This is a security measure, whereby the
966     // canvas is flipped to write-only if a cross-origin image is drawn to it.
967     // This is to stop bad actors from reading back data they shouldn't have
968     // access to.
969     //
970     // This also limits what filters we can apply to the context; in particular
971     // feDisplacementMap is restricted.
972     //
973     // We keep track of this to ensure that if this gets out of sync with the
974     // tainted state of the canvas itself, we update our filters accordingly.
975     bool filterSourceGraphicTainted = false;
976 
977     bool imageSmoothingEnabled = true;
978     bool fontExplicitLanguage = false;
979   };
980 
981   AutoTArray<ContextState, 3> mStyleStack;
982 
CurrentState()983   inline ContextState& CurrentState() {
984     return mStyleStack[mStyleStack.Length() - 1];
985   }
986 
CurrentState()987   inline const ContextState& CurrentState() const {
988     return mStyleStack[mStyleStack.Length() - 1];
989   }
990 
991   friend class CanvasGeneralPattern;
992   friend class AdjustedTarget;
993   friend class AdjustedTargetForShadow;
994   friend class AdjustedTargetForFilter;
995 
996   // other helpers
997   void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel);
998 
999   friend struct CanvasBidiProcessor;
1000   friend class CanvasDrawObserver;
1001   friend class ImageBitmap;
1002 
1003   void SetWriteOnly();
1004 
IsWriteOnly()1005   bool IsWriteOnly() const { return mWriteOnly; }
1006 
1007   bool mWriteOnly;
1008   bool mClipsNeedConverting = false;
1009 };
1010 
1011 size_t BindingJSObjectMallocBytes(CanvasRenderingContext2D* aContext);
1012 
1013 }  // namespace dom
1014 }  // namespace mozilla
1015 
1016 #endif /* CanvasRenderingContext2D_h */
1017