image/src/VectorImage.cpp

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

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

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

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

mercurial