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