1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "VectorImage.h"
9 #include "gfxContext.h"
10 #include "gfxDrawable.h"
11 #include "gfxPlatform.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "mozilla/gfx/2D.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/Tuple.h"
20 #include "nsIDOMEvent.h"
21 #include "nsIPresShell.h"
22 #include "nsIStreamListener.h"
23 #include "nsMimeTypes.h"
24 #include "nsPresContext.h"
27 #include "nsStubDocumentObserver.h"
28 #include "SVGObserverUtils.h" // for nsSVGRenderingObserver
29 #include "nsWindowSizes.h"
30 #include "ImageRegion.h"
31 #include "ISurfaceProvider.h"
32 #include "LookupResult.h"
33 #include "Orientation.h"
34 #include "SVGDocumentWrapper.h"
35 #include "SVGDrawingParameters.h"
36 #include "nsIDOMEventListener.h"
37 #include "SurfaceCache.h"
38 #include "nsDocument.h"
40 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
46 using namespace dom::SVGPreserveAspectRatioBinding
;
48 using namespace layers
;
52 // Helper-class: SVGRootRenderingObserver
53 class SVGRootRenderingObserver final
: public nsSVGRenderingObserver
{
57 SVGRootRenderingObserver(SVGDocumentWrapper
* aDocWrapper
,
58 VectorImage
* aVectorImage
)
59 : nsSVGRenderingObserver()
60 , mDocWrapper(aDocWrapper
)
61 , mVectorImage(aVectorImage
)
62 , mHonoringInvalidations(true)
64 MOZ_ASSERT(mDocWrapper
, "Need a non-null SVG document wrapper");
65 MOZ_ASSERT(mVectorImage
, "Need a non-null VectorImage");
68 Element
* elem
= GetTarget();
69 MOZ_ASSERT(elem
, "no root SVG node for us to observe");
71 SVGObserverUtils::AddRenderingObserver(elem
, this);
72 mInObserverList
= true;
76 void ResumeHonoringInvalidations()
78 mHonoringInvalidations
= true;
82 virtual ~SVGRootRenderingObserver()
87 virtual Element
* GetTarget() override
89 return mDocWrapper
->GetRootSVGElem();
92 virtual void OnRenderingChange() override
94 Element
* elem
= GetTarget();
95 MOZ_ASSERT(elem
, "missing root SVG node");
97 if (mHonoringInvalidations
&& !mDocWrapper
->ShouldIgnoreInvalidation()) {
98 nsIFrame
* frame
= elem
->GetPrimaryFrame();
99 if (!frame
|| frame
->PresShell()->IsDestroying()) {
100 // We're being destroyed. Bail out.
104 // Ignore further invalidations until we draw.
105 mHonoringInvalidations
= false;
107 mVectorImage
->InvalidateObserversOnNextRefreshDriverTick();
110 // Our caller might've removed us from rendering-observer list.
111 // Add ourselves back!
112 if (!mInObserverList
) {
113 SVGObserverUtils::AddRenderingObserver(elem
, this);
114 mInObserverList
= true;
119 const RefPtr
<SVGDocumentWrapper
> mDocWrapper
;
120 VectorImage
* const mVectorImage
; // Raw pointer because it owns me.
121 bool mHonoringInvalidations
;
124 NS_IMPL_ISUPPORTS(SVGRootRenderingObserver
, nsIMutationObserver
)
126 class SVGParseCompleteListener final
: public nsStubDocumentObserver
{
130 SVGParseCompleteListener(nsIDocument
* aDocument
,
132 : mDocument(aDocument
)
135 MOZ_ASSERT(mDocument
, "Need an SVG document");
136 MOZ_ASSERT(mImage
, "Need an image");
138 mDocument
->AddObserver(this);
142 ~SVGParseCompleteListener()
145 // The document must have been destroyed before we got our event.
146 // Otherwise this can't happen, since documents hold strong references to
153 void EndLoad(nsIDocument
* aDocument
) override
155 MOZ_ASSERT(aDocument
== mDocument
, "Got EndLoad for wrong document?");
157 // OnSVGDocumentParsed will release our owner's reference to us, so ensure
158 // we stick around long enough to complete our work.
159 RefPtr
<SVGParseCompleteListener
> kungFuDeathGrip(this);
161 mImage
->OnSVGDocumentParsed();
166 MOZ_ASSERT(mDocument
, "Duplicate call to Cancel");
168 mDocument
->RemoveObserver(this);
174 nsCOMPtr
<nsIDocument
> mDocument
;
175 VectorImage
* const mImage
; // Raw pointer to owner.
178 NS_IMPL_ISUPPORTS(SVGParseCompleteListener
, nsIDocumentObserver
)
180 class SVGLoadEventListener final
: public nsIDOMEventListener
{
184 SVGLoadEventListener(nsIDocument
* aDocument
,
186 : mDocument(aDocument
)
189 MOZ_ASSERT(mDocument
, "Need an SVG document");
190 MOZ_ASSERT(mImage
, "Need an image");
192 mDocument
->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
194 mDocument
->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true,
196 mDocument
->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true,
201 ~SVGLoadEventListener()
204 // The document must have been destroyed before we got our event.
205 // Otherwise this can't happen, since documents hold strong references to
212 NS_IMETHOD
HandleEvent(nsIDOMEvent
* aEvent
) override
214 MOZ_ASSERT(mDocument
, "Need an SVG document. Received multiple events?");
216 // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference
217 // to us, so ensure we stick around long enough to complete our work.
218 RefPtr
<SVGLoadEventListener
> kungFuDeathGrip(this);
220 nsAutoString eventType
;
221 aEvent
->GetType(eventType
);
222 MOZ_ASSERT(eventType
.EqualsLiteral("MozSVGAsImageDocumentLoad") ||
223 eventType
.EqualsLiteral("SVGAbort") ||
224 eventType
.EqualsLiteral("SVGError"),
225 "Received unexpected event");
227 if (eventType
.EqualsLiteral("MozSVGAsImageDocumentLoad")) {
228 mImage
->OnSVGDocumentLoaded();
230 mImage
->OnSVGDocumentError();
238 MOZ_ASSERT(mDocument
, "Duplicate call to Cancel");
241 ->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
243 mDocument
->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
244 mDocument
->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
250 nsCOMPtr
<nsIDocument
> mDocument
;
251 VectorImage
* const mImage
; // Raw pointer to owner.
254 NS_IMPL_ISUPPORTS(SVGLoadEventListener
, nsIDOMEventListener
)
256 // Helper-class: SVGDrawingCallback
257 class SVGDrawingCallback
: public gfxDrawingCallback
{
259 SVGDrawingCallback(SVGDocumentWrapper
* aSVGDocumentWrapper
,
260 const IntSize
& aViewportSize
,
261 const IntSize
& aSize
,
262 uint32_t aImageFlags
)
263 : mSVGDocumentWrapper(aSVGDocumentWrapper
)
264 , mViewportSize(aViewportSize
)
266 , mImageFlags(aImageFlags
)
268 virtual bool operator()(gfxContext
* aContext
,
269 const gfxRect
& aFillRect
,
270 const SamplingFilter aSamplingFilter
,
271 const gfxMatrix
& aTransform
) override
;
273 RefPtr
<SVGDocumentWrapper
> mSVGDocumentWrapper
;
274 const IntSize mViewportSize
;
276 uint32_t mImageFlags
;
279 // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
281 SVGDrawingCallback::operator()(gfxContext
* aContext
,
282 const gfxRect
& aFillRect
,
283 const SamplingFilter aSamplingFilter
,
284 const gfxMatrix
& aTransform
)
286 MOZ_ASSERT(mSVGDocumentWrapper
, "need an SVGDocumentWrapper");
288 // Get (& sanity-check) the helper-doc's presShell
289 nsCOMPtr
<nsIPresShell
> presShell
;
290 if (NS_FAILED(mSVGDocumentWrapper
->GetPresShell(getter_AddRefs(presShell
)))) {
291 NS_WARNING("Unable to draw -- presShell lookup failed");
294 MOZ_ASSERT(presShell
, "GetPresShell succeeded but returned null");
296 gfxContextAutoSaveRestore
contextRestorer(aContext
);
298 // Clip to aFillRect so that we don't paint outside.
300 aContext
->Rectangle(aFillRect
);
303 gfxMatrix matrix
= aTransform
;
304 if (!matrix
.Invert()) {
307 aContext
->SetMatrixDouble(
308 aContext
->CurrentMatrixDouble().PreMultiply(matrix
).
309 PreScale(double(mSize
.width
) / mViewportSize
.width
,
310 double(mSize
.height
) / mViewportSize
.height
));
312 nsPresContext
* presContext
= presShell
->GetPresContext();
313 MOZ_ASSERT(presContext
, "pres shell w/out pres context");
316 presContext
->DevPixelsToAppUnits(mViewportSize
.width
),
317 presContext
->DevPixelsToAppUnits(mViewportSize
.height
));
319 uint32_t renderDocFlags
= nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING
;
320 if (!(mImageFlags
& imgIContainer::FLAG_SYNC_DECODE
)) {
321 renderDocFlags
|= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES
;
324 presShell
->RenderDocument(svgRect
, renderDocFlags
,
325 NS_RGBA(0, 0, 0, 0), // transparent
331 class MOZ_STACK_CLASS AutoRestoreSVGState final
{
333 AutoRestoreSVGState(const SVGDrawingParameters
& aParams
,
334 SVGDocumentWrapper
* aSVGDocumentWrapper
,
337 : mIsDrawing(aIsDrawing
)
338 // Apply any 'preserveAspectRatio' override (if specified) to the root
340 , mPAR(aParams
.svgContext
, aSVGDocumentWrapper
->GetRootSVGElem())
341 // Set the animation time:
342 , mTime(aSVGDocumentWrapper
->GetRootSVGElem(), aParams
.animationTime
)
344 MOZ_ASSERT(!aIsDrawing
);
347 // Set context paint (if specified) on the document:
349 mContextPaint
.emplace(aParams
.svgContext
->GetContextPaint(),
350 aSVGDocumentWrapper
->GetDocument());
355 AutoRestore
<bool> mIsDrawing
;
356 AutoPreserveAspectRatioOverride mPAR
;
357 AutoSVGTimeSetRestore mTime
;
358 Maybe
<AutoSetRestoreSVGContextPaint
> mContextPaint
;
361 // Implement VectorImage's nsISupports-inherited methods
362 NS_IMPL_ISUPPORTS(VectorImage
,
367 //------------------------------------------------------------------------------
368 // Constructor / Destructor
370 VectorImage::VectorImage(ImageURL
* aURI
/* = nullptr */) :
371 ImageResource(aURI
), // invoke superclass's constructor
373 mIsInitialized(false),
374 mIsFullyLoaded(false),
376 mHaveAnimations(false),
377 mHasPendingInvalidation(false)
380 VectorImage::~VectorImage()
382 CancelAllListeners();
383 SurfaceCache::RemoveImage(ImageKey(this));
386 //------------------------------------------------------------------------------
387 // Methods inherited from Image.h
390 VectorImage::Init(const char* aMimeType
,
393 // We don't support re-initialization
394 if (mIsInitialized
) {
395 return NS_ERROR_ILLEGAL_VALUE
;
398 MOZ_ASSERT(!mIsFullyLoaded
&& !mHaveAnimations
&& !mError
,
399 "Flags unexpectedly set before initialization");
400 MOZ_ASSERT(!strcmp(aMimeType
, IMAGE_SVG_XML
), "Unexpected mimetype");
402 mDiscardable
= !!(aFlags
& INIT_FLAG_DISCARDABLE
);
404 // Lock this image's surfaces in the SurfaceCache if we're not discardable.
407 SurfaceCache::LockImage(ImageKey(this));
410 mIsInitialized
= true;
415 VectorImage::SizeOfSourceWithComputedFallback(SizeOfState
& aState
) const
417 if (!mSVGDocumentWrapper
) {
418 return 0; // No document, so no memory used for the document.
421 nsIDocument
* doc
= mSVGDocumentWrapper
->GetDocument();
423 return 0; // No document, so no memory used for the document.
426 nsWindowSizes
windowSizes(aState
);
427 doc
->DocAddSizeOfIncludingThis(windowSizes
);
429 if (windowSizes
.getTotalSize() == 0) {
430 // MallocSizeOf fails on this platform. Because we also use this method for
431 // determining the size of cache entries, we need to return something
432 // reasonable here. Unfortunately, there's no way to estimate the document's
433 // size accurately, so we just use a constant value of 100KB, which will
434 // generally underestimate the true size.
438 return windowSizes
.getTotalSize();
442 VectorImage::CollectSizeOfSurfaces(nsTArray
<SurfaceMemoryCounter
>& aCounters
,
443 MallocSizeOf aMallocSizeOf
) const
445 SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters
, aMallocSizeOf
);
449 VectorImage::OnImageDataComplete(nsIRequest
* aRequest
,
450 nsISupports
* aContext
,
454 // Call our internal OnStopRequest method, which only talks to our embedded
455 // SVG document. This won't have any effect on our ProgressTracker.
456 nsresult finalStatus
= OnStopRequest(aRequest
, aContext
, aStatus
);
458 // Give precedence to Necko failure codes.
459 if (NS_FAILED(aStatus
)) {
460 finalStatus
= aStatus
;
463 Progress loadProgress
= LoadCompleteProgress(aLastPart
, mError
, finalStatus
);
465 if (mIsFullyLoaded
|| mError
) {
466 // Our document is loaded, so we're ready to notify now.
467 mProgressTracker
->SyncNotifyProgress(loadProgress
);
469 // Record our progress so far; we'll actually send the notifications in
470 // OnSVGDocumentLoaded or OnSVGDocumentError.
471 mLoadProgress
= Some(loadProgress
);
478 VectorImage::OnImageDataAvailable(nsIRequest
* aRequest
,
479 nsISupports
* aContext
,
480 nsIInputStream
* aInStr
,
481 uint64_t aSourceOffset
,
484 return OnDataAvailable(aRequest
, aContext
, aInStr
, aSourceOffset
, aCount
);
488 VectorImage::StartAnimation()
491 return NS_ERROR_FAILURE
;
494 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
496 mSVGDocumentWrapper
->StartAnimation();
501 VectorImage::StopAnimation()
505 rv
= NS_ERROR_FAILURE
;
507 MOZ_ASSERT(mIsFullyLoaded
&& mHaveAnimations
,
508 "Should not have been animating!");
510 mSVGDocumentWrapper
->StopAnimation();
518 VectorImage::ShouldAnimate()
520 return ImageResource::ShouldAnimate() && mIsFullyLoaded
&& mHaveAnimations
;
524 VectorImage::SetAnimationStartTime(const TimeStamp
& aTime
)
526 // We don't care about animation start time.
529 //------------------------------------------------------------------------------
530 // imgIContainer methods
532 //******************************************************************************
534 VectorImage::GetWidth(int32_t* aWidth
)
536 if (mError
|| !mIsFullyLoaded
) {
537 // XXXdholbert Technically we should leave outparam untouched when we
538 // fail. But since many callers don't check for failure, we set it to 0 on
539 // failure, for sane/predictable results.
541 return NS_ERROR_FAILURE
;
544 SVGSVGElement
* rootElem
= mSVGDocumentWrapper
->GetRootSVGElem();
545 MOZ_ASSERT(rootElem
, "Should have a root SVG elem, since we finished "
546 "loading without errors");
547 int32_t rootElemWidth
= rootElem
->GetIntrinsicWidth();
548 if (rootElemWidth
< 0) {
550 return NS_ERROR_FAILURE
;
552 *aWidth
= rootElemWidth
;
556 //******************************************************************************
558 VectorImage::GetNativeSizes(nsTArray
<IntSize
>& aNativeSizes
) const
560 return NS_ERROR_NOT_IMPLEMENTED
;
563 //******************************************************************************
565 VectorImage::GetNativeSizesLength() const
570 //******************************************************************************
572 VectorImage::RequestRefresh(const TimeStamp
& aTime
)
574 if (HadRecentRefresh(aTime
)) {
578 PendingAnimationTracker
* tracker
=
579 mSVGDocumentWrapper
->GetDocument()->GetPendingAnimationTracker();
580 if (tracker
&& ShouldAnimate()) {
581 tracker
->TriggerPendingAnimationsOnNextTick(aTime
);
586 mSVGDocumentWrapper
->TickRefreshDriver();
588 if (mHasPendingInvalidation
) {
589 mHasPendingInvalidation
= false;
590 SendInvalidationNotifications();
595 VectorImage::SendInvalidationNotifications()
597 // Animated images don't send out invalidation notifications as soon as
598 // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick
599 // records that there are pending invalidations and then returns immediately.
600 // The notifications are actually sent from RequestRefresh(). We send these
601 // notifications there to ensure that there is actually a document observing
602 // us. Otherwise, the notifications are just wasted effort.
604 // Non-animated images call this method directly from
605 // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
606 // called for them. Ordinarily this isn't needed, since we send out
607 // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
608 // SVG document may not be 100% ready to render at that time. In those cases
609 // we would miss the subsequent invalidations if we didn't send out the
610 // notifications directly in |InvalidateObservers...|.
612 if (mProgressTracker
) {
613 SurfaceCache::RemoveImage(ImageKey(this));
614 mProgressTracker
->SyncNotifyProgress(FLAG_FRAME_COMPLETE
,
615 GetMaxSizedIntRect());
618 UpdateImageContainer();
621 NS_IMETHODIMP_(IntRect
)
622 VectorImage::GetImageSpaceInvalidationRect(const IntRect
& aRect
)
627 //******************************************************************************
629 VectorImage::GetHeight(int32_t* aHeight
)
631 if (mError
|| !mIsFullyLoaded
) {
632 // XXXdholbert Technically we should leave outparam untouched when we
633 // fail. But since many callers don't check for failure, we set it to 0 on
634 // failure, for sane/predictable results.
636 return NS_ERROR_FAILURE
;
639 SVGSVGElement
* rootElem
= mSVGDocumentWrapper
->GetRootSVGElem();
640 MOZ_ASSERT(rootElem
, "Should have a root SVG elem, since we finished "
641 "loading without errors");
642 int32_t rootElemHeight
= rootElem
->GetIntrinsicHeight();
643 if (rootElemHeight
< 0) {
645 return NS_ERROR_FAILURE
;
647 *aHeight
= rootElemHeight
;
651 //******************************************************************************
653 VectorImage::GetIntrinsicSize(nsSize
* aSize
)
655 if (mError
|| !mIsFullyLoaded
) {
656 return NS_ERROR_FAILURE
;
659 nsIFrame
* rootFrame
= mSVGDocumentWrapper
->GetRootLayoutFrame();
661 return NS_ERROR_FAILURE
;
664 *aSize
= nsSize(-1, -1);
665 IntrinsicSize rfSize
= rootFrame
->GetIntrinsicSize();
666 if (rfSize
.width
.GetUnit() == eStyleUnit_Coord
) {
667 aSize
->width
= rfSize
.width
.GetCoordValue();
669 if (rfSize
.height
.GetUnit() == eStyleUnit_Coord
) {
670 aSize
->height
= rfSize
.height
.GetCoordValue();
676 //******************************************************************************
678 VectorImage::GetIntrinsicRatio(nsSize
* aRatio
)
680 if (mError
|| !mIsFullyLoaded
) {
681 return NS_ERROR_FAILURE
;
684 nsIFrame
* rootFrame
= mSVGDocumentWrapper
->GetRootLayoutFrame();
686 return NS_ERROR_FAILURE
;
689 *aRatio
= rootFrame
->GetIntrinsicRatio();
693 NS_IMETHODIMP_(Orientation
)
694 VectorImage::GetOrientation()
696 return Orientation();
699 //******************************************************************************
701 VectorImage::GetType(uint16_t* aType
)
703 NS_ENSURE_ARG_POINTER(aType
);
705 *aType
= imgIContainer::TYPE_VECTOR
;
709 //******************************************************************************
711 VectorImage::GetAnimated(bool* aAnimated
)
713 if (mError
|| !mIsFullyLoaded
) {
714 return NS_ERROR_FAILURE
;
717 *aAnimated
= mSVGDocumentWrapper
->IsAnimated();
721 //******************************************************************************
723 VectorImage::GetFirstFrameDelay()
729 if (!mSVGDocumentWrapper
->IsAnimated()) {
733 // We don't really have a frame delay, so just pretend that we constantly
739 VectorImage::WillDrawOpaqueNow()
741 return false; // In general, SVG content is not opaque.
744 //******************************************************************************
745 NS_IMETHODIMP_(already_AddRefed
<SourceSurface
>)
746 VectorImage::GetFrame(uint32_t aWhichFrame
, uint32_t aFlags
)
752 // Look up height & width
753 // ----------------------
754 SVGSVGElement
* svgElem
= mSVGDocumentWrapper
->GetRootSVGElem();
755 MOZ_ASSERT(svgElem
, "Should have a root SVG elem, since we finished "
756 "loading without errors");
757 nsIntSize
imageIntSize(svgElem
->GetIntrinsicWidth(),
758 svgElem
->GetIntrinsicHeight());
760 if (imageIntSize
.IsEmpty()) {
761 // We'll get here if our SVG doc has a percent-valued or negative width or
766 return GetFrameAtSize(imageIntSize
, aWhichFrame
, aFlags
);
769 NS_IMETHODIMP_(already_AddRefed
<SourceSurface
>)
770 VectorImage::GetFrameAtSize(const IntSize
& aSize
,
771 uint32_t aWhichFrame
,
775 NotifyDrawingObservers();
778 auto result
= GetFrameInternal(aSize
, Nothing(), aWhichFrame
, aFlags
);
779 return Get
<2>(result
).forget();
782 Tuple
<ImgDrawResult
, IntSize
, RefPtr
<SourceSurface
>>
783 VectorImage::GetFrameInternal(const IntSize
& aSize
,
784 const Maybe
<SVGImageContext
>& aSVGContext
,
785 uint32_t aWhichFrame
,
788 MOZ_ASSERT(aWhichFrame
<= FRAME_MAX_VALUE
);
790 if (aSize
.IsEmpty() || aWhichFrame
> FRAME_MAX_VALUE
) {
791 return MakeTuple(ImgDrawResult::BAD_ARGS
, aSize
,
792 RefPtr
<SourceSurface
>());
796 return MakeTuple(ImgDrawResult::BAD_IMAGE
, aSize
,
797 RefPtr
<SourceSurface
>());
800 if (!mIsFullyLoaded
) {
801 return MakeTuple(ImgDrawResult::NOT_READY
, aSize
,
802 RefPtr
<SourceSurface
>());
805 RefPtr
<SourceSurface
> sourceSurface
=
806 LookupCachedSurface(aSize
, aSVGContext
, aFlags
);
808 return MakeTuple(ImgDrawResult::SUCCESS
, aSize
, Move(sourceSurface
));
812 NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
813 return MakeTuple(ImgDrawResult::TEMPORARY_ERROR
, aSize
,
814 RefPtr
<SourceSurface
>());
817 // By using a null gfxContext, we ensure that we will always attempt to
818 // create a surface, even if we aren't capable of caching it (e.g. due to our
819 // flags, having an animation, etc). Otherwise CreateSurface will assume that
820 // the caller is capable of drawing directly to its own draw target if we
822 SVGDrawingParameters
params(nullptr, aSize
, ImageRegion::Create(aSize
),
823 SamplingFilter::POINT
, aSVGContext
,
824 mSVGDocumentWrapper
->GetCurrentTime(),
827 bool didCache
; // Was the surface put into the cache?
828 bool contextPaint
= aSVGContext
&& aSVGContext
->GetContextPaint();
830 AutoRestoreSVGState
autoRestore(params
, mSVGDocumentWrapper
,
831 mIsDrawing
, contextPaint
);
833 RefPtr
<gfxDrawable
> svgDrawable
= CreateSVGDrawable(params
);
834 RefPtr
<SourceSurface
> surface
=
835 CreateSurface(params
, svgDrawable
, didCache
);
837 MOZ_ASSERT(!didCache
);
838 return MakeTuple(ImgDrawResult::TEMPORARY_ERROR
, aSize
,
839 RefPtr
<SourceSurface
>());
842 SendFrameComplete(didCache
, params
.flags
);
843 return MakeTuple(ImgDrawResult::SUCCESS
, aSize
, Move(surface
));
846 //******************************************************************************
848 VectorImage::GetImageContainerSize(LayerManager
* aManager
,
849 const IntSize
& aSize
,
852 if (!IsImageContainerAvailableAtSize(aManager
, aSize
, aFlags
)) {
853 return IntSize(0, 0);
860 VectorImage::IsImageContainerAvailable(LayerManager
* aManager
, uint32_t aFlags
)
862 if (mError
|| !mIsFullyLoaded
|| mHaveAnimations
||
863 aManager
->GetBackendType() != LayersBackend::LAYERS_WR
) {
870 //******************************************************************************
871 NS_IMETHODIMP_(already_AddRefed
<ImageContainer
>)
872 VectorImage::GetImageContainer(LayerManager
* aManager
, uint32_t aFlags
)
874 MOZ_ASSERT(aManager
->GetBackendType() != LayersBackend::LAYERS_WR
,
875 "WebRender should always use GetImageContainerAvailableAtSize!");
879 //******************************************************************************
881 VectorImage::IsImageContainerAvailableAtSize(LayerManager
* aManager
,
882 const IntSize
& aSize
,
885 // Since we only support image containers with WebRender, and it can handle
886 // textures larger than the hw max texture size, we don't need to check aSize.
887 return !aSize
.IsEmpty() && IsImageContainerAvailable(aManager
, aFlags
);
890 //******************************************************************************
891 NS_IMETHODIMP_(already_AddRefed
<ImageContainer
>)
892 VectorImage::GetImageContainerAtSize(LayerManager
* aManager
,
893 const IntSize
& aSize
,
894 const Maybe
<SVGImageContext
>& aSVGContext
,
897 Maybe
<SVGImageContext
> newSVGContext
;
898 MaybeRestrictSVGContext(newSVGContext
, aSVGContext
, aFlags
);
900 // Since we do not support high quality scaling with SVG, we mask it off so
901 // that container requests with and without it map to the same container.
902 // Similarly the aspect ratio flag was already handled as part of the SVG
903 // context restriction above.
904 uint32_t flags
= aFlags
& ~(FLAG_HIGH_QUALITY_SCALING
|
905 FLAG_FORCE_PRESERVEASPECTRATIO_NONE
);
906 return GetImageContainerImpl(aManager
, aSize
,
907 newSVGContext
? newSVGContext
: aSVGContext
,
912 VectorImage::MaybeRestrictSVGContext(Maybe
<SVGImageContext
>& aNewSVGContext
,
913 const Maybe
<SVGImageContext
>& aSVGContext
,
916 bool overridePAR
= (aFlags
& FLAG_FORCE_PRESERVEASPECTRATIO_NONE
) && aSVGContext
;
918 bool haveContextPaint
= aSVGContext
&& aSVGContext
->GetContextPaint();
919 bool blockContextPaint
= false;
920 if (haveContextPaint
) {
921 nsCOMPtr
<nsIURI
> imageURI
= mURI
->ToIURI();
922 blockContextPaint
= !SVGContextPaint::IsAllowedForImageFromURI(imageURI
);
925 if (overridePAR
|| blockContextPaint
) {
926 // The key that we create for the image surface cache must match the way
927 // that the image will be painted, so we need to initialize a new matching
928 // SVGImageContext here in order to generate the correct key.
930 aNewSVGContext
= aSVGContext
; // copy
933 // The SVGImageContext must take account of the preserveAspectRatio
935 MOZ_ASSERT(!aSVGContext
->GetPreserveAspectRatio(),
936 "FLAG_FORCE_PRESERVEASPECTRATIO_NONE is not expected if a "
937 "preserveAspectRatio override is supplied");
938 Maybe
<SVGPreserveAspectRatio
> aspectRatio
=
939 Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE
,
940 SVG_MEETORSLICE_UNKNOWN
));
941 aNewSVGContext
->SetPreserveAspectRatio(aspectRatio
);
944 if (blockContextPaint
) {
945 // The SVGImageContext must not include context paint if the image is
946 // not allowed to use it:
947 aNewSVGContext
->ClearContextPaint();
951 return haveContextPaint
&& !blockContextPaint
;
954 //******************************************************************************
955 NS_IMETHODIMP_(ImgDrawResult
)
956 VectorImage::Draw(gfxContext
* aContext
,
957 const nsIntSize
& aSize
,
958 const ImageRegion
& aRegion
,
959 uint32_t aWhichFrame
,
960 SamplingFilter aSamplingFilter
,
961 const Maybe
<SVGImageContext
>& aSVGContext
,
965 if (aWhichFrame
> FRAME_MAX_VALUE
) {
966 return ImgDrawResult::BAD_ARGS
;
970 return ImgDrawResult::BAD_ARGS
;
974 return ImgDrawResult::BAD_IMAGE
;
977 if (!mIsFullyLoaded
) {
978 return ImgDrawResult::NOT_READY
;
981 if (mAnimationConsumers
== 0) {
982 SendOnUnlockedDraw(aFlags
);
985 // We should always bypass the cache when using DrawTargetRecording because
986 // we prefer the drawing commands in general to the rasterized surface. This
987 // allows blob images to avoid rasterized SVGs with WebRender.
988 if (aContext
->GetDrawTarget()->GetBackendType() == BackendType::RECORDING
) {
989 aFlags
|= FLAG_BYPASS_SURFACE_CACHE
;
992 MOZ_ASSERT(!(aFlags
& FLAG_FORCE_PRESERVEASPECTRATIO_NONE
) ||
993 (aSVGContext
&& aSVGContext
->GetViewportSize()),
994 "Viewport size is required when using "
995 "FLAG_FORCE_PRESERVEASPECTRATIO_NONE");
997 float animTime
= (aWhichFrame
== FRAME_FIRST
)
998 ? 0.0f
: mSVGDocumentWrapper
->GetCurrentTime();
1000 Maybe
<SVGImageContext
> newSVGContext
;
1002 MaybeRestrictSVGContext(newSVGContext
, aSVGContext
, aFlags
);
1004 SVGDrawingParameters
params(aContext
, aSize
, aRegion
, aSamplingFilter
,
1005 newSVGContext
? newSVGContext
: aSVGContext
,
1006 animTime
, aFlags
, aOpacity
);
1008 // If we have an prerasterized version of this image that matches the
1009 // drawing parameters, use that.
1010 RefPtr
<SourceSurface
> sourceSurface
=
1011 LookupCachedSurface(aSize
, params
.svgContext
, aFlags
);
1012 if (sourceSurface
) {
1013 RefPtr
<gfxDrawable
> svgDrawable
=
1014 new gfxSurfaceDrawable(sourceSurface
, sourceSurface
->GetSize());
1015 Show(svgDrawable
, params
);
1016 return ImgDrawResult::SUCCESS
;
1019 // else, we need to paint the image:
1022 NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
1023 return ImgDrawResult::TEMPORARY_ERROR
;
1026 AutoRestoreSVGState
autoRestore(params
, mSVGDocumentWrapper
,
1027 mIsDrawing
, contextPaint
);
1029 bool didCache
; // Was the surface put into the cache?
1030 RefPtr
<gfxDrawable
> svgDrawable
= CreateSVGDrawable(params
);
1031 sourceSurface
= CreateSurface(params
, svgDrawable
, didCache
);
1032 if (!sourceSurface
) {
1033 MOZ_ASSERT(!didCache
);
1034 Show(svgDrawable
, params
);
1035 return ImgDrawResult::SUCCESS
;
1038 RefPtr
<gfxDrawable
> drawable
=
1039 new gfxSurfaceDrawable(sourceSurface
, params
.size
);
1040 Show(drawable
, params
);
1041 SendFrameComplete(didCache
, params
.flags
);
1042 return ImgDrawResult::SUCCESS
;
1045 already_AddRefed
<gfxDrawable
>
1046 VectorImage::CreateSVGDrawable(const SVGDrawingParameters
& aParams
)
1048 RefPtr
<gfxDrawingCallback
> cb
=
1049 new SVGDrawingCallback(mSVGDocumentWrapper
,
1050 aParams
.viewportSize
,
1054 RefPtr
<gfxDrawable
> svgDrawable
=
1055 new gfxCallbackDrawable(cb
, aParams
.size
);
1056 return svgDrawable
.forget();
1059 already_AddRefed
<SourceSurface
>
1060 VectorImage::LookupCachedSurface(const IntSize
& aSize
,
1061 const Maybe
<SVGImageContext
>& aSVGContext
,
1064 // If we're not allowed to use a cached surface, don't attempt a lookup.
1065 if (aFlags
& FLAG_BYPASS_SURFACE_CACHE
) {
1069 // We don't do any caching if we have animation, so don't bother with a lookup
1070 // in this case either.
1071 if (mHaveAnimations
) {
1075 LookupResult result
=
1076 SurfaceCache::Lookup(ImageKey(this),
1077 VectorSurfaceKey(aSize
, aSVGContext
));
1079 MOZ_ASSERT(result
.SuggestedSize().IsEmpty(), "SVG should not substitute!");
1081 return nullptr; // No matching surface, or the OS freed the volatile buffer.
1084 RefPtr
<SourceSurface
> sourceSurface
= result
.Surface()->GetSourceSurface();
1085 if (!sourceSurface
) {
1086 // Something went wrong. (Probably a GPU driver crash or device reset.)
1087 // Attempt to recover.
1088 RecoverFromLossOfSurfaces();
1092 return sourceSurface
.forget();
1095 already_AddRefed
<SourceSurface
>
1096 VectorImage::CreateSurface(const SVGDrawingParameters
& aParams
,
1097 gfxDrawable
* aSVGDrawable
,
1100 MOZ_ASSERT(mIsDrawing
);
1102 mSVGDocumentWrapper
->UpdateViewportBounds(aParams
.viewportSize
);
1103 mSVGDocumentWrapper
->FlushImageTransformInvalidation();
1105 // Determine whether or not we should put the surface to be created into
1106 // the cache. If we fail, we need to reset this to false to let the caller
1107 // know nothing was put in the cache.
1108 aWillCache
= !(aParams
.flags
& FLAG_BYPASS_SURFACE_CACHE
) &&
1109 // Refuse to cache animated images:
1110 // XXX(seth): We may remove this restriction in bug 922893.
1112 // The image is too big to fit in the cache:
1113 SurfaceCache::CanHold(aParams
.size
);
1115 // If we weren't given a context, then we know we just want the rasterized
1116 // surface. We will create the frame below but only insert it into the cache
1117 // if we actually need to.
1118 if (!aWillCache
&& aParams
.context
) {
1122 // We're about to rerasterize, which may mean that some of the previous
1123 // surfaces we've rasterized aren't useful anymore. We can allow them to
1124 // expire from the cache by unlocking them here, and then sending out an
1125 // invalidation. If this image is locked, any surfaces that are still useful
1126 // will become locked again when Draw touches them, and the remainder will
1127 // eventually expire.
1129 SurfaceCache::UnlockEntries(ImageKey(this));
1132 // If there is no context, the default backend is fine.
1133 BackendType backend
=
1134 aParams
.context
? aParams
.context
->GetDrawTarget()->GetBackendType()
1135 : gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1137 // Try to create an imgFrame, initializing the surface it contains by drawing
1138 // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
1139 auto frame
= MakeNotNull
<RefPtr
<imgFrame
>>();
1141 frame
->InitWithDrawable(aSVGDrawable
, aParams
.size
,
1142 SurfaceFormat::B8G8R8A8
,
1143 SamplingFilter::POINT
, aParams
.flags
,
1146 // If we couldn't create the frame, it was probably because it would end
1147 // up way too big. Generally it also wouldn't fit in the cache, but the prefs
1148 // could be set such that the cache isn't the limiting factor.
1149 if (NS_FAILED(rv
)) {
1154 // Take a strong reference to the frame's surface and make sure it hasn't
1155 // already been purged by the operating system.
1156 RefPtr
<SourceSurface
> surface
= frame
->GetSourceSurface();
1162 // We created the frame, but only because we had no context to draw to
1163 // directly. All the caller wants is the surface in this case.
1165 return surface
.forget();
1168 // Attempt to cache the frame.
1169 SurfaceKey surfaceKey
= VectorSurfaceKey(aParams
.size
, aParams
.svgContext
);
1170 NotNull
<RefPtr
<ISurfaceProvider
>> provider
=
1171 MakeNotNull
<SimpleSurfaceProvider
*>(ImageKey(this), surfaceKey
, frame
);
1172 SurfaceCache::Insert(provider
);
1173 return surface
.forget();
1177 VectorImage::SendFrameComplete(bool aDidCache
, uint32_t aFlags
)
1179 // If the cache was not updated, we have nothing to do.
1184 // Send out an invalidation so that surfaces that are still in use get
1185 // re-locked. See the discussion of the UnlockSurfaces call above.
1186 if (!(aFlags
& FLAG_ASYNC_NOTIFY
)) {
1187 mProgressTracker
->SyncNotifyProgress(FLAG_FRAME_COMPLETE
,
1188 GetMaxSizedIntRect());
1190 NotNull
<RefPtr
<VectorImage
>> image
= WrapNotNull(this);
1191 NS_DispatchToMainThread(NS_NewRunnableFunction(
1192 "ProgressTracker::SyncNotifyProgress",
1194 RefPtr
<ProgressTracker
> tracker
= image
->GetProgressTracker();
1196 tracker
->SyncNotifyProgress(FLAG_FRAME_COMPLETE
,
1197 GetMaxSizedIntRect());
1205 VectorImage::Show(gfxDrawable
* aDrawable
, const SVGDrawingParameters
& aParams
)
1207 MOZ_ASSERT(aDrawable
, "Should have a gfxDrawable by now");
1208 gfxUtils::DrawPixelSnapped(aParams
.context
, aDrawable
,
1209 SizeDouble(aParams
.size
),
1211 SurfaceFormat::B8G8R8A8
,
1212 aParams
.samplingFilter
,
1213 aParams
.flags
, aParams
.opacity
);
1216 NotifyDrawingObservers();
1219 MOZ_ASSERT(mRenderingObserver
, "Should have a rendering observer by now");
1220 mRenderingObserver
->ResumeHonoringInvalidations();
1224 VectorImage::RecoverFromLossOfSurfaces()
1226 NS_WARNING("An imgFrame became invalid. Attempting to recover...");
1228 // Discard all existing frames, since they're probably all now invalid.
1229 SurfaceCache::RemoveImage(ImageKey(this));
1233 VectorImage::StartDecoding(uint32_t aFlags
)
1235 // Nothing to do for SVG images
1240 VectorImage::StartDecodingWithResult(uint32_t aFlags
)
1242 // SVG images are ready to draw when they are loaded
1243 return mIsFullyLoaded
;
1247 VectorImage::RequestDecodeForSize(const nsIntSize
& aSize
, uint32_t aFlags
)
1249 // Nothing to do for SVG images, though in theory we could rasterize to the
1250 // provided size ahead of time if we supported off-main-thread SVG
1255 //******************************************************************************
1258 VectorImage::LockImage()
1260 MOZ_ASSERT(NS_IsMainThread());
1263 return NS_ERROR_FAILURE
;
1268 if (mLockCount
== 1) {
1269 // Lock this image's surfaces in the SurfaceCache.
1270 SurfaceCache::LockImage(ImageKey(this));
1276 //******************************************************************************
1279 VectorImage::UnlockImage()
1281 MOZ_ASSERT(NS_IsMainThread());
1284 return NS_ERROR_FAILURE
;
1287 if (mLockCount
== 0) {
1288 MOZ_ASSERT_UNREACHABLE("Calling UnlockImage with a zero lock count");
1289 return NS_ERROR_ABORT
;
1294 if (mLockCount
== 0) {
1295 // Unlock this image's surfaces in the SurfaceCache.
1296 SurfaceCache::UnlockImage(ImageKey(this));
1302 //******************************************************************************
1305 VectorImage::RequestDiscard()
1307 MOZ_ASSERT(NS_IsMainThread());
1309 if (mDiscardable
&& mLockCount
== 0) {
1310 SurfaceCache::RemoveImage(ImageKey(this));
1311 mProgressTracker
->OnDiscard();
1318 VectorImage::OnSurfaceDiscarded(const SurfaceKey
& aSurfaceKey
)
1320 MOZ_ASSERT(mProgressTracker
);
1322 NS_DispatchToMainThread(NewRunnableMethod("ProgressTracker::OnDiscard",
1323 mProgressTracker
, &ProgressTracker::OnDiscard
));
1326 //******************************************************************************
1328 VectorImage::ResetAnimation()
1331 return NS_ERROR_FAILURE
;
1334 if (!mIsFullyLoaded
|| !mHaveAnimations
) {
1335 return NS_OK
; // There are no animations to be reset.
1338 mSVGDocumentWrapper
->ResetAnimation();
1343 NS_IMETHODIMP_(float)
1344 VectorImage::GetFrameIndex(uint32_t aWhichFrame
)
1346 MOZ_ASSERT(aWhichFrame
<= FRAME_MAX_VALUE
, "Invalid argument");
1347 return aWhichFrame
== FRAME_FIRST
1349 : mSVGDocumentWrapper
->GetCurrentTime();
1352 //------------------------------------------------------------------------------
1353 // nsIRequestObserver methods
1355 //******************************************************************************
1357 VectorImage::OnStartRequest(nsIRequest
* aRequest
, nsISupports
* aCtxt
)
1359 MOZ_ASSERT(!mSVGDocumentWrapper
,
1360 "Repeated call to OnStartRequest -- can this happen?");
1362 mSVGDocumentWrapper
= new SVGDocumentWrapper();
1363 nsresult rv
= mSVGDocumentWrapper
->OnStartRequest(aRequest
, aCtxt
);
1364 if (NS_FAILED(rv
)) {
1365 mSVGDocumentWrapper
= nullptr;
1370 // Create a listener to wait until the SVG document is fully loaded, which
1371 // will signal that this image is ready to render. Certain error conditions
1372 // will prevent us from ever getting this notification, so we also create a
1373 // listener that waits for parsing to complete and cancels the
1374 // SVGLoadEventListener if needed. The listeners are automatically attached
1375 // to the document by their constructors.
1376 nsIDocument
* document
= mSVGDocumentWrapper
->GetDocument();
1377 mLoadEventListener
= new SVGLoadEventListener(document
, this);
1378 mParseCompleteListener
= new SVGParseCompleteListener(document
, this);
1383 //******************************************************************************
1385 VectorImage::OnStopRequest(nsIRequest
* aRequest
, nsISupports
* aCtxt
,
1389 return NS_ERROR_FAILURE
;
1392 return mSVGDocumentWrapper
->OnStopRequest(aRequest
, aCtxt
, aStatus
);
1396 VectorImage::OnSVGDocumentParsed()
1398 MOZ_ASSERT(mParseCompleteListener
, "Should have the parse complete listener");
1399 MOZ_ASSERT(mLoadEventListener
, "Should have the load event listener");
1401 if (!mSVGDocumentWrapper
->GetRootSVGElem()) {
1402 // This is an invalid SVG document. It may have failed to parse, or it may
1403 // be missing the <svg> root element, or the <svg> root element may not
1404 // declare the correct namespace. In any of these cases, we'll never be
1405 // notified that the SVG finished loading, so we need to treat this as an
1407 OnSVGDocumentError();
1412 VectorImage::CancelAllListeners()
1414 if (mParseCompleteListener
) {
1415 mParseCompleteListener
->Cancel();
1416 mParseCompleteListener
= nullptr;
1418 if (mLoadEventListener
) {
1419 mLoadEventListener
->Cancel();
1420 mLoadEventListener
= nullptr;
1425 VectorImage::OnSVGDocumentLoaded()
1427 MOZ_ASSERT(mSVGDocumentWrapper
->GetRootSVGElem(),
1428 "Should have parsed successfully");
1429 MOZ_ASSERT(!mIsFullyLoaded
&& !mHaveAnimations
,
1430 "These flags shouldn't get set until OnSVGDocumentLoaded. "
1431 "Duplicate calls to OnSVGDocumentLoaded?");
1433 CancelAllListeners();
1435 // XXX Flushing is wasteful if embedding frame hasn't had initial reflow.
1436 mSVGDocumentWrapper
->FlushLayout();
1438 mIsFullyLoaded
= true;
1439 mHaveAnimations
= mSVGDocumentWrapper
->IsAnimated();
1441 // Start listening to our image for rendering updates.
1442 mRenderingObserver
= new SVGRootRenderingObserver(mSVGDocumentWrapper
, this);
1444 // ProgressTracker::SyncNotifyProgress may release us, so ensure we
1445 // stick around long enough to complete our work.
1446 RefPtr
<VectorImage
> kungFuDeathGrip(this);
1448 // Tell *our* observers that we're done loading.
1449 if (mProgressTracker
) {
1450 Progress progress
= FLAG_SIZE_AVAILABLE
|
1451 FLAG_HAS_TRANSPARENCY
|
1452 FLAG_FRAME_COMPLETE
|
1453 FLAG_DECODE_COMPLETE
;
1455 if (mHaveAnimations
) {
1456 progress
|= FLAG_IS_ANIMATED
;
1459 // Merge in any saved progress from OnImageDataComplete.
1460 if (mLoadProgress
) {
1461 progress
|= *mLoadProgress
;
1462 mLoadProgress
= Nothing();
1465 mProgressTracker
->SyncNotifyProgress(progress
, GetMaxSizedIntRect());
1468 EvaluateAnimation();
1472 VectorImage::OnSVGDocumentError()
1474 CancelAllListeners();
1478 if (mProgressTracker
) {
1479 // Notify observers about the error and unblock page load.
1480 Progress progress
= FLAG_HAS_ERROR
;
1482 // Merge in any saved progress from OnImageDataComplete.
1483 if (mLoadProgress
) {
1484 progress
|= *mLoadProgress
;
1485 mLoadProgress
= Nothing();
1488 mProgressTracker
->SyncNotifyProgress(progress
);
1492 //------------------------------------------------------------------------------
1493 // nsIStreamListener method
1495 //******************************************************************************
1497 VectorImage::OnDataAvailable(nsIRequest
* aRequest
, nsISupports
* aCtxt
,
1498 nsIInputStream
* aInStr
, uint64_t aSourceOffset
,
1502 return NS_ERROR_FAILURE
;
1505 return mSVGDocumentWrapper
->OnDataAvailable(aRequest
, aCtxt
, aInStr
,
1506 aSourceOffset
, aCount
);
1509 // --------------------------
1510 // Invalidation helper method
1513 VectorImage::InvalidateObserversOnNextRefreshDriverTick()
1515 if (mHaveAnimations
) {
1516 mHasPendingInvalidation
= true;
1518 SendInvalidationNotifications();
1523 VectorImage::PropagateUseCounters(nsIDocument
* aParentDocument
)
1525 nsIDocument
* doc
= mSVGDocumentWrapper
->GetDocument();
1527 doc
->PropagateUseCounters(aParentDocument
);
1532 VectorImage::ReportUseCounters()
1534 nsIDocument
* doc
= mSVGDocumentWrapper
->GetDocument();
1536 static_cast<nsDocument
*>(doc
)->ReportUseCounters();
1541 VectorImage::OptimalImageSizeForDest(const gfxSize
& aDest
,
1542 uint32_t aWhichFrame
,
1543 SamplingFilter aSamplingFilter
,
1546 MOZ_ASSERT(aDest
.width
>= 0 || ceil(aDest
.width
) <= INT32_MAX
||
1547 aDest
.height
>= 0 || ceil(aDest
.height
) <= INT32_MAX
,
1548 "Unexpected destination size");
1550 // We can rescale SVGs freely, so just return the provided destination size.
1551 return nsIntSize::Ceil(aDest
.width
, aDest
.height
);
1554 already_AddRefed
<imgIContainer
>
1555 VectorImage::Unwrap()
1557 nsCOMPtr
<imgIContainer
> self(this);
1558 return self
.forget();
1561 } // namespace image
1562 } // namespace mozilla