LCOV - code coverage report
Current view: top level - dom/canvas - CanvasRenderingContext2D.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 3178 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 273 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "CanvasRenderingContext2D.h"
       7             : 
       8             : #include "mozilla/gfx/Helpers.h"
       9             : #include "nsXULElement.h"
      10             : 
      11             : #include "nsAutoPtr.h"
      12             : #include "nsIServiceManager.h"
      13             : #include "nsMathUtils.h"
      14             : #include "SVGImageContext.h"
      15             : 
      16             : #include "nsContentUtils.h"
      17             : 
      18             : #include "nsIDocument.h"
      19             : #include "mozilla/dom/HTMLCanvasElement.h"
      20             : #include "nsSVGEffects.h"
      21             : #include "nsPresContext.h"
      22             : #include "nsIPresShell.h"
      23             : 
      24             : #include "nsIInterfaceRequestorUtils.h"
      25             : #include "nsIFrame.h"
      26             : #include "nsError.h"
      27             : 
      28             : #include "nsCSSParser.h"
      29             : #include "nsCSSPseudoElements.h"
      30             : #include "mozilla/css/StyleRule.h"
      31             : #include "mozilla/css/Declaration.h"
      32             : #include "nsComputedDOMStyle.h"
      33             : #include "nsStyleSet.h"
      34             : 
      35             : #include "nsPrintfCString.h"
      36             : 
      37             : #include "nsReadableUtils.h"
      38             : 
      39             : #include "nsColor.h"
      40             : #include "nsGfxCIID.h"
      41             : #include "nsIDocShell.h"
      42             : #include "nsIDOMWindow.h"
      43             : #include "nsPIDOMWindow.h"
      44             : #include "nsDisplayList.h"
      45             : #include "nsFocusManager.h"
      46             : #include "nsContentUtils.h"
      47             : 
      48             : #include "nsTArray.h"
      49             : 
      50             : #include "ImageEncoder.h"
      51             : #include "ImageRegion.h"
      52             : 
      53             : #include "gfxContext.h"
      54             : #include "gfxPlatform.h"
      55             : #include "gfxFont.h"
      56             : #include "gfxBlur.h"
      57             : #include "gfxPrefs.h"
      58             : #include "gfxUtils.h"
      59             : 
      60             : #include "nsFrameLoader.h"
      61             : #include "nsBidiPresUtils.h"
      62             : #include "Layers.h"
      63             : #include "LayerUserData.h"
      64             : #include "CanvasUtils.h"
      65             : #include "nsIMemoryReporter.h"
      66             : #include "nsStyleUtil.h"
      67             : #include "CanvasImageCache.h"
      68             : 
      69             : #include <algorithm>
      70             : 
      71             : #include "jsapi.h"
      72             : #include "jsfriendapi.h"
      73             : #include "js/Conversions.h"
      74             : #include "js/HeapAPI.h"
      75             : 
      76             : #include "mozilla/Alignment.h"
      77             : #include "mozilla/Assertions.h"
      78             : #include "mozilla/CheckedInt.h"
      79             : #include "mozilla/DebugOnly.h"
      80             : #include "mozilla/dom/ContentParent.h"
      81             : #include "mozilla/dom/ImageBitmap.h"
      82             : #include "mozilla/dom/ImageData.h"
      83             : #include "mozilla/dom/PBrowserParent.h"
      84             : #include "mozilla/dom/ToJSValue.h"
      85             : #include "mozilla/dom/TypedArray.h"
      86             : #include "mozilla/EndianUtils.h"
      87             : #include "mozilla/gfx/2D.h"
      88             : #include "mozilla/gfx/Helpers.h"
      89             : #include "mozilla/gfx/Tools.h"
      90             : #include "mozilla/gfx/PathHelpers.h"
      91             : #include "mozilla/gfx/DataSurfaceHelpers.h"
      92             : #include "mozilla/gfx/PatternHelpers.h"
      93             : #include "mozilla/gfx/Swizzle.h"
      94             : #include "mozilla/ipc/DocumentRendererParent.h"
      95             : #include "mozilla/ipc/PDocumentRendererParent.h"
      96             : #include "mozilla/layers/PersistentBufferProvider.h"
      97             : #include "mozilla/MathAlgorithms.h"
      98             : #include "mozilla/Preferences.h"
      99             : #include "mozilla/ServoBindings.h"
     100             : #include "mozilla/Telemetry.h"
     101             : #include "mozilla/TimeStamp.h"
     102             : #include "mozilla/UniquePtr.h"
     103             : #include "mozilla/Unused.h"
     104             : #include "nsCCUncollectableMarker.h"
     105             : #include "nsWrapperCacheInlines.h"
     106             : #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
     107             : #include "mozilla/dom/CanvasPath.h"
     108             : #include "mozilla/dom/HTMLImageElement.h"
     109             : #include "mozilla/dom/HTMLVideoElement.h"
     110             : #include "mozilla/dom/SVGMatrix.h"
     111             : #include "mozilla/dom/TextMetrics.h"
     112             : #include "mozilla/dom/SVGMatrix.h"
     113             : #include "mozilla/FloatingPoint.h"
     114             : #include "nsGlobalWindow.h"
     115             : #include "GLContext.h"
     116             : #include "GLContextProvider.h"
     117             : #include "SVGContentUtils.h"
     118             : #include "nsIScreenManager.h"
     119             : #include "nsFilterInstance.h"
     120             : #include "nsSVGLength2.h"
     121             : #include "nsDeviceContext.h"
     122             : #include "nsFontMetrics.h"
     123             : #include "Units.h"
     124             : #include "CanvasUtils.h"
     125             : #include "mozilla/CycleCollectedJSRuntime.h"
     126             : #include "mozilla/StyleSetHandle.h"
     127             : #include "mozilla/StyleSetHandleInlines.h"
     128             : #include "mozilla/layers/CanvasClient.h"
     129             : 
     130             : #undef free // apparently defined by some windows header, clashing with a free()
     131             :             // method in SkTypes.h
     132             : #include "SkiaGLGlue.h"
     133             : #ifdef USE_SKIA
     134             : #include "SurfaceTypes.h"
     135             : #include "GLBlitHelper.h"
     136             : #include "ScopedGLHelpers.h"
     137             : #endif
     138             : 
     139             : using mozilla::gl::GLContext;
     140             : using mozilla::gl::SkiaGLGlue;
     141             : using mozilla::gl::GLContextProvider;
     142             : 
     143             : #ifdef XP_WIN
     144             : #include "gfxWindowsPlatform.h"
     145             : #endif
     146             : 
     147             : #ifdef MOZ_WIDGET_GONK
     148             : #include "mozilla/layers/ShadowLayers.h"
     149             : #endif
     150             : 
     151             : // windows.h (included by chromium code) defines this, in its infinite wisdom
     152             : #undef DrawText
     153             : 
     154             : using namespace mozilla;
     155             : using namespace mozilla::CanvasUtils;
     156             : using namespace mozilla::css;
     157             : using namespace mozilla::gfx;
     158             : using namespace mozilla::image;
     159             : using namespace mozilla::ipc;
     160             : using namespace mozilla::layers;
     161             : 
     162             : namespace mozilla {
     163             : namespace dom {
     164             : 
     165             : // Cap sigma to avoid overly large temp surfaces.
     166             : const Float SIGMA_MAX = 100;
     167             : 
     168             : const size_t MAX_STYLE_STACK_SIZE = 1024;
     169             : 
     170             : /* Memory reporter stuff */
     171             : static int64_t gCanvasAzureMemoryUsed = 0;
     172             : 
     173             : // Adds Save() / Restore() calls to the scope.
     174             : class MOZ_RAII AutoSaveRestore
     175             : {
     176             : public:
     177           0 :   explicit AutoSaveRestore(CanvasRenderingContext2D* aCtx
     178             :                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     179           0 :     : mCtx(aCtx)
     180             :   {
     181           0 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     182           0 :     mCtx->Save();
     183           0 :   }
     184           0 :   ~AutoSaveRestore() { mCtx->Restore(); }
     185             : private:
     186             :   RefPtr<CanvasRenderingContext2D> mCtx;
     187             :   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     188             : };
     189             : 
     190             : // This is KIND_OTHER because it's not always clear where in memory the pixels
     191             : // of a canvas are stored.  Furthermore, this memory will be tracked by the
     192             : // underlying surface implementations.  See bug 655638 for details.
     193             : class Canvas2dPixelsReporter final : public nsIMemoryReporter
     194             : {
     195           0 :   ~Canvas2dPixelsReporter() {}
     196             : public:
     197             :   NS_DECL_ISUPPORTS
     198             : 
     199           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     200             :                             nsISupports* aData, bool aAnonymize) override
     201             :   {
     202           0 :     MOZ_COLLECT_REPORT(
     203             :       "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES, gCanvasAzureMemoryUsed,
     204             :       "Memory used by 2D canvases. Each canvas requires "
     205           0 :       "(width * height * 4) bytes.");
     206             : 
     207           0 :     return NS_OK;
     208             :   }
     209             : };
     210             : 
     211           0 : NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
     212             : 
     213           0 : class CanvasRadialGradient : public CanvasGradient
     214             : {
     215             : public:
     216           0 :   CanvasRadialGradient(CanvasRenderingContext2D* aContext,
     217             :                        const Point& aBeginOrigin, Float aBeginRadius,
     218             :                        const Point& aEndOrigin, Float aEndRadius)
     219           0 :     : CanvasGradient(aContext, Type::RADIAL)
     220             :     , mCenter1(aBeginOrigin)
     221             :     , mCenter2(aEndOrigin)
     222             :     , mRadius1(aBeginRadius)
     223           0 :     , mRadius2(aEndRadius)
     224             :   {
     225           0 :   }
     226             : 
     227             :   Point mCenter1;
     228             :   Point mCenter2;
     229             :   Float mRadius1;
     230             :   Float mRadius2;
     231             : };
     232             : 
     233           0 : class CanvasLinearGradient : public CanvasGradient
     234             : {
     235             : public:
     236           0 :   CanvasLinearGradient(CanvasRenderingContext2D* aContext,
     237             :                        const Point& aBegin, const Point& aEnd)
     238           0 :     : CanvasGradient(aContext, Type::LINEAR)
     239             :     , mBegin(aBegin)
     240           0 :     , mEnd(aEnd)
     241             :   {
     242           0 :   }
     243             : 
     244             : protected:
     245             :   friend struct CanvasBidiProcessor;
     246             :   friend class CanvasGeneralPattern;
     247             : 
     248             :   // Beginning of linear gradient.
     249             :   Point mBegin;
     250             :   // End of linear gradient.
     251             :   Point mEnd;
     252             : };
     253             : 
     254             : bool
     255           0 : CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
     256             : {
     257           0 :   const ContextState& state = CurrentState();
     258           0 :   if (state.globalAlpha < 1.0) {
     259           0 :     return false;
     260             :   }
     261             : 
     262           0 :   if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
     263           0 :     return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
     264             :   }
     265             : 
     266             :   // TODO: for gradient patterns we could check that all stops are opaque
     267             :   // colors.
     268             : 
     269           0 :   if (!state.gradientStyles[aStyle]) {
     270             :     // it's a color pattern.
     271           0 :     return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
     272             :   }
     273             : 
     274           0 :   return false;
     275             : }
     276             : 
     277             : // This class is named 'GeneralCanvasPattern' instead of just
     278             : // 'GeneralPattern' to keep Windows PGO builds from confusing the
     279             : // GeneralPattern class in gfxContext.cpp with this one.
     280           0 : class CanvasGeneralPattern
     281             : {
     282             : public:
     283             :   typedef CanvasRenderingContext2D::Style Style;
     284             :   typedef CanvasRenderingContext2D::ContextState ContextState;
     285             : 
     286           0 :   Pattern& ForStyle(CanvasRenderingContext2D* aCtx,
     287             :                     Style aStyle,
     288             :                     DrawTarget* aRT)
     289             :   {
     290             :     // This should only be called once or the mPattern destructor will
     291             :     // not be executed.
     292           0 :     NS_ASSERTION(!mPattern.GetPattern(), "ForStyle() should only be called once on CanvasGeneralPattern!");
     293             : 
     294           0 :     const ContextState& state = aCtx->CurrentState();
     295             : 
     296           0 :     if (state.StyleIsColor(aStyle)) {
     297           0 :       mPattern.InitColorPattern(ToDeviceColor(state.colorStyles[aStyle]));
     298           0 :     } else if (state.gradientStyles[aStyle] &&
     299           0 :                state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
     300             :       auto gradient =
     301           0 :         static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
     302             : 
     303           0 :       mPattern.InitLinearGradientPattern(gradient->mBegin, gradient->mEnd,
     304           0 :                                          gradient->GetGradientStopsForTarget(aRT));
     305           0 :     } else if (state.gradientStyles[aStyle] &&
     306           0 :                state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
     307             :       auto gradient =
     308           0 :         static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
     309             : 
     310           0 :       mPattern.InitRadialGradientPattern(gradient->mCenter1, gradient->mCenter2,
     311             :                                          gradient->mRadius1, gradient->mRadius2,
     312           0 :                                          gradient->GetGradientStopsForTarget(aRT));
     313           0 :     } else if (state.patternStyles[aStyle]) {
     314           0 :       if (aCtx->mCanvasElement) {
     315           0 :         CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
     316           0 :                                               state.patternStyles[aStyle]->mPrincipal,
     317           0 :                                               state.patternStyles[aStyle]->mForceWriteOnly,
     318           0 :                                               state.patternStyles[aStyle]->mCORSUsed);
     319             :       }
     320             : 
     321             :       ExtendMode mode;
     322           0 :       if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
     323           0 :         mode = ExtendMode::CLAMP;
     324             :       } else {
     325           0 :         mode = ExtendMode::REPEAT;
     326             :       }
     327             : 
     328             :       SamplingFilter samplingFilter;
     329           0 :       if (state.imageSmoothingEnabled) {
     330           0 :         samplingFilter = SamplingFilter::GOOD;
     331             :       } else {
     332           0 :         samplingFilter = SamplingFilter::POINT;
     333             :       }
     334             : 
     335           0 :       mPattern.InitSurfacePattern(state.patternStyles[aStyle]->mSurface, mode,
     336           0 :                                   state.patternStyles[aStyle]->mTransform,
     337           0 :                                   samplingFilter);
     338             :     }
     339             : 
     340           0 :     return *mPattern.GetPattern();
     341             :   }
     342             : 
     343             :   GeneralPattern mPattern;
     344             : };
     345             : 
     346             : /* This is an RAII based class that can be used as a drawtarget for
     347             :  * operations that need to have a filter applied to their results.
     348             :  * All coordinates passed to the constructor are in device space.
     349             :  */
     350             : class AdjustedTargetForFilter
     351             : {
     352             : public:
     353             :   typedef CanvasRenderingContext2D::ContextState ContextState;
     354             : 
     355           0 :   AdjustedTargetForFilter(CanvasRenderingContext2D* aCtx,
     356             :                           DrawTarget* aFinalTarget,
     357             :                           const gfx::IntPoint& aFilterSpaceToTargetOffset,
     358             :                           const gfx::IntRect& aPreFilterBounds,
     359             :                           const gfx::IntRect& aPostFilterBounds,
     360             :                           gfx::CompositionOp aCompositionOp)
     361           0 :     : mFinalTarget(aFinalTarget)
     362             :     , mCtx(aCtx)
     363             :     , mPostFilterBounds(aPostFilterBounds)
     364             :     , mOffset(aFilterSpaceToTargetOffset)
     365           0 :     , mCompositionOp(aCompositionOp)
     366             :   {
     367           0 :     nsIntRegion sourceGraphicNeededRegion;
     368           0 :     nsIntRegion fillPaintNeededRegion;
     369           0 :     nsIntRegion strokePaintNeededRegion;
     370             : 
     371           0 :     FilterSupport::ComputeSourceNeededRegions(
     372           0 :       aCtx->CurrentState().filter, mPostFilterBounds,
     373             :       sourceGraphicNeededRegion, fillPaintNeededRegion,
     374           0 :       strokePaintNeededRegion);
     375             : 
     376           0 :     mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
     377           0 :     mFillPaintRect = fillPaintNeededRegion.GetBounds();
     378           0 :     mStrokePaintRect = strokePaintNeededRegion.GetBounds();
     379             : 
     380           0 :     mSourceGraphicRect = mSourceGraphicRect.Intersect(aPreFilterBounds);
     381             : 
     382           0 :     if (mSourceGraphicRect.IsEmpty()) {
     383             :       // The filter might not make any use of the source graphic. We need to
     384             :       // create a DrawTarget that we can return from DT() anyway, so we'll
     385             :       // just use a 1x1-sized one.
     386           0 :       mSourceGraphicRect.SizeTo(1, 1);
     387             :     }
     388             : 
     389           0 :     mTarget = mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(),
     390           0 :                                                     SurfaceFormat::B8G8R8A8);
     391             : 
     392           0 :     if (!mTarget) {
     393             :       // XXX - Deal with the situation where our temp size is too big to
     394             :       // fit in a texture (bug 1066622).
     395           0 :       mTarget = mFinalTarget;
     396           0 :       mCtx = nullptr;
     397           0 :       mFinalTarget = nullptr;
     398           0 :       return;
     399             :     }
     400             : 
     401           0 :     mTarget->SetTransform(
     402           0 :       mFinalTarget->GetTransform().PostTranslate(-mSourceGraphicRect.TopLeft() + mOffset));
     403             :   }
     404             : 
     405             :   // Return a SourceSurface that contains the FillPaint or StrokePaint source.
     406             :   already_AddRefed<SourceSurface>
     407           0 :   DoSourcePaint(gfx::IntRect& aRect, CanvasRenderingContext2D::Style aStyle)
     408             :   {
     409           0 :     if (aRect.IsEmpty()) {
     410           0 :       return nullptr;
     411             :     }
     412             : 
     413             :     RefPtr<DrawTarget> dt =
     414           0 :       mFinalTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
     415           0 :     if (!dt) {
     416           0 :       aRect.SetEmpty();
     417           0 :       return nullptr;
     418             :     }
     419             : 
     420             :     Matrix transform =
     421           0 :       mFinalTarget->GetTransform().PostTranslate(-aRect.TopLeft() + mOffset);
     422             : 
     423           0 :     dt->SetTransform(transform);
     424             : 
     425           0 :     if (transform.Invert()) {
     426           0 :       gfx::Rect dtBounds(0, 0, aRect.width, aRect.height);
     427           0 :       gfx::Rect fillRect = transform.TransformBounds(dtBounds);
     428           0 :       dt->FillRect(fillRect, CanvasGeneralPattern().ForStyle(mCtx, aStyle, dt));
     429             :     }
     430           0 :     return dt->Snapshot();
     431             :   }
     432             : 
     433           0 :   ~AdjustedTargetForFilter()
     434           0 :   {
     435           0 :     if (!mCtx) {
     436           0 :       return;
     437             :     }
     438             : 
     439           0 :     RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
     440             : 
     441             :     RefPtr<SourceSurface> fillPaint =
     442           0 :       DoSourcePaint(mFillPaintRect, CanvasRenderingContext2D::Style::FILL);
     443             :     RefPtr<SourceSurface> strokePaint =
     444           0 :       DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
     445             : 
     446           0 :     AutoRestoreTransform autoRestoreTransform(mFinalTarget);
     447           0 :     mFinalTarget->SetTransform(Matrix());
     448             : 
     449           0 :     MOZ_RELEASE_ASSERT(!mCtx->CurrentState().filter.mPrimitives.IsEmpty());
     450           0 :     gfx::FilterSupport::RenderFilterDescription(
     451           0 :       mFinalTarget, mCtx->CurrentState().filter,
     452           0 :       gfx::Rect(mPostFilterBounds),
     453             :       snapshot, mSourceGraphicRect,
     454             :       fillPaint, mFillPaintRect,
     455             :       strokePaint, mStrokePaintRect,
     456           0 :       mCtx->CurrentState().filterAdditionalImages,
     457           0 :       mPostFilterBounds.TopLeft() - mOffset,
     458           0 :       DrawOptions(1.0f, mCompositionOp));
     459             : 
     460           0 :     const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
     461           0 :     MOZ_RELEASE_ASSERT(!filter.mPrimitives.IsEmpty());
     462           0 :     if (filter.mPrimitives.LastElement().IsTainted() && mCtx->mCanvasElement) {
     463           0 :       mCtx->mCanvasElement->SetWriteOnly();
     464             :     }
     465           0 :   }
     466             : 
     467           0 :   DrawTarget* DT()
     468             :   {
     469           0 :     return mTarget;
     470             :   }
     471             : 
     472             : private:
     473             :   RefPtr<DrawTarget> mTarget;
     474             :   RefPtr<DrawTarget> mFinalTarget;
     475             :   CanvasRenderingContext2D* mCtx;
     476             :   gfx::IntRect mSourceGraphicRect;
     477             :   gfx::IntRect mFillPaintRect;
     478             :   gfx::IntRect mStrokePaintRect;
     479             :   gfx::IntRect mPostFilterBounds;
     480             :   gfx::IntPoint mOffset;
     481             :   gfx::CompositionOp mCompositionOp;
     482             : };
     483             : 
     484             : /* This is an RAII based class that can be used as a drawtarget for
     485             :  * operations that need to have a shadow applied to their results.
     486             :  * All coordinates passed to the constructor are in device space.
     487             :  */
     488             : class AdjustedTargetForShadow
     489             : {
     490             : public:
     491             :   typedef CanvasRenderingContext2D::ContextState ContextState;
     492             : 
     493           0 :   AdjustedTargetForShadow(CanvasRenderingContext2D* aCtx,
     494             :                           DrawTarget* aFinalTarget,
     495             :                           const gfx::Rect& aBounds,
     496             :                           gfx::CompositionOp aCompositionOp)
     497           0 :     : mFinalTarget(aFinalTarget)
     498             :     , mCtx(aCtx)
     499           0 :     , mCompositionOp(aCompositionOp)
     500             :   {
     501           0 :     const ContextState& state = mCtx->CurrentState();
     502           0 :     mSigma = state.ShadowBlurSigma();
     503             : 
     504             :     // We actually include the bounds of the shadow blur, this makes it
     505             :     // easier to execute the actual blur on hardware, and shouldn't affect
     506             :     // the amount of pixels that need to be touched.
     507           0 :     gfx::Rect bounds = aBounds;
     508           0 :     int32_t blurRadius = state.ShadowBlurRadius();
     509           0 :     bounds.Inflate(blurRadius);
     510           0 :     bounds.RoundOut();
     511           0 :     bounds.ToIntRect(&mTempRect);
     512             : 
     513             :     mTarget =
     514           0 :       mFinalTarget->CreateShadowDrawTarget(mTempRect.Size(),
     515           0 :                                            SurfaceFormat::B8G8R8A8, mSigma);
     516             : 
     517           0 :     if (!mTarget) {
     518             :       // XXX - Deal with the situation where our temp size is too big to
     519             :       // fit in a texture (bug 1066622).
     520           0 :       mTarget = mFinalTarget;
     521           0 :       mCtx = nullptr;
     522           0 :       mFinalTarget = nullptr;
     523             :     } else {
     524           0 :       mTarget->SetTransform(
     525           0 :         mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
     526             :     }
     527           0 :   }
     528             : 
     529           0 :   ~AdjustedTargetForShadow()
     530           0 :   {
     531           0 :     if (!mCtx) {
     532           0 :       return;
     533             :     }
     534             : 
     535           0 :     RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
     536             : 
     537           0 :     mFinalTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
     538           0 :                                         Color::FromABGR(mCtx->CurrentState().shadowColor),
     539           0 :                                         mCtx->CurrentState().shadowOffset, mSigma,
     540           0 :                                         mCompositionOp);
     541           0 :   }
     542             : 
     543           0 :   DrawTarget* DT()
     544             :   {
     545           0 :     return mTarget;
     546             :   }
     547             : 
     548           0 :   gfx::IntPoint OffsetToFinalDT()
     549             :   {
     550           0 :     return mTempRect.TopLeft();
     551             :   }
     552             : 
     553             : private:
     554             :   RefPtr<DrawTarget> mTarget;
     555             :   RefPtr<DrawTarget> mFinalTarget;
     556             :   CanvasRenderingContext2D* mCtx;
     557             :   Float mSigma;
     558             :   gfx::IntRect mTempRect;
     559             :   gfx::CompositionOp mCompositionOp;
     560             : };
     561             : 
     562             : /*
     563             :  * This is an RAII based class that can be used as a drawtarget for
     564             :  * operations that need a shadow or a filter drawn. It will automatically
     565             :  * provide a temporary target when needed, and if so blend it back with a
     566             :  * shadow, filter, or both.
     567             :  * If both a shadow and a filter are needed, the filter is applied first,
     568             :  * and the shadow is applied to the filtered results.
     569             :  *
     570             :  * aBounds specifies the bounds of the drawing operation that will be
     571             :  * drawn to the target, it is given in device space! If this is nullptr the
     572             :  * drawing operation will be assumed to cover the whole canvas.
     573             :  */
     574             : class AdjustedTarget
     575             : {
     576             : public:
     577             :   typedef CanvasRenderingContext2D::ContextState ContextState;
     578             : 
     579           0 :   explicit AdjustedTarget(CanvasRenderingContext2D* aCtx,
     580             :                           const gfx::Rect* aBounds = nullptr)
     581           0 :   {
     582             :     // All rects in this function are in the device space of ctx->mTarget.
     583             : 
     584             :     // In order to keep our temporary surfaces as small as possible, we first
     585             :     // calculate what their maximum required bounds would need to be if we
     586             :     // were to fill the whole canvas. Everything outside those bounds we don't
     587             :     // need to render.
     588           0 :     gfx::Rect r(0, 0, aCtx->mWidth, aCtx->mHeight);
     589             :     gfx::Rect maxSourceNeededBoundsForShadow =
     590           0 :       MaxSourceNeededBoundsForShadow(r, aCtx);
     591             :     gfx::Rect maxSourceNeededBoundsForFilter =
     592           0 :       MaxSourceNeededBoundsForFilter(maxSourceNeededBoundsForShadow, aCtx);
     593           0 :     if (!aCtx->IsTargetValid()) {
     594           0 :       return;
     595             :     }
     596             : 
     597           0 :     gfx::Rect bounds = maxSourceNeededBoundsForFilter;
     598           0 :     if (aBounds) {
     599           0 :       bounds = bounds.Intersect(*aBounds);
     600             :     }
     601           0 :     gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
     602           0 :     if (!aCtx->IsTargetValid()) {
     603           0 :       return;
     604             :     }
     605             : 
     606           0 :     mozilla::gfx::CompositionOp op = aCtx->CurrentState().op;
     607             : 
     608           0 :     gfx::IntPoint offsetToFinalDT;
     609             : 
     610             :     // First set up the shadow draw target, because the shadow goes outside.
     611             :     // It applies to the post-filter results, if both a filter and a shadow
     612             :     // are used.
     613           0 :     if (aCtx->NeedToDrawShadow()) {
     614           0 :       mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
     615           0 :         aCtx, aCtx->mTarget, boundsAfterFilter, op);
     616           0 :       mTarget = mShadowTarget->DT();
     617           0 :       offsetToFinalDT = mShadowTarget->OffsetToFinalDT();
     618             : 
     619             :       // If we also have a filter, the filter needs to be drawn with OP_OVER
     620             :       // because shadow drawing already applies op on the result.
     621           0 :       op = gfx::CompositionOp::OP_OVER;
     622             :     }
     623             : 
     624             :     // Now set up the filter draw target.
     625           0 :     const bool applyFilter = aCtx->NeedToApplyFilter();
     626           0 :     if (!aCtx->IsTargetValid()) {
     627           0 :       return;
     628             :     }
     629           0 :     if (applyFilter) {
     630           0 :       bounds.RoundOut();
     631             : 
     632           0 :       if (!mTarget) {
     633           0 :         mTarget = aCtx->mTarget;
     634             :       }
     635           0 :       gfx::IntRect intBounds;
     636           0 :       if (!bounds.ToIntRect(&intBounds)) {
     637           0 :         return;
     638             :       }
     639           0 :       mFilterTarget = MakeUnique<AdjustedTargetForFilter>(
     640             :         aCtx, mTarget, offsetToFinalDT, intBounds,
     641           0 :         gfx::RoundedToInt(boundsAfterFilter), op);
     642           0 :       mTarget = mFilterTarget->DT();
     643             :     }
     644           0 :     if (!mTarget) {
     645           0 :       mTarget = aCtx->mTarget;
     646             :     }
     647             :   }
     648             : 
     649           0 :   ~AdjustedTarget()
     650           0 :   {
     651             :     // The order in which the targets are finalized is important.
     652             :     // Filters are inside, any shadow applies to the post-filter results.
     653           0 :     mFilterTarget.reset();
     654           0 :     mShadowTarget.reset();
     655           0 :   }
     656             : 
     657           0 :   operator DrawTarget*()
     658             :   {
     659           0 :     return mTarget;
     660             :   }
     661             : 
     662           0 :   DrawTarget* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
     663             :   {
     664           0 :     return mTarget;
     665             :   }
     666             : 
     667             : private:
     668             : 
     669             :   gfx::Rect
     670           0 :   MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, CanvasRenderingContext2D* aCtx)
     671             :   {
     672           0 :     const bool applyFilter = aCtx->NeedToApplyFilter();
     673           0 :     if (!aCtx->IsTargetValid()) {
     674           0 :       return aDestBounds;
     675             :     }
     676           0 :     if (!applyFilter) {
     677           0 :       return aDestBounds;
     678             :     }
     679             : 
     680           0 :     nsIntRegion sourceGraphicNeededRegion;
     681           0 :     nsIntRegion fillPaintNeededRegion;
     682           0 :     nsIntRegion strokePaintNeededRegion;
     683             : 
     684           0 :     FilterSupport::ComputeSourceNeededRegions(
     685           0 :       aCtx->CurrentState().filter, gfx::RoundedToInt(aDestBounds),
     686           0 :       sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
     687             : 
     688           0 :     return gfx::Rect(sourceGraphicNeededRegion.GetBounds());
     689             :   }
     690             : 
     691             :   gfx::Rect
     692           0 :   MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, CanvasRenderingContext2D* aCtx)
     693             :   {
     694           0 :     if (!aCtx->NeedToDrawShadow()) {
     695           0 :       return aDestBounds;
     696             :     }
     697             : 
     698           0 :     const ContextState& state = aCtx->CurrentState();
     699           0 :     gfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
     700           0 :     sourceBounds.Inflate(state.ShadowBlurRadius());
     701             : 
     702             :     // Union the shadow source with the original rect because we're going to
     703             :     // draw both.
     704           0 :     return sourceBounds.Union(aDestBounds);
     705             :   }
     706             : 
     707             :   gfx::Rect
     708           0 :   BoundsAfterFilter(const gfx::Rect& aBounds, CanvasRenderingContext2D* aCtx)
     709             :   {
     710           0 :     const bool applyFilter = aCtx->NeedToApplyFilter();
     711           0 :     if (!aCtx->IsTargetValid()) {
     712           0 :       return aBounds;
     713             :     }
     714           0 :     if (!applyFilter) {
     715           0 :       return aBounds;
     716             :     }
     717             : 
     718           0 :     gfx::Rect bounds(aBounds);
     719           0 :     bounds.RoundOut();
     720             : 
     721           0 :     gfx::IntRect intBounds;
     722           0 :     if (!bounds.ToIntRect(&intBounds)) {
     723           0 :       return gfx::Rect();
     724             :     }
     725             : 
     726             :     nsIntRegion extents =
     727           0 :       gfx::FilterSupport::ComputePostFilterExtents(aCtx->CurrentState().filter,
     728           0 :                                                    intBounds);
     729           0 :     return gfx::Rect(extents.GetBounds());
     730             :   }
     731             : 
     732             :   RefPtr<DrawTarget> mTarget;
     733             :   UniquePtr<AdjustedTargetForShadow> mShadowTarget;
     734             :   UniquePtr<AdjustedTargetForFilter> mFilterTarget;
     735             : };
     736             : 
     737             : void
     738           0 : CanvasPattern::SetTransform(SVGMatrix& aMatrix)
     739             : {
     740           0 :   mTransform = ToMatrix(aMatrix.GetMatrix());
     741           0 : }
     742             : 
     743             : void
     744           0 : CanvasGradient::AddColorStop(float aOffset, const nsAString& aColorstr, ErrorResult& aRv)
     745             : {
     746           0 :   if (aOffset < 0.0 || aOffset > 1.0) {
     747           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     748           0 :     return;
     749             :   }
     750             : 
     751           0 :   nsCSSValue value;
     752           0 :   nsCSSParser parser;
     753           0 :   if (!parser.ParseColorString(aColorstr, nullptr, 0, value)) {
     754           0 :     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     755           0 :     return;
     756             :   }
     757             : 
     758             :   nscolor color;
     759           0 :   nsCOMPtr<nsIPresShell> presShell = mContext ? mContext->GetPresShell() : nullptr;
     760           0 :   if (!nsRuleNode::ComputeColor(value, presShell ? presShell->GetPresContext() : nullptr,
     761             :                                 nullptr, color)) {
     762           0 :     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     763           0 :     return;
     764             :   }
     765             : 
     766           0 :   mStops = nullptr;
     767             : 
     768           0 :   GradientStop newStop;
     769             : 
     770           0 :   newStop.offset = aOffset;
     771           0 :   newStop.color = Color::FromABGR(color);
     772             : 
     773           0 :   mRawStops.AppendElement(newStop);
     774             : }
     775             : 
     776           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
     777           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
     778             : 
     779           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
     780             : 
     781           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
     782           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
     783             : 
     784           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
     785             : 
     786             : class CanvasShutdownObserver final : public nsIObserver
     787             : {
     788             : public:
     789           0 :   explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
     790           0 :   : mCanvas(aCanvas)
     791           0 :   {}
     792             : 
     793             :   NS_DECL_ISUPPORTS
     794             :   NS_DECL_NSIOBSERVER
     795             : private:
     796           0 :   ~CanvasShutdownObserver() {}
     797             : 
     798             :   CanvasRenderingContext2D* mCanvas;
     799             : };
     800             : 
     801           0 : NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
     802             : 
     803             : NS_IMETHODIMP
     804           0 : CanvasShutdownObserver::Observe(nsISupports* aSubject,
     805             :                                 const char* aTopic,
     806             :                                 const char16_t* aData)
     807             : {
     808           0 :   if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
     809           0 :     mCanvas->OnShutdown();
     810           0 :     nsContentUtils::UnregisterShutdownObserver(this);
     811             :   }
     812             : 
     813           0 :   return NS_OK;
     814             : }
     815             : 
     816             : class CanvasDrawObserver
     817             : {
     818             : public:
     819             :   explicit CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
     820             : 
     821             :   // Only enumerate draw calls that could affect the heuristic
     822             :   enum DrawCallType {
     823             :     PutImageData,
     824             :     GetImageData,
     825             :     DrawImage
     826             :   };
     827             : 
     828             :   // This is the one that we call on relevant draw calls and count
     829             :   // GPU vs. CPU preferrable calls...
     830             :   void DidDrawCall(DrawCallType aType);
     831             : 
     832             :   // When this returns true, the observer is done making the decisions.
     833             :   // Right now, we expect to get rid of the observer after the FrameEnd
     834             :   // returns true, though the decision could eventually change if the
     835             :   // function calls shift.  If we change to monitor the functions called
     836             :   // and make decisions to change more than once, we would probably want
     837             :   // FrameEnd to reset the timer and counters as it returns true.
     838             :   bool FrameEnd();
     839             : 
     840             : private:
     841             :   // These values will be picked up from preferences:
     842             :   int32_t mMinFramesBeforeDecision;
     843             :   float mMinSecondsBeforeDecision;
     844             :   int32_t mMinCallsBeforeDecision;
     845             : 
     846             :   CanvasRenderingContext2D* mCanvasContext;
     847             :   int32_t mSoftwarePreferredCalls;
     848             :   int32_t mGPUPreferredCalls;
     849             :   int32_t mFramesRendered;
     850             :   TimeStamp mCreationTime;
     851             : };
     852             : 
     853             : // We are not checking for the validity of the preference values.  For example,
     854             : // negative values will have an effect of a quick exit, so no harm done.
     855           0 : CanvasDrawObserver::CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext)
     856           0 :  : mMinFramesBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinFrames())
     857           0 :  , mMinSecondsBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinSeconds())
     858           0 :  , mMinCallsBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinCalls())
     859             :  , mCanvasContext(aCanvasContext)
     860             :  , mSoftwarePreferredCalls(0)
     861             :  , mGPUPreferredCalls(0)
     862             :  , mFramesRendered(0)
     863           0 :  , mCreationTime(TimeStamp::NowLoRes())
     864           0 : {}
     865             : 
     866             : void
     867           0 : CanvasDrawObserver::DidDrawCall(DrawCallType aType)
     868             : {
     869           0 :   switch (aType) {
     870             :     case PutImageData:
     871             :     case GetImageData:
     872           0 :       if (mGPUPreferredCalls == 0 && mSoftwarePreferredCalls == 0) {
     873           0 :         mCreationTime = TimeStamp::NowLoRes();
     874             :       }
     875           0 :       mSoftwarePreferredCalls++;
     876           0 :       break;
     877             :     case DrawImage:
     878           0 :       if (mGPUPreferredCalls == 0 && mSoftwarePreferredCalls == 0) {
     879           0 :         mCreationTime = TimeStamp::NowLoRes();
     880             :       }
     881           0 :       mGPUPreferredCalls++;
     882           0 :       break;
     883             :   }
     884           0 : }
     885             : 
     886             : // If we return true, the observer is done making the decisions...
     887             : bool
     888           0 : CanvasDrawObserver::FrameEnd()
     889             : {
     890           0 :   mFramesRendered++;
     891             : 
     892             :   // We log the first mMinFramesBeforeDecision frames of any
     893             :   // canvas object then make a call to determine whether it should
     894             :   // be GPU or CPU backed
     895           0 :   if ((mFramesRendered >= mMinFramesBeforeDecision) ||
     896           0 :       ((TimeStamp::NowLoRes() - mCreationTime).ToSeconds()) > mMinSecondsBeforeDecision) {
     897             : 
     898             :     // If we don't have enough data, don't bother changing...
     899           0 :     if (mGPUPreferredCalls > mMinCallsBeforeDecision ||
     900           0 :         mSoftwarePreferredCalls > mMinCallsBeforeDecision) {
     901             :       CanvasRenderingContext2D::RenderingMode switchToMode;
     902           0 :       if (mGPUPreferredCalls >= mSoftwarePreferredCalls) {
     903           0 :         switchToMode = CanvasRenderingContext2D::RenderingMode::OpenGLBackendMode;
     904             :       } else {
     905           0 :         switchToMode = CanvasRenderingContext2D::RenderingMode::SoftwareBackendMode;
     906             :       }
     907           0 :       if (switchToMode != mCanvasContext->mRenderingMode) {
     908           0 :         if (!mCanvasContext->SwitchRenderingMode(switchToMode)) {
     909           0 :           gfxDebug() << "Canvas acceleration failed mode switch to " << switchToMode;
     910             :         }
     911             :       }
     912             :     }
     913             : 
     914             :     // If we ever redesign this class to constantly monitor the functions
     915             :     // and keep making decisions, we would probably want to reset the counters
     916             :     // and the timers here...
     917           0 :     return true;
     918             :   }
     919           0 :   return false;
     920             : }
     921             : 
     922             : class CanvasRenderingContext2DUserData : public LayerUserData {
     923             : public:
     924           0 :   explicit CanvasRenderingContext2DUserData(CanvasRenderingContext2D* aContext)
     925           0 :     : mContext(aContext)
     926             :   {
     927           0 :     aContext->mUserDatas.AppendElement(this);
     928           0 :   }
     929           0 :   ~CanvasRenderingContext2DUserData()
     930           0 :   {
     931           0 :     if (mContext) {
     932           0 :       mContext->mUserDatas.RemoveElement(this);
     933             :     }
     934           0 :   }
     935             : 
     936           0 :   static void PreTransactionCallback(void* aData)
     937             :   {
     938           0 :     auto self = static_cast<CanvasRenderingContext2DUserData*>(aData);
     939           0 :     CanvasRenderingContext2D* context = self->mContext;
     940           0 :     if (!context || !context->mTarget)
     941           0 :       return;
     942             : 
     943           0 :     context->OnStableState();
     944             :   }
     945             : 
     946           0 :   static void DidTransactionCallback(void* aData)
     947             :   {
     948           0 :     auto self = static_cast<CanvasRenderingContext2DUserData*>(aData);
     949           0 :     if (self->mContext) {
     950           0 :       self->mContext->MarkContextClean();
     951           0 :       if (self->mContext->mDrawObserver) {
     952           0 :         if (self->mContext->mDrawObserver->FrameEnd()) {
     953             :           // Note that this call deletes and nulls out mDrawObserver:
     954           0 :           self->mContext->RemoveDrawObserver();
     955             :         }
     956             :       }
     957             :     }
     958           0 :   }
     959           0 :   bool IsForContext(CanvasRenderingContext2D* aContext)
     960             :   {
     961           0 :     return mContext == aContext;
     962             :   }
     963           0 :   void Forget()
     964             :   {
     965           0 :     mContext = nullptr;
     966           0 :   }
     967             : 
     968             : private:
     969             :   CanvasRenderingContext2D* mContext;
     970             : };
     971             : 
     972           0 : class CanvasFilterChainObserver : public nsSVGFilterChainObserver
     973             : {
     974             : public:
     975           0 :   CanvasFilterChainObserver(nsTArray<nsStyleFilter>& aFilters,
     976             :                             Element* aCanvasElement,
     977             :                             CanvasRenderingContext2D* aContext)
     978           0 :     : nsSVGFilterChainObserver(aFilters, aCanvasElement)
     979           0 :     , mContext(aContext)
     980             :   {
     981           0 :   }
     982             : 
     983           0 :   virtual void DoUpdate() override
     984             :   {
     985           0 :     if (!mContext) {
     986           0 :       MOZ_CRASH("GFX: This should never be called without a context");
     987             :     }
     988             :     // Refresh the cached FilterDescription in mContext->CurrentState().filter.
     989             :     // If this filter is not at the top of the state stack, we'll refresh the
     990             :     // wrong filter, but that's ok, because we'll refresh the right filter
     991             :     // when we pop the state stack in CanvasRenderingContext2D::Restore().
     992           0 :     RefPtr<CanvasRenderingContext2D> kungFuDeathGrip(mContext);
     993           0 :     kungFuDeathGrip->UpdateFilter();
     994           0 :   }
     995             : 
     996           0 :   void DetachFromContext() { mContext = nullptr; }
     997             : 
     998             : private:
     999             :   CanvasRenderingContext2D* mContext;
    1000             : };
    1001             : 
    1002           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
    1003           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
    1004             : 
    1005             : NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
    1006             : 
    1007           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
    1008             :   // Make sure we remove ourselves from the list of demotable contexts (raw pointers),
    1009             :   // since we're logically destructed at this point.
    1010           0 :   CanvasRenderingContext2D::RemoveDemotableContext(tmp);
    1011           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
    1012           0 :   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
    1013           0 :     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
    1014           0 :     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
    1015           0 :     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
    1016           0 :     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
    1017             :     auto filterChainObserver =
    1018           0 :       static_cast<CanvasFilterChainObserver*>(tmp->mStyleStack[i].filterChainObserver.get());
    1019           0 :     if (filterChainObserver) {
    1020           0 :       filterChainObserver->DetachFromContext();
    1021             :     }
    1022           0 :     ImplCycleCollectionUnlink(tmp->mStyleStack[i].filterChainObserver);
    1023             :   }
    1024           0 :   for (size_t x = 0 ; x < tmp->mHitRegionsOptions.Length(); x++) {
    1025           0 :     RegionInfo& info = tmp->mHitRegionsOptions[x];
    1026           0 :     if (info.mElement) {
    1027           0 :       ImplCycleCollectionUnlink(info.mElement);
    1028             :     }
    1029             :   }
    1030           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    1031           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1032             : 
    1033           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
    1034           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
    1035           0 :   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
    1036           0 :     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::STROKE], "Stroke CanvasPattern");
    1037           0 :     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::FILL], "Fill CanvasPattern");
    1038           0 :     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE], "Stroke CanvasGradient");
    1039           0 :     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::FILL], "Fill CanvasGradient");
    1040           0 :     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].filterChainObserver, "Filter Chain Observer");
    1041             :   }
    1042           0 :   for (size_t x = 0 ; x < tmp->mHitRegionsOptions.Length(); x++) {
    1043           0 :     RegionInfo& info = tmp->mHitRegionsOptions[x];
    1044           0 :     if (info.mElement) {
    1045           0 :       ImplCycleCollectionTraverse(cb, info.mElement, "Hit region fallback element");
    1046             :     }
    1047             :   }
    1048           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1049             : 
    1050           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(CanvasRenderingContext2D)
    1051             : 
    1052           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
    1053           0 :  if (nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper()) {
    1054           0 :    dom::Element* canvasElement = tmp->mCanvasElement;
    1055           0 :     if (canvasElement) {
    1056           0 :       if (canvasElement->IsPurple()) {
    1057           0 :         canvasElement->RemovePurple();
    1058             :       }
    1059           0 :       dom::Element::MarkNodeChildren(canvasElement);
    1060             :     }
    1061           0 :     return true;
    1062             :   }
    1063           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
    1064             : 
    1065           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
    1066           0 :   return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
    1067             : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
    1068             : 
    1069           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
    1070           0 :   return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
    1071             : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
    1072             : 
    1073           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
    1074           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    1075           0 :   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
    1076           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
    1077           0 : NS_INTERFACE_MAP_END
    1078             : 
    1079             : /**
    1080             :  ** CanvasRenderingContext2D impl
    1081             :  **/
    1082             : 
    1083             : 
    1084             : // Initialize our static variables.
    1085             : uintptr_t CanvasRenderingContext2D::sNumLivingContexts = 0;
    1086             : DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
    1087             : static bool sMaxContextsInitialized = false;
    1088             : static int32_t sMaxContexts = 0;
    1089             : 
    1090             : 
    1091             : 
    1092           0 : CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
    1093             :   : mRenderingMode(RenderingMode::OpenGLBackendMode)
    1094             :   , mCompositorBackend(aCompositorBackend)
    1095             :   // these are the default values from the Canvas spec
    1096             :   , mWidth(0), mHeight(0)
    1097             :   , mZero(false), mOpaque(false)
    1098             :   , mResetLayer(true)
    1099             :   , mIPC(false)
    1100             :   , mIsSkiaGL(false)
    1101             :   , mHasPendingStableStateCallback(false)
    1102             :   , mDrawObserver(nullptr)
    1103             :   , mIsEntireFrameInvalid(false)
    1104             :   , mPredictManyRedrawCalls(false)
    1105             :   , mIsCapturedFrameInvalid(false)
    1106             :   , mPathTransformWillUpdate(false)
    1107           0 :   , mInvalidateCount(0)
    1108             : {
    1109           0 :   if (!sMaxContextsInitialized) {
    1110           0 :     sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
    1111           0 :     sMaxContextsInitialized = true;
    1112             :   }
    1113             : 
    1114           0 :   sNumLivingContexts++;
    1115             : 
    1116           0 :   mShutdownObserver = new CanvasShutdownObserver(this);
    1117           0 :   nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
    1118             : 
    1119             :   // The default is to use OpenGL mode
    1120           0 :   if (AllowOpenGLCanvas()) {
    1121           0 :     mDrawObserver = new CanvasDrawObserver(this);
    1122             :   } else {
    1123           0 :     mRenderingMode = RenderingMode::SoftwareBackendMode;
    1124             :   }
    1125           0 : }
    1126             : 
    1127           0 : CanvasRenderingContext2D::~CanvasRenderingContext2D()
    1128             : {
    1129           0 :   RemoveDrawObserver();
    1130           0 :   RemovePostRefreshObserver();
    1131           0 :   RemoveShutdownObserver();
    1132           0 :   Reset();
    1133             :   // Drop references from all CanvasRenderingContext2DUserData to this context
    1134           0 :   for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
    1135           0 :     mUserDatas[i]->Forget();
    1136             :   }
    1137           0 :   sNumLivingContexts--;
    1138           0 :   if (!sNumLivingContexts) {
    1139           0 :     NS_IF_RELEASE(sErrorTarget);
    1140             :   }
    1141           0 :   RemoveDemotableContext(this);
    1142           0 : }
    1143             : 
    1144             : JSObject*
    1145           0 : CanvasRenderingContext2D::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
    1146             : {
    1147           0 :   return CanvasRenderingContext2DBinding::Wrap(aCx, this, aGivenProto);
    1148             : }
    1149             : 
    1150             : bool
    1151           0 : CanvasRenderingContext2D::ParseColor(const nsAString& aString,
    1152             :                                      nscolor* aColor)
    1153             : {
    1154             :   nsIDocument* document = mCanvasElement
    1155           0 :                           ? mCanvasElement->OwnerDoc()
    1156           0 :                           : nullptr;
    1157             : 
    1158             :   // Pass the CSS Loader object to the parser, to allow parser error
    1159             :   // reports to include the outer window ID.
    1160           0 :   nsCSSParser parser(document ? document->CSSLoader() : nullptr);
    1161           0 :   nsCSSValue value;
    1162           0 :   if (!parser.ParseColorString(aString, nullptr, 0, value)) {
    1163           0 :     return false;
    1164             :   }
    1165             : 
    1166           0 :   if (value.IsNumericColorUnit()) {
    1167             :     // if we already have a color we can just use it directly
    1168           0 :     *aColor = value.GetColorValue();
    1169             :   } else {
    1170             :     // otherwise resolve it
    1171           0 :     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    1172           0 :     RefPtr<nsStyleContext> parentContext;
    1173           0 :     if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) {
    1174             :       // Inherit from the canvas element.
    1175           0 :       parentContext = nsComputedDOMStyle::GetStyleContext(mCanvasElement,
    1176             :                                                           nullptr,
    1177           0 :                                                           presShell);
    1178             :     }
    1179             : 
    1180           0 :     Unused << nsRuleNode::ComputeColor(
    1181           0 :       value, presShell ? presShell->GetPresContext() : nullptr, parentContext,
    1182             :       *aColor);
    1183             :   }
    1184           0 :   return true;
    1185             : }
    1186             : 
    1187             : nsresult
    1188           0 : CanvasRenderingContext2D::Reset()
    1189             : {
    1190           0 :   if (mCanvasElement) {
    1191           0 :     mCanvasElement->InvalidateCanvas();
    1192             :   }
    1193             : 
    1194             :   // only do this for non-docshell created contexts,
    1195             :   // since those are the ones that we created a surface for
    1196           0 :   if (mTarget && IsTargetValid() && !mDocShell) {
    1197           0 :     gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
    1198             :   }
    1199             : 
    1200           0 :   bool forceReset = true;
    1201           0 :   ReturnTarget(forceReset);
    1202           0 :   mTarget = nullptr;
    1203           0 :   mBufferProvider = nullptr;
    1204             : 
    1205             :   // reset hit regions
    1206           0 :   mHitRegionsOptions.ClearAndRetainStorage();
    1207             : 
    1208             :   // Since the target changes the backing texture will change, and this will
    1209             :   // no longer be valid.
    1210           0 :   mIsEntireFrameInvalid = false;
    1211           0 :   mPredictManyRedrawCalls = false;
    1212           0 :   mIsCapturedFrameInvalid = false;
    1213             : 
    1214           0 :   return NS_OK;
    1215             : }
    1216             : 
    1217             : void
    1218           0 : CanvasRenderingContext2D::OnShutdown()
    1219             : {
    1220           0 :   mShutdownObserver = nullptr;
    1221             : 
    1222           0 :   RefPtr<PersistentBufferProvider> provider = mBufferProvider;
    1223             : 
    1224           0 :   Reset();
    1225             : 
    1226           0 :   if (provider) {
    1227           0 :     provider->OnShutdown();
    1228             :   }
    1229           0 : }
    1230             : 
    1231             : void
    1232           0 : CanvasRenderingContext2D::RemoveShutdownObserver()
    1233             : {
    1234           0 :   if (mShutdownObserver) {
    1235           0 :     nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
    1236           0 :     mShutdownObserver = nullptr;
    1237             :   }
    1238           0 : }
    1239             : 
    1240             : void
    1241           0 : CanvasRenderingContext2D::SetStyleFromString(const nsAString& aStr,
    1242             :                                              Style aWhichStyle)
    1243             : {
    1244           0 :   MOZ_ASSERT(!aStr.IsVoid());
    1245             : 
    1246             :   nscolor color;
    1247           0 :   if (!ParseColor(aStr, &color)) {
    1248           0 :     return;
    1249             :   }
    1250             : 
    1251           0 :   CurrentState().SetColorStyle(aWhichStyle, color);
    1252             : }
    1253             : 
    1254             : void
    1255           0 : CanvasRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
    1256             :                                           Style aWhichStyle)
    1257             : {
    1258           0 :   const ContextState& state = CurrentState();
    1259           0 :   if (state.patternStyles[aWhichStyle]) {
    1260           0 :     aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
    1261           0 :   } else if (state.gradientStyles[aWhichStyle]) {
    1262           0 :     aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
    1263             :   } else {
    1264           0 :     StyleColorToString(state.colorStyles[aWhichStyle], aValue.SetAsString());
    1265             :   }
    1266           0 : }
    1267             : 
    1268             : // static
    1269             : void
    1270           0 : CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
    1271             : {
    1272             :   // We can't reuse the normal CSS color stringification code,
    1273             :   // because the spec calls for a different algorithm for canvas.
    1274           0 :   if (NS_GET_A(aColor) == 255) {
    1275           0 :     CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
    1276           0 :                                     NS_GET_R(aColor),
    1277           0 :                                     NS_GET_G(aColor),
    1278           0 :                                     NS_GET_B(aColor)),
    1279           0 :                     aStr);
    1280             :   } else {
    1281           0 :     CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
    1282           0 :                                     NS_GET_R(aColor),
    1283           0 :                                     NS_GET_G(aColor),
    1284           0 :                                     NS_GET_B(aColor)),
    1285           0 :                     aStr);
    1286           0 :     aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
    1287           0 :     aStr.Append(')');
    1288             :   }
    1289           0 : }
    1290             : 
    1291             : nsresult
    1292           0 : CanvasRenderingContext2D::Redraw()
    1293             : {
    1294           0 :   mIsCapturedFrameInvalid = true;
    1295             : 
    1296           0 :   if (mIsEntireFrameInvalid) {
    1297           0 :     return NS_OK;
    1298             :   }
    1299             : 
    1300           0 :   mIsEntireFrameInvalid = true;
    1301             : 
    1302           0 :   if (!mCanvasElement) {
    1303           0 :     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
    1304           0 :     return NS_OK;
    1305             :   }
    1306             : 
    1307           0 :   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
    1308             : 
    1309           0 :   mCanvasElement->InvalidateCanvasContent(nullptr);
    1310             : 
    1311           0 :   return NS_OK;
    1312             : }
    1313             : 
    1314             : void
    1315           0 : CanvasRenderingContext2D::Redraw(const gfx::Rect& aR)
    1316             : {
    1317           0 :   mIsCapturedFrameInvalid = true;
    1318             : 
    1319           0 :   ++mInvalidateCount;
    1320             : 
    1321           0 :   if (mIsEntireFrameInvalid) {
    1322           0 :     return;
    1323             :   }
    1324             : 
    1325           0 :   if (mPredictManyRedrawCalls ||
    1326           0 :     mInvalidateCount > kCanvasMaxInvalidateCount) {
    1327           0 :     Redraw();
    1328           0 :     return;
    1329             :   }
    1330             : 
    1331           0 :   if (!mCanvasElement) {
    1332           0 :     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
    1333           0 :     return;
    1334             :   }
    1335             : 
    1336           0 :   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
    1337             : 
    1338           0 :   mCanvasElement->InvalidateCanvasContent(&aR);
    1339             : }
    1340             : 
    1341             : void
    1342           0 : CanvasRenderingContext2D::DidRefresh()
    1343             : {
    1344           0 :   if (IsTargetValid() && mIsSkiaGL) {
    1345           0 :     SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
    1346           0 :     MOZ_ASSERT(glue);
    1347             : 
    1348           0 :     auto gl = glue->GetGLContext();
    1349           0 :     gl->FlushIfHeavyGLCallsSinceLastFlush();
    1350             :   }
    1351           0 : }
    1352             : 
    1353             : void
    1354           0 : CanvasRenderingContext2D::RedrawUser(const gfxRect& aR)
    1355             : {
    1356           0 :   mIsCapturedFrameInvalid = true;
    1357             : 
    1358           0 :   if (mIsEntireFrameInvalid) {
    1359           0 :     ++mInvalidateCount;
    1360           0 :     return;
    1361             :   }
    1362             : 
    1363           0 :   gfx::Rect newr = mTarget->GetTransform().TransformBounds(ToRect(aR));
    1364           0 :   Redraw(newr);
    1365             : }
    1366             : 
    1367             : bool
    1368           0 : CanvasRenderingContext2D::AllowOpenGLCanvas() const
    1369             : {
    1370             :   // If we somehow didn't have the correct compositor in the constructor,
    1371             :   // we could do something like this to get it:
    1372             :   //
    1373             :   // HTMLCanvasElement* el = GetCanvas();
    1374             :   // if (el) {
    1375             :   //   mCompositorBackend = el->GetCompositorBackendType();
    1376             :   // }
    1377             :   //
    1378             :   // We could have LAYERS_NONE if there was no widget at the time of
    1379             :   // canvas creation, but in that case the
    1380             :   // HTMLCanvasElement::GetCompositorBackendType would return LAYERS_NONE
    1381             :   // as well, so it wouldn't help much.
    1382             : 
    1383           0 :   return (mCompositorBackend == LayersBackend::LAYERS_OPENGL) &&
    1384           0 :     gfxPlatform::GetPlatform()->AllowOpenGLCanvas();
    1385             : }
    1386             : 
    1387           0 : bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode)
    1388             : {
    1389           0 :   if (!(IsTargetValid() || mBufferProvider) || mRenderingMode == aRenderingMode) {
    1390           0 :     return false;
    1391             :   }
    1392             : 
    1393           0 :   MOZ_ASSERT(mBufferProvider);
    1394             : 
    1395             : #ifdef USE_SKIA_GPU
    1396             :   // Do not attempt to switch into GL mode if the platform doesn't allow it.
    1397           0 :   if ((aRenderingMode == RenderingMode::OpenGLBackendMode) &&
    1398           0 :       !AllowOpenGLCanvas()) {
    1399           0 :       return false;
    1400             :   }
    1401             : #endif
    1402             : 
    1403           0 :   RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
    1404             : 
    1405             :   // Return the old target to the buffer provider.
    1406             :   // We need to do this before calling EnsureTarget.
    1407           0 :   ReturnTarget();
    1408           0 :   mTarget = nullptr;
    1409           0 :   mBufferProvider = nullptr;
    1410           0 :   mResetLayer = true;
    1411             : 
    1412             :   // Recreate mTarget using the new rendering mode
    1413           0 :   RenderingMode attemptedMode = EnsureTarget(nullptr, aRenderingMode);
    1414           0 :   if (!IsTargetValid()) {
    1415           0 :     return false;
    1416             :   }
    1417             : 
    1418           0 :   if (oldBufferProvider && mTarget) {
    1419           0 :     CopyBufferProvider(*oldBufferProvider, *mTarget, IntRect(0, 0, mWidth, mHeight));
    1420             :   }
    1421             : 
    1422             :   // We succeeded, so update mRenderingMode to reflect reality
    1423           0 :   mRenderingMode = attemptedMode;
    1424             : 
    1425           0 :   return true;
    1426             : }
    1427             : 
    1428             : bool
    1429           0 : CanvasRenderingContext2D::CopyBufferProvider(PersistentBufferProvider& aOld,
    1430             :                                              DrawTarget& aTarget,
    1431             :                                              IntRect aCopyRect)
    1432             : {
    1433             :   // Borrowing the snapshot must be done after ReturnTarget.
    1434           0 :   RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();
    1435             : 
    1436           0 :   if (!snapshot) {
    1437           0 :     return false;
    1438             :   }
    1439             : 
    1440           0 :   aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
    1441           0 :   aOld.ReturnSnapshot(snapshot.forget());
    1442           0 :   return true;
    1443             : }
    1444             : 
    1445           0 : void CanvasRenderingContext2D::Demote()
    1446             : {
    1447           0 :   if (SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
    1448           0 :     RemoveDemotableContext(this);
    1449             :   }
    1450           0 : }
    1451             : 
    1452             : std::vector<CanvasRenderingContext2D*>&
    1453           0 : CanvasRenderingContext2D::DemotableContexts()
    1454             : {
    1455             :   // This is a list of raw pointers to cycle-collected objects. We need to ensure
    1456             :   // that we remove elements from it during UNLINK (which can happen considerably before
    1457             :   // the actual destructor) since the object is logically destroyed at that point
    1458             :   // and will be in an inconsistant state.
    1459           0 :   static std::vector<CanvasRenderingContext2D*> contexts;
    1460           0 :   return contexts;
    1461             : }
    1462             : 
    1463             : void
    1464           0 : CanvasRenderingContext2D::DemoteOldestContextIfNecessary()
    1465             : {
    1466           0 :   MOZ_ASSERT(sMaxContextsInitialized);
    1467           0 :   if (sMaxContexts <= 0) {
    1468           0 :     return;
    1469             :   }
    1470             : 
    1471           0 :   std::vector<CanvasRenderingContext2D*>& contexts = DemotableContexts();
    1472           0 :   if (contexts.size() < (size_t)sMaxContexts)
    1473           0 :     return;
    1474             : 
    1475           0 :   CanvasRenderingContext2D* oldest = contexts.front();
    1476           0 :   if (oldest->SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
    1477           0 :     RemoveDemotableContext(oldest);
    1478             :   }
    1479             : }
    1480             : 
    1481             : void
    1482           0 : CanvasRenderingContext2D::AddDemotableContext(CanvasRenderingContext2D* aContext)
    1483             : {
    1484           0 :   MOZ_ASSERT(sMaxContextsInitialized);
    1485           0 :   if (sMaxContexts <= 0)
    1486           0 :     return;
    1487             : 
    1488           0 :   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), aContext);
    1489           0 :   if (iter != DemotableContexts().end())
    1490           0 :     return;
    1491             : 
    1492           0 :   DemotableContexts().push_back(aContext);
    1493             : }
    1494             : 
    1495             : void
    1496           0 : CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D* aContext)
    1497             : {
    1498           0 :   MOZ_ASSERT(sMaxContextsInitialized);
    1499           0 :   if (sMaxContexts <= 0)
    1500           0 :     return;
    1501             : 
    1502           0 :   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), aContext);
    1503           0 :   if (iter != DemotableContexts().end())
    1504           0 :     DemotableContexts().erase(iter);
    1505             : }
    1506             : 
    1507             : #define MIN_SKIA_GL_DIMENSION 16
    1508             : 
    1509             : bool
    1510           0 : CanvasRenderingContext2D::CheckSizeForSkiaGL(IntSize aSize) {
    1511           0 :   MOZ_ASSERT(NS_IsMainThread());
    1512             : 
    1513           0 :   int minsize = Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128);
    1514           0 :   if (aSize.width < MIN_SKIA_GL_DIMENSION || aSize.height < MIN_SKIA_GL_DIMENSION ||
    1515           0 :       (aSize.width * aSize.height < minsize * minsize)) {
    1516           0 :     return false;
    1517             :   }
    1518             : 
    1519             :   // Maximum pref allows 3 different options:
    1520             :   //  0   means unlimited size
    1521             :   //  > 0 means use value as an absolute threshold
    1522             :   //  < 0 means use the number of screen pixels as a threshold
    1523           0 :   int maxsize = Preferences::GetInt("gfx.canvas.max-size-for-skia-gl", 0);
    1524             : 
    1525             :   // unlimited max size
    1526           0 :   if (!maxsize) {
    1527           0 :     return true;
    1528             :   }
    1529             : 
    1530             :   // absolute max size threshold
    1531           0 :   if (maxsize > 0) {
    1532           0 :     return aSize.width <= maxsize && aSize.height <= maxsize;
    1533             :   }
    1534             : 
    1535             :   // Cache the number of pixels on the primary screen
    1536             :   static int32_t gScreenPixels = -1;
    1537           0 :   if (gScreenPixels < 0) {
    1538             :     // Default to historical mobile screen size of 980x480, like FishIEtank.
    1539             :     // In addition, allow skia use up to this size even if the screen is smaller.
    1540             :     // A lot content expects this size to work well.
    1541             :     // See Bug 999841
    1542           0 :     if (gfxPlatform::GetPlatform()->HasEnoughTotalSystemMemoryForSkiaGL()) {
    1543           0 :       gScreenPixels = 980 * 480;
    1544             :     }
    1545             : 
    1546             :     nsCOMPtr<nsIScreenManager> screenManager =
    1547           0 :       do_GetService("@mozilla.org/gfx/screenmanager;1");
    1548           0 :     if (screenManager) {
    1549           0 :       nsCOMPtr<nsIScreen> primaryScreen;
    1550           0 :       screenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
    1551           0 :       if (primaryScreen) {
    1552             :         int32_t x, y, width, height;
    1553           0 :         primaryScreen->GetRect(&x, &y, &width, &height);
    1554             : 
    1555           0 :         gScreenPixels = std::max(gScreenPixels, width * height);
    1556             :       }
    1557             :     }
    1558             :   }
    1559             : 
    1560             :   // Just always use a scale of 1.0. It can be changed if a lot of contents need it.
    1561             :   static double gDefaultScale = 1.0;
    1562             : 
    1563           0 :   double scale = gDefaultScale > 0 ? gDefaultScale : 1.0;
    1564           0 :   int32_t threshold = ceil(scale * scale * gScreenPixels);
    1565             : 
    1566             :   // screen size acts as max threshold
    1567           0 :   return threshold < 0 || (aSize.width * aSize.height) <= threshold;
    1568             : }
    1569             : 
    1570             : void
    1571           0 : CanvasRenderingContext2D::ScheduleStableStateCallback()
    1572             : {
    1573           0 :   if (mHasPendingStableStateCallback) {
    1574           0 :     return;
    1575             :   }
    1576           0 :   mHasPendingStableStateCallback = true;
    1577             : 
    1578           0 :   nsContentUtils::RunInStableState(
    1579           0 :     NewRunnableMethod("dom::CanvasRenderingContext2D::OnStableState",
    1580             :                       this,
    1581           0 :                       &CanvasRenderingContext2D::OnStableState));
    1582             : }
    1583             : 
    1584             : void
    1585           0 : CanvasRenderingContext2D::OnStableState()
    1586             : {
    1587           0 :   if (!mHasPendingStableStateCallback) {
    1588           0 :     return;
    1589             :   }
    1590             : 
    1591           0 :   ReturnTarget();
    1592             : 
    1593           0 :   mHasPendingStableStateCallback = false;
    1594             : }
    1595             : 
    1596             : void
    1597           0 : CanvasRenderingContext2D::RestoreClipsAndTransformToTarget()
    1598             : {
    1599             :   // Restore clips and transform.
    1600           0 :   mTarget->SetTransform(Matrix());
    1601             : 
    1602           0 :   if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
    1603             :     // Cairo doesn't play well with huge clips. When given a very big clip it
    1604             :     // will try to allocate big mask surface without taking the target
    1605             :     // size into account which can cause OOM. See bug 1034593.
    1606             :     // This limits the clip extents to the size of the canvas.
    1607             :     // A fix in Cairo would probably be preferable, but requires somewhat
    1608             :     // invasive changes.
    1609           0 :     mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
    1610             :   }
    1611             : 
    1612           0 :   for (const auto& style : mStyleStack) {
    1613           0 :     for (const auto& clipOrTransform : style.clipsAndTransforms) {
    1614           0 :       if (clipOrTransform.IsClip()) {
    1615           0 :         mTarget->PushClip(clipOrTransform.clip);
    1616             :       } else {
    1617           0 :         mTarget->SetTransform(clipOrTransform.transform);
    1618             :       }
    1619             :     }
    1620             :   }
    1621           0 : }
    1622             : 
    1623             : CanvasRenderingContext2D::RenderingMode
    1624           0 : CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
    1625             :                                        RenderingMode aRenderingMode)
    1626             : {
    1627           0 :   if (AlreadyShutDown()) {
    1628           0 :     gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
    1629           0 :     SetErrorState();
    1630           0 :     return aRenderingMode;
    1631             :   }
    1632             : 
    1633             :   // This would make no sense, so make sure we don't get ourselves in a mess
    1634           0 :   MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
    1635             : 
    1636           0 :   RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
    1637             : 
    1638           0 :   if (mTarget && mode == mRenderingMode) {
    1639           0 :     return mRenderingMode;
    1640             :   }
    1641             : 
    1642             :   // Check that the dimensions are sane
    1643           0 :   if (mWidth > gfxPrefs::MaxCanvasSize() ||
    1644           0 :       mHeight > gfxPrefs::MaxCanvasSize() ||
    1645           0 :       mWidth < 0 || mHeight < 0) {
    1646             : 
    1647           0 :     SetErrorState();
    1648           0 :     return aRenderingMode;
    1649             :   }
    1650             : 
    1651             :   // If the next drawing command covers the entire canvas, we can skip copying
    1652             :   // from the previous frame and/or clearing the canvas.
    1653           0 :   gfx::Rect canvasRect(0, 0, mWidth, mHeight);
    1654           0 :   bool canDiscardContent = aCoveredRect &&
    1655           0 :     CurrentState().transform.TransformBounds(*aCoveredRect).Contains(canvasRect);
    1656             : 
    1657             :   // If a clip is active we don't know for sure that the next drawing command
    1658             :   // will really cover the entire canvas.
    1659           0 :   for (const auto& style : mStyleStack) {
    1660           0 :     if (!canDiscardContent) {
    1661           0 :       break;
    1662             :     }
    1663           0 :     for (const auto& clipOrTransform : style.clipsAndTransforms) {
    1664           0 :       if (clipOrTransform.IsClip()) {
    1665           0 :         canDiscardContent = false;
    1666           0 :         break;
    1667             :       }
    1668             :     }
    1669             :   }
    1670             : 
    1671           0 :   ScheduleStableStateCallback();
    1672             : 
    1673             :   IntRect persistedRect = canDiscardContent ? IntRect()
    1674           0 :                                             : IntRect(0, 0, mWidth, mHeight);
    1675             : 
    1676           0 :   if (mBufferProvider && mode == mRenderingMode) {
    1677           0 :     mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
    1678             : 
    1679           0 :     if (mTarget && !mBufferProvider->PreservesDrawingState()) {
    1680           0 :       RestoreClipsAndTransformToTarget();
    1681             :     }
    1682             : 
    1683           0 :     if (mTarget) {
    1684           0 :       return mode;
    1685             :     }
    1686             :   }
    1687             : 
    1688           0 :   RefPtr<DrawTarget> newTarget;
    1689           0 :   RefPtr<PersistentBufferProvider> newProvider;
    1690             : 
    1691           0 :   if (mode == RenderingMode::OpenGLBackendMode &&
    1692           0 :       !TrySkiaGLTarget(newTarget, newProvider)) {
    1693             :     // Fall back to software.
    1694           0 :     mode = RenderingMode::SoftwareBackendMode;
    1695             :   }
    1696             : 
    1697           0 :   if (mode == RenderingMode::SoftwareBackendMode &&
    1698           0 :       !TrySharedTarget(newTarget, newProvider) &&
    1699           0 :       !TryBasicTarget(newTarget, newProvider)) {
    1700             : 
    1701           0 :     gfxCriticalError(
    1702           0 :       CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))
    1703           0 :     ) << "Failed borrow shared and basic targets.";
    1704             : 
    1705           0 :     SetErrorState();
    1706           0 :     return mode;
    1707             :   }
    1708             : 
    1709             : 
    1710           0 :   MOZ_ASSERT(newTarget);
    1711           0 :   MOZ_ASSERT(newProvider);
    1712             : 
    1713           0 :   bool needsClear = !canDiscardContent;
    1714           0 :   if (newTarget->GetBackendType() == gfx::BackendType::SKIA) {
    1715             :     // Skia expects the unused X channel to contains 0xFF even for opaque operations
    1716             :     // so we can't skip clearing in that case, even if we are going to cover the
    1717             :     // entire canvas in the next drawing operation.
    1718           0 :     newTarget->ClearRect(canvasRect);
    1719           0 :     needsClear = false;
    1720             :   }
    1721             : 
    1722             :   // Try to copy data from the previous buffer provider if there is one.
    1723           0 :   if (!canDiscardContent && mBufferProvider && CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
    1724           0 :     needsClear = false;
    1725             :   }
    1726             : 
    1727           0 :   if (needsClear) {
    1728           0 :     newTarget->ClearRect(canvasRect);
    1729             :   }
    1730             : 
    1731           0 :   mTarget = newTarget.forget();
    1732           0 :   mBufferProvider = newProvider.forget();
    1733             : 
    1734           0 :   RegisterAllocation();
    1735             : 
    1736           0 :   RestoreClipsAndTransformToTarget();
    1737             : 
    1738             :   // Force a full layer transaction since we didn't have a layer before
    1739             :   // and now we might need one.
    1740           0 :   if (mCanvasElement) {
    1741           0 :     mCanvasElement->InvalidateCanvas();
    1742             :   }
    1743             :   // Calling Redraw() tells our invalidation machinery that the entire
    1744             :   // canvas is already invalid, which can speed up future drawing.
    1745           0 :   Redraw();
    1746             : 
    1747           0 :   return mode;
    1748             : }
    1749             : 
    1750             : void
    1751           0 : CanvasRenderingContext2D::SetInitialState()
    1752             : {
    1753             :   // Set up the initial canvas defaults
    1754           0 :   mPathBuilder = nullptr;
    1755           0 :   mPath = nullptr;
    1756           0 :   mDSPathBuilder = nullptr;
    1757           0 :   mPathTransformWillUpdate = false;
    1758             : 
    1759           0 :   mStyleStack.Clear();
    1760           0 :   ContextState* state = mStyleStack.AppendElement();
    1761           0 :   state->globalAlpha = 1.0;
    1762             : 
    1763           0 :   state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
    1764           0 :   state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
    1765           0 :   state->shadowColor = NS_RGBA(0,0,0,0);
    1766           0 : }
    1767             : 
    1768             : void
    1769           0 : CanvasRenderingContext2D::SetErrorState()
    1770             : {
    1771           0 :   EnsureErrorTarget();
    1772             : 
    1773           0 :   if (mTarget && mTarget != sErrorTarget) {
    1774           0 :     gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
    1775             :   }
    1776             : 
    1777           0 :   mTarget = sErrorTarget;
    1778           0 :   mBufferProvider = nullptr;
    1779             : 
    1780             :   // clear transforms, clips, etc.
    1781           0 :   SetInitialState();
    1782           0 : }
    1783             : 
    1784             : void
    1785           0 : CanvasRenderingContext2D::RegisterAllocation()
    1786             : {
    1787             :   // XXX - It would make more sense to track the allocation in
    1788             :   // PeristentBufferProvider, rather than here.
    1789             :   static bool registered = false;
    1790             :   // FIXME: Disable the reporter for now, see bug 1241865
    1791             :   if (!registered && false) {
    1792             :     registered = true;
    1793             :     RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
    1794             :   }
    1795             : 
    1796           0 :   gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
    1797           0 :   JSContext* context = nsContentUtils::GetCurrentJSContext();
    1798           0 :   if (context) {
    1799           0 :     JS_updateMallocCounter(context, mWidth * mHeight * 4);
    1800             :   }
    1801             : 
    1802           0 :   JSObject* wrapper = GetWrapperPreserveColor();
    1803           0 :   if (wrapper) {
    1804             :     CycleCollectedJSRuntime::Get()->
    1805           0 :       AddZoneWaitingForGC(JS::GetObjectZone(wrapper));
    1806             :   }
    1807           0 : }
    1808             : 
    1809             : static already_AddRefed<LayerManager>
    1810           0 : LayerManagerFromCanvasElement(nsINode* aCanvasElement)
    1811             : {
    1812           0 :   if (!aCanvasElement || !aCanvasElement->OwnerDoc()) {
    1813           0 :     return nullptr;
    1814             :   }
    1815             : 
    1816           0 :   return nsContentUtils::PersistentLayerManagerForDocument(aCanvasElement->OwnerDoc());
    1817             : }
    1818             : 
    1819             : bool
    1820           0 : CanvasRenderingContext2D::TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
    1821             :                                           RefPtr<layers::PersistentBufferProvider>& aOutProvider)
    1822             : {
    1823           0 :   aOutDT = nullptr;
    1824           0 :   aOutProvider = nullptr;
    1825             : 
    1826           0 :   mIsSkiaGL = false;
    1827             : 
    1828           0 :   IntSize size(mWidth, mHeight);
    1829           0 :   if (!AllowOpenGLCanvas() || !CheckSizeForSkiaGL(size)) {
    1830           0 :     return false;
    1831             :   }
    1832             : 
    1833             : 
    1834           0 :   RefPtr<LayerManager> layerManager = LayerManagerFromCanvasElement(mCanvasElement);
    1835             : 
    1836           0 :   if (!layerManager) {
    1837           0 :     return false;
    1838             :   }
    1839             : 
    1840           0 :   DemoteOldestContextIfNecessary();
    1841           0 :   mBufferProvider = nullptr;
    1842             : 
    1843             : #ifdef USE_SKIA_GPU
    1844           0 :   SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
    1845           0 :   if (!glue || !glue->GetGrContext() || !glue->GetGLContext()) {
    1846           0 :     return false;
    1847             :   }
    1848             : 
    1849           0 :   SurfaceFormat format = GetSurfaceFormat();
    1850           0 :   aOutDT = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(),
    1851           0 :                                                       size, format);
    1852           0 :   if (!aOutDT) {
    1853           0 :     gfxCriticalNote << "Failed to create a SkiaGL DrawTarget, falling back to software\n";
    1854           0 :     return false;
    1855             :   }
    1856             : 
    1857           0 :   MOZ_ASSERT(aOutDT->GetType() == DrawTargetType::HARDWARE_RASTER);
    1858             : 
    1859           0 :   AddDemotableContext(this);
    1860           0 :   aOutProvider = new PersistentBufferProviderBasic(aOutDT);
    1861           0 :   mIsSkiaGL = true;
    1862             :   // Drop a note in the debug builds if we ever use accelerated Skia canvas.
    1863           0 :   gfxWarningOnce() << "Using SkiaGL canvas.";
    1864             : #endif
    1865             : 
    1866             :   // could still be null if USE_SKIA_GPU is not #defined.
    1867           0 :   return !!aOutDT;
    1868             : }
    1869             : 
    1870             : bool
    1871           0 : CanvasRenderingContext2D::TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
    1872             :                                           RefPtr<layers::PersistentBufferProvider>& aOutProvider)
    1873             : {
    1874           0 :   aOutDT = nullptr;
    1875           0 :   aOutProvider = nullptr;
    1876             : 
    1877           0 :   if (!mCanvasElement || !mCanvasElement->OwnerDoc()) {
    1878           0 :     return false;
    1879             :   }
    1880             : 
    1881           0 :   if (mBufferProvider && mBufferProvider->GetType() == LayersBackend::LAYERS_CLIENT) {
    1882             :     // we are already using a shared buffer provider, we are allocating a new one
    1883             :     // because the current one failed so let's just fall back to the basic provider.
    1884           0 :     return false;
    1885             :   }
    1886             : 
    1887           0 :   RefPtr<LayerManager> layerManager = LayerManagerFromCanvasElement(mCanvasElement);
    1888             : 
    1889           0 :   if (!layerManager) {
    1890           0 :     return false;
    1891             :   }
    1892             : 
    1893           0 :   aOutProvider = layerManager->CreatePersistentBufferProvider(GetSize(), GetSurfaceFormat());
    1894             : 
    1895           0 :   if (!aOutProvider) {
    1896           0 :     return false;
    1897             :   }
    1898             : 
    1899             :   // We can pass an empty persisted rect since we just created the buffer
    1900             :   // provider (nothing to restore).
    1901           0 :   aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
    1902           0 :   MOZ_ASSERT(aOutDT);
    1903             : 
    1904           0 :   return !!aOutDT;
    1905             : }
    1906             : 
    1907             : bool
    1908           0 : CanvasRenderingContext2D::TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
    1909             :                                          RefPtr<layers::PersistentBufferProvider>& aOutProvider)
    1910             : {
    1911           0 :   aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(GetSize(),
    1912           0 :                                                                        GetSurfaceFormat());
    1913           0 :   if (!aOutDT) {
    1914           0 :     return false;
    1915             :   }
    1916             : 
    1917           0 :   aOutProvider = new PersistentBufferProviderBasic(aOutDT);
    1918           0 :   return true;
    1919             : }
    1920             : 
    1921             : int32_t
    1922           0 : CanvasRenderingContext2D::GetWidth() const
    1923             : {
    1924           0 :   return mWidth;
    1925             : }
    1926             : 
    1927             : int32_t
    1928           0 : CanvasRenderingContext2D::GetHeight() const
    1929             : {
    1930           0 :   return mHeight;
    1931             : }
    1932             : 
    1933             : NS_IMETHODIMP
    1934           0 : CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
    1935             : {
    1936           0 :   ClearTarget();
    1937             : 
    1938             :   // Zero sized surfaces can cause problems.
    1939           0 :   mZero = false;
    1940           0 :   if (aHeight == 0) {
    1941           0 :     aHeight = 1;
    1942           0 :     mZero = true;
    1943             :   }
    1944           0 :   if (aWidth == 0) {
    1945           0 :     aWidth = 1;
    1946           0 :     mZero = true;
    1947             :   }
    1948           0 :   mWidth = aWidth;
    1949           0 :   mHeight = aHeight;
    1950             : 
    1951           0 :   return NS_OK;
    1952             : }
    1953             : 
    1954             : void
    1955           0 : CanvasRenderingContext2D::ClearTarget()
    1956             : {
    1957           0 :   Reset();
    1958             : 
    1959           0 :   mResetLayer = true;
    1960             : 
    1961           0 :   SetInitialState();
    1962             : 
    1963             :   // For vertical writing-mode, unless text-orientation is sideways,
    1964             :   // we'll modify the initial value of textBaseline to 'middle'.
    1965           0 :   RefPtr<nsStyleContext> canvasStyle;
    1966           0 :   if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) {
    1967           0 :     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    1968           0 :     if (presShell) {
    1969             :       canvasStyle =
    1970           0 :         nsComputedDOMStyle::GetStyleContext(mCanvasElement, nullptr, presShell);
    1971           0 :       if (canvasStyle) {
    1972           0 :         WritingMode wm(canvasStyle);
    1973           0 :         if (wm.IsVertical() && !wm.IsSideways()) {
    1974           0 :           CurrentState().textBaseline = TextBaseline::MIDDLE;
    1975             :         }
    1976             :       }
    1977             :     }
    1978             :   }
    1979           0 : }
    1980             : 
    1981             : void
    1982           0 : CanvasRenderingContext2D::ReturnTarget(bool aForceReset)
    1983             : {
    1984           0 :   if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
    1985           0 :     CurrentState().transform = mTarget->GetTransform();
    1986           0 :     if (aForceReset || !mBufferProvider->PreservesDrawingState()) {
    1987           0 :       for (const auto& style : mStyleStack) {
    1988           0 :         for (const auto& clipOrTransform : style.clipsAndTransforms) {
    1989           0 :           if (clipOrTransform.IsClip()) {
    1990           0 :             mTarget->PopClip();
    1991             :           }
    1992             :         }
    1993             :       }
    1994             : 
    1995           0 :       if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
    1996             :         // With the cairo backend we pushed an extra clip rect which we have to
    1997             :         // balance out here. See the comment in RestoreClipsAndTransformToTarget.
    1998           0 :         mTarget->PopClip();
    1999             :       }
    2000             : 
    2001           0 :       mTarget->SetTransform(Matrix());
    2002             :     }
    2003             : 
    2004           0 :     mBufferProvider->ReturnDrawTarget(mTarget.forget());
    2005             :   }
    2006           0 : }
    2007             : 
    2008             : NS_IMETHODIMP
    2009           0 : CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
    2010             :                                                    NotNull<gfx::DrawTarget*> aTarget)
    2011             : {
    2012           0 :   RemovePostRefreshObserver();
    2013           0 :   mDocShell = aShell;
    2014           0 :   AddPostRefreshObserverIfNecessary();
    2015             : 
    2016           0 :   IntSize size = aTarget->GetSize();
    2017           0 :   SetDimensions(size.width, size.height);
    2018             : 
    2019           0 :   mTarget = aTarget;
    2020           0 :   mBufferProvider = new PersistentBufferProviderBasic(aTarget);
    2021             : 
    2022           0 :   if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
    2023             :     // Cf comment in EnsureTarget
    2024           0 :     mTarget->PushClipRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
    2025             :   }
    2026             : 
    2027           0 :   return NS_OK;
    2028             : }
    2029             : 
    2030             : void
    2031           0 : CanvasRenderingContext2D::SetIsOpaque(bool aIsOpaque)
    2032             : {
    2033           0 :   if (aIsOpaque != mOpaque) {
    2034           0 :     mOpaque = aIsOpaque;
    2035           0 :     ClearTarget();
    2036             :   }
    2037           0 : }
    2038             : 
    2039             : NS_IMETHODIMP
    2040           0 : CanvasRenderingContext2D::SetIsIPC(bool aIsIPC)
    2041             : {
    2042           0 :   if (aIsIPC != mIPC) {
    2043           0 :     mIPC = aIsIPC;
    2044           0 :     ClearTarget();
    2045             :   }
    2046             : 
    2047           0 :   return NS_OK;
    2048             : }
    2049             : 
    2050             : NS_IMETHODIMP
    2051           0 : CanvasRenderingContext2D::SetContextOptions(JSContext* aCx,
    2052             :                                             JS::Handle<JS::Value> aOptions,
    2053             :                                             ErrorResult& aRvForDictionaryInit)
    2054             : {
    2055           0 :   if (aOptions.isNullOrUndefined()) {
    2056           0 :     return NS_OK;
    2057             :   }
    2058             : 
    2059             :   // This shouldn't be called before drawing starts, so there should be no drawtarget yet
    2060           0 :   MOZ_ASSERT(!mTarget);
    2061             : 
    2062           0 :   ContextAttributes2D attributes;
    2063           0 :   if (!attributes.Init(aCx, aOptions)) {
    2064           0 :     aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
    2065           0 :     return NS_ERROR_UNEXPECTED;
    2066             :   }
    2067             : 
    2068           0 :   if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
    2069             :     // Use software when there is going to be a lot of readback
    2070           0 :     if (attributes.mWillReadFrequently) {
    2071             : 
    2072             :       // We want to lock into software, so remove the observer that
    2073             :       // may potentially change that...
    2074           0 :       RemoveDrawObserver();
    2075           0 :       mRenderingMode = RenderingMode::SoftwareBackendMode;
    2076             :     }
    2077             :   }
    2078             : 
    2079           0 :   if (!attributes.mAlpha) {
    2080           0 :     SetIsOpaque(true);
    2081             :   }
    2082             : 
    2083           0 :   return NS_OK;
    2084             : }
    2085             : 
    2086             : UniquePtr<uint8_t[]>
    2087           0 : CanvasRenderingContext2D::GetImageBuffer(int32_t* aFormat)
    2088             : {
    2089           0 :   UniquePtr<uint8_t[]> ret;
    2090             : 
    2091           0 :   *aFormat = 0;
    2092             : 
    2093           0 :   RefPtr<SourceSurface> snapshot;
    2094           0 :   if (mTarget) {
    2095           0 :     snapshot = mTarget->Snapshot();
    2096           0 :   } else if (mBufferProvider) {
    2097           0 :     snapshot = mBufferProvider->BorrowSnapshot();
    2098             :   } else {
    2099           0 :     EnsureTarget();
    2100           0 :     if (!IsTargetValid()) {
    2101           0 :       return nullptr;
    2102             :     }
    2103           0 :     snapshot = mTarget->Snapshot();
    2104             :   }
    2105             : 
    2106           0 :   if (snapshot) {
    2107           0 :     RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
    2108           0 :     if (data && data->GetSize() == GetSize()) {
    2109           0 :       *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
    2110           0 :       ret = SurfaceToPackedBGRA(data);
    2111             :     }
    2112             :   }
    2113             : 
    2114           0 :   if (!mTarget && mBufferProvider) {
    2115           0 :     mBufferProvider->ReturnSnapshot(snapshot.forget());
    2116             :   }
    2117             : 
    2118           0 :   return ret;
    2119             : }
    2120             : 
    2121           0 : nsString CanvasRenderingContext2D::GetHitRegion(const mozilla::gfx::Point& aPoint)
    2122             : {
    2123           0 :   for (size_t x = 0 ; x < mHitRegionsOptions.Length(); x++) {
    2124           0 :     RegionInfo& info = mHitRegionsOptions[x];
    2125           0 :     if (info.mPath->ContainsPoint(aPoint, Matrix())) {
    2126           0 :       return info.mId;
    2127             :     }
    2128             :   }
    2129           0 :   return nsString();
    2130             : }
    2131             : 
    2132             : NS_IMETHODIMP
    2133           0 : CanvasRenderingContext2D::GetInputStream(const char* aMimeType,
    2134             :                                          const char16_t* aEncoderOptions,
    2135             :                                          nsIInputStream** aStream)
    2136             : {
    2137           0 :   nsCString enccid("@mozilla.org/image/encoder;2?type=");
    2138           0 :   enccid += aMimeType;
    2139           0 :   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
    2140           0 :   if (!encoder) {
    2141           0 :     return NS_ERROR_FAILURE;
    2142             :   }
    2143             : 
    2144           0 :   int32_t format = 0;
    2145           0 :   UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
    2146           0 :   if (!imageBuffer) {
    2147           0 :     return NS_ERROR_FAILURE;
    2148             :   }
    2149             : 
    2150           0 :   return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(),
    2151             :                                       format, encoder, aEncoderOptions,
    2152           0 :                                       aStream);
    2153             : }
    2154             : 
    2155             : SurfaceFormat
    2156           0 : CanvasRenderingContext2D::GetSurfaceFormat() const
    2157             : {
    2158           0 :   return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
    2159             : }
    2160             : 
    2161             : //
    2162             : // state
    2163             : //
    2164             : 
    2165             : void
    2166           0 : CanvasRenderingContext2D::Save()
    2167             : {
    2168           0 :   EnsureTarget();
    2169           0 :   if (MOZ_UNLIKELY(!mTarget || mStyleStack.IsEmpty())) {
    2170           0 :     SetErrorState();
    2171           0 :     return;
    2172             :   }
    2173           0 :   mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
    2174           0 :   mStyleStack.SetCapacity(mStyleStack.Length() + 1);
    2175           0 :   mStyleStack.AppendElement(CurrentState());
    2176             : 
    2177           0 :   if (mStyleStack.Length() > MAX_STYLE_STACK_SIZE) {
    2178             :     // This is not fast, but is better than OOMing and shouldn't be hit by
    2179             :     // reasonable code.
    2180           0 :     mStyleStack.RemoveElementAt(0);
    2181             :   }
    2182             : }
    2183             : 
    2184             : void
    2185           0 : CanvasRenderingContext2D::Restore()
    2186             : {
    2187           0 :   if (MOZ_UNLIKELY(mStyleStack.Length() < 2)) {
    2188           0 :     return;
    2189             :   }
    2190             : 
    2191           0 :   TransformWillUpdate();
    2192           0 :   if (!IsTargetValid()) {
    2193           0 :     return;
    2194             :   }
    2195             : 
    2196           0 :   for (const auto& clipOrTransform : CurrentState().clipsAndTransforms) {
    2197           0 :     if (clipOrTransform.IsClip()) {
    2198           0 :       mTarget->PopClip();
    2199             :     }
    2200             :   }
    2201             : 
    2202           0 :   mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
    2203             : 
    2204           0 :   mTarget->SetTransform(CurrentState().transform);
    2205             : }
    2206             : 
    2207             : //
    2208             : // transformations
    2209             : //
    2210             : 
    2211             : void
    2212           0 : CanvasRenderingContext2D::Scale(double aX, double aY, ErrorResult& aError)
    2213             : {
    2214           0 :   TransformWillUpdate();
    2215           0 :   if (!IsTargetValid()) {
    2216           0 :     aError.Throw(NS_ERROR_FAILURE);
    2217           0 :     return;
    2218             :   }
    2219             : 
    2220           0 :   Matrix newMatrix = mTarget->GetTransform();
    2221           0 :   newMatrix.PreScale(aX, aY);
    2222             : 
    2223           0 :   SetTransformInternal(newMatrix);
    2224             : }
    2225             : 
    2226             : void
    2227           0 : CanvasRenderingContext2D::Rotate(double aAngle, ErrorResult& aError)
    2228             : {
    2229           0 :   TransformWillUpdate();
    2230           0 :   if (!IsTargetValid()) {
    2231           0 :     aError.Throw(NS_ERROR_FAILURE);
    2232           0 :     return;
    2233             :   }
    2234             : 
    2235           0 :   Matrix newMatrix = Matrix::Rotation(aAngle) * mTarget->GetTransform();
    2236             : 
    2237           0 :   SetTransformInternal(newMatrix);
    2238             : }
    2239             : 
    2240             : void
    2241           0 : CanvasRenderingContext2D::Translate(double aX, double aY, ErrorResult& aError)
    2242             : {
    2243           0 :   TransformWillUpdate();
    2244           0 :   if (!IsTargetValid()) {
    2245           0 :     aError.Throw(NS_ERROR_FAILURE);
    2246           0 :     return;
    2247             :   }
    2248             : 
    2249           0 :   Matrix newMatrix = mTarget->GetTransform();
    2250           0 :   newMatrix.PreTranslate(aX, aY);
    2251             : 
    2252           0 :   SetTransformInternal(newMatrix);
    2253             : }
    2254             : 
    2255             : void
    2256           0 : CanvasRenderingContext2D::Transform(double aM11, double aM12, double aM21,
    2257             :                                     double aM22, double aDx, double aDy,
    2258             :                                     ErrorResult& aError)
    2259             : {
    2260           0 :   TransformWillUpdate();
    2261           0 :   if (!IsTargetValid()) {
    2262           0 :     aError.Throw(NS_ERROR_FAILURE);
    2263           0 :     return;
    2264             :   }
    2265             : 
    2266           0 :   Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy);
    2267           0 :   newMatrix *= mTarget->GetTransform();
    2268             : 
    2269           0 :   SetTransformInternal(newMatrix);
    2270             : }
    2271             : 
    2272             : void
    2273           0 : CanvasRenderingContext2D::SetTransform(double aM11, double aM12,
    2274             :                                        double aM21, double aM22,
    2275             :                                        double aDx, double aDy,
    2276             :                                        ErrorResult& aError)
    2277             : {
    2278           0 :   TransformWillUpdate();
    2279           0 :   if (!IsTargetValid()) {
    2280           0 :     aError.Throw(NS_ERROR_FAILURE);
    2281           0 :     return;
    2282             :   }
    2283             : 
    2284           0 :   SetTransformInternal(Matrix(aM11, aM12, aM21, aM22, aDx, aDy));
    2285             : }
    2286             : 
    2287             : void
    2288           0 : CanvasRenderingContext2D::SetTransformInternal(const Matrix& aTransform)
    2289             : {
    2290           0 :   if (!aTransform.IsFinite()) {
    2291           0 :     return;
    2292             :   }
    2293             : 
    2294             :   // Save the transform in the clip stack to be able to replay clips properly.
    2295           0 :   auto& clipsAndTransforms = CurrentState().clipsAndTransforms;
    2296           0 :   if (clipsAndTransforms.IsEmpty() || clipsAndTransforms.LastElement().IsClip()) {
    2297           0 :     clipsAndTransforms.AppendElement(ClipState(aTransform));
    2298             :   } else {
    2299             :     // If the last item is a transform we can replace it instead of appending
    2300             :     // a new item.
    2301           0 :     clipsAndTransforms.LastElement().transform = aTransform;
    2302             :   }
    2303           0 :   mTarget->SetTransform(aTransform);
    2304             : }
    2305             : 
    2306             : void
    2307           0 : CanvasRenderingContext2D::ResetTransform(ErrorResult& aError)
    2308             : {
    2309           0 :   SetTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, aError);
    2310           0 : }
    2311             : 
    2312             : static void
    2313           0 : MatrixToJSObject(JSContext* aCx, const Matrix& aMatrix,
    2314             :                  JS::MutableHandle<JSObject*> aResult, ErrorResult& aError)
    2315             : {
    2316           0 :   double elts[6] = { aMatrix._11, aMatrix._12,
    2317           0 :                      aMatrix._21, aMatrix._22,
    2318           0 :                      aMatrix._31, aMatrix._32 };
    2319             : 
    2320             :   // XXX Should we enter GetWrapper()'s compartment?
    2321           0 :   JS::Rooted<JS::Value> val(aCx);
    2322           0 :   if (!ToJSValue(aCx, elts, &val)) {
    2323           0 :     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
    2324             :   } else {
    2325           0 :     aResult.set(&val.toObject());
    2326             :   }
    2327           0 : }
    2328             : 
    2329             : static bool
    2330           0 : ObjectToMatrix(JSContext* aCx, JS::Handle<JSObject*> aObj, Matrix& aMatrix,
    2331             :                ErrorResult& aError)
    2332             : {
    2333             :   uint32_t length;
    2334           0 :   if (!JS_GetArrayLength(aCx, aObj, &length) || length != 6) {
    2335             :     // Not an array-like thing or wrong size
    2336           0 :     aError.Throw(NS_ERROR_INVALID_ARG);
    2337           0 :     return false;
    2338             :   }
    2339             : 
    2340           0 :   Float* elts[] = { &aMatrix._11, &aMatrix._12, &aMatrix._21, &aMatrix._22,
    2341           0 :                     &aMatrix._31, &aMatrix._32 };
    2342           0 :   for (uint32_t i = 0; i < 6; ++i) {
    2343           0 :     JS::Rooted<JS::Value> elt(aCx);
    2344             :     double d;
    2345           0 :     if (!JS_GetElement(aCx, aObj, i, &elt)) {
    2346           0 :       aError.Throw(NS_ERROR_FAILURE);
    2347           0 :       return false;
    2348             :     }
    2349           0 :     if (!CoerceDouble(elt, &d)) {
    2350           0 :       aError.Throw(NS_ERROR_INVALID_ARG);
    2351           0 :       return false;
    2352             :     }
    2353           0 :     if (!FloatValidate(d)) {
    2354             :       // This is weird, but it's the behavior of SetTransform()
    2355           0 :       return false;
    2356             :     }
    2357           0 :     *elts[i] = Float(d);
    2358             :   }
    2359           0 :   return true;
    2360             : }
    2361             : 
    2362             : void
    2363           0 : CanvasRenderingContext2D::SetMozCurrentTransform(JSContext* aCx,
    2364             :                                                  JS::Handle<JSObject*> aCurrentTransform,
    2365             :                                                  ErrorResult& aError)
    2366             : {
    2367           0 :   EnsureTarget();
    2368           0 :   if (!IsTargetValid()) {
    2369           0 :     aError.Throw(NS_ERROR_FAILURE);
    2370           0 :     return;
    2371             :   }
    2372             : 
    2373           0 :   Matrix newCTM;
    2374           0 :   if (ObjectToMatrix(aCx, aCurrentTransform, newCTM, aError) && newCTM.IsFinite()) {
    2375           0 :     mTarget->SetTransform(newCTM);
    2376             :   }
    2377             : }
    2378             : 
    2379             : void
    2380           0 : CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* aCx,
    2381             :                                                  JS::MutableHandle<JSObject*> aResult,
    2382             :                                                  ErrorResult& aError)
    2383             : {
    2384           0 :   EnsureTarget();
    2385             : 
    2386           0 :   MatrixToJSObject(aCx, mTarget ? mTarget->GetTransform() : Matrix(),
    2387           0 :                    aResult, aError);
    2388           0 : }
    2389             : 
    2390             : void
    2391           0 : CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* aCx,
    2392             :                                                         JS::Handle<JSObject*> aCurrentTransform,
    2393             :                                                         ErrorResult& aError)
    2394             : {
    2395           0 :   EnsureTarget();
    2396           0 :   if (!IsTargetValid()) {
    2397           0 :     aError.Throw(NS_ERROR_FAILURE);
    2398           0 :     return;
    2399             :   }
    2400             : 
    2401           0 :   Matrix newCTMInverse;
    2402           0 :   if (ObjectToMatrix(aCx, aCurrentTransform, newCTMInverse, aError)) {
    2403             :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
    2404           0 :     if (newCTMInverse.Invert() && newCTMInverse.IsFinite()) {
    2405           0 :       mTarget->SetTransform(newCTMInverse);
    2406             :     }
    2407             :   }
    2408             : }
    2409             : 
    2410             : void
    2411           0 : CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* aCx,
    2412             :                                                         JS::MutableHandle<JSObject*> aResult,
    2413             :                                                         ErrorResult& aError)
    2414             : {
    2415           0 :   EnsureTarget();
    2416             : 
    2417           0 :   if (!mTarget) {
    2418           0 :     MatrixToJSObject(aCx, Matrix(), aResult, aError);
    2419           0 :     return;
    2420             :   }
    2421             : 
    2422           0 :   Matrix ctm = mTarget->GetTransform();
    2423             : 
    2424           0 :   if (!ctm.Invert()) {
    2425           0 :     double NaN = JS_GetNaNValue(aCx).toDouble();
    2426           0 :     ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
    2427             :   }
    2428             : 
    2429           0 :   MatrixToJSObject(aCx, ctm, aResult, aError);
    2430             : }
    2431             : 
    2432             : //
    2433             : // colors
    2434             : //
    2435             : 
    2436             : void
    2437           0 : CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
    2438             :                                             Style aWhichStyle)
    2439             : {
    2440           0 :   if (aValue.IsString()) {
    2441           0 :     SetStyleFromString(aValue.GetAsString(), aWhichStyle);
    2442           0 :     return;
    2443             :   }
    2444             : 
    2445           0 :   if (aValue.IsCanvasGradient()) {
    2446           0 :     SetStyleFromGradient(aValue.GetAsCanvasGradient(), aWhichStyle);
    2447           0 :     return;
    2448             :   }
    2449             : 
    2450           0 :   if (aValue.IsCanvasPattern()) {
    2451           0 :     SetStyleFromPattern(aValue.GetAsCanvasPattern(), aWhichStyle);
    2452           0 :     return;
    2453             :   }
    2454             : 
    2455           0 :   MOZ_ASSERT_UNREACHABLE("Invalid union value");
    2456             : }
    2457             : 
    2458             : void
    2459           0 : CanvasRenderingContext2D::SetFillRule(const nsAString& aString)
    2460             : {
    2461             :   FillRule rule;
    2462             : 
    2463           0 :   if (aString.EqualsLiteral("evenodd"))
    2464           0 :     rule = FillRule::FILL_EVEN_ODD;
    2465           0 :   else if (aString.EqualsLiteral("nonzero"))
    2466           0 :     rule = FillRule::FILL_WINDING;
    2467             :   else
    2468           0 :     return;
    2469             : 
    2470           0 :   CurrentState().fillRule = rule;
    2471             : }
    2472             : 
    2473             : void
    2474           0 : CanvasRenderingContext2D::GetFillRule(nsAString& aString)
    2475             : {
    2476           0 :   switch (CurrentState().fillRule) {
    2477             :   case FillRule::FILL_WINDING:
    2478           0 :     aString.AssignLiteral("nonzero"); break;
    2479             :   case FillRule::FILL_EVEN_ODD:
    2480           0 :     aString.AssignLiteral("evenodd"); break;
    2481             :   }
    2482           0 : }
    2483             : //
    2484             : // gradients and patterns
    2485             : //
    2486             : already_AddRefed<CanvasGradient>
    2487           0 : CanvasRenderingContext2D::CreateLinearGradient(double aX0, double aY0, double aX1, double aY1)
    2488             : {
    2489             :   RefPtr<CanvasGradient> grad =
    2490           0 :     new CanvasLinearGradient(this, Point(aX0, aY0), Point(aX1, aY1));
    2491             : 
    2492           0 :   return grad.forget();
    2493             : }
    2494             : 
    2495             : already_AddRefed<CanvasGradient>
    2496           0 : CanvasRenderingContext2D::CreateRadialGradient(double aX0, double aY0, double aR0,
    2497             :                                                double aX1, double aY1, double aR1,
    2498             :                                                ErrorResult& aError)
    2499             : {
    2500           0 :   if (aR0 < 0.0 || aR1 < 0.0) {
    2501           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    2502           0 :     return nullptr;
    2503             :   }
    2504             : 
    2505             :   RefPtr<CanvasGradient> grad =
    2506           0 :     new CanvasRadialGradient(this, Point(aX0, aY0), aR0, Point(aX1, aY1), aR1);
    2507             : 
    2508           0 :   return grad.forget();
    2509             : }
    2510             : 
    2511             : already_AddRefed<CanvasPattern>
    2512           0 : CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource,
    2513             :                                         const nsAString& aRepeat,
    2514             :                                         ErrorResult& aError)
    2515             : {
    2516             :   CanvasPattern::RepeatMode repeatMode =
    2517           0 :     CanvasPattern::RepeatMode::NOREPEAT;
    2518             : 
    2519           0 :   if (aRepeat.IsEmpty() || aRepeat.EqualsLiteral("repeat")) {
    2520           0 :     repeatMode = CanvasPattern::RepeatMode::REPEAT;
    2521           0 :   } else if (aRepeat.EqualsLiteral("repeat-x")) {
    2522           0 :     repeatMode = CanvasPattern::RepeatMode::REPEATX;
    2523           0 :   } else if (aRepeat.EqualsLiteral("repeat-y")) {
    2524           0 :     repeatMode = CanvasPattern::RepeatMode::REPEATY;
    2525           0 :   } else if (aRepeat.EqualsLiteral("no-repeat")) {
    2526           0 :     repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
    2527             :   } else {
    2528           0 :     aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
    2529           0 :     return nullptr;
    2530             :   }
    2531             : 
    2532             :   Element* htmlElement;
    2533           0 :   if (aSource.IsHTMLCanvasElement()) {
    2534           0 :     HTMLCanvasElement* canvas = &aSource.GetAsHTMLCanvasElement();
    2535           0 :     htmlElement = canvas;
    2536             : 
    2537           0 :     nsIntSize size = canvas->GetSize();
    2538           0 :     if (size.width == 0 || size.height == 0) {
    2539           0 :       aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2540           0 :       return nullptr;
    2541             :     }
    2542             : 
    2543             :     // Special case for Canvas, which could be an Azure canvas!
    2544           0 :     nsICanvasRenderingContextInternal* srcCanvas = canvas->GetContextAtIndex(0);
    2545           0 :     if (srcCanvas) {
    2546             :       // This might not be an Azure canvas!
    2547           0 :       RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
    2548           0 :       if (!srcSurf) {
    2549           0 :         JSContext* context = nsContentUtils::GetCurrentJSContext();
    2550           0 :         if (context) {
    2551             :           JS_ReportWarningASCII(context,
    2552             :                                 "CanvasRenderingContext2D.createPattern()"
    2553           0 :                                 " failed to snapshot source canvas.");
    2554             :         }
    2555           0 :         aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2556           0 :         return nullptr;
    2557             :       }
    2558             : 
    2559             :       RefPtr<CanvasPattern> pat =
    2560           0 :         new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
    2561             : 
    2562           0 :       return pat.forget();
    2563             :     }
    2564           0 :   } else if (aSource.IsHTMLImageElement()) {
    2565           0 :     HTMLImageElement* img = &aSource.GetAsHTMLImageElement();
    2566           0 :     if (img->IntrinsicState().HasState(NS_EVENT_STATE_BROKEN)) {
    2567           0 :       aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2568           0 :       return nullptr;
    2569             :     }
    2570             : 
    2571           0 :     htmlElement = img;
    2572           0 :   } else if (aSource.IsHTMLVideoElement()) {
    2573           0 :     auto& video = aSource.GetAsHTMLVideoElement();
    2574           0 :     video.MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_PATTERN);
    2575           0 :     htmlElement = &video;
    2576             :   } else {
    2577             :     // Special case for ImageBitmap
    2578           0 :     ImageBitmap& imgBitmap = aSource.GetAsImageBitmap();
    2579           0 :     EnsureTarget();
    2580           0 :     if (!IsTargetValid()) {
    2581           0 :       aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2582           0 :       return nullptr;
    2583             :     }
    2584           0 :     RefPtr<SourceSurface> srcSurf = imgBitmap.PrepareForDrawTarget(mTarget);
    2585           0 :     if (!srcSurf) {
    2586           0 :       JSContext* context = nsContentUtils::GetCurrentJSContext();
    2587           0 :       if (context) {
    2588             :         JS_ReportWarningASCII(context,
    2589             :                               "CanvasRenderingContext2D.createPattern()"
    2590           0 :                               " failed to prepare source ImageBitmap.");
    2591             :       }
    2592           0 :       aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2593           0 :       return nullptr;
    2594             :     }
    2595             : 
    2596             :     // An ImageBitmap never taints others so we set principalForSecurityCheck to
    2597             :     // nullptr and set CORSUsed to true for passing the security check in
    2598             :     // CanvasUtils::DoDrawImageSecurityCheck().
    2599             :     RefPtr<CanvasPattern> pat =
    2600           0 :       new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true);
    2601             : 
    2602           0 :     return pat.forget();
    2603             :   }
    2604             : 
    2605           0 :   EnsureTarget();
    2606           0 :   if (!IsTargetValid()) {
    2607           0 :     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2608           0 :     return nullptr;
    2609             :   }
    2610             : 
    2611             :   // The canvas spec says that createPattern should use the first frame
    2612             :   // of animated images
    2613             :   nsLayoutUtils::SurfaceFromElementResult res =
    2614             :     nsLayoutUtils::SurfaceFromElement(htmlElement,
    2615           0 :       nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE, mTarget);
    2616             : 
    2617           0 :   if (!res.GetSourceSurface()) {
    2618           0 :     return nullptr;
    2619             :   }
    2620             : 
    2621           0 :   RefPtr<CanvasPattern> pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode,
    2622           0 :                                                 res.mPrincipal, res.mIsWriteOnly,
    2623           0 :                                                 res.mCORSUsed);
    2624           0 :   return pat.forget();
    2625             : }
    2626             : 
    2627             : //
    2628             : // shadows
    2629             : //
    2630             : void
    2631           0 : CanvasRenderingContext2D::SetShadowColor(const nsAString& aShadowColor)
    2632             : {
    2633             :   nscolor color;
    2634           0 :   if (!ParseColor(aShadowColor, &color)) {
    2635           0 :     return;
    2636             :   }
    2637             : 
    2638           0 :   CurrentState().shadowColor = color;
    2639             : }
    2640             : 
    2641             : //
    2642             : // filters
    2643             : //
    2644             : 
    2645             : static already_AddRefed<Declaration>
    2646           0 : CreateDeclaration(nsINode* aNode,
    2647             :   const nsCSSPropertyID aProp1, const nsAString& aValue1, bool* aChanged1,
    2648             :   const nsCSSPropertyID aProp2, const nsAString& aValue2, bool* aChanged2)
    2649             : {
    2650           0 :   nsIPrincipal* principal = aNode->NodePrincipal();
    2651           0 :   nsIDocument* document = aNode->OwnerDoc();
    2652             : 
    2653           0 :   nsIURI* docURL = document->GetDocumentURI();
    2654           0 :   nsIURI* baseURL = document->GetDocBaseURI();
    2655             : 
    2656             :   // Pass the CSS Loader object to the parser, to allow parser error reports
    2657             :   // to include the outer window ID.
    2658           0 :   nsCSSParser parser(document->CSSLoader());
    2659             : 
    2660             :   RefPtr<Declaration> declaration =
    2661           0 :     parser.ParseStyleAttribute(EmptyString(), docURL, baseURL, principal);
    2662             : 
    2663           0 :   if (aProp1 != eCSSProperty_UNKNOWN) {
    2664           0 :     parser.ParseProperty(aProp1, aValue1, docURL, baseURL, principal,
    2665           0 :                          declaration, aChanged1, false);
    2666             :   }
    2667             : 
    2668           0 :   if (aProp2 != eCSSProperty_UNKNOWN) {
    2669           0 :     parser.ParseProperty(aProp2, aValue2, docURL, baseURL, principal,
    2670           0 :                          declaration, aChanged2, false);
    2671             :   }
    2672             : 
    2673           0 :   declaration->SetImmutable();
    2674           0 :   return declaration.forget();
    2675             : }
    2676             : 
    2677             : static already_AddRefed<Declaration>
    2678           0 : CreateFontDeclaration(const nsAString& aFont,
    2679             :                       nsINode* aNode,
    2680             :                       bool* aOutFontChanged)
    2681             : {
    2682             :   bool lineHeightChanged;
    2683             :   return CreateDeclaration(aNode,
    2684             :     eCSSProperty_font, aFont, aOutFontChanged,
    2685           0 :     eCSSProperty_line_height, NS_LITERAL_STRING("normal"), &lineHeightChanged);
    2686             : }
    2687             : 
    2688             : static already_AddRefed<nsStyleContext>
    2689           0 : GetFontParentStyleContext(Element* aElement, nsIPresShell* aPresShell,
    2690             :                           ErrorResult& aError)
    2691             : {
    2692           0 :   if (aElement && aElement->IsInUncomposedDoc()) {
    2693             :     // Inherit from the canvas element.
    2694             :     RefPtr<nsStyleContext> result =
    2695           0 :       nsComputedDOMStyle::GetStyleContext(aElement, nullptr, aPresShell);
    2696           0 :     if (!result) {
    2697           0 :       aError.Throw(NS_ERROR_FAILURE);
    2698           0 :       return nullptr;
    2699             :     }
    2700           0 :     return result.forget();
    2701             :   }
    2702             : 
    2703             :   // otherwise inherit from default (10px sans-serif)
    2704             : 
    2705             :   bool changed;
    2706             :   RefPtr<css::Declaration> parentRule =
    2707           0 :     CreateFontDeclaration(NS_LITERAL_STRING("10px sans-serif"),
    2708           0 :                           aPresShell->GetDocument(), &changed);
    2709             : 
    2710           0 :   nsTArray<nsCOMPtr<nsIStyleRule>> parentRules;
    2711           0 :   parentRules.AppendElement(parentRule);
    2712             : 
    2713           0 :   nsStyleSet* styleSet = aPresShell->StyleSet()->GetAsGecko();
    2714           0 :   MOZ_RELEASE_ASSERT(styleSet);
    2715             : 
    2716             :   RefPtr<nsStyleContext> result =
    2717           0 :     styleSet->ResolveStyleForRules(nullptr, parentRules);
    2718             : 
    2719           0 :   if (!result) {
    2720           0 :     aError.Throw(NS_ERROR_FAILURE);
    2721           0 :     return nullptr;
    2722             :   }
    2723           0 :   return result.forget();
    2724             : }
    2725             : 
    2726             : static bool
    2727           0 : PropertyIsInheritOrInitial(Declaration* aDeclaration, const nsCSSPropertyID aProperty)
    2728             : {
    2729             :   // We know the declaration is not !important, so we can use
    2730             :   // GetNormalBlock().
    2731             :   const nsCSSValue* filterVal =
    2732           0 :     aDeclaration->GetNormalBlock()->ValueFor(aProperty);
    2733           0 :   return (!filterVal || (filterVal->GetUnit() == eCSSUnit_Unset ||
    2734           0 :                          filterVal->GetUnit() == eCSSUnit_Inherit ||
    2735           0 :                          filterVal->GetUnit() == eCSSUnit_Initial));
    2736             : }
    2737             : 
    2738             : static already_AddRefed<nsStyleContext>
    2739           0 : GetFontStyleContext(Element* aElement, const nsAString& aFont,
    2740             :                     nsIPresShell* aPresShell,
    2741             :                     nsAString& aOutUsedFont,
    2742             :                     ErrorResult& aError)
    2743             : {
    2744           0 :   bool fontParsedSuccessfully = false;
    2745             :   RefPtr<css::Declaration> decl =
    2746           0 :     CreateFontDeclaration(aFont, aPresShell->GetDocument(),
    2747           0 :                           &fontParsedSuccessfully);
    2748             : 
    2749           0 :   if (!fontParsedSuccessfully) {
    2750             :     // We got a syntax error.  The spec says this value must be ignored.
    2751           0 :     return nullptr;
    2752             :   }
    2753             : 
    2754             :   // In addition to unparseable values, the spec says we need to reject
    2755             :   // 'inherit' and 'initial'. The easiest way to check for this is to look
    2756             :   // at font-size-adjust, which the font shorthand resets to either 'none' or
    2757             :   // '-moz-system-font'.
    2758           0 :   if (PropertyIsInheritOrInitial(decl, eCSSProperty_font_size_adjust)) {
    2759           0 :     return nullptr;
    2760             :   }
    2761             : 
    2762             :   // have to get a parent style context for inherit-like relative
    2763             :   // values (2em, bolder, etc.)
    2764             :   RefPtr<nsStyleContext> parentContext =
    2765           0 :     GetFontParentStyleContext(aElement, aPresShell, aError);
    2766             : 
    2767           0 :   if (aError.Failed()) {
    2768           0 :     aError.Throw(NS_ERROR_FAILURE);
    2769           0 :     return nullptr;
    2770             :   }
    2771             : 
    2772           0 :   MOZ_RELEASE_ASSERT(parentContext,
    2773             :                      "GFX: GetFontParentStyleContext should have returned an error if it couldn't get a parent context.");
    2774             : 
    2775           0 :   MOZ_ASSERT(!aPresShell->IsDestroying(),
    2776             :              "GetFontParentStyleContext should have returned an error if the presshell is being destroyed.");
    2777             : 
    2778           0 :   nsTArray<nsCOMPtr<nsIStyleRule>> rules;
    2779           0 :   rules.AppendElement(decl);
    2780             :   // add a rule to prevent text zoom from affecting the style
    2781           0 :   rules.AppendElement(new nsDisableTextZoomStyleRule);
    2782             : 
    2783           0 :   nsStyleSet* styleSet = aPresShell->StyleSet()->GetAsGecko();
    2784           0 :   MOZ_RELEASE_ASSERT(styleSet);
    2785             : 
    2786             :   RefPtr<nsStyleContext> sc =
    2787           0 :     styleSet->ResolveStyleForRules(parentContext, rules);
    2788             : 
    2789             :   // The font getter is required to be reserialized based on what we
    2790             :   // parsed (including having line-height removed).  (Older drafts of
    2791             :   // the spec required font sizes be converted to pixels, but that no
    2792             :   // longer seems to be required.)
    2793           0 :   decl->GetPropertyValueByID(eCSSProperty_font, aOutUsedFont);
    2794             : 
    2795           0 :   return sc.forget();
    2796             : }
    2797             : 
    2798             : static already_AddRefed<RawServoDeclarationBlock>
    2799           0 : CreateDeclarationForServo(nsCSSPropertyID aProperty,
    2800             :                           const nsAString& aPropertyValue,
    2801             :                           nsIDocument* aDocument)
    2802             : {
    2803             :   RefPtr<URLExtraData> data =
    2804           0 :     new URLExtraData(aDocument->GetDocBaseURI(),
    2805           0 :                      aDocument->GetDocumentURI(),
    2806           0 :                      aDocument->NodePrincipal());
    2807             : 
    2808           0 :   NS_ConvertUTF16toUTF8 value(aPropertyValue);
    2809             : 
    2810             :   RefPtr<RawServoDeclarationBlock> servoDeclarations =
    2811           0 :     Servo_ParseProperty(aProperty,
    2812             :                         &value,
    2813             :                         data,
    2814             :                         ParsingMode::Default,
    2815             :                         aDocument->GetCompatibilityMode(),
    2816           0 :                         aDocument->CSSLoader()).Consume();
    2817             : 
    2818           0 :   if (!servoDeclarations) {
    2819             :     // We got a syntax error.  The spec says this value must be ignored.
    2820           0 :     return nullptr;
    2821             :   }
    2822             : 
    2823             :   // From canvas spec, force to set line-height property to 'normal' font
    2824             :   // property.
    2825           0 :   if (aProperty == eCSSProperty_font) {
    2826           0 :     const nsCString normalString = NS_LITERAL_CSTRING("normal");
    2827           0 :     Servo_DeclarationBlock_SetPropertyById(servoDeclarations,
    2828             :                                            eCSSProperty_line_height,
    2829             :                                            &normalString,
    2830             :                                            false,
    2831             :                                            data,
    2832             :                                            ParsingMode::Default,
    2833             :                                            aDocument->GetCompatibilityMode(),
    2834           0 :                                            aDocument->CSSLoader());
    2835             :   }
    2836             : 
    2837           0 :   return servoDeclarations.forget();
    2838             : }
    2839             : 
    2840             : static already_AddRefed<RawServoDeclarationBlock>
    2841           0 : CreateFontDeclarationForServo(const nsAString& aFont,
    2842             :                               nsIDocument* aDocument)
    2843             : {
    2844           0 :   return CreateDeclarationForServo(eCSSProperty_font, aFont, aDocument);
    2845             : }
    2846             : 
    2847             : static already_AddRefed<ServoComputedValues>
    2848           0 : GetFontStyleForServo(Element* aElement, const nsAString& aFont,
    2849             :                      nsIPresShell* aPresShell,
    2850             :                      nsAString& aOutUsedFont,
    2851             :                      ErrorResult& aError)
    2852             : {
    2853           0 :   MOZ_ASSERT(aPresShell->StyleSet()->IsServo());
    2854             : 
    2855             :   RefPtr<RawServoDeclarationBlock> declarations =
    2856           0 :     CreateFontDeclarationForServo(aFont, aPresShell->GetDocument());
    2857           0 :   if (!declarations) {
    2858             :     // We got a syntax error.  The spec says this value must be ignored.
    2859           0 :     return nullptr;
    2860             :   }
    2861             : 
    2862             :   // In addition to unparseable values, the spec says we need to reject
    2863             :   // 'inherit' and 'initial'. The easiest way to check for this is to look
    2864             :   // at font-size-adjust, which the font shorthand resets to 'none'.
    2865           0 :   if (Servo_DeclarationBlock_HasCSSWideKeyword(declarations,
    2866           0 :                                                eCSSProperty_font_size_adjust)) {
    2867           0 :     return nullptr;
    2868             :   }
    2869             : 
    2870           0 :   ServoStyleSet* styleSet = aPresShell->StyleSet()->AsServo();
    2871             : 
    2872           0 :   RefPtr<ServoComputedValues> parentStyle;
    2873             :   // have to get a parent style context for inherit-like relative
    2874             :   // values (2em, bolder, etc.)
    2875           0 :   if (aElement && aElement->IsInUncomposedDoc()) {
    2876             :     // Inherit from the canvas element.
    2877           0 :     aPresShell->FlushPendingNotifications(FlushType::Style);
    2878             :     // We need to use ResolveTransientServoStyle, which involves traversal,
    2879             :     // instead of ResolveServoStyle() because we need up-to-date style even if
    2880             :     // the canvas element is display:none.
    2881             :     parentStyle =
    2882           0 :       styleSet->ResolveTransientServoStyle(aElement,
    2883           0 :                                            CSSPseudoElementType::NotPseudo);
    2884             :   } else {
    2885             :     RefPtr<RawServoDeclarationBlock> declarations =
    2886           0 :       CreateFontDeclarationForServo(NS_LITERAL_STRING("10px sans-serif"),
    2887           0 :                                     aPresShell->GetDocument());
    2888           0 :     MOZ_ASSERT(declarations);
    2889             : 
    2890           0 :     parentStyle = aPresShell->StyleSet()->AsServo()->
    2891           0 :       ResolveForDeclarations(nullptr, declarations);
    2892             :   }
    2893             : 
    2894           0 :   MOZ_RELEASE_ASSERT(parentStyle, "Should have a valid parent style");
    2895             : 
    2896           0 :   MOZ_ASSERT(!aPresShell->IsDestroying(),
    2897             :              "GetFontParentStyleContext should have returned an error if the presshell is being destroyed.");
    2898             : 
    2899             :   RefPtr<ServoComputedValues> sc =
    2900           0 :     styleSet->ResolveForDeclarations(parentStyle, declarations);
    2901             : 
    2902             :   // The font getter is required to be reserialized based on what we
    2903             :   // parsed (including having line-height removed).  (Older drafts of
    2904             :   // the spec required font sizes be converted to pixels, but that no
    2905             :   // longer seems to be required.)
    2906           0 :   Servo_SerializeFontValueForCanvas(declarations, &aOutUsedFont);
    2907             : 
    2908           0 :   return sc.forget();
    2909             : }
    2910             : 
    2911             : static already_AddRefed<Declaration>
    2912           0 : CreateFilterDeclaration(const nsAString& aFilter,
    2913             :                         nsINode* aNode,
    2914             :                         bool* aOutFilterChanged)
    2915             : {
    2916             :   bool dummy;
    2917             :   return CreateDeclaration(aNode,
    2918             :     eCSSProperty_filter, aFilter, aOutFilterChanged,
    2919           0 :     eCSSProperty_UNKNOWN, EmptyString(), &dummy);
    2920             : }
    2921             : 
    2922             : static already_AddRefed<nsStyleContext>
    2923           0 : ResolveFilterStyle(const nsAString& aFilterString,
    2924             :                    nsIPresShell* aPresShell,
    2925             :                    nsStyleContext* aParentContext,
    2926             :                    ErrorResult& aError)
    2927             : {
    2928           0 :   nsIDocument* document = aPresShell->GetDocument();
    2929           0 :   bool filterChanged = false;
    2930             :   RefPtr<css::Declaration> decl =
    2931           0 :     CreateFilterDeclaration(aFilterString, document, &filterChanged);
    2932             : 
    2933           0 :   if (!filterChanged) {
    2934             :     // Refuse to accept the filter, but do not throw an error.
    2935           0 :     return nullptr;
    2936             :   }
    2937             : 
    2938             :   // In addition to unparseable values, the spec says we need to reject
    2939             :   // 'inherit' and 'initial'.
    2940           0 :   if (PropertyIsInheritOrInitial(decl, eCSSProperty_filter)) {
    2941           0 :     return nullptr;
    2942             :   }
    2943             : 
    2944           0 :   nsTArray<nsCOMPtr<nsIStyleRule>> rules;
    2945           0 :   rules.AppendElement(decl);
    2946             : 
    2947           0 :   nsStyleSet* styleSet = aPresShell->StyleSet()->GetAsGecko();
    2948           0 :   MOZ_RELEASE_ASSERT(styleSet);
    2949             : 
    2950             :   RefPtr<nsStyleContext> sc =
    2951           0 :     styleSet->ResolveStyleForRules(aParentContext, rules);
    2952             : 
    2953           0 :   return sc.forget();
    2954             : }
    2955             : 
    2956             : static already_AddRefed<RawServoDeclarationBlock>
    2957           0 : CreateFilterDeclarationForServo(const nsAString& aFilter,
    2958             :                                 nsIDocument* aDocument)
    2959             : {
    2960           0 :   return CreateDeclarationForServo(eCSSProperty_filter, aFilter, aDocument);
    2961             : }
    2962             : 
    2963             : static already_AddRefed<ServoComputedValues>
    2964           0 : ResolveFilterStyleForServo(const nsAString& aFilterString,
    2965             :                            const ServoComputedValues* aParentStyle,
    2966             :                            nsIPresShell* aPresShell,
    2967             :                            ErrorResult& aError)
    2968             : {
    2969           0 :   MOZ_ASSERT(aPresShell->StyleSet()->IsServo());
    2970             : 
    2971             :   RefPtr<RawServoDeclarationBlock> declarations =
    2972           0 :     CreateFilterDeclarationForServo(aFilterString, aPresShell->GetDocument());
    2973           0 :   if (!declarations) {
    2974             :     // Refuse to accept the filter, but do not throw an error.
    2975           0 :     return nullptr;
    2976             :   }
    2977             : 
    2978             :   // In addition to unparseable values, the spec says we need to reject
    2979             :   // 'inherit' and 'initial'.
    2980           0 :   if (Servo_DeclarationBlock_HasCSSWideKeyword(declarations,
    2981           0 :                                                eCSSProperty_filter)) {
    2982           0 :     return nullptr;
    2983             :   }
    2984             : 
    2985           0 :   ServoStyleSet* styleSet = aPresShell->StyleSet()->AsServo();
    2986             :   RefPtr<ServoComputedValues> computedValues =
    2987           0 :     styleSet->ResolveForDeclarations(aParentStyle, declarations);
    2988             : 
    2989           0 :   return computedValues.forget();
    2990             : }
    2991             : 
    2992             : bool
    2993           0 : CanvasRenderingContext2D::ParseFilter(const nsAString& aString,
    2994             :                                       nsTArray<nsStyleFilter>& aFilterChain,
    2995             :                                       ErrorResult& aError)
    2996             : {
    2997           0 :   if (!mCanvasElement && !mDocShell) {
    2998           0 :     NS_WARNING("Canvas element must be non-null or a docshell must be provided");
    2999           0 :     aError.Throw(NS_ERROR_FAILURE);
    3000           0 :     return false;
    3001             :   }
    3002             : 
    3003           0 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    3004           0 :   if (!presShell) {
    3005           0 :     aError.Throw(NS_ERROR_FAILURE);
    3006           0 :     return false;
    3007             :   }
    3008             : 
    3009           0 :   nsString usedFont;
    3010           0 :   if (presShell->StyleSet()->IsGecko()) {
    3011             :     RefPtr<nsStyleContext> parentContext =
    3012           0 :       GetFontStyleContext(mCanvasElement, GetFont(),
    3013           0 :                           presShell, usedFont, aError);
    3014           0 :     if (!parentContext) {
    3015           0 :       aError.Throw(NS_ERROR_FAILURE);
    3016           0 :       return false;
    3017             :     }
    3018             :     RefPtr<nsStyleContext> sc =
    3019           0 :       ResolveFilterStyle(aString, presShell, parentContext, aError);
    3020             : 
    3021           0 :     if (!sc) {
    3022           0 :       return false;
    3023             :     }
    3024           0 :     aFilterChain = sc->StyleEffects()->mFilters;
    3025           0 :     return true;
    3026             :   }
    3027             : 
    3028             :   // For stylo
    3029           0 :   MOZ_ASSERT(presShell->StyleSet()->IsServo());
    3030             : 
    3031             :   RefPtr<ServoComputedValues> parentStyle =
    3032           0 :     GetFontStyleForServo(mCanvasElement,
    3033           0 :                          GetFont(),
    3034             :                          presShell,
    3035             :                          usedFont,
    3036           0 :                          aError);
    3037           0 :   if (!parentStyle) {
    3038           0 :     return false;
    3039             :   }
    3040             : 
    3041             :   RefPtr<ServoComputedValues> computedValues =
    3042           0 :     ResolveFilterStyleForServo(aString,
    3043             :                                parentStyle,
    3044             :                                presShell,
    3045           0 :                                aError);
    3046           0 :   if (!computedValues) {
    3047           0 :      return false;
    3048             :   }
    3049             : 
    3050           0 :   const nsStyleEffects* effects = Servo_GetStyleEffects(computedValues);
    3051             :   // XXX: This mFilters is a one shot object, we probably could avoid copying.
    3052           0 :   aFilterChain = effects->mFilters;
    3053           0 :   return true;
    3054             : }
    3055             : 
    3056             : void
    3057           0 : CanvasRenderingContext2D::SetFilter(const nsAString& aFilter, ErrorResult& aError)
    3058             : {
    3059           0 :   nsTArray<nsStyleFilter> filterChain;
    3060           0 :   if (ParseFilter(aFilter, filterChain, aError)) {
    3061           0 :     CurrentState().filterString = aFilter;
    3062           0 :     filterChain.SwapElements(CurrentState().filterChain);
    3063           0 :     if (mCanvasElement) {
    3064           0 :       CurrentState().filterChainObserver =
    3065           0 :         new CanvasFilterChainObserver(CurrentState().filterChain,
    3066           0 :                                       mCanvasElement, this);
    3067           0 :       UpdateFilter();
    3068             :     }
    3069             :   }
    3070           0 : }
    3071             : 
    3072           0 : class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize
    3073             : {
    3074             : public:
    3075           0 :   CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
    3076             :                          nsIAtom* aFontLanguage, bool aExplicitLanguage,
    3077             :                          nsPresContext* aPresContext)
    3078           0 :     : mSize(aSize)
    3079             :     , mFont(aFont)
    3080             :     , mFontLanguage(aFontLanguage)
    3081             :     , mExplicitLanguage(aExplicitLanguage)
    3082           0 :     , mPresContext(aPresContext)
    3083             :   {
    3084           0 :   }
    3085             : 
    3086           0 :   virtual float GetEmLength() const override
    3087             :   {
    3088           0 :     return NSAppUnitsToFloatPixels(mFont.size,
    3089           0 :                                    nsPresContext::AppUnitsPerCSSPixel());
    3090             :   }
    3091             : 
    3092           0 :   virtual float GetExLength() const override
    3093             :   {
    3094           0 :     nsDeviceContext* dc = mPresContext->DeviceContext();
    3095           0 :     nsFontMetrics::Params params;
    3096           0 :     params.language = mFontLanguage;
    3097           0 :     params.explicitLanguage = mExplicitLanguage;
    3098           0 :     params.textPerf = mPresContext->GetTextPerfMetrics();
    3099           0 :     RefPtr<nsFontMetrics> fontMetrics = dc->GetMetricsFor(mFont, params);
    3100           0 :     return NSAppUnitsToFloatPixels(fontMetrics->XHeight(),
    3101           0 :                                    nsPresContext::AppUnitsPerCSSPixel());
    3102             :   }
    3103             : 
    3104           0 :   virtual gfx::Size GetSize() const override
    3105           0 :   { return Size(mSize); }
    3106             : 
    3107             : private:
    3108             :   gfx::IntSize mSize;
    3109             :   const nsFont& mFont;
    3110             :   nsIAtom* mFontLanguage;
    3111             :   bool mExplicitLanguage;
    3112             :   nsPresContext* mPresContext;
    3113             : };
    3114             : 
    3115             : void
    3116           0 : CanvasRenderingContext2D::UpdateFilter()
    3117             : {
    3118           0 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    3119           0 :   if (!presShell || presShell->IsDestroying()) {
    3120             :     // Ensure we set an empty filter and update the state to
    3121             :     // reflect the current "taint" status of the canvas
    3122           0 :     CurrentState().filter = FilterDescription();
    3123           0 :     CurrentState().filterSourceGraphicTainted =
    3124           0 :       (mCanvasElement && mCanvasElement->IsWriteOnly());
    3125           0 :     return;
    3126             :   }
    3127             : 
    3128             :   // The filter might reference an SVG filter that is declared inside this
    3129             :   // document. Flush frames so that we'll have an nsSVGFilterFrame to work
    3130             :   // with.
    3131           0 :   presShell->FlushPendingNotifications(FlushType::Frames);
    3132           0 :   MOZ_RELEASE_ASSERT(!mStyleStack.IsEmpty());
    3133           0 :   if (MOZ_UNLIKELY(presShell->IsDestroying())) {
    3134           0 :     return;
    3135             :   }
    3136             : 
    3137             :   bool sourceGraphicIsTainted =
    3138           0 :     (mCanvasElement && mCanvasElement->IsWriteOnly());
    3139             : 
    3140           0 :   CurrentState().filter =
    3141           0 :     nsFilterInstance::GetFilterDescription(mCanvasElement,
    3142           0 :       CurrentState().filterChain,
    3143             :       sourceGraphicIsTainted,
    3144           0 :       CanvasUserSpaceMetrics(GetSize(),
    3145           0 :                              CurrentState().fontFont,
    3146           0 :                              CurrentState().fontLanguage,
    3147           0 :                              CurrentState().fontExplicitLanguage,
    3148             :                              presShell->GetPresContext()),
    3149           0 :       gfxRect(0, 0, mWidth, mHeight),
    3150           0 :       CurrentState().filterAdditionalImages);
    3151           0 :   CurrentState().filterSourceGraphicTainted = sourceGraphicIsTainted;
    3152             : }
    3153             : 
    3154             : //
    3155             : // rects
    3156             : //
    3157             : 
    3158             : static bool
    3159           0 : ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid)
    3160             : {
    3161           0 :   if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) {
    3162           0 :     return false;
    3163             :   }
    3164             : 
    3165             :   // bug 1018527
    3166             :   // The values of canvas API input are in double precision, but Moz2D APIs are
    3167             :   // using float precision. Bypass canvas API calls when the input is out of
    3168             :   // float precision to avoid precision problem
    3169           0 :   if (!std::isfinite((float)aX) | !std::isfinite((float)aY) |
    3170           0 :       !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) {
    3171           0 :     return false;
    3172             :   }
    3173             : 
    3174             :   // bug 1074733
    3175             :   // The canvas spec does not forbid rects with negative w or h, so given
    3176             :   // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate
    3177             :   // the appropriate rect by flipping negative dimensions. This prevents
    3178             :   // draw targets from receiving "empty" rects later on.
    3179           0 :   if (aWidth < 0) {
    3180           0 :     aWidth = -aWidth;
    3181           0 :     aX -= aWidth;
    3182             :   }
    3183           0 :   if (aHeight < 0) {
    3184           0 :     aHeight = -aHeight;
    3185           0 :     aY -= aHeight;
    3186             :   }
    3187           0 :   return true;
    3188             : }
    3189             : 
    3190             : void
    3191           0 : CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW,
    3192             :                                     double aH)
    3193             : {
    3194             :   // Do not allow zeros - it's a no-op at that point per spec.
    3195           0 :   if (!ValidateRect(aX, aY, aW, aH, false)) {
    3196           0 :     return;
    3197             :   }
    3198             : 
    3199           0 :   gfx::Rect clearRect(aX, aY, aW, aH);
    3200             : 
    3201           0 :   EnsureTarget(&clearRect);
    3202           0 :   if (!IsTargetValid()) {
    3203           0 :     return;
    3204             :   }
    3205             : 
    3206           0 :   mTarget->ClearRect(clearRect);
    3207             : 
    3208           0 :   RedrawUser(gfxRect(aX, aY, aW, aH));
    3209             : }
    3210             : 
    3211             : void
    3212           0 : CanvasRenderingContext2D::FillRect(double aX, double aY, double aW, double aH)
    3213             : {
    3214           0 :   if (!ValidateRect(aX, aY, aW, aH, true)) {
    3215           0 :     return;
    3216             :   }
    3217             : 
    3218           0 :   const ContextState* state = &CurrentState();
    3219           0 :   if (state->patternStyles[Style::FILL]) {
    3220             :     CanvasPattern::RepeatMode repeat =
    3221           0 :       state->patternStyles[Style::FILL]->mRepeat;
    3222             :     // In the FillRect case repeat modes are easy to deal with.
    3223           0 :     bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT ||
    3224           0 :                   repeat == CanvasPattern::RepeatMode::REPEATY;
    3225           0 :     bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT ||
    3226           0 :                   repeat == CanvasPattern::RepeatMode::REPEATX;
    3227             : 
    3228             :     IntSize patternSize =
    3229           0 :       state->patternStyles[Style::FILL]->mSurface->GetSize();
    3230             : 
    3231             :     // We always need to execute painting for non-over operators, even if
    3232             :     // we end up with w/h = 0.
    3233           0 :     if (limitx) {
    3234           0 :       if (aX < 0) {
    3235           0 :         aW += aX;
    3236           0 :         if (aW < 0) {
    3237           0 :           aW = 0;
    3238             :         }
    3239             : 
    3240           0 :         aX = 0;
    3241             :       }
    3242           0 :       if (aX + aW > patternSize.width) {
    3243           0 :         aW = patternSize.width - aX;
    3244           0 :         if (aW < 0) {
    3245           0 :           aW = 0;
    3246             :         }
    3247             :       }
    3248             :     }
    3249           0 :     if (limity) {
    3250           0 :       if (aY < 0) {
    3251           0 :         aH += aY;
    3252           0 :         if (aH < 0) {
    3253           0 :           aH = 0;
    3254             :         }
    3255             : 
    3256           0 :         aY = 0;
    3257             :       }
    3258           0 :       if (aY + aH > patternSize.height) {
    3259           0 :         aH = patternSize.height - aY;
    3260           0 :         if (aH < 0) {
    3261           0 :           aH = 0;
    3262             :         }
    3263             :       }
    3264             :     }
    3265             :   }
    3266           0 :   state = nullptr;
    3267             : 
    3268           0 :   CompositionOp op = UsedOperation();
    3269           0 :   bool discardContent = PatternIsOpaque(Style::FILL)
    3270           0 :     && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
    3271           0 :   const gfx::Rect fillRect(aX, aY, aW, aH);
    3272           0 :   EnsureTarget(discardContent ? &fillRect : nullptr);
    3273           0 :   if (!IsTargetValid()) {
    3274           0 :     return;
    3275             :   }
    3276             : 
    3277           0 :   gfx::Rect bounds;
    3278           0 :   const bool needBounds = NeedToCalculateBounds();
    3279           0 :   if (!IsTargetValid()) {
    3280           0 :     return;
    3281             :   }
    3282           0 :   if (needBounds) {
    3283           0 :     bounds = mTarget->GetTransform().TransformBounds(fillRect);
    3284             :   }
    3285             : 
    3286           0 :   AntialiasMode antialiasMode = CurrentState().imageSmoothingEnabled ?
    3287           0 :                                 AntialiasMode::DEFAULT : AntialiasMode::NONE;
    3288             : 
    3289           0 :   AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3290           0 :   if (!target) {
    3291           0 :     return;
    3292             :   }
    3293           0 :   target->FillRect(gfx::Rect(aX, aY, aW, aH),
    3294           0 :                    CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
    3295           0 :                    DrawOptions(CurrentState().globalAlpha, op, antialiasMode));
    3296             : 
    3297           0 :   RedrawUser(gfxRect(aX, aY, aW, aH));
    3298             : }
    3299             : 
    3300             : void
    3301           0 : CanvasRenderingContext2D::StrokeRect(double aX, double aY, double aW, double aH)
    3302             : {
    3303           0 :   if (!aW && !aH) {
    3304           0 :     return;
    3305             :   }
    3306             : 
    3307           0 :   if (!ValidateRect(aX, aY, aW, aH, true)) {
    3308           0 :     return;
    3309             :   }
    3310             : 
    3311           0 :   EnsureTarget();
    3312           0 :   if (!IsTargetValid()) {
    3313           0 :     return;
    3314             :   }
    3315             : 
    3316           0 :   const bool needBounds = NeedToCalculateBounds();
    3317           0 :   if (!IsTargetValid()) {
    3318           0 :     return;
    3319             :   }
    3320             : 
    3321           0 :   gfx::Rect bounds;
    3322           0 :   if (needBounds) {
    3323           0 :     const ContextState& state = CurrentState();
    3324           0 :     bounds = gfx::Rect(aX - state.lineWidth / 2.0f, aY - state.lineWidth / 2.0f,
    3325           0 :                        aW + state.lineWidth, aH + state.lineWidth);
    3326           0 :     bounds = mTarget->GetTransform().TransformBounds(bounds);
    3327             :   }
    3328             : 
    3329           0 :   auto op = UsedOperation();
    3330           0 :   if (!IsTargetValid()) {
    3331           0 :     return;
    3332             :   }
    3333             : 
    3334           0 :   if (!aH) {
    3335           0 :     CapStyle cap = CapStyle::BUTT;
    3336           0 :     if (CurrentState().lineJoin == JoinStyle::ROUND) {
    3337           0 :       cap = CapStyle::ROUND;
    3338             :     }
    3339           0 :     AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3340           0 :     if (!target) {
    3341           0 :       return;
    3342             :     }
    3343             : 
    3344           0 :     const ContextState& state = CurrentState();
    3345           0 :     target->
    3346           0 :       StrokeLine(Point(aX, aY), Point(aX + aW, aY),
    3347           0 :                   CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
    3348           0 :                   StrokeOptions(state.lineWidth, state.lineJoin,
    3349           0 :                                 cap, state.miterLimit,
    3350             :                                 state.dash.Length(),
    3351             :                                 state.dash.Elements(),
    3352           0 :                                 state.dashOffset),
    3353           0 :                   DrawOptions(state.globalAlpha, op));
    3354           0 :     return;
    3355             :   }
    3356             : 
    3357           0 :   if (!aW) {
    3358           0 :     CapStyle cap = CapStyle::BUTT;
    3359           0 :     if (CurrentState().lineJoin == JoinStyle::ROUND) {
    3360           0 :       cap = CapStyle::ROUND;
    3361             :     }
    3362           0 :     AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3363           0 :     if (!target) {
    3364           0 :       return;
    3365             :     }
    3366             : 
    3367           0 :     const ContextState& state = CurrentState();
    3368           0 :     target->
    3369           0 :       StrokeLine(Point(aX, aY), Point(aX, aY + aH),
    3370           0 :                   CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
    3371           0 :                   StrokeOptions(state.lineWidth, state.lineJoin,
    3372           0 :                                 cap, state.miterLimit,
    3373             :                                 state.dash.Length(),
    3374             :                                 state.dash.Elements(),
    3375           0 :                                 state.dashOffset),
    3376           0 :                   DrawOptions(state.globalAlpha, op));
    3377           0 :     return;
    3378             :   }
    3379             : 
    3380           0 :   AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3381           0 :   if (!target) {
    3382           0 :     return;
    3383             :   }
    3384             : 
    3385           0 :   const ContextState& state = CurrentState();
    3386           0 :   target->
    3387           0 :     StrokeRect(gfx::Rect(aX, aY, aW, aH),
    3388           0 :                CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
    3389           0 :                StrokeOptions(state.lineWidth, state.lineJoin,
    3390           0 :                              state.lineCap, state.miterLimit,
    3391             :                              state.dash.Length(),
    3392             :                              state.dash.Elements(),
    3393           0 :                              state.dashOffset),
    3394           0 :                DrawOptions(state.globalAlpha, op));
    3395             : 
    3396           0 :   Redraw();
    3397             : }
    3398             : 
    3399             : //
    3400             : // path bits
    3401             : //
    3402             : 
    3403             : void
    3404           0 : CanvasRenderingContext2D::BeginPath()
    3405             : {
    3406           0 :   mPath = nullptr;
    3407           0 :   mPathBuilder = nullptr;
    3408           0 :   mDSPathBuilder = nullptr;
    3409           0 :   mPathTransformWillUpdate = false;
    3410           0 : }
    3411             : 
    3412             : void
    3413           0 : CanvasRenderingContext2D::Fill(const CanvasWindingRule& aWinding)
    3414             : {
    3415           0 :   EnsureUserSpacePath(aWinding);
    3416             : 
    3417           0 :   if (!mPath) {
    3418           0 :     return;
    3419             :   }
    3420             : 
    3421           0 :   const bool needBounds = NeedToCalculateBounds();
    3422           0 :   if (!IsTargetValid()) {
    3423           0 :     return;
    3424             :   }
    3425           0 :   gfx::Rect bounds;
    3426           0 :   if (needBounds) {
    3427           0 :     bounds = mPath->GetBounds(mTarget->GetTransform());
    3428             :   }
    3429             : 
    3430           0 :   AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3431           0 :   if (!target) {
    3432           0 :     return;
    3433             :   }
    3434             : 
    3435           0 :   auto op = UsedOperation();
    3436           0 :   if (!IsTargetValid() || !target) {
    3437           0 :     return;
    3438             :   }
    3439           0 :   target->Fill(mPath,
    3440           0 :                CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
    3441           0 :                DrawOptions(CurrentState().globalAlpha, op));
    3442           0 :   Redraw();
    3443             : }
    3444             : 
    3445           0 : void CanvasRenderingContext2D::Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding)
    3446             : {
    3447           0 :   EnsureTarget();
    3448           0 :   if (!IsTargetValid()) {
    3449           0 :     return;
    3450             :   }
    3451             : 
    3452           0 :   RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget);
    3453           0 :   if (!gfxpath) {
    3454           0 :     return;
    3455             :   }
    3456             : 
    3457           0 :   const bool needBounds = NeedToCalculateBounds();
    3458           0 :   if (!IsTargetValid()) {
    3459           0 :     return;
    3460             :   }
    3461           0 :   gfx::Rect bounds;
    3462           0 :   if (needBounds) {
    3463           0 :     bounds = gfxpath->GetBounds(mTarget->GetTransform());
    3464             :   }
    3465             : 
    3466           0 :   AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3467           0 :   if (!target) {
    3468           0 :     return;
    3469             :   }
    3470             : 
    3471           0 :   auto op = UsedOperation();
    3472           0 :   if (!IsTargetValid() || !target) {
    3473           0 :     return;
    3474             :   }
    3475           0 :   target->Fill(gfxpath,
    3476           0 :                CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
    3477           0 :                DrawOptions(CurrentState().globalAlpha, op));
    3478           0 :   Redraw();
    3479             : }
    3480             : 
    3481             : void
    3482           0 : CanvasRenderingContext2D::Stroke()
    3483             : {
    3484           0 :   EnsureUserSpacePath();
    3485             : 
    3486           0 :   if (!mPath) {
    3487           0 :     return;
    3488             :   }
    3489             : 
    3490           0 :   const ContextState* state = &CurrentState();
    3491           0 :   StrokeOptions strokeOptions(state->lineWidth, state->lineJoin,
    3492           0 :                               state->lineCap, state->miterLimit,
    3493             :                               state->dash.Length(), state->dash.Elements(),
    3494           0 :                               state->dashOffset);
    3495           0 :   state = nullptr;
    3496             : 
    3497           0 :   const bool needBounds = NeedToCalculateBounds();
    3498           0 :   if (!IsTargetValid()) {
    3499           0 :     return;
    3500             :   }
    3501           0 :   gfx::Rect bounds;
    3502           0 :   if (needBounds) {
    3503             :     bounds =
    3504           0 :       mPath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
    3505             :   }
    3506             : 
    3507           0 :   AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3508           0 :   if (!target) {
    3509           0 :     return;
    3510             :   }
    3511             : 
    3512           0 :   auto op = UsedOperation();
    3513           0 :   if (!IsTargetValid() || !target) {
    3514           0 :     return;
    3515             :   }
    3516           0 :   target->Stroke(mPath,
    3517           0 :                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
    3518           0 :                  strokeOptions, DrawOptions(CurrentState().globalAlpha, op));
    3519           0 :   Redraw();
    3520             : }
    3521             : 
    3522             : void
    3523           0 : CanvasRenderingContext2D::Stroke(const CanvasPath& aPath)
    3524             : {
    3525           0 :   EnsureTarget();
    3526           0 :   if (!IsTargetValid()) {
    3527           0 :     return;
    3528             :   }
    3529             : 
    3530           0 :   RefPtr<gfx::Path> gfxpath = aPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
    3531             : 
    3532           0 :   if (!gfxpath) {
    3533           0 :     return;
    3534             :   }
    3535             : 
    3536           0 :   const ContextState* state = &CurrentState();
    3537           0 :   StrokeOptions strokeOptions(state->lineWidth, state->lineJoin,
    3538           0 :                               state->lineCap, state->miterLimit,
    3539             :                               state->dash.Length(), state->dash.Elements(),
    3540           0 :                               state->dashOffset);
    3541           0 :   state = nullptr;
    3542             : 
    3543           0 :   const bool needBounds = NeedToCalculateBounds();
    3544           0 :   if (!IsTargetValid()) {
    3545           0 :     return;
    3546             :   }
    3547           0 :   gfx::Rect bounds;
    3548           0 :   if (needBounds) {
    3549             :     bounds =
    3550           0 :       gfxpath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
    3551             :   }
    3552             : 
    3553           0 :   AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds);
    3554           0 :   if (!target) {
    3555           0 :     return;
    3556             :   }
    3557             : 
    3558           0 :   auto op = UsedOperation();
    3559           0 :   if (!IsTargetValid() || !target) {
    3560           0 :     return;
    3561             :   }
    3562           0 :   target->Stroke(gfxpath,
    3563           0 :                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
    3564           0 :                  strokeOptions, DrawOptions(CurrentState().globalAlpha, op));
    3565           0 :   Redraw();
    3566             : }
    3567             : 
    3568           0 : void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement,
    3569             :                                                  ErrorResult& aRv)
    3570             : {
    3571           0 :   EnsureUserSpacePath();
    3572           0 :   if (!mPath) {
    3573           0 :     return;
    3574             :   }
    3575             : 
    3576           0 :   if (DrawCustomFocusRing(aElement)) {
    3577           0 :     AutoSaveRestore asr(this);
    3578             : 
    3579             :     // set state to conforming focus state
    3580           0 :     ContextState* state = &CurrentState();
    3581           0 :     state->globalAlpha = 1.0;
    3582           0 :     state->shadowBlur = 0;
    3583           0 :     state->shadowOffset.x = 0;
    3584           0 :     state->shadowOffset.y = 0;
    3585           0 :     state->op = mozilla::gfx::CompositionOp::OP_OVER;
    3586             : 
    3587           0 :     state->lineCap = CapStyle::BUTT;
    3588           0 :     state->lineJoin = mozilla::gfx::JoinStyle::MITER_OR_BEVEL;
    3589           0 :     state->lineWidth = 1;
    3590           0 :     state->dash.Clear();
    3591             : 
    3592             :     // color and style of the rings is the same as for image maps
    3593             :     // set the background focus color
    3594           0 :     state->SetColorStyle(Style::STROKE, NS_RGBA(255, 255, 255, 255));
    3595           0 :     state = nullptr;
    3596             : 
    3597             :     // draw the focus ring
    3598           0 :     Stroke();
    3599           0 :     if (!mPath) {
    3600           0 :       return;
    3601             :     }
    3602             : 
    3603             :     // set dashing for foreground
    3604           0 :     nsTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
    3605           0 :     for (uint32_t i = 0; i < 2; ++i) {
    3606           0 :       if (!dash.AppendElement(1, fallible)) {
    3607           0 :         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    3608           0 :         return;
    3609             :       }
    3610             :     }
    3611             : 
    3612             :     // set the foreground focus color
    3613           0 :     CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(0,0,0, 255));
    3614             :     // draw the focus ring
    3615           0 :     Stroke();
    3616           0 :     if (!mPath) {
    3617           0 :       return;
    3618             :     }
    3619             :   }
    3620             : }
    3621             : 
    3622           0 : bool CanvasRenderingContext2D::DrawCustomFocusRing(mozilla::dom::Element& aElement)
    3623             : {
    3624           0 :   EnsureUserSpacePath();
    3625             : 
    3626           0 :   HTMLCanvasElement* canvas = GetCanvas();
    3627             : 
    3628           0 :   if (!canvas|| !nsContentUtils::ContentIsDescendantOf(&aElement, canvas)) {
    3629           0 :     return false;
    3630             :   }
    3631             : 
    3632           0 :   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
    3633           0 :   if (fm) {
    3634             :     // check that the element i focused
    3635           0 :     nsCOMPtr<nsIDOMElement> focusedElement;
    3636           0 :     fm->GetFocusedElement(getter_AddRefs(focusedElement));
    3637           0 :     if (SameCOMIdentity(aElement.AsDOMNode(), focusedElement)) {
    3638           0 :       if (nsPIDOMWindowOuter* window = aElement.OwnerDoc()->GetWindow()) {
    3639           0 :         return window->ShouldShowFocusRing();
    3640             :       }
    3641             :     }
    3642             :   }
    3643             : 
    3644           0 :   return false;
    3645             : }
    3646             : 
    3647             : void
    3648           0 : CanvasRenderingContext2D::Clip(const CanvasWindingRule& aWinding)
    3649             : {
    3650           0 :   EnsureUserSpacePath(aWinding);
    3651             : 
    3652           0 :   if (!mPath) {
    3653           0 :     return;
    3654             :   }
    3655             : 
    3656           0 :   mTarget->PushClip(mPath);
    3657           0 :   CurrentState().clipsAndTransforms.AppendElement(ClipState(mPath));
    3658             : }
    3659             : 
    3660             : void
    3661           0 : CanvasRenderingContext2D::Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding)
    3662             : {
    3663           0 :   EnsureTarget();
    3664           0 :   if (!IsTargetValid()) {
    3665           0 :     return;
    3666             :   }
    3667             : 
    3668           0 :   RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget);
    3669             : 
    3670           0 :   if (!gfxpath) {
    3671           0 :     return;
    3672             :   }
    3673             : 
    3674           0 :   mTarget->PushClip(gfxpath);
    3675           0 :   CurrentState().clipsAndTransforms.AppendElement(ClipState(gfxpath));
    3676             : }
    3677             : 
    3678             : void
    3679           0 : CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
    3680             :                                 double aY2, double aRadius,
    3681             :                                 ErrorResult& aError)
    3682             : {
    3683           0 :   if (aRadius < 0) {
    3684           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    3685           0 :     return;
    3686             :   }
    3687             : 
    3688           0 :   EnsureWritablePath();
    3689             : 
    3690             :   // Current point in user space!
    3691           0 :   Point p0;
    3692           0 :   if (mPathBuilder) {
    3693           0 :     p0 = mPathBuilder->CurrentPoint();
    3694             :   } else {
    3695           0 :     Matrix invTransform = mTarget->GetTransform();
    3696           0 :     if (!invTransform.Invert()) {
    3697           0 :       return;
    3698             :     }
    3699             : 
    3700           0 :     p0 = invTransform.TransformPoint(mDSPathBuilder->CurrentPoint());
    3701             :   }
    3702             : 
    3703           0 :   Point p1(aX1, aY1);
    3704           0 :   Point p2(aX2, aY2);
    3705             : 
    3706             :   // Execute these calculations in double precision to avoid cumulative
    3707             :   // rounding errors.
    3708             :   double dir, a2, b2, c2, cosx, sinx, d, anx, any,
    3709             :          bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
    3710             :   bool anticlockwise;
    3711             : 
    3712           0 :   if (p0 == p1 || p1 == p2 || aRadius == 0) {
    3713           0 :     LineTo(p1.x, p1.y);
    3714           0 :     return;
    3715             :   }
    3716             : 
    3717             :   // Check for colinearity
    3718           0 :   dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
    3719           0 :   if (dir == 0) {
    3720           0 :     LineTo(p1.x, p1.y);
    3721           0 :     return;
    3722             :   }
    3723             : 
    3724             : 
    3725             :   // XXX - Math for this code was already available from the non-azure code
    3726             :   // and would be well tested. Perhaps converting to bezier directly might
    3727             :   // be more efficient longer run.
    3728           0 :   a2 = (p0.x-aX1)*(p0.x-aX1) + (p0.y-aY1)*(p0.y-aY1);
    3729           0 :   b2 = (aX1-aX2)*(aX1-aX2) + (aY1-aY2)*(aY1-aY2);
    3730           0 :   c2 = (p0.x-aX2)*(p0.x-aX2) + (p0.y-aY2)*(p0.y-aY2);
    3731           0 :   cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
    3732             : 
    3733           0 :   sinx = sqrt(1 - cosx*cosx);
    3734           0 :   d = aRadius / ((1 - cosx) / sinx);
    3735             : 
    3736           0 :   anx = (aX1-p0.x) / sqrt(a2);
    3737           0 :   any = (aY1-p0.y) / sqrt(a2);
    3738           0 :   bnx = (aX1-aX2) / sqrt(b2);
    3739           0 :   bny = (aY1-aY2) / sqrt(b2);
    3740           0 :   x3 = aX1 - anx*d;
    3741           0 :   y3 = aY1 - any*d;
    3742           0 :   x4 = aX1 - bnx*d;
    3743           0 :   y4 = aY1 - bny*d;
    3744           0 :   anticlockwise = (dir < 0);
    3745           0 :   cx = x3 + any*aRadius*(anticlockwise ? 1 : -1);
    3746           0 :   cy = y3 - anx*aRadius*(anticlockwise ? 1 : -1);
    3747           0 :   angle0 = atan2((y3-cy), (x3-cx));
    3748           0 :   angle1 = atan2((y4-cy), (x4-cx));
    3749             : 
    3750             : 
    3751           0 :   LineTo(x3, y3);
    3752             : 
    3753           0 :   Arc(cx, cy, aRadius, angle0, angle1, anticlockwise, aError);
    3754             : }
    3755             : 
    3756             : void
    3757           0 : CanvasRenderingContext2D::Arc(double aX, double aY, double aR,
    3758             :                               double aStartAngle, double aEndAngle,
    3759             :                               bool aAnticlockwise, ErrorResult& aError)
    3760             : {
    3761           0 :   if (aR < 0.0) {
    3762           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    3763           0 :     return;
    3764             :   }
    3765             : 
    3766           0 :   EnsureWritablePath();
    3767             : 
    3768           0 :   ArcToBezier(this, Point(aX, aY), Size(aR, aR), aStartAngle, aEndAngle, aAnticlockwise);
    3769             : }
    3770             : 
    3771             : void
    3772           0 : CanvasRenderingContext2D::Rect(double aX, double aY, double aW, double aH)
    3773             : {
    3774           0 :   EnsureWritablePath();
    3775             : 
    3776           0 :   if (mPathBuilder) {
    3777           0 :     mPathBuilder->MoveTo(Point(aX, aY));
    3778           0 :     mPathBuilder->LineTo(Point(aX + aW, aY));
    3779           0 :     mPathBuilder->LineTo(Point(aX + aW, aY + aH));
    3780           0 :     mPathBuilder->LineTo(Point(aX, aY + aH));
    3781           0 :     mPathBuilder->Close();
    3782             :   } else {
    3783           0 :     mDSPathBuilder->MoveTo(mTarget->GetTransform().TransformPoint(Point(aX, aY)));
    3784           0 :     mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(Point(aX + aW, aY)));
    3785           0 :     mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(Point(aX + aW, aY + aH)));
    3786           0 :     mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(Point(aX, aY + aH)));
    3787           0 :     mDSPathBuilder->Close();
    3788             :   }
    3789           0 : }
    3790             : 
    3791             : void
    3792           0 : CanvasRenderingContext2D::Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
    3793             :                                   double aRotation, double aStartAngle, double aEndAngle,
    3794             :                                   bool aAnticlockwise, ErrorResult& aError)
    3795             : {
    3796           0 :   if (aRadiusX < 0.0 || aRadiusY < 0.0) {
    3797           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    3798           0 :     return;
    3799             :   }
    3800             : 
    3801           0 :   EnsureWritablePath();
    3802             : 
    3803           0 :   ArcToBezier(this, Point(aX, aY), Size(aRadiusX, aRadiusY), aStartAngle, aEndAngle,
    3804           0 :               aAnticlockwise, aRotation);
    3805             : }
    3806             : 
    3807             : void
    3808           0 : CanvasRenderingContext2D::EnsureWritablePath()
    3809             : {
    3810           0 :   EnsureTarget();
    3811             :   // NOTE: IsTargetValid() may be false here (mTarget == sErrorTarget) but we
    3812             :   // go ahead and create a path anyway since callers depend on that.
    3813             : 
    3814           0 :   if (mDSPathBuilder) {
    3815           0 :     return;
    3816             :   }
    3817             : 
    3818           0 :   FillRule fillRule = CurrentState().fillRule;
    3819             : 
    3820           0 :   if (mPathBuilder) {
    3821           0 :     if (mPathTransformWillUpdate) {
    3822           0 :       mPath = mPathBuilder->Finish();
    3823             :       mDSPathBuilder =
    3824           0 :         mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
    3825           0 :       mPath = nullptr;
    3826           0 :       mPathBuilder = nullptr;
    3827           0 :       mPathTransformWillUpdate = false;
    3828             :     }
    3829           0 :     return;
    3830             :   }
    3831             : 
    3832           0 :   if (!mPath) {
    3833           0 :     NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
    3834           0 :     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
    3835           0 :   } else if (!mPathTransformWillUpdate) {
    3836           0 :     mPathBuilder = mPath->CopyToBuilder(fillRule);
    3837             :   } else {
    3838             :     mDSPathBuilder =
    3839           0 :       mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
    3840           0 :     mPathTransformWillUpdate = false;
    3841           0 :     mPath = nullptr;
    3842             :   }
    3843             : }
    3844             : 
    3845             : void
    3846           0 : CanvasRenderingContext2D::EnsureUserSpacePath(const CanvasWindingRule& aWinding)
    3847             : {
    3848           0 :   FillRule fillRule = CurrentState().fillRule;
    3849           0 :   if (aWinding == CanvasWindingRule::Evenodd)
    3850           0 :     fillRule = FillRule::FILL_EVEN_ODD;
    3851             : 
    3852           0 :   EnsureTarget();
    3853           0 :   if (!IsTargetValid()) {
    3854           0 :     return;
    3855             :   }
    3856             : 
    3857           0 :   if (!mPath && !mPathBuilder && !mDSPathBuilder) {
    3858           0 :     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
    3859             :   }
    3860             : 
    3861           0 :   if (mPathBuilder) {
    3862           0 :     mPath = mPathBuilder->Finish();
    3863           0 :     mPathBuilder = nullptr;
    3864             :   }
    3865             : 
    3866           0 :   if (mPath &&
    3867           0 :       mPathTransformWillUpdate) {
    3868             :     mDSPathBuilder =
    3869           0 :       mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
    3870           0 :     mPath = nullptr;
    3871           0 :     mPathTransformWillUpdate = false;
    3872             :   }
    3873             : 
    3874           0 :   if (mDSPathBuilder) {
    3875           0 :     RefPtr<Path> dsPath;
    3876           0 :     dsPath = mDSPathBuilder->Finish();
    3877           0 :     mDSPathBuilder = nullptr;
    3878             : 
    3879           0 :     Matrix inverse = mTarget->GetTransform();
    3880           0 :     if (!inverse.Invert()) {
    3881           0 :       NS_WARNING("Could not invert transform");
    3882           0 :       return;
    3883             :     }
    3884             : 
    3885             :     mPathBuilder =
    3886           0 :       dsPath->TransformedCopyToBuilder(inverse, fillRule);
    3887           0 :     mPath = mPathBuilder->Finish();
    3888           0 :     mPathBuilder = nullptr;
    3889             :   }
    3890             : 
    3891           0 :   if (mPath && mPath->GetFillRule() != fillRule) {
    3892           0 :     mPathBuilder = mPath->CopyToBuilder(fillRule);
    3893           0 :     mPath = mPathBuilder->Finish();
    3894           0 :     mPathBuilder = nullptr;
    3895             :   }
    3896             : 
    3897           0 :   NS_ASSERTION(mPath, "mPath should exist");
    3898             : }
    3899             : 
    3900             : void
    3901           0 : CanvasRenderingContext2D::TransformWillUpdate()
    3902             : {
    3903           0 :   EnsureTarget();
    3904           0 :   if (!IsTargetValid()) {
    3905           0 :     return;
    3906             :   }
    3907             : 
    3908             :   // Store the matrix that would transform the current path to device
    3909             :   // space.
    3910           0 :   if (mPath || mPathBuilder) {
    3911           0 :     if (!mPathTransformWillUpdate) {
    3912             :       // If the transform has already been updated, but a device space builder
    3913             :       // has not been created yet mPathToDS contains the right transform to
    3914             :       // transform the current mPath into device space.
    3915             :       // We should leave it alone.
    3916           0 :       mPathToDS = mTarget->GetTransform();
    3917             :     }
    3918           0 :     mPathTransformWillUpdate = true;
    3919             :   }
    3920             : }
    3921             : 
    3922             : //
    3923             : // text
    3924             : //
    3925             : 
    3926             : void
    3927           0 : CanvasRenderingContext2D::SetFont(const nsAString& aFont,
    3928             :                                   ErrorResult& aError)
    3929             : {
    3930           0 :   SetFontInternal(aFont, aError);
    3931           0 : }
    3932             : 
    3933             : bool
    3934           0 : CanvasRenderingContext2D::SetFontInternal(const nsAString& aFont,
    3935             :                                           ErrorResult& aError)
    3936             : {
    3937             :   /*
    3938             :     * If font is defined with relative units (e.g. ems) and the parent
    3939             :     * style context changes in between calls, setting the font to the
    3940             :     * same value as previous could result in a different computed value,
    3941             :     * so we cannot have the optimization where we check if the new font
    3942             :     * string is equal to the old one.
    3943             :     */
    3944             : 
    3945           0 :   if (!mCanvasElement && !mDocShell) {
    3946           0 :     NS_WARNING("Canvas element must be non-null or a docshell must be provided");
    3947           0 :     aError.Throw(NS_ERROR_FAILURE);
    3948           0 :     return false;
    3949             :   }
    3950             : 
    3951           0 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    3952           0 :   if (!presShell) {
    3953           0 :     aError.Throw(NS_ERROR_FAILURE);
    3954           0 :     return false;
    3955             :   }
    3956             : 
    3957           0 :   RefPtr<nsStyleContext> sc;
    3958           0 :   RefPtr<ServoComputedValues> computedValues;
    3959           0 :   nsString usedFont;
    3960             :   const nsStyleFont* fontStyle;
    3961           0 :   if (presShell->StyleSet()->IsServo()) {
    3962           0 :     computedValues = GetFontStyleForServo(mCanvasElement,
    3963             :                                           aFont,
    3964             :                                           presShell,
    3965             :                                           usedFont,
    3966           0 :                                           aError);
    3967           0 :     if (!computedValues) {
    3968           0 :       return false;
    3969             :     }
    3970           0 :     fontStyle = Servo_GetStyleFont(computedValues);
    3971             :   } else {
    3972           0 :     sc = GetFontStyleContext(mCanvasElement,
    3973             :                              aFont,
    3974             :                              presShell,
    3975             :                              usedFont,
    3976           0 :                              aError);
    3977           0 :     if (!sc) {
    3978           0 :       return false;
    3979             :     }
    3980           0 :     fontStyle = sc->StyleFont();
    3981             :   }
    3982             : 
    3983           0 :   nsPresContext* c = presShell->GetPresContext();
    3984             : 
    3985             :   // Purposely ignore the font size that respects the user's minimum
    3986             :   // font preference (fontStyle->mFont.size) in favor of the computed
    3987             :   // size (fontStyle->mSize).  See
    3988             :   // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
    3989             :   // FIXME: Nobody initializes mAllowZoom for servo?
    3990           0 :   MOZ_ASSERT(presShell->StyleSet()->IsServo() || !fontStyle->mAllowZoom,
    3991             :              "expected text zoom to be disabled on this nsStyleFont");
    3992           0 :   nsFont resizedFont(fontStyle->mFont);
    3993             :   // Create a font group working in units of CSS pixels instead of the usual
    3994             :   // device pixels, to avoid being affected by page zoom. nsFontMetrics will
    3995             :   // convert nsFont size in app units to device pixels for the font group, so
    3996             :   // here we first apply to the size the equivalent of a conversion from device
    3997             :   // pixels to CSS pixels, to adjust for the difference in expectations from
    3998             :   // other nsFontMetrics clients.
    3999           0 :   resizedFont.size =
    4000           0 :     (fontStyle->mSize * c->AppUnitsPerDevPixel()) / c->AppUnitsPerCSSPixel();
    4001             : 
    4002           0 :   nsFontMetrics::Params params;
    4003           0 :   params.language = fontStyle->mLanguage;
    4004           0 :   params.explicitLanguage = fontStyle->mExplicitLanguage;
    4005           0 :   params.userFontSet = c->GetUserFontSet();
    4006           0 :   params.textPerf = c->GetTextPerfMetrics();
    4007             :   RefPtr<nsFontMetrics> metrics =
    4008           0 :     c->DeviceContext()->GetMetricsFor(resizedFont, params);
    4009             : 
    4010           0 :   gfxFontGroup* newFontGroup = metrics->GetThebesFontGroup();
    4011           0 :   CurrentState().fontGroup = newFontGroup;
    4012           0 :   NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
    4013           0 :   CurrentState().font = usedFont;
    4014           0 :   CurrentState().fontFont = fontStyle->mFont;
    4015           0 :   CurrentState().fontFont.size = fontStyle->mSize;
    4016           0 :   CurrentState().fontLanguage = fontStyle->mLanguage;
    4017           0 :   CurrentState().fontExplicitLanguage = fontStyle->mExplicitLanguage;
    4018             : 
    4019           0 :   return true;
    4020             : }
    4021             : 
    4022             : void
    4023           0 : CanvasRenderingContext2D::SetTextAlign(const nsAString& aTextAlign)
    4024             : {
    4025           0 :   if (aTextAlign.EqualsLiteral("start"))
    4026           0 :     CurrentState().textAlign = TextAlign::START;
    4027           0 :   else if (aTextAlign.EqualsLiteral("end"))
    4028           0 :     CurrentState().textAlign = TextAlign::END;
    4029           0 :   else if (aTextAlign.EqualsLiteral("left"))
    4030           0 :     CurrentState().textAlign = TextAlign::LEFT;
    4031           0 :   else if (aTextAlign.EqualsLiteral("right"))
    4032           0 :     CurrentState().textAlign = TextAlign::RIGHT;
    4033           0 :   else if (aTextAlign.EqualsLiteral("center"))
    4034           0 :     CurrentState().textAlign = TextAlign::CENTER;
    4035           0 : }
    4036             : 
    4037             : void
    4038           0 : CanvasRenderingContext2D::GetTextAlign(nsAString& aTextAlign)
    4039             : {
    4040           0 :   switch (CurrentState().textAlign)
    4041             :   {
    4042             :   case TextAlign::START:
    4043           0 :     aTextAlign.AssignLiteral("start");
    4044           0 :     break;
    4045             :   case TextAlign::END:
    4046           0 :     aTextAlign.AssignLiteral("end");
    4047           0 :     break;
    4048             :   case TextAlign::LEFT:
    4049           0 :     aTextAlign.AssignLiteral("left");
    4050           0 :     break;
    4051             :   case TextAlign::RIGHT:
    4052           0 :     aTextAlign.AssignLiteral("right");
    4053           0 :     break;
    4054             :   case TextAlign::CENTER:
    4055           0 :     aTextAlign.AssignLiteral("center");
    4056           0 :     break;
    4057             :   }
    4058           0 : }
    4059             : 
    4060             : void
    4061           0 : CanvasRenderingContext2D::SetTextBaseline(const nsAString& aTextBaseline)
    4062             : {
    4063           0 :   if (aTextBaseline.EqualsLiteral("top"))
    4064           0 :     CurrentState().textBaseline = TextBaseline::TOP;
    4065           0 :   else if (aTextBaseline.EqualsLiteral("hanging"))
    4066           0 :     CurrentState().textBaseline = TextBaseline::HANGING;
    4067           0 :   else if (aTextBaseline.EqualsLiteral("middle"))
    4068           0 :     CurrentState().textBaseline = TextBaseline::MIDDLE;
    4069           0 :   else if (aTextBaseline.EqualsLiteral("alphabetic"))
    4070           0 :     CurrentState().textBaseline = TextBaseline::ALPHABETIC;
    4071           0 :   else if (aTextBaseline.EqualsLiteral("ideographic"))
    4072           0 :     CurrentState().textBaseline = TextBaseline::IDEOGRAPHIC;
    4073           0 :   else if (aTextBaseline.EqualsLiteral("bottom"))
    4074           0 :     CurrentState().textBaseline = TextBaseline::BOTTOM;
    4075           0 : }
    4076             : 
    4077             : void
    4078           0 : CanvasRenderingContext2D::GetTextBaseline(nsAString& aTextBaseline)
    4079             : {
    4080           0 :   switch (CurrentState().textBaseline)
    4081             :   {
    4082             :   case TextBaseline::TOP:
    4083           0 :     aTextBaseline.AssignLiteral("top");
    4084           0 :     break;
    4085             :   case TextBaseline::HANGING:
    4086           0 :     aTextBaseline.AssignLiteral("hanging");
    4087           0 :     break;
    4088             :   case TextBaseline::MIDDLE:
    4089           0 :     aTextBaseline.AssignLiteral("middle");
    4090           0 :     break;
    4091             :   case TextBaseline::ALPHABETIC:
    4092           0 :     aTextBaseline.AssignLiteral("alphabetic");
    4093           0 :     break;
    4094             :   case TextBaseline::IDEOGRAPHIC:
    4095           0 :     aTextBaseline.AssignLiteral("ideographic");
    4096           0 :     break;
    4097             :   case TextBaseline::BOTTOM:
    4098           0 :     aTextBaseline.AssignLiteral("bottom");
    4099           0 :     break;
    4100             :   }
    4101           0 : }
    4102             : 
    4103             : /*
    4104             :  * Helper function that replaces the whitespace characters in a string
    4105             :  * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
    4106             :  * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
    4107             :  * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
    4108             :  * @param str The string whose whitespace characters to replace.
    4109             :  */
    4110             : static inline void
    4111           0 : TextReplaceWhitespaceCharacters(nsAutoString& aStr)
    4112             : {
    4113           0 :   aStr.ReplaceChar("\x09\x0A\x0B\x0C\x0D", char16_t(' '));
    4114           0 : }
    4115             : 
    4116             : void
    4117           0 : CanvasRenderingContext2D::FillText(const nsAString& aText, double aX,
    4118             :                                    double aY,
    4119             :                                    const Optional<double>& aMaxWidth,
    4120             :                                    ErrorResult& aError)
    4121             : {
    4122           0 :   aError = DrawOrMeasureText(aText, aX, aY, aMaxWidth, TextDrawOperation::FILL, nullptr);
    4123           0 : }
    4124             : 
    4125             : void
    4126           0 : CanvasRenderingContext2D::StrokeText(const nsAString& aText, double aX,
    4127             :                                      double aY,
    4128             :                                      const Optional<double>& aMaxWidth,
    4129             :                                      ErrorResult& aError)
    4130             : {
    4131           0 :   aError = DrawOrMeasureText(aText, aX, aY, aMaxWidth, TextDrawOperation::STROKE, nullptr);
    4132           0 : }
    4133             : 
    4134             : TextMetrics*
    4135           0 : CanvasRenderingContext2D::MeasureText(const nsAString& aRawText,
    4136             :                                       ErrorResult& aError)
    4137             : {
    4138             :   float width;
    4139           0 :   Optional<double> maxWidth;
    4140           0 :   aError = DrawOrMeasureText(aRawText, 0, 0, maxWidth, TextDrawOperation::MEASURE, &width);
    4141           0 :   if (aError.Failed()) {
    4142           0 :     return nullptr;
    4143             :   }
    4144             : 
    4145           0 :   return new TextMetrics(width);
    4146             : }
    4147             : 
    4148             : void
    4149           0 : CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions& aOptions, ErrorResult& aError)
    4150             : {
    4151           0 :   RefPtr<gfx::Path> path;
    4152           0 :   if (aOptions.mPath) {
    4153           0 :     EnsureTarget();
    4154           0 :     if (!IsTargetValid()) {
    4155           0 :       return;
    4156             :     }
    4157           0 :     path = aOptions.mPath->GetPath(CanvasWindingRule::Nonzero, mTarget);
    4158             :   }
    4159             : 
    4160           0 :   if (!path) {
    4161             :     // check if the path is valid
    4162           0 :     EnsureUserSpacePath(CanvasWindingRule::Nonzero);
    4163           0 :     path = mPath;
    4164             :   }
    4165             : 
    4166           0 :   if (!path) {
    4167           0 :     aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    4168           0 :     return;
    4169             :   }
    4170             : 
    4171             :   // get the bounds of the current path. They are relative to the canvas
    4172           0 :   gfx::Rect bounds(path->GetBounds(mTarget->GetTransform()));
    4173           0 :   if ((bounds.width == 0) || (bounds.height == 0) || !bounds.IsFinite()) {
    4174             :     // The specified region has no pixels.
    4175           0 :     aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    4176           0 :     return;
    4177             :   }
    4178             : 
    4179             :   // remove old hit region first
    4180           0 :   RemoveHitRegion(aOptions.mId);
    4181             : 
    4182           0 :   if (aOptions.mControl) {
    4183             :     // also remove regions with this control
    4184           0 :     for (size_t x = 0; x < mHitRegionsOptions.Length(); x++) {
    4185           0 :       RegionInfo& info = mHitRegionsOptions[x];
    4186           0 :       if (info.mElement == aOptions.mControl) {
    4187           0 :         mHitRegionsOptions.RemoveElementAt(x);
    4188           0 :         break;
    4189             :       }
    4190             :     }
    4191             : #ifdef ACCESSIBILITY
    4192           0 :   aOptions.mControl->SetProperty(nsGkAtoms::hitregion, new bool(true),
    4193           0 :                                 nsINode::DeleteProperty<bool>);
    4194             : #endif
    4195             :   }
    4196             : 
    4197             :   // finally, add the region to the list
    4198           0 :   RegionInfo info;
    4199           0 :   info.mId = aOptions.mId;
    4200           0 :   info.mElement = aOptions.mControl;
    4201           0 :   RefPtr<PathBuilder> pathBuilder = path->TransformedCopyToBuilder(mTarget->GetTransform());
    4202           0 :   info.mPath = pathBuilder->Finish();
    4203             : 
    4204           0 :   mHitRegionsOptions.InsertElementAt(0, info);
    4205             : }
    4206             : 
    4207             : void
    4208           0 : CanvasRenderingContext2D::RemoveHitRegion(const nsAString& aId)
    4209             : {
    4210           0 :   if (aId.Length() == 0) {
    4211           0 :      return;
    4212             :    }
    4213             : 
    4214           0 :   for (size_t x = 0; x < mHitRegionsOptions.Length(); x++) {
    4215           0 :     RegionInfo& info = mHitRegionsOptions[x];
    4216           0 :     if (info.mId == aId) {
    4217           0 :       mHitRegionsOptions.RemoveElementAt(x);
    4218             : 
    4219           0 :       return;
    4220             :     }
    4221             :   }
    4222             : }
    4223             : 
    4224             : void
    4225           0 : CanvasRenderingContext2D::ClearHitRegions()
    4226             : {
    4227           0 :   mHitRegionsOptions.Clear();
    4228           0 : }
    4229             : 
    4230             : bool
    4231           0 : CanvasRenderingContext2D::GetHitRegionRect(Element* aElement, nsRect& aRect)
    4232             : {
    4233           0 :   for (unsigned int x = 0; x < mHitRegionsOptions.Length(); x++) {
    4234           0 :     RegionInfo& info = mHitRegionsOptions[x];
    4235           0 :     if (info.mElement == aElement) {
    4236           0 :       gfx::Rect bounds(info.mPath->GetBounds());
    4237           0 :       gfxRect rect(bounds.x, bounds.y, bounds.width, bounds.height);
    4238           0 :       aRect = nsLayoutUtils::RoundGfxRectToAppRect(rect, AppUnitsPerCSSPixel());
    4239             : 
    4240           0 :       return true;
    4241             :     }
    4242             :   }
    4243             : 
    4244           0 :   return false;
    4245             : }
    4246             : 
    4247             : /**
    4248             :  * Used for nsBidiPresUtils::ProcessText
    4249             :  */
    4250             : struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
    4251             : {
    4252             :   typedef CanvasRenderingContext2D::Style Style;
    4253             : 
    4254           0 :   CanvasBidiProcessor()
    4255           0 :     : nsBidiPresUtils::BidiProcessor()
    4256             :   {
    4257           0 :     if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
    4258           0 :       mMissingFonts = new gfxMissingFontRecorder();
    4259             :     }
    4260           0 :   }
    4261             : 
    4262           0 :   ~CanvasBidiProcessor()
    4263           0 :   {
    4264             :     // notify front-end code if we encountered missing glyphs in any script
    4265           0 :     if (mMissingFonts) {
    4266           0 :       mMissingFonts->Flush();
    4267             :     }
    4268           0 :   }
    4269             : 
    4270             :   typedef CanvasRenderingContext2D::ContextState ContextState;
    4271             : 
    4272           0 :   virtual void SetText(const char16_t* aText, int32_t aLength, nsBidiDirection aDirection)
    4273             :   {
    4274           0 :     mFontgrp->UpdateUserFonts(); // ensure user font generation is current
    4275             :     // adjust flags for current direction run
    4276           0 :     gfx::ShapedTextFlags flags = mTextRunFlags;
    4277           0 :     if (aDirection == NSBIDI_RTL) {
    4278           0 :       flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
    4279             :     } else {
    4280           0 :       flags &= ~gfx::ShapedTextFlags::TEXT_IS_RTL;
    4281             :     }
    4282           0 :     mTextRun = mFontgrp->MakeTextRun(aText,
    4283             :                                      aLength,
    4284             :                                      mDrawTarget,
    4285             :                                      mAppUnitsPerDevPixel,
    4286             :                                      flags,
    4287             :                                      nsTextFrameUtils::Flags(),
    4288           0 :                                      mMissingFonts);
    4289           0 :   }
    4290             : 
    4291           0 :   virtual nscoord GetWidth()
    4292             :   {
    4293             :     gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(
    4294           0 :         mDoMeasureBoundingBox ? gfxFont::TIGHT_INK_EXTENTS
    4295           0 :                               : gfxFont::LOOSE_INK_EXTENTS, mDrawTarget);
    4296             : 
    4297             :     // this only measures the height; the total width is gotten from the
    4298             :     // the return value of ProcessText.
    4299           0 :     if (mDoMeasureBoundingBox) {
    4300           0 :       textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
    4301           0 :       mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
    4302             :     }
    4303             : 
    4304           0 :     return NSToCoordRound(textRunMetrics.mAdvanceWidth);
    4305             :   }
    4306             : 
    4307           0 :   already_AddRefed<gfxPattern> GetGradientFor(Style aStyle)
    4308             :   {
    4309           0 :     RefPtr<gfxPattern> pattern;
    4310           0 :     CanvasGradient* gradient = mCtx->CurrentState().gradientStyles[aStyle];
    4311           0 :     CanvasGradient::Type type = gradient->GetType();
    4312             : 
    4313           0 :     switch (type) {
    4314             :     case CanvasGradient::Type::RADIAL: {
    4315           0 :       auto radial = static_cast<CanvasRadialGradient*>(gradient);
    4316           0 :       pattern = new gfxPattern(radial->mCenter1.x, radial->mCenter1.y,
    4317           0 :                                radial->mRadius1, radial->mCenter2.x,
    4318           0 :                                radial->mCenter2.y, radial->mRadius2);
    4319           0 :       break;
    4320             :     }
    4321             :     case CanvasGradient::Type::LINEAR: {
    4322           0 :       auto linear = static_cast<CanvasLinearGradient*>(gradient);
    4323           0 :       pattern = new gfxPattern(linear->mBegin.x, linear->mBegin.y,
    4324           0 :                                linear->mEnd.x, linear->mEnd.y);
    4325           0 :       break;
    4326             :     }
    4327             :     default:
    4328           0 :       MOZ_ASSERT(false, "Should be linear or radial gradient.");
    4329             :       return nullptr;
    4330             :     }
    4331             : 
    4332           0 :     for (auto stop : gradient->mRawStops) {
    4333           0 :       pattern->AddColorStop(stop.offset, stop.color);
    4334             :     }
    4335             : 
    4336           0 :     return pattern.forget();
    4337             :   }
    4338             : 
    4339           0 :   gfx::ExtendMode CvtCanvasRepeatToGfxRepeat(
    4340             :     CanvasPattern::RepeatMode aRepeatMode)
    4341             :   {
    4342           0 :     switch (aRepeatMode) {
    4343             :     case CanvasPattern::RepeatMode::REPEAT:
    4344           0 :       return gfx::ExtendMode::REPEAT;
    4345             :     case CanvasPattern::RepeatMode::REPEATX:
    4346           0 :       return gfx::ExtendMode::REPEAT_X;
    4347             :     case CanvasPattern::RepeatMode::REPEATY:
    4348           0 :       return gfx::ExtendMode::REPEAT_Y;
    4349             :     case CanvasPattern::RepeatMode::NOREPEAT:
    4350           0 :       return gfx::ExtendMode::CLAMP;
    4351             :     default:
    4352           0 :       return gfx::ExtendMode::CLAMP;
    4353             :     }
    4354             :   }
    4355             : 
    4356           0 :   already_AddRefed<gfxPattern> GetPatternFor(Style aStyle)
    4357             :   {
    4358           0 :     const CanvasPattern* pat = mCtx->CurrentState().patternStyles[aStyle];
    4359           0 :     RefPtr<gfxPattern> pattern = new gfxPattern(pat->mSurface, Matrix());
    4360           0 :     pattern->SetExtend(CvtCanvasRepeatToGfxRepeat(pat->mRepeat));
    4361           0 :     return pattern.forget();
    4362             :   }
    4363             : 
    4364           0 :   virtual void DrawText(nscoord aXOffset, nscoord aWidth)
    4365             :   {
    4366           0 :     gfxPoint point = mPt;
    4367           0 :     bool rtl = mTextRun->IsRightToLeft();
    4368           0 :     bool verticalRun = mTextRun->IsVertical();
    4369           0 :     RefPtr<gfxPattern> pattern;
    4370             : 
    4371           0 :     gfxFloat& inlineCoord = verticalRun ? point.y : point.x;
    4372           0 :     inlineCoord += aXOffset;
    4373             : 
    4374             :     // offset is given in terms of left side of string
    4375           0 :     if (rtl) {
    4376             :       // Bug 581092 - don't use rounded pixel width to advance to
    4377             :       // right-hand end of run, because this will cause different
    4378             :       // glyph positioning for LTR vs RTL drawing of the same
    4379             :       // glyph string on OS X and DWrite where textrun widths may
    4380             :       // involve fractional pixels.
    4381             :       gfxTextRun::Metrics textRunMetrics =
    4382           0 :         mTextRun->MeasureText(mDoMeasureBoundingBox ?
    4383             :                                 gfxFont::TIGHT_INK_EXTENTS :
    4384             :                                 gfxFont::LOOSE_INK_EXTENTS,
    4385           0 :                               mDrawTarget);
    4386           0 :       inlineCoord += textRunMetrics.mAdvanceWidth;
    4387             :       // old code was:
    4388             :       //   point.x += width * mAppUnitsPerDevPixel;
    4389             :       // TODO: restore this if/when we move to fractional coords
    4390             :       // throughout the text layout process
    4391             :     }
    4392             : 
    4393           0 :     mCtx->EnsureTarget();
    4394           0 :     if (!mCtx->IsTargetValid()) {
    4395           0 :       return;
    4396             :     }
    4397             : 
    4398             :     // Defer the tasks to gfxTextRun which will handle color/svg-in-ot fonts
    4399             :     // appropriately.
    4400           0 :     StrokeOptions strokeOpts;
    4401           0 :     DrawOptions drawOpts;
    4402           0 :     Style style = (mOp == CanvasRenderingContext2D::TextDrawOperation::FILL)
    4403           0 :                     ? Style::FILL
    4404           0 :                     : Style::STROKE;
    4405             : 
    4406           0 :     AdjustedTarget target(mCtx);
    4407           0 :     if (!target) {
    4408           0 :       return;
    4409             :     }
    4410             : 
    4411             :     RefPtr<gfxContext> thebes =
    4412           0 :       gfxContext::CreatePreservingTransformOrNull(target);
    4413           0 :     if (!thebes) {
    4414             :       // If CreatePreservingTransformOrNull returns null, it will also have
    4415             :       // issued a gfxCriticalNote already, so here we'll just bail out.
    4416           0 :       return;
    4417             :     }
    4418           0 :     gfxTextRun::DrawParams params(thebes);
    4419             : 
    4420           0 :     const ContextState* state = &mCtx->CurrentState();
    4421           0 :     if (state->StyleIsColor(style)) { // Color
    4422           0 :       nscolor fontColor = state->colorStyles[style];
    4423           0 :       if (style == Style::FILL) {
    4424           0 :         params.context->SetColor(Color::FromABGR(fontColor));
    4425             :       } else {
    4426           0 :         params.textStrokeColor = fontColor;
    4427             :       }
    4428             :     } else {
    4429           0 :       if (state->gradientStyles[style]) { // Gradient
    4430           0 :         pattern = GetGradientFor(style);
    4431           0 :       } else if (state->patternStyles[style]) { // Pattern
    4432           0 :         pattern = GetPatternFor(style);
    4433             :       } else {
    4434           0 :         MOZ_ASSERT(false, "Should never reach here.");
    4435             :         return;
    4436             :       }
    4437           0 :       MOZ_ASSERT(pattern, "No valid pattern.");
    4438             : 
    4439           0 :       if (style == Style::FILL) {
    4440           0 :         params.context->SetPattern(pattern);
    4441             :       } else {
    4442           0 :         params.textStrokePattern = pattern;
    4443             :       }
    4444             :     }
    4445             : 
    4446           0 :     drawOpts.mAlpha = state->globalAlpha;
    4447           0 :     drawOpts.mCompositionOp = mCtx->UsedOperation();
    4448           0 :     if (!mCtx->IsTargetValid()) {
    4449           0 :       return;
    4450             :     }
    4451           0 :     state = &mCtx->CurrentState();
    4452           0 :     params.drawOpts = &drawOpts;
    4453             : 
    4454           0 :     if (style == Style::STROKE) {
    4455           0 :       strokeOpts.mLineWidth = state->lineWidth;
    4456           0 :       strokeOpts.mLineJoin = state->lineJoin;
    4457           0 :       strokeOpts.mLineCap = state->lineCap;
    4458           0 :       strokeOpts.mMiterLimit = state->miterLimit;
    4459           0 :       strokeOpts.mDashLength = state->dash.Length();
    4460           0 :       strokeOpts.mDashPattern =
    4461           0 :         (strokeOpts.mDashLength > 0) ? state->dash.Elements() : 0;
    4462           0 :       strokeOpts.mDashOffset = state->dashOffset;
    4463             : 
    4464           0 :       params.drawMode = DrawMode::GLYPH_STROKE;
    4465           0 :       params.strokeOpts = &strokeOpts;
    4466             :     }
    4467             : 
    4468           0 :     mTextRun->Draw(gfxTextRun::Range(mTextRun.get()), point, params);
    4469             :   }
    4470             : 
    4471             :   // current text run
    4472             :   RefPtr<gfxTextRun> mTextRun;
    4473             : 
    4474             :   // pointer to a screen reference context used to measure text and such
    4475             :   RefPtr<DrawTarget> mDrawTarget;
    4476             : 
    4477             :   // Pointer to the draw target we should fill our text to
    4478             :   CanvasRenderingContext2D* mCtx;
    4479             : 
    4480             :   // position of the left side of the string, alphabetic baseline
    4481             :   gfxPoint mPt;
    4482             : 
    4483             :   // current font
    4484             :   gfxFontGroup* mFontgrp;
    4485             : 
    4486             :   // to record any unsupported characters found in the text,
    4487             :   // and notify front-end if it is interested
    4488             :   nsAutoPtr<gfxMissingFontRecorder> mMissingFonts;
    4489             : 
    4490             :   // dev pixel conversion factor
    4491             :   int32_t mAppUnitsPerDevPixel;
    4492             : 
    4493             :   // operation (fill or stroke)
    4494             :   CanvasRenderingContext2D::TextDrawOperation mOp;
    4495             : 
    4496             :   // union of bounding boxes of all runs, needed for shadows
    4497             :   gfxRect mBoundingBox;
    4498             : 
    4499             :   // flags to use when creating textrun, based on CSS style
    4500             :   gfx::ShapedTextFlags mTextRunFlags;
    4501             : 
    4502             :   // true iff the bounding box should be measured
    4503             :   bool mDoMeasureBoundingBox;
    4504             : };
    4505             : 
    4506             : nsresult
    4507           0 : CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
    4508             :                                             float aX,
    4509             :                                             float aY,
    4510             :                                             const Optional<double>& aMaxWidth,
    4511             :                                             TextDrawOperation aOp,
    4512             :                                             float* aWidth)
    4513             : {
    4514             :   nsresult rv;
    4515             : 
    4516           0 :   if (!mCanvasElement && !mDocShell) {
    4517           0 :     NS_WARNING("Canvas element must be non-null or a docshell must be provided");
    4518           0 :     return NS_ERROR_FAILURE;
    4519             :   }
    4520             : 
    4521           0 :   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    4522           0 :   if (!presShell)
    4523           0 :     return NS_ERROR_FAILURE;
    4524             : 
    4525           0 :   nsIDocument* document = presShell->GetDocument();
    4526             : 
    4527             :   // replace all the whitespace characters with U+0020 SPACE
    4528           0 :   nsAutoString textToDraw(aRawText);
    4529           0 :   TextReplaceWhitespaceCharacters(textToDraw);
    4530             : 
    4531             :   // According to spec, the API should return an empty array if maxWidth was provided
    4532             :   // but is less than or equal to zero or equal to NaN.
    4533           0 :   if (aMaxWidth.WasPassed() && (aMaxWidth.Value() <= 0 || IsNaN(aMaxWidth.Value()))) {
    4534           0 :     textToDraw.Truncate();
    4535             :   }
    4536             : 
    4537             :   // for now, default to ltr if not in doc
    4538           0 :   bool isRTL = false;
    4539             : 
    4540           0 :   RefPtr<nsStyleContext> canvasStyle;
    4541           0 :   if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) {
    4542             :     // try to find the closest context
    4543             :     canvasStyle =
    4544           0 :       nsComputedDOMStyle::GetStyleContext(mCanvasElement, nullptr, presShell);
    4545           0 :     if (!canvasStyle) {
    4546           0 :       return NS_ERROR_FAILURE;
    4547             :     }
    4548             : 
    4549           0 :     isRTL = canvasStyle->StyleVisibility()->mDirection ==
    4550             :       NS_STYLE_DIRECTION_RTL;
    4551             :   } else {
    4552           0 :     isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
    4553             :   }
    4554             : 
    4555             :   // This is only needed to know if we can know the drawing bounding box easily.
    4556           0 :   const bool doCalculateBounds = NeedToCalculateBounds();
    4557           0 :   if (presShell->IsDestroying()) {
    4558           0 :     return NS_ERROR_FAILURE;
    4559             :   }
    4560             : 
    4561           0 :   gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
    4562           0 :   if (!currentFontStyle) {
    4563           0 :     return NS_ERROR_FAILURE;
    4564             :   }
    4565             : 
    4566           0 :   MOZ_ASSERT(!presShell->IsDestroying(),
    4567             :              "GetCurrentFontStyle() should have returned null if the presshell is being destroyed");
    4568             : 
    4569             :   // ensure user font set is up to date
    4570             :   currentFontStyle->
    4571           0 :     SetUserFontSet(presShell->GetPresContext()->GetUserFontSet());
    4572             : 
    4573           0 :   if (currentFontStyle->GetStyle()->size == 0.0F) {
    4574           0 :     if (aWidth) {
    4575           0 :       *aWidth = 0;
    4576             :     }
    4577           0 :     return NS_OK;
    4578             :   }
    4579             : 
    4580           0 :   if (!IsFinite(aX) || !IsFinite(aY)) {
    4581           0 :     return NS_OK;
    4582             :   }
    4583             : 
    4584           0 :   CanvasBidiProcessor processor;
    4585             : 
    4586             :   // If we don't have a style context, we can't set up vertical-text flags
    4587             :   // (for now, at least; perhaps we need new Canvas API to control this).
    4588           0 :   processor.mTextRunFlags = canvasStyle
    4589           0 :     ? nsLayoutUtils::GetTextRunFlagsForStyle(canvasStyle,
    4590             :                                              canvasStyle->StyleFont(),
    4591             :                                              canvasStyle->StyleText(),
    4592             :                                              0)
    4593           0 :     : gfx::ShapedTextFlags();
    4594             : 
    4595           0 :   GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
    4596           0 :   processor.mPt = gfxPoint(aX, aY);
    4597             :   processor.mDrawTarget =
    4598           0 :     gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
    4599             : 
    4600             :   // If we don't have a target then we don't have a transform. A target won't
    4601             :   // be needed in the case where we're measuring the text size. This allows
    4602             :   // to avoid creating a target if it's only being used to measure text sizes.
    4603           0 :   if (mTarget) {
    4604           0 :     processor.mDrawTarget->SetTransform(mTarget->GetTransform());
    4605             :   }
    4606           0 :   processor.mCtx = this;
    4607           0 :   processor.mOp = aOp;
    4608           0 :   processor.mBoundingBox = gfxRect(0, 0, 0, 0);
    4609           0 :   processor.mDoMeasureBoundingBox = doCalculateBounds || !mIsEntireFrameInvalid;
    4610           0 :   processor.mFontgrp = currentFontStyle;
    4611             : 
    4612             :   nscoord totalWidthCoord;
    4613             : 
    4614             :   // calls bidi algo twice since it needs the full text width and the
    4615             :   // bounding boxes before rendering anything
    4616           0 :   rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
    4617           0 :                                     textToDraw.Length(),
    4618             :                                     isRTL ? NSBIDI_RTL : NSBIDI_LTR,
    4619             :                                     presShell->GetPresContext(),
    4620             :                                     processor,
    4621             :                                     nsBidiPresUtils::MODE_MEASURE,
    4622             :                                     nullptr,
    4623             :                                     0,
    4624             :                                     &totalWidthCoord,
    4625           0 :                                     &mBidiEngine);
    4626           0 :   if (NS_FAILED(rv)) {
    4627           0 :     return rv;
    4628             :   }
    4629             : 
    4630           0 :   float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
    4631           0 :   if (aWidth) {
    4632           0 :     *aWidth = totalWidth;
    4633             :   }
    4634             : 
    4635             :   // if only measuring, don't need to do any more work
    4636           0 :   if (aOp==TextDrawOperation::MEASURE) {
    4637           0 :     return NS_OK;
    4638             :   }
    4639             : 
    4640             :   // offset pt.x based on text align
    4641             :   gfxFloat anchorX;
    4642             : 
    4643           0 :   const ContextState& state = CurrentState();
    4644           0 :   if (state.textAlign == TextAlign::CENTER) {
    4645           0 :     anchorX = .5;
    4646           0 :   } else if (state.textAlign == TextAlign::LEFT ||
    4647           0 :             (!isRTL && state.textAlign == TextAlign::START) ||
    4648           0 :             (isRTL && state.textAlign == TextAlign::END)) {
    4649           0 :     anchorX = 0;
    4650             :   } else {
    4651           0 :     anchorX = 1;
    4652             :   }
    4653             : 
    4654           0 :   processor.mPt.x -= anchorX * totalWidth;
    4655             : 
    4656             :   // offset pt.y (or pt.x, for vertical text) based on text baseline
    4657           0 :   processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
    4658             :   const gfxFont::Metrics& fontMetrics =
    4659           0 :     processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
    4660             : 
    4661             :   gfxFloat baselineAnchor;
    4662             : 
    4663           0 :   switch (state.textBaseline)
    4664             :   {
    4665             :   case TextBaseline::HANGING:
    4666             :       // fall through; best we can do with the information available
    4667             :   case TextBaseline::TOP:
    4668           0 :     baselineAnchor = fontMetrics.emAscent;
    4669           0 :     break;
    4670             :   case TextBaseline::MIDDLE:
    4671           0 :     baselineAnchor = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
    4672           0 :     break;
    4673             :   case TextBaseline::IDEOGRAPHIC:
    4674             :     // fall through; best we can do with the information available
    4675             :   case TextBaseline::ALPHABETIC:
    4676           0 :     baselineAnchor = 0;
    4677           0 :     break;
    4678             :   case TextBaseline::BOTTOM:
    4679           0 :     baselineAnchor = -fontMetrics.emDescent;
    4680           0 :     break;
    4681             :   default:
    4682           0 :     MOZ_CRASH("GFX: unexpected TextBaseline");
    4683             :   }
    4684             : 
    4685             :   // We can't query the textRun directly, as it may not have been created yet;
    4686             :   // so instead we check the flags that will be used to initialize it.
    4687             :   gfx::ShapedTextFlags runOrientation =
    4688           0 :     (processor.mTextRunFlags & gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
    4689           0 :   if (runOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL) {
    4690           0 :     if (runOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED ||
    4691             :         runOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) {
    4692             :       // Adjust to account for mTextRun being shaped using center baseline
    4693             :       // rather than alphabetic.
    4694           0 :       baselineAnchor -= (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
    4695             :     }
    4696           0 :     processor.mPt.x -= baselineAnchor;
    4697             :   } else {
    4698           0 :     processor.mPt.y += baselineAnchor;
    4699             :   }
    4700             : 
    4701             :   // correct bounding box to get it to be the correct size/position
    4702           0 :   processor.mBoundingBox.width = totalWidth;
    4703           0 :   processor.mBoundingBox.MoveBy(processor.mPt);
    4704             : 
    4705           0 :   processor.mPt.x *= processor.mAppUnitsPerDevPixel;
    4706           0 :   processor.mPt.y *= processor.mAppUnitsPerDevPixel;
    4707             : 
    4708           0 :   EnsureTarget();
    4709           0 :   if (!IsTargetValid()) {
    4710           0 :     return NS_ERROR_FAILURE;
    4711             :   }
    4712             : 
    4713           0 :   Matrix oldTransform = mTarget->GetTransform();
    4714             :   // if text is over aMaxWidth, then scale the text horizontally such that its
    4715             :   // width is precisely aMaxWidth
    4716           0 :   if (aMaxWidth.WasPassed() && aMaxWidth.Value() > 0 &&
    4717           0 :       totalWidth > aMaxWidth.Value()) {
    4718           0 :     Matrix newTransform = oldTransform;
    4719             : 
    4720             :     // Translate so that the anchor point is at 0,0, then scale and then
    4721             :     // translate back.
    4722           0 :     newTransform.PreTranslate(aX, 0);
    4723           0 :     newTransform.PreScale(aMaxWidth.Value() / totalWidth, 1);
    4724           0 :     newTransform.PreTranslate(-aX, 0);
    4725             :     /* we do this to avoid an ICE in the android compiler */
    4726           0 :     Matrix androidCompilerBug = newTransform;
    4727           0 :     mTarget->SetTransform(androidCompilerBug);
    4728             :   }
    4729             : 
    4730             :   // save the previous bounding box
    4731           0 :   gfxRect boundingBox = processor.mBoundingBox;
    4732             : 
    4733             :   // don't ever need to measure the bounding box twice
    4734           0 :   processor.mDoMeasureBoundingBox = false;
    4735             : 
    4736           0 :   rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
    4737           0 :                                     textToDraw.Length(),
    4738             :                                     isRTL ? NSBIDI_RTL : NSBIDI_LTR,
    4739             :                                     presShell->GetPresContext(),
    4740             :                                     processor,
    4741             :                                     nsBidiPresUtils::MODE_DRAW,
    4742             :                                     nullptr,
    4743             :                                     0,
    4744             :                                     nullptr,
    4745           0 :                                     &mBidiEngine);
    4746             : 
    4747             : 
    4748           0 :   mTarget->SetTransform(oldTransform);
    4749             : 
    4750           0 :   if (aOp == CanvasRenderingContext2D::TextDrawOperation::FILL &&
    4751           0 :       !doCalculateBounds) {
    4752           0 :     RedrawUser(boundingBox);
    4753           0 :     return NS_OK;
    4754             :   }
    4755             : 
    4756           0 :   Redraw();
    4757           0 :   return NS_OK;
    4758             : }
    4759             : 
    4760             : gfxFontGroup*
    4761           0 : CanvasRenderingContext2D::GetCurrentFontStyle()
    4762             : {
    4763             :   // use lazy initilization for the font group since it's rather expensive
    4764           0 :   if (!CurrentState().fontGroup) {
    4765           0 :     ErrorResult err;
    4766           0 :     NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
    4767             :     static float kDefaultFontSize = 10.0;
    4768           0 :     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
    4769           0 :     bool fontUpdated = SetFontInternal(kDefaultFontStyle, err);
    4770           0 :     if (err.Failed() || !fontUpdated) {
    4771           0 :       err.SuppressException();
    4772           0 :       gfxFontStyle style;
    4773           0 :       style.size = kDefaultFontSize;
    4774           0 :       gfxTextPerfMetrics* tp = nullptr;
    4775           0 :       if (presShell && !presShell->IsDestroying()) {
    4776           0 :         tp = presShell->GetPresContext()->GetTextPerfMetrics();
    4777             :       }
    4778             :       int32_t perDevPixel, perCSSPixel;
    4779           0 :       GetAppUnitsValues(&perDevPixel, &perCSSPixel);
    4780           0 :       gfxFloat devToCssSize = gfxFloat(perDevPixel) / gfxFloat(perCSSPixel);
    4781           0 :       CurrentState().fontGroup =
    4782           0 :         gfxPlatform::GetPlatform()->CreateFontGroup(FontFamilyList(eFamily_sans_serif),
    4783             :                                                     &style, tp,
    4784           0 :                                                     nullptr, devToCssSize);
    4785           0 :       if (CurrentState().fontGroup) {
    4786           0 :         CurrentState().font = kDefaultFontStyle;
    4787             :       } else {
    4788           0 :         NS_ERROR("Default canvas font is invalid");
    4789             :       }
    4790             :     }
    4791             :   }
    4792             : 
    4793           0 :   return CurrentState().fontGroup;
    4794             : }
    4795             : 
    4796             : //
    4797             : // line caps/joins
    4798             : //
    4799             : 
    4800             : void
    4801           0 : CanvasRenderingContext2D::SetLineCap(const nsAString& aLinecapStyle)
    4802             : {
    4803             :   CapStyle cap;
    4804             : 
    4805           0 :   if (aLinecapStyle.EqualsLiteral("butt")) {
    4806           0 :     cap = CapStyle::BUTT;
    4807           0 :   } else if (aLinecapStyle.EqualsLiteral("round")) {
    4808           0 :     cap = CapStyle::ROUND;
    4809           0 :   } else if (aLinecapStyle.EqualsLiteral("square")) {
    4810           0 :     cap = CapStyle::SQUARE;
    4811             :   } else {
    4812             :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
    4813           0 :     return;
    4814             :   }
    4815             : 
    4816           0 :   CurrentState().lineCap = cap;
    4817             : }
    4818             : 
    4819             : void
    4820           0 : CanvasRenderingContext2D::GetLineCap(nsAString& aLinecapStyle)
    4821             : {
    4822           0 :   switch (CurrentState().lineCap) {
    4823             :   case CapStyle::BUTT:
    4824           0 :     aLinecapStyle.AssignLiteral("butt");
    4825           0 :     break;
    4826             :   case CapStyle::ROUND:
    4827           0 :     aLinecapStyle.AssignLiteral("round");
    4828           0 :     break;
    4829             :   case CapStyle::SQUARE:
    4830           0 :     aLinecapStyle.AssignLiteral("square");
    4831           0 :     break;
    4832             :   }
    4833           0 : }
    4834             : 
    4835             : void
    4836           0 : CanvasRenderingContext2D::SetLineJoin(const nsAString& aLinejoinStyle)
    4837             : {
    4838             :   JoinStyle j;
    4839             : 
    4840           0 :   if (aLinejoinStyle.EqualsLiteral("round")) {
    4841           0 :     j = JoinStyle::ROUND;
    4842           0 :   } else if (aLinejoinStyle.EqualsLiteral("bevel")) {
    4843           0 :     j = JoinStyle::BEVEL;
    4844           0 :   } else if (aLinejoinStyle.EqualsLiteral("miter")) {
    4845           0 :     j = JoinStyle::MITER_OR_BEVEL;
    4846             :   } else {
    4847             :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
    4848           0 :     return;
    4849             :   }
    4850             : 
    4851           0 :   CurrentState().lineJoin = j;
    4852             : }
    4853             : 
    4854             : void
    4855           0 : CanvasRenderingContext2D::GetLineJoin(nsAString& aLinejoinStyle, ErrorResult& aError)
    4856             : {
    4857           0 :   switch (CurrentState().lineJoin) {
    4858             :   case JoinStyle::ROUND:
    4859           0 :     aLinejoinStyle.AssignLiteral("round");
    4860           0 :     break;
    4861             :   case JoinStyle::BEVEL:
    4862           0 :     aLinejoinStyle.AssignLiteral("bevel");
    4863           0 :     break;
    4864             :   case JoinStyle::MITER_OR_BEVEL:
    4865           0 :     aLinejoinStyle.AssignLiteral("miter");
    4866           0 :     break;
    4867             :   default:
    4868           0 :     aError.Throw(NS_ERROR_FAILURE);
    4869             :   }
    4870           0 : }
    4871             : 
    4872             : void
    4873           0 : CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments,
    4874             :                                       ErrorResult& aRv)
    4875             : {
    4876           0 :   nsTArray<mozilla::gfx::Float> dash;
    4877             : 
    4878           0 :   for (uint32_t x = 0; x < aSegments.Length(); x++) {
    4879           0 :     if (aSegments[x] < 0.0) {
    4880             :       // Pattern elements must be finite "numbers" >= 0, with "finite"
    4881             :       // taken care of by WebIDL
    4882           0 :       return;
    4883             :     }
    4884             : 
    4885           0 :     if (!dash.AppendElement(aSegments[x], fallible)) {
    4886           0 :       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    4887           0 :       return;
    4888             :     }
    4889             :   }
    4890           0 :   if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
    4891           0 :     for (uint32_t x = 0; x < aSegments.Length(); x++) {
    4892           0 :       if (!dash.AppendElement(aSegments[x], fallible)) {
    4893           0 :         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    4894           0 :         return;
    4895             :       }
    4896             :     }
    4897             :   }
    4898             : 
    4899           0 :   CurrentState().dash = Move(dash);
    4900             : }
    4901             : 
    4902             : void
    4903           0 : CanvasRenderingContext2D::GetLineDash(nsTArray<double>& aSegments) const {
    4904           0 :   const nsTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
    4905           0 :   aSegments.Clear();
    4906             : 
    4907           0 :   for (uint32_t x = 0; x < dash.Length(); x++) {
    4908           0 :     aSegments.AppendElement(dash[x]);
    4909             :   }
    4910           0 : }
    4911             : 
    4912             : void
    4913           0 : CanvasRenderingContext2D::SetLineDashOffset(double aOffset) {
    4914           0 :   CurrentState().dashOffset = aOffset;
    4915           0 : }
    4916             : 
    4917             : double
    4918           0 : CanvasRenderingContext2D::LineDashOffset() const {
    4919           0 :   return CurrentState().dashOffset;
    4920             : }
    4921             : 
    4922             : bool
    4923           0 : CanvasRenderingContext2D::IsPointInPath(double aX, double aY, const CanvasWindingRule& aWinding)
    4924             : {
    4925           0 :   if (!FloatValidate(aX, aY)) {
    4926           0 :     return false;
    4927             :   }
    4928             : 
    4929           0 :   EnsureUserSpacePath(aWinding);
    4930           0 :   if (!mPath) {
    4931           0 :     return false;
    4932             :   }
    4933             : 
    4934           0 :   if (mPathTransformWillUpdate) {
    4935           0 :     return mPath->ContainsPoint(Point(aX, aY), mPathToDS);
    4936             :   }
    4937             : 
    4938           0 :   return mPath->ContainsPoint(Point(aX, aY), mTarget->GetTransform());
    4939             : }
    4940             : 
    4941           0 : bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding)
    4942             : {
    4943           0 :   if (!FloatValidate(aX, aY)) {
    4944           0 :     return false;
    4945             :   }
    4946             : 
    4947           0 :   EnsureTarget();
    4948           0 :   if (!IsTargetValid()) {
    4949           0 :     return false;
    4950             :   }
    4951             : 
    4952           0 :   RefPtr<gfx::Path> tempPath = aPath.GetPath(aWinding, mTarget);
    4953             : 
    4954           0 :   return tempPath->ContainsPoint(Point(aX, aY), mTarget->GetTransform());
    4955             : }
    4956             : 
    4957             : bool
    4958           0 : CanvasRenderingContext2D::IsPointInStroke(double aX, double aY)
    4959             : {
    4960           0 :   if (!FloatValidate(aX, aY)) {
    4961           0 :     return false;
    4962             :   }
    4963             : 
    4964           0 :   EnsureUserSpacePath();
    4965           0 :   if (!mPath) {
    4966           0 :     return false;
    4967             :   }
    4968             : 
    4969           0 :   const ContextState &state = CurrentState();
    4970             : 
    4971           0 :   StrokeOptions strokeOptions(state.lineWidth,
    4972           0 :                               state.lineJoin,
    4973           0 :                               state.lineCap,
    4974           0 :                               state.miterLimit,
    4975             :                               state.dash.Length(),
    4976             :                               state.dash.Elements(),
    4977           0 :                               state.dashOffset);
    4978             : 
    4979           0 :   if (mPathTransformWillUpdate) {
    4980           0 :     return mPath->StrokeContainsPoint(strokeOptions, Point(aX, aY), mPathToDS);
    4981             :   }
    4982           0 :   return mPath->StrokeContainsPoint(strokeOptions, Point(aX, aY), mTarget->GetTransform());
    4983             : }
    4984             : 
    4985           0 : bool CanvasRenderingContext2D::IsPointInStroke(const CanvasPath& aPath, double aX, double aY)
    4986             : {
    4987           0 :   if (!FloatValidate(aX, aY)) {
    4988           0 :     return false;
    4989             :   }
    4990             : 
    4991           0 :   EnsureTarget();
    4992           0 :   if (!IsTargetValid()) {
    4993           0 :     return false;
    4994             :   }
    4995             : 
    4996           0 :   RefPtr<gfx::Path> tempPath = aPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
    4997             : 
    4998           0 :   const ContextState &state = CurrentState();
    4999             : 
    5000           0 :   StrokeOptions strokeOptions(state.lineWidth,
    5001           0 :                               state.lineJoin,
    5002           0 :                               state.lineCap,
    5003           0 :                               state.miterLimit,
    5004             :                               state.dash.Length(),
    5005             :                               state.dash.Elements(),
    5006           0 :                               state.dashOffset);
    5007             : 
    5008           0 :   return tempPath->StrokeContainsPoint(strokeOptions, Point(aX, aY), mTarget->GetTransform());
    5009             : }
    5010             : 
    5011             : // Returns a surface that contains only the part needed to draw aSourceRect.
    5012             : // On entry, aSourceRect is relative to aSurface, and on return aSourceRect is
    5013             : // relative to the returned surface.
    5014             : static already_AddRefed<SourceSurface>
    5015           0 : ExtractSubrect(SourceSurface* aSurface, gfx::Rect* aSourceRect, DrawTarget* aTargetDT)
    5016             : {
    5017           0 :   gfx::Rect roundedOutSourceRect = *aSourceRect;
    5018           0 :   roundedOutSourceRect.RoundOut();
    5019           0 :   gfx::IntRect roundedOutSourceRectInt;
    5020           0 :   if (!roundedOutSourceRect.ToIntRect(&roundedOutSourceRectInt)) {
    5021           0 :     RefPtr<SourceSurface> surface(aSurface);
    5022           0 :     return surface.forget();
    5023             :   }
    5024             : 
    5025             :   RefPtr<DrawTarget> subrectDT =
    5026           0 :     aTargetDT->CreateSimilarDrawTarget(roundedOutSourceRectInt.Size(), SurfaceFormat::B8G8R8A8);
    5027             : 
    5028           0 :   if (!subrectDT) {
    5029           0 :     RefPtr<SourceSurface> surface(aSurface);
    5030           0 :     return surface.forget();
    5031             :   }
    5032             : 
    5033           0 :   *aSourceRect -= roundedOutSourceRect.TopLeft();
    5034             : 
    5035           0 :   subrectDT->CopySurface(aSurface, roundedOutSourceRectInt, IntPoint());
    5036           0 :   return subrectDT->Snapshot();
    5037             : }
    5038             : 
    5039             : //
    5040             : // image
    5041             : //
    5042             : 
    5043             : static void
    5044           0 : ClipImageDimension(double& aSourceCoord, double& aSourceSize, int32_t aImageSize,
    5045             :                    double& aDestCoord, double& aDestSize)
    5046             : {
    5047           0 :   double scale = aDestSize / aSourceSize;
    5048           0 :   if (aSourceCoord < 0.0) {
    5049           0 :     double destEnd = aDestCoord + aDestSize;
    5050           0 :     aDestCoord -= aSourceCoord * scale;
    5051           0 :     aDestSize = destEnd - aDestCoord;
    5052           0 :     aSourceSize += aSourceCoord;
    5053           0 :     aSourceCoord = 0.0;
    5054             :   }
    5055           0 :   double delta = aImageSize - (aSourceCoord + aSourceSize);
    5056           0 :   if (delta < 0.0) {
    5057           0 :     aDestSize += delta * scale;
    5058           0 :     aSourceSize = aImageSize - aSourceCoord;
    5059             :   }
    5060           0 : }
    5061             : 
    5062             : // Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt
    5063             : // to pull a SourceSurface from our cache. This allows us to avoid
    5064             : // reoptimizing surfaces if content and canvas backends are different.
    5065             : nsLayoutUtils::SurfaceFromElementResult
    5066           0 : CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement)
    5067             : {
    5068           0 :   nsLayoutUtils::SurfaceFromElementResult res;
    5069           0 :   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
    5070           0 :   if (!imageLoader) {
    5071           0 :     return res;
    5072             :   }
    5073             : 
    5074           0 :   nsCOMPtr<imgIRequest> imgRequest;
    5075           0 :   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
    5076           0 :                           getter_AddRefs(imgRequest));
    5077           0 :   if (!imgRequest) {
    5078           0 :     return res;
    5079             :   }
    5080             : 
    5081           0 :   uint32_t status = 0;
    5082           0 :   if (NS_FAILED(imgRequest->GetImageStatus(&status)) ||
    5083           0 :       !(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
    5084           0 :     return res;
    5085             :   }
    5086             : 
    5087           0 :   nsCOMPtr<nsIPrincipal> principal;
    5088           0 :   if (NS_FAILED(imgRequest->GetImagePrincipal(getter_AddRefs(principal))) ||
    5089           0 :       !principal) {
    5090           0 :     return res;
    5091             :   }
    5092             : 
    5093             :   res.mSourceSurface =
    5094           0 :     CanvasImageCache::LookupAllCanvas(aElement, mIsSkiaGL);
    5095           0 :   if (!res.mSourceSurface) {
    5096           0 :     return res;
    5097             :   }
    5098             : 
    5099           0 :   int32_t corsmode = imgIRequest::CORS_NONE;
    5100           0 :   if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
    5101           0 :     res.mCORSUsed = corsmode != imgIRequest::CORS_NONE;
    5102             :   }
    5103             : 
    5104           0 :   res.mSize = res.mSourceSurface->GetSize();
    5105           0 :   res.mPrincipal = principal.forget();
    5106           0 :   res.mIsWriteOnly = false;
    5107           0 :   res.mImageRequest = imgRequest.forget();
    5108             : 
    5109           0 :   return res;
    5110             : }
    5111             : 
    5112             : // drawImage(in HTMLImageElement image, in float dx, in float dy);
    5113             : //   -- render image from 0,0 at dx,dy top-left coords
    5114             : // drawImage(in HTMLImageElement image, in float dx, in float dy, in float dw, in float dh);
    5115             : //   -- render image from 0,0 at dx,dy top-left coords clipping it to dw,dh
    5116             : // drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
    5117             : //   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
    5118             : 
    5119             : // If only dx and dy are passed in then optional_argc should be 0. If only
    5120             : // dx, dy, dw and dh are passed in then optional_argc should be 2. The only
    5121             : // other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
    5122             : // are all passed in.
    5123             : 
    5124             : void
    5125           0 : CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
    5126             :                                     double aSx, double aSy, double aSw,
    5127             :                                     double aSh, double aDx, double aDy,
    5128             :                                     double aDw, double aDh,
    5129             :                                     uint8_t aOptional_argc,
    5130             :                                     ErrorResult& aError)
    5131             : {
    5132           0 :   if (mDrawObserver) {
    5133           0 :     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage);
    5134             :   }
    5135             : 
    5136           0 :   MOZ_ASSERT(aOptional_argc == 0 || aOptional_argc == 2 || aOptional_argc == 6);
    5137             : 
    5138           0 :   if (!ValidateRect(aDx, aDy, aDw, aDh, true)) {
    5139           0 :     return;
    5140             :   }
    5141           0 :   if (aOptional_argc == 6) {
    5142           0 :     if (!ValidateRect(aSx, aSy, aSw, aSh, true)) {
    5143           0 :       return;
    5144             :     }
    5145             :   }
    5146             : 
    5147           0 :   RefPtr<SourceSurface> srcSurf;
    5148           0 :   gfx::IntSize imgSize;
    5149             : 
    5150           0 :   Element* element = nullptr;
    5151             : 
    5152           0 :   EnsureTarget();
    5153           0 :   if (!IsTargetValid()) {
    5154           0 :     return;
    5155             :   }
    5156             : 
    5157           0 :   if (aImage.IsHTMLCanvasElement()) {
    5158           0 :     HTMLCanvasElement* canvas = &aImage.GetAsHTMLCanvasElement();
    5159           0 :     element = canvas;
    5160           0 :     nsIntSize size = canvas->GetSize();
    5161           0 :     if (size.width == 0 || size.height == 0) {
    5162           0 :       aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    5163           0 :       return;
    5164             :     }
    5165           0 :   } else if (aImage.IsImageBitmap()) {
    5166           0 :     ImageBitmap& imageBitmap = aImage.GetAsImageBitmap();
    5167           0 :     srcSurf = imageBitmap.PrepareForDrawTarget(mTarget);
    5168             : 
    5169           0 :     if (!srcSurf) {
    5170           0 :       return;
    5171             :     }
    5172             : 
    5173           0 :     imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height());
    5174             :   }
    5175             :   else {
    5176           0 :     if (aImage.IsHTMLImageElement()) {
    5177           0 :       HTMLImageElement* img = &aImage.GetAsHTMLImageElement();
    5178           0 :       element = img;
    5179             :     } else {
    5180           0 :       HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
    5181           0 :       video->MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::DRAW_IMAGE);
    5182           0 :       element = video;
    5183             :     }
    5184             : 
    5185             :     srcSurf =
    5186           0 :      CanvasImageCache::LookupCanvas(element, mCanvasElement, &imgSize, mIsSkiaGL);
    5187             :   }
    5188             : 
    5189           0 :   nsLayoutUtils::DirectDrawInfo drawInfo;
    5190             : 
    5191             : #ifdef USE_SKIA_GPU
    5192           0 :   if (mRenderingMode == RenderingMode::OpenGLBackendMode &&
    5193           0 :       mIsSkiaGL &&
    5194           0 :       !srcSurf &&
    5195           0 :       aImage.IsHTMLVideoElement() &&
    5196           0 :       AllowOpenGLCanvas()) {
    5197           0 :     mozilla::gl::GLContext* gl = gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext();
    5198           0 :     MOZ_ASSERT(gl);
    5199             : 
    5200           0 :     HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
    5201           0 :     if (!video) {
    5202           0 :       return;
    5203             :     }
    5204             : 
    5205           0 :     if (video->ContainsRestrictedContent()) {
    5206           0 :       aError.Throw(NS_ERROR_NOT_AVAILABLE);
    5207           0 :       return;
    5208             :     }
    5209             : 
    5210             :     uint16_t readyState;
    5211           0 :     if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
    5212           0 :         readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
    5213             :       // still loading, just return
    5214           0 :       return;
    5215             :     }
    5216             : 
    5217             :     // If it doesn't have a principal, just bail
    5218           0 :     nsCOMPtr<nsIPrincipal> principal = video->GetCurrentVideoPrincipal();
    5219           0 :     if (!principal) {
    5220           0 :       aError.Throw(NS_ERROR_NOT_AVAILABLE);
    5221           0 :       return;
    5222             :     }
    5223             : 
    5224           0 :     mozilla::layers::ImageContainer* container = video->GetImageContainer();
    5225           0 :     if (!container) {
    5226           0 :       aError.Throw(NS_ERROR_NOT_AVAILABLE);
    5227           0 :       return;
    5228             :     }
    5229             : 
    5230           0 :     AutoLockImage lockImage(container);
    5231           0 :     layers::Image* srcImage = lockImage.GetImage();
    5232           0 :     if (!srcImage) {
    5233           0 :       aError.Throw(NS_ERROR_NOT_AVAILABLE);
    5234           0 :       return;
    5235             :     }
    5236             : 
    5237             :     {
    5238           0 :       gl->MakeCurrent();
    5239           0 :       GLuint videoTexture = 0;
    5240           0 :       gl->fGenTextures(1, &videoTexture);
    5241             :       // skiaGL expect upload on drawing, and uses texture 0 for texturing,
    5242             :       // so we must active texture 0 and bind the texture for it.
    5243           0 :       gl->fActiveTexture(LOCAL_GL_TEXTURE0);
    5244           0 :       const gl::ScopedBindTexture scopeBindTexture(gl, videoTexture);
    5245             : 
    5246           0 :       gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGB, srcImage->GetSize().width, srcImage->GetSize().height, 0, LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5, nullptr);
    5247           0 :       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
    5248           0 :       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
    5249           0 :       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
    5250           0 :       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
    5251             : 
    5252           0 :       const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
    5253           0 :       bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage, srcImage->GetSize(),
    5254             :                                                      videoTexture, LOCAL_GL_TEXTURE_2D,
    5255           0 :                                                      destOrigin);
    5256           0 :       if (ok) {
    5257           0 :         NativeSurface texSurf;
    5258           0 :         texSurf.mType = NativeSurfaceType::OPENGL_TEXTURE;
    5259           0 :         texSurf.mFormat = SurfaceFormat::R5G6B5_UINT16;
    5260           0 :         texSurf.mSize.width = srcImage->GetSize().width;
    5261           0 :         texSurf.mSize.height = srcImage->GetSize().height;
    5262           0 :         texSurf.mSurface = (void*)((uintptr_t)videoTexture);
    5263             : 
    5264           0 :         srcSurf = mTarget->CreateSourceSurfaceFromNativeSurface(texSurf);
    5265           0 :         if (!srcSurf) {
    5266           0 :           gl->fDeleteTextures(1, &videoTexture);
    5267             :         }
    5268           0 :         imgSize.width = srcImage->GetSize().width;
    5269           0 :         imgSize.height = srcImage->GetSize().height;
    5270             : 
    5271           0 :         int32_t displayWidth = video->VideoWidth();
    5272           0 :         int32_t displayHeight = video->VideoHeight();
    5273           0 :         aSw *= (double)imgSize.width / (double)displayWidth;
    5274           0 :         aSh *= (double)imgSize.height / (double)displayHeight;
    5275             :       } else {
    5276           0 :         gl->fDeleteTextures(1, &videoTexture);
    5277             :       }
    5278             :     }
    5279             : 
    5280           0 :     srcImage = nullptr;
    5281             : 
    5282           0 :     if (mCanvasElement) {
    5283           0 :       CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
    5284             :                                             principal, false,
    5285           0 :                                             video->GetCORSMode() != CORS_NONE);
    5286             :     }
    5287             :   }
    5288             : #endif
    5289           0 :   if (!srcSurf) {
    5290             :     // The canvas spec says that drawImage should draw the first frame
    5291             :     // of animated images. We also don't want to rasterize vector images.
    5292             :     uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
    5293           0 :                         nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS;
    5294             : 
    5295             :     nsLayoutUtils::SurfaceFromElementResult res =
    5296           0 :       CanvasRenderingContext2D::CachedSurfaceFromElement(element);
    5297             : 
    5298           0 :     if (!res.mSourceSurface) {
    5299           0 :       res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
    5300             :     }
    5301             : 
    5302           0 :     if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
    5303             :       // The spec says to silently do nothing in the following cases:
    5304             :       //   - The element is still loading.
    5305             :       //   - The image is bad, but it's not in the broken state (i.e., we could
    5306             :       //     decode the headers and get the size).
    5307           0 :       if (!res.mIsStillLoading && !res.mHasSize) {
    5308           0 :         aError.Throw(NS_ERROR_NOT_AVAILABLE);
    5309             :       }
    5310           0 :       return;
    5311             :     }
    5312             : 
    5313           0 :     imgSize = res.mSize;
    5314             : 
    5315             :     // Scale sw/sh based on aspect ratio
    5316           0 :     if (aImage.IsHTMLVideoElement()) {
    5317           0 :       HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
    5318           0 :       int32_t displayWidth = video->VideoWidth();
    5319           0 :       int32_t displayHeight = video->VideoHeight();
    5320           0 :       aSw *= (double)imgSize.width / (double)displayWidth;
    5321           0 :       aSh *= (double)imgSize.height / (double)displayHeight;
    5322             :     }
    5323             : 
    5324           0 :     if (mCanvasElement) {
    5325           0 :       CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
    5326           0 :                                             res.mPrincipal, res.mIsWriteOnly,
    5327           0 :                                             res.mCORSUsed);
    5328             :     }
    5329             : 
    5330           0 :     if (res.mSourceSurface) {
    5331           0 :       if (res.mImageRequest) {
    5332           0 :         CanvasImageCache::NotifyDrawImage(element, mCanvasElement, res.mSourceSurface, imgSize, mIsSkiaGL);
    5333             :       }
    5334           0 :       srcSurf = res.mSourceSurface;
    5335             :     } else {
    5336           0 :       drawInfo = res.mDrawInfo;
    5337             :     }
    5338             :   }
    5339             : 
    5340           0 :   if (aOptional_argc == 0) {
    5341           0 :     aSx = aSy = 0.0;
    5342           0 :     aDw = aSw = (double) imgSize.width;
    5343           0 :     aDh = aSh = (double) imgSize.height;
    5344           0 :   } else if (aOptional_argc == 2) {
    5345           0 :     aSx = aSy = 0.0;
    5346           0 :     aSw = (double) imgSize.width;
    5347           0 :     aSh = (double) imgSize.height;
    5348             :   }
    5349             : 
    5350           0 :   if (aSw == 0.0 || aSh == 0.0) {
    5351           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    5352           0 :     return;
    5353             :   }
    5354             : 
    5355           0 :   ClipImageDimension(aSx, aSw, imgSize.width, aDx, aDw);
    5356           0 :   ClipImageDimension(aSy, aSh, imgSize.height, aDy, aDh);
    5357             : 
    5358           0 :   if (aSw <= 0.0 || aSh <= 0.0 ||
    5359           0 :       aDw <= 0.0 || aDh <= 0.0) {
    5360             :     // source and/or destination are fully clipped, so nothing is painted
    5361           0 :     return;
    5362             :   }
    5363             : 
    5364             :   SamplingFilter samplingFilter;
    5365             :   AntialiasMode antialiasMode;
    5366             : 
    5367           0 :   if (CurrentState().imageSmoothingEnabled) {
    5368           0 :     samplingFilter = gfx::SamplingFilter::LINEAR;
    5369           0 :     antialiasMode = AntialiasMode::DEFAULT;
    5370             :   } else {
    5371           0 :     samplingFilter = gfx::SamplingFilter::POINT;
    5372           0 :     antialiasMode = AntialiasMode::NONE;
    5373             :   }
    5374             : 
    5375           0 :   const bool needBounds = NeedToCalculateBounds();
    5376           0 :   if (!IsTargetValid()) {
    5377           0 :     return;
    5378             :   }
    5379           0 :   gfx::Rect bounds;
    5380           0 :   if (needBounds) {
    5381           0 :     bounds = gfx::Rect(aDx, aDy, aDw, aDh);
    5382           0 :     bounds = mTarget->GetTransform().TransformBounds(bounds);
    5383             :   }
    5384             : 
    5385           0 :   if (!IsTargetValid()) {
    5386           0 :     aError.Throw(NS_ERROR_FAILURE);
    5387           0 :     return;
    5388             :   }
    5389             : 
    5390           0 :   if (srcSurf) {
    5391           0 :     gfx::Rect sourceRect(aSx, aSy, aSw, aSh);
    5392           0 :     if (element == mCanvasElement) {
    5393             :       // srcSurf is a snapshot of mTarget. If we draw to mTarget now, we'll
    5394             :       // trigger a COW copy of the whole canvas into srcSurf. That's a huge
    5395             :       // waste if sourceRect doesn't cover the whole canvas.
    5396             :       // We avoid copying the whole canvas by manually copying just the part
    5397             :       // that we need.
    5398           0 :       srcSurf = ExtractSubrect(srcSurf, &sourceRect, mTarget);
    5399             :     }
    5400             : 
    5401           0 :     AdjustedTarget tempTarget(this, bounds.IsEmpty() ? nullptr : &bounds);
    5402           0 :     if (!tempTarget) {
    5403           0 :       gfxDevCrash(LogReason::InvalidDrawTarget) << "Invalid adjusted target in Canvas2D " << gfx::hexa((DrawTarget*)mTarget) << ", " << NeedToDrawShadow() << NeedToApplyFilter();
    5404           0 :       return;
    5405             :     }
    5406             : 
    5407           0 :     auto op = UsedOperation();
    5408           0 :     if (!IsTargetValid() || !tempTarget) {
    5409           0 :       return;
    5410             :     }
    5411           0 :     tempTarget->DrawSurface(srcSurf,
    5412           0 :                   gfx::Rect(aDx, aDy, aDw, aDh),
    5413             :                   sourceRect,
    5414           0 :                   DrawSurfaceOptions(samplingFilter, SamplingBounds::UNBOUNDED),
    5415           0 :                   DrawOptions(CurrentState().globalAlpha, op, antialiasMode));
    5416             :   } else {
    5417           0 :     DrawDirectlyToCanvas(drawInfo, &bounds,
    5418             :                          gfx::Rect(aDx, aDy, aDw, aDh),
    5419             :                          gfx::Rect(aSx, aSy, aSw, aSh),
    5420           0 :                          imgSize);
    5421             :   }
    5422             : 
    5423           0 :   RedrawUser(gfxRect(aDx, aDy, aDw, aDh));
    5424             : }
    5425             : 
    5426             : void
    5427           0 : CanvasRenderingContext2D::DrawDirectlyToCanvas(
    5428             :                           const nsLayoutUtils::DirectDrawInfo& aImage,
    5429             :                           gfx::Rect* aBounds,
    5430             :                           gfx::Rect aDest,
    5431             :                           gfx::Rect aSrc,
    5432             :                           gfx::IntSize aImgSize)
    5433             : {
    5434           0 :   MOZ_ASSERT(aSrc.width > 0 && aSrc.height > 0,
    5435             :              "Need positive source width and height");
    5436             : 
    5437           0 :   AdjustedTarget tempTarget(this, aBounds->IsEmpty() ? nullptr: aBounds);
    5438           0 :   if (!tempTarget) {
    5439           0 :     return;
    5440             :   }
    5441             : 
    5442             :   // Get any existing transforms on the context, including transformations used
    5443             :   // for context shadow.
    5444           0 :   Matrix matrix = tempTarget->GetTransform();
    5445           0 :   gfxMatrix contextMatrix;
    5446           0 :   contextMatrix = gfxMatrix(matrix._11, matrix._12, matrix._21,
    5447           0 :                             matrix._22, matrix._31, matrix._32);
    5448           0 :   gfxSize contextScale(contextMatrix.ScaleFactors(true));
    5449             : 
    5450             :   // Scale the dest rect to include the context scale.
    5451           0 :   aDest.Scale(contextScale.width, contextScale.height);
    5452             : 
    5453             :   // Scale the image size to the dest rect, and adjust the source rect to match.
    5454           0 :   gfxSize scale(aDest.width / aSrc.width, aDest.height / aSrc.height);
    5455           0 :   IntSize scaledImageSize = IntSize::Ceil(aImgSize.width * scale.width,
    5456           0 :                                           aImgSize.height * scale.height);
    5457           0 :   aSrc.Scale(scale.width, scale.height);
    5458             : 
    5459             :   // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
    5460             :   // the matrix even though this is a temp gfxContext.
    5461           0 :   AutoRestoreTransform autoRestoreTransform(mTarget);
    5462             : 
    5463           0 :   RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempTarget);
    5464           0 :   if (!context) {
    5465           0 :     gfxDevCrash(LogReason::InvalidContext) << "Canvas context problem";
    5466           0 :     return;
    5467             :   }
    5468           0 :   context->SetMatrix(contextMatrix.
    5469           0 :                        PreScale(1.0 / contextScale.width,
    5470           0 :                                 1.0 / contextScale.height).
    5471           0 :                        PreTranslate(aDest.x - aSrc.x, aDest.y - aSrc.y));
    5472             : 
    5473             :   // FLAG_CLAMP is added for increased performance, since we never tile here.
    5474           0 :   uint32_t modifiedFlags = aImage.mDrawingFlags | imgIContainer::FLAG_CLAMP;
    5475             : 
    5476           0 :   CSSIntSize sz(scaledImageSize.width, scaledImageSize.height); // XXX hmm is scaledImageSize really in CSS pixels?
    5477           0 :   SVGImageContext svgContext(Some(sz));
    5478             : 
    5479           0 :   auto result = aImage.mImgContainer->
    5480           0 :     Draw(context, scaledImageSize,
    5481           0 :          ImageRegion::Create(gfxRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height)),
    5482           0 :          aImage.mWhichFrame, SamplingFilter::GOOD, Some(svgContext), modifiedFlags, CurrentState().globalAlpha);
    5483             : 
    5484           0 :   if (result != DrawResult::SUCCESS) {
    5485           0 :     NS_WARNING("imgIContainer::Draw failed");
    5486             :   }
    5487             : }
    5488             : 
    5489             : void
    5490           0 : CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& aOp,
    5491             :                                                       ErrorResult& aError)
    5492             : {
    5493             :   CompositionOp comp_op;
    5494             : 
    5495             : #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
    5496             :   if (aOp.EqualsLiteral(cvsop))   \
    5497             :     comp_op = CompositionOp::OP_##op2d;
    5498             : 
    5499           0 :   CANVAS_OP_TO_GFX_OP("copy", SOURCE)
    5500           0 :   else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
    5501           0 :   else CANVAS_OP_TO_GFX_OP("source-in", IN)
    5502           0 :   else CANVAS_OP_TO_GFX_OP("source-out", OUT)
    5503           0 :   else CANVAS_OP_TO_GFX_OP("source-over", OVER)
    5504           0 :   else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
    5505           0 :   else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
    5506           0 :   else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
    5507           0 :   else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
    5508           0 :   else CANVAS_OP_TO_GFX_OP("lighter", ADD)
    5509           0 :   else CANVAS_OP_TO_GFX_OP("xor", XOR)
    5510           0 :   else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
    5511           0 :   else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
    5512           0 :   else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
    5513           0 :   else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
    5514           0 :   else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
    5515           0 :   else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
    5516           0 :   else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
    5517           0 :   else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
    5518           0 :   else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
    5519           0 :   else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
    5520           0 :   else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
    5521           0 :   else CANVAS_OP_TO_GFX_OP("hue", HUE)
    5522           0 :   else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
    5523           0 :   else CANVAS_OP_TO_GFX_OP("color", COLOR)
    5524           0 :   else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
    5525             :   // XXX ERRMSG we need to report an error to developers here! (bug 329026)
    5526           0 :   else return;
    5527             : 
    5528             : #undef CANVAS_OP_TO_GFX_OP
    5529           0 :   CurrentState().op = comp_op;
    5530             : }
    5531             : 
    5532             : void
    5533           0 : CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& aOp,
    5534             :                                                       ErrorResult& aError)
    5535             : {
    5536           0 :   CompositionOp comp_op = CurrentState().op;
    5537             : 
    5538             : #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
    5539             :   if (comp_op == CompositionOp::OP_##op2d) \
    5540             :     aOp.AssignLiteral(cvsop);
    5541             : 
    5542           0 :   CANVAS_OP_TO_GFX_OP("copy", SOURCE)
    5543           0 :   else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
    5544           0 :   else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
    5545           0 :   else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
    5546           0 :   else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
    5547           0 :   else CANVAS_OP_TO_GFX_OP("lighter", ADD)
    5548           0 :   else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
    5549           0 :   else CANVAS_OP_TO_GFX_OP("source-in", IN)
    5550           0 :   else CANVAS_OP_TO_GFX_OP("source-out", OUT)
    5551           0 :   else CANVAS_OP_TO_GFX_OP("source-over", OVER)
    5552           0 :   else CANVAS_OP_TO_GFX_OP("xor", XOR)
    5553           0 :   else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
    5554           0 :   else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
    5555           0 :   else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
    5556           0 :   else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
    5557           0 :   else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
    5558           0 :   else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
    5559           0 :   else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
    5560           0 :   else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
    5561           0 :   else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
    5562           0 :   else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
    5563           0 :   else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
    5564           0 :   else CANVAS_OP_TO_GFX_OP("hue", HUE)
    5565           0 :   else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
    5566           0 :   else CANVAS_OP_TO_GFX_OP("color", COLOR)
    5567           0 :   else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
    5568             :   else {
    5569           0 :     aError.Throw(NS_ERROR_FAILURE);
    5570             :   }
    5571             : 
    5572             : #undef CANVAS_OP_TO_GFX_OP
    5573           0 : }
    5574             : 
    5575             : void
    5576           0 : CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX,
    5577             :                                      double aY, double aW, double aH,
    5578             :                                      const nsAString& aBgColor,
    5579             :                                      uint32_t aFlags, ErrorResult& aError)
    5580             : {
    5581           0 :   MOZ_ASSERT(aWindow.IsInnerWindow());
    5582             : 
    5583           0 :   if (int32_t(aW) == 0 || int32_t(aH) == 0) {
    5584           0 :     return;
    5585             :   }
    5586             : 
    5587             :   // protect against too-large surfaces that will cause allocation
    5588             :   // or overflow issues
    5589           0 :   if (!Factory::CheckSurfaceSize(IntSize(int32_t(aW), int32_t(aH)), 0xffff)) {
    5590           0 :     aError.Throw(NS_ERROR_FAILURE);
    5591           0 :     return;
    5592             :   }
    5593             : 
    5594             :   // Flush layout updates
    5595           0 :   if (!(aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
    5596           0 :     nsContentUtils::FlushLayoutForTree(aWindow.AsInner()->GetOuterWindow());
    5597             :   }
    5598             : 
    5599           0 :   CompositionOp op = UsedOperation();
    5600           0 :   bool discardContent = GlobalAlpha() == 1.0f
    5601           0 :     && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
    5602           0 :   const gfx::Rect drawRect(aX, aY, aW, aH);
    5603           0 :   EnsureTarget(discardContent ? &drawRect : nullptr);
    5604           0 :   if (!IsTargetValid()) {
    5605           0 :     return;
    5606             :   }
    5607             : 
    5608           0 :   RefPtr<nsPresContext> presContext;
    5609           0 :   nsIDocShell* docshell = aWindow.GetDocShell();
    5610           0 :   if (docshell) {
    5611           0 :     docshell->GetPresContext(getter_AddRefs(presContext));
    5612             :   }
    5613           0 :   if (!presContext) {
    5614           0 :     aError.Throw(NS_ERROR_FAILURE);
    5615           0 :     return;
    5616             :   }
    5617             : 
    5618             :   nscolor backgroundColor;
    5619           0 :   if (!ParseColor(aBgColor, &backgroundColor)) {
    5620           0 :     aError.Throw(NS_ERROR_FAILURE);
    5621           0 :     return;
    5622             :   }
    5623             : 
    5624             :   nsRect r(nsPresContext::CSSPixelsToAppUnits((float)aX),
    5625             :            nsPresContext::CSSPixelsToAppUnits((float)aY),
    5626             :            nsPresContext::CSSPixelsToAppUnits((float)aW),
    5627           0 :            nsPresContext::CSSPixelsToAppUnits((float)aH));
    5628             :   uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
    5629           0 :                              nsIPresShell::RENDER_DOCUMENT_RELATIVE);
    5630           0 :   if (aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
    5631           0 :     renderDocFlags |= nsIPresShell::RENDER_CARET;
    5632             :   }
    5633           0 :   if (aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
    5634           0 :     renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
    5635             :                         nsIPresShell::RENDER_DOCUMENT_RELATIVE);
    5636             :   }
    5637           0 :   if (aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
    5638           0 :     renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
    5639             :   }
    5640           0 :   if (aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
    5641           0 :     renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
    5642             :   }
    5643           0 :   if (aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) {
    5644           0 :     renderDocFlags |= nsIPresShell::RENDER_DRAWWINDOW_NOT_FLUSHING;
    5645             :   }
    5646             : 
    5647             :   // gfxContext-over-Azure may modify the DrawTarget's transform, so
    5648             :   // save and restore it
    5649           0 :   Matrix matrix = mTarget->GetTransform();
    5650           0 :   double sw = matrix._11 * aW;
    5651           0 :   double sh = matrix._22 * aH;
    5652           0 :   if (!sw || !sh) {
    5653           0 :     return;
    5654             :   }
    5655             : 
    5656           0 :   RefPtr<gfxContext> thebes;
    5657           0 :   RefPtr<DrawTarget> drawDT;
    5658             :   // Rendering directly is faster and can be done if mTarget supports Azure
    5659             :   // and does not need alpha blending.
    5660             :   // Since the pre-transaction callback calls ReturnTarget, we can't have a
    5661             :   // gfxContext wrapped around it when using a shared buffer provider because
    5662             :   // the DrawTarget's shared buffer may be unmapped in ReturnTarget.
    5663           0 :   op = CompositionOp::OP_ADD;
    5664           0 :   if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) &&
    5665           0 :       GlobalAlpha() == 1.0f) {
    5666           0 :     op = UsedOperation();
    5667           0 :     if (!IsTargetValid()) {
    5668           0 :       aError.Throw(NS_ERROR_FAILURE);
    5669           0 :       return;
    5670             :     }
    5671             :   }
    5672           0 :   if (op == CompositionOp::OP_OVER &&
    5673           0 :       (!mBufferProvider || mBufferProvider->GetType() != LayersBackend::LAYERS_CLIENT))
    5674             :   {
    5675           0 :     thebes = gfxContext::CreateOrNull(mTarget);
    5676           0 :     MOZ_ASSERT(thebes); // already checked the draw target above
    5677             :                         // (in SupportsAzureContentForDrawTarget)
    5678           0 :     thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
    5679           0 :                                 matrix._22, matrix._31, matrix._32));
    5680             :   } else {
    5681           0 :     IntSize dtSize = IntSize::Ceil(sw, sh);
    5682           0 :     if (!Factory::AllowedSurfaceSize(dtSize)) {
    5683           0 :       aError.Throw(NS_ERROR_FAILURE);
    5684           0 :       return;
    5685             :     }
    5686             :     drawDT =
    5687           0 :       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(dtSize,
    5688           0 :                                                                    SurfaceFormat::B8G8R8A8);
    5689           0 :     if (!drawDT || !drawDT->IsValid()) {
    5690           0 :       aError.Throw(NS_ERROR_FAILURE);
    5691           0 :       return;
    5692             :     }
    5693             : 
    5694           0 :     thebes = gfxContext::CreateOrNull(drawDT);
    5695           0 :     MOZ_ASSERT(thebes); // alrady checked the draw target above
    5696           0 :     thebes->SetMatrix(gfxMatrix::Scaling(matrix._11, matrix._22));
    5697             :   }
    5698             : 
    5699           0 :   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
    5700             : 
    5701           0 :   Unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
    5702             :   // If this canvas was contained in the drawn window, the pre-transaction callback
    5703             :   // may have returned its DT. If so, we must reacquire it here.
    5704           0 :   EnsureTarget(discardContent ? &drawRect : nullptr);
    5705             : 
    5706           0 :   if (drawDT) {
    5707           0 :     RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
    5708           0 :     if (NS_WARN_IF(!snapshot)) {
    5709           0 :       aError.Throw(NS_ERROR_FAILURE);
    5710           0 :       return;
    5711             :     }
    5712           0 :     RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
    5713           0 :     if (!data || !Factory::AllowedSurfaceSize(data->GetSize())) {
    5714           0 :       gfxCriticalError() << "Unexpected invalid data source surface " <<
    5715           0 :         (data ? data->GetSize() : IntSize(0,0));
    5716           0 :       aError.Throw(NS_ERROR_FAILURE);
    5717           0 :       return;
    5718             :     }
    5719             : 
    5720             :     DataSourceSurface::MappedSurface rawData;
    5721           0 :     if (NS_WARN_IF(!data->Map(DataSourceSurface::READ, &rawData))) {
    5722           0 :         aError.Throw(NS_ERROR_FAILURE);
    5723           0 :         return;
    5724             :     }
    5725             :     RefPtr<SourceSurface> source =
    5726           0 :       mTarget->CreateSourceSurfaceFromData(rawData.mData,
    5727           0 :                                            data->GetSize(),
    5728             :                                            rawData.mStride,
    5729           0 :                                            data->GetFormat());
    5730           0 :     data->Unmap();
    5731             : 
    5732           0 :     if (!source) {
    5733           0 :       aError.Throw(NS_ERROR_FAILURE);
    5734           0 :       return;
    5735             :     }
    5736             : 
    5737           0 :     op = UsedOperation();
    5738           0 :     if (!IsTargetValid()) {
    5739           0 :       aError.Throw(NS_ERROR_FAILURE);
    5740           0 :       return;
    5741             :     }
    5742           0 :     gfx::Rect destRect(0, 0, aW, aH);
    5743           0 :     gfx::Rect sourceRect(0, 0, sw, sh);
    5744           0 :     mTarget->DrawSurface(source, destRect, sourceRect,
    5745           0 :                          DrawSurfaceOptions(gfx::SamplingFilter::POINT),
    5746           0 :                          DrawOptions(GlobalAlpha(), op,
    5747           0 :                                      AntialiasMode::NONE));
    5748             :   } else {
    5749           0 :     mTarget->SetTransform(matrix);
    5750             :   }
    5751             : 
    5752             :   // note that x and y are coordinates in the document that
    5753             :   // we're drawing; x and y are drawn to 0,0 in current user
    5754             :   // space.
    5755           0 :   RedrawUser(gfxRect(0, 0, aW, aH));
    5756             : }
    5757             : 
    5758             : //
    5759             : // device pixel getting/setting
    5760             : //
    5761             : 
    5762             : already_AddRefed<ImageData>
    5763           0 : CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
    5764             :                                        double aSy, double aSw,
    5765             :                                        double aSh, ErrorResult& aError)
    5766             : {
    5767           0 :   if (mDrawObserver) {
    5768           0 :     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
    5769             :   }
    5770             : 
    5771           0 :   if (!mCanvasElement && !mDocShell) {
    5772           0 :     NS_ERROR("No canvas element and no docshell in GetImageData!!!");
    5773           0 :     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    5774           0 :     return nullptr;
    5775             :   }
    5776             : 
    5777             :   // Check only if we have a canvas element; if we were created with a docshell,
    5778             :   // then it's special internal use.
    5779           0 :   if (mCanvasElement && mCanvasElement->IsWriteOnly() &&
    5780             :       // We could ask bindings for the caller type, but they already hand us a
    5781             :       // JSContext, and we're at least _somewhat_ perf-sensitive (so may not
    5782             :       // want to compute the caller type in the common non-write-only case), so
    5783             :       // let's just use what we have.
    5784           0 :       !nsContentUtils::CallerHasPermission(aCx, NS_LITERAL_STRING("<all_urls>")))
    5785             :   {
    5786             :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
    5787           0 :     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    5788           0 :     return nullptr;
    5789             :   }
    5790             : 
    5791           0 :   if (!IsFinite(aSx) || !IsFinite(aSy) ||
    5792           0 :       !IsFinite(aSw) || !IsFinite(aSh)) {
    5793           0 :     aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    5794           0 :     return nullptr;
    5795             :   }
    5796             : 
    5797           0 :   if (!aSw || !aSh) {
    5798           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    5799           0 :     return nullptr;
    5800             :   }
    5801             : 
    5802           0 :   int32_t x = JS::ToInt32(aSx);
    5803           0 :   int32_t y = JS::ToInt32(aSy);
    5804           0 :   int32_t wi = JS::ToInt32(aSw);
    5805           0 :   int32_t hi = JS::ToInt32(aSh);
    5806             : 
    5807             :   // Handle negative width and height by flipping the rectangle over in the
    5808             :   // relevant direction.
    5809             :   uint32_t w, h;
    5810           0 :   if (aSw < 0) {
    5811           0 :     w = -wi;
    5812           0 :     x -= w;
    5813             :   } else {
    5814           0 :     w = wi;
    5815             :   }
    5816           0 :   if (aSh < 0) {
    5817           0 :     h = -hi;
    5818           0 :     y -= h;
    5819             :   } else {
    5820           0 :     h = hi;
    5821             :   }
    5822             : 
    5823           0 :   if (w == 0) {
    5824           0 :     w = 1;
    5825             :   }
    5826           0 :   if (h == 0) {
    5827           0 :     h = 1;
    5828             :   }
    5829             : 
    5830           0 :   JS::Rooted<JSObject*> array(aCx);
    5831           0 :   aError = GetImageDataArray(aCx, x, y, w, h, array.address());
    5832           0 :   if (aError.Failed()) {
    5833           0 :     return nullptr;
    5834             :   }
    5835           0 :   MOZ_ASSERT(array);
    5836             : 
    5837           0 :   RefPtr<ImageData> imageData = new ImageData(w, h, *array);
    5838           0 :   return imageData.forget();
    5839             : }
    5840             : 
    5841             : nsresult
    5842           0 : CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
    5843             :                                             int32_t aX,
    5844             :                                             int32_t aY,
    5845             :                                             uint32_t aWidth,
    5846             :                                             uint32_t aHeight,
    5847             :                                             JSObject** aRetval)
    5848             : {
    5849           0 :   if (mDrawObserver) {
    5850           0 :     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
    5851             :   }
    5852             : 
    5853           0 :   MOZ_ASSERT(aWidth && aHeight);
    5854             : 
    5855           0 :   CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
    5856           0 :   if (!len.isValid()) {
    5857           0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    5858             :   }
    5859             : 
    5860           0 :   CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
    5861           0 :   CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
    5862             : 
    5863           0 :   if (!rightMost.isValid() || !bottomMost.isValid()) {
    5864           0 :     return NS_ERROR_DOM_SYNTAX_ERR;
    5865             :   }
    5866             : 
    5867           0 :   JS::Rooted<JSObject*> darray(aCx, JS_NewUint8ClampedArray(aCx, len.value()));
    5868           0 :   if (!darray) {
    5869           0 :     return NS_ERROR_OUT_OF_MEMORY;
    5870             :   }
    5871             : 
    5872           0 :   if (mZero) {
    5873           0 :     *aRetval = darray;
    5874           0 :     return NS_OK;
    5875             :   }
    5876             : 
    5877           0 :   IntRect srcRect(0, 0, mWidth, mHeight);
    5878           0 :   IntRect destRect(aX, aY, aWidth, aHeight);
    5879           0 :   IntRect srcReadRect = srcRect.Intersect(destRect);
    5880           0 :   if (srcReadRect.IsEmpty()) {
    5881           0 :     *aRetval = darray;
    5882           0 :     return NS_OK;
    5883             :   }
    5884             : 
    5885           0 :   RefPtr<DataSourceSurface> readback;
    5886             :   DataSourceSurface::MappedSurface rawData;
    5887           0 :   RefPtr<SourceSurface> snapshot;
    5888           0 :   if (!mTarget && mBufferProvider) {
    5889           0 :     snapshot = mBufferProvider->BorrowSnapshot();
    5890             :   } else {
    5891           0 :     EnsureTarget();
    5892           0 :     if (!IsTargetValid()) {
    5893           0 :       return NS_ERROR_FAILURE;
    5894             :     }
    5895           0 :     snapshot = mTarget->Snapshot();
    5896             :   }
    5897             : 
    5898           0 :   if (snapshot) {
    5899           0 :     readback = snapshot->GetDataSurface();
    5900             :   }
    5901             : 
    5902           0 :   if (!mTarget && mBufferProvider) {
    5903           0 :     mBufferProvider->ReturnSnapshot(snapshot.forget());
    5904             :   }
    5905             : 
    5906           0 :   if (!readback || !readback->Map(DataSourceSurface::READ, &rawData)) {
    5907           0 :     return NS_ERROR_OUT_OF_MEMORY;
    5908             :   }
    5909             : 
    5910           0 :   IntRect dstWriteRect = srcReadRect;
    5911           0 :   dstWriteRect.MoveBy(-aX, -aY);
    5912             : 
    5913             :   {
    5914           0 :     JS::AutoCheckCannotGC nogc;
    5915             :     bool isShared;
    5916           0 :     uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
    5917           0 :     MOZ_ASSERT(!isShared);        // Should not happen, data was created above
    5918             : 
    5919           0 :     uint32_t srcStride = rawData.mStride;
    5920           0 :     uint8_t* src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
    5921           0 :     uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
    5922             : 
    5923           0 :     if (mOpaque) {
    5924           0 :       SwizzleData(src, srcStride, SurfaceFormat::X8R8G8B8_UINT32,
    5925           0 :                   dst, aWidth * 4, SurfaceFormat::R8G8B8A8,
    5926           0 :                   dstWriteRect.Size());
    5927             :     } else {
    5928           0 :       UnpremultiplyData(src, srcStride, SurfaceFormat::A8R8G8B8_UINT32,
    5929           0 :                         dst, aWidth * 4, SurfaceFormat::R8G8B8A8,
    5930           0 :                         dstWriteRect.Size());
    5931             :     }
    5932             :   }
    5933             : 
    5934           0 :   readback->Unmap();
    5935           0 :   *aRetval = darray;
    5936           0 :   return NS_OK;
    5937             : }
    5938             : 
    5939             : void
    5940           0 : CanvasRenderingContext2D::EnsureErrorTarget()
    5941             : {
    5942           0 :   if (sErrorTarget) {
    5943           0 :     return;
    5944             :   }
    5945             : 
    5946           0 :   RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
    5947           0 :   MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
    5948             : 
    5949           0 :   sErrorTarget = errorTarget;
    5950           0 :   NS_ADDREF(sErrorTarget);
    5951             : }
    5952             : 
    5953             : void
    5954           0 : CanvasRenderingContext2D::FillRuleChanged()
    5955             : {
    5956           0 :   if (mPath) {
    5957           0 :     mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
    5958           0 :     mPath = nullptr;
    5959             :   }
    5960           0 : }
    5961             : 
    5962             : void
    5963           0 : CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
    5964             :                                        double aDy, ErrorResult& aError)
    5965             : {
    5966           0 :   RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
    5967           0 :   DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
    5968           0 :   MOZ_ASSERT(inited);
    5969             : 
    5970           0 :   aError = PutImageData_explicit(JS::ToInt32(aDx), JS::ToInt32(aDy),
    5971             :                                 aImageData.Width(), aImageData.Height(),
    5972           0 :                                 &arr, false, 0, 0, 0, 0);
    5973           0 : }
    5974             : 
    5975             : void
    5976           0 : CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
    5977             :                                        double aDy, double aDirtyX,
    5978             :                                        double aDirtyY, double aDirtyWidth,
    5979             :                                        double aDirtyHeight,
    5980             :                                        ErrorResult& aError)
    5981             : {
    5982           0 :   RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
    5983           0 :   DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
    5984           0 :   MOZ_ASSERT(inited);
    5985             : 
    5986           0 :   aError = PutImageData_explicit(JS::ToInt32(aDx), JS::ToInt32(aDy),
    5987             :                                 aImageData.Width(), aImageData.Height(),
    5988             :                                 &arr, true,
    5989             :                                 JS::ToInt32(aDirtyX),
    5990             :                                 JS::ToInt32(aDirtyY),
    5991             :                                 JS::ToInt32(aDirtyWidth),
    5992           0 :                                 JS::ToInt32(aDirtyHeight));
    5993           0 : }
    5994             : 
    5995             : nsresult
    5996           0 : CanvasRenderingContext2D::PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
    5997             :                                                 dom::Uint8ClampedArray* aArray,
    5998             :                                                 bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
    5999             :                                                 int32_t aDirtyWidth, int32_t aDirtyHeight)
    6000             : {
    6001           0 :   if (mDrawObserver) {
    6002           0 :     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::PutImageData);
    6003             :   }
    6004             : 
    6005           0 :   if (aW == 0 || aH == 0) {
    6006           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    6007             :   }
    6008             : 
    6009           0 :   IntRect dirtyRect;
    6010           0 :   IntRect imageDataRect(0, 0, aW, aH);
    6011             : 
    6012           0 :   if (aHasDirtyRect) {
    6013             :     // fix up negative dimensions
    6014           0 :     if (aDirtyWidth < 0) {
    6015           0 :       NS_ENSURE_TRUE(aDirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
    6016             : 
    6017           0 :       CheckedInt32 checkedDirtyX = CheckedInt32(aDirtyX) + aDirtyWidth;
    6018             : 
    6019           0 :       if (!checkedDirtyX.isValid())
    6020           0 :         return NS_ERROR_DOM_INDEX_SIZE_ERR;
    6021             : 
    6022           0 :       aDirtyX = checkedDirtyX.value();
    6023           0 :       aDirtyWidth = -aDirtyWidth;
    6024             :     }
    6025             : 
    6026           0 :     if (aDirtyHeight < 0) {
    6027           0 :       NS_ENSURE_TRUE(aDirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
    6028             : 
    6029           0 :       CheckedInt32 checkedDirtyY = CheckedInt32(aDirtyY) + aDirtyHeight;
    6030             : 
    6031           0 :       if (!checkedDirtyY.isValid())
    6032           0 :         return NS_ERROR_DOM_INDEX_SIZE_ERR;
    6033             : 
    6034           0 :       aDirtyY = checkedDirtyY.value();
    6035           0 :       aDirtyHeight = -aDirtyHeight;
    6036             :     }
    6037             : 
    6038             :     // bound the dirty rect within the imageData rectangle
    6039           0 :     dirtyRect = imageDataRect.Intersect(IntRect(aDirtyX, aDirtyY, aDirtyWidth, aDirtyHeight));
    6040             : 
    6041           0 :     if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
    6042           0 :       return NS_OK;
    6043             :   } else {
    6044           0 :     dirtyRect = imageDataRect;
    6045             :   }
    6046             : 
    6047           0 :   dirtyRect.MoveBy(IntPoint(aX, aY));
    6048           0 :   dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
    6049             : 
    6050           0 :   if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
    6051           0 :     return NS_OK;
    6052             :   }
    6053             : 
    6054           0 :   aArray->ComputeLengthAndData();
    6055             : 
    6056           0 :   uint32_t dataLen = aArray->Length();
    6057             : 
    6058           0 :   uint32_t len = aW * aH * 4;
    6059           0 :   if (dataLen != len) {
    6060           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    6061             :   }
    6062             : 
    6063             :   // The canvas spec says that the current path, transformation matrix, shadow attributes,
    6064             :   // global alpha, the clipping region, and global composition operator must not affect the
    6065             :   // getImageData() and putImageData() methods.
    6066           0 :   const gfx::Rect putRect(dirtyRect);
    6067           0 :   EnsureTarget(&putRect);
    6068             : 
    6069           0 :   if (!IsTargetValid()) {
    6070           0 :     return NS_ERROR_FAILURE;
    6071             :   }
    6072             : 
    6073           0 :   RefPtr<DataSourceSurface> sourceSurface;
    6074           0 :   uint8_t* lockedBits = nullptr;
    6075             :   uint8_t* dstData;
    6076           0 :   IntSize dstSize;
    6077             :   int32_t dstStride;
    6078             :   SurfaceFormat dstFormat;
    6079           0 :   if (mTarget->LockBits(&lockedBits, &dstSize, &dstStride, &dstFormat)) {
    6080           0 :     dstData = lockedBits + dirtyRect.y * dstStride + dirtyRect.x * 4;
    6081             :   } else {
    6082             :     sourceSurface =
    6083           0 :       Factory::CreateDataSourceSurface(dirtyRect.Size(),
    6084             :                                        SurfaceFormat::B8G8R8A8,
    6085           0 :                                        false);
    6086             : 
    6087             :     // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
    6088             :     // covers the details of how to run into it, but the full detailed
    6089             :     // investigation hasn't been done to determine the underlying cause.  We
    6090             :     // will just handle the failure to allocate the surface to avoid a crash.
    6091           0 :     if (!sourceSurface) {
    6092           0 :       return NS_ERROR_FAILURE;
    6093             :     }
    6094           0 :     dstData = sourceSurface->GetData();
    6095           0 :     if (!dstData) {
    6096           0 :       return NS_ERROR_OUT_OF_MEMORY;
    6097             :     }
    6098           0 :     dstStride = sourceSurface->Stride();
    6099           0 :     dstFormat = sourceSurface->GetFormat();
    6100             :   }
    6101             : 
    6102           0 :   IntRect srcRect = dirtyRect - IntPoint(aX, aY);
    6103           0 :   uint8_t* srcData = aArray->Data() + srcRect.y * (aW * 4) + srcRect.x * 4;
    6104             : 
    6105           0 :   PremultiplyData(srcData, aW * 4, SurfaceFormat::R8G8B8A8,
    6106             :                   dstData, dstStride,
    6107           0 :                   mOpaque ? SurfaceFormat::X8R8G8B8_UINT32 : SurfaceFormat::A8R8G8B8_UINT32,
    6108           0 :                   dirtyRect.Size());
    6109             : 
    6110           0 :   if (lockedBits) {
    6111           0 :     mTarget->ReleaseBits(lockedBits);
    6112           0 :   } else if (sourceSurface) {
    6113           0 :     mTarget->CopySurface(sourceSurface, dirtyRect - dirtyRect.TopLeft(), dirtyRect.TopLeft());
    6114             :   }
    6115             : 
    6116           0 :   Redraw(gfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
    6117             : 
    6118           0 :   return NS_OK;
    6119             : }
    6120             : 
    6121             : static already_AddRefed<ImageData>
    6122           0 : CreateImageData(JSContext* aCx, CanvasRenderingContext2D* aContext,
    6123             :                 uint32_t aW, uint32_t aH, ErrorResult& aError)
    6124             : {
    6125           0 :   if (aW == 0)
    6126           0 :       aW = 1;
    6127           0 :   if (aH == 0)
    6128           0 :       aH = 1;
    6129             : 
    6130           0 :   CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aW) * aH * 4;
    6131           0 :   if (!len.isValid()) {
    6132           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    6133           0 :     return nullptr;
    6134             :   }
    6135             : 
    6136             :   // Create the fast typed array; it's initialized to 0 by default.
    6137           0 :   JSObject* darray = Uint8ClampedArray::Create(aCx, aContext, len.value());
    6138           0 :   if (!darray) {
    6139           0 :     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
    6140           0 :     return nullptr;
    6141             :   }
    6142             : 
    6143             :   RefPtr<mozilla::dom::ImageData> imageData =
    6144           0 :     new mozilla::dom::ImageData(aW, aH, *darray);
    6145           0 :   return imageData.forget();
    6146             : }
    6147             : 
    6148             : already_AddRefed<ImageData>
    6149           0 : CanvasRenderingContext2D::CreateImageData(JSContext* aCx, double aSw,
    6150             :                                           double aSh, ErrorResult& aError)
    6151             : {
    6152           0 :   if (!aSw || !aSh) {
    6153           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    6154           0 :     return nullptr;
    6155             :   }
    6156             : 
    6157           0 :   int32_t wi = JS::ToInt32(aSw);
    6158           0 :   int32_t hi = JS::ToInt32(aSh);
    6159             : 
    6160           0 :   uint32_t w = Abs(wi);
    6161           0 :   uint32_t h = Abs(hi);
    6162           0 :   return mozilla::dom::CreateImageData(aCx, this, w, h, aError);
    6163             : }
    6164             : 
    6165             : already_AddRefed<ImageData>
    6166           0 : CanvasRenderingContext2D::CreateImageData(JSContext* aCx,
    6167             :                                           ImageData& aImagedata,
    6168             :                                           ErrorResult& aError)
    6169             : {
    6170             :   return mozilla::dom::CreateImageData(aCx, this, aImagedata.Width(),
    6171           0 :                                        aImagedata.Height(), aError);
    6172             : }
    6173             : 
    6174             : static uint8_t g2DContextLayerUserData;
    6175             : 
    6176             : 
    6177             : uint32_t
    6178           0 : CanvasRenderingContext2D::SkiaGLTex() const
    6179             : {
    6180           0 :   if (!mTarget) {
    6181           0 :     return 0;
    6182             :   }
    6183           0 :   MOZ_ASSERT(IsTargetValid());
    6184           0 :   return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
    6185             : }
    6186             : 
    6187           0 : void CanvasRenderingContext2D::RemoveDrawObserver()
    6188             : {
    6189           0 :   if (mDrawObserver) {
    6190           0 :     delete mDrawObserver;
    6191           0 :     mDrawObserver = nullptr;
    6192             :   }
    6193           0 : }
    6194             : 
    6195             : already_AddRefed<Layer>
    6196           0 : CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
    6197             :                                          Layer* aOldLayer,
    6198             :                                          LayerManager* aManager,
    6199             :                                          bool aMirror /* = false */)
    6200             : {
    6201           0 :   if (aMirror) {
    6202             :     // Not supported for CanvasRenderingContext2D
    6203           0 :     return nullptr;
    6204             :   }
    6205             : 
    6206           0 :   if (mOpaque || mIsSkiaGL) {
    6207             :     // If we're opaque then make sure we have a surface so we paint black
    6208             :     // instead of transparent.
    6209             :     // If we're using SkiaGL, then SkiaGLTex() below needs the target to
    6210             :     // be accessible.
    6211           0 :     EnsureTarget();
    6212             :   }
    6213             : 
    6214             :   // Don't call EnsureTarget() ... if there isn't already a surface, then
    6215             :   // we have nothing to paint and there is no need to create a surface just
    6216             :   // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
    6217             :   // layer manager which must NOT happen during a paint.
    6218           0 :   if (!mBufferProvider && !IsTargetValid()) {
    6219             :     // No DidTransactionCallback will be received, so mark the context clean
    6220             :     // now so future invalidations will be dispatched.
    6221           0 :     MarkContextClean();
    6222           0 :     return nullptr;
    6223             :   }
    6224             : 
    6225           0 :   if (!mResetLayer && aOldLayer) {
    6226             :     auto userData =
    6227             :       static_cast<CanvasRenderingContext2DUserData*>(
    6228           0 :         aOldLayer->GetUserData(&g2DContextLayerUserData));
    6229             : 
    6230           0 :     CanvasLayer::Data data;
    6231             : 
    6232           0 :     if (mIsSkiaGL) {
    6233           0 :       GLuint skiaGLTex = SkiaGLTex();
    6234           0 :       if (skiaGLTex) {
    6235           0 :         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
    6236           0 :         MOZ_ASSERT(glue);
    6237           0 :         data.mGLContext = glue->GetGLContext();
    6238           0 :         data.mFrontbufferGLTex = skiaGLTex;
    6239             :       }
    6240             :     }
    6241             : 
    6242           0 :     data.mBufferProvider = mBufferProvider;
    6243             : 
    6244           0 :     if (userData &&
    6245           0 :         userData->IsForContext(this) &&
    6246           0 :         static_cast<CanvasLayer*>(aOldLayer)->IsDataValid(data)) {
    6247           0 :       RefPtr<Layer> ret = aOldLayer;
    6248           0 :       return ret.forget();
    6249             :     }
    6250             :   }
    6251             : 
    6252           0 :   RefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
    6253           0 :   if (!canvasLayer) {
    6254           0 :     NS_WARNING("CreateCanvasLayer returned null!");
    6255             :     // No DidTransactionCallback will be received, so mark the context clean
    6256             :     // now so future invalidations will be dispatched.
    6257           0 :     MarkContextClean();
    6258           0 :     return nullptr;
    6259             :   }
    6260           0 :   CanvasRenderingContext2DUserData* userData = nullptr;
    6261             :   // Make the layer tell us whenever a transaction finishes (including
    6262             :   // the current transaction), so we can clear our invalidation state and
    6263             :   // start invalidating again. We need to do this for all layers since
    6264             :   // callers of DrawWindow may be expecting to receive normal invalidation
    6265             :   // notifications after this paint.
    6266             : 
    6267             :   // The layer will be destroyed when we tear down the presentation
    6268             :   // (at the latest), at which time this userData will be destroyed,
    6269             :   // releasing the reference to the element.
    6270             :   // The userData will receive DidTransactionCallbacks, which flush the
    6271             :   // the invalidation state to indicate that the canvas is up to date.
    6272           0 :   userData = new CanvasRenderingContext2DUserData(this);
    6273           0 :   canvasLayer->SetDidTransactionCallback(
    6274           0 :           CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
    6275           0 :   canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
    6276             : 
    6277           0 :   CanvasLayer::Data data;
    6278           0 :   data.mSize = GetSize();
    6279           0 :   data.mHasAlpha = !mOpaque;
    6280             : 
    6281           0 :   canvasLayer->SetPreTransactionCallback(
    6282           0 :           CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
    6283             : 
    6284             : 
    6285           0 :   if (mIsSkiaGL) {
    6286           0 :       GLuint skiaGLTex = SkiaGLTex();
    6287           0 :       if (skiaGLTex) {
    6288           0 :         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
    6289           0 :         MOZ_ASSERT(glue);
    6290           0 :         data.mGLContext = glue->GetGLContext();
    6291           0 :         data.mFrontbufferGLTex = skiaGLTex;
    6292             :       }
    6293             :   }
    6294             : 
    6295           0 :   data.mBufferProvider = mBufferProvider;
    6296             : 
    6297           0 :   canvasLayer->Initialize(data);
    6298           0 :   uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
    6299           0 :   canvasLayer->SetContentFlags(flags);
    6300           0 :   canvasLayer->Updated();
    6301             : 
    6302           0 :   mResetLayer = false;
    6303             : 
    6304           0 :   return canvasLayer.forget();
    6305             : }
    6306             : 
    6307             : void
    6308           0 : CanvasRenderingContext2D::MarkContextClean()
    6309             : {
    6310           0 :   if (mInvalidateCount > 0) {
    6311           0 :     mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
    6312             :   }
    6313           0 :   mIsEntireFrameInvalid = false;
    6314           0 :   mInvalidateCount = 0;
    6315           0 : }
    6316             : 
    6317             : void
    6318           0 : CanvasRenderingContext2D::MarkContextCleanForFrameCapture()
    6319             : {
    6320           0 :   mIsCapturedFrameInvalid = false;
    6321           0 : }
    6322             : 
    6323             : bool
    6324           0 : CanvasRenderingContext2D::IsContextCleanForFrameCapture()
    6325             : {
    6326           0 :   return !mIsCapturedFrameInvalid;
    6327             : }
    6328             : 
    6329             : bool
    6330           0 : CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager* aManager)
    6331             : {
    6332           0 :   return !aManager->CanUseCanvasLayerForSize(GetSize());
    6333             : }
    6334             : 
    6335           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPath, AddRef)
    6336           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath, Release)
    6337             : 
    6338           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPath, mParent)
    6339             : 
    6340           0 : CanvasPath::CanvasPath(nsISupports* aParent)
    6341           0 :   : mParent(aParent)
    6342             : {
    6343           0 :   mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
    6344           0 : }
    6345             : 
    6346           0 : CanvasPath::CanvasPath(nsISupports* aParent, already_AddRefed<PathBuilder> aPathBuilder)
    6347           0 :   : mParent(aParent), mPathBuilder(aPathBuilder)
    6348             : {
    6349           0 :   if (!mPathBuilder) {
    6350           0 :     mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
    6351             :   }
    6352           0 : }
    6353             : 
    6354             : JSObject*
    6355           0 : CanvasPath::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
    6356             : {
    6357           0 :   return Path2DBinding::Wrap(aCx, this, aGivenProto);
    6358             : }
    6359             : 
    6360             : already_AddRefed<CanvasPath>
    6361           0 : CanvasPath::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
    6362             : {
    6363           0 :   RefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports());
    6364           0 :   return path.forget();
    6365             : }
    6366             : 
    6367             : already_AddRefed<CanvasPath>
    6368           0 : CanvasPath::Constructor(const GlobalObject& aGlobal, CanvasPath& aCanvasPath, ErrorResult& aRv)
    6369             : {
    6370           0 :   RefPtr<gfx::Path> tempPath = aCanvasPath.GetPath(CanvasWindingRule::Nonzero,
    6371           0 :                                                    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
    6372             : 
    6373           0 :   RefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
    6374           0 :   return path.forget();
    6375             : }
    6376             : 
    6377             : already_AddRefed<CanvasPath>
    6378           0 : CanvasPath::Constructor(const GlobalObject& aGlobal, const nsAString& aPathString, ErrorResult& aRv)
    6379             : {
    6380           0 :   RefPtr<gfx::Path> tempPath = SVGContentUtils::GetPath(aPathString);
    6381           0 :   if (!tempPath) {
    6382           0 :     return Constructor(aGlobal, aRv);
    6383             :   }
    6384             : 
    6385           0 :   RefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
    6386           0 :   return path.forget();
    6387             : }
    6388             : 
    6389             : void
    6390           0 : CanvasPath::ClosePath()
    6391             : {
    6392           0 :   EnsurePathBuilder();
    6393             : 
    6394           0 :   mPathBuilder->Close();
    6395           0 : }
    6396             : 
    6397             : void
    6398           0 : CanvasPath::MoveTo(double aX, double aY)
    6399             : {
    6400           0 :   EnsurePathBuilder();
    6401             : 
    6402           0 :   mPathBuilder->MoveTo(Point(ToFloat(aX), ToFloat(aY)));
    6403           0 : }
    6404             : 
    6405             : void
    6406           0 : CanvasPath::LineTo(double aX, double aY)
    6407             : {
    6408           0 :   EnsurePathBuilder();
    6409             : 
    6410           0 :   mPathBuilder->LineTo(Point(ToFloat(aX), ToFloat(aY)));
    6411           0 : }
    6412             : 
    6413             : void
    6414           0 : CanvasPath::QuadraticCurveTo(double aCpx, double aCpy, double aX, double aY)
    6415             : {
    6416           0 :   EnsurePathBuilder();
    6417             : 
    6418           0 :   mPathBuilder->QuadraticBezierTo(gfx::Point(ToFloat(aCpx), ToFloat(aCpy)),
    6419           0 :                                   gfx::Point(ToFloat(aX), ToFloat(aY)));
    6420           0 : }
    6421             : 
    6422             : void
    6423           0 : CanvasPath::BezierCurveTo(double aCp1x, double aCp1y,
    6424             :                           double aCp2x, double aCp2y,
    6425             :                           double aX, double aY)
    6426             : {
    6427           0 :   BezierTo(gfx::Point(ToFloat(aCp1x), ToFloat(aCp1y)),
    6428           0 :              gfx::Point(ToFloat(aCp2x), ToFloat(aCp2y)),
    6429           0 :              gfx::Point(ToFloat(aX), ToFloat(aY)));
    6430           0 : }
    6431             : 
    6432             : void
    6433           0 : CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2, double aRadius,
    6434             :                   ErrorResult& aError)
    6435             : {
    6436           0 :   if (aRadius < 0) {
    6437           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    6438           0 :     return;
    6439             :   }
    6440             : 
    6441           0 :   EnsurePathBuilder();
    6442             : 
    6443             :   // Current point in user space!
    6444           0 :   Point p0 = mPathBuilder->CurrentPoint();
    6445           0 :   Point p1(aX1, aY1);
    6446           0 :   Point p2(aX2, aY2);
    6447             : 
    6448             :   // Execute these calculations in double precision to avoid cumulative
    6449             :   // rounding errors.
    6450             :   double dir, a2, b2, c2, cosx, sinx, d, anx, any,
    6451             :          bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
    6452             :   bool anticlockwise;
    6453             : 
    6454           0 :   if (p0 == p1 || p1 == p2 || aRadius == 0) {
    6455           0 :     LineTo(p1.x, p1.y);
    6456           0 :     return;
    6457             :   }
    6458             : 
    6459             :   // Check for colinearity
    6460           0 :   dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
    6461           0 :   if (dir == 0) {
    6462           0 :     LineTo(p1.x, p1.y);
    6463           0 :     return;
    6464             :   }
    6465             : 
    6466             : 
    6467             :   // XXX - Math for this code was already available from the non-azure code
    6468             :   // and would be well tested. Perhaps converting to bezier directly might
    6469             :   // be more efficient longer run.
    6470           0 :   a2 = (p0.x-aX1)*(p0.x-aX1) + (p0.y-aY1)*(p0.y-aY1);
    6471           0 :   b2 = (aX1-aX2)*(aX1-aX2) + (aY1-aY2)*(aY1-aY2);
    6472           0 :   c2 = (p0.x-aX2)*(p0.x-aX2) + (p0.y-aY2)*(p0.y-aY2);
    6473           0 :   cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
    6474             : 
    6475           0 :   sinx = sqrt(1 - cosx*cosx);
    6476           0 :   d = aRadius / ((1 - cosx) / sinx);
    6477             : 
    6478           0 :   anx = (aX1-p0.x) / sqrt(a2);
    6479           0 :   any = (aY1-p0.y) / sqrt(a2);
    6480           0 :   bnx = (aX1-aX2) / sqrt(b2);
    6481           0 :   bny = (aY1-aY2) / sqrt(b2);
    6482           0 :   x3 = aX1 - anx*d;
    6483           0 :   y3 = aY1 - any*d;
    6484           0 :   x4 = aX1 - bnx*d;
    6485           0 :   y4 = aY1 - bny*d;
    6486           0 :   anticlockwise = (dir < 0);
    6487           0 :   cx = x3 + any*aRadius*(anticlockwise ? 1 : -1);
    6488           0 :   cy = y3 - anx*aRadius*(anticlockwise ? 1 : -1);
    6489           0 :   angle0 = atan2((y3-cy), (x3-cx));
    6490           0 :   angle1 = atan2((y4-cy), (x4-cx));
    6491             : 
    6492             : 
    6493           0 :   LineTo(x3, y3);
    6494             : 
    6495           0 :   Arc(cx, cy, aRadius, angle0, angle1, anticlockwise, aError);
    6496             : }
    6497             : 
    6498             : void
    6499           0 : CanvasPath::Rect(double aX, double aY, double aW, double aH)
    6500             : {
    6501           0 :   MoveTo(aX, aY);
    6502           0 :   LineTo(aX + aW, aY);
    6503           0 :   LineTo(aX + aW, aY + aH);
    6504           0 :   LineTo(aX, aY + aH);
    6505           0 :   ClosePath();
    6506           0 : }
    6507             : 
    6508             : void
    6509           0 : CanvasPath::Arc(double aX, double aY, double aRadius,
    6510             :                 double aStartAngle, double aEndAngle, bool aAnticlockwise,
    6511             :                 ErrorResult& aError)
    6512             : {
    6513           0 :   if (aRadius < 0.0) {
    6514           0 :     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    6515           0 :     return;
    6516             :   }
    6517             : 
    6518           0 :   EnsurePathBuilder();
    6519             : 
    6520           0 :   ArcToBezier(this, Point(aX, aY), Size(aRadius, aRadius), aStartAngle, aEndAngle, aAnticlockwise);
    6521             : }
    6522             : 
    6523             : void
    6524           0 : CanvasPath::Ellipse(double x, double y, double radiusX, double radiusY,
    6525             :                     double rotation, double startAngle, double endAngle,
    6526             :                     bool anticlockwise, ErrorResult& error)
    6527             : {
    6528           0 :   if (radiusX < 0.0 || radiusY < 0.0) {
    6529           0 :     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    6530           0 :     return;
    6531             :   }
    6532             : 
    6533           0 :   EnsurePathBuilder();
    6534             : 
    6535           0 :   ArcToBezier(this, Point(x, y), Size(radiusX, radiusY), startAngle, endAngle,
    6536           0 :               anticlockwise, rotation);
    6537             : }
    6538             : 
    6539             : void
    6540           0 : CanvasPath::LineTo(const gfx::Point& aPoint)
    6541             : {
    6542           0 :   EnsurePathBuilder();
    6543             : 
    6544           0 :   mPathBuilder->LineTo(aPoint);
    6545           0 : }
    6546             : 
    6547             : void
    6548           0 : CanvasPath::BezierTo(const gfx::Point& aCP1,
    6549             :                      const gfx::Point& aCP2,
    6550             :                      const gfx::Point& aCP3)
    6551             : {
    6552           0 :   EnsurePathBuilder();
    6553             : 
    6554           0 :   mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
    6555           0 : }
    6556             : 
    6557             : void
    6558           0 : CanvasPath::AddPath(CanvasPath& aCanvasPath, const Optional<NonNull<SVGMatrix>>& aMatrix)
    6559             : {
    6560           0 :   RefPtr<gfx::Path> tempPath = aCanvasPath.GetPath(CanvasWindingRule::Nonzero,
    6561           0 :                                                    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
    6562             : 
    6563           0 :   if (aMatrix.WasPassed()) {
    6564           0 :     const SVGMatrix& m = aMatrix.Value();
    6565           0 :     Matrix transform(m.A(), m.B(), m.C(), m.D(), m.E(), m.F());
    6566             : 
    6567           0 :     if (!transform.IsIdentity()) {
    6568           0 :       RefPtr<PathBuilder> tempBuilder = tempPath->TransformedCopyToBuilder(transform, FillRule::FILL_WINDING);
    6569           0 :       tempPath = tempBuilder->Finish();
    6570             :     }
    6571             :   }
    6572             : 
    6573           0 :   EnsurePathBuilder(); // in case a path is added to itself
    6574           0 :   tempPath->StreamToSink(mPathBuilder);
    6575           0 : }
    6576             : 
    6577             : already_AddRefed<gfx::Path>
    6578           0 : CanvasPath::GetPath(const CanvasWindingRule& aWinding, const DrawTarget* aTarget) const
    6579             : {
    6580           0 :   FillRule fillRule = FillRule::FILL_WINDING;
    6581           0 :   if (aWinding == CanvasWindingRule::Evenodd) {
    6582           0 :     fillRule = FillRule::FILL_EVEN_ODD;
    6583             :   }
    6584             : 
    6585           0 :   if (mPath &&
    6586           0 :       (mPath->GetBackendType() == aTarget->GetBackendType()) &&
    6587           0 :       (mPath->GetFillRule() == fillRule)) {
    6588           0 :     RefPtr<gfx::Path> path(mPath);
    6589           0 :     return path.forget();
    6590             :   }
    6591             : 
    6592           0 :   if (!mPath) {
    6593             :     // if there is no path, there must be a pathbuilder
    6594           0 :     MOZ_ASSERT(mPathBuilder);
    6595           0 :     mPath = mPathBuilder->Finish();
    6596           0 :     if (!mPath) {
    6597           0 :       RefPtr<gfx::Path> path(mPath);
    6598           0 :       return path.forget();
    6599             :     }
    6600             : 
    6601           0 :     mPathBuilder = nullptr;
    6602             :   }
    6603             : 
    6604             :   // retarget our backend if we're used with a different backend
    6605           0 :   if (mPath->GetBackendType() != aTarget->GetBackendType()) {
    6606           0 :     RefPtr<PathBuilder> tmpPathBuilder = aTarget->CreatePathBuilder(fillRule);
    6607           0 :     mPath->StreamToSink(tmpPathBuilder);
    6608           0 :     mPath = tmpPathBuilder->Finish();
    6609           0 :   } else if (mPath->GetFillRule() != fillRule) {
    6610           0 :     RefPtr<PathBuilder> tmpPathBuilder = mPath->CopyToBuilder(fillRule);
    6611           0 :     mPath = tmpPathBuilder->Finish();
    6612             :   }
    6613             : 
    6614           0 :   RefPtr<gfx::Path> path(mPath);
    6615           0 :   return path.forget();
    6616             : }
    6617             : 
    6618             : void
    6619           0 : CanvasPath::EnsurePathBuilder() const
    6620             : {
    6621           0 :   if (mPathBuilder) {
    6622           0 :     return;
    6623             :   }
    6624             : 
    6625             :   // if there is not pathbuilder, there must be a path
    6626           0 :   MOZ_ASSERT(mPath);
    6627           0 :   mPathBuilder = mPath->CopyToBuilder();
    6628           0 :   mPath = nullptr;
    6629             : }
    6630             : 
    6631             : } // namespace dom
    6632             : } // namespace mozilla

Generated by: LCOV version 1.13