image/src/VectorImage.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "VectorImage.h"
     8 #include "gfx2DGlue.h"
     9 #include "gfxContext.h"
    10 #include "gfxDrawable.h"
    11 #include "gfxPlatform.h"
    12 #include "gfxUtils.h"
    13 #include "imgDecoderObserver.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 "nsStubDocumentObserver.h"
    26 #include "nsSVGEffects.h" // for nsSVGRenderingObserver
    27 #include "Orientation.h"
    28 #include "SVGDocumentWrapper.h"
    29 #include "nsIDOMEventListener.h"
    30 #include "SurfaceCache.h"
    32 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
    33 #undef GetCurrentTime
    35 namespace mozilla {
    37 using namespace dom;
    38 using namespace gfx;
    39 using namespace layers;
    41 namespace image {
    43 // Helper-class: SVGRootRenderingObserver
    44 class SVGRootRenderingObserver MOZ_FINAL : public nsSVGRenderingObserver {
    45 public:
    46   SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper,
    47                            VectorImage*        aVectorImage)
    48     : nsSVGRenderingObserver()
    49     , mDocWrapper(aDocWrapper)
    50     , mVectorImage(aVectorImage)
    51     , mHonoringInvalidations(true)
    52   {
    53     MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
    54     MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
    56     StartListening();
    57     Element* elem = GetTarget();
    58     MOZ_ASSERT(elem, "no root SVG node for us to observe");
    60     nsSVGEffects::AddRenderingObserver(elem, this);
    61     mInObserverList = true;
    62   }
    64   virtual ~SVGRootRenderingObserver()
    65   {
    66     StopListening();
    67   }
    69   void ResumeHonoringInvalidations()
    70   {
    71     mHonoringInvalidations = true;
    72   }
    74 protected:
    75   virtual Element* GetTarget() MOZ_OVERRIDE
    76   {
    77     return mDocWrapper->GetRootSVGElem();
    78   }
    80   virtual void DoUpdate() MOZ_OVERRIDE
    81   {
    82     Element* elem = GetTarget();
    83     MOZ_ASSERT(elem, "missing root SVG node");
    85     if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) {
    86       nsIFrame* frame = elem->GetPrimaryFrame();
    87       if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
    88         // We're being destroyed. Bail out.
    89         return;
    90       }
    92       // Ignore further invalidations until we draw.
    93       mHonoringInvalidations = false;
    95       mVectorImage->InvalidateObserversOnNextRefreshDriverTick();
    96     }
    98     // Our caller might've removed us from rendering-observer list.
    99     // Add ourselves back!
   100     if (!mInObserverList) {
   101       nsSVGEffects::AddRenderingObserver(elem, this);
   102       mInObserverList = true;
   103     } 
   104   }
   106   // Private data
   107   const nsRefPtr<SVGDocumentWrapper> mDocWrapper;
   108   VectorImage* const mVectorImage;   // Raw pointer because it owns me.
   109   bool mHonoringInvalidations;
   110 };
   112 class SVGParseCompleteListener MOZ_FINAL : public nsStubDocumentObserver {
   113 public:
   114   NS_DECL_ISUPPORTS
   116   SVGParseCompleteListener(nsIDocument* aDocument,
   117                            VectorImage* aImage)
   118     : mDocument(aDocument)
   119     , mImage(aImage)
   120   {
   121     MOZ_ASSERT(mDocument, "Need an SVG document");
   122     MOZ_ASSERT(mImage, "Need an image");
   124     mDocument->AddObserver(this);
   125   }
   127   ~SVGParseCompleteListener()
   128   {
   129     if (mDocument) {
   130       // The document must have been destroyed before we got our event.
   131       // Otherwise this can't happen, since documents hold strong references to
   132       // their observers.
   133       Cancel();
   134     }
   135   }
   137   void EndLoad(nsIDocument* aDocument) MOZ_OVERRIDE
   138   {
   139     MOZ_ASSERT(aDocument == mDocument, "Got EndLoad for wrong document?");
   141     // OnSVGDocumentParsed will release our owner's reference to us, so ensure
   142     // we stick around long enough to complete our work.
   143     nsRefPtr<SVGParseCompleteListener> kungFuDeathGroup(this);
   145     mImage->OnSVGDocumentParsed();
   146   }
   148   void Cancel()
   149   {
   150     MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
   151     if (mDocument) {
   152       mDocument->RemoveObserver(this);
   153       mDocument = nullptr;
   154     }
   155   }
   157 private:
   158   nsCOMPtr<nsIDocument> mDocument;
   159   VectorImage* const mImage; // Raw pointer to owner.
   160 };
   162 NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver)
   164 class SVGLoadEventListener MOZ_FINAL : public nsIDOMEventListener {
   165 public:
   166   NS_DECL_ISUPPORTS
   168   SVGLoadEventListener(nsIDocument* aDocument,
   169                        VectorImage* aImage)
   170     : mDocument(aDocument)
   171     , mImage(aImage)
   172   {
   173     MOZ_ASSERT(mDocument, "Need an SVG document");
   174     MOZ_ASSERT(mImage, "Need an image");
   176     mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true, false);
   177     mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true, false);
   178     mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true, false);
   179   }
   181   ~SVGLoadEventListener()
   182   {
   183     if (mDocument) {
   184       // The document must have been destroyed before we got our event.
   185       // Otherwise this can't happen, since documents hold strong references to
   186       // their observers.
   187       Cancel();
   188     }
   189   }
   191   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE
   192   {
   193     MOZ_ASSERT(mDocument, "Need an SVG document. Received multiple events?");
   195     // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference
   196     // to us, so ensure we stick around long enough to complete our work.
   197     nsRefPtr<SVGLoadEventListener> kungFuDeathGroup(this);
   199     nsAutoString eventType;
   200     aEvent->GetType(eventType);
   201     MOZ_ASSERT(eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")  ||
   202                eventType.EqualsLiteral("SVGAbort")                   ||
   203                eventType.EqualsLiteral("SVGError"),
   204                "Received unexpected event");
   206     if (eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")) {
   207       mImage->OnSVGDocumentLoaded();
   208     } else {
   209       mImage->OnSVGDocumentError();
   210     }
   212     return NS_OK;
   213   }
   215   void Cancel()
   216   {
   217     MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
   218     if (mDocument) {
   219       mDocument->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true);
   220       mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
   221       mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
   222       mDocument = nullptr;
   223     }
   224   }
   226 private:
   227   nsCOMPtr<nsIDocument> mDocument;
   228   VectorImage* const mImage; // Raw pointer to owner.
   229 };
   231 NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener)
   233 // Helper-class: SVGDrawingCallback
   234 class SVGDrawingCallback : public gfxDrawingCallback {
   235 public:
   236   SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
   237                      const nsIntRect& aViewport,
   238                      const gfxSize& aScale,
   239                      uint32_t aImageFlags) :
   240     mSVGDocumentWrapper(aSVGDocumentWrapper),
   241     mViewport(aViewport),
   242     mScale(aScale),
   243     mImageFlags(aImageFlags)
   244   {}
   245   virtual bool operator()(gfxContext* aContext,
   246                             const gfxRect& aFillRect,
   247                             const GraphicsFilter& aFilter,
   248                             const gfxMatrix& aTransform);
   249 private:
   250   nsRefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
   251   const nsIntRect mViewport;
   252   const gfxSize   mScale;
   253   uint32_t        mImageFlags;
   254 };
   256 // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
   257 bool
   258 SVGDrawingCallback::operator()(gfxContext* aContext,
   259                                const gfxRect& aFillRect,
   260                                const GraphicsFilter& aFilter,
   261                                const gfxMatrix& aTransform)
   262 {
   263   MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper");
   265   // Get (& sanity-check) the helper-doc's presShell
   266   nsCOMPtr<nsIPresShell> presShell;
   267   if (NS_FAILED(mSVGDocumentWrapper->GetPresShell(getter_AddRefs(presShell)))) {
   268     NS_WARNING("Unable to draw -- presShell lookup failed");
   269     return false;
   270   }
   271   MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null");
   273   gfxContextAutoSaveRestore contextRestorer(aContext);
   275   // Clip to aFillRect so that we don't paint outside.
   276   aContext->NewPath();
   277   aContext->Rectangle(aFillRect);
   278   aContext->Clip();
   280   gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
   281   aContext->Multiply(gfxMatrix(aTransform).Invert());
   282   aContext->Scale(1.0 / mScale.width, 1.0 / mScale.height);
   284   nsPresContext* presContext = presShell->GetPresContext();
   285   MOZ_ASSERT(presContext, "pres shell w/out pres context");
   287   nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x),
   288                  presContext->DevPixelsToAppUnits(mViewport.y),
   289                  presContext->DevPixelsToAppUnits(mViewport.width),
   290                  presContext->DevPixelsToAppUnits(mViewport.height));
   292   uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
   293   if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
   294     renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
   295   }
   297   presShell->RenderDocument(svgRect, renderDocFlags,
   298                             NS_RGBA(0, 0, 0, 0), // transparent
   299                             aContext);
   301   return true;
   302 }
   304 // Implement VectorImage's nsISupports-inherited methods
   305 NS_IMPL_ISUPPORTS(VectorImage,
   306                   imgIContainer,
   307                   nsIStreamListener,
   308                   nsIRequestObserver)
   310 //------------------------------------------------------------------------------
   311 // Constructor / Destructor
   313 VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
   314                          ImageURL* aURI /* = nullptr */) :
   315   ImageResource(aURI), // invoke superclass's constructor
   316   mIsInitialized(false),
   317   mIsFullyLoaded(false),
   318   mIsDrawing(false),
   319   mHaveAnimations(false),
   320   mHasPendingInvalidation(false)
   321 {
   322   mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
   323 }
   325 VectorImage::~VectorImage()
   326 {
   327   CancelAllListeners();
   328   SurfaceCache::Discard(this);
   329 }
   331 //------------------------------------------------------------------------------
   332 // Methods inherited from Image.h
   334 nsresult
   335 VectorImage::Init(const char* aMimeType,
   336                   uint32_t aFlags)
   337 {
   338   // We don't support re-initialization
   339   if (mIsInitialized)
   340     return NS_ERROR_ILLEGAL_VALUE;
   342   MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError,
   343              "Flags unexpectedly set before initialization");
   344   MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype");
   346   mIsInitialized = true;
   347   return NS_OK;
   348 }
   350 nsIntRect
   351 VectorImage::FrameRect(uint32_t aWhichFrame)
   352 {
   353   return nsIntRect::GetMaxSizedIntRect();
   354 }
   356 size_t
   357 VectorImage::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
   358 {
   359   // We're not storing the source data -- we just feed that directly to
   360   // our helper SVG document as we receive it, for it to parse.
   361   // So 0 is an appropriate return value here.
   362   return 0;
   363 }
   365 size_t
   366 VectorImage::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
   367 {
   368   // XXXdholbert TODO: return num bytes used by helper SVG doc. (bug 590790)
   369   return 0;
   370 }
   372 size_t
   373 VectorImage::NonHeapSizeOfDecoded() const
   374 {
   375   return 0;
   376 }
   378 size_t
   379 VectorImage::OutOfProcessSizeOfDecoded() const
   380 {
   381   return 0;
   382 }
   384 nsresult
   385 VectorImage::OnImageDataComplete(nsIRequest* aRequest,
   386                                  nsISupports* aContext,
   387                                  nsresult aStatus,
   388                                  bool aLastPart)
   389 {
   390   // Call our internal OnStopRequest method, which only talks to our embedded
   391   // SVG document. This won't have any effect on our imgStatusTracker.
   392   nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus);
   394   // Give precedence to Necko failure codes.
   395   if (NS_FAILED(aStatus))
   396     finalStatus = aStatus;
   398   // Actually fire OnStopRequest.
   399   if (mStatusTracker) {
   400     // XXX(seth): Is this seriously the least insane way to do this?
   401     nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
   402     imgDecoderObserver* observer = clone->GetDecoderObserver();
   403     observer->OnStopRequest(aLastPart, finalStatus);
   404     ImageStatusDiff diff = mStatusTracker->Difference(clone);
   405     mStatusTracker->ApplyDifference(diff);
   406     mStatusTracker->SyncNotifyDifference(diff);
   407   }
   408   return finalStatus;
   409 }
   411 nsresult
   412 VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
   413                                   nsISupports* aContext,
   414                                   nsIInputStream* aInStr,
   415                                   uint64_t aSourceOffset,
   416                                   uint32_t aCount)
   417 {
   418   return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount);
   419 }
   421 nsresult
   422 VectorImage::OnNewSourceData()
   423 {
   424   return NS_OK;
   425 }
   427 nsresult
   428 VectorImage::StartAnimation()
   429 {
   430   if (mError)
   431     return NS_ERROR_FAILURE;
   433   MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
   435   mSVGDocumentWrapper->StartAnimation();
   436   return NS_OK;
   437 }
   439 nsresult
   440 VectorImage::StopAnimation()
   441 {
   442   nsresult rv = NS_OK;
   443   if (mError) {
   444     rv = NS_ERROR_FAILURE;
   445   } else {
   446     MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations,
   447                "Should not have been animating!");
   449     mSVGDocumentWrapper->StopAnimation();
   450   }
   452   mAnimating = false;
   453   return rv;
   454 }
   456 bool
   457 VectorImage::ShouldAnimate()
   458 {
   459   return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
   460 }
   462 NS_IMETHODIMP_(void)
   463 VectorImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
   464 {
   465   // We don't care about animation start time.
   466 }
   468 //------------------------------------------------------------------------------
   469 // imgIContainer methods
   471 //******************************************************************************
   472 /* readonly attribute int32_t width; */
   473 NS_IMETHODIMP
   474 VectorImage::GetWidth(int32_t* aWidth)
   475 {
   476   if (mError || !mIsFullyLoaded) {
   477     *aWidth = 0;
   478     return NS_ERROR_FAILURE;
   479   }
   481   if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
   482                                              *aWidth)) {
   483     *aWidth = 0;
   484     return NS_ERROR_FAILURE;
   485   }
   487   return NS_OK;
   488 }
   490 //******************************************************************************
   491 /* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
   492 NS_IMETHODIMP_(void)
   493 VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
   494 {
   495   // TODO: Implement for b666446.
   496   EvaluateAnimation();
   498   if (mHasPendingInvalidation) {
   499     SendInvalidationNotifications();
   500     mHasPendingInvalidation = false;
   501   }
   502 }
   504 void
   505 VectorImage::SendInvalidationNotifications()
   506 {
   507   // Animated images don't send out invalidation notifications as soon as
   508   // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick
   509   // records that there are pending invalidations and then returns immediately.
   510   // The notifications are actually sent from RequestRefresh(). We send these
   511   // notifications there to ensure that there is actually a document observing
   512   // us. Otherwise, the notifications are just wasted effort.
   513   //
   514   // Non-animated images call this method directly from
   515   // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
   516   // called for them. Ordinarily this isn't needed, since we send out
   517   // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
   518   // SVG document may not be 100% ready to render at that time. In those cases
   519   // we would miss the subsequent invalidations if we didn't send out the
   520   // notifications directly in |InvalidateObservers...|.
   522   if (mStatusTracker) {
   523     SurfaceCache::Discard(this);
   524     mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
   525     mStatusTracker->OnStopFrame();
   526   }
   527 }
   529 //******************************************************************************
   530 /* readonly attribute int32_t height; */
   531 NS_IMETHODIMP
   532 VectorImage::GetHeight(int32_t* aHeight)
   533 {
   534   if (mError || !mIsFullyLoaded) {
   535     *aHeight = 0;
   536     return NS_ERROR_FAILURE;
   537   }
   539   if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
   540                                              *aHeight)) {
   541     *aHeight = 0;
   542     return NS_ERROR_FAILURE;
   543   }
   545   return NS_OK;
   546 }
   548 //******************************************************************************
   549 /* [noscript] readonly attribute nsSize intrinsicSize; */
   550 NS_IMETHODIMP
   551 VectorImage::GetIntrinsicSize(nsSize* aSize)
   552 {
   553   if (mError || !mIsFullyLoaded)
   554     return NS_ERROR_FAILURE;
   556   nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
   557   if (!rootFrame)
   558     return NS_ERROR_FAILURE;
   560   *aSize = nsSize(-1, -1);
   561   IntrinsicSize rfSize = rootFrame->GetIntrinsicSize();
   562   if (rfSize.width.GetUnit() == eStyleUnit_Coord)
   563     aSize->width = rfSize.width.GetCoordValue();
   564   if (rfSize.height.GetUnit() == eStyleUnit_Coord)
   565     aSize->height = rfSize.height.GetCoordValue();
   567   return NS_OK;
   568 }
   570 //******************************************************************************
   571 /* [noscript] readonly attribute nsSize intrinsicRatio; */
   572 NS_IMETHODIMP
   573 VectorImage::GetIntrinsicRatio(nsSize* aRatio)
   574 {
   575   if (mError || !mIsFullyLoaded)
   576     return NS_ERROR_FAILURE;
   578   nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
   579   if (!rootFrame)
   580     return NS_ERROR_FAILURE;
   582   *aRatio = rootFrame->GetIntrinsicRatio();
   583   return NS_OK;
   584 }
   586 NS_IMETHODIMP_(Orientation)
   587 VectorImage::GetOrientation()
   588 {
   589   return Orientation();
   590 }
   592 //******************************************************************************
   593 /* readonly attribute unsigned short type; */
   594 NS_IMETHODIMP
   595 VectorImage::GetType(uint16_t* aType)
   596 {
   597   NS_ENSURE_ARG_POINTER(aType);
   599   *aType = GetType();
   600   return NS_OK;
   601 }
   603 //******************************************************************************
   604 /* [noscript, notxpcom] uint16_t GetType(); */
   605 NS_IMETHODIMP_(uint16_t)
   606 VectorImage::GetType()
   607 {
   608   return imgIContainer::TYPE_VECTOR;
   609 }
   611 //******************************************************************************
   612 /* readonly attribute boolean animated; */
   613 NS_IMETHODIMP
   614 VectorImage::GetAnimated(bool* aAnimated)
   615 {
   616   if (mError || !mIsFullyLoaded)
   617     return NS_ERROR_FAILURE;
   619   *aAnimated = mSVGDocumentWrapper->IsAnimated();
   620   return NS_OK;
   621 }
   623 //******************************************************************************
   624 /* [notxpcom] int32_t getFirstFrameDelay (); */
   625 int32_t
   626 VectorImage::GetFirstFrameDelay()
   627 {
   628   if (mError)
   629     return -1;
   631   if (!mSVGDocumentWrapper->IsAnimated())
   632     return -1;
   634   // We don't really have a frame delay, so just pretend that we constantly
   635   // need updates.
   636   return 0;
   637 }
   640 //******************************************************************************
   641 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
   642 NS_IMETHODIMP_(bool)
   643 VectorImage::FrameIsOpaque(uint32_t aWhichFrame)
   644 {
   645   if (aWhichFrame > FRAME_MAX_VALUE)
   646     NS_WARNING("aWhichFrame outside valid range!");
   648   return false; // In general, SVG content is not opaque.
   649 }
   651 //******************************************************************************
   652 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
   653  *                                   in uint32_t aFlags; */
   654 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
   655 VectorImage::GetFrame(uint32_t aWhichFrame,
   656                       uint32_t aFlags)
   657 {
   658   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
   660   if (aWhichFrame > FRAME_MAX_VALUE)
   661     return nullptr;
   663   if (mError)
   664     return nullptr;
   666   // Look up height & width
   667   // ----------------------
   668   nsIntSize imageIntSize;
   669   if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
   670                                              imageIntSize.width) ||
   671       !mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
   672                                              imageIntSize.height)) {
   673     // We'll get here if our SVG doc has a percent-valued width or height.
   674     return nullptr;
   675   }
   677   // Make our surface the size of what will ultimately be drawn to it.
   678   // (either the full image size, or the restricted region)
   679   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
   680     CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width,
   681                                              imageIntSize.height),
   682                                      SurfaceFormat::B8G8R8A8);
   683   nsRefPtr<gfxContext> context = new gfxContext(dt);
   685   nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
   686                      gfxRect(gfxPoint(0,0), gfxIntSize(imageIntSize.width,
   687                                                        imageIntSize.height)),
   688                      nsIntRect(nsIntPoint(0,0), imageIntSize),
   689                      imageIntSize, nullptr, aWhichFrame, aFlags);
   691   NS_ENSURE_SUCCESS(rv, nullptr);
   692   return dt->Snapshot();
   693 }
   695 //******************************************************************************
   696 /* [noscript] ImageContainer getImageContainer(); */
   697 NS_IMETHODIMP
   698 VectorImage::GetImageContainer(LayerManager* aManager,
   699                                mozilla::layers::ImageContainer** _retval)
   700 {
   701   *_retval = nullptr;
   702   return NS_OK;
   703 }
   705 struct SVGDrawingParameters
   706 {
   707   SVGDrawingParameters(gfxContext* aContext,
   708                        GraphicsFilter aFilter,
   709                        const gfxMatrix& aUserSpaceToImageSpace,
   710                        const gfxRect& aFill,
   711                        const nsIntRect& aSubimage,
   712                        const nsIntSize& aViewportSize,
   713                        const SVGImageContext* aSVGContext,
   714                        float aAnimationTime,
   715                        uint32_t aFlags)
   716     : context(aContext)
   717     , filter(aFilter)
   718     , fill(aFill)
   719     , viewportSize(aViewportSize)
   720     , animationTime(aAnimationTime)
   721     , svgContext(aSVGContext)
   722     , flags(aFlags)
   723   {
   724     // gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface
   725     // if we hit the tiling path. Unfortunately, the temporary surface isn't
   726     // created at the size at which we'll ultimately draw, causing fuzzy output.
   727     // To fix this we pre-apply the transform's scaling to the drawing parameters
   728     // and remove the scaling from the transform, so the fact that temporary
   729     // surfaces won't take the scaling into account doesn't matter. (Bug 600207.)
   730     scale = aUserSpaceToImageSpace.ScaleFactors(true);
   731     gfxPoint translation(aUserSpaceToImageSpace.GetTranslation());
   733     // Remove the scaling from the transform.
   734     gfxMatrix unscale;
   735     unscale.Translate(gfxPoint(translation.x / scale.width,
   736                                translation.y / scale.height));
   737     unscale.Scale(1.0 / scale.width, 1.0 / scale.height);
   738     unscale.Translate(-translation);
   739     userSpaceToImageSpace = aUserSpaceToImageSpace * unscale;
   741     // Rescale drawing parameters.
   742     IntSize drawableSize(aViewportSize.width / scale.width,
   743                          aViewportSize.height / scale.height);
   744     sourceRect = userSpaceToImageSpace.Transform(aFill);
   745     imageRect = IntRect(IntPoint(0, 0), drawableSize);
   746     subimage = gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
   747     subimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height);
   748   }
   750   gfxContext* context;
   751   GraphicsFilter filter;
   752   gfxMatrix userSpaceToImageSpace;
   753   gfxRect fill;
   754   gfxRect subimage;
   755   gfxRect sourceRect;
   756   IntRect imageRect;
   757   nsIntSize viewportSize;
   758   gfxSize scale;
   759   float animationTime;
   760   const SVGImageContext* svgContext;
   761   uint32_t flags;
   762 };
   764 //******************************************************************************
   765 /* [noscript] void draw(in gfxContext aContext,
   766  *                      in gfxGraphicsFilter aFilter,
   767  *                      [const] in gfxMatrix aUserSpaceToImageSpace,
   768  *                      [const] in gfxRect aFill,
   769  *                      [const] in nsIntRect aSubimage,
   770  *                      [const] in nsIntSize aViewportSize,
   771  *                      [const] in SVGImageContext aSVGContext,
   772  *                      in uint32_t aWhichFrame,
   773  *                      in uint32_t aFlags); */
   774 NS_IMETHODIMP
   775 VectorImage::Draw(gfxContext* aContext,
   776                   GraphicsFilter aFilter,
   777                   const gfxMatrix& aUserSpaceToImageSpace,
   778                   const gfxRect& aFill,
   779                   const nsIntRect& aSubimage,
   780                   const nsIntSize& aViewportSize,
   781                   const SVGImageContext* aSVGContext,
   782                   uint32_t aWhichFrame,
   783                   uint32_t aFlags)
   784 {
   785   if (aWhichFrame > FRAME_MAX_VALUE)
   786     return NS_ERROR_INVALID_ARG;
   788   NS_ENSURE_ARG_POINTER(aContext);
   789   if (mError || !mIsFullyLoaded)
   790     return NS_ERROR_FAILURE;
   792   if (mIsDrawing) {
   793     NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
   794     return NS_ERROR_FAILURE;
   795   }
   797   if (mAnimationConsumers == 0 && mStatusTracker) {
   798     mStatusTracker->OnUnlockedDraw();
   799   }
   801   AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
   802   mIsDrawing = true;
   804   float animTime = (aWhichFrame == FRAME_FIRST) ? 0.0f
   805                                                 : mSVGDocumentWrapper->GetCurrentTime();
   806   AutoSVGRenderingState autoSVGState(aSVGContext, animTime,
   807                                      mSVGDocumentWrapper->GetRootSVGElem());
   809   // Pack up the drawing parameters.
   810   SVGDrawingParameters params(aContext, aFilter, aUserSpaceToImageSpace, aFill,
   811                               aSubimage, aViewportSize, aSVGContext, animTime, aFlags);
   813   // Check the cache.
   814   nsRefPtr<gfxDrawable> drawable =
   815     SurfaceCache::Lookup(ImageKey(this),
   816                          SurfaceKey(params.imageRect.Size(), params.scale,
   817                                     aSVGContext, animTime, aFlags));
   819   // Draw.
   820   if (drawable) {
   821     Show(drawable, params);
   822   } else {
   823     CreateDrawableAndShow(params);
   824   }
   826   return NS_OK;
   827 }
   829 void
   830 VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
   831 {
   832   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   833   mSVGDocumentWrapper->FlushImageTransformInvalidation();
   835   nsRefPtr<gfxDrawingCallback> cb =
   836     new SVGDrawingCallback(mSVGDocumentWrapper,
   837                            nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
   838                            aParams.scale,
   839                            aParams.flags);
   841   nsRefPtr<gfxDrawable> svgDrawable =
   842     new gfxCallbackDrawable(cb, ThebesIntSize(aParams.imageRect.Size()));
   844   // Refuse to cache animated images.
   845   // XXX(seth): We may remove this restriction in bug 922893.
   846   if (mHaveAnimations)
   847     return Show(svgDrawable, aParams);
   849   // If the image is too big to fit in the cache, don't go any further.
   850   if (!SurfaceCache::CanHold(aParams.imageRect.Size()))
   851     return Show(svgDrawable, aParams);
   853   // Try to create an offscreen surface.
   854   mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
   855    gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8);
   857   // If we couldn't create the draw target, it was probably because it would end
   858   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   859   // could be set such that the cache isn't the limiting factor.
   860   if (!target)
   861     return Show(svgDrawable, aParams);
   863   nsRefPtr<gfxContext> ctx = new gfxContext(target);
   865   // Actually draw. (We use FILTER_NEAREST since we never scale here.)
   866   gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
   867                              ThebesIntRect(aParams.imageRect),
   868                              ThebesIntRect(aParams.imageRect),
   869                              ThebesIntRect(aParams.imageRect),
   870                              ThebesIntRect(aParams.imageRect),
   871                              gfxImageFormat::ARGB32,
   872                              GraphicsFilter::FILTER_NEAREST, aParams.flags);
   874   // Attempt to cache the resulting surface.
   875   SurfaceCache::Insert(target,
   876                        ImageKey(this),
   877                        SurfaceKey(aParams.imageRect.Size(), aParams.scale,
   878                                   aParams.svgContext, aParams.animationTime,
   879                                   aParams.flags));
   881   // Draw. Note that if SurfaceCache::Insert failed for whatever reason,
   882   // then |target| is all that is keeping the pixel data alive, so we have
   883   // to draw before returning from this function.
   884   nsRefPtr<gfxDrawable> drawable =
   885     new gfxSurfaceDrawable(target, ThebesIntSize(aParams.imageRect.Size()));
   886   Show(drawable, aParams);
   887 }
   890 void
   891 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
   892 {
   893   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
   894   gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
   895                              aParams.userSpaceToImageSpace,
   896                              aParams.subimage, aParams.sourceRect,
   897                              ThebesIntRect(aParams.imageRect), aParams.fill,
   898                              gfxImageFormat::ARGB32,
   899                              aParams.filter, aParams.flags);
   901   MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
   902   mRenderingObserver->ResumeHonoringInvalidations();
   903 }
   905 //******************************************************************************
   906 /* void requestDecode() */
   907 NS_IMETHODIMP
   908 VectorImage::RequestDecode()
   909 {
   910   // Nothing to do for SVG images
   911   return NS_OK;
   912 }
   914 NS_IMETHODIMP
   915 VectorImage::StartDecoding()
   916 {
   917   // Nothing to do for SVG images
   918   return NS_OK;
   919 }
   921 bool
   922 VectorImage::IsDecoded()
   923 {
   924   return mIsFullyLoaded || mError;
   925 }
   927 //******************************************************************************
   928 /* void lockImage() */
   929 NS_IMETHODIMP
   930 VectorImage::LockImage()
   931 {
   932   // This method is for image-discarding, which only applies to RasterImages.
   933   return NS_OK;
   934 }
   936 //******************************************************************************
   937 /* void unlockImage() */
   938 NS_IMETHODIMP
   939 VectorImage::UnlockImage()
   940 {
   941   // This method is for image-discarding, which only applies to RasterImages.
   942   return NS_OK;
   943 }
   945 //******************************************************************************
   946 /* void requestDiscard() */
   947 NS_IMETHODIMP
   948 VectorImage::RequestDiscard()
   949 {
   950   SurfaceCache::Discard(this);
   951   return NS_OK;
   952 }
   954 //******************************************************************************
   955 /* void resetAnimation (); */
   956 NS_IMETHODIMP
   957 VectorImage::ResetAnimation()
   958 {
   959   if (mError)
   960     return NS_ERROR_FAILURE;
   962   if (!mIsFullyLoaded || !mHaveAnimations) {
   963     return NS_OK; // There are no animations to be reset.
   964   }
   966   mSVGDocumentWrapper->ResetAnimation();
   968   return NS_OK;
   969 }
   971 NS_IMETHODIMP_(float)
   972 VectorImage::GetFrameIndex(uint32_t aWhichFrame)
   973 {
   974   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
   975   return aWhichFrame == FRAME_FIRST
   976          ? 0.0f
   977          : mSVGDocumentWrapper->GetCurrentTime();
   978 }
   980 //------------------------------------------------------------------------------
   981 // nsIRequestObserver methods
   983 //******************************************************************************
   984 /* void onStartRequest(in nsIRequest request, in nsISupports ctxt); */
   985 NS_IMETHODIMP
   986 VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
   987 {
   988   MOZ_ASSERT(!mSVGDocumentWrapper,
   989              "Repeated call to OnStartRequest -- can this happen?");
   991   mSVGDocumentWrapper = new SVGDocumentWrapper();
   992   nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt);
   993   if (NS_FAILED(rv)) {
   994     mSVGDocumentWrapper = nullptr;
   995     mError = true;
   996     return rv;
   997   }
   999   // Sending StartDecode will block page load until the document's ready.  (We
  1000   // unblock it by sending StopDecode in OnSVGDocumentLoaded or
  1001   // OnSVGDocumentError.)
  1002   if (mStatusTracker) {
  1003     nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
  1004     imgDecoderObserver* observer = clone->GetDecoderObserver();
  1005     observer->OnStartDecode();
  1006     ImageStatusDiff diff = mStatusTracker->Difference(clone);
  1007     mStatusTracker->ApplyDifference(diff);
  1008     mStatusTracker->SyncNotifyDifference(diff);
  1011   // Create a listener to wait until the SVG document is fully loaded, which
  1012   // will signal that this image is ready to render. Certain error conditions
  1013   // will prevent us from ever getting this notification, so we also create a
  1014   // listener that waits for parsing to complete and cancels the
  1015   // SVGLoadEventListener if needed. The listeners are automatically attached
  1016   // to the document by their constructors.
  1017   nsIDocument* document = mSVGDocumentWrapper->GetDocument();
  1018   mLoadEventListener = new SVGLoadEventListener(document, this);
  1019   mParseCompleteListener = new SVGParseCompleteListener(document, this);
  1021   return NS_OK;
  1024 //******************************************************************************
  1025 /* void onStopRequest(in nsIRequest request, in nsISupports ctxt,
  1026                       in nsresult status); */
  1027 NS_IMETHODIMP
  1028 VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
  1029                            nsresult aStatus)
  1031   if (mError)
  1032     return NS_ERROR_FAILURE;
  1034   return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus);
  1037 void
  1038 VectorImage::OnSVGDocumentParsed()
  1040   MOZ_ASSERT(mParseCompleteListener, "Should have the parse complete listener");
  1041   MOZ_ASSERT(mLoadEventListener, "Should have the load event listener");
  1043   if (!mSVGDocumentWrapper->GetRootSVGElem()) {
  1044     // This is an invalid SVG document. It may have failed to parse, or it may
  1045     // be missing the <svg> root element, or the <svg> root element may not
  1046     // declare the correct namespace. In any of these cases, we'll never be
  1047     // notified that the SVG finished loading, so we need to treat this as an error.
  1048     OnSVGDocumentError();
  1052 void
  1053 VectorImage::CancelAllListeners()
  1055   if (mParseCompleteListener) {
  1056     mParseCompleteListener->Cancel();
  1057     mParseCompleteListener = nullptr;
  1059   if (mLoadEventListener) {
  1060     mLoadEventListener->Cancel();
  1061     mLoadEventListener = nullptr;
  1065 void
  1066 VectorImage::OnSVGDocumentLoaded()
  1068   MOZ_ASSERT(mSVGDocumentWrapper->GetRootSVGElem(),
  1069              "Should have parsed successfully");
  1070   MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations,
  1071              "These flags shouldn't get set until OnSVGDocumentLoaded. "
  1072              "Duplicate calls to OnSVGDocumentLoaded?");
  1074   CancelAllListeners();
  1076   // XXX Flushing is wasteful if embedding frame hasn't had initial reflow.
  1077   mSVGDocumentWrapper->FlushLayout();
  1079   mIsFullyLoaded = true;
  1080   mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
  1082   // Start listening to our image for rendering updates.
  1083   mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
  1085   // Tell *our* observers that we're done loading.
  1086   if (mStatusTracker) {
  1087     nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
  1088     imgDecoderObserver* observer = clone->GetDecoderObserver();
  1090     observer->OnStartContainer(); // Signal that width/height are available.
  1091     observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
  1092     observer->OnStopFrame();
  1093     observer->OnStopDecode(NS_OK); // Unblock page load.
  1095     ImageStatusDiff diff = mStatusTracker->Difference(clone);
  1096     mStatusTracker->ApplyDifference(diff);
  1097     mStatusTracker->SyncNotifyDifference(diff);
  1100   EvaluateAnimation();
  1103 void
  1104 VectorImage::OnSVGDocumentError()
  1106   CancelAllListeners();
  1108   // XXXdholbert Need to do something more for the parsing failed case -- right
  1109   // now, this just makes us draw the "object" icon, rather than the (jagged)
  1110   // "broken image" icon.  See bug 594505.
  1111   mError = true;
  1113   if (mStatusTracker) {
  1114     nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
  1115     imgDecoderObserver* observer = clone->GetDecoderObserver();
  1117     // Unblock page load.
  1118     observer->OnStopDecode(NS_ERROR_FAILURE);
  1119     ImageStatusDiff diff = mStatusTracker->Difference(clone);
  1120     mStatusTracker->ApplyDifference(diff);
  1121     mStatusTracker->SyncNotifyDifference(diff);
  1125 //------------------------------------------------------------------------------
  1126 // nsIStreamListener method
  1128 //******************************************************************************
  1129 /* void onDataAvailable(in nsIRequest request, in nsISupports ctxt,
  1130                         in nsIInputStream inStr, in unsigned long sourceOffset,
  1131                         in unsigned long count); */
  1132 NS_IMETHODIMP
  1133 VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
  1134                              nsIInputStream* aInStr, uint64_t aSourceOffset,
  1135                              uint32_t aCount)
  1137   if (mError)
  1138     return NS_ERROR_FAILURE;
  1140   return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
  1141                                               aSourceOffset, aCount);
  1144 // --------------------------
  1145 // Invalidation helper method
  1147 void
  1148 VectorImage::InvalidateObserversOnNextRefreshDriverTick()
  1150   if (mHaveAnimations) {
  1151     mHasPendingInvalidation = true;
  1152   } else {
  1153     SendInvalidationNotifications();
  1157 } // namespace image
  1158 } // namespace mozilla

mercurial