tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CanvasRenderingContext2D.cpp (251594B)


      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 <algorithm>
      9 
     10 #include "CanvasImageCache.h"
     11 #include "CanvasUtils.h"
     12 #include "GeckoBindings.h"
     13 #include "ImageEncoder.h"
     14 #include "ImageRegion.h"
     15 #include "LayerUserData.h"
     16 #include "Units.h"
     17 #include "WindowRenderer.h"
     18 #include "gfxBlur.h"
     19 #include "gfxContext.h"
     20 #include "gfxFont.h"
     21 #include "gfxPlatform.h"
     22 #include "gfxTextRun.h"
     23 #include "gfxUtils.h"
     24 #include "js/Array.h"  // JS::GetArrayLength
     25 #include "js/Conversions.h"
     26 #include "js/HeapAPI.h"
     27 #include "js/PropertyAndElement.h"  // JS_GetElement
     28 #include "js/Warnings.h"            // JS::WarnASCII
     29 #include "js/experimental/TypedData.h"  // JS_NewUint8ClampedArray, JS_GetUint8ClampedArrayData
     30 #include "jsapi.h"
     31 #include "jsfriendapi.h"
     32 #include "mozilla/Assertions.h"
     33 #include "mozilla/CheckedInt.h"
     34 #include "mozilla/CycleCollectedJSRuntime.h"
     35 #include "mozilla/DebugOnly.h"
     36 #include "mozilla/FilterInstance.h"
     37 #include "mozilla/GeckoBindings.h"
     38 #include "mozilla/Logging.h"
     39 #include "mozilla/MathAlgorithms.h"
     40 #include "mozilla/Preferences.h"
     41 #include "mozilla/PresShell.h"
     42 #include "mozilla/PresShellInlines.h"
     43 #include "mozilla/RestyleManager.h"
     44 #include "mozilla/SVGContentUtils.h"
     45 #include "mozilla/SVGImageContext.h"
     46 #include "mozilla/SVGObserverUtils.h"
     47 #include "mozilla/ServoBindings.h"
     48 #include "mozilla/ServoCSSParser.h"
     49 #include "mozilla/ServoStyleSet.h"
     50 #include "mozilla/StaticPrefs_browser.h"
     51 #include "mozilla/StaticPrefs_gfx.h"
     52 #include "mozilla/TimeStamp.h"
     53 #include "mozilla/UniquePtr.h"
     54 #include "mozilla/dom/CanvasGradient.h"
     55 #include "mozilla/dom/CanvasPath.h"
     56 #include "mozilla/dom/CanvasPattern.h"
     57 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
     58 #include "mozilla/dom/DOMMatrix.h"
     59 #include "mozilla/dom/Document.h"
     60 #include "mozilla/dom/FontFaceSet.h"
     61 #include "mozilla/dom/FontFaceSetImpl.h"
     62 #include "mozilla/dom/GeneratePlaceholderCanvasData.h"
     63 #include "mozilla/dom/HTMLCanvasElement.h"
     64 #include "mozilla/dom/HTMLImageElement.h"
     65 #include "mozilla/dom/HTMLVideoElement.h"
     66 #include "mozilla/dom/ImageBitmap.h"
     67 #include "mozilla/dom/ImageData.h"
     68 #include "mozilla/dom/PBrowserParent.h"
     69 #include "mozilla/dom/SVGImageElement.h"
     70 #include "mozilla/dom/TextMetrics.h"
     71 #include "mozilla/dom/ToJSValue.h"
     72 #include "mozilla/dom/TypedArray.h"
     73 #include "mozilla/dom/VideoFrame.h"
     74 #include "mozilla/dom/WorkerPrivate.h"
     75 #include "mozilla/gfx/2D.h"
     76 #include "mozilla/gfx/CanvasShutdownManager.h"
     77 #include "mozilla/gfx/DataSurfaceHelpers.h"
     78 #include "mozilla/gfx/Filters.h"
     79 #include "mozilla/gfx/Helpers.h"
     80 #include "mozilla/gfx/PathHelpers.h"
     81 #include "mozilla/gfx/PatternHelpers.h"
     82 #include "mozilla/gfx/Swizzle.h"
     83 #include "mozilla/gfx/Tools.h"
     84 #include "mozilla/intl/BidiEmbeddingLevel.h"
     85 #include "mozilla/layers/CanvasClient.h"
     86 #include "mozilla/layers/ImageBridgeChild.h"
     87 #include "mozilla/layers/PersistentBufferProvider.h"
     88 #include "mozilla/layers/WebRenderCanvasRenderer.h"
     89 #include "mozilla/layers/WebRenderUserData.h"
     90 #include "nsBidiPresUtils.h"
     91 #include "nsCCUncollectableMarker.h"
     92 #include "nsCSSPseudoElements.h"
     93 #include "nsCSSValue.h"
     94 #include "nsColor.h"
     95 #include "nsComputedDOMStyle.h"
     96 #include "nsContentUtils.h"
     97 #include "nsDeviceContext.h"
     98 #include "nsDisplayList.h"
     99 #include "nsError.h"
    100 #include "nsFocusManager.h"
    101 #include "nsFontMetrics.h"
    102 #include "nsFrameLoader.h"
    103 #include "nsGfxCIID.h"
    104 #include "nsGlobalWindowInner.h"
    105 #include "nsIDocShell.h"
    106 #include "nsIFrame.h"
    107 #include "nsIInterfaceRequestorUtils.h"
    108 #include "nsIMemoryReporter.h"
    109 #include "nsLayoutUtils.h"
    110 #include "nsMathUtils.h"
    111 #include "nsPIDOMWindow.h"
    112 #include "nsPresContext.h"
    113 #include "nsPrintfCString.h"
    114 #include "nsRFPService.h"
    115 #include "nsReadableUtils.h"
    116 #include "nsStyleUtil.h"
    117 #include "nsTArray.h"
    118 #include "nsWrapperCacheInlines.h"
    119 #include "nsXULElement.h"
    120 
    121 #undef free  // apparently defined by some windows header, clashing with a
    122             // free() method in SkTypes.h
    123 
    124 #ifdef XP_WIN
    125 #  include "gfxWindowsPlatform.h"
    126 #endif
    127 
    128 // windows.h (included by chromium code) defines this, in its infinite wisdom
    129 #undef DrawText
    130 
    131 using namespace mozilla;
    132 using namespace mozilla::CanvasUtils;
    133 using namespace mozilla::css;
    134 using namespace mozilla::gfx;
    135 using namespace mozilla::image;
    136 using namespace mozilla::ipc;
    137 using namespace mozilla::layers;
    138 
    139 static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection");
    140 
    141 namespace mozilla::dom {
    142 
    143 // Cap sigma to avoid overly large temp surfaces.
    144 const Float SIGMA_MAX = 100;
    145 
    146 const size_t MAX_STYLE_STACK_SIZE = 1024;
    147 
    148 /* Memory reporter stuff */
    149 static Atomic<int64_t> gCanvasAzureMemoryUsed(0);
    150 
    151 // Adds Save() / Restore() calls to the scope.
    152 class MOZ_RAII AutoSaveRestore {
    153 public:
    154  explicit AutoSaveRestore(CanvasRenderingContext2D* aCtx) : mCtx(aCtx) {
    155    mCtx->Save();
    156  }
    157  ~AutoSaveRestore() { mCtx->Restore(); }
    158 
    159 private:
    160  RefPtr<CanvasRenderingContext2D> mCtx;
    161 };
    162 
    163 // This is KIND_OTHER because it's not always clear where in memory the pixels
    164 // of a canvas are stored.  Furthermore, this memory will be tracked by the
    165 // underlying surface implementations.  See bug 655638 for details.
    166 class Canvas2dPixelsReporter final : public nsIMemoryReporter {
    167  ~Canvas2dPixelsReporter() = default;
    168 
    169 public:
    170  NS_DECL_ISUPPORTS
    171 
    172  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    173                            nsISupports* aData, bool aAnonymize) override {
    174    MOZ_COLLECT_REPORT("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
    175                       gCanvasAzureMemoryUsed,
    176                       "Memory used by 2D canvases. Each canvas requires "
    177                       "(width * height * 4) bytes.");
    178 
    179    return NS_OK;
    180  }
    181 };
    182 
    183 NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
    184 
    185 class CanvasConicGradient : public CanvasGradient {
    186 public:
    187  CanvasConicGradient(CanvasRenderingContext2D* aContext, Float aAngle,
    188                      const Point& aCenter)
    189      : CanvasGradient(aContext, Type::CONIC),
    190        mAngle(aAngle),
    191        mCenter(aCenter) {}
    192 
    193  const Float mAngle;
    194  const Point mCenter;
    195 };
    196 
    197 class CanvasRadialGradient : public CanvasGradient {
    198 public:
    199  CanvasRadialGradient(CanvasRenderingContext2D* aContext,
    200                       const Point& aBeginOrigin, Float aBeginRadius,
    201                       const Point& aEndOrigin, Float aEndRadius)
    202      : CanvasGradient(aContext, Type::RADIAL),
    203        mCenter1(aBeginOrigin),
    204        mCenter2(aEndOrigin),
    205        mRadius1(aBeginRadius),
    206        mRadius2(aEndRadius) {}
    207 
    208  Point mCenter1;
    209  Point mCenter2;
    210  Float mRadius1;
    211  Float mRadius2;
    212 };
    213 
    214 class CanvasLinearGradient : public CanvasGradient {
    215 public:
    216  CanvasLinearGradient(CanvasRenderingContext2D* aContext, const Point& aBegin,
    217                       const Point& aEnd)
    218      : CanvasGradient(aContext, Type::LINEAR), mBegin(aBegin), mEnd(aEnd) {}
    219 
    220 protected:
    221  friend struct CanvasBidiProcessor;
    222  friend class CanvasGeneralPattern;
    223 
    224  // Beginning of linear gradient.
    225  Point mBegin;
    226  // End of linear gradient.
    227  Point mEnd;
    228 };
    229 
    230 bool CanvasRenderingContext2D::PatternIsOpaque(
    231    CanvasRenderingContext2D::Style aStyle, bool* aIsColor) const {
    232  const ContextState& state = CurrentState();
    233  bool opaque = false;
    234  bool color = false;
    235  if (state.globalAlpha >= 1.0) {
    236    if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
    237      opaque = IsOpaque(state.patternStyles[aStyle]->mSurface->GetFormat());
    238    } else if (!state.gradientStyles[aStyle]) {
    239      // TODO: for gradient patterns we could check that all stops are opaque
    240      // colors.
    241      // it's a color pattern.
    242      opaque = sRGBColor::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
    243      color = true;
    244    }
    245  }
    246  if (aIsColor) {
    247    *aIsColor = color;
    248  }
    249  return opaque;
    250 }
    251 
    252 // This class is named 'GeneralCanvasPattern' instead of just
    253 // 'GeneralPattern' to keep Windows PGO builds from confusing the
    254 // GeneralPattern class in gfxContext.cpp with this one.
    255 class CanvasGeneralPattern {
    256 public:
    257  using Style = CanvasRenderingContext2D::Style;
    258  using ContextState = CanvasRenderingContext2D::ContextState;
    259 
    260  Pattern& ForStyle(CanvasRenderingContext2D* aCtx, Style aStyle,
    261                    DrawTarget* aRT) {
    262    // This should only be called once or the mPattern destructor will
    263    // not be executed.
    264    NS_ASSERTION(
    265        !mPattern.GetPattern(),
    266        "ForStyle() should only be called once on CanvasGeneralPattern!");
    267 
    268    const ContextState& state = aCtx->CurrentState();
    269 
    270    if (state.StyleIsColor(aStyle)) {
    271      mPattern.InitColorPattern(ToDeviceColor(state.colorStyles[aStyle]));
    272    } else if (state.gradientStyles[aStyle] &&
    273               state.gradientStyles[aStyle]->GetType() ==
    274                   CanvasGradient::Type::LINEAR) {
    275      auto gradient = static_cast<CanvasLinearGradient*>(
    276          state.gradientStyles[aStyle].get());
    277 
    278      mPattern.InitLinearGradientPattern(
    279          gradient->mBegin, gradient->mEnd,
    280          gradient->GetGradientStopsForTarget(aRT));
    281    } else if (state.gradientStyles[aStyle] &&
    282               state.gradientStyles[aStyle]->GetType() ==
    283                   CanvasGradient::Type::RADIAL) {
    284      auto gradient = static_cast<CanvasRadialGradient*>(
    285          state.gradientStyles[aStyle].get());
    286 
    287      mPattern.InitRadialGradientPattern(
    288          gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
    289          gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
    290    } else if (state.gradientStyles[aStyle] &&
    291               state.gradientStyles[aStyle]->GetType() ==
    292                   CanvasGradient::Type::CONIC) {
    293      auto gradient =
    294          static_cast<CanvasConicGradient*>(state.gradientStyles[aStyle].get());
    295 
    296      mPattern.InitConicGradientPattern(
    297          gradient->mCenter, gradient->mAngle, 0, 1,
    298          gradient->GetGradientStopsForTarget(aRT));
    299    } else if (state.patternStyles[aStyle]) {
    300      aCtx->DoSecurityCheck(state.patternStyles[aStyle]->mPrincipal,
    301                            state.patternStyles[aStyle]->mForceWriteOnly,
    302                            state.patternStyles[aStyle]->mCORSUsed);
    303 
    304      ExtendMode mode;
    305      if (state.patternStyles[aStyle]->mRepeat ==
    306          CanvasPattern::RepeatMode::NOREPEAT) {
    307        mode = ExtendMode::CLAMP;
    308      } else {
    309        mode = ExtendMode::REPEAT;
    310      }
    311 
    312      SamplingFilter samplingFilter;
    313      if (state.imageSmoothingEnabled) {
    314        samplingFilter = SamplingFilter::GOOD;
    315      } else {
    316        samplingFilter = SamplingFilter::POINT;
    317      }
    318 
    319      mPattern.InitSurfacePattern(state.patternStyles[aStyle]->mSurface, mode,
    320                                  state.patternStyles[aStyle]->mTransform,
    321                                  samplingFilter);
    322    }
    323 
    324    return *mPattern.GetPattern();
    325  }
    326 
    327  GeneralPattern mPattern;
    328 };
    329 
    330 /* This is an RAII based class that can be used as a drawtarget for
    331 * operations that need to have a filter applied to their results.
    332 * All coordinates passed to the constructor are in device space.
    333 */
    334 class AdjustedTargetForFilter {
    335 public:
    336  using ContextState = CanvasRenderingContext2D::ContextState;
    337 
    338  AdjustedTargetForFilter(CanvasRenderingContext2D* aCtx,
    339                          DrawTarget* aFinalTarget,
    340                          const gfx::IntPoint& aFilterSpaceToTargetOffset,
    341                          const gfx::IntRect& aPreFilterBounds,
    342                          const gfx::IntRect& aPostFilterBounds,
    343                          gfx::CompositionOp aCompositionOp,
    344                          bool aAllowOptimization = false)
    345      : mFinalTarget(aFinalTarget),
    346        mCtx(aCtx),
    347        mPostFilterBounds(aPostFilterBounds),
    348        mOffset(aFilterSpaceToTargetOffset),
    349        mCompositionOp(aCompositionOp),
    350        mAllowOptimization(aAllowOptimization) {
    351    nsIntRegion sourceGraphicNeededRegion;
    352    nsIntRegion fillPaintNeededRegion;
    353    nsIntRegion strokePaintNeededRegion;
    354 
    355    FilterSupport::ComputeSourceNeededRegions(
    356        aCtx->CurrentState().filter, mPostFilterBounds,
    357        sourceGraphicNeededRegion, fillPaintNeededRegion,
    358        strokePaintNeededRegion);
    359 
    360    mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
    361    mFillPaintRect = fillPaintNeededRegion.GetBounds();
    362    mStrokePaintRect = strokePaintNeededRegion.GetBounds();
    363 
    364    mSourceGraphicRect = mSourceGraphicRect.Intersect(aPreFilterBounds);
    365 
    366    if (mSourceGraphicRect.IsEmpty()) {
    367      // The filter might not make any use of the source graphic. We need to
    368      // create a DrawTarget that we can return from DT() anyway, so we'll
    369      // just use a 1x1-sized one.
    370      mSourceGraphicRect.SizeTo(1, 1);
    371    }
    372 
    373    if (mAllowOptimization) {
    374      return;
    375    }
    376 
    377    if (!(mFillPaintRect.IsEmpty() ||
    378          mFinalTarget->CanCreateSimilarDrawTarget(mFillPaintRect.Size(),
    379                                                   SurfaceFormat::B8G8R8A8)) ||
    380        !(mStrokePaintRect.IsEmpty() ||
    381          mFinalTarget->CanCreateSimilarDrawTarget(mStrokePaintRect.Size(),
    382                                                   SurfaceFormat::B8G8R8A8)) ||
    383        !mFinalTarget->CanCreateSimilarDrawTarget(mSourceGraphicRect.Size(),
    384                                                  SurfaceFormat::B8G8R8A8)) {
    385      mTarget = mFinalTarget;
    386      mCtx = nullptr;
    387      mFinalTarget = nullptr;
    388      return;
    389    }
    390 
    391    mTarget = mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(),
    392                                                    SurfaceFormat::B8G8R8A8);
    393 
    394    if (mTarget) {
    395      // See bug 1524554.
    396      mTarget->ClearRect(gfx::Rect());
    397    }
    398 
    399    if (!mTarget || !mTarget->IsValid()) {
    400      // XXX - Deal with the situation where our temp size is too big to
    401      // fit in a texture (bug 1066622).
    402      mTarget = mFinalTarget;
    403      mCtx = nullptr;
    404      mFinalTarget = nullptr;
    405      return;
    406    }
    407 
    408    mTarget->SetTransform(mFinalTarget->GetTransform().PostTranslate(
    409        -mSourceGraphicRect.TopLeft() + mOffset));
    410  }
    411 
    412  void Fill(const Path* aPath, const Pattern& aPattern,
    413            const DrawOptions& aOptions) {
    414    if (mAllowOptimization) {
    415      mDeferInput = mFinalTarget->DeferFilterInput(
    416          aPath, aPattern, mSourceGraphicRect, mOffset, aOptions);
    417    } else {
    418      mTarget->Fill(aPath, aPattern, aOptions);
    419    }
    420  }
    421 
    422  void FillRect(const Rect& aRect, const Pattern& aPattern,
    423                const DrawOptions& aOptions) {
    424    if (mAllowOptimization) {
    425      RefPtr<Path> path = MakePathForRect(*mFinalTarget, aRect);
    426      mDeferInput = mFinalTarget->DeferFilterInput(
    427          path, aPattern, mSourceGraphicRect, mOffset, aOptions);
    428    } else {
    429      mTarget->FillRect(aRect, aPattern, aOptions);
    430    }
    431  }
    432 
    433  void Stroke(const Path* aPath, const Pattern& aPattern,
    434              const StrokeOptions& aStrokeOptions,
    435              const DrawOptions& aOptions) {
    436    if (mAllowOptimization) {
    437      mDeferInput =
    438          mFinalTarget->DeferFilterInput(aPath, aPattern, mSourceGraphicRect,
    439                                         mOffset, aOptions, &aStrokeOptions);
    440    } else {
    441      mTarget->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
    442    }
    443  }
    444 
    445  void StrokeRect(const Rect& aRect, const Pattern& aPattern,
    446                  const StrokeOptions& aStrokeOptions,
    447                  const DrawOptions& aOptions) {
    448    if (mAllowOptimization) {
    449      RefPtr<Path> path = MakePathForRect(*mFinalTarget, aRect);
    450      mDeferInput =
    451          mFinalTarget->DeferFilterInput(path, aPattern, mSourceGraphicRect,
    452                                         mOffset, aOptions, &aStrokeOptions);
    453    } else {
    454      mTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
    455    }
    456  }
    457 
    458  void StrokeLine(const Point& aStart, const Point& aEnd,
    459                  const Pattern& aPattern, const StrokeOptions& aStrokeOptions,
    460                  const DrawOptions& aOptions) {
    461    if (mAllowOptimization) {
    462      RefPtr<PathBuilder> builder = mFinalTarget->CreatePathBuilder();
    463      builder->MoveTo(aStart);
    464      builder->LineTo(aEnd);
    465      RefPtr<Path> path = builder->Finish();
    466      mDeferInput =
    467          mFinalTarget->DeferFilterInput(path, aPattern, mSourceGraphicRect,
    468                                         mOffset, aOptions, &aStrokeOptions);
    469    } else {
    470      mTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
    471    }
    472  }
    473 
    474  void DrawSurface(SourceSurface* aSurface, const Rect& aDest,
    475                   const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
    476                   const DrawOptions& aOptions) {
    477    if (mAllowOptimization) {
    478      RefPtr<Path> path = MakePathForRect(*mFinalTarget, aSource);
    479      SurfacePattern pattern(aSurface, ExtendMode::CLAMP, Matrix(),
    480                             aSurfOptions.mSamplingFilter);
    481      Matrix matrix = Matrix::Scaling(aDest.width / aSource.width,
    482                                      aDest.height / aSource.height);
    483      matrix.PreTranslate(-aSource.x, -aSource.y);
    484      matrix.PostTranslate(aDest.x, aDest.y);
    485      AutoRestoreTransform autoRestoreTransform(mFinalTarget);
    486      mFinalTarget->ConcatTransform(matrix);
    487      mDeferInput = mFinalTarget->DeferFilterInput(
    488          path, pattern, mSourceGraphicRect, mOffset, aOptions);
    489    } else {
    490      mTarget->DrawSurface(aSurface, aDest, aSource, aSurfOptions, aOptions);
    491    }
    492  }
    493 
    494  // Return a SourceSurface that contains the FillPaint or StrokePaint source.
    495  already_AddRefed<FilterNode> DoSourcePaint(
    496      gfx::IntRect& aRect, CanvasRenderingContext2D::Style aStyle) {
    497    if (aRect.IsEmpty()) {
    498      return nullptr;
    499    }
    500 
    501    if (mAllowOptimization) {
    502      Matrix transform = mFinalTarget->GetTransform();
    503      RefPtr<Path> path =
    504          transform.Invert()
    505              ? MakePathForRect(*mFinalTarget, transform.TransformBounds(
    506                                                   Rect(aRect - mOffset)))
    507              : MakeEmptyPath(*mFinalTarget);
    508      return mFinalTarget->DeferFilterInput(
    509          path, CanvasGeneralPattern().ForStyle(mCtx, aStyle, mFinalTarget),
    510          aRect, mOffset);
    511    }
    512 
    513    RefPtr<DrawTarget> dt = mFinalTarget->CreateSimilarDrawTarget(
    514        aRect.Size(), SurfaceFormat::B8G8R8A8);
    515 
    516    if (dt) {
    517      // See bug 1524554.
    518      dt->ClearRect(gfx::Rect());
    519    }
    520 
    521    if (!dt || !dt->IsValid()) {
    522      aRect.SetEmpty();
    523      return nullptr;
    524    }
    525 
    526    Matrix transform =
    527        mFinalTarget->GetTransform().PostTranslate(-aRect.TopLeft() + mOffset);
    528 
    529    dt->SetTransform(transform);
    530 
    531    if (transform.Invert()) {
    532      gfx::Rect dtBounds(0, 0, aRect.width, aRect.height);
    533      gfx::Rect fillRect = transform.TransformBounds(dtBounds);
    534      dt->FillRect(fillRect, CanvasGeneralPattern().ForStyle(mCtx, aStyle, dt));
    535    }
    536 
    537    RefPtr<SourceSurface> snapshot = dt->Snapshot();
    538    if (!snapshot) {
    539      return nullptr;
    540    }
    541 
    542    return FilterWrappers::ForSurface(mFinalTarget, snapshot, aRect.TopLeft());
    543  }
    544 
    545  ~AdjustedTargetForFilter() {
    546    if (!mCtx) {
    547      return;
    548    }
    549 
    550    RefPtr<FilterNode> sourceGraphic;
    551    if (mAllowOptimization) {
    552      sourceGraphic = mDeferInput;
    553    } else if (RefPtr<SourceSurface> snapshot = mTarget->Snapshot()) {
    554      sourceGraphic = FilterWrappers::ForSurface(mFinalTarget, snapshot,
    555                                                 mSourceGraphicRect.TopLeft());
    556    }
    557    RefPtr<FilterNode> fillPaint =
    558        DoSourcePaint(mFillPaintRect, CanvasRenderingContext2D::Style::FILL);
    559    RefPtr<FilterNode> strokePaint = DoSourcePaint(
    560        mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
    561 
    562    AutoRestoreTransform autoRestoreTransform(mFinalTarget);
    563    mFinalTarget->SetTransform(Matrix());
    564 
    565    MOZ_RELEASE_ASSERT(!mCtx->CurrentState().filter.mPrimitives.IsEmpty());
    566    gfx::FilterSupport::RenderFilterDescription(
    567        mFinalTarget, mCtx->CurrentState().filter, gfx::Rect(mPostFilterBounds),
    568        std::move(sourceGraphic), mSourceGraphicRect, std::move(fillPaint),
    569        mFillPaintRect, std::move(strokePaint), mStrokePaintRect,
    570        mCtx->CurrentState().filterAdditionalImages,
    571        mPostFilterBounds.TopLeft() - mOffset,
    572        DrawOptions(1.0f, mCompositionOp));
    573 
    574    const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
    575    MOZ_RELEASE_ASSERT(!filter.mPrimitives.IsEmpty());
    576    if (filter.mPrimitives.LastElement().IsTainted()) {
    577      if (mCtx->mCanvasElement) {
    578        mCtx->mCanvasElement->SetWriteOnly();
    579      } else if (mCtx->mOffscreenCanvas) {
    580        mCtx->mOffscreenCanvas->SetWriteOnly();
    581      }
    582    }
    583  }
    584 
    585  DrawTarget* DT() { return mTarget; }
    586 
    587 private:
    588  RefPtr<DrawTarget> mTarget;
    589  RefPtr<DrawTarget> mFinalTarget;
    590  CanvasRenderingContext2D* mCtx;
    591  gfx::IntRect mSourceGraphicRect;
    592  gfx::IntRect mFillPaintRect;
    593  gfx::IntRect mStrokePaintRect;
    594  gfx::IntRect mPostFilterBounds;
    595  gfx::IntPoint mOffset;
    596  gfx::CompositionOp mCompositionOp;
    597  bool mAllowOptimization;
    598  RefPtr<FilterNode> mDeferInput;
    599 };
    600 
    601 /* This is an RAII based class that can be used as a drawtarget for
    602 * operations that need to have a shadow applied to their results.
    603 * All coordinates passed to the constructor are in device space.
    604 */
    605 class AdjustedTargetForShadow {
    606 public:
    607  using ContextState = CanvasRenderingContext2D::ContextState;
    608 
    609  AdjustedTargetForShadow(CanvasRenderingContext2D* aCtx,
    610                          DrawTarget* aFinalTarget, const gfx::Rect& aBounds,
    611                          gfx::CompositionOp aCompositionOp)
    612      : mFinalTarget(aFinalTarget), mCtx(aCtx), mCompositionOp(aCompositionOp) {
    613    const ContextState& state = mCtx->CurrentState();
    614    mSigma = state.ShadowBlurSigma();
    615 
    616    // We actually include the bounds of the shadow blur, this makes it
    617    // easier to execute the actual blur on hardware, and shouldn't affect
    618    // the amount of pixels that need to be touched.
    619    gfx::Rect bounds = aBounds;
    620    int32_t blurRadius = state.ShadowBlurRadius();
    621    bounds.Inflate(blurRadius);
    622    bounds.RoundOut();
    623    if (!bounds.ToIntRect(&mTempRect) ||
    624        !mFinalTarget->CanCreateSimilarDrawTarget(mTempRect.Size(),
    625                                                  SurfaceFormat::B8G8R8A8)) {
    626      mTarget = mFinalTarget;
    627      mCtx = nullptr;
    628      mFinalTarget = nullptr;
    629      return;
    630    }
    631 
    632    mTarget = mFinalTarget->CreateShadowDrawTarget(
    633        mTempRect.Size(), SurfaceFormat::B8G8R8A8, mSigma);
    634 
    635    if (mTarget) {
    636      // See bug 1524554.
    637      mTarget->ClearRect(gfx::Rect());
    638    }
    639 
    640    if (!mTarget || !mTarget->IsValid()) {
    641      // XXX - Deal with the situation where our temp size is too big to
    642      // fit in a texture (bug 1066622).
    643      mTarget = mFinalTarget;
    644      mCtx = nullptr;
    645      mFinalTarget = nullptr;
    646    } else {
    647      mTarget->SetTransform(
    648          mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
    649    }
    650  }
    651 
    652  ~AdjustedTargetForShadow() {
    653    if (!mCtx) {
    654      return;
    655    }
    656 
    657    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
    658 
    659    mFinalTarget->DrawSurfaceWithShadow(
    660        snapshot, mTempRect.TopLeft(),
    661        ShadowOptions(ToDeviceColor(mCtx->CurrentState().shadowColor),
    662                      mCtx->CurrentState().shadowOffset, mSigma),
    663        mCompositionOp);
    664  }
    665 
    666  DrawTarget* DT() { return mTarget; }
    667 
    668  gfx::IntPoint OffsetToFinalDT() { return mTempRect.TopLeft(); }
    669 
    670 private:
    671  RefPtr<DrawTarget> mTarget;
    672  RefPtr<DrawTarget> mFinalTarget;
    673  CanvasRenderingContext2D* mCtx;
    674  Float mSigma;
    675  gfx::IntRect mTempRect;
    676  gfx::CompositionOp mCompositionOp;
    677 };
    678 
    679 /*
    680 * This is an RAII based class that can be used as a drawtarget for
    681 * operations that need a shadow or a filter drawn. It will automatically
    682 * provide a temporary target when needed, and if so blend it back with a
    683 * shadow, filter, or both.
    684 * If both a shadow and a filter are needed, the filter is applied first,
    685 * and the shadow is applied to the filtered results.
    686 *
    687 * aBounds specifies the bounds of the drawing operation that will be
    688 * drawn to the target, it is given in device space! If this is nullptr the
    689 * drawing operation will be assumed to cover the whole canvas.
    690 */
    691 class AdjustedTarget {
    692 public:
    693  using ContextState = CanvasRenderingContext2D::ContextState;
    694 
    695  explicit AdjustedTarget(CanvasRenderingContext2D* aCtx,
    696                          const gfx::Rect* aBounds = nullptr,
    697                          bool aAllowOptimization = false)
    698      : mCtx(aCtx), mUsedOperation(aCtx->CurrentState().op) {
    699    // All rects in this function are in the device space of ctx->mTarget.
    700 
    701    // In order to keep our temporary surfaces as small as possible, we first
    702    // calculate what their maximum required bounds would need to be if we
    703    // were to fill the whole canvas. Everything outside those bounds we don't
    704    // need to render.
    705    gfx::Rect r(0, 0, aCtx->mWidth, aCtx->mHeight);
    706    gfx::Rect maxSourceNeededBoundsForShadow =
    707        MaxSourceNeededBoundsForShadow(r, aCtx);
    708    gfx::Rect maxSourceNeededBoundsForFilter =
    709        MaxSourceNeededBoundsForFilter(maxSourceNeededBoundsForShadow, aCtx);
    710    if (!aCtx->IsTargetValid()) {
    711      return;
    712    }
    713 
    714    gfx::Rect bounds = maxSourceNeededBoundsForFilter;
    715    if (aBounds) {
    716      bounds = bounds.Intersect(*aBounds);
    717    }
    718    gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
    719    if (!aCtx->IsTargetValid() || !boundsAfterFilter.IsFinite()) {
    720      return;
    721    }
    722 
    723    gfx::IntPoint offsetToFinalDT;
    724 
    725    // First set up the shadow draw target, because the shadow goes outside.
    726    // It applies to the post-filter results, if both a filter and a shadow
    727    // are used.
    728    const bool applyFilter = aCtx->NeedToApplyFilter();
    729    if (aCtx->NeedToDrawShadow()) {
    730      if (aAllowOptimization && !applyFilter) {
    731        // If only drawing a shadow and no filter, then avoid buffering to an
    732        // intermediate target while drawing the shadow directly to the final
    733        // target. When doing so, we want to use the actual composition op
    734        // instead of OP_OVER.
    735        mTarget = aCtx->mTarget;
    736        if (mTarget && mTarget->IsValid()) {
    737          mOptimizeShadow = true;
    738          return;
    739        }
    740      }
    741      mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
    742          aCtx, aCtx->mTarget, boundsAfterFilter, mUsedOperation);
    743      mTarget = mShadowTarget->DT();
    744      offsetToFinalDT = mShadowTarget->OffsetToFinalDT();
    745 
    746      // If we also have a filter, the filter needs to be drawn with OP_OVER
    747      // because shadow drawing already applies op on the result.
    748      mUsedOperation = CompositionOp::OP_OVER;
    749    }
    750 
    751    // Now set up the filter draw target.
    752    if (!aCtx->IsTargetValid()) {
    753      return;
    754    }
    755    if (applyFilter) {
    756      bounds.RoundOut();
    757 
    758      if (!mTarget) {
    759        mTarget = aCtx->mTarget;
    760      }
    761      gfx::IntRect intBounds;
    762      if (!bounds.ToIntRect(&intBounds)) {
    763        return;
    764      }
    765 
    766      // Only allow optimization of filters if no shadow is being drawn.
    767      if (aAllowOptimization && !mShadowTarget) {
    768        mOptimizeFilter = true;
    769      }
    770 
    771      mFilterTarget = MakeUnique<AdjustedTargetForFilter>(
    772          aCtx, mTarget, offsetToFinalDT, intBounds,
    773          gfx::RoundedToInt(boundsAfterFilter), mUsedOperation,
    774          mOptimizeFilter);
    775      mTarget = mFilterTarget->DT();
    776      mUsedOperation = CompositionOp::OP_OVER;
    777    }
    778    if (!mTarget) {
    779      mTarget = aCtx->mTarget;
    780    }
    781  }
    782 
    783  ~AdjustedTarget() {
    784    // The order in which the targets are finalized is important.
    785    // Filters are inside, any shadow applies to the post-filter results.
    786    mFilterTarget.reset();
    787    mShadowTarget.reset();
    788  }
    789 
    790  operator DrawTarget*() { return mTarget; }
    791 
    792  DrawTarget* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mTarget; }
    793 
    794  CompositionOp UsedOperation() const { return mUsedOperation; }
    795 
    796  bool UseOptimizeShadow() const { return mOptimizeShadow; }
    797  bool UseOptimizeFilter() const { return mOptimizeFilter; }
    798 
    799  ShadowOptions ShadowParams() const {
    800    const ContextState& state = mCtx->CurrentState();
    801    return ShadowOptions(ToDeviceColor(state.shadowColor), state.shadowOffset,
    802                         state.ShadowBlurSigma());
    803  }
    804 
    805  void Fill(const Path* aPath, const Pattern& aPattern,
    806            const DrawOptions& aOptions) {
    807    if (mOptimizeFilter) {
    808      mFilterTarget->Fill(aPath, aPattern, aOptions);
    809      return;
    810    }
    811    if (mOptimizeShadow) {
    812      mTarget->DrawShadow(aPath, aPattern, ShadowParams(), aOptions);
    813    }
    814    mTarget->Fill(aPath, aPattern, aOptions);
    815  }
    816 
    817  void FillRect(const Rect& aRect, const Pattern& aPattern,
    818                const DrawOptions& aOptions) {
    819    if (mOptimizeFilter) {
    820      mFilterTarget->FillRect(aRect, aPattern, aOptions);
    821      return;
    822    }
    823    if (mOptimizeShadow) {
    824      RefPtr<Path> path = MakePathForRect(*mTarget, aRect);
    825      mTarget->DrawShadow(path, aPattern, ShadowParams(), aOptions);
    826    }
    827    mTarget->FillRect(aRect, aPattern, aOptions);
    828  }
    829 
    830  void Stroke(const Path* aPath, const Pattern& aPattern,
    831              const StrokeOptions& aStrokeOptions,
    832              const DrawOptions& aOptions) {
    833    if (mOptimizeFilter) {
    834      mFilterTarget->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
    835      return;
    836    }
    837    if (mOptimizeShadow) {
    838      mTarget->DrawShadow(aPath, aPattern, ShadowParams(), aOptions,
    839                          &aStrokeOptions);
    840    }
    841    mTarget->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
    842  }
    843 
    844  void StrokeRect(const Rect& aRect, const Pattern& aPattern,
    845                  const StrokeOptions& aStrokeOptions,
    846                  const DrawOptions& aOptions) {
    847    if (mOptimizeFilter) {
    848      mFilterTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
    849      return;
    850    }
    851    if (mOptimizeShadow) {
    852      RefPtr<Path> path = MakePathForRect(*mTarget, aRect);
    853      mTarget->DrawShadow(path, aPattern, ShadowParams(), aOptions,
    854                          &aStrokeOptions);
    855    }
    856    mTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
    857  }
    858 
    859  void StrokeLine(const Point& aStart, const Point& aEnd,
    860                  const Pattern& aPattern, const StrokeOptions& aStrokeOptions,
    861                  const DrawOptions& aOptions) {
    862    if (mOptimizeFilter) {
    863      mFilterTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions,
    864                                aOptions);
    865      return;
    866    }
    867    if (mOptimizeShadow) {
    868      RefPtr<PathBuilder> builder = mTarget->CreatePathBuilder();
    869      builder->MoveTo(aStart);
    870      builder->LineTo(aEnd);
    871      RefPtr<Path> path = builder->Finish();
    872      mTarget->DrawShadow(path, aPattern, ShadowParams(), aOptions,
    873                          &aStrokeOptions);
    874    }
    875    mTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
    876  }
    877 
    878  void DrawSurface(SourceSurface* aSurface, const Rect& aDest,
    879                   const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
    880                   const DrawOptions& aOptions) {
    881    if (mOptimizeFilter) {
    882      mFilterTarget->DrawSurface(aSurface, aDest, aSource, aSurfOptions,
    883                                 aOptions);
    884      return;
    885    }
    886    if (mOptimizeShadow) {
    887      RefPtr<Path> path = MakePathForRect(*mTarget, aSource);
    888      ShadowOptions shadowParams(ShadowParams());
    889      SurfacePattern pattern(aSurface, ExtendMode::CLAMP, Matrix(),
    890                             shadowParams.BlurRadius() > 1
    891                                 ? SamplingFilter::POINT
    892                                 : aSurfOptions.mSamplingFilter);
    893      Matrix matrix = Matrix::Scaling(aDest.width / aSource.width,
    894                                      aDest.height / aSource.height);
    895      matrix.PreTranslate(-aSource.x, -aSource.y);
    896      matrix.PostTranslate(aDest.x, aDest.y);
    897      AutoRestoreTransform autoRestoreTransform(mTarget);
    898      mTarget->ConcatTransform(matrix);
    899      mTarget->DrawShadow(path, pattern, shadowParams, aOptions);
    900    }
    901    mTarget->DrawSurface(aSurface, aDest, aSource, aSurfOptions, aOptions);
    902  }
    903 
    904 private:
    905  gfx::Rect MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds,
    906                                           CanvasRenderingContext2D* aCtx) {
    907    const bool applyFilter = aCtx->NeedToApplyFilter();
    908    if (!aCtx->IsTargetValid()) {
    909      return aDestBounds;
    910    }
    911    if (!applyFilter) {
    912      return aDestBounds;
    913    }
    914 
    915    nsIntRegion sourceGraphicNeededRegion;
    916    nsIntRegion fillPaintNeededRegion;
    917    nsIntRegion strokePaintNeededRegion;
    918 
    919    FilterSupport::ComputeSourceNeededRegions(
    920        aCtx->CurrentState().filter, gfx::RoundedToInt(aDestBounds),
    921        sourceGraphicNeededRegion, fillPaintNeededRegion,
    922        strokePaintNeededRegion);
    923 
    924    return gfx::Rect(sourceGraphicNeededRegion.GetBounds());
    925  }
    926 
    927  gfx::Rect MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds,
    928                                           CanvasRenderingContext2D* aCtx) {
    929    if (!aCtx->NeedToDrawShadow()) {
    930      return aDestBounds;
    931    }
    932 
    933    const ContextState& state = aCtx->CurrentState();
    934    gfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
    935    sourceBounds.Inflate(state.ShadowBlurRadius());
    936 
    937    // Union the shadow source with the original rect because we're going to
    938    // draw both.
    939    return sourceBounds.Union(aDestBounds);
    940  }
    941 
    942  gfx::Rect BoundsAfterFilter(const gfx::Rect& aBounds,
    943                              CanvasRenderingContext2D* aCtx) {
    944    const bool applyFilter = aCtx->NeedToApplyFilter();
    945    if (!aCtx->IsTargetValid()) {
    946      return aBounds;
    947    }
    948    if (!applyFilter) {
    949      return aBounds;
    950    }
    951 
    952    gfx::Rect bounds(aBounds);
    953    bounds.RoundOut();
    954 
    955    gfx::IntRect intBounds;
    956    if (!bounds.ToIntRect(&intBounds)) {
    957      return gfx::Rect();
    958    }
    959 
    960    nsIntRegion extents = gfx::FilterSupport::ComputePostFilterExtents(
    961        aCtx->CurrentState().filter, intBounds);
    962    return gfx::Rect(extents.GetBounds());
    963  }
    964 
    965  CanvasRenderingContext2D* mCtx;
    966  bool mOptimizeShadow = false;
    967  bool mOptimizeFilter = false;
    968  CompositionOp mUsedOperation;
    969  RefPtr<DrawTarget> mTarget;
    970  UniquePtr<AdjustedTargetForShadow> mShadowTarget;
    971  UniquePtr<AdjustedTargetForFilter> mFilterTarget;
    972 };
    973 
    974 void CanvasPattern::SetTransform(const DOMMatrix2DInit& aInit,
    975                                 ErrorResult& aError) {
    976  Matrix matrix2D(DOMMatrixReadOnly::ToValidatedMatrixDouble(aInit, aError));
    977  if (aError.Failed()) {
    978    return;
    979  }
    980  if (!matrix2D.IsFinite()) {
    981    return;
    982  }
    983  mTransform = Matrix(matrix2D);
    984 }
    985 
    986 void CanvasGradient::AddColorStop(float aOffset, const nsACString& aColorstr,
    987                                  ErrorResult& aRv) {
    988  if (aOffset < 0.0 || aOffset > 1.0) {
    989    return aRv.ThrowIndexSizeError("Offset out of 0-1.0 range");
    990  }
    991 
    992  if (!mContext) {
    993    return aRv.ThrowSyntaxError("No canvas context");
    994  }
    995 
    996  auto color = mContext->ParseColor(
    997      aColorstr, CanvasRenderingContext2D::ResolveCurrentColor::No);
    998  if (!color) {
    999    return aRv.ThrowSyntaxError("Invalid color");
   1000  }
   1001 
   1002  GradientStop newStop;
   1003 
   1004  newStop.offset = aOffset;
   1005  newStop.color = ToDeviceColor(*color);
   1006 
   1007  mRawStops.AppendElement(newStop);
   1008 }
   1009 
   1010 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
   1011 
   1012 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
   1013 
   1014 NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
   1015 NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
   1016 
   1017 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(CanvasRenderingContext2D)
   1018 
   1019 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
   1020  // Make sure we remove ourselves from the list of demotable contexts (raw
   1021  // pointers), since we're logically destructed at this point.
   1022  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
   1023  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOffscreenCanvas)
   1024  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   1025  for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
   1026    ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
   1027    ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
   1028    ImplCycleCollectionUnlink(
   1029        tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
   1030    ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
   1031    if (auto* autoSVGFiltersObserver =
   1032            tmp->mStyleStack[i].autoSVGFiltersObserver.get()) {
   1033      /*
   1034       * XXXjwatt: I don't think this is doing anything useful.  All we do under
   1035       * this function is clear a raw C-style (i.e. not strong) pointer.  That's
   1036       * clearly not helping in breaking any cycles.  The fact that we MOZ_CRASH
   1037       * in OnRenderingChange if that pointer is null indicates that this isn't
   1038       * even doing anything useful in terms of preventing further invalidation
   1039       * from any observed filters.
   1040       */
   1041      autoSVGFiltersObserver->Detach();
   1042    }
   1043    ImplCycleCollectionUnlink(tmp->mStyleStack[i].autoSVGFiltersObserver);
   1044  }
   1045  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1046  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
   1047 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1048 
   1049 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
   1050  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
   1051  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOffscreenCanvas)
   1052  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
   1053  for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
   1054    ImplCycleCollectionTraverse(
   1055        cb, tmp->mStyleStack[i].patternStyles[Style::STROKE],
   1056        "Stroke CanvasPattern");
   1057    ImplCycleCollectionTraverse(cb,
   1058                                tmp->mStyleStack[i].patternStyles[Style::FILL],
   1059                                "Fill CanvasPattern");
   1060    ImplCycleCollectionTraverse(
   1061        cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE],
   1062        "Stroke CanvasGradient");
   1063    ImplCycleCollectionTraverse(cb,
   1064                                tmp->mStyleStack[i].gradientStyles[Style::FILL],
   1065                                "Fill CanvasGradient");
   1066    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].autoSVGFiltersObserver,
   1067                                "RAII SVG Filters Observer");
   1068  }
   1069 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1070 
   1071 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
   1072  if (nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper()) {
   1073    dom::Element* canvasElement = tmp->mCanvasElement;
   1074    if (canvasElement) {
   1075      if (canvasElement->IsPurple()) {
   1076        canvasElement->RemovePurple();
   1077      }
   1078      dom::Element::MarkNodeChildren(canvasElement);
   1079    }
   1080    return true;
   1081  }
   1082 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   1083 
   1084 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
   1085  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
   1086 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   1087 
   1088 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
   1089  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
   1090 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   1091 
   1092 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
   1093  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1094  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
   1095  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1096 NS_INTERFACE_MAP_END
   1097 
   1098 CanvasRenderingContext2D::ContextState::ContextState() = default;
   1099 
   1100 CanvasRenderingContext2D::ContextState::ContextState(const ContextState& aOther)
   1101    : fontGroup(aOther.fontGroup),
   1102      fontFont(aOther.fontFont),
   1103      fontComputedStyle(aOther.fontComputedStyle),
   1104      gradientStyles(aOther.gradientStyles),
   1105      patternStyles(aOther.patternStyles),
   1106      colorStyles(aOther.colorStyles),
   1107      font(aOther.font),
   1108      textAlign(aOther.textAlign),
   1109      textBaseline(aOther.textBaseline),
   1110      textDirection(aOther.textDirection),
   1111      fontKerning(aOther.fontKerning),
   1112      fontStretch(aOther.fontStretch),
   1113      fontVariantCaps(aOther.fontVariantCaps),
   1114      textRendering(aOther.textRendering),
   1115      letterSpacing(aOther.letterSpacing),
   1116      wordSpacing(aOther.wordSpacing),
   1117      letterSpacingStr(aOther.letterSpacingStr),
   1118      wordSpacingStr(aOther.wordSpacingStr),
   1119      shadowColor(aOther.shadowColor),
   1120      transform(aOther.transform),
   1121      shadowOffset(aOther.shadowOffset),
   1122      lineWidth(aOther.lineWidth),
   1123      miterLimit(aOther.miterLimit),
   1124      globalAlpha(aOther.globalAlpha),
   1125      shadowBlur(aOther.shadowBlur),
   1126      dash(aOther.dash.Clone()),
   1127      dashOffset(aOther.dashOffset),
   1128      op(aOther.op),
   1129      fillRule(aOther.fillRule),
   1130      lineCap(aOther.lineCap),
   1131      lineJoin(aOther.lineJoin),
   1132      filterString(aOther.filterString),
   1133      filterChain(aOther.filterChain),
   1134      autoSVGFiltersObserver(aOther.autoSVGFiltersObserver),
   1135      filter(aOther.filter),
   1136      filterAdditionalImages(aOther.filterAdditionalImages.Clone()),
   1137      filterSourceGraphicTainted(aOther.filterSourceGraphicTainted),
   1138      imageSmoothingEnabled(aOther.imageSmoothingEnabled) {}
   1139 
   1140 CanvasRenderingContext2D::ContextState::~ContextState() = default;
   1141 
   1142 void CanvasRenderingContext2D::ContextState::SetColorStyle(Style aWhichStyle,
   1143                                                           nscolor aColor) {
   1144  colorStyles[aWhichStyle] = aColor;
   1145  gradientStyles[aWhichStyle] = nullptr;
   1146  patternStyles[aWhichStyle] = nullptr;
   1147 }
   1148 
   1149 void CanvasRenderingContext2D::ContextState::SetPatternStyle(
   1150    Style aWhichStyle, CanvasPattern* aPat) {
   1151  gradientStyles[aWhichStyle] = nullptr;
   1152  patternStyles[aWhichStyle] = aPat;
   1153 }
   1154 
   1155 void CanvasRenderingContext2D::ContextState::SetGradientStyle(
   1156    Style aWhichStyle, CanvasGradient* aGrad) {
   1157  gradientStyles[aWhichStyle] = aGrad;
   1158  patternStyles[aWhichStyle] = nullptr;
   1159 }
   1160 
   1161 /**
   1162 ** CanvasRenderingContext2D impl
   1163 **/
   1164 
   1165 // Initialize our static variables.
   1166 MOZ_THREAD_LOCAL(uintptr_t) CanvasRenderingContext2D::sNumLivingContexts;
   1167 MOZ_THREAD_LOCAL(DrawTarget*) CanvasRenderingContext2D::sErrorTarget;
   1168 
   1169 // Helpers to map Canvas2D WebIDL enum values to gfx constants for rendering.
   1170 static JoinStyle CanvasToGfx(CanvasLineJoin aJoin) {
   1171  switch (aJoin) {
   1172    case CanvasLineJoin::Round:
   1173      return JoinStyle::ROUND;
   1174    case CanvasLineJoin::Bevel:
   1175      return JoinStyle::BEVEL;
   1176    case CanvasLineJoin::Miter:
   1177      return JoinStyle::MITER_OR_BEVEL;
   1178    default:
   1179      MOZ_CRASH("unknown lineJoin!");
   1180  }
   1181 }
   1182 
   1183 static CapStyle CanvasToGfx(CanvasLineCap aCap) {
   1184  switch (aCap) {
   1185    case CanvasLineCap::Butt:
   1186      return CapStyle::BUTT;
   1187    case CanvasLineCap::Round:
   1188      return CapStyle::ROUND;
   1189    case CanvasLineCap::Square:
   1190      return CapStyle::SQUARE;
   1191    default:
   1192      MOZ_CRASH("unknown lineCap!");
   1193  }
   1194 }
   1195 
   1196 static uint8_t CanvasToGfx(CanvasFontKerning aKerning) {
   1197  switch (aKerning) {
   1198    case CanvasFontKerning::Auto:
   1199      return NS_FONT_KERNING_AUTO;
   1200    case CanvasFontKerning::Normal:
   1201      return NS_FONT_KERNING_NORMAL;
   1202    case CanvasFontKerning::None:
   1203      return NS_FONT_KERNING_NONE;
   1204    default:
   1205      MOZ_CRASH("unknown kerning!");
   1206  }
   1207 }
   1208 
   1209 CanvasRenderingContext2D::CanvasRenderingContext2D(
   1210    layers::LayersBackend aCompositorBackend)
   1211    :  // these are the default values from the Canvas spec
   1212      mWidth(0),
   1213      mHeight(0),
   1214      mZero(false),
   1215      mOpaqueAttrValue(false),
   1216      mContextAttributesHasAlpha(true),
   1217      mOpaque(false),
   1218      mResetLayer(true),
   1219      mIPC(false),
   1220      mHasPendingStableStateCallback(false),
   1221      mIsEntireFrameInvalid(false),
   1222      mPredictManyRedrawCalls(false),
   1223      mFrameCaptureState(FrameCaptureState::CLEAN,
   1224                         "CanvasRenderingContext2D::mFrameCaptureState"),
   1225      mInvalidateCount(0),
   1226      mWriteOnly(false) {
   1227  sNumLivingContexts.infallibleInit();
   1228  sErrorTarget.infallibleInit();
   1229  sNumLivingContexts.set(sNumLivingContexts.get() + 1);
   1230 }
   1231 
   1232 CanvasRenderingContext2D::~CanvasRenderingContext2D() {
   1233  CanvasImageCache::NotifyCanvasDestroyed(this);
   1234  RemovePostRefreshObserver();
   1235  RemoveShutdownObserver();
   1236  ResetBitmap();
   1237 
   1238  sNumLivingContexts.set(sNumLivingContexts.get() - 1);
   1239  if (sNumLivingContexts.get() == 0 && sErrorTarget.get()) {
   1240    RefPtr<DrawTarget> target = dont_AddRef(sErrorTarget.get());
   1241    sErrorTarget.set(nullptr);
   1242  }
   1243 }
   1244 
   1245 nsresult CanvasRenderingContext2D::Initialize() {
   1246  if (NS_WARN_IF(!AddShutdownObserver())) {
   1247    return NS_ERROR_FAILURE;
   1248  }
   1249 
   1250  return NS_OK;
   1251 }
   1252 
   1253 JSObject* CanvasRenderingContext2D::WrapObject(
   1254    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   1255  return CanvasRenderingContext2D_Binding::Wrap(aCx, this, aGivenProto);
   1256 }
   1257 
   1258 void CanvasRenderingContext2D::GetContextAttributes(
   1259    CanvasRenderingContext2DSettings& aSettings) const {
   1260  aSettings = CanvasRenderingContext2DSettings();
   1261 
   1262  aSettings.mAlpha = mContextAttributesHasAlpha;
   1263  aSettings.mWillReadFrequently = mWillReadFrequently;
   1264  aSettings.mForceSoftwareRendering = mForceSoftwareRendering;
   1265 
   1266  // We don't support the 'desynchronized' and 'colorSpace' attributes, so
   1267  // those just keep their default values.
   1268 }
   1269 
   1270 void CanvasRenderingContext2D::GetDebugInfo(
   1271    bool aEnsureTarget, CanvasRenderingContext2DDebugInfo& aDebugInfo,
   1272    ErrorResult& aError) {
   1273  if (aEnsureTarget && !EnsureTarget(aError)) {
   1274    return;
   1275  }
   1276 
   1277  if (!mBufferProvider) {
   1278    aError.ThrowInvalidStateError("No buffer provider available");
   1279    return;
   1280  }
   1281 
   1282  if (!mTarget) {
   1283    aError.ThrowInvalidStateError("No target available");
   1284    return;
   1285  }
   1286 
   1287  aDebugInfo.mIsAccelerated = mBufferProvider->IsAccelerated();
   1288  aDebugInfo.mIsShared = mBufferProvider->IsShared();
   1289  aDebugInfo.mBackendType = static_cast<int8_t>(mTarget->GetBackendType());
   1290  aDebugInfo.mDrawTargetType = static_cast<int8_t>(mTarget->GetType());
   1291 }
   1292 
   1293 CanvasRenderingContext2D::ColorStyleCacheEntry
   1294 CanvasRenderingContext2D::ParseColorSlow(const nsACString& aString) {
   1295  ColorStyleCacheEntry result{nsCString(aString)};
   1296  Document* document = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
   1297  css::Loader* loader = document ? document->GetExistingCSSLoader() : nullptr;
   1298 
   1299  PresShell* presShell = GetPresShell();
   1300  ServoStyleSet* set = presShell ? presShell->StyleSet() : nullptr;
   1301  const StylePerDocumentStyleData* data = set ? set->RawData() : nullptr;
   1302  bool wasCurrentColor = false;
   1303  nscolor color;
   1304  if (ServoCSSParser::ComputeColor(data, NS_RGB(0, 0, 0), aString, &color,
   1305                                   &wasCurrentColor, loader)) {
   1306    result.mWasCurrentColor = wasCurrentColor;
   1307    result.mColor.emplace(color);
   1308  }
   1309 
   1310  return result;
   1311 }
   1312 
   1313 Maybe<nscolor> CanvasRenderingContext2D::ParseColor(
   1314    const nsACString& aString, ResolveCurrentColor aResolveCurrentColor) {
   1315  auto entry = mColorStyleCache.Lookup(aString);
   1316  if (!entry) {
   1317    entry.Set(ParseColorSlow(aString));
   1318  }
   1319 
   1320  const auto& data = entry.Data();
   1321  if (data.mWasCurrentColor && mCanvasElement &&
   1322      aResolveCurrentColor == ResolveCurrentColor::Yes) {
   1323    // If it was currentColor, get the value of the color property, flushing
   1324    // style if necessary.
   1325    RefPtr<const ComputedStyle> canvasStyle =
   1326        nsComputedDOMStyle::GetComputedStyle(mCanvasElement);
   1327    if (canvasStyle) {
   1328      return Some(canvasStyle->StyleText()->mColor.ToColor());
   1329    }
   1330  }
   1331  return data.mColor;
   1332 }
   1333 
   1334 void CanvasRenderingContext2D::ResetBitmap(bool aFreeBuffer) {
   1335  if (mCanvasElement) {
   1336    mCanvasElement->InvalidateCanvas();
   1337  }
   1338 
   1339  // only do this for non-docshell created contexts,
   1340  // since those are the ones that we created a surface for
   1341  if (mTarget && IsTargetValid() && !mDocShell) {
   1342    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
   1343  }
   1344 
   1345  bool forceReset = true;
   1346  ReturnTarget(forceReset);
   1347  mTarget = nullptr;
   1348  if (aFreeBuffer) {
   1349    mBufferProvider = nullptr;
   1350  } else if (mBufferProvider) {
   1351    // Try to keep the buffer around. However, we still need to clear the
   1352    // contents as if it was recreated before next use.
   1353    mBufferNeedsClear = true;
   1354  }
   1355 
   1356  // Since the target changes the backing texture will change, and this will
   1357  // no longer be valid.
   1358  mIsEntireFrameInvalid = false;
   1359  mPredictManyRedrawCalls = false;
   1360  mFrameCaptureState = FrameCaptureState::CLEAN;
   1361 }
   1362 
   1363 void CanvasRenderingContext2D::OnShutdown() {
   1364  RefPtr<PersistentBufferProvider> provider = mBufferProvider;
   1365 
   1366  ResetBitmap();
   1367 
   1368  if (provider) {
   1369    provider->OnShutdown();
   1370  }
   1371 
   1372  if (mOffscreenCanvas) {
   1373    mOffscreenCanvas->Destroy();
   1374  }
   1375 
   1376  mHasShutdown = true;
   1377 }
   1378 
   1379 bool CanvasRenderingContext2D::AddShutdownObserver() {
   1380  auto* const canvasManager = CanvasShutdownManager::Get();
   1381  if (NS_WARN_IF(!canvasManager)) {
   1382    mHasShutdown = true;
   1383    return false;
   1384  }
   1385 
   1386  canvasManager->AddShutdownObserver(this);
   1387  return true;
   1388 }
   1389 
   1390 void CanvasRenderingContext2D::RemoveShutdownObserver() {
   1391  auto* const canvasManager = CanvasShutdownManager::MaybeGet();
   1392  if (!canvasManager) {
   1393    return;
   1394  }
   1395 
   1396  canvasManager->RemoveShutdownObserver(this);
   1397 }
   1398 
   1399 void CanvasRenderingContext2D::OnRemoteCanvasLost() {
   1400  // We only lose context / data if we are using remote canvas, which is only
   1401  // for accelerated targets.
   1402  if (!mBufferProvider || !mBufferProvider->IsAccelerated() || mIsContextLost) {
   1403    return;
   1404  }
   1405 
   1406  // 2. Set context's context lost to true.
   1407  mIsContextLost = mAllowContextRestore = true;
   1408 
   1409  // 3. Reset the rendering context to its default state given context.
   1410  ClearTarget();
   1411 
   1412  // We dispatch because it isn't safe to call into the script event handlers,
   1413  // and we don't want to mutate our state in CanvasShutdownManager.
   1414  NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction(
   1415      "CanvasRenderingContext2D::OnRemoteCanvasLost", [self = RefPtr{this}] {
   1416        // 4. Let shouldRestore be the result of firing an event named
   1417        // contextlost at canvas, with the cancelable attribute initialized to
   1418        // true.
   1419        self->mAllowContextRestore = self->DispatchEvent(
   1420            u"contextlost"_ns, CanBubble::eNo, Cancelable::eYes);
   1421        gfxCriticalNote << gfx::hexa(self.get())
   1422                        << " accel canvas lost, can restore: "
   1423                        << self->mAllowContextRestore;
   1424      }));
   1425 }
   1426 
   1427 void CanvasRenderingContext2D::OnRemoteCanvasRestored() {
   1428  // We never lost our context if it was not a remote canvas, nor can we restore
   1429  // if we have already shutdown.
   1430  if (mHasShutdown || !mIsContextLost || !mAllowContextRestore) {
   1431    return;
   1432  }
   1433 
   1434  // We dispatch because it isn't safe to call into the script event handlers,
   1435  // and we don't want to mutate our state in CanvasShutdownManager.
   1436  NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction(
   1437      "CanvasRenderingContext2D::OnRemoteCanvasRestored",
   1438      [self = RefPtr{this}] {
   1439        // 5. If shouldRestore is false, then abort these steps.
   1440        if (!self->mHasShutdown && self->mIsContextLost &&
   1441            self->mAllowContextRestore) {
   1442          // 7. Set context's context lost to false.
   1443          self->mIsContextLost = false;
   1444 
   1445          // 6. Attempt to restore context by creating a backing storage using
   1446          // context's attributes and associating them with context. If this
   1447          // fails, then abort these steps.
   1448          if (!self->EnsureTarget()) {
   1449            gfxCriticalNote << gfx::hexa(self.get())
   1450                            << " accel canvas failed to restore";
   1451            self->mIsContextLost = true;
   1452            return;
   1453          }
   1454 
   1455          // 8. Fire an event named contextrestored at canvas.
   1456          self->DispatchEvent(u"contextrestored"_ns, CanBubble::eNo,
   1457                              Cancelable::eNo);
   1458          gfxCriticalNote << gfx::hexa(self.get()) << " accel canvas restored";
   1459        }
   1460      }));
   1461 }
   1462 
   1463 void CanvasRenderingContext2D::SetStyleFromString(const nsACString& aStr,
   1464                                                  Style aWhichStyle) {
   1465  MOZ_ASSERT(!aStr.IsVoid());
   1466 
   1467  Maybe<nscolor> color = ParseColor(aStr);
   1468  if (!color) {
   1469    return;
   1470  }
   1471 
   1472  CurrentState().SetColorStyle(aWhichStyle, *color);
   1473 }
   1474 
   1475 void CanvasRenderingContext2D::GetStyleAsUnion(
   1476    OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue,
   1477    Style aWhichStyle) {
   1478  const ContextState& state = CurrentState();
   1479  if (state.patternStyles[aWhichStyle]) {
   1480    aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
   1481  } else if (state.gradientStyles[aWhichStyle]) {
   1482    aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
   1483  } else {
   1484    StyleColorToString(state.colorStyles[aWhichStyle],
   1485                       aValue.SetAsUTF8String());
   1486  }
   1487 }
   1488 
   1489 // static
   1490 void CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor,
   1491                                                  nsACString& aStr) {
   1492  aStr.Truncate();
   1493  // We can't reuse the normal CSS color stringification code,
   1494  // because the spec calls for a different algorithm for canvas.
   1495  if (NS_GET_A(aColor) == 255) {
   1496    aStr.AppendPrintf("#%02x%02x%02x", NS_GET_R(aColor), NS_GET_G(aColor),
   1497                      NS_GET_B(aColor));
   1498  } else {
   1499    aStr.AppendPrintf("rgba(%d, %d, %d, ", NS_GET_R(aColor), NS_GET_G(aColor),
   1500                      NS_GET_B(aColor));
   1501    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
   1502    aStr.Append(')');
   1503  }
   1504 }
   1505 
   1506 nsresult CanvasRenderingContext2D::Redraw() {
   1507  mFrameCaptureState = FrameCaptureState::DIRTY;
   1508 
   1509  if (mIsEntireFrameInvalid) {
   1510    return NS_OK;
   1511  }
   1512 
   1513  mIsEntireFrameInvalid = true;
   1514 
   1515  if (mCanvasElement) {
   1516    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
   1517    mCanvasElement->InvalidateCanvasContent(nullptr);
   1518  } else if (mOffscreenCanvas) {
   1519    mOffscreenCanvas->QueueCommitToCompositor();
   1520  } else {
   1521    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
   1522  }
   1523 
   1524  return NS_OK;
   1525 }
   1526 
   1527 void CanvasRenderingContext2D::Redraw(const gfx::Rect& aR) {
   1528  mFrameCaptureState = FrameCaptureState::DIRTY;
   1529 
   1530  ++mInvalidateCount;
   1531 
   1532  if (mIsEntireFrameInvalid) {
   1533    return;
   1534  }
   1535 
   1536  if (mPredictManyRedrawCalls || mInvalidateCount > kCanvasMaxInvalidateCount) {
   1537    Redraw();
   1538    return;
   1539  }
   1540 
   1541  if (mCanvasElement) {
   1542    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
   1543    mCanvasElement->InvalidateCanvasContent(&aR);
   1544  } else if (mOffscreenCanvas) {
   1545    mOffscreenCanvas->QueueCommitToCompositor();
   1546  } else {
   1547    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
   1548  }
   1549 }
   1550 
   1551 void CanvasRenderingContext2D::DidRefresh() {}
   1552 
   1553 void CanvasRenderingContext2D::RedrawUser(const gfxRect& aR) {
   1554  mFrameCaptureState = FrameCaptureState::DIRTY;
   1555 
   1556  if (mIsEntireFrameInvalid) {
   1557    ++mInvalidateCount;
   1558    return;
   1559  }
   1560 
   1561  gfx::Rect newr = mTarget->GetTransform().TransformBounds(ToRect(aR));
   1562  Redraw(newr);
   1563 }
   1564 
   1565 bool CanvasRenderingContext2D::CopyBufferProvider(
   1566    PersistentBufferProvider& aOld, DrawTarget& aTarget, IntRect aCopyRect) {
   1567  // Borrowing the snapshot must be done after ReturnTarget.
   1568  RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();
   1569 
   1570  if (!snapshot) {
   1571    return false;
   1572  }
   1573 
   1574  aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
   1575 
   1576  aOld.ReturnSnapshot(snapshot.forget());
   1577  return true;
   1578 }
   1579 
   1580 void CanvasRenderingContext2D::Demote() {}
   1581 
   1582 void CanvasRenderingContext2D::ScheduleStableStateCallback() {
   1583  if (mHasPendingStableStateCallback) {
   1584    return;
   1585  }
   1586  mHasPendingStableStateCallback = true;
   1587 
   1588  nsContentUtils::RunInStableState(
   1589      NewRunnableMethod("dom::CanvasRenderingContext2D::OnStableState", this,
   1590                        &CanvasRenderingContext2D::OnStableState));
   1591 }
   1592 
   1593 void CanvasRenderingContext2D::OnStableState() {
   1594  if (!mHasPendingStableStateCallback) {
   1595    return;
   1596  }
   1597 
   1598  ReturnTarget();
   1599 
   1600  mHasPendingStableStateCallback = false;
   1601 }
   1602 
   1603 void CanvasRenderingContext2D::RestoreClipsAndTransformToTarget() {
   1604  // Restore clips and transform.
   1605  mTarget->SetTransform(Matrix());
   1606 
   1607  if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
   1608    // Cairo doesn't play well with huge clips. When given a very big clip it
   1609    // will try to allocate big mask surface without taking the target
   1610    // size into account which can cause OOM. See bug 1034593.
   1611    // This limits the clip extents to the size of the canvas.
   1612    // A fix in Cairo would probably be preferable, but requires somewhat
   1613    // invasive changes.
   1614    mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
   1615  }
   1616 
   1617  for (auto& style : mStyleStack) {
   1618    for (auto& clipOrTransform : style.clipsAndTransforms) {
   1619      if (clipOrTransform.IsClip()) {
   1620        if (clipOrTransform.clip->GetBackendType() != mPathType) {
   1621          // We have possibly changed backends, so we need to convert the clips
   1622          // in case they are no longer compatible with mTarget.
   1623          RefPtr<PathBuilder> pathBuilder = mTarget->CreatePathBuilder();
   1624          clipOrTransform.clip->StreamToSink(pathBuilder);
   1625          clipOrTransform.clip = pathBuilder->Finish();
   1626        }
   1627        mTarget->PushClip(clipOrTransform.clip);
   1628      } else {
   1629        mTarget->SetTransform(clipOrTransform.transform);
   1630      }
   1631    }
   1632  }
   1633 }
   1634 
   1635 bool CanvasRenderingContext2D::BorrowTarget(const IntRect& aPersistedRect,
   1636                                            bool aNeedsClear) {
   1637  // We are attempting to request a DrawTarget from the current
   1638  // PersistentBufferProvider. However, if the provider needs to be refreshed,
   1639  // or if it is accelerated and the application has requested that we disallow
   1640  // acceleration, then we skip trying to use this provider so that it will be
   1641  // recreated by EnsureTarget later.
   1642  if (!mBufferProvider || mBufferProvider->RequiresRefresh() ||
   1643      (mBufferProvider->IsAccelerated() && UseSoftwareRendering())) {
   1644    return false;
   1645  }
   1646  mTarget = mBufferProvider->BorrowDrawTarget(aPersistedRect);
   1647  if (!mTarget || !mTarget->IsValid()) {
   1648    if (mTarget) {
   1649      mBufferProvider->ReturnDrawTarget(mTarget.forget());
   1650    }
   1651    return false;
   1652  }
   1653  if (!mBufferProvider->PreservesDrawingState() || mBufferNeedsClear ||
   1654      mTargetNeedsClipsAndTransforms) {
   1655    if (mBufferProvider->PreservesDrawingState()) {
   1656      // If the buffer provider preserves the clip and transform state, then
   1657      // we must ensure it is cleared before reusing the target.
   1658      if (!mTarget->RemoveAllClips()) {
   1659        mBufferProvider->ReturnDrawTarget(mTarget.forget());
   1660        return false;
   1661      }
   1662      mTarget->SetTransform(Matrix());
   1663    }
   1664 
   1665    // If the canvas was reset, then we need to clear the target in case its
   1666    // contents was somehow preserved. We only need to clear the target if
   1667    // the operation doesn't fill the entire canvas.
   1668    if (mBufferNeedsClear && aNeedsClear) {
   1669      mTarget->ClearRect(gfx::Rect(mTarget->GetRect()));
   1670    }
   1671 
   1672    RestoreClipsAndTransformToTarget();
   1673 
   1674    mBufferNeedsClear = false;
   1675    mTargetNeedsClipsAndTransforms = false;
   1676  }
   1677  return true;
   1678 }
   1679 
   1680 bool CanvasRenderingContext2D::HasAnyClips() const {
   1681  for (const auto& style : mStyleStack) {
   1682    for (const auto& clipOrTransform : style.clipsAndTransforms) {
   1683      if (clipOrTransform.IsClip()) {
   1684        return true;
   1685      }
   1686    }
   1687  }
   1688  return false;
   1689 }
   1690 
   1691 bool CanvasRenderingContext2D::HasErrorState(ErrorResult& aError,
   1692                                             bool aInitProvider) {
   1693  // If there is no buffer provider, then attempt to initialize it to flush any
   1694  // error state. It also forces any backend resources (such as WebGL contexts)
   1695  // in the buffer provider to initialize as soon as possible, so that it may
   1696  // speed up initial page loads.
   1697  // It is normally beneficial to delay initialization of just the target until
   1698  // we encounter a drawing operation, since this may allow us to elide copying
   1699  // the old contents of the target to the new target if the drawing operation
   1700  // would overwrite the entire framebuffer contents.
   1701  // However, if there is no old target to copy from, there is no benefit to
   1702  // delaying it. It may incidentally delay creation of the buffer provider,
   1703  // which is an expensive operation that benefits from being scheduled as soon
   1704  // as possible. Thus, we only want to delay initialization of the target when
   1705  // a buffer provider already exists.
   1706  if (aInitProvider && !mBufferProvider && !EnsureTarget(aError)) {
   1707    return true;
   1708  }
   1709 
   1710  if (AlreadyShutDown()) {
   1711    gfxCriticalNoteOnce << "Attempt to render into a Canvas2d after shutdown.";
   1712    SetErrorState();
   1713    aError.ThrowInvalidStateError(
   1714        "Cannot use canvas after shutdown initiated.");
   1715    return true;
   1716  }
   1717 
   1718  // The spec doesn't say what to do in this case, but Chrome silently fails
   1719  // without throwing an error. We should at least throw if the canvas is
   1720  // permanently disabled.
   1721  if (NS_WARN_IF(mIsContextLost)) {
   1722    if (!mAllowContextRestore) {
   1723      aError.ThrowInvalidStateError(
   1724          "Cannot use canvas as context is lost forever.");
   1725    }
   1726    return true;
   1727  }
   1728 
   1729  if (mTarget && mTarget == sErrorTarget.get()) {
   1730    aError.ThrowInvalidStateError("Canvas is already in error state.");
   1731    return true;
   1732  }
   1733 
   1734  return false;
   1735 }
   1736 
   1737 bool CanvasRenderingContext2D::EnsureTarget(ErrorResult& aError,
   1738                                            const gfx::Rect* aCoveredRect,
   1739                                            bool aWillClear,
   1740                                            bool aSkipTransform) {
   1741  if (HasErrorState(aError, false)) {
   1742    return false;
   1743  }
   1744 
   1745  // If there is an existing target and no error state, just reuse it.
   1746  if (mTarget) {
   1747    return true;
   1748  }
   1749 
   1750  // Check that the dimensions are sane
   1751  if (mWidth > StaticPrefs::gfx_canvas_max_size() ||
   1752      mHeight > StaticPrefs::gfx_canvas_max_size()) {
   1753    SetErrorState();
   1754    aError.ThrowInvalidStateError("Canvas exceeds max size.");
   1755    return false;
   1756  }
   1757 
   1758  if (mWidth < 0 || mHeight < 0) {
   1759    SetErrorState();
   1760    aError.ThrowInvalidStateError("Canvas has invalid size.");
   1761    return false;
   1762  }
   1763 
   1764  // If the next drawing command covers the entire canvas, we can skip copying
   1765  // from the previous frame and/or clearing the canvas.
   1766  // If a clip is active we don't know for sure that the next drawing command
   1767  // will really cover the entire canvas.
   1768  gfx::Rect canvasRect(0, 0, mWidth, mHeight);
   1769  bool canDiscardContent =
   1770      aCoveredRect &&
   1771      (aSkipTransform ? *aCoveredRect
   1772                      : GetCurrentTransform().TransformBounds(*aCoveredRect))
   1773          .Contains(canvasRect) &&
   1774      !HasAnyClips();
   1775 
   1776  ScheduleStableStateCallback();
   1777 
   1778  IntRect persistedRect = canDiscardContent || mBufferNeedsClear
   1779                              ? IntRect()
   1780                              : IntRect(0, 0, mWidth, mHeight);
   1781 
   1782  // Attempt to reuse the existing buffer provider.
   1783  if (BorrowTarget(persistedRect, !canDiscardContent)) {
   1784    return true;
   1785  }
   1786 
   1787  RefPtr<DrawTarget> newTarget;
   1788  RefPtr<PersistentBufferProvider> newProvider;
   1789 
   1790  if (!TryAcceleratedTarget(newTarget, newProvider) &&
   1791      !TrySharedTarget(newTarget, newProvider) &&
   1792      !TryBasicTarget(newTarget, newProvider, aError)) {
   1793    gfxCriticalError(
   1794        CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize())))
   1795        << "Failed borrow shared and basic targets.";
   1796 
   1797    SetErrorState();
   1798    return false;
   1799  }
   1800 
   1801  MOZ_ASSERT(newTarget);
   1802  MOZ_ASSERT(newProvider);
   1803 
   1804  bool needsClear =
   1805      !canDiscardContent || (mBufferProvider && mBufferNeedsClear);
   1806  if (newTarget->GetBackendType() == gfx::BackendType::SKIA &&
   1807      (needsClear || !aWillClear)) {
   1808    // Skia expects the unused X channel to contains 0xFF even for opaque
   1809    // operations so we can't skip clearing in that case, even if we are going
   1810    // to cover the entire canvas in the next drawing operation.
   1811    newTarget->ClearRect(canvasRect);
   1812    needsClear = false;
   1813  }
   1814 
   1815  // Try to copy data from the previous buffer provider if there is one.
   1816  if (!canDiscardContent && mBufferProvider && !mBufferNeedsClear &&
   1817      CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
   1818    needsClear = false;
   1819  }
   1820 
   1821  if (needsClear) {
   1822    newTarget->ClearRect(canvasRect);
   1823  }
   1824 
   1825  // Ensure any Path state is compatible with the type of DrawTarget used. This
   1826  // may require making a copy with the correct type if they (rarely) mismatch.
   1827  mPathType = newTarget->GetPathType();
   1828  MOZ_ASSERT(mPathType != BackendType::NONE);
   1829  if (mPathBuilder && mPathBuilder->GetBackendType() != mPathType) {
   1830    RefPtr<Path> path = mPathBuilder->Finish();
   1831    mPathBuilder = newTarget->CreatePathBuilder(path->GetFillRule());
   1832    path->StreamToSink(mPathBuilder);
   1833  }
   1834  if (mPath && mPath->GetBackendType() != mPathType) {
   1835    RefPtr<PathBuilder> builder =
   1836        newTarget->CreatePathBuilder(mPath->GetFillRule());
   1837    mPath->StreamToSink(builder);
   1838    mPath = builder->Finish();
   1839  }
   1840 
   1841  mTarget = std::move(newTarget);
   1842  mBufferProvider = std::move(newProvider);
   1843  mBufferNeedsClear = false;
   1844  mTargetNeedsClipsAndTransforms = false;
   1845 
   1846  RegisterAllocation();
   1847  AddZoneWaitingForGC();
   1848 
   1849  RestoreClipsAndTransformToTarget();
   1850 
   1851  // Force a full layer transaction since we didn't have a layer before
   1852  // and now we might need one.
   1853  if (mCanvasElement) {
   1854    mCanvasElement->InvalidateCanvas();
   1855  }
   1856  // EnsureTarget hasn't drawn anything. Preserve mFrameCaptureState.
   1857  FrameCaptureState captureState = mFrameCaptureState;
   1858  // Calling Redraw() tells our invalidation machinery that the entire
   1859  // canvas is already invalid, which can speed up future drawing.
   1860  Redraw();
   1861  mFrameCaptureState = captureState;
   1862 
   1863  return true;
   1864 }
   1865 
   1866 void CanvasRenderingContext2D::SetInitialState() {
   1867  // Set up the initial canvas defaults
   1868  mPathBuilder = nullptr;
   1869  mPath = nullptr;
   1870  mPathPruned = false;
   1871  mPathTransform = Matrix();
   1872  mPathTransformDirty = false;
   1873  mPathType =
   1874      (mTarget ? mTarget : gfxPlatform::ThreadLocalScreenReferenceDrawTarget())
   1875          ->GetPathType();
   1876  MOZ_ASSERT(mPathType != BackendType::NONE);
   1877 
   1878  mStyleStack.Clear();
   1879  ContextState* state = mStyleStack.AppendElement();
   1880  state->globalAlpha = 1.0;
   1881 
   1882  state->colorStyles[Style::FILL] = NS_RGB(0, 0, 0);
   1883  state->colorStyles[Style::STROKE] = NS_RGB(0, 0, 0);
   1884  state->shadowColor = NS_RGBA(0, 0, 0, 0);
   1885 }
   1886 
   1887 void CanvasRenderingContext2D::SetErrorState() {
   1888  EnsureErrorTarget();
   1889 
   1890  if (mTarget && mTarget != sErrorTarget.get()) {
   1891    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
   1892  }
   1893 
   1894  mTarget = sErrorTarget.get();
   1895  mBufferProvider = nullptr;
   1896 
   1897  // clear transforms, clips, etc.
   1898  SetInitialState();
   1899 }
   1900 
   1901 void CanvasRenderingContext2D::RegisterAllocation() {
   1902  // XXX - It would make more sense to track the allocation in
   1903  // PeristentBufferProvider, rather than here.
   1904  static bool registered = false;
   1905  // FIXME: Disable the reporter for now, see bug 1241865
   1906  if (!registered && false) {
   1907    registered = true;
   1908    RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
   1909  }
   1910 }
   1911 
   1912 void CanvasRenderingContext2D::AddZoneWaitingForGC() {
   1913  JSObject* wrapper = GetWrapperPreserveColor();
   1914  if (wrapper) {
   1915    CycleCollectedJSRuntime::Get()->AddZoneWaitingForGC(
   1916        JS::GetObjectZone(wrapper));
   1917  }
   1918 }
   1919 
   1920 static WindowRenderer* WindowRendererFromCanvasElement(
   1921    nsINode* aCanvasElement) {
   1922  if (!aCanvasElement) {
   1923    return nullptr;
   1924  }
   1925 
   1926  return nsContentUtils::WindowRendererForDocument(aCanvasElement->OwnerDoc());
   1927 }
   1928 
   1929 bool CanvasRenderingContext2D::TryAcceleratedTarget(
   1930    RefPtr<gfx::DrawTarget>& aOutDT,
   1931    RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
   1932  if (!XRE_IsContentProcess()) {
   1933    // Only allow accelerated contexts to be created in a content process to
   1934    // ensure it is remoted appropriately and run on the correct parent or
   1935    // GPU process threads.
   1936    return false;
   1937  }
   1938  if (mBufferProvider && mBufferProvider->IsAccelerated() &&
   1939      mBufferProvider->RequiresRefresh()) {
   1940    // If there is already a provider and we got here, then the provider needs
   1941    // to be refreshed and we should avoid using acceleration in the future.
   1942    mAllowAcceleration = false;
   1943  }
   1944  // Don't try creating an accelerate DrawTarget if either acceleration failed
   1945  // previously or if the application expects acceleration to be slow.
   1946  if (!mAllowAcceleration || UseSoftwareRendering()) {
   1947    return false;
   1948  }
   1949 
   1950  if (mCanvasElement) {
   1951    MOZ_ASSERT(NS_IsMainThread());
   1952 
   1953    WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement);
   1954    if (NS_WARN_IF(!renderer)) {
   1955      return false;
   1956    }
   1957 
   1958    aOutProvider = PersistentBufferProviderAccelerated::Create(
   1959        GetSize(), GetSurfaceFormat(), renderer->AsKnowsCompositor());
   1960  } else if (mOffscreenCanvas &&
   1961             StaticPrefs::gfx_canvas_remote_allow_offscreen()) {
   1962    RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
   1963    if (NS_WARN_IF(!imageBridge)) {
   1964      return false;
   1965    }
   1966 
   1967    aOutProvider = PersistentBufferProviderAccelerated::Create(
   1968        GetSize(), GetSurfaceFormat(), imageBridge);
   1969  }
   1970 
   1971  if (!aOutProvider) {
   1972    return false;
   1973  }
   1974  aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
   1975  MOZ_ASSERT(aOutDT);
   1976  return !!aOutDT;
   1977 }
   1978 
   1979 bool CanvasRenderingContext2D::TrySharedTarget(
   1980    RefPtr<gfx::DrawTarget>& aOutDT,
   1981    RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
   1982  aOutDT = nullptr;
   1983  aOutProvider = nullptr;
   1984 
   1985  if (mBufferProvider && mBufferProvider->IsShared()) {
   1986    // we are already using a shared buffer provider, we are allocating a new
   1987    // one because the current one failed so let's just fall back to the basic
   1988    // provider.
   1989    return false;
   1990  }
   1991 
   1992  if (mCanvasElement) {
   1993    MOZ_ASSERT(NS_IsMainThread());
   1994 
   1995    WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement);
   1996    if (NS_WARN_IF(!renderer)) {
   1997      return false;
   1998    }
   1999 
   2000    aOutProvider = renderer->CreatePersistentBufferProvider(
   2001        GetSize(), GetSurfaceFormat(),
   2002        !mAllowAcceleration || UseSoftwareRendering());
   2003  } else if (mOffscreenCanvas) {
   2004    if (!StaticPrefs::gfx_offscreencanvas_shared_provider()) {
   2005      return false;
   2006    }
   2007 
   2008    RefPtr<layers::ImageBridgeChild> imageBridge =
   2009        layers::ImageBridgeChild::GetSingleton();
   2010    if (NS_WARN_IF(!imageBridge)) {
   2011      return false;
   2012    }
   2013 
   2014    aOutProvider = PersistentBufferProviderShared::Create(
   2015        GetSize(), GetSurfaceFormat(), imageBridge,
   2016        !mAllowAcceleration || UseSoftwareRendering(),
   2017        mOffscreenCanvas->GetWindowID());
   2018  }
   2019 
   2020  if (!aOutProvider) {
   2021    return false;
   2022  }
   2023 
   2024  // We can pass an empty persisted rect since we just created the buffer
   2025  // provider (nothing to restore).
   2026  aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
   2027  MOZ_ASSERT(aOutDT);
   2028 
   2029  return !!aOutDT;
   2030 }
   2031 
   2032 bool CanvasRenderingContext2D::TryBasicTarget(
   2033    RefPtr<gfx::DrawTarget>& aOutDT,
   2034    RefPtr<layers::PersistentBufferProvider>& aOutProvider,
   2035    ErrorResult& aError) {
   2036  aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(
   2037      GetSize(), GetSurfaceFormat(), UseSoftwareRendering());
   2038  if (!aOutDT) {
   2039    aError.ThrowInvalidStateError("Canvas could not create basic draw target.");
   2040    return false;
   2041  }
   2042 
   2043  // See Bug 1524554 - this forces DT initialization.
   2044  aOutDT->ClearRect(gfx::Rect());
   2045 
   2046  if (!aOutDT->IsValid()) {
   2047    aOutDT = nullptr;
   2048    aError.ThrowInvalidStateError("Canvas could not init basic draw target.");
   2049    return false;
   2050  }
   2051 
   2052  aOutProvider = new PersistentBufferProviderBasic(aOutDT);
   2053  return true;
   2054 }
   2055 
   2056 PersistentBufferProvider* CanvasRenderingContext2D::GetBufferProvider() {
   2057  if (mBufferProvider && mBufferNeedsClear) {
   2058    // Force the buffer to clear before it is used.
   2059    EnsureTarget();
   2060  }
   2061  return mBufferProvider;
   2062 }
   2063 
   2064 Maybe<SurfaceDescriptor> CanvasRenderingContext2D::GetFrontBuffer(
   2065    WebGLFramebufferJS*, const bool webvr) {
   2066  if (auto* provider = GetBufferProvider()) {
   2067    return provider->GetFrontBuffer();
   2068  }
   2069  return Nothing();
   2070 }
   2071 
   2072 already_AddRefed<layers::FwdTransactionTracker>
   2073 CanvasRenderingContext2D::UseCompositableForwarder(
   2074    layers::CompositableForwarder* aForwarder) {
   2075  if (mBufferProvider) {
   2076    return mBufferProvider->UseCompositableForwarder(aForwarder);
   2077  }
   2078  return nullptr;
   2079 }
   2080 
   2081 PresShell* CanvasRenderingContext2D::GetPresShell() {
   2082  if (mCanvasElement) {
   2083    return mCanvasElement->OwnerDoc()->GetPresShell();
   2084  }
   2085  if (mDocShell) {
   2086    return mDocShell->GetPresShell();
   2087  }
   2088  return nullptr;
   2089 }
   2090 
   2091 NS_IMETHODIMP
   2092 CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight) {
   2093  // Zero sized surfaces can cause problems.
   2094  mZero = false;
   2095  if (aHeight == 0) {
   2096    aHeight = 1;
   2097    mZero = true;
   2098  }
   2099  if (aWidth == 0) {
   2100    aWidth = 1;
   2101    mZero = true;
   2102  }
   2103 
   2104  ClearTarget(aWidth, aHeight);
   2105 
   2106  return NS_OK;
   2107 }
   2108 
   2109 void CanvasRenderingContext2D::AddAssociatedMemory() {
   2110  JSObject* wrapper = GetWrapperMaybeDead();
   2111  if (wrapper) {
   2112    JS::AddAssociatedMemory(wrapper, BindingJSObjectMallocBytes(this),
   2113                            JS::MemoryUse::DOMBinding);
   2114  }
   2115 }
   2116 
   2117 void CanvasRenderingContext2D::RemoveAssociatedMemory() {
   2118  JSObject* wrapper = GetWrapperMaybeDead();
   2119  if (wrapper) {
   2120    JS::RemoveAssociatedMemory(wrapper, BindingJSObjectMallocBytes(this),
   2121                               JS::MemoryUse::DOMBinding);
   2122  }
   2123 }
   2124 
   2125 void CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight) {
   2126  // Only free the buffer provider if the size no longer matches.
   2127  bool freeBuffer = aWidth != mWidth || aHeight != mHeight;
   2128  ResetBitmap(freeBuffer);
   2129 
   2130  mResetLayer = true;
   2131 
   2132  SetInitialState();
   2133 
   2134  // Update dimensions only if new (strictly positive) values were passed.
   2135  if (aWidth > 0 && aHeight > 0) {
   2136    // Update the memory size associated with the wrapper object when we change
   2137    // the dimensions. Note that we need to keep updating dying wrappers before
   2138    // they are finalized so that the memory accounting balances out.
   2139    RemoveAssociatedMemory();
   2140    mWidth = aWidth;
   2141    mHeight = aHeight;
   2142    AddAssociatedMemory();
   2143  }
   2144 
   2145  if (mOffscreenCanvas) {
   2146    OffscreenCanvasDisplayData data;
   2147    data.mSize = {mWidth, mHeight};
   2148    data.mIsOpaque = mOpaque;
   2149    data.mIsAlphaPremult = true;
   2150    data.mDoPaintCallbacks = true;
   2151    mOffscreenCanvas->UpdateDisplayData(data);
   2152  }
   2153 
   2154  if (!mCanvasElement || !mCanvasElement->IsInComposedDoc()) {
   2155    return;
   2156  }
   2157 
   2158  // For vertical writing-mode, unless text-orientation is sideways,
   2159  // we'll modify the initial value of textBaseline to 'middle'.
   2160  RefPtr<const ComputedStyle> canvasStyle =
   2161      nsComputedDOMStyle::GetComputedStyle(mCanvasElement);
   2162  if (canvasStyle) {
   2163    WritingMode wm(canvasStyle);
   2164    if (wm.IsVertical() && !wm.IsSideways()) {
   2165      CurrentState().textBaseline = CanvasTextBaseline::Middle;
   2166    }
   2167  }
   2168 }
   2169 
   2170 void CanvasRenderingContext2D::ReturnTarget(bool aForceReset) {
   2171  if (mTarget && mBufferProvider && mTarget != sErrorTarget.get()) {
   2172    CurrentState().transform = mTarget->GetTransform();
   2173    if (aForceReset || !mBufferProvider->PreservesDrawingState()) {
   2174      for (const auto& style : mStyleStack) {
   2175        for (const auto& clipOrTransform : style.clipsAndTransforms) {
   2176          if (clipOrTransform.IsClip()) {
   2177            mTarget->PopClip();
   2178          }
   2179        }
   2180      }
   2181 
   2182      if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
   2183        // With the cairo backend we pushed an extra clip rect which we have to
   2184        // balance out here. See the comment in
   2185        // RestoreClipsAndTransformToTarget.
   2186        mTarget->PopClip();
   2187      }
   2188 
   2189      mTarget->SetTransform(Matrix());
   2190    }
   2191 
   2192    mBufferProvider->ReturnDrawTarget(mTarget.forget());
   2193  }
   2194 }
   2195 
   2196 NS_IMETHODIMP
   2197 CanvasRenderingContext2D::InitializeWithDrawTarget(
   2198    nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) {
   2199  if (NS_WARN_IF(!AddShutdownObserver())) {
   2200    return NS_ERROR_FAILURE;
   2201  }
   2202 
   2203  RemovePostRefreshObserver();
   2204  mDocShell = aShell;
   2205  AddPostRefreshObserverIfNecessary();
   2206 
   2207  IntSize size = aTarget->GetSize();
   2208  SetDimensions(size.width, size.height);
   2209 
   2210  mTarget = aTarget;
   2211  mBufferProvider = new PersistentBufferProviderBasic(aTarget);
   2212 
   2213  RestoreClipsAndTransformToTarget();
   2214 
   2215  return NS_OK;
   2216 }
   2217 
   2218 void CanvasRenderingContext2D::SetOpaqueValueFromOpaqueAttr(
   2219    bool aOpaqueAttrValue) {
   2220  if (aOpaqueAttrValue != mOpaqueAttrValue) {
   2221    mOpaqueAttrValue = aOpaqueAttrValue;
   2222    UpdateIsOpaque();
   2223  }
   2224 }
   2225 
   2226 void CanvasRenderingContext2D::UpdateIsOpaque() {
   2227  mOpaque = !mContextAttributesHasAlpha || mOpaqueAttrValue;
   2228  ClearTarget();
   2229 }
   2230 
   2231 NS_IMETHODIMP
   2232 CanvasRenderingContext2D::SetContextOptions(JSContext* aCx,
   2233                                            JS::Handle<JS::Value> aOptions,
   2234                                            ErrorResult& aRvForDictionaryInit) {
   2235  if (aOptions.isNullOrUndefined()) {
   2236    return NS_OK;
   2237  }
   2238 
   2239  // This shouldn't be called before drawing starts, so there should be no
   2240  // drawtarget yet
   2241  MOZ_ASSERT(!mTarget);
   2242 
   2243  CanvasRenderingContext2DSettings attributes;
   2244  if (!attributes.Init(aCx, aOptions)) {
   2245    aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
   2246    return NS_ERROR_UNEXPECTED;
   2247  }
   2248 
   2249  mWillReadFrequently = attributes.mWillReadFrequently;
   2250  mForceSoftwareRendering = attributes.mForceSoftwareRendering;
   2251 
   2252  mContextAttributesHasAlpha = attributes.mAlpha;
   2253  UpdateIsOpaque();
   2254 
   2255  return NS_OK;
   2256 }
   2257 
   2258 UniquePtr<uint8_t[]> CanvasRenderingContext2D::GetImageBuffer(
   2259    mozilla::CanvasUtils::ImageExtraction aExtractionBehavior,
   2260    int32_t* out_format, gfx::IntSize* out_imageSize) {
   2261  UniquePtr<uint8_t[]> ret;
   2262 
   2263  *out_format = 0;
   2264  *out_imageSize = {};
   2265 
   2266  if (!GetBufferProvider() && !EnsureTarget()) {
   2267    return nullptr;
   2268  }
   2269 
   2270  RefPtr<SourceSurface> snapshot = mBufferProvider->BorrowSnapshot();
   2271  if (snapshot) {
   2272    RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
   2273    if (data && data->GetSize() == GetSize()) {
   2274      *out_format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
   2275      *out_imageSize = data->GetSize();
   2276      ret = SurfaceToPackedBGRA(data);
   2277    }
   2278  }
   2279 
   2280  mBufferProvider->ReturnSnapshot(snapshot.forget());
   2281 
   2282  if (ret) {
   2283    nsRFPService::PotentiallyDumpImage(
   2284        PrincipalOrNull(), ret.get(), out_imageSize->width,
   2285        out_imageSize->height,
   2286        out_imageSize->width * out_imageSize->height * 4);
   2287    if (aExtractionBehavior == CanvasUtils::ImageExtraction::Randomize) {
   2288      nsRFPService::RandomizePixels(
   2289          GetCookieJarSettings(), PrincipalOrNull(), ret.get(),
   2290          out_imageSize->width, out_imageSize->height,
   2291          out_imageSize->width * out_imageSize->height * 4,
   2292          SurfaceFormat::A8R8G8B8_UINT32);
   2293    }
   2294  }
   2295 
   2296  return ret;
   2297 }
   2298 
   2299 NS_IMETHODIMP
   2300 CanvasRenderingContext2D::GetInputStream(
   2301    const char* aMimeType, const nsAString& aEncoderOptions,
   2302    mozilla::CanvasUtils::ImageExtraction aExtractionBehavior,
   2303    const nsACString& aRandomizationKey, nsIInputStream** aStream) {
   2304  nsCString enccid("@mozilla.org/image/encoder;2?type=");
   2305  enccid += aMimeType;
   2306  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
   2307  if (!encoder) {
   2308    return NS_ERROR_FAILURE;
   2309  }
   2310 
   2311  int32_t format = 0;
   2312  gfx::IntSize imageSize = {};
   2313  UniquePtr<uint8_t[]> imageBuffer =
   2314      GetImageBuffer(aExtractionBehavior, &format, &imageSize);
   2315  if (!imageBuffer) {
   2316    return NS_ERROR_FAILURE;
   2317  }
   2318 
   2319  return ImageEncoder::GetInputStream(
   2320      imageSize.width, imageSize.height, imageBuffer.get(), format, encoder,
   2321      aEncoderOptions, aRandomizationKey, aStream);
   2322 }
   2323 
   2324 already_AddRefed<mozilla::gfx::SourceSurface>
   2325 CanvasRenderingContext2D::GetOptimizedSnapshot(DrawTarget* aTarget,
   2326                                               gfxAlphaType* aOutAlphaType) {
   2327  if (aOutAlphaType) {
   2328    *aOutAlphaType = (mOpaque ? gfxAlphaType::Opaque : gfxAlphaType::Premult);
   2329  }
   2330 
   2331  // For GetSurfaceSnapshot we always call EnsureTarget even if mBufferProvider
   2332  // already exists, otherwise we get performance issues. See bug 1567054.
   2333  if (!EnsureTarget()) {
   2334    MOZ_ASSERT(
   2335        mTarget == sErrorTarget.get() || mIsContextLost,
   2336        "On EnsureTarget failure mTarget should be set to sErrorTarget.");
   2337    // In rare circumstances we may have failed to create an error target.
   2338    return mTarget ? mTarget->Snapshot() : nullptr;
   2339  }
   2340 
   2341  // The concept of BorrowSnapshot seems a bit broken here, but the original
   2342  // code in GetSurfaceSnapshot just returned a snapshot from mTarget, which
   2343  // amounts to breaking the concept implicitly.
   2344  RefPtr<SourceSurface> snapshot = mBufferProvider->BorrowSnapshot(aTarget);
   2345  RefPtr<SourceSurface> retSurface = snapshot;
   2346  mBufferProvider->ReturnSnapshot(snapshot.forget());
   2347  return retSurface.forget();
   2348 }
   2349 
   2350 SurfaceFormat CanvasRenderingContext2D::GetSurfaceFormat() const {
   2351  return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
   2352 }
   2353 
   2354 //
   2355 // state
   2356 //
   2357 
   2358 Matrix CanvasRenderingContext2D::GetCurrentTransform() const {
   2359  if (IsTargetValid()) {
   2360    return mTarget->GetTransform();
   2361  }
   2362  for (auto style = mStyleStack.crbegin(); style != mStyleStack.crend();
   2363       ++style) {
   2364    const auto& clipsAndTransforms = style->clipsAndTransforms;
   2365    auto clipOrTransform = clipsAndTransforms.end();
   2366    while (clipOrTransform != clipsAndTransforms.begin()) {
   2367      --clipOrTransform;
   2368      if (!clipOrTransform->IsClip()) {
   2369        return clipOrTransform->transform;
   2370      }
   2371    }
   2372  }
   2373  return Matrix();
   2374 }
   2375 
   2376 void CanvasRenderingContext2D::Save() {
   2377  if (MOZ_UNLIKELY(HasErrorState() || mStyleStack.IsEmpty())) {
   2378    SetErrorState();
   2379    return;
   2380  }
   2381  mStyleStack[mStyleStack.Length() - 1].transform = GetCurrentTransform();
   2382  mStyleStack.SetCapacity(mStyleStack.Length() + 1);
   2383  mStyleStack.AppendElement(CurrentState());
   2384 
   2385  if (mStyleStack.Length() > MAX_STYLE_STACK_SIZE) {
   2386    // This is not fast, but is better than OOMing and shouldn't be hit by
   2387    // reasonable code.
   2388    mStyleStack.RemoveElementAt(0);
   2389  }
   2390 }
   2391 
   2392 void CanvasRenderingContext2D::Restore() {
   2393  if (MOZ_UNLIKELY(mStyleStack.Length() < 2 || HasErrorState())) {
   2394    return;
   2395  }
   2396 
   2397  if (IsTargetValid()) {
   2398    for (const auto& clipOrTransform : CurrentState().clipsAndTransforms) {
   2399      if (clipOrTransform.IsClip()) {
   2400        mTarget->PopClip();
   2401      }
   2402    }
   2403    mTarget->SetTransform(PreviousState().transform);
   2404  } else {
   2405    mTargetNeedsClipsAndTransforms = true;
   2406  }
   2407 
   2408  mStyleStack.RemoveLastElement();
   2409 
   2410  mPathTransformDirty = true;
   2411 }
   2412 
   2413 //
   2414 // transformations
   2415 //
   2416 
   2417 void CanvasRenderingContext2D::Scale(double aX, double aY,
   2418                                     ErrorResult& aError) {
   2419  if (HasErrorState(aError)) {
   2420    return;
   2421  }
   2422  Matrix newMatrix = GetCurrentTransform();
   2423  newMatrix.PreScale(aX, aY);
   2424  SetTransformInternal(newMatrix);
   2425 }
   2426 
   2427 void CanvasRenderingContext2D::Rotate(double aAngle, ErrorResult& aError) {
   2428  if (HasErrorState(aError)) {
   2429    return;
   2430  }
   2431  Matrix newMatrix = Matrix::Rotation(aAngle) * GetCurrentTransform();
   2432  SetTransformInternal(newMatrix);
   2433 }
   2434 
   2435 void CanvasRenderingContext2D::Translate(double aX, double aY,
   2436                                         ErrorResult& aError) {
   2437  if (HasErrorState(aError)) {
   2438    return;
   2439  }
   2440  Matrix newMatrix = GetCurrentTransform();
   2441  newMatrix.PreTranslate(aX, aY);
   2442  SetTransformInternal(newMatrix);
   2443 }
   2444 
   2445 void CanvasRenderingContext2D::Transform(double aM11, double aM12, double aM21,
   2446                                         double aM22, double aDx, double aDy,
   2447                                         ErrorResult& aError) {
   2448  if (HasErrorState(aError)) {
   2449    return;
   2450  }
   2451  Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy);
   2452  newMatrix *= GetCurrentTransform();
   2453  SetTransformInternal(newMatrix);
   2454 }
   2455 
   2456 already_AddRefed<DOMMatrix> CanvasRenderingContext2D::GetTransform(
   2457    ErrorResult& aError) {
   2458  // If we are silently failing, then we still need to return a transform while
   2459  // we are in the process of recovering.
   2460  RefPtr<DOMMatrix> matrix =
   2461      new DOMMatrix(GetParentObject(), GetCurrentTransform());
   2462  return matrix.forget();
   2463 }
   2464 
   2465 void CanvasRenderingContext2D::SetTransform(double aM11, double aM12,
   2466                                            double aM21, double aM22,
   2467                                            double aDx, double aDy,
   2468                                            ErrorResult& aError) {
   2469  if (HasErrorState(aError)) {
   2470    return;
   2471  }
   2472  Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy);
   2473  SetTransformInternal(newMatrix);
   2474 }
   2475 
   2476 void CanvasRenderingContext2D::SetTransform(const DOMMatrix2DInit& aInit,
   2477                                            ErrorResult& aError) {
   2478  if (HasErrorState(aError)) {
   2479    return;
   2480  }
   2481  Matrix matrix2D(DOMMatrixReadOnly::ToValidatedMatrixDouble(aInit, aError));
   2482  if (!aError.Failed()) {
   2483    SetTransformInternal(matrix2D);
   2484  }
   2485 }
   2486 
   2487 void CanvasRenderingContext2D::SetTransformInternal(const Matrix& aTransform) {
   2488  if (!aTransform.IsFinite()) {
   2489    return;
   2490  }
   2491 
   2492  // Save the transform in the clip stack to be able to replay clips properly.
   2493  auto& clipsAndTransforms = CurrentState().clipsAndTransforms;
   2494  if (clipsAndTransforms.IsEmpty() ||
   2495      clipsAndTransforms.LastElement().IsClip()) {
   2496    clipsAndTransforms.AppendElement(ClipState(aTransform));
   2497  } else {
   2498    // If the last item is a transform we can replace it instead of appending
   2499    // a new item.
   2500    clipsAndTransforms.LastElement().transform = aTransform;
   2501  }
   2502 
   2503  if (IsTargetValid()) {
   2504    mTarget->SetTransform(aTransform);
   2505  } else {
   2506    mTargetNeedsClipsAndTransforms = true;
   2507  }
   2508  mPathTransformDirty = true;
   2509 }
   2510 
   2511 void CanvasRenderingContext2D::ResetTransform(ErrorResult& aError) {
   2512  SetTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, aError);
   2513 }
   2514 
   2515 //
   2516 // colors
   2517 //
   2518 
   2519 void CanvasRenderingContext2D::SetStyleFromUnion(
   2520    const UTF8StringOrCanvasGradientOrCanvasPattern& aValue,
   2521    Style aWhichStyle) {
   2522  if (aValue.IsUTF8String()) {
   2523    SetStyleFromString(aValue.GetAsUTF8String(), aWhichStyle);
   2524    return;
   2525  }
   2526 
   2527  if (aValue.IsCanvasGradient()) {
   2528    SetStyleFromGradient(aValue.GetAsCanvasGradient(), aWhichStyle);
   2529    return;
   2530  }
   2531 
   2532  if (aValue.IsCanvasPattern()) {
   2533    CanvasPattern& pattern = aValue.GetAsCanvasPattern();
   2534    SetStyleFromPattern(pattern, aWhichStyle);
   2535    if (pattern.mForceWriteOnly) {
   2536      SetWriteOnly();
   2537    }
   2538    return;
   2539  }
   2540 
   2541  MOZ_ASSERT_UNREACHABLE("Invalid union value");
   2542 }
   2543 
   2544 void CanvasRenderingContext2D::SetFillRule(const nsAString& aString) {
   2545  FillRule rule;
   2546 
   2547  if (aString.EqualsLiteral("evenodd"))
   2548    rule = FillRule::FILL_EVEN_ODD;
   2549  else if (aString.EqualsLiteral("nonzero"))
   2550    rule = FillRule::FILL_WINDING;
   2551  else
   2552    return;
   2553 
   2554  CurrentState().fillRule = rule;
   2555 }
   2556 
   2557 void CanvasRenderingContext2D::GetFillRule(nsAString& aString) {
   2558  switch (CurrentState().fillRule) {
   2559    case FillRule::FILL_WINDING:
   2560      aString.AssignLiteral("nonzero");
   2561      break;
   2562    case FillRule::FILL_EVEN_ODD:
   2563      aString.AssignLiteral("evenodd");
   2564      break;
   2565  }
   2566 }
   2567 //
   2568 // gradients and patterns
   2569 //
   2570 already_AddRefed<CanvasGradient> CanvasRenderingContext2D::CreateLinearGradient(
   2571    double aX0, double aY0, double aX1, double aY1) {
   2572  RefPtr<CanvasGradient> grad =
   2573      new CanvasLinearGradient(this, Point(aX0, aY0), Point(aX1, aY1));
   2574 
   2575  return grad.forget();
   2576 }
   2577 
   2578 already_AddRefed<CanvasGradient> CanvasRenderingContext2D::CreateRadialGradient(
   2579    double aX0, double aY0, double aR0, double aX1, double aY1, double aR1,
   2580    ErrorResult& aError) {
   2581  if (aR0 < 0.0 || aR1 < 0.0) {
   2582    aError.ThrowIndexSizeError("Negative radius");
   2583    return nullptr;
   2584  }
   2585 
   2586  RefPtr<CanvasGradient> grad = new CanvasRadialGradient(
   2587      this, Point(aX0, aY0), aR0, Point(aX1, aY1), aR1);
   2588 
   2589  return grad.forget();
   2590 }
   2591 
   2592 already_AddRefed<CanvasGradient> CanvasRenderingContext2D::CreateConicGradient(
   2593    double aAngle, double aCx, double aCy) {
   2594  double adjustedStartAngle = aAngle + M_PI / 2.0;
   2595  return MakeAndAddRef<CanvasConicGradient>(this, adjustedStartAngle,
   2596                                            Point(aCx, aCy));
   2597 }
   2598 
   2599 already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
   2600    const CanvasImageSource& aSource, const nsAString& aRepeat,
   2601    ErrorResult& aError) {
   2602  CanvasPattern::RepeatMode repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
   2603 
   2604  if (aRepeat.IsEmpty() || aRepeat.EqualsLiteral("repeat")) {
   2605    repeatMode = CanvasPattern::RepeatMode::REPEAT;
   2606  } else if (aRepeat.EqualsLiteral("repeat-x")) {
   2607    repeatMode = CanvasPattern::RepeatMode::REPEATX;
   2608  } else if (aRepeat.EqualsLiteral("repeat-y")) {
   2609    repeatMode = CanvasPattern::RepeatMode::REPEATY;
   2610  } else if (aRepeat.EqualsLiteral("no-repeat")) {
   2611    repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
   2612  } else {
   2613    aError.ThrowSyntaxError("Invalid pattern keyword");
   2614    return nullptr;
   2615  }
   2616 
   2617  Element* element = nullptr;
   2618  OffscreenCanvas* offscreenCanvas = nullptr;
   2619  VideoFrame* videoFrame = nullptr;
   2620 
   2621  if (aSource.IsHTMLCanvasElement()) {
   2622    HTMLCanvasElement* canvas = &aSource.GetAsHTMLCanvasElement();
   2623    element = canvas;
   2624 
   2625    CSSIntSize size = canvas->GetSize();
   2626    if (size.width == 0) {
   2627      aError.ThrowInvalidStateError("Passed-in canvas has width 0");
   2628      return nullptr;
   2629    }
   2630 
   2631    if (size.height == 0) {
   2632      aError.ThrowInvalidStateError("Passed-in canvas has height 0");
   2633      return nullptr;
   2634    }
   2635 
   2636    // Special case for Canvas, which could be an Azure canvas!
   2637    nsICanvasRenderingContextInternal* srcCanvas = canvas->GetCurrentContext();
   2638    if (srcCanvas) {
   2639      // This might not be an Azure canvas!
   2640      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
   2641      if (!srcSurf) {
   2642        aError.ThrowInvalidStateError(
   2643            "CanvasRenderingContext2D.createPattern() failed to snapshot source"
   2644            "canvas.");
   2645        return nullptr;
   2646      }
   2647 
   2648      RefPtr<CanvasPattern> pat =
   2649          new CanvasPattern(this, srcSurf, repeatMode, element->NodePrincipal(),
   2650                            canvas->IsWriteOnly(), false);
   2651 
   2652      return pat.forget();
   2653    }
   2654  } else if (aSource.IsHTMLImageElement()) {
   2655    HTMLImageElement* img = &aSource.GetAsHTMLImageElement();
   2656    element = img;
   2657  } else if (aSource.IsSVGImageElement()) {
   2658    SVGImageElement* img = &aSource.GetAsSVGImageElement();
   2659    element = img;
   2660  } else if (aSource.IsHTMLVideoElement()) {
   2661    auto& video = aSource.GetAsHTMLVideoElement();
   2662    video.LogVisibility(
   2663        mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_PATTERN);
   2664    element = &video;
   2665  } else if (aSource.IsOffscreenCanvas()) {
   2666    offscreenCanvas = &aSource.GetAsOffscreenCanvas();
   2667 
   2668    CSSIntSize size = offscreenCanvas->GetWidthHeight();
   2669    if (size.width == 0) {
   2670      aError.ThrowInvalidStateError("Passed-in canvas has width 0");
   2671      return nullptr;
   2672    }
   2673 
   2674    if (size.height == 0) {
   2675      aError.ThrowInvalidStateError("Passed-in canvas has height 0");
   2676      return nullptr;
   2677    }
   2678 
   2679    nsICanvasRenderingContextInternal* srcCanvas =
   2680        offscreenCanvas->GetContext();
   2681    if (srcCanvas) {
   2682      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
   2683      if (!srcSurf) {
   2684        aError.ThrowInvalidStateError(
   2685            "Passed-in canvas failed to create snapshot");
   2686        return nullptr;
   2687      }
   2688 
   2689      RefPtr<CanvasPattern> pat = new CanvasPattern(
   2690          this, srcSurf, repeatMode, srcCanvas->PrincipalOrNull(),
   2691          offscreenCanvas->IsWriteOnly(), false);
   2692 
   2693      return pat.forget();
   2694    }
   2695  } else if (aSource.IsVideoFrame()) {
   2696    videoFrame = &aSource.GetAsVideoFrame();
   2697 
   2698    if (videoFrame->CodedWidth() == 0) {
   2699      aError.ThrowInvalidStateError("Passed-in canvas has width 0");
   2700      return nullptr;
   2701    }
   2702 
   2703    if (videoFrame->CodedHeight() == 0) {
   2704      aError.ThrowInvalidStateError("Passed-in canvas has height 0");
   2705      return nullptr;
   2706    }
   2707  } else {
   2708    // Special case for ImageBitmap
   2709    ImageBitmap& imgBitmap = aSource.GetAsImageBitmap();
   2710    if (!EnsureTarget(aError)) {
   2711      return nullptr;
   2712    }
   2713 
   2714    MOZ_ASSERT(IsTargetValid());
   2715 
   2716    RefPtr<SourceSurface> srcSurf = imgBitmap.PrepareForDrawTarget(mTarget);
   2717    if (!srcSurf) {
   2718      aError.ThrowInvalidStateError(
   2719          "Passed-in ImageBitmap has been transferred");
   2720      return nullptr;
   2721    }
   2722 
   2723    // An ImageBitmap never taints others so we set principalForSecurityCheck to
   2724    // nullptr and set CORSUsed to true for passing the security check in
   2725    // CanvasUtils::DoDrawImageSecurityCheck().
   2726    RefPtr<CanvasPattern> pat = new CanvasPattern(
   2727        this, srcSurf, repeatMode, nullptr, imgBitmap.IsWriteOnly(), true);
   2728 
   2729    return pat.forget();
   2730  }
   2731 
   2732  if (!EnsureTarget(aError)) {
   2733    return nullptr;
   2734  }
   2735 
   2736  MOZ_ASSERT(IsTargetValid());
   2737 
   2738  // The canvas spec says that createPattern should use the first frame
   2739  // of animated images
   2740  auto flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
   2741               nsLayoutUtils::SFE_EXACT_SIZE_SURFACE;
   2742  SurfaceFromElementResult res;
   2743  if (offscreenCanvas) {
   2744    res = nsLayoutUtils::SurfaceFromOffscreenCanvas(offscreenCanvas, flags,
   2745                                                    mTarget);
   2746  } else if (videoFrame) {
   2747    res = nsLayoutUtils::SurfaceFromVideoFrame(videoFrame, flags, mTarget);
   2748  } else {
   2749    res = nsLayoutUtils::SurfaceFromElement(element, flags, mTarget);
   2750  }
   2751 
   2752  // Per spec, we should throw here for the HTMLImageElement and SVGImageElement
   2753  // cases if the image request state is "broken".  In terms of the infromation
   2754  // in "res", the "broken" state corresponds to not having a size and not being
   2755  // still-loading (so there is no size forthcoming).
   2756  if (aSource.IsHTMLImageElement() || aSource.IsSVGImageElement()) {
   2757    if (!res.mIsStillLoading && !res.mHasSize) {
   2758      aError.ThrowInvalidStateError(
   2759          "Passed-in image's current request's state is \"broken\"");
   2760      return nullptr;
   2761    }
   2762 
   2763    if (res.mSize.width == 0 || res.mSize.height == 0) {
   2764      return nullptr;
   2765    }
   2766 
   2767    // Is the "fully decodable" check already done in SurfaceFromElement?  It's
   2768    // not clear how to do it from here, exactly.
   2769  }
   2770 
   2771  RefPtr<SourceSurface> surface = res.GetSourceSurface();
   2772  if (!surface) {
   2773    return nullptr;
   2774  }
   2775 
   2776  RefPtr<CanvasPattern> pat =
   2777      new CanvasPattern(this, surface, repeatMode, res.mPrincipal,
   2778                        res.mIsWriteOnly, res.mCORSUsed);
   2779  return pat.forget();
   2780 }
   2781 
   2782 //
   2783 // shadows
   2784 //
   2785 void CanvasRenderingContext2D::SetShadowColor(const nsACString& aShadowColor) {
   2786  Maybe<nscolor> color = ParseColor(aShadowColor);
   2787  if (!color) {
   2788    return;
   2789  }
   2790 
   2791  CurrentState().shadowColor = *color;
   2792 }
   2793 
   2794 //
   2795 // filters
   2796 //
   2797 
   2798 static already_AddRefed<StyleLockedDeclarationBlock> CreateDeclarationForServo(
   2799    NonCustomCSSPropertyId aProperty, const nsACString& aPropertyValue,
   2800    Document* aDocument) {
   2801  ServoCSSParser::ParsingEnvironment env{aDocument->DefaultStyleAttrURLData(),
   2802                                         aDocument->GetCompatibilityMode(),
   2803                                         // Loader only for error reporting
   2804                                         aDocument->GetExistingCSSLoader()};
   2805  RefPtr<StyleLockedDeclarationBlock> servoDeclarations =
   2806      ServoCSSParser::ParseProperty(aProperty, aPropertyValue, env,
   2807                                    StyleParsingMode::DEFAULT);
   2808 
   2809  if (!servoDeclarations) {
   2810    // We got a syntax error.  The spec says this value must be ignored.
   2811    return nullptr;
   2812  }
   2813 
   2814  if (aProperty == eCSSProperty_font) {
   2815    Servo_DeclarationBlock_SanitizeForCanvas(servoDeclarations);
   2816  }
   2817 
   2818  return servoDeclarations.forget();
   2819 }
   2820 
   2821 static already_AddRefed<StyleLockedDeclarationBlock>
   2822 CreateFontDeclarationForServo(const nsACString& aFont, Document* aDocument) {
   2823  return CreateDeclarationForServo(eCSSProperty_font, aFont, aDocument);
   2824 }
   2825 
   2826 static already_AddRefed<const ComputedStyle> GetFontStyleForServo(
   2827    Element* aElement, const nsACString& aFont, PresShell* aPresShell,
   2828    nsACString& aOutUsedFont, ErrorResult& aError) {
   2829  RefPtr<StyleLockedDeclarationBlock> declarations =
   2830      CreateFontDeclarationForServo(aFont, aPresShell->GetDocument());
   2831  if (!declarations) {
   2832    // We got a syntax error.  The spec says this value must be ignored.
   2833    return nullptr;
   2834  }
   2835 
   2836  // In addition to unparseable values, the spec says we need to reject
   2837  // 'inherit' and 'initial'. The easiest way to check for this is to look
   2838  // at font-size-adjust, which the font shorthand resets to 'none'.
   2839  if (Servo_DeclarationBlock_HasCSSWideKeyword(declarations,
   2840                                               eCSSProperty_font_size_adjust)) {
   2841    return nullptr;
   2842  }
   2843 
   2844  ServoStyleSet* styleSet = aPresShell->StyleSet();
   2845 
   2846  // Have to get a parent ComputedStyle for inherit-like relative values (2em,
   2847  // bolder, etc.)
   2848  RefPtr<const ComputedStyle> parentStyle;
   2849  if (aElement) {
   2850    parentStyle = nsComputedDOMStyle::GetComputedStyle(aElement);
   2851    if (NS_WARN_IF(aPresShell->IsDestroying())) {
   2852      // The flush might've killed the shell.
   2853      aError.Throw(NS_ERROR_FAILURE);
   2854      return nullptr;
   2855    }
   2856  }
   2857  if (!parentStyle) {
   2858    RefPtr<StyleLockedDeclarationBlock> declarations =
   2859        CreateFontDeclarationForServo("10px sans-serif"_ns,
   2860                                      aPresShell->GetDocument());
   2861    MOZ_ASSERT(declarations);
   2862 
   2863    parentStyle =
   2864        aPresShell->StyleSet()->ResolveForDeclarations(nullptr, declarations);
   2865  }
   2866 
   2867  MOZ_RELEASE_ASSERT(parentStyle, "Should have a valid parent style");
   2868 
   2869  MOZ_ASSERT(!aPresShell->IsDestroying(),
   2870             "We should have returned an error above if the presshell is "
   2871             "being destroyed.");
   2872 
   2873  RefPtr<const ComputedStyle> sc =
   2874      styleSet->ResolveForDeclarations(parentStyle, declarations);
   2875 
   2876  // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-font
   2877  // The font-size component must be converted to CSS px for reserialization,
   2878  // so we update the declarations with the value from the computed style.
   2879  if (!sc->StyleFont()->mFont.family.is_system_font) {
   2880    float px = sc->StyleFont()->mFont.size.ToCSSPixels();
   2881    Servo_DeclarationBlock_SetLengthValue(declarations, eCSSProperty_font_size,
   2882                                          px, eCSSUnit_Pixel);
   2883  }
   2884 
   2885  // The font getter is required to be reserialized based on what we
   2886  // parsed (including having line-height removed).
   2887  // If we failed to reserialize, ignore this attempt to set the value.
   2888  Servo_SerializeFontValueForCanvas(declarations, &aOutUsedFont);
   2889  if (aOutUsedFont.IsEmpty()) {
   2890    return nullptr;
   2891  }
   2892 
   2893  return sc.forget();
   2894 }
   2895 
   2896 static already_AddRefed<StyleLockedDeclarationBlock>
   2897 CreateFilterDeclarationForServo(const nsACString& aFilter,
   2898                                Document* aDocument) {
   2899  return CreateDeclarationForServo(eCSSProperty_filter, aFilter, aDocument);
   2900 }
   2901 
   2902 static already_AddRefed<const ComputedStyle> ResolveFilterStyleForServo(
   2903    const nsACString& aFilterString, const ComputedStyle* aParentStyle,
   2904    PresShell* aPresShell, ErrorResult& aError) {
   2905  RefPtr<StyleLockedDeclarationBlock> declarations =
   2906      CreateFilterDeclarationForServo(aFilterString, aPresShell->GetDocument());
   2907  if (!declarations) {
   2908    // Refuse to accept the filter, but do not throw an error.
   2909    return nullptr;
   2910  }
   2911 
   2912  // In addition to unparseable values, the spec says we need to reject
   2913  // 'inherit' and 'initial'.
   2914  if (Servo_DeclarationBlock_HasCSSWideKeyword(declarations,
   2915                                               eCSSProperty_filter)) {
   2916    return nullptr;
   2917  }
   2918 
   2919  ServoStyleSet* styleSet = aPresShell->StyleSet();
   2920  RefPtr<const ComputedStyle> computedValues =
   2921      styleSet->ResolveForDeclarations(aParentStyle, declarations);
   2922 
   2923  return computedValues.forget();
   2924 }
   2925 
   2926 bool CanvasRenderingContext2D::ParseFilter(
   2927    const nsACString& aString, StyleOwnedSlice<StyleFilter>& aFilterChain,
   2928    ErrorResult& aError) {
   2929  RefPtr<PresShell> presShell = GetPresShell();
   2930  if (!presShell) {
   2931    nsIGlobalObject* global = GetParentObject();
   2932    FontFaceSet* fontFaceSet = global ? global->GetFonts() : nullptr;
   2933    FontFaceSetImpl* fontFaceSetImpl =
   2934        fontFaceSet ? fontFaceSet->GetImpl() : nullptr;
   2935    RefPtr<URLExtraData> urlExtraData =
   2936        fontFaceSetImpl ? fontFaceSetImpl->GetURLExtraData() : nullptr;
   2937 
   2938    if (NS_WARN_IF(!urlExtraData)) {
   2939      // Provided we have a FontFaceSetImpl object, this should only happen on
   2940      // worker threads, where we failed to initialize the worker before it was
   2941      // shutdown.
   2942      aError.ThrowInvalidStateError("Missing URLExtraData");
   2943      return false;
   2944    }
   2945 
   2946    if (NS_WARN_IF(!Servo_ParseFilters(&aString, /* aIgnoreUrls */ true,
   2947                                       urlExtraData, &aFilterChain))) {
   2948      return false;
   2949    }
   2950 
   2951    return true;
   2952  }
   2953 
   2954  const ComputedStyle* parentStyle = GetCurrentFontComputedStyle();
   2955  if (!parentStyle) {
   2956    return false;
   2957  }
   2958 
   2959  RefPtr<const ComputedStyle> style =
   2960      ResolveFilterStyleForServo(aString, parentStyle, presShell, aError);
   2961  if (!style) {
   2962    return false;
   2963  }
   2964 
   2965  aFilterChain = style->StyleEffects()->mFilters;
   2966  return true;
   2967 }
   2968 
   2969 void CanvasRenderingContext2D::SetFilter(const nsACString& aFilter,
   2970                                         ErrorResult& aError) {
   2971  StyleOwnedSlice<StyleFilter> filterChain;
   2972  if (ParseFilter(aFilter, filterChain, aError)) {
   2973    CurrentState().filterString = aFilter;
   2974    CurrentState().filterChain = std::move(filterChain);
   2975    if (mCanvasElement) {
   2976      CurrentState().autoSVGFiltersObserver =
   2977          SVGObserverUtils::ObserveFiltersForCanvasContext(
   2978              this, mCanvasElement, CurrentState().filterChain.AsSpan());
   2979    }
   2980    UpdateFilter(/* aFlushIfNeeded = */ true);
   2981  }
   2982 }
   2983 
   2984 static already_AddRefed<const ComputedStyle> ResolveStyleForServo(
   2985    NonCustomCSSPropertyId aProperty, const nsACString& aString,
   2986    const ComputedStyle* aParentStyle, PresShell* aPresShell,
   2987    ErrorResult& aError) {
   2988  RefPtr<StyleLockedDeclarationBlock> declarations =
   2989      CreateDeclarationForServo(aProperty, aString, aPresShell->GetDocument());
   2990  if (!declarations) {
   2991    return nullptr;
   2992  }
   2993 
   2994  // In addition to unparseable values, reject 'inherit' and 'initial'.
   2995  if (Servo_DeclarationBlock_HasCSSWideKeyword(declarations, aProperty)) {
   2996    return nullptr;
   2997  }
   2998 
   2999  ServoStyleSet* styleSet = aPresShell->StyleSet();
   3000  return styleSet->ResolveForDeclarations(aParentStyle, declarations);
   3001 }
   3002 
   3003 already_AddRefed<const ComputedStyle>
   3004 CanvasRenderingContext2D::ResolveStyleForProperty(
   3005    NonCustomCSSPropertyId aProperty, const nsACString& aValue) {
   3006  RefPtr<PresShell> presShell = GetPresShell();
   3007  if (NS_WARN_IF(!presShell)) {
   3008    return nullptr;
   3009  }
   3010 
   3011  const ComputedStyle* parentStyle = GetCurrentFontComputedStyle();
   3012  if (!parentStyle) {
   3013    return nullptr;
   3014  }
   3015 
   3016  return ResolveStyleForServo(aProperty, aValue, parentStyle, presShell,
   3017                              IgnoreErrors());
   3018 }
   3019 
   3020 void CanvasRenderingContext2D::GetLetterSpacing(nsACString& aLetterSpacing) {
   3021  if (CurrentState().letterSpacingStr.IsEmpty()) {
   3022    aLetterSpacing.AssignLiteral("0px");
   3023  } else {
   3024    aLetterSpacing = CurrentState().letterSpacingStr;
   3025  }
   3026 }
   3027 
   3028 void CanvasRenderingContext2D::SetLetterSpacing(
   3029    const nsACString& aLetterSpacing) {
   3030  ParseSpacing(aLetterSpacing, &CurrentState().letterSpacing,
   3031               CurrentState().letterSpacingStr);
   3032 }
   3033 
   3034 void CanvasRenderingContext2D::GetWordSpacing(nsACString& aWordSpacing) {
   3035  if (CurrentState().wordSpacingStr.IsEmpty()) {
   3036    aWordSpacing.AssignLiteral("0px");
   3037  } else {
   3038    aWordSpacing = CurrentState().wordSpacingStr;
   3039  }
   3040 }
   3041 
   3042 void CanvasRenderingContext2D::SetWordSpacing(const nsACString& aWordSpacing) {
   3043  ParseSpacing(aWordSpacing, &CurrentState().wordSpacing,
   3044               CurrentState().wordSpacingStr);
   3045 }
   3046 
   3047 static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
   3048  auto* ctx = static_cast<CanvasRenderingContext2D*>(aContext);
   3049  auto* fontGroup = ctx->GetCurrentFontStyle();
   3050  if (!fontGroup) {
   3051    // Shouldn't happen, but just in case... return plausible values for a
   3052    // 10px font (canvas default size).
   3053    return {Length::FromPixels(5.0),
   3054            Length::FromPixels(5.0),
   3055            Length::FromPixels(8.0),
   3056            Length::FromPixels(10.0),
   3057            Length::FromPixels(8.0),
   3058            Length::FromPixels(10.0),
   3059            0.0f,
   3060            0.0f};
   3061  }
   3062  auto metrics = fontGroup->GetMetricsForCSSUnits(
   3063      nsFontMetrics::eHorizontal, StyleQueryFontMetricsFlags::NEEDS_CH |
   3064                                      StyleQueryFontMetricsFlags::NEEDS_IC);
   3065  return {Length::FromPixels(metrics.xHeight),
   3066          Length::FromPixels(metrics.zeroWidth),
   3067          Length::FromPixels(metrics.capHeight),
   3068          Length::FromPixels(metrics.ideographicWidth),
   3069          Length::FromPixels(metrics.maxAscent),
   3070          Length::FromPixels(fontGroup->GetStyle()->size),
   3071          0.0f,
   3072          0.0f};
   3073 }
   3074 
   3075 void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
   3076                                            float* aValue,
   3077                                            nsACString& aNormalized) {
   3078  // Normalize whitespace in the string before trying to parse it, as we want
   3079  // to store it in normalized form, and this allows a simple check against the
   3080  // 'normal' keyword, which is not accepted.
   3081  nsAutoCString normalized(aSpacing);
   3082  normalized.CompressWhitespace(true, true);
   3083  ToLowerCase(normalized);
   3084  if (normalized.EqualsLiteral("normal")) {
   3085    return;
   3086  }
   3087  float value;
   3088  if (!Servo_ParseLengthWithoutStyleContext(&normalized, &value,
   3089                                            GetFontMetricsFromCanvas, this)) {
   3090    if (!GetPresShell()) {
   3091      return;
   3092    }
   3093    // This will parse aSpacing as a <length-percentage>...
   3094    RefPtr<const ComputedStyle> style =
   3095        ResolveStyleForProperty(eCSSProperty_letter_spacing, aSpacing);
   3096    if (!style) {
   3097      return;
   3098    }
   3099    // ...but only <length> is allowed according to the canvas spec.
   3100    if (!style->StyleText()->mLetterSpacing.IsLength()) {
   3101      return;
   3102    }
   3103    value = style->StyleText()->mLetterSpacing.AsLength().ToCSSPixels();
   3104  }
   3105  aNormalized = normalized;
   3106  *aValue = value;
   3107 }
   3108 
   3109 class CanvasUserSpaceMetrics final : public UserSpaceMetricsWithSize {
   3110 public:
   3111  CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
   3112                         const StyleLineHeight& aLineHeight,
   3113                         RefPtr<nsAtom> aFontLanguage,
   3114                         bool aFontExplicitLanguage,
   3115                         const ComputedStyle* aCanvasStyle,
   3116                         nsPresContext* aPresContext)
   3117      : mSize(aSize),
   3118        mFont(aFont),
   3119        mLineHeight(aLineHeight),
   3120        mFontLanguage(std::move(aFontLanguage)),
   3121        mFontExplicitLanguage(aFontExplicitLanguage),
   3122        mCanvasStyle(aCanvasStyle),
   3123        mPresContext(aPresContext) {}
   3124 
   3125  float GetZoom() const override {
   3126    return mCanvasStyle ? mCanvasStyle->EffectiveZoom().ToFloat() : 1.0f;
   3127  }
   3128 
   3129  float GetRootZoom() const override {
   3130    return UserSpaceMetrics::GetZoom(
   3131        mPresContext->Document()->GetRootElement());
   3132  }
   3133 
   3134  float GetEmLength(Type aType) const override {
   3135    switch (aType) {
   3136      case Type::This:
   3137        return mFont.size.ToCSSPixels();
   3138      case Type::Root:
   3139        return SVGContentUtils::GetFontSize(
   3140            mPresContext->Document()->GetRootElement());
   3141      default:
   3142        MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
   3143        return 1.0f;
   3144    }
   3145  }
   3146  gfx::Size GetSize() const override { return Size(mSize); }
   3147 
   3148  CSSSize GetCSSViewportSize() const override {
   3149    return GetCSSViewportSizeFromContext(mPresContext);
   3150  }
   3151 
   3152  float GetLineHeight(Type aType) const override {
   3153    // This is used if a filter is added through `url()`, and if the SVG
   3154    // filter being referred to is using line-height units.
   3155    switch (aType) {
   3156      case Type::This: {
   3157        const auto wm = GetWritingModeForType(aType);
   3158        const auto lh = ReflowInput::CalcLineHeightForCanvas(
   3159            mLineHeight, mFont, mFontLanguage, mFontExplicitLanguage,
   3160            mPresContext, wm);
   3161        return nsPresContext::AppUnitsToFloatCSSPixels(lh);
   3162      }
   3163      case Type::Root: {
   3164        return SVGContentUtils::GetLineHeight(
   3165            mPresContext->Document()->GetRootElement());
   3166      }
   3167    }
   3168    MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
   3169    return 1.0f;
   3170  }
   3171 
   3172 private:
   3173  GeckoFontMetrics GetFontMetricsForType(Type aType) const override {
   3174    switch (aType) {
   3175      case Type::This: {
   3176        if (!mCanvasStyle) {
   3177          return DefaultFontMetrics();
   3178        }
   3179        return Gecko_GetFontMetrics(
   3180            mPresContext, WritingMode(mCanvasStyle).IsVertical(),
   3181            mCanvasStyle->StyleFont(), mCanvasStyle->StyleFont()->mFont.size,
   3182            StyleQueryFontMetricsFlags::USE_USER_FONT_SET |
   3183                StyleQueryFontMetricsFlags::NEEDS_CH |
   3184                StyleQueryFontMetricsFlags::NEEDS_IC);
   3185      }
   3186      case Type::Root:
   3187        return GetFontMetrics(mPresContext->Document()->GetRootElement());
   3188      default:
   3189        MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
   3190        return DefaultFontMetrics();
   3191    }
   3192  }
   3193 
   3194  WritingMode GetWritingModeForType(Type aType) const override {
   3195    switch (aType) {
   3196      case Type::This:
   3197        return mCanvasStyle ? WritingMode(mCanvasStyle) : WritingMode();
   3198      case Type::Root:
   3199        return GetWritingMode(mPresContext->Document()->GetRootElement());
   3200      default:
   3201        MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
   3202        return WritingMode();
   3203    }
   3204  }
   3205 
   3206  gfx::IntSize mSize;
   3207  const nsFont& mFont;
   3208  StyleLineHeight mLineHeight;
   3209  RefPtr<nsAtom> mFontLanguage;
   3210  bool mFontExplicitLanguage;
   3211  RefPtr<const ComputedStyle> mCanvasStyle;
   3212  nsPresContext* mPresContext;
   3213 };
   3214 
   3215 // The filter might reference an SVG filter that is declared inside this
   3216 // document. Flush frames so that we'll have a SVGFilterFrame to work
   3217 // with.
   3218 static bool FiltersNeedFrameFlush(Span<const StyleFilter> aFilters) {
   3219  for (const auto& filter : aFilters) {
   3220    if (filter.IsUrl()) {
   3221      return true;
   3222    }
   3223  }
   3224  return false;
   3225 }
   3226 
   3227 void CanvasRenderingContext2D::UpdateFilter(bool aFlushIfNeeded) {
   3228  const bool writeOnly = IsWriteOnly() ||
   3229                         (mCanvasElement && mCanvasElement->IsWriteOnly()) ||
   3230                         (mOffscreenCanvas && mOffscreenCanvas->IsWriteOnly());
   3231 
   3232  RefPtr<PresShell> presShell = GetPresShell();
   3233  if (!mOffscreenCanvas && (!presShell || presShell->IsDestroying())) {
   3234    // Ensure we set an empty filter and update the state to
   3235    // reflect the current "taint" status of the canvas
   3236    CurrentState().filter = FilterDescription();
   3237    CurrentState().filterSourceGraphicTainted = writeOnly;
   3238    return;
   3239  }
   3240 
   3241  // The PresContext is only used with URL filters and we don't allow those to
   3242  // be used on worker threads.
   3243  nsPresContext* presContext = nullptr;
   3244  if (presShell) {
   3245    if (aFlushIfNeeded &&
   3246        FiltersNeedFrameFlush(CurrentState().filterChain.AsSpan())) {
   3247      presShell->FlushPendingNotifications(FlushType::Frames);
   3248    }
   3249 
   3250    if (MOZ_UNLIKELY(presShell->IsDestroying())) {
   3251      return;
   3252    }
   3253 
   3254    presContext = presShell->GetPresContext();
   3255  }
   3256  // FIXME(emilio): This seems like it should use GetCurrentFontComputedStyle to
   3257  // make sure the font is up-to-date, but the old code didn't... Find a
   3258  // test-case where it matters?
   3259  const ComputedStyle* currentFontStyle = CurrentState().fontComputedStyle;
   3260  const RefPtr<const ComputedStyle> canvasStyle =
   3261      mCanvasElement
   3262          ? nsComputedDOMStyle::GetComputedStyleNoFlush(mCanvasElement)
   3263          : nullptr;
   3264 
   3265  MOZ_RELEASE_ASSERT(!mStyleStack.IsEmpty());
   3266  auto& state = CurrentState();
   3267  auto lineHeight = currentFontStyle
   3268                        ? currentFontStyle->StyleFont()->mLineHeight
   3269                        : StyleLineHeight::Normal();
   3270  auto* language = currentFontStyle
   3271                       ? currentFontStyle->StyleFont()->mLanguage.get()
   3272                       : nullptr;
   3273  bool explicitLanguage =
   3274      state.fontComputedStyle &&
   3275      state.fontComputedStyle->StyleFont()->mExplicitLanguage;
   3276  state.filter = FilterInstance::GetFilterDescription(
   3277      mCanvasElement, state.filterChain.AsSpan(), state.autoSVGFiltersObserver,
   3278      writeOnly,
   3279      CanvasUserSpaceMetrics(GetSize(), state.fontFont, lineHeight, language,
   3280                             explicitLanguage, canvasStyle, presContext),
   3281      gfxRect(0, 0, mWidth, mHeight), state.filterAdditionalImages);
   3282  state.filterSourceGraphicTainted = writeOnly;
   3283 }
   3284 
   3285 //
   3286 // rects
   3287 //
   3288 
   3289 static bool ValidateRect(double& aX, double& aY, double& aWidth,
   3290                         double& aHeight, bool aIsZeroSizeValid) {
   3291  if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) {
   3292    return false;
   3293  }
   3294 
   3295  // bug 1018527
   3296  // The values of canvas API input are in double precision, but Moz2D APIs are
   3297  // using float precision. Bypass canvas API calls when the input is out of
   3298  // float precision to avoid precision problem
   3299  if (!std::isfinite((float)aX) || !std::isfinite((float)aY) ||
   3300      !std::isfinite((float)aWidth) || !std::isfinite((float)aHeight)) {
   3301    return false;
   3302  }
   3303 
   3304  // bug 1074733
   3305  // The canvas spec does not forbid rects with negative w or h, so given
   3306  // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate
   3307  // the appropriate rect by flipping negative dimensions. This prevents
   3308  // draw targets from receiving "empty" rects later on.
   3309  if (aWidth < 0) {
   3310    aWidth = -aWidth;
   3311    aX -= aWidth;
   3312  }
   3313  if (aHeight < 0) {
   3314    aHeight = -aHeight;
   3315    aY -= aHeight;
   3316  }
   3317  return true;
   3318 }
   3319 
   3320 void CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW,
   3321                                         double aH) {
   3322  // Do not allow zeros - it's a no-op at that point per spec.
   3323  if (!ValidateRect(aX, aY, aW, aH, false)) {
   3324    return;
   3325  }
   3326 
   3327  gfx::Rect clearRect(aX, aY, aW, aH);
   3328 
   3329  EnsureTarget(&clearRect, true);
   3330  if (!IsTargetValid()) {
   3331    return;
   3332  }
   3333 
   3334  mTarget->ClearRect(clearRect);
   3335 
   3336  RedrawUser(gfxRect(aX, aY, aW, aH));
   3337 }
   3338 
   3339 void CanvasRenderingContext2D::FillRect(double aX, double aY, double aW,
   3340                                        double aH) {
   3341  mFeatureUsage |= CanvasFeatureUsage::FillRect;
   3342 
   3343  if (!ValidateRect(aX, aY, aW, aH, true)) {
   3344    return;
   3345  }
   3346 
   3347  const ContextState* state = &CurrentState();
   3348  if (state->patternStyles[Style::FILL]) {
   3349    auto& style = state->patternStyles[Style::FILL];
   3350    CanvasPattern::RepeatMode repeat = style->mRepeat;
   3351    // In the FillRect case repeat modes are easy to deal with.
   3352    bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT ||
   3353                  repeat == CanvasPattern::RepeatMode::REPEATY;
   3354    bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT ||
   3355                  repeat == CanvasPattern::RepeatMode::REPEATX;
   3356    if ((limitx || limity) && style->mTransform.IsRectilinear()) {
   3357      // For rectilinear transforms, we can just get the transformed pattern
   3358      // bounds and intersect them with the fill rectangle bounds.
   3359      // TODO: If the transform is not rectilinear, then we would need a fully
   3360      // general clip path to represent the X and Y clip planes bounding the
   3361      // pattern. For such cases, it would be more efficient to rely on Skia's
   3362      // Decal tiling mode rather than trying to generate a path. Until then,
   3363      // just punt to relying on the default Clamp mode.
   3364      gfx::Rect patternBounds(style->mSurface->GetRect());
   3365      patternBounds = style->mTransform.TransformBounds(patternBounds);
   3366      if (style->mTransform.HasNonAxisAlignedTransform()) {
   3367        // If there is an rotation (90 or 270 degrees), the X axis of the
   3368        // pattern projects onto the Y axis of the geometry, and vice versa.
   3369        std::swap(limitx, limity);
   3370      }
   3371      // We always need to execute painting for non-over operators, even if
   3372      // we end up with w/h = 0. The default Rect::Intersect can cause both
   3373      // dimensions to become empty if either dimension individually fails
   3374      // to overlap, which is unsuitable. Instead, we need to independently
   3375      // limit the supplied rectangle on each dimension as required.
   3376      if (limitx) {
   3377        double x2 = aX + aW;
   3378        aX = std::max(aX, double(patternBounds.x));
   3379        aW = std::max(std::min(x2, double(patternBounds.XMost())) - aX, 0.0);
   3380      }
   3381      if (limity) {
   3382        double y2 = aY + aH;
   3383        aY = std::max(aY, double(patternBounds.y));
   3384        aH = std::max(std::min(y2, double(patternBounds.YMost())) - aY, 0.0);
   3385      }
   3386    }
   3387  }
   3388  state = nullptr;
   3389 
   3390  bool isColor;
   3391  bool discardContent = PatternIsOpaque(Style::FILL, &isColor) &&
   3392                        (CurrentState().op == CompositionOp::OP_OVER ||
   3393                         CurrentState().op == CompositionOp::OP_SOURCE);
   3394  const gfx::Rect fillRect(aX, aY, aW, aH);
   3395  EnsureTarget(discardContent ? &fillRect : nullptr, discardContent && isColor);
   3396  if (!IsTargetValid()) {
   3397    return;
   3398  }
   3399 
   3400  gfx::Rect bounds;
   3401  const bool needBounds = NeedToCalculateBounds();
   3402  if (!IsTargetValid()) {
   3403    return;
   3404  }
   3405  if (needBounds) {
   3406    bounds = mTarget->GetTransform().TransformBounds(fillRect);
   3407  }
   3408 
   3409  AntialiasMode antialiasMode = CurrentState().imageSmoothingEnabled
   3410                                    ? AntialiasMode::DEFAULT
   3411                                    : AntialiasMode::NONE;
   3412 
   3413  AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   3414  CompositionOp op = target.UsedOperation();
   3415  if (!target) {
   3416    return;
   3417  }
   3418  target.FillRect(gfx::Rect(aX, aY, aW, aH),
   3419                  CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
   3420                  DrawOptions(CurrentState().globalAlpha, op, antialiasMode));
   3421 
   3422  RedrawUser(gfxRect(aX, aY, aW, aH));
   3423 }
   3424 
   3425 void CanvasRenderingContext2D::StrokeRect(double aX, double aY, double aW,
   3426                                          double aH) {
   3427  if (!aW && !aH) {
   3428    return;
   3429  }
   3430 
   3431  if (!ValidateRect(aX, aY, aW, aH, true)) {
   3432    return;
   3433  }
   3434 
   3435  EnsureTarget();
   3436  if (!IsTargetValid()) {
   3437    return;
   3438  }
   3439 
   3440  const bool needBounds = NeedToCalculateBounds();
   3441  if (!IsTargetValid()) {
   3442    return;
   3443  }
   3444 
   3445  gfx::Rect bounds;
   3446  if (needBounds) {
   3447    const ContextState& state = CurrentState();
   3448    bounds = gfx::Rect(aX - state.lineWidth / 2.0f, aY - state.lineWidth / 2.0f,
   3449                       aW + state.lineWidth, aH + state.lineWidth);
   3450    bounds = mTarget->GetTransform().TransformBounds(bounds);
   3451  }
   3452 
   3453  if (!IsTargetValid()) {
   3454    return;
   3455  }
   3456 
   3457  if (!aH) {
   3458    CapStyle cap = CapStyle::BUTT;
   3459    if (CurrentState().lineJoin == CanvasLineJoin::Round) {
   3460      cap = CapStyle::ROUND;
   3461    }
   3462    AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   3463    auto op = target.UsedOperation();
   3464    if (!target) {
   3465      return;
   3466    }
   3467 
   3468    const ContextState& state = CurrentState();
   3469    target.StrokeLine(
   3470        Point(aX, aY), Point(aX + aW, aY),
   3471        CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
   3472        StrokeOptions(state.lineWidth, CanvasToGfx(state.lineJoin), cap,
   3473                      state.miterLimit, state.dash.Length(),
   3474                      state.dash.Elements(), state.dashOffset),
   3475        DrawOptions(state.globalAlpha, op));
   3476    return;
   3477  }
   3478 
   3479  if (!aW) {
   3480    CapStyle cap = CapStyle::BUTT;
   3481    if (CurrentState().lineJoin == CanvasLineJoin::Round) {
   3482      cap = CapStyle::ROUND;
   3483    }
   3484    AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   3485    auto op = target.UsedOperation();
   3486    if (!target) {
   3487      return;
   3488    }
   3489 
   3490    const ContextState& state = CurrentState();
   3491    target.StrokeLine(
   3492        Point(aX, aY), Point(aX, aY + aH),
   3493        CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
   3494        StrokeOptions(state.lineWidth, CanvasToGfx(state.lineJoin), cap,
   3495                      state.miterLimit, state.dash.Length(),
   3496                      state.dash.Elements(), state.dashOffset),
   3497        DrawOptions(state.globalAlpha, op));
   3498    return;
   3499  }
   3500 
   3501  AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   3502  auto op = target.UsedOperation();
   3503  if (!target) {
   3504    return;
   3505  }
   3506 
   3507  const ContextState& state = CurrentState();
   3508  target.StrokeRect(
   3509      gfx::Rect(aX, aY, aW, aH),
   3510      CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
   3511      StrokeOptions(state.lineWidth, CanvasToGfx(state.lineJoin),
   3512                    CanvasToGfx(state.lineCap), state.miterLimit,
   3513                    state.dash.Length(), state.dash.Elements(),
   3514                    state.dashOffset),
   3515      DrawOptions(state.globalAlpha, op));
   3516 
   3517  Redraw();
   3518 }
   3519 
   3520 //
   3521 // path bits
   3522 //
   3523 
   3524 void CanvasRenderingContext2D::BeginPath() {
   3525  mPath = nullptr;
   3526  mPathBuilder = nullptr;
   3527  mPathPruned = false;
   3528 }
   3529 
   3530 void CanvasRenderingContext2D::FillImpl(const gfx::Path& aPath) {
   3531  MOZ_ASSERT(IsTargetValid());
   3532  if (aPath.IsEmpty()) {
   3533    return;
   3534  }
   3535 
   3536  const bool needBounds = NeedToCalculateBounds();
   3537  gfx::Rect bounds;
   3538  if (needBounds) {
   3539    bounds = aPath.GetBounds(mTarget->GetTransform());
   3540  }
   3541 
   3542  AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   3543  if (!target) {
   3544    return;
   3545  }
   3546 
   3547  auto op = target.UsedOperation();
   3548  if (!IsTargetValid() || !target) {
   3549    return;
   3550  }
   3551  target.Fill(&aPath,
   3552              CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
   3553              DrawOptions(CurrentState().globalAlpha, op));
   3554  Redraw();
   3555 }
   3556 
   3557 void CanvasRenderingContext2D::Fill(const CanvasWindingRule& aWinding) {
   3558  EnsureTargetAndUserSpacePath(aWinding);
   3559  if (!IsTargetValid()) {
   3560    return;
   3561  }
   3562 
   3563  if (mPath) {
   3564    FillImpl(*mPath);
   3565  }
   3566 }
   3567 
   3568 void CanvasRenderingContext2D::Fill(const CanvasPath& aPath,
   3569                                    const CanvasWindingRule& aWinding) {
   3570  EnsureTarget();
   3571  if (!IsTargetValid()) {
   3572    return;
   3573  }
   3574 
   3575  RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget);
   3576  if (gfxpath) {
   3577    FillImpl(*gfxpath);
   3578  }
   3579 }
   3580 
   3581 void CanvasRenderingContext2D::StrokeImpl(const gfx::Path& aPath) {
   3582  MOZ_ASSERT(IsTargetValid());
   3583  if (aPath.IsEmpty()) {
   3584    return;
   3585  }
   3586 
   3587  const ContextState* state = &CurrentState();
   3588  StrokeOptions strokeOptions(state->lineWidth, CanvasToGfx(state->lineJoin),
   3589                              CanvasToGfx(state->lineCap), state->miterLimit,
   3590                              state->dash.Length(), state->dash.Elements(),
   3591                              state->dashOffset);
   3592  state = nullptr;
   3593 
   3594  const bool needBounds = NeedToCalculateBounds();
   3595  if (!IsTargetValid()) {
   3596    return;
   3597  }
   3598  gfx::Rect bounds;
   3599  if (needBounds) {
   3600    bounds = aPath.GetStrokedBounds(strokeOptions, mTarget->GetTransform());
   3601  }
   3602 
   3603  AdjustedTarget target(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   3604  if (!target) {
   3605    return;
   3606  }
   3607 
   3608  auto op = target.UsedOperation();
   3609  if (!IsTargetValid() || !target) {
   3610    return;
   3611  }
   3612  target.Stroke(&aPath,
   3613                CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
   3614                strokeOptions, DrawOptions(CurrentState().globalAlpha, op));
   3615  Redraw();
   3616 }
   3617 
   3618 void CanvasRenderingContext2D::Stroke() {
   3619  mFeatureUsage |= CanvasFeatureUsage::Stroke;
   3620 
   3621  EnsureTargetAndUserSpacePath();
   3622  if (!IsTargetValid()) {
   3623    return;
   3624  }
   3625 
   3626  if (mPath) {
   3627    StrokeImpl(*mPath);
   3628  }
   3629 }
   3630 
   3631 void CanvasRenderingContext2D::Stroke(const CanvasPath& aPath) {
   3632  EnsureTarget();
   3633  if (!IsTargetValid()) {
   3634    return;
   3635  }
   3636  RefPtr<gfx::Path> gfxpath =
   3637      aPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
   3638  if (gfxpath) {
   3639    StrokeImpl(*gfxpath);
   3640  }
   3641 }
   3642 
   3643 void CanvasRenderingContext2D::DrawFocusIfNeeded(
   3644    mozilla::dom::Element& aElement, ErrorResult& aRv) {
   3645  EnsureTargetAndUserSpacePath();
   3646  if (!mPath) {
   3647    return;
   3648  }
   3649 
   3650  if (DrawCustomFocusRing(aElement)) {
   3651    AutoSaveRestore asr(this);
   3652 
   3653    // set state to conforming focus state
   3654    ContextState* state = &CurrentState();
   3655    state->globalAlpha = 1.0;
   3656    state->shadowBlur = 0;
   3657    state->shadowOffset.x = 0;
   3658    state->shadowOffset.y = 0;
   3659    state->op = mozilla::gfx::CompositionOp::OP_OVER;
   3660 
   3661    state->lineCap = CanvasLineCap::Butt;
   3662    state->lineJoin = CanvasLineJoin::Miter;
   3663    state->lineWidth = 1;
   3664    state->dash.Clear();
   3665 
   3666    // color and style of the rings is the same as for image maps
   3667    // set the background focus color
   3668    state->SetColorStyle(Style::STROKE, NS_RGBA(255, 255, 255, 255));
   3669    state = nullptr;
   3670 
   3671    // draw the focus ring
   3672    Stroke();
   3673    if (!mPath) {
   3674      return;
   3675    }
   3676 
   3677    // set dashing for foreground
   3678    nsTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
   3679    for (uint32_t i = 0; i < 2; ++i) {
   3680      if (!dash.AppendElement(1, fallible)) {
   3681        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   3682        return;
   3683      }
   3684    }
   3685 
   3686    // set the foreground focus color
   3687    CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(0, 0, 0, 255));
   3688    // draw the focus ring
   3689    Stroke();
   3690    if (!mPath) {
   3691      return;
   3692    }
   3693  }
   3694 }
   3695 
   3696 bool CanvasRenderingContext2D::DrawCustomFocusRing(Element& aElement) {
   3697  if (!aElement.State().HasState(ElementState::FOCUSRING)) {
   3698    return false;
   3699  }
   3700 
   3701  HTMLCanvasElement* canvas = GetCanvas();
   3702  if (!canvas || !aElement.IsInclusiveDescendantOf(canvas)) {
   3703    return false;
   3704  }
   3705 
   3706  EnsureTargetAndUserSpacePath();
   3707  return true;
   3708 }
   3709 
   3710 void CanvasRenderingContext2D::Clip(const CanvasWindingRule& aWinding) {
   3711  EnsureUserSpacePath(aWinding);
   3712 
   3713  if (!mPath) {
   3714    return;
   3715  }
   3716 
   3717  if (IsTargetValid()) {
   3718    mTarget->PushClip(mPath);
   3719  } else {
   3720    mTargetNeedsClipsAndTransforms = true;
   3721  }
   3722  CurrentState().clipsAndTransforms.AppendElement(ClipState(mPath));
   3723 }
   3724 
   3725 void CanvasRenderingContext2D::Clip(const CanvasPath& aPath,
   3726                                    const CanvasWindingRule& aWinding) {
   3727  if (!mBufferProvider) {
   3728    EnsureTarget();
   3729    if (!IsTargetValid()) {
   3730      return;
   3731    }
   3732  }
   3733 
   3734  RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mPathType);
   3735 
   3736  if (!gfxpath) {
   3737    return;
   3738  }
   3739 
   3740  if (IsTargetValid()) {
   3741    mTarget->PushClip(gfxpath);
   3742  } else {
   3743    mTargetNeedsClipsAndTransforms = true;
   3744  }
   3745  CurrentState().clipsAndTransforms.AppendElement(ClipState(gfxpath));
   3746 }
   3747 
   3748 void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
   3749                                     double aY2, double aRadius,
   3750                                     ErrorResult& aError) {
   3751  if (aRadius < 0) {
   3752    return aError.ThrowIndexSizeError("Negative radius");
   3753  }
   3754 
   3755  if (!EnsureWritablePath()) {
   3756    return;
   3757  }
   3758 
   3759  // Current point in user space!
   3760  Point p0 = mPathBuilder->CurrentPoint();
   3761 
   3762  Point p1(aX1, aY1);
   3763  Point p2(aX2, aY2);
   3764 
   3765  if (!p1.IsFinite() || !p2.IsFinite() || !std::isfinite(aRadius)) {
   3766    return;
   3767  }
   3768 
   3769  // Execute these calculations in double precision to avoid cumulative
   3770  // rounding errors.
   3771  double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx,
   3772      cy, angle0, angle1;
   3773  bool anticlockwise;
   3774 
   3775  if (p0 == p1 || p1 == p2 || aRadius == 0) {
   3776    LineTo(p1);
   3777    return;
   3778  }
   3779 
   3780  // Check for colinearity
   3781  dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) +
   3782        (p2.y.value - p1.y.value) * (p1.x.value - p0.x.value);
   3783  if (dir == 0) {
   3784    LineTo(p1);
   3785    return;
   3786  }
   3787 
   3788  // XXX - Math for this code was already available from the non-azure code
   3789  // and would be well tested. Perhaps converting to bezier directly might
   3790  // be more efficient longer run.
   3791  a2 = (p0.x - aX1) * (p0.x - aX1) + (p0.y - aY1) * (p0.y - aY1);
   3792  b2 = (aX1 - aX2) * (aX1 - aX2) + (aY1 - aY2) * (aY1 - aY2);
   3793  c2 = (p0.x - aX2) * (p0.x - aX2) + (p0.y - aY2) * (p0.y - aY2);
   3794  cosx = (a2 + b2 - c2) / (2 * sqrt(a2 * b2));
   3795 
   3796  sinx = sqrt(1 - cosx * cosx);
   3797  d = aRadius / ((1 - cosx) / sinx);
   3798 
   3799  anx = (aX1 - p0.x) / sqrt(a2);
   3800  any = (aY1 - p0.y) / sqrt(a2);
   3801  bnx = (aX1 - aX2) / sqrt(b2);
   3802  bny = (aY1 - aY2) / sqrt(b2);
   3803  x3 = aX1 - anx * d;
   3804  y3 = aY1 - any * d;
   3805  x4 = aX1 - bnx * d;
   3806  y4 = aY1 - bny * d;
   3807  anticlockwise = (dir < 0);
   3808  cx = x3 + any * aRadius * (anticlockwise ? 1 : -1);
   3809  cy = y3 - anx * aRadius * (anticlockwise ? 1 : -1);
   3810  angle0 = atan2((y3 - cy), (x3 - cx));
   3811  angle1 = atan2((y4 - cy), (x4 - cx));
   3812 
   3813  LineTo(x3, y3);
   3814 
   3815  Arc(cx, cy, aRadius, angle0, angle1, anticlockwise, aError);
   3816 }
   3817 
   3818 void CanvasRenderingContext2D::Arc(double aX, double aY, double aR,
   3819                                   double aStartAngle, double aEndAngle,
   3820                                   bool aAnticlockwise, ErrorResult& aError) {
   3821  if (aR < 0.0) {
   3822    return aError.ThrowIndexSizeError("Negative radius");
   3823  }
   3824  if (aStartAngle == aEndAngle) {
   3825    LineTo(aX + aR * cos(aStartAngle), aY + aR * sin(aStartAngle));
   3826    return;
   3827  }
   3828 
   3829  if (!EnsureWritablePath()) {
   3830    return;
   3831  }
   3832 
   3833  EnsureActivePath();
   3834 
   3835  mPathBuilder->Arc(Point(aX, aY), aR, aStartAngle, aEndAngle, aAnticlockwise);
   3836  mPathPruned = false;
   3837 }
   3838 
   3839 void CanvasRenderingContext2D::Rect(double aX, double aY, double aW,
   3840                                    double aH) {
   3841  if (!EnsureWritablePath()) {
   3842    return;
   3843  }
   3844 
   3845  if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
   3846      !std::isfinite(aH)) {
   3847    return;
   3848  }
   3849 
   3850  EnsureCapped();
   3851  mPathBuilder->MoveTo(Point(aX, aY));
   3852  if (aW == 0 && aH == 0) {
   3853    return;
   3854  }
   3855  mPathBuilder->LineTo(Point(aX + aW, aY));
   3856  mPathBuilder->LineTo(Point(aX + aW, aY + aH));
   3857  mPathBuilder->LineTo(Point(aX, aY + aH));
   3858  mPathBuilder->Close();
   3859 }
   3860 
   3861 // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-roundrect
   3862 static void RoundRectImpl(
   3863    PathBuilder* aPathBuilder, const Maybe<Matrix>& aTransform, double aX,
   3864    double aY, double aW, double aH,
   3865    const UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence&
   3866        aRadii,
   3867    ErrorResult& aError) {
   3868  // Step 1. If any of x, y, w, or h are infinite or NaN, then return.
   3869  if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
   3870      !std::isfinite(aH)) {
   3871    return;
   3872  }
   3873 
   3874  nsTArray<OwningUnrestrictedDoubleOrDOMPointInit> radii;
   3875  // Step 2. If radii is an unrestricted double or DOMPointInit, then set radii
   3876  // to « radii ».
   3877  if (aRadii.IsUnrestrictedDouble()) {
   3878    radii.AppendElement()->SetAsUnrestrictedDouble() =
   3879        aRadii.GetAsUnrestrictedDouble();
   3880  } else if (aRadii.IsDOMPointInit()) {
   3881    radii.AppendElement()->SetAsDOMPointInit() = aRadii.GetAsDOMPointInit();
   3882  } else {
   3883    radii = aRadii.GetAsUnrestrictedDoubleOrDOMPointInitSequence();
   3884    // Step 3. If radii is not a list of size one, two, three, or
   3885    // four, then throw a RangeError.
   3886    if (radii.Length() < 1 || radii.Length() > 4) {
   3887      aError.ThrowRangeError("Can have between 1 and 4 radii");
   3888      return;
   3889    }
   3890  }
   3891 
   3892  // Step 4. Let normalizedRadii be an empty list.
   3893  AutoTArray<Size, 4> normalizedRadii;
   3894 
   3895  // Step 5. For each radius of radii:
   3896  for (const auto& radius : radii) {
   3897    // Step 5.1. If radius is a DOMPointInit:
   3898    if (radius.IsDOMPointInit()) {
   3899      const DOMPointInit& point = radius.GetAsDOMPointInit();
   3900      // Step 5.1.1. If radius["x"] or radius["y"] is infinite or NaN, then
   3901      // return.
   3902      if (!std::isfinite(point.mX) || !std::isfinite(point.mY)) {
   3903        return;
   3904      }
   3905 
   3906      // Step 5.1.2. If radius["x"] or radius["y"] is negative, then
   3907      // throw a RangeError.
   3908      if (point.mX < 0 || point.mY < 0) {
   3909        aError.ThrowRangeError("Radius can not be negative");
   3910        return;
   3911      }
   3912 
   3913      // Step 5.1.3. Otherwise, append radius to
   3914      // normalizedRadii.
   3915      normalizedRadii.AppendElement(
   3916          Size(gfx::Float(point.mX), gfx::Float(point.mY)));
   3917      continue;
   3918    }
   3919 
   3920    // Step 5.2. If radius is a unrestricted double:
   3921    double r = radius.GetAsUnrestrictedDouble();
   3922    // Step 5.2.1. If radius is infinite or NaN, then return.
   3923    if (!std::isfinite(r)) {
   3924      return;
   3925    }
   3926 
   3927    // Step 5.2.2. If radius is negative, then throw a RangeError.
   3928    if (r < 0) {
   3929      aError.ThrowRangeError("Radius can not be negative");
   3930      return;
   3931    }
   3932 
   3933    // Step 5.2.3. Otherwise append «[ "x" → radius, "y" → radius ]» to
   3934    // normalizedRadii.
   3935    normalizedRadii.AppendElement(Size(gfx::Float(r), gfx::Float(r)));
   3936  }
   3937 
   3938  // Step 6. Let upperLeft, upperRight, lowerRight, and lowerLeft be null.
   3939  Size upperLeft, upperRight, lowerRight, lowerLeft;
   3940 
   3941  if (normalizedRadii.Length() == 4) {
   3942    // Step 7. If normalizedRadii's size is 4, then set upperLeft to
   3943    // normalizedRadii[0], set upperRight to normalizedRadii[1], set lowerRight
   3944    // to normalizedRadii[2], and set lowerLeft to normalizedRadii[3].
   3945    upperLeft = normalizedRadii[0];
   3946    upperRight = normalizedRadii[1];
   3947    lowerRight = normalizedRadii[2];
   3948    lowerLeft = normalizedRadii[3];
   3949  } else if (normalizedRadii.Length() == 3) {
   3950    // Step 8. If normalizedRadii's size is 3, then set upperLeft to
   3951    // normalizedRadii[0], set upperRight and lowerLeft to normalizedRadii[1],
   3952    // and set lowerRight to normalizedRadii[2].
   3953    upperLeft = normalizedRadii[0];
   3954    upperRight = normalizedRadii[1];
   3955    lowerRight = normalizedRadii[2];
   3956    lowerLeft = normalizedRadii[1];
   3957  } else if (normalizedRadii.Length() == 2) {
   3958    // Step 9. If normalizedRadii's size is 2, then set upperLeft and lowerRight
   3959    // to normalizedRadii[0] and set upperRight and lowerLeft to
   3960    // normalizedRadii[1].
   3961    upperLeft = normalizedRadii[0];
   3962    upperRight = normalizedRadii[1];
   3963    lowerRight = normalizedRadii[0];
   3964    lowerLeft = normalizedRadii[1];
   3965  } else {
   3966    // Step 10. If normalizedRadii's size is 1, then set upperLeft, upperRight,
   3967    // lowerRight, and lowerLeft to normalizedRadii[0].
   3968    MOZ_ASSERT(normalizedRadii.Length() == 1);
   3969    upperLeft = normalizedRadii[0];
   3970    upperRight = normalizedRadii[0];
   3971    lowerRight = normalizedRadii[0];
   3972    lowerLeft = normalizedRadii[0];
   3973  }
   3974 
   3975  // This is not as specified but copied from Chrome.
   3976  // XXX Maybe if we implemented Step 12 (the path algorithm) per
   3977  // spec this wouldn't be needed?
   3978  Float x(aX), y(aY), w(aW), h(aH);
   3979  bool clockwise = true;
   3980  if (w < 0) {
   3981    // Horizontal flip
   3982    clockwise = false;
   3983    x += w;
   3984    w = -w;
   3985    std::swap(upperLeft, upperRight);
   3986    std::swap(lowerLeft, lowerRight);
   3987  }
   3988 
   3989  if (h < 0) {
   3990    // Vertical flip
   3991    clockwise = !clockwise;
   3992    y += h;
   3993    h = -h;
   3994    std::swap(upperLeft, lowerLeft);
   3995    std::swap(upperRight, lowerRight);
   3996  }
   3997 
   3998  // Step 11. Corner curves must not overlap. Scale all radii to prevent this:
   3999  // Step 11.1. Let top be upperLeft["x"] + upperRight["x"].
   4000  Float top = upperLeft.width + upperRight.width;
   4001  // Step 11.2. Let right be upperRight["y"] + lowerRight["y"].
   4002  Float right = upperRight.height + lowerRight.height;
   4003  // Step 11.3. Let bottom be lowerRight["x"] + lowerLeft["x"].
   4004  Float bottom = lowerRight.width + lowerLeft.width;
   4005  // Step 11.4. Let left be upperLeft["y"] + lowerLeft["y"].
   4006  Float left = upperLeft.height + lowerLeft.height;
   4007  // Step 11.5. Let scale be the minimum value of the ratios w / top, h / right,
   4008  // w / bottom, h / left.
   4009  Float scale = std::min({w / top, h / right, w / bottom, h / left});
   4010  // Step 11.6. If scale is less than 1, then set the x and y members of
   4011  // upperLeft, upperRight, lowerLeft, and lowerRight to their current values
   4012  // multiplied by scale.
   4013  if (scale < 1.0f) {
   4014    upperLeft = upperLeft * scale;
   4015    upperRight = upperRight * scale;
   4016    lowerLeft = lowerLeft * scale;
   4017    lowerRight = lowerRight * scale;
   4018  }
   4019 
   4020  // Step 12. Create a new subpath:
   4021  // Step 13. Mark the subpath as closed.
   4022  // Note: Implemented by AppendRoundedRectToPath, which is shared with CSS
   4023  // borders etc.
   4024  gfx::Rect rect{x, y, w, h};
   4025  RectCornerRadii cornerRadii(upperLeft, upperRight, lowerRight, lowerLeft);
   4026  AppendRoundedRectToPath(aPathBuilder, rect, cornerRadii, clockwise,
   4027                          aTransform);
   4028 
   4029  // Step 14. Create a new subpath with the point (x, y) as the only point in
   4030  // the subpath.
   4031  // XXX We don't seem to be doing this for ::Rect either?
   4032 }
   4033 
   4034 void CanvasRenderingContext2D::RoundRect(
   4035    double aX, double aY, double aW, double aH,
   4036    const UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence&
   4037        aRadii,
   4038    ErrorResult& aError) {
   4039  if (!EnsureWritablePath()) {
   4040    return;
   4041  }
   4042 
   4043  PathBuilder* builder = mPathBuilder;
   4044  Maybe<Matrix> transform = Nothing();
   4045 
   4046  EnsureCapped();
   4047  RoundRectImpl(builder, transform, aX, aY, aW, aH, aRadii, aError);
   4048 }
   4049 
   4050 void CanvasRenderingContext2D::Ellipse(double aX, double aY, double aRadiusX,
   4051                                       double aRadiusY, double aRotation,
   4052                                       double aStartAngle, double aEndAngle,
   4053                                       bool aAnticlockwise,
   4054                                       ErrorResult& aError) {
   4055  if (aRadiusX < 0.0 || aRadiusY < 0.0) {
   4056    return aError.ThrowIndexSizeError("Negative radius");
   4057  }
   4058 
   4059  if (!EnsureWritablePath()) {
   4060    return;
   4061  }
   4062 
   4063  ArcToBezier(this, Point(aX, aY), Size(aRadiusX, aRadiusY), aStartAngle,
   4064              aEndAngle, aAnticlockwise, aRotation);
   4065  mPathPruned = false;
   4066 }
   4067 
   4068 void CanvasRenderingContext2D::FlushPathTransform() {
   4069  if (!mPathTransformDirty) {
   4070    return;
   4071  }
   4072  Matrix newTransform = GetCurrentTransform();
   4073  if (mPath || mPathBuilder) {
   4074    Matrix inverse = newTransform;
   4075    if (!inverse.ExactlyEquals(mPathTransform) && inverse.Invert()) {
   4076      TransformCurrentPath(mPathTransform * inverse);
   4077    }
   4078  }
   4079  mPathTransform = newTransform;
   4080  mPathTransformDirty = false;
   4081 }
   4082 
   4083 bool CanvasRenderingContext2D::EnsureWritablePath() {
   4084  if (!mBufferProvider) {
   4085    EnsureTarget();
   4086    // NOTE: IsTargetValid() may be false here (mTarget == sErrorTarget) but we
   4087    // go ahead and create a path anyway since callers depend on that.
   4088    if (!mTarget) {
   4089      return false;
   4090    }
   4091  }
   4092 
   4093  FillRule fillRule = CurrentState().fillRule;
   4094 
   4095  if (mPathTransformDirty) {
   4096    FlushPathTransform();
   4097  }
   4098 
   4099  if (mPathBuilder) {
   4100    return true;
   4101  }
   4102 
   4103  if (!mPath) {
   4104    if (mBufferProvider) {
   4105      mPathBuilder = Factory::CreatePathBuilder(mPathType, fillRule);
   4106    } else {
   4107      mPathBuilder = mTarget->CreatePathBuilder(fillRule);
   4108    }
   4109  } else {
   4110    mPathBuilder = Path::ToBuilder(mPath.forget(), fillRule);
   4111  }
   4112  return true;
   4113 }
   4114 
   4115 bool CanvasRenderingContext2D::EnsureBufferProvider() {
   4116  if (mBufferProvider) {
   4117    return true;
   4118  }
   4119  EnsureTarget();
   4120  return IsTargetValid();
   4121 }
   4122 
   4123 void CanvasRenderingContext2D::EnsureUserSpacePath(
   4124    const CanvasWindingRule& aWinding) {
   4125  FillRule fillRule = CurrentState().fillRule;
   4126  if (aWinding == CanvasWindingRule::Evenodd) {
   4127    fillRule = FillRule::FILL_EVEN_ODD;
   4128  }
   4129 
   4130  if (!EnsureBufferProvider()) {
   4131    return;
   4132  }
   4133 
   4134  if (mPathTransformDirty) {
   4135    FlushPathTransform();
   4136  }
   4137 
   4138  if (!mPath && !mPathBuilder) {
   4139    mPathBuilder = Factory::CreatePathBuilder(mPathType, fillRule);
   4140  }
   4141 
   4142  if (mPathBuilder) {
   4143    EnsureCapped();
   4144    mPath = mPathBuilder->Finish();
   4145    mPathBuilder = nullptr;
   4146  }
   4147 
   4148  if (mPath && mPath->GetFillRule() != fillRule) {
   4149    Path::SetFillRule(mPath, fillRule);
   4150  }
   4151 
   4152  NS_ASSERTION(mPath, "mPath should exist");
   4153 }
   4154 
   4155 void CanvasRenderingContext2D::TransformCurrentPath(const Matrix& aTransform) {
   4156  if (mPathBuilder) {
   4157    mPathBuilder = Path::ToBuilder(mPathBuilder->Finish(), aTransform);
   4158  } else if (mPath) {
   4159    mPathBuilder = Path::ToBuilder(mPath.forget(), aTransform);
   4160  }
   4161 }
   4162 
   4163 //
   4164 // text
   4165 //
   4166 
   4167 void CanvasRenderingContext2D::SetFont(const nsACString& aFont,
   4168                                       ErrorResult& aError) {
   4169  mFeatureUsage |= CanvasFeatureUsage::SetFont;
   4170 
   4171  SetFontInternal(aFont, aError);
   4172  if (aError.Failed()) {
   4173    return;
   4174  }
   4175 
   4176  // Setting the font attribute magically resets fontVariantCaps and
   4177  // fontStretch to normal.
   4178  // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
   4179  SetFontVariantCaps(CanvasFontVariantCaps::Normal);
   4180  SetFontStretch(CanvasFontStretch::Normal);
   4181 
   4182  // If letterSpacing or wordSpacing is present, recompute to account for
   4183  // changes to font-relative dimensions.
   4184  UpdateSpacing();
   4185 }
   4186 
   4187 static float QuantizeFontSize(float aSize) {
   4188  // Based on the Veltkamp-Dekker float-splitting algorithm, see e.g.
   4189  // https://indico.cern.ch/event/313684/contributions/1687773/attachments/600513/826490/FPArith-Part2.pdf
   4190  // A 32-bit float has 24 bits of precision (23 stored, plus an implicit 1 bit
   4191  // at the start of the mantissa).
   4192  constexpr int bitsToDrop = 17;  // leaving 7 bits of precision
   4193  constexpr int scale = 1 << bitsToDrop;
   4194  float d = aSize * (scale + 1);
   4195  float t = d - aSize;
   4196  return d - t;
   4197 }
   4198 
   4199 bool CanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
   4200                                               ErrorResult& aError) {
   4201  RefPtr<PresShell> presShell = GetPresShell();
   4202  if (!presShell) {
   4203    return SetFontInternalDisconnected(aFont, aError);
   4204  }
   4205 
   4206  nsPresContext* c = presShell->GetPresContext();
   4207  FontStyleCacheKey key{aFont, c->RestyleManager()->GetRestyleGeneration()};
   4208  auto entry = mFontStyleCache.Lookup(key);
   4209  if (!entry) {
   4210    FontStyleData newData;
   4211    newData.mKey = key;
   4212    newData.mStyle = GetFontStyleForServo(mCanvasElement, aFont, presShell,
   4213                                          newData.mUsedFont, aError);
   4214    entry.Set(newData);
   4215  }
   4216 
   4217  const auto& data = entry.Data();
   4218  if (!data.mStyle) {
   4219    return false;
   4220  }
   4221 
   4222  const nsStyleFont* fontStyle = data.mStyle->StyleFont();
   4223 
   4224  // Purposely ignore the font size that respects the user's minimum
   4225  // font preference (fontStyle->mFont.size) in favor of the computed
   4226  // size (fontStyle->mSize).  See
   4227  // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
   4228  // FIXME: Nobody initializes mAllowZoom for servo?
   4229  // MOZ_ASSERT(!fontStyle->mAllowZoom,
   4230  //           "expected text zoom to be disabled on this nsStyleFont");
   4231  nsFont resizedFont(fontStyle->mFont);
   4232  // Create a font group working in units of CSS pixels instead of the usual
   4233  // device pixels, to avoid being affected by page zoom. nsFontMetrics will
   4234  // convert nsFont size in app units to device pixels for the font group, so
   4235  // here we first apply to the size the equivalent of a conversion from device
   4236  // pixels to CSS pixels, to adjust for the difference in expectations from
   4237  // other nsFontMetrics clients.
   4238  resizedFont.size =
   4239      fontStyle->mSize.ScaledBy(1.0f / c->CSSToDevPixelScale().scale);
   4240 
   4241  // Quantize font size to avoid filling caches with thousands of fonts that
   4242  // differ by imperceptibly-tiny size deltas.
   4243  resizedFont.size = StyleCSSPixelLength::FromPixels(
   4244      QuantizeFontSize(resizedFont.size.ToCSSPixels()));
   4245 
   4246  resizedFont.kerning = CanvasToGfx(CurrentState().fontKerning);
   4247 
   4248  // fontStretch handling: if fontStretch is not 'normal', apply it;
   4249  // if it is normal, then use whatever the shorthand set.
   4250  // XXX(jfkthame) The interaction between the shorthand and the separate attr
   4251  // here is not clearly spec'd, and we may want to reconsider it (or revise
   4252  // the available values); see https://github.com/whatwg/html/issues/8103.
   4253  switch (CurrentState().fontStretch) {
   4254    case CanvasFontStretch::Normal:
   4255      // Leave whatever the shorthand set.
   4256      break;
   4257    case CanvasFontStretch::Ultra_condensed:
   4258      resizedFont.stretch = StyleFontStretch::ULTRA_CONDENSED;
   4259      break;
   4260    case CanvasFontStretch::Extra_condensed:
   4261      resizedFont.stretch = StyleFontStretch::EXTRA_CONDENSED;
   4262      break;
   4263    case CanvasFontStretch::Condensed:
   4264      resizedFont.stretch = StyleFontStretch::CONDENSED;
   4265      break;
   4266    case CanvasFontStretch::Semi_condensed:
   4267      resizedFont.stretch = StyleFontStretch::SEMI_CONDENSED;
   4268      break;
   4269    case CanvasFontStretch::Semi_expanded:
   4270      resizedFont.stretch = StyleFontStretch::SEMI_EXPANDED;
   4271      break;
   4272    case CanvasFontStretch::Expanded:
   4273      resizedFont.stretch = StyleFontStretch::EXPANDED;
   4274      break;
   4275    case CanvasFontStretch::Extra_expanded:
   4276      resizedFont.stretch = StyleFontStretch::EXTRA_EXPANDED;
   4277      break;
   4278    case CanvasFontStretch::Ultra_expanded:
   4279      resizedFont.stretch = StyleFontStretch::ULTRA_EXPANDED;
   4280      break;
   4281    default:
   4282      MOZ_ASSERT_UNREACHABLE("unknown stretch value");
   4283      break;
   4284  }
   4285 
   4286  // fontVariantCaps handling: if fontVariantCaps is not 'normal', apply it;
   4287  // if it is, then use the smallCaps boolean from the shorthand.
   4288  // XXX(jfkthame) The interaction between the shorthand and the separate attr
   4289  // here is not clearly spec'd, and we may want to reconsider it (or revise
   4290  // the available values); see https://github.com/whatwg/html/issues/8103.
   4291  switch (CurrentState().fontVariantCaps) {
   4292    case CanvasFontVariantCaps::Normal:
   4293      // Leave whatever the shorthand set.
   4294      break;
   4295    case CanvasFontVariantCaps::Small_caps:
   4296      resizedFont.variantCaps = NS_FONT_VARIANT_CAPS_SMALLCAPS;
   4297      break;
   4298    case CanvasFontVariantCaps::All_small_caps:
   4299      resizedFont.variantCaps = NS_FONT_VARIANT_CAPS_ALLSMALL;
   4300      break;
   4301    case CanvasFontVariantCaps::Petite_caps:
   4302      resizedFont.variantCaps = NS_FONT_VARIANT_CAPS_PETITECAPS;
   4303      break;
   4304    case CanvasFontVariantCaps::All_petite_caps:
   4305      resizedFont.variantCaps = NS_FONT_VARIANT_CAPS_ALLPETITE;
   4306      break;
   4307    case CanvasFontVariantCaps::Unicase:
   4308      resizedFont.variantCaps = NS_FONT_VARIANT_CAPS_UNICASE;
   4309      break;
   4310    case CanvasFontVariantCaps::Titling_caps:
   4311      resizedFont.variantCaps = NS_FONT_VARIANT_CAPS_TITLING;
   4312      break;
   4313    default:
   4314      MOZ_ASSERT_UNREACHABLE("unknown caps value");
   4315      break;
   4316  }
   4317 
   4318  c->Document()->FlushUserFontSet();
   4319 
   4320  nsFontMetrics::Params params;
   4321  params.language = fontStyle->mLanguage;
   4322  params.explicitLanguage = fontStyle->mExplicitLanguage;
   4323  params.userFontSet = c->GetUserFontSet();
   4324  params.textPerf = c->GetTextPerfMetrics();
   4325 #ifdef XP_WIN
   4326  params.allowForceGDIClassic = false;
   4327 #endif
   4328  RefPtr<nsFontMetrics> metrics = c->GetMetricsFor(resizedFont, params);
   4329 
   4330  gfxFontGroup* newFontGroup = metrics->GetThebesFontGroup();
   4331  CurrentState().fontGroup = newFontGroup;
   4332  NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
   4333  CurrentState().font = data.mUsedFont;
   4334  CurrentState().fontFont = fontStyle->mFont;
   4335  CurrentState().fontFont.size = fontStyle->mSize;
   4336  CurrentState().fontComputedStyle = data.mStyle;
   4337 
   4338  return true;
   4339 }
   4340 
   4341 static nsAutoCString FamilyListToString(
   4342    const StyleFontFamilyList& aFamilyList) {
   4343  return StringJoin(", "_ns, aFamilyList.list.AsSpan(),
   4344                    [](nsACString& dst, const StyleSingleFontFamily& name) {
   4345                      name.AppendToString(dst);
   4346                    });
   4347 }
   4348 
   4349 static void SerializeFontForCanvas(const StyleFontFamilyList& aList,
   4350                                   const gfxFontStyle& aStyle,
   4351                                   nsACString& aUsedFont) {
   4352  // Re-serialize the font shorthand as required by the canvas spec.
   4353  aUsedFont.Truncate();
   4354 
   4355  if (!aStyle.style.IsNormal()) {
   4356    aStyle.style.ToString(aUsedFont);
   4357    aUsedFont.Append(" ");
   4358  }
   4359 
   4360  // font-weight is serialized as a number
   4361  if (!aStyle.weight.IsNormal()) {
   4362    aUsedFont.AppendFloat(aStyle.weight.ToFloat());
   4363    aUsedFont.Append(" ");
   4364  }
   4365 
   4366  // font-stretch is serialized using CSS Fonts 3 keywords, not percentages.
   4367  if (!aStyle.stretch.IsNormal() &&
   4368      Servo_FontStretch_SerializeKeyword(&aStyle.stretch, &aUsedFont)) {
   4369    aUsedFont.Append(" ");
   4370  }
   4371 
   4372  if (aStyle.variantCaps == NS_FONT_VARIANT_CAPS_SMALLCAPS) {
   4373    aUsedFont.Append("small-caps ");
   4374  }
   4375 
   4376  // Serialize the computed (not specified) size, and the family name(s).
   4377  aUsedFont.AppendFloat(aStyle.size);
   4378  aUsedFont.Append("px ");
   4379  aUsedFont.Append(FamilyListToString(aList));
   4380 }
   4381 
   4382 bool CanvasRenderingContext2D::SetFontInternalDisconnected(
   4383    const nsACString& aFont, ErrorResult& aError) {
   4384  FontFaceSet* fontFaceSet = nullptr;
   4385  if (mCanvasElement) {
   4386    fontFaceSet = mCanvasElement->OwnerDoc()->Fonts();
   4387  } else {
   4388    nsIGlobalObject* global = GetParentObject();
   4389    fontFaceSet = global ? global->GetFonts() : nullptr;
   4390  }
   4391 
   4392  FontFaceSetImpl* fontFaceSetImpl =
   4393      fontFaceSet ? fontFaceSet->GetImpl() : nullptr;
   4394  RefPtr<URLExtraData> urlExtraData =
   4395      fontFaceSetImpl ? fontFaceSetImpl->GetURLExtraData() : nullptr;
   4396 
   4397  if (NS_WARN_IF(!urlExtraData)) {
   4398    // Provided we have a FontFaceSetImpl object, this should only happen on
   4399    // worker threads, where we failed to initialize the worker before it was
   4400    // shutdown.
   4401    aError.ThrowInvalidStateError("Missing URLExtraData");
   4402    return false;
   4403  }
   4404 
   4405  if (fontFaceSetImpl) {
   4406    fontFaceSetImpl->FlushUserFontSet();
   4407  }
   4408 
   4409  // In the OffscreenCanvas case we don't have the context necessary to call
   4410  // GetFontStyleForServo(), as we do in the main-thread canvas context, so
   4411  // instead we borrow ParseFontShorthandForMatching to parse the attribute.
   4412  StyleFontFamilyList list;
   4413  gfxFontStyle fontStyle;
   4414  float size = 0.0f;
   4415  bool smallCaps = false;
   4416  if (!ServoCSSParser::ParseFontShorthandForMatching(
   4417          aFont, urlExtraData, list, fontStyle.style, fontStyle.stretch,
   4418          fontStyle.weight, &size, &smallCaps)) {
   4419    return false;
   4420  }
   4421 
   4422  fontStyle.size = QuantizeFontSize(size);
   4423 #ifdef XP_WIN
   4424  fontStyle.allowForceGDIClassic = false;
   4425 #endif
   4426 
   4427  switch (CurrentState().fontStretch) {
   4428    case CanvasFontStretch::Normal:
   4429      // Leave whatever the shorthand set.
   4430      break;
   4431    case CanvasFontStretch::Ultra_condensed:
   4432      fontStyle.stretch = StyleFontStretch::ULTRA_CONDENSED;
   4433      break;
   4434    case CanvasFontStretch::Extra_condensed:
   4435      fontStyle.stretch = StyleFontStretch::EXTRA_CONDENSED;
   4436      break;
   4437    case CanvasFontStretch::Condensed:
   4438      fontStyle.stretch = StyleFontStretch::CONDENSED;
   4439      break;
   4440    case CanvasFontStretch::Semi_condensed:
   4441      fontStyle.stretch = StyleFontStretch::SEMI_CONDENSED;
   4442      break;
   4443    case CanvasFontStretch::Semi_expanded:
   4444      fontStyle.stretch = StyleFontStretch::SEMI_EXPANDED;
   4445      break;
   4446    case CanvasFontStretch::Expanded:
   4447      fontStyle.stretch = StyleFontStretch::EXPANDED;
   4448      break;
   4449    case CanvasFontStretch::Extra_expanded:
   4450      fontStyle.stretch = StyleFontStretch::EXTRA_EXPANDED;
   4451      break;
   4452    case CanvasFontStretch::Ultra_expanded:
   4453      fontStyle.stretch = StyleFontStretch::ULTRA_EXPANDED;
   4454      break;
   4455    default:
   4456      MOZ_ASSERT_UNREACHABLE("unknown stretch value");
   4457      break;
   4458  }
   4459 
   4460  // fontVariantCaps handling: if fontVariantCaps is not 'normal', apply it;
   4461  // if it is, then use the smallCaps boolean from the shorthand.
   4462  // XXX(jfkthame) The interaction between the shorthand and the separate attr
   4463  // here is not clearly spec'd, and we may want to reconsider it (or revise
   4464  // the available values); see https://github.com/whatwg/html/issues/8103.
   4465  switch (CurrentState().fontVariantCaps) {
   4466    case CanvasFontVariantCaps::Normal:
   4467      fontStyle.variantCaps = smallCaps ? NS_FONT_VARIANT_CAPS_SMALLCAPS
   4468                                        : NS_FONT_VARIANT_CAPS_NORMAL;
   4469      break;
   4470    case CanvasFontVariantCaps::Small_caps:
   4471      fontStyle.variantCaps = NS_FONT_VARIANT_CAPS_SMALLCAPS;
   4472      break;
   4473    case CanvasFontVariantCaps::All_small_caps:
   4474      fontStyle.variantCaps = NS_FONT_VARIANT_CAPS_ALLSMALL;
   4475      break;
   4476    case CanvasFontVariantCaps::Petite_caps:
   4477      fontStyle.variantCaps = NS_FONT_VARIANT_CAPS_PETITECAPS;
   4478      break;
   4479    case CanvasFontVariantCaps::All_petite_caps:
   4480      fontStyle.variantCaps = NS_FONT_VARIANT_CAPS_ALLPETITE;
   4481      break;
   4482    case CanvasFontVariantCaps::Unicase:
   4483      fontStyle.variantCaps = NS_FONT_VARIANT_CAPS_UNICASE;
   4484      break;
   4485    case CanvasFontVariantCaps::Titling_caps:
   4486      fontStyle.variantCaps = NS_FONT_VARIANT_CAPS_TITLING;
   4487      break;
   4488    default:
   4489      MOZ_ASSERT_UNREACHABLE("unknown caps value");
   4490      break;
   4491  }
   4492  // If variantCaps is set, we need to disable a gfxFont fast-path.
   4493  fontStyle.noFallbackVariantFeatures =
   4494      (fontStyle.variantCaps == NS_FONT_VARIANT_CAPS_NORMAL);
   4495 
   4496  // Set the kerning feature, if required by the fontKerning attribute.
   4497  gfxFontFeature setting{TRUETYPE_TAG('k', 'e', 'r', 'n'), 0};
   4498  switch (CurrentState().fontKerning) {
   4499    case CanvasFontKerning::None:
   4500      setting.mValue = 0;
   4501      fontStyle.featureSettings.AppendElement(setting);
   4502      break;
   4503    case CanvasFontKerning::Normal:
   4504      setting.mValue = 1;
   4505      fontStyle.featureSettings.AppendElement(setting);
   4506      break;
   4507    default:
   4508      // auto case implies use user agent default
   4509      break;
   4510  }
   4511 
   4512  // If we have a canvas element, get its lang (if known).
   4513  RefPtr<nsAtom> language;
   4514  bool explicitLanguage = false;
   4515  if (mCanvasElement) {
   4516    language = mCanvasElement->FragmentOrElement::GetLang();
   4517    if (language) {
   4518      explicitLanguage = true;
   4519    } else {
   4520      language = mCanvasElement->OwnerDoc()->GetLanguageForStyle();
   4521    }
   4522  } else {
   4523    // Pass the OS default language, to behave similarly to HTML or canvas-
   4524    // element content with no language tag.
   4525    language = nsLanguageAtomService::GetService()->GetLocaleLanguage();
   4526  }
   4527 
   4528  // TODO: Cache fontGroups in the Worker (use an nsFontCache?)
   4529  gfxFontGroup* fontGroup =
   4530      new gfxFontGroup(mOffscreenCanvas,  // aFontVisibilityProvider
   4531                       list,              // aFontFamilyList
   4532                       &fontStyle,        // aStyle
   4533                       language,          // aLanguage
   4534                       explicitLanguage,  // aExplicitLanguage
   4535                       nullptr,           // aTextPerf
   4536                       fontFaceSetImpl,   // aUserFontSet
   4537                       1.0,               // aDevToCssSize
   4538                       StyleFontVariantEmoji::Normal);
   4539  auto& state = CurrentState();
   4540  state.fontGroup = fontGroup;
   4541  SerializeFontForCanvas(list, fontStyle, state.font);
   4542  state.fontFont = nsFont(StyleFontFamily{list, false, false},
   4543                          StyleCSSPixelLength::FromPixels(size));
   4544  state.fontFont.variantCaps = fontStyle.variantCaps;
   4545  state.fontComputedStyle = nullptr;
   4546  return true;
   4547 }
   4548 
   4549 void CanvasRenderingContext2D::UpdateSpacing() {
   4550  auto state = CurrentState();
   4551  if (!state.letterSpacingStr.IsEmpty()) {
   4552    SetLetterSpacing(state.letterSpacingStr);
   4553  }
   4554  if (!state.wordSpacingStr.IsEmpty()) {
   4555    SetWordSpacing(state.wordSpacingStr);
   4556  }
   4557 }
   4558 
   4559 /*
   4560 * Helper function that replaces the whitespace characters in a string
   4561 * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
   4562 * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
   4563 * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
   4564 * We also replace characters with Bidi type Segment Separator or Block
   4565 * Separator.
   4566 * @param str The string whose whitespace characters to replace.
   4567 */
   4568 static inline void TextReplaceWhitespaceCharacters(nsAutoString& aStr) {
   4569  aStr.ReplaceChar(u"\x09\x0A\x0B\x0C\x0D\x1C\x1D\x1E\x1F\x85\x2029",
   4570                   char16_t(' '));
   4571 }
   4572 
   4573 void CanvasRenderingContext2D::FillText(const nsAString& aText, double aX,
   4574                                        double aY,
   4575                                        const Optional<double>& aMaxWidth,
   4576                                        ErrorResult& aError) {
   4577  // We try to match the most commonly observed strings used by canvas
   4578  // fingerprinting scripts.
   4579  MOZ_LOG(gFingerprinterDetection, LogLevel::Verbose,
   4580          ("mFillTextCalls %i FillText: "
   4581           "\"%s\"\n",
   4582           mFillTextCalls, NS_ConvertUTF16toUTF8(aText).get()));
   4583  if (mFillTextCalls <= 5) {
   4584    if (aText == u"Cwm fjordbank glyphs vext quiz, 😃"_ns) {
   4585      mFeatureUsage |= CanvasFeatureUsage::KnownText_1;
   4586    } else if (StringBeginsWith(aText, u"Hel$&?6%"_ns)) {
   4587      mFeatureUsage |= CanvasFeatureUsage::KnownText_2;  // Imperva
   4588    } else if (StringBeginsWith(aText, u"<@nv45. "_ns)) {
   4589      mFeatureUsage |= CanvasFeatureUsage::KnownText_3;
   4590    } else if (aText == u"Cañvas FP 😎 12345"_ns) {
   4591      mFeatureUsage |= CanvasFeatureUsage::KnownText_4;
   4592    } else if (StringBeginsWith(aText, u"❤️🤪🎉👋"_ns)) {
   4593      mFeatureUsage |= CanvasFeatureUsage::KnownText_5;  // hCaptcha
   4594    } else if (aText == u"SomeCanvasFingerPrint.65@345876"_ns) {
   4595      mFeatureUsage |= CanvasFeatureUsage::KnownText_6;
   4596    } else if (aText == u"Browser,Signal <canvas> 2.0"_ns) {
   4597      mFeatureUsage |= CanvasFeatureUsage::KnownText_7;
   4598    } else if (aText == u"@Browsers~%fingGPRint$&,<canvas>"_ns) {
   4599      mFeatureUsage |= CanvasFeatureUsage::KnownText_8;
   4600    } else if (aText == u"M"_ns) {
   4601      mFeatureUsage |= CanvasFeatureUsage::KnownText_9;
   4602    } else if (aText == u"E"_ns) {
   4603      mFeatureUsage |= CanvasFeatureUsage::KnownText_10;
   4604    } else if (aText == u"g"_ns) {
   4605      mFeatureUsage |= CanvasFeatureUsage::KnownText_11;
   4606    } else if (aText == u"Soft Ruddy Foothold 2"_ns) {
   4607      mFeatureUsage |= CanvasFeatureUsage::KnownText_12;  // Akamai
   4608    } else if (aText == u"!H71JCaj)]# 1@#"_ns) {
   4609      mFeatureUsage |= CanvasFeatureUsage::KnownText_13;  // Akamai
   4610    } else if (aText == u"oubrg5h56e@!$3t4"_ns) {
   4611      mFeatureUsage |= CanvasFeatureUsage::KnownText_14;
   4612    } else if (aText == u"Cwm fjordbank glyphs vext quiz,"_ns) {
   4613      mFeatureUsage |= CanvasFeatureUsage::KnownText_15;
   4614    } else if (aText == u"ClientJS,org <canvas> 1.0"_ns) {
   4615      mFeatureUsage |= CanvasFeatureUsage::KnownText_16;
   4616    } else if (aText == u"IaID,org <canvas> 1.0"_ns) {
   4617      mFeatureUsage |= CanvasFeatureUsage::KnownText_17;
   4618    } else if (aText == u"conviva"_ns) {
   4619      mFeatureUsage |= CanvasFeatureUsage::KnownText_18;
   4620    } else if (aText == u"Random Text WMwmil10Oo"_ns) {
   4621      mFeatureUsage |= CanvasFeatureUsage::KnownText_19;
   4622    } else if (aText == u"-0.5753861119575491"_ns) {
   4623      mFeatureUsage |= CanvasFeatureUsage::KnownText_20;
   4624    } else if (aText == u"0.8178819121159085"_ns) {
   4625      mFeatureUsage |= CanvasFeatureUsage::KnownText_21;
   4626    } else if (StringBeginsWith(aText, u"Cwm fjordbank"_ns)) {
   4627      mFeatureUsage |= CanvasFeatureUsage::KnownText_22;
   4628    } else if (StringBeginsWith(aText, u"iO0A"_ns)) {
   4629      mFeatureUsage |= CanvasFeatureUsage::KnownText_23;
   4630    } else if (aText == u"<@nv45. F1n63r,Pr1n71n6!"_ns) {
   4631      mFeatureUsage |= CanvasFeatureUsage::KnownText_24;
   4632    } else if (aText == u"Cwm fjordbank gly 😃"_ns) {
   4633      mFeatureUsage |= CanvasFeatureUsage::KnownText_25;
   4634    } else if (aText == u"clientgear.com <canvas> 1.0") {
   4635      mFeatureUsage |= CanvasFeatureUsage::KnownText_26;
   4636    } else if (aText == u"iO0A🤣💩") {
   4637      mFeatureUsage |= CanvasFeatureUsage::KnownText_27;
   4638    } else if (aText == u"Ry"_ns) {
   4639      mFeatureUsage |= CanvasFeatureUsage::KnownText_28;
   4640    }
   4641  }
   4642  mFillTextCalls++;
   4643 
   4644  DebugOnly<UniquePtr<TextMetrics>> metrics = DrawOrMeasureText(
   4645      aText, aX, aY, aMaxWidth, TextDrawOperation::FILL, aError);
   4646  MOZ_ASSERT(
   4647      !metrics.inspect());  // drawing operation never returns TextMetrics
   4648 }
   4649 
   4650 void CanvasRenderingContext2D::StrokeText(const nsAString& aText, double aX,
   4651                                          double aY,
   4652                                          const Optional<double>& aMaxWidth,
   4653                                          ErrorResult& aError) {
   4654  DebugOnly<UniquePtr<TextMetrics>> metrics = DrawOrMeasureText(
   4655      aText, aX, aY, aMaxWidth, TextDrawOperation::STROKE, aError);
   4656  MOZ_ASSERT(
   4657      !metrics.inspect());  // drawing operation never returns TextMetrics
   4658 }
   4659 
   4660 UniquePtr<TextMetrics> CanvasRenderingContext2D::MeasureText(
   4661    const nsAString& aRawText, ErrorResult& aError) {
   4662  Optional<double> maxWidth;
   4663  return DrawOrMeasureText(aRawText, 0, 0, maxWidth, TextDrawOperation::MEASURE,
   4664                           aError);
   4665 }
   4666 
   4667 /**
   4668 * Used for nsBidiPresUtils::ProcessText
   4669 */
   4670 struct MOZ_STACK_CLASS CanvasBidiProcessor final
   4671    : public nsBidiPresUtils::BidiProcessor {
   4672  using Style = CanvasRenderingContext2D::Style;
   4673 
   4674  explicit CanvasBidiProcessor(mozilla::gfx::PaletteCache& aPaletteCache)
   4675      : mPaletteCache(aPaletteCache) {
   4676    if (StaticPrefs::gfx_missing_fonts_notify()) {
   4677      mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
   4678    }
   4679  }
   4680 
   4681  ~CanvasBidiProcessor() {
   4682    // notify front-end code if we encountered missing glyphs in any script
   4683    if (mMissingFonts) {
   4684      mMissingFonts->Flush();
   4685    }
   4686  }
   4687 
   4688  class PropertyProvider : public gfxTextRun::PropertyProvider {
   4689   public:
   4690    explicit PropertyProvider(const CanvasBidiProcessor& aProcessor)
   4691        : mProcessor(aProcessor) {}
   4692 
   4693    bool GetSpacing(gfxTextRun::Range aRange,
   4694                    gfxFont::Spacing* aSpacing) const {
   4695      for (auto i = aRange.start; i < aRange.end; ++i) {
   4696        auto* charGlyphs = mProcessor.mTextRun->GetCharacterGlyphs();
   4697        if (i == mProcessor.mTextRun->GetLength() - 1 ||
   4698            (charGlyphs[i + 1].IsClusterStart() &&
   4699             charGlyphs[i + 1].IsLigatureGroupStart())) {
   4700          // Currently we add all the letterspacing to the right of the glyph,
   4701          // which is similar to Chrome's behavior, though the LTR vs RTL
   4702          // asymmetry seems unfortunate.
   4703          if (mProcessor.mTextRun->IsRightToLeft()) {
   4704            aSpacing->mAfter = 0;
   4705            aSpacing->mBefore = NSToCoordRound(mProcessor.mLetterSpacing);
   4706          } else {
   4707            aSpacing->mBefore = 0;
   4708            aSpacing->mAfter = NSToCoordRound(mProcessor.mLetterSpacing);
   4709          }
   4710        } else {
   4711          aSpacing->mBefore = 0;
   4712          aSpacing->mAfter = 0;
   4713        }
   4714        if (charGlyphs[i].CharIsSpace()) {
   4715          if (mProcessor.mTextRun->IsRightToLeft()) {
   4716            aSpacing->mBefore += NSToCoordRound(mProcessor.mWordSpacing);
   4717          } else {
   4718            aSpacing->mAfter += NSToCoordRound(mProcessor.mWordSpacing);
   4719          }
   4720        }
   4721        aSpacing++;
   4722      }
   4723      return mProcessor.mLetterSpacing != 0.0 || mProcessor.mWordSpacing != 0.0;
   4724    }
   4725 
   4726    mozilla::StyleHyphens GetHyphensOption() const {
   4727      return mozilla::StyleHyphens::None;
   4728    }
   4729 
   4730    // Methods only used when hyphenation is active, not relevant to canvas2d:
   4731    void GetHyphenationBreaks(gfxTextRun::Range aRange,
   4732                              gfxTextRun::HyphenType* aBreakBefore) const {
   4733      MOZ_ASSERT_UNREACHABLE("no hyphenation in canvas2d text!");
   4734    }
   4735    gfxFloat GetHyphenWidth() const {
   4736      MOZ_ASSERT_UNREACHABLE("no hyphenation in canvas2d text!");
   4737      return 0.0;
   4738    }
   4739    already_AddRefed<DrawTarget> GetDrawTarget() const {
   4740      MOZ_ASSERT_UNREACHABLE("no hyphenation in canvas2d text!");
   4741      return nullptr;
   4742    }
   4743    uint32_t GetAppUnitsPerDevUnit() const {
   4744      MOZ_ASSERT_UNREACHABLE("no hyphenation in canvas2d text!");
   4745      return 60;
   4746    }
   4747    gfx::ShapedTextFlags GetShapedTextFlags() const {
   4748      MOZ_ASSERT_UNREACHABLE("no hyphenation in canvas2d text!");
   4749      return gfx::ShapedTextFlags();
   4750    }
   4751 
   4752   private:
   4753    const CanvasBidiProcessor& mProcessor;
   4754  };
   4755 
   4756  using ContextState = CanvasRenderingContext2D::ContextState;
   4757 
   4758  void SetText(const char16_t* aText, int32_t aLength,
   4759               intl::BidiDirection aDirection) override {
   4760    if (mIgnoreSetText) {
   4761      // We've been told to ignore SetText because the processor is only ever
   4762      // handling a single, fixed string.
   4763      MOZ_ASSERT(mTextRun && mTextRun->GetLength() == uint32_t(aLength));
   4764      return;
   4765    }
   4766    mSetTextCount++;
   4767    auto* pfl = gfxPlatformFontList::PlatformFontList();
   4768    pfl->Lock();
   4769    mFontgrp->UpdateUserFonts();  // ensure user font generation is current
   4770    // adjust flags for current direction run
   4771    gfx::ShapedTextFlags flags = mTextRunFlags;
   4772    if (aDirection == intl::BidiDirection::RTL) {
   4773      flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
   4774    } else {
   4775      flags &= ~gfx::ShapedTextFlags::TEXT_IS_RTL;
   4776    }
   4777    mTextRun = mFontgrp->MakeTextRun(
   4778        aText, aLength, mDrawTarget, mAppUnitsPerDevPixel, flags,
   4779        nsTextFrameUtils::Flags::DontSkipDrawingForPendingUserFonts,
   4780        mMissingFonts.get());
   4781    pfl->Unlock();
   4782  }
   4783 
   4784  nscoord GetWidth() override {
   4785    PropertyProvider provider(*this);
   4786    gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(
   4787        mDoMeasureBoundingBox ? gfxFont::TIGHT_INK_EXTENTS
   4788                              : gfxFont::LOOSE_INK_EXTENTS,
   4789        mDrawTarget, &provider);
   4790 
   4791    // this only measures the height; the total width is gotten from the
   4792    // the return value of ProcessText.
   4793    if (mDoMeasureBoundingBox) {
   4794      textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
   4795      mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
   4796    }
   4797 
   4798    return NSToCoordRound(textRunMetrics.mAdvanceWidth);
   4799  }
   4800 
   4801  already_AddRefed<gfxPattern> GetGradientFor(Style aStyle) {
   4802    RefPtr<gfxPattern> pattern;
   4803    CanvasGradient* gradient = mCtx->CurrentState().gradientStyles[aStyle];
   4804    CanvasGradient::Type type = gradient->GetType();
   4805 
   4806    switch (type) {
   4807      case CanvasGradient::Type::CONIC: {
   4808        auto conic = static_cast<CanvasConicGradient*>(gradient);
   4809        pattern = new gfxPattern(conic->mCenter.x, conic->mCenter.y,
   4810                                 conic->mAngle, 0, 1);
   4811        break;
   4812      }
   4813      case CanvasGradient::Type::RADIAL: {
   4814        auto radial = static_cast<CanvasRadialGradient*>(gradient);
   4815        pattern = new gfxPattern(radial->mCenter1.x, radial->mCenter1.y,
   4816                                 radial->mRadius1, radial->mCenter2.x,
   4817                                 radial->mCenter2.y, radial->mRadius2);
   4818        break;
   4819      }
   4820      case CanvasGradient::Type::LINEAR: {
   4821        auto linear = static_cast<CanvasLinearGradient*>(gradient);
   4822        pattern = new gfxPattern(linear->mBegin.x, linear->mBegin.y,
   4823                                 linear->mEnd.x, linear->mEnd.y);
   4824        break;
   4825      }
   4826      default:
   4827        MOZ_ASSERT(false, "Should be linear, radial or conic gradient.");
   4828        return nullptr;
   4829    }
   4830 
   4831    for (auto stop : gradient->mRawStops) {
   4832      pattern->AddColorStop(stop.offset, stop.color);
   4833    }
   4834 
   4835    return pattern.forget();
   4836  }
   4837 
   4838  gfx::ExtendMode CvtCanvasRepeatToGfxRepeat(
   4839      CanvasPattern::RepeatMode aRepeatMode) {
   4840    switch (aRepeatMode) {
   4841      case CanvasPattern::RepeatMode::REPEAT:
   4842        return gfx::ExtendMode::REPEAT;
   4843      case CanvasPattern::RepeatMode::REPEATX:
   4844        return gfx::ExtendMode::REPEAT_X;
   4845      case CanvasPattern::RepeatMode::REPEATY:
   4846        return gfx::ExtendMode::REPEAT_Y;
   4847      case CanvasPattern::RepeatMode::NOREPEAT:
   4848        return gfx::ExtendMode::CLAMP;
   4849      default:
   4850        return gfx::ExtendMode::CLAMP;
   4851    }
   4852  }
   4853 
   4854  already_AddRefed<gfxPattern> GetPatternFor(Style aStyle) {
   4855    const CanvasPattern* pat = mCtx->CurrentState().patternStyles[aStyle];
   4856    RefPtr<gfxPattern> pattern = new gfxPattern(pat->mSurface, pat->mTransform);
   4857    pattern->SetExtend(CvtCanvasRepeatToGfxRepeat(pat->mRepeat));
   4858    return pattern.forget();
   4859  }
   4860 
   4861  void DrawText(nscoord aXOffset) override {
   4862    gfx::Point point = mPt;
   4863    bool rtl = mTextRun->IsRightToLeft();
   4864    bool verticalRun = mTextRun->IsVertical();
   4865    RefPtr<gfxPattern> pattern;
   4866 
   4867    float& inlineCoord = verticalRun ? point.y.value : point.x.value;
   4868    inlineCoord += aXOffset;
   4869 
   4870    PropertyProvider provider(*this);
   4871 
   4872    // offset is given in terms of left side of string
   4873    if (rtl) {
   4874      // Bug 581092 - don't use rounded pixel width to advance to
   4875      // right-hand end of run, because this will cause different
   4876      // glyph positioning for LTR vs RTL drawing of the same
   4877      // glyph string on OS X and DWrite where textrun widths may
   4878      // involve fractional pixels.
   4879      gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(
   4880          mDoMeasureBoundingBox ? gfxFont::TIGHT_INK_EXTENTS
   4881                                : gfxFont::LOOSE_INK_EXTENTS,
   4882          mDrawTarget, &provider);
   4883      inlineCoord += textRunMetrics.mAdvanceWidth;
   4884      // old code was:
   4885      //   point.x += width * mAppUnitsPerDevPixel;
   4886      // TODO: restore this if/when we move to fractional coords
   4887      // throughout the text layout process
   4888    }
   4889 
   4890    mCtx->EnsureTarget();
   4891    if (!mCtx->IsTargetValid()) {
   4892      return;
   4893    }
   4894 
   4895    // Defer the tasks to gfxTextRun which will handle color/svg-in-ot fonts
   4896    // appropriately.
   4897    StrokeOptions strokeOpts;
   4898    DrawOptions drawOpts;
   4899    Style style = (mOp == CanvasRenderingContext2D::TextDrawOperation::FILL)
   4900                      ? Style::FILL
   4901                      : Style::STROKE;
   4902    const ContextState& state = mCtx->CurrentState();
   4903 
   4904    gfx::Rect bounds;
   4905    if (mCtx->NeedToCalculateBounds()) {
   4906      bounds = ToRect(mBoundingBox);
   4907      bounds.MoveBy(mPt / mAppUnitsPerDevPixel);
   4908      if (style == Style::STROKE) {
   4909        bounds.Inflate(state.lineWidth / 2.0);
   4910      }
   4911      bounds = mDrawTarget->GetTransform().TransformBounds(bounds);
   4912    }
   4913 
   4914    AdjustedTarget target(mCtx, bounds.IsEmpty() ? nullptr : &bounds, false);
   4915    if (!target) {
   4916      return;
   4917    }
   4918 
   4919    gfxContext thebes(target, /* aPreserveTransform */ true);
   4920    gfxTextRun::DrawParams params(&thebes, mPaletteCache);
   4921 
   4922    params.allowGDI = false;
   4923 
   4924    if (state.StyleIsColor(style)) {  // Color
   4925      nscolor fontColor = state.colorStyles[style];
   4926      if (style == Style::FILL) {
   4927        params.context->SetColor(sRGBColor::FromABGR(fontColor));
   4928      } else {
   4929        params.textStrokeColor = fontColor;
   4930      }
   4931    } else {
   4932      if (state.gradientStyles[style]) {  // Gradient
   4933        pattern = GetGradientFor(style);
   4934      } else if (state.patternStyles[style]) {  // Pattern
   4935        pattern = GetPatternFor(style);
   4936      } else {
   4937        MOZ_ASSERT(false, "Should never reach here.");
   4938        return;
   4939      }
   4940      MOZ_ASSERT(pattern, "No valid pattern.");
   4941 
   4942      if (style == Style::FILL) {
   4943        params.context->SetPattern(pattern);
   4944      } else {
   4945        params.textStrokePattern = pattern;
   4946      }
   4947    }
   4948 
   4949    drawOpts.mAlpha = state.globalAlpha;
   4950    drawOpts.mCompositionOp = target.UsedOperation();
   4951    if (!mCtx->IsTargetValid()) {
   4952      return;
   4953    }
   4954 
   4955    params.drawOpts = &drawOpts;
   4956    params.provider = &provider;
   4957 
   4958    if (style == Style::STROKE) {
   4959      strokeOpts.mLineWidth = state.lineWidth;
   4960      strokeOpts.mLineJoin = CanvasToGfx(state.lineJoin);
   4961      strokeOpts.mLineCap = CanvasToGfx(state.lineCap);
   4962      strokeOpts.mMiterLimit = state.miterLimit;
   4963      strokeOpts.mDashLength = state.dash.Length();
   4964      strokeOpts.mDashPattern =
   4965          (strokeOpts.mDashLength > 0) ? state.dash.Elements() : 0;
   4966      strokeOpts.mDashOffset = state.dashOffset;
   4967 
   4968      params.drawMode = DrawMode::GLYPH_STROKE;
   4969      params.strokeOpts = &strokeOpts;
   4970    }
   4971 
   4972    mTextRun->Draw(gfxTextRun::Range(mTextRun.get()), point, params);
   4973  }
   4974 
   4975  // current text run
   4976  RefPtr<gfxTextRun> mTextRun;
   4977 
   4978  // pointer to a screen reference context used to measure text and such
   4979  RefPtr<DrawTarget> mDrawTarget;
   4980 
   4981  // Pointer to the draw target we should fill our text to
   4982  CanvasRenderingContext2D* mCtx = nullptr;
   4983 
   4984  // position of the left side of the string, alphabetic baseline
   4985  gfx::Point mPt;
   4986 
   4987  // current font
   4988  gfxFontGroup* mFontgrp = nullptr;
   4989 
   4990  // palette cache for COLR font rendering
   4991  mozilla::gfx::PaletteCache& mPaletteCache;
   4992 
   4993  // spacing adjustments to be applied
   4994  gfx::Float mLetterSpacing = 0.0f;
   4995  gfx::Float mWordSpacing = 0.0f;
   4996 
   4997  // to record any unsupported characters found in the text,
   4998  // and notify front-end if it is interested
   4999  UniquePtr<gfxMissingFontRecorder> mMissingFonts;
   5000 
   5001  // dev pixel conversion factor
   5002  int32_t mAppUnitsPerDevPixel = 0;
   5003 
   5004  // operation (fill or stroke)
   5005  CanvasRenderingContext2D::TextDrawOperation mOp =
   5006      CanvasRenderingContext2D::TextDrawOperation::FILL;
   5007 
   5008  // union of bounding boxes of all runs, needed for shadows
   5009  gfxRect mBoundingBox;
   5010 
   5011  // flags to use when creating textrun, based on CSS style
   5012  gfx::ShapedTextFlags mTextRunFlags = gfx::ShapedTextFlags();
   5013 
   5014  // Count of how many times SetText has been called on this processor.
   5015  uint32_t mSetTextCount = 0;
   5016 
   5017  // true iff the bounding box should be measured
   5018  bool mDoMeasureBoundingBox = false;
   5019 
   5020  // true if future SetText calls should be ignored
   5021  bool mIgnoreSetText = false;
   5022 };
   5023 
   5024 UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
   5025    const nsAString& aText, float aX, float aY,
   5026    const Optional<double>& aMaxWidth, TextDrawOperation aOp,
   5027    ErrorResult& aError) {
   5028  RefPtr<gfxFontGroup> currentFontStyle = GetCurrentFontStyle();
   5029  if (NS_WARN_IF(!currentFontStyle)) {
   5030    aError = NS_ERROR_FAILURE;
   5031    return nullptr;
   5032  }
   5033 
   5034  RefPtr<PresShell> presShell = GetPresShell();
   5035  RefPtr<Document> document = presShell ? presShell->GetDocument() : nullptr;
   5036 
   5037  // replace all the whitespace characters with U+0020 SPACE
   5038  nsAutoString textToDraw(aText);
   5039  TextReplaceWhitespaceCharacters(textToDraw);
   5040 
   5041  // According to spec, the API should return an empty array if maxWidth was
   5042  // provided but is less than or equal to zero or equal to NaN.
   5043  if (aMaxWidth.WasPassed() &&
   5044      (aMaxWidth.Value() <= 0 || std::isnan(aMaxWidth.Value()))) {
   5045    textToDraw.Truncate();
   5046  }
   5047 
   5048  RefPtr<const ComputedStyle> canvasStyle;
   5049  if (mCanvasElement) {
   5050    canvasStyle = nsComputedDOMStyle::GetComputedStyle(mCanvasElement);
   5051  }
   5052 
   5053  // Get text direction, either from the property or inherited from context.
   5054  const ContextState& state = CurrentState();
   5055  bool isRTL;
   5056  switch (state.textDirection) {
   5057    case CanvasDirection::Ltr:
   5058      isRTL = false;
   5059      break;
   5060    case CanvasDirection::Rtl:
   5061      isRTL = true;
   5062      break;
   5063    case CanvasDirection::Inherit:
   5064      if (canvasStyle) {
   5065        isRTL =
   5066            canvasStyle->StyleVisibility()->mDirection == StyleDirection::Rtl;
   5067      } else if (document) {
   5068        isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) ==
   5069                IBMBIDI_TEXTDIRECTION_RTL;
   5070      } else {
   5071        isRTL = false;
   5072      }
   5073      break;
   5074    default:
   5075      MOZ_CRASH("unknown direction!");
   5076  }
   5077 
   5078  // This is only needed to know if we can know the drawing bounding box easily.
   5079  const bool doCalculateBounds = NeedToCalculateBounds();
   5080  if (presShell && presShell->IsDestroying()) {
   5081    aError = NS_ERROR_FAILURE;
   5082    return nullptr;
   5083  }
   5084 
   5085  nsPresContext* presContext =
   5086      presShell ? presShell->GetPresContext() : nullptr;
   5087 
   5088  if (presContext) {
   5089    // ensure user font set is up to date
   5090    presContext->Document()->FlushUserFontSet();
   5091    currentFontStyle->SetUserFontSet(presContext->GetUserFontSet());
   5092  }
   5093 
   5094  if (currentFontStyle->GetStyle()->size == 0.0F) {
   5095    aError = NS_OK;
   5096    if (aOp == TextDrawOperation::MEASURE) {
   5097      return MakeUnique<TextMetrics>(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
   5098                                     0.0, 0.0, 0.0, 0.0);
   5099    }
   5100    return nullptr;
   5101  }
   5102 
   5103  if (!std::isfinite(aX) || !std::isfinite(aY)) {
   5104    aError = NS_OK;
   5105    // This may not be correct - what should TextMetrics contain in the case of
   5106    // infinite width or height?
   5107    if (aOp == TextDrawOperation::MEASURE) {
   5108      return MakeUnique<TextMetrics>(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
   5109                                     0.0, 0.0, 0.0, 0.0);
   5110    }
   5111    return nullptr;
   5112  }
   5113 
   5114  CanvasBidiProcessor processor(mPaletteCache);
   5115 
   5116  // If we don't have a ComputedStyle, we can't set up vertical-text flags
   5117  // (for now, at least; perhaps we need new Canvas API to control this).
   5118  processor.mTextRunFlags =
   5119      canvasStyle ? nsLayoutUtils::GetTextRunFlagsForStyle(
   5120                        canvasStyle, presContext, canvasStyle->StyleFont(),
   5121                        canvasStyle->StyleText(), 0)
   5122                  : gfx::ShapedTextFlags();
   5123 
   5124  switch (state.textRendering) {
   5125    case CanvasTextRendering::Auto:
   5126      if (state.fontFont.size.ToCSSPixels() <
   5127          StaticPrefs::browser_display_auto_quality_min_font_size()) {
   5128        processor.mTextRunFlags |= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
   5129      } else {
   5130        processor.mTextRunFlags &= ~gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
   5131      }
   5132      break;
   5133    case CanvasTextRendering::OptimizeSpeed:
   5134      processor.mTextRunFlags |= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
   5135      break;
   5136    case CanvasTextRendering::OptimizeLegibility:
   5137    case CanvasTextRendering::GeometricPrecision:
   5138      processor.mTextRunFlags &= ~gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
   5139      break;
   5140    default:
   5141      MOZ_CRASH("unknown textRendering!");
   5142  }
   5143 
   5144  GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
   5145  processor.mPt = gfx::Point(aX, aY);
   5146  processor.mDrawTarget = gfxPlatform::ThreadLocalScreenReferenceDrawTarget();
   5147 
   5148  // If we don't have a target then we don't have a transform. A target won't
   5149  // be needed in the case where we're measuring the text size. This allows
   5150  // to avoid creating a target if it's only being used to measure text sizes.
   5151  processor.mDrawTarget->SetTransform(GetCurrentTransform());
   5152  processor.mCtx = this;
   5153  processor.mOp = aOp;
   5154  processor.mBoundingBox = gfxRect(0, 0, 0, 0);
   5155  processor.mDoMeasureBoundingBox = doCalculateBounds ||
   5156                                    !mIsEntireFrameInvalid ||
   5157                                    aOp == TextDrawOperation::MEASURE;
   5158  processor.mFontgrp = currentFontStyle;
   5159 
   5160  if (state.letterSpacing != 0.0 || state.wordSpacing != 0.0) {
   5161    processor.mLetterSpacing =
   5162        state.letterSpacing * processor.mAppUnitsPerDevPixel;
   5163    processor.mWordSpacing = state.wordSpacing * processor.mAppUnitsPerDevPixel;
   5164    processor.mTextRunFlags |= gfx::ShapedTextFlags::TEXT_ENABLE_SPACING;
   5165    if (state.letterSpacing != 0.0) {
   5166      processor.mTextRunFlags |=
   5167          gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES;
   5168    }
   5169  }
   5170 
   5171  nscoord totalWidthCoord;
   5172 
   5173  processor.mFontgrp
   5174      ->UpdateUserFonts();  // ensure user font generation is current
   5175  RefPtr<gfxFont> font = processor.mFontgrp->GetFirstValidFont();
   5176  const gfxFont::Metrics& fontMetrics =
   5177      font->GetMetrics(nsFontMetrics::eHorizontal);
   5178 
   5179  // calls bidi algo twice since it needs the full text width and the
   5180  // bounding boxes before rendering anything
   5181  aError = nsBidiPresUtils::ProcessText(
   5182      textToDraw.get(), textToDraw.Length(),
   5183      isRTL ? intl::BidiEmbeddingLevel::RTL() : intl::BidiEmbeddingLevel::LTR(),
   5184      presContext, processor, nsBidiPresUtils::MODE_MEASURE, nullptr, 0,
   5185      &totalWidthCoord, mBidiEngine);
   5186  if (aError.Failed()) {
   5187    return nullptr;
   5188  }
   5189 
   5190  // If ProcessText only called SetText once, we're dealing with a single run,
   5191  // and so we don't need to repeat SetText and textRun construction at drawing
   5192  // time below; we can just re-use the existing textRun.
   5193  if (processor.mSetTextCount == 1) {
   5194    processor.mIgnoreSetText = true;
   5195  }
   5196 
   5197  float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
   5198 
   5199  // offset pt.x based on text align
   5200  gfxFloat anchorX;
   5201 
   5202  if (state.textAlign == CanvasTextAlign::Center) {
   5203    anchorX = .5;
   5204  } else if (state.textAlign == CanvasTextAlign::Left ||
   5205             (!isRTL && state.textAlign == CanvasTextAlign::Start) ||
   5206             (isRTL && state.textAlign == CanvasTextAlign::End)) {
   5207    anchorX = 0;
   5208  } else {
   5209    anchorX = 1;
   5210  }
   5211 
   5212  float offsetX = anchorX * totalWidth;
   5213  processor.mPt.x -= offsetX;
   5214 
   5215  gfx::ShapedTextFlags runOrientation =
   5216      (processor.mTextRunFlags & gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
   5217  nsFontMetrics::FontOrientation fontOrientation =
   5218      (runOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED ||
   5219       runOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT)
   5220          ? nsFontMetrics::eVertical
   5221          : nsFontMetrics::eHorizontal;
   5222 
   5223  // offset pt.y (or pt.x, for vertical text) based on text baseline
   5224  gfxFloat baselineAnchor;
   5225 
   5226  switch (state.textBaseline) {
   5227    case CanvasTextBaseline::Hanging:
   5228      baselineAnchor = font->GetBaselines(fontOrientation).mHanging;
   5229      break;
   5230    case CanvasTextBaseline::Top:
   5231      baselineAnchor = fontMetrics.emAscent;
   5232      break;
   5233    case CanvasTextBaseline::Middle:
   5234      baselineAnchor = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
   5235      break;
   5236    case CanvasTextBaseline::Alphabetic:
   5237      baselineAnchor = font->GetBaselines(fontOrientation).mAlphabetic;
   5238      break;
   5239    case CanvasTextBaseline::Ideographic:
   5240      baselineAnchor = font->GetBaselines(fontOrientation).mIdeographic;
   5241      break;
   5242    case CanvasTextBaseline::Bottom:
   5243      baselineAnchor = -fontMetrics.emDescent;
   5244      break;
   5245    default:
   5246      MOZ_CRASH("GFX: unexpected TextBaseline");
   5247  }
   5248 
   5249  // We can't query the textRun directly, as it may not have been created yet;
   5250  // so instead we check the flags that will be used to initialize it.
   5251  if (runOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL) {
   5252    if (fontOrientation == nsFontMetrics::eVertical) {
   5253      // Adjust to account for mTextRun being shaped using center baseline
   5254      // rather than alphabetic.
   5255      baselineAnchor -= (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
   5256    }
   5257    processor.mPt.x -= baselineAnchor;
   5258  } else {
   5259    processor.mPt.y += baselineAnchor;
   5260  }
   5261 
   5262  // if only measuring, don't need to do any more work
   5263  if (aOp == TextDrawOperation::MEASURE) {
   5264    aError = NS_OK;
   5265    // Note that actualBoundingBoxLeft measures the distance in the leftward
   5266    // direction, so its sign is reversed from our usual physical coordinates.
   5267    double actualBoundingBoxLeft = offsetX - processor.mBoundingBox.X();
   5268    double actualBoundingBoxRight = processor.mBoundingBox.XMost() - offsetX;
   5269    double actualBoundingBoxAscent =
   5270        -processor.mBoundingBox.Y() - baselineAnchor;
   5271    double actualBoundingBoxDescent =
   5272        processor.mBoundingBox.YMost() + baselineAnchor;
   5273    auto baselines = font->GetBaselines(fontOrientation);
   5274    return MakeUnique<TextMetrics>(
   5275        totalWidth, actualBoundingBoxLeft, actualBoundingBoxRight,
   5276        fontMetrics.maxAscent - baselineAnchor,   // fontBBAscent
   5277        fontMetrics.maxDescent + baselineAnchor,  // fontBBDescent
   5278        actualBoundingBoxAscent, actualBoundingBoxDescent,
   5279        fontMetrics.emAscent - baselineAnchor,   // emHeightAscent
   5280        fontMetrics.emDescent + baselineAnchor,  // emHeightDescent
   5281        baselines.mHanging - baselineAnchor,
   5282        baselines.mAlphabetic - baselineAnchor,
   5283        baselines.mIdeographic - baselineAnchor);
   5284  }
   5285 
   5286  // If we did not actually calculate bounds, set up a simple bounding box
   5287  // based on the text position and advance.
   5288  if (!doCalculateBounds) {
   5289    processor.mBoundingBox.width = totalWidth;
   5290    processor.mBoundingBox.MoveBy(gfxPoint(processor.mPt.x, processor.mPt.y));
   5291  }
   5292 
   5293  processor.mPt.x *= processor.mAppUnitsPerDevPixel;
   5294  processor.mPt.y *= processor.mAppUnitsPerDevPixel;
   5295 
   5296  if (!EnsureTarget(aError)) {
   5297    return nullptr;
   5298  }
   5299 
   5300  MOZ_ASSERT(IsTargetValid());
   5301 
   5302  Matrix oldTransform = mTarget->GetTransform();
   5303  bool restoreTransform = false;
   5304  // if text is over aMaxWidth, then scale the text horizontally such that its
   5305  // width is precisely aMaxWidth
   5306  if (aMaxWidth.WasPassed() && aMaxWidth.Value() > 0 &&
   5307      totalWidth > aMaxWidth.Value()) {
   5308    Matrix newTransform = oldTransform;
   5309 
   5310    // Translate so that the anchor point is at 0,0, then scale and then
   5311    // translate back.
   5312    newTransform.PreTranslate(aX, 0);
   5313    newTransform.PreScale(aMaxWidth.Value() / totalWidth, 1);
   5314    newTransform.PreTranslate(-aX, 0);
   5315    /* we do this to avoid an ICE in the android compiler */
   5316    Matrix androidCompilerBug = newTransform;
   5317    mTarget->SetTransform(androidCompilerBug);
   5318    restoreTransform = true;
   5319  }
   5320 
   5321  // save the previous bounding box
   5322  gfxRect boundingBox = processor.mBoundingBox;
   5323 
   5324  // don't ever need to measure the bounding box twice
   5325  processor.mDoMeasureBoundingBox = false;
   5326 
   5327  aError = nsBidiPresUtils::ProcessText(
   5328      textToDraw.get(), textToDraw.Length(),
   5329      isRTL ? intl::BidiEmbeddingLevel::RTL() : intl::BidiEmbeddingLevel::LTR(),
   5330      presContext, processor, nsBidiPresUtils::MODE_DRAW, nullptr, 0, nullptr,
   5331      mBidiEngine);
   5332 
   5333  if (aError.Failed()) {
   5334    return nullptr;
   5335  }
   5336 
   5337  if (restoreTransform) {
   5338    mTarget->SetTransform(oldTransform);
   5339  }
   5340 
   5341  if (aOp == CanvasRenderingContext2D::TextDrawOperation::FILL &&
   5342      !doCalculateBounds) {
   5343    RedrawUser(boundingBox);
   5344  } else {
   5345    Redraw();
   5346  }
   5347 
   5348  aError = NS_OK;
   5349  return nullptr;
   5350 }
   5351 
   5352 gfxFontGroup* CanvasRenderingContext2D::GetCurrentFontStyle() {
   5353  // Use lazy (re)initialization for the fontGroup since it's rather expensive.
   5354 
   5355  RefPtr<PresShell> presShell = GetPresShell();
   5356  nsPresContext* presContext =
   5357      presShell ? presShell->GetPresContext() : nullptr;
   5358 
   5359  FontVisibilityProvider* visProvider = nullptr;
   5360  if (presContext) {
   5361    visProvider = presContext;
   5362  } else {
   5363    visProvider = mOffscreenCanvas;
   5364  }
   5365 
   5366  // If we have a cached fontGroup, check that it is valid for the current
   5367  // prescontext or canvas; if not, we need to discard and re-create it.
   5368  RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup;
   5369  if (fontGroup) {
   5370    if (fontGroup->GetFontVisibilityProvider() != visProvider) {
   5371      fontGroup = nullptr;
   5372    }
   5373  }
   5374 
   5375  if (!fontGroup) {
   5376    ErrorResult err;
   5377    constexpr auto kDefaultFontStyle = "10px sans-serif"_ns;
   5378    const float kDefaultFontSize = 10.0;
   5379    // If the font has already been set, we're re-creating the fontGroup
   5380    // and should re-use the existing font attribute; if not, we initialize
   5381    // it to the canvas default.
   5382    const nsCString& currentFont = CurrentState().font;
   5383    bool fontUpdated = SetFontInternal(
   5384        currentFont.IsEmpty() ? kDefaultFontStyle : currentFont, err);
   5385    if (err.Failed() || !fontUpdated) {
   5386      err.SuppressException();
   5387      // XXX Should we get a default lang from the prescontext or something?
   5388      nsAtom* language = nsGkAtoms::x_western;
   5389      bool explicitLanguage = false;
   5390      gfxFontStyle style;
   5391      style.size = kDefaultFontSize;
   5392      int32_t perDevPixel, perCSSPixel;
   5393      GetAppUnitsValues(&perDevPixel, &perCSSPixel);
   5394      gfxFloat devToCssSize = gfxFloat(perDevPixel) / gfxFloat(perCSSPixel);
   5395      const auto* sans =
   5396          Servo_FontFamily_Generic(StyleGenericFontFamily::SansSerif);
   5397      fontGroup = new gfxFontGroup(
   5398          visProvider, sans->families, &style, language, explicitLanguage,
   5399          presContext ? presContext->GetTextPerfMetrics() : nullptr, nullptr,
   5400          devToCssSize, StyleFontVariantEmoji::Normal);
   5401      if (fontGroup) {
   5402        CurrentState().font = kDefaultFontStyle;
   5403      } else {
   5404        NS_ERROR("Default canvas font is invalid");
   5405      }
   5406    }
   5407  }
   5408 
   5409  return fontGroup;
   5410 }
   5411 
   5412 //
   5413 // line dash styles
   5414 //
   5415 
   5416 void CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments,
   5417                                           ErrorResult& aRv) {
   5418  nsTArray<mozilla::gfx::Float> dash;
   5419 
   5420  for (uint32_t x = 0; x < aSegments.Length(); x++) {
   5421    if (aSegments[x] < 0.0) {
   5422      // Pattern elements must be finite "numbers" >= 0, with "finite"
   5423      // taken care of by WebIDL
   5424      return;
   5425    }
   5426 
   5427    if (!dash.AppendElement(aSegments[x], fallible)) {
   5428      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   5429      return;
   5430    }
   5431  }
   5432  if (aSegments.Length() %
   5433      2) {  // If the number of elements is odd, concatenate again
   5434    for (uint32_t x = 0; x < aSegments.Length(); x++) {
   5435      if (!dash.AppendElement(aSegments[x], fallible)) {
   5436        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   5437        return;
   5438      }
   5439    }
   5440  }
   5441 
   5442  CurrentState().dash = std::move(dash);
   5443 }
   5444 
   5445 void CanvasRenderingContext2D::GetLineDash(nsTArray<double>& aSegments) const {
   5446  const nsTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
   5447  aSegments.Clear();
   5448 
   5449  for (uint32_t x = 0; x < dash.Length(); x++) {
   5450    aSegments.AppendElement(dash[x]);
   5451  }
   5452 }
   5453 
   5454 void CanvasRenderingContext2D::SetLineDashOffset(double aOffset) {
   5455  CurrentState().dashOffset = aOffset;
   5456 }
   5457 
   5458 double CanvasRenderingContext2D::LineDashOffset() const {
   5459  return CurrentState().dashOffset;
   5460 }
   5461 
   5462 bool CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, double aX,
   5463                                             double aY,
   5464                                             const CanvasWindingRule& aWinding,
   5465                                             nsIPrincipal& aSubjectPrincipal) {
   5466  if (!FloatValidate(aX, aY)) {
   5467    return false;
   5468  }
   5469 
   5470  // Check for site-specific permission and return false if no permission.
   5471  if (mCanvasElement) {
   5472    nsCOMPtr<Document> ownerDoc = mCanvasElement->OwnerDoc();
   5473    if (!CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx,
   5474                                               &aSubjectPrincipal)) {
   5475      return false;
   5476    }
   5477  } else if (mOffscreenCanvas && mOffscreenCanvas->ShouldResistFingerprinting(
   5478                                     RFPTarget::CanvasImageExtractionPrompt)) {
   5479    return false;
   5480  }
   5481 
   5482  EnsureUserSpacePath(aWinding);
   5483  if (!mPath) {
   5484    return false;
   5485  }
   5486 
   5487  return mPath->ContainsPoint(Point(aX, aY), GetCurrentTransform());
   5488 }
   5489 
   5490 bool CanvasRenderingContext2D::IsPointInPath(JSContext* aCx,
   5491                                             const CanvasPath& aPath, double aX,
   5492                                             double aY,
   5493                                             const CanvasWindingRule& aWinding,
   5494                                             nsIPrincipal& aSubjectPrincipal) {
   5495  if (!FloatValidate(aX, aY)) {
   5496    return false;
   5497  }
   5498 
   5499  if (!EnsureBufferProvider()) {
   5500    return false;
   5501  }
   5502 
   5503  RefPtr<gfx::Path> tempPath = aPath.GetPath(aWinding, mPathType);
   5504 
   5505  return tempPath->ContainsPoint(Point(aX, aY), GetCurrentTransform());
   5506 }
   5507 
   5508 bool CanvasRenderingContext2D::IsPointInStroke(
   5509    JSContext* aCx, double aX, double aY, nsIPrincipal& aSubjectPrincipal) {
   5510  if (!FloatValidate(aX, aY)) {
   5511    return false;
   5512  }
   5513 
   5514  // Check for site-specific permission and return false if no permission.
   5515  if (mCanvasElement) {
   5516    nsCOMPtr<Document> ownerDoc = mCanvasElement->OwnerDoc();
   5517    if (!CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx,
   5518                                               &aSubjectPrincipal)) {
   5519      return false;
   5520    }
   5521  } else if (mOffscreenCanvas && mOffscreenCanvas->ShouldResistFingerprinting(
   5522                                     RFPTarget::CanvasImageExtractionPrompt)) {
   5523    return false;
   5524  }
   5525 
   5526  EnsureUserSpacePath();
   5527  if (!mPath) {
   5528    return false;
   5529  }
   5530 
   5531  const ContextState& state = CurrentState();
   5532 
   5533  StrokeOptions strokeOptions(state.lineWidth, CanvasToGfx(state.lineJoin),
   5534                              CanvasToGfx(state.lineCap), state.miterLimit,
   5535                              state.dash.Length(), state.dash.Elements(),
   5536                              state.dashOffset);
   5537 
   5538  return mPath->StrokeContainsPoint(strokeOptions, Point(aX, aY),
   5539                                    GetCurrentTransform());
   5540 }
   5541 
   5542 bool CanvasRenderingContext2D::IsPointInStroke(
   5543    JSContext* aCx, const CanvasPath& aPath, double aX, double aY,
   5544    nsIPrincipal& aSubjectPrincipal) {
   5545  if (!FloatValidate(aX, aY)) {
   5546    return false;
   5547  }
   5548 
   5549  if (!EnsureBufferProvider()) {
   5550    return false;
   5551  }
   5552 
   5553  RefPtr<gfx::Path> tempPath =
   5554      aPath.GetPath(CanvasWindingRule::Nonzero, mPathType);
   5555 
   5556  const ContextState& state = CurrentState();
   5557 
   5558  StrokeOptions strokeOptions(state.lineWidth, CanvasToGfx(state.lineJoin),
   5559                              CanvasToGfx(state.lineCap), state.miterLimit,
   5560                              state.dash.Length(), state.dash.Elements(),
   5561                              state.dashOffset);
   5562 
   5563  return tempPath->StrokeContainsPoint(strokeOptions, Point(aX, aY),
   5564                                       GetCurrentTransform());
   5565 }
   5566 
   5567 // Returns a surface that contains only the part needed to draw aSourceRect.
   5568 // On entry, aSourceRect is relative to aSurface, and on return aSourceRect is
   5569 // relative to the returned surface.
   5570 static already_AddRefed<SourceSurface> ExtractSubrect(SourceSurface* aSurface,
   5571                                                      gfx::Rect* aSourceRect,
   5572                                                      DrawTarget* aTargetDT) {
   5573  gfx::Rect roundedOutSourceRect = *aSourceRect;
   5574  roundedOutSourceRect.RoundOut();
   5575  gfx::IntRect roundedOutSourceRectInt;
   5576  if (!roundedOutSourceRect.ToIntRect(&roundedOutSourceRectInt) ||
   5577      roundedOutSourceRectInt.IsEmpty()) {
   5578    RefPtr<SourceSurface> surface(aSurface);
   5579    return surface.forget();
   5580  }
   5581 
   5582  // Try to extract an optimized sub-surface.
   5583  if (RefPtr<SourceSurface> surface =
   5584          aSurface->ExtractSubrect(roundedOutSourceRectInt)) {
   5585    *aSourceRect -= roundedOutSourceRect.TopLeft();
   5586    return surface.forget();
   5587  }
   5588 
   5589  RefPtr<DrawTarget> subrectDT = aTargetDT->CreateSimilarDrawTarget(
   5590      roundedOutSourceRectInt.Size(), SurfaceFormat::B8G8R8A8);
   5591 
   5592  if (subrectDT) {
   5593    // See bug 1524554.
   5594    subrectDT->ClearRect(gfx::Rect());
   5595  }
   5596 
   5597  if (!subrectDT || !subrectDT->IsValid()) {
   5598    RefPtr<SourceSurface> surface(aSurface);
   5599    return surface.forget();
   5600  }
   5601 
   5602  *aSourceRect -= roundedOutSourceRect.TopLeft();
   5603 
   5604  subrectDT->CopySurface(aSurface, roundedOutSourceRectInt, IntPoint());
   5605  return subrectDT->Snapshot();
   5606 }
   5607 
   5608 //
   5609 // image
   5610 //
   5611 
   5612 static void ClipImageDimension(double& aSourceCoord, double& aSourceSize,
   5613                               double& aClipOriginCoord, double& aClipSize,
   5614                               double& aDestCoord, double& aDestSize) {
   5615  double scale = aDestSize / aSourceSize;
   5616  double relativeCoord = aSourceCoord - aClipOriginCoord;
   5617  if (relativeCoord < 0.0) {
   5618    double destEnd = aDestCoord + aDestSize;
   5619    aDestCoord -= relativeCoord * scale;
   5620    aDestSize = destEnd - aDestCoord;
   5621    aSourceSize += relativeCoord;
   5622    aSourceCoord = aClipOriginCoord;
   5623    relativeCoord = 0.0;
   5624  }
   5625  double delta = aClipSize - (relativeCoord + aSourceSize);
   5626  if (delta < 0.0) {
   5627    aDestSize += delta * scale;
   5628    aSourceSize = aClipSize - relativeCoord;
   5629  }
   5630 }
   5631 
   5632 // Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt
   5633 // to pull a SourceSurface from our cache. This allows us to avoid
   5634 // reoptimizing surfaces if content and canvas backends are different.
   5635 SurfaceFromElementResult CanvasRenderingContext2D::CachedSurfaceFromElement(
   5636    Element* aElement) {
   5637  SurfaceFromElementResult res;
   5638  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
   5639  if (!imageLoader) {
   5640    return res;
   5641  }
   5642 
   5643  nsCOMPtr<imgIRequest> imgRequest;
   5644  imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   5645                          getter_AddRefs(imgRequest));
   5646  if (!imgRequest) {
   5647    return res;
   5648  }
   5649 
   5650  uint32_t status = 0;
   5651  if (NS_FAILED(imgRequest->GetImageStatus(&status)) ||
   5652      !(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
   5653    return res;
   5654  }
   5655 
   5656  nsCOMPtr<nsIPrincipal> principal;
   5657  if (NS_FAILED(imgRequest->GetImagePrincipal(getter_AddRefs(principal))) ||
   5658      !principal) {
   5659    return res;
   5660  }
   5661 
   5662  if (NS_FAILED(imgRequest->GetHadCrossOriginRedirects(
   5663          &res.mHadCrossOriginRedirects))) {
   5664    return res;
   5665  }
   5666 
   5667  res.mSourceSurface = CanvasImageCache::LookupAllCanvas(aElement, mTarget);
   5668  if (!res.mSourceSurface) {
   5669    return res;
   5670  }
   5671 
   5672  res.mCORSUsed = nsLayoutUtils::ImageRequestUsesCORS(imgRequest);
   5673  res.mSize = res.mIntrinsicSize = res.mSourceSurface->GetSize();
   5674  res.mPrincipal = std::move(principal);
   5675  res.mImageRequest = std::move(imgRequest);
   5676  res.mIsWriteOnly = CheckWriteOnlySecurity(res.mCORSUsed, res.mPrincipal,
   5677                                            res.mHadCrossOriginRedirects);
   5678 
   5679  return res;
   5680 }
   5681 
   5682 static void SwapScaleWidthHeightForRotation(gfx::Rect& aRect,
   5683                                            VideoRotation aDegrees) {
   5684  if (aDegrees == VideoRotation::kDegree_90 ||
   5685      aDegrees == VideoRotation::kDegree_270) {
   5686    std::swap(aRect.width, aRect.height);
   5687  }
   5688 }
   5689 
   5690 static Matrix ComputeRotationMatrix(gfxFloat aRotatedWidth,
   5691                                    gfxFloat aRotatedHeight,
   5692                                    VideoRotation aDegrees) {
   5693  Point shiftVideoCenterToOrigin(-aRotatedWidth / 2.0, -aRotatedHeight / 2.0);
   5694  if (aDegrees == VideoRotation::kDegree_90 ||
   5695      aDegrees == VideoRotation::kDegree_270) {
   5696    std::swap(shiftVideoCenterToOrigin.x, shiftVideoCenterToOrigin.y);
   5697  }
   5698  auto angle = static_cast<double>(aDegrees) / 180.0 * M_PI;
   5699  Matrix rotation = Matrix::Rotation(static_cast<gfx::Float>(angle));
   5700  Point shiftLeftTopToOrigin(aRotatedWidth / 2.0, aRotatedHeight / 2.0);
   5701  return rotation.PreTranslate(shiftVideoCenterToOrigin)
   5702      .PostTranslate(shiftLeftTopToOrigin);
   5703 }
   5704 
   5705 // -
   5706 
   5707 bool ValidSurfaceDescriptorForRemoteCanvas2d(
   5708    const layers::SurfaceDescriptor& aSd,
   5709    Maybe<layers::SurfaceDescriptor>* aResultSd) {
   5710  if (aSd.type() != layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo) {
   5711    return false;
   5712  }
   5713 
   5714  const auto& sdv = aSd.get_SurfaceDescriptorGPUVideo();
   5715  if (sdv.type() !=
   5716      layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder) {
   5717    return false;
   5718  }
   5719  const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder();
   5720  const auto& subdesc = sdrd.subdesc();
   5721  switch (subdesc.type()) {
   5722    case layers::RemoteDecoderVideoSubDescriptor::Tnull_t:
   5723      break;
   5724 #ifdef XP_MACOSX
   5725    case layers::RemoteDecoderVideoSubDescriptor::
   5726        TSurfaceDescriptorMacIOSurface: {
   5727      const auto& ssd = subdesc.get_SurfaceDescriptorMacIOSurface();
   5728      if (ssd.gpuFence()) {
   5729        return false;
   5730      }
   5731      break;
   5732    }
   5733 #endif
   5734 #ifdef XP_WIN
   5735    case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10: {
   5736      if (!StaticPrefs::gfx_canvas_remote_use_draw_image_fast_path_d3d()) {
   5737        return false;
   5738      }
   5739      const auto& ssd = subdesc.get_SurfaceDescriptorD3D10();
   5740      if (aResultSd) {
   5741        *aResultSd = Some(aSd);
   5742        // Not IPC-able, but it's just an optimization to have this.
   5743        aResultSd->ref()
   5744            .get_SurfaceDescriptorGPUVideo()
   5745            .get_SurfaceDescriptorRemoteDecoder()
   5746            .subdesc()
   5747            .get_SurfaceDescriptorD3D10()
   5748            .handle() = nullptr;
   5749      } else if (ssd.handle()) {
   5750        return false;
   5751      }
   5752      return true;
   5753    }
   5754 #endif
   5755    default:
   5756      return false;
   5757  }
   5758  if (aResultSd) {
   5759    *aResultSd = Some(aSd);
   5760  }
   5761  return true;
   5762 }
   5763 
   5764 static Maybe<layers::SurfaceDescriptor>
   5765 MaybeGetSurfaceDescriptorForRemoteCanvas(
   5766    const SurfaceFromElementResult& aResult) {
   5767  if (!StaticPrefs::gfx_canvas_remote_use_draw_image_fast_path()) {
   5768    return Nothing();
   5769  }
   5770 
   5771  if (!aResult.mLayersImage) {
   5772    return Nothing();
   5773  }
   5774 
   5775  if (const auto sd = aResult.mLayersImage->GetDesc()) {
   5776    Maybe<layers::SurfaceDescriptor> result;
   5777    if (ValidSurfaceDescriptorForRemoteCanvas2d(*sd, &result)) {
   5778      return result;
   5779    }
   5780  }
   5781  return Nothing();
   5782 }
   5783 
   5784 // drawImage(in HTMLImageElement image, in float dx, in float dy);
   5785 //   -- render image from 0,0 at dx,dy top-left coords
   5786 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float dw,
   5787 //           in float dh);
   5788 //   -- render image from 0,0 at dx,dy top-left coords clipping it to dw,dh
   5789 // drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw,
   5790 //           in float sh, in float dx, in float dy, in float dw, in float dh);
   5791 //   -- render the region defined by (sx,sy,sw,wh) in image-local space into the
   5792 //      region (dx,dy,dw,dh) on the canvas
   5793 
   5794 // If only dx and dy are passed in then optional_argc should be 0. If only
   5795 // dx, dy, dw and dh are passed in then optional_argc should be 2. The only
   5796 // other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
   5797 // are all passed in.
   5798 
   5799 void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
   5800                                         double aSx, double aSy, double aSw,
   5801                                         double aSh, double aDx, double aDy,
   5802                                         double aDw, double aDh,
   5803                                         uint8_t aOptional_argc,
   5804                                         ErrorResult& aError) {
   5805  MOZ_ASSERT(aOptional_argc == 0 || aOptional_argc == 2 || aOptional_argc == 6);
   5806 
   5807  if (!ValidateRect(aDx, aDy, aDw, aDh, true)) {
   5808    return;
   5809  }
   5810  if (aOptional_argc == 6) {
   5811    if (!ValidateRect(aSx, aSy, aSw, aSh, true)) {
   5812      return;
   5813    }
   5814  }
   5815 
   5816  RefPtr<SourceSurface> srcSurf;
   5817  gfx::IntSize imgSize;
   5818  gfx::IntSize intrinsicImgSize;
   5819  Maybe<IntRect> cropRect;
   5820  Element* element = nullptr;
   5821  OffscreenCanvas* offscreenCanvas = nullptr;
   5822  VideoFrame* videoFrame = nullptr;
   5823 
   5824  if (!EnsureTarget(aError)) {
   5825    return;
   5826  }
   5827 
   5828  MOZ_ASSERT(IsTargetValid());
   5829 
   5830  if (aImage.IsHTMLCanvasElement()) {
   5831    HTMLCanvasElement* canvas = &aImage.GetAsHTMLCanvasElement();
   5832    element = canvas;
   5833    CSSIntSize size = canvas->GetSize();
   5834    if (size.width == 0 || size.height == 0) {
   5835      return aError.ThrowInvalidStateError("Passed-in canvas is empty");
   5836    }
   5837 
   5838    if (canvas->IsWriteOnly()) {
   5839      SetWriteOnly();
   5840    }
   5841  } else if (aImage.IsOffscreenCanvas()) {
   5842    offscreenCanvas = &aImage.GetAsOffscreenCanvas();
   5843    CSSIntSize size = offscreenCanvas->GetWidthHeight();
   5844    if (size.IsEmpty()) {
   5845      return aError.ThrowInvalidStateError("Passed-in canvas is empty");
   5846    }
   5847 
   5848    srcSurf = offscreenCanvas->GetSurfaceSnapshot();
   5849    if (srcSurf) {
   5850      imgSize = intrinsicImgSize = srcSurf->GetSize();
   5851    }
   5852 
   5853    if (offscreenCanvas->IsWriteOnly()) {
   5854      SetWriteOnly();
   5855    }
   5856  } else if (aImage.IsImageBitmap()) {
   5857    ImageBitmap& imageBitmap = aImage.GetAsImageBitmap();
   5858    srcSurf = imageBitmap.PrepareForDrawTarget(mTarget);
   5859 
   5860    if (!srcSurf) {
   5861      if (imageBitmap.IsClosed()) {
   5862        aError.ThrowInvalidStateError("Passed-in ImageBitmap is closed");
   5863      }
   5864      return;
   5865    }
   5866 
   5867    if (imageBitmap.IsWriteOnly()) {
   5868      SetWriteOnly();
   5869    }
   5870 
   5871    imgSize = intrinsicImgSize =
   5872        gfx::IntSize(imageBitmap.Width(), imageBitmap.Height());
   5873  } else if (aImage.IsVideoFrame()) {
   5874    videoFrame = &aImage.GetAsVideoFrame();
   5875  } else {
   5876    if (aImage.IsHTMLImageElement()) {
   5877      HTMLImageElement* img = &aImage.GetAsHTMLImageElement();
   5878      element = img;
   5879    } else if (aImage.IsSVGImageElement()) {
   5880      SVGImageElement* img = &aImage.GetAsSVGImageElement();
   5881      element = img;
   5882    } else {
   5883      HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
   5884      video->LogVisibility(
   5885          mozilla::dom::HTMLVideoElement::CallerAPI::DRAW_IMAGE);
   5886      element = video;
   5887    }
   5888 
   5889    srcSurf = CanvasImageCache::LookupCanvas(element, this, mTarget, &imgSize,
   5890                                             &intrinsicImgSize, &cropRect);
   5891  }
   5892 
   5893  DirectDrawInfo drawInfo;
   5894  Maybe<layers::SurfaceDescriptor> surfaceDescriptor;
   5895  SurfaceFromElementResult res;
   5896 
   5897  if (!srcSurf) {
   5898    // The canvas spec says that drawImage should draw the first frame
   5899    // of animated images. We also don't want to rasterize vector images.
   5900    uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
   5901                        nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS |
   5902                        nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED;
   5903 
   5904    if (offscreenCanvas) {
   5905      res = nsLayoutUtils::SurfaceFromOffscreenCanvas(offscreenCanvas, sfeFlags,
   5906                                                      mTarget);
   5907    } else if (videoFrame) {
   5908      res = nsLayoutUtils::SurfaceFromVideoFrame(videoFrame, sfeFlags, mTarget);
   5909    } else {
   5910      res = CanvasRenderingContext2D::CachedSurfaceFromElement(element);
   5911      if (!res.mSourceSurface) {
   5912        HTMLVideoElement* video = HTMLVideoElement::FromNodeOrNull(element);
   5913        if (video && mBufferProvider->IsAccelerated() &&
   5914            mTarget->IsRecording() &&
   5915            !(!NeedToApplyFilter() && NeedToDrawShadow())) {
   5916          res = nsLayoutUtils::SurfaceFromElement(
   5917              video, sfeFlags, mTarget, /* aOptimizeSourceSurface */ false);
   5918          surfaceDescriptor = MaybeGetSurfaceDescriptorForRemoteCanvas(res);
   5919          if (surfaceDescriptor.isNothing() && res.mLayersImage) {
   5920            if ((res.mSourceSurface = res.mLayersImage->GetAsSourceSurface())) {
   5921              RefPtr<SourceSurface> opt =
   5922                  mTarget->OptimizeSourceSurface(res.mSourceSurface);
   5923              if (opt) {
   5924                res.mSourceSurface = opt;
   5925              }
   5926            }
   5927          }
   5928        } else {
   5929          res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
   5930        }
   5931      }
   5932    }
   5933 
   5934    if (surfaceDescriptor.isNothing()) {
   5935      srcSurf = res.GetSourceSurface();
   5936    }
   5937 
   5938    if (!srcSurf && surfaceDescriptor.isNothing() &&
   5939        !res.mDrawInfo.mImgContainer) {
   5940      // https://html.spec.whatwg.org/#check-the-usability-of-the-image-argument:
   5941      //
   5942      // Only throw if the request is broken and the element is an
   5943      // HTMLImageElement / SVGImageElement. Note that even for those the spec
   5944      // says to silently do nothing in the following cases:
   5945      //   - The element is still loading.
   5946      //   - The image is bad, but it's not in the broken state (i.e., we could
   5947      //     decode the headers and get the size).
   5948      if (!res.mIsStillLoading && !res.mHasSize &&
   5949          (aImage.IsHTMLImageElement() || aImage.IsSVGImageElement())) {
   5950        aError.ThrowInvalidStateError("Passed-in image is \"broken\"");
   5951      } else if (videoFrame) {
   5952        aError.ThrowInvalidStateError("Passed-in video frame is \"broken\"");
   5953      }
   5954      return;
   5955    }
   5956 
   5957    imgSize = res.mSize;
   5958    intrinsicImgSize = res.mIntrinsicSize;
   5959    cropRect = res.mCropRect;
   5960    DoSecurityCheck(res.mPrincipal, res.mIsWriteOnly, res.mCORSUsed);
   5961 
   5962    if (srcSurf) {
   5963      if (res.mImageRequest) {
   5964        CanvasImageCache::NotifyDrawImage(element, this, mTarget, srcSurf,
   5965                                          imgSize, intrinsicImgSize, cropRect);
   5966      }
   5967    } else {
   5968      drawInfo = res.mDrawInfo;
   5969    }
   5970  }
   5971 
   5972  double clipOriginX, clipOriginY, clipWidth, clipHeight;
   5973  if (cropRect) {
   5974    clipOriginX = cropRect.ref().X();
   5975    clipOriginY = cropRect.ref().Y();
   5976    clipWidth = cropRect.ref().Width();
   5977    clipHeight = cropRect.ref().Height();
   5978  } else {
   5979    clipOriginX = clipOriginY = 0.0;
   5980    clipWidth = imgSize.width;
   5981    clipHeight = imgSize.height;
   5982  }
   5983 
   5984  // Any provided coordinates are in the display space, or the same as the
   5985  // intrinsic size. In order to get to the surface coordinate space, we may
   5986  // need to adjust for scaling and/or cropping. If no source coordinates are
   5987  // provided, then we can just directly use the actual surface size.
   5988  if (aOptional_argc == 0) {
   5989    aSx = clipOriginX;
   5990    aSy = clipOriginY;
   5991    aSw = clipWidth;
   5992    aSh = clipHeight;
   5993    aDw = (double)intrinsicImgSize.width;
   5994    aDh = (double)intrinsicImgSize.height;
   5995  } else if (aOptional_argc == 2) {
   5996    aSx = clipOriginX;
   5997    aSy = clipOriginY;
   5998    aSw = clipWidth;
   5999    aSh = clipHeight;
   6000  } else if (cropRect || intrinsicImgSize != imgSize) {
   6001    // We need to first scale between the cropped size and the intrinsic size,
   6002    // and then adjust for the offset from the crop rect.
   6003    double scaleXToCrop = clipWidth / intrinsicImgSize.width;
   6004    double scaleYToCrop = clipHeight / intrinsicImgSize.height;
   6005    aSx = aSx * scaleXToCrop + clipOriginX;
   6006    aSy = aSy * scaleYToCrop + clipOriginY;
   6007    aSw = aSw * scaleXToCrop;
   6008    aSh = aSh * scaleYToCrop;
   6009  }
   6010 
   6011  if (aSw == 0.0 || aSh == 0.0) {
   6012    return;
   6013  }
   6014 
   6015  ClipImageDimension(aSx, aSw, clipOriginX, clipWidth, aDx, aDw);
   6016  ClipImageDimension(aSy, aSh, clipOriginY, clipHeight, aDy, aDh);
   6017 
   6018  if (aSw <= 0.0 || aSh <= 0.0 || aDw <= 0.0 || aDh <= 0.0) {
   6019    // source and/or destination are fully clipped, so nothing is painted
   6020    return;
   6021  }
   6022 
   6023  if (static_cast<float>(aSw) <= 0.0 || static_cast<float>(aSh) <= 0.0 ||
   6024      static_cast<float>(aDw) <= 0.0 || static_cast<float>(aDh) <= 0.0) {
   6025    // When we actually draw we convert to float, so also check the values as
   6026    // floats.
   6027    return;
   6028  }
   6029 
   6030  // Per spec, the smoothing setting applies only to scaling up a bitmap image.
   6031  // When down-scaling the user agent is free to choose whether or not to smooth
   6032  // the image. Nearest sampling when down-scaling is rarely desirable and
   6033  // smoothing when down-scaling matches chromium's behavior.
   6034  // If any dimension is up-scaled, we consider the image as being up-scaled.
   6035  auto scale = mTarget->GetTransform().ScaleFactors();
   6036  bool isDownScale =
   6037      aDw * Abs(scale.xScale) < aSw && aDh * Abs(scale.yScale) < aSh;
   6038 
   6039  SamplingFilter samplingFilter;
   6040  AntialiasMode antialiasMode;
   6041 
   6042  if (CurrentState().imageSmoothingEnabled || isDownScale) {
   6043    samplingFilter = gfx::SamplingFilter::LINEAR;
   6044    antialiasMode = AntialiasMode::DEFAULT;
   6045  } else {
   6046    samplingFilter = gfx::SamplingFilter::POINT;
   6047    antialiasMode = AntialiasMode::NONE;
   6048  }
   6049 
   6050  const bool needBounds = NeedToCalculateBounds();
   6051  if (!IsTargetValid()) {
   6052    return;
   6053  }
   6054  gfx::Rect bounds;
   6055  if (needBounds) {
   6056    bounds = gfx::Rect(aDx, aDy, aDw, aDh);
   6057    bounds = mTarget->GetTransform().TransformBounds(bounds);
   6058  }
   6059 
   6060  if (!IsTargetValid()) {
   6061    aError.Throw(NS_ERROR_FAILURE);
   6062    return;
   6063  }
   6064 
   6065  if (srcSurf || surfaceDescriptor.isSome()) {
   6066    gfx::Rect sourceRect(aSx, aSy, aSw, aSh);
   6067    if (srcSurf && ((element && element == mCanvasElement) ||
   6068                    (offscreenCanvas && offscreenCanvas == mOffscreenCanvas))) {
   6069      // srcSurf is a snapshot of mTarget. If we draw to mTarget now, we'll
   6070      // trigger a COW copy of the whole canvas into srcSurf. That's a huge
   6071      // waste if sourceRect doesn't cover the whole canvas.
   6072      // We avoid copying the whole canvas by manually copying just the part
   6073      // that we need.
   6074      srcSurf = ExtractSubrect(srcSurf, &sourceRect, mTarget);
   6075      // The SFE result may inadvertently keep the snapshot alive, forcing a
   6076      // copy when MarkChanged is called. Clear out possibly the last reference
   6077      // to the original snapshot to avoid this.
   6078      res.mSourceSurface = nullptr;
   6079    }
   6080 
   6081    AdjustedTarget tempTarget(this, bounds.IsEmpty() ? nullptr : &bounds, true);
   6082    if (!tempTarget) {
   6083      gfxWarning() << "Invalid adjusted target in Canvas2D "
   6084                   << gfx::hexa((DrawTarget*)mTarget) << ", "
   6085                   << NeedToDrawShadow() << NeedToApplyFilter();
   6086      return;
   6087    }
   6088 
   6089    auto op = tempTarget.UsedOperation();
   6090    if (!IsTargetValid() || !tempTarget) {
   6091      return;
   6092    }
   6093 
   6094    VideoRotation rotationDeg = VideoRotation::kDegree_0;
   6095    if (HTMLVideoElement* video = HTMLVideoElement::FromNodeOrNull(element)) {
   6096      rotationDeg = video->RotationDegrees();
   6097    }
   6098 
   6099    gfx::Rect destRect(aDx, aDy, aDw, aDh);
   6100 
   6101    Matrix currentTransform = tempTarget->GetTransform();
   6102    if (rotationDeg != VideoRotation::kDegree_0) {
   6103      tempTarget->ConcatTransform(
   6104          ComputeRotationMatrix(aDw, aDh, rotationDeg).PostTranslate(aDx, aDy));
   6105 
   6106      SwapScaleWidthHeightForRotation(destRect, rotationDeg);
   6107      // When rotation exists, aDx, aDy is handled by transform, Since aDest.x
   6108      // aDest.y handling of DrawSurface() does not care about the rotation.
   6109      destRect.x = 0;
   6110      destRect.y = 0;
   6111    }
   6112 
   6113    if (srcSurf) {
   6114      MOZ_ASSERT(surfaceDescriptor.isNothing());
   6115 
   6116      tempTarget.DrawSurface(
   6117          srcSurf, destRect, sourceRect,
   6118          DrawSurfaceOptions(samplingFilter, SamplingBounds::UNBOUNDED),
   6119          DrawOptions(CurrentState().globalAlpha, op, antialiasMode));
   6120    } else if (surfaceDescriptor.isSome()) {
   6121      MOZ_ASSERT(!tempTarget.UseOptimizeShadow());
   6122      MOZ_ASSERT(res.mLayersImage);
   6123 
   6124      mTarget->DrawSurfaceDescriptor(
   6125          surfaceDescriptor.ref(), res.mLayersImage, destRect, sourceRect,
   6126          DrawSurfaceOptions(samplingFilter, SamplingBounds::UNBOUNDED),
   6127          DrawOptions(CurrentState().globalAlpha, op, antialiasMode));
   6128    } else {
   6129      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   6130    }
   6131 
   6132    if (rotationDeg != VideoRotation::kDegree_0) {
   6133      tempTarget->SetTransform(currentTransform);
   6134    }
   6135 
   6136  } else {
   6137    DrawDirectlyToCanvas(drawInfo, &bounds, gfx::Rect(aDx, aDy, aDw, aDh),
   6138                         gfx::Rect(aSx, aSy, aSw, aSh), imgSize);
   6139  }
   6140 
   6141  RedrawUser(gfxRect(aDx, aDy, aDw, aDh));
   6142 }
   6143 
   6144 void CanvasRenderingContext2D::DrawDirectlyToCanvas(
   6145    const DirectDrawInfo& aImage, gfx::Rect* aBounds, gfx::Rect aDest,
   6146    gfx::Rect aSrc, gfx::IntSize aImgSize) {
   6147  MOZ_ASSERT(aSrc.width > 0 && aSrc.height > 0,
   6148             "Need positive source width and height");
   6149 
   6150  AdjustedTarget tempTarget(this, aBounds->IsEmpty() ? nullptr : aBounds);
   6151  if (!tempTarget || !tempTarget->IsValid()) {
   6152    return;
   6153  }
   6154 
   6155  // Get any existing transforms on the context, including transformations used
   6156  // for context shadow.
   6157  Matrix matrix = tempTarget->GetTransform();
   6158  gfxMatrix contextMatrix = ThebesMatrix(matrix);
   6159  MatrixScalesDouble contextScale = contextMatrix.ScaleFactors();
   6160 
   6161  // Scale the dest rect to include the context scale.
   6162  aDest.Scale((float)contextScale.xScale, (float)contextScale.yScale);
   6163 
   6164  // Scale the image size to the dest rect, and adjust the source rect to match.
   6165  MatrixScalesDouble scale(aDest.width / aSrc.width,
   6166                           aDest.height / aSrc.height);
   6167  IntSize scaledImageSize =
   6168      IntSize::Ceil(static_cast<float>(scale.xScale * aImgSize.width),
   6169                    static_cast<float>(scale.yScale * aImgSize.height));
   6170  aSrc.Scale(static_cast<float>(scale.xScale),
   6171             static_cast<float>(scale.yScale));
   6172 
   6173  // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
   6174  // the matrix even though this is a temp gfxContext.
   6175  AutoRestoreTransform autoRestoreTransform(mTarget);
   6176 
   6177  gfxContext context(tempTarget);
   6178  context.SetMatrixDouble(
   6179      contextMatrix
   6180          .PreScale(1.0 / contextScale.xScale, 1.0 / contextScale.yScale)
   6181          .PreTranslate(aDest.x - aSrc.x, aDest.y - aSrc.y));
   6182 
   6183  context.SetOp(tempTarget.UsedOperation());
   6184 
   6185  // FLAG_CLAMP is added for increased performance, since we never tile here.
   6186  uint32_t modifiedFlags = aImage.mDrawingFlags | imgIContainer::FLAG_CLAMP;
   6187 
   6188  // XXX hmm is scaledImageSize really in CSS pixels?
   6189  CSSIntSize sz(scaledImageSize.width, scaledImageSize.height);
   6190  SVGImageContext svgContext(Some(sz));
   6191 
   6192  if (mContextProperties != CanvasContextProperties::None &&
   6193      aImage.mImgContainer->GetType() == imgIContainer::TYPE_VECTOR) {
   6194    SVGEmbeddingContextPaint* contextPaint =
   6195        svgContext.GetOrCreateContextPaint();
   6196    const ContextState& state = CurrentState();
   6197 
   6198    if (mContextProperties != CanvasContextProperties::Fill &&
   6199        state.StyleIsColor(Style::STROKE)) {
   6200      contextPaint->SetStroke(state.colorStyles[Style::STROKE]);
   6201    }
   6202 
   6203    if (mContextProperties != CanvasContextProperties::Stroke &&
   6204        state.StyleIsColor(Style::FILL)) {
   6205      contextPaint->SetFill(state.colorStyles[Style::FILL]);
   6206    }
   6207  }
   6208 
   6209  auto result = aImage.mImgContainer->Draw(
   6210      &context, scaledImageSize,
   6211      ImageRegion::Create(gfxRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height)),
   6212      aImage.mWhichFrame, SamplingFilter::GOOD, svgContext, modifiedFlags,
   6213      CurrentState().globalAlpha);
   6214 
   6215  if (result != ImgDrawResult::SUCCESS) {
   6216    NS_WARNING("imgIContainer::Draw failed");
   6217  }
   6218 }
   6219 
   6220 void CanvasRenderingContext2D::SetGlobalCompositeOperation(
   6221    const nsAString& aOp, ErrorResult& aError) {
   6222  CompositionOp comp_op;
   6223 
   6224 #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
   6225  if (aOp.EqualsLiteral(cvsop)) comp_op = CompositionOp::OP_##op2d;
   6226 
   6227  CANVAS_OP_TO_GFX_OP("clear", CLEAR)
   6228  else CANVAS_OP_TO_GFX_OP("copy", SOURCE)
   6229  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
   6230  else CANVAS_OP_TO_GFX_OP("source-in", IN)
   6231  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
   6232  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
   6233  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
   6234  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
   6235  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
   6236  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
   6237  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
   6238  else CANVAS_OP_TO_GFX_OP("xor", XOR)
   6239  else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
   6240  else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
   6241  else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
   6242  else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
   6243  else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
   6244  else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
   6245  else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
   6246  else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
   6247  else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
   6248  else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
   6249  else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
   6250  else CANVAS_OP_TO_GFX_OP("hue", HUE)
   6251  else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
   6252  else CANVAS_OP_TO_GFX_OP("color", COLOR)
   6253  else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
   6254  // XXX ERRMSG we need to report an error to developers here! (bug 329026)
   6255  else return;
   6256 
   6257 #undef CANVAS_OP_TO_GFX_OP
   6258  CurrentState().op = comp_op;
   6259 }
   6260 
   6261 void CanvasRenderingContext2D::GetGlobalCompositeOperation(
   6262    nsAString& aOp, ErrorResult& aError) {
   6263  CompositionOp comp_op = CurrentState().op;
   6264 
   6265 #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
   6266  if (comp_op == CompositionOp::OP_##op2d) aOp.AssignLiteral(cvsop);
   6267 
   6268  CANVAS_OP_TO_GFX_OP("clear", CLEAR)
   6269  else CANVAS_OP_TO_GFX_OP("copy", SOURCE)
   6270  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
   6271  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
   6272  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
   6273  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
   6274  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
   6275  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
   6276  else CANVAS_OP_TO_GFX_OP("source-in", IN)
   6277  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
   6278  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
   6279  else CANVAS_OP_TO_GFX_OP("xor", XOR)
   6280  else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
   6281  else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
   6282  else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
   6283  else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
   6284  else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
   6285  else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
   6286  else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
   6287  else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
   6288  else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
   6289  else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
   6290  else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
   6291  else CANVAS_OP_TO_GFX_OP("hue", HUE)
   6292  else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
   6293  else CANVAS_OP_TO_GFX_OP("color", COLOR)
   6294  else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
   6295  else {
   6296    aError.Throw(NS_ERROR_FAILURE);
   6297  }
   6298 
   6299 #undef CANVAS_OP_TO_GFX_OP
   6300 }
   6301 
   6302 void CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow,
   6303                                          double aX, double aY, double aW,
   6304                                          double aH, const nsACString& aBgColor,
   6305                                          uint32_t aFlags,
   6306                                          nsIPrincipal& aSubjectPrincipal,
   6307                                          ErrorResult& aError) {
   6308  if (int32_t(aW) == 0 || int32_t(aH) == 0) {
   6309    return;
   6310  }
   6311 
   6312  // protect against too-large surfaces that will cause allocation
   6313  // or overflow issues
   6314  if (!Factory::CheckSurfaceSize(IntSize(int32_t(aW), int32_t(aH)), 0xffff)) {
   6315    aError.Throw(NS_ERROR_FAILURE);
   6316    return;
   6317  }
   6318 
   6319  Document* doc = aWindow.GetExtantDoc();
   6320  if (doc && aSubjectPrincipal.GetIsAddonOrExpandedAddonPrincipal()) {
   6321    doc->WarnOnceAbout(
   6322        DeprecatedOperations::eDrawWindowCanvasRenderingContext2D);
   6323  }
   6324 
   6325  // Flush layout updates
   6326  if (!(aFlags & CanvasRenderingContext2D_Binding::DRAWWINDOW_DO_NOT_FLUSH)) {
   6327    nsContentUtils::FlushLayoutForTree(aWindow.GetOuterWindow());
   6328  }
   6329 
   6330  CompositionOp op = CurrentState().op;
   6331  bool discardContent =
   6332      GlobalAlpha() == 1.0f &&
   6333      (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
   6334  const gfx::Rect drawRect(aX, aY, aW, aH);
   6335  if (!EnsureTarget(aError, discardContent ? &drawRect : nullptr)) {
   6336    return;
   6337  }
   6338 
   6339  MOZ_ASSERT(IsTargetValid());
   6340 
   6341  RefPtr<nsPresContext> presContext;
   6342  nsIDocShell* docshell = aWindow.GetDocShell();
   6343  if (docshell) {
   6344    presContext = docshell->GetPresContext();
   6345  }
   6346  if (!presContext) {
   6347    aError.Throw(NS_ERROR_FAILURE);
   6348    return;
   6349  }
   6350 
   6351  Maybe<nscolor> backgroundColor = ParseColor(aBgColor);
   6352  if (!backgroundColor) {
   6353    aError.Throw(NS_ERROR_FAILURE);
   6354    return;
   6355  }
   6356 
   6357  nsRect r(nsPresContext::CSSPixelsToAppUnits((float)aX),
   6358           nsPresContext::CSSPixelsToAppUnits((float)aY),
   6359           nsPresContext::CSSPixelsToAppUnits((float)aW),
   6360           nsPresContext::CSSPixelsToAppUnits((float)aH));
   6361  RenderDocumentFlags renderDocFlags =
   6362      (RenderDocumentFlags::IgnoreViewportScrolling |
   6363       RenderDocumentFlags::DocumentRelative);
   6364  if (aFlags & CanvasRenderingContext2D_Binding::DRAWWINDOW_DRAW_CARET) {
   6365    renderDocFlags |= RenderDocumentFlags::DrawCaret;
   6366  }
   6367  if (aFlags & CanvasRenderingContext2D_Binding::DRAWWINDOW_DRAW_VIEW) {
   6368    renderDocFlags &= ~(RenderDocumentFlags::IgnoreViewportScrolling |
   6369                        RenderDocumentFlags::DocumentRelative);
   6370  }
   6371  if (aFlags & CanvasRenderingContext2D_Binding::DRAWWINDOW_USE_WIDGET_LAYERS) {
   6372    renderDocFlags |= RenderDocumentFlags::UseWidgetLayers;
   6373  }
   6374  if (aFlags &
   6375      CanvasRenderingContext2D_Binding::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
   6376    renderDocFlags |= RenderDocumentFlags::AsyncDecodeImages;
   6377  }
   6378  if (aFlags & CanvasRenderingContext2D_Binding::DRAWWINDOW_DO_NOT_FLUSH) {
   6379    renderDocFlags |= RenderDocumentFlags::DrawWindowNotFlushing;
   6380  }
   6381 
   6382  // gfxContext-over-Azure may modify the DrawTarget's transform, so
   6383  // save and restore it
   6384  Matrix matrix = mTarget->GetTransform();
   6385  double sw = matrix._11 * aW;
   6386  double sh = matrix._22 * aH;
   6387  if (!sw || !sh) {
   6388    return;
   6389  }
   6390 
   6391  Maybe<gfxContext> thebes;
   6392  RefPtr<DrawTarget> drawDT;
   6393  // Rendering directly is faster and can be done if mTarget supports Azure
   6394  // and does not need alpha blending.
   6395  // Since the pre-transaction callback calls ReturnTarget, we can't have a
   6396  // gfxContext wrapped around it when using a shared buffer provider because
   6397  // the DrawTarget's shared buffer may be unmapped in ReturnTarget.
   6398  op = CompositionOp::OP_ADD;
   6399  if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) &&
   6400      GlobalAlpha() == 1.0f) {
   6401    op = CurrentState().op;
   6402    if (!IsTargetValid()) {
   6403      aError.Throw(NS_ERROR_FAILURE);
   6404      return;
   6405    }
   6406  }
   6407  if (op == CompositionOp::OP_OVER &&
   6408      (!mBufferProvider || !mBufferProvider->IsShared())) {
   6409    thebes.emplace(mTarget);
   6410    thebes.ref().SetMatrix(matrix);
   6411  } else {
   6412    IntSize dtSize = IntSize::Ceil(sw, sh);
   6413    if (!Factory::AllowedSurfaceSize(dtSize)) {
   6414      // attempt to limit the DT to what will actually cover the target
   6415      Size limitSize(mTarget->GetSize());
   6416      limitSize.Scale(matrix._11, matrix._22);
   6417      dtSize = Min(dtSize, IntSize::Ceil(limitSize));
   6418      // if the DT is still too big, then error
   6419      if (!Factory::AllowedSurfaceSize(dtSize)) {
   6420        aError.Throw(NS_ERROR_FAILURE);
   6421        return;
   6422      }
   6423    }
   6424    drawDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
   6425        dtSize, SurfaceFormat::B8G8R8A8);
   6426    if (!drawDT || !drawDT->IsValid()) {
   6427      aError.Throw(NS_ERROR_FAILURE);
   6428      return;
   6429    }
   6430 
   6431    thebes.emplace(drawDT);
   6432    thebes.ref().SetMatrix(Matrix::Scaling(matrix._11, matrix._22));
   6433  }
   6434  MOZ_ASSERT(thebes.isSome());
   6435 
   6436  RefPtr<PresShell> presShell = presContext->PresShell();
   6437 
   6438  (void)presShell->RenderDocument(r, renderDocFlags, *backgroundColor,
   6439                                  &thebes.ref());
   6440  // If this canvas was contained in the drawn window, the pre-transaction
   6441  // callback may have returned its DT. If so, we must reacquire it here.
   6442  if (!EnsureTarget(aError, discardContent ? &drawRect : nullptr)) {
   6443    return;
   6444  }
   6445 
   6446  MOZ_ASSERT(IsTargetValid());
   6447 
   6448  if (drawDT) {
   6449    RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
   6450    if (NS_WARN_IF(!snapshot)) {
   6451      aError.Throw(NS_ERROR_FAILURE);
   6452      return;
   6453    }
   6454 
   6455    op = CurrentState().op;
   6456    if (!IsTargetValid()) {
   6457      aError.Throw(NS_ERROR_FAILURE);
   6458      return;
   6459    }
   6460    gfx::Rect destRect(0, 0, aW, aH);
   6461    gfx::Rect sourceRect(0, 0, sw, sh);
   6462    mTarget->DrawSurface(snapshot, destRect, sourceRect,
   6463                         DrawSurfaceOptions(gfx::SamplingFilter::POINT),
   6464                         DrawOptions(GlobalAlpha(), op, AntialiasMode::NONE));
   6465  } else {
   6466    mTarget->SetTransform(matrix);
   6467  }
   6468 
   6469  // note that x and y are coordinates in the document that
   6470  // we're drawing; x and y are drawn to 0,0 in current user
   6471  // space.
   6472  RedrawUser(gfxRect(0, 0, aW, aH));
   6473 }
   6474 
   6475 //
   6476 // device pixel getting/setting
   6477 //
   6478 
   6479 already_AddRefed<ImageData> CanvasRenderingContext2D::GetImageData(
   6480    JSContext* aCx, int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
   6481    nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
   6482  if (!mCanvasElement && !mDocShell && !mOffscreenCanvas) {
   6483    NS_ERROR("No canvas element and no docshell in GetImageData!!!");
   6484    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
   6485    return nullptr;
   6486  }
   6487 
   6488  // Check only if we have a canvas element; if we were created with a docshell,
   6489  // then it's special internal use.
   6490  if (IsWriteOnly() ||
   6491      (mCanvasElement && !mCanvasElement->CallerCanRead(aSubjectPrincipal)) ||
   6492      (mOffscreenCanvas &&
   6493       !mOffscreenCanvas->CallerCanRead(aSubjectPrincipal))) {
   6494    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
   6495    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
   6496    return nullptr;
   6497  }
   6498 
   6499  if (!aSw || !aSh) {
   6500    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   6501    return nullptr;
   6502  }
   6503 
   6504  // Handle negative width and height by flipping the rectangle over in the
   6505  // relevant direction.
   6506  uint32_t w, h;
   6507  if (aSw < 0) {
   6508    w = uint32_t(-aSw);
   6509    aSx -= w;
   6510  } else {
   6511    w = aSw;
   6512  }
   6513  if (aSh < 0) {
   6514    h = uint32_t(-aSh);
   6515    aSy -= h;
   6516  } else {
   6517    h = aSh;
   6518  }
   6519 
   6520  if (w == 0) {
   6521    w = 1;
   6522  }
   6523  if (h == 0) {
   6524    h = 1;
   6525  }
   6526 
   6527  RecordCanvasUsage(CanvasExtractionAPI::GetImageData, CSSIntSize(w, h));
   6528 
   6529  JS::Rooted<JSObject*> array(aCx);
   6530  aError = GetImageDataArray(aCx, aSx, aSy, w, h, aSubjectPrincipal,
   6531                             array.address());
   6532  if (aError.Failed()) {
   6533    return nullptr;
   6534  }
   6535  MOZ_ASSERT(array);
   6536  return do_AddRef(new ImageData(GetParentObject(), w, h, array));
   6537 }
   6538 
   6539 static IntRect ClipImageDataTransfer(IntRect& aSrc, const IntPoint& aDestOffset,
   6540                                     const IntSize& aDestBounds) {
   6541  IntRect dest = aSrc;
   6542  dest.SafeMoveBy(aDestOffset);
   6543  dest = IntRect(IntPoint(0, 0), aDestBounds).SafeIntersect(dest);
   6544 
   6545  aSrc = aSrc.SafeIntersect(dest - aDestOffset);
   6546  return aSrc + aDestOffset;
   6547 }
   6548 
   6549 nsresult CanvasRenderingContext2D::GetImageDataArray(
   6550    JSContext* aCx, int32_t aX, int32_t aY, uint32_t aWidth, uint32_t aHeight,
   6551    nsIPrincipal& aSubjectPrincipal, JSObject** aRetval) {
   6552  MOZ_ASSERT(aWidth && aHeight);
   6553 
   6554  // Restrict the typed array length to INT32_MAX because that's all we support.
   6555  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
   6556  if (!len.isValid() || len.value() > INT32_MAX) {
   6557    return NS_ERROR_DOM_INDEX_SIZE_ERR;
   6558  }
   6559 
   6560  CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
   6561  CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
   6562 
   6563  if (!rightMost.isValid() || !bottomMost.isValid()) {
   6564    return NS_ERROR_DOM_SYNTAX_ERR;
   6565  }
   6566 
   6567  JS::Rooted<JSObject*> darray(aCx, JS_NewUint8ClampedArray(aCx, len.value()));
   6568  if (!darray) {
   6569    return NS_ERROR_OUT_OF_MEMORY;
   6570  }
   6571 
   6572  if (mZero) {
   6573    *aRetval = darray;
   6574    return NS_OK;
   6575  }
   6576 
   6577  IntRect dstWriteRect(0, 0, aWidth, aHeight);
   6578  IntRect srcReadRect = ClipImageDataTransfer(dstWriteRect, IntPoint(aX, aY),
   6579                                              IntSize(mWidth, mHeight));
   6580  if (srcReadRect.IsEmpty()) {
   6581    *aRetval = darray;
   6582    return NS_OK;
   6583  }
   6584 
   6585  if (!GetBufferProvider() && !EnsureTarget()) {
   6586    return NS_ERROR_FAILURE;
   6587  }
   6588 
   6589  RefPtr<SourceSurface> snapshot = mBufferProvider->BorrowSnapshot();
   6590  if (!snapshot) {
   6591    return NS_ERROR_OUT_OF_MEMORY;
   6592  }
   6593 
   6594  RefPtr<DataSourceSurface> readback = snapshot->GetDataSurface();
   6595  mBufferProvider->ReturnSnapshot(snapshot.forget());
   6596 
   6597  // Check for site-specific permission.
   6598  CanvasUtils::ImageExtraction extractionBehavior =
   6599      CanvasUtils::ImageExtraction::Unrestricted;
   6600  if (mCanvasElement) {
   6601    extractionBehavior = CanvasUtils::ImageExtractionResult(mCanvasElement, aCx,
   6602                                                            &aSubjectPrincipal);
   6603  } else if (mOffscreenCanvas) {
   6604    extractionBehavior = CanvasUtils::ImageExtractionResult(
   6605        mOffscreenCanvas, aCx, &aSubjectPrincipal);
   6606  }
   6607 
   6608  // Clone the data source surface if canvas randomization is enabled. We need
   6609  // to do this because we don't want to alter the actual image buffer.
   6610  // Otherwise, we will provide inconsistent image data with multiple calls.
   6611  //
   6612  // Note that we don't need to clone if we will use the place holder because
   6613  // the place holder doesn't use actual image data.
   6614  if (extractionBehavior == CanvasUtils::ImageExtraction::Randomize) {
   6615    if (readback) {
   6616      readback = CreateDataSourceSurfaceByCloning(readback);
   6617    }
   6618  }
   6619 
   6620  DataSourceSurface::MappedSurface rawData;
   6621  if (!readback || !readback->Map(DataSourceSurface::READ, &rawData)) {
   6622    return NS_ERROR_OUT_OF_MEMORY;
   6623  }
   6624 
   6625  do {
   6626    uint8_t* randomData;
   6627    const IntSize size = readback->GetSize();
   6628    nsRFPService::PotentiallyDumpImage(PrincipalOrNull(), rawData.mData,
   6629                                       size.width, size.height,
   6630                                       size.height * size.width * 4);
   6631    if (extractionBehavior == CanvasUtils::ImageExtraction::Placeholder) {
   6632      // Since we cannot call any GC-able functions (like requesting the RNG
   6633      // service) after we call JS_GetUint8ClampedArrayData, we will
   6634      // pre-generate the randomness required for GeneratePlaceholderCanvasData.
   6635      randomData = TryToGenerateRandomDataForPlaceholderCanvasData();
   6636    } else if (extractionBehavior == CanvasUtils::ImageExtraction::Randomize) {
   6637      // Apply the random noises if canvan randomization is enabled. We don't
   6638      // need to calculate random noises if we are going to use the place
   6639      // holder.
   6640 
   6641      nsRFPService::RandomizePixels(GetCookieJarSettings(), PrincipalOrNull(),
   6642                                    rawData.mData, size.width, size.height,
   6643                                    size.height * size.width * 4,
   6644                                    SurfaceFormat::A8R8G8B8_UINT32);
   6645    }
   6646 
   6647    JS::AutoCheckCannotGC nogc;
   6648    bool isShared;
   6649    uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
   6650    MOZ_ASSERT(!isShared);  // Should not happen, data was created above
   6651 
   6652    if (extractionBehavior == CanvasUtils::ImageExtraction::Placeholder) {
   6653      FillPlaceholderCanvas(randomData, len.value(), data);
   6654      break;
   6655    }
   6656 
   6657    uint32_t srcStride = rawData.mStride;
   6658    uint8_t* src =
   6659        rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
   6660 
   6661    uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
   6662 
   6663    if (mOpaque) {
   6664      SwizzleData(src, srcStride, SurfaceFormat::X8R8G8B8_UINT32, dst,
   6665                  aWidth * 4, SurfaceFormat::R8G8B8A8, dstWriteRect.Size());
   6666    } else {
   6667      UnpremultiplyData(src, srcStride, SurfaceFormat::A8R8G8B8_UINT32, dst,
   6668                        aWidth * 4, SurfaceFormat::R8G8B8A8,
   6669                        dstWriteRect.Size());
   6670    }
   6671  } while (false);
   6672 
   6673  readback->Unmap();
   6674  *aRetval = darray;
   6675  return NS_OK;
   6676 }
   6677 
   6678 void CanvasRenderingContext2D::EnsureErrorTarget() {
   6679  if (sErrorTarget.get()) {
   6680    return;
   6681  }
   6682 
   6683  RefPtr<DrawTarget> errorTarget =
   6684      gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(
   6685          IntSize(1, 1), SurfaceFormat::B8G8R8A8);
   6686  MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
   6687 
   6688  sErrorTarget.set(errorTarget.forget().take());
   6689 }
   6690 
   6691 void CanvasRenderingContext2D::FillRuleChanged() {
   6692  if (mPath) {
   6693    mPathBuilder = Path::ToBuilder(mPath.forget(), CurrentState().fillRule);
   6694  }
   6695 }
   6696 
   6697 void CanvasRenderingContext2D::PutImageData(ImageData& aImageData, int32_t aDx,
   6698                                            int32_t aDy, ErrorResult& aRv) {
   6699  RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
   6700  PutImageData_explicit(aDx, aDy, aImageData, false, 0, 0, 0, 0, aRv);
   6701 }
   6702 
   6703 void CanvasRenderingContext2D::PutImageData(ImageData& aImageData, int32_t aDx,
   6704                                            int32_t aDy, int32_t aDirtyX,
   6705                                            int32_t aDirtyY,
   6706                                            int32_t aDirtyWidth,
   6707                                            int32_t aDirtyHeight,
   6708                                            ErrorResult& aRv) {
   6709  PutImageData_explicit(aDx, aDy, aImageData, true, aDirtyX, aDirtyY,
   6710                        aDirtyWidth, aDirtyHeight, aRv);
   6711 }
   6712 
   6713 void CanvasRenderingContext2D::PutImageData_explicit(
   6714    int32_t aX, int32_t aY, ImageData& aImageData, bool aHasDirtyRect,
   6715    int32_t aDirtyX, int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight,
   6716    ErrorResult& aRv) {
   6717  RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
   6718  if (!arr.Init(aImageData.GetDataObject())) {
   6719    return aRv.ThrowInvalidStateError(
   6720        "Failed to extract Uint8ClampedArray from ImageData (security check "
   6721        "failed?)");
   6722  }
   6723 
   6724  const uint32_t width = aImageData.Width();
   6725  const uint32_t height = aImageData.Height();
   6726  if (width == 0 || height == 0) {
   6727    return aRv.ThrowInvalidStateError("Passed-in image is empty");
   6728  }
   6729 
   6730  IntRect dirtyRect;
   6731  IntRect imageDataRect(0, 0, width, height);
   6732 
   6733  if (aHasDirtyRect) {
   6734    // fix up negative dimensions
   6735    if (aDirtyWidth < 0) {
   6736      if (aDirtyWidth == INT_MIN) {
   6737        return aRv.ThrowInvalidStateError("Dirty width is invalid");
   6738      }
   6739 
   6740      CheckedInt32 checkedDirtyX = CheckedInt32(aDirtyX) + aDirtyWidth;
   6741 
   6742      if (!checkedDirtyX.isValid()) {
   6743        return aRv.ThrowInvalidStateError("Dirty width is invalid");
   6744      }
   6745 
   6746      aDirtyX = checkedDirtyX.value();
   6747      aDirtyWidth = -aDirtyWidth;
   6748    }
   6749 
   6750    if (aDirtyHeight < 0) {
   6751      if (aDirtyHeight == INT_MIN) {
   6752        return aRv.ThrowInvalidStateError("Dirty height is invalid");
   6753      }
   6754 
   6755      CheckedInt32 checkedDirtyY = CheckedInt32(aDirtyY) + aDirtyHeight;
   6756 
   6757      if (!checkedDirtyY.isValid()) {
   6758        return aRv.ThrowInvalidStateError("Dirty height is invalid");
   6759      }
   6760 
   6761      aDirtyY = checkedDirtyY.value();
   6762      aDirtyHeight = -aDirtyHeight;
   6763    }
   6764 
   6765    // bound the dirty rect within the imageData rectangle
   6766    dirtyRect = imageDataRect.Intersect(
   6767        IntRect(aDirtyX, aDirtyY, aDirtyWidth, aDirtyHeight));
   6768 
   6769    if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
   6770      return;
   6771    }
   6772  } else {
   6773    dirtyRect = imageDataRect;
   6774  }
   6775 
   6776  IntRect srcRect = dirtyRect;
   6777  dirtyRect = ClipImageDataTransfer(srcRect, IntPoint(aX, aY),
   6778                                    IntSize(mWidth, mHeight));
   6779  if (dirtyRect.IsEmpty()) {
   6780    return;
   6781  }
   6782 
   6783  RefPtr<DataSourceSurface> sourceSurface;
   6784  uint8_t* lockedBits = nullptr;
   6785 
   6786  // The canvas spec says that the current path, transformation matrix,
   6787  // shadow attributes, global alpha, the clipping region, and global
   6788  // composition operator must not affect the getImageData() and
   6789  // putImageData() methods.
   6790  const gfx::Rect putRect(dirtyRect);
   6791  if (!EnsureTarget(aRv, &putRect, true, true)) {
   6792    return;
   6793  }
   6794 
   6795  MOZ_ASSERT(IsTargetValid());
   6796 
   6797  DataSourceSurface::MappedSurface map;
   6798  uint8_t* dstData;
   6799  IntSize dstSize;
   6800  int32_t dstStride;
   6801  SurfaceFormat dstFormat;
   6802  if (mTarget->LockBits(&lockedBits, &dstSize, &dstStride, &dstFormat)) {
   6803    dstData = lockedBits + dirtyRect.y * dstStride + dirtyRect.x * 4;
   6804  } else {
   6805    sourceSurface = Factory::CreateDataSourceSurface(
   6806        dirtyRect.Size(), SurfaceFormat::B8G8R8A8, false);
   6807 
   6808    // In certain scenarios, requesting larger than 8k image fails.  Bug
   6809    // 803568 covers the details of how to run into it, but the full
   6810    // detailed investigation hasn't been done to determine the
   6811    // underlying cause.  We will just handle the failure to allocate
   6812    // the surface to avoid a crash.
   6813    if (!sourceSurface) {
   6814      return aRv.Throw(NS_ERROR_FAILURE);
   6815    }
   6816    if (!sourceSurface->Map(DataSourceSurface::READ_WRITE, &map)) {
   6817      return aRv.Throw(NS_ERROR_FAILURE);
   6818    }
   6819 
   6820    dstData = map.mData;
   6821    if (!dstData) {
   6822      return aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   6823    }
   6824    dstStride = map.mStride;
   6825    dstFormat = sourceSurface->GetFormat();
   6826  }
   6827 
   6828  arr.ProcessData(
   6829      [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&& nogc) {
   6830        // Verify that the length hasn't changed.
   6831        if (aData.Length() != width * height * 4) {
   6832          // FIXME Should this call ReleaseBits/Unmap?
   6833          return aRv.ThrowInvalidStateError("Invalid width or height");
   6834        }
   6835 
   6836        uint8_t* srcData =
   6837            aData.Elements() + srcRect.y * (width * 4) + srcRect.x * 4;
   6838 
   6839        PremultiplyData(srcData, width * 4, SurfaceFormat::R8G8B8A8, dstData,
   6840                        dstStride,
   6841                        mOpaque ? SurfaceFormat::X8R8G8B8_UINT32
   6842                                : SurfaceFormat::A8R8G8B8_UINT32,
   6843                        dirtyRect.Size());
   6844      });
   6845 
   6846  // Ensure surfaces unmapped before potential error exit.
   6847  if (lockedBits) {
   6848    mTarget->ReleaseBits(lockedBits);
   6849  } else if (sourceSurface) {
   6850    sourceSurface->Unmap();
   6851  }
   6852 
   6853  if (aRv.Failed()) {
   6854    return;
   6855  }
   6856 
   6857  if (sourceSurface) {
   6858    mTarget->CopySurface(sourceSurface, dirtyRect - dirtyRect.TopLeft(),
   6859                         dirtyRect.TopLeft());
   6860  }
   6861 
   6862  Redraw(
   6863      gfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
   6864 }
   6865 
   6866 static already_AddRefed<ImageData> CreateImageData(
   6867    JSContext* aCx, CanvasRenderingContext2D* aContext, uint32_t aW,
   6868    uint32_t aH, ErrorResult& aError) {
   6869  if (aW == 0) aW = 1;
   6870  if (aH == 0) aH = 1;
   6871 
   6872  // Restrict the typed array length to INT32_MAX because that's all we support
   6873  // in dom::TypedArray::ComputeState.
   6874  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aW) * aH * 4;
   6875  if (!len.isValid() || len.value() > INT32_MAX) {
   6876    aError.ThrowIndexSizeError("Invalid width or height");
   6877    return nullptr;
   6878  }
   6879 
   6880  // Create the fast typed array; it's initialized to 0 by default.
   6881  JS::Rooted<JSObject*> darray(
   6882      aCx, Uint8ClampedArray::Create(aCx, aContext, len.value(), aError));
   6883  if (aError.Failed()) {
   6884    return nullptr;
   6885  }
   6886 
   6887  return do_AddRef(new ImageData(aContext->GetParentObject(), aW, aH, darray));
   6888 }
   6889 
   6890 already_AddRefed<ImageData> CanvasRenderingContext2D::CreateImageData(
   6891    JSContext* aCx, int32_t aSw, int32_t aSh, ErrorResult& aError) {
   6892  if (!aSw || !aSh) {
   6893    aError.ThrowIndexSizeError("Invalid width or height");
   6894    return nullptr;
   6895  }
   6896 
   6897  uint32_t w = Abs(aSw);
   6898  uint32_t h = Abs(aSh);
   6899  return dom::CreateImageData(aCx, this, w, h, aError);
   6900 }
   6901 
   6902 already_AddRefed<ImageData> CanvasRenderingContext2D::CreateImageData(
   6903    JSContext* aCx, ImageData& aImagedata, ErrorResult& aError) {
   6904  return dom::CreateImageData(aCx, this, aImagedata.Width(),
   6905                              aImagedata.Height(), aError);
   6906 }
   6907 
   6908 void CanvasRenderingContext2D::OnMemoryPressure() {
   6909  if (mBufferProvider) {
   6910    mBufferProvider->OnMemoryPressure();
   6911  }
   6912 }
   6913 
   6914 void CanvasRenderingContext2D::OnBeforePaintTransaction() {
   6915  if (!mTarget) return;
   6916  OnStableState();
   6917 }
   6918 
   6919 void CanvasRenderingContext2D::OnDidPaintTransaction() { MarkContextClean(); }
   6920 
   6921 bool CanvasRenderingContext2D::UpdateWebRenderCanvasData(
   6922    nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) {
   6923  if (mOpaque) {
   6924    // If we're opaque then make sure we have a surface so we paint black
   6925    // instead of transparent.
   6926    EnsureTarget();
   6927  }
   6928 
   6929  // Don't call EnsureTarget() ... if there isn't already a surface, then
   6930  // we have nothing to paint and there is no need to create a surface just
   6931  // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
   6932  // layer manager which must NOT happen during a paint.
   6933  if (!mBufferProvider && !IsTargetValid()) {
   6934    // No DidTransactionCallback will be received, so mark the context clean
   6935    // now so future invalidations will be dispatched.
   6936    MarkContextClean();
   6937    // Clear CanvasRenderer of WebRenderCanvasData
   6938    aCanvasData->ClearCanvasRenderer();
   6939    return false;
   6940  }
   6941 
   6942  auto renderer = aCanvasData->GetCanvasRenderer();
   6943 
   6944  if (!mResetLayer && renderer) {
   6945    CanvasRendererData data;
   6946    data.mContext = this;
   6947    data.mSize = GetSize();
   6948 
   6949    if (renderer->IsDataValid(data)) {
   6950      return true;
   6951    }
   6952  }
   6953 
   6954  renderer = aCanvasData->CreateCanvasRenderer();
   6955  if (!InitializeCanvasRenderer(aBuilder, renderer)) {
   6956    // Clear CanvasRenderer of WebRenderCanvasData
   6957    aCanvasData->ClearCanvasRenderer();
   6958    return false;
   6959  }
   6960 
   6961  MOZ_ASSERT(renderer);
   6962  mResetLayer = false;
   6963  return true;
   6964 }
   6965 
   6966 bool CanvasRenderingContext2D::InitializeCanvasRenderer(
   6967    nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer) {
   6968  CanvasRendererData data;
   6969  data.mContext = this;
   6970  data.mSize = GetSize();
   6971  data.mIsOpaque = mOpaque;
   6972  data.mDoPaintCallbacks = true;
   6973 
   6974  if (!mBufferProvider) {
   6975    // Force the creation of a buffer provider.
   6976    EnsureTarget();
   6977    ReturnTarget();
   6978    if (!mBufferProvider) {
   6979      MarkContextClean();
   6980      return false;
   6981    }
   6982  }
   6983 
   6984  aRenderer->Initialize(data);
   6985  aRenderer->SetDirty();
   6986  return true;
   6987 }
   6988 
   6989 void CanvasRenderingContext2D::MarkContextClean() {
   6990  if (mInvalidateCount > 0) {
   6991    mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
   6992  }
   6993  mIsEntireFrameInvalid = false;
   6994  mInvalidateCount = 0;
   6995 }
   6996 
   6997 void CanvasRenderingContext2D::GetAppUnitsValues(int32_t* aPerDevPixel,
   6998                                                 int32_t* aPerCSSPixel) {
   6999  // If we don't have a canvas element, we just return something generic.
   7000  if (aPerDevPixel) {
   7001    *aPerDevPixel = 60;
   7002  }
   7003  if (aPerCSSPixel) {
   7004    *aPerCSSPixel = 60;
   7005  }
   7006  PresShell* presShell = GetPresShell();
   7007  if (!presShell) {
   7008    return;
   7009  }
   7010  nsPresContext* presContext = presShell->GetPresContext();
   7011  if (!presContext) {
   7012    return;
   7013  }
   7014  if (aPerDevPixel) {
   7015    *aPerDevPixel = presContext->AppUnitsPerDevPixel();
   7016  }
   7017  if (aPerCSSPixel) {
   7018    *aPerCSSPixel = AppUnitsPerCSSPixel();
   7019  }
   7020 }
   7021 
   7022 void CanvasRenderingContext2D::SetWriteOnly() {
   7023  mWriteOnly = true;
   7024  if (mCanvasElement) {
   7025    mCanvasElement->SetWriteOnly();
   7026  } else if (mOffscreenCanvas) {
   7027    mOffscreenCanvas->SetWriteOnly();
   7028  }
   7029 }
   7030 
   7031 bool CanvasRenderingContext2D::UseSoftwareRendering() const {
   7032  return mWillReadFrequently || mForceSoftwareRendering;
   7033 }
   7034 
   7035 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPath, mParent)
   7036 
   7037 CanvasPath::CanvasPath(nsISupports* aParent) : mParent(aParent) {
   7038  mPathBuilder =
   7039      gfxPlatform::ThreadLocalScreenReferenceDrawTarget()->CreatePathBuilder();
   7040 }
   7041 
   7042 CanvasPath::CanvasPath(nsISupports* aParent,
   7043                       already_AddRefed<PathBuilder> aPathBuilder)
   7044    : mParent(aParent), mPathBuilder(aPathBuilder) {
   7045  if (!mPathBuilder) {
   7046    mPathBuilder = gfxPlatform::ThreadLocalScreenReferenceDrawTarget()
   7047                       ->CreatePathBuilder();
   7048  }
   7049 }
   7050 
   7051 JSObject* CanvasPath::WrapObject(JSContext* aCx,
   7052                                 JS::Handle<JSObject*> aGivenProto) {
   7053  return Path2D_Binding::Wrap(aCx, this, aGivenProto);
   7054 }
   7055 
   7056 already_AddRefed<CanvasPath> CanvasPath::Constructor(
   7057    const GlobalObject& aGlobal) {
   7058  RefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports());
   7059  return path.forget();
   7060 }
   7061 
   7062 already_AddRefed<CanvasPath> CanvasPath::Constructor(
   7063    const GlobalObject& aGlobal, CanvasPath& aCanvasPath) {
   7064  RefPtr<gfx::DrawTarget> drawTarget =
   7065      gfxPlatform::ThreadLocalScreenReferenceDrawTarget();
   7066  RefPtr<gfx::Path> tempPath =
   7067      aCanvasPath.GetPath(CanvasWindingRule::Nonzero, drawTarget.get());
   7068 
   7069  RefPtr<CanvasPath> path =
   7070      new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
   7071  return path.forget();
   7072 }
   7073 
   7074 already_AddRefed<CanvasPath> CanvasPath::Constructor(
   7075    const GlobalObject& aGlobal, const nsACString& aPathString) {
   7076  RefPtr<gfx::Path> tempPath = SVGContentUtils::GetPath(aPathString);
   7077  if (!tempPath) {
   7078    return Constructor(aGlobal);
   7079  }
   7080 
   7081  RefPtr<CanvasPath> path =
   7082      new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
   7083  return path.forget();
   7084 }
   7085 
   7086 void CanvasPath::ClosePath() {
   7087  EnsurePathBuilder();
   7088 
   7089  mPathBuilder->Close();
   7090  mPruned = false;
   7091 }
   7092 
   7093 inline void CanvasPath::EnsureCapped() const {
   7094  // If there were zero-length segments emitted that were pruned, we need to
   7095  // emit a LineTo to ensure that caps are generated for the segment.
   7096  if (mPruned) {
   7097    mPathBuilder->LineTo(mPathBuilder->CurrentPoint());
   7098    mPruned = false;
   7099  }
   7100 }
   7101 
   7102 inline void CanvasPath::EnsureActive() const {
   7103  // If the path is not active, then adding an op to the path may cause the path
   7104  // to add the first point of the op as the initial point instead of the actual
   7105  // current point.
   7106  if (mPruned && !mPathBuilder->IsActive()) {
   7107    mPathBuilder->MoveTo(mPathBuilder->CurrentPoint());
   7108    mPruned = false;
   7109  }
   7110 }
   7111 
   7112 void CanvasPath::MoveTo(double aX, double aY) {
   7113  EnsurePathBuilder();
   7114 
   7115  Point pos(ToFloat(aX), ToFloat(aY));
   7116  if (!pos.IsFinite()) {
   7117    return;
   7118  }
   7119 
   7120  EnsureCapped();
   7121  mPathBuilder->MoveTo(pos);
   7122 }
   7123 
   7124 void CanvasPath::LineTo(double aX, double aY) {
   7125  LineTo(Point(ToFloat(aX), ToFloat(aY)));
   7126 }
   7127 
   7128 void CanvasPath::QuadraticCurveTo(double aCpx, double aCpy, double aX,
   7129                                  double aY) {
   7130  EnsurePathBuilder();
   7131 
   7132  Point cp1(ToFloat(aCpx), ToFloat(aCpy));
   7133  Point cp2(ToFloat(aX), ToFloat(aY));
   7134  if (!cp1.IsFinite() || !cp2.IsFinite()) {
   7135    return;
   7136  }
   7137  if (cp1 == mPathBuilder->CurrentPoint() && cp1 == cp2) {
   7138    mPruned = true;
   7139    return;
   7140  }
   7141 
   7142  EnsureActive();
   7143 
   7144  mPathBuilder->QuadraticBezierTo(cp1, cp2);
   7145  mPruned = false;
   7146 }
   7147 
   7148 void CanvasPath::BezierCurveTo(double aCp1x, double aCp1y, double aCp2x,
   7149                               double aCp2y, double aX, double aY) {
   7150  BezierTo(gfx::Point(ToFloat(aCp1x), ToFloat(aCp1y)),
   7151           gfx::Point(ToFloat(aCp2x), ToFloat(aCp2y)),
   7152           gfx::Point(ToFloat(aX), ToFloat(aY)));
   7153 }
   7154 
   7155 void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
   7156                       double aRadius, ErrorResult& aError) {
   7157  if (aRadius < 0) {
   7158    return aError.ThrowIndexSizeError("Negative radius");
   7159  }
   7160 
   7161  EnsurePathBuilder();
   7162 
   7163  // Current point in user space!
   7164  Point p0 = mPathBuilder->CurrentPoint();
   7165  Point p1(aX1, aY1);
   7166  Point p2(aX2, aY2);
   7167 
   7168  if (!p1.IsFinite() || !p2.IsFinite() || !std::isfinite(aRadius)) {
   7169    return;
   7170  }
   7171 
   7172  // Execute these calculations in double precision to avoid cumulative
   7173  // rounding errors.
   7174  double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx,
   7175      cy, angle0, angle1;
   7176  bool anticlockwise;
   7177 
   7178  if (p0 == p1 || p1 == p2 || aRadius == 0) {
   7179    LineTo(p1);
   7180    return;
   7181  }
   7182 
   7183  // Check for colinearity
   7184  dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) +
   7185        (p2.y.value - p1.y.value) * (p1.x.value - p0.x.value);
   7186  if (dir == 0) {
   7187    LineTo(p1);
   7188    return;
   7189  }
   7190 
   7191  // XXX - Math for this code was already available from the non-azure code
   7192  // and would be well tested. Perhaps converting to bezier directly might
   7193  // be more efficient longer run.
   7194  a2 = (p0.x - aX1) * (p0.x - aX1) + (p0.y - aY1) * (p0.y - aY1);
   7195  b2 = (aX1 - aX2) * (aX1 - aX2) + (aY1 - aY2) * (aY1 - aY2);
   7196  c2 = (p0.x - aX2) * (p0.x - aX2) + (p0.y - aY2) * (p0.y - aY2);
   7197  cosx = (a2 + b2 - c2) / (2 * sqrt(a2 * b2));
   7198 
   7199  sinx = sqrt(1 - cosx * cosx);
   7200  d = aRadius / ((1 - cosx) / sinx);
   7201 
   7202  anx = (aX1 - p0.x) / sqrt(a2);
   7203  any = (aY1 - p0.y) / sqrt(a2);
   7204  bnx = (aX1 - aX2) / sqrt(b2);
   7205  bny = (aY1 - aY2) / sqrt(b2);
   7206  x3 = aX1 - anx * d;
   7207  y3 = aY1 - any * d;
   7208  x4 = aX1 - bnx * d;
   7209  y4 = aY1 - bny * d;
   7210  anticlockwise = (dir < 0);
   7211  cx = x3 + any * aRadius * (anticlockwise ? 1 : -1);
   7212  cy = y3 - anx * aRadius * (anticlockwise ? 1 : -1);
   7213  angle0 = atan2((y3 - cy), (x3 - cx));
   7214  angle1 = atan2((y4 - cy), (x4 - cx));
   7215 
   7216  LineTo(x3, y3);
   7217 
   7218  Arc(cx, cy, aRadius, angle0, angle1, anticlockwise, aError);
   7219 }
   7220 
   7221 void CanvasPath::Rect(double aX, double aY, double aW, double aH) {
   7222  EnsurePathBuilder();
   7223 
   7224  if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
   7225      !std::isfinite(aH)) {
   7226    return;
   7227  }
   7228 
   7229  MoveTo(aX, aY);
   7230  if (aW == 0 && aH == 0) {
   7231    return;
   7232  }
   7233  LineTo(aX + aW, aY);
   7234  LineTo(aX + aW, aY + aH);
   7235  LineTo(aX, aY + aH);
   7236  ClosePath();
   7237 }
   7238 
   7239 void CanvasPath::RoundRect(
   7240    double aX, double aY, double aW, double aH,
   7241    const UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence&
   7242        aRadii,
   7243    ErrorResult& aError) {
   7244  EnsurePathBuilder();
   7245 
   7246  EnsureCapped();
   7247  RoundRectImpl(mPathBuilder, Nothing(), aX, aY, aW, aH, aRadii, aError);
   7248 }
   7249 
   7250 void CanvasPath::Arc(double aX, double aY, double aRadius, double aStartAngle,
   7251                     double aEndAngle, bool aAnticlockwise,
   7252                     ErrorResult& aError) {
   7253  if (aRadius < 0.0) {
   7254    return aError.ThrowIndexSizeError("Negative radius");
   7255  }
   7256  if (aStartAngle == aEndAngle) {
   7257    LineTo(aX + aRadius * cos(aStartAngle), aY + aRadius * sin(aStartAngle));
   7258    return;
   7259  }
   7260 
   7261  EnsurePathBuilder();
   7262 
   7263  EnsureActive();
   7264 
   7265  mPathBuilder->Arc(Point(aX, aY), aRadius, aStartAngle, aEndAngle,
   7266                    aAnticlockwise);
   7267  mPruned = false;
   7268 }
   7269 
   7270 void CanvasPath::Ellipse(double x, double y, double radiusX, double radiusY,
   7271                         double rotation, double startAngle, double endAngle,
   7272                         bool anticlockwise, ErrorResult& aError) {
   7273  if (radiusX < 0.0 || radiusY < 0.0) {
   7274    return aError.ThrowIndexSizeError("Negative radius");
   7275  }
   7276 
   7277  EnsurePathBuilder();
   7278 
   7279  ArcToBezier(this, Point(x, y), Size(radiusX, radiusY), startAngle, endAngle,
   7280              anticlockwise, rotation);
   7281  mPruned = false;
   7282 }
   7283 
   7284 void CanvasPath::LineTo(const gfx::Point& aPoint) {
   7285  EnsurePathBuilder();
   7286 
   7287  if (!aPoint.IsFinite()) {
   7288    return;
   7289  }
   7290  if (aPoint == mPathBuilder->CurrentPoint()) {
   7291    mPruned = true;
   7292    return;
   7293  }
   7294 
   7295  EnsureActive();
   7296 
   7297  mPathBuilder->LineTo(aPoint);
   7298  mPruned = false;
   7299 }
   7300 
   7301 void CanvasPath::BezierTo(const gfx::Point& aCP1, const gfx::Point& aCP2,
   7302                          const gfx::Point& aCP3) {
   7303  EnsurePathBuilder();
   7304 
   7305  if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) {
   7306    return;
   7307  }
   7308  if (aCP1 == mPathBuilder->CurrentPoint() && aCP1 == aCP2 && aCP1 == aCP3) {
   7309    mPruned = true;
   7310    return;
   7311  }
   7312 
   7313  EnsureActive();
   7314 
   7315  mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
   7316  mPruned = false;
   7317 }
   7318 
   7319 void CanvasPath::AddPath(CanvasPath& aCanvasPath, const DOMMatrix2DInit& aInit,
   7320                         ErrorResult& aError) {
   7321  RefPtr<gfx::DrawTarget> drawTarget =
   7322      gfxPlatform::ThreadLocalScreenReferenceDrawTarget();
   7323  RefPtr<gfx::Path> tempPath =
   7324      aCanvasPath.GetPath(CanvasWindingRule::Nonzero, drawTarget.get());
   7325 
   7326  Matrix transform(DOMMatrixReadOnly::ToValidatedMatrixDouble(aInit, aError));
   7327  if (aError.Failed()) {
   7328    return;
   7329  }
   7330 
   7331  if (!transform.IsFinite()) {
   7332    return;
   7333  }
   7334 
   7335  if (!transform.IsIdentity()) {
   7336    Path::TransformAndSetFillRule(tempPath, transform, FillRule::FILL_WINDING);
   7337  }
   7338 
   7339  EnsurePathBuilder();  // in case a path is added to itself
   7340  EnsureCapped();
   7341  tempPath->StreamToSink(mPathBuilder);
   7342 }
   7343 
   7344 already_AddRefed<gfx::Path> CanvasPath::GetPath(
   7345    const CanvasWindingRule& aWinding, BackendType aBackendType) const {
   7346  FillRule fillRule = FillRule::FILL_WINDING;
   7347  if (aWinding == CanvasWindingRule::Evenodd) {
   7348    fillRule = FillRule::FILL_EVEN_ODD;
   7349  }
   7350 
   7351  if (mPath && (mPath->GetBackendType() == aBackendType) &&
   7352      (mPath->GetFillRule() == fillRule)) {
   7353    RefPtr<gfx::Path> path(mPath);
   7354    return path.forget();
   7355  }
   7356 
   7357  if (!mPath) {
   7358    // if there is no path, there must be a pathbuilder
   7359    MOZ_ASSERT(mPathBuilder);
   7360    EnsureCapped();
   7361    mPath = mPathBuilder->Finish();
   7362    if (!mPath) {
   7363      RefPtr<gfx::Path> path(mPath);
   7364      return path.forget();
   7365    }
   7366 
   7367    mPathBuilder = nullptr;
   7368  }
   7369 
   7370  // retarget our backend if we're used with a different backend
   7371  if (mPath->GetBackendType() != aBackendType) {
   7372    RefPtr<PathBuilder> tmpPathBuilder =
   7373        Factory::CreatePathBuilder(aBackendType, fillRule);
   7374    mPath->StreamToSink(tmpPathBuilder);
   7375    mPath = tmpPathBuilder->Finish();
   7376  } else if (mPath->GetFillRule() != fillRule) {
   7377    Path::SetFillRule(mPath, fillRule);
   7378  }
   7379 
   7380  RefPtr<gfx::Path> path(mPath);
   7381  return path.forget();
   7382 }
   7383 
   7384 void CanvasPath::EnsurePathBuilder() const {
   7385  if (mPathBuilder) {
   7386    return;
   7387  }
   7388 
   7389  // if there is not pathbuilder, there must be a path
   7390  MOZ_ASSERT(mPath);
   7391  mPathBuilder = Path::ToBuilder(mPath.forget());
   7392 }
   7393 
   7394 size_t BindingJSObjectMallocBytes(CanvasRenderingContext2D* aContext) {
   7395  IntSize size = aContext->GetSize();
   7396 
   7397  // TODO: Bug 1552137: No memory will be allocated if either dimension is
   7398  // greater than gfxPrefs::gfx_canvas_max_size(). We should check this here
   7399  // too.
   7400 
   7401  CheckedInt<uint32_t> bytes =
   7402      CheckedInt<uint32_t>(size.width) * size.height * 4;
   7403  if (!bytes.isValid()) {
   7404    return 0;
   7405  }
   7406 
   7407  return bytes.value();
   7408 }
   7409 
   7410 }  // namespace mozilla::dom