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

Generated by: LCOV version 1.13-14-ga5dd952