content/canvas/src/CanvasRenderingContext2D.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rwxr-xr-x

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #include "CanvasRenderingContext2D.h"
     8 #include "nsXULElement.h"
    10 #include "nsIServiceManager.h"
    11 #include "nsMathUtils.h"
    13 #include "nsContentUtils.h"
    15 #include "nsIDocument.h"
    16 #include "mozilla/dom/HTMLCanvasElement.h"
    17 #include "nsSVGEffects.h"
    18 #include "nsPresContext.h"
    19 #include "nsIPresShell.h"
    21 #include "nsIInterfaceRequestorUtils.h"
    22 #include "nsIFrame.h"
    23 #include "nsError.h"
    25 #include "nsCSSParser.h"
    26 #include "mozilla/css/StyleRule.h"
    27 #include "mozilla/css/Declaration.h"
    28 #include "mozilla/css/Loader.h"
    29 #include "nsComputedDOMStyle.h"
    30 #include "nsStyleSet.h"
    32 #include "nsPrintfCString.h"
    34 #include "nsReadableUtils.h"
    36 #include "nsColor.h"
    37 #include "nsGfxCIID.h"
    38 #include "nsIDocShell.h"
    39 #include "nsIDOMWindow.h"
    40 #include "nsPIDOMWindow.h"
    41 #include "nsDisplayList.h"
    42 #include "nsFocusManager.h"
    44 #include "nsTArray.h"
    46 #include "ImageEncoder.h"
    48 #include "gfxContext.h"
    49 #include "gfxASurface.h"
    50 #include "gfxImageSurface.h"
    51 #include "gfxPlatform.h"
    52 #include "gfxFont.h"
    53 #include "gfxBlur.h"
    54 #include "gfxUtils.h"
    56 #include "nsFrameManager.h"
    57 #include "nsFrameLoader.h"
    58 #include "nsBidi.h"
    59 #include "nsBidiPresUtils.h"
    60 #include "Layers.h"
    61 #include "CanvasUtils.h"
    62 #include "nsIMemoryReporter.h"
    63 #include "nsStyleUtil.h"
    64 #include "CanvasImageCache.h"
    66 #include <algorithm>
    68 #include "jsapi.h"
    69 #include "jsfriendapi.h"
    71 #include "mozilla/Alignment.h"
    72 #include "mozilla/Assertions.h"
    73 #include "mozilla/CheckedInt.h"
    74 #include "mozilla/dom/ContentParent.h"
    75 #include "mozilla/dom/ImageData.h"
    76 #include "mozilla/dom/PBrowserParent.h"
    77 #include "mozilla/dom/ToJSValue.h"
    78 #include "mozilla/dom/TypedArray.h"
    79 #include "mozilla/Endian.h"
    80 #include "mozilla/gfx/2D.h"
    81 #include "mozilla/gfx/PathHelpers.h"
    82 #include "mozilla/gfx/DataSurfaceHelpers.h"
    83 #include "mozilla/ipc/DocumentRendererParent.h"
    84 #include "mozilla/ipc/PDocumentRendererParent.h"
    85 #include "mozilla/MathAlgorithms.h"
    86 #include "mozilla/Preferences.h"
    87 #include "mozilla/Telemetry.h"
    88 #include "mozilla/unused.h"
    89 #include "nsCCUncollectableMarker.h"
    90 #include "nsWrapperCacheInlines.h"
    91 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
    92 #include "mozilla/dom/HTMLImageElement.h"
    93 #include "mozilla/dom/HTMLVideoElement.h"
    94 #include "mozilla/dom/TextMetrics.h"
    95 #include "mozilla/dom/UnionTypes.h"
    96 #include "nsGlobalWindow.h"
    97 #include "GLContext.h"
    98 #include "GLContextProvider.h"
    99 #include "SVGContentUtils.h"
   100 #include "nsIScreenManager.h"
   102 #undef free // apparently defined by some windows header, clashing with a free()
   103             // method in SkTypes.h
   104 #ifdef USE_SKIA
   105 #include "SkiaGLGlue.h"
   106 #include "SurfaceStream.h"
   107 #include "SurfaceTypes.h"
   108 #endif
   110 using mozilla::gl::GLContext;
   111 using mozilla::gl::SkiaGLGlue;
   112 using mozilla::gl::GLContextProvider;
   114 #ifdef XP_WIN
   115 #include "gfxWindowsPlatform.h"
   116 #endif
   118 #ifdef MOZ_WIDGET_GONK
   119 #include "mozilla/layers/ShadowLayers.h"
   120 #endif
   122 // windows.h (included by chromium code) defines this, in its infinite wisdom
   123 #undef DrawText
   125 using namespace mozilla;
   126 using namespace mozilla::CanvasUtils;
   127 using namespace mozilla::css;
   128 using namespace mozilla::gfx;
   129 using namespace mozilla::ipc;
   130 using namespace mozilla::layers;
   132 namespace mgfx = mozilla::gfx;
   134 namespace mozilla {
   135 namespace dom {
   137 // Cap sigma to avoid overly large temp surfaces.
   138 const Float SIGMA_MAX = 100;
   140 /* Memory reporter stuff */
   141 static int64_t gCanvasAzureMemoryUsed = 0;
   143 // This is KIND_OTHER because it's not always clear where in memory the pixels
   144 // of a canvas are stored.  Furthermore, this memory will be tracked by the
   145 // underlying surface implementations.  See bug 655638 for details.
   146 class Canvas2dPixelsReporter MOZ_FINAL : public nsIMemoryReporter
   147 {
   148 public:
   149   NS_DECL_ISUPPORTS
   151   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   152                             nsISupports* aData)
   153   {
   154     return MOZ_COLLECT_REPORT(
   155       "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
   156       gCanvasAzureMemoryUsed,
   157       "Memory used by 2D canvases. Each canvas requires "
   158       "(width * height * 4) bytes.");
   159   }
   160 };
   162 NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
   164 class CanvasRadialGradient : public CanvasGradient
   165 {
   166 public:
   167   CanvasRadialGradient(CanvasRenderingContext2D* aContext,
   168                        mozilla::css::Loader *aLoader,
   169                        const Point &aBeginOrigin, Float aBeginRadius,
   170                        const Point &aEndOrigin, Float aEndRadius)
   171     : CanvasGradient(aContext, aLoader, Type::RADIAL)
   172     , mCenter1(aBeginOrigin)
   173     , mCenter2(aEndOrigin)
   174     , mRadius1(aBeginRadius)
   175     , mRadius2(aEndRadius)
   176   {
   177   }
   179   Point mCenter1;
   180   Point mCenter2;
   181   Float mRadius1;
   182   Float mRadius2;
   183 };
   185 class CanvasLinearGradient : public CanvasGradient
   186 {
   187 public:
   188   CanvasLinearGradient(CanvasRenderingContext2D* aContext,
   189                        mozilla::css::Loader *aLoader,
   190                        const Point &aBegin, const Point &aEnd)
   191     : CanvasGradient(aContext, aLoader, Type::LINEAR)
   192     , mBegin(aBegin)
   193     , mEnd(aEnd)
   194   {
   195   }
   197 protected:
   198   friend class CanvasGeneralPattern;
   200   // Beginning of linear gradient.
   201   Point mBegin;
   202   // End of linear gradient.
   203   Point mEnd;
   204 };
   206 // This class is named 'GeneralCanvasPattern' instead of just
   207 // 'GeneralPattern' to keep Windows PGO builds from confusing the
   208 // GeneralPattern class in gfxContext.cpp with this one.
   210 class CanvasGeneralPattern
   211 {
   212 public:
   213   typedef CanvasRenderingContext2D::Style Style;
   214   typedef CanvasRenderingContext2D::ContextState ContextState;
   216   CanvasGeneralPattern() : mPattern(nullptr) {}
   217   ~CanvasGeneralPattern()
   218   {
   219     if (mPattern) {
   220       mPattern->~Pattern();
   221     }
   222   }
   224   Pattern& ForStyle(CanvasRenderingContext2D *aCtx,
   225                     Style aStyle,
   226                     DrawTarget *aRT)
   227   {
   228     // This should only be called once or the mPattern destructor will
   229     // not be executed.
   230     NS_ASSERTION(!mPattern, "ForStyle() should only be called once on CanvasGeneralPattern!");
   232     const ContextState &state = aCtx->CurrentState();
   234     if (state.StyleIsColor(aStyle)) {
   235       mPattern = new (mColorPattern.addr()) ColorPattern(Color::FromABGR(state.colorStyles[aStyle]));
   236     } else if (state.gradientStyles[aStyle] &&
   237                state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
   238       CanvasLinearGradient *gradient =
   239         static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
   241       mPattern = new (mLinearGradientPattern.addr())
   242         LinearGradientPattern(gradient->mBegin, gradient->mEnd,
   243                               gradient->GetGradientStopsForTarget(aRT));
   244     } else if (state.gradientStyles[aStyle] &&
   245                state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
   246       CanvasRadialGradient *gradient =
   247         static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
   249       mPattern = new (mRadialGradientPattern.addr())
   250         RadialGradientPattern(gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
   251                               gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
   252     } else if (state.patternStyles[aStyle]) {
   253       if (aCtx->mCanvasElement) {
   254         CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
   255                                               state.patternStyles[aStyle]->mPrincipal,
   256                                               state.patternStyles[aStyle]->mForceWriteOnly,
   257                                               state.patternStyles[aStyle]->mCORSUsed);
   258       }
   260       ExtendMode mode;
   261       if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
   262         mode = ExtendMode::CLAMP;
   263       } else {
   264         mode = ExtendMode::REPEAT;
   265       }
   266       mPattern = new (mSurfacePattern.addr())
   267         SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
   268     }
   270     return *mPattern;
   271   }
   273   union {
   274     AlignedStorage2<ColorPattern> mColorPattern;
   275     AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
   276     AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
   277     AlignedStorage2<SurfacePattern> mSurfacePattern;
   278   };
   279   Pattern *mPattern;
   280 };
   282 /* This is an RAII based class that can be used as a drawtarget for
   283  * operations that need a shadow drawn. It will automatically provide a
   284  * temporary target when needed, and if so blend it back with a shadow.
   285  *
   286  * aBounds specifies the bounds of the drawing operation that will be
   287  * drawn to the target, it is given in device space! This function will
   288  * change aBounds to incorporate shadow bounds. If this is nullptr the drawing
   289  * operation will be assumed to cover an infinite rect.
   290  */
   291 class AdjustedTarget
   292 {
   293 public:
   294   typedef CanvasRenderingContext2D::ContextState ContextState;
   296   AdjustedTarget(CanvasRenderingContext2D *ctx,
   297                  mgfx::Rect *aBounds = nullptr)
   298     : mCtx(nullptr)
   299   {
   300     if (!ctx->NeedToDrawShadow()) {
   301       mTarget = ctx->mTarget;
   302       return;
   303     }
   304     mCtx = ctx;
   306     const ContextState &state = mCtx->CurrentState();
   308     mSigma = state.shadowBlur / 2.0f;
   310     if (mSigma > SIGMA_MAX) {
   311       mSigma = SIGMA_MAX;
   312     }
   314     Matrix transform = mCtx->mTarget->GetTransform();
   316     mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
   318     static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
   319     int32_t blurRadius = (int32_t) floor(mSigma * GAUSSIAN_SCALE_FACTOR + 0.5);
   321     // We need to enlarge and possibly offset our temporary surface
   322     // so that things outside of the canvas may cast shadows.
   323     mTempRect.Inflate(Margin(blurRadius + std::max<Float>(state.shadowOffset.y, 0),
   324                              blurRadius + std::max<Float>(-state.shadowOffset.x, 0),
   325                              blurRadius + std::max<Float>(-state.shadowOffset.y, 0),
   326                              blurRadius + std::max<Float>(state.shadowOffset.x, 0)));
   328     if (aBounds) {
   329       // We actually include the bounds of the shadow blur, this makes it
   330       // easier to execute the actual blur on hardware, and shouldn't affect
   331       // the amount of pixels that need to be touched.
   332       aBounds->Inflate(Margin(blurRadius, blurRadius,
   333                               blurRadius, blurRadius));
   334       mTempRect = mTempRect.Intersect(*aBounds);
   335     }
   337     mTempRect.ScaleRoundOut(1.0f);
   339     transform._31 -= mTempRect.x;
   340     transform._32 -= mTempRect.y;
   342     mTarget =
   343       mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
   344                                             SurfaceFormat::B8G8R8A8, mSigma);
   346     if (!mTarget) {
   347       // XXX - Deal with the situation where our temp size is too big to
   348       // fit in a texture.
   349       mTarget = ctx->mTarget;
   350       mCtx = nullptr;
   351     } else {
   352       mTarget->SetTransform(transform);
   353     }
   354   }
   356   ~AdjustedTarget()
   357   {
   358     if (!mCtx) {
   359       return;
   360     }
   362     RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
   364     mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
   365                                          Color::FromABGR(mCtx->CurrentState().shadowColor),
   366                                          mCtx->CurrentState().shadowOffset, mSigma,
   367                                          mCtx->CurrentState().op);
   368   }
   370   operator DrawTarget*() 
   371   {
   372     return mTarget;
   373   }
   375   DrawTarget* operator->()
   376   {
   377     return mTarget;
   378   }
   380 private:
   381   RefPtr<DrawTarget> mTarget;
   382   CanvasRenderingContext2D *mCtx;
   383   Float mSigma;
   384   mgfx::Rect mTempRect;
   385 };
   387 void
   388 CanvasGradient::AddColorStop(float offset, const nsAString& colorstr, ErrorResult& rv)
   389 {
   390   if (offset < 0.0 || offset > 1.0) {
   391     rv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   392     return;
   393   }
   395   nsCSSValue value;
   396   nsCSSParser parser;
   397   if (!parser.ParseColorString(colorstr, nullptr, 0, value)) {
   398     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   399     return;
   400   }
   402   nsIPresShell* presShell = nullptr;
   403   if (mCSSLoader) {
   404     nsIDocument *doc = mCSSLoader->GetDocument();
   405     if (doc)
   406       presShell = doc->GetShell();
   407   }
   409   nscolor color;
   410   if (!nsRuleNode::ComputeColor(value, presShell ? presShell->GetPresContext() : nullptr,
   411                                 nullptr, color)) {
   412     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   413     return;
   414   }
   416   mStops = nullptr;
   418   GradientStop newStop;
   420   newStop.offset = offset;
   421   newStop.color = Color::FromABGR(color);
   423   mRawStops.AppendElement(newStop);
   424 }
   426 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
   427 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
   429 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasGradient, mContext)
   431 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
   432 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
   434 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasPattern, mContext)
   436 class CanvasRenderingContext2DUserData : public LayerUserData {
   437 public:
   438     CanvasRenderingContext2DUserData(CanvasRenderingContext2D *aContext)
   439     : mContext(aContext)
   440   {
   441     aContext->mUserDatas.AppendElement(this);
   442   }
   443   ~CanvasRenderingContext2DUserData()
   444   {
   445     if (mContext) {
   446       mContext->mUserDatas.RemoveElement(this);
   447     }
   448   }
   450   static void PreTransactionCallback(void* aData)
   451   {
   452     CanvasRenderingContext2DUserData* self =
   453       static_cast<CanvasRenderingContext2DUserData*>(aData);
   454     CanvasRenderingContext2D* context = self->mContext;
   455     if (!context || !context->mStream || !context->mTarget)
   456       return;
   458     // Since SkiaGL default to store drawing command until flush
   459     // We will have to flush it before present.
   460     context->mTarget->Flush();
   461   }
   463   static void DidTransactionCallback(void* aData)
   464   {
   465     CanvasRenderingContext2DUserData* self =
   466       static_cast<CanvasRenderingContext2DUserData*>(aData);
   467     if (self->mContext) {
   468       self->mContext->MarkContextClean();
   469     }
   470   }
   471   bool IsForContext(CanvasRenderingContext2D *aContext)
   472   {
   473     return mContext == aContext;
   474   }
   475   void Forget()
   476   {
   477     mContext = nullptr;
   478   }
   480 private:
   481   CanvasRenderingContext2D *mContext;
   482 };
   484 NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
   485 NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
   487 NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
   489 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
   490   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
   491   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
   492     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
   493     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
   494     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
   495     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
   496   }
   497   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   498 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   500 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
   501   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
   502   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
   503     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::STROKE], "Stroke CanvasPattern");
   504     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::FILL], "Fill CanvasPattern");
   505     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE], "Stroke CanvasGradient");
   506     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::FILL], "Fill CanvasGradient");
   507   }
   508   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   509 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   511 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(CanvasRenderingContext2D)
   513 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
   514  if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
   515    dom::Element* canvasElement = tmp->mCanvasElement;
   516     if (canvasElement) {
   517       if (canvasElement->IsPurple()) {
   518         canvasElement->RemovePurple();
   519       }
   520       dom::Element::MarkNodeChildren(canvasElement);
   521     }
   522     return true;
   523   }
   524 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   526 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
   527   return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
   528 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   530 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
   531   return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
   532 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   534 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
   535   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   536   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
   537   NS_INTERFACE_MAP_ENTRY(nsISupports)
   538 NS_INTERFACE_MAP_END
   540 /**
   541  ** CanvasRenderingContext2D impl
   542  **/
   545 // Initialize our static variables.
   546 uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
   547 DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
   551 CanvasRenderingContext2D::CanvasRenderingContext2D()
   552   : mForceSoftware(false), mZero(false), mOpaque(false), mResetLayer(true)
   553   , mIPC(false)
   554   , mStream(nullptr)
   555   , mIsEntireFrameInvalid(false)
   556   , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
   557   , mInvalidateCount(0)
   558 {
   559   sNumLivingContexts++;
   560   SetIsDOMBinding();
   561 }
   563 CanvasRenderingContext2D::~CanvasRenderingContext2D()
   564 {
   565   Reset();
   566   // Drop references from all CanvasRenderingContext2DUserData to this context
   567   for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
   568     mUserDatas[i]->Forget();
   569   }
   570   sNumLivingContexts--;
   571   if (!sNumLivingContexts) {
   572     NS_IF_RELEASE(sErrorTarget);
   573   }
   575   RemoveDemotableContext(this);
   576 }
   578 JSObject*
   579 CanvasRenderingContext2D::WrapObject(JSContext *cx)
   580 {
   581   return CanvasRenderingContext2DBinding::Wrap(cx, this);
   582 }
   584 bool
   585 CanvasRenderingContext2D::ParseColor(const nsAString& aString,
   586                                      nscolor* aColor)
   587 {
   588   nsIDocument* document = mCanvasElement
   589                           ? mCanvasElement->OwnerDoc()
   590                           : nullptr;
   592   // Pass the CSS Loader object to the parser, to allow parser error
   593   // reports to include the outer window ID.
   594   nsCSSParser parser(document ? document->CSSLoader() : nullptr);
   595   nsCSSValue value;
   596   if (!parser.ParseColorString(aString, nullptr, 0, value)) {
   597     return false;
   598   }
   600   if (value.IsNumericColorUnit()) {
   601     // if we already have a color we can just use it directly
   602     *aColor = value.GetColorValue();
   603   } else {
   604     // otherwise resolve it
   605     nsIPresShell* presShell = GetPresShell();
   606     nsRefPtr<nsStyleContext> parentContext;
   607     if (mCanvasElement && mCanvasElement->IsInDoc()) {
   608       // Inherit from the canvas element.
   609       parentContext = nsComputedDOMStyle::GetStyleContextForElement(
   610         mCanvasElement, nullptr, presShell);
   611     }
   613     unused << nsRuleNode::ComputeColor(
   614       value, presShell ? presShell->GetPresContext() : nullptr, parentContext,
   615       *aColor);
   616   }
   617   return true;
   618 }
   620 #ifdef ACCESSIBILITY
   621 PLDHashOperator
   622 CanvasRenderingContext2D::RemoveHitRegionProperty(RegionInfo* aEntry, void*)
   623 {
   624   aEntry->mElement->DeleteProperty(nsGkAtoms::hitregion);
   625   return PL_DHASH_NEXT;
   626 }
   627 #endif
   629 nsresult
   630 CanvasRenderingContext2D::Reset()
   631 {
   632   if (mCanvasElement) {
   633     mCanvasElement->InvalidateCanvas();
   634   }
   636   // only do this for non-docshell created contexts,
   637   // since those are the ones that we created a surface for
   638   if (mTarget && IsTargetValid() && !mDocShell) {
   639     gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
   640   }
   642   mTarget = nullptr;
   643   mStream = nullptr;
   645   // reset hit regions
   646 #ifdef ACCESSIBILITY
   647   mHitRegionsOptions.EnumerateEntries(RemoveHitRegionProperty, nullptr);
   648 #endif
   649   mHitRegionsOptions.Clear();
   651   // Since the target changes the backing texture will change, and this will
   652   // no longer be valid.
   653   mIsEntireFrameInvalid = false;
   654   mPredictManyRedrawCalls = false;
   656   return NS_OK;
   657 }
   659 void
   660 CanvasRenderingContext2D::SetStyleFromString(const nsAString& str,
   661                                              Style whichStyle)
   662 {
   663   MOZ_ASSERT(!str.IsVoid());
   665   nscolor color;
   666   if (!ParseColor(str, &color)) {
   667     return;
   668   }
   670   CurrentState().SetColorStyle(whichStyle, color);
   671 }
   673 void
   674 CanvasRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
   675                                           Style aWhichStyle)
   676 {
   677   const ContextState &state = CurrentState();
   678   if (state.patternStyles[aWhichStyle]) {
   679     aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
   680   } else if (state.gradientStyles[aWhichStyle]) {
   681     aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
   682   } else {
   683     StyleColorToString(state.colorStyles[aWhichStyle], aValue.SetAsString());
   684   }
   685 }
   687 // static
   688 void
   689 CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
   690 {
   691   // We can't reuse the normal CSS color stringification code,
   692   // because the spec calls for a different algorithm for canvas.
   693   if (NS_GET_A(aColor) == 255) {
   694     CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
   695                                     NS_GET_R(aColor),
   696                                     NS_GET_G(aColor),
   697                                     NS_GET_B(aColor)),
   698                     aStr);
   699   } else {
   700     CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
   701                                     NS_GET_R(aColor),
   702                                     NS_GET_G(aColor),
   703                                     NS_GET_B(aColor)),
   704                     aStr);
   705     aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
   706     aStr.Append(')');
   707   }
   708 }
   710 nsresult
   711 CanvasRenderingContext2D::Redraw()
   712 {
   713   if (mIsEntireFrameInvalid) {
   714     return NS_OK;
   715   }
   717   mIsEntireFrameInvalid = true;
   719   if (!mCanvasElement) {
   720     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
   721     return NS_OK;
   722   }
   724   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
   726   mCanvasElement->InvalidateCanvasContent(nullptr);
   728   return NS_OK;
   729 }
   731 void
   732 CanvasRenderingContext2D::Redraw(const mgfx::Rect &r)
   733 {
   734   ++mInvalidateCount;
   736   if (mIsEntireFrameInvalid) {
   737     return;
   738   }
   740   if (mPredictManyRedrawCalls ||
   741     mInvalidateCount > kCanvasMaxInvalidateCount) {
   742     Redraw();
   743     return;
   744   }
   746   if (!mCanvasElement) {
   747     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
   748     return;
   749   }
   751   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
   753   mCanvasElement->InvalidateCanvasContent(&r);
   754 }
   756 void
   757 CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
   758 {
   759   if (mIsEntireFrameInvalid) {
   760     ++mInvalidateCount;
   761     return;
   762   }
   764   mgfx::Rect newr =
   765     mTarget->GetTransform().TransformBounds(ToRect(r));
   766   Redraw(newr);
   767 }
   769 void CanvasRenderingContext2D::Demote()
   770 {
   771   if (!IsTargetValid() || mForceSoftware || !mStream)
   772     return;
   774   RemoveDemotableContext(this);
   776   RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
   777   RefPtr<DrawTarget> oldTarget = mTarget;
   778   mTarget = nullptr;
   779   mStream = nullptr;
   780   mResetLayer = true;
   781   mForceSoftware = true;
   783   // Recreate target, now demoted to software only
   784   EnsureTarget();
   785   if (!IsTargetValid())
   786     return;
   788   // Restore the content from the old DrawTarget
   789   mgfx::Rect r(0, 0, mWidth, mHeight);
   790   mTarget->DrawSurface(snapshot, r, r);
   792   // Restore the clips and transform
   793   for (uint32_t i = 0; i < CurrentState().clipsPushed.size(); i++) {
   794     mTarget->PushClip(CurrentState().clipsPushed[i]);
   795   }
   797   mTarget->SetTransform(oldTarget->GetTransform());
   798 }
   800 std::vector<CanvasRenderingContext2D*>&
   801 CanvasRenderingContext2D::DemotableContexts()
   802 {
   803   static std::vector<CanvasRenderingContext2D*> contexts;
   804   return contexts;
   805 }
   807 void
   808 CanvasRenderingContext2D::DemoteOldestContextIfNecessary()
   809 {
   810   const size_t kMaxContexts = 64;
   812   std::vector<CanvasRenderingContext2D*>& contexts = DemotableContexts();
   813   if (contexts.size() < kMaxContexts)
   814     return;
   816   CanvasRenderingContext2D* oldest = contexts.front();
   817   oldest->Demote();
   818 }
   820 void
   821 CanvasRenderingContext2D::AddDemotableContext(CanvasRenderingContext2D* context)
   822 {
   823   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), context);
   824   if (iter != DemotableContexts().end())
   825     return;
   827   DemotableContexts().push_back(context);
   828 }
   830 void
   831 CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D* context)
   832 {
   833   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), context);
   834   if (iter != DemotableContexts().end())
   835     DemotableContexts().erase(iter);
   836 }
   838 bool
   839 CheckSizeForSkiaGL(IntSize size) {
   840   MOZ_ASSERT(NS_IsMainThread());
   842   int minsize = Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128);
   843   if (size.width < minsize || size.height < minsize) {
   844     return false;
   845   }
   847   // Maximum pref allows 3 different options:
   848   //  0   means unlimited size
   849   //  > 0 means use value as an absolute threshold
   850   //  < 0 means use the number of screen pixels as a threshold
   851   int maxsize = Preferences::GetInt("gfx.canvas.max-size-for-skia-gl", 0);
   853   // unlimited max size
   854   if (!maxsize) {
   855     return true;
   856   }
   858   // absolute max size threshold
   859   if (maxsize > 0) {
   860     return size.width <= maxsize && size.height <= maxsize;
   861   }
   863   // Cache the number of pixels on the primary screen
   864   static int32_t gScreenPixels = -1;
   865   if (gScreenPixels < 0) {
   866     nsCOMPtr<nsIScreenManager> screenManager =
   867       do_GetService("@mozilla.org/gfx/screenmanager;1");
   868     if (screenManager) {
   869       nsCOMPtr<nsIScreen> primaryScreen;
   870       screenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
   871       if (primaryScreen) {
   872         int32_t x, y, width, height;
   873         primaryScreen->GetRect(&x, &y, &width, &height);
   875         gScreenPixels = width * height;
   876       }
   877     }
   878   }
   880   // screen size acts as max threshold
   881   return gScreenPixels < 0 || (size.width * size.height) <= gScreenPixels;
   882 }
   884 void
   885 CanvasRenderingContext2D::EnsureTarget()
   886 {
   887   if (mTarget) {
   888     return;
   889   }
   891    // Check that the dimensions are sane
   892   IntSize size(mWidth, mHeight);
   893   if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
   894       size.width >= 0 && size.height >= 0) {
   895     SurfaceFormat format = GetSurfaceFormat();
   896     nsIDocument* ownerDoc = nullptr;
   897     if (mCanvasElement) {
   898       ownerDoc = mCanvasElement->OwnerDoc();
   899     }
   901     nsRefPtr<LayerManager> layerManager = nullptr;
   903     if (ownerDoc) {
   904       layerManager =
   905         nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
   906     }
   908      if (layerManager) {
   909       if (gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas() &&
   910           !mForceSoftware &&
   911           CheckSizeForSkiaGL(size)) {
   912         DemoteOldestContextIfNecessary();
   914         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
   916 #if USE_SKIA
   917         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
   918           mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
   919           if (mTarget) {
   920             mStream = gfx::SurfaceStream::CreateForType(SurfaceStreamType::TripleBuffer, glue->GetGLContext());
   921             AddDemotableContext(this);
   922           } else {
   923             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
   924           }
   925         }
   926 #endif
   927         if (!mTarget) {
   928           mTarget = layerManager->CreateDrawTarget(size, format);
   929         }
   930       } else
   931         mTarget = layerManager->CreateDrawTarget(size, format);
   932      } else {
   933         mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
   934      }
   935   }
   937   if (mTarget) {
   938     static bool registered = false;
   939     if (!registered) {
   940       registered = true;
   941       RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
   942     }
   944     gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
   945     JSContext* context = nsContentUtils::GetCurrentJSContext();
   946     if (context) {
   947       JS_updateMallocCounter(context, mWidth * mHeight * 4);
   948     }
   950     mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
   951     // Force a full layer transaction since we didn't have a layer before
   952     // and now we might need one.
   953     if (mCanvasElement) {
   954       mCanvasElement->InvalidateCanvas();
   955     }
   956     // Calling Redraw() tells our invalidation machinery that the entire
   957     // canvas is already invalid, which can speed up future drawing.
   958     Redraw();
   959   } else {
   960     EnsureErrorTarget();
   961     mTarget = sErrorTarget;
   962   }
   963 }
   965 #ifdef DEBUG
   966 int32_t
   967 CanvasRenderingContext2D::GetWidth() const
   968 {
   969   return mWidth;
   970 }
   972 int32_t
   973 CanvasRenderingContext2D::GetHeight() const
   974 {
   975   return mHeight;
   976 }
   977 #endif
   979 NS_IMETHODIMP
   980 CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
   981 {
   982   ClearTarget();
   984   // Zero sized surfaces can cause problems.
   985   mZero = false;
   986   if (height == 0) {
   987     height = 1;
   988     mZero = true;
   989   }
   990   if (width == 0) {
   991     width = 1;
   992     mZero = true;
   993   }
   994   mWidth = width;
   995   mHeight = height;
   997   return NS_OK;
   998 }
  1000 void
  1001 CanvasRenderingContext2D::ClearTarget()
  1003   Reset();
  1005   mResetLayer = true;
  1007   // set up the initial canvas defaults
  1008   mStyleStack.Clear();
  1009   mPathBuilder = nullptr;
  1010   mPath = nullptr;
  1011   mDSPathBuilder = nullptr;
  1013   ContextState *state = mStyleStack.AppendElement();
  1014   state->globalAlpha = 1.0;
  1016   state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
  1017   state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
  1018   state->shadowColor = NS_RGBA(0,0,0,0);
  1021 NS_IMETHODIMP
  1022 CanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *shell,
  1023                                                 gfxASurface *surface,
  1024                                                 int32_t width,
  1025                                                 int32_t height)
  1027   mDocShell = shell;
  1029   SetDimensions(width, height);
  1030   mTarget = gfxPlatform::GetPlatform()->
  1031     CreateDrawTargetForSurface(surface, IntSize(width, height));
  1033   if (!mTarget) {
  1034     EnsureErrorTarget();
  1035     mTarget = sErrorTarget;
  1038   return NS_OK;
  1041 NS_IMETHODIMP
  1042 CanvasRenderingContext2D::SetIsOpaque(bool isOpaque)
  1044   if (isOpaque != mOpaque) {
  1045     mOpaque = isOpaque;
  1046     ClearTarget();
  1049   if (mOpaque) {
  1050     EnsureTarget();
  1053   return NS_OK;
  1056 NS_IMETHODIMP
  1057 CanvasRenderingContext2D::SetIsIPC(bool isIPC)
  1059   if (isIPC != mIPC) {
  1060     mIPC = isIPC;
  1061     ClearTarget();
  1064   return NS_OK;
  1067 NS_IMETHODIMP
  1068 CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
  1070   if (aOptions.isNullOrUndefined()) {
  1071     return NS_OK;
  1074   ContextAttributes2D attributes;
  1075   NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
  1077   if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
  1078     // Use software when there is going to be a lot of readback
  1079     mForceSoftware = attributes.mWillReadFrequently;
  1082   if (!attributes.mAlpha) {
  1083     SetIsOpaque(true);
  1086   return NS_OK;
  1089 void
  1090 CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
  1091                                          int32_t* aFormat)
  1093   *aImageBuffer = nullptr;
  1094   *aFormat = 0;
  1096   EnsureTarget();
  1097   RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
  1098   if (!snapshot) {
  1099     return;
  1102   RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
  1103   if (!data || data->GetSize() != IntSize(mWidth, mHeight)) {
  1104     return;
  1107   *aImageBuffer = SurfaceToPackedBGRA(data);
  1108   *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
  1111 NS_IMETHODIMP
  1112 CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
  1113                                          const char16_t *aEncoderOptions,
  1114                                          nsIInputStream **aStream)
  1116   nsCString enccid("@mozilla.org/image/encoder;2?type=");
  1117   enccid += aMimeType;
  1118   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
  1119   if (!encoder) {
  1120     return NS_ERROR_FAILURE;
  1123   nsAutoArrayPtr<uint8_t> imageBuffer;
  1124   int32_t format = 0;
  1125   GetImageBuffer(getter_Transfers(imageBuffer), &format);
  1126   if (!imageBuffer) {
  1127     return NS_ERROR_FAILURE;
  1130   return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
  1131                                       encoder, aEncoderOptions, aStream);
  1134 SurfaceFormat
  1135 CanvasRenderingContext2D::GetSurfaceFormat() const
  1137   return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
  1140 //
  1141 // state
  1142 //
  1144 void
  1145 CanvasRenderingContext2D::Save()
  1147   EnsureTarget();
  1148   mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
  1149   mStyleStack.SetCapacity(mStyleStack.Length() + 1);
  1150   mStyleStack.AppendElement(CurrentState());
  1153 void
  1154 CanvasRenderingContext2D::Restore()
  1156   if (mStyleStack.Length() - 1 == 0)
  1157     return;
  1159   TransformWillUpdate();
  1161   for (uint32_t i = 0; i < CurrentState().clipsPushed.size(); i++) {
  1162     mTarget->PopClip();
  1165   mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
  1167   mTarget->SetTransform(CurrentState().transform);
  1170 //
  1171 // transformations
  1172 //
  1174 void
  1175 CanvasRenderingContext2D::Scale(double x, double y, ErrorResult& error)
  1177   TransformWillUpdate();
  1178   if (!IsTargetValid()) {
  1179     error.Throw(NS_ERROR_FAILURE);
  1180     return;
  1183   Matrix newMatrix = mTarget->GetTransform();
  1184   mTarget->SetTransform(newMatrix.Scale(x, y));
  1187 void
  1188 CanvasRenderingContext2D::Rotate(double angle, ErrorResult& error)
  1190   TransformWillUpdate();
  1191   if (!IsTargetValid()) {
  1192     error.Throw(NS_ERROR_FAILURE);
  1193     return;
  1196   Matrix rotation = Matrix::Rotation(angle);
  1197   mTarget->SetTransform(rotation * mTarget->GetTransform());
  1200 void
  1201 CanvasRenderingContext2D::Translate(double x, double y, ErrorResult& error)
  1203   TransformWillUpdate();
  1204   if (!IsTargetValid()) {
  1205     error.Throw(NS_ERROR_FAILURE);
  1206     return;
  1209   Matrix newMatrix = mTarget->GetTransform();
  1210   mTarget->SetTransform(newMatrix.Translate(x, y));
  1213 void
  1214 CanvasRenderingContext2D::Transform(double m11, double m12, double m21,
  1215                                     double m22, double dx, double dy,
  1216                                     ErrorResult& error)
  1218   TransformWillUpdate();
  1219   if (!IsTargetValid()) {
  1220     error.Throw(NS_ERROR_FAILURE);
  1221     return;
  1224   Matrix matrix(m11, m12, m21, m22, dx, dy);
  1225   mTarget->SetTransform(matrix * mTarget->GetTransform());
  1228 void
  1229 CanvasRenderingContext2D::SetTransform(double m11, double m12,
  1230                                        double m21, double m22,
  1231                                        double dx, double dy,
  1232                                        ErrorResult& error)
  1234   TransformWillUpdate();
  1235   if (!IsTargetValid()) {
  1236     error.Throw(NS_ERROR_FAILURE);
  1237     return;
  1240   Matrix matrix(m11, m12, m21, m22, dx, dy);
  1241   mTarget->SetTransform(matrix);
  1244 static void
  1245 MatrixToJSObject(JSContext* cx, const Matrix& matrix,
  1246                  JS::MutableHandle<JSObject*> result, ErrorResult& error)
  1248   double elts[6] = { matrix._11, matrix._12,
  1249                      matrix._21, matrix._22,
  1250                      matrix._31, matrix._32 };
  1252   // XXX Should we enter GetWrapper()'s compartment?
  1253   JS::Rooted<JS::Value> val(cx);
  1254   if (!ToJSValue(cx, elts, &val)) {
  1255     error.Throw(NS_ERROR_OUT_OF_MEMORY);
  1256   } else {
  1257     result.set(&val.toObject());
  1261 static bool
  1262 ObjectToMatrix(JSContext* cx, JS::Handle<JSObject*> obj, Matrix& matrix,
  1263                ErrorResult& error)
  1265   uint32_t length;
  1266   if (!JS_GetArrayLength(cx, obj, &length) || length != 6) {
  1267     // Not an array-like thing or wrong size
  1268     error.Throw(NS_ERROR_INVALID_ARG);
  1269     return false;
  1272   Float* elts[] = { &matrix._11, &matrix._12, &matrix._21, &matrix._22,
  1273                     &matrix._31, &matrix._32 };
  1274   for (uint32_t i = 0; i < 6; ++i) {
  1275     JS::Rooted<JS::Value> elt(cx);
  1276     double d;
  1277     if (!JS_GetElement(cx, obj, i, &elt)) {
  1278       error.Throw(NS_ERROR_FAILURE);
  1279       return false;
  1281     if (!CoerceDouble(elt, &d)) {
  1282       error.Throw(NS_ERROR_INVALID_ARG);
  1283       return false;
  1285     if (!FloatValidate(d)) {
  1286       // This is weird, but it's the behavior of SetTransform()
  1287       return false;
  1289     *elts[i] = Float(d);
  1291   return true;
  1294 void
  1295 CanvasRenderingContext2D::SetMozCurrentTransform(JSContext* cx,
  1296                                                  JS::Handle<JSObject*> currentTransform,
  1297                                                  ErrorResult& error)
  1299   EnsureTarget();
  1300   if (!IsTargetValid()) {
  1301     error.Throw(NS_ERROR_FAILURE);
  1302     return;
  1305   Matrix newCTM;
  1306   if (ObjectToMatrix(cx, currentTransform, newCTM, error)) {
  1307     mTarget->SetTransform(newCTM);
  1311 void
  1312 CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
  1313                                                  JS::MutableHandle<JSObject*> result,
  1314                                                  ErrorResult& error) const
  1316   MatrixToJSObject(cx, mTarget ? mTarget->GetTransform() : Matrix(),
  1317                    result, error);
  1320 void
  1321 CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
  1322                                                         JS::Handle<JSObject*> currentTransform,
  1323                                                         ErrorResult& error)
  1325   EnsureTarget();
  1326   if (!IsTargetValid()) {
  1327     error.Throw(NS_ERROR_FAILURE);
  1328     return;
  1331   Matrix newCTMInverse;
  1332   if (ObjectToMatrix(cx, currentTransform, newCTMInverse, error)) {
  1333     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1334     if (newCTMInverse.Invert()) {
  1335       mTarget->SetTransform(newCTMInverse);
  1340 void
  1341 CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* cx,
  1342                                                         JS::MutableHandle<JSObject*> result,
  1343                                                         ErrorResult& error) const
  1345   if (!mTarget) {
  1346     MatrixToJSObject(cx, Matrix(), result, error);
  1347     return;
  1350   Matrix ctm = mTarget->GetTransform();
  1352   if (!ctm.Invert()) {
  1353     double NaN = JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));
  1354     ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
  1357   MatrixToJSObject(cx, ctm, result, error);
  1360 //
  1361 // colors
  1362 //
  1364 void
  1365 CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& value,
  1366                                             Style whichStyle)
  1368   if (value.IsString()) {
  1369     SetStyleFromString(value.GetAsString(), whichStyle);
  1370     return;
  1373   if (value.IsCanvasGradient()) {
  1374     SetStyleFromGradient(value.GetAsCanvasGradient(), whichStyle);
  1375     return;
  1378   if (value.IsCanvasPattern()) {
  1379     SetStyleFromPattern(value.GetAsCanvasPattern(), whichStyle);
  1380     return;
  1383   MOZ_ASSUME_UNREACHABLE("Invalid union value");
  1386 void
  1387 CanvasRenderingContext2D::SetFillRule(const nsAString& aString)
  1389   FillRule rule;
  1391   if (aString.EqualsLiteral("evenodd"))
  1392     rule = FillRule::FILL_EVEN_ODD;
  1393   else if (aString.EqualsLiteral("nonzero"))
  1394     rule = FillRule::FILL_WINDING;
  1395   else
  1396     return;
  1398   CurrentState().fillRule = rule;
  1401 void
  1402 CanvasRenderingContext2D::GetFillRule(nsAString& aString)
  1404   switch (CurrentState().fillRule) {
  1405   case FillRule::FILL_WINDING:
  1406     aString.AssignLiteral("nonzero"); break;
  1407   case FillRule::FILL_EVEN_ODD:
  1408     aString.AssignLiteral("evenodd"); break;
  1411 //
  1412 // gradients and patterns
  1413 //
  1414 already_AddRefed<CanvasGradient>
  1415 CanvasRenderingContext2D::CreateLinearGradient(double x0, double y0, double x1, double y1)
  1417   nsIDocument *doc = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
  1418   mozilla::css::Loader *cssLoader = doc ? doc->CSSLoader() : nullptr;
  1420   nsRefPtr<CanvasGradient> grad =
  1421     new CanvasLinearGradient(this, cssLoader, Point(x0, y0), Point(x1, y1));
  1423   return grad.forget();
  1426 already_AddRefed<CanvasGradient>
  1427 CanvasRenderingContext2D::CreateRadialGradient(double x0, double y0, double r0,
  1428                                                double x1, double y1, double r1,
  1429                                                ErrorResult& aError)
  1431   if (r0 < 0.0 || r1 < 0.0) {
  1432     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1433     return nullptr;
  1436   nsIDocument *doc = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
  1437   mozilla::css::Loader *cssLoader = doc ? doc->CSSLoader() : nullptr;
  1438   nsRefPtr<CanvasGradient> grad =
  1439     new CanvasRadialGradient(this, cssLoader, Point(x0, y0), r0, Point(x1, y1), r1);
  1441   return grad.forget();
  1444 already_AddRefed<CanvasPattern>
  1445 CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement& element,
  1446                                         const nsAString& repeat,
  1447                                         ErrorResult& error)
  1449   CanvasPattern::RepeatMode repeatMode =
  1450     CanvasPattern::RepeatMode::NOREPEAT;
  1452   if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
  1453     repeatMode = CanvasPattern::RepeatMode::REPEAT;
  1454   } else if (repeat.EqualsLiteral("repeat-x")) {
  1455     repeatMode = CanvasPattern::RepeatMode::REPEATX;
  1456   } else if (repeat.EqualsLiteral("repeat-y")) {
  1457     repeatMode = CanvasPattern::RepeatMode::REPEATY;
  1458   } else if (repeat.EqualsLiteral("no-repeat")) {
  1459     repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
  1460   } else {
  1461     error.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  1462     return nullptr;
  1465   Element* htmlElement;
  1466   if (element.IsHTMLCanvasElement()) {
  1467     HTMLCanvasElement* canvas = &element.GetAsHTMLCanvasElement();
  1468     htmlElement = canvas;
  1470     nsIntSize size = canvas->GetSize();
  1471     if (size.width == 0 || size.height == 0) {
  1472       error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1473       return nullptr;
  1476     // Special case for Canvas, which could be an Azure canvas!
  1477     nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
  1478     if (srcCanvas) {
  1479       // This might not be an Azure canvas!
  1480       RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
  1482       nsRefPtr<CanvasPattern> pat =
  1483         new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
  1485       return pat.forget();
  1487   } else if (element.IsHTMLImageElement()) {
  1488     htmlElement = &element.GetAsHTMLImageElement();
  1489   } else {
  1490     htmlElement = &element.GetAsHTMLVideoElement();
  1493   EnsureTarget();
  1495   // The canvas spec says that createPattern should use the first frame
  1496   // of animated images
  1497   nsLayoutUtils::SurfaceFromElementResult res =
  1498     nsLayoutUtils::SurfaceFromElement(htmlElement,
  1499       nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
  1501   if (!res.mSourceSurface) {
  1502     error.Throw(NS_ERROR_NOT_AVAILABLE);
  1503     return nullptr;
  1506   nsRefPtr<CanvasPattern> pat =
  1507     new CanvasPattern(this, res.mSourceSurface, repeatMode, res.mPrincipal,
  1508                              res.mIsWriteOnly, res.mCORSUsed);
  1510   return pat.forget();
  1513 //
  1514 // shadows
  1515 //
  1516 void
  1517 CanvasRenderingContext2D::SetShadowColor(const nsAString& shadowColor)
  1519   nscolor color;
  1520   if (!ParseColor(shadowColor, &color)) {
  1521     return;
  1524   CurrentState().shadowColor = color;
  1527 //
  1528 // rects
  1529 //
  1531 void
  1532 CanvasRenderingContext2D::ClearRect(double x, double y, double w,
  1533                                     double h)
  1535   if (!mTarget) {
  1536     return;
  1539   mTarget->ClearRect(mgfx::Rect(x, y, w, h));
  1541   RedrawUser(gfxRect(x, y, w, h));
  1544 void
  1545 CanvasRenderingContext2D::FillRect(double x, double y, double w,
  1546                                    double h)
  1548   const ContextState &state = CurrentState();
  1550   if (state.patternStyles[Style::FILL]) {
  1551     CanvasPattern::RepeatMode repeat =
  1552       state.patternStyles[Style::FILL]->mRepeat;
  1553     // In the FillRect case repeat modes are easy to deal with.
  1554     bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY;
  1555     bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX;
  1557     IntSize patternSize =
  1558       state.patternStyles[Style::FILL]->mSurface->GetSize();
  1560     // We always need to execute painting for non-over operators, even if
  1561     // we end up with w/h = 0.
  1562     if (limitx) {
  1563       if (x < 0) {
  1564         w += x;
  1565         if (w < 0) {
  1566           w = 0;
  1569         x = 0;
  1571       if (x + w > patternSize.width) {
  1572         w = patternSize.width - x;
  1573         if (w < 0) {
  1574           w = 0;
  1578     if (limity) {
  1579       if (y < 0) {
  1580         h += y;
  1581         if (h < 0) {
  1582           h = 0;
  1585         y = 0;
  1587       if (y + h > patternSize.height) {
  1588         h = patternSize.height - y;
  1589         if (h < 0) {
  1590           h = 0;
  1596   mgfx::Rect bounds;
  1598   EnsureTarget();
  1599   if (NeedToDrawShadow()) {
  1600     bounds = mgfx::Rect(x, y, w, h);
  1601     bounds = mTarget->GetTransform().TransformBounds(bounds);
  1604   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1605     FillRect(mgfx::Rect(x, y, w, h),
  1606              CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
  1607              DrawOptions(state.globalAlpha, UsedOperation()));
  1609   RedrawUser(gfxRect(x, y, w, h));
  1612 void
  1613 CanvasRenderingContext2D::StrokeRect(double x, double y, double w,
  1614                                      double h)
  1616   const ContextState &state = CurrentState();
  1618   mgfx::Rect bounds;
  1620   if (!w && !h) {
  1621     return;
  1624   EnsureTarget();
  1625   if (!IsTargetValid()) {
  1626     return;
  1629   if (NeedToDrawShadow()) {
  1630     bounds = mgfx::Rect(x - state.lineWidth / 2.0f, y - state.lineWidth / 2.0f,
  1631                         w + state.lineWidth, h + state.lineWidth);
  1632     bounds = mTarget->GetTransform().TransformBounds(bounds);
  1635   if (!h) {
  1636     CapStyle cap = CapStyle::BUTT;
  1637     if (state.lineJoin == JoinStyle::ROUND) {
  1638       cap = CapStyle::ROUND;
  1640     AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1641       StrokeLine(Point(x, y), Point(x + w, y),
  1642                   CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1643                   StrokeOptions(state.lineWidth, state.lineJoin,
  1644                                 cap, state.miterLimit,
  1645                                 state.dash.Length(),
  1646                                 state.dash.Elements(),
  1647                                 state.dashOffset),
  1648                   DrawOptions(state.globalAlpha, UsedOperation()));
  1649     return;
  1652   if (!w) {
  1653     CapStyle cap = CapStyle::BUTT;
  1654     if (state.lineJoin == JoinStyle::ROUND) {
  1655       cap = CapStyle::ROUND;
  1657     AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1658       StrokeLine(Point(x, y), Point(x, y + h),
  1659                   CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1660                   StrokeOptions(state.lineWidth, state.lineJoin,
  1661                                 cap, state.miterLimit,
  1662                                 state.dash.Length(),
  1663                                 state.dash.Elements(),
  1664                                 state.dashOffset),
  1665                   DrawOptions(state.globalAlpha, UsedOperation()));
  1666     return;
  1669   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1670     StrokeRect(mgfx::Rect(x, y, w, h),
  1671                 CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1672                 StrokeOptions(state.lineWidth, state.lineJoin,
  1673                               state.lineCap, state.miterLimit,
  1674                               state.dash.Length(),
  1675                               state.dash.Elements(),
  1676                               state.dashOffset),
  1677                 DrawOptions(state.globalAlpha, UsedOperation()));
  1679   Redraw();
  1682 //
  1683 // path bits
  1684 //
  1686 void
  1687 CanvasRenderingContext2D::BeginPath()
  1689   mPath = nullptr;
  1690   mPathBuilder = nullptr;
  1691   mDSPathBuilder = nullptr;
  1692   mPathTransformWillUpdate = false;
  1695 void
  1696 CanvasRenderingContext2D::Fill(const CanvasWindingRule& winding)
  1698   EnsureUserSpacePath(winding);
  1700   if (!mPath) {
  1701     return;
  1704   mgfx::Rect bounds;
  1706   if (NeedToDrawShadow()) {
  1707     bounds = mPath->GetBounds(mTarget->GetTransform());
  1710   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1711     Fill(mPath, CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
  1712          DrawOptions(CurrentState().globalAlpha, UsedOperation()));
  1714   Redraw();
  1717 void CanvasRenderingContext2D::Fill(const CanvasPath& path, const CanvasWindingRule& winding)
  1719   EnsureTarget();
  1721   RefPtr<gfx::Path> gfxpath = path.GetPath(winding, mTarget);
  1723   if (!gfxpath) {
  1724     return;
  1727   mgfx::Rect bounds;
  1729   if (NeedToDrawShadow()) {
  1730     bounds = gfxpath->GetBounds(mTarget->GetTransform());
  1733   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1734     Fill(gfxpath, CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
  1735          DrawOptions(CurrentState().globalAlpha, UsedOperation()));
  1737   Redraw();
  1740 void
  1741 CanvasRenderingContext2D::Stroke()
  1743   EnsureUserSpacePath();
  1745   if (!mPath) {
  1746     return;
  1749   const ContextState &state = CurrentState();
  1751   StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
  1752                               state.lineCap, state.miterLimit,
  1753                               state.dash.Length(), state.dash.Elements(),
  1754                               state.dashOffset);
  1756   mgfx::Rect bounds;
  1757   if (NeedToDrawShadow()) {
  1758     bounds =
  1759       mPath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
  1762   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1763     Stroke(mPath, CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1764            strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
  1766   Redraw();
  1769 void
  1770 CanvasRenderingContext2D::Stroke(const CanvasPath& path)
  1772   EnsureTarget();
  1774   RefPtr<gfx::Path> gfxpath = path.GetPath(CanvasWindingRule::Nonzero, mTarget);
  1776   if (!gfxpath) {
  1777     return;
  1780   const ContextState &state = CurrentState();
  1782   StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
  1783                               state.lineCap, state.miterLimit,
  1784                               state.dash.Length(), state.dash.Elements(),
  1785                               state.dashOffset);
  1787   mgfx::Rect bounds;
  1788   if (NeedToDrawShadow()) {
  1789     bounds =
  1790       gfxpath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
  1793   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1794     Stroke(gfxpath, CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1795            strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
  1797   Redraw();
  1800 void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement)
  1802   EnsureUserSpacePath();
  1804   if (!mPath) {
  1805     return;
  1808   if(DrawCustomFocusRing(aElement)) {
  1809     Save();
  1811     // set state to conforming focus state
  1812     ContextState& state = CurrentState();
  1813     state.globalAlpha = 1.0;
  1814     state.shadowBlur = 0;
  1815     state.shadowOffset.x = 0;
  1816     state.shadowOffset.y = 0;
  1817     state.op = mozilla::gfx::CompositionOp::OP_OVER;
  1819     state.lineCap = CapStyle::BUTT;
  1820     state.lineJoin = mozilla::gfx::JoinStyle::MITER_OR_BEVEL;
  1821     state.lineWidth = 1;
  1822     CurrentState().dash.Clear();
  1824     // color and style of the rings is the same as for image maps
  1825     // set the background focus color
  1826     CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(255, 255, 255, 255));
  1827     // draw the focus ring
  1828     Stroke();
  1830     // set dashing for foreground
  1831     FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
  1832     dash.AppendElement(1);
  1833     dash.AppendElement(1);
  1835     // set the foreground focus color
  1836     CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(0,0,0, 255));
  1837     // draw the focus ring
  1838     Stroke();
  1840     Restore();
  1844 bool CanvasRenderingContext2D::DrawCustomFocusRing(mozilla::dom::Element& aElement)
  1846   EnsureUserSpacePath();
  1848   HTMLCanvasElement* canvas = GetCanvas();
  1850   if (!canvas|| !nsContentUtils::ContentIsDescendantOf(&aElement, canvas)) {
  1851     return false;
  1854   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  1855   if (fm) {
  1856     // check that the element i focused
  1857     nsCOMPtr<nsIDOMElement> focusedElement;
  1858     fm->GetFocusedElement(getter_AddRefs(focusedElement));
  1859     if (SameCOMIdentity(aElement.AsDOMNode(), focusedElement)) {
  1860       return true;
  1864   return false;
  1867 void
  1868 CanvasRenderingContext2D::Clip(const CanvasWindingRule& winding)
  1870   EnsureUserSpacePath(winding);
  1872   if (!mPath) {
  1873     return;
  1876   mTarget->PushClip(mPath);
  1877   CurrentState().clipsPushed.push_back(mPath);
  1880 void
  1881 CanvasRenderingContext2D::Clip(const CanvasPath& path, const CanvasWindingRule& winding)
  1883   EnsureTarget();
  1885   RefPtr<gfx::Path> gfxpath = path.GetPath(winding, mTarget);
  1887   if (!gfxpath) {
  1888     return;
  1891   mTarget->PushClip(gfxpath);
  1892   CurrentState().clipsPushed.push_back(gfxpath);
  1895 void
  1896 CanvasRenderingContext2D::ArcTo(double x1, double y1, double x2,
  1897                                 double y2, double radius,
  1898                                 ErrorResult& error)
  1900   if (radius < 0) {
  1901     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1902     return;
  1905   EnsureWritablePath();
  1907   // Current point in user space!
  1908   Point p0;
  1909   if (mPathBuilder) {
  1910     p0 = mPathBuilder->CurrentPoint();
  1911   } else {
  1912     Matrix invTransform = mTarget->GetTransform();
  1913     if (!invTransform.Invert()) {
  1914       return;
  1917     p0 = invTransform * mDSPathBuilder->CurrentPoint();
  1920   Point p1(x1, y1);
  1921   Point p2(x2, y2);
  1923   // Execute these calculations in double precision to avoid cumulative
  1924   // rounding errors.
  1925   double dir, a2, b2, c2, cosx, sinx, d, anx, any,
  1926          bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
  1927   bool anticlockwise;
  1929   if (p0 == p1 || p1 == p2 || radius == 0) {
  1930     LineTo(p1.x, p1.y);
  1931     return;
  1934   // Check for colinearity
  1935   dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
  1936   if (dir == 0) {
  1937     LineTo(p1.x, p1.y);
  1938     return;
  1942   // XXX - Math for this code was already available from the non-azure code
  1943   // and would be well tested. Perhaps converting to bezier directly might
  1944   // be more efficient longer run.
  1945   a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
  1946   b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
  1947   c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
  1948   cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
  1950   sinx = sqrt(1 - cosx*cosx);
  1951   d = radius / ((1 - cosx) / sinx);
  1953   anx = (x1-p0.x) / sqrt(a2);
  1954   any = (y1-p0.y) / sqrt(a2);
  1955   bnx = (x1-x2) / sqrt(b2);
  1956   bny = (y1-y2) / sqrt(b2);
  1957   x3 = x1 - anx*d;
  1958   y3 = y1 - any*d;
  1959   x4 = x1 - bnx*d;
  1960   y4 = y1 - bny*d;
  1961   anticlockwise = (dir < 0);
  1962   cx = x3 + any*radius*(anticlockwise ? 1 : -1);
  1963   cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
  1964   angle0 = atan2((y3-cy), (x3-cx));
  1965   angle1 = atan2((y4-cy), (x4-cx));
  1968   LineTo(x3, y3);
  1970   Arc(cx, cy, radius, angle0, angle1, anticlockwise, error);
  1973 void
  1974 CanvasRenderingContext2D::Arc(double x, double y, double r,
  1975                               double startAngle, double endAngle,
  1976                               bool anticlockwise, ErrorResult& error)
  1978   if (r < 0.0) {
  1979     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1980     return;
  1983   EnsureWritablePath();
  1985   ArcToBezier(this, Point(x, y), Size(r, r), startAngle, endAngle, anticlockwise);
  1988 void
  1989 CanvasRenderingContext2D::Rect(double x, double y, double w, double h)
  1991   EnsureWritablePath();
  1993   if (mPathBuilder) {
  1994     mPathBuilder->MoveTo(Point(x, y));
  1995     mPathBuilder->LineTo(Point(x + w, y));
  1996     mPathBuilder->LineTo(Point(x + w, y + h));
  1997     mPathBuilder->LineTo(Point(x, y + h));
  1998     mPathBuilder->Close();
  1999   } else {
  2000     mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
  2001     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y));
  2002     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y + h));
  2003     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
  2004     mDSPathBuilder->Close();
  2008 void
  2009 CanvasRenderingContext2D::EnsureWritablePath()
  2011   if (mDSPathBuilder) {
  2012     return;
  2015   FillRule fillRule = CurrentState().fillRule;
  2017   if (mPathBuilder) {
  2018     if (mPathTransformWillUpdate) {
  2019       mPath = mPathBuilder->Finish();
  2020       mDSPathBuilder =
  2021         mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
  2022       mPath = nullptr;
  2023       mPathBuilder = nullptr;
  2024       mPathTransformWillUpdate = false;
  2026     return;
  2029   EnsureTarget();
  2030   if (!mPath) {
  2031     NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
  2032     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
  2033   } else if (!mPathTransformWillUpdate) {
  2034     mPathBuilder = mPath->CopyToBuilder(fillRule);
  2035   } else {
  2036     mDSPathBuilder =
  2037       mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
  2038     mPathTransformWillUpdate = false;
  2039     mPath = nullptr;
  2043 void
  2044 CanvasRenderingContext2D::EnsureUserSpacePath(const CanvasWindingRule& winding)
  2046   FillRule fillRule = CurrentState().fillRule;
  2047   if(winding == CanvasWindingRule::Evenodd)
  2048     fillRule = FillRule::FILL_EVEN_ODD;
  2050   if (!mPath && !mPathBuilder && !mDSPathBuilder) {
  2051     EnsureTarget();
  2052     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
  2055   if (mPathBuilder) {
  2056     mPath = mPathBuilder->Finish();
  2057     mPathBuilder = nullptr;
  2060   if (mPath &&
  2061       mPathTransformWillUpdate) {
  2062     mDSPathBuilder =
  2063       mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
  2064     mPath = nullptr;
  2065     mPathTransformWillUpdate = false;
  2068   if (mDSPathBuilder) {
  2069     RefPtr<Path> dsPath;
  2070     dsPath = mDSPathBuilder->Finish();
  2071     mDSPathBuilder = nullptr;
  2073     Matrix inverse = mTarget->GetTransform();
  2074     if (!inverse.Invert()) {
  2075       NS_WARNING("Could not invert transform");
  2076       return;
  2079     mPathBuilder =
  2080       dsPath->TransformedCopyToBuilder(inverse, fillRule);
  2081     mPath = mPathBuilder->Finish();
  2082     mPathBuilder = nullptr;
  2085   if (mPath && mPath->GetFillRule() != fillRule) {
  2086     mPathBuilder = mPath->CopyToBuilder(fillRule);
  2087     mPath = mPathBuilder->Finish();
  2088     mPathBuilder = nullptr;
  2091   NS_ASSERTION(mPath, "mPath should exist");
  2094 void
  2095 CanvasRenderingContext2D::TransformWillUpdate()
  2097   EnsureTarget();
  2099   // Store the matrix that would transform the current path to device
  2100   // space.
  2101   if (mPath || mPathBuilder) {
  2102     if (!mPathTransformWillUpdate) {
  2103       // If the transform has already been updated, but a device space builder
  2104       // has not been created yet mPathToDS contains the right transform to
  2105       // transform the current mPath into device space.
  2106       // We should leave it alone.
  2107       mPathToDS = mTarget->GetTransform();
  2109     mPathTransformWillUpdate = true;
  2113 //
  2114 // text
  2115 //
  2117 /**
  2118  * Helper function for SetFont that creates a style rule for the given font.
  2119  * @param aFont The CSS font string
  2120  * @param aNode The canvas element
  2121  * @param aResult Pointer in which to place the new style rule.
  2122  * @remark Assumes all pointer arguments are non-null.
  2123  */
  2124 static nsresult
  2125 CreateFontStyleRule(const nsAString& aFont,
  2126                     nsINode* aNode,
  2127                     StyleRule** aResult)
  2129   nsRefPtr<StyleRule> rule;
  2130   bool changed;
  2132   nsIPrincipal* principal = aNode->NodePrincipal();
  2133   nsIDocument* document = aNode->OwnerDoc();
  2135   nsIURI* docURL = document->GetDocumentURI();
  2136   nsIURI* baseURL = document->GetDocBaseURI();
  2138   // Pass the CSS Loader object to the parser, to allow parser error reports
  2139   // to include the outer window ID.
  2140   nsCSSParser parser(document->CSSLoader());
  2142   nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
  2143                                            principal, getter_AddRefs(rule));
  2144   if (NS_FAILED(rv)) {
  2145     return rv;
  2148   rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
  2149                             principal, rule->GetDeclaration(), &changed,
  2150                             false);
  2151   if (NS_FAILED(rv))
  2152     return rv;
  2154   rv = parser.ParseProperty(eCSSProperty_line_height,
  2155                             NS_LITERAL_STRING("normal"), docURL, baseURL,
  2156                             principal, rule->GetDeclaration(), &changed,
  2157                             false);
  2158   if (NS_FAILED(rv)) {
  2159     return rv;
  2162   rule->RuleMatched();
  2164   rule.forget(aResult);
  2165   return NS_OK;
  2168 void
  2169 CanvasRenderingContext2D::SetFont(const nsAString& font,
  2170                                   ErrorResult& error)
  2172   /*
  2173     * If font is defined with relative units (e.g. ems) and the parent
  2174     * style context changes in between calls, setting the font to the
  2175     * same value as previous could result in a different computed value,
  2176     * so we cannot have the optimization where we check if the new font
  2177     * string is equal to the old one.
  2178     */
  2180   if (!mCanvasElement && !mDocShell) {
  2181     NS_WARNING("Canvas element must be non-null or a docshell must be provided");
  2182     error.Throw(NS_ERROR_FAILURE);
  2183     return;
  2186   nsIPresShell* presShell = GetPresShell();
  2187   if (!presShell) {
  2188     error.Throw(NS_ERROR_FAILURE);
  2189     return;
  2191   nsIDocument* document = presShell->GetDocument();
  2193   nsRefPtr<css::StyleRule> rule;
  2194   error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
  2196   if (error.Failed()) {
  2197     return;
  2200   css::Declaration *declaration = rule->GetDeclaration();
  2201   // The easiest way to see whether we got a syntax error or whether
  2202   // we got 'inherit' or 'initial' is to look at font-size-adjust,
  2203   // which the shorthand resets to either 'none' or
  2204   // '-moz-system-font'.
  2205   // We know the declaration is not !important, so we can use
  2206   // GetNormalBlock().
  2207   const nsCSSValue *fsaVal =
  2208     declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
  2209   if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
  2210                   fsaVal->GetUnit() != eCSSUnit_System_Font)) {
  2211       // We got an all-property value or a syntax error.  The spec says
  2212       // this value must be ignored.
  2213     return;
  2216   nsTArray< nsCOMPtr<nsIStyleRule> > rules;
  2217   rules.AppendElement(rule);
  2219   nsStyleSet* styleSet = presShell->StyleSet();
  2221   // have to get a parent style context for inherit-like relative
  2222   // values (2em, bolder, etc.)
  2223   nsRefPtr<nsStyleContext> parentContext;
  2225   if (mCanvasElement && mCanvasElement->IsInDoc()) {
  2226       // inherit from the canvas element
  2227       parentContext = nsComputedDOMStyle::GetStyleContextForElement(
  2228               mCanvasElement,
  2229               nullptr,
  2230               presShell);
  2231   } else {
  2232     // otherwise inherit from default (10px sans-serif)
  2233     nsRefPtr<css::StyleRule> parentRule;
  2234     error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
  2235                                 document,
  2236                                 getter_AddRefs(parentRule));
  2238     if (error.Failed()) {
  2239       return;
  2242     nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
  2243     parentRules.AppendElement(parentRule);
  2244     parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
  2247   if (!parentContext) {
  2248     error.Throw(NS_ERROR_FAILURE);
  2249     return;
  2252   // add a rule to prevent text zoom from affecting the style
  2253   rules.AppendElement(new nsDisableTextZoomStyleRule);
  2255   nsRefPtr<nsStyleContext> sc =
  2256       styleSet->ResolveStyleForRules(parentContext, rules);
  2257   if (!sc) {
  2258     error.Throw(NS_ERROR_FAILURE);
  2259     return;
  2262   const nsStyleFont* fontStyle = sc->StyleFont();
  2264   NS_ASSERTION(fontStyle, "Could not obtain font style");
  2266   nsIAtom* language = sc->StyleFont()->mLanguage;
  2267   if (!language) {
  2268     language = presShell->GetPresContext()->GetLanguageFromCharset();
  2271   // use CSS pixels instead of dev pixels to avoid being affected by page zoom
  2272   const uint32_t aupcp = nsPresContext::AppUnitsPerCSSPixel();
  2274   bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
  2275                       presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
  2277   // Purposely ignore the font size that respects the user's minimum
  2278   // font preference (fontStyle->mFont.size) in favor of the computed
  2279   // size (fontStyle->mSize).  See
  2280   // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
  2281   MOZ_ASSERT(!fontStyle->mAllowZoom,
  2282              "expected text zoom to be disabled on this nsStyleFont");
  2283   gfxFontStyle style(fontStyle->mFont.style,
  2284                      fontStyle->mFont.weight,
  2285                      fontStyle->mFont.stretch,
  2286                      NSAppUnitsToFloatPixels(fontStyle->mSize, float(aupcp)),
  2287                      language,
  2288                      fontStyle->mFont.sizeAdjust,
  2289                      fontStyle->mFont.systemFont,
  2290                      printerFont,
  2291                      fontStyle->mFont.languageOverride);
  2293   fontStyle->mFont.AddFontFeaturesToStyle(&style);
  2295   nsPresContext *c = presShell->GetPresContext();
  2296   CurrentState().fontGroup =
  2297       gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
  2298                                                   &style,
  2299                                                   c->GetUserFontSet());
  2300   NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
  2301   CurrentState().fontGroup->SetTextPerfMetrics(c->GetTextPerfMetrics());
  2303   // The font getter is required to be reserialized based on what we
  2304   // parsed (including having line-height removed).  (Older drafts of
  2305   // the spec required font sizes be converted to pixels, but that no
  2306   // longer seems to be required.)
  2307   declaration->GetValue(eCSSProperty_font, CurrentState().font);
  2310 void
  2311 CanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
  2313   if (ta.EqualsLiteral("start"))
  2314     CurrentState().textAlign = TextAlign::START;
  2315   else if (ta.EqualsLiteral("end"))
  2316     CurrentState().textAlign = TextAlign::END;
  2317   else if (ta.EqualsLiteral("left"))
  2318     CurrentState().textAlign = TextAlign::LEFT;
  2319   else if (ta.EqualsLiteral("right"))
  2320     CurrentState().textAlign = TextAlign::RIGHT;
  2321   else if (ta.EqualsLiteral("center"))
  2322     CurrentState().textAlign = TextAlign::CENTER;
  2325 void
  2326 CanvasRenderingContext2D::GetTextAlign(nsAString& ta)
  2328   switch (CurrentState().textAlign)
  2330   case TextAlign::START:
  2331     ta.AssignLiteral("start");
  2332     break;
  2333   case TextAlign::END:
  2334     ta.AssignLiteral("end");
  2335     break;
  2336   case TextAlign::LEFT:
  2337     ta.AssignLiteral("left");
  2338     break;
  2339   case TextAlign::RIGHT:
  2340     ta.AssignLiteral("right");
  2341     break;
  2342   case TextAlign::CENTER:
  2343     ta.AssignLiteral("center");
  2344     break;
  2348 void
  2349 CanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
  2351   if (tb.EqualsLiteral("top"))
  2352     CurrentState().textBaseline = TextBaseline::TOP;
  2353   else if (tb.EqualsLiteral("hanging"))
  2354     CurrentState().textBaseline = TextBaseline::HANGING;
  2355   else if (tb.EqualsLiteral("middle"))
  2356     CurrentState().textBaseline = TextBaseline::MIDDLE;
  2357   else if (tb.EqualsLiteral("alphabetic"))
  2358     CurrentState().textBaseline = TextBaseline::ALPHABETIC;
  2359   else if (tb.EqualsLiteral("ideographic"))
  2360     CurrentState().textBaseline = TextBaseline::IDEOGRAPHIC;
  2361   else if (tb.EqualsLiteral("bottom"))
  2362     CurrentState().textBaseline = TextBaseline::BOTTOM;
  2365 void
  2366 CanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
  2368   switch (CurrentState().textBaseline)
  2370   case TextBaseline::TOP:
  2371     tb.AssignLiteral("top");
  2372     break;
  2373   case TextBaseline::HANGING:
  2374     tb.AssignLiteral("hanging");
  2375     break;
  2376   case TextBaseline::MIDDLE:
  2377     tb.AssignLiteral("middle");
  2378     break;
  2379   case TextBaseline::ALPHABETIC:
  2380     tb.AssignLiteral("alphabetic");
  2381     break;
  2382   case TextBaseline::IDEOGRAPHIC:
  2383     tb.AssignLiteral("ideographic");
  2384     break;
  2385   case TextBaseline::BOTTOM:
  2386     tb.AssignLiteral("bottom");
  2387     break;
  2391 /*
  2392  * Helper function that replaces the whitespace characters in a string
  2393  * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
  2394  * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
  2395  * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
  2396  * @param str The string whose whitespace characters to replace.
  2397  */
  2398 static inline void
  2399 TextReplaceWhitespaceCharacters(nsAutoString& str)
  2401   str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", char16_t(' '));
  2404 void
  2405 CanvasRenderingContext2D::FillText(const nsAString& text, double x,
  2406                                    double y,
  2407                                    const Optional<double>& maxWidth,
  2408                                    ErrorResult& error)
  2410   error = DrawOrMeasureText(text, x, y, maxWidth, TextDrawOperation::FILL, nullptr);
  2413 void
  2414 CanvasRenderingContext2D::StrokeText(const nsAString& text, double x,
  2415                                      double y,
  2416                                      const Optional<double>& maxWidth,
  2417                                      ErrorResult& error)
  2419   error = DrawOrMeasureText(text, x, y, maxWidth, TextDrawOperation::STROKE, nullptr);
  2422 TextMetrics*
  2423 CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
  2424                                       ErrorResult& error)
  2426   float width;
  2427   Optional<double> maxWidth;
  2428   error = DrawOrMeasureText(rawText, 0, 0, maxWidth, TextDrawOperation::MEASURE, &width);
  2429   if (error.Failed()) {
  2430     return nullptr;
  2433   return new TextMetrics(width);
  2436 void
  2437 CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions& options, ErrorResult& error)
  2439   // remove old hit region first
  2440   RemoveHitRegion(options.mId);
  2442   // for now, we require a fallback element
  2443   if (options.mControl == NULL) {
  2444     error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  2445     return;
  2448   // check if the control is a descendant of our canvas
  2449   HTMLCanvasElement* canvas = GetCanvas();
  2450   bool isDescendant = true;
  2451   if (!canvas || !nsContentUtils::ContentIsDescendantOf(options.mControl, canvas)) {
  2452     isDescendant = false;
  2455   // check if the path is valid
  2456   EnsureUserSpacePath(CanvasWindingRule::Nonzero);
  2457   if(!mPath) {
  2458     error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  2459     return;
  2462   // get the bounds of the current path. They are relative to the canvas
  2463   mgfx::Rect bounds(mPath->GetBounds(mTarget->GetTransform()));
  2464   if ((bounds.width == 0) || (bounds.height == 0) || !bounds.IsFinite()) {
  2465     // The specified region has no pixels.
  2466     error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  2467     return;
  2470 #ifdef ACCESSIBILITY
  2471   if (isDescendant) {
  2472     nsRect* nsBounds = new nsRect();
  2473     gfxRect rect(bounds.x, bounds.y, bounds.width, bounds.height);
  2474     *nsBounds = nsLayoutUtils::RoundGfxRectToAppRect(rect, AppUnitsPerCSSPixel());
  2475     options.mControl->DeleteProperty(nsGkAtoms::hitregion);
  2476     options.mControl->SetProperty(nsGkAtoms::hitregion, nsBounds,
  2477                                   nsINode::DeleteProperty<nsRect>);
  2479 #endif
  2481   // finally, add the region to the list if it has an ID
  2482   if (options.mId.Length() != 0) {
  2483     mHitRegionsOptions.PutEntry(options.mId)->mElement = options.mControl;
  2487 void
  2488 CanvasRenderingContext2D::RemoveHitRegion(const nsAString& id)
  2490   RegionInfo* info = mHitRegionsOptions.GetEntry(id);
  2491   if (!info) {
  2492     return;
  2495 #ifdef ACCESSIBILITY
  2496   info->mElement->DeleteProperty(nsGkAtoms::hitregion);
  2497 #endif
  2498   mHitRegionsOptions.RemoveEntry(id);
  2501 /**
  2502  * Used for nsBidiPresUtils::ProcessText
  2503  */
  2504 struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
  2506   typedef CanvasRenderingContext2D::ContextState ContextState;
  2508   virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
  2510     mFontgrp->UpdateFontList(); // ensure user font generation is current
  2511     mTextRun = mFontgrp->MakeTextRun(text,
  2512                                      length,
  2513                                      mThebes,
  2514                                      mAppUnitsPerDevPixel,
  2515                                      direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
  2518   virtual nscoord GetWidth()
  2520     gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
  2521                                                                mTextRun->GetLength(),
  2522                                                                mDoMeasureBoundingBox ?
  2523                                                                  gfxFont::TIGHT_INK_EXTENTS :
  2524                                                                  gfxFont::LOOSE_INK_EXTENTS,
  2525                                                                mThebes,
  2526                                                                nullptr);
  2528     // this only measures the height; the total width is gotten from the
  2529     // the return value of ProcessText.
  2530     if (mDoMeasureBoundingBox) {
  2531       textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
  2532       mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
  2535     return NSToCoordRound(textRunMetrics.mAdvanceWidth);
  2538   virtual void DrawText(nscoord xOffset, nscoord width)
  2540     gfxPoint point = mPt;
  2541     point.x += xOffset;
  2543     // offset is given in terms of left side of string
  2544     if (mTextRun->IsRightToLeft()) {
  2545       // Bug 581092 - don't use rounded pixel width to advance to
  2546       // right-hand end of run, because this will cause different
  2547       // glyph positioning for LTR vs RTL drawing of the same
  2548       // glyph string on OS X and DWrite where textrun widths may
  2549       // involve fractional pixels.
  2550       gfxTextRun::Metrics textRunMetrics =
  2551         mTextRun->MeasureText(0,
  2552                               mTextRun->GetLength(),
  2553                               mDoMeasureBoundingBox ?
  2554                                   gfxFont::TIGHT_INK_EXTENTS :
  2555                                   gfxFont::LOOSE_INK_EXTENTS,
  2556                               mThebes,
  2557                               nullptr);
  2558       point.x += textRunMetrics.mAdvanceWidth;
  2559       // old code was:
  2560       //   point.x += width * mAppUnitsPerDevPixel;
  2561       // TODO: restore this if/when we move to fractional coords
  2562       // throughout the text layout process
  2565     uint32_t numRuns;
  2566     const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
  2567     const int32_t appUnitsPerDevUnit = mAppUnitsPerDevPixel;
  2568     const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
  2569     Point baselineOrigin =
  2570       Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
  2572     float advanceSum = 0;
  2574     mCtx->EnsureTarget();
  2575     for (uint32_t c = 0; c < numRuns; c++) {
  2576       gfxFont *font = runs[c].mFont;
  2577       uint32_t endRun = 0;
  2578       if (c + 1 < numRuns) {
  2579         endRun = runs[c + 1].mCharacterOffset;
  2580       } else {
  2581         endRun = mTextRun->GetLength();
  2584       const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
  2586       RefPtr<ScaledFont> scaledFont =
  2587         gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
  2589       if (!scaledFont) {
  2590         // This can occur when something switched DirectWrite off.
  2591         return;
  2594       RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
  2596       GlyphBuffer buffer;
  2598       std::vector<Glyph> glyphBuf;
  2600       for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
  2601         Glyph newGlyph;
  2602         if (glyphs[i].IsSimpleGlyph()) {
  2603           newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
  2604           if (mTextRun->IsRightToLeft()) {
  2605             newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
  2606               glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
  2607           } else {
  2608             newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
  2610           newGlyph.mPosition.y = baselineOrigin.y;
  2611           advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
  2612           glyphBuf.push_back(newGlyph);
  2613           continue;
  2616         if (!glyphs[i].GetGlyphCount()) {
  2617           continue;
  2620         gfxTextRun::DetailedGlyph *detailedGlyphs =
  2621           mTextRun->GetDetailedGlyphs(i);
  2623         if (glyphs[i].IsMissing()) {
  2624           newGlyph.mIndex = 0;
  2625           if (mTextRun->IsRightToLeft()) {
  2626             newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
  2627               detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
  2628           } else {
  2629             newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
  2631           newGlyph.mPosition.y = baselineOrigin.y;
  2632           advanceSum += detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
  2633           glyphBuf.push_back(newGlyph);
  2634           continue;
  2637         for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++) {
  2638           newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
  2639           if (mTextRun->IsRightToLeft()) {
  2640             newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
  2641               advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
  2642           } else {
  2643             newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
  2645           newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
  2646           glyphBuf.push_back(newGlyph);
  2647           advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
  2651       if (!glyphBuf.size()) {
  2652         // This may happen for glyph runs for a 0 size font.
  2653         continue;
  2656       buffer.mGlyphs = &glyphBuf.front();
  2657       buffer.mNumGlyphs = glyphBuf.size();
  2659       Rect bounds = mCtx->mTarget->GetTransform().
  2660         TransformBounds(Rect(mBoundingBox.x, mBoundingBox.y,
  2661                              mBoundingBox.width, mBoundingBox.height));
  2662       if (mOp == CanvasRenderingContext2D::TextDrawOperation::FILL) {
  2663         AdjustedTarget(mCtx, &bounds)->
  2664           FillGlyphs(scaledFont, buffer,
  2665                      CanvasGeneralPattern().
  2666                        ForStyle(mCtx, CanvasRenderingContext2D::Style::FILL, mCtx->mTarget),
  2667                      DrawOptions(mState->globalAlpha, mCtx->UsedOperation()),
  2668                      renderingOptions);
  2669       } else if (mOp == CanvasRenderingContext2D::TextDrawOperation::STROKE) {
  2670         // stroke glyphs one at a time to avoid poor CoreGraphics performance
  2671         // when stroking a path with a very large number of points
  2672         buffer.mGlyphs = &glyphBuf.front();
  2673         buffer.mNumGlyphs = 1;
  2674         const ContextState& state = *mState;
  2675         AdjustedTarget target(mCtx, &bounds);
  2676         const StrokeOptions strokeOpts(state.lineWidth, state.lineJoin,
  2677                                        state.lineCap, state.miterLimit,
  2678                                        state.dash.Length(),
  2679                                        state.dash.Elements(),
  2680                                        state.dashOffset);
  2681         CanvasGeneralPattern cgp;
  2682         const Pattern& patForStyle
  2683           (cgp.ForStyle(mCtx, CanvasRenderingContext2D::Style::STROKE, mCtx->mTarget));
  2684         const DrawOptions drawOpts(state.globalAlpha, mCtx->UsedOperation());
  2686         for (unsigned i = glyphBuf.size(); i > 0; --i) {
  2687           RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
  2688           target->Stroke(path, patForStyle, strokeOpts, drawOpts);
  2689           buffer.mGlyphs++;
  2695   // current text run
  2696   nsAutoPtr<gfxTextRun> mTextRun;
  2698   // pointer to a screen reference context used to measure text and such
  2699   nsRefPtr<gfxContext> mThebes;
  2701   // Pointer to the draw target we should fill our text to
  2702   CanvasRenderingContext2D *mCtx;
  2704   // position of the left side of the string, alphabetic baseline
  2705   gfxPoint mPt;
  2707   // current font
  2708   gfxFontGroup* mFontgrp;
  2710   // dev pixel conversion factor
  2711   int32_t mAppUnitsPerDevPixel;
  2713   // operation (fill or stroke)
  2714   CanvasRenderingContext2D::TextDrawOperation mOp;
  2716   // context state
  2717   ContextState *mState;
  2719   // union of bounding boxes of all runs, needed for shadows
  2720   gfxRect mBoundingBox;
  2722   // true iff the bounding box should be measured
  2723   bool mDoMeasureBoundingBox;
  2724 };
  2726 nsresult
  2727 CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
  2728                                             float aX,
  2729                                             float aY,
  2730                                             const Optional<double>& aMaxWidth,
  2731                                             TextDrawOperation aOp,
  2732                                             float* aWidth)
  2734   nsresult rv;
  2736   // spec isn't clear on what should happen if aMaxWidth <= 0, so
  2737   // treat it as an invalid argument
  2738   // technically, 0 should be an invalid value as well, but 0 is the default
  2739   // arg, and there is no way to tell if the default was used
  2740   if (aMaxWidth.WasPassed() && aMaxWidth.Value() < 0)
  2741     return NS_ERROR_INVALID_ARG;
  2743   if (!mCanvasElement && !mDocShell) {
  2744     NS_WARNING("Canvas element must be non-null or a docshell must be provided");
  2745     return NS_ERROR_FAILURE;
  2748   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
  2749   if (!presShell)
  2750     return NS_ERROR_FAILURE;
  2752   nsIDocument* document = presShell->GetDocument();
  2754   // replace all the whitespace characters with U+0020 SPACE
  2755   nsAutoString textToDraw(aRawText);
  2756   TextReplaceWhitespaceCharacters(textToDraw);
  2758   // for now, default to ltr if not in doc
  2759   bool isRTL = false;
  2761   if (mCanvasElement && mCanvasElement->IsInDoc()) {
  2762     // try to find the closest context
  2763     nsRefPtr<nsStyleContext> canvasStyle =
  2764       nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
  2765                                                     nullptr,
  2766                                                     presShell);
  2767     if (!canvasStyle) {
  2768       return NS_ERROR_FAILURE;
  2771     isRTL = canvasStyle->StyleVisibility()->mDirection ==
  2772       NS_STYLE_DIRECTION_RTL;
  2773   } else {
  2774     isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
  2777   gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
  2778   NS_ASSERTION(currentFontStyle, "font group is null");
  2780   // ensure user font set is up to date
  2781   currentFontStyle->
  2782     SetUserFontSet(presShell->GetPresContext()->GetUserFontSet());
  2784   if (currentFontStyle->GetStyle()->size == 0.0F) {
  2785     if (aWidth) {
  2786       *aWidth = 0;
  2788     return NS_OK;
  2791   const ContextState &state = CurrentState();
  2793   // This is only needed to know if we can know the drawing bounding box easily.
  2794   bool doDrawShadow = NeedToDrawShadow();
  2796   CanvasBidiProcessor processor;
  2798   GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
  2799   processor.mPt = gfxPoint(aX, aY);
  2800   processor.mThebes =
  2801     new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
  2803   // If we don't have a target then we don't have a transform. A target won't
  2804   // be needed in the case where we're measuring the text size. This allows
  2805   // to avoid creating a target if it's only being used to measure text sizes.
  2806   if (mTarget) {
  2807     Matrix matrix = mTarget->GetTransform();
  2808     processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
  2810   processor.mCtx = this;
  2811   processor.mOp = aOp;
  2812   processor.mBoundingBox = gfxRect(0, 0, 0, 0);
  2813   processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
  2814   processor.mState = &CurrentState();
  2815   processor.mFontgrp = currentFontStyle;
  2817   nscoord totalWidthCoord;
  2819   // calls bidi algo twice since it needs the full text width and the
  2820   // bounding boxes before rendering anything
  2821   nsBidi bidiEngine;
  2822   rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
  2823                                     textToDraw.Length(),
  2824                                     isRTL ? NSBIDI_RTL : NSBIDI_LTR,
  2825                                     presShell->GetPresContext(),
  2826                                     processor,
  2827                                     nsBidiPresUtils::MODE_MEASURE,
  2828                                     nullptr,
  2829                                     0,
  2830                                     &totalWidthCoord,
  2831                                     &bidiEngine);
  2832   if (NS_FAILED(rv)) {
  2833     return rv;
  2836   float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
  2837   if (aWidth) {
  2838     *aWidth = totalWidth;
  2841   // if only measuring, don't need to do any more work
  2842   if (aOp==TextDrawOperation::MEASURE) {
  2843     return NS_OK;
  2846   // offset pt.x based on text align
  2847   gfxFloat anchorX;
  2849   if (state.textAlign == TextAlign::CENTER) {
  2850     anchorX = .5;
  2851   } else if (state.textAlign == TextAlign::LEFT ||
  2852             (!isRTL && state.textAlign == TextAlign::START) ||
  2853             (isRTL && state.textAlign == TextAlign::END)) {
  2854     anchorX = 0;
  2855   } else {
  2856     anchorX = 1;
  2859   processor.mPt.x -= anchorX * totalWidth;
  2861   // offset pt.y based on text baseline
  2862   processor.mFontgrp->UpdateFontList(); // ensure user font generation is current
  2863   NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
  2864   const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
  2866   gfxFloat anchorY;
  2868   switch (state.textBaseline)
  2870   case TextBaseline::HANGING:
  2871       // fall through; best we can do with the information available
  2872   case TextBaseline::TOP:
  2873     anchorY = fontMetrics.emAscent;
  2874     break;
  2875   case TextBaseline::MIDDLE:
  2876     anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
  2877     break;
  2878   case TextBaseline::IDEOGRAPHIC:
  2879     // fall through; best we can do with the information available
  2880   case TextBaseline::ALPHABETIC:
  2881     anchorY = 0;
  2882     break;
  2883   case TextBaseline::BOTTOM:
  2884     anchorY = -fontMetrics.emDescent;
  2885     break;
  2886   default:
  2887     MOZ_CRASH("unexpected TextBaseline");
  2890   processor.mPt.y += anchorY;
  2892   // correct bounding box to get it to be the correct size/position
  2893   processor.mBoundingBox.width = totalWidth;
  2894   processor.mBoundingBox.MoveBy(processor.mPt);
  2896   processor.mPt.x *= processor.mAppUnitsPerDevPixel;
  2897   processor.mPt.y *= processor.mAppUnitsPerDevPixel;
  2899   EnsureTarget();
  2900   Matrix oldTransform = mTarget->GetTransform();
  2901   // if text is over aMaxWidth, then scale the text horizontally such that its
  2902   // width is precisely aMaxWidth
  2903   if (aMaxWidth.WasPassed() && aMaxWidth.Value() > 0 &&
  2904       totalWidth > aMaxWidth.Value()) {
  2905     Matrix newTransform = oldTransform;
  2907     // Translate so that the anchor point is at 0,0, then scale and then
  2908     // translate back.
  2909     newTransform.Translate(aX, 0);
  2910     newTransform.Scale(aMaxWidth.Value() / totalWidth, 1);
  2911     newTransform.Translate(-aX, 0);
  2912     /* we do this to avoid an ICE in the android compiler */
  2913     Matrix androidCompilerBug = newTransform;
  2914     mTarget->SetTransform(androidCompilerBug);
  2917   // save the previous bounding box
  2918   gfxRect boundingBox = processor.mBoundingBox;
  2920   // don't ever need to measure the bounding box twice
  2921   processor.mDoMeasureBoundingBox = false;
  2923   rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
  2924                                     textToDraw.Length(),
  2925                                     isRTL ? NSBIDI_RTL : NSBIDI_LTR,
  2926                                     presShell->GetPresContext(),
  2927                                     processor,
  2928                                     nsBidiPresUtils::MODE_DRAW,
  2929                                     nullptr,
  2930                                     0,
  2931                                     nullptr,
  2932                                     &bidiEngine);
  2935   mTarget->SetTransform(oldTransform);
  2937   if (aOp == CanvasRenderingContext2D::TextDrawOperation::FILL &&
  2938       !doDrawShadow) {
  2939     RedrawUser(boundingBox);
  2940     return NS_OK;
  2943   Redraw();
  2944   return NS_OK;
  2947 gfxFontGroup *CanvasRenderingContext2D::GetCurrentFontStyle()
  2949   // use lazy initilization for the font group since it's rather expensive
  2950   if (!CurrentState().fontGroup) {
  2951     ErrorResult err;
  2952     NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
  2953     static float kDefaultFontSize = 10.0;
  2954     SetFont(kDefaultFontStyle, err);
  2955     if (err.Failed()) {
  2956       gfxFontStyle style;
  2957       style.size = kDefaultFontSize;
  2958       CurrentState().fontGroup =
  2959         gfxPlatform::GetPlatform()->CreateFontGroup(NS_LITERAL_STRING("sans-serif"),
  2960                                                     &style,
  2961                                                     nullptr);
  2962       if (CurrentState().fontGroup) {
  2963         CurrentState().font = kDefaultFontStyle;
  2965         nsIPresShell* presShell = GetPresShell();
  2966         if (presShell) {
  2967           CurrentState().fontGroup->SetTextPerfMetrics(
  2968             presShell->GetPresContext()->GetTextPerfMetrics());
  2970       } else {
  2971         NS_ERROR("Default canvas font is invalid");
  2977   return CurrentState().fontGroup;
  2980 //
  2981 // line caps/joins
  2982 //
  2984 void
  2985 CanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
  2987   CapStyle cap;
  2989   if (capstyle.EqualsLiteral("butt")) {
  2990     cap = CapStyle::BUTT;
  2991   } else if (capstyle.EqualsLiteral("round")) {
  2992     cap = CapStyle::ROUND;
  2993   } else if (capstyle.EqualsLiteral("square")) {
  2994     cap = CapStyle::SQUARE;
  2995   } else {
  2996     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  2997     return;
  3000   CurrentState().lineCap = cap;
  3003 void
  3004 CanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
  3006   switch (CurrentState().lineCap) {
  3007   case CapStyle::BUTT:
  3008     capstyle.AssignLiteral("butt");
  3009     break;
  3010   case CapStyle::ROUND:
  3011     capstyle.AssignLiteral("round");
  3012     break;
  3013   case CapStyle::SQUARE:
  3014     capstyle.AssignLiteral("square");
  3015     break;
  3019 void
  3020 CanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
  3022   JoinStyle j;
  3024   if (joinstyle.EqualsLiteral("round")) {
  3025     j = JoinStyle::ROUND;
  3026   } else if (joinstyle.EqualsLiteral("bevel")) {
  3027     j = JoinStyle::BEVEL;
  3028   } else if (joinstyle.EqualsLiteral("miter")) {
  3029     j = JoinStyle::MITER_OR_BEVEL;
  3030   } else {
  3031     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  3032     return;
  3035   CurrentState().lineJoin = j;
  3038 void
  3039 CanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle, ErrorResult& error)
  3041   switch (CurrentState().lineJoin) {
  3042   case JoinStyle::ROUND:
  3043     joinstyle.AssignLiteral("round");
  3044     break;
  3045   case JoinStyle::BEVEL:
  3046     joinstyle.AssignLiteral("bevel");
  3047     break;
  3048   case JoinStyle::MITER_OR_BEVEL:
  3049     joinstyle.AssignLiteral("miter");
  3050     break;
  3051   default:
  3052     error.Throw(NS_ERROR_FAILURE);
  3056 void
  3057 CanvasRenderingContext2D::SetMozDash(JSContext* cx,
  3058                                      const JS::Value& mozDash,
  3059                                      ErrorResult& error)
  3061   FallibleTArray<Float> dash;
  3062   error = JSValToDashArray(cx, mozDash, dash);
  3063   if (!error.Failed()) {
  3064     ContextState& state = CurrentState();
  3065     state.dash = dash;
  3066     if (state.dash.IsEmpty()) {
  3067       state.dashOffset = 0;
  3072 void
  3073 CanvasRenderingContext2D::GetMozDash(JSContext* cx,
  3074                                      JS::MutableHandle<JS::Value> retval,
  3075                                      ErrorResult& error)
  3077   DashArrayToJSVal(CurrentState().dash, cx, retval, error);
  3080 void
  3081 CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
  3083   ContextState& state = CurrentState();
  3084   if (!state.dash.IsEmpty()) {
  3085     state.dashOffset = mozDashOffset;
  3089 void
  3090 CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments)
  3092   FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
  3093   dash.Clear();
  3095   for (uint32_t x = 0; x < aSegments.Length(); x++) {
  3096     dash.AppendElement(aSegments[x]);
  3098   if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
  3099     for (uint32_t x = 0; x < aSegments.Length(); x++) {
  3100       dash.AppendElement(aSegments[x]);
  3105 void
  3106 CanvasRenderingContext2D::GetLineDash(nsTArray<double>& aSegments) const {
  3107   const FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
  3108   aSegments.Clear();
  3110   for (uint32_t x = 0; x < dash.Length(); x++) {
  3111     aSegments.AppendElement(dash[x]);
  3115 void
  3116 CanvasRenderingContext2D::SetLineDashOffset(double mOffset) {
  3117   CurrentState().dashOffset = mOffset;
  3120 double
  3121 CanvasRenderingContext2D::LineDashOffset() const {
  3122   return CurrentState().dashOffset;
  3125 bool
  3126 CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, double x, double y, const CanvasWindingRule& winding)
  3128   if (!FloatValidate(x,y)) {
  3129     return false;
  3132   // Check for site-specific permission and return false if no permission.
  3133   if (mCanvasElement) {
  3134     nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  3135     if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  3136       return false;
  3139   EnsureUserSpacePath(winding);
  3140   if (!mPath) {
  3141     return false;
  3144   if (mPathTransformWillUpdate) {
  3145     return mPath->ContainsPoint(Point(x, y), mPathToDS);
  3148   return mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
  3151 bool CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, const CanvasPath& mPath, double x, double y, const CanvasWindingRule& mWinding)
  3153   if (!FloatValidate(x,y)) {
  3154     return false;
  3157   // Check for site-specific permission and return false if no permission.
  3158   if (mCanvasElement) {
  3159     nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  3160     if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  3161       return false;
  3164   EnsureTarget();
  3165   RefPtr<gfx::Path> tempPath = mPath.GetPath(mWinding, mTarget);
  3167   return tempPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
  3170 bool
  3171 CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, double x, double y)
  3173   if (!FloatValidate(x,y)) {
  3174     return false;
  3177   // Check for site-specific permission and return false if no permission.
  3178   if (mCanvasElement) {
  3179     nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  3180     if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  3181       return false;
  3184   EnsureUserSpacePath();
  3185   if (!mPath) {
  3186     return false;
  3189   const ContextState &state = CurrentState();
  3191   StrokeOptions strokeOptions(state.lineWidth,
  3192                               state.lineJoin,
  3193                               state.lineCap,
  3194                               state.miterLimit,
  3195                               state.dash.Length(),
  3196                               state.dash.Elements(),
  3197                               state.dashOffset);
  3199   if (mPathTransformWillUpdate) {
  3200     return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mPathToDS);
  3202   return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
  3205 bool CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, const CanvasPath& mPath, double x, double y)
  3207   if (!FloatValidate(x,y)) {
  3208     return false;
  3211   // Check for site-specific permission and return false if no permission.
  3212   if (mCanvasElement) {
  3213     nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  3214     if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  3215       return false;
  3218   EnsureTarget();
  3219   RefPtr<gfx::Path> tempPath = mPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
  3221   const ContextState &state = CurrentState();
  3223   StrokeOptions strokeOptions(state.lineWidth,
  3224                               state.lineJoin,
  3225                               state.lineCap,
  3226                               state.miterLimit,
  3227                               state.dash.Length(),
  3228                               state.dash.Elements(),
  3229                               state.dashOffset);
  3231   return tempPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
  3234 //
  3235 // image
  3236 //
  3238 // drawImage(in HTMLImageElement image, in float dx, in float dy);
  3239 //   -- render image from 0,0 at dx,dy top-left coords
  3240 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
  3241 //   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
  3242 // drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
  3243 //   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
  3245 // If only dx and dy are passed in then optional_argc should be 0. If only
  3246 // dx, dy, dw and dh are passed in then optional_argc should be 2. The only
  3247 // other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
  3248 // are all passed in.
  3250 void
  3251 CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image,
  3252                                     double sx, double sy, double sw,
  3253                                     double sh, double dx, double dy,
  3254                                     double dw, double dh,
  3255                                     uint8_t optional_argc,
  3256                                     ErrorResult& error)
  3258   MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6);
  3260   RefPtr<SourceSurface> srcSurf;
  3261   gfxIntSize imgSize;
  3263   Element* element;
  3265   EnsureTarget();
  3266   if (image.IsHTMLCanvasElement()) {
  3267     HTMLCanvasElement* canvas = &image.GetAsHTMLCanvasElement();
  3268     element = canvas;
  3269     nsIntSize size = canvas->GetSize();
  3270     if (size.width == 0 || size.height == 0) {
  3271       error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  3272       return;
  3274   } else {
  3275     if (image.IsHTMLImageElement()) {
  3276       HTMLImageElement* img = &image.GetAsHTMLImageElement();
  3277       element = img;
  3278     } else {
  3279       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
  3280       element = video;
  3283     srcSurf =
  3284       CanvasImageCache::Lookup(element, mCanvasElement, &imgSize);
  3287   nsLayoutUtils::DirectDrawInfo drawInfo;
  3289   if (!srcSurf) {
  3290     // The canvas spec says that drawImage should draw the first frame
  3291     // of animated images. We also don't want to rasterize vector images.
  3292     uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME |
  3293                         nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS;
  3294     nsLayoutUtils::SurfaceFromElementResult res =
  3295       nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
  3297     if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
  3298       // Spec says to silently do nothing if the element is still loading.
  3299       if (!res.mIsStillLoading) {
  3300         error.Throw(NS_ERROR_NOT_AVAILABLE);
  3302       return;
  3305     imgSize = res.mSize;
  3307     // Scale sw/sh based on aspect ratio
  3308     if (image.IsHTMLVideoElement()) {
  3309       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
  3310       int32_t displayWidth = video->VideoWidth();
  3311       int32_t displayHeight = video->VideoHeight();
  3312       sw *= (double)imgSize.width / (double)displayWidth;
  3313       sh *= (double)imgSize.height / (double)displayHeight;
  3316     if (mCanvasElement) {
  3317       CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
  3318                                             res.mPrincipal, res.mIsWriteOnly,
  3319                                             res.mCORSUsed);
  3322     if (res.mSourceSurface) {
  3323       if (res.mImageRequest) {
  3324         CanvasImageCache::NotifyDrawImage(element, mCanvasElement, res.mImageRequest,
  3325                                           res.mSourceSurface, imgSize);
  3328       srcSurf = res.mSourceSurface;
  3329     } else {
  3330       drawInfo = res.mDrawInfo;
  3334   if (optional_argc == 0) {
  3335     sx = sy = 0.0;
  3336     dw = sw = (double) imgSize.width;
  3337     dh = sh = (double) imgSize.height;
  3338   } else if (optional_argc == 2) {
  3339     sx = sy = 0.0;
  3340     sw = (double) imgSize.width;
  3341     sh = (double) imgSize.height;
  3344   if (sw == 0.0 || sh == 0.0) {
  3345     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  3346     return;
  3349   if (dw == 0.0 || dh == 0.0) {
  3350     // not really failure, but nothing to do --
  3351     // and noone likes a divide-by-zero
  3352     return;
  3355   if (sx < 0.0 || sy < 0.0 ||
  3356       sw < 0.0 || sw > (double) imgSize.width ||
  3357       sh < 0.0 || sh > (double) imgSize.height ||
  3358       dw < 0.0 || dh < 0.0) {
  3359     // XXX - Unresolved spec issues here, for now return error.
  3360     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  3361     return;
  3364   Filter filter;
  3366   if (CurrentState().imageSmoothingEnabled)
  3367     filter = mgfx::Filter::LINEAR;
  3368   else
  3369     filter = mgfx::Filter::POINT;
  3371   mgfx::Rect bounds;
  3373   if (NeedToDrawShadow()) {
  3374     bounds = mgfx::Rect(dx, dy, dw, dh);
  3375     bounds = mTarget->GetTransform().TransformBounds(bounds);
  3378   if (srcSurf) {
  3379     AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  3380       DrawSurface(srcSurf,
  3381                   mgfx::Rect(dx, dy, dw, dh),
  3382                   mgfx::Rect(sx, sy, sw, sh),
  3383                   DrawSurfaceOptions(filter),
  3384                   DrawOptions(CurrentState().globalAlpha, UsedOperation()));
  3385   } else {
  3386     DrawDirectlyToCanvas(drawInfo, &bounds, dx, dy, dw, dh,
  3387                          sx, sy, sw, sh, imgSize);
  3390   RedrawUser(gfxRect(dx, dy, dw, dh));
  3393 void
  3394 CanvasRenderingContext2D::DrawDirectlyToCanvas(
  3395                           const nsLayoutUtils::DirectDrawInfo& image,
  3396                           mgfx::Rect* bounds, double dx, double dy,
  3397                           double dw, double dh, double sx, double sy,
  3398                           double sw, double sh, gfxIntSize imgSize)
  3400   gfxMatrix contextMatrix;
  3402   AdjustedTarget tempTarget(this, bounds->IsEmpty() ? nullptr: bounds);
  3404   // get any already existing transforms on the context. Include transformations used for context shadow
  3405   if (tempTarget) {
  3406     Matrix matrix = tempTarget->GetTransform();
  3407     contextMatrix = gfxMatrix(matrix._11, matrix._12, matrix._21,
  3408                               matrix._22, matrix._31, matrix._32);
  3411   gfxMatrix transformMatrix;
  3412   transformMatrix.Translate(gfxPoint(sx, sy));
  3413   if (dw > 0 && dh > 0) {
  3414     transformMatrix.Scale(sw/dw, sh/dh);
  3416   transformMatrix.Translate(gfxPoint(-dx, -dy));
  3418   nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
  3419   context->SetMatrix(contextMatrix);
  3421   // FLAG_CLAMP is added for increased performance
  3422   uint32_t modifiedFlags = image.mDrawingFlags | imgIContainer::FLAG_CLAMP;
  3424   nsresult rv = image.mImgContainer->
  3425     Draw(context, GraphicsFilter::FILTER_GOOD, transformMatrix,
  3426          gfxRect(gfxPoint(dx, dy), gfxIntSize(dw, dh)),
  3427          nsIntRect(nsIntPoint(0, 0), gfxIntSize(imgSize.width, imgSize.height)),
  3428          gfxIntSize(imgSize.width, imgSize.height), nullptr, image.mWhichFrame,
  3429          modifiedFlags);
  3431   NS_ENSURE_SUCCESS_VOID(rv);
  3434 static bool
  3435 IsStandardCompositeOp(CompositionOp op)
  3437     return (op == CompositionOp::OP_SOURCE ||
  3438             op == CompositionOp::OP_ATOP ||
  3439             op == CompositionOp::OP_IN ||
  3440             op == CompositionOp::OP_OUT ||
  3441             op == CompositionOp::OP_OVER ||
  3442             op == CompositionOp::OP_DEST_IN ||
  3443             op == CompositionOp::OP_DEST_OUT ||
  3444             op == CompositionOp::OP_DEST_OVER ||
  3445             op == CompositionOp::OP_DEST_ATOP ||
  3446             op == CompositionOp::OP_ADD ||
  3447             op == CompositionOp::OP_XOR);
  3450 void
  3451 CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op,
  3452                                                       ErrorResult& error)
  3454   CompositionOp comp_op;
  3456 #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
  3457   if (op.EqualsLiteral(cvsop))   \
  3458     comp_op = CompositionOp::OP_##op2d;
  3460   CANVAS_OP_TO_GFX_OP("copy", SOURCE)
  3461   else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
  3462   else CANVAS_OP_TO_GFX_OP("source-in", IN)
  3463   else CANVAS_OP_TO_GFX_OP("source-out", OUT)
  3464   else CANVAS_OP_TO_GFX_OP("source-over", OVER)
  3465   else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
  3466   else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
  3467   else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
  3468   else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
  3469   else CANVAS_OP_TO_GFX_OP("lighter", ADD)
  3470   else CANVAS_OP_TO_GFX_OP("xor", XOR)
  3471   else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
  3472   else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
  3473   else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
  3474   else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
  3475   else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
  3476   else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
  3477   else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
  3478   else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
  3479   else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
  3480   else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
  3481   else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
  3482   else CANVAS_OP_TO_GFX_OP("hue", HUE)
  3483   else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
  3484   else CANVAS_OP_TO_GFX_OP("color", COLOR)
  3485   else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
  3486   // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  3487   else return;
  3489   if (!IsStandardCompositeOp(comp_op)) {
  3490     Demote();
  3493 #undef CANVAS_OP_TO_GFX_OP
  3494   CurrentState().op = comp_op;
  3497 void
  3498 CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op,
  3499                                                       ErrorResult& error)
  3501   CompositionOp comp_op = CurrentState().op;
  3503 #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
  3504   if (comp_op == CompositionOp::OP_##op2d) \
  3505     op.AssignLiteral(cvsop);
  3507   CANVAS_OP_TO_GFX_OP("copy", SOURCE)
  3508   else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
  3509   else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
  3510   else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
  3511   else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
  3512   else CANVAS_OP_TO_GFX_OP("lighter", ADD)
  3513   else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
  3514   else CANVAS_OP_TO_GFX_OP("source-in", IN)
  3515   else CANVAS_OP_TO_GFX_OP("source-out", OUT)
  3516   else CANVAS_OP_TO_GFX_OP("source-over", OVER)
  3517   else CANVAS_OP_TO_GFX_OP("xor", XOR)
  3518   else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
  3519   else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
  3520   else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
  3521   else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
  3522   else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
  3523   else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
  3524   else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
  3525   else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
  3526   else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
  3527   else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
  3528   else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
  3529   else CANVAS_OP_TO_GFX_OP("hue", HUE)
  3530   else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
  3531   else CANVAS_OP_TO_GFX_OP("color", COLOR)
  3532   else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
  3533   else {
  3534     error.Throw(NS_ERROR_FAILURE);
  3537   if (!IsStandardCompositeOp(comp_op)) {
  3538     Demote();
  3541 #undef CANVAS_OP_TO_GFX_OP
  3544 void
  3545 CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
  3546                                      double y, double w, double h,
  3547                                      const nsAString& bgColor,
  3548                                      uint32_t flags, ErrorResult& error)
  3550   // protect against too-large surfaces that will cause allocation
  3551   // or overflow issues
  3552   if (!gfxASurface::CheckSurfaceSize(gfxIntSize(int32_t(w), int32_t(h)),
  3553                                      0xffff)) {
  3554     error.Throw(NS_ERROR_FAILURE);
  3555     return;
  3558   EnsureTarget();
  3559   // We can't allow web apps to call this until we fix at least the
  3560   // following potential security issues:
  3561   // -- rendering cross-domain IFRAMEs and then extracting the results
  3562   // -- rendering the user's theme and then extracting the results
  3563   // -- rendering native anonymous content (e.g., file input paths;
  3564   // scrollbars should be allowed)
  3565   if (!nsContentUtils::IsCallerChrome()) {
  3566     // not permitted to use DrawWindow
  3567     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  3568     error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  3569     return;
  3572   // Flush layout updates
  3573   if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
  3574     nsContentUtils::FlushLayoutForTree(&window);
  3577   nsRefPtr<nsPresContext> presContext;
  3578   nsIDocShell* docshell = window.GetDocShell();
  3579   if (docshell) {
  3580     docshell->GetPresContext(getter_AddRefs(presContext));
  3582   if (!presContext) {
  3583     error.Throw(NS_ERROR_FAILURE);
  3584     return;
  3587   nscolor backgroundColor;
  3588   if (!ParseColor(bgColor, &backgroundColor)) {
  3589     error.Throw(NS_ERROR_FAILURE);
  3590     return;
  3593   nsRect r(nsPresContext::CSSPixelsToAppUnits((float)x),
  3594            nsPresContext::CSSPixelsToAppUnits((float)y),
  3595            nsPresContext::CSSPixelsToAppUnits((float)w),
  3596            nsPresContext::CSSPixelsToAppUnits((float)h));
  3597   uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
  3598                              nsIPresShell::RENDER_DOCUMENT_RELATIVE);
  3599   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
  3600     renderDocFlags |= nsIPresShell::RENDER_CARET;
  3602   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
  3603     renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
  3604                         nsIPresShell::RENDER_DOCUMENT_RELATIVE);
  3606   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
  3607     renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
  3609   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
  3610     renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
  3612   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) {
  3613     renderDocFlags |= nsIPresShell::RENDER_DRAWWINDOW_NOT_FLUSHING;
  3616   // gfxContext-over-Azure may modify the DrawTarget's transform, so
  3617   // save and restore it
  3618   Matrix matrix = mTarget->GetTransform();
  3619   double sw = matrix._11 * w;
  3620   double sh = matrix._22 * h;
  3621   if (!sw || !sh) {
  3622     return;
  3624   nsRefPtr<gfxContext> thebes;
  3625   RefPtr<DrawTarget> drawDT;
  3626   if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget)) {
  3627     thebes = new gfxContext(mTarget);
  3628     thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
  3629                                 matrix._22, matrix._31, matrix._32));
  3630   } else {
  3631     drawDT =
  3632       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
  3633                                                                    SurfaceFormat::B8G8R8A8);
  3634     if (!drawDT) {
  3635       error.Throw(NS_ERROR_FAILURE);
  3636       return;
  3639     thebes = new gfxContext(drawDT);
  3640     thebes->Scale(matrix._11, matrix._22);
  3643   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
  3644   unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
  3645   if (drawDT) {
  3646     RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
  3647     RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
  3649     RefPtr<SourceSurface> source =
  3650       mTarget->CreateSourceSurfaceFromData(data->GetData(),
  3651                                            data->GetSize(),
  3652                                            data->Stride(),
  3653                                            data->GetFormat());
  3655     if (!source) {
  3656       error.Throw(NS_ERROR_FAILURE);
  3657       return;
  3660     mgfx::Rect destRect(0, 0, w, h);
  3661     mgfx::Rect sourceRect(0, 0, sw, sh);
  3662     mTarget->DrawSurface(source, destRect, sourceRect,
  3663                          DrawSurfaceOptions(mgfx::Filter::POINT),
  3664                          DrawOptions(1.0f, CompositionOp::OP_OVER,
  3665                                      AntialiasMode::NONE));
  3666     mTarget->Flush();
  3667   } else {
  3668     mTarget->SetTransform(matrix);
  3671   // note that x and y are coordinates in the document that
  3672   // we're drawing; x and y are drawn to 0,0 in current user
  3673   // space.
  3674   RedrawUser(gfxRect(0, 0, w, h));
  3677 void
  3678 CanvasRenderingContext2D::AsyncDrawXULElement(nsXULElement& elem,
  3679                                               double x, double y,
  3680                                               double w, double h,
  3681                                               const nsAString& bgColor,
  3682                                               uint32_t flags,
  3683                                               ErrorResult& error)
  3685   // We can't allow web apps to call this until we fix at least the
  3686   // following potential security issues:
  3687   // -- rendering cross-domain IFRAMEs and then extracting the results
  3688   // -- rendering the user's theme and then extracting the results
  3689   // -- rendering native anonymous content (e.g., file input paths;
  3690   // scrollbars should be allowed)
  3691   if (!nsContentUtils::IsCallerChrome()) {
  3692     // not permitted to use DrawWindow
  3693     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  3694     error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  3695     return;
  3698 #if 0
  3699   nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(&elem);
  3700   if (!loaderOwner) {
  3701     error.Throw(NS_ERROR_FAILURE);
  3702     return;
  3705   nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
  3706   if (!frameloader) {
  3707     error.Throw(NS_ERROR_FAILURE);
  3708     return;
  3711   PBrowserParent *child = frameloader->GetRemoteBrowser();
  3712   if (!child) {
  3713     nsCOMPtr<nsIDOMWindow> window =
  3714       do_GetInterface(frameloader->GetExistingDocShell());
  3715     if (!window) {
  3716       error.Throw(NS_ERROR_FAILURE);
  3717       return;
  3720     return DrawWindow(window, x, y, w, h, bgColor, flags);
  3723   // protect against too-large surfaces that will cause allocation
  3724   // or overflow issues
  3725   if (!gfxASurface::CheckSurfaceSize(gfxIntSize(w, h), 0xffff)) {
  3726     error.Throw(NS_ERROR_FAILURE);
  3727     return;
  3730   bool flush =
  3731     (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
  3733   uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
  3734   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
  3735     renderDocFlags |= nsIPresShell::RENDER_CARET;
  3737   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
  3738     renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
  3741   nsRect rect(nsPresContext::CSSPixelsToAppUnits(x),
  3742               nsPresContext::CSSPixelsToAppUnits(y),
  3743               nsPresContext::CSSPixelsToAppUnits(w),
  3744               nsPresContext::CSSPixelsToAppUnits(h));
  3745   if (mIPC) {
  3746     PDocumentRendererParent *pdocrender =
  3747       child->SendPDocumentRendererConstructor(rect,
  3748                                               mThebes->CurrentMatrix(),
  3749                                               nsString(aBGColor),
  3750                                               renderDocFlags, flush,
  3751                                               nsIntSize(mWidth, mHeight));
  3752     if (!pdocrender)
  3753       return NS_ERROR_FAILURE;
  3755     DocumentRendererParent *docrender =
  3756       static_cast<DocumentRendererParent *>(pdocrender);
  3758     docrender->SetCanvasContext(this, mThebes);
  3760 #endif
  3763 //
  3764 // device pixel getting/setting
  3765 //
  3767 already_AddRefed<ImageData>
  3768 CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
  3769                                        double aSy, double aSw,
  3770                                        double aSh, ErrorResult& error)
  3772   EnsureTarget();
  3773   if (!IsTargetValid()) {
  3774     error.Throw(NS_ERROR_FAILURE);
  3775     return nullptr;
  3778   if (!mCanvasElement && !mDocShell) {
  3779     NS_ERROR("No canvas element and no docshell in GetImageData!!!");
  3780     error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  3781     return nullptr;
  3784   // Check only if we have a canvas element; if we were created with a docshell,
  3785   // then it's special internal use.
  3786   if (mCanvasElement && mCanvasElement->IsWriteOnly() &&
  3787       !nsContentUtils::IsCallerChrome())
  3789     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  3790     error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  3791     return nullptr;
  3794   if (!NS_finite(aSx) || !NS_finite(aSy) ||
  3795       !NS_finite(aSw) || !NS_finite(aSh)) {
  3796     error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  3797     return nullptr;
  3800   if (!aSw || !aSh) {
  3801     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  3802     return nullptr;
  3805   int32_t x = JS_DoubleToInt32(aSx);
  3806   int32_t y = JS_DoubleToInt32(aSy);
  3807   int32_t wi = JS_DoubleToInt32(aSw);
  3808   int32_t hi = JS_DoubleToInt32(aSh);
  3810   // Handle negative width and height by flipping the rectangle over in the
  3811   // relevant direction.
  3812   uint32_t w, h;
  3813   if (aSw < 0) {
  3814     w = -wi;
  3815     x -= w;
  3816   } else {
  3817     w = wi;
  3819   if (aSh < 0) {
  3820     h = -hi;
  3821     y -= h;
  3822   } else {
  3823     h = hi;
  3826   if (w == 0) {
  3827     w = 1;
  3829   if (h == 0) {
  3830     h = 1;
  3833   JS::Rooted<JSObject*> array(aCx);
  3834   error = GetImageDataArray(aCx, x, y, w, h, array.address());
  3835   if (error.Failed()) {
  3836     return nullptr;
  3838   MOZ_ASSERT(array);
  3840   nsRefPtr<ImageData> imageData = new ImageData(w, h, *array);
  3841   return imageData.forget();
  3844 nsresult
  3845 CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
  3846                                             int32_t aX,
  3847                                             int32_t aY,
  3848                                             uint32_t aWidth,
  3849                                             uint32_t aHeight,
  3850                                             JSObject** aRetval)
  3852   MOZ_ASSERT(aWidth && aHeight);
  3854   CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
  3855   if (!len.isValid()) {
  3856     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  3859   CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
  3860   CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
  3862   if (!rightMost.isValid() || !bottomMost.isValid()) {
  3863     return NS_ERROR_DOM_SYNTAX_ERR;
  3866   IntRect srcRect(0, 0, mWidth, mHeight);
  3867   IntRect destRect(aX, aY, aWidth, aHeight);
  3868   IntRect srcReadRect = srcRect.Intersect(destRect);
  3869   RefPtr<DataSourceSurface> readback;
  3870   if (!srcReadRect.IsEmpty() && !mZero) {
  3871     RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
  3872     if (snapshot) {
  3873       readback = snapshot->GetDataSurface();
  3875     if (!readback || !readback->GetData()) {
  3876       return NS_ERROR_OUT_OF_MEMORY;
  3880   JS::Rooted<JSObject*> darray(aCx, JS_NewUint8ClampedArray(aCx, len.value()));
  3881   if (!darray) {
  3882     return NS_ERROR_OUT_OF_MEMORY;
  3885   if (mZero) {
  3886     *aRetval = darray;
  3887     return NS_OK;
  3890   uint8_t* data = JS_GetUint8ClampedArrayData(darray);
  3892   // Check for site-specific permission and return all-white, opaque pixel
  3893   // data if no permission.  This check is not needed if the canvas was
  3894   // created with a docshell (that is only done for special internal uses).
  3895   bool usePlaceholder = false;
  3896   if (mCanvasElement) {
  3897     nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  3898     usePlaceholder = !ownerDoc ||
  3899       !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx);
  3902   if (usePlaceholder) {
  3903     memset(data, 0xFF, len.value());
  3904     *aRetval = darray;
  3905     return NS_OK;
  3908   IntRect dstWriteRect = srcReadRect;
  3909   dstWriteRect.MoveBy(-aX, -aY);
  3911   uint8_t* src = data;
  3912   uint32_t srcStride = aWidth * 4;
  3913   if (readback) {
  3914     srcStride = readback->Stride();
  3915     src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
  3918   // NOTE! dst is the same as src, and this relies on reading
  3919   // from src and advancing that ptr before writing to dst.
  3920   // NOTE! I'm not sure that it is, I think this comment might have been
  3921   // inherited from Thebes canvas and is no longer true
  3922   uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
  3924   if (mOpaque) {
  3925     for (int32_t j = 0; j < dstWriteRect.height; ++j) {
  3926       for (int32_t i = 0; i < dstWriteRect.width; ++i) {
  3927         // XXX Is there some useful swizzle MMX we can use here?
  3928 #if MOZ_LITTLE_ENDIAN
  3929         uint8_t b = *src++;
  3930         uint8_t g = *src++;
  3931         uint8_t r = *src++;
  3932         src++;
  3933 #else
  3934         src++;
  3935         uint8_t r = *src++;
  3936         uint8_t g = *src++;
  3937         uint8_t b = *src++;
  3938 #endif
  3939         *dst++ = r;
  3940         *dst++ = g;
  3941         *dst++ = b;
  3942         *dst++ = 255;
  3944       src += srcStride - (dstWriteRect.width * 4);
  3945       dst += (aWidth * 4) - (dstWriteRect.width * 4);
  3947   } else
  3948   for (int32_t j = 0; j < dstWriteRect.height; ++j) {
  3949     for (int32_t i = 0; i < dstWriteRect.width; ++i) {
  3950       // XXX Is there some useful swizzle MMX we can use here?
  3951 #if MOZ_LITTLE_ENDIAN
  3952       uint8_t b = *src++;
  3953       uint8_t g = *src++;
  3954       uint8_t r = *src++;
  3955       uint8_t a = *src++;
  3956 #else
  3957       uint8_t a = *src++;
  3958       uint8_t r = *src++;
  3959       uint8_t g = *src++;
  3960       uint8_t b = *src++;
  3961 #endif
  3962       // Convert to non-premultiplied color
  3963       *dst++ = gfxUtils::sUnpremultiplyTable[a * 256 + r];
  3964       *dst++ = gfxUtils::sUnpremultiplyTable[a * 256 + g];
  3965       *dst++ = gfxUtils::sUnpremultiplyTable[a * 256 + b];
  3966       *dst++ = a;
  3968     src += srcStride - (dstWriteRect.width * 4);
  3969     dst += (aWidth * 4) - (dstWriteRect.width * 4);
  3972   *aRetval = darray;
  3973   return NS_OK;
  3976 void
  3977 CanvasRenderingContext2D::EnsureErrorTarget()
  3979   if (sErrorTarget) {
  3980     return;
  3983   RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
  3984   MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
  3986   sErrorTarget = errorTarget;
  3987   NS_ADDREF(sErrorTarget);
  3990 void
  3991 CanvasRenderingContext2D::FillRuleChanged()
  3993   if (mPath) {
  3994     mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
  3995     mPath = nullptr;
  3999 void
  4000 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
  4001                                        double dy, ErrorResult& error)
  4003   dom::Uint8ClampedArray arr(imageData.GetDataObject());
  4005   error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
  4006                                 imageData.Width(), imageData.Height(),
  4007                                 &arr, false, 0, 0, 0, 0);
  4010 void
  4011 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
  4012                                        double dy, double dirtyX,
  4013                                        double dirtyY, double dirtyWidth,
  4014                                        double dirtyHeight,
  4015                                        ErrorResult& error)
  4017   dom::Uint8ClampedArray arr(imageData.GetDataObject());
  4019   error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
  4020                                 imageData.Width(), imageData.Height(),
  4021                                 &arr, true,
  4022                                 JS_DoubleToInt32(dirtyX),
  4023                                 JS_DoubleToInt32(dirtyY),
  4024                                 JS_DoubleToInt32(dirtyWidth),
  4025                                 JS_DoubleToInt32(dirtyHeight));
  4028 // void putImageData (in ImageData d, in float x, in float y);
  4029 // void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
  4031 nsresult
  4032 CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
  4033                                                 dom::Uint8ClampedArray* aArray,
  4034                                                 bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
  4035                                                 int32_t dirtyWidth, int32_t dirtyHeight)
  4037   if (w == 0 || h == 0) {
  4038     return NS_ERROR_DOM_SYNTAX_ERR;
  4041   IntRect dirtyRect;
  4042   IntRect imageDataRect(0, 0, w, h);
  4044   if (hasDirtyRect) {
  4045     // fix up negative dimensions
  4046     if (dirtyWidth < 0) {
  4047       NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
  4049       CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
  4051       if (!checkedDirtyX.isValid())
  4052         return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4054       dirtyX = checkedDirtyX.value();
  4055       dirtyWidth = -dirtyWidth;
  4058     if (dirtyHeight < 0) {
  4059       NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
  4061       CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
  4063       if (!checkedDirtyY.isValid())
  4064         return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4066       dirtyY = checkedDirtyY.value();
  4067       dirtyHeight = -dirtyHeight;
  4070     // bound the dirty rect within the imageData rectangle
  4071     dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
  4073     if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
  4074       return NS_OK;
  4075   } else {
  4076     dirtyRect = imageDataRect;
  4079   dirtyRect.MoveBy(IntPoint(x, y));
  4080   dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
  4082   if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
  4083     return NS_OK;
  4086   aArray->ComputeLengthAndData();
  4088   uint32_t dataLen = aArray->Length();
  4090   uint32_t len = w * h * 4;
  4091   if (dataLen != len) {
  4092     return NS_ERROR_DOM_SYNTAX_ERR;
  4095   nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
  4096                                                           gfxImageFormat::ARGB32,
  4097                                                           false);
  4098   if (!imgsurf || imgsurf->CairoStatus()) {
  4099     return NS_ERROR_FAILURE;
  4102   uint8_t *src = aArray->Data();
  4103   uint8_t *dst = imgsurf->Data();
  4105   for (uint32_t j = 0; j < h; j++) {
  4106     for (uint32_t i = 0; i < w; i++) {
  4107       uint8_t r = *src++;
  4108       uint8_t g = *src++;
  4109       uint8_t b = *src++;
  4110       uint8_t a = *src++;
  4112       // Convert to premultiplied color (losslessly if the input came from getImageData)
  4113 #if MOZ_LITTLE_ENDIAN
  4114       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
  4115       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
  4116       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
  4117       *dst++ = a;
  4118 #else
  4119       *dst++ = a;
  4120       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
  4121       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
  4122       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
  4123 #endif
  4127   EnsureTarget();
  4128   if (!IsTargetValid()) {
  4129     return NS_ERROR_FAILURE;
  4132   RefPtr<SourceSurface> sourceSurface =
  4133     mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), SurfaceFormat::B8G8R8A8);
  4135   // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
  4136   // covers the details of how to run into it, but the full detailed
  4137   // investigation hasn't been done to determine the underlying cause.  We
  4138   // will just handle the failure to allocate the surface to avoid a crash.
  4139   if (!sourceSurface) {
  4140     return NS_ERROR_FAILURE;
  4143   mTarget->CopySurface(sourceSurface,
  4144                        IntRect(dirtyRect.x - x, dirtyRect.y - y,
  4145                                dirtyRect.width, dirtyRect.height),
  4146                        IntPoint(dirtyRect.x, dirtyRect.y));
  4148   Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
  4150   return NS_OK;
  4153 static already_AddRefed<ImageData>
  4154 CreateImageData(JSContext* cx, CanvasRenderingContext2D* context,
  4155                 uint32_t w, uint32_t h, ErrorResult& error)
  4157   if (w == 0)
  4158       w = 1;
  4159   if (h == 0)
  4160       h = 1;
  4162   CheckedInt<uint32_t> len = CheckedInt<uint32_t>(w) * h * 4;
  4163   if (!len.isValid()) {
  4164     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  4165     return nullptr;
  4168   // Create the fast typed array; it's initialized to 0 by default.
  4169   JSObject* darray = Uint8ClampedArray::Create(cx, context, len.value());
  4170   if (!darray) {
  4171     error.Throw(NS_ERROR_OUT_OF_MEMORY);
  4172     return nullptr;
  4175   nsRefPtr<mozilla::dom::ImageData> imageData =
  4176     new mozilla::dom::ImageData(w, h, *darray);
  4177   return imageData.forget();
  4180 already_AddRefed<ImageData>
  4181 CanvasRenderingContext2D::CreateImageData(JSContext* cx, double sw,
  4182                                           double sh, ErrorResult& error)
  4184   if (!sw || !sh) {
  4185     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  4186     return nullptr;
  4189   int32_t wi = JS_DoubleToInt32(sw);
  4190   int32_t hi = JS_DoubleToInt32(sh);
  4192   uint32_t w = Abs(wi);
  4193   uint32_t h = Abs(hi);
  4194   return mozilla::dom::CreateImageData(cx, this, w, h, error);
  4197 already_AddRefed<ImageData>
  4198 CanvasRenderingContext2D::CreateImageData(JSContext* cx,
  4199                                           ImageData& imagedata,
  4200                                           ErrorResult& error)
  4202   return mozilla::dom::CreateImageData(cx, this, imagedata.Width(),
  4203                                        imagedata.Height(), error);
  4206 static uint8_t g2DContextLayerUserData;
  4208 already_AddRefed<CanvasLayer>
  4209 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
  4210                                          CanvasLayer *aOldLayer,
  4211                                          LayerManager *aManager)
  4213   // Don't call EnsureTarget() ... if there isn't already a surface, then
  4214   // we have nothing to paint and there is no need to create a surface just
  4215   // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
  4216   // layer manager which must NOT happen during a paint.
  4217   if (!mTarget || !IsTargetValid()) {
  4218     // No DidTransactionCallback will be received, so mark the context clean
  4219     // now so future invalidations will be dispatched.
  4220     MarkContextClean();
  4221     return nullptr;
  4224   mTarget->Flush();
  4226   if (!mResetLayer && aOldLayer) {
  4227     CanvasRenderingContext2DUserData* userData =
  4228       static_cast<CanvasRenderingContext2DUserData*>(
  4229         aOldLayer->GetUserData(&g2DContextLayerUserData));
  4231     CanvasLayer::Data data;
  4232     if (mStream) {
  4233 #ifdef USE_SKIA
  4234       SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
  4236       if (glue) {
  4237         data.mGLContext = glue->GetGLContext();
  4238         data.mStream = mStream.get();
  4240 #endif
  4241     } else {
  4242       data.mDrawTarget = mTarget;
  4245     if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
  4246       nsRefPtr<CanvasLayer> ret = aOldLayer;
  4247       return ret.forget();
  4251   nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
  4252   if (!canvasLayer) {
  4253     NS_WARNING("CreateCanvasLayer returned null!");
  4254     // No DidTransactionCallback will be received, so mark the context clean
  4255     // now so future invalidations will be dispatched.
  4256     MarkContextClean();
  4257     return nullptr;
  4259   CanvasRenderingContext2DUserData *userData = nullptr;
  4260   // Make the layer tell us whenever a transaction finishes (including
  4261   // the current transaction), so we can clear our invalidation state and
  4262   // start invalidating again. We need to do this for all layers since
  4263   // callers of DrawWindow may be expecting to receive normal invalidation
  4264   // notifications after this paint.
  4266   // The layer will be destroyed when we tear down the presentation
  4267   // (at the latest), at which time this userData will be destroyed,
  4268   // releasing the reference to the element.
  4269   // The userData will receive DidTransactionCallbacks, which flush the
  4270   // the invalidation state to indicate that the canvas is up to date.
  4271   userData = new CanvasRenderingContext2DUserData(this);
  4272   canvasLayer->SetDidTransactionCallback(
  4273           CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
  4274   canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
  4276   CanvasLayer::Data data;
  4277   if (mStream) {
  4278     SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
  4280     if (glue) {
  4281       canvasLayer->SetPreTransactionCallback(
  4282               CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
  4283 #if USE_SKIA
  4284       data.mGLContext = glue->GetGLContext();
  4285 #endif
  4286       data.mStream = mStream.get();
  4287       data.mTexID = (uint32_t)((uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE));
  4289   } else {
  4290     data.mDrawTarget = mTarget;
  4293   data.mSize = nsIntSize(mWidth, mHeight);
  4295   canvasLayer->Initialize(data);
  4296   uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
  4297   canvasLayer->SetContentFlags(flags);
  4298   canvasLayer->Updated();
  4300   mResetLayer = false;
  4302   return canvasLayer.forget();
  4305 void
  4306 CanvasRenderingContext2D::MarkContextClean()
  4308   if (mInvalidateCount > 0) {
  4309     mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
  4311   mIsEntireFrameInvalid = false;
  4312   mInvalidateCount = 0;
  4316 bool
  4317 CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager)
  4319   return !aManager->CanUseCanvasLayerForSize(IntSize(mWidth, mHeight));
  4322 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPath, AddRef)
  4323 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath, Release)
  4325 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasPath, mParent)
  4327 CanvasPath::CanvasPath(nsISupports* aParent)
  4328   : mParent(aParent)
  4330   SetIsDOMBinding();
  4332   mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
  4335 CanvasPath::CanvasPath(nsISupports* aParent, RefPtr<PathBuilder> aPathBuilder)
  4336   : mParent(aParent), mPathBuilder(aPathBuilder)
  4338   SetIsDOMBinding();
  4340   if (!mPathBuilder) {
  4341     mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
  4345 JSObject*
  4346 CanvasPath::WrapObject(JSContext* aCx)
  4348   return Path2DBinding::Wrap(aCx, this);
  4351 already_AddRefed<CanvasPath>
  4352 CanvasPath::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
  4354   nsRefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports());
  4355   return path.forget();
  4358 already_AddRefed<CanvasPath>
  4359 CanvasPath::Constructor(const GlobalObject& aGlobal, CanvasPath& aCanvasPath, ErrorResult& aRv)
  4361   RefPtr<gfx::Path> tempPath = aCanvasPath.GetPath(CanvasWindingRule::Nonzero,
  4362                                                    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
  4364   nsRefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
  4365   return path.forget();
  4368 already_AddRefed<CanvasPath>
  4369 CanvasPath::Constructor(const GlobalObject& aGlobal, const nsAString& aPathString, ErrorResult& aRv)
  4371   RefPtr<gfx::Path> tempPath = SVGContentUtils::GetPath(aPathString);
  4372   if (!tempPath) {
  4373     return Constructor(aGlobal, aRv);
  4376   nsRefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
  4377   return path.forget();
  4380 void
  4381 CanvasPath::ClosePath()
  4383   EnsurePathBuilder();
  4385   mPathBuilder->Close();
  4388 void
  4389 CanvasPath::MoveTo(double x, double y)
  4391   EnsurePathBuilder();
  4393   mPathBuilder->MoveTo(Point(ToFloat(x), ToFloat(y)));
  4396 void
  4397 CanvasPath::LineTo(double x, double y)
  4399   EnsurePathBuilder();
  4401   mPathBuilder->LineTo(Point(ToFloat(x), ToFloat(y)));
  4404 void
  4405 CanvasPath::QuadraticCurveTo(double cpx, double cpy, double x, double y)
  4407   EnsurePathBuilder();
  4409   mPathBuilder->QuadraticBezierTo(gfx::Point(ToFloat(cpx), ToFloat(cpy)),
  4410                                   gfx::Point(ToFloat(x), ToFloat(y)));
  4413 void
  4414 CanvasPath::BezierCurveTo(double cp1x, double cp1y,
  4415                           double cp2x, double cp2y,
  4416                           double x, double y)
  4418   BezierTo(gfx::Point(ToFloat(cp1x), ToFloat(cp1y)),
  4419              gfx::Point(ToFloat(cp2x), ToFloat(cp2y)),
  4420              gfx::Point(ToFloat(x), ToFloat(y)));
  4423 void
  4424 CanvasPath::ArcTo(double x1, double y1, double x2, double y2, double radius,
  4425                   ErrorResult& error)
  4427   if (radius < 0) {
  4428     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  4429     return;
  4432   // Current point in user space!
  4433   Point p0 = mPathBuilder->CurrentPoint();
  4434   Point p1(x1, y1);
  4435   Point p2(x2, y2);
  4437   // Execute these calculations in double precision to avoid cumulative
  4438   // rounding errors.
  4439   double dir, a2, b2, c2, cosx, sinx, d, anx, any,
  4440          bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
  4441   bool anticlockwise;
  4443   if (p0 == p1 || p1 == p2 || radius == 0) {
  4444     LineTo(p1.x, p1.y);
  4445     return;
  4448   // Check for colinearity
  4449   dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
  4450   if (dir == 0) {
  4451     LineTo(p1.x, p1.y);
  4452     return;
  4456   // XXX - Math for this code was already available from the non-azure code
  4457   // and would be well tested. Perhaps converting to bezier directly might
  4458   // be more efficient longer run.
  4459   a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
  4460   b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
  4461   c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
  4462   cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
  4464   sinx = sqrt(1 - cosx*cosx);
  4465   d = radius / ((1 - cosx) / sinx);
  4467   anx = (x1-p0.x) / sqrt(a2);
  4468   any = (y1-p0.y) / sqrt(a2);
  4469   bnx = (x1-x2) / sqrt(b2);
  4470   bny = (y1-y2) / sqrt(b2);
  4471   x3 = x1 - anx*d;
  4472   y3 = y1 - any*d;
  4473   x4 = x1 - bnx*d;
  4474   y4 = y1 - bny*d;
  4475   anticlockwise = (dir < 0);
  4476   cx = x3 + any*radius*(anticlockwise ? 1 : -1);
  4477   cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
  4478   angle0 = atan2((y3-cy), (x3-cx));
  4479   angle1 = atan2((y4-cy), (x4-cx));
  4482   LineTo(x3, y3);
  4484   Arc(cx, cy, radius, angle0, angle1, anticlockwise, error);
  4487 void
  4488 CanvasPath::Rect(double x, double y, double w, double h)
  4490   MoveTo(x, y);
  4491   LineTo(x + w, y);
  4492   LineTo(x + w, y + h);
  4493   LineTo(x, y + h);
  4494   ClosePath();
  4497 void
  4498 CanvasPath::Arc(double x, double y, double radius,
  4499                 double startAngle, double endAngle, bool anticlockwise,
  4500                 ErrorResult& error)
  4502   if (radius < 0.0) {
  4503     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  4504     return;
  4507   ArcToBezier(this, Point(x, y), Size(radius, radius), startAngle, endAngle, anticlockwise);
  4510 void
  4511 CanvasPath::LineTo(const gfx::Point& aPoint)
  4513   EnsurePathBuilder();
  4515   mPathBuilder->LineTo(aPoint);
  4518 void
  4519 CanvasPath::BezierTo(const gfx::Point& aCP1,
  4520                      const gfx::Point& aCP2,
  4521                      const gfx::Point& aCP3)
  4523   EnsurePathBuilder();
  4525   mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
  4528 RefPtr<gfx::Path>
  4529 CanvasPath::GetPath(const CanvasWindingRule& winding, const mozilla::RefPtr<mozilla::gfx::DrawTarget>& mTarget) const
  4531   FillRule fillRule = FillRule::FILL_WINDING;
  4532   if (winding == CanvasWindingRule::Evenodd) {
  4533     fillRule = FillRule::FILL_EVEN_ODD;
  4536   if (mPath &&
  4537       (mPath->GetBackendType() == mTarget->GetType()) &&
  4538       (mPath->GetFillRule() == fillRule)) {
  4539     return mPath;
  4542   if (!mPath) {
  4543     // if there is no path, there must be a pathbuilder
  4544     MOZ_ASSERT(mPathBuilder);
  4545     mPath = mPathBuilder->Finish();
  4546     if (!mPath)
  4547       return mPath;
  4549     mPathBuilder = nullptr;
  4552   // retarget our backend if we're used with a different backend
  4553   if (mPath->GetBackendType() != mTarget->GetType()) {
  4554     RefPtr<PathBuilder> tmpPathBuilder = mTarget->CreatePathBuilder(fillRule);
  4555     mPath->StreamToSink(tmpPathBuilder);
  4556     mPath = tmpPathBuilder->Finish();
  4557   } else if (mPath->GetFillRule() != fillRule) {
  4558     RefPtr<PathBuilder> tmpPathBuilder = mPath->CopyToBuilder(fillRule);
  4559     mPath = tmpPathBuilder->Finish();
  4562   return mPath;
  4565 void
  4566 CanvasPath::EnsurePathBuilder() const
  4568   if (mPathBuilder) {
  4569     return;
  4572   // if there is not pathbuilder, there must be a path
  4573   MOZ_ASSERT(mPath);
  4574   mPathBuilder = mPath->CopyToBuilder();
  4575   mPath = nullptr;

mercurial