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 #ifndef mozilla_image_Image_h
7 #define mozilla_image_Image_h
8 
9 #include "mozilla/Maybe.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/ThreadSafeWeakPtr.h"
12 #include "mozilla/TimeStamp.h"
13 #include "mozilla/Tuple.h"
14 #include "gfx2DGlue.h"
15 #include "imgIContainer.h"
16 #include "ImageContainer.h"
17 #include "LookupResult.h"
18 #include "nsStringFwd.h"
19 #include "ProgressTracker.h"
20 #include "SurfaceCache.h"
21 
22 class imgRequest;
23 class nsIRequest;
24 class nsIInputStream;
25 
26 namespace mozilla {
27 namespace image {
28 
29 class Image;
30 
31 ///////////////////////////////////////////////////////////////////////////////
32 // Memory Reporting
33 ///////////////////////////////////////////////////////////////////////////////
34 
35 struct MemoryCounter {
MemoryCounterMemoryCounter36   MemoryCounter()
37       : mSource(0),
38         mDecodedHeap(0),
39         mDecodedNonHeap(0),
40         mDecodedUnknown(0),
41         mExternalHandles(0),
42         mFrameIndex(0),
43         mExternalId(0),
44         mSurfaceTypes(0) {}
45 
SetSourceMemoryCounter46   void SetSource(size_t aCount) { mSource = aCount; }
SourceMemoryCounter47   size_t Source() const { return mSource; }
SetDecodedHeapMemoryCounter48   void SetDecodedHeap(size_t aCount) { mDecodedHeap = aCount; }
DecodedHeapMemoryCounter49   size_t DecodedHeap() const { return mDecodedHeap; }
SetDecodedNonHeapMemoryCounter50   void SetDecodedNonHeap(size_t aCount) { mDecodedNonHeap = aCount; }
DecodedNonHeapMemoryCounter51   size_t DecodedNonHeap() const { return mDecodedNonHeap; }
SetDecodedUnknownMemoryCounter52   void SetDecodedUnknown(size_t aCount) { mDecodedUnknown = aCount; }
DecodedUnknownMemoryCounter53   size_t DecodedUnknown() const { return mDecodedUnknown; }
SetExternalHandlesMemoryCounter54   void SetExternalHandles(size_t aCount) { mExternalHandles = aCount; }
ExternalHandlesMemoryCounter55   size_t ExternalHandles() const { return mExternalHandles; }
SetFrameIndexMemoryCounter56   void SetFrameIndex(size_t aIndex) { mFrameIndex = aIndex; }
FrameIndexMemoryCounter57   size_t FrameIndex() const { return mFrameIndex; }
SetExternalIdMemoryCounter58   void SetExternalId(uint64_t aId) { mExternalId = aId; }
ExternalIdMemoryCounter59   uint64_t ExternalId() const { return mExternalId; }
SetSurfaceTypesMemoryCounter60   void SetSurfaceTypes(uint32_t aTypes) { mSurfaceTypes = aTypes; }
SurfaceTypesMemoryCounter61   uint32_t SurfaceTypes() const { return mSurfaceTypes; }
62 
63   MemoryCounter& operator+=(const MemoryCounter& aOther) {
64     mSource += aOther.mSource;
65     mDecodedHeap += aOther.mDecodedHeap;
66     mDecodedNonHeap += aOther.mDecodedNonHeap;
67     mDecodedUnknown += aOther.mDecodedUnknown;
68     mExternalHandles += aOther.mExternalHandles;
69     mSurfaceTypes |= aOther.mSurfaceTypes;
70     return *this;
71   }
72 
73  private:
74   size_t mSource;
75   size_t mDecodedHeap;
76   size_t mDecodedNonHeap;
77   size_t mDecodedUnknown;
78   size_t mExternalHandles;
79   size_t mFrameIndex;
80   uint64_t mExternalId;
81   uint32_t mSurfaceTypes;
82 };
83 
84 enum class SurfaceMemoryCounterType { NORMAL, COMPOSITING, COMPOSITING_PREV };
85 
86 struct SurfaceMemoryCounter {
87   SurfaceMemoryCounter(
88       const SurfaceKey& aKey, bool aIsLocked, bool aCannotSubstitute,
89       bool aIsFactor2, bool aFinished,
90       SurfaceMemoryCounterType aType = SurfaceMemoryCounterType::NORMAL)
mKeySurfaceMemoryCounter91       : mKey(aKey),
92         mType(aType),
93         mIsLocked(aIsLocked),
94         mCannotSubstitute(aCannotSubstitute),
95         mIsFactor2(aIsFactor2),
96         mFinished(aFinished) {}
97 
KeySurfaceMemoryCounter98   const SurfaceKey& Key() const { return mKey; }
ValuesSurfaceMemoryCounter99   MemoryCounter& Values() { return mValues; }
ValuesSurfaceMemoryCounter100   const MemoryCounter& Values() const { return mValues; }
TypeSurfaceMemoryCounter101   SurfaceMemoryCounterType Type() const { return mType; }
IsLockedSurfaceMemoryCounter102   bool IsLocked() const { return mIsLocked; }
CannotSubstituteSurfaceMemoryCounter103   bool CannotSubstitute() const { return mCannotSubstitute; }
IsFactor2SurfaceMemoryCounter104   bool IsFactor2() const { return mIsFactor2; }
IsFinishedSurfaceMemoryCounter105   bool IsFinished() const { return mFinished; }
106 
107  private:
108   const SurfaceKey mKey;
109   MemoryCounter mValues;
110   const SurfaceMemoryCounterType mType;
111   const bool mIsLocked;
112   const bool mCannotSubstitute;
113   const bool mIsFactor2;
114   const bool mFinished;
115 };
116 
117 struct ImageMemoryCounter {
118   ImageMemoryCounter(imgRequest* aRequest, SizeOfState& aState, bool aIsUsed);
119   ImageMemoryCounter(imgRequest* aRequest, Image* aImage, SizeOfState& aState,
120                      bool aIsUsed);
121 
URIImageMemoryCounter122   nsCString& URI() { return mURI; }
URIImageMemoryCounter123   const nsCString& URI() const { return mURI; }
SurfacesImageMemoryCounter124   const nsTArray<SurfaceMemoryCounter>& Surfaces() const { return mSurfaces; }
IntrinsicSizeImageMemoryCounter125   const gfx::IntSize IntrinsicSize() const { return mIntrinsicSize; }
ValuesImageMemoryCounter126   const MemoryCounter& Values() const { return mValues; }
ProgressImageMemoryCounter127   uint32_t Progress() const { return mProgress; }
TypeImageMemoryCounter128   uint16_t Type() const { return mType; }
IsUsedImageMemoryCounter129   bool IsUsed() const { return mIsUsed; }
HasErrorImageMemoryCounter130   bool HasError() const { return mHasError; }
IsValidatingImageMemoryCounter131   bool IsValidating() const { return mValidating; }
132 
IsNotableImageMemoryCounter133   bool IsNotable() const {
134     // Errors or requests without images are always notable.
135     if (mHasError || mValidating || mProgress == UINT32_MAX ||
136         mProgress & FLAG_HAS_ERROR || mType == imgIContainer::TYPE_REQUEST) {
137       return true;
138     }
139 
140     // Sufficiently large images are notable.
141     const size_t NotableThreshold = 16 * 1024;
142     size_t total = mValues.Source() + mValues.DecodedHeap() +
143                    mValues.DecodedNonHeap() + mValues.DecodedUnknown();
144     if (total >= NotableThreshold) {
145       return true;
146     }
147 
148     // Incomplete images are always notable as well; the odds of capturing
149     // mid-decode should be fairly low.
150     for (const auto& surface : mSurfaces) {
151       if (!surface.IsFinished()) {
152         return true;
153       }
154     }
155 
156     return false;
157   }
158 
159  private:
160   nsCString mURI;
161   nsTArray<SurfaceMemoryCounter> mSurfaces;
162   gfx::IntSize mIntrinsicSize;
163   MemoryCounter mValues;
164   uint32_t mProgress;
165   uint16_t mType;
166   const bool mIsUsed;
167   bool mHasError;
168   bool mValidating;
169 };
170 
171 ///////////////////////////////////////////////////////////////////////////////
172 // Image Base Types
173 ///////////////////////////////////////////////////////////////////////////////
174 
175 class Image : public imgIContainer {
176  public:
177   /**
178    * Flags for Image initialization.
179    *
180    * Meanings:
181    *
182    * INIT_FLAG_NONE: Lack of flags
183    *
184    * INIT_FLAG_DISCARDABLE: The container should be discardable
185    *
186    * INIT_FLAG_DECODE_IMMEDIATELY: The container should decode as soon as
187    * possible, regardless of what our heuristics say.
188    *
189    * INIT_FLAG_TRANSIENT: The container is likely to exist for only a short time
190    * before being destroyed. (For example, containers for
191    * multipart/x-mixed-replace image parts fall into this category.) If this
192    * flag is set, INIT_FLAG_DISCARDABLE and INIT_FLAG_DECODE_ONLY_ON_DRAW must
193    * not be set.
194    *
195    * INIT_FLAG_SYNC_LOAD: The container is being loaded synchronously, so
196    * it should avoid relying on async workers to get the container ready.
197    */
198   static const uint32_t INIT_FLAG_NONE = 0x0;
199   static const uint32_t INIT_FLAG_DISCARDABLE = 0x1;
200   static const uint32_t INIT_FLAG_DECODE_IMMEDIATELY = 0x2;
201   static const uint32_t INIT_FLAG_TRANSIENT = 0x4;
202   static const uint32_t INIT_FLAG_SYNC_LOAD = 0x8;
203 
204   virtual already_AddRefed<ProgressTracker> GetProgressTracker() = 0;
SetProgressTracker(ProgressTracker * aProgressTracker)205   virtual void SetProgressTracker(ProgressTracker* aProgressTracker) {}
206 
207   /**
208    * The size, in bytes, occupied by the compressed source data of the image.
209    * If MallocSizeOf does not work on this platform, uses a fallback approach to
210    * ensure that something reasonable is always returned.
211    */
212   virtual size_t SizeOfSourceWithComputedFallback(
213       SizeOfState& aState) const = 0;
214 
215   /**
216    * Collect an accounting of the memory occupied by the image's surfaces (which
217    * together make up its decoded data). Each surface is recorded as a separate
218    * SurfaceMemoryCounter, stored in @aCounters.
219    */
220   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
221                                      MallocSizeOf aMallocSizeOf) const = 0;
222 
223   virtual void IncrementAnimationConsumers() = 0;
224   virtual void DecrementAnimationConsumers() = 0;
225 #ifdef DEBUG
226   virtual uint32_t GetAnimationConsumers() = 0;
227 #endif
228 
229   /**
230    * Called from OnDataAvailable when the stream associated with the image has
231    * received new image data. The arguments are the same as OnDataAvailable's,
232    * but by separating this functionality into a different method we don't
233    * interfere with subclasses which wish to implement nsIStreamListener.
234    *
235    * Images should not do anything that could send out notifications until they
236    * have received their first OnImageDataAvailable notification; in
237    * particular, this means that instantiating decoders should be deferred
238    * until OnImageDataAvailable is called.
239    */
240   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
241                                         nsISupports* aContext,
242                                         nsIInputStream* aInStr,
243                                         uint64_t aSourceOffset,
244                                         uint32_t aCount) = 0;
245 
246   /**
247    * Called from OnStopRequest when the image's underlying request completes.
248    *
249    * @param aRequest  The completed request.
250    * @param aContext  Context from Necko's OnStopRequest.
251    * @param aStatus   A success or failure code.
252    * @param aLastPart Whether this is the final part of the underlying request.
253    */
254   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
255                                        nsISupports* aContext, nsresult aStatus,
256                                        bool aLastPart) = 0;
257 
258   /**
259    * Called when the SurfaceCache discards a surface belonging to this image.
260    */
261   virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) = 0;
262 
263   virtual void SetInnerWindowID(uint64_t aInnerWindowId) = 0;
264   virtual uint64_t InnerWindowID() const = 0;
265 
266   virtual bool HasError() = 0;
267   virtual void SetHasError() = 0;
268 
269   virtual nsIURI* GetURI() const = 0;
270 
GetHotspotX(int32_t * aX)271   NS_IMETHOD GetHotspotX(int32_t* aX) override {
272     *aX = 0;
273     return NS_OK;
274   }
GetHotspotY(int32_t * aY)275   NS_IMETHOD GetHotspotY(int32_t* aY) override {
276     *aY = 0;
277     return NS_OK;
278   }
279 };
280 
281 class ImageResource : public Image {
282  public:
GetProgressTracker()283   already_AddRefed<ProgressTracker> GetProgressTracker() override {
284     RefPtr<ProgressTracker> progressTracker = mProgressTracker;
285     MOZ_ASSERT(progressTracker);
286     return progressTracker.forget();
287   }
288 
SetProgressTracker(ProgressTracker * aProgressTracker)289   void SetProgressTracker(ProgressTracker* aProgressTracker) final {
290     MOZ_ASSERT(aProgressTracker);
291     MOZ_ASSERT(!mProgressTracker);
292     mProgressTracker = aProgressTracker;
293   }
294 
295   virtual void IncrementAnimationConsumers() override;
296   virtual void DecrementAnimationConsumers() override;
297 #ifdef DEBUG
GetAnimationConsumers()298   virtual uint32_t GetAnimationConsumers() override {
299     return mAnimationConsumers;
300   }
301 #endif
302 
OnSurfaceDiscarded(const SurfaceKey & aSurfaceKey)303   virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override {}
304 
SetInnerWindowID(uint64_t aInnerWindowId)305   virtual void SetInnerWindowID(uint64_t aInnerWindowId) override {
306     mInnerWindowId = aInnerWindowId;
307   }
InnerWindowID()308   virtual uint64_t InnerWindowID() const override { return mInnerWindowId; }
309 
HasError()310   virtual bool HasError() override { return mError; }
SetHasError()311   virtual void SetHasError() override { mError = true; }
312 
313   /*
314    * Returns a non-AddRefed pointer to the URI associated with this image.
315    * Illegal to use off-main-thread.
316    */
GetURI()317   nsIURI* GetURI() const override { return mURI; }
318 
319  protected:
320   explicit ImageResource(nsIURI* aURI);
321   ~ImageResource();
322 
GetImageProducerId()323   layers::ContainerProducerID GetImageProducerId() const {
324     return mImageProducerID;
325   }
326 
327   bool GetSpecTruncatedTo1k(nsCString& aSpec) const;
328 
329   // Shared functionality for implementors of imgIContainer. Every
330   // implementation of attribute animationMode should forward here.
331   nsresult GetAnimationModeInternal(uint16_t* aAnimationMode);
332   nsresult SetAnimationModeInternal(uint16_t aAnimationMode);
333 
334   /**
335    * Helper for RequestRefresh.
336    *
337    * If we've had a "recent" refresh (i.e. if this image is being used in
338    * multiple documents & some other document *just* called RequestRefresh() on
339    * this image with a timestamp close to aTime), this method returns true.
340    *
341    * Otherwise, this method updates mLastRefreshTime to aTime & returns false.
342    */
343   bool HadRecentRefresh(const TimeStamp& aTime);
344 
345   /**
346    * Decides whether animation should or should not be happening,
347    * and makes sure the right thing is being done.
348    */
349   virtual void EvaluateAnimation();
350 
351   /**
352    * Extended by child classes, if they have additional
353    * conditions for being able to animate.
354    */
ShouldAnimate()355   virtual bool ShouldAnimate() {
356     return mAnimationConsumers > 0 && mAnimationMode != kDontAnimMode;
357   }
358 
359   virtual nsresult StartAnimation() = 0;
360   virtual nsresult StopAnimation() = 0;
361 
362   void SendOnUnlockedDraw(uint32_t aFlags);
363 
364 #ifdef DEBUG
365   // Records the image drawing for startup performance testing.
366   void NotifyDrawingObservers();
367 #endif
368 
369   // Member data shared by all implementations of this abstract class
370   RefPtr<ProgressTracker> mProgressTracker;
371   nsCOMPtr<nsIURI> mURI;
372   TimeStamp mLastRefreshTime;
373   uint64_t mInnerWindowId;
374   uint32_t mAnimationConsumers;
375   uint16_t mAnimationMode;  // Enum values in imgIContainer
376   bool mInitialized : 1;    // Have we been initialized?
377   bool mAnimating : 1;      // Are we currently animating?
378   bool mError : 1;          // Error handling
379 
380   /**
381    * Attempt to find a matching cached surface in the SurfaceCache, and if not
382    * available, request the production of such a surface (either synchronously
383    * or asynchronously).
384    *
385    * If the draw result is BAD_IMAGE, BAD_ARGS or NOT_READY, the size will be
386    * the same as aSize. If it is TEMPORARY_ERROR, INCOMPLETE, or SUCCESS, the
387    * size is a hint as to what we expect the surface size to be, once the best
388    * fitting size is available. It may or may not match the size of the surface
389    * returned at this moment. This is useful for choosing how to store the final
390    * result (e.g. if going into an ImageContainer, ideally we would share the
391    * same container for many requested sizes, if they all end up with the same
392    * best fit size in the end).
393    *
394    * A valid surface should only be returned for SUCCESS and INCOMPLETE.
395    *
396    * Any other draw result is invalid.
397    */
398   virtual Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
GetFrameInternal(const gfx::IntSize & aSize,const Maybe<SVGImageContext> & aSVGContext,uint32_t aWhichFrame,uint32_t aFlags)399   GetFrameInternal(const gfx::IntSize& aSize,
400                    const Maybe<SVGImageContext>& aSVGContext,
401                    uint32_t aWhichFrame, uint32_t aFlags) {
402     return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize,
403                      RefPtr<gfx::SourceSurface>());
404   }
405 
406   /**
407    * Calculate the estimated size to use for an image container with the given
408    * parameters. It may not be the same as the given size, and it may not be
409    * the same as the size of the surface in the image container, but it is the
410    * best effort estimate.
411    */
GetImageContainerSize(layers::LayerManager * aManager,const gfx::IntSize & aSize,uint32_t aFlags)412   virtual Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize(
413       layers::LayerManager* aManager, const gfx::IntSize& aSize,
414       uint32_t aFlags) {
415     return MakeTuple(ImgDrawResult::NOT_SUPPORTED, gfx::IntSize(0, 0));
416   }
417 
418   ImgDrawResult GetImageContainerImpl(layers::LayerManager* aManager,
419                                       const gfx::IntSize& aSize,
420                                       const Maybe<SVGImageContext>& aSVGContext,
421                                       uint32_t aFlags,
422                                       layers::ImageContainer** aContainer);
423 
424   /**
425    * Re-requests the appropriate frames for each image container using
426    * GetFrameInternal.
427    * @returns True if any image containers were updated, else false.
428    */
429   bool UpdateImageContainer(const Maybe<gfx::IntRect>& aDirtyRect);
430 
431   void ReleaseImageContainer();
432 
433  private:
434   void SetCurrentImage(layers::ImageContainer* aContainer,
435                        gfx::SourceSurface* aSurface,
436                        const Maybe<gfx::IntRect>& aDirtyRect);
437 
438   struct ImageContainerEntry {
ImageContainerEntryImageContainerEntry439     ImageContainerEntry(const gfx::IntSize& aSize,
440                         const Maybe<SVGImageContext>& aSVGContext,
441                         layers::ImageContainer* aContainer, uint32_t aFlags)
442         : mSize(aSize),
443           mSVGContext(aSVGContext),
444           mContainer(aContainer),
445           mLastDrawResult(ImgDrawResult::NOT_READY),
446           mFlags(aFlags) {}
447 
448     gfx::IntSize mSize;
449     Maybe<SVGImageContext> mSVGContext;
450     // A weak pointer to our ImageContainer, which stays alive only as long as
451     // the layer system needs it.
452     ThreadSafeWeakPtr<layers::ImageContainer> mContainer;
453     // If mContainer is non-null, this contains the ImgDrawResult we obtained
454     // the last time we updated it.
455     ImgDrawResult mLastDrawResult;
456     // Cached flags to use for decoding. FLAG_ASYNC_NOTIFY should always be set
457     // but FLAG_HIGH_QUALITY_SCALING may vary.
458     uint32_t mFlags;
459   };
460 
461   AutoTArray<ImageContainerEntry, 1> mImageContainers;
462   layers::ImageContainer::ProducerID mImageProducerID;
463   layers::ImageContainer::FrameID mLastFrameID;
464 };
465 
466 }  // namespace image
467 }  // namespace mozilla
468 
469 #endif  // mozilla_image_Image_h
470