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