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