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