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