LCOV - code coverage report
Current view: top level - image - VectorImage.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 341 558 61.1 %
Date: 2017-07-14 16:53:18 Functions: 55 88 62.5 %
Legend: Lines: hit not hit

          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 "VectorImage.h"
       7             : 
       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 "nsIDOMEvent.h"
      20             : #include "nsIPresShell.h"
      21             : #include "nsIStreamListener.h"
      22             : #include "nsMimeTypes.h"
      23             : #include "nsPresContext.h"
      24             : #include "nsRect.h"
      25             : #include "nsString.h"
      26             : #include "nsStubDocumentObserver.h"
      27             : #include "nsSVGEffects.h" // for nsSVGRenderingObserver
      28             : #include "nsWindowMemoryReporter.h"
      29             : #include "ImageRegion.h"
      30             : #include "ISurfaceProvider.h"
      31             : #include "LookupResult.h"
      32             : #include "Orientation.h"
      33             : #include "SVGDocumentWrapper.h"
      34             : #include "SVGDrawingParameters.h"
      35             : #include "nsIDOMEventListener.h"
      36             : #include "SurfaceCache.h"
      37             : #include "nsDocument.h"
      38             : 
      39             : // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
      40             : #undef GetCurrentTime
      41             : 
      42             : namespace mozilla {
      43             : 
      44             : using namespace dom;
      45             : using namespace gfx;
      46             : using namespace layers;
      47             : 
      48             : namespace image {
      49             : 
      50             : // Helper-class: SVGRootRenderingObserver
      51             : class SVGRootRenderingObserver final : public nsSVGRenderingObserver {
      52             : public:
      53             :   NS_DECL_ISUPPORTS
      54             : 
      55          21 :   SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper,
      56             :                            VectorImage*        aVectorImage)
      57          21 :     : nsSVGRenderingObserver()
      58             :     , mDocWrapper(aDocWrapper)
      59             :     , mVectorImage(aVectorImage)
      60          21 :     , mHonoringInvalidations(true)
      61             :   {
      62          21 :     MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
      63          21 :     MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
      64             : 
      65          21 :     StartListening();
      66          21 :     Element* elem = GetTarget();
      67          21 :     MOZ_ASSERT(elem, "no root SVG node for us to observe");
      68             : 
      69          21 :     nsSVGEffects::AddRenderingObserver(elem, this);
      70          21 :     mInObserverList = true;
      71          21 :   }
      72             : 
      73             : 
      74          73 :   void ResumeHonoringInvalidations()
      75             :   {
      76          73 :     mHonoringInvalidations = true;
      77          73 :   }
      78             : 
      79             : protected:
      80           0 :   virtual ~SVGRootRenderingObserver()
      81           0 :   {
      82           0 :     StopListening();
      83           0 :   }
      84             : 
      85         114 :   virtual Element* GetTarget() override
      86             :   {
      87         114 :     return mDocWrapper->GetRootSVGElem();
      88             :   }
      89             : 
      90          72 :   virtual void DoUpdate() override
      91             :   {
      92          72 :     Element* elem = GetTarget();
      93          72 :     MOZ_ASSERT(elem, "missing root SVG node");
      94             : 
      95          72 :     if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) {
      96           0 :       nsIFrame* frame = elem->GetPrimaryFrame();
      97           0 :       if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
      98             :         // We're being destroyed. Bail out.
      99           0 :         return;
     100             :       }
     101             : 
     102             :       // Ignore further invalidations until we draw.
     103           0 :       mHonoringInvalidations = false;
     104             : 
     105           0 :       mVectorImage->InvalidateObserversOnNextRefreshDriverTick();
     106             :     }
     107             : 
     108             :     // Our caller might've removed us from rendering-observer list.
     109             :     // Add ourselves back!
     110          72 :     if (!mInObserverList) {
     111          72 :       nsSVGEffects::AddRenderingObserver(elem, this);
     112          72 :       mInObserverList = true;
     113             :     }
     114             :   }
     115             : 
     116             :   // Private data
     117             :   const RefPtr<SVGDocumentWrapper> mDocWrapper;
     118             :   VectorImage* const mVectorImage;   // Raw pointer because it owns me.
     119             :   bool mHonoringInvalidations;
     120             : };
     121             : 
     122          21 : NS_IMPL_ISUPPORTS(SVGRootRenderingObserver, nsIMutationObserver)
     123             : 
     124             : class SVGParseCompleteListener final : public nsStubDocumentObserver {
     125             : public:
     126             :   NS_DECL_ISUPPORTS
     127             : 
     128          21 :   SVGParseCompleteListener(nsIDocument* aDocument,
     129             :                            VectorImage* aImage)
     130          21 :     : mDocument(aDocument)
     131          21 :     , mImage(aImage)
     132             :   {
     133          21 :     MOZ_ASSERT(mDocument, "Need an SVG document");
     134          21 :     MOZ_ASSERT(mImage, "Need an image");
     135             : 
     136          21 :     mDocument->AddObserver(this);
     137          21 :   }
     138             : 
     139             : private:
     140          21 :   ~SVGParseCompleteListener()
     141          42 :   {
     142          21 :     if (mDocument) {
     143             :       // The document must have been destroyed before we got our event.
     144             :       // Otherwise this can't happen, since documents hold strong references to
     145             :       // their observers.
     146           0 :       Cancel();
     147             :     }
     148          21 :   }
     149             : 
     150             : public:
     151          21 :   void EndLoad(nsIDocument* aDocument) override
     152             :   {
     153          21 :     MOZ_ASSERT(aDocument == mDocument, "Got EndLoad for wrong document?");
     154             : 
     155             :     // OnSVGDocumentParsed will release our owner's reference to us, so ensure
     156             :     // we stick around long enough to complete our work.
     157          42 :     RefPtr<SVGParseCompleteListener> kungFuDeathGrip(this);
     158             : 
     159          21 :     mImage->OnSVGDocumentParsed();
     160          21 :   }
     161             : 
     162          21 :   void Cancel()
     163             :   {
     164          21 :     MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
     165          21 :     if (mDocument) {
     166          21 :       mDocument->RemoveObserver(this);
     167          21 :       mDocument = nullptr;
     168             :     }
     169          21 :   }
     170             : 
     171             : private:
     172             :   nsCOMPtr<nsIDocument> mDocument;
     173             :   VectorImage* const mImage; // Raw pointer to owner.
     174             : };
     175             : 
     176         955 : NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver)
     177             : 
     178             : class SVGLoadEventListener final : public nsIDOMEventListener {
     179             : public:
     180             :   NS_DECL_ISUPPORTS
     181             : 
     182          21 :   SVGLoadEventListener(nsIDocument* aDocument,
     183             :                        VectorImage* aImage)
     184          21 :     : mDocument(aDocument)
     185          21 :     , mImage(aImage)
     186             :   {
     187          21 :     MOZ_ASSERT(mDocument, "Need an SVG document");
     188          21 :     MOZ_ASSERT(mImage, "Need an image");
     189             : 
     190          63 :     mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
     191          42 :                                 this, true, false);
     192          63 :     mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true,
     193          42 :                                 false);
     194          63 :     mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true,
     195          42 :                                 false);
     196          21 :   }
     197             : 
     198             : private:
     199          21 :   ~SVGLoadEventListener()
     200          42 :   {
     201          21 :     if (mDocument) {
     202             :       // The document must have been destroyed before we got our event.
     203             :       // Otherwise this can't happen, since documents hold strong references to
     204             :       // their observers.
     205           0 :       Cancel();
     206             :     }
     207          21 :   }
     208             : 
     209             : public:
     210          21 :   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override
     211             :   {
     212          21 :     MOZ_ASSERT(mDocument, "Need an SVG document. Received multiple events?");
     213             : 
     214             :     // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference
     215             :     // to us, so ensure we stick around long enough to complete our work.
     216          42 :     RefPtr<SVGLoadEventListener> kungFuDeathGrip(this);
     217             : 
     218          42 :     nsAutoString eventType;
     219          21 :     aEvent->GetType(eventType);
     220          21 :     MOZ_ASSERT(eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")  ||
     221             :                eventType.EqualsLiteral("SVGAbort")                   ||
     222             :                eventType.EqualsLiteral("SVGError"),
     223             :                "Received unexpected event");
     224             : 
     225          21 :     if (eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")) {
     226          21 :       mImage->OnSVGDocumentLoaded();
     227             :     } else {
     228           0 :       mImage->OnSVGDocumentError();
     229             :     }
     230             : 
     231          42 :     return NS_OK;
     232             :   }
     233             : 
     234          21 :   void Cancel()
     235             :   {
     236          21 :     MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
     237          21 :     if (mDocument) {
     238             :       mDocument
     239          84 :         ->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
     240          63 :                               this, true);
     241          21 :       mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
     242          21 :       mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
     243          21 :       mDocument = nullptr;
     244             :     }
     245          21 :   }
     246             : 
     247             : private:
     248             :   nsCOMPtr<nsIDocument> mDocument;
     249             :   VectorImage* const mImage; // Raw pointer to owner.
     250             : };
     251             : 
     252         462 : NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener)
     253             : 
     254             : // Helper-class: SVGDrawingCallback
     255          54 : class SVGDrawingCallback : public gfxDrawingCallback {
     256             : public:
     257          18 :   SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
     258             :                      const IntSize& aViewportSize,
     259             :                      const IntSize& aSize,
     260             :                      uint32_t aImageFlags)
     261          18 :     : mSVGDocumentWrapper(aSVGDocumentWrapper)
     262             :     , mViewportSize(aViewportSize)
     263             :     , mSize(aSize)
     264          18 :     , mImageFlags(aImageFlags)
     265          18 :   { }
     266             :   virtual bool operator()(gfxContext* aContext,
     267             :                           const gfxRect& aFillRect,
     268             :                           const SamplingFilter aSamplingFilter,
     269             :                           const gfxMatrix& aTransform);
     270             : private:
     271             :   RefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
     272             :   const IntSize                mViewportSize;
     273             :   const IntSize                mSize;
     274             :   uint32_t                     mImageFlags;
     275             : };
     276             : 
     277             : // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
     278             : bool
     279          18 : SVGDrawingCallback::operator()(gfxContext* aContext,
     280             :                                const gfxRect& aFillRect,
     281             :                                const SamplingFilter aSamplingFilter,
     282             :                                const gfxMatrix& aTransform)
     283             : {
     284          18 :   MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper");
     285             : 
     286             :   // Get (& sanity-check) the helper-doc's presShell
     287          36 :   nsCOMPtr<nsIPresShell> presShell;
     288          18 :   if (NS_FAILED(mSVGDocumentWrapper->GetPresShell(getter_AddRefs(presShell)))) {
     289           0 :     NS_WARNING("Unable to draw -- presShell lookup failed");
     290           0 :     return false;
     291             :   }
     292          18 :   MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null");
     293             : 
     294          36 :   gfxContextAutoSaveRestore contextRestorer(aContext);
     295             : 
     296             :   // Clip to aFillRect so that we don't paint outside.
     297          18 :   aContext->NewPath();
     298          18 :   aContext->Rectangle(aFillRect);
     299          18 :   aContext->Clip();
     300             : 
     301          18 :   gfxMatrix matrix = aTransform;
     302          18 :   if (!matrix.Invert()) {
     303           0 :     return false;
     304             :   }
     305             :   aContext->SetMatrix(
     306          36 :     aContext->CurrentMatrix().PreMultiply(matrix).
     307          18 :                               PreScale(double(mSize.width) / mViewportSize.width,
     308          54 :                                        double(mSize.height) / mViewportSize.height));
     309             : 
     310          18 :   nsPresContext* presContext = presShell->GetPresContext();
     311          18 :   MOZ_ASSERT(presContext, "pres shell w/out pres context");
     312             : 
     313             :   nsRect svgRect(0, 0,
     314          18 :                  presContext->DevPixelsToAppUnits(mViewportSize.width),
     315          54 :                  presContext->DevPixelsToAppUnits(mViewportSize.height));
     316             : 
     317          18 :   uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
     318          18 :   if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
     319          18 :     renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
     320             :   }
     321             : 
     322          18 :   presShell->RenderDocument(svgRect, renderDocFlags,
     323             :                             NS_RGBA(0, 0, 0, 0), // transparent
     324          18 :                             aContext);
     325             : 
     326          18 :   return true;
     327             : }
     328             : 
     329             : // Implement VectorImage's nsISupports-inherited methods
     330        5704 : NS_IMPL_ISUPPORTS(VectorImage,
     331             :                   imgIContainer,
     332             :                   nsIStreamListener,
     333             :                   nsIRequestObserver)
     334             : 
     335             : //------------------------------------------------------------------------------
     336             : // Constructor / Destructor
     337             : 
     338          21 : VectorImage::VectorImage(ImageURL* aURI /* = nullptr */) :
     339             :   ImageResource(aURI), // invoke superclass's constructor
     340             :   mLockCount(0),
     341             :   mIsInitialized(false),
     342             :   mIsFullyLoaded(false),
     343             :   mIsDrawing(false),
     344             :   mHaveAnimations(false),
     345          21 :   mHasPendingInvalidation(false)
     346          21 : { }
     347             : 
     348           0 : VectorImage::~VectorImage()
     349             : {
     350           0 :   CancelAllListeners();
     351           0 :   SurfaceCache::RemoveImage(ImageKey(this));
     352           0 : }
     353             : 
     354             : //------------------------------------------------------------------------------
     355             : // Methods inherited from Image.h
     356             : 
     357             : nsresult
     358          21 : VectorImage::Init(const char* aMimeType,
     359             :                   uint32_t aFlags)
     360             : {
     361             :   // We don't support re-initialization
     362          21 :   if (mIsInitialized) {
     363           0 :     return NS_ERROR_ILLEGAL_VALUE;
     364             :   }
     365             : 
     366          21 :   MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError,
     367             :              "Flags unexpectedly set before initialization");
     368          21 :   MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype");
     369             : 
     370          21 :   mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
     371             : 
     372             :   // Lock this image's surfaces in the SurfaceCache if we're not discardable.
     373          21 :   if (!mDiscardable) {
     374          21 :     mLockCount++;
     375          21 :     SurfaceCache::LockImage(ImageKey(this));
     376             :   }
     377             : 
     378          21 :   mIsInitialized = true;
     379          21 :   return NS_OK;
     380             : }
     381             : 
     382             : size_t
     383          21 : VectorImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
     384             : {
     385          21 :   if (!mSVGDocumentWrapper) {
     386           0 :     return 0; // No document, so no memory used for the document.
     387             :   }
     388             : 
     389          21 :   nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
     390          21 :   if (!doc) {
     391           0 :     return 0; // No document, so no memory used for the document.
     392             :   }
     393             : 
     394          21 :   nsWindowSizes windowSizes(aMallocSizeOf);
     395          21 :   doc->DocAddSizeOfIncludingThis(&windowSizes);
     396             : 
     397          21 :   if (windowSizes.getTotalSize() == 0) {
     398             :     // MallocSizeOf fails on this platform. Because we also use this method for
     399             :     // determining the size of cache entries, we need to return something
     400             :     // reasonable here. Unfortunately, there's no way to estimate the document's
     401             :     // size accurately, so we just use a constant value of 100KB, which will
     402             :     // generally underestimate the true size.
     403           0 :     return 100 * 1024;
     404             :   }
     405             : 
     406          21 :   return windowSizes.getTotalSize();
     407             : }
     408             : 
     409             : void
     410           0 : VectorImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
     411             :                                    MallocSizeOf aMallocSizeOf) const
     412             : {
     413           0 :   SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
     414           0 : }
     415             : 
     416             : nsresult
     417          21 : VectorImage::OnImageDataComplete(nsIRequest* aRequest,
     418             :                                  nsISupports* aContext,
     419             :                                  nsresult aStatus,
     420             :                                  bool aLastPart)
     421             : {
     422             :   // Call our internal OnStopRequest method, which only talks to our embedded
     423             :   // SVG document. This won't have any effect on our ProgressTracker.
     424          21 :   nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus);
     425             : 
     426             :   // Give precedence to Necko failure codes.
     427          21 :   if (NS_FAILED(aStatus)) {
     428           0 :     finalStatus = aStatus;
     429             :   }
     430             : 
     431          21 :   Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
     432             : 
     433          21 :   if (mIsFullyLoaded || mError) {
     434             :     // Our document is loaded, so we're ready to notify now.
     435           0 :     mProgressTracker->SyncNotifyProgress(loadProgress);
     436             :   } else {
     437             :     // Record our progress so far; we'll actually send the notifications in
     438             :     // OnSVGDocumentLoaded or OnSVGDocumentError.
     439          21 :     mLoadProgress = Some(loadProgress);
     440             :   }
     441             : 
     442          21 :   return finalStatus;
     443             : }
     444             : 
     445             : nsresult
     446          21 : VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
     447             :                                   nsISupports* aContext,
     448             :                                   nsIInputStream* aInStr,
     449             :                                   uint64_t aSourceOffset,
     450             :                                   uint32_t aCount)
     451             : {
     452          21 :   return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount);
     453             : }
     454             : 
     455             : nsresult
     456           0 : VectorImage::StartAnimation()
     457             : {
     458           0 :   if (mError) {
     459           0 :     return NS_ERROR_FAILURE;
     460             :   }
     461             : 
     462           0 :   MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
     463             : 
     464           0 :   mSVGDocumentWrapper->StartAnimation();
     465           0 :   return NS_OK;
     466             : }
     467             : 
     468             : nsresult
     469           0 : VectorImage::StopAnimation()
     470             : {
     471           0 :   nsresult rv = NS_OK;
     472           0 :   if (mError) {
     473           0 :     rv = NS_ERROR_FAILURE;
     474             :   } else {
     475           0 :     MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations,
     476             :                "Should not have been animating!");
     477             : 
     478           0 :     mSVGDocumentWrapper->StopAnimation();
     479             :   }
     480             : 
     481           0 :   mAnimating = false;
     482           0 :   return rv;
     483             : }
     484             : 
     485             : bool
     486          21 : VectorImage::ShouldAnimate()
     487             : {
     488          21 :   return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
     489             : }
     490             : 
     491             : NS_IMETHODIMP_(void)
     492           0 : VectorImage::SetAnimationStartTime(const TimeStamp& aTime)
     493             : {
     494             :   // We don't care about animation start time.
     495           0 : }
     496             : 
     497             : //------------------------------------------------------------------------------
     498             : // imgIContainer methods
     499             : 
     500             : //******************************************************************************
     501             : NS_IMETHODIMP
     502         329 : VectorImage::GetWidth(int32_t* aWidth)
     503             : {
     504         329 :   if (mError || !mIsFullyLoaded) {
     505             :     // XXXdholbert Technically we should leave outparam untouched when we
     506             :     // fail. But since many callers don't check for failure, we set it to 0 on
     507             :     // failure, for sane/predictable results.
     508           4 :     *aWidth = 0;
     509           4 :     return NS_ERROR_FAILURE;
     510             :   }
     511             : 
     512         325 :   SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
     513         325 :   MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
     514             :              "loading without errors");
     515         325 :   int32_t rootElemWidth = rootElem->GetIntrinsicWidth();
     516         325 :   if (rootElemWidth < 0) {
     517           0 :     *aWidth = 0;
     518           0 :     return NS_ERROR_FAILURE;
     519             :   }
     520         325 :   *aWidth = rootElemWidth;
     521         325 :   return NS_OK;
     522             : }
     523             : 
     524             : //******************************************************************************
     525             : nsresult
     526           0 : VectorImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
     527             : {
     528           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     529             : }
     530             : 
     531             : //******************************************************************************
     532             : NS_IMETHODIMP_(void)
     533           0 : VectorImage::RequestRefresh(const TimeStamp& aTime)
     534             : {
     535           0 :   if (HadRecentRefresh(aTime)) {
     536           0 :     return;
     537             :   }
     538             : 
     539             :   PendingAnimationTracker* tracker =
     540           0 :     mSVGDocumentWrapper->GetDocument()->GetPendingAnimationTracker();
     541           0 :   if (tracker && ShouldAnimate()) {
     542           0 :     tracker->TriggerPendingAnimationsOnNextTick(aTime);
     543             :   }
     544             : 
     545           0 :   EvaluateAnimation();
     546             : 
     547           0 :   mSVGDocumentWrapper->TickRefreshDriver();
     548             : 
     549           0 :   if (mHasPendingInvalidation) {
     550           0 :     mHasPendingInvalidation = false;
     551           0 :     SendInvalidationNotifications();
     552             :   }
     553             : }
     554             : 
     555             : void
     556           0 : VectorImage::SendInvalidationNotifications()
     557             : {
     558             :   // Animated images don't send out invalidation notifications as soon as
     559             :   // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick
     560             :   // records that there are pending invalidations and then returns immediately.
     561             :   // The notifications are actually sent from RequestRefresh(). We send these
     562             :   // notifications there to ensure that there is actually a document observing
     563             :   // us. Otherwise, the notifications are just wasted effort.
     564             :   //
     565             :   // Non-animated images call this method directly from
     566             :   // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
     567             :   // called for them. Ordinarily this isn't needed, since we send out
     568             :   // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
     569             :   // SVG document may not be 100% ready to render at that time. In those cases
     570             :   // we would miss the subsequent invalidations if we didn't send out the
     571             :   // notifications directly in |InvalidateObservers...|.
     572             : 
     573           0 :   if (mProgressTracker) {
     574           0 :     SurfaceCache::RemoveImage(ImageKey(this));
     575           0 :     mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
     576           0 :                                          GetMaxSizedIntRect());
     577             :   }
     578           0 : }
     579             : 
     580             : NS_IMETHODIMP_(IntRect)
     581           0 : VectorImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
     582             : {
     583           0 :   return aRect;
     584             : }
     585             : 
     586             : //******************************************************************************
     587             : NS_IMETHODIMP
     588         325 : VectorImage::GetHeight(int32_t* aHeight)
     589             : {
     590         325 :   if (mError || !mIsFullyLoaded) {
     591             :     // XXXdholbert Technically we should leave outparam untouched when we
     592             :     // fail. But since many callers don't check for failure, we set it to 0 on
     593             :     // failure, for sane/predictable results.
     594           0 :     *aHeight = 0;
     595           0 :     return NS_ERROR_FAILURE;
     596             :   }
     597             : 
     598         325 :   SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
     599         325 :   MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
     600             :              "loading without errors");
     601         325 :   int32_t rootElemHeight = rootElem->GetIntrinsicHeight();
     602         325 :   if (rootElemHeight < 0) {
     603           0 :     *aHeight = 0;
     604           0 :     return NS_ERROR_FAILURE;
     605             :   }
     606         325 :   *aHeight = rootElemHeight;
     607         325 :   return NS_OK;
     608             : }
     609             : 
     610             : //******************************************************************************
     611             : NS_IMETHODIMP
     612           0 : VectorImage::GetIntrinsicSize(nsSize* aSize)
     613             : {
     614           0 :   if (mError || !mIsFullyLoaded) {
     615           0 :     return NS_ERROR_FAILURE;
     616             :   }
     617             : 
     618           0 :   nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
     619           0 :   if (!rootFrame) {
     620           0 :     return NS_ERROR_FAILURE;
     621             :   }
     622             : 
     623           0 :   *aSize = nsSize(-1, -1);
     624           0 :   IntrinsicSize rfSize = rootFrame->GetIntrinsicSize();
     625           0 :   if (rfSize.width.GetUnit() == eStyleUnit_Coord) {
     626           0 :     aSize->width = rfSize.width.GetCoordValue();
     627             :   }
     628           0 :   if (rfSize.height.GetUnit() == eStyleUnit_Coord) {
     629           0 :     aSize->height = rfSize.height.GetCoordValue();
     630             :   }
     631             : 
     632           0 :   return NS_OK;
     633             : }
     634             : 
     635             : //******************************************************************************
     636             : NS_IMETHODIMP
     637         165 : VectorImage::GetIntrinsicRatio(nsSize* aRatio)
     638             : {
     639         165 :   if (mError || !mIsFullyLoaded) {
     640           0 :     return NS_ERROR_FAILURE;
     641             :   }
     642             : 
     643         165 :   nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
     644         165 :   if (!rootFrame) {
     645           0 :     return NS_ERROR_FAILURE;
     646             :   }
     647             : 
     648         165 :   *aRatio = rootFrame->GetIntrinsicRatio();
     649         165 :   return NS_OK;
     650             : }
     651             : 
     652             : NS_IMETHODIMP_(Orientation)
     653           0 : VectorImage::GetOrientation()
     654             : {
     655           0 :   return Orientation();
     656             : }
     657             : 
     658             : //******************************************************************************
     659             : NS_IMETHODIMP
     660         125 : VectorImage::GetType(uint16_t* aType)
     661             : {
     662         125 :   NS_ENSURE_ARG_POINTER(aType);
     663             : 
     664         125 :   *aType = imgIContainer::TYPE_VECTOR;
     665         125 :   return NS_OK;
     666             : }
     667             : 
     668             : //******************************************************************************
     669             : NS_IMETHODIMP
     670           0 : VectorImage::GetAnimated(bool* aAnimated)
     671             : {
     672           0 :   if (mError || !mIsFullyLoaded) {
     673           0 :     return NS_ERROR_FAILURE;
     674             :   }
     675             : 
     676           0 :   *aAnimated = mSVGDocumentWrapper->IsAnimated();
     677           0 :   return NS_OK;
     678             : }
     679             : 
     680             : //******************************************************************************
     681             : int32_t
     682           0 : VectorImage::GetFirstFrameDelay()
     683             : {
     684           0 :   if (mError) {
     685           0 :     return -1;
     686             :   }
     687             : 
     688           0 :   if (!mSVGDocumentWrapper->IsAnimated()) {
     689           0 :     return -1;
     690             :   }
     691             : 
     692             :   // We don't really have a frame delay, so just pretend that we constantly
     693             :   // need updates.
     694           0 :   return 0;
     695             : }
     696             : 
     697             : NS_IMETHODIMP_(bool)
     698          88 : VectorImage::WillDrawOpaqueNow()
     699             : {
     700          88 :   return false; // In general, SVG content is not opaque.
     701             : }
     702             : 
     703             : //******************************************************************************
     704             : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
     705           0 : VectorImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags)
     706             : {
     707           0 :   if (mError) {
     708           0 :     return nullptr;
     709             :   }
     710             : 
     711             :   // Look up height & width
     712             :   // ----------------------
     713           0 :   SVGSVGElement* svgElem = mSVGDocumentWrapper->GetRootSVGElem();
     714           0 :   MOZ_ASSERT(svgElem, "Should have a root SVG elem, since we finished "
     715             :                       "loading without errors");
     716             :   nsIntSize imageIntSize(svgElem->GetIntrinsicWidth(),
     717           0 :                          svgElem->GetIntrinsicHeight());
     718             : 
     719           0 :   if (imageIntSize.IsEmpty()) {
     720             :     // We'll get here if our SVG doc has a percent-valued or negative width or
     721             :     // height.
     722           0 :     return nullptr;
     723             :   }
     724             : 
     725           0 :   return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags);
     726             : }
     727             : 
     728             : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
     729           0 : VectorImage::GetFrameAtSize(const IntSize& aSize,
     730             :                             uint32_t aWhichFrame,
     731             :                             uint32_t aFlags)
     732             : {
     733           0 :   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
     734             : 
     735           0 :   if (aSize.IsEmpty()) {
     736           0 :     return nullptr;
     737             :   }
     738             : 
     739           0 :   if (aWhichFrame > FRAME_MAX_VALUE) {
     740           0 :     return nullptr;
     741             :   }
     742             : 
     743           0 :   if (mError || !mIsFullyLoaded) {
     744           0 :     return nullptr;
     745             :   }
     746             : 
     747             :   // Make our surface the size of what will ultimately be drawn to it.
     748             :   // (either the full image size, or the restricted region)
     749             :   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
     750           0 :     CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
     751           0 :   if (!dt || !dt->IsValid()) {
     752           0 :     NS_ERROR("Could not create a DrawTarget");
     753           0 :     return nullptr;
     754             :   }
     755             : 
     756           0 :   RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
     757           0 :   MOZ_ASSERT(context); // already checked the draw target above
     758             : 
     759           0 :   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
     760           0 :                      aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags,
     761           0 :                      1.0);
     762             : 
     763           0 :   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
     764             : }
     765             : 
     766             : NS_IMETHODIMP_(bool)
     767          40 : VectorImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
     768             : {
     769          40 :   return false;
     770             : }
     771             : 
     772             : //******************************************************************************
     773             : NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
     774           0 : VectorImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
     775             : {
     776           0 :   return nullptr;
     777             : }
     778             : 
     779             : //******************************************************************************
     780             : NS_IMETHODIMP_(DrawResult)
     781          73 : VectorImage::Draw(gfxContext* aContext,
     782             :                   const nsIntSize& aSize,
     783             :                   const ImageRegion& aRegion,
     784             :                   uint32_t aWhichFrame,
     785             :                   SamplingFilter aSamplingFilter,
     786             :                   const Maybe<SVGImageContext>& aSVGContext,
     787             :                   uint32_t aFlags,
     788             :                   float aOpacity)
     789             : {
     790          73 :   if (aWhichFrame > FRAME_MAX_VALUE) {
     791           0 :     return DrawResult::BAD_ARGS;
     792             :   }
     793             : 
     794          73 :   if (!aContext) {
     795           0 :     return DrawResult::BAD_ARGS;
     796             :   }
     797             : 
     798          73 :   if (mError) {
     799           0 :     return DrawResult::BAD_IMAGE;
     800             :   }
     801             : 
     802          73 :   if (!mIsFullyLoaded) {
     803           0 :     return DrawResult::NOT_READY;
     804             :   }
     805             : 
     806          73 :   if (mAnimationConsumers == 0) {
     807           0 :     SendOnUnlockedDraw(aFlags);
     808             :   }
     809             : 
     810          73 :   MOZ_ASSERT(!(aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) ||
     811             :              (aSVGContext && aSVGContext->GetViewportSize()),
     812             :              "Viewport size is required when using "
     813             :              "FLAG_FORCE_PRESERVEASPECTRATIO_NONE");
     814             : 
     815          73 :   bool overridePAR = (aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext;
     816             : 
     817          73 :   bool haveContextPaint = aSVGContext && aSVGContext->GetContextPaint();
     818          73 :   bool blockContextPaint = false;
     819          73 :   if (haveContextPaint) {
     820         106 :     nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
     821          53 :     blockContextPaint = !SVGContextPaint::IsAllowedForImageFromURI(imageURI);
     822             :   }
     823             : 
     824         146 :   Maybe<SVGImageContext> newSVGContext;
     825          73 :   if (overridePAR || blockContextPaint) {
     826             :     // The key that we create for the image surface cache must match the way
     827             :     // that the image will be painted, so we need to initialize a new matching
     828             :     // SVGImageContext here in order to generate the correct key.
     829             : 
     830           0 :     newSVGContext = aSVGContext; // copy
     831             : 
     832           0 :     if (overridePAR) {
     833             :       // The SVGImageContext must take account of the preserveAspectRatio
     834             :       // overide:
     835           0 :       MOZ_ASSERT(!aSVGContext->GetPreserveAspectRatio(),
     836             :                  "FLAG_FORCE_PRESERVEASPECTRATIO_NONE is not expected if a "
     837             :                  "preserveAspectRatio override is supplied");
     838             :       Maybe<SVGPreserveAspectRatio> aspectRatio =
     839           0 :         Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
     840           0 :                                     SVG_MEETORSLICE_UNKNOWN));
     841           0 :       newSVGContext->SetPreserveAspectRatio(aspectRatio);
     842             :     }
     843             : 
     844           0 :     if (blockContextPaint) {
     845             :       // The SVGImageContext must not include context paint if the image is
     846             :       // not allowed to use it:
     847           0 :       newSVGContext->ClearContextPaint();
     848             :     }
     849             :   }
     850             : 
     851             :   float animTime = (aWhichFrame == FRAME_FIRST)
     852          73 :                      ? 0.0f : mSVGDocumentWrapper->GetCurrentTime();
     853             : 
     854             :   SVGDrawingParameters params(aContext, aSize, aRegion, aSamplingFilter,
     855          73 :                               newSVGContext ? newSVGContext : aSVGContext,
     856          73 :                               animTime, aFlags, aOpacity);
     857             : 
     858             :   // If we have an prerasterized version of this image that matches the
     859             :   // drawing parameters, use that.
     860         146 :   RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
     861          73 :   if (svgDrawable) {
     862          55 :     Show(svgDrawable, params);
     863          55 :     return DrawResult::SUCCESS;
     864             :   }
     865             : 
     866             :   // else, we need to paint the image:
     867             : 
     868          18 :   if (mIsDrawing) {
     869           0 :     NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
     870           0 :     return DrawResult::TEMPORARY_ERROR;
     871             :   }
     872          36 :   AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
     873          18 :   mIsDrawing = true;
     874             : 
     875             :   // Apply any 'preserveAspectRatio' override (if specified) to the root
     876             :   // element:
     877          18 :   AutoPreserveAspectRatioOverride autoPAR(newSVGContext ? newSVGContext : aSVGContext,
     878          54 :                                           mSVGDocumentWrapper->GetRootSVGElem());
     879             : 
     880             :   // Set the animation time:
     881             :   AutoSVGTimeSetRestore autoSVGTime(mSVGDocumentWrapper->GetRootSVGElem(),
     882          36 :                                     animTime);
     883             : 
     884             :   // Set context paint (if specified) on the document:
     885          36 :   Maybe<AutoSetRestoreSVGContextPaint> autoContextPaint;
     886          18 :   if (haveContextPaint && !blockContextPaint) {
     887          26 :     autoContextPaint.emplace(aSVGContext->GetContextPaint(),
     888          39 :                              mSVGDocumentWrapper->GetDocument());
     889             :   }
     890             : 
     891             :   // We didn't get a hit in the surface cache, so we'll need to rerasterize.
     892          18 :   CreateSurfaceAndShow(params, aContext->GetDrawTarget()->GetBackendType());
     893          18 :   return DrawResult::SUCCESS;
     894             : }
     895             : 
     896             : already_AddRefed<gfxDrawable>
     897          73 : VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
     898             : {
     899             :   // If we're not allowed to use a cached surface, don't attempt a lookup.
     900          73 :   if (aParams.flags & FLAG_BYPASS_SURFACE_CACHE) {
     901           0 :     return nullptr;
     902             :   }
     903             : 
     904             :   // We don't do any caching if we have animation, so don't bother with a lookup
     905             :   // in this case either.
     906          73 :   if (mHaveAnimations) {
     907           0 :     return nullptr;
     908             :   }
     909             : 
     910             :   LookupResult result =
     911             :     SurfaceCache::Lookup(ImageKey(this),
     912         146 :                          VectorSurfaceKey(aParams.size, aParams.svgContext));
     913          73 :   if (!result) {
     914          18 :     return nullptr;  // No matching surface, or the OS freed the volatile buffer.
     915             :   }
     916             : 
     917         110 :   RefPtr<SourceSurface> sourceSurface = result.Surface()->GetSourceSurface();
     918          55 :   if (!sourceSurface) {
     919             :     // Something went wrong. (Probably a GPU driver crash or device reset.)
     920             :     // Attempt to recover.
     921           0 :     RecoverFromLossOfSurfaces();
     922           0 :     return nullptr;
     923             :   }
     924             : 
     925             :   RefPtr<gfxDrawable> svgDrawable =
     926         165 :     new gfxSurfaceDrawable(sourceSurface, result.Surface()->GetSize());
     927          55 :   return svgDrawable.forget();
     928             : }
     929             : 
     930             : void
     931          18 : VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendType aBackend)
     932             : {
     933          18 :   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
     934          18 :   mSVGDocumentWrapper->FlushImageTransformInvalidation();
     935             : 
     936             :   RefPtr<gfxDrawingCallback> cb =
     937             :     new SVGDrawingCallback(mSVGDocumentWrapper,
     938             :                            aParams.viewportSize,
     939             :                            aParams.size,
     940          54 :                            aParams.flags);
     941             : 
     942             :   RefPtr<gfxDrawable> svgDrawable =
     943          54 :     new gfxCallbackDrawable(cb, aParams.size);
     944             : 
     945          36 :   bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
     946             :                      // Refuse to cache animated images:
     947             :                      // XXX(seth): We may remove this restriction in bug 922893.
     948          36 :                      mHaveAnimations ||
     949             :                      // The image is too big to fit in the cache:
     950          36 :                      !SurfaceCache::CanHold(aParams.size);
     951          18 :   if (bypassCache) {
     952           0 :     return Show(svgDrawable, aParams);
     953             :   }
     954             : 
     955             :   // We're about to rerasterize, which may mean that some of the previous
     956             :   // surfaces we've rasterized aren't useful anymore. We can allow them to
     957             :   // expire from the cache by unlocking them here, and then sending out an
     958             :   // invalidation. If this image is locked, any surfaces that are still useful
     959             :   // will become locked again when Draw touches them, and the remainder will
     960             :   // eventually expire.
     961          18 :   SurfaceCache::UnlockEntries(ImageKey(this));
     962             : 
     963             :   // Try to create an imgFrame, initializing the surface it contains by drawing
     964             :   // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
     965          36 :   NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame);
     966             :   nsresult rv =
     967          18 :     frame->InitWithDrawable(svgDrawable, aParams.size,
     968             :                             SurfaceFormat::B8G8R8A8,
     969          18 :                             SamplingFilter::POINT, aParams.flags,
     970          18 :                             aBackend);
     971             : 
     972             :   // If we couldn't create the frame, it was probably because it would end
     973             :   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
     974             :   // could be set such that the cache isn't the limiting factor.
     975          18 :   if (NS_FAILED(rv)) {
     976           0 :     return Show(svgDrawable, aParams);
     977             :   }
     978             : 
     979             :   // Take a strong reference to the frame's surface and make sure it hasn't
     980             :   // already been purged by the operating system.
     981          36 :   RefPtr<SourceSurface> surface = frame->GetSourceSurface();
     982          18 :   if (!surface) {
     983           0 :     return Show(svgDrawable, aParams);
     984             :   }
     985             : 
     986             :   // Attempt to cache the frame.
     987          36 :   SurfaceKey surfaceKey = VectorSurfaceKey(aParams.size, aParams.svgContext);
     988             :   NotNull<RefPtr<ISurfaceProvider>> provider =
     989          54 :     WrapNotNull(new SimpleSurfaceProvider(ImageKey(this), surfaceKey, frame));
     990          18 :   SurfaceCache::Insert(provider);
     991             : 
     992             :   // Draw.
     993             :   RefPtr<gfxDrawable> drawable =
     994          54 :     new gfxSurfaceDrawable(surface, aParams.size);
     995          18 :   Show(drawable, aParams);
     996             : 
     997             :   // Send out an invalidation so that surfaces that are still in use get
     998             :   // re-locked. See the discussion of the UnlockSurfaces call above.
     999          18 :   if (!(aParams.flags & FLAG_ASYNC_NOTIFY)) {
    1000           0 :     mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
    1001           0 :                                          GetMaxSizedIntRect());
    1002             :   } else {
    1003          36 :     NotNull<RefPtr<VectorImage>> image = WrapNotNull(this);
    1004          36 :     NS_DispatchToMainThread(NS_NewRunnableFunction(
    1005             :                               "ProgressTracker::SyncNotifyProgress",
    1006          72 :                               [=]() -> void {
    1007          36 :       RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
    1008          18 :       if (tracker) {
    1009          18 :         tracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
    1010          36 :                                     GetMaxSizedIntRect());
    1011             :       }
    1012          36 :     }));
    1013             :   }
    1014             : }
    1015             : 
    1016             : 
    1017             : void
    1018          73 : VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
    1019             : {
    1020          73 :   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
    1021         146 :   gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
    1022         146 :                              SizeDouble(aParams.size),
    1023             :                              aParams.region,
    1024             :                              SurfaceFormat::B8G8R8A8,
    1025          73 :                              aParams.samplingFilter,
    1026         146 :                              aParams.flags, aParams.opacity);
    1027             : 
    1028             : #ifdef DEBUG
    1029             :   // Record the image drawing for startup performance testing.
    1030          73 :   if (NS_IsMainThread()) {
    1031         146 :     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    1032          73 :     NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
    1033          73 :     if (obs) {
    1034         146 :       nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
    1035         146 :       nsAutoCString spec;
    1036          73 :       imageURI->GetSpec(spec);
    1037          73 :       obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
    1038             :     }
    1039             :   }
    1040             : #endif
    1041             : 
    1042          73 :   MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
    1043          73 :   mRenderingObserver->ResumeHonoringInvalidations();
    1044          73 : }
    1045             : 
    1046             : void
    1047           0 : VectorImage::RecoverFromLossOfSurfaces()
    1048             : {
    1049           0 :   NS_WARNING("An imgFrame became invalid. Attempting to recover...");
    1050             : 
    1051             :   // Discard all existing frames, since they're probably all now invalid.
    1052           0 :   SurfaceCache::RemoveImage(ImageKey(this));
    1053           0 : }
    1054             : 
    1055             : NS_IMETHODIMP
    1056          31 : VectorImage::StartDecoding(uint32_t aFlags)
    1057             : {
    1058             :   // Nothing to do for SVG images
    1059          31 :   return NS_OK;
    1060             : }
    1061             : 
    1062             : bool
    1063           0 : VectorImage::StartDecodingWithResult(uint32_t aFlags)
    1064             : {
    1065             :   // SVG images are ready to draw when they are loaded
    1066           0 :   return mIsFullyLoaded;
    1067             : }
    1068             : 
    1069             : NS_IMETHODIMP
    1070           0 : VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
    1071             : {
    1072             :   // Nothing to do for SVG images, though in theory we could rasterize to the
    1073             :   // provided size ahead of time if we supported off-main-thread SVG
    1074             :   // rasterization...
    1075           0 :   return NS_OK;
    1076             : }
    1077             : 
    1078             : //******************************************************************************
    1079             : 
    1080             : NS_IMETHODIMP
    1081         111 : VectorImage::LockImage()
    1082             : {
    1083         111 :   MOZ_ASSERT(NS_IsMainThread());
    1084             : 
    1085         111 :   if (mError) {
    1086           0 :     return NS_ERROR_FAILURE;
    1087             :   }
    1088             : 
    1089         111 :   mLockCount++;
    1090             : 
    1091         111 :   if (mLockCount == 1) {
    1092             :     // Lock this image's surfaces in the SurfaceCache.
    1093           0 :     SurfaceCache::LockImage(ImageKey(this));
    1094             :   }
    1095             : 
    1096         111 :   return NS_OK;
    1097             : }
    1098             : 
    1099             : //******************************************************************************
    1100             : 
    1101             : NS_IMETHODIMP
    1102          75 : VectorImage::UnlockImage()
    1103             : {
    1104          75 :   MOZ_ASSERT(NS_IsMainThread());
    1105             : 
    1106          75 :   if (mError) {
    1107           0 :     return NS_ERROR_FAILURE;
    1108             :   }
    1109             : 
    1110          75 :   if (mLockCount == 0) {
    1111           0 :     MOZ_ASSERT_UNREACHABLE("Calling UnlockImage with a zero lock count");
    1112             :     return NS_ERROR_ABORT;
    1113             :   }
    1114             : 
    1115          75 :   mLockCount--;
    1116             : 
    1117          75 :   if (mLockCount == 0) {
    1118             :     // Unlock this image's surfaces in the SurfaceCache.
    1119           0 :     SurfaceCache::UnlockImage(ImageKey(this));
    1120             :   }
    1121             : 
    1122          75 :   return NS_OK;
    1123             : }
    1124             : 
    1125             : //******************************************************************************
    1126             : 
    1127             : NS_IMETHODIMP
    1128           0 : VectorImage::RequestDiscard()
    1129             : {
    1130           0 :   MOZ_ASSERT(NS_IsMainThread());
    1131             : 
    1132           0 :   if (mDiscardable && mLockCount == 0) {
    1133           0 :     SurfaceCache::RemoveImage(ImageKey(this));
    1134           0 :     mProgressTracker->OnDiscard();
    1135             :   }
    1136             : 
    1137           0 :   return NS_OK;
    1138             : }
    1139             : 
    1140             : void
    1141           0 : VectorImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
    1142             : {
    1143           0 :   MOZ_ASSERT(mProgressTracker);
    1144             : 
    1145           0 :   NS_DispatchToMainThread(NewRunnableMethod("ProgressTracker::OnDiscard",
    1146           0 :                                             mProgressTracker, &ProgressTracker::OnDiscard));
    1147           0 : }
    1148             : 
    1149             : //******************************************************************************
    1150             : NS_IMETHODIMP
    1151           0 : VectorImage::ResetAnimation()
    1152             : {
    1153           0 :   if (mError) {
    1154           0 :     return NS_ERROR_FAILURE;
    1155             :   }
    1156             : 
    1157           0 :   if (!mIsFullyLoaded || !mHaveAnimations) {
    1158           0 :     return NS_OK; // There are no animations to be reset.
    1159             :   }
    1160             : 
    1161           0 :   mSVGDocumentWrapper->ResetAnimation();
    1162             : 
    1163           0 :   return NS_OK;
    1164             : }
    1165             : 
    1166             : NS_IMETHODIMP_(float)
    1167           0 : VectorImage::GetFrameIndex(uint32_t aWhichFrame)
    1168             : {
    1169           0 :   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
    1170             :   return aWhichFrame == FRAME_FIRST
    1171           0 :          ? 0.0f
    1172           0 :          : mSVGDocumentWrapper->GetCurrentTime();
    1173             : }
    1174             : 
    1175             : //------------------------------------------------------------------------------
    1176             : // nsIRequestObserver methods
    1177             : 
    1178             : //******************************************************************************
    1179             : NS_IMETHODIMP
    1180          21 : VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
    1181             : {
    1182          21 :   MOZ_ASSERT(!mSVGDocumentWrapper,
    1183             :              "Repeated call to OnStartRequest -- can this happen?");
    1184             : 
    1185          21 :   mSVGDocumentWrapper = new SVGDocumentWrapper();
    1186          21 :   nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt);
    1187          21 :   if (NS_FAILED(rv)) {
    1188           0 :     mSVGDocumentWrapper = nullptr;
    1189           0 :     mError = true;
    1190           0 :     return rv;
    1191             :   }
    1192             : 
    1193             :   // ProgressTracker::SyncNotifyProgress may release us, so ensure we
    1194             :   // stick around long enough to complete our work.
    1195          42 :   RefPtr<VectorImage> kungFuDeathGrip(this);
    1196             : 
    1197             :   // Block page load until the document's ready.  (We unblock it in
    1198             :   // OnSVGDocumentLoaded or OnSVGDocumentError.)
    1199          21 :   if (mProgressTracker) {
    1200          21 :     mProgressTracker->SyncNotifyProgress(FLAG_ONLOAD_BLOCKED);
    1201             :   }
    1202             : 
    1203             :   // Create a listener to wait until the SVG document is fully loaded, which
    1204             :   // will signal that this image is ready to render. Certain error conditions
    1205             :   // will prevent us from ever getting this notification, so we also create a
    1206             :   // listener that waits for parsing to complete and cancels the
    1207             :   // SVGLoadEventListener if needed. The listeners are automatically attached
    1208             :   // to the document by their constructors.
    1209          21 :   nsIDocument* document = mSVGDocumentWrapper->GetDocument();
    1210          21 :   mLoadEventListener = new SVGLoadEventListener(document, this);
    1211          21 :   mParseCompleteListener = new SVGParseCompleteListener(document, this);
    1212             : 
    1213          21 :   return NS_OK;
    1214             : }
    1215             : 
    1216             : //******************************************************************************
    1217             : NS_IMETHODIMP
    1218          21 : VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
    1219             :                            nsresult aStatus)
    1220             : {
    1221          21 :   if (mError) {
    1222           0 :     return NS_ERROR_FAILURE;
    1223             :   }
    1224             : 
    1225          21 :   return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus);
    1226             : }
    1227             : 
    1228             : void
    1229          21 : VectorImage::OnSVGDocumentParsed()
    1230             : {
    1231          21 :   MOZ_ASSERT(mParseCompleteListener, "Should have the parse complete listener");
    1232          21 :   MOZ_ASSERT(mLoadEventListener, "Should have the load event listener");
    1233             : 
    1234          21 :   if (!mSVGDocumentWrapper->GetRootSVGElem()) {
    1235             :     // This is an invalid SVG document. It may have failed to parse, or it may
    1236             :     // be missing the <svg> root element, or the <svg> root element may not
    1237             :     // declare the correct namespace. In any of these cases, we'll never be
    1238             :     // notified that the SVG finished loading, so we need to treat this as an
    1239             :     // error.
    1240           0 :     OnSVGDocumentError();
    1241             :   }
    1242          21 : }
    1243             : 
    1244             : void
    1245          21 : VectorImage::CancelAllListeners()
    1246             : {
    1247          21 :   if (mParseCompleteListener) {
    1248          21 :     mParseCompleteListener->Cancel();
    1249          21 :     mParseCompleteListener = nullptr;
    1250             :   }
    1251          21 :   if (mLoadEventListener) {
    1252          21 :     mLoadEventListener->Cancel();
    1253          21 :     mLoadEventListener = nullptr;
    1254             :   }
    1255          21 : }
    1256             : 
    1257             : void
    1258          21 : VectorImage::OnSVGDocumentLoaded()
    1259             : {
    1260          21 :   MOZ_ASSERT(mSVGDocumentWrapper->GetRootSVGElem(),
    1261             :              "Should have parsed successfully");
    1262          21 :   MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations,
    1263             :              "These flags shouldn't get set until OnSVGDocumentLoaded. "
    1264             :              "Duplicate calls to OnSVGDocumentLoaded?");
    1265             : 
    1266          21 :   CancelAllListeners();
    1267             : 
    1268             :   // XXX Flushing is wasteful if embedding frame hasn't had initial reflow.
    1269          21 :   mSVGDocumentWrapper->FlushLayout();
    1270             : 
    1271          21 :   mIsFullyLoaded = true;
    1272          21 :   mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
    1273             : 
    1274             :   // Start listening to our image for rendering updates.
    1275          42 :   mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
    1276             : 
    1277             :   // ProgressTracker::SyncNotifyProgress may release us, so ensure we
    1278             :   // stick around long enough to complete our work.
    1279          42 :   RefPtr<VectorImage> kungFuDeathGrip(this);
    1280             : 
    1281             :   // Tell *our* observers that we're done loading.
    1282          21 :   if (mProgressTracker) {
    1283             :     Progress progress = FLAG_SIZE_AVAILABLE |
    1284             :                         FLAG_HAS_TRANSPARENCY |
    1285             :                         FLAG_FRAME_COMPLETE |
    1286             :                         FLAG_DECODE_COMPLETE |
    1287          21 :                         FLAG_ONLOAD_UNBLOCKED;
    1288             : 
    1289          21 :     if (mHaveAnimations) {
    1290           0 :       progress |= FLAG_IS_ANIMATED;
    1291             :     }
    1292             : 
    1293             :     // Merge in any saved progress from OnImageDataComplete.
    1294          21 :     if (mLoadProgress) {
    1295          21 :       progress |= *mLoadProgress;
    1296          21 :       mLoadProgress = Nothing();
    1297             :     }
    1298             : 
    1299          21 :     mProgressTracker->SyncNotifyProgress(progress, GetMaxSizedIntRect());
    1300             :   }
    1301             : 
    1302          21 :   EvaluateAnimation();
    1303          21 : }
    1304             : 
    1305             : void
    1306           0 : VectorImage::OnSVGDocumentError()
    1307             : {
    1308           0 :   CancelAllListeners();
    1309             : 
    1310           0 :   mError = true;
    1311             : 
    1312           0 :   if (mProgressTracker) {
    1313             :     // Notify observers about the error and unblock page load.
    1314           0 :     Progress progress = FLAG_ONLOAD_UNBLOCKED | FLAG_HAS_ERROR;
    1315             : 
    1316             :     // Merge in any saved progress from OnImageDataComplete.
    1317           0 :     if (mLoadProgress) {
    1318           0 :       progress |= *mLoadProgress;
    1319           0 :       mLoadProgress = Nothing();
    1320             :     }
    1321             : 
    1322           0 :     mProgressTracker->SyncNotifyProgress(progress);
    1323             :   }
    1324           0 : }
    1325             : 
    1326             : //------------------------------------------------------------------------------
    1327             : // nsIStreamListener method
    1328             : 
    1329             : //******************************************************************************
    1330             : NS_IMETHODIMP
    1331          21 : VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
    1332             :                              nsIInputStream* aInStr, uint64_t aSourceOffset,
    1333             :                              uint32_t aCount)
    1334             : {
    1335          21 :   if (mError) {
    1336           0 :     return NS_ERROR_FAILURE;
    1337             :   }
    1338             : 
    1339          21 :   return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
    1340          21 :                                               aSourceOffset, aCount);
    1341             : }
    1342             : 
    1343             : // --------------------------
    1344             : // Invalidation helper method
    1345             : 
    1346             : void
    1347           0 : VectorImage::InvalidateObserversOnNextRefreshDriverTick()
    1348             : {
    1349           0 :   if (mHaveAnimations) {
    1350           0 :     mHasPendingInvalidation = true;
    1351             :   } else {
    1352           0 :     SendInvalidationNotifications();
    1353             :   }
    1354           0 : }
    1355             : 
    1356             : void
    1357          25 : VectorImage::PropagateUseCounters(nsIDocument* aParentDocument)
    1358             : {
    1359          25 :   nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
    1360          25 :   if (doc) {
    1361          25 :     doc->PropagateUseCounters(aParentDocument);
    1362             :   }
    1363          25 : }
    1364             : 
    1365             : void
    1366           0 : VectorImage::ReportUseCounters()
    1367             : {
    1368           0 :   nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
    1369           0 :   if (doc) {
    1370           0 :     static_cast<nsDocument*>(doc)->ReportUseCounters();
    1371             :   }
    1372           0 : }
    1373             : 
    1374             : nsIntSize
    1375          73 : VectorImage::OptimalImageSizeForDest(const gfxSize& aDest,
    1376             :                                      uint32_t aWhichFrame,
    1377             :                                      SamplingFilter aSamplingFilter,
    1378             :                                      uint32_t aFlags)
    1379             : {
    1380          73 :   MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
    1381             :              aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
    1382             :              "Unexpected destination size");
    1383             : 
    1384             :   // We can rescale SVGs freely, so just return the provided destination size.
    1385          73 :   return nsIntSize::Ceil(aDest.width, aDest.height);
    1386             : }
    1387             : 
    1388             : already_AddRefed<imgIContainer>
    1389           0 : VectorImage::Unwrap()
    1390             : {
    1391           0 :   nsCOMPtr<imgIContainer> self(this);
    1392           0 :   return self.forget();
    1393             : }
    1394             : 
    1395             : } // namespace image
    1396             : } // namespace mozilla

Generated by: LCOV version 1.13