image/src/VectorImage.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/src/VectorImage.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1158 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "VectorImage.h"
    1.10 +
    1.11 +#include "gfx2DGlue.h"
    1.12 +#include "gfxContext.h"
    1.13 +#include "gfxDrawable.h"
    1.14 +#include "gfxPlatform.h"
    1.15 +#include "gfxUtils.h"
    1.16 +#include "imgDecoderObserver.h"
    1.17 +#include "mozilla/AutoRestore.h"
    1.18 +#include "mozilla/MemoryReporting.h"
    1.19 +#include "mozilla/dom/SVGSVGElement.h"
    1.20 +#include "mozilla/gfx/2D.h"
    1.21 +#include "mozilla/RefPtr.h"
    1.22 +#include "nsIDOMEvent.h"
    1.23 +#include "nsIPresShell.h"
    1.24 +#include "nsIStreamListener.h"
    1.25 +#include "nsMimeTypes.h"
    1.26 +#include "nsPresContext.h"
    1.27 +#include "nsRect.h"
    1.28 +#include "nsStubDocumentObserver.h"
    1.29 +#include "nsSVGEffects.h" // for nsSVGRenderingObserver
    1.30 +#include "Orientation.h"
    1.31 +#include "SVGDocumentWrapper.h"
    1.32 +#include "nsIDOMEventListener.h"
    1.33 +#include "SurfaceCache.h"
    1.34 +
    1.35 +// undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
    1.36 +#undef GetCurrentTime
    1.37 +
    1.38 +namespace mozilla {
    1.39 +
    1.40 +using namespace dom;
    1.41 +using namespace gfx;
    1.42 +using namespace layers;
    1.43 +
    1.44 +namespace image {
    1.45 +
    1.46 +// Helper-class: SVGRootRenderingObserver
    1.47 +class SVGRootRenderingObserver MOZ_FINAL : public nsSVGRenderingObserver {
    1.48 +public:
    1.49 +  SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper,
    1.50 +                           VectorImage*        aVectorImage)
    1.51 +    : nsSVGRenderingObserver()
    1.52 +    , mDocWrapper(aDocWrapper)
    1.53 +    , mVectorImage(aVectorImage)
    1.54 +    , mHonoringInvalidations(true)
    1.55 +  {
    1.56 +    MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
    1.57 +    MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
    1.58 +
    1.59 +    StartListening();
    1.60 +    Element* elem = GetTarget();
    1.61 +    MOZ_ASSERT(elem, "no root SVG node for us to observe");
    1.62 +
    1.63 +    nsSVGEffects::AddRenderingObserver(elem, this);
    1.64 +    mInObserverList = true;
    1.65 +  }
    1.66 +
    1.67 +  virtual ~SVGRootRenderingObserver()
    1.68 +  {
    1.69 +    StopListening();
    1.70 +  }
    1.71 +
    1.72 +  void ResumeHonoringInvalidations()
    1.73 +  {
    1.74 +    mHonoringInvalidations = true;
    1.75 +  }
    1.76 +
    1.77 +protected:
    1.78 +  virtual Element* GetTarget() MOZ_OVERRIDE
    1.79 +  {
    1.80 +    return mDocWrapper->GetRootSVGElem();
    1.81 +  }
    1.82 +
    1.83 +  virtual void DoUpdate() MOZ_OVERRIDE
    1.84 +  {
    1.85 +    Element* elem = GetTarget();
    1.86 +    MOZ_ASSERT(elem, "missing root SVG node");
    1.87 +
    1.88 +    if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) {
    1.89 +      nsIFrame* frame = elem->GetPrimaryFrame();
    1.90 +      if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
    1.91 +        // We're being destroyed. Bail out.
    1.92 +        return;
    1.93 +      }
    1.94 +
    1.95 +      // Ignore further invalidations until we draw.
    1.96 +      mHonoringInvalidations = false;
    1.97 +
    1.98 +      mVectorImage->InvalidateObserversOnNextRefreshDriverTick();
    1.99 +    }
   1.100 +
   1.101 +    // Our caller might've removed us from rendering-observer list.
   1.102 +    // Add ourselves back!
   1.103 +    if (!mInObserverList) {
   1.104 +      nsSVGEffects::AddRenderingObserver(elem, this);
   1.105 +      mInObserverList = true;
   1.106 +    } 
   1.107 +  }
   1.108 +
   1.109 +  // Private data
   1.110 +  const nsRefPtr<SVGDocumentWrapper> mDocWrapper;
   1.111 +  VectorImage* const mVectorImage;   // Raw pointer because it owns me.
   1.112 +  bool mHonoringInvalidations;
   1.113 +};
   1.114 +
   1.115 +class SVGParseCompleteListener MOZ_FINAL : public nsStubDocumentObserver {
   1.116 +public:
   1.117 +  NS_DECL_ISUPPORTS
   1.118 +
   1.119 +  SVGParseCompleteListener(nsIDocument* aDocument,
   1.120 +                           VectorImage* aImage)
   1.121 +    : mDocument(aDocument)
   1.122 +    , mImage(aImage)
   1.123 +  {
   1.124 +    MOZ_ASSERT(mDocument, "Need an SVG document");
   1.125 +    MOZ_ASSERT(mImage, "Need an image");
   1.126 +
   1.127 +    mDocument->AddObserver(this);
   1.128 +  }
   1.129 +
   1.130 +  ~SVGParseCompleteListener()
   1.131 +  {
   1.132 +    if (mDocument) {
   1.133 +      // The document must have been destroyed before we got our event.
   1.134 +      // Otherwise this can't happen, since documents hold strong references to
   1.135 +      // their observers.
   1.136 +      Cancel();
   1.137 +    }
   1.138 +  }
   1.139 +
   1.140 +  void EndLoad(nsIDocument* aDocument) MOZ_OVERRIDE
   1.141 +  {
   1.142 +    MOZ_ASSERT(aDocument == mDocument, "Got EndLoad for wrong document?");
   1.143 +
   1.144 +    // OnSVGDocumentParsed will release our owner's reference to us, so ensure
   1.145 +    // we stick around long enough to complete our work.
   1.146 +    nsRefPtr<SVGParseCompleteListener> kungFuDeathGroup(this);
   1.147 +
   1.148 +    mImage->OnSVGDocumentParsed();
   1.149 +  }
   1.150 +
   1.151 +  void Cancel()
   1.152 +  {
   1.153 +    MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
   1.154 +    if (mDocument) {
   1.155 +      mDocument->RemoveObserver(this);
   1.156 +      mDocument = nullptr;
   1.157 +    }
   1.158 +  }
   1.159 +
   1.160 +private:
   1.161 +  nsCOMPtr<nsIDocument> mDocument;
   1.162 +  VectorImage* const mImage; // Raw pointer to owner.
   1.163 +};
   1.164 +
   1.165 +NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver)
   1.166 +
   1.167 +class SVGLoadEventListener MOZ_FINAL : public nsIDOMEventListener {
   1.168 +public:
   1.169 +  NS_DECL_ISUPPORTS
   1.170 +
   1.171 +  SVGLoadEventListener(nsIDocument* aDocument,
   1.172 +                       VectorImage* aImage)
   1.173 +    : mDocument(aDocument)
   1.174 +    , mImage(aImage)
   1.175 +  {
   1.176 +    MOZ_ASSERT(mDocument, "Need an SVG document");
   1.177 +    MOZ_ASSERT(mImage, "Need an image");
   1.178 +
   1.179 +    mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true, false);
   1.180 +    mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true, false);
   1.181 +    mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true, false);
   1.182 +  }
   1.183 +
   1.184 +  ~SVGLoadEventListener()
   1.185 +  {
   1.186 +    if (mDocument) {
   1.187 +      // The document must have been destroyed before we got our event.
   1.188 +      // Otherwise this can't happen, since documents hold strong references to
   1.189 +      // their observers.
   1.190 +      Cancel();
   1.191 +    }
   1.192 +  }
   1.193 +
   1.194 +  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE
   1.195 +  {
   1.196 +    MOZ_ASSERT(mDocument, "Need an SVG document. Received multiple events?");
   1.197 +
   1.198 +    // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference
   1.199 +    // to us, so ensure we stick around long enough to complete our work.
   1.200 +    nsRefPtr<SVGLoadEventListener> kungFuDeathGroup(this);
   1.201 +
   1.202 +    nsAutoString eventType;
   1.203 +    aEvent->GetType(eventType);
   1.204 +    MOZ_ASSERT(eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")  ||
   1.205 +               eventType.EqualsLiteral("SVGAbort")                   ||
   1.206 +               eventType.EqualsLiteral("SVGError"),
   1.207 +               "Received unexpected event");
   1.208 +
   1.209 +    if (eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")) {
   1.210 +      mImage->OnSVGDocumentLoaded();
   1.211 +    } else {
   1.212 +      mImage->OnSVGDocumentError();
   1.213 +    }
   1.214 +
   1.215 +    return NS_OK;
   1.216 +  }
   1.217 +
   1.218 +  void Cancel()
   1.219 +  {
   1.220 +    MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
   1.221 +    if (mDocument) {
   1.222 +      mDocument->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true);
   1.223 +      mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
   1.224 +      mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
   1.225 +      mDocument = nullptr;
   1.226 +    }
   1.227 +  }
   1.228 +
   1.229 +private:
   1.230 +  nsCOMPtr<nsIDocument> mDocument;
   1.231 +  VectorImage* const mImage; // Raw pointer to owner.
   1.232 +};
   1.233 +
   1.234 +NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener)
   1.235 +
   1.236 +// Helper-class: SVGDrawingCallback
   1.237 +class SVGDrawingCallback : public gfxDrawingCallback {
   1.238 +public:
   1.239 +  SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
   1.240 +                     const nsIntRect& aViewport,
   1.241 +                     const gfxSize& aScale,
   1.242 +                     uint32_t aImageFlags) :
   1.243 +    mSVGDocumentWrapper(aSVGDocumentWrapper),
   1.244 +    mViewport(aViewport),
   1.245 +    mScale(aScale),
   1.246 +    mImageFlags(aImageFlags)
   1.247 +  {}
   1.248 +  virtual bool operator()(gfxContext* aContext,
   1.249 +                            const gfxRect& aFillRect,
   1.250 +                            const GraphicsFilter& aFilter,
   1.251 +                            const gfxMatrix& aTransform);
   1.252 +private:
   1.253 +  nsRefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
   1.254 +  const nsIntRect mViewport;
   1.255 +  const gfxSize   mScale;
   1.256 +  uint32_t        mImageFlags;
   1.257 +};
   1.258 +
   1.259 +// Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
   1.260 +bool
   1.261 +SVGDrawingCallback::operator()(gfxContext* aContext,
   1.262 +                               const gfxRect& aFillRect,
   1.263 +                               const GraphicsFilter& aFilter,
   1.264 +                               const gfxMatrix& aTransform)
   1.265 +{
   1.266 +  MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper");
   1.267 +
   1.268 +  // Get (& sanity-check) the helper-doc's presShell
   1.269 +  nsCOMPtr<nsIPresShell> presShell;
   1.270 +  if (NS_FAILED(mSVGDocumentWrapper->GetPresShell(getter_AddRefs(presShell)))) {
   1.271 +    NS_WARNING("Unable to draw -- presShell lookup failed");
   1.272 +    return false;
   1.273 +  }
   1.274 +  MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null");
   1.275 +
   1.276 +  gfxContextAutoSaveRestore contextRestorer(aContext);
   1.277 +
   1.278 +  // Clip to aFillRect so that we don't paint outside.
   1.279 +  aContext->NewPath();
   1.280 +  aContext->Rectangle(aFillRect);
   1.281 +  aContext->Clip();
   1.282 +
   1.283 +  gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
   1.284 +  aContext->Multiply(gfxMatrix(aTransform).Invert());
   1.285 +  aContext->Scale(1.0 / mScale.width, 1.0 / mScale.height);
   1.286 +
   1.287 +  nsPresContext* presContext = presShell->GetPresContext();
   1.288 +  MOZ_ASSERT(presContext, "pres shell w/out pres context");
   1.289 +
   1.290 +  nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x),
   1.291 +                 presContext->DevPixelsToAppUnits(mViewport.y),
   1.292 +                 presContext->DevPixelsToAppUnits(mViewport.width),
   1.293 +                 presContext->DevPixelsToAppUnits(mViewport.height));
   1.294 +
   1.295 +  uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
   1.296 +  if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
   1.297 +    renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
   1.298 +  }
   1.299 +
   1.300 +  presShell->RenderDocument(svgRect, renderDocFlags,
   1.301 +                            NS_RGBA(0, 0, 0, 0), // transparent
   1.302 +                            aContext);
   1.303 +
   1.304 +  return true;
   1.305 +}
   1.306 +
   1.307 +// Implement VectorImage's nsISupports-inherited methods
   1.308 +NS_IMPL_ISUPPORTS(VectorImage,
   1.309 +                  imgIContainer,
   1.310 +                  nsIStreamListener,
   1.311 +                  nsIRequestObserver)
   1.312 +
   1.313 +//------------------------------------------------------------------------------
   1.314 +// Constructor / Destructor
   1.315 +
   1.316 +VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
   1.317 +                         ImageURL* aURI /* = nullptr */) :
   1.318 +  ImageResource(aURI), // invoke superclass's constructor
   1.319 +  mIsInitialized(false),
   1.320 +  mIsFullyLoaded(false),
   1.321 +  mIsDrawing(false),
   1.322 +  mHaveAnimations(false),
   1.323 +  mHasPendingInvalidation(false)
   1.324 +{
   1.325 +  mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
   1.326 +}
   1.327 +
   1.328 +VectorImage::~VectorImage()
   1.329 +{
   1.330 +  CancelAllListeners();
   1.331 +  SurfaceCache::Discard(this);
   1.332 +}
   1.333 +
   1.334 +//------------------------------------------------------------------------------
   1.335 +// Methods inherited from Image.h
   1.336 +
   1.337 +nsresult
   1.338 +VectorImage::Init(const char* aMimeType,
   1.339 +                  uint32_t aFlags)
   1.340 +{
   1.341 +  // We don't support re-initialization
   1.342 +  if (mIsInitialized)
   1.343 +    return NS_ERROR_ILLEGAL_VALUE;
   1.344 +
   1.345 +  MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError,
   1.346 +             "Flags unexpectedly set before initialization");
   1.347 +  MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype");
   1.348 +
   1.349 +  mIsInitialized = true;
   1.350 +  return NS_OK;
   1.351 +}
   1.352 +
   1.353 +nsIntRect
   1.354 +VectorImage::FrameRect(uint32_t aWhichFrame)
   1.355 +{
   1.356 +  return nsIntRect::GetMaxSizedIntRect();
   1.357 +}
   1.358 +
   1.359 +size_t
   1.360 +VectorImage::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
   1.361 +{
   1.362 +  // We're not storing the source data -- we just feed that directly to
   1.363 +  // our helper SVG document as we receive it, for it to parse.
   1.364 +  // So 0 is an appropriate return value here.
   1.365 +  return 0;
   1.366 +}
   1.367 +
   1.368 +size_t
   1.369 +VectorImage::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
   1.370 +{
   1.371 +  // XXXdholbert TODO: return num bytes used by helper SVG doc. (bug 590790)
   1.372 +  return 0;
   1.373 +}
   1.374 +
   1.375 +size_t
   1.376 +VectorImage::NonHeapSizeOfDecoded() const
   1.377 +{
   1.378 +  return 0;
   1.379 +}
   1.380 +
   1.381 +size_t
   1.382 +VectorImage::OutOfProcessSizeOfDecoded() const
   1.383 +{
   1.384 +  return 0;
   1.385 +}
   1.386 +
   1.387 +nsresult
   1.388 +VectorImage::OnImageDataComplete(nsIRequest* aRequest,
   1.389 +                                 nsISupports* aContext,
   1.390 +                                 nsresult aStatus,
   1.391 +                                 bool aLastPart)
   1.392 +{
   1.393 +  // Call our internal OnStopRequest method, which only talks to our embedded
   1.394 +  // SVG document. This won't have any effect on our imgStatusTracker.
   1.395 +  nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus);
   1.396 +
   1.397 +  // Give precedence to Necko failure codes.
   1.398 +  if (NS_FAILED(aStatus))
   1.399 +    finalStatus = aStatus;
   1.400 +
   1.401 +  // Actually fire OnStopRequest.
   1.402 +  if (mStatusTracker) {
   1.403 +    // XXX(seth): Is this seriously the least insane way to do this?
   1.404 +    nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
   1.405 +    imgDecoderObserver* observer = clone->GetDecoderObserver();
   1.406 +    observer->OnStopRequest(aLastPart, finalStatus);
   1.407 +    ImageStatusDiff diff = mStatusTracker->Difference(clone);
   1.408 +    mStatusTracker->ApplyDifference(diff);
   1.409 +    mStatusTracker->SyncNotifyDifference(diff);
   1.410 +  }
   1.411 +  return finalStatus;
   1.412 +}
   1.413 +
   1.414 +nsresult
   1.415 +VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
   1.416 +                                  nsISupports* aContext,
   1.417 +                                  nsIInputStream* aInStr,
   1.418 +                                  uint64_t aSourceOffset,
   1.419 +                                  uint32_t aCount)
   1.420 +{
   1.421 +  return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount);
   1.422 +}
   1.423 +
   1.424 +nsresult
   1.425 +VectorImage::OnNewSourceData()
   1.426 +{
   1.427 +  return NS_OK;
   1.428 +}
   1.429 +
   1.430 +nsresult
   1.431 +VectorImage::StartAnimation()
   1.432 +{
   1.433 +  if (mError)
   1.434 +    return NS_ERROR_FAILURE;
   1.435 +
   1.436 +  MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
   1.437 +
   1.438 +  mSVGDocumentWrapper->StartAnimation();
   1.439 +  return NS_OK;
   1.440 +}
   1.441 +
   1.442 +nsresult
   1.443 +VectorImage::StopAnimation()
   1.444 +{
   1.445 +  nsresult rv = NS_OK;
   1.446 +  if (mError) {
   1.447 +    rv = NS_ERROR_FAILURE;
   1.448 +  } else {
   1.449 +    MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations,
   1.450 +               "Should not have been animating!");
   1.451 +
   1.452 +    mSVGDocumentWrapper->StopAnimation();
   1.453 +  }
   1.454 +
   1.455 +  mAnimating = false;
   1.456 +  return rv;
   1.457 +}
   1.458 +
   1.459 +bool
   1.460 +VectorImage::ShouldAnimate()
   1.461 +{
   1.462 +  return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
   1.463 +}
   1.464 +
   1.465 +NS_IMETHODIMP_(void)
   1.466 +VectorImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
   1.467 +{
   1.468 +  // We don't care about animation start time.
   1.469 +}
   1.470 +
   1.471 +//------------------------------------------------------------------------------
   1.472 +// imgIContainer methods
   1.473 +
   1.474 +//******************************************************************************
   1.475 +/* readonly attribute int32_t width; */
   1.476 +NS_IMETHODIMP
   1.477 +VectorImage::GetWidth(int32_t* aWidth)
   1.478 +{
   1.479 +  if (mError || !mIsFullyLoaded) {
   1.480 +    *aWidth = 0;
   1.481 +    return NS_ERROR_FAILURE;
   1.482 +  }
   1.483 +
   1.484 +  if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
   1.485 +                                             *aWidth)) {
   1.486 +    *aWidth = 0;
   1.487 +    return NS_ERROR_FAILURE;
   1.488 +  }
   1.489 +
   1.490 +  return NS_OK;
   1.491 +}
   1.492 +
   1.493 +//******************************************************************************
   1.494 +/* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
   1.495 +NS_IMETHODIMP_(void)
   1.496 +VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
   1.497 +{
   1.498 +  // TODO: Implement for b666446.
   1.499 +  EvaluateAnimation();
   1.500 +
   1.501 +  if (mHasPendingInvalidation) {
   1.502 +    SendInvalidationNotifications();
   1.503 +    mHasPendingInvalidation = false;
   1.504 +  }
   1.505 +}
   1.506 +
   1.507 +void
   1.508 +VectorImage::SendInvalidationNotifications()
   1.509 +{
   1.510 +  // Animated images don't send out invalidation notifications as soon as
   1.511 +  // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick
   1.512 +  // records that there are pending invalidations and then returns immediately.
   1.513 +  // The notifications are actually sent from RequestRefresh(). We send these
   1.514 +  // notifications there to ensure that there is actually a document observing
   1.515 +  // us. Otherwise, the notifications are just wasted effort.
   1.516 +  //
   1.517 +  // Non-animated images call this method directly from
   1.518 +  // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
   1.519 +  // called for them. Ordinarily this isn't needed, since we send out
   1.520 +  // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
   1.521 +  // SVG document may not be 100% ready to render at that time. In those cases
   1.522 +  // we would miss the subsequent invalidations if we didn't send out the
   1.523 +  // notifications directly in |InvalidateObservers...|.
   1.524 +
   1.525 +  if (mStatusTracker) {
   1.526 +    SurfaceCache::Discard(this);
   1.527 +    mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
   1.528 +    mStatusTracker->OnStopFrame();
   1.529 +  }
   1.530 +}
   1.531 +
   1.532 +//******************************************************************************
   1.533 +/* readonly attribute int32_t height; */
   1.534 +NS_IMETHODIMP
   1.535 +VectorImage::GetHeight(int32_t* aHeight)
   1.536 +{
   1.537 +  if (mError || !mIsFullyLoaded) {
   1.538 +    *aHeight = 0;
   1.539 +    return NS_ERROR_FAILURE;
   1.540 +  }
   1.541 +
   1.542 +  if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
   1.543 +                                             *aHeight)) {
   1.544 +    *aHeight = 0;
   1.545 +    return NS_ERROR_FAILURE;
   1.546 +  }
   1.547 +
   1.548 +  return NS_OK;
   1.549 +}
   1.550 +
   1.551 +//******************************************************************************
   1.552 +/* [noscript] readonly attribute nsSize intrinsicSize; */
   1.553 +NS_IMETHODIMP
   1.554 +VectorImage::GetIntrinsicSize(nsSize* aSize)
   1.555 +{
   1.556 +  if (mError || !mIsFullyLoaded)
   1.557 +    return NS_ERROR_FAILURE;
   1.558 +
   1.559 +  nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
   1.560 +  if (!rootFrame)
   1.561 +    return NS_ERROR_FAILURE;
   1.562 +
   1.563 +  *aSize = nsSize(-1, -1);
   1.564 +  IntrinsicSize rfSize = rootFrame->GetIntrinsicSize();
   1.565 +  if (rfSize.width.GetUnit() == eStyleUnit_Coord)
   1.566 +    aSize->width = rfSize.width.GetCoordValue();
   1.567 +  if (rfSize.height.GetUnit() == eStyleUnit_Coord)
   1.568 +    aSize->height = rfSize.height.GetCoordValue();
   1.569 +
   1.570 +  return NS_OK;
   1.571 +}
   1.572 +
   1.573 +//******************************************************************************
   1.574 +/* [noscript] readonly attribute nsSize intrinsicRatio; */
   1.575 +NS_IMETHODIMP
   1.576 +VectorImage::GetIntrinsicRatio(nsSize* aRatio)
   1.577 +{
   1.578 +  if (mError || !mIsFullyLoaded)
   1.579 +    return NS_ERROR_FAILURE;
   1.580 +
   1.581 +  nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
   1.582 +  if (!rootFrame)
   1.583 +    return NS_ERROR_FAILURE;
   1.584 +
   1.585 +  *aRatio = rootFrame->GetIntrinsicRatio();
   1.586 +  return NS_OK;
   1.587 +}
   1.588 +
   1.589 +NS_IMETHODIMP_(Orientation)
   1.590 +VectorImage::GetOrientation()
   1.591 +{
   1.592 +  return Orientation();
   1.593 +}
   1.594 +
   1.595 +//******************************************************************************
   1.596 +/* readonly attribute unsigned short type; */
   1.597 +NS_IMETHODIMP
   1.598 +VectorImage::GetType(uint16_t* aType)
   1.599 +{
   1.600 +  NS_ENSURE_ARG_POINTER(aType);
   1.601 +
   1.602 +  *aType = GetType();
   1.603 +  return NS_OK;
   1.604 +}
   1.605 +
   1.606 +//******************************************************************************
   1.607 +/* [noscript, notxpcom] uint16_t GetType(); */
   1.608 +NS_IMETHODIMP_(uint16_t)
   1.609 +VectorImage::GetType()
   1.610 +{
   1.611 +  return imgIContainer::TYPE_VECTOR;
   1.612 +}
   1.613 +
   1.614 +//******************************************************************************
   1.615 +/* readonly attribute boolean animated; */
   1.616 +NS_IMETHODIMP
   1.617 +VectorImage::GetAnimated(bool* aAnimated)
   1.618 +{
   1.619 +  if (mError || !mIsFullyLoaded)
   1.620 +    return NS_ERROR_FAILURE;
   1.621 +
   1.622 +  *aAnimated = mSVGDocumentWrapper->IsAnimated();
   1.623 +  return NS_OK;
   1.624 +}
   1.625 +
   1.626 +//******************************************************************************
   1.627 +/* [notxpcom] int32_t getFirstFrameDelay (); */
   1.628 +int32_t
   1.629 +VectorImage::GetFirstFrameDelay()
   1.630 +{
   1.631 +  if (mError)
   1.632 +    return -1;
   1.633 +
   1.634 +  if (!mSVGDocumentWrapper->IsAnimated())
   1.635 +    return -1;
   1.636 +
   1.637 +  // We don't really have a frame delay, so just pretend that we constantly
   1.638 +  // need updates.
   1.639 +  return 0;
   1.640 +}
   1.641 +
   1.642 +
   1.643 +//******************************************************************************
   1.644 +/* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
   1.645 +NS_IMETHODIMP_(bool)
   1.646 +VectorImage::FrameIsOpaque(uint32_t aWhichFrame)
   1.647 +{
   1.648 +  if (aWhichFrame > FRAME_MAX_VALUE)
   1.649 +    NS_WARNING("aWhichFrame outside valid range!");
   1.650 +
   1.651 +  return false; // In general, SVG content is not opaque.
   1.652 +}
   1.653 +
   1.654 +//******************************************************************************
   1.655 +/* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
   1.656 + *                                   in uint32_t aFlags; */
   1.657 +NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
   1.658 +VectorImage::GetFrame(uint32_t aWhichFrame,
   1.659 +                      uint32_t aFlags)
   1.660 +{
   1.661 +  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
   1.662 +
   1.663 +  if (aWhichFrame > FRAME_MAX_VALUE)
   1.664 +    return nullptr;
   1.665 +
   1.666 +  if (mError)
   1.667 +    return nullptr;
   1.668 +
   1.669 +  // Look up height & width
   1.670 +  // ----------------------
   1.671 +  nsIntSize imageIntSize;
   1.672 +  if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
   1.673 +                                             imageIntSize.width) ||
   1.674 +      !mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
   1.675 +                                             imageIntSize.height)) {
   1.676 +    // We'll get here if our SVG doc has a percent-valued width or height.
   1.677 +    return nullptr;
   1.678 +  }
   1.679 +
   1.680 +  // Make our surface the size of what will ultimately be drawn to it.
   1.681 +  // (either the full image size, or the restricted region)
   1.682 +  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
   1.683 +    CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width,
   1.684 +                                             imageIntSize.height),
   1.685 +                                     SurfaceFormat::B8G8R8A8);
   1.686 +  nsRefPtr<gfxContext> context = new gfxContext(dt);
   1.687 +
   1.688 +  nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
   1.689 +                     gfxRect(gfxPoint(0,0), gfxIntSize(imageIntSize.width,
   1.690 +                                                       imageIntSize.height)),
   1.691 +                     nsIntRect(nsIntPoint(0,0), imageIntSize),
   1.692 +                     imageIntSize, nullptr, aWhichFrame, aFlags);
   1.693 +
   1.694 +  NS_ENSURE_SUCCESS(rv, nullptr);
   1.695 +  return dt->Snapshot();
   1.696 +}
   1.697 +
   1.698 +//******************************************************************************
   1.699 +/* [noscript] ImageContainer getImageContainer(); */
   1.700 +NS_IMETHODIMP
   1.701 +VectorImage::GetImageContainer(LayerManager* aManager,
   1.702 +                               mozilla::layers::ImageContainer** _retval)
   1.703 +{
   1.704 +  *_retval = nullptr;
   1.705 +  return NS_OK;
   1.706 +}
   1.707 +
   1.708 +struct SVGDrawingParameters
   1.709 +{
   1.710 +  SVGDrawingParameters(gfxContext* aContext,
   1.711 +                       GraphicsFilter aFilter,
   1.712 +                       const gfxMatrix& aUserSpaceToImageSpace,
   1.713 +                       const gfxRect& aFill,
   1.714 +                       const nsIntRect& aSubimage,
   1.715 +                       const nsIntSize& aViewportSize,
   1.716 +                       const SVGImageContext* aSVGContext,
   1.717 +                       float aAnimationTime,
   1.718 +                       uint32_t aFlags)
   1.719 +    : context(aContext)
   1.720 +    , filter(aFilter)
   1.721 +    , fill(aFill)
   1.722 +    , viewportSize(aViewportSize)
   1.723 +    , animationTime(aAnimationTime)
   1.724 +    , svgContext(aSVGContext)
   1.725 +    , flags(aFlags)
   1.726 +  {
   1.727 +    // gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface
   1.728 +    // if we hit the tiling path. Unfortunately, the temporary surface isn't
   1.729 +    // created at the size at which we'll ultimately draw, causing fuzzy output.
   1.730 +    // To fix this we pre-apply the transform's scaling to the drawing parameters
   1.731 +    // and remove the scaling from the transform, so the fact that temporary
   1.732 +    // surfaces won't take the scaling into account doesn't matter. (Bug 600207.)
   1.733 +    scale = aUserSpaceToImageSpace.ScaleFactors(true);
   1.734 +    gfxPoint translation(aUserSpaceToImageSpace.GetTranslation());
   1.735 +
   1.736 +    // Remove the scaling from the transform.
   1.737 +    gfxMatrix unscale;
   1.738 +    unscale.Translate(gfxPoint(translation.x / scale.width,
   1.739 +                               translation.y / scale.height));
   1.740 +    unscale.Scale(1.0 / scale.width, 1.0 / scale.height);
   1.741 +    unscale.Translate(-translation);
   1.742 +    userSpaceToImageSpace = aUserSpaceToImageSpace * unscale;
   1.743 +
   1.744 +    // Rescale drawing parameters.
   1.745 +    IntSize drawableSize(aViewportSize.width / scale.width,
   1.746 +                         aViewportSize.height / scale.height);
   1.747 +    sourceRect = userSpaceToImageSpace.Transform(aFill);
   1.748 +    imageRect = IntRect(IntPoint(0, 0), drawableSize);
   1.749 +    subimage = gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
   1.750 +    subimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height);
   1.751 +  }
   1.752 +
   1.753 +  gfxContext* context;
   1.754 +  GraphicsFilter filter;
   1.755 +  gfxMatrix userSpaceToImageSpace;
   1.756 +  gfxRect fill;
   1.757 +  gfxRect subimage;
   1.758 +  gfxRect sourceRect;
   1.759 +  IntRect imageRect;
   1.760 +  nsIntSize viewportSize;
   1.761 +  gfxSize scale;
   1.762 +  float animationTime;
   1.763 +  const SVGImageContext* svgContext;
   1.764 +  uint32_t flags;
   1.765 +};
   1.766 +
   1.767 +//******************************************************************************
   1.768 +/* [noscript] void draw(in gfxContext aContext,
   1.769 + *                      in gfxGraphicsFilter aFilter,
   1.770 + *                      [const] in gfxMatrix aUserSpaceToImageSpace,
   1.771 + *                      [const] in gfxRect aFill,
   1.772 + *                      [const] in nsIntRect aSubimage,
   1.773 + *                      [const] in nsIntSize aViewportSize,
   1.774 + *                      [const] in SVGImageContext aSVGContext,
   1.775 + *                      in uint32_t aWhichFrame,
   1.776 + *                      in uint32_t aFlags); */
   1.777 +NS_IMETHODIMP
   1.778 +VectorImage::Draw(gfxContext* aContext,
   1.779 +                  GraphicsFilter aFilter,
   1.780 +                  const gfxMatrix& aUserSpaceToImageSpace,
   1.781 +                  const gfxRect& aFill,
   1.782 +                  const nsIntRect& aSubimage,
   1.783 +                  const nsIntSize& aViewportSize,
   1.784 +                  const SVGImageContext* aSVGContext,
   1.785 +                  uint32_t aWhichFrame,
   1.786 +                  uint32_t aFlags)
   1.787 +{
   1.788 +  if (aWhichFrame > FRAME_MAX_VALUE)
   1.789 +    return NS_ERROR_INVALID_ARG;
   1.790 +
   1.791 +  NS_ENSURE_ARG_POINTER(aContext);
   1.792 +  if (mError || !mIsFullyLoaded)
   1.793 +    return NS_ERROR_FAILURE;
   1.794 +
   1.795 +  if (mIsDrawing) {
   1.796 +    NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
   1.797 +    return NS_ERROR_FAILURE;
   1.798 +  }
   1.799 +
   1.800 +  if (mAnimationConsumers == 0 && mStatusTracker) {
   1.801 +    mStatusTracker->OnUnlockedDraw();
   1.802 +  }
   1.803 +
   1.804 +  AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
   1.805 +  mIsDrawing = true;
   1.806 +
   1.807 +  float animTime = (aWhichFrame == FRAME_FIRST) ? 0.0f
   1.808 +                                                : mSVGDocumentWrapper->GetCurrentTime();
   1.809 +  AutoSVGRenderingState autoSVGState(aSVGContext, animTime,
   1.810 +                                     mSVGDocumentWrapper->GetRootSVGElem());
   1.811 +
   1.812 +  // Pack up the drawing parameters.
   1.813 +  SVGDrawingParameters params(aContext, aFilter, aUserSpaceToImageSpace, aFill,
   1.814 +                              aSubimage, aViewportSize, aSVGContext, animTime, aFlags);
   1.815 +
   1.816 +  // Check the cache.
   1.817 +  nsRefPtr<gfxDrawable> drawable =
   1.818 +    SurfaceCache::Lookup(ImageKey(this),
   1.819 +                         SurfaceKey(params.imageRect.Size(), params.scale,
   1.820 +                                    aSVGContext, animTime, aFlags));
   1.821 +
   1.822 +  // Draw.
   1.823 +  if (drawable) {
   1.824 +    Show(drawable, params);
   1.825 +  } else {
   1.826 +    CreateDrawableAndShow(params);
   1.827 +  }
   1.828 +
   1.829 +  return NS_OK;
   1.830 +}
   1.831 +
   1.832 +void
   1.833 +VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
   1.834 +{
   1.835 +  mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   1.836 +  mSVGDocumentWrapper->FlushImageTransformInvalidation();
   1.837 +
   1.838 +  nsRefPtr<gfxDrawingCallback> cb =
   1.839 +    new SVGDrawingCallback(mSVGDocumentWrapper,
   1.840 +                           nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
   1.841 +                           aParams.scale,
   1.842 +                           aParams.flags);
   1.843 +
   1.844 +  nsRefPtr<gfxDrawable> svgDrawable =
   1.845 +    new gfxCallbackDrawable(cb, ThebesIntSize(aParams.imageRect.Size()));
   1.846 +
   1.847 +  // Refuse to cache animated images.
   1.848 +  // XXX(seth): We may remove this restriction in bug 922893.
   1.849 +  if (mHaveAnimations)
   1.850 +    return Show(svgDrawable, aParams);
   1.851 +
   1.852 +  // If the image is too big to fit in the cache, don't go any further.
   1.853 +  if (!SurfaceCache::CanHold(aParams.imageRect.Size()))
   1.854 +    return Show(svgDrawable, aParams);
   1.855 +
   1.856 +  // Try to create an offscreen surface.
   1.857 +  mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
   1.858 +   gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8);
   1.859 +
   1.860 +  // If we couldn't create the draw target, it was probably because it would end
   1.861 +  // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   1.862 +  // could be set such that the cache isn't the limiting factor.
   1.863 +  if (!target)
   1.864 +    return Show(svgDrawable, aParams);
   1.865 +
   1.866 +  nsRefPtr<gfxContext> ctx = new gfxContext(target);
   1.867 +
   1.868 +  // Actually draw. (We use FILTER_NEAREST since we never scale here.)
   1.869 +  gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
   1.870 +                             ThebesIntRect(aParams.imageRect),
   1.871 +                             ThebesIntRect(aParams.imageRect),
   1.872 +                             ThebesIntRect(aParams.imageRect),
   1.873 +                             ThebesIntRect(aParams.imageRect),
   1.874 +                             gfxImageFormat::ARGB32,
   1.875 +                             GraphicsFilter::FILTER_NEAREST, aParams.flags);
   1.876 +
   1.877 +  // Attempt to cache the resulting surface.
   1.878 +  SurfaceCache::Insert(target,
   1.879 +                       ImageKey(this),
   1.880 +                       SurfaceKey(aParams.imageRect.Size(), aParams.scale,
   1.881 +                                  aParams.svgContext, aParams.animationTime,
   1.882 +                                  aParams.flags));
   1.883 +
   1.884 +  // Draw. Note that if SurfaceCache::Insert failed for whatever reason,
   1.885 +  // then |target| is all that is keeping the pixel data alive, so we have
   1.886 +  // to draw before returning from this function.
   1.887 +  nsRefPtr<gfxDrawable> drawable =
   1.888 +    new gfxSurfaceDrawable(target, ThebesIntSize(aParams.imageRect.Size()));
   1.889 +  Show(drawable, aParams);
   1.890 +}
   1.891 +
   1.892 +
   1.893 +void
   1.894 +VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
   1.895 +{
   1.896 +  MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
   1.897 +  gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
   1.898 +                             aParams.userSpaceToImageSpace,
   1.899 +                             aParams.subimage, aParams.sourceRect,
   1.900 +                             ThebesIntRect(aParams.imageRect), aParams.fill,
   1.901 +                             gfxImageFormat::ARGB32,
   1.902 +                             aParams.filter, aParams.flags);
   1.903 +
   1.904 +  MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
   1.905 +  mRenderingObserver->ResumeHonoringInvalidations();
   1.906 +}
   1.907 +
   1.908 +//******************************************************************************
   1.909 +/* void requestDecode() */
   1.910 +NS_IMETHODIMP
   1.911 +VectorImage::RequestDecode()
   1.912 +{
   1.913 +  // Nothing to do for SVG images
   1.914 +  return NS_OK;
   1.915 +}
   1.916 +
   1.917 +NS_IMETHODIMP
   1.918 +VectorImage::StartDecoding()
   1.919 +{
   1.920 +  // Nothing to do for SVG images
   1.921 +  return NS_OK;
   1.922 +}
   1.923 +
   1.924 +bool
   1.925 +VectorImage::IsDecoded()
   1.926 +{
   1.927 +  return mIsFullyLoaded || mError;
   1.928 +}
   1.929 +
   1.930 +//******************************************************************************
   1.931 +/* void lockImage() */
   1.932 +NS_IMETHODIMP
   1.933 +VectorImage::LockImage()
   1.934 +{
   1.935 +  // This method is for image-discarding, which only applies to RasterImages.
   1.936 +  return NS_OK;
   1.937 +}
   1.938 +
   1.939 +//******************************************************************************
   1.940 +/* void unlockImage() */
   1.941 +NS_IMETHODIMP
   1.942 +VectorImage::UnlockImage()
   1.943 +{
   1.944 +  // This method is for image-discarding, which only applies to RasterImages.
   1.945 +  return NS_OK;
   1.946 +}
   1.947 +
   1.948 +//******************************************************************************
   1.949 +/* void requestDiscard() */
   1.950 +NS_IMETHODIMP
   1.951 +VectorImage::RequestDiscard()
   1.952 +{
   1.953 +  SurfaceCache::Discard(this);
   1.954 +  return NS_OK;
   1.955 +}
   1.956 +
   1.957 +//******************************************************************************
   1.958 +/* void resetAnimation (); */
   1.959 +NS_IMETHODIMP
   1.960 +VectorImage::ResetAnimation()
   1.961 +{
   1.962 +  if (mError)
   1.963 +    return NS_ERROR_FAILURE;
   1.964 +
   1.965 +  if (!mIsFullyLoaded || !mHaveAnimations) {
   1.966 +    return NS_OK; // There are no animations to be reset.
   1.967 +  }
   1.968 +
   1.969 +  mSVGDocumentWrapper->ResetAnimation();
   1.970 +
   1.971 +  return NS_OK;
   1.972 +}
   1.973 +
   1.974 +NS_IMETHODIMP_(float)
   1.975 +VectorImage::GetFrameIndex(uint32_t aWhichFrame)
   1.976 +{
   1.977 +  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
   1.978 +  return aWhichFrame == FRAME_FIRST
   1.979 +         ? 0.0f
   1.980 +         : mSVGDocumentWrapper->GetCurrentTime();
   1.981 +}
   1.982 +
   1.983 +//------------------------------------------------------------------------------
   1.984 +// nsIRequestObserver methods
   1.985 +
   1.986 +//******************************************************************************
   1.987 +/* void onStartRequest(in nsIRequest request, in nsISupports ctxt); */
   1.988 +NS_IMETHODIMP
   1.989 +VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
   1.990 +{
   1.991 +  MOZ_ASSERT(!mSVGDocumentWrapper,
   1.992 +             "Repeated call to OnStartRequest -- can this happen?");
   1.993 +
   1.994 +  mSVGDocumentWrapper = new SVGDocumentWrapper();
   1.995 +  nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt);
   1.996 +  if (NS_FAILED(rv)) {
   1.997 +    mSVGDocumentWrapper = nullptr;
   1.998 +    mError = true;
   1.999 +    return rv;
  1.1000 +  }
  1.1001 +
  1.1002 +  // Sending StartDecode will block page load until the document's ready.  (We
  1.1003 +  // unblock it by sending StopDecode in OnSVGDocumentLoaded or
  1.1004 +  // OnSVGDocumentError.)
  1.1005 +  if (mStatusTracker) {
  1.1006 +    nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
  1.1007 +    imgDecoderObserver* observer = clone->GetDecoderObserver();
  1.1008 +    observer->OnStartDecode();
  1.1009 +    ImageStatusDiff diff = mStatusTracker->Difference(clone);
  1.1010 +    mStatusTracker->ApplyDifference(diff);
  1.1011 +    mStatusTracker->SyncNotifyDifference(diff);
  1.1012 +  }
  1.1013 +
  1.1014 +  // Create a listener to wait until the SVG document is fully loaded, which
  1.1015 +  // will signal that this image is ready to render. Certain error conditions
  1.1016 +  // will prevent us from ever getting this notification, so we also create a
  1.1017 +  // listener that waits for parsing to complete and cancels the
  1.1018 +  // SVGLoadEventListener if needed. The listeners are automatically attached
  1.1019 +  // to the document by their constructors.
  1.1020 +  nsIDocument* document = mSVGDocumentWrapper->GetDocument();
  1.1021 +  mLoadEventListener = new SVGLoadEventListener(document, this);
  1.1022 +  mParseCompleteListener = new SVGParseCompleteListener(document, this);
  1.1023 +
  1.1024 +  return NS_OK;
  1.1025 +}
  1.1026 +
  1.1027 +//******************************************************************************
  1.1028 +/* void onStopRequest(in nsIRequest request, in nsISupports ctxt,
  1.1029 +                      in nsresult status); */
  1.1030 +NS_IMETHODIMP
  1.1031 +VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
  1.1032 +                           nsresult aStatus)
  1.1033 +{
  1.1034 +  if (mError)
  1.1035 +    return NS_ERROR_FAILURE;
  1.1036 +
  1.1037 +  return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus);
  1.1038 +}
  1.1039 +
  1.1040 +void
  1.1041 +VectorImage::OnSVGDocumentParsed()
  1.1042 +{
  1.1043 +  MOZ_ASSERT(mParseCompleteListener, "Should have the parse complete listener");
  1.1044 +  MOZ_ASSERT(mLoadEventListener, "Should have the load event listener");
  1.1045 +
  1.1046 +  if (!mSVGDocumentWrapper->GetRootSVGElem()) {
  1.1047 +    // This is an invalid SVG document. It may have failed to parse, or it may
  1.1048 +    // be missing the <svg> root element, or the <svg> root element may not
  1.1049 +    // declare the correct namespace. In any of these cases, we'll never be
  1.1050 +    // notified that the SVG finished loading, so we need to treat this as an error.
  1.1051 +    OnSVGDocumentError();
  1.1052 +  }
  1.1053 +}
  1.1054 +
  1.1055 +void
  1.1056 +VectorImage::CancelAllListeners()
  1.1057 +{
  1.1058 +  if (mParseCompleteListener) {
  1.1059 +    mParseCompleteListener->Cancel();
  1.1060 +    mParseCompleteListener = nullptr;
  1.1061 +  }
  1.1062 +  if (mLoadEventListener) {
  1.1063 +    mLoadEventListener->Cancel();
  1.1064 +    mLoadEventListener = nullptr;
  1.1065 +  }
  1.1066 +}
  1.1067 +
  1.1068 +void
  1.1069 +VectorImage::OnSVGDocumentLoaded()
  1.1070 +{
  1.1071 +  MOZ_ASSERT(mSVGDocumentWrapper->GetRootSVGElem(),
  1.1072 +             "Should have parsed successfully");
  1.1073 +  MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations,
  1.1074 +             "These flags shouldn't get set until OnSVGDocumentLoaded. "
  1.1075 +             "Duplicate calls to OnSVGDocumentLoaded?");
  1.1076 +
  1.1077 +  CancelAllListeners();
  1.1078 +
  1.1079 +  // XXX Flushing is wasteful if embedding frame hasn't had initial reflow.
  1.1080 +  mSVGDocumentWrapper->FlushLayout();
  1.1081 +
  1.1082 +  mIsFullyLoaded = true;
  1.1083 +  mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
  1.1084 +
  1.1085 +  // Start listening to our image for rendering updates.
  1.1086 +  mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
  1.1087 +
  1.1088 +  // Tell *our* observers that we're done loading.
  1.1089 +  if (mStatusTracker) {
  1.1090 +    nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
  1.1091 +    imgDecoderObserver* observer = clone->GetDecoderObserver();
  1.1092 +
  1.1093 +    observer->OnStartContainer(); // Signal that width/height are available.
  1.1094 +    observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
  1.1095 +    observer->OnStopFrame();
  1.1096 +    observer->OnStopDecode(NS_OK); // Unblock page load.
  1.1097 +
  1.1098 +    ImageStatusDiff diff = mStatusTracker->Difference(clone);
  1.1099 +    mStatusTracker->ApplyDifference(diff);
  1.1100 +    mStatusTracker->SyncNotifyDifference(diff);
  1.1101 +  }
  1.1102 +
  1.1103 +  EvaluateAnimation();
  1.1104 +}
  1.1105 +
  1.1106 +void
  1.1107 +VectorImage::OnSVGDocumentError()
  1.1108 +{
  1.1109 +  CancelAllListeners();
  1.1110 +
  1.1111 +  // XXXdholbert Need to do something more for the parsing failed case -- right
  1.1112 +  // now, this just makes us draw the "object" icon, rather than the (jagged)
  1.1113 +  // "broken image" icon.  See bug 594505.
  1.1114 +  mError = true;
  1.1115 +
  1.1116 +  if (mStatusTracker) {
  1.1117 +    nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
  1.1118 +    imgDecoderObserver* observer = clone->GetDecoderObserver();
  1.1119 +
  1.1120 +    // Unblock page load.
  1.1121 +    observer->OnStopDecode(NS_ERROR_FAILURE);
  1.1122 +    ImageStatusDiff diff = mStatusTracker->Difference(clone);
  1.1123 +    mStatusTracker->ApplyDifference(diff);
  1.1124 +    mStatusTracker->SyncNotifyDifference(diff);
  1.1125 +  }
  1.1126 +}
  1.1127 +
  1.1128 +//------------------------------------------------------------------------------
  1.1129 +// nsIStreamListener method
  1.1130 +
  1.1131 +//******************************************************************************
  1.1132 +/* void onDataAvailable(in nsIRequest request, in nsISupports ctxt,
  1.1133 +                        in nsIInputStream inStr, in unsigned long sourceOffset,
  1.1134 +                        in unsigned long count); */
  1.1135 +NS_IMETHODIMP
  1.1136 +VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
  1.1137 +                             nsIInputStream* aInStr, uint64_t aSourceOffset,
  1.1138 +                             uint32_t aCount)
  1.1139 +{
  1.1140 +  if (mError)
  1.1141 +    return NS_ERROR_FAILURE;
  1.1142 +
  1.1143 +  return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
  1.1144 +                                              aSourceOffset, aCount);
  1.1145 +}
  1.1146 +
  1.1147 +// --------------------------
  1.1148 +// Invalidation helper method
  1.1149 +
  1.1150 +void
  1.1151 +VectorImage::InvalidateObserversOnNextRefreshDriverTick()
  1.1152 +{
  1.1153 +  if (mHaveAnimations) {
  1.1154 +    mHasPendingInvalidation = true;
  1.1155 +  } else {
  1.1156 +    SendInvalidationNotifications();
  1.1157 +  }
  1.1158 +}
  1.1159 +
  1.1160 +} // namespace image
  1.1161 +} // namespace mozilla

mercurial