content/canvas/src/CanvasRenderingContext2D.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/src/CanvasRenderingContext2D.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4579 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "CanvasRenderingContext2D.h"
    1.10 +
    1.11 +#include "nsXULElement.h"
    1.12 +
    1.13 +#include "nsIServiceManager.h"
    1.14 +#include "nsMathUtils.h"
    1.15 +
    1.16 +#include "nsContentUtils.h"
    1.17 +
    1.18 +#include "nsIDocument.h"
    1.19 +#include "mozilla/dom/HTMLCanvasElement.h"
    1.20 +#include "nsSVGEffects.h"
    1.21 +#include "nsPresContext.h"
    1.22 +#include "nsIPresShell.h"
    1.23 +
    1.24 +#include "nsIInterfaceRequestorUtils.h"
    1.25 +#include "nsIFrame.h"
    1.26 +#include "nsError.h"
    1.27 +
    1.28 +#include "nsCSSParser.h"
    1.29 +#include "mozilla/css/StyleRule.h"
    1.30 +#include "mozilla/css/Declaration.h"
    1.31 +#include "mozilla/css/Loader.h"
    1.32 +#include "nsComputedDOMStyle.h"
    1.33 +#include "nsStyleSet.h"
    1.34 +
    1.35 +#include "nsPrintfCString.h"
    1.36 +
    1.37 +#include "nsReadableUtils.h"
    1.38 +
    1.39 +#include "nsColor.h"
    1.40 +#include "nsGfxCIID.h"
    1.41 +#include "nsIDocShell.h"
    1.42 +#include "nsIDOMWindow.h"
    1.43 +#include "nsPIDOMWindow.h"
    1.44 +#include "nsDisplayList.h"
    1.45 +#include "nsFocusManager.h"
    1.46 +
    1.47 +#include "nsTArray.h"
    1.48 +
    1.49 +#include "ImageEncoder.h"
    1.50 +
    1.51 +#include "gfxContext.h"
    1.52 +#include "gfxASurface.h"
    1.53 +#include "gfxImageSurface.h"
    1.54 +#include "gfxPlatform.h"
    1.55 +#include "gfxFont.h"
    1.56 +#include "gfxBlur.h"
    1.57 +#include "gfxUtils.h"
    1.58 +
    1.59 +#include "nsFrameManager.h"
    1.60 +#include "nsFrameLoader.h"
    1.61 +#include "nsBidi.h"
    1.62 +#include "nsBidiPresUtils.h"
    1.63 +#include "Layers.h"
    1.64 +#include "CanvasUtils.h"
    1.65 +#include "nsIMemoryReporter.h"
    1.66 +#include "nsStyleUtil.h"
    1.67 +#include "CanvasImageCache.h"
    1.68 +
    1.69 +#include <algorithm>
    1.70 +
    1.71 +#include "jsapi.h"
    1.72 +#include "jsfriendapi.h"
    1.73 +
    1.74 +#include "mozilla/Alignment.h"
    1.75 +#include "mozilla/Assertions.h"
    1.76 +#include "mozilla/CheckedInt.h"
    1.77 +#include "mozilla/dom/ContentParent.h"
    1.78 +#include "mozilla/dom/ImageData.h"
    1.79 +#include "mozilla/dom/PBrowserParent.h"
    1.80 +#include "mozilla/dom/ToJSValue.h"
    1.81 +#include "mozilla/dom/TypedArray.h"
    1.82 +#include "mozilla/Endian.h"
    1.83 +#include "mozilla/gfx/2D.h"
    1.84 +#include "mozilla/gfx/PathHelpers.h"
    1.85 +#include "mozilla/gfx/DataSurfaceHelpers.h"
    1.86 +#include "mozilla/ipc/DocumentRendererParent.h"
    1.87 +#include "mozilla/ipc/PDocumentRendererParent.h"
    1.88 +#include "mozilla/MathAlgorithms.h"
    1.89 +#include "mozilla/Preferences.h"
    1.90 +#include "mozilla/Telemetry.h"
    1.91 +#include "mozilla/unused.h"
    1.92 +#include "nsCCUncollectableMarker.h"
    1.93 +#include "nsWrapperCacheInlines.h"
    1.94 +#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
    1.95 +#include "mozilla/dom/HTMLImageElement.h"
    1.96 +#include "mozilla/dom/HTMLVideoElement.h"
    1.97 +#include "mozilla/dom/TextMetrics.h"
    1.98 +#include "mozilla/dom/UnionTypes.h"
    1.99 +#include "nsGlobalWindow.h"
   1.100 +#include "GLContext.h"
   1.101 +#include "GLContextProvider.h"
   1.102 +#include "SVGContentUtils.h"
   1.103 +#include "nsIScreenManager.h"
   1.104 +
   1.105 +#undef free // apparently defined by some windows header, clashing with a free()
   1.106 +            // method in SkTypes.h
   1.107 +#ifdef USE_SKIA
   1.108 +#include "SkiaGLGlue.h"
   1.109 +#include "SurfaceStream.h"
   1.110 +#include "SurfaceTypes.h"
   1.111 +#endif
   1.112 +
   1.113 +using mozilla::gl::GLContext;
   1.114 +using mozilla::gl::SkiaGLGlue;
   1.115 +using mozilla::gl::GLContextProvider;
   1.116 +
   1.117 +#ifdef XP_WIN
   1.118 +#include "gfxWindowsPlatform.h"
   1.119 +#endif
   1.120 +
   1.121 +#ifdef MOZ_WIDGET_GONK
   1.122 +#include "mozilla/layers/ShadowLayers.h"
   1.123 +#endif
   1.124 +
   1.125 +// windows.h (included by chromium code) defines this, in its infinite wisdom
   1.126 +#undef DrawText
   1.127 +
   1.128 +using namespace mozilla;
   1.129 +using namespace mozilla::CanvasUtils;
   1.130 +using namespace mozilla::css;
   1.131 +using namespace mozilla::gfx;
   1.132 +using namespace mozilla::ipc;
   1.133 +using namespace mozilla::layers;
   1.134 +
   1.135 +namespace mgfx = mozilla::gfx;
   1.136 +
   1.137 +namespace mozilla {
   1.138 +namespace dom {
   1.139 +
   1.140 +// Cap sigma to avoid overly large temp surfaces.
   1.141 +const Float SIGMA_MAX = 100;
   1.142 +
   1.143 +/* Memory reporter stuff */
   1.144 +static int64_t gCanvasAzureMemoryUsed = 0;
   1.145 +
   1.146 +// This is KIND_OTHER because it's not always clear where in memory the pixels
   1.147 +// of a canvas are stored.  Furthermore, this memory will be tracked by the
   1.148 +// underlying surface implementations.  See bug 655638 for details.
   1.149 +class Canvas2dPixelsReporter MOZ_FINAL : public nsIMemoryReporter
   1.150 +{
   1.151 +public:
   1.152 +  NS_DECL_ISUPPORTS
   1.153 +
   1.154 +  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.155 +                            nsISupports* aData)
   1.156 +  {
   1.157 +    return MOZ_COLLECT_REPORT(
   1.158 +      "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
   1.159 +      gCanvasAzureMemoryUsed,
   1.160 +      "Memory used by 2D canvases. Each canvas requires "
   1.161 +      "(width * height * 4) bytes.");
   1.162 +  }
   1.163 +};
   1.164 +
   1.165 +NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
   1.166 +
   1.167 +class CanvasRadialGradient : public CanvasGradient
   1.168 +{
   1.169 +public:
   1.170 +  CanvasRadialGradient(CanvasRenderingContext2D* aContext,
   1.171 +                       mozilla::css::Loader *aLoader,
   1.172 +                       const Point &aBeginOrigin, Float aBeginRadius,
   1.173 +                       const Point &aEndOrigin, Float aEndRadius)
   1.174 +    : CanvasGradient(aContext, aLoader, Type::RADIAL)
   1.175 +    , mCenter1(aBeginOrigin)
   1.176 +    , mCenter2(aEndOrigin)
   1.177 +    , mRadius1(aBeginRadius)
   1.178 +    , mRadius2(aEndRadius)
   1.179 +  {
   1.180 +  }
   1.181 +
   1.182 +  Point mCenter1;
   1.183 +  Point mCenter2;
   1.184 +  Float mRadius1;
   1.185 +  Float mRadius2;
   1.186 +};
   1.187 +
   1.188 +class CanvasLinearGradient : public CanvasGradient
   1.189 +{
   1.190 +public:
   1.191 +  CanvasLinearGradient(CanvasRenderingContext2D* aContext,
   1.192 +                       mozilla::css::Loader *aLoader,
   1.193 +                       const Point &aBegin, const Point &aEnd)
   1.194 +    : CanvasGradient(aContext, aLoader, Type::LINEAR)
   1.195 +    , mBegin(aBegin)
   1.196 +    , mEnd(aEnd)
   1.197 +  {
   1.198 +  }
   1.199 +
   1.200 +protected:
   1.201 +  friend class CanvasGeneralPattern;
   1.202 +
   1.203 +  // Beginning of linear gradient.
   1.204 +  Point mBegin;
   1.205 +  // End of linear gradient.
   1.206 +  Point mEnd;
   1.207 +};
   1.208 +
   1.209 +// This class is named 'GeneralCanvasPattern' instead of just
   1.210 +// 'GeneralPattern' to keep Windows PGO builds from confusing the
   1.211 +// GeneralPattern class in gfxContext.cpp with this one.
   1.212 +
   1.213 +class CanvasGeneralPattern
   1.214 +{
   1.215 +public:
   1.216 +  typedef CanvasRenderingContext2D::Style Style;
   1.217 +  typedef CanvasRenderingContext2D::ContextState ContextState;
   1.218 +
   1.219 +  CanvasGeneralPattern() : mPattern(nullptr) {}
   1.220 +  ~CanvasGeneralPattern()
   1.221 +  {
   1.222 +    if (mPattern) {
   1.223 +      mPattern->~Pattern();
   1.224 +    }
   1.225 +  }
   1.226 +
   1.227 +  Pattern& ForStyle(CanvasRenderingContext2D *aCtx,
   1.228 +                    Style aStyle,
   1.229 +                    DrawTarget *aRT)
   1.230 +  {
   1.231 +    // This should only be called once or the mPattern destructor will
   1.232 +    // not be executed.
   1.233 +    NS_ASSERTION(!mPattern, "ForStyle() should only be called once on CanvasGeneralPattern!");
   1.234 +
   1.235 +    const ContextState &state = aCtx->CurrentState();
   1.236 +
   1.237 +    if (state.StyleIsColor(aStyle)) {
   1.238 +      mPattern = new (mColorPattern.addr()) ColorPattern(Color::FromABGR(state.colorStyles[aStyle]));
   1.239 +    } else if (state.gradientStyles[aStyle] &&
   1.240 +               state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
   1.241 +      CanvasLinearGradient *gradient =
   1.242 +        static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
   1.243 +
   1.244 +      mPattern = new (mLinearGradientPattern.addr())
   1.245 +        LinearGradientPattern(gradient->mBegin, gradient->mEnd,
   1.246 +                              gradient->GetGradientStopsForTarget(aRT));
   1.247 +    } else if (state.gradientStyles[aStyle] &&
   1.248 +               state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
   1.249 +      CanvasRadialGradient *gradient =
   1.250 +        static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
   1.251 +
   1.252 +      mPattern = new (mRadialGradientPattern.addr())
   1.253 +        RadialGradientPattern(gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
   1.254 +                              gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
   1.255 +    } else if (state.patternStyles[aStyle]) {
   1.256 +      if (aCtx->mCanvasElement) {
   1.257 +        CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
   1.258 +                                              state.patternStyles[aStyle]->mPrincipal,
   1.259 +                                              state.patternStyles[aStyle]->mForceWriteOnly,
   1.260 +                                              state.patternStyles[aStyle]->mCORSUsed);
   1.261 +      }
   1.262 +
   1.263 +      ExtendMode mode;
   1.264 +      if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
   1.265 +        mode = ExtendMode::CLAMP;
   1.266 +      } else {
   1.267 +        mode = ExtendMode::REPEAT;
   1.268 +      }
   1.269 +      mPattern = new (mSurfacePattern.addr())
   1.270 +        SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
   1.271 +    }
   1.272 +
   1.273 +    return *mPattern;
   1.274 +  }
   1.275 +
   1.276 +  union {
   1.277 +    AlignedStorage2<ColorPattern> mColorPattern;
   1.278 +    AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
   1.279 +    AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
   1.280 +    AlignedStorage2<SurfacePattern> mSurfacePattern;
   1.281 +  };
   1.282 +  Pattern *mPattern;
   1.283 +};
   1.284 +
   1.285 +/* This is an RAII based class that can be used as a drawtarget for
   1.286 + * operations that need a shadow drawn. It will automatically provide a
   1.287 + * temporary target when needed, and if so blend it back with a shadow.
   1.288 + *
   1.289 + * aBounds specifies the bounds of the drawing operation that will be
   1.290 + * drawn to the target, it is given in device space! This function will
   1.291 + * change aBounds to incorporate shadow bounds. If this is nullptr the drawing
   1.292 + * operation will be assumed to cover an infinite rect.
   1.293 + */
   1.294 +class AdjustedTarget
   1.295 +{
   1.296 +public:
   1.297 +  typedef CanvasRenderingContext2D::ContextState ContextState;
   1.298 +
   1.299 +  AdjustedTarget(CanvasRenderingContext2D *ctx,
   1.300 +                 mgfx::Rect *aBounds = nullptr)
   1.301 +    : mCtx(nullptr)
   1.302 +  {
   1.303 +    if (!ctx->NeedToDrawShadow()) {
   1.304 +      mTarget = ctx->mTarget;
   1.305 +      return;
   1.306 +    }
   1.307 +    mCtx = ctx;
   1.308 +
   1.309 +    const ContextState &state = mCtx->CurrentState();
   1.310 +
   1.311 +    mSigma = state.shadowBlur / 2.0f;
   1.312 +
   1.313 +    if (mSigma > SIGMA_MAX) {
   1.314 +      mSigma = SIGMA_MAX;
   1.315 +    }
   1.316 +
   1.317 +    Matrix transform = mCtx->mTarget->GetTransform();
   1.318 +
   1.319 +    mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
   1.320 +
   1.321 +    static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
   1.322 +    int32_t blurRadius = (int32_t) floor(mSigma * GAUSSIAN_SCALE_FACTOR + 0.5);
   1.323 +
   1.324 +    // We need to enlarge and possibly offset our temporary surface
   1.325 +    // so that things outside of the canvas may cast shadows.
   1.326 +    mTempRect.Inflate(Margin(blurRadius + std::max<Float>(state.shadowOffset.y, 0),
   1.327 +                             blurRadius + std::max<Float>(-state.shadowOffset.x, 0),
   1.328 +                             blurRadius + std::max<Float>(-state.shadowOffset.y, 0),
   1.329 +                             blurRadius + std::max<Float>(state.shadowOffset.x, 0)));
   1.330 +
   1.331 +    if (aBounds) {
   1.332 +      // We actually include the bounds of the shadow blur, this makes it
   1.333 +      // easier to execute the actual blur on hardware, and shouldn't affect
   1.334 +      // the amount of pixels that need to be touched.
   1.335 +      aBounds->Inflate(Margin(blurRadius, blurRadius,
   1.336 +                              blurRadius, blurRadius));
   1.337 +      mTempRect = mTempRect.Intersect(*aBounds);
   1.338 +    }
   1.339 +
   1.340 +    mTempRect.ScaleRoundOut(1.0f);
   1.341 +
   1.342 +    transform._31 -= mTempRect.x;
   1.343 +    transform._32 -= mTempRect.y;
   1.344 +
   1.345 +    mTarget =
   1.346 +      mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
   1.347 +                                            SurfaceFormat::B8G8R8A8, mSigma);
   1.348 +
   1.349 +    if (!mTarget) {
   1.350 +      // XXX - Deal with the situation where our temp size is too big to
   1.351 +      // fit in a texture.
   1.352 +      mTarget = ctx->mTarget;
   1.353 +      mCtx = nullptr;
   1.354 +    } else {
   1.355 +      mTarget->SetTransform(transform);
   1.356 +    }
   1.357 +  }
   1.358 +
   1.359 +  ~AdjustedTarget()
   1.360 +  {
   1.361 +    if (!mCtx) {
   1.362 +      return;
   1.363 +    }
   1.364 +
   1.365 +    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
   1.366 +
   1.367 +    mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
   1.368 +                                         Color::FromABGR(mCtx->CurrentState().shadowColor),
   1.369 +                                         mCtx->CurrentState().shadowOffset, mSigma,
   1.370 +                                         mCtx->CurrentState().op);
   1.371 +  }
   1.372 +
   1.373 +  operator DrawTarget*() 
   1.374 +  {
   1.375 +    return mTarget;
   1.376 +  }
   1.377 +
   1.378 +  DrawTarget* operator->()
   1.379 +  {
   1.380 +    return mTarget;
   1.381 +  }
   1.382 +
   1.383 +private:
   1.384 +  RefPtr<DrawTarget> mTarget;
   1.385 +  CanvasRenderingContext2D *mCtx;
   1.386 +  Float mSigma;
   1.387 +  mgfx::Rect mTempRect;
   1.388 +};
   1.389 +
   1.390 +void
   1.391 +CanvasGradient::AddColorStop(float offset, const nsAString& colorstr, ErrorResult& rv)
   1.392 +{
   1.393 +  if (offset < 0.0 || offset > 1.0) {
   1.394 +    rv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1.395 +    return;
   1.396 +  }
   1.397 +
   1.398 +  nsCSSValue value;
   1.399 +  nsCSSParser parser;
   1.400 +  if (!parser.ParseColorString(colorstr, nullptr, 0, value)) {
   1.401 +    rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.402 +    return;
   1.403 +  }
   1.404 +
   1.405 +  nsIPresShell* presShell = nullptr;
   1.406 +  if (mCSSLoader) {
   1.407 +    nsIDocument *doc = mCSSLoader->GetDocument();
   1.408 +    if (doc)
   1.409 +      presShell = doc->GetShell();
   1.410 +  }
   1.411 +
   1.412 +  nscolor color;
   1.413 +  if (!nsRuleNode::ComputeColor(value, presShell ? presShell->GetPresContext() : nullptr,
   1.414 +                                nullptr, color)) {
   1.415 +    rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.416 +    return;
   1.417 +  }
   1.418 +
   1.419 +  mStops = nullptr;
   1.420 +
   1.421 +  GradientStop newStop;
   1.422 +
   1.423 +  newStop.offset = offset;
   1.424 +  newStop.color = Color::FromABGR(color);
   1.425 +
   1.426 +  mRawStops.AppendElement(newStop);
   1.427 +}
   1.428 +
   1.429 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
   1.430 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
   1.431 +
   1.432 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasGradient, mContext)
   1.433 +
   1.434 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
   1.435 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
   1.436 +
   1.437 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasPattern, mContext)
   1.438 +
   1.439 +class CanvasRenderingContext2DUserData : public LayerUserData {
   1.440 +public:
   1.441 +    CanvasRenderingContext2DUserData(CanvasRenderingContext2D *aContext)
   1.442 +    : mContext(aContext)
   1.443 +  {
   1.444 +    aContext->mUserDatas.AppendElement(this);
   1.445 +  }
   1.446 +  ~CanvasRenderingContext2DUserData()
   1.447 +  {
   1.448 +    if (mContext) {
   1.449 +      mContext->mUserDatas.RemoveElement(this);
   1.450 +    }
   1.451 +  }
   1.452 +
   1.453 +  static void PreTransactionCallback(void* aData)
   1.454 +  {
   1.455 +    CanvasRenderingContext2DUserData* self =
   1.456 +      static_cast<CanvasRenderingContext2DUserData*>(aData);
   1.457 +    CanvasRenderingContext2D* context = self->mContext;
   1.458 +    if (!context || !context->mStream || !context->mTarget)
   1.459 +      return;
   1.460 +
   1.461 +    // Since SkiaGL default to store drawing command until flush
   1.462 +    // We will have to flush it before present.
   1.463 +    context->mTarget->Flush();
   1.464 +  }
   1.465 +
   1.466 +  static void DidTransactionCallback(void* aData)
   1.467 +  {
   1.468 +    CanvasRenderingContext2DUserData* self =
   1.469 +      static_cast<CanvasRenderingContext2DUserData*>(aData);
   1.470 +    if (self->mContext) {
   1.471 +      self->mContext->MarkContextClean();
   1.472 +    }
   1.473 +  }
   1.474 +  bool IsForContext(CanvasRenderingContext2D *aContext)
   1.475 +  {
   1.476 +    return mContext == aContext;
   1.477 +  }
   1.478 +  void Forget()
   1.479 +  {
   1.480 +    mContext = nullptr;
   1.481 +  }
   1.482 +
   1.483 +private:
   1.484 +  CanvasRenderingContext2D *mContext;
   1.485 +};
   1.486 +
   1.487 +NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
   1.488 +NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
   1.489 +
   1.490 +NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
   1.491 +
   1.492 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
   1.493 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
   1.494 +  for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
   1.495 +    ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
   1.496 +    ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
   1.497 +    ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
   1.498 +    ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
   1.499 +  }
   1.500 +  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1.501 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.502 +
   1.503 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
   1.504 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
   1.505 +  for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
   1.506 +    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::STROKE], "Stroke CanvasPattern");
   1.507 +    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::FILL], "Fill CanvasPattern");
   1.508 +    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE], "Stroke CanvasGradient");
   1.509 +    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::FILL], "Fill CanvasGradient");
   1.510 +  }
   1.511 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   1.512 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.513 +
   1.514 +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(CanvasRenderingContext2D)
   1.515 +
   1.516 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
   1.517 + if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
   1.518 +   dom::Element* canvasElement = tmp->mCanvasElement;
   1.519 +    if (canvasElement) {
   1.520 +      if (canvasElement->IsPurple()) {
   1.521 +        canvasElement->RemovePurple();
   1.522 +      }
   1.523 +      dom::Element::MarkNodeChildren(canvasElement);
   1.524 +    }
   1.525 +    return true;
   1.526 +  }
   1.527 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   1.528 +
   1.529 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
   1.530 +  return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
   1.531 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   1.532 +
   1.533 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
   1.534 +  return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
   1.535 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   1.536 +
   1.537 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
   1.538 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1.539 +  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
   1.540 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.541 +NS_INTERFACE_MAP_END
   1.542 +
   1.543 +/**
   1.544 + ** CanvasRenderingContext2D impl
   1.545 + **/
   1.546 +
   1.547 +
   1.548 +// Initialize our static variables.
   1.549 +uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
   1.550 +DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
   1.551 +
   1.552 +
   1.553 +
   1.554 +CanvasRenderingContext2D::CanvasRenderingContext2D()
   1.555 +  : mForceSoftware(false), mZero(false), mOpaque(false), mResetLayer(true)
   1.556 +  , mIPC(false)
   1.557 +  , mStream(nullptr)
   1.558 +  , mIsEntireFrameInvalid(false)
   1.559 +  , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
   1.560 +  , mInvalidateCount(0)
   1.561 +{
   1.562 +  sNumLivingContexts++;
   1.563 +  SetIsDOMBinding();
   1.564 +}
   1.565 +
   1.566 +CanvasRenderingContext2D::~CanvasRenderingContext2D()
   1.567 +{
   1.568 +  Reset();
   1.569 +  // Drop references from all CanvasRenderingContext2DUserData to this context
   1.570 +  for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
   1.571 +    mUserDatas[i]->Forget();
   1.572 +  }
   1.573 +  sNumLivingContexts--;
   1.574 +  if (!sNumLivingContexts) {
   1.575 +    NS_IF_RELEASE(sErrorTarget);
   1.576 +  }
   1.577 +
   1.578 +  RemoveDemotableContext(this);
   1.579 +}
   1.580 +
   1.581 +JSObject*
   1.582 +CanvasRenderingContext2D::WrapObject(JSContext *cx)
   1.583 +{
   1.584 +  return CanvasRenderingContext2DBinding::Wrap(cx, this);
   1.585 +}
   1.586 +
   1.587 +bool
   1.588 +CanvasRenderingContext2D::ParseColor(const nsAString& aString,
   1.589 +                                     nscolor* aColor)
   1.590 +{
   1.591 +  nsIDocument* document = mCanvasElement
   1.592 +                          ? mCanvasElement->OwnerDoc()
   1.593 +                          : nullptr;
   1.594 +
   1.595 +  // Pass the CSS Loader object to the parser, to allow parser error
   1.596 +  // reports to include the outer window ID.
   1.597 +  nsCSSParser parser(document ? document->CSSLoader() : nullptr);
   1.598 +  nsCSSValue value;
   1.599 +  if (!parser.ParseColorString(aString, nullptr, 0, value)) {
   1.600 +    return false;
   1.601 +  }
   1.602 +
   1.603 +  if (value.IsNumericColorUnit()) {
   1.604 +    // if we already have a color we can just use it directly
   1.605 +    *aColor = value.GetColorValue();
   1.606 +  } else {
   1.607 +    // otherwise resolve it
   1.608 +    nsIPresShell* presShell = GetPresShell();
   1.609 +    nsRefPtr<nsStyleContext> parentContext;
   1.610 +    if (mCanvasElement && mCanvasElement->IsInDoc()) {
   1.611 +      // Inherit from the canvas element.
   1.612 +      parentContext = nsComputedDOMStyle::GetStyleContextForElement(
   1.613 +        mCanvasElement, nullptr, presShell);
   1.614 +    }
   1.615 +
   1.616 +    unused << nsRuleNode::ComputeColor(
   1.617 +      value, presShell ? presShell->GetPresContext() : nullptr, parentContext,
   1.618 +      *aColor);
   1.619 +  }
   1.620 +  return true;
   1.621 +}
   1.622 +
   1.623 +#ifdef ACCESSIBILITY
   1.624 +PLDHashOperator
   1.625 +CanvasRenderingContext2D::RemoveHitRegionProperty(RegionInfo* aEntry, void*)
   1.626 +{
   1.627 +  aEntry->mElement->DeleteProperty(nsGkAtoms::hitregion);
   1.628 +  return PL_DHASH_NEXT;
   1.629 +}
   1.630 +#endif
   1.631 +
   1.632 +nsresult
   1.633 +CanvasRenderingContext2D::Reset()
   1.634 +{
   1.635 +  if (mCanvasElement) {
   1.636 +    mCanvasElement->InvalidateCanvas();
   1.637 +  }
   1.638 +
   1.639 +  // only do this for non-docshell created contexts,
   1.640 +  // since those are the ones that we created a surface for
   1.641 +  if (mTarget && IsTargetValid() && !mDocShell) {
   1.642 +    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
   1.643 +  }
   1.644 +
   1.645 +  mTarget = nullptr;
   1.646 +  mStream = nullptr;
   1.647 +
   1.648 +  // reset hit regions
   1.649 +#ifdef ACCESSIBILITY
   1.650 +  mHitRegionsOptions.EnumerateEntries(RemoveHitRegionProperty, nullptr);
   1.651 +#endif
   1.652 +  mHitRegionsOptions.Clear();
   1.653 +
   1.654 +  // Since the target changes the backing texture will change, and this will
   1.655 +  // no longer be valid.
   1.656 +  mIsEntireFrameInvalid = false;
   1.657 +  mPredictManyRedrawCalls = false;
   1.658 +
   1.659 +  return NS_OK;
   1.660 +}
   1.661 +
   1.662 +void
   1.663 +CanvasRenderingContext2D::SetStyleFromString(const nsAString& str,
   1.664 +                                             Style whichStyle)
   1.665 +{
   1.666 +  MOZ_ASSERT(!str.IsVoid());
   1.667 +
   1.668 +  nscolor color;
   1.669 +  if (!ParseColor(str, &color)) {
   1.670 +    return;
   1.671 +  }
   1.672 +
   1.673 +  CurrentState().SetColorStyle(whichStyle, color);
   1.674 +}
   1.675 +
   1.676 +void
   1.677 +CanvasRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
   1.678 +                                          Style aWhichStyle)
   1.679 +{
   1.680 +  const ContextState &state = CurrentState();
   1.681 +  if (state.patternStyles[aWhichStyle]) {
   1.682 +    aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
   1.683 +  } else if (state.gradientStyles[aWhichStyle]) {
   1.684 +    aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
   1.685 +  } else {
   1.686 +    StyleColorToString(state.colorStyles[aWhichStyle], aValue.SetAsString());
   1.687 +  }
   1.688 +}
   1.689 +
   1.690 +// static
   1.691 +void
   1.692 +CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
   1.693 +{
   1.694 +  // We can't reuse the normal CSS color stringification code,
   1.695 +  // because the spec calls for a different algorithm for canvas.
   1.696 +  if (NS_GET_A(aColor) == 255) {
   1.697 +    CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
   1.698 +                                    NS_GET_R(aColor),
   1.699 +                                    NS_GET_G(aColor),
   1.700 +                                    NS_GET_B(aColor)),
   1.701 +                    aStr);
   1.702 +  } else {
   1.703 +    CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
   1.704 +                                    NS_GET_R(aColor),
   1.705 +                                    NS_GET_G(aColor),
   1.706 +                                    NS_GET_B(aColor)),
   1.707 +                    aStr);
   1.708 +    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
   1.709 +    aStr.Append(')');
   1.710 +  }
   1.711 +}
   1.712 +
   1.713 +nsresult
   1.714 +CanvasRenderingContext2D::Redraw()
   1.715 +{
   1.716 +  if (mIsEntireFrameInvalid) {
   1.717 +    return NS_OK;
   1.718 +  }
   1.719 +
   1.720 +  mIsEntireFrameInvalid = true;
   1.721 +
   1.722 +  if (!mCanvasElement) {
   1.723 +    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
   1.724 +    return NS_OK;
   1.725 +  }
   1.726 +
   1.727 +  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
   1.728 +
   1.729 +  mCanvasElement->InvalidateCanvasContent(nullptr);
   1.730 +
   1.731 +  return NS_OK;
   1.732 +}
   1.733 +
   1.734 +void
   1.735 +CanvasRenderingContext2D::Redraw(const mgfx::Rect &r)
   1.736 +{
   1.737 +  ++mInvalidateCount;
   1.738 +
   1.739 +  if (mIsEntireFrameInvalid) {
   1.740 +    return;
   1.741 +  }
   1.742 +
   1.743 +  if (mPredictManyRedrawCalls ||
   1.744 +    mInvalidateCount > kCanvasMaxInvalidateCount) {
   1.745 +    Redraw();
   1.746 +    return;
   1.747 +  }
   1.748 +
   1.749 +  if (!mCanvasElement) {
   1.750 +    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
   1.751 +    return;
   1.752 +  }
   1.753 +
   1.754 +  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
   1.755 +
   1.756 +  mCanvasElement->InvalidateCanvasContent(&r);
   1.757 +}
   1.758 +
   1.759 +void
   1.760 +CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
   1.761 +{
   1.762 +  if (mIsEntireFrameInvalid) {
   1.763 +    ++mInvalidateCount;
   1.764 +    return;
   1.765 +  }
   1.766 +
   1.767 +  mgfx::Rect newr =
   1.768 +    mTarget->GetTransform().TransformBounds(ToRect(r));
   1.769 +  Redraw(newr);
   1.770 +}
   1.771 +
   1.772 +void CanvasRenderingContext2D::Demote()
   1.773 +{
   1.774 +  if (!IsTargetValid() || mForceSoftware || !mStream)
   1.775 +    return;
   1.776 +
   1.777 +  RemoveDemotableContext(this);
   1.778 +
   1.779 +  RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
   1.780 +  RefPtr<DrawTarget> oldTarget = mTarget;
   1.781 +  mTarget = nullptr;
   1.782 +  mStream = nullptr;
   1.783 +  mResetLayer = true;
   1.784 +  mForceSoftware = true;
   1.785 +
   1.786 +  // Recreate target, now demoted to software only
   1.787 +  EnsureTarget();
   1.788 +  if (!IsTargetValid())
   1.789 +    return;
   1.790 +
   1.791 +  // Restore the content from the old DrawTarget
   1.792 +  mgfx::Rect r(0, 0, mWidth, mHeight);
   1.793 +  mTarget->DrawSurface(snapshot, r, r);
   1.794 +
   1.795 +  // Restore the clips and transform
   1.796 +  for (uint32_t i = 0; i < CurrentState().clipsPushed.size(); i++) {
   1.797 +    mTarget->PushClip(CurrentState().clipsPushed[i]);
   1.798 +  }
   1.799 +
   1.800 +  mTarget->SetTransform(oldTarget->GetTransform());
   1.801 +}
   1.802 +
   1.803 +std::vector<CanvasRenderingContext2D*>&
   1.804 +CanvasRenderingContext2D::DemotableContexts()
   1.805 +{
   1.806 +  static std::vector<CanvasRenderingContext2D*> contexts;
   1.807 +  return contexts;
   1.808 +}
   1.809 +
   1.810 +void
   1.811 +CanvasRenderingContext2D::DemoteOldestContextIfNecessary()
   1.812 +{
   1.813 +  const size_t kMaxContexts = 64;
   1.814 +
   1.815 +  std::vector<CanvasRenderingContext2D*>& contexts = DemotableContexts();
   1.816 +  if (contexts.size() < kMaxContexts)
   1.817 +    return;
   1.818 +
   1.819 +  CanvasRenderingContext2D* oldest = contexts.front();
   1.820 +  oldest->Demote();
   1.821 +}
   1.822 +
   1.823 +void
   1.824 +CanvasRenderingContext2D::AddDemotableContext(CanvasRenderingContext2D* context)
   1.825 +{
   1.826 +  std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), context);
   1.827 +  if (iter != DemotableContexts().end())
   1.828 +    return;
   1.829 +
   1.830 +  DemotableContexts().push_back(context);
   1.831 +}
   1.832 +
   1.833 +void
   1.834 +CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D* context)
   1.835 +{
   1.836 +  std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), context);
   1.837 +  if (iter != DemotableContexts().end())
   1.838 +    DemotableContexts().erase(iter);
   1.839 +}
   1.840 +
   1.841 +bool
   1.842 +CheckSizeForSkiaGL(IntSize size) {
   1.843 +  MOZ_ASSERT(NS_IsMainThread());
   1.844 +
   1.845 +  int minsize = Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128);
   1.846 +  if (size.width < minsize || size.height < minsize) {
   1.847 +    return false;
   1.848 +  }
   1.849 +
   1.850 +  // Maximum pref allows 3 different options:
   1.851 +  //  0   means unlimited size
   1.852 +  //  > 0 means use value as an absolute threshold
   1.853 +  //  < 0 means use the number of screen pixels as a threshold
   1.854 +  int maxsize = Preferences::GetInt("gfx.canvas.max-size-for-skia-gl", 0);
   1.855 +
   1.856 +  // unlimited max size
   1.857 +  if (!maxsize) {
   1.858 +    return true;
   1.859 +  }
   1.860 +
   1.861 +  // absolute max size threshold
   1.862 +  if (maxsize > 0) {
   1.863 +    return size.width <= maxsize && size.height <= maxsize;
   1.864 +  }
   1.865 +
   1.866 +  // Cache the number of pixels on the primary screen
   1.867 +  static int32_t gScreenPixels = -1;
   1.868 +  if (gScreenPixels < 0) {
   1.869 +    nsCOMPtr<nsIScreenManager> screenManager =
   1.870 +      do_GetService("@mozilla.org/gfx/screenmanager;1");
   1.871 +    if (screenManager) {
   1.872 +      nsCOMPtr<nsIScreen> primaryScreen;
   1.873 +      screenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
   1.874 +      if (primaryScreen) {
   1.875 +        int32_t x, y, width, height;
   1.876 +        primaryScreen->GetRect(&x, &y, &width, &height);
   1.877 +
   1.878 +        gScreenPixels = width * height;
   1.879 +      }
   1.880 +    }
   1.881 +  }
   1.882 +
   1.883 +  // screen size acts as max threshold
   1.884 +  return gScreenPixels < 0 || (size.width * size.height) <= gScreenPixels;
   1.885 +}
   1.886 +
   1.887 +void
   1.888 +CanvasRenderingContext2D::EnsureTarget()
   1.889 +{
   1.890 +  if (mTarget) {
   1.891 +    return;
   1.892 +  }
   1.893 +
   1.894 +   // Check that the dimensions are sane
   1.895 +  IntSize size(mWidth, mHeight);
   1.896 +  if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
   1.897 +      size.width >= 0 && size.height >= 0) {
   1.898 +    SurfaceFormat format = GetSurfaceFormat();
   1.899 +    nsIDocument* ownerDoc = nullptr;
   1.900 +    if (mCanvasElement) {
   1.901 +      ownerDoc = mCanvasElement->OwnerDoc();
   1.902 +    }
   1.903 +
   1.904 +    nsRefPtr<LayerManager> layerManager = nullptr;
   1.905 +
   1.906 +    if (ownerDoc) {
   1.907 +      layerManager =
   1.908 +        nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
   1.909 +    }
   1.910 +
   1.911 +     if (layerManager) {
   1.912 +      if (gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas() &&
   1.913 +          !mForceSoftware &&
   1.914 +          CheckSizeForSkiaGL(size)) {
   1.915 +        DemoteOldestContextIfNecessary();
   1.916 +
   1.917 +        SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
   1.918 +
   1.919 +#if USE_SKIA
   1.920 +        if (glue && glue->GetGrContext() && glue->GetGLContext()) {
   1.921 +          mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
   1.922 +          if (mTarget) {
   1.923 +            mStream = gfx::SurfaceStream::CreateForType(SurfaceStreamType::TripleBuffer, glue->GetGLContext());
   1.924 +            AddDemotableContext(this);
   1.925 +          } else {
   1.926 +            printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
   1.927 +          }
   1.928 +        }
   1.929 +#endif
   1.930 +        if (!mTarget) {
   1.931 +          mTarget = layerManager->CreateDrawTarget(size, format);
   1.932 +        }
   1.933 +      } else
   1.934 +        mTarget = layerManager->CreateDrawTarget(size, format);
   1.935 +     } else {
   1.936 +        mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
   1.937 +     }
   1.938 +  }
   1.939 +
   1.940 +  if (mTarget) {
   1.941 +    static bool registered = false;
   1.942 +    if (!registered) {
   1.943 +      registered = true;
   1.944 +      RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
   1.945 +    }
   1.946 +
   1.947 +    gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
   1.948 +    JSContext* context = nsContentUtils::GetCurrentJSContext();
   1.949 +    if (context) {
   1.950 +      JS_updateMallocCounter(context, mWidth * mHeight * 4);
   1.951 +    }
   1.952 +
   1.953 +    mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
   1.954 +    // Force a full layer transaction since we didn't have a layer before
   1.955 +    // and now we might need one.
   1.956 +    if (mCanvasElement) {
   1.957 +      mCanvasElement->InvalidateCanvas();
   1.958 +    }
   1.959 +    // Calling Redraw() tells our invalidation machinery that the entire
   1.960 +    // canvas is already invalid, which can speed up future drawing.
   1.961 +    Redraw();
   1.962 +  } else {
   1.963 +    EnsureErrorTarget();
   1.964 +    mTarget = sErrorTarget;
   1.965 +  }
   1.966 +}
   1.967 +
   1.968 +#ifdef DEBUG
   1.969 +int32_t
   1.970 +CanvasRenderingContext2D::GetWidth() const
   1.971 +{
   1.972 +  return mWidth;
   1.973 +}
   1.974 +
   1.975 +int32_t
   1.976 +CanvasRenderingContext2D::GetHeight() const
   1.977 +{
   1.978 +  return mHeight;
   1.979 +}
   1.980 +#endif
   1.981 +
   1.982 +NS_IMETHODIMP
   1.983 +CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
   1.984 +{
   1.985 +  ClearTarget();
   1.986 +
   1.987 +  // Zero sized surfaces can cause problems.
   1.988 +  mZero = false;
   1.989 +  if (height == 0) {
   1.990 +    height = 1;
   1.991 +    mZero = true;
   1.992 +  }
   1.993 +  if (width == 0) {
   1.994 +    width = 1;
   1.995 +    mZero = true;
   1.996 +  }
   1.997 +  mWidth = width;
   1.998 +  mHeight = height;
   1.999 +
  1.1000 +  return NS_OK;
  1.1001 +}
  1.1002 +
  1.1003 +void
  1.1004 +CanvasRenderingContext2D::ClearTarget()
  1.1005 +{
  1.1006 +  Reset();
  1.1007 +
  1.1008 +  mResetLayer = true;
  1.1009 +
  1.1010 +  // set up the initial canvas defaults
  1.1011 +  mStyleStack.Clear();
  1.1012 +  mPathBuilder = nullptr;
  1.1013 +  mPath = nullptr;
  1.1014 +  mDSPathBuilder = nullptr;
  1.1015 +
  1.1016 +  ContextState *state = mStyleStack.AppendElement();
  1.1017 +  state->globalAlpha = 1.0;
  1.1018 +
  1.1019 +  state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
  1.1020 +  state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
  1.1021 +  state->shadowColor = NS_RGBA(0,0,0,0);
  1.1022 +}
  1.1023 +
  1.1024 +NS_IMETHODIMP
  1.1025 +CanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *shell,
  1.1026 +                                                gfxASurface *surface,
  1.1027 +                                                int32_t width,
  1.1028 +                                                int32_t height)
  1.1029 +{
  1.1030 +  mDocShell = shell;
  1.1031 +
  1.1032 +  SetDimensions(width, height);
  1.1033 +  mTarget = gfxPlatform::GetPlatform()->
  1.1034 +    CreateDrawTargetForSurface(surface, IntSize(width, height));
  1.1035 +
  1.1036 +  if (!mTarget) {
  1.1037 +    EnsureErrorTarget();
  1.1038 +    mTarget = sErrorTarget;
  1.1039 +  }
  1.1040 +
  1.1041 +  return NS_OK;
  1.1042 +}
  1.1043 +
  1.1044 +NS_IMETHODIMP
  1.1045 +CanvasRenderingContext2D::SetIsOpaque(bool isOpaque)
  1.1046 +{
  1.1047 +  if (isOpaque != mOpaque) {
  1.1048 +    mOpaque = isOpaque;
  1.1049 +    ClearTarget();
  1.1050 +  }
  1.1051 +
  1.1052 +  if (mOpaque) {
  1.1053 +    EnsureTarget();
  1.1054 +  }
  1.1055 +
  1.1056 +  return NS_OK;
  1.1057 +}
  1.1058 +
  1.1059 +NS_IMETHODIMP
  1.1060 +CanvasRenderingContext2D::SetIsIPC(bool isIPC)
  1.1061 +{
  1.1062 +  if (isIPC != mIPC) {
  1.1063 +    mIPC = isIPC;
  1.1064 +    ClearTarget();
  1.1065 +  }
  1.1066 +
  1.1067 +  return NS_OK;
  1.1068 +}
  1.1069 +
  1.1070 +NS_IMETHODIMP
  1.1071 +CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
  1.1072 +{
  1.1073 +  if (aOptions.isNullOrUndefined()) {
  1.1074 +    return NS_OK;
  1.1075 +  }
  1.1076 +
  1.1077 +  ContextAttributes2D attributes;
  1.1078 +  NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
  1.1079 +
  1.1080 +  if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
  1.1081 +    // Use software when there is going to be a lot of readback
  1.1082 +    mForceSoftware = attributes.mWillReadFrequently;
  1.1083 +  }
  1.1084 +
  1.1085 +  if (!attributes.mAlpha) {
  1.1086 +    SetIsOpaque(true);
  1.1087 +  }
  1.1088 +
  1.1089 +  return NS_OK;
  1.1090 +}
  1.1091 +
  1.1092 +void
  1.1093 +CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
  1.1094 +                                         int32_t* aFormat)
  1.1095 +{
  1.1096 +  *aImageBuffer = nullptr;
  1.1097 +  *aFormat = 0;
  1.1098 +
  1.1099 +  EnsureTarget();
  1.1100 +  RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
  1.1101 +  if (!snapshot) {
  1.1102 +    return;
  1.1103 +  }
  1.1104 +
  1.1105 +  RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
  1.1106 +  if (!data || data->GetSize() != IntSize(mWidth, mHeight)) {
  1.1107 +    return;
  1.1108 +  }
  1.1109 +
  1.1110 +  *aImageBuffer = SurfaceToPackedBGRA(data);
  1.1111 +  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
  1.1112 +}
  1.1113 +
  1.1114 +NS_IMETHODIMP
  1.1115 +CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
  1.1116 +                                         const char16_t *aEncoderOptions,
  1.1117 +                                         nsIInputStream **aStream)
  1.1118 +{
  1.1119 +  nsCString enccid("@mozilla.org/image/encoder;2?type=");
  1.1120 +  enccid += aMimeType;
  1.1121 +  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
  1.1122 +  if (!encoder) {
  1.1123 +    return NS_ERROR_FAILURE;
  1.1124 +  }
  1.1125 +
  1.1126 +  nsAutoArrayPtr<uint8_t> imageBuffer;
  1.1127 +  int32_t format = 0;
  1.1128 +  GetImageBuffer(getter_Transfers(imageBuffer), &format);
  1.1129 +  if (!imageBuffer) {
  1.1130 +    return NS_ERROR_FAILURE;
  1.1131 +  }
  1.1132 +
  1.1133 +  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
  1.1134 +                                      encoder, aEncoderOptions, aStream);
  1.1135 +}
  1.1136 +
  1.1137 +SurfaceFormat
  1.1138 +CanvasRenderingContext2D::GetSurfaceFormat() const
  1.1139 +{
  1.1140 +  return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
  1.1141 +}
  1.1142 +
  1.1143 +//
  1.1144 +// state
  1.1145 +//
  1.1146 +
  1.1147 +void
  1.1148 +CanvasRenderingContext2D::Save()
  1.1149 +{
  1.1150 +  EnsureTarget();
  1.1151 +  mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
  1.1152 +  mStyleStack.SetCapacity(mStyleStack.Length() + 1);
  1.1153 +  mStyleStack.AppendElement(CurrentState());
  1.1154 +}
  1.1155 +
  1.1156 +void
  1.1157 +CanvasRenderingContext2D::Restore()
  1.1158 +{
  1.1159 +  if (mStyleStack.Length() - 1 == 0)
  1.1160 +    return;
  1.1161 +
  1.1162 +  TransformWillUpdate();
  1.1163 +
  1.1164 +  for (uint32_t i = 0; i < CurrentState().clipsPushed.size(); i++) {
  1.1165 +    mTarget->PopClip();
  1.1166 +  }
  1.1167 +
  1.1168 +  mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
  1.1169 +
  1.1170 +  mTarget->SetTransform(CurrentState().transform);
  1.1171 +}
  1.1172 +
  1.1173 +//
  1.1174 +// transformations
  1.1175 +//
  1.1176 +
  1.1177 +void
  1.1178 +CanvasRenderingContext2D::Scale(double x, double y, ErrorResult& error)
  1.1179 +{
  1.1180 +  TransformWillUpdate();
  1.1181 +  if (!IsTargetValid()) {
  1.1182 +    error.Throw(NS_ERROR_FAILURE);
  1.1183 +    return;
  1.1184 +  }
  1.1185 +
  1.1186 +  Matrix newMatrix = mTarget->GetTransform();
  1.1187 +  mTarget->SetTransform(newMatrix.Scale(x, y));
  1.1188 +}
  1.1189 +
  1.1190 +void
  1.1191 +CanvasRenderingContext2D::Rotate(double angle, ErrorResult& error)
  1.1192 +{
  1.1193 +  TransformWillUpdate();
  1.1194 +  if (!IsTargetValid()) {
  1.1195 +    error.Throw(NS_ERROR_FAILURE);
  1.1196 +    return;
  1.1197 +  }
  1.1198 +
  1.1199 +  Matrix rotation = Matrix::Rotation(angle);
  1.1200 +  mTarget->SetTransform(rotation * mTarget->GetTransform());
  1.1201 +}
  1.1202 +
  1.1203 +void
  1.1204 +CanvasRenderingContext2D::Translate(double x, double y, ErrorResult& error)
  1.1205 +{
  1.1206 +  TransformWillUpdate();
  1.1207 +  if (!IsTargetValid()) {
  1.1208 +    error.Throw(NS_ERROR_FAILURE);
  1.1209 +    return;
  1.1210 +  }
  1.1211 +
  1.1212 +  Matrix newMatrix = mTarget->GetTransform();
  1.1213 +  mTarget->SetTransform(newMatrix.Translate(x, y));
  1.1214 +}
  1.1215 +
  1.1216 +void
  1.1217 +CanvasRenderingContext2D::Transform(double m11, double m12, double m21,
  1.1218 +                                    double m22, double dx, double dy,
  1.1219 +                                    ErrorResult& error)
  1.1220 +{
  1.1221 +  TransformWillUpdate();
  1.1222 +  if (!IsTargetValid()) {
  1.1223 +    error.Throw(NS_ERROR_FAILURE);
  1.1224 +    return;
  1.1225 +  }
  1.1226 +
  1.1227 +  Matrix matrix(m11, m12, m21, m22, dx, dy);
  1.1228 +  mTarget->SetTransform(matrix * mTarget->GetTransform());
  1.1229 +}
  1.1230 +
  1.1231 +void
  1.1232 +CanvasRenderingContext2D::SetTransform(double m11, double m12,
  1.1233 +                                       double m21, double m22,
  1.1234 +                                       double dx, double dy,
  1.1235 +                                       ErrorResult& error)
  1.1236 +{
  1.1237 +  TransformWillUpdate();
  1.1238 +  if (!IsTargetValid()) {
  1.1239 +    error.Throw(NS_ERROR_FAILURE);
  1.1240 +    return;
  1.1241 +  }
  1.1242 +
  1.1243 +  Matrix matrix(m11, m12, m21, m22, dx, dy);
  1.1244 +  mTarget->SetTransform(matrix);
  1.1245 +}
  1.1246 +
  1.1247 +static void
  1.1248 +MatrixToJSObject(JSContext* cx, const Matrix& matrix,
  1.1249 +                 JS::MutableHandle<JSObject*> result, ErrorResult& error)
  1.1250 +{
  1.1251 +  double elts[6] = { matrix._11, matrix._12,
  1.1252 +                     matrix._21, matrix._22,
  1.1253 +                     matrix._31, matrix._32 };
  1.1254 +
  1.1255 +  // XXX Should we enter GetWrapper()'s compartment?
  1.1256 +  JS::Rooted<JS::Value> val(cx);
  1.1257 +  if (!ToJSValue(cx, elts, &val)) {
  1.1258 +    error.Throw(NS_ERROR_OUT_OF_MEMORY);
  1.1259 +  } else {
  1.1260 +    result.set(&val.toObject());
  1.1261 +  }
  1.1262 +}
  1.1263 +
  1.1264 +static bool
  1.1265 +ObjectToMatrix(JSContext* cx, JS::Handle<JSObject*> obj, Matrix& matrix,
  1.1266 +               ErrorResult& error)
  1.1267 +{
  1.1268 +  uint32_t length;
  1.1269 +  if (!JS_GetArrayLength(cx, obj, &length) || length != 6) {
  1.1270 +    // Not an array-like thing or wrong size
  1.1271 +    error.Throw(NS_ERROR_INVALID_ARG);
  1.1272 +    return false;
  1.1273 +  }
  1.1274 +
  1.1275 +  Float* elts[] = { &matrix._11, &matrix._12, &matrix._21, &matrix._22,
  1.1276 +                    &matrix._31, &matrix._32 };
  1.1277 +  for (uint32_t i = 0; i < 6; ++i) {
  1.1278 +    JS::Rooted<JS::Value> elt(cx);
  1.1279 +    double d;
  1.1280 +    if (!JS_GetElement(cx, obj, i, &elt)) {
  1.1281 +      error.Throw(NS_ERROR_FAILURE);
  1.1282 +      return false;
  1.1283 +    }
  1.1284 +    if (!CoerceDouble(elt, &d)) {
  1.1285 +      error.Throw(NS_ERROR_INVALID_ARG);
  1.1286 +      return false;
  1.1287 +    }
  1.1288 +    if (!FloatValidate(d)) {
  1.1289 +      // This is weird, but it's the behavior of SetTransform()
  1.1290 +      return false;
  1.1291 +    }
  1.1292 +    *elts[i] = Float(d);
  1.1293 +  }
  1.1294 +  return true;
  1.1295 +}
  1.1296 +
  1.1297 +void
  1.1298 +CanvasRenderingContext2D::SetMozCurrentTransform(JSContext* cx,
  1.1299 +                                                 JS::Handle<JSObject*> currentTransform,
  1.1300 +                                                 ErrorResult& error)
  1.1301 +{
  1.1302 +  EnsureTarget();
  1.1303 +  if (!IsTargetValid()) {
  1.1304 +    error.Throw(NS_ERROR_FAILURE);
  1.1305 +    return;
  1.1306 +  }
  1.1307 +
  1.1308 +  Matrix newCTM;
  1.1309 +  if (ObjectToMatrix(cx, currentTransform, newCTM, error)) {
  1.1310 +    mTarget->SetTransform(newCTM);
  1.1311 +  }
  1.1312 +}
  1.1313 +
  1.1314 +void
  1.1315 +CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
  1.1316 +                                                 JS::MutableHandle<JSObject*> result,
  1.1317 +                                                 ErrorResult& error) const
  1.1318 +{
  1.1319 +  MatrixToJSObject(cx, mTarget ? mTarget->GetTransform() : Matrix(),
  1.1320 +                   result, error);
  1.1321 +}
  1.1322 +
  1.1323 +void
  1.1324 +CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
  1.1325 +                                                        JS::Handle<JSObject*> currentTransform,
  1.1326 +                                                        ErrorResult& error)
  1.1327 +{
  1.1328 +  EnsureTarget();
  1.1329 +  if (!IsTargetValid()) {
  1.1330 +    error.Throw(NS_ERROR_FAILURE);
  1.1331 +    return;
  1.1332 +  }
  1.1333 +
  1.1334 +  Matrix newCTMInverse;
  1.1335 +  if (ObjectToMatrix(cx, currentTransform, newCTMInverse, error)) {
  1.1336 +    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.1337 +    if (newCTMInverse.Invert()) {
  1.1338 +      mTarget->SetTransform(newCTMInverse);
  1.1339 +    }
  1.1340 +  }
  1.1341 +}
  1.1342 +
  1.1343 +void
  1.1344 +CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* cx,
  1.1345 +                                                        JS::MutableHandle<JSObject*> result,
  1.1346 +                                                        ErrorResult& error) const
  1.1347 +{
  1.1348 +  if (!mTarget) {
  1.1349 +    MatrixToJSObject(cx, Matrix(), result, error);
  1.1350 +    return;
  1.1351 +  }
  1.1352 +
  1.1353 +  Matrix ctm = mTarget->GetTransform();
  1.1354 +
  1.1355 +  if (!ctm.Invert()) {
  1.1356 +    double NaN = JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));
  1.1357 +    ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
  1.1358 +  }
  1.1359 +
  1.1360 +  MatrixToJSObject(cx, ctm, result, error);
  1.1361 +}
  1.1362 +
  1.1363 +//
  1.1364 +// colors
  1.1365 +//
  1.1366 +
  1.1367 +void
  1.1368 +CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& value,
  1.1369 +                                            Style whichStyle)
  1.1370 +{
  1.1371 +  if (value.IsString()) {
  1.1372 +    SetStyleFromString(value.GetAsString(), whichStyle);
  1.1373 +    return;
  1.1374 +  }
  1.1375 +
  1.1376 +  if (value.IsCanvasGradient()) {
  1.1377 +    SetStyleFromGradient(value.GetAsCanvasGradient(), whichStyle);
  1.1378 +    return;
  1.1379 +  }
  1.1380 +
  1.1381 +  if (value.IsCanvasPattern()) {
  1.1382 +    SetStyleFromPattern(value.GetAsCanvasPattern(), whichStyle);
  1.1383 +    return;
  1.1384 +  }
  1.1385 +
  1.1386 +  MOZ_ASSUME_UNREACHABLE("Invalid union value");
  1.1387 +}
  1.1388 +
  1.1389 +void
  1.1390 +CanvasRenderingContext2D::SetFillRule(const nsAString& aString)
  1.1391 +{
  1.1392 +  FillRule rule;
  1.1393 +
  1.1394 +  if (aString.EqualsLiteral("evenodd"))
  1.1395 +    rule = FillRule::FILL_EVEN_ODD;
  1.1396 +  else if (aString.EqualsLiteral("nonzero"))
  1.1397 +    rule = FillRule::FILL_WINDING;
  1.1398 +  else
  1.1399 +    return;
  1.1400 +
  1.1401 +  CurrentState().fillRule = rule;
  1.1402 +}
  1.1403 +
  1.1404 +void
  1.1405 +CanvasRenderingContext2D::GetFillRule(nsAString& aString)
  1.1406 +{
  1.1407 +  switch (CurrentState().fillRule) {
  1.1408 +  case FillRule::FILL_WINDING:
  1.1409 +    aString.AssignLiteral("nonzero"); break;
  1.1410 +  case FillRule::FILL_EVEN_ODD:
  1.1411 +    aString.AssignLiteral("evenodd"); break;
  1.1412 +  }
  1.1413 +}
  1.1414 +//
  1.1415 +// gradients and patterns
  1.1416 +//
  1.1417 +already_AddRefed<CanvasGradient>
  1.1418 +CanvasRenderingContext2D::CreateLinearGradient(double x0, double y0, double x1, double y1)
  1.1419 +{
  1.1420 +  nsIDocument *doc = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
  1.1421 +  mozilla::css::Loader *cssLoader = doc ? doc->CSSLoader() : nullptr;
  1.1422 +
  1.1423 +  nsRefPtr<CanvasGradient> grad =
  1.1424 +    new CanvasLinearGradient(this, cssLoader, Point(x0, y0), Point(x1, y1));
  1.1425 +
  1.1426 +  return grad.forget();
  1.1427 +}
  1.1428 +
  1.1429 +already_AddRefed<CanvasGradient>
  1.1430 +CanvasRenderingContext2D::CreateRadialGradient(double x0, double y0, double r0,
  1.1431 +                                               double x1, double y1, double r1,
  1.1432 +                                               ErrorResult& aError)
  1.1433 +{
  1.1434 +  if (r0 < 0.0 || r1 < 0.0) {
  1.1435 +    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.1436 +    return nullptr;
  1.1437 +  }
  1.1438 +
  1.1439 +  nsIDocument *doc = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
  1.1440 +  mozilla::css::Loader *cssLoader = doc ? doc->CSSLoader() : nullptr;
  1.1441 +  nsRefPtr<CanvasGradient> grad =
  1.1442 +    new CanvasRadialGradient(this, cssLoader, Point(x0, y0), r0, Point(x1, y1), r1);
  1.1443 +
  1.1444 +  return grad.forget();
  1.1445 +}
  1.1446 +
  1.1447 +already_AddRefed<CanvasPattern>
  1.1448 +CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement& element,
  1.1449 +                                        const nsAString& repeat,
  1.1450 +                                        ErrorResult& error)
  1.1451 +{
  1.1452 +  CanvasPattern::RepeatMode repeatMode =
  1.1453 +    CanvasPattern::RepeatMode::NOREPEAT;
  1.1454 +
  1.1455 +  if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
  1.1456 +    repeatMode = CanvasPattern::RepeatMode::REPEAT;
  1.1457 +  } else if (repeat.EqualsLiteral("repeat-x")) {
  1.1458 +    repeatMode = CanvasPattern::RepeatMode::REPEATX;
  1.1459 +  } else if (repeat.EqualsLiteral("repeat-y")) {
  1.1460 +    repeatMode = CanvasPattern::RepeatMode::REPEATY;
  1.1461 +  } else if (repeat.EqualsLiteral("no-repeat")) {
  1.1462 +    repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
  1.1463 +  } else {
  1.1464 +    error.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  1.1465 +    return nullptr;
  1.1466 +  }
  1.1467 +
  1.1468 +  Element* htmlElement;
  1.1469 +  if (element.IsHTMLCanvasElement()) {
  1.1470 +    HTMLCanvasElement* canvas = &element.GetAsHTMLCanvasElement();
  1.1471 +    htmlElement = canvas;
  1.1472 +
  1.1473 +    nsIntSize size = canvas->GetSize();
  1.1474 +    if (size.width == 0 || size.height == 0) {
  1.1475 +      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.1476 +      return nullptr;
  1.1477 +    }
  1.1478 +
  1.1479 +    // Special case for Canvas, which could be an Azure canvas!
  1.1480 +    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
  1.1481 +    if (srcCanvas) {
  1.1482 +      // This might not be an Azure canvas!
  1.1483 +      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
  1.1484 +
  1.1485 +      nsRefPtr<CanvasPattern> pat =
  1.1486 +        new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
  1.1487 +
  1.1488 +      return pat.forget();
  1.1489 +    }
  1.1490 +  } else if (element.IsHTMLImageElement()) {
  1.1491 +    htmlElement = &element.GetAsHTMLImageElement();
  1.1492 +  } else {
  1.1493 +    htmlElement = &element.GetAsHTMLVideoElement();
  1.1494 +  }
  1.1495 +
  1.1496 +  EnsureTarget();
  1.1497 +
  1.1498 +  // The canvas spec says that createPattern should use the first frame
  1.1499 +  // of animated images
  1.1500 +  nsLayoutUtils::SurfaceFromElementResult res =
  1.1501 +    nsLayoutUtils::SurfaceFromElement(htmlElement,
  1.1502 +      nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
  1.1503 +
  1.1504 +  if (!res.mSourceSurface) {
  1.1505 +    error.Throw(NS_ERROR_NOT_AVAILABLE);
  1.1506 +    return nullptr;
  1.1507 +  }
  1.1508 +
  1.1509 +  nsRefPtr<CanvasPattern> pat =
  1.1510 +    new CanvasPattern(this, res.mSourceSurface, repeatMode, res.mPrincipal,
  1.1511 +                             res.mIsWriteOnly, res.mCORSUsed);
  1.1512 +
  1.1513 +  return pat.forget();
  1.1514 +}
  1.1515 +
  1.1516 +//
  1.1517 +// shadows
  1.1518 +//
  1.1519 +void
  1.1520 +CanvasRenderingContext2D::SetShadowColor(const nsAString& shadowColor)
  1.1521 +{
  1.1522 +  nscolor color;
  1.1523 +  if (!ParseColor(shadowColor, &color)) {
  1.1524 +    return;
  1.1525 +  }
  1.1526 +
  1.1527 +  CurrentState().shadowColor = color;
  1.1528 +}
  1.1529 +
  1.1530 +//
  1.1531 +// rects
  1.1532 +//
  1.1533 +
  1.1534 +void
  1.1535 +CanvasRenderingContext2D::ClearRect(double x, double y, double w,
  1.1536 +                                    double h)
  1.1537 +{
  1.1538 +  if (!mTarget) {
  1.1539 +    return;
  1.1540 +  }
  1.1541 +
  1.1542 +  mTarget->ClearRect(mgfx::Rect(x, y, w, h));
  1.1543 +
  1.1544 +  RedrawUser(gfxRect(x, y, w, h));
  1.1545 +}
  1.1546 +
  1.1547 +void
  1.1548 +CanvasRenderingContext2D::FillRect(double x, double y, double w,
  1.1549 +                                   double h)
  1.1550 +{
  1.1551 +  const ContextState &state = CurrentState();
  1.1552 +
  1.1553 +  if (state.patternStyles[Style::FILL]) {
  1.1554 +    CanvasPattern::RepeatMode repeat =
  1.1555 +      state.patternStyles[Style::FILL]->mRepeat;
  1.1556 +    // In the FillRect case repeat modes are easy to deal with.
  1.1557 +    bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY;
  1.1558 +    bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX;
  1.1559 +
  1.1560 +    IntSize patternSize =
  1.1561 +      state.patternStyles[Style::FILL]->mSurface->GetSize();
  1.1562 +
  1.1563 +    // We always need to execute painting for non-over operators, even if
  1.1564 +    // we end up with w/h = 0.
  1.1565 +    if (limitx) {
  1.1566 +      if (x < 0) {
  1.1567 +        w += x;
  1.1568 +        if (w < 0) {
  1.1569 +          w = 0;
  1.1570 +        }
  1.1571 +
  1.1572 +        x = 0;
  1.1573 +      }
  1.1574 +      if (x + w > patternSize.width) {
  1.1575 +        w = patternSize.width - x;
  1.1576 +        if (w < 0) {
  1.1577 +          w = 0;
  1.1578 +        }
  1.1579 +      }
  1.1580 +    }
  1.1581 +    if (limity) {
  1.1582 +      if (y < 0) {
  1.1583 +        h += y;
  1.1584 +        if (h < 0) {
  1.1585 +          h = 0;
  1.1586 +        }
  1.1587 +
  1.1588 +        y = 0;
  1.1589 +      }
  1.1590 +      if (y + h > patternSize.height) {
  1.1591 +        h = patternSize.height - y;
  1.1592 +        if (h < 0) {
  1.1593 +          h = 0;
  1.1594 +        }
  1.1595 +      }
  1.1596 +    }
  1.1597 +  }
  1.1598 +
  1.1599 +  mgfx::Rect bounds;
  1.1600 +
  1.1601 +  EnsureTarget();
  1.1602 +  if (NeedToDrawShadow()) {
  1.1603 +    bounds = mgfx::Rect(x, y, w, h);
  1.1604 +    bounds = mTarget->GetTransform().TransformBounds(bounds);
  1.1605 +  }
  1.1606 +
  1.1607 +  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1608 +    FillRect(mgfx::Rect(x, y, w, h),
  1.1609 +             CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
  1.1610 +             DrawOptions(state.globalAlpha, UsedOperation()));
  1.1611 +
  1.1612 +  RedrawUser(gfxRect(x, y, w, h));
  1.1613 +}
  1.1614 +
  1.1615 +void
  1.1616 +CanvasRenderingContext2D::StrokeRect(double x, double y, double w,
  1.1617 +                                     double h)
  1.1618 +{
  1.1619 +  const ContextState &state = CurrentState();
  1.1620 +
  1.1621 +  mgfx::Rect bounds;
  1.1622 +
  1.1623 +  if (!w && !h) {
  1.1624 +    return;
  1.1625 +  }
  1.1626 +
  1.1627 +  EnsureTarget();
  1.1628 +  if (!IsTargetValid()) {
  1.1629 +    return;
  1.1630 +  }
  1.1631 +
  1.1632 +  if (NeedToDrawShadow()) {
  1.1633 +    bounds = mgfx::Rect(x - state.lineWidth / 2.0f, y - state.lineWidth / 2.0f,
  1.1634 +                        w + state.lineWidth, h + state.lineWidth);
  1.1635 +    bounds = mTarget->GetTransform().TransformBounds(bounds);
  1.1636 +  }
  1.1637 +
  1.1638 +  if (!h) {
  1.1639 +    CapStyle cap = CapStyle::BUTT;
  1.1640 +    if (state.lineJoin == JoinStyle::ROUND) {
  1.1641 +      cap = CapStyle::ROUND;
  1.1642 +    }
  1.1643 +    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1644 +      StrokeLine(Point(x, y), Point(x + w, y),
  1.1645 +                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1.1646 +                  StrokeOptions(state.lineWidth, state.lineJoin,
  1.1647 +                                cap, state.miterLimit,
  1.1648 +                                state.dash.Length(),
  1.1649 +                                state.dash.Elements(),
  1.1650 +                                state.dashOffset),
  1.1651 +                  DrawOptions(state.globalAlpha, UsedOperation()));
  1.1652 +    return;
  1.1653 +  }
  1.1654 +
  1.1655 +  if (!w) {
  1.1656 +    CapStyle cap = CapStyle::BUTT;
  1.1657 +    if (state.lineJoin == JoinStyle::ROUND) {
  1.1658 +      cap = CapStyle::ROUND;
  1.1659 +    }
  1.1660 +    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1661 +      StrokeLine(Point(x, y), Point(x, y + h),
  1.1662 +                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1.1663 +                  StrokeOptions(state.lineWidth, state.lineJoin,
  1.1664 +                                cap, state.miterLimit,
  1.1665 +                                state.dash.Length(),
  1.1666 +                                state.dash.Elements(),
  1.1667 +                                state.dashOffset),
  1.1668 +                  DrawOptions(state.globalAlpha, UsedOperation()));
  1.1669 +    return;
  1.1670 +  }
  1.1671 +
  1.1672 +  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1673 +    StrokeRect(mgfx::Rect(x, y, w, h),
  1.1674 +                CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1.1675 +                StrokeOptions(state.lineWidth, state.lineJoin,
  1.1676 +                              state.lineCap, state.miterLimit,
  1.1677 +                              state.dash.Length(),
  1.1678 +                              state.dash.Elements(),
  1.1679 +                              state.dashOffset),
  1.1680 +                DrawOptions(state.globalAlpha, UsedOperation()));
  1.1681 +
  1.1682 +  Redraw();
  1.1683 +}
  1.1684 +
  1.1685 +//
  1.1686 +// path bits
  1.1687 +//
  1.1688 +
  1.1689 +void
  1.1690 +CanvasRenderingContext2D::BeginPath()
  1.1691 +{
  1.1692 +  mPath = nullptr;
  1.1693 +  mPathBuilder = nullptr;
  1.1694 +  mDSPathBuilder = nullptr;
  1.1695 +  mPathTransformWillUpdate = false;
  1.1696 +}
  1.1697 +
  1.1698 +void
  1.1699 +CanvasRenderingContext2D::Fill(const CanvasWindingRule& winding)
  1.1700 +{
  1.1701 +  EnsureUserSpacePath(winding);
  1.1702 +
  1.1703 +  if (!mPath) {
  1.1704 +    return;
  1.1705 +  }
  1.1706 +
  1.1707 +  mgfx::Rect bounds;
  1.1708 +
  1.1709 +  if (NeedToDrawShadow()) {
  1.1710 +    bounds = mPath->GetBounds(mTarget->GetTransform());
  1.1711 +  }
  1.1712 +
  1.1713 +  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1714 +    Fill(mPath, CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
  1.1715 +         DrawOptions(CurrentState().globalAlpha, UsedOperation()));
  1.1716 +
  1.1717 +  Redraw();
  1.1718 +}
  1.1719 +
  1.1720 +void CanvasRenderingContext2D::Fill(const CanvasPath& path, const CanvasWindingRule& winding)
  1.1721 +{
  1.1722 +  EnsureTarget();
  1.1723 +
  1.1724 +  RefPtr<gfx::Path> gfxpath = path.GetPath(winding, mTarget);
  1.1725 +
  1.1726 +  if (!gfxpath) {
  1.1727 +    return;
  1.1728 +  }
  1.1729 +
  1.1730 +  mgfx::Rect bounds;
  1.1731 +
  1.1732 +  if (NeedToDrawShadow()) {
  1.1733 +    bounds = gfxpath->GetBounds(mTarget->GetTransform());
  1.1734 +  }
  1.1735 +
  1.1736 +  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1737 +    Fill(gfxpath, CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
  1.1738 +         DrawOptions(CurrentState().globalAlpha, UsedOperation()));
  1.1739 +
  1.1740 +  Redraw();
  1.1741 +}
  1.1742 +
  1.1743 +void
  1.1744 +CanvasRenderingContext2D::Stroke()
  1.1745 +{
  1.1746 +  EnsureUserSpacePath();
  1.1747 +
  1.1748 +  if (!mPath) {
  1.1749 +    return;
  1.1750 +  }
  1.1751 +
  1.1752 +  const ContextState &state = CurrentState();
  1.1753 +
  1.1754 +  StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
  1.1755 +                              state.lineCap, state.miterLimit,
  1.1756 +                              state.dash.Length(), state.dash.Elements(),
  1.1757 +                              state.dashOffset);
  1.1758 +
  1.1759 +  mgfx::Rect bounds;
  1.1760 +  if (NeedToDrawShadow()) {
  1.1761 +    bounds =
  1.1762 +      mPath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
  1.1763 +  }
  1.1764 +
  1.1765 +  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1766 +    Stroke(mPath, CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1.1767 +           strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
  1.1768 +
  1.1769 +  Redraw();
  1.1770 +}
  1.1771 +
  1.1772 +void
  1.1773 +CanvasRenderingContext2D::Stroke(const CanvasPath& path)
  1.1774 +{
  1.1775 +  EnsureTarget();
  1.1776 +
  1.1777 +  RefPtr<gfx::Path> gfxpath = path.GetPath(CanvasWindingRule::Nonzero, mTarget);
  1.1778 +
  1.1779 +  if (!gfxpath) {
  1.1780 +    return;
  1.1781 +  }
  1.1782 +
  1.1783 +  const ContextState &state = CurrentState();
  1.1784 +
  1.1785 +  StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
  1.1786 +                              state.lineCap, state.miterLimit,
  1.1787 +                              state.dash.Length(), state.dash.Elements(),
  1.1788 +                              state.dashOffset);
  1.1789 +
  1.1790 +  mgfx::Rect bounds;
  1.1791 +  if (NeedToDrawShadow()) {
  1.1792 +    bounds =
  1.1793 +      gfxpath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
  1.1794 +  }
  1.1795 +
  1.1796 +  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.1797 +    Stroke(gfxpath, CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
  1.1798 +           strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
  1.1799 +
  1.1800 +  Redraw();
  1.1801 +}
  1.1802 +
  1.1803 +void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement)
  1.1804 +{
  1.1805 +  EnsureUserSpacePath();
  1.1806 +
  1.1807 +  if (!mPath) {
  1.1808 +    return;
  1.1809 +  }
  1.1810 +
  1.1811 +  if(DrawCustomFocusRing(aElement)) {
  1.1812 +    Save();
  1.1813 +
  1.1814 +    // set state to conforming focus state
  1.1815 +    ContextState& state = CurrentState();
  1.1816 +    state.globalAlpha = 1.0;
  1.1817 +    state.shadowBlur = 0;
  1.1818 +    state.shadowOffset.x = 0;
  1.1819 +    state.shadowOffset.y = 0;
  1.1820 +    state.op = mozilla::gfx::CompositionOp::OP_OVER;
  1.1821 +
  1.1822 +    state.lineCap = CapStyle::BUTT;
  1.1823 +    state.lineJoin = mozilla::gfx::JoinStyle::MITER_OR_BEVEL;
  1.1824 +    state.lineWidth = 1;
  1.1825 +    CurrentState().dash.Clear();
  1.1826 +
  1.1827 +    // color and style of the rings is the same as for image maps
  1.1828 +    // set the background focus color
  1.1829 +    CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(255, 255, 255, 255));
  1.1830 +    // draw the focus ring
  1.1831 +    Stroke();
  1.1832 +
  1.1833 +    // set dashing for foreground
  1.1834 +    FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
  1.1835 +    dash.AppendElement(1);
  1.1836 +    dash.AppendElement(1);
  1.1837 +
  1.1838 +    // set the foreground focus color
  1.1839 +    CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(0,0,0, 255));
  1.1840 +    // draw the focus ring
  1.1841 +    Stroke();
  1.1842 +
  1.1843 +    Restore();
  1.1844 +  }
  1.1845 +}
  1.1846 +
  1.1847 +bool CanvasRenderingContext2D::DrawCustomFocusRing(mozilla::dom::Element& aElement)
  1.1848 +{
  1.1849 +  EnsureUserSpacePath();
  1.1850 +
  1.1851 +  HTMLCanvasElement* canvas = GetCanvas();
  1.1852 +
  1.1853 +  if (!canvas|| !nsContentUtils::ContentIsDescendantOf(&aElement, canvas)) {
  1.1854 +    return false;
  1.1855 +  }
  1.1856 +
  1.1857 +  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  1.1858 +  if (fm) {
  1.1859 +    // check that the element i focused
  1.1860 +    nsCOMPtr<nsIDOMElement> focusedElement;
  1.1861 +    fm->GetFocusedElement(getter_AddRefs(focusedElement));
  1.1862 +    if (SameCOMIdentity(aElement.AsDOMNode(), focusedElement)) {
  1.1863 +      return true;
  1.1864 +    }
  1.1865 +  }
  1.1866 +
  1.1867 +  return false;
  1.1868 +}
  1.1869 +
  1.1870 +void
  1.1871 +CanvasRenderingContext2D::Clip(const CanvasWindingRule& winding)
  1.1872 +{
  1.1873 +  EnsureUserSpacePath(winding);
  1.1874 +
  1.1875 +  if (!mPath) {
  1.1876 +    return;
  1.1877 +  }
  1.1878 +
  1.1879 +  mTarget->PushClip(mPath);
  1.1880 +  CurrentState().clipsPushed.push_back(mPath);
  1.1881 +}
  1.1882 +
  1.1883 +void
  1.1884 +CanvasRenderingContext2D::Clip(const CanvasPath& path, const CanvasWindingRule& winding)
  1.1885 +{
  1.1886 +  EnsureTarget();
  1.1887 +
  1.1888 +  RefPtr<gfx::Path> gfxpath = path.GetPath(winding, mTarget);
  1.1889 +
  1.1890 +  if (!gfxpath) {
  1.1891 +    return;
  1.1892 +  }
  1.1893 +
  1.1894 +  mTarget->PushClip(gfxpath);
  1.1895 +  CurrentState().clipsPushed.push_back(gfxpath);
  1.1896 +}
  1.1897 +
  1.1898 +void
  1.1899 +CanvasRenderingContext2D::ArcTo(double x1, double y1, double x2,
  1.1900 +                                double y2, double radius,
  1.1901 +                                ErrorResult& error)
  1.1902 +{
  1.1903 +  if (radius < 0) {
  1.1904 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.1905 +    return;
  1.1906 +  }
  1.1907 +
  1.1908 +  EnsureWritablePath();
  1.1909 +
  1.1910 +  // Current point in user space!
  1.1911 +  Point p0;
  1.1912 +  if (mPathBuilder) {
  1.1913 +    p0 = mPathBuilder->CurrentPoint();
  1.1914 +  } else {
  1.1915 +    Matrix invTransform = mTarget->GetTransform();
  1.1916 +    if (!invTransform.Invert()) {
  1.1917 +      return;
  1.1918 +    }
  1.1919 +
  1.1920 +    p0 = invTransform * mDSPathBuilder->CurrentPoint();
  1.1921 +  }
  1.1922 +
  1.1923 +  Point p1(x1, y1);
  1.1924 +  Point p2(x2, y2);
  1.1925 +
  1.1926 +  // Execute these calculations in double precision to avoid cumulative
  1.1927 +  // rounding errors.
  1.1928 +  double dir, a2, b2, c2, cosx, sinx, d, anx, any,
  1.1929 +         bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
  1.1930 +  bool anticlockwise;
  1.1931 +
  1.1932 +  if (p0 == p1 || p1 == p2 || radius == 0) {
  1.1933 +    LineTo(p1.x, p1.y);
  1.1934 +    return;
  1.1935 +  }
  1.1936 +
  1.1937 +  // Check for colinearity
  1.1938 +  dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
  1.1939 +  if (dir == 0) {
  1.1940 +    LineTo(p1.x, p1.y);
  1.1941 +    return;
  1.1942 +  }
  1.1943 +
  1.1944 +
  1.1945 +  // XXX - Math for this code was already available from the non-azure code
  1.1946 +  // and would be well tested. Perhaps converting to bezier directly might
  1.1947 +  // be more efficient longer run.
  1.1948 +  a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
  1.1949 +  b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
  1.1950 +  c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
  1.1951 +  cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
  1.1952 +
  1.1953 +  sinx = sqrt(1 - cosx*cosx);
  1.1954 +  d = radius / ((1 - cosx) / sinx);
  1.1955 +
  1.1956 +  anx = (x1-p0.x) / sqrt(a2);
  1.1957 +  any = (y1-p0.y) / sqrt(a2);
  1.1958 +  bnx = (x1-x2) / sqrt(b2);
  1.1959 +  bny = (y1-y2) / sqrt(b2);
  1.1960 +  x3 = x1 - anx*d;
  1.1961 +  y3 = y1 - any*d;
  1.1962 +  x4 = x1 - bnx*d;
  1.1963 +  y4 = y1 - bny*d;
  1.1964 +  anticlockwise = (dir < 0);
  1.1965 +  cx = x3 + any*radius*(anticlockwise ? 1 : -1);
  1.1966 +  cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
  1.1967 +  angle0 = atan2((y3-cy), (x3-cx));
  1.1968 +  angle1 = atan2((y4-cy), (x4-cx));
  1.1969 +
  1.1970 +
  1.1971 +  LineTo(x3, y3);
  1.1972 +
  1.1973 +  Arc(cx, cy, radius, angle0, angle1, anticlockwise, error);
  1.1974 +}
  1.1975 +
  1.1976 +void
  1.1977 +CanvasRenderingContext2D::Arc(double x, double y, double r,
  1.1978 +                              double startAngle, double endAngle,
  1.1979 +                              bool anticlockwise, ErrorResult& error)
  1.1980 +{
  1.1981 +  if (r < 0.0) {
  1.1982 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.1983 +    return;
  1.1984 +  }
  1.1985 +
  1.1986 +  EnsureWritablePath();
  1.1987 +
  1.1988 +  ArcToBezier(this, Point(x, y), Size(r, r), startAngle, endAngle, anticlockwise);
  1.1989 +}
  1.1990 +
  1.1991 +void
  1.1992 +CanvasRenderingContext2D::Rect(double x, double y, double w, double h)
  1.1993 +{
  1.1994 +  EnsureWritablePath();
  1.1995 +
  1.1996 +  if (mPathBuilder) {
  1.1997 +    mPathBuilder->MoveTo(Point(x, y));
  1.1998 +    mPathBuilder->LineTo(Point(x + w, y));
  1.1999 +    mPathBuilder->LineTo(Point(x + w, y + h));
  1.2000 +    mPathBuilder->LineTo(Point(x, y + h));
  1.2001 +    mPathBuilder->Close();
  1.2002 +  } else {
  1.2003 +    mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
  1.2004 +    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y));
  1.2005 +    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y + h));
  1.2006 +    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
  1.2007 +    mDSPathBuilder->Close();
  1.2008 +  }
  1.2009 +}
  1.2010 +
  1.2011 +void
  1.2012 +CanvasRenderingContext2D::EnsureWritablePath()
  1.2013 +{
  1.2014 +  if (mDSPathBuilder) {
  1.2015 +    return;
  1.2016 +  }
  1.2017 +
  1.2018 +  FillRule fillRule = CurrentState().fillRule;
  1.2019 +
  1.2020 +  if (mPathBuilder) {
  1.2021 +    if (mPathTransformWillUpdate) {
  1.2022 +      mPath = mPathBuilder->Finish();
  1.2023 +      mDSPathBuilder =
  1.2024 +        mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
  1.2025 +      mPath = nullptr;
  1.2026 +      mPathBuilder = nullptr;
  1.2027 +      mPathTransformWillUpdate = false;
  1.2028 +    }
  1.2029 +    return;
  1.2030 +  }
  1.2031 +
  1.2032 +  EnsureTarget();
  1.2033 +  if (!mPath) {
  1.2034 +    NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
  1.2035 +    mPathBuilder = mTarget->CreatePathBuilder(fillRule);
  1.2036 +  } else if (!mPathTransformWillUpdate) {
  1.2037 +    mPathBuilder = mPath->CopyToBuilder(fillRule);
  1.2038 +  } else {
  1.2039 +    mDSPathBuilder =
  1.2040 +      mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
  1.2041 +    mPathTransformWillUpdate = false;
  1.2042 +    mPath = nullptr;
  1.2043 +  }
  1.2044 +}
  1.2045 +
  1.2046 +void
  1.2047 +CanvasRenderingContext2D::EnsureUserSpacePath(const CanvasWindingRule& winding)
  1.2048 +{
  1.2049 +  FillRule fillRule = CurrentState().fillRule;
  1.2050 +  if(winding == CanvasWindingRule::Evenodd)
  1.2051 +    fillRule = FillRule::FILL_EVEN_ODD;
  1.2052 +
  1.2053 +  if (!mPath && !mPathBuilder && !mDSPathBuilder) {
  1.2054 +    EnsureTarget();
  1.2055 +    mPathBuilder = mTarget->CreatePathBuilder(fillRule);
  1.2056 +  }
  1.2057 +
  1.2058 +  if (mPathBuilder) {
  1.2059 +    mPath = mPathBuilder->Finish();
  1.2060 +    mPathBuilder = nullptr;
  1.2061 +  }
  1.2062 +
  1.2063 +  if (mPath &&
  1.2064 +      mPathTransformWillUpdate) {
  1.2065 +    mDSPathBuilder =
  1.2066 +      mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
  1.2067 +    mPath = nullptr;
  1.2068 +    mPathTransformWillUpdate = false;
  1.2069 +  }
  1.2070 +
  1.2071 +  if (mDSPathBuilder) {
  1.2072 +    RefPtr<Path> dsPath;
  1.2073 +    dsPath = mDSPathBuilder->Finish();
  1.2074 +    mDSPathBuilder = nullptr;
  1.2075 +
  1.2076 +    Matrix inverse = mTarget->GetTransform();
  1.2077 +    if (!inverse.Invert()) {
  1.2078 +      NS_WARNING("Could not invert transform");
  1.2079 +      return;
  1.2080 +    }
  1.2081 +
  1.2082 +    mPathBuilder =
  1.2083 +      dsPath->TransformedCopyToBuilder(inverse, fillRule);
  1.2084 +    mPath = mPathBuilder->Finish();
  1.2085 +    mPathBuilder = nullptr;
  1.2086 +  }
  1.2087 +
  1.2088 +  if (mPath && mPath->GetFillRule() != fillRule) {
  1.2089 +    mPathBuilder = mPath->CopyToBuilder(fillRule);
  1.2090 +    mPath = mPathBuilder->Finish();
  1.2091 +    mPathBuilder = nullptr;
  1.2092 +  }
  1.2093 +
  1.2094 +  NS_ASSERTION(mPath, "mPath should exist");
  1.2095 +}
  1.2096 +
  1.2097 +void
  1.2098 +CanvasRenderingContext2D::TransformWillUpdate()
  1.2099 +{
  1.2100 +  EnsureTarget();
  1.2101 +
  1.2102 +  // Store the matrix that would transform the current path to device
  1.2103 +  // space.
  1.2104 +  if (mPath || mPathBuilder) {
  1.2105 +    if (!mPathTransformWillUpdate) {
  1.2106 +      // If the transform has already been updated, but a device space builder
  1.2107 +      // has not been created yet mPathToDS contains the right transform to
  1.2108 +      // transform the current mPath into device space.
  1.2109 +      // We should leave it alone.
  1.2110 +      mPathToDS = mTarget->GetTransform();
  1.2111 +    }
  1.2112 +    mPathTransformWillUpdate = true;
  1.2113 +  }
  1.2114 +}
  1.2115 +
  1.2116 +//
  1.2117 +// text
  1.2118 +//
  1.2119 +
  1.2120 +/**
  1.2121 + * Helper function for SetFont that creates a style rule for the given font.
  1.2122 + * @param aFont The CSS font string
  1.2123 + * @param aNode The canvas element
  1.2124 + * @param aResult Pointer in which to place the new style rule.
  1.2125 + * @remark Assumes all pointer arguments are non-null.
  1.2126 + */
  1.2127 +static nsresult
  1.2128 +CreateFontStyleRule(const nsAString& aFont,
  1.2129 +                    nsINode* aNode,
  1.2130 +                    StyleRule** aResult)
  1.2131 +{
  1.2132 +  nsRefPtr<StyleRule> rule;
  1.2133 +  bool changed;
  1.2134 +
  1.2135 +  nsIPrincipal* principal = aNode->NodePrincipal();
  1.2136 +  nsIDocument* document = aNode->OwnerDoc();
  1.2137 +
  1.2138 +  nsIURI* docURL = document->GetDocumentURI();
  1.2139 +  nsIURI* baseURL = document->GetDocBaseURI();
  1.2140 +
  1.2141 +  // Pass the CSS Loader object to the parser, to allow parser error reports
  1.2142 +  // to include the outer window ID.
  1.2143 +  nsCSSParser parser(document->CSSLoader());
  1.2144 +
  1.2145 +  nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
  1.2146 +                                           principal, getter_AddRefs(rule));
  1.2147 +  if (NS_FAILED(rv)) {
  1.2148 +    return rv;
  1.2149 +  }
  1.2150 +
  1.2151 +  rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
  1.2152 +                            principal, rule->GetDeclaration(), &changed,
  1.2153 +                            false);
  1.2154 +  if (NS_FAILED(rv))
  1.2155 +    return rv;
  1.2156 +
  1.2157 +  rv = parser.ParseProperty(eCSSProperty_line_height,
  1.2158 +                            NS_LITERAL_STRING("normal"), docURL, baseURL,
  1.2159 +                            principal, rule->GetDeclaration(), &changed,
  1.2160 +                            false);
  1.2161 +  if (NS_FAILED(rv)) {
  1.2162 +    return rv;
  1.2163 +  }
  1.2164 +
  1.2165 +  rule->RuleMatched();
  1.2166 +
  1.2167 +  rule.forget(aResult);
  1.2168 +  return NS_OK;
  1.2169 +}
  1.2170 +
  1.2171 +void
  1.2172 +CanvasRenderingContext2D::SetFont(const nsAString& font,
  1.2173 +                                  ErrorResult& error)
  1.2174 +{
  1.2175 +  /*
  1.2176 +    * If font is defined with relative units (e.g. ems) and the parent
  1.2177 +    * style context changes in between calls, setting the font to the
  1.2178 +    * same value as previous could result in a different computed value,
  1.2179 +    * so we cannot have the optimization where we check if the new font
  1.2180 +    * string is equal to the old one.
  1.2181 +    */
  1.2182 +
  1.2183 +  if (!mCanvasElement && !mDocShell) {
  1.2184 +    NS_WARNING("Canvas element must be non-null or a docshell must be provided");
  1.2185 +    error.Throw(NS_ERROR_FAILURE);
  1.2186 +    return;
  1.2187 +  }
  1.2188 +
  1.2189 +  nsIPresShell* presShell = GetPresShell();
  1.2190 +  if (!presShell) {
  1.2191 +    error.Throw(NS_ERROR_FAILURE);
  1.2192 +    return;
  1.2193 +  }
  1.2194 +  nsIDocument* document = presShell->GetDocument();
  1.2195 +
  1.2196 +  nsRefPtr<css::StyleRule> rule;
  1.2197 +  error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
  1.2198 +
  1.2199 +  if (error.Failed()) {
  1.2200 +    return;
  1.2201 +  }
  1.2202 +
  1.2203 +  css::Declaration *declaration = rule->GetDeclaration();
  1.2204 +  // The easiest way to see whether we got a syntax error or whether
  1.2205 +  // we got 'inherit' or 'initial' is to look at font-size-adjust,
  1.2206 +  // which the shorthand resets to either 'none' or
  1.2207 +  // '-moz-system-font'.
  1.2208 +  // We know the declaration is not !important, so we can use
  1.2209 +  // GetNormalBlock().
  1.2210 +  const nsCSSValue *fsaVal =
  1.2211 +    declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
  1.2212 +  if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
  1.2213 +                  fsaVal->GetUnit() != eCSSUnit_System_Font)) {
  1.2214 +      // We got an all-property value or a syntax error.  The spec says
  1.2215 +      // this value must be ignored.
  1.2216 +    return;
  1.2217 +  }
  1.2218 +
  1.2219 +  nsTArray< nsCOMPtr<nsIStyleRule> > rules;
  1.2220 +  rules.AppendElement(rule);
  1.2221 +
  1.2222 +  nsStyleSet* styleSet = presShell->StyleSet();
  1.2223 +
  1.2224 +  // have to get a parent style context for inherit-like relative
  1.2225 +  // values (2em, bolder, etc.)
  1.2226 +  nsRefPtr<nsStyleContext> parentContext;
  1.2227 +
  1.2228 +  if (mCanvasElement && mCanvasElement->IsInDoc()) {
  1.2229 +      // inherit from the canvas element
  1.2230 +      parentContext = nsComputedDOMStyle::GetStyleContextForElement(
  1.2231 +              mCanvasElement,
  1.2232 +              nullptr,
  1.2233 +              presShell);
  1.2234 +  } else {
  1.2235 +    // otherwise inherit from default (10px sans-serif)
  1.2236 +    nsRefPtr<css::StyleRule> parentRule;
  1.2237 +    error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
  1.2238 +                                document,
  1.2239 +                                getter_AddRefs(parentRule));
  1.2240 +
  1.2241 +    if (error.Failed()) {
  1.2242 +      return;
  1.2243 +    }
  1.2244 +
  1.2245 +    nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
  1.2246 +    parentRules.AppendElement(parentRule);
  1.2247 +    parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
  1.2248 +  }
  1.2249 +
  1.2250 +  if (!parentContext) {
  1.2251 +    error.Throw(NS_ERROR_FAILURE);
  1.2252 +    return;
  1.2253 +  }
  1.2254 +
  1.2255 +  // add a rule to prevent text zoom from affecting the style
  1.2256 +  rules.AppendElement(new nsDisableTextZoomStyleRule);
  1.2257 +
  1.2258 +  nsRefPtr<nsStyleContext> sc =
  1.2259 +      styleSet->ResolveStyleForRules(parentContext, rules);
  1.2260 +  if (!sc) {
  1.2261 +    error.Throw(NS_ERROR_FAILURE);
  1.2262 +    return;
  1.2263 +  }
  1.2264 +
  1.2265 +  const nsStyleFont* fontStyle = sc->StyleFont();
  1.2266 +
  1.2267 +  NS_ASSERTION(fontStyle, "Could not obtain font style");
  1.2268 +
  1.2269 +  nsIAtom* language = sc->StyleFont()->mLanguage;
  1.2270 +  if (!language) {
  1.2271 +    language = presShell->GetPresContext()->GetLanguageFromCharset();
  1.2272 +  }
  1.2273 +
  1.2274 +  // use CSS pixels instead of dev pixels to avoid being affected by page zoom
  1.2275 +  const uint32_t aupcp = nsPresContext::AppUnitsPerCSSPixel();
  1.2276 +
  1.2277 +  bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
  1.2278 +                      presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
  1.2279 +
  1.2280 +  // Purposely ignore the font size that respects the user's minimum
  1.2281 +  // font preference (fontStyle->mFont.size) in favor of the computed
  1.2282 +  // size (fontStyle->mSize).  See
  1.2283 +  // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
  1.2284 +  MOZ_ASSERT(!fontStyle->mAllowZoom,
  1.2285 +             "expected text zoom to be disabled on this nsStyleFont");
  1.2286 +  gfxFontStyle style(fontStyle->mFont.style,
  1.2287 +                     fontStyle->mFont.weight,
  1.2288 +                     fontStyle->mFont.stretch,
  1.2289 +                     NSAppUnitsToFloatPixels(fontStyle->mSize, float(aupcp)),
  1.2290 +                     language,
  1.2291 +                     fontStyle->mFont.sizeAdjust,
  1.2292 +                     fontStyle->mFont.systemFont,
  1.2293 +                     printerFont,
  1.2294 +                     fontStyle->mFont.languageOverride);
  1.2295 +
  1.2296 +  fontStyle->mFont.AddFontFeaturesToStyle(&style);
  1.2297 +
  1.2298 +  nsPresContext *c = presShell->GetPresContext();
  1.2299 +  CurrentState().fontGroup =
  1.2300 +      gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
  1.2301 +                                                  &style,
  1.2302 +                                                  c->GetUserFontSet());
  1.2303 +  NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
  1.2304 +  CurrentState().fontGroup->SetTextPerfMetrics(c->GetTextPerfMetrics());
  1.2305 +
  1.2306 +  // The font getter is required to be reserialized based on what we
  1.2307 +  // parsed (including having line-height removed).  (Older drafts of
  1.2308 +  // the spec required font sizes be converted to pixels, but that no
  1.2309 +  // longer seems to be required.)
  1.2310 +  declaration->GetValue(eCSSProperty_font, CurrentState().font);
  1.2311 +}
  1.2312 +
  1.2313 +void
  1.2314 +CanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
  1.2315 +{
  1.2316 +  if (ta.EqualsLiteral("start"))
  1.2317 +    CurrentState().textAlign = TextAlign::START;
  1.2318 +  else if (ta.EqualsLiteral("end"))
  1.2319 +    CurrentState().textAlign = TextAlign::END;
  1.2320 +  else if (ta.EqualsLiteral("left"))
  1.2321 +    CurrentState().textAlign = TextAlign::LEFT;
  1.2322 +  else if (ta.EqualsLiteral("right"))
  1.2323 +    CurrentState().textAlign = TextAlign::RIGHT;
  1.2324 +  else if (ta.EqualsLiteral("center"))
  1.2325 +    CurrentState().textAlign = TextAlign::CENTER;
  1.2326 +}
  1.2327 +
  1.2328 +void
  1.2329 +CanvasRenderingContext2D::GetTextAlign(nsAString& ta)
  1.2330 +{
  1.2331 +  switch (CurrentState().textAlign)
  1.2332 +  {
  1.2333 +  case TextAlign::START:
  1.2334 +    ta.AssignLiteral("start");
  1.2335 +    break;
  1.2336 +  case TextAlign::END:
  1.2337 +    ta.AssignLiteral("end");
  1.2338 +    break;
  1.2339 +  case TextAlign::LEFT:
  1.2340 +    ta.AssignLiteral("left");
  1.2341 +    break;
  1.2342 +  case TextAlign::RIGHT:
  1.2343 +    ta.AssignLiteral("right");
  1.2344 +    break;
  1.2345 +  case TextAlign::CENTER:
  1.2346 +    ta.AssignLiteral("center");
  1.2347 +    break;
  1.2348 +  }
  1.2349 +}
  1.2350 +
  1.2351 +void
  1.2352 +CanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
  1.2353 +{
  1.2354 +  if (tb.EqualsLiteral("top"))
  1.2355 +    CurrentState().textBaseline = TextBaseline::TOP;
  1.2356 +  else if (tb.EqualsLiteral("hanging"))
  1.2357 +    CurrentState().textBaseline = TextBaseline::HANGING;
  1.2358 +  else if (tb.EqualsLiteral("middle"))
  1.2359 +    CurrentState().textBaseline = TextBaseline::MIDDLE;
  1.2360 +  else if (tb.EqualsLiteral("alphabetic"))
  1.2361 +    CurrentState().textBaseline = TextBaseline::ALPHABETIC;
  1.2362 +  else if (tb.EqualsLiteral("ideographic"))
  1.2363 +    CurrentState().textBaseline = TextBaseline::IDEOGRAPHIC;
  1.2364 +  else if (tb.EqualsLiteral("bottom"))
  1.2365 +    CurrentState().textBaseline = TextBaseline::BOTTOM;
  1.2366 +}
  1.2367 +
  1.2368 +void
  1.2369 +CanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
  1.2370 +{
  1.2371 +  switch (CurrentState().textBaseline)
  1.2372 +  {
  1.2373 +  case TextBaseline::TOP:
  1.2374 +    tb.AssignLiteral("top");
  1.2375 +    break;
  1.2376 +  case TextBaseline::HANGING:
  1.2377 +    tb.AssignLiteral("hanging");
  1.2378 +    break;
  1.2379 +  case TextBaseline::MIDDLE:
  1.2380 +    tb.AssignLiteral("middle");
  1.2381 +    break;
  1.2382 +  case TextBaseline::ALPHABETIC:
  1.2383 +    tb.AssignLiteral("alphabetic");
  1.2384 +    break;
  1.2385 +  case TextBaseline::IDEOGRAPHIC:
  1.2386 +    tb.AssignLiteral("ideographic");
  1.2387 +    break;
  1.2388 +  case TextBaseline::BOTTOM:
  1.2389 +    tb.AssignLiteral("bottom");
  1.2390 +    break;
  1.2391 +  }
  1.2392 +}
  1.2393 +
  1.2394 +/*
  1.2395 + * Helper function that replaces the whitespace characters in a string
  1.2396 + * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
  1.2397 + * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
  1.2398 + * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
  1.2399 + * @param str The string whose whitespace characters to replace.
  1.2400 + */
  1.2401 +static inline void
  1.2402 +TextReplaceWhitespaceCharacters(nsAutoString& str)
  1.2403 +{
  1.2404 +  str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", char16_t(' '));
  1.2405 +}
  1.2406 +
  1.2407 +void
  1.2408 +CanvasRenderingContext2D::FillText(const nsAString& text, double x,
  1.2409 +                                   double y,
  1.2410 +                                   const Optional<double>& maxWidth,
  1.2411 +                                   ErrorResult& error)
  1.2412 +{
  1.2413 +  error = DrawOrMeasureText(text, x, y, maxWidth, TextDrawOperation::FILL, nullptr);
  1.2414 +}
  1.2415 +
  1.2416 +void
  1.2417 +CanvasRenderingContext2D::StrokeText(const nsAString& text, double x,
  1.2418 +                                     double y,
  1.2419 +                                     const Optional<double>& maxWidth,
  1.2420 +                                     ErrorResult& error)
  1.2421 +{
  1.2422 +  error = DrawOrMeasureText(text, x, y, maxWidth, TextDrawOperation::STROKE, nullptr);
  1.2423 +}
  1.2424 +
  1.2425 +TextMetrics*
  1.2426 +CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
  1.2427 +                                      ErrorResult& error)
  1.2428 +{
  1.2429 +  float width;
  1.2430 +  Optional<double> maxWidth;
  1.2431 +  error = DrawOrMeasureText(rawText, 0, 0, maxWidth, TextDrawOperation::MEASURE, &width);
  1.2432 +  if (error.Failed()) {
  1.2433 +    return nullptr;
  1.2434 +  }
  1.2435 +
  1.2436 +  return new TextMetrics(width);
  1.2437 +}
  1.2438 +
  1.2439 +void
  1.2440 +CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions& options, ErrorResult& error)
  1.2441 +{
  1.2442 +  // remove old hit region first
  1.2443 +  RemoveHitRegion(options.mId);
  1.2444 +
  1.2445 +  // for now, we require a fallback element
  1.2446 +  if (options.mControl == NULL) {
  1.2447 +    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  1.2448 +    return;
  1.2449 +  }
  1.2450 +
  1.2451 +  // check if the control is a descendant of our canvas
  1.2452 +  HTMLCanvasElement* canvas = GetCanvas();
  1.2453 +  bool isDescendant = true;
  1.2454 +  if (!canvas || !nsContentUtils::ContentIsDescendantOf(options.mControl, canvas)) {
  1.2455 +    isDescendant = false;
  1.2456 +  }
  1.2457 +
  1.2458 +  // check if the path is valid
  1.2459 +  EnsureUserSpacePath(CanvasWindingRule::Nonzero);
  1.2460 +  if(!mPath) {
  1.2461 +    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  1.2462 +    return;
  1.2463 +  }
  1.2464 +
  1.2465 +  // get the bounds of the current path. They are relative to the canvas
  1.2466 +  mgfx::Rect bounds(mPath->GetBounds(mTarget->GetTransform()));
  1.2467 +  if ((bounds.width == 0) || (bounds.height == 0) || !bounds.IsFinite()) {
  1.2468 +    // The specified region has no pixels.
  1.2469 +    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  1.2470 +    return;
  1.2471 +  }
  1.2472 +
  1.2473 +#ifdef ACCESSIBILITY
  1.2474 +  if (isDescendant) {
  1.2475 +    nsRect* nsBounds = new nsRect();
  1.2476 +    gfxRect rect(bounds.x, bounds.y, bounds.width, bounds.height);
  1.2477 +    *nsBounds = nsLayoutUtils::RoundGfxRectToAppRect(rect, AppUnitsPerCSSPixel());
  1.2478 +    options.mControl->DeleteProperty(nsGkAtoms::hitregion);
  1.2479 +    options.mControl->SetProperty(nsGkAtoms::hitregion, nsBounds,
  1.2480 +                                  nsINode::DeleteProperty<nsRect>);
  1.2481 +  }
  1.2482 +#endif
  1.2483 +
  1.2484 +  // finally, add the region to the list if it has an ID
  1.2485 +  if (options.mId.Length() != 0) {
  1.2486 +    mHitRegionsOptions.PutEntry(options.mId)->mElement = options.mControl;
  1.2487 +  }
  1.2488 +}
  1.2489 +
  1.2490 +void
  1.2491 +CanvasRenderingContext2D::RemoveHitRegion(const nsAString& id)
  1.2492 +{
  1.2493 +  RegionInfo* info = mHitRegionsOptions.GetEntry(id);
  1.2494 +  if (!info) {
  1.2495 +    return;
  1.2496 +  }
  1.2497 +
  1.2498 +#ifdef ACCESSIBILITY
  1.2499 +  info->mElement->DeleteProperty(nsGkAtoms::hitregion);
  1.2500 +#endif
  1.2501 +  mHitRegionsOptions.RemoveEntry(id);
  1.2502 +}
  1.2503 +
  1.2504 +/**
  1.2505 + * Used for nsBidiPresUtils::ProcessText
  1.2506 + */
  1.2507 +struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
  1.2508 +{
  1.2509 +  typedef CanvasRenderingContext2D::ContextState ContextState;
  1.2510 +
  1.2511 +  virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
  1.2512 +  {
  1.2513 +    mFontgrp->UpdateFontList(); // ensure user font generation is current
  1.2514 +    mTextRun = mFontgrp->MakeTextRun(text,
  1.2515 +                                     length,
  1.2516 +                                     mThebes,
  1.2517 +                                     mAppUnitsPerDevPixel,
  1.2518 +                                     direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
  1.2519 +  }
  1.2520 +
  1.2521 +  virtual nscoord GetWidth()
  1.2522 +  {
  1.2523 +    gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
  1.2524 +                                                               mTextRun->GetLength(),
  1.2525 +                                                               mDoMeasureBoundingBox ?
  1.2526 +                                                                 gfxFont::TIGHT_INK_EXTENTS :
  1.2527 +                                                                 gfxFont::LOOSE_INK_EXTENTS,
  1.2528 +                                                               mThebes,
  1.2529 +                                                               nullptr);
  1.2530 +
  1.2531 +    // this only measures the height; the total width is gotten from the
  1.2532 +    // the return value of ProcessText.
  1.2533 +    if (mDoMeasureBoundingBox) {
  1.2534 +      textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
  1.2535 +      mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
  1.2536 +    }
  1.2537 +
  1.2538 +    return NSToCoordRound(textRunMetrics.mAdvanceWidth);
  1.2539 +  }
  1.2540 +
  1.2541 +  virtual void DrawText(nscoord xOffset, nscoord width)
  1.2542 +  {
  1.2543 +    gfxPoint point = mPt;
  1.2544 +    point.x += xOffset;
  1.2545 +
  1.2546 +    // offset is given in terms of left side of string
  1.2547 +    if (mTextRun->IsRightToLeft()) {
  1.2548 +      // Bug 581092 - don't use rounded pixel width to advance to
  1.2549 +      // right-hand end of run, because this will cause different
  1.2550 +      // glyph positioning for LTR vs RTL drawing of the same
  1.2551 +      // glyph string on OS X and DWrite where textrun widths may
  1.2552 +      // involve fractional pixels.
  1.2553 +      gfxTextRun::Metrics textRunMetrics =
  1.2554 +        mTextRun->MeasureText(0,
  1.2555 +                              mTextRun->GetLength(),
  1.2556 +                              mDoMeasureBoundingBox ?
  1.2557 +                                  gfxFont::TIGHT_INK_EXTENTS :
  1.2558 +                                  gfxFont::LOOSE_INK_EXTENTS,
  1.2559 +                              mThebes,
  1.2560 +                              nullptr);
  1.2561 +      point.x += textRunMetrics.mAdvanceWidth;
  1.2562 +      // old code was:
  1.2563 +      //   point.x += width * mAppUnitsPerDevPixel;
  1.2564 +      // TODO: restore this if/when we move to fractional coords
  1.2565 +      // throughout the text layout process
  1.2566 +    }
  1.2567 +
  1.2568 +    uint32_t numRuns;
  1.2569 +    const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
  1.2570 +    const int32_t appUnitsPerDevUnit = mAppUnitsPerDevPixel;
  1.2571 +    const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
  1.2572 +    Point baselineOrigin =
  1.2573 +      Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
  1.2574 +
  1.2575 +    float advanceSum = 0;
  1.2576 +
  1.2577 +    mCtx->EnsureTarget();
  1.2578 +    for (uint32_t c = 0; c < numRuns; c++) {
  1.2579 +      gfxFont *font = runs[c].mFont;
  1.2580 +      uint32_t endRun = 0;
  1.2581 +      if (c + 1 < numRuns) {
  1.2582 +        endRun = runs[c + 1].mCharacterOffset;
  1.2583 +      } else {
  1.2584 +        endRun = mTextRun->GetLength();
  1.2585 +      }
  1.2586 +
  1.2587 +      const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
  1.2588 +
  1.2589 +      RefPtr<ScaledFont> scaledFont =
  1.2590 +        gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
  1.2591 +
  1.2592 +      if (!scaledFont) {
  1.2593 +        // This can occur when something switched DirectWrite off.
  1.2594 +        return;
  1.2595 +      }
  1.2596 +
  1.2597 +      RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
  1.2598 +
  1.2599 +      GlyphBuffer buffer;
  1.2600 +
  1.2601 +      std::vector<Glyph> glyphBuf;
  1.2602 +
  1.2603 +      for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
  1.2604 +        Glyph newGlyph;
  1.2605 +        if (glyphs[i].IsSimpleGlyph()) {
  1.2606 +          newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
  1.2607 +          if (mTextRun->IsRightToLeft()) {
  1.2608 +            newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
  1.2609 +              glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
  1.2610 +          } else {
  1.2611 +            newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
  1.2612 +          }
  1.2613 +          newGlyph.mPosition.y = baselineOrigin.y;
  1.2614 +          advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
  1.2615 +          glyphBuf.push_back(newGlyph);
  1.2616 +          continue;
  1.2617 +        }
  1.2618 +
  1.2619 +        if (!glyphs[i].GetGlyphCount()) {
  1.2620 +          continue;
  1.2621 +        }
  1.2622 +
  1.2623 +        gfxTextRun::DetailedGlyph *detailedGlyphs =
  1.2624 +          mTextRun->GetDetailedGlyphs(i);
  1.2625 +
  1.2626 +        if (glyphs[i].IsMissing()) {
  1.2627 +          newGlyph.mIndex = 0;
  1.2628 +          if (mTextRun->IsRightToLeft()) {
  1.2629 +            newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
  1.2630 +              detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
  1.2631 +          } else {
  1.2632 +            newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
  1.2633 +          }
  1.2634 +          newGlyph.mPosition.y = baselineOrigin.y;
  1.2635 +          advanceSum += detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
  1.2636 +          glyphBuf.push_back(newGlyph);
  1.2637 +          continue;
  1.2638 +        }
  1.2639 +
  1.2640 +        for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++) {
  1.2641 +          newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
  1.2642 +          if (mTextRun->IsRightToLeft()) {
  1.2643 +            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
  1.2644 +              advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
  1.2645 +          } else {
  1.2646 +            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
  1.2647 +          }
  1.2648 +          newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
  1.2649 +          glyphBuf.push_back(newGlyph);
  1.2650 +          advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
  1.2651 +        }
  1.2652 +      }
  1.2653 +
  1.2654 +      if (!glyphBuf.size()) {
  1.2655 +        // This may happen for glyph runs for a 0 size font.
  1.2656 +        continue;
  1.2657 +      }
  1.2658 +
  1.2659 +      buffer.mGlyphs = &glyphBuf.front();
  1.2660 +      buffer.mNumGlyphs = glyphBuf.size();
  1.2661 +
  1.2662 +      Rect bounds = mCtx->mTarget->GetTransform().
  1.2663 +        TransformBounds(Rect(mBoundingBox.x, mBoundingBox.y,
  1.2664 +                             mBoundingBox.width, mBoundingBox.height));
  1.2665 +      if (mOp == CanvasRenderingContext2D::TextDrawOperation::FILL) {
  1.2666 +        AdjustedTarget(mCtx, &bounds)->
  1.2667 +          FillGlyphs(scaledFont, buffer,
  1.2668 +                     CanvasGeneralPattern().
  1.2669 +                       ForStyle(mCtx, CanvasRenderingContext2D::Style::FILL, mCtx->mTarget),
  1.2670 +                     DrawOptions(mState->globalAlpha, mCtx->UsedOperation()),
  1.2671 +                     renderingOptions);
  1.2672 +      } else if (mOp == CanvasRenderingContext2D::TextDrawOperation::STROKE) {
  1.2673 +        // stroke glyphs one at a time to avoid poor CoreGraphics performance
  1.2674 +        // when stroking a path with a very large number of points
  1.2675 +        buffer.mGlyphs = &glyphBuf.front();
  1.2676 +        buffer.mNumGlyphs = 1;
  1.2677 +        const ContextState& state = *mState;
  1.2678 +        AdjustedTarget target(mCtx, &bounds);
  1.2679 +        const StrokeOptions strokeOpts(state.lineWidth, state.lineJoin,
  1.2680 +                                       state.lineCap, state.miterLimit,
  1.2681 +                                       state.dash.Length(),
  1.2682 +                                       state.dash.Elements(),
  1.2683 +                                       state.dashOffset);
  1.2684 +        CanvasGeneralPattern cgp;
  1.2685 +        const Pattern& patForStyle
  1.2686 +          (cgp.ForStyle(mCtx, CanvasRenderingContext2D::Style::STROKE, mCtx->mTarget));
  1.2687 +        const DrawOptions drawOpts(state.globalAlpha, mCtx->UsedOperation());
  1.2688 +
  1.2689 +        for (unsigned i = glyphBuf.size(); i > 0; --i) {
  1.2690 +          RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
  1.2691 +          target->Stroke(path, patForStyle, strokeOpts, drawOpts);
  1.2692 +          buffer.mGlyphs++;
  1.2693 +        }
  1.2694 +      }
  1.2695 +    }
  1.2696 +  }
  1.2697 +
  1.2698 +  // current text run
  1.2699 +  nsAutoPtr<gfxTextRun> mTextRun;
  1.2700 +
  1.2701 +  // pointer to a screen reference context used to measure text and such
  1.2702 +  nsRefPtr<gfxContext> mThebes;
  1.2703 +
  1.2704 +  // Pointer to the draw target we should fill our text to
  1.2705 +  CanvasRenderingContext2D *mCtx;
  1.2706 +
  1.2707 +  // position of the left side of the string, alphabetic baseline
  1.2708 +  gfxPoint mPt;
  1.2709 +
  1.2710 +  // current font
  1.2711 +  gfxFontGroup* mFontgrp;
  1.2712 +
  1.2713 +  // dev pixel conversion factor
  1.2714 +  int32_t mAppUnitsPerDevPixel;
  1.2715 +
  1.2716 +  // operation (fill or stroke)
  1.2717 +  CanvasRenderingContext2D::TextDrawOperation mOp;
  1.2718 +
  1.2719 +  // context state
  1.2720 +  ContextState *mState;
  1.2721 +
  1.2722 +  // union of bounding boxes of all runs, needed for shadows
  1.2723 +  gfxRect mBoundingBox;
  1.2724 +
  1.2725 +  // true iff the bounding box should be measured
  1.2726 +  bool mDoMeasureBoundingBox;
  1.2727 +};
  1.2728 +
  1.2729 +nsresult
  1.2730 +CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
  1.2731 +                                            float aX,
  1.2732 +                                            float aY,
  1.2733 +                                            const Optional<double>& aMaxWidth,
  1.2734 +                                            TextDrawOperation aOp,
  1.2735 +                                            float* aWidth)
  1.2736 +{
  1.2737 +  nsresult rv;
  1.2738 +
  1.2739 +  // spec isn't clear on what should happen if aMaxWidth <= 0, so
  1.2740 +  // treat it as an invalid argument
  1.2741 +  // technically, 0 should be an invalid value as well, but 0 is the default
  1.2742 +  // arg, and there is no way to tell if the default was used
  1.2743 +  if (aMaxWidth.WasPassed() && aMaxWidth.Value() < 0)
  1.2744 +    return NS_ERROR_INVALID_ARG;
  1.2745 +
  1.2746 +  if (!mCanvasElement && !mDocShell) {
  1.2747 +    NS_WARNING("Canvas element must be non-null or a docshell must be provided");
  1.2748 +    return NS_ERROR_FAILURE;
  1.2749 +  }
  1.2750 +
  1.2751 +  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
  1.2752 +  if (!presShell)
  1.2753 +    return NS_ERROR_FAILURE;
  1.2754 +
  1.2755 +  nsIDocument* document = presShell->GetDocument();
  1.2756 +
  1.2757 +  // replace all the whitespace characters with U+0020 SPACE
  1.2758 +  nsAutoString textToDraw(aRawText);
  1.2759 +  TextReplaceWhitespaceCharacters(textToDraw);
  1.2760 +
  1.2761 +  // for now, default to ltr if not in doc
  1.2762 +  bool isRTL = false;
  1.2763 +
  1.2764 +  if (mCanvasElement && mCanvasElement->IsInDoc()) {
  1.2765 +    // try to find the closest context
  1.2766 +    nsRefPtr<nsStyleContext> canvasStyle =
  1.2767 +      nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
  1.2768 +                                                    nullptr,
  1.2769 +                                                    presShell);
  1.2770 +    if (!canvasStyle) {
  1.2771 +      return NS_ERROR_FAILURE;
  1.2772 +    }
  1.2773 +
  1.2774 +    isRTL = canvasStyle->StyleVisibility()->mDirection ==
  1.2775 +      NS_STYLE_DIRECTION_RTL;
  1.2776 +  } else {
  1.2777 +    isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
  1.2778 +  }
  1.2779 +
  1.2780 +  gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
  1.2781 +  NS_ASSERTION(currentFontStyle, "font group is null");
  1.2782 +
  1.2783 +  // ensure user font set is up to date
  1.2784 +  currentFontStyle->
  1.2785 +    SetUserFontSet(presShell->GetPresContext()->GetUserFontSet());
  1.2786 +
  1.2787 +  if (currentFontStyle->GetStyle()->size == 0.0F) {
  1.2788 +    if (aWidth) {
  1.2789 +      *aWidth = 0;
  1.2790 +    }
  1.2791 +    return NS_OK;
  1.2792 +  }
  1.2793 +
  1.2794 +  const ContextState &state = CurrentState();
  1.2795 +
  1.2796 +  // This is only needed to know if we can know the drawing bounding box easily.
  1.2797 +  bool doDrawShadow = NeedToDrawShadow();
  1.2798 +
  1.2799 +  CanvasBidiProcessor processor;
  1.2800 +
  1.2801 +  GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
  1.2802 +  processor.mPt = gfxPoint(aX, aY);
  1.2803 +  processor.mThebes =
  1.2804 +    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
  1.2805 +
  1.2806 +  // If we don't have a target then we don't have a transform. A target won't
  1.2807 +  // be needed in the case where we're measuring the text size. This allows
  1.2808 +  // to avoid creating a target if it's only being used to measure text sizes.
  1.2809 +  if (mTarget) {
  1.2810 +    Matrix matrix = mTarget->GetTransform();
  1.2811 +    processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
  1.2812 +  }
  1.2813 +  processor.mCtx = this;
  1.2814 +  processor.mOp = aOp;
  1.2815 +  processor.mBoundingBox = gfxRect(0, 0, 0, 0);
  1.2816 +  processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
  1.2817 +  processor.mState = &CurrentState();
  1.2818 +  processor.mFontgrp = currentFontStyle;
  1.2819 +
  1.2820 +  nscoord totalWidthCoord;
  1.2821 +
  1.2822 +  // calls bidi algo twice since it needs the full text width and the
  1.2823 +  // bounding boxes before rendering anything
  1.2824 +  nsBidi bidiEngine;
  1.2825 +  rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
  1.2826 +                                    textToDraw.Length(),
  1.2827 +                                    isRTL ? NSBIDI_RTL : NSBIDI_LTR,
  1.2828 +                                    presShell->GetPresContext(),
  1.2829 +                                    processor,
  1.2830 +                                    nsBidiPresUtils::MODE_MEASURE,
  1.2831 +                                    nullptr,
  1.2832 +                                    0,
  1.2833 +                                    &totalWidthCoord,
  1.2834 +                                    &bidiEngine);
  1.2835 +  if (NS_FAILED(rv)) {
  1.2836 +    return rv;
  1.2837 +  }
  1.2838 +
  1.2839 +  float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
  1.2840 +  if (aWidth) {
  1.2841 +    *aWidth = totalWidth;
  1.2842 +  }
  1.2843 +
  1.2844 +  // if only measuring, don't need to do any more work
  1.2845 +  if (aOp==TextDrawOperation::MEASURE) {
  1.2846 +    return NS_OK;
  1.2847 +  }
  1.2848 +
  1.2849 +  // offset pt.x based on text align
  1.2850 +  gfxFloat anchorX;
  1.2851 +
  1.2852 +  if (state.textAlign == TextAlign::CENTER) {
  1.2853 +    anchorX = .5;
  1.2854 +  } else if (state.textAlign == TextAlign::LEFT ||
  1.2855 +            (!isRTL && state.textAlign == TextAlign::START) ||
  1.2856 +            (isRTL && state.textAlign == TextAlign::END)) {
  1.2857 +    anchorX = 0;
  1.2858 +  } else {
  1.2859 +    anchorX = 1;
  1.2860 +  }
  1.2861 +
  1.2862 +  processor.mPt.x -= anchorX * totalWidth;
  1.2863 +
  1.2864 +  // offset pt.y based on text baseline
  1.2865 +  processor.mFontgrp->UpdateFontList(); // ensure user font generation is current
  1.2866 +  NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
  1.2867 +  const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
  1.2868 +
  1.2869 +  gfxFloat anchorY;
  1.2870 +
  1.2871 +  switch (state.textBaseline)
  1.2872 +  {
  1.2873 +  case TextBaseline::HANGING:
  1.2874 +      // fall through; best we can do with the information available
  1.2875 +  case TextBaseline::TOP:
  1.2876 +    anchorY = fontMetrics.emAscent;
  1.2877 +    break;
  1.2878 +  case TextBaseline::MIDDLE:
  1.2879 +    anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
  1.2880 +    break;
  1.2881 +  case TextBaseline::IDEOGRAPHIC:
  1.2882 +    // fall through; best we can do with the information available
  1.2883 +  case TextBaseline::ALPHABETIC:
  1.2884 +    anchorY = 0;
  1.2885 +    break;
  1.2886 +  case TextBaseline::BOTTOM:
  1.2887 +    anchorY = -fontMetrics.emDescent;
  1.2888 +    break;
  1.2889 +  default:
  1.2890 +    MOZ_CRASH("unexpected TextBaseline");
  1.2891 +  }
  1.2892 +
  1.2893 +  processor.mPt.y += anchorY;
  1.2894 +
  1.2895 +  // correct bounding box to get it to be the correct size/position
  1.2896 +  processor.mBoundingBox.width = totalWidth;
  1.2897 +  processor.mBoundingBox.MoveBy(processor.mPt);
  1.2898 +
  1.2899 +  processor.mPt.x *= processor.mAppUnitsPerDevPixel;
  1.2900 +  processor.mPt.y *= processor.mAppUnitsPerDevPixel;
  1.2901 +
  1.2902 +  EnsureTarget();
  1.2903 +  Matrix oldTransform = mTarget->GetTransform();
  1.2904 +  // if text is over aMaxWidth, then scale the text horizontally such that its
  1.2905 +  // width is precisely aMaxWidth
  1.2906 +  if (aMaxWidth.WasPassed() && aMaxWidth.Value() > 0 &&
  1.2907 +      totalWidth > aMaxWidth.Value()) {
  1.2908 +    Matrix newTransform = oldTransform;
  1.2909 +
  1.2910 +    // Translate so that the anchor point is at 0,0, then scale and then
  1.2911 +    // translate back.
  1.2912 +    newTransform.Translate(aX, 0);
  1.2913 +    newTransform.Scale(aMaxWidth.Value() / totalWidth, 1);
  1.2914 +    newTransform.Translate(-aX, 0);
  1.2915 +    /* we do this to avoid an ICE in the android compiler */
  1.2916 +    Matrix androidCompilerBug = newTransform;
  1.2917 +    mTarget->SetTransform(androidCompilerBug);
  1.2918 +  }
  1.2919 +
  1.2920 +  // save the previous bounding box
  1.2921 +  gfxRect boundingBox = processor.mBoundingBox;
  1.2922 +
  1.2923 +  // don't ever need to measure the bounding box twice
  1.2924 +  processor.mDoMeasureBoundingBox = false;
  1.2925 +
  1.2926 +  rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
  1.2927 +                                    textToDraw.Length(),
  1.2928 +                                    isRTL ? NSBIDI_RTL : NSBIDI_LTR,
  1.2929 +                                    presShell->GetPresContext(),
  1.2930 +                                    processor,
  1.2931 +                                    nsBidiPresUtils::MODE_DRAW,
  1.2932 +                                    nullptr,
  1.2933 +                                    0,
  1.2934 +                                    nullptr,
  1.2935 +                                    &bidiEngine);
  1.2936 +
  1.2937 +
  1.2938 +  mTarget->SetTransform(oldTransform);
  1.2939 +
  1.2940 +  if (aOp == CanvasRenderingContext2D::TextDrawOperation::FILL &&
  1.2941 +      !doDrawShadow) {
  1.2942 +    RedrawUser(boundingBox);
  1.2943 +    return NS_OK;
  1.2944 +  }
  1.2945 +
  1.2946 +  Redraw();
  1.2947 +  return NS_OK;
  1.2948 +}
  1.2949 +
  1.2950 +gfxFontGroup *CanvasRenderingContext2D::GetCurrentFontStyle()
  1.2951 +{
  1.2952 +  // use lazy initilization for the font group since it's rather expensive
  1.2953 +  if (!CurrentState().fontGroup) {
  1.2954 +    ErrorResult err;
  1.2955 +    NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
  1.2956 +    static float kDefaultFontSize = 10.0;
  1.2957 +    SetFont(kDefaultFontStyle, err);
  1.2958 +    if (err.Failed()) {
  1.2959 +      gfxFontStyle style;
  1.2960 +      style.size = kDefaultFontSize;
  1.2961 +      CurrentState().fontGroup =
  1.2962 +        gfxPlatform::GetPlatform()->CreateFontGroup(NS_LITERAL_STRING("sans-serif"),
  1.2963 +                                                    &style,
  1.2964 +                                                    nullptr);
  1.2965 +      if (CurrentState().fontGroup) {
  1.2966 +        CurrentState().font = kDefaultFontStyle;
  1.2967 +
  1.2968 +        nsIPresShell* presShell = GetPresShell();
  1.2969 +        if (presShell) {
  1.2970 +          CurrentState().fontGroup->SetTextPerfMetrics(
  1.2971 +            presShell->GetPresContext()->GetTextPerfMetrics());
  1.2972 +        }
  1.2973 +      } else {
  1.2974 +        NS_ERROR("Default canvas font is invalid");
  1.2975 +      }
  1.2976 +    }
  1.2977 +
  1.2978 +  }
  1.2979 +
  1.2980 +  return CurrentState().fontGroup;
  1.2981 +}
  1.2982 +
  1.2983 +//
  1.2984 +// line caps/joins
  1.2985 +//
  1.2986 +
  1.2987 +void
  1.2988 +CanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
  1.2989 +{
  1.2990 +  CapStyle cap;
  1.2991 +
  1.2992 +  if (capstyle.EqualsLiteral("butt")) {
  1.2993 +    cap = CapStyle::BUTT;
  1.2994 +  } else if (capstyle.EqualsLiteral("round")) {
  1.2995 +    cap = CapStyle::ROUND;
  1.2996 +  } else if (capstyle.EqualsLiteral("square")) {
  1.2997 +    cap = CapStyle::SQUARE;
  1.2998 +  } else {
  1.2999 +    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.3000 +    return;
  1.3001 +  }
  1.3002 +
  1.3003 +  CurrentState().lineCap = cap;
  1.3004 +}
  1.3005 +
  1.3006 +void
  1.3007 +CanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
  1.3008 +{
  1.3009 +  switch (CurrentState().lineCap) {
  1.3010 +  case CapStyle::BUTT:
  1.3011 +    capstyle.AssignLiteral("butt");
  1.3012 +    break;
  1.3013 +  case CapStyle::ROUND:
  1.3014 +    capstyle.AssignLiteral("round");
  1.3015 +    break;
  1.3016 +  case CapStyle::SQUARE:
  1.3017 +    capstyle.AssignLiteral("square");
  1.3018 +    break;
  1.3019 +  }
  1.3020 +}
  1.3021 +
  1.3022 +void
  1.3023 +CanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
  1.3024 +{
  1.3025 +  JoinStyle j;
  1.3026 +
  1.3027 +  if (joinstyle.EqualsLiteral("round")) {
  1.3028 +    j = JoinStyle::ROUND;
  1.3029 +  } else if (joinstyle.EqualsLiteral("bevel")) {
  1.3030 +    j = JoinStyle::BEVEL;
  1.3031 +  } else if (joinstyle.EqualsLiteral("miter")) {
  1.3032 +    j = JoinStyle::MITER_OR_BEVEL;
  1.3033 +  } else {
  1.3034 +    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.3035 +    return;
  1.3036 +  }
  1.3037 +
  1.3038 +  CurrentState().lineJoin = j;
  1.3039 +}
  1.3040 +
  1.3041 +void
  1.3042 +CanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle, ErrorResult& error)
  1.3043 +{
  1.3044 +  switch (CurrentState().lineJoin) {
  1.3045 +  case JoinStyle::ROUND:
  1.3046 +    joinstyle.AssignLiteral("round");
  1.3047 +    break;
  1.3048 +  case JoinStyle::BEVEL:
  1.3049 +    joinstyle.AssignLiteral("bevel");
  1.3050 +    break;
  1.3051 +  case JoinStyle::MITER_OR_BEVEL:
  1.3052 +    joinstyle.AssignLiteral("miter");
  1.3053 +    break;
  1.3054 +  default:
  1.3055 +    error.Throw(NS_ERROR_FAILURE);
  1.3056 +  }
  1.3057 +}
  1.3058 +
  1.3059 +void
  1.3060 +CanvasRenderingContext2D::SetMozDash(JSContext* cx,
  1.3061 +                                     const JS::Value& mozDash,
  1.3062 +                                     ErrorResult& error)
  1.3063 +{
  1.3064 +  FallibleTArray<Float> dash;
  1.3065 +  error = JSValToDashArray(cx, mozDash, dash);
  1.3066 +  if (!error.Failed()) {
  1.3067 +    ContextState& state = CurrentState();
  1.3068 +    state.dash = dash;
  1.3069 +    if (state.dash.IsEmpty()) {
  1.3070 +      state.dashOffset = 0;
  1.3071 +    }
  1.3072 +  }
  1.3073 +}
  1.3074 +
  1.3075 +void
  1.3076 +CanvasRenderingContext2D::GetMozDash(JSContext* cx,
  1.3077 +                                     JS::MutableHandle<JS::Value> retval,
  1.3078 +                                     ErrorResult& error)
  1.3079 +{
  1.3080 +  DashArrayToJSVal(CurrentState().dash, cx, retval, error);
  1.3081 +}
  1.3082 +
  1.3083 +void
  1.3084 +CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
  1.3085 +{
  1.3086 +  ContextState& state = CurrentState();
  1.3087 +  if (!state.dash.IsEmpty()) {
  1.3088 +    state.dashOffset = mozDashOffset;
  1.3089 +  }
  1.3090 +}
  1.3091 +
  1.3092 +void
  1.3093 +CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments)
  1.3094 +{
  1.3095 +  FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
  1.3096 +  dash.Clear();
  1.3097 +
  1.3098 +  for (uint32_t x = 0; x < aSegments.Length(); x++) {
  1.3099 +    dash.AppendElement(aSegments[x]);
  1.3100 +  }
  1.3101 +  if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
  1.3102 +    for (uint32_t x = 0; x < aSegments.Length(); x++) {
  1.3103 +      dash.AppendElement(aSegments[x]);
  1.3104 +    }
  1.3105 +  }
  1.3106 +}
  1.3107 +
  1.3108 +void
  1.3109 +CanvasRenderingContext2D::GetLineDash(nsTArray<double>& aSegments) const {
  1.3110 +  const FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
  1.3111 +  aSegments.Clear();
  1.3112 +
  1.3113 +  for (uint32_t x = 0; x < dash.Length(); x++) {
  1.3114 +    aSegments.AppendElement(dash[x]);
  1.3115 +  }
  1.3116 +}
  1.3117 +
  1.3118 +void
  1.3119 +CanvasRenderingContext2D::SetLineDashOffset(double mOffset) {
  1.3120 +  CurrentState().dashOffset = mOffset;
  1.3121 +}
  1.3122 +
  1.3123 +double
  1.3124 +CanvasRenderingContext2D::LineDashOffset() const {
  1.3125 +  return CurrentState().dashOffset;
  1.3126 +}
  1.3127 +
  1.3128 +bool
  1.3129 +CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, double x, double y, const CanvasWindingRule& winding)
  1.3130 +{
  1.3131 +  if (!FloatValidate(x,y)) {
  1.3132 +    return false;
  1.3133 +  }
  1.3134 +
  1.3135 +  // Check for site-specific permission and return false if no permission.
  1.3136 +  if (mCanvasElement) {
  1.3137 +    nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  1.3138 +    if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  1.3139 +      return false;
  1.3140 +  }
  1.3141 +
  1.3142 +  EnsureUserSpacePath(winding);
  1.3143 +  if (!mPath) {
  1.3144 +    return false;
  1.3145 +  }
  1.3146 +
  1.3147 +  if (mPathTransformWillUpdate) {
  1.3148 +    return mPath->ContainsPoint(Point(x, y), mPathToDS);
  1.3149 +  }
  1.3150 +
  1.3151 +  return mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
  1.3152 +}
  1.3153 +
  1.3154 +bool CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, const CanvasPath& mPath, double x, double y, const CanvasWindingRule& mWinding)
  1.3155 +{
  1.3156 +  if (!FloatValidate(x,y)) {
  1.3157 +    return false;
  1.3158 +  }
  1.3159 +
  1.3160 +  // Check for site-specific permission and return false if no permission.
  1.3161 +  if (mCanvasElement) {
  1.3162 +    nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  1.3163 +    if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  1.3164 +      return false;
  1.3165 +  }
  1.3166 +
  1.3167 +  EnsureTarget();
  1.3168 +  RefPtr<gfx::Path> tempPath = mPath.GetPath(mWinding, mTarget);
  1.3169 +
  1.3170 +  return tempPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
  1.3171 +}
  1.3172 +
  1.3173 +bool
  1.3174 +CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, double x, double y)
  1.3175 +{
  1.3176 +  if (!FloatValidate(x,y)) {
  1.3177 +    return false;
  1.3178 +  }
  1.3179 +
  1.3180 +  // Check for site-specific permission and return false if no permission.
  1.3181 +  if (mCanvasElement) {
  1.3182 +    nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  1.3183 +    if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  1.3184 +      return false;
  1.3185 +  }
  1.3186 +
  1.3187 +  EnsureUserSpacePath();
  1.3188 +  if (!mPath) {
  1.3189 +    return false;
  1.3190 +  }
  1.3191 +
  1.3192 +  const ContextState &state = CurrentState();
  1.3193 +
  1.3194 +  StrokeOptions strokeOptions(state.lineWidth,
  1.3195 +                              state.lineJoin,
  1.3196 +                              state.lineCap,
  1.3197 +                              state.miterLimit,
  1.3198 +                              state.dash.Length(),
  1.3199 +                              state.dash.Elements(),
  1.3200 +                              state.dashOffset);
  1.3201 +
  1.3202 +  if (mPathTransformWillUpdate) {
  1.3203 +    return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mPathToDS);
  1.3204 +  }
  1.3205 +  return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
  1.3206 +}
  1.3207 +
  1.3208 +bool CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, const CanvasPath& mPath, double x, double y)
  1.3209 +{
  1.3210 +  if (!FloatValidate(x,y)) {
  1.3211 +    return false;
  1.3212 +  }
  1.3213 +
  1.3214 +  // Check for site-specific permission and return false if no permission.
  1.3215 +  if (mCanvasElement) {
  1.3216 +    nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  1.3217 +    if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
  1.3218 +      return false;
  1.3219 +  }
  1.3220 +
  1.3221 +  EnsureTarget();
  1.3222 +  RefPtr<gfx::Path> tempPath = mPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
  1.3223 +
  1.3224 +  const ContextState &state = CurrentState();
  1.3225 +
  1.3226 +  StrokeOptions strokeOptions(state.lineWidth,
  1.3227 +                              state.lineJoin,
  1.3228 +                              state.lineCap,
  1.3229 +                              state.miterLimit,
  1.3230 +                              state.dash.Length(),
  1.3231 +                              state.dash.Elements(),
  1.3232 +                              state.dashOffset);
  1.3233 +
  1.3234 +  return tempPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
  1.3235 +}
  1.3236 +
  1.3237 +//
  1.3238 +// image
  1.3239 +//
  1.3240 +
  1.3241 +// drawImage(in HTMLImageElement image, in float dx, in float dy);
  1.3242 +//   -- render image from 0,0 at dx,dy top-left coords
  1.3243 +// drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
  1.3244 +//   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
  1.3245 +// 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);
  1.3246 +//   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
  1.3247 +
  1.3248 +// If only dx and dy are passed in then optional_argc should be 0. If only
  1.3249 +// dx, dy, dw and dh are passed in then optional_argc should be 2. The only
  1.3250 +// other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
  1.3251 +// are all passed in.
  1.3252 +
  1.3253 +void
  1.3254 +CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image,
  1.3255 +                                    double sx, double sy, double sw,
  1.3256 +                                    double sh, double dx, double dy,
  1.3257 +                                    double dw, double dh,
  1.3258 +                                    uint8_t optional_argc,
  1.3259 +                                    ErrorResult& error)
  1.3260 +{
  1.3261 +  MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6);
  1.3262 +
  1.3263 +  RefPtr<SourceSurface> srcSurf;
  1.3264 +  gfxIntSize imgSize;
  1.3265 +
  1.3266 +  Element* element;
  1.3267 +
  1.3268 +  EnsureTarget();
  1.3269 +  if (image.IsHTMLCanvasElement()) {
  1.3270 +    HTMLCanvasElement* canvas = &image.GetAsHTMLCanvasElement();
  1.3271 +    element = canvas;
  1.3272 +    nsIntSize size = canvas->GetSize();
  1.3273 +    if (size.width == 0 || size.height == 0) {
  1.3274 +      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.3275 +      return;
  1.3276 +    }
  1.3277 +  } else {
  1.3278 +    if (image.IsHTMLImageElement()) {
  1.3279 +      HTMLImageElement* img = &image.GetAsHTMLImageElement();
  1.3280 +      element = img;
  1.3281 +    } else {
  1.3282 +      HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
  1.3283 +      element = video;
  1.3284 +    }
  1.3285 +
  1.3286 +    srcSurf =
  1.3287 +      CanvasImageCache::Lookup(element, mCanvasElement, &imgSize);
  1.3288 +  }
  1.3289 +
  1.3290 +  nsLayoutUtils::DirectDrawInfo drawInfo;
  1.3291 +
  1.3292 +  if (!srcSurf) {
  1.3293 +    // The canvas spec says that drawImage should draw the first frame
  1.3294 +    // of animated images. We also don't want to rasterize vector images.
  1.3295 +    uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME |
  1.3296 +                        nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS;
  1.3297 +    nsLayoutUtils::SurfaceFromElementResult res =
  1.3298 +      nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
  1.3299 +
  1.3300 +    if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
  1.3301 +      // Spec says to silently do nothing if the element is still loading.
  1.3302 +      if (!res.mIsStillLoading) {
  1.3303 +        error.Throw(NS_ERROR_NOT_AVAILABLE);
  1.3304 +      }
  1.3305 +      return;
  1.3306 +    }
  1.3307 +
  1.3308 +    imgSize = res.mSize;
  1.3309 +
  1.3310 +    // Scale sw/sh based on aspect ratio
  1.3311 +    if (image.IsHTMLVideoElement()) {
  1.3312 +      HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
  1.3313 +      int32_t displayWidth = video->VideoWidth();
  1.3314 +      int32_t displayHeight = video->VideoHeight();
  1.3315 +      sw *= (double)imgSize.width / (double)displayWidth;
  1.3316 +      sh *= (double)imgSize.height / (double)displayHeight;
  1.3317 +    }
  1.3318 +
  1.3319 +    if (mCanvasElement) {
  1.3320 +      CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
  1.3321 +                                            res.mPrincipal, res.mIsWriteOnly,
  1.3322 +                                            res.mCORSUsed);
  1.3323 +    }
  1.3324 +
  1.3325 +    if (res.mSourceSurface) {
  1.3326 +      if (res.mImageRequest) {
  1.3327 +        CanvasImageCache::NotifyDrawImage(element, mCanvasElement, res.mImageRequest,
  1.3328 +                                          res.mSourceSurface, imgSize);
  1.3329 +      }
  1.3330 +
  1.3331 +      srcSurf = res.mSourceSurface;
  1.3332 +    } else {
  1.3333 +      drawInfo = res.mDrawInfo;
  1.3334 +    }
  1.3335 +  }
  1.3336 +
  1.3337 +  if (optional_argc == 0) {
  1.3338 +    sx = sy = 0.0;
  1.3339 +    dw = sw = (double) imgSize.width;
  1.3340 +    dh = sh = (double) imgSize.height;
  1.3341 +  } else if (optional_argc == 2) {
  1.3342 +    sx = sy = 0.0;
  1.3343 +    sw = (double) imgSize.width;
  1.3344 +    sh = (double) imgSize.height;
  1.3345 +  }
  1.3346 +
  1.3347 +  if (sw == 0.0 || sh == 0.0) {
  1.3348 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.3349 +    return;
  1.3350 +  }
  1.3351 +
  1.3352 +  if (dw == 0.0 || dh == 0.0) {
  1.3353 +    // not really failure, but nothing to do --
  1.3354 +    // and noone likes a divide-by-zero
  1.3355 +    return;
  1.3356 +  }
  1.3357 +
  1.3358 +  if (sx < 0.0 || sy < 0.0 ||
  1.3359 +      sw < 0.0 || sw > (double) imgSize.width ||
  1.3360 +      sh < 0.0 || sh > (double) imgSize.height ||
  1.3361 +      dw < 0.0 || dh < 0.0) {
  1.3362 +    // XXX - Unresolved spec issues here, for now return error.
  1.3363 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.3364 +    return;
  1.3365 +  }
  1.3366 +
  1.3367 +  Filter filter;
  1.3368 +
  1.3369 +  if (CurrentState().imageSmoothingEnabled)
  1.3370 +    filter = mgfx::Filter::LINEAR;
  1.3371 +  else
  1.3372 +    filter = mgfx::Filter::POINT;
  1.3373 +
  1.3374 +  mgfx::Rect bounds;
  1.3375 +
  1.3376 +  if (NeedToDrawShadow()) {
  1.3377 +    bounds = mgfx::Rect(dx, dy, dw, dh);
  1.3378 +    bounds = mTarget->GetTransform().TransformBounds(bounds);
  1.3379 +  }
  1.3380 +
  1.3381 +  if (srcSurf) {
  1.3382 +    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
  1.3383 +      DrawSurface(srcSurf,
  1.3384 +                  mgfx::Rect(dx, dy, dw, dh),
  1.3385 +                  mgfx::Rect(sx, sy, sw, sh),
  1.3386 +                  DrawSurfaceOptions(filter),
  1.3387 +                  DrawOptions(CurrentState().globalAlpha, UsedOperation()));
  1.3388 +  } else {
  1.3389 +    DrawDirectlyToCanvas(drawInfo, &bounds, dx, dy, dw, dh,
  1.3390 +                         sx, sy, sw, sh, imgSize);
  1.3391 +  }
  1.3392 +
  1.3393 +  RedrawUser(gfxRect(dx, dy, dw, dh));
  1.3394 +}
  1.3395 +
  1.3396 +void
  1.3397 +CanvasRenderingContext2D::DrawDirectlyToCanvas(
  1.3398 +                          const nsLayoutUtils::DirectDrawInfo& image,
  1.3399 +                          mgfx::Rect* bounds, double dx, double dy,
  1.3400 +                          double dw, double dh, double sx, double sy,
  1.3401 +                          double sw, double sh, gfxIntSize imgSize)
  1.3402 +{
  1.3403 +  gfxMatrix contextMatrix;
  1.3404 +
  1.3405 +  AdjustedTarget tempTarget(this, bounds->IsEmpty() ? nullptr: bounds);
  1.3406 +
  1.3407 +  // get any already existing transforms on the context. Include transformations used for context shadow
  1.3408 +  if (tempTarget) {
  1.3409 +    Matrix matrix = tempTarget->GetTransform();
  1.3410 +    contextMatrix = gfxMatrix(matrix._11, matrix._12, matrix._21,
  1.3411 +                              matrix._22, matrix._31, matrix._32);
  1.3412 +  }
  1.3413 +
  1.3414 +  gfxMatrix transformMatrix;
  1.3415 +  transformMatrix.Translate(gfxPoint(sx, sy));
  1.3416 +  if (dw > 0 && dh > 0) {
  1.3417 +    transformMatrix.Scale(sw/dw, sh/dh);
  1.3418 +  }
  1.3419 +  transformMatrix.Translate(gfxPoint(-dx, -dy));
  1.3420 +
  1.3421 +  nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
  1.3422 +  context->SetMatrix(contextMatrix);
  1.3423 +  
  1.3424 +  // FLAG_CLAMP is added for increased performance
  1.3425 +  uint32_t modifiedFlags = image.mDrawingFlags | imgIContainer::FLAG_CLAMP;
  1.3426 +
  1.3427 +  nsresult rv = image.mImgContainer->
  1.3428 +    Draw(context, GraphicsFilter::FILTER_GOOD, transformMatrix,
  1.3429 +         gfxRect(gfxPoint(dx, dy), gfxIntSize(dw, dh)),
  1.3430 +         nsIntRect(nsIntPoint(0, 0), gfxIntSize(imgSize.width, imgSize.height)),
  1.3431 +         gfxIntSize(imgSize.width, imgSize.height), nullptr, image.mWhichFrame,
  1.3432 +         modifiedFlags);
  1.3433 +
  1.3434 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3435 +}
  1.3436 +
  1.3437 +static bool
  1.3438 +IsStandardCompositeOp(CompositionOp op)
  1.3439 +{
  1.3440 +    return (op == CompositionOp::OP_SOURCE ||
  1.3441 +            op == CompositionOp::OP_ATOP ||
  1.3442 +            op == CompositionOp::OP_IN ||
  1.3443 +            op == CompositionOp::OP_OUT ||
  1.3444 +            op == CompositionOp::OP_OVER ||
  1.3445 +            op == CompositionOp::OP_DEST_IN ||
  1.3446 +            op == CompositionOp::OP_DEST_OUT ||
  1.3447 +            op == CompositionOp::OP_DEST_OVER ||
  1.3448 +            op == CompositionOp::OP_DEST_ATOP ||
  1.3449 +            op == CompositionOp::OP_ADD ||
  1.3450 +            op == CompositionOp::OP_XOR);
  1.3451 +}
  1.3452 +
  1.3453 +void
  1.3454 +CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op,
  1.3455 +                                                      ErrorResult& error)
  1.3456 +{
  1.3457 +  CompositionOp comp_op;
  1.3458 +
  1.3459 +#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
  1.3460 +  if (op.EqualsLiteral(cvsop))   \
  1.3461 +    comp_op = CompositionOp::OP_##op2d;
  1.3462 +
  1.3463 +  CANVAS_OP_TO_GFX_OP("copy", SOURCE)
  1.3464 +  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
  1.3465 +  else CANVAS_OP_TO_GFX_OP("source-in", IN)
  1.3466 +  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
  1.3467 +  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
  1.3468 +  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
  1.3469 +  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
  1.3470 +  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
  1.3471 +  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
  1.3472 +  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
  1.3473 +  else CANVAS_OP_TO_GFX_OP("xor", XOR)
  1.3474 +  else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
  1.3475 +  else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
  1.3476 +  else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
  1.3477 +  else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
  1.3478 +  else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
  1.3479 +  else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
  1.3480 +  else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
  1.3481 +  else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
  1.3482 +  else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
  1.3483 +  else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
  1.3484 +  else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
  1.3485 +  else CANVAS_OP_TO_GFX_OP("hue", HUE)
  1.3486 +  else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
  1.3487 +  else CANVAS_OP_TO_GFX_OP("color", COLOR)
  1.3488 +  else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
  1.3489 +  // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.3490 +  else return;
  1.3491 +
  1.3492 +  if (!IsStandardCompositeOp(comp_op)) {
  1.3493 +    Demote();
  1.3494 +  }
  1.3495 +
  1.3496 +#undef CANVAS_OP_TO_GFX_OP
  1.3497 +  CurrentState().op = comp_op;
  1.3498 +}
  1.3499 +
  1.3500 +void
  1.3501 +CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op,
  1.3502 +                                                      ErrorResult& error)
  1.3503 +{
  1.3504 +  CompositionOp comp_op = CurrentState().op;
  1.3505 +
  1.3506 +#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
  1.3507 +  if (comp_op == CompositionOp::OP_##op2d) \
  1.3508 +    op.AssignLiteral(cvsop);
  1.3509 +
  1.3510 +  CANVAS_OP_TO_GFX_OP("copy", SOURCE)
  1.3511 +  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
  1.3512 +  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
  1.3513 +  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
  1.3514 +  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
  1.3515 +  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
  1.3516 +  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
  1.3517 +  else CANVAS_OP_TO_GFX_OP("source-in", IN)
  1.3518 +  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
  1.3519 +  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
  1.3520 +  else CANVAS_OP_TO_GFX_OP("xor", XOR)
  1.3521 +  else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY)
  1.3522 +  else CANVAS_OP_TO_GFX_OP("screen", SCREEN)
  1.3523 +  else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY)
  1.3524 +  else CANVAS_OP_TO_GFX_OP("darken", DARKEN)
  1.3525 +  else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN)
  1.3526 +  else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE)
  1.3527 +  else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN)
  1.3528 +  else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT)
  1.3529 +  else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT)
  1.3530 +  else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE)
  1.3531 +  else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION)
  1.3532 +  else CANVAS_OP_TO_GFX_OP("hue", HUE)
  1.3533 +  else CANVAS_OP_TO_GFX_OP("saturation", SATURATION)
  1.3534 +  else CANVAS_OP_TO_GFX_OP("color", COLOR)
  1.3535 +  else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY)
  1.3536 +  else {
  1.3537 +    error.Throw(NS_ERROR_FAILURE);
  1.3538 +  }
  1.3539 +
  1.3540 +  if (!IsStandardCompositeOp(comp_op)) {
  1.3541 +    Demote();
  1.3542 +  }
  1.3543 +
  1.3544 +#undef CANVAS_OP_TO_GFX_OP
  1.3545 +}
  1.3546 +
  1.3547 +void
  1.3548 +CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
  1.3549 +                                     double y, double w, double h,
  1.3550 +                                     const nsAString& bgColor,
  1.3551 +                                     uint32_t flags, ErrorResult& error)
  1.3552 +{
  1.3553 +  // protect against too-large surfaces that will cause allocation
  1.3554 +  // or overflow issues
  1.3555 +  if (!gfxASurface::CheckSurfaceSize(gfxIntSize(int32_t(w), int32_t(h)),
  1.3556 +                                     0xffff)) {
  1.3557 +    error.Throw(NS_ERROR_FAILURE);
  1.3558 +    return;
  1.3559 +  }
  1.3560 +
  1.3561 +  EnsureTarget();
  1.3562 +  // We can't allow web apps to call this until we fix at least the
  1.3563 +  // following potential security issues:
  1.3564 +  // -- rendering cross-domain IFRAMEs and then extracting the results
  1.3565 +  // -- rendering the user's theme and then extracting the results
  1.3566 +  // -- rendering native anonymous content (e.g., file input paths;
  1.3567 +  // scrollbars should be allowed)
  1.3568 +  if (!nsContentUtils::IsCallerChrome()) {
  1.3569 +    // not permitted to use DrawWindow
  1.3570 +    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.3571 +    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1.3572 +    return;
  1.3573 +  }
  1.3574 +
  1.3575 +  // Flush layout updates
  1.3576 +  if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
  1.3577 +    nsContentUtils::FlushLayoutForTree(&window);
  1.3578 +  }
  1.3579 +
  1.3580 +  nsRefPtr<nsPresContext> presContext;
  1.3581 +  nsIDocShell* docshell = window.GetDocShell();
  1.3582 +  if (docshell) {
  1.3583 +    docshell->GetPresContext(getter_AddRefs(presContext));
  1.3584 +  }
  1.3585 +  if (!presContext) {
  1.3586 +    error.Throw(NS_ERROR_FAILURE);
  1.3587 +    return;
  1.3588 +  }
  1.3589 +
  1.3590 +  nscolor backgroundColor;
  1.3591 +  if (!ParseColor(bgColor, &backgroundColor)) {
  1.3592 +    error.Throw(NS_ERROR_FAILURE);
  1.3593 +    return;
  1.3594 +  }
  1.3595 +
  1.3596 +  nsRect r(nsPresContext::CSSPixelsToAppUnits((float)x),
  1.3597 +           nsPresContext::CSSPixelsToAppUnits((float)y),
  1.3598 +           nsPresContext::CSSPixelsToAppUnits((float)w),
  1.3599 +           nsPresContext::CSSPixelsToAppUnits((float)h));
  1.3600 +  uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
  1.3601 +                             nsIPresShell::RENDER_DOCUMENT_RELATIVE);
  1.3602 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
  1.3603 +    renderDocFlags |= nsIPresShell::RENDER_CARET;
  1.3604 +  }
  1.3605 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
  1.3606 +    renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
  1.3607 +                        nsIPresShell::RENDER_DOCUMENT_RELATIVE);
  1.3608 +  }
  1.3609 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
  1.3610 +    renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
  1.3611 +  }
  1.3612 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
  1.3613 +    renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
  1.3614 +  }
  1.3615 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) {
  1.3616 +    renderDocFlags |= nsIPresShell::RENDER_DRAWWINDOW_NOT_FLUSHING;
  1.3617 +  }
  1.3618 +
  1.3619 +  // gfxContext-over-Azure may modify the DrawTarget's transform, so
  1.3620 +  // save and restore it
  1.3621 +  Matrix matrix = mTarget->GetTransform();
  1.3622 +  double sw = matrix._11 * w;
  1.3623 +  double sh = matrix._22 * h;
  1.3624 +  if (!sw || !sh) {
  1.3625 +    return;
  1.3626 +  }
  1.3627 +  nsRefPtr<gfxContext> thebes;
  1.3628 +  RefPtr<DrawTarget> drawDT;
  1.3629 +  if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget)) {
  1.3630 +    thebes = new gfxContext(mTarget);
  1.3631 +    thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
  1.3632 +                                matrix._22, matrix._31, matrix._32));
  1.3633 +  } else {
  1.3634 +    drawDT =
  1.3635 +      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
  1.3636 +                                                                   SurfaceFormat::B8G8R8A8);
  1.3637 +    if (!drawDT) {
  1.3638 +      error.Throw(NS_ERROR_FAILURE);
  1.3639 +      return;
  1.3640 +    }
  1.3641 +
  1.3642 +    thebes = new gfxContext(drawDT);
  1.3643 +    thebes->Scale(matrix._11, matrix._22);
  1.3644 +  }
  1.3645 +
  1.3646 +  nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
  1.3647 +  unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
  1.3648 +  if (drawDT) {
  1.3649 +    RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
  1.3650 +    RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
  1.3651 +
  1.3652 +    RefPtr<SourceSurface> source =
  1.3653 +      mTarget->CreateSourceSurfaceFromData(data->GetData(),
  1.3654 +                                           data->GetSize(),
  1.3655 +                                           data->Stride(),
  1.3656 +                                           data->GetFormat());
  1.3657 +
  1.3658 +    if (!source) {
  1.3659 +      error.Throw(NS_ERROR_FAILURE);
  1.3660 +      return;
  1.3661 +    }
  1.3662 +
  1.3663 +    mgfx::Rect destRect(0, 0, w, h);
  1.3664 +    mgfx::Rect sourceRect(0, 0, sw, sh);
  1.3665 +    mTarget->DrawSurface(source, destRect, sourceRect,
  1.3666 +                         DrawSurfaceOptions(mgfx::Filter::POINT),
  1.3667 +                         DrawOptions(1.0f, CompositionOp::OP_OVER,
  1.3668 +                                     AntialiasMode::NONE));
  1.3669 +    mTarget->Flush();
  1.3670 +  } else {
  1.3671 +    mTarget->SetTransform(matrix);
  1.3672 +  }
  1.3673 +
  1.3674 +  // note that x and y are coordinates in the document that
  1.3675 +  // we're drawing; x and y are drawn to 0,0 in current user
  1.3676 +  // space.
  1.3677 +  RedrawUser(gfxRect(0, 0, w, h));
  1.3678 +}
  1.3679 +
  1.3680 +void
  1.3681 +CanvasRenderingContext2D::AsyncDrawXULElement(nsXULElement& elem,
  1.3682 +                                              double x, double y,
  1.3683 +                                              double w, double h,
  1.3684 +                                              const nsAString& bgColor,
  1.3685 +                                              uint32_t flags,
  1.3686 +                                              ErrorResult& error)
  1.3687 +{
  1.3688 +  // We can't allow web apps to call this until we fix at least the
  1.3689 +  // following potential security issues:
  1.3690 +  // -- rendering cross-domain IFRAMEs and then extracting the results
  1.3691 +  // -- rendering the user's theme and then extracting the results
  1.3692 +  // -- rendering native anonymous content (e.g., file input paths;
  1.3693 +  // scrollbars should be allowed)
  1.3694 +  if (!nsContentUtils::IsCallerChrome()) {
  1.3695 +    // not permitted to use DrawWindow
  1.3696 +    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.3697 +    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1.3698 +    return;
  1.3699 +  }
  1.3700 +
  1.3701 +#if 0
  1.3702 +  nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(&elem);
  1.3703 +  if (!loaderOwner) {
  1.3704 +    error.Throw(NS_ERROR_FAILURE);
  1.3705 +    return;
  1.3706 +  }
  1.3707 +
  1.3708 +  nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
  1.3709 +  if (!frameloader) {
  1.3710 +    error.Throw(NS_ERROR_FAILURE);
  1.3711 +    return;
  1.3712 +  }
  1.3713 +
  1.3714 +  PBrowserParent *child = frameloader->GetRemoteBrowser();
  1.3715 +  if (!child) {
  1.3716 +    nsCOMPtr<nsIDOMWindow> window =
  1.3717 +      do_GetInterface(frameloader->GetExistingDocShell());
  1.3718 +    if (!window) {
  1.3719 +      error.Throw(NS_ERROR_FAILURE);
  1.3720 +      return;
  1.3721 +    }
  1.3722 +
  1.3723 +    return DrawWindow(window, x, y, w, h, bgColor, flags);
  1.3724 +  }
  1.3725 +
  1.3726 +  // protect against too-large surfaces that will cause allocation
  1.3727 +  // or overflow issues
  1.3728 +  if (!gfxASurface::CheckSurfaceSize(gfxIntSize(w, h), 0xffff)) {
  1.3729 +    error.Throw(NS_ERROR_FAILURE);
  1.3730 +    return;
  1.3731 +  }
  1.3732 +
  1.3733 +  bool flush =
  1.3734 +    (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
  1.3735 +
  1.3736 +  uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
  1.3737 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
  1.3738 +    renderDocFlags |= nsIPresShell::RENDER_CARET;
  1.3739 +  }
  1.3740 +  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
  1.3741 +    renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
  1.3742 +  }
  1.3743 +
  1.3744 +  nsRect rect(nsPresContext::CSSPixelsToAppUnits(x),
  1.3745 +              nsPresContext::CSSPixelsToAppUnits(y),
  1.3746 +              nsPresContext::CSSPixelsToAppUnits(w),
  1.3747 +              nsPresContext::CSSPixelsToAppUnits(h));
  1.3748 +  if (mIPC) {
  1.3749 +    PDocumentRendererParent *pdocrender =
  1.3750 +      child->SendPDocumentRendererConstructor(rect,
  1.3751 +                                              mThebes->CurrentMatrix(),
  1.3752 +                                              nsString(aBGColor),
  1.3753 +                                              renderDocFlags, flush,
  1.3754 +                                              nsIntSize(mWidth, mHeight));
  1.3755 +    if (!pdocrender)
  1.3756 +      return NS_ERROR_FAILURE;
  1.3757 +
  1.3758 +    DocumentRendererParent *docrender =
  1.3759 +      static_cast<DocumentRendererParent *>(pdocrender);
  1.3760 +
  1.3761 +    docrender->SetCanvasContext(this, mThebes);
  1.3762 +  }
  1.3763 +#endif
  1.3764 +}
  1.3765 +
  1.3766 +//
  1.3767 +// device pixel getting/setting
  1.3768 +//
  1.3769 +
  1.3770 +already_AddRefed<ImageData>
  1.3771 +CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
  1.3772 +                                       double aSy, double aSw,
  1.3773 +                                       double aSh, ErrorResult& error)
  1.3774 +{
  1.3775 +  EnsureTarget();
  1.3776 +  if (!IsTargetValid()) {
  1.3777 +    error.Throw(NS_ERROR_FAILURE);
  1.3778 +    return nullptr;
  1.3779 +  }
  1.3780 +
  1.3781 +  if (!mCanvasElement && !mDocShell) {
  1.3782 +    NS_ERROR("No canvas element and no docshell in GetImageData!!!");
  1.3783 +    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1.3784 +    return nullptr;
  1.3785 +  }
  1.3786 +
  1.3787 +  // Check only if we have a canvas element; if we were created with a docshell,
  1.3788 +  // then it's special internal use.
  1.3789 +  if (mCanvasElement && mCanvasElement->IsWriteOnly() &&
  1.3790 +      !nsContentUtils::IsCallerChrome())
  1.3791 +  {
  1.3792 +    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
  1.3793 +    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1.3794 +    return nullptr;
  1.3795 +  }
  1.3796 +
  1.3797 +  if (!NS_finite(aSx) || !NS_finite(aSy) ||
  1.3798 +      !NS_finite(aSw) || !NS_finite(aSh)) {
  1.3799 +    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  1.3800 +    return nullptr;
  1.3801 +  }
  1.3802 +
  1.3803 +  if (!aSw || !aSh) {
  1.3804 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.3805 +    return nullptr;
  1.3806 +  }
  1.3807 +
  1.3808 +  int32_t x = JS_DoubleToInt32(aSx);
  1.3809 +  int32_t y = JS_DoubleToInt32(aSy);
  1.3810 +  int32_t wi = JS_DoubleToInt32(aSw);
  1.3811 +  int32_t hi = JS_DoubleToInt32(aSh);
  1.3812 +
  1.3813 +  // Handle negative width and height by flipping the rectangle over in the
  1.3814 +  // relevant direction.
  1.3815 +  uint32_t w, h;
  1.3816 +  if (aSw < 0) {
  1.3817 +    w = -wi;
  1.3818 +    x -= w;
  1.3819 +  } else {
  1.3820 +    w = wi;
  1.3821 +  }
  1.3822 +  if (aSh < 0) {
  1.3823 +    h = -hi;
  1.3824 +    y -= h;
  1.3825 +  } else {
  1.3826 +    h = hi;
  1.3827 +  }
  1.3828 +
  1.3829 +  if (w == 0) {
  1.3830 +    w = 1;
  1.3831 +  }
  1.3832 +  if (h == 0) {
  1.3833 +    h = 1;
  1.3834 +  }
  1.3835 +
  1.3836 +  JS::Rooted<JSObject*> array(aCx);
  1.3837 +  error = GetImageDataArray(aCx, x, y, w, h, array.address());
  1.3838 +  if (error.Failed()) {
  1.3839 +    return nullptr;
  1.3840 +  }
  1.3841 +  MOZ_ASSERT(array);
  1.3842 +
  1.3843 +  nsRefPtr<ImageData> imageData = new ImageData(w, h, *array);
  1.3844 +  return imageData.forget();
  1.3845 +}
  1.3846 +
  1.3847 +nsresult
  1.3848 +CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
  1.3849 +                                            int32_t aX,
  1.3850 +                                            int32_t aY,
  1.3851 +                                            uint32_t aWidth,
  1.3852 +                                            uint32_t aHeight,
  1.3853 +                                            JSObject** aRetval)
  1.3854 +{
  1.3855 +  MOZ_ASSERT(aWidth && aHeight);
  1.3856 +
  1.3857 +  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
  1.3858 +  if (!len.isValid()) {
  1.3859 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.3860 +  }
  1.3861 +
  1.3862 +  CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
  1.3863 +  CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
  1.3864 +
  1.3865 +  if (!rightMost.isValid() || !bottomMost.isValid()) {
  1.3866 +    return NS_ERROR_DOM_SYNTAX_ERR;
  1.3867 +  }
  1.3868 +
  1.3869 +  IntRect srcRect(0, 0, mWidth, mHeight);
  1.3870 +  IntRect destRect(aX, aY, aWidth, aHeight);
  1.3871 +  IntRect srcReadRect = srcRect.Intersect(destRect);
  1.3872 +  RefPtr<DataSourceSurface> readback;
  1.3873 +  if (!srcReadRect.IsEmpty() && !mZero) {
  1.3874 +    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
  1.3875 +    if (snapshot) {
  1.3876 +      readback = snapshot->GetDataSurface();
  1.3877 +    }
  1.3878 +    if (!readback || !readback->GetData()) {
  1.3879 +      return NS_ERROR_OUT_OF_MEMORY;
  1.3880 +    }
  1.3881 +  }
  1.3882 +
  1.3883 +  JS::Rooted<JSObject*> darray(aCx, JS_NewUint8ClampedArray(aCx, len.value()));
  1.3884 +  if (!darray) {
  1.3885 +    return NS_ERROR_OUT_OF_MEMORY;
  1.3886 +  }
  1.3887 +
  1.3888 +  if (mZero) {
  1.3889 +    *aRetval = darray;
  1.3890 +    return NS_OK;
  1.3891 +  }
  1.3892 +
  1.3893 +  uint8_t* data = JS_GetUint8ClampedArrayData(darray);
  1.3894 +
  1.3895 +  // Check for site-specific permission and return all-white, opaque pixel
  1.3896 +  // data if no permission.  This check is not needed if the canvas was
  1.3897 +  // created with a docshell (that is only done for special internal uses).
  1.3898 +  bool usePlaceholder = false;
  1.3899 +  if (mCanvasElement) {
  1.3900 +    nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
  1.3901 +    usePlaceholder = !ownerDoc ||
  1.3902 +      !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx);
  1.3903 +  }
  1.3904 +
  1.3905 +  if (usePlaceholder) {
  1.3906 +    memset(data, 0xFF, len.value());
  1.3907 +    *aRetval = darray;
  1.3908 +    return NS_OK;
  1.3909 +  }
  1.3910 +
  1.3911 +  IntRect dstWriteRect = srcReadRect;
  1.3912 +  dstWriteRect.MoveBy(-aX, -aY);
  1.3913 +
  1.3914 +  uint8_t* src = data;
  1.3915 +  uint32_t srcStride = aWidth * 4;
  1.3916 +  if (readback) {
  1.3917 +    srcStride = readback->Stride();
  1.3918 +    src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
  1.3919 +  }
  1.3920 +
  1.3921 +  // NOTE! dst is the same as src, and this relies on reading
  1.3922 +  // from src and advancing that ptr before writing to dst.
  1.3923 +  // NOTE! I'm not sure that it is, I think this comment might have been
  1.3924 +  // inherited from Thebes canvas and is no longer true
  1.3925 +  uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
  1.3926 +
  1.3927 +  if (mOpaque) {
  1.3928 +    for (int32_t j = 0; j < dstWriteRect.height; ++j) {
  1.3929 +      for (int32_t i = 0; i < dstWriteRect.width; ++i) {
  1.3930 +        // XXX Is there some useful swizzle MMX we can use here?
  1.3931 +#if MOZ_LITTLE_ENDIAN
  1.3932 +        uint8_t b = *src++;
  1.3933 +        uint8_t g = *src++;
  1.3934 +        uint8_t r = *src++;
  1.3935 +        src++;
  1.3936 +#else
  1.3937 +        src++;
  1.3938 +        uint8_t r = *src++;
  1.3939 +        uint8_t g = *src++;
  1.3940 +        uint8_t b = *src++;
  1.3941 +#endif
  1.3942 +        *dst++ = r;
  1.3943 +        *dst++ = g;
  1.3944 +        *dst++ = b;
  1.3945 +        *dst++ = 255;
  1.3946 +      }
  1.3947 +      src += srcStride - (dstWriteRect.width * 4);
  1.3948 +      dst += (aWidth * 4) - (dstWriteRect.width * 4);
  1.3949 +    }
  1.3950 +  } else
  1.3951 +  for (int32_t j = 0; j < dstWriteRect.height; ++j) {
  1.3952 +    for (int32_t i = 0; i < dstWriteRect.width; ++i) {
  1.3953 +      // XXX Is there some useful swizzle MMX we can use here?
  1.3954 +#if MOZ_LITTLE_ENDIAN
  1.3955 +      uint8_t b = *src++;
  1.3956 +      uint8_t g = *src++;
  1.3957 +      uint8_t r = *src++;
  1.3958 +      uint8_t a = *src++;
  1.3959 +#else
  1.3960 +      uint8_t a = *src++;
  1.3961 +      uint8_t r = *src++;
  1.3962 +      uint8_t g = *src++;
  1.3963 +      uint8_t b = *src++;
  1.3964 +#endif
  1.3965 +      // Convert to non-premultiplied color
  1.3966 +      *dst++ = gfxUtils::sUnpremultiplyTable[a * 256 + r];
  1.3967 +      *dst++ = gfxUtils::sUnpremultiplyTable[a * 256 + g];
  1.3968 +      *dst++ = gfxUtils::sUnpremultiplyTable[a * 256 + b];
  1.3969 +      *dst++ = a;
  1.3970 +    }
  1.3971 +    src += srcStride - (dstWriteRect.width * 4);
  1.3972 +    dst += (aWidth * 4) - (dstWriteRect.width * 4);
  1.3973 +  }
  1.3974 +
  1.3975 +  *aRetval = darray;
  1.3976 +  return NS_OK;
  1.3977 +}
  1.3978 +
  1.3979 +void
  1.3980 +CanvasRenderingContext2D::EnsureErrorTarget()
  1.3981 +{
  1.3982 +  if (sErrorTarget) {
  1.3983 +    return;
  1.3984 +  }
  1.3985 +
  1.3986 +  RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
  1.3987 +  MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
  1.3988 +
  1.3989 +  sErrorTarget = errorTarget;
  1.3990 +  NS_ADDREF(sErrorTarget);
  1.3991 +}
  1.3992 +
  1.3993 +void
  1.3994 +CanvasRenderingContext2D::FillRuleChanged()
  1.3995 +{
  1.3996 +  if (mPath) {
  1.3997 +    mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
  1.3998 +    mPath = nullptr;
  1.3999 +  }
  1.4000 +}
  1.4001 +
  1.4002 +void
  1.4003 +CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
  1.4004 +                                       double dy, ErrorResult& error)
  1.4005 +{
  1.4006 +  dom::Uint8ClampedArray arr(imageData.GetDataObject());
  1.4007 +
  1.4008 +  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
  1.4009 +                                imageData.Width(), imageData.Height(),
  1.4010 +                                &arr, false, 0, 0, 0, 0);
  1.4011 +}
  1.4012 +
  1.4013 +void
  1.4014 +CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
  1.4015 +                                       double dy, double dirtyX,
  1.4016 +                                       double dirtyY, double dirtyWidth,
  1.4017 +                                       double dirtyHeight,
  1.4018 +                                       ErrorResult& error)
  1.4019 +{
  1.4020 +  dom::Uint8ClampedArray arr(imageData.GetDataObject());
  1.4021 +
  1.4022 +  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
  1.4023 +                                imageData.Width(), imageData.Height(),
  1.4024 +                                &arr, true,
  1.4025 +                                JS_DoubleToInt32(dirtyX),
  1.4026 +                                JS_DoubleToInt32(dirtyY),
  1.4027 +                                JS_DoubleToInt32(dirtyWidth),
  1.4028 +                                JS_DoubleToInt32(dirtyHeight));
  1.4029 +}
  1.4030 +
  1.4031 +// void putImageData (in ImageData d, in float x, in float y);
  1.4032 +// void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
  1.4033 +
  1.4034 +nsresult
  1.4035 +CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
  1.4036 +                                                dom::Uint8ClampedArray* aArray,
  1.4037 +                                                bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
  1.4038 +                                                int32_t dirtyWidth, int32_t dirtyHeight)
  1.4039 +{
  1.4040 +  if (w == 0 || h == 0) {
  1.4041 +    return NS_ERROR_DOM_SYNTAX_ERR;
  1.4042 +  }
  1.4043 +
  1.4044 +  IntRect dirtyRect;
  1.4045 +  IntRect imageDataRect(0, 0, w, h);
  1.4046 +
  1.4047 +  if (hasDirtyRect) {
  1.4048 +    // fix up negative dimensions
  1.4049 +    if (dirtyWidth < 0) {
  1.4050 +      NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.4051 +
  1.4052 +      CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
  1.4053 +
  1.4054 +      if (!checkedDirtyX.isValid())
  1.4055 +        return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4056 +
  1.4057 +      dirtyX = checkedDirtyX.value();
  1.4058 +      dirtyWidth = -dirtyWidth;
  1.4059 +    }
  1.4060 +
  1.4061 +    if (dirtyHeight < 0) {
  1.4062 +      NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.4063 +
  1.4064 +      CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
  1.4065 +
  1.4066 +      if (!checkedDirtyY.isValid())
  1.4067 +        return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4068 +
  1.4069 +      dirtyY = checkedDirtyY.value();
  1.4070 +      dirtyHeight = -dirtyHeight;
  1.4071 +    }
  1.4072 +
  1.4073 +    // bound the dirty rect within the imageData rectangle
  1.4074 +    dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
  1.4075 +
  1.4076 +    if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
  1.4077 +      return NS_OK;
  1.4078 +  } else {
  1.4079 +    dirtyRect = imageDataRect;
  1.4080 +  }
  1.4081 +
  1.4082 +  dirtyRect.MoveBy(IntPoint(x, y));
  1.4083 +  dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
  1.4084 +
  1.4085 +  if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
  1.4086 +    return NS_OK;
  1.4087 +  }
  1.4088 +
  1.4089 +  aArray->ComputeLengthAndData();
  1.4090 +
  1.4091 +  uint32_t dataLen = aArray->Length();
  1.4092 +
  1.4093 +  uint32_t len = w * h * 4;
  1.4094 +  if (dataLen != len) {
  1.4095 +    return NS_ERROR_DOM_SYNTAX_ERR;
  1.4096 +  }
  1.4097 +
  1.4098 +  nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
  1.4099 +                                                          gfxImageFormat::ARGB32,
  1.4100 +                                                          false);
  1.4101 +  if (!imgsurf || imgsurf->CairoStatus()) {
  1.4102 +    return NS_ERROR_FAILURE;
  1.4103 +  }
  1.4104 +
  1.4105 +  uint8_t *src = aArray->Data();
  1.4106 +  uint8_t *dst = imgsurf->Data();
  1.4107 +
  1.4108 +  for (uint32_t j = 0; j < h; j++) {
  1.4109 +    for (uint32_t i = 0; i < w; i++) {
  1.4110 +      uint8_t r = *src++;
  1.4111 +      uint8_t g = *src++;
  1.4112 +      uint8_t b = *src++;
  1.4113 +      uint8_t a = *src++;
  1.4114 +
  1.4115 +      // Convert to premultiplied color (losslessly if the input came from getImageData)
  1.4116 +#if MOZ_LITTLE_ENDIAN
  1.4117 +      *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
  1.4118 +      *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
  1.4119 +      *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
  1.4120 +      *dst++ = a;
  1.4121 +#else
  1.4122 +      *dst++ = a;
  1.4123 +      *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
  1.4124 +      *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
  1.4125 +      *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
  1.4126 +#endif
  1.4127 +    }
  1.4128 +  }
  1.4129 +
  1.4130 +  EnsureTarget();
  1.4131 +  if (!IsTargetValid()) {
  1.4132 +    return NS_ERROR_FAILURE;
  1.4133 +  }
  1.4134 +
  1.4135 +  RefPtr<SourceSurface> sourceSurface =
  1.4136 +    mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), SurfaceFormat::B8G8R8A8);
  1.4137 +
  1.4138 +  // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
  1.4139 +  // covers the details of how to run into it, but the full detailed
  1.4140 +  // investigation hasn't been done to determine the underlying cause.  We
  1.4141 +  // will just handle the failure to allocate the surface to avoid a crash.
  1.4142 +  if (!sourceSurface) {
  1.4143 +    return NS_ERROR_FAILURE;
  1.4144 +  }
  1.4145 +
  1.4146 +  mTarget->CopySurface(sourceSurface,
  1.4147 +                       IntRect(dirtyRect.x - x, dirtyRect.y - y,
  1.4148 +                               dirtyRect.width, dirtyRect.height),
  1.4149 +                       IntPoint(dirtyRect.x, dirtyRect.y));
  1.4150 +
  1.4151 +  Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
  1.4152 +
  1.4153 +  return NS_OK;
  1.4154 +}
  1.4155 +
  1.4156 +static already_AddRefed<ImageData>
  1.4157 +CreateImageData(JSContext* cx, CanvasRenderingContext2D* context,
  1.4158 +                uint32_t w, uint32_t h, ErrorResult& error)
  1.4159 +{
  1.4160 +  if (w == 0)
  1.4161 +      w = 1;
  1.4162 +  if (h == 0)
  1.4163 +      h = 1;
  1.4164 +
  1.4165 +  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(w) * h * 4;
  1.4166 +  if (!len.isValid()) {
  1.4167 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.4168 +    return nullptr;
  1.4169 +  }
  1.4170 +
  1.4171 +  // Create the fast typed array; it's initialized to 0 by default.
  1.4172 +  JSObject* darray = Uint8ClampedArray::Create(cx, context, len.value());
  1.4173 +  if (!darray) {
  1.4174 +    error.Throw(NS_ERROR_OUT_OF_MEMORY);
  1.4175 +    return nullptr;
  1.4176 +  }
  1.4177 +
  1.4178 +  nsRefPtr<mozilla::dom::ImageData> imageData =
  1.4179 +    new mozilla::dom::ImageData(w, h, *darray);
  1.4180 +  return imageData.forget();
  1.4181 +}
  1.4182 +
  1.4183 +already_AddRefed<ImageData>
  1.4184 +CanvasRenderingContext2D::CreateImageData(JSContext* cx, double sw,
  1.4185 +                                          double sh, ErrorResult& error)
  1.4186 +{
  1.4187 +  if (!sw || !sh) {
  1.4188 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.4189 +    return nullptr;
  1.4190 +  }
  1.4191 +
  1.4192 +  int32_t wi = JS_DoubleToInt32(sw);
  1.4193 +  int32_t hi = JS_DoubleToInt32(sh);
  1.4194 +
  1.4195 +  uint32_t w = Abs(wi);
  1.4196 +  uint32_t h = Abs(hi);
  1.4197 +  return mozilla::dom::CreateImageData(cx, this, w, h, error);
  1.4198 +}
  1.4199 +
  1.4200 +already_AddRefed<ImageData>
  1.4201 +CanvasRenderingContext2D::CreateImageData(JSContext* cx,
  1.4202 +                                          ImageData& imagedata,
  1.4203 +                                          ErrorResult& error)
  1.4204 +{
  1.4205 +  return mozilla::dom::CreateImageData(cx, this, imagedata.Width(),
  1.4206 +                                       imagedata.Height(), error);
  1.4207 +}
  1.4208 +
  1.4209 +static uint8_t g2DContextLayerUserData;
  1.4210 +
  1.4211 +already_AddRefed<CanvasLayer>
  1.4212 +CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
  1.4213 +                                         CanvasLayer *aOldLayer,
  1.4214 +                                         LayerManager *aManager)
  1.4215 +{
  1.4216 +  // Don't call EnsureTarget() ... if there isn't already a surface, then
  1.4217 +  // we have nothing to paint and there is no need to create a surface just
  1.4218 +  // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
  1.4219 +  // layer manager which must NOT happen during a paint.
  1.4220 +  if (!mTarget || !IsTargetValid()) {
  1.4221 +    // No DidTransactionCallback will be received, so mark the context clean
  1.4222 +    // now so future invalidations will be dispatched.
  1.4223 +    MarkContextClean();
  1.4224 +    return nullptr;
  1.4225 +  }
  1.4226 +
  1.4227 +  mTarget->Flush();
  1.4228 +
  1.4229 +  if (!mResetLayer && aOldLayer) {
  1.4230 +    CanvasRenderingContext2DUserData* userData =
  1.4231 +      static_cast<CanvasRenderingContext2DUserData*>(
  1.4232 +        aOldLayer->GetUserData(&g2DContextLayerUserData));
  1.4233 +
  1.4234 +    CanvasLayer::Data data;
  1.4235 +    if (mStream) {
  1.4236 +#ifdef USE_SKIA
  1.4237 +      SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
  1.4238 +
  1.4239 +      if (glue) {
  1.4240 +        data.mGLContext = glue->GetGLContext();
  1.4241 +        data.mStream = mStream.get();
  1.4242 +      }
  1.4243 +#endif
  1.4244 +    } else {
  1.4245 +      data.mDrawTarget = mTarget;
  1.4246 +    }
  1.4247 +
  1.4248 +    if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
  1.4249 +      nsRefPtr<CanvasLayer> ret = aOldLayer;
  1.4250 +      return ret.forget();
  1.4251 +    }
  1.4252 +  }
  1.4253 +
  1.4254 +  nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
  1.4255 +  if (!canvasLayer) {
  1.4256 +    NS_WARNING("CreateCanvasLayer returned null!");
  1.4257 +    // No DidTransactionCallback will be received, so mark the context clean
  1.4258 +    // now so future invalidations will be dispatched.
  1.4259 +    MarkContextClean();
  1.4260 +    return nullptr;
  1.4261 +  }
  1.4262 +  CanvasRenderingContext2DUserData *userData = nullptr;
  1.4263 +  // Make the layer tell us whenever a transaction finishes (including
  1.4264 +  // the current transaction), so we can clear our invalidation state and
  1.4265 +  // start invalidating again. We need to do this for all layers since
  1.4266 +  // callers of DrawWindow may be expecting to receive normal invalidation
  1.4267 +  // notifications after this paint.
  1.4268 +
  1.4269 +  // The layer will be destroyed when we tear down the presentation
  1.4270 +  // (at the latest), at which time this userData will be destroyed,
  1.4271 +  // releasing the reference to the element.
  1.4272 +  // The userData will receive DidTransactionCallbacks, which flush the
  1.4273 +  // the invalidation state to indicate that the canvas is up to date.
  1.4274 +  userData = new CanvasRenderingContext2DUserData(this);
  1.4275 +  canvasLayer->SetDidTransactionCallback(
  1.4276 +          CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
  1.4277 +  canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
  1.4278 +
  1.4279 +  CanvasLayer::Data data;
  1.4280 +  if (mStream) {
  1.4281 +    SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
  1.4282 +
  1.4283 +    if (glue) {
  1.4284 +      canvasLayer->SetPreTransactionCallback(
  1.4285 +              CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
  1.4286 +#if USE_SKIA
  1.4287 +      data.mGLContext = glue->GetGLContext();
  1.4288 +#endif
  1.4289 +      data.mStream = mStream.get();
  1.4290 +      data.mTexID = (uint32_t)((uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE));
  1.4291 +    }
  1.4292 +  } else {
  1.4293 +    data.mDrawTarget = mTarget;
  1.4294 +  }
  1.4295 +
  1.4296 +  data.mSize = nsIntSize(mWidth, mHeight);
  1.4297 +
  1.4298 +  canvasLayer->Initialize(data);
  1.4299 +  uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
  1.4300 +  canvasLayer->SetContentFlags(flags);
  1.4301 +  canvasLayer->Updated();
  1.4302 +
  1.4303 +  mResetLayer = false;
  1.4304 +
  1.4305 +  return canvasLayer.forget();
  1.4306 +}
  1.4307 +
  1.4308 +void
  1.4309 +CanvasRenderingContext2D::MarkContextClean()
  1.4310 +{
  1.4311 +  if (mInvalidateCount > 0) {
  1.4312 +    mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
  1.4313 +  }
  1.4314 +  mIsEntireFrameInvalid = false;
  1.4315 +  mInvalidateCount = 0;
  1.4316 +}
  1.4317 +
  1.4318 +
  1.4319 +bool
  1.4320 +CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager)
  1.4321 +{
  1.4322 +  return !aManager->CanUseCanvasLayerForSize(IntSize(mWidth, mHeight));
  1.4323 +}
  1.4324 +
  1.4325 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPath, AddRef)
  1.4326 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath, Release)
  1.4327 +
  1.4328 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasPath, mParent)
  1.4329 +
  1.4330 +CanvasPath::CanvasPath(nsISupports* aParent)
  1.4331 +  : mParent(aParent)
  1.4332 +{
  1.4333 +  SetIsDOMBinding();
  1.4334 +
  1.4335 +  mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
  1.4336 +}
  1.4337 +
  1.4338 +CanvasPath::CanvasPath(nsISupports* aParent, RefPtr<PathBuilder> aPathBuilder)
  1.4339 +  : mParent(aParent), mPathBuilder(aPathBuilder)
  1.4340 +{
  1.4341 +  SetIsDOMBinding();
  1.4342 +
  1.4343 +  if (!mPathBuilder) {
  1.4344 +    mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
  1.4345 +  }
  1.4346 +}
  1.4347 +
  1.4348 +JSObject*
  1.4349 +CanvasPath::WrapObject(JSContext* aCx)
  1.4350 +{
  1.4351 +  return Path2DBinding::Wrap(aCx, this);
  1.4352 +}
  1.4353 +
  1.4354 +already_AddRefed<CanvasPath>
  1.4355 +CanvasPath::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
  1.4356 +{
  1.4357 +  nsRefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports());
  1.4358 +  return path.forget();
  1.4359 +}
  1.4360 +
  1.4361 +already_AddRefed<CanvasPath>
  1.4362 +CanvasPath::Constructor(const GlobalObject& aGlobal, CanvasPath& aCanvasPath, ErrorResult& aRv)
  1.4363 +{
  1.4364 +  RefPtr<gfx::Path> tempPath = aCanvasPath.GetPath(CanvasWindingRule::Nonzero,
  1.4365 +                                                   gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
  1.4366 +
  1.4367 +  nsRefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
  1.4368 +  return path.forget();
  1.4369 +}
  1.4370 +
  1.4371 +already_AddRefed<CanvasPath>
  1.4372 +CanvasPath::Constructor(const GlobalObject& aGlobal, const nsAString& aPathString, ErrorResult& aRv)
  1.4373 +{
  1.4374 +  RefPtr<gfx::Path> tempPath = SVGContentUtils::GetPath(aPathString);
  1.4375 +  if (!tempPath) {
  1.4376 +    return Constructor(aGlobal, aRv);
  1.4377 +  }
  1.4378 +
  1.4379 +  nsRefPtr<CanvasPath> path = new CanvasPath(aGlobal.GetAsSupports(), tempPath->CopyToBuilder());
  1.4380 +  return path.forget();
  1.4381 +}
  1.4382 +
  1.4383 +void
  1.4384 +CanvasPath::ClosePath()
  1.4385 +{
  1.4386 +  EnsurePathBuilder();
  1.4387 +
  1.4388 +  mPathBuilder->Close();
  1.4389 +}
  1.4390 +
  1.4391 +void
  1.4392 +CanvasPath::MoveTo(double x, double y)
  1.4393 +{
  1.4394 +  EnsurePathBuilder();
  1.4395 +
  1.4396 +  mPathBuilder->MoveTo(Point(ToFloat(x), ToFloat(y)));
  1.4397 +}
  1.4398 +
  1.4399 +void
  1.4400 +CanvasPath::LineTo(double x, double y)
  1.4401 +{
  1.4402 +  EnsurePathBuilder();
  1.4403 +
  1.4404 +  mPathBuilder->LineTo(Point(ToFloat(x), ToFloat(y)));
  1.4405 +}
  1.4406 +
  1.4407 +void
  1.4408 +CanvasPath::QuadraticCurveTo(double cpx, double cpy, double x, double y)
  1.4409 +{
  1.4410 +  EnsurePathBuilder();
  1.4411 +
  1.4412 +  mPathBuilder->QuadraticBezierTo(gfx::Point(ToFloat(cpx), ToFloat(cpy)),
  1.4413 +                                  gfx::Point(ToFloat(x), ToFloat(y)));
  1.4414 +}
  1.4415 +
  1.4416 +void
  1.4417 +CanvasPath::BezierCurveTo(double cp1x, double cp1y,
  1.4418 +                          double cp2x, double cp2y,
  1.4419 +                          double x, double y)
  1.4420 +{
  1.4421 +  BezierTo(gfx::Point(ToFloat(cp1x), ToFloat(cp1y)),
  1.4422 +             gfx::Point(ToFloat(cp2x), ToFloat(cp2y)),
  1.4423 +             gfx::Point(ToFloat(x), ToFloat(y)));
  1.4424 +}
  1.4425 +
  1.4426 +void
  1.4427 +CanvasPath::ArcTo(double x1, double y1, double x2, double y2, double radius,
  1.4428 +                  ErrorResult& error)
  1.4429 +{
  1.4430 +  if (radius < 0) {
  1.4431 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.4432 +    return;
  1.4433 +  }
  1.4434 +
  1.4435 +  // Current point in user space!
  1.4436 +  Point p0 = mPathBuilder->CurrentPoint();
  1.4437 +  Point p1(x1, y1);
  1.4438 +  Point p2(x2, y2);
  1.4439 +
  1.4440 +  // Execute these calculations in double precision to avoid cumulative
  1.4441 +  // rounding errors.
  1.4442 +  double dir, a2, b2, c2, cosx, sinx, d, anx, any,
  1.4443 +         bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
  1.4444 +  bool anticlockwise;
  1.4445 +
  1.4446 +  if (p0 == p1 || p1 == p2 || radius == 0) {
  1.4447 +    LineTo(p1.x, p1.y);
  1.4448 +    return;
  1.4449 +  }
  1.4450 +
  1.4451 +  // Check for colinearity
  1.4452 +  dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
  1.4453 +  if (dir == 0) {
  1.4454 +    LineTo(p1.x, p1.y);
  1.4455 +    return;
  1.4456 +  }
  1.4457 +
  1.4458 +
  1.4459 +  // XXX - Math for this code was already available from the non-azure code
  1.4460 +  // and would be well tested. Perhaps converting to bezier directly might
  1.4461 +  // be more efficient longer run.
  1.4462 +  a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
  1.4463 +  b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
  1.4464 +  c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
  1.4465 +  cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
  1.4466 +
  1.4467 +  sinx = sqrt(1 - cosx*cosx);
  1.4468 +  d = radius / ((1 - cosx) / sinx);
  1.4469 +
  1.4470 +  anx = (x1-p0.x) / sqrt(a2);
  1.4471 +  any = (y1-p0.y) / sqrt(a2);
  1.4472 +  bnx = (x1-x2) / sqrt(b2);
  1.4473 +  bny = (y1-y2) / sqrt(b2);
  1.4474 +  x3 = x1 - anx*d;
  1.4475 +  y3 = y1 - any*d;
  1.4476 +  x4 = x1 - bnx*d;
  1.4477 +  y4 = y1 - bny*d;
  1.4478 +  anticlockwise = (dir < 0);
  1.4479 +  cx = x3 + any*radius*(anticlockwise ? 1 : -1);
  1.4480 +  cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
  1.4481 +  angle0 = atan2((y3-cy), (x3-cx));
  1.4482 +  angle1 = atan2((y4-cy), (x4-cx));
  1.4483 +
  1.4484 +
  1.4485 +  LineTo(x3, y3);
  1.4486 +
  1.4487 +  Arc(cx, cy, radius, angle0, angle1, anticlockwise, error);
  1.4488 +}
  1.4489 +
  1.4490 +void
  1.4491 +CanvasPath::Rect(double x, double y, double w, double h)
  1.4492 +{
  1.4493 +  MoveTo(x, y);
  1.4494 +  LineTo(x + w, y);
  1.4495 +  LineTo(x + w, y + h);
  1.4496 +  LineTo(x, y + h);
  1.4497 +  ClosePath();
  1.4498 +}
  1.4499 +
  1.4500 +void
  1.4501 +CanvasPath::Arc(double x, double y, double radius,
  1.4502 +                double startAngle, double endAngle, bool anticlockwise,
  1.4503 +                ErrorResult& error)
  1.4504 +{
  1.4505 +  if (radius < 0.0) {
  1.4506 +    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  1.4507 +    return;
  1.4508 +  }
  1.4509 +
  1.4510 +  ArcToBezier(this, Point(x, y), Size(radius, radius), startAngle, endAngle, anticlockwise);
  1.4511 +}
  1.4512 +
  1.4513 +void
  1.4514 +CanvasPath::LineTo(const gfx::Point& aPoint)
  1.4515 +{
  1.4516 +  EnsurePathBuilder();
  1.4517 +
  1.4518 +  mPathBuilder->LineTo(aPoint);
  1.4519 +}
  1.4520 +
  1.4521 +void
  1.4522 +CanvasPath::BezierTo(const gfx::Point& aCP1,
  1.4523 +                     const gfx::Point& aCP2,
  1.4524 +                     const gfx::Point& aCP3)
  1.4525 +{
  1.4526 +  EnsurePathBuilder();
  1.4527 +
  1.4528 +  mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
  1.4529 +}
  1.4530 +
  1.4531 +RefPtr<gfx::Path>
  1.4532 +CanvasPath::GetPath(const CanvasWindingRule& winding, const mozilla::RefPtr<mozilla::gfx::DrawTarget>& mTarget) const
  1.4533 +{
  1.4534 +  FillRule fillRule = FillRule::FILL_WINDING;
  1.4535 +  if (winding == CanvasWindingRule::Evenodd) {
  1.4536 +    fillRule = FillRule::FILL_EVEN_ODD;
  1.4537 +  }
  1.4538 +
  1.4539 +  if (mPath &&
  1.4540 +      (mPath->GetBackendType() == mTarget->GetType()) &&
  1.4541 +      (mPath->GetFillRule() == fillRule)) {
  1.4542 +    return mPath;
  1.4543 +  }
  1.4544 +
  1.4545 +  if (!mPath) {
  1.4546 +    // if there is no path, there must be a pathbuilder
  1.4547 +    MOZ_ASSERT(mPathBuilder);
  1.4548 +    mPath = mPathBuilder->Finish();
  1.4549 +    if (!mPath)
  1.4550 +      return mPath;
  1.4551 +
  1.4552 +    mPathBuilder = nullptr;
  1.4553 +  }
  1.4554 +
  1.4555 +  // retarget our backend if we're used with a different backend
  1.4556 +  if (mPath->GetBackendType() != mTarget->GetType()) {
  1.4557 +    RefPtr<PathBuilder> tmpPathBuilder = mTarget->CreatePathBuilder(fillRule);
  1.4558 +    mPath->StreamToSink(tmpPathBuilder);
  1.4559 +    mPath = tmpPathBuilder->Finish();
  1.4560 +  } else if (mPath->GetFillRule() != fillRule) {
  1.4561 +    RefPtr<PathBuilder> tmpPathBuilder = mPath->CopyToBuilder(fillRule);
  1.4562 +    mPath = tmpPathBuilder->Finish();
  1.4563 +  }
  1.4564 +
  1.4565 +  return mPath;
  1.4566 +}
  1.4567 +
  1.4568 +void
  1.4569 +CanvasPath::EnsurePathBuilder() const
  1.4570 +{
  1.4571 +  if (mPathBuilder) {
  1.4572 +    return;
  1.4573 +  }
  1.4574 +
  1.4575 +  // if there is not pathbuilder, there must be a path
  1.4576 +  MOZ_ASSERT(mPath);
  1.4577 +  mPathBuilder = mPath->CopyToBuilder();
  1.4578 +  mPath = nullptr;
  1.4579 +}
  1.4580 +
  1.4581 +}
  1.4582 +}

mercurial