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 +}