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