tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

VectorImage.cpp (57368B)


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