Bug 1444460 [wpt PR 9948] - gyroscope: Rename LocalCoordinateSystem to GyroscopeLocal...
[gecko.git] / image / VectorImage.cpp
blob861db40a60b15188ee90868f48c47bb57462baaf
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"
8 #include "gfx2DGlue.h"
9 #include "gfxContext.h"
10 #include "gfxDrawable.h"
11 #include "gfxPlatform.h"
12 #include "gfxUtils.h"
13 #include "imgFrame.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"
25 #include "nsRect.h"
26 #include "nsString.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
41 #undef GetCurrentTime
43 namespace mozilla {
45 using namespace dom;
46 using namespace dom::SVGPreserveAspectRatioBinding;
47 using namespace gfx;
48 using namespace layers;
50 namespace image {
52 // Helper-class: SVGRootRenderingObserver
53 class SVGRootRenderingObserver final : public nsSVGRenderingObserver {
54 public:
55 NS_DECL_ISUPPORTS
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");
67 StartObserving();
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;
81 protected:
82 virtual ~SVGRootRenderingObserver()
84 StopObserving();
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.
101 return;
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;
118 // Private data
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 {
127 public:
128 NS_DECL_ISUPPORTS
130 SVGParseCompleteListener(nsIDocument* aDocument,
131 VectorImage* aImage)
132 : mDocument(aDocument)
133 , mImage(aImage)
135 MOZ_ASSERT(mDocument, "Need an SVG document");
136 MOZ_ASSERT(mImage, "Need an image");
138 mDocument->AddObserver(this);
141 private:
142 ~SVGParseCompleteListener()
144 if (mDocument) {
145 // The document must have been destroyed before we got our event.
146 // Otherwise this can't happen, since documents hold strong references to
147 // their observers.
148 Cancel();
152 public:
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();
164 void Cancel()
166 MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
167 if (mDocument) {
168 mDocument->RemoveObserver(this);
169 mDocument = nullptr;
173 private:
174 nsCOMPtr<nsIDocument> mDocument;
175 VectorImage* const mImage; // Raw pointer to owner.
178 NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver)
180 class SVGLoadEventListener final : public nsIDOMEventListener {
181 public:
182 NS_DECL_ISUPPORTS
184 SVGLoadEventListener(nsIDocument* aDocument,
185 VectorImage* aImage)
186 : mDocument(aDocument)
187 , mImage(aImage)
189 MOZ_ASSERT(mDocument, "Need an SVG document");
190 MOZ_ASSERT(mImage, "Need an image");
192 mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
193 this, true, false);
194 mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true,
195 false);
196 mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true,
197 false);
200 private:
201 ~SVGLoadEventListener()
203 if (mDocument) {
204 // The document must have been destroyed before we got our event.
205 // Otherwise this can't happen, since documents hold strong references to
206 // their observers.
207 Cancel();
211 public:
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();
229 } else {
230 mImage->OnSVGDocumentError();
233 return NS_OK;
236 void Cancel()
238 MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
239 if (mDocument) {
240 mDocument
241 ->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
242 this, true);
243 mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
244 mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
245 mDocument = nullptr;
249 private:
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 {
258 public:
259 SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
260 const IntSize& aViewportSize,
261 const IntSize& aSize,
262 uint32_t aImageFlags)
263 : mSVGDocumentWrapper(aSVGDocumentWrapper)
264 , mViewportSize(aViewportSize)
265 , mSize(aSize)
266 , mImageFlags(aImageFlags)
268 virtual bool operator()(gfxContext* aContext,
269 const gfxRect& aFillRect,
270 const SamplingFilter aSamplingFilter,
271 const gfxMatrix& aTransform) override;
272 private:
273 RefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
274 const IntSize mViewportSize;
275 const IntSize mSize;
276 uint32_t mImageFlags;
279 // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
280 bool
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");
292 return false;
294 MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null");
296 gfxContextAutoSaveRestore contextRestorer(aContext);
298 // Clip to aFillRect so that we don't paint outside.
299 aContext->NewPath();
300 aContext->Rectangle(aFillRect);
301 aContext->Clip();
303 gfxMatrix matrix = aTransform;
304 if (!matrix.Invert()) {
305 return false;
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");
315 nsRect svgRect(0, 0,
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
326 aContext);
328 return true;
331 class MOZ_STACK_CLASS AutoRestoreSVGState final {
332 public:
333 AutoRestoreSVGState(const SVGDrawingParameters& aParams,
334 SVGDocumentWrapper* aSVGDocumentWrapper,
335 bool& aIsDrawing,
336 bool aContextPaint)
337 : mIsDrawing(aIsDrawing)
338 // Apply any 'preserveAspectRatio' override (if specified) to the root
339 // element:
340 , mPAR(aParams.svgContext, aSVGDocumentWrapper->GetRootSVGElem())
341 // Set the animation time:
342 , mTime(aSVGDocumentWrapper->GetRootSVGElem(), aParams.animationTime)
344 MOZ_ASSERT(!aIsDrawing);
345 aIsDrawing = true;
347 // Set context paint (if specified) on the document:
348 if (aContextPaint) {
349 mContextPaint.emplace(aParams.svgContext->GetContextPaint(),
350 aSVGDocumentWrapper->GetDocument());
354 private:
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,
363 imgIContainer,
364 nsIStreamListener,
365 nsIRequestObserver)
367 //------------------------------------------------------------------------------
368 // Constructor / Destructor
370 VectorImage::VectorImage(ImageURL* aURI /* = nullptr */) :
371 ImageResource(aURI), // invoke superclass's constructor
372 mLockCount(0),
373 mIsInitialized(false),
374 mIsFullyLoaded(false),
375 mIsDrawing(false),
376 mHaveAnimations(false),
377 mHasPendingInvalidation(false)
380 VectorImage::~VectorImage()
382 CancelAllListeners();
383 SurfaceCache::RemoveImage(ImageKey(this));
386 //------------------------------------------------------------------------------
387 // Methods inherited from Image.h
389 nsresult
390 VectorImage::Init(const char* aMimeType,
391 uint32_t aFlags)
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.
405 if (!mDiscardable) {
406 mLockCount++;
407 SurfaceCache::LockImage(ImageKey(this));
410 mIsInitialized = true;
411 return NS_OK;
414 size_t
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();
422 if (!doc) {
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.
435 return 100 * 1024;
438 return windowSizes.getTotalSize();
441 void
442 VectorImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
443 MallocSizeOf aMallocSizeOf) const
445 SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
448 nsresult
449 VectorImage::OnImageDataComplete(nsIRequest* aRequest,
450 nsISupports* aContext,
451 nsresult aStatus,
452 bool aLastPart)
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);
468 } else {
469 // Record our progress so far; we'll actually send the notifications in
470 // OnSVGDocumentLoaded or OnSVGDocumentError.
471 mLoadProgress = Some(loadProgress);
474 return finalStatus;
477 nsresult
478 VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
479 nsISupports* aContext,
480 nsIInputStream* aInStr,
481 uint64_t aSourceOffset,
482 uint32_t aCount)
484 return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount);
487 nsresult
488 VectorImage::StartAnimation()
490 if (mError) {
491 return NS_ERROR_FAILURE;
494 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
496 mSVGDocumentWrapper->StartAnimation();
497 return NS_OK;
500 nsresult
501 VectorImage::StopAnimation()
503 nsresult rv = NS_OK;
504 if (mError) {
505 rv = NS_ERROR_FAILURE;
506 } else {
507 MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations,
508 "Should not have been animating!");
510 mSVGDocumentWrapper->StopAnimation();
513 mAnimating = false;
514 return rv;
517 bool
518 VectorImage::ShouldAnimate()
520 return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
523 NS_IMETHODIMP_(void)
524 VectorImage::SetAnimationStartTime(const TimeStamp& aTime)
526 // We don't care about animation start time.
529 //------------------------------------------------------------------------------
530 // imgIContainer methods
532 //******************************************************************************
533 NS_IMETHODIMP
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.
540 *aWidth = 0;
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) {
549 *aWidth = 0;
550 return NS_ERROR_FAILURE;
552 *aWidth = rootElemWidth;
553 return NS_OK;
556 //******************************************************************************
557 nsresult
558 VectorImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
560 return NS_ERROR_NOT_IMPLEMENTED;
563 //******************************************************************************
564 size_t
565 VectorImage::GetNativeSizesLength() const
567 return 0;
570 //******************************************************************************
571 NS_IMETHODIMP_(void)
572 VectorImage::RequestRefresh(const TimeStamp& aTime)
574 if (HadRecentRefresh(aTime)) {
575 return;
578 PendingAnimationTracker* tracker =
579 mSVGDocumentWrapper->GetDocument()->GetPendingAnimationTracker();
580 if (tracker && ShouldAnimate()) {
581 tracker->TriggerPendingAnimationsOnNextTick(aTime);
584 EvaluateAnimation();
586 mSVGDocumentWrapper->TickRefreshDriver();
588 if (mHasPendingInvalidation) {
589 mHasPendingInvalidation = false;
590 SendInvalidationNotifications();
594 void
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)
624 return aRect;
627 //******************************************************************************
628 NS_IMETHODIMP
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.
635 *aHeight = 0;
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) {
644 *aHeight = 0;
645 return NS_ERROR_FAILURE;
647 *aHeight = rootElemHeight;
648 return NS_OK;
651 //******************************************************************************
652 NS_IMETHODIMP
653 VectorImage::GetIntrinsicSize(nsSize* aSize)
655 if (mError || !mIsFullyLoaded) {
656 return NS_ERROR_FAILURE;
659 nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
660 if (!rootFrame) {
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();
673 return NS_OK;
676 //******************************************************************************
677 NS_IMETHODIMP
678 VectorImage::GetIntrinsicRatio(nsSize* aRatio)
680 if (mError || !mIsFullyLoaded) {
681 return NS_ERROR_FAILURE;
684 nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
685 if (!rootFrame) {
686 return NS_ERROR_FAILURE;
689 *aRatio = rootFrame->GetIntrinsicRatio();
690 return NS_OK;
693 NS_IMETHODIMP_(Orientation)
694 VectorImage::GetOrientation()
696 return Orientation();
699 //******************************************************************************
700 NS_IMETHODIMP
701 VectorImage::GetType(uint16_t* aType)
703 NS_ENSURE_ARG_POINTER(aType);
705 *aType = imgIContainer::TYPE_VECTOR;
706 return NS_OK;
709 //******************************************************************************
710 NS_IMETHODIMP
711 VectorImage::GetAnimated(bool* aAnimated)
713 if (mError || !mIsFullyLoaded) {
714 return NS_ERROR_FAILURE;
717 *aAnimated = mSVGDocumentWrapper->IsAnimated();
718 return NS_OK;
721 //******************************************************************************
722 int32_t
723 VectorImage::GetFirstFrameDelay()
725 if (mError) {
726 return -1;
729 if (!mSVGDocumentWrapper->IsAnimated()) {
730 return -1;
733 // We don't really have a frame delay, so just pretend that we constantly
734 // need updates.
735 return 0;
738 NS_IMETHODIMP_(bool)
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)
748 if (mError) {
749 return nullptr;
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
762 // height.
763 return nullptr;
766 return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags);
769 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
770 VectorImage::GetFrameAtSize(const IntSize& aSize,
771 uint32_t aWhichFrame,
772 uint32_t aFlags)
774 #ifdef DEBUG
775 NotifyDrawingObservers();
776 #endif
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,
786 uint32_t aFlags)
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>());
795 if (mError) {
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);
807 if (sourceSurface) {
808 return MakeTuple(ImgDrawResult::SUCCESS, aSize, Move(sourceSurface));
811 if (mIsDrawing) {
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
821 // cannot cache.
822 SVGDrawingParameters params(nullptr, aSize, ImageRegion::Create(aSize),
823 SamplingFilter::POINT, aSVGContext,
824 mSVGDocumentWrapper->GetCurrentTime(),
825 aFlags, 1.0);
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);
836 if (!surface) {
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 //******************************************************************************
847 IntSize
848 VectorImage::GetImageContainerSize(LayerManager* aManager,
849 const IntSize& aSize,
850 uint32_t aFlags)
852 if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
853 return IntSize(0, 0);
856 return aSize;
859 NS_IMETHODIMP_(bool)
860 VectorImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
862 if (mError || !mIsFullyLoaded || mHaveAnimations ||
863 aManager->GetBackendType() != LayersBackend::LAYERS_WR) {
864 return false;
867 return true;
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!");
876 return nullptr;
879 //******************************************************************************
880 NS_IMETHODIMP_(bool)
881 VectorImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
882 const IntSize& aSize,
883 uint32_t aFlags)
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,
895 uint32_t aFlags)
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,
908 flags);
911 bool
912 VectorImage::MaybeRestrictSVGContext(Maybe<SVGImageContext>& aNewSVGContext,
913 const Maybe<SVGImageContext>& aSVGContext,
914 uint32_t aFlags)
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
932 if (overridePAR) {
933 // The SVGImageContext must take account of the preserveAspectRatio
934 // overide:
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,
962 uint32_t aFlags,
963 float aOpacity)
965 if (aWhichFrame > FRAME_MAX_VALUE) {
966 return ImgDrawResult::BAD_ARGS;
969 if (!aContext) {
970 return ImgDrawResult::BAD_ARGS;
973 if (mError) {
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;
1001 bool contextPaint =
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:
1021 if (mIsDrawing) {
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,
1051 aParams.size,
1052 aParams.flags);
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,
1062 uint32_t aFlags)
1064 // If we're not allowed to use a cached surface, don't attempt a lookup.
1065 if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
1066 return nullptr;
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) {
1072 return nullptr;
1075 LookupResult result =
1076 SurfaceCache::Lookup(ImageKey(this),
1077 VectorSurfaceKey(aSize, aSVGContext));
1079 MOZ_ASSERT(result.SuggestedSize().IsEmpty(), "SVG should not substitute!");
1080 if (!result) {
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();
1089 return nullptr;
1092 return sourceSurface.forget();
1095 already_AddRefed<SourceSurface>
1096 VectorImage::CreateSurface(const SVGDrawingParameters& aParams,
1097 gfxDrawable* aSVGDrawable,
1098 bool& aWillCache)
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.
1111 !mHaveAnimations &&
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) {
1119 return nullptr;
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.
1128 if (aWillCache) {
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>>();
1140 nsresult rv =
1141 frame->InitWithDrawable(aSVGDrawable, aParams.size,
1142 SurfaceFormat::B8G8R8A8,
1143 SamplingFilter::POINT, aParams.flags,
1144 backend);
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)) {
1150 aWillCache = false;
1151 return nullptr;
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();
1157 if (!surface) {
1158 aWillCache = false;
1159 return nullptr;
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.
1164 if (!aWillCache) {
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();
1176 void
1177 VectorImage::SendFrameComplete(bool aDidCache, uint32_t aFlags)
1179 // If the cache was not updated, we have nothing to do.
1180 if (!aDidCache) {
1181 return;
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());
1189 } else {
1190 NotNull<RefPtr<VectorImage>> image = WrapNotNull(this);
1191 NS_DispatchToMainThread(NS_NewRunnableFunction(
1192 "ProgressTracker::SyncNotifyProgress",
1193 [=]() -> void {
1194 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
1195 if (tracker) {
1196 tracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
1197 GetMaxSizedIntRect());
1199 }));
1204 void
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),
1210 aParams.region,
1211 SurfaceFormat::B8G8R8A8,
1212 aParams.samplingFilter,
1213 aParams.flags, aParams.opacity);
1215 #ifdef DEBUG
1216 NotifyDrawingObservers();
1217 #endif
1219 MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
1220 mRenderingObserver->ResumeHonoringInvalidations();
1223 void
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));
1232 NS_IMETHODIMP
1233 VectorImage::StartDecoding(uint32_t aFlags)
1235 // Nothing to do for SVG images
1236 return NS_OK;
1239 bool
1240 VectorImage::StartDecodingWithResult(uint32_t aFlags)
1242 // SVG images are ready to draw when they are loaded
1243 return mIsFullyLoaded;
1246 NS_IMETHODIMP
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
1251 // rasterization...
1252 return NS_OK;
1255 //******************************************************************************
1257 NS_IMETHODIMP
1258 VectorImage::LockImage()
1260 MOZ_ASSERT(NS_IsMainThread());
1262 if (mError) {
1263 return NS_ERROR_FAILURE;
1266 mLockCount++;
1268 if (mLockCount == 1) {
1269 // Lock this image's surfaces in the SurfaceCache.
1270 SurfaceCache::LockImage(ImageKey(this));
1273 return NS_OK;
1276 //******************************************************************************
1278 NS_IMETHODIMP
1279 VectorImage::UnlockImage()
1281 MOZ_ASSERT(NS_IsMainThread());
1283 if (mError) {
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;
1292 mLockCount--;
1294 if (mLockCount == 0) {
1295 // Unlock this image's surfaces in the SurfaceCache.
1296 SurfaceCache::UnlockImage(ImageKey(this));
1299 return NS_OK;
1302 //******************************************************************************
1304 NS_IMETHODIMP
1305 VectorImage::RequestDiscard()
1307 MOZ_ASSERT(NS_IsMainThread());
1309 if (mDiscardable && mLockCount == 0) {
1310 SurfaceCache::RemoveImage(ImageKey(this));
1311 mProgressTracker->OnDiscard();
1314 return NS_OK;
1317 void
1318 VectorImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
1320 MOZ_ASSERT(mProgressTracker);
1322 NS_DispatchToMainThread(NewRunnableMethod("ProgressTracker::OnDiscard",
1323 mProgressTracker, &ProgressTracker::OnDiscard));
1326 //******************************************************************************
1327 NS_IMETHODIMP
1328 VectorImage::ResetAnimation()
1330 if (mError) {
1331 return NS_ERROR_FAILURE;
1334 if (!mIsFullyLoaded || !mHaveAnimations) {
1335 return NS_OK; // There are no animations to be reset.
1338 mSVGDocumentWrapper->ResetAnimation();
1340 return NS_OK;
1343 NS_IMETHODIMP_(float)
1344 VectorImage::GetFrameIndex(uint32_t aWhichFrame)
1346 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
1347 return aWhichFrame == FRAME_FIRST
1348 ? 0.0f
1349 : mSVGDocumentWrapper->GetCurrentTime();
1352 //------------------------------------------------------------------------------
1353 // nsIRequestObserver methods
1355 //******************************************************************************
1356 NS_IMETHODIMP
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;
1366 mError = true;
1367 return rv;
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);
1380 return NS_OK;
1383 //******************************************************************************
1384 NS_IMETHODIMP
1385 VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
1386 nsresult aStatus)
1388 if (mError) {
1389 return NS_ERROR_FAILURE;
1392 return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus);
1395 void
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
1406 // error.
1407 OnSVGDocumentError();
1411 void
1412 VectorImage::CancelAllListeners()
1414 if (mParseCompleteListener) {
1415 mParseCompleteListener->Cancel();
1416 mParseCompleteListener = nullptr;
1418 if (mLoadEventListener) {
1419 mLoadEventListener->Cancel();
1420 mLoadEventListener = nullptr;
1424 void
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();
1471 void
1472 VectorImage::OnSVGDocumentError()
1474 CancelAllListeners();
1476 mError = true;
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 //******************************************************************************
1496 NS_IMETHODIMP
1497 VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
1498 nsIInputStream* aInStr, uint64_t aSourceOffset,
1499 uint32_t aCount)
1501 if (mError) {
1502 return NS_ERROR_FAILURE;
1505 return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
1506 aSourceOffset, aCount);
1509 // --------------------------
1510 // Invalidation helper method
1512 void
1513 VectorImage::InvalidateObserversOnNextRefreshDriverTick()
1515 if (mHaveAnimations) {
1516 mHasPendingInvalidation = true;
1517 } else {
1518 SendInvalidationNotifications();
1522 void
1523 VectorImage::PropagateUseCounters(nsIDocument* aParentDocument)
1525 nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
1526 if (doc) {
1527 doc->PropagateUseCounters(aParentDocument);
1531 void
1532 VectorImage::ReportUseCounters()
1534 nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
1535 if (doc) {
1536 static_cast<nsDocument*>(doc)->ReportUseCounters();
1540 nsIntSize
1541 VectorImage::OptimalImageSizeForDest(const gfxSize& aDest,
1542 uint32_t aWhichFrame,
1543 SamplingFilter aSamplingFilter,
1544 uint32_t aFlags)
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