Coverage Report

Created: 2018-09-25 14:53

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