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