Bug 1698786: part 1) Add some logging to `mozInlineSpellChecker`. r=masayuki
[gecko.git] / image / Image.cpp
blob77d1e329a680d226b75d398615983cf81e48e8b8
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/. */
6 #include "Image.h"
8 #include "imgRequest.h"
9 #include "Layers.h" // for LayerManager
10 #include "nsIObserverService.h"
11 #include "nsRefreshDriver.h"
12 #include "nsContentUtils.h"
13 #include "mozilla/gfx/Point.h"
14 #include "mozilla/gfx/Rect.h"
15 #include "mozilla/Services.h"
16 #include "mozilla/SizeOfState.h"
17 #include "mozilla/TimeStamp.h"
18 #include "mozilla/Tuple.h" // for Tie
19 #include "mozilla/layers/SharedSurfacesChild.h"
21 namespace mozilla {
22 namespace image {
24 ///////////////////////////////////////////////////////////////////////////////
25 // Memory Reporting
26 ///////////////////////////////////////////////////////////////////////////////
28 ImageMemoryCounter::ImageMemoryCounter(imgRequest* aRequest,
29 SizeOfState& aState, bool aIsUsed)
30 : mProgress(UINT32_MAX),
31 mType(UINT16_MAX),
32 mIsUsed(aIsUsed),
33 mHasError(false),
34 mValidating(false) {
35 MOZ_ASSERT(aRequest);
37 // We don't have the image object yet, but we can get some information.
38 nsCOMPtr<nsIURI> imageURL;
39 nsresult rv = aRequest->GetURI(getter_AddRefs(imageURL));
40 if (NS_SUCCEEDED(rv) && imageURL) {
41 imageURL->GetSpec(mURI);
44 mType = imgIContainer::TYPE_REQUEST;
45 mHasError = NS_FAILED(aRequest->GetImageErrorCode());
46 mValidating = !!aRequest->GetValidator();
48 RefPtr<ProgressTracker> tracker = aRequest->GetProgressTracker();
49 if (tracker) {
50 mProgress = tracker->GetProgress();
54 ImageMemoryCounter::ImageMemoryCounter(imgRequest* aRequest, Image* aImage,
55 SizeOfState& aState, bool aIsUsed)
56 : mProgress(UINT32_MAX),
57 mType(UINT16_MAX),
58 mIsUsed(aIsUsed),
59 mHasError(false),
60 mValidating(false) {
61 MOZ_ASSERT(aRequest);
62 MOZ_ASSERT(aImage);
64 // Extract metadata about the image.
65 nsCOMPtr<nsIURI> imageURL(aImage->GetURI());
66 if (imageURL) {
67 imageURL->GetSpec(mURI);
70 int32_t width = 0;
71 int32_t height = 0;
72 aImage->GetWidth(&width);
73 aImage->GetHeight(&height);
74 mIntrinsicSize.SizeTo(width, height);
76 mType = aImage->GetType();
77 mHasError = aImage->HasError();
78 mValidating = !!aRequest->GetValidator();
80 RefPtr<ProgressTracker> tracker = aImage->GetProgressTracker();
81 if (tracker) {
82 mProgress = tracker->GetProgress();
85 // Populate memory counters for source and decoded data.
86 mValues.SetSource(aImage->SizeOfSourceWithComputedFallback(aState));
87 aImage->CollectSizeOfSurfaces(mSurfaces, aState.mMallocSizeOf);
89 // Compute totals.
90 for (const SurfaceMemoryCounter& surfaceCounter : mSurfaces) {
91 mValues += surfaceCounter.Values();
95 ///////////////////////////////////////////////////////////////////////////////
96 // Image Base Types
97 ///////////////////////////////////////////////////////////////////////////////
99 bool ImageResource::GetSpecTruncatedTo1k(nsCString& aSpec) const {
100 static const size_t sMaxTruncatedLength = 1024;
102 mURI->GetSpec(aSpec);
103 if (sMaxTruncatedLength >= aSpec.Length()) {
104 return true;
107 aSpec.Truncate(sMaxTruncatedLength);
108 return false;
111 void ImageResource::SetCurrentImage(layers::ImageContainer* aContainer,
112 gfx::SourceSurface* aSurface,
113 const Maybe<gfx::IntRect>& aDirtyRect) {
114 MOZ_ASSERT(NS_IsMainThread());
115 MOZ_ASSERT(aContainer);
117 if (!aSurface) {
118 // The OS threw out some or all of our buffer. We'll need to wait for the
119 // redecode (which was automatically triggered by GetFrame) to complete.
120 return;
123 // |image| holds a reference to a SourceSurface which in turn holds a lock on
124 // the current frame's data buffer, ensuring that it doesn't get freed as
125 // long as the layer system keeps this ImageContainer alive.
126 RefPtr<layers::Image> image = new layers::SourceSurfaceImage(aSurface);
128 // We can share the producer ID with other containers because it is only
129 // used internally to validate the frames given to a particular container
130 // so that another object cannot add its own. Similarly the frame ID is
131 // only used internally to ensure it is always increasing, and skipping
132 // IDs from an individual container's perspective is acceptable.
133 AutoTArray<layers::ImageContainer::NonOwningImage, 1> imageList;
134 imageList.AppendElement(layers::ImageContainer::NonOwningImage(
135 image, TimeStamp(), mLastFrameID++, mImageProducerID));
137 if (aDirtyRect) {
138 aContainer->SetCurrentImagesInTransaction(imageList);
139 } else {
140 aContainer->SetCurrentImages(imageList);
143 // If we are animated, then we should request that the image container be
144 // treated as such, to avoid display list rebuilding to update frames for
145 // WebRender.
146 if (mProgressTracker->GetProgress() & FLAG_IS_ANIMATED) {
147 if (aDirtyRect) {
148 layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface,
149 aDirtyRect.ref());
150 } else {
151 gfx::IntRect dirtyRect(gfx::IntPoint(0, 0), aSurface->GetSize());
152 layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface,
153 dirtyRect);
158 ImgDrawResult ImageResource::GetImageContainerImpl(
159 layers::LayerManager* aManager, const gfx::IntSize& aSize,
160 const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
161 layers::ImageContainer** aOutContainer) {
162 MOZ_ASSERT(NS_IsMainThread());
163 MOZ_ASSERT(aManager);
164 MOZ_ASSERT(
165 (aFlags & ~(FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST |
166 FLAG_ASYNC_NOTIFY | FLAG_HIGH_QUALITY_SCALING)) == FLAG_NONE,
167 "Unsupported flag passed to GetImageContainer");
169 ImgDrawResult drawResult;
170 gfx::IntSize size;
171 Tie(drawResult, size) = GetImageContainerSize(aManager, aSize, aFlags);
172 if (drawResult != ImgDrawResult::SUCCESS) {
173 return drawResult;
176 MOZ_ASSERT(!size.IsEmpty());
178 if (mAnimationConsumers == 0) {
179 SendOnUnlockedDraw(aFlags);
182 uint32_t flags = (aFlags & ~(FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST)) |
183 FLAG_ASYNC_NOTIFY;
184 RefPtr<layers::ImageContainer> container;
185 ImageContainerEntry* entry = nullptr;
186 int i = mImageContainers.Length() - 1;
187 for (; i >= 0; --i) {
188 entry = &mImageContainers[i];
189 if (size == entry->mSize && flags == entry->mFlags &&
190 aSVGContext == entry->mSVGContext) {
191 // Lack of a container is handled below.
192 container = RefPtr<layers::ImageContainer>(entry->mContainer);
193 break;
194 } else if (entry->mContainer.IsDead()) {
195 // Stop tracking if our weak pointer to the image container was freed.
196 mImageContainers.RemoveElementAt(i);
200 if (container) {
201 switch (entry->mLastDrawResult) {
202 case ImgDrawResult::SUCCESS:
203 case ImgDrawResult::BAD_IMAGE:
204 case ImgDrawResult::BAD_ARGS:
205 case ImgDrawResult::NOT_SUPPORTED:
206 container.forget(aOutContainer);
207 return entry->mLastDrawResult;
208 case ImgDrawResult::NOT_READY:
209 case ImgDrawResult::INCOMPLETE:
210 case ImgDrawResult::TEMPORARY_ERROR:
211 // Temporary conditions where we need to rerequest the frame to recover.
212 break;
213 case ImgDrawResult::WRONG_SIZE:
214 // Unused by GetFrameInternal
215 default:
216 MOZ_ASSERT_UNREACHABLE("Unhandled ImgDrawResult type!");
217 container.forget(aOutContainer);
218 return entry->mLastDrawResult;
222 #ifdef DEBUG
223 NotifyDrawingObservers();
224 #endif
226 gfx::IntSize bestSize;
227 RefPtr<gfx::SourceSurface> surface;
228 Tie(drawResult, bestSize, surface) = GetFrameInternal(
229 size, aSVGContext, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
231 // The requested size might be refused by the surface cache (i.e. due to
232 // factor-of-2 mode). In that case we don't want to create an entry for this
233 // specific size, but rather re-use the entry for the substituted size.
234 if (bestSize != size) {
235 MOZ_ASSERT(!bestSize.IsEmpty());
237 // We can only remove the entry if we no longer have a container, because if
238 // there are strong references to it remaining, we need to still update it
239 // in UpdateImageContainer.
240 if (i >= 0 && !container) {
241 mImageContainers.RemoveElementAt(i);
244 // Forget about the stale container, if any. This lets the entry creation
245 // logic do its job below, if it turns out there is no existing best entry
246 // or the best entry doesn't have a container.
247 container = nullptr;
249 // We need to do the entry search again for the new size. We skip pruning
250 // because we did this above once already, but ImageContainer is threadsafe,
251 // so there is a remote possibility it got freed.
252 i = mImageContainers.Length() - 1;
253 for (; i >= 0; --i) {
254 entry = &mImageContainers[i];
255 if (bestSize == entry->mSize && flags == entry->mFlags &&
256 aSVGContext == entry->mSVGContext) {
257 container = RefPtr<layers::ImageContainer>(entry->mContainer);
258 if (container) {
259 switch (entry->mLastDrawResult) {
260 case ImgDrawResult::SUCCESS:
261 case ImgDrawResult::BAD_IMAGE:
262 case ImgDrawResult::BAD_ARGS:
263 case ImgDrawResult::NOT_SUPPORTED:
264 container.forget(aOutContainer);
265 return entry->mLastDrawResult;
266 case ImgDrawResult::NOT_READY:
267 case ImgDrawResult::INCOMPLETE:
268 case ImgDrawResult::TEMPORARY_ERROR:
269 // Temporary conditions where we need to rerequest the frame to
270 // recover. We have already done so!
271 break;
272 case ImgDrawResult::WRONG_SIZE:
273 // Unused by GetFrameInternal
274 default:
275 MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!");
276 container.forget(aOutContainer);
277 return entry->mLastDrawResult;
280 break;
285 if (!container) {
286 // We need a new ImageContainer, so create one.
287 container = layers::LayerManager::CreateImageContainer();
289 if (i >= 0) {
290 entry->mContainer = container;
291 } else {
292 entry = mImageContainers.AppendElement(
293 ImageContainerEntry(bestSize, aSVGContext, container.get(), flags));
297 SetCurrentImage(container, surface, Nothing());
298 entry->mLastDrawResult = drawResult;
299 container.forget(aOutContainer);
300 return drawResult;
303 bool ImageResource::UpdateImageContainer(
304 const Maybe<gfx::IntRect>& aDirtyRect) {
305 MOZ_ASSERT(NS_IsMainThread());
307 for (int i = mImageContainers.Length() - 1; i >= 0; --i) {
308 ImageContainerEntry& entry = mImageContainers[i];
309 RefPtr<layers::ImageContainer> container(entry.mContainer);
310 if (container) {
311 gfx::IntSize bestSize;
312 RefPtr<gfx::SourceSurface> surface;
313 Tie(entry.mLastDrawResult, bestSize, surface) = GetFrameInternal(
314 entry.mSize, entry.mSVGContext, FRAME_CURRENT, entry.mFlags);
316 // It is possible that this is a factor-of-2 substitution. Since we
317 // managed to convert the weak reference into a strong reference, that
318 // means that an imagelib user still is holding onto the container. thus
319 // we cannot consolidate and must keep updating the duplicate container.
320 if (aDirtyRect) {
321 SetCurrentImage(container, surface, aDirtyRect);
322 } else {
323 gfx::IntRect dirtyRect(gfx::IntPoint(0, 0), bestSize);
324 SetCurrentImage(container, surface, Some(dirtyRect));
326 } else {
327 // Stop tracking if our weak pointer to the image container was freed.
328 mImageContainers.RemoveElementAt(i);
332 return !mImageContainers.IsEmpty();
335 void ImageResource::ReleaseImageContainer() {
336 MOZ_ASSERT(NS_IsMainThread());
337 mImageContainers.Clear();
340 // Constructor
341 ImageResource::ImageResource(nsIURI* aURI)
342 : mURI(aURI),
343 mInnerWindowId(0),
344 mAnimationConsumers(0),
345 mAnimationMode(kNormalAnimMode),
346 mInitialized(false),
347 mAnimating(false),
348 mError(false),
349 mImageProducerID(layers::ImageContainer::AllocateProducerID()),
350 mLastFrameID(0) {}
352 ImageResource::~ImageResource() {
353 // Ask our ProgressTracker to drop its weak reference to us.
354 mProgressTracker->ResetImage();
357 void ImageResource::IncrementAnimationConsumers() {
358 MOZ_ASSERT(NS_IsMainThread(),
359 "Main thread only to encourage serialization "
360 "with DecrementAnimationConsumers");
361 mAnimationConsumers++;
364 void ImageResource::DecrementAnimationConsumers() {
365 MOZ_ASSERT(NS_IsMainThread(),
366 "Main thread only to encourage serialization "
367 "with IncrementAnimationConsumers");
368 MOZ_ASSERT(mAnimationConsumers >= 1, "Invalid no. of animation consumers!");
369 mAnimationConsumers--;
372 nsresult ImageResource::GetAnimationModeInternal(uint16_t* aAnimationMode) {
373 if (mError) {
374 return NS_ERROR_FAILURE;
377 NS_ENSURE_ARG_POINTER(aAnimationMode);
379 *aAnimationMode = mAnimationMode;
380 return NS_OK;
383 nsresult ImageResource::SetAnimationModeInternal(uint16_t aAnimationMode) {
384 if (mError) {
385 return NS_ERROR_FAILURE;
388 NS_ASSERTION(aAnimationMode == kNormalAnimMode ||
389 aAnimationMode == kDontAnimMode ||
390 aAnimationMode == kLoopOnceAnimMode,
391 "Wrong Animation Mode is being set!");
393 mAnimationMode = aAnimationMode;
395 return NS_OK;
398 bool ImageResource::HadRecentRefresh(const TimeStamp& aTime) {
399 // Our threshold for "recent" is 1/2 of the default refresh-driver interval.
400 // This ensures that we allow for frame rates at least as fast as the
401 // refresh driver's default rate.
402 static TimeDuration recentThreshold =
403 TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval() / 2.0);
405 if (!mLastRefreshTime.IsNull() &&
406 aTime - mLastRefreshTime < recentThreshold) {
407 return true;
410 // else, we can proceed with a refresh.
411 // But first, update our last refresh time:
412 mLastRefreshTime = aTime;
413 return false;
416 void ImageResource::EvaluateAnimation() {
417 if (!mAnimating && ShouldAnimate()) {
418 nsresult rv = StartAnimation();
419 mAnimating = NS_SUCCEEDED(rv);
420 } else if (mAnimating && !ShouldAnimate()) {
421 StopAnimation();
425 void ImageResource::SendOnUnlockedDraw(uint32_t aFlags) {
426 if (!mProgressTracker) {
427 return;
430 if (!(aFlags & FLAG_ASYNC_NOTIFY)) {
431 mProgressTracker->OnUnlockedDraw();
432 } else {
433 NotNull<RefPtr<ImageResource>> image = WrapNotNull(this);
434 nsCOMPtr<nsIEventTarget> eventTarget = mProgressTracker->GetEventTarget();
435 nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction(
436 "image::ImageResource::SendOnUnlockedDraw", [=]() -> void {
437 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
438 if (tracker) {
439 tracker->OnUnlockedDraw();
442 eventTarget->Dispatch(CreateMediumHighRunnable(ev.forget()),
443 NS_DISPATCH_NORMAL);
447 #ifdef DEBUG
448 void ImageResource::NotifyDrawingObservers() {
449 if (!mURI || !NS_IsMainThread()) {
450 return;
453 if (!mURI->SchemeIs("resource") && !mURI->SchemeIs("chrome")) {
454 return;
457 // Record the image drawing for startup performance testing.
458 nsCOMPtr<nsIURI> uri = mURI;
459 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
460 "image::ImageResource::NotifyDrawingObservers", [uri]() {
461 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
462 NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
463 if (obs) {
464 nsAutoCString spec;
465 uri->GetSpec(spec);
466 obs->NotifyObservers(nullptr, "image-drawing",
467 NS_ConvertUTF8toUTF16(spec).get());
469 }));
471 #endif
473 } // namespace image
474 } // namespace mozilla