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 "Image.h"
7 
8 #include "imgRequest.h"
9 #include "Layers.h"  // for LayerManager
10 #include "WebRenderImageProvider.h"
11 #include "nsIObserverService.h"
12 #include "nsRefreshDriver.h"
13 #include "nsContentUtils.h"
14 #include "mozilla/Atomics.h"
15 #include "mozilla/gfx/Point.h"
16 #include "mozilla/gfx/Rect.h"
17 #include "mozilla/gfx/SourceSurfaceRawData.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/SizeOfState.h"
20 #include "mozilla/TimeStamp.h"
21 #include "mozilla/Tuple.h"  // for Tie
22 #include "mozilla/layers/SharedSurfacesChild.h"
23 
24 namespace mozilla {
25 namespace image {
26 
WebRenderImageProvider(const ImageResource * aImage)27 WebRenderImageProvider::WebRenderImageProvider(const ImageResource* aImage)
28     : mProviderId(aImage->GetImageProviderId()) {}
29 
AllocateProviderId()30 /* static */ ImageProviderId WebRenderImageProvider::AllocateProviderId() {
31   // Callable on all threads.
32   static Atomic<ImageProviderId> sProviderId(0u);
33   return ++sProviderId;
34 }
35 
36 ///////////////////////////////////////////////////////////////////////////////
37 // Memory Reporting
38 ///////////////////////////////////////////////////////////////////////////////
39 
ImageMemoryCounter(imgRequest * aRequest,SizeOfState & aState,bool aIsUsed)40 ImageMemoryCounter::ImageMemoryCounter(imgRequest* aRequest,
41                                        SizeOfState& aState, bool aIsUsed)
42     : mProgress(UINT32_MAX),
43       mType(UINT16_MAX),
44       mIsUsed(aIsUsed),
45       mHasError(false),
46       mValidating(false) {
47   MOZ_ASSERT(aRequest);
48 
49   // We don't have the image object yet, but we can get some information.
50   nsCOMPtr<nsIURI> imageURL;
51   nsresult rv = aRequest->GetURI(getter_AddRefs(imageURL));
52   if (NS_SUCCEEDED(rv) && imageURL) {
53     imageURL->GetSpec(mURI);
54   }
55 
56   mType = imgIContainer::TYPE_REQUEST;
57   mHasError = NS_FAILED(aRequest->GetImageErrorCode());
58   mValidating = !!aRequest->GetValidator();
59 
60   RefPtr<ProgressTracker> tracker = aRequest->GetProgressTracker();
61   if (tracker) {
62     mProgress = tracker->GetProgress();
63   }
64 }
65 
ImageMemoryCounter(imgRequest * aRequest,Image * aImage,SizeOfState & aState,bool aIsUsed)66 ImageMemoryCounter::ImageMemoryCounter(imgRequest* aRequest, Image* aImage,
67                                        SizeOfState& aState, bool aIsUsed)
68     : mProgress(UINT32_MAX),
69       mType(UINT16_MAX),
70       mIsUsed(aIsUsed),
71       mHasError(false),
72       mValidating(false) {
73   MOZ_ASSERT(aRequest);
74   MOZ_ASSERT(aImage);
75 
76   // Extract metadata about the image.
77   nsCOMPtr<nsIURI> imageURL(aImage->GetURI());
78   if (imageURL) {
79     imageURL->GetSpec(mURI);
80   }
81 
82   int32_t width = 0;
83   int32_t height = 0;
84   aImage->GetWidth(&width);
85   aImage->GetHeight(&height);
86   mIntrinsicSize.SizeTo(width, height);
87 
88   mType = aImage->GetType();
89   mHasError = aImage->HasError();
90   mValidating = !!aRequest->GetValidator();
91 
92   RefPtr<ProgressTracker> tracker = aImage->GetProgressTracker();
93   if (tracker) {
94     mProgress = tracker->GetProgress();
95   }
96 
97   // Populate memory counters for source and decoded data.
98   mValues.SetSource(aImage->SizeOfSourceWithComputedFallback(aState));
99   aImage->CollectSizeOfSurfaces(mSurfaces, aState.mMallocSizeOf);
100 
101   // Compute totals.
102   for (const SurfaceMemoryCounter& surfaceCounter : mSurfaces) {
103     mValues += surfaceCounter.Values();
104   }
105 }
106 
107 ///////////////////////////////////////////////////////////////////////////////
108 // Image Base Types
109 ///////////////////////////////////////////////////////////////////////////////
110 
GetSpecTruncatedTo1k(nsCString & aSpec) const111 bool ImageResource::GetSpecTruncatedTo1k(nsCString& aSpec) const {
112   static const size_t sMaxTruncatedLength = 1024;
113 
114   mURI->GetSpec(aSpec);
115   if (sMaxTruncatedLength >= aSpec.Length()) {
116     return true;
117   }
118 
119   aSpec.Truncate(sMaxTruncatedLength);
120   return false;
121 }
122 
CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter> & aCounters,MallocSizeOf aMallocSizeOf) const123 void ImageResource::CollectSizeOfSurfaces(
124     nsTArray<SurfaceMemoryCounter>& aCounters,
125     MallocSizeOf aMallocSizeOf) const {
126   SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
127 }
128 
129 // Constructor
ImageResource(nsIURI * aURI)130 ImageResource::ImageResource(nsIURI* aURI)
131     : mURI(aURI),
132       mInnerWindowId(0),
133       mAnimationConsumers(0),
134       mAnimationMode(kNormalAnimMode),
135       mInitialized(false),
136       mAnimating(false),
137       mError(false),
138       mProviderId(WebRenderImageProvider::AllocateProviderId()) {}
139 
~ImageResource()140 ImageResource::~ImageResource() {
141   // Ask our ProgressTracker to drop its weak reference to us.
142   mProgressTracker->ResetImage();
143 }
144 
IncrementAnimationConsumers()145 void ImageResource::IncrementAnimationConsumers() {
146   MOZ_ASSERT(NS_IsMainThread(),
147              "Main thread only to encourage serialization "
148              "with DecrementAnimationConsumers");
149   mAnimationConsumers++;
150 }
151 
DecrementAnimationConsumers()152 void ImageResource::DecrementAnimationConsumers() {
153   MOZ_ASSERT(NS_IsMainThread(),
154              "Main thread only to encourage serialization "
155              "with IncrementAnimationConsumers");
156   MOZ_ASSERT(mAnimationConsumers >= 1, "Invalid no. of animation consumers!");
157   mAnimationConsumers--;
158 }
159 
GetAnimationModeInternal(uint16_t * aAnimationMode)160 nsresult ImageResource::GetAnimationModeInternal(uint16_t* aAnimationMode) {
161   if (mError) {
162     return NS_ERROR_FAILURE;
163   }
164 
165   NS_ENSURE_ARG_POINTER(aAnimationMode);
166 
167   *aAnimationMode = mAnimationMode;
168   return NS_OK;
169 }
170 
SetAnimationModeInternal(uint16_t aAnimationMode)171 nsresult ImageResource::SetAnimationModeInternal(uint16_t aAnimationMode) {
172   if (mError) {
173     return NS_ERROR_FAILURE;
174   }
175 
176   NS_ASSERTION(aAnimationMode == kNormalAnimMode ||
177                    aAnimationMode == kDontAnimMode ||
178                    aAnimationMode == kLoopOnceAnimMode,
179                "Wrong Animation Mode is being set!");
180 
181   mAnimationMode = aAnimationMode;
182 
183   return NS_OK;
184 }
185 
HadRecentRefresh(const TimeStamp & aTime)186 bool ImageResource::HadRecentRefresh(const TimeStamp& aTime) {
187   // Our threshold for "recent" is 1/2 of the default refresh-driver interval.
188   // This ensures that we allow for frame rates at least as fast as the
189   // refresh driver's default rate.
190   static TimeDuration recentThreshold =
191       TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval() / 2.0);
192 
193   if (!mLastRefreshTime.IsNull() &&
194       aTime - mLastRefreshTime < recentThreshold) {
195     return true;
196   }
197 
198   // else, we can proceed with a refresh.
199   // But first, update our last refresh time:
200   mLastRefreshTime = aTime;
201   return false;
202 }
203 
EvaluateAnimation()204 void ImageResource::EvaluateAnimation() {
205   if (!mAnimating && ShouldAnimate()) {
206     nsresult rv = StartAnimation();
207     mAnimating = NS_SUCCEEDED(rv);
208   } else if (mAnimating && !ShouldAnimate()) {
209     StopAnimation();
210   }
211 }
212 
SendOnUnlockedDraw(uint32_t aFlags)213 void ImageResource::SendOnUnlockedDraw(uint32_t aFlags) {
214   if (!mProgressTracker) {
215     return;
216   }
217 
218   if (!(aFlags & FLAG_ASYNC_NOTIFY)) {
219     mProgressTracker->OnUnlockedDraw();
220   } else {
221     NotNull<RefPtr<ImageResource>> image = WrapNotNull(this);
222     nsCOMPtr<nsIEventTarget> eventTarget = mProgressTracker->GetEventTarget();
223     nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction(
224         "image::ImageResource::SendOnUnlockedDraw", [=]() -> void {
225           RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
226           if (tracker) {
227             tracker->OnUnlockedDraw();
228           }
229         });
230     eventTarget->Dispatch(CreateRenderBlockingRunnable(ev.forget()),
231                           NS_DISPATCH_NORMAL);
232   }
233 }
234 
235 #ifdef DEBUG
NotifyDrawingObservers()236 void ImageResource::NotifyDrawingObservers() {
237   if (!mURI || !NS_IsMainThread()) {
238     return;
239   }
240 
241   if (!mURI->SchemeIs("resource") && !mURI->SchemeIs("chrome")) {
242     return;
243   }
244 
245   // Record the image drawing for startup performance testing.
246   nsCOMPtr<nsIURI> uri = mURI;
247   nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
248       "image::ImageResource::NotifyDrawingObservers", [uri]() {
249         nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
250         NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
251         if (obs) {
252           nsAutoCString spec;
253           uri->GetSpec(spec);
254           obs->NotifyObservers(nullptr, "image-drawing",
255                                NS_ConvertUTF8toUTF16(spec).get());
256         }
257       }));
258 }
259 #endif
260 
261 }  // namespace image
262 }  // namespace mozilla
263