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