1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "VectorImage.h"
- #include "gfx2DGlue.h"
- #include "gfxContext.h"
- #include "gfxDrawable.h"
- #include "gfxPlatform.h"
- #include "gfxPrefs.h" // for surface cache size
- #include "gfxUtils.h"
- #include "imgFrame.h"
- #include "mozilla/AutoRestore.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/dom/SVGSVGElement.h"
- #include "mozilla/gfx/2D.h"
- #include "mozilla/RefPtr.h"
- #include "nsIDOMEvent.h"
- #include "nsIPresShell.h"
- #include "nsIStreamListener.h"
- #include "nsMimeTypes.h"
- #include "nsPresContext.h"
- #include "nsRect.h"
- #include "nsString.h"
- #include "nsStubDocumentObserver.h"
- #include "nsSVGEffects.h" // for nsSVGRenderingObserver
- #include "nsWindowMemoryReporter.h"
- #include "ImageRegion.h"
- #include "ISurfaceProvider.h"
- #include "LookupResult.h"
- #include "Orientation.h"
- #include "SVGDocumentWrapper.h"
- #include "nsIDOMEventListener.h"
- #include "SurfaceCache.h"
- #include "nsDocument.h"
- // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
- #undef GetCurrentTime
- namespace mozilla {
- using namespace dom;
- using namespace gfx;
- using namespace layers;
- namespace image {
- // Helper-class: SVGRootRenderingObserver
- class SVGRootRenderingObserver final : public nsSVGRenderingObserver {
- public:
- NS_DECL_ISUPPORTS
- SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper,
- VectorImage* aVectorImage)
- : nsSVGRenderingObserver()
- , mDocWrapper(aDocWrapper)
- , mVectorImage(aVectorImage)
- , mHonoringInvalidations(true)
- {
- MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
- MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
- StartListening();
- Element* elem = GetTarget();
- MOZ_ASSERT(elem, "no root SVG node for us to observe");
- nsSVGEffects::AddRenderingObserver(elem, this);
- mInObserverList = true;
- }
- void ResumeHonoringInvalidations()
- {
- mHonoringInvalidations = true;
- }
- protected:
- virtual ~SVGRootRenderingObserver()
- {
- StopListening();
- }
- virtual Element* GetTarget() override
- {
- return mDocWrapper->GetRootSVGElem();
- }
- virtual void DoUpdate() override
- {
- Element* elem = GetTarget();
- MOZ_ASSERT(elem, "missing root SVG node");
- if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) {
- nsIFrame* frame = elem->GetPrimaryFrame();
- if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
- // We're being destroyed. Bail out.
- return;
- }
- // Ignore further invalidations until we draw.
- mHonoringInvalidations = false;
- mVectorImage->InvalidateObserversOnNextRefreshDriverTick();
- }
- // Our caller might've removed us from rendering-observer list.
- // Add ourselves back!
- if (!mInObserverList) {
- nsSVGEffects::AddRenderingObserver(elem, this);
- mInObserverList = true;
- }
- }
- // Private data
- const RefPtr<SVGDocumentWrapper> mDocWrapper;
- VectorImage* const mVectorImage; // Raw pointer because it owns me.
- bool mHonoringInvalidations;
- };
- NS_IMPL_ISUPPORTS(SVGRootRenderingObserver, nsIMutationObserver)
- class SVGParseCompleteListener final : public nsStubDocumentObserver {
- public:
- NS_DECL_ISUPPORTS
- SVGParseCompleteListener(nsIDocument* aDocument,
- VectorImage* aImage)
- : mDocument(aDocument)
- , mImage(aImage)
- {
- MOZ_ASSERT(mDocument, "Need an SVG document");
- MOZ_ASSERT(mImage, "Need an image");
- mDocument->AddObserver(this);
- }
- private:
- ~SVGParseCompleteListener()
- {
- if (mDocument) {
- // The document must have been destroyed before we got our event.
- // Otherwise this can't happen, since documents hold strong references to
- // their observers.
- Cancel();
- }
- }
- public:
- void EndLoad(nsIDocument* aDocument) override
- {
- MOZ_ASSERT(aDocument == mDocument, "Got EndLoad for wrong document?");
- // OnSVGDocumentParsed will release our owner's reference to us, so ensure
- // we stick around long enough to complete our work.
- RefPtr<SVGParseCompleteListener> kungFuDeathGrip(this);
- mImage->OnSVGDocumentParsed();
- }
- void Cancel()
- {
- MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
- if (mDocument) {
- mDocument->RemoveObserver(this);
- mDocument = nullptr;
- }
- }
- private:
- nsCOMPtr<nsIDocument> mDocument;
- VectorImage* const mImage; // Raw pointer to owner.
- };
- NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver)
- class SVGLoadEventListener final : public nsIDOMEventListener {
- public:
- NS_DECL_ISUPPORTS
- SVGLoadEventListener(nsIDocument* aDocument,
- VectorImage* aImage)
- : mDocument(aDocument)
- , mImage(aImage)
- {
- MOZ_ASSERT(mDocument, "Need an SVG document");
- MOZ_ASSERT(mImage, "Need an image");
- mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
- this, true, false);
- mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true,
- false);
- mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true,
- false);
- }
- private:
- ~SVGLoadEventListener()
- {
- if (mDocument) {
- // The document must have been destroyed before we got our event.
- // Otherwise this can't happen, since documents hold strong references to
- // their observers.
- Cancel();
- }
- }
- public:
- NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override
- {
- MOZ_ASSERT(mDocument, "Need an SVG document. Received multiple events?");
- // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference
- // to us, so ensure we stick around long enough to complete our work.
- RefPtr<SVGLoadEventListener> kungFuDeathGrip(this);
- nsAutoString eventType;
- aEvent->GetType(eventType);
- MOZ_ASSERT(eventType.EqualsLiteral("MozSVGAsImageDocumentLoad") ||
- eventType.EqualsLiteral("SVGAbort") ||
- eventType.EqualsLiteral("SVGError"),
- "Received unexpected event");
- if (eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")) {
- mImage->OnSVGDocumentLoaded();
- } else {
- mImage->OnSVGDocumentError();
- }
- return NS_OK;
- }
- void Cancel()
- {
- MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
- if (mDocument) {
- mDocument
- ->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
- this, true);
- mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
- mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
- mDocument = nullptr;
- }
- }
- private:
- nsCOMPtr<nsIDocument> mDocument;
- VectorImage* const mImage; // Raw pointer to owner.
- };
- NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener)
- // Helper-class: SVGDrawingCallback
- class SVGDrawingCallback : public gfxDrawingCallback {
- public:
- SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
- const IntRect& aViewport,
- const IntSize& aSize,
- uint32_t aImageFlags)
- : mSVGDocumentWrapper(aSVGDocumentWrapper)
- , mViewport(aViewport)
- , mSize(aSize)
- , mImageFlags(aImageFlags)
- { }
- virtual bool operator()(gfxContext* aContext,
- const gfxRect& aFillRect,
- const SamplingFilter aSamplingFilter,
- const gfxMatrix& aTransform);
- private:
- RefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
- const IntRect mViewport;
- const IntSize mSize;
- uint32_t mImageFlags;
- };
- // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
- bool
- SVGDrawingCallback::operator()(gfxContext* aContext,
- const gfxRect& aFillRect,
- const SamplingFilter aSamplingFilter,
- const gfxMatrix& aTransform)
- {
- MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper");
- // Get (& sanity-check) the helper-doc's presShell
- nsCOMPtr<nsIPresShell> presShell;
- if (NS_FAILED(mSVGDocumentWrapper->GetPresShell(getter_AddRefs(presShell)))) {
- NS_WARNING("Unable to draw -- presShell lookup failed");
- return false;
- }
- MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null");
- gfxContextAutoSaveRestore contextRestorer(aContext);
- // Clip to aFillRect so that we don't paint outside.
- aContext->NewPath();
- aContext->Rectangle(aFillRect);
- aContext->Clip();
- gfxMatrix matrix = aTransform;
- if (!matrix.Invert()) {
- return false;
- }
- aContext->SetMatrix(
- aContext->CurrentMatrix().PreMultiply(matrix).
- Scale(double(mSize.width) / mViewport.width,
- double(mSize.height) / mViewport.height));
- nsPresContext* presContext = presShell->GetPresContext();
- MOZ_ASSERT(presContext, "pres shell w/out pres context");
- nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x),
- presContext->DevPixelsToAppUnits(mViewport.y),
- presContext->DevPixelsToAppUnits(mViewport.width),
- presContext->DevPixelsToAppUnits(mViewport.height));
- uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
- if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
- renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
- }
- presShell->RenderDocument(svgRect, renderDocFlags,
- NS_RGBA(0, 0, 0, 0), // transparent
- aContext);
- return true;
- }
- // Implement VectorImage's nsISupports-inherited methods
- NS_IMPL_ISUPPORTS(VectorImage,
- imgIContainer,
- nsIStreamListener,
- nsIRequestObserver)
- //------------------------------------------------------------------------------
- // Constructor / Destructor
- VectorImage::VectorImage(ImageURL* aURI /* = nullptr */) :
- ImageResource(aURI), // invoke superclass's constructor
- mLockCount(0),
- mIsInitialized(false),
- mIsFullyLoaded(false),
- mIsDrawing(false),
- mHaveAnimations(false),
- mHasPendingInvalidation(false)
- { }
- VectorImage::~VectorImage()
- {
- CancelAllListeners();
- SurfaceCache::RemoveImage(ImageKey(this));
- }
- //------------------------------------------------------------------------------
- // Methods inherited from Image.h
- nsresult
- VectorImage::Init(const char* aMimeType,
- uint32_t aFlags)
- {
- // We don't support re-initialization
- if (mIsInitialized) {
- return NS_ERROR_ILLEGAL_VALUE;
- }
- MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError,
- "Flags unexpectedly set before initialization");
- MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype");
- mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
- // Lock this image's surfaces in the SurfaceCache if we're not discardable.
- if (!mDiscardable) {
- mLockCount++;
- SurfaceCache::LockImage(ImageKey(this));
- }
- mIsInitialized = true;
- return NS_OK;
- }
- size_t
- VectorImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
- {
- if (!mSVGDocumentWrapper) {
- return 0; // No document, so no memory used for the document.
- }
- nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
- if (!doc) {
- return 0; // No document, so no memory used for the document.
- }
- nsWindowSizes windowSizes(aMallocSizeOf);
- doc->DocAddSizeOfIncludingThis(&windowSizes);
- if (windowSizes.getTotalSize() == 0) {
- // MallocSizeOf fails on this platform. Because we also use this method for
- // determining the size of cache entries, we need to return something
- // reasonable here. Unfortunately, there's no way to estimate the document's
- // size accurately, so we just use a constant value of 100KB, which will
- // generally underestimate the true size.
- return 100 * 1024;
- }
- return windowSizes.getTotalSize();
- }
- void
- VectorImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
- MallocSizeOf aMallocSizeOf) const
- {
- SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
- }
- nsresult
- VectorImage::OnImageDataComplete(nsIRequest* aRequest,
- nsISupports* aContext,
- nsresult aStatus,
- bool aLastPart)
- {
- // Call our internal OnStopRequest method, which only talks to our embedded
- // SVG document. This won't have any effect on our ProgressTracker.
- nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus);
- // Give precedence to Necko failure codes.
- if (NS_FAILED(aStatus)) {
- finalStatus = aStatus;
- }
-
- Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
- if (mIsFullyLoaded || mError) {
- // Our document is loaded, so we're ready to notify now.
- mProgressTracker->SyncNotifyProgress(loadProgress);
- } else {
- // Record our progress so far; we'll actually send the notifications in
- // OnSVGDocumentLoaded or OnSVGDocumentError.
- mLoadProgress = Some(loadProgress);
- }
- return finalStatus;
- }
- nsresult
- VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
- nsISupports* aContext,
- nsIInputStream* aInStr,
- uint64_t aSourceOffset,
- uint32_t aCount)
- {
- return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount);
- }
- nsresult
- VectorImage::StartAnimation()
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
- mSVGDocumentWrapper->StartAnimation();
- return NS_OK;
- }
- nsresult
- VectorImage::StopAnimation()
- {
- nsresult rv = NS_OK;
- if (mError) {
- rv = NS_ERROR_FAILURE;
- } else {
- MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations,
- "Should not have been animating!");
- mSVGDocumentWrapper->StopAnimation();
- }
- mAnimating = false;
- return rv;
- }
- bool
- VectorImage::ShouldAnimate()
- {
- return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
- }
- NS_IMETHODIMP_(void)
- VectorImage::SetAnimationStartTime(const TimeStamp& aTime)
- {
- // We don't care about animation start time.
- }
- //------------------------------------------------------------------------------
- // imgIContainer methods
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::GetWidth(int32_t* aWidth)
- {
- if (mError || !mIsFullyLoaded) {
- // XXXdholbert Technically we should leave outparam untouched when we
- // fail. But since many callers don't check for failure, we set it to 0 on
- // failure, for sane/predictable results.
- *aWidth = 0;
- return NS_ERROR_FAILURE;
- }
- SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
- MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
- "loading without errors");
- int32_t rootElemWidth = rootElem->GetIntrinsicWidth();
- if (rootElemWidth < 0) {
- *aWidth = 0;
- return NS_ERROR_FAILURE;
- }
- *aWidth = rootElemWidth;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP_(void)
- VectorImage::RequestRefresh(const TimeStamp& aTime)
- {
- if (HadRecentRefresh(aTime)) {
- return;
- }
- PendingAnimationTracker* tracker =
- mSVGDocumentWrapper->GetDocument()->GetPendingAnimationTracker();
- if (tracker && ShouldAnimate()) {
- tracker->TriggerPendingAnimationsOnNextTick(aTime);
- }
- EvaluateAnimation();
- mSVGDocumentWrapper->TickRefreshDriver();
- if (mHasPendingInvalidation) {
- mHasPendingInvalidation = false;
- SendInvalidationNotifications();
- }
- }
- void
- VectorImage::SendInvalidationNotifications()
- {
- // Animated images don't send out invalidation notifications as soon as
- // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick
- // records that there are pending invalidations and then returns immediately.
- // The notifications are actually sent from RequestRefresh(). We send these
- // notifications there to ensure that there is actually a document observing
- // us. Otherwise, the notifications are just wasted effort.
- //
- // Non-animated images call this method directly from
- // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
- // called for them. Ordinarily this isn't needed, since we send out
- // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
- // SVG document may not be 100% ready to render at that time. In those cases
- // we would miss the subsequent invalidations if we didn't send out the
- // notifications directly in |InvalidateObservers...|.
- if (mProgressTracker) {
- SurfaceCache::RemoveImage(ImageKey(this));
- mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
- GetMaxSizedIntRect());
- }
- }
- NS_IMETHODIMP_(IntRect)
- VectorImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
- {
- return aRect;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::GetHeight(int32_t* aHeight)
- {
- if (mError || !mIsFullyLoaded) {
- // XXXdholbert Technically we should leave outparam untouched when we
- // fail. But since many callers don't check for failure, we set it to 0 on
- // failure, for sane/predictable results.
- *aHeight = 0;
- return NS_ERROR_FAILURE;
- }
- SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
- MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
- "loading without errors");
- int32_t rootElemHeight = rootElem->GetIntrinsicHeight();
- if (rootElemHeight < 0) {
- *aHeight = 0;
- return NS_ERROR_FAILURE;
- }
- *aHeight = rootElemHeight;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::GetIntrinsicSize(nsSize* aSize)
- {
- if (mError || !mIsFullyLoaded) {
- return NS_ERROR_FAILURE;
- }
- nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
- if (!rootFrame) {
- return NS_ERROR_FAILURE;
- }
- *aSize = nsSize(-1, -1);
- IntrinsicSize rfSize = rootFrame->GetIntrinsicSize();
- if (rfSize.width.GetUnit() == eStyleUnit_Coord) {
- aSize->width = rfSize.width.GetCoordValue();
- }
- if (rfSize.height.GetUnit() == eStyleUnit_Coord) {
- aSize->height = rfSize.height.GetCoordValue();
- }
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::GetIntrinsicRatio(AspectRatio* aRatio)
- {
- if (mError || !mIsFullyLoaded) {
- return NS_ERROR_FAILURE;
- }
- nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
- if (!rootFrame) {
- return NS_ERROR_FAILURE;
- }
- *aRatio = rootFrame->GetIntrinsicRatio();
- return NS_OK;
- }
- NS_IMETHODIMP_(Orientation)
- VectorImage::GetOrientation()
- {
- return Orientation();
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::GetType(uint16_t* aType)
- {
- NS_ENSURE_ARG_POINTER(aType);
- *aType = imgIContainer::TYPE_VECTOR;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::GetAnimated(bool* aAnimated)
- {
- if (mError || !mIsFullyLoaded) {
- return NS_ERROR_FAILURE;
- }
- *aAnimated = mSVGDocumentWrapper->IsAnimated();
- return NS_OK;
- }
- //******************************************************************************
- int32_t
- VectorImage::GetFirstFrameDelay()
- {
- if (mError) {
- return -1;
- }
- if (!mSVGDocumentWrapper->IsAnimated()) {
- return -1;
- }
- // We don't really have a frame delay, so just pretend that we constantly
- // need updates.
- return 0;
- }
- NS_IMETHODIMP_(bool)
- VectorImage::WillDrawOpaqueNow()
- {
- return false; // In general, SVG content is not opaque.
- }
- //******************************************************************************
- NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
- VectorImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags)
- {
- if (mError) {
- return nullptr;
- }
- // Look up height & width
- // ----------------------
- SVGSVGElement* svgElem = mSVGDocumentWrapper->GetRootSVGElem();
- MOZ_ASSERT(svgElem, "Should have a root SVG elem, since we finished "
- "loading without errors");
- nsIntSize imageIntSize(svgElem->GetIntrinsicWidth(),
- svgElem->GetIntrinsicHeight());
- if (imageIntSize.IsEmpty()) {
- // We'll get here if our SVG doc has a percent-valued or negative width or
- // height.
- return nullptr;
- }
- return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags);
- }
- NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
- VectorImage::GetFrameAtSize(const IntSize& aSize,
- uint32_t aWhichFrame,
- uint32_t aFlags)
- {
- MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
- if (aSize.IsEmpty()) {
- return nullptr;
- }
- if (aWhichFrame > FRAME_MAX_VALUE) {
- return nullptr;
- }
- if (mError || !mIsFullyLoaded) {
- return nullptr;
- }
- // Make our surface the size of what will ultimately be drawn to it.
- // (either the full image size, or the restricted region)
- RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
- CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
- if (!dt || !dt->IsValid()) {
- NS_ERROR("Could not create a DrawTarget");
- return nullptr;
- }
- RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
- MOZ_ASSERT(context); // already checked the draw target above
- auto result = Draw(context, aSize, ImageRegion::Create(aSize),
- aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags);
- return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
- }
- NS_IMETHODIMP_(bool)
- VectorImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
- {
- return false;
- }
- //******************************************************************************
- NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
- VectorImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
- {
- return nullptr;
- }
- struct SVGDrawingParameters
- {
- SVGDrawingParameters(gfxContext* aContext,
- const nsIntSize& aSize,
- const ImageRegion& aRegion,
- SamplingFilter aSamplingFilter,
- const Maybe<SVGImageContext>& aSVGContext,
- float aAnimationTime,
- uint32_t aFlags)
- : context(aContext)
- , size(aSize.width, aSize.height)
- , region(aRegion)
- , samplingFilter(aSamplingFilter)
- , svgContext(aSVGContext)
- , viewportSize(aSize)
- , animationTime(aAnimationTime)
- , flags(aFlags)
- , opacity(aSVGContext ? aSVGContext->GetGlobalOpacity() : 1.0)
- {
- if (aSVGContext) {
- CSSIntSize sz = aSVGContext->GetViewportSize();
- viewportSize = nsIntSize(sz.width, sz.height); // XXX losing unit
- }
- }
- gfxContext* context;
- IntSize size;
- ImageRegion region;
- SamplingFilter samplingFilter;
- const Maybe<SVGImageContext>& svgContext;
- nsIntSize viewportSize;
- float animationTime;
- uint32_t flags;
- gfxFloat opacity;
- };
- //******************************************************************************
- NS_IMETHODIMP_(DrawResult)
- VectorImage::Draw(gfxContext* aContext,
- const nsIntSize& aSize,
- const ImageRegion& aRegion,
- uint32_t aWhichFrame,
- SamplingFilter aSamplingFilter,
- const Maybe<SVGImageContext>& aSVGContext,
- uint32_t aFlags)
- {
- if (aWhichFrame > FRAME_MAX_VALUE) {
- return DrawResult::BAD_ARGS;
- }
- if (!aContext) {
- return DrawResult::BAD_ARGS;
- }
- if (mError) {
- return DrawResult::BAD_IMAGE;
- }
- if (!mIsFullyLoaded) {
- return DrawResult::NOT_READY;
- }
- if (mIsDrawing) {
- NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
- return DrawResult::TEMPORARY_ERROR;
- }
- if (mAnimationConsumers == 0 && mProgressTracker) {
- mProgressTracker->OnUnlockedDraw();
- }
- AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
- mIsDrawing = true;
- Maybe<SVGImageContext> svgContext;
- // If FLAG_FORCE_PRESERVEASPECTRATIO_NONE bit is set, that mean we should
- // overwrite SVG preserveAspectRatio attibute of this image with none, and
- // always stretch this image to viewport non-uniformly.
- // And we can do this only if the caller pass in the the SVG viewport, via
- // aSVGContext.
- if ((aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext.isSome()) {
- Maybe<SVGPreserveAspectRatio> aspectRatio =
- Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
- SVG_MEETORSLICE_UNKNOWN));
- svgContext =
- Some(SVGImageContext(aSVGContext->GetViewportSize(),
- aspectRatio));
- } else {
- svgContext = aSVGContext;
- }
- float animTime =
- (aWhichFrame == FRAME_FIRST) ? 0.0f
- : mSVGDocumentWrapper->GetCurrentTime();
- AutoSVGRenderingState autoSVGState(svgContext, animTime,
- mSVGDocumentWrapper->GetRootSVGElem());
- SVGDrawingParameters params(aContext, aSize, aRegion, aSamplingFilter,
- svgContext, animTime, aFlags);
- // If we have an prerasterized version of this image that matches the
- // drawing parameters, use that.
- RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
- if (svgDrawable) {
- Show(svgDrawable, params);
- return DrawResult::SUCCESS;
- }
- // We didn't get a hit in the surface cache, so we'll need to rerasterize.
- CreateSurfaceAndShow(params, aContext->GetDrawTarget()->GetBackendType());
- return DrawResult::SUCCESS;
- }
- already_AddRefed<gfxDrawable>
- VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
- {
- // If we're not allowed to use a cached surface, don't attempt a lookup.
- if (aParams.flags & FLAG_BYPASS_SURFACE_CACHE) {
- return nullptr;
- }
- // We don't do any caching if we have animation, so don't bother with a lookup
- // in this case either.
- if (mHaveAnimations) {
- return nullptr;
- }
- LookupResult result =
- SurfaceCache::Lookup(ImageKey(this),
- VectorSurfaceKey(aParams.size, aParams.svgContext));
- if (!result) {
- return nullptr; // No matching surface, or the OS freed the volatile buffer.
- }
- RefPtr<SourceSurface> sourceSurface = result.Surface()->GetSourceSurface();
- if (!sourceSurface) {
- // Something went wrong. (Probably a GPU driver crash or device reset.)
- // Attempt to recover.
- RecoverFromLossOfSurfaces();
- return nullptr;
- }
- RefPtr<gfxDrawable> svgDrawable =
- new gfxSurfaceDrawable(sourceSurface, result.Surface()->GetSize());
- return svgDrawable.forget();
- }
- void
- VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendType aBackend)
- {
- mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
- mSVGDocumentWrapper->FlushImageTransformInvalidation();
- RefPtr<gfxDrawingCallback> cb =
- new SVGDrawingCallback(mSVGDocumentWrapper,
- IntRect(IntPoint(0, 0), aParams.viewportSize),
- aParams.size,
- aParams.flags);
- RefPtr<gfxDrawable> svgDrawable =
- new gfxCallbackDrawable(cb, aParams.size);
- // We take an early exit without using the surface cache if too large,
- // because for vector images this can cause bad perf issues if large sizes
- // are scaled repeatedly (a rather common scenario) that can quickly exhaust
- // the cache.
- // Similar to max image size calculations, this has a max cap and size check.
- // max cap = 8000 (pixels); size check = 5% of cache
- int32_t maxDimension = 8000;
- int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 20;
-
- bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
- // Refuse to cache animated images:
- // XXX(seth): We may remove this restriction in bug 922893.
- mHaveAnimations ||
- // The image is too big to fit in the cache:
- !SurfaceCache::CanHold(aParams.size) ||
- // Image x or y is larger than our cache cap:
- aParams.size.width > maxDimension ||
- aParams.size.height > maxDimension;
- if (!bypassCache) {
- // This is separated out to make sure width and height are sane at this point
- // and the result can't overflow. Note: keep maxDimension low enough so that
- // (maxDimension)^2 x 4 < INT32_MAX.
- // Assuming surface size for any rendered vector image is RGBA, so 4Bpp.
- bypassCache = (aParams.size.width * aParams.size.height * 4) > maxCacheElemSize;
- }
- if (bypassCache) {
- return Show(svgDrawable, aParams);
- }
- // We're about to rerasterize, which may mean that some of the previous
- // surfaces we've rasterized aren't useful anymore. We can allow them to
- // expire from the cache by unlocking them here, and then sending out an
- // invalidation. If this image is locked, any surfaces that are still useful
- // will become locked again when Draw touches them, and the remainder will
- // eventually expire.
- SurfaceCache::UnlockEntries(ImageKey(this));
- // Try to create an imgFrame, initializing the surface it contains by drawing
- // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
- NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame);
- nsresult rv =
- frame->InitWithDrawable(svgDrawable, aParams.size,
- SurfaceFormat::B8G8R8A8,
- SamplingFilter::POINT, aParams.flags,
- aBackend);
- // If we couldn't create the frame, it was probably because it would end
- // up way too big. Generally it also wouldn't fit in the cache, but the prefs
- // could be set such that the cache isn't the limiting factor.
- if (NS_FAILED(rv)) {
- return Show(svgDrawable, aParams);
- }
- // Take a strong reference to the frame's surface and make sure it hasn't
- // already been purged by the operating system.
- RefPtr<SourceSurface> surface = frame->GetSourceSurface();
- if (!surface) {
- return Show(svgDrawable, aParams);
- }
- // Attempt to cache the frame.
- SurfaceKey surfaceKey = VectorSurfaceKey(aParams.size, aParams.svgContext);
- NotNull<RefPtr<ISurfaceProvider>> provider =
- WrapNotNull(new SimpleSurfaceProvider(ImageKey(this), surfaceKey, frame));
- SurfaceCache::Insert(provider);
- // Draw.
- RefPtr<gfxDrawable> drawable =
- new gfxSurfaceDrawable(surface, aParams.size);
- Show(drawable, aParams);
- // Send out an invalidation so that surfaces that are still in use get
- // re-locked. See the discussion of the UnlockSurfaces call above.
- mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
- GetMaxSizedIntRect());
- }
- void
- VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
- {
- MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
- gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
- aParams.size,
- aParams.region,
- SurfaceFormat::B8G8R8A8,
- aParams.samplingFilter,
- aParams.flags, aParams.opacity);
- MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
- mRenderingObserver->ResumeHonoringInvalidations();
- }
- void
- VectorImage::RecoverFromLossOfSurfaces()
- {
- NS_WARNING("An imgFrame became invalid. Attempting to recover...");
- // Discard all existing frames, since they're probably all now invalid.
- SurfaceCache::RemoveImage(ImageKey(this));
- }
- NS_IMETHODIMP
- VectorImage::StartDecoding()
- {
- // Nothing to do for SVG images
- return NS_OK;
- }
- NS_IMETHODIMP
- VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
- {
- // Nothing to do for SVG images, though in theory we could rasterize to the
- // provided size ahead of time if we supported off-main-thread SVG
- // rasterization...
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::LockImage()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- mLockCount++;
- if (mLockCount == 1) {
- // Lock this image's surfaces in the SurfaceCache.
- SurfaceCache::LockImage(ImageKey(this));
- }
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::UnlockImage()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- if (mLockCount == 0) {
- MOZ_ASSERT_UNREACHABLE("Calling UnlockImage with a zero lock count");
- return NS_ERROR_ABORT;
- }
- mLockCount--;
- if (mLockCount == 0) {
- // Unlock this image's surfaces in the SurfaceCache.
- SurfaceCache::UnlockImage(ImageKey(this));
- }
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::RequestDiscard()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mDiscardable && mLockCount == 0) {
- SurfaceCache::RemoveImage(ImageKey(this));
- mProgressTracker->OnDiscard();
- }
- return NS_OK;
- }
- void
- VectorImage::OnSurfaceDiscarded()
- {
- MOZ_ASSERT(mProgressTracker);
- NS_DispatchToMainThread(NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard));
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::ResetAnimation()
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- if (!mIsFullyLoaded || !mHaveAnimations) {
- return NS_OK; // There are no animations to be reset.
- }
- mSVGDocumentWrapper->ResetAnimation();
- return NS_OK;
- }
- NS_IMETHODIMP_(float)
- VectorImage::GetFrameIndex(uint32_t aWhichFrame)
- {
- MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
- return aWhichFrame == FRAME_FIRST
- ? 0.0f
- : mSVGDocumentWrapper->GetCurrentTime();
- }
- //------------------------------------------------------------------------------
- // nsIRequestObserver methods
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
- {
- MOZ_ASSERT(!mSVGDocumentWrapper,
- "Repeated call to OnStartRequest -- can this happen?");
- mSVGDocumentWrapper = new SVGDocumentWrapper();
- nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt);
- if (NS_FAILED(rv)) {
- mSVGDocumentWrapper = nullptr;
- mError = true;
- return rv;
- }
- // ProgressTracker::SyncNotifyProgress may release us, so ensure we
- // stick around long enough to complete our work.
- RefPtr<VectorImage> kungFuDeathGrip(this);
- // Block page load until the document's ready. (We unblock it in
- // OnSVGDocumentLoaded or OnSVGDocumentError.)
- if (mProgressTracker) {
- mProgressTracker->SyncNotifyProgress(FLAG_ONLOAD_BLOCKED);
- }
- // Create a listener to wait until the SVG document is fully loaded, which
- // will signal that this image is ready to render. Certain error conditions
- // will prevent us from ever getting this notification, so we also create a
- // listener that waits for parsing to complete and cancels the
- // SVGLoadEventListener if needed. The listeners are automatically attached
- // to the document by their constructors.
- nsIDocument* document = mSVGDocumentWrapper->GetDocument();
- mLoadEventListener = new SVGLoadEventListener(document, this);
- mParseCompleteListener = new SVGParseCompleteListener(document, this);
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
- nsresult aStatus)
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus);
- }
- void
- VectorImage::OnSVGDocumentParsed()
- {
- MOZ_ASSERT(mParseCompleteListener, "Should have the parse complete listener");
- MOZ_ASSERT(mLoadEventListener, "Should have the load event listener");
- if (!mSVGDocumentWrapper->GetRootSVGElem()) {
- // This is an invalid SVG document. It may have failed to parse, or it may
- // be missing the <svg> root element, or the <svg> root element may not
- // declare the correct namespace. In any of these cases, we'll never be
- // notified that the SVG finished loading, so we need to treat this as an
- // error.
- OnSVGDocumentError();
- }
- }
- void
- VectorImage::CancelAllListeners()
- {
- if (mParseCompleteListener) {
- mParseCompleteListener->Cancel();
- mParseCompleteListener = nullptr;
- }
- if (mLoadEventListener) {
- mLoadEventListener->Cancel();
- mLoadEventListener = nullptr;
- }
- }
- void
- VectorImage::OnSVGDocumentLoaded()
- {
- MOZ_ASSERT(mSVGDocumentWrapper->GetRootSVGElem(),
- "Should have parsed successfully");
- MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations,
- "These flags shouldn't get set until OnSVGDocumentLoaded. "
- "Duplicate calls to OnSVGDocumentLoaded?");
- CancelAllListeners();
- // XXX Flushing is wasteful if embedding frame hasn't had initial reflow.
- mSVGDocumentWrapper->FlushLayout();
- mIsFullyLoaded = true;
- mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
- // Start listening to our image for rendering updates.
- mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
- // ProgressTracker::SyncNotifyProgress may release us, so ensure we
- // stick around long enough to complete our work.
- RefPtr<VectorImage> kungFuDeathGrip(this);
- // Tell *our* observers that we're done loading.
- if (mProgressTracker) {
- Progress progress = FLAG_SIZE_AVAILABLE |
- FLAG_HAS_TRANSPARENCY |
- FLAG_FRAME_COMPLETE |
- FLAG_DECODE_COMPLETE |
- FLAG_ONLOAD_UNBLOCKED;
- if (mHaveAnimations) {
- progress |= FLAG_IS_ANIMATED;
- }
- // Merge in any saved progress from OnImageDataComplete.
- if (mLoadProgress) {
- progress |= *mLoadProgress;
- mLoadProgress = Nothing();
- }
- mProgressTracker->SyncNotifyProgress(progress, GetMaxSizedIntRect());
- }
- EvaluateAnimation();
- }
- void
- VectorImage::OnSVGDocumentError()
- {
- CancelAllListeners();
- mError = true;
- if (mProgressTracker) {
- // Notify observers about the error and unblock page load.
- Progress progress = FLAG_ONLOAD_UNBLOCKED | FLAG_HAS_ERROR;
- // Merge in any saved progress from OnImageDataComplete.
- if (mLoadProgress) {
- progress |= *mLoadProgress;
- mLoadProgress = Nothing();
- }
- mProgressTracker->SyncNotifyProgress(progress);
- }
- }
- //------------------------------------------------------------------------------
- // nsIStreamListener method
- //******************************************************************************
- NS_IMETHODIMP
- VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
- nsIInputStream* aInStr, uint64_t aSourceOffset,
- uint32_t aCount)
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
- aSourceOffset, aCount);
- }
- // --------------------------
- // Invalidation helper method
- void
- VectorImage::InvalidateObserversOnNextRefreshDriverTick()
- {
- if (mHaveAnimations) {
- mHasPendingInvalidation = true;
- } else {
- SendInvalidationNotifications();
- }
- }
- void
- VectorImage::PropagateUseCounters(nsIDocument* aParentDocument)
- {
- nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
- if (doc) {
- doc->PropagateUseCounters(aParentDocument);
- }
- }
- nsIntSize
- VectorImage::OptimalImageSizeForDest(const gfxSize& aDest,
- uint32_t aWhichFrame,
- SamplingFilter aSamplingFilter,
- uint32_t aFlags)
- {
- MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
- aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
- "Unexpected destination size");
- // We can rescale SVGs freely, so just return the provided destination size.
- return nsIntSize::Ceil(aDest.width, aDest.height);
- }
- already_AddRefed<imgIContainer>
- VectorImage::Unwrap()
- {
- nsCOMPtr<imgIContainer> self(this);
- return self.forget();
- }
- } // namespace image
- } // namespace mozilla
|