1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/imgStatusTracker.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1097 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "imgStatusTracker.h" 1.11 + 1.12 +#include "imgIContainer.h" 1.13 +#include "imgRequestProxy.h" 1.14 +#include "imgDecoderObserver.h" 1.15 +#include "Image.h" 1.16 +#include "ImageLogging.h" 1.17 +#include "nsNetUtil.h" 1.18 +#include "nsIObserverService.h" 1.19 + 1.20 +#include "mozilla/Assertions.h" 1.21 +#include "mozilla/Services.h" 1.22 + 1.23 +using namespace mozilla::image; 1.24 +using mozilla::WeakPtr; 1.25 + 1.26 +class imgStatusTrackerObserver : public imgDecoderObserver 1.27 +{ 1.28 +public: 1.29 + imgStatusTrackerObserver(imgStatusTracker* aTracker) 1.30 + : mTracker(aTracker->asWeakPtr()) 1.31 + { 1.32 + MOZ_ASSERT(aTracker); 1.33 + } 1.34 + 1.35 + void SetTracker(imgStatusTracker* aTracker) 1.36 + { 1.37 + MOZ_ASSERT(aTracker); 1.38 + mTracker = aTracker->asWeakPtr(); 1.39 + } 1.40 + 1.41 + /** imgDecoderObserver methods **/ 1.42 + 1.43 + virtual void OnStartDecode() MOZ_OVERRIDE 1.44 + { 1.45 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode"); 1.46 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.47 + if (!tracker) { return; } 1.48 + tracker->RecordStartDecode(); 1.49 + if (!tracker->IsMultipart()) { 1.50 + tracker->RecordBlockOnload(); 1.51 + } 1.52 + } 1.53 + 1.54 + virtual void OnStartRequest() MOZ_OVERRIDE 1.55 + { 1.56 + NS_NOTREACHED("imgStatusTrackerObserver(imgDecoderObserver)::OnStartRequest"); 1.57 + } 1.58 + 1.59 + virtual void OnStartContainer() MOZ_OVERRIDE 1.60 + { 1.61 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer"); 1.62 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.63 + if (!tracker) { return; } 1.64 + nsRefPtr<Image> image = tracker->GetImage();; 1.65 + tracker->RecordStartContainer(image); 1.66 + } 1.67 + 1.68 + virtual void OnStartFrame() MOZ_OVERRIDE 1.69 + { 1.70 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartFrame"); 1.71 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.72 + if (!tracker) { return; } 1.73 + tracker->RecordStartFrame(); 1.74 + } 1.75 + 1.76 + virtual void FrameChanged(const nsIntRect* dirtyRect) MOZ_OVERRIDE 1.77 + { 1.78 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged"); 1.79 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.80 + if (!tracker) { return; } 1.81 + tracker->RecordFrameChanged(dirtyRect); 1.82 + } 1.83 + 1.84 + virtual void OnStopFrame() MOZ_OVERRIDE 1.85 + { 1.86 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame"); 1.87 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.88 + if (!tracker) { return; } 1.89 + tracker->RecordStopFrame(); 1.90 + tracker->RecordUnblockOnload(); 1.91 + } 1.92 + 1.93 + virtual void OnStopDecode(nsresult aStatus) MOZ_OVERRIDE 1.94 + { 1.95 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode"); 1.96 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.97 + if (!tracker) { return; } 1.98 + tracker->RecordStopDecode(aStatus); 1.99 + 1.100 + // This is really hacky. We need to handle the case where we start decoding, 1.101 + // block onload, but then hit an error before we get to our first frame. 1.102 + tracker->RecordUnblockOnload(); 1.103 + } 1.104 + 1.105 + virtual void OnStopRequest(bool aLastPart, nsresult aStatus) MOZ_OVERRIDE 1.106 + { 1.107 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopRequest"); 1.108 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.109 + if (!tracker) { return; } 1.110 + tracker->RecordStopRequest(aLastPart, aStatus); 1.111 + } 1.112 + 1.113 + virtual void OnDiscard() MOZ_OVERRIDE 1.114 + { 1.115 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDiscard"); 1.116 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.117 + if (!tracker) { return; } 1.118 + tracker->RecordDiscard(); 1.119 + } 1.120 + 1.121 + virtual void OnUnlockedDraw() MOZ_OVERRIDE 1.122 + { 1.123 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw"); 1.124 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.125 + if (!tracker) { return; } 1.126 + NS_ABORT_IF_FALSE(tracker->HasImage(), 1.127 + "OnUnlockedDraw callback before we've created our image"); 1.128 + tracker->RecordUnlockedDraw(); 1.129 + } 1.130 + 1.131 + virtual void OnImageIsAnimated() MOZ_OVERRIDE 1.132 + { 1.133 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnImageIsAnimated"); 1.134 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.135 + if (!tracker) { return; } 1.136 + tracker->RecordImageIsAnimated(); 1.137 + } 1.138 + 1.139 + virtual void OnError() MOZ_OVERRIDE 1.140 + { 1.141 + LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnError"); 1.142 + nsRefPtr<imgStatusTracker> tracker = mTracker.get(); 1.143 + if (!tracker) { return; } 1.144 + tracker->RecordError(); 1.145 + } 1.146 + 1.147 +protected: 1.148 + virtual ~imgStatusTrackerObserver() {} 1.149 + 1.150 +private: 1.151 + WeakPtr<imgStatusTracker> mTracker; 1.152 +}; 1.153 + 1.154 +// imgStatusTracker methods 1.155 + 1.156 +imgStatusTracker::imgStatusTracker(Image* aImage) 1.157 + : mImage(aImage), 1.158 + mState(0), 1.159 + mImageStatus(imgIRequest::STATUS_NONE), 1.160 + mIsMultipart(false), 1.161 + mHadLastPart(false), 1.162 + mHasBeenDecoded(false) 1.163 +{ 1.164 + mTrackerObserver = new imgStatusTrackerObserver(this); 1.165 +} 1.166 + 1.167 +// Private, used only by CloneForRecording. 1.168 +imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther) 1.169 + : mImage(aOther.mImage), 1.170 + mState(aOther.mState), 1.171 + mImageStatus(aOther.mImageStatus), 1.172 + mIsMultipart(aOther.mIsMultipart), 1.173 + mHadLastPart(aOther.mHadLastPart), 1.174 + mHasBeenDecoded(aOther.mHasBeenDecoded) 1.175 + // Note: we explicitly don't copy several fields: 1.176 + // - mRequestRunnable, because it won't be nulled out when the 1.177 + // mRequestRunnable's Run function eventually gets called. 1.178 + // - mProperties, because we don't need it and it'd just point at the same 1.179 + // object 1.180 + // - mConsumers, because we don't need to talk to consumers 1.181 + // - mInvalidRect, because the point of it is to be fired off and reset 1.182 +{ 1.183 + mTrackerObserver = new imgStatusTrackerObserver(this); 1.184 +} 1.185 + 1.186 +imgStatusTracker::~imgStatusTracker() 1.187 +{} 1.188 + 1.189 +imgStatusTrackerInit::imgStatusTrackerInit(mozilla::image::Image* aImage, 1.190 + imgStatusTracker* aTracker) 1.191 +{ 1.192 + MOZ_ASSERT(aImage); 1.193 + 1.194 + if (aTracker) { 1.195 + mTracker = aTracker; 1.196 + mTracker->SetImage(aImage); 1.197 + } else { 1.198 + mTracker = new imgStatusTracker(aImage); 1.199 + } 1.200 + aImage->SetStatusTracker(mTracker); 1.201 + MOZ_ASSERT(mTracker); 1.202 +} 1.203 + 1.204 +imgStatusTrackerInit::~imgStatusTrackerInit() 1.205 +{ 1.206 + mTracker->ResetImage(); 1.207 +} 1.208 + 1.209 +void 1.210 +imgStatusTracker::SetImage(Image* aImage) 1.211 +{ 1.212 + NS_ABORT_IF_FALSE(aImage, "Setting null image"); 1.213 + NS_ABORT_IF_FALSE(!mImage, "Setting image when we already have one"); 1.214 + mImage = aImage; 1.215 +} 1.216 + 1.217 +void 1.218 +imgStatusTracker::ResetImage() 1.219 +{ 1.220 + NS_ABORT_IF_FALSE(mImage, "Resetting image when it's already null!"); 1.221 + mImage = nullptr; 1.222 +} 1.223 + 1.224 +bool 1.225 +imgStatusTracker::IsLoading() const 1.226 +{ 1.227 + // Checking for whether OnStopRequest has fired allows us to say we're 1.228 + // loading before OnStartRequest gets called, letting the request properly 1.229 + // get removed from the cache in certain cases. 1.230 + return !(mState & stateRequestStopped); 1.231 +} 1.232 + 1.233 +uint32_t 1.234 +imgStatusTracker::GetImageStatus() const 1.235 +{ 1.236 + return mImageStatus; 1.237 +} 1.238 + 1.239 +// A helper class to allow us to call SyncNotify asynchronously. 1.240 +class imgRequestNotifyRunnable : public nsRunnable 1.241 +{ 1.242 + public: 1.243 + imgRequestNotifyRunnable(imgStatusTracker* aTracker, 1.244 + imgRequestProxy* aRequestProxy) 1.245 + : mTracker(aTracker) 1.246 + { 1.247 + MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread"); 1.248 + MOZ_ASSERT(aRequestProxy, "aRequestProxy should not be null"); 1.249 + MOZ_ASSERT(aTracker, "aTracker should not be null"); 1.250 + mProxies.AppendElement(aRequestProxy); 1.251 + } 1.252 + 1.253 + NS_IMETHOD Run() 1.254 + { 1.255 + MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); 1.256 + MOZ_ASSERT(mTracker, "mTracker should not be null"); 1.257 + for (uint32_t i = 0; i < mProxies.Length(); ++i) { 1.258 + mProxies[i]->SetNotificationsDeferred(false); 1.259 + mTracker->SyncNotify(mProxies[i]); 1.260 + } 1.261 + 1.262 + mTracker->mRequestRunnable = nullptr; 1.263 + return NS_OK; 1.264 + } 1.265 + 1.266 + void AddProxy(imgRequestProxy* aRequestProxy) 1.267 + { 1.268 + mProxies.AppendElement(aRequestProxy); 1.269 + } 1.270 + 1.271 + void RemoveProxy(imgRequestProxy* aRequestProxy) 1.272 + { 1.273 + mProxies.RemoveElement(aRequestProxy); 1.274 + } 1.275 + 1.276 + private: 1.277 + friend class imgStatusTracker; 1.278 + 1.279 + nsRefPtr<imgStatusTracker> mTracker; 1.280 + nsTArray< nsRefPtr<imgRequestProxy> > mProxies; 1.281 +}; 1.282 + 1.283 +void 1.284 +imgStatusTracker::Notify(imgRequestProxy* proxy) 1.285 +{ 1.286 + MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe"); 1.287 +#ifdef PR_LOGGING 1.288 + if (mImage && mImage->GetURI()) { 1.289 + nsRefPtr<ImageURL> uri(mImage->GetURI()); 1.290 + nsAutoCString spec; 1.291 + uri->GetSpec(spec); 1.292 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get()); 1.293 + } else { 1.294 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", "<unknown>"); 1.295 + } 1.296 +#endif 1.297 + 1.298 + proxy->SetNotificationsDeferred(true); 1.299 + 1.300 + // If we have an existing runnable that we can use, we just append this proxy 1.301 + // to its list of proxies to be notified. This ensures we don't unnecessarily 1.302 + // delay onload. 1.303 + imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get()); 1.304 + if (runnable) { 1.305 + runnable->AddProxy(proxy); 1.306 + } else { 1.307 + mRequestRunnable = new imgRequestNotifyRunnable(this, proxy); 1.308 + NS_DispatchToCurrentThread(mRequestRunnable); 1.309 + } 1.310 +} 1.311 + 1.312 +// A helper class to allow us to call SyncNotify asynchronously for a given, 1.313 +// fixed, state. 1.314 +class imgStatusNotifyRunnable : public nsRunnable 1.315 +{ 1.316 + public: 1.317 + imgStatusNotifyRunnable(imgStatusTracker* statusTracker, 1.318 + imgRequestProxy* requestproxy) 1.319 + : mStatusTracker(statusTracker), mProxy(requestproxy) 1.320 + { 1.321 + MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread"); 1.322 + MOZ_ASSERT(requestproxy, "requestproxy cannot be null"); 1.323 + MOZ_ASSERT(statusTracker, "status should not be null"); 1.324 + mImage = statusTracker->GetImage(); 1.325 + } 1.326 + 1.327 + NS_IMETHOD Run() 1.328 + { 1.329 + MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); 1.330 + mProxy->SetNotificationsDeferred(false); 1.331 + 1.332 + mStatusTracker->SyncNotify(mProxy); 1.333 + return NS_OK; 1.334 + } 1.335 + 1.336 + private: 1.337 + nsRefPtr<imgStatusTracker> mStatusTracker; 1.338 + // We have to hold on to a reference to the tracker's image, just in case 1.339 + // it goes away while we're in the event queue. 1.340 + nsRefPtr<Image> mImage; 1.341 + nsRefPtr<imgRequestProxy> mProxy; 1.342 +}; 1.343 + 1.344 +void 1.345 +imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy) 1.346 +{ 1.347 + MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe"); 1.348 +#ifdef PR_LOGGING 1.349 + nsRefPtr<ImageURL> uri; 1.350 + proxy->GetURI(getter_AddRefs(uri)); 1.351 + nsAutoCString spec; 1.352 + uri->GetSpec(spec); 1.353 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::NotifyCurrentState", "uri", spec.get()); 1.354 +#endif 1.355 + 1.356 + proxy->SetNotificationsDeferred(true); 1.357 + 1.358 + // We don't keep track of 1.359 + nsCOMPtr<nsIRunnable> ev = new imgStatusNotifyRunnable(this, proxy); 1.360 + NS_DispatchToCurrentThread(ev); 1.361 +} 1.362 + 1.363 +#define NOTIFY_IMAGE_OBSERVERS(func) \ 1.364 + do { \ 1.365 + ProxyArray::ForwardIterator iter(proxies); \ 1.366 + while (iter.HasMore()) { \ 1.367 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); \ 1.368 + if (proxy && !proxy->NotificationsDeferred()) { \ 1.369 + proxy->func; \ 1.370 + } \ 1.371 + } \ 1.372 + } while (false); 1.373 + 1.374 +/* static */ void 1.375 +imgStatusTracker::SyncNotifyState(ProxyArray& proxies, 1.376 + bool hasImage, uint32_t state, 1.377 + nsIntRect& dirtyRect, bool hadLastPart) 1.378 +{ 1.379 + MOZ_ASSERT(NS_IsMainThread()); 1.380 + // OnStartRequest 1.381 + if (state & stateRequestStarted) 1.382 + NOTIFY_IMAGE_OBSERVERS(OnStartRequest()); 1.383 + 1.384 + // OnStartContainer 1.385 + if (state & stateHasSize) 1.386 + NOTIFY_IMAGE_OBSERVERS(OnStartContainer()); 1.387 + 1.388 + // OnStartDecode 1.389 + if (state & stateDecodeStarted) 1.390 + NOTIFY_IMAGE_OBSERVERS(OnStartDecode()); 1.391 + 1.392 + // BlockOnload 1.393 + if (state & stateBlockingOnload) 1.394 + NOTIFY_IMAGE_OBSERVERS(BlockOnload()); 1.395 + 1.396 + if (hasImage) { 1.397 + // OnFrameUpdate 1.398 + // If there's any content in this frame at all (always true for 1.399 + // vector images, true for raster images that have decoded at 1.400 + // least one frame) then send OnFrameUpdate. 1.401 + if (!dirtyRect.IsEmpty()) 1.402 + NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect)); 1.403 + 1.404 + if (state & stateFrameStopped) 1.405 + NOTIFY_IMAGE_OBSERVERS(OnStopFrame()); 1.406 + 1.407 + // OnImageIsAnimated 1.408 + if (state & stateImageIsAnimated) 1.409 + NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated()); 1.410 + } 1.411 + 1.412 + if (state & stateDecodeStopped) { 1.413 + NS_ABORT_IF_FALSE(hasImage, "stopped decoding without ever having an image?"); 1.414 + NOTIFY_IMAGE_OBSERVERS(OnStopDecode()); 1.415 + } 1.416 + 1.417 + if (state & stateRequestStopped) { 1.418 + NOTIFY_IMAGE_OBSERVERS(OnStopRequest(hadLastPart)); 1.419 + } 1.420 +} 1.421 + 1.422 +ImageStatusDiff 1.423 +imgStatusTracker::Difference(imgStatusTracker* aOther) const 1.424 +{ 1.425 + MOZ_ASSERT(aOther, "aOther cannot be null"); 1.426 + ImageStatusDiff diff; 1.427 + diff.diffState = ~mState & aOther->mState & ~stateRequestStarted; 1.428 + diff.diffImageStatus = ~mImageStatus & aOther->mImageStatus; 1.429 + diff.unblockedOnload = mState & stateBlockingOnload && !(aOther->mState & stateBlockingOnload); 1.430 + diff.unsetDecodeStarted = mImageStatus & imgIRequest::STATUS_DECODE_STARTED 1.431 + && !(aOther->mImageStatus & imgIRequest::STATUS_DECODE_STARTED); 1.432 + diff.foundError = (mImageStatus != imgIRequest::STATUS_ERROR) 1.433 + && (aOther->mImageStatus == imgIRequest::STATUS_ERROR); 1.434 + 1.435 + MOZ_ASSERT(!mIsMultipart || aOther->mIsMultipart, "mIsMultipart should be monotonic"); 1.436 + diff.foundIsMultipart = !mIsMultipart && aOther->mIsMultipart; 1.437 + diff.foundLastPart = !mHadLastPart && aOther->mHadLastPart; 1.438 + 1.439 + diff.gotDecoded = !mHasBeenDecoded && aOther->mHasBeenDecoded; 1.440 + 1.441 + // Only record partial invalidations if we haven't been decoded before. 1.442 + // When images are re-decoded after discarding, we don't want to display 1.443 + // partially decoded versions to the user. 1.444 + const uint32_t combinedStatus = mImageStatus | aOther->mImageStatus; 1.445 + const bool doInvalidations = !(mHasBeenDecoded || aOther->mHasBeenDecoded) 1.446 + || combinedStatus & imgIRequest::STATUS_ERROR 1.447 + || combinedStatus & imgIRequest::STATUS_DECODE_COMPLETE; 1.448 + 1.449 + // Record and reset the invalid rectangle. 1.450 + // XXX(seth): We shouldn't be resetting anything here; see bug 910441. 1.451 + if (doInvalidations) { 1.452 + diff.invalidRect = aOther->mInvalidRect; 1.453 + aOther->mInvalidRect.SetEmpty(); 1.454 + } 1.455 + 1.456 + return diff; 1.457 +} 1.458 + 1.459 +ImageStatusDiff 1.460 +imgStatusTracker::DecodeStateAsDifference() const 1.461 +{ 1.462 + ImageStatusDiff diff; 1.463 + diff.diffState = mState & ~stateRequestStarted; 1.464 + 1.465 + // All other ImageStatusDiff fields are intentionally left at their default 1.466 + // values; we only want to notify decode state changes. 1.467 + 1.468 + return diff; 1.469 +} 1.470 + 1.471 +void 1.472 +imgStatusTracker::ApplyDifference(const ImageStatusDiff& aDiff) 1.473 +{ 1.474 + LOG_SCOPE(GetImgLog(), "imgStatusTracker::ApplyDifference"); 1.475 + 1.476 + // We must not modify or notify for the start-load state, which happens from Necko callbacks. 1.477 + uint32_t loadState = mState & stateRequestStarted; 1.478 + 1.479 + // Synchronize our state. 1.480 + mState |= aDiff.diffState | loadState; 1.481 + if (aDiff.unblockedOnload) 1.482 + mState &= ~stateBlockingOnload; 1.483 + 1.484 + mIsMultipart = mIsMultipart || aDiff.foundIsMultipart; 1.485 + mHadLastPart = mHadLastPart || aDiff.foundLastPart; 1.486 + mHasBeenDecoded = mHasBeenDecoded || aDiff.gotDecoded; 1.487 + 1.488 + // Update the image status. There are some subtle points which are handled below. 1.489 + mImageStatus |= aDiff.diffImageStatus; 1.490 + 1.491 + // Unset bits which can get unset as part of the decoding process. 1.492 + if (aDiff.unsetDecodeStarted) 1.493 + mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; 1.494 + 1.495 + // The error state is sticky and overrides all other bits. 1.496 + if (mImageStatus & imgIRequest::STATUS_ERROR) 1.497 + mImageStatus = imgIRequest::STATUS_ERROR; 1.498 +} 1.499 + 1.500 +void 1.501 +imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff) 1.502 +{ 1.503 + MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); 1.504 + LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference"); 1.505 + 1.506 + nsIntRect invalidRect = mInvalidRect.Union(diff.invalidRect); 1.507 + 1.508 + SyncNotifyState(mConsumers, !!mImage, diff.diffState, invalidRect, mHadLastPart); 1.509 + 1.510 + mInvalidRect.SetEmpty(); 1.511 + 1.512 + if (diff.unblockedOnload) { 1.513 + ProxyArray::ForwardIterator iter(mConsumers); 1.514 + while (iter.HasMore()) { 1.515 + // Hold on to a reference to this proxy, since notifying the state can 1.516 + // cause it to disappear. 1.517 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.518 + 1.519 + if (proxy && !proxy->NotificationsDeferred()) { 1.520 + SendUnblockOnload(proxy); 1.521 + } 1.522 + } 1.523 + } 1.524 + 1.525 + if (diff.foundError) { 1.526 + FireFailureNotification(); 1.527 + } 1.528 +} 1.529 + 1.530 +already_AddRefed<imgStatusTracker> 1.531 +imgStatusTracker::CloneForRecording() 1.532 +{ 1.533 + // Grab a ref to this to ensure it isn't deleted. 1.534 + nsRefPtr<imgStatusTracker> thisStatusTracker = this; 1.535 + nsRefPtr<imgStatusTracker> clone = new imgStatusTracker(*thisStatusTracker); 1.536 + return clone.forget(); 1.537 +} 1.538 + 1.539 +void 1.540 +imgStatusTracker::SyncNotify(imgRequestProxy* proxy) 1.541 +{ 1.542 + MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe"); 1.543 +#ifdef PR_LOGGING 1.544 + nsRefPtr<ImageURL> uri; 1.545 + proxy->GetURI(getter_AddRefs(uri)); 1.546 + nsAutoCString spec; 1.547 + uri->GetSpec(spec); 1.548 + LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgStatusTracker::SyncNotify", "uri", spec.get()); 1.549 +#endif 1.550 + 1.551 + nsIntRect r; 1.552 + if (mImage) { 1.553 + // XXX - Should only send partial rects here, but that needs to 1.554 + // wait until we fix up the observer interface 1.555 + r = mImage->FrameRect(imgIContainer::FRAME_CURRENT); 1.556 + } 1.557 + 1.558 + ProxyArray array; 1.559 + array.AppendElement(proxy->asWeakPtr()); 1.560 + SyncNotifyState(array, !!mImage, mState, r, mHadLastPart); 1.561 +} 1.562 + 1.563 +void 1.564 +imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy, 1.565 + nsresult aStatus) 1.566 +{ 1.567 + MOZ_ASSERT(NS_IsMainThread(), 1.568 + "SyncNotifyState and mConsumers are not threadsafe"); 1.569 + nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy); 1.570 + 1.571 + // In certain cases the request might not have started yet. 1.572 + // We still need to fulfill the contract. 1.573 + if (!(mState & stateRequestStarted)) { 1.574 + aProxy->OnStartRequest(); 1.575 + } 1.576 + 1.577 + if (mState & stateBlockingOnload) { 1.578 + aProxy->UnblockOnload(); 1.579 + } 1.580 + 1.581 + if (!(mState & stateRequestStopped)) { 1.582 + aProxy->OnStopRequest(true); 1.583 + } 1.584 +} 1.585 + 1.586 +void 1.587 +imgStatusTracker::AddConsumer(imgRequestProxy* aConsumer) 1.588 +{ 1.589 + MOZ_ASSERT(NS_IsMainThread()); 1.590 + mConsumers.AppendElementUnlessExists(aConsumer->asWeakPtr()); 1.591 +} 1.592 + 1.593 +// XXX - The last argument should go away. 1.594 +bool 1.595 +imgStatusTracker::RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus) 1.596 +{ 1.597 + MOZ_ASSERT(NS_IsMainThread()); 1.598 + // Remove the proxy from the list. 1.599 + bool removed = mConsumers.RemoveElement(aConsumer); 1.600 + 1.601 + // Consumers can get confused if they don't get all the proper teardown 1.602 + // notifications. Part ways on good terms. 1.603 + if (removed && !aConsumer->NotificationsDeferred()) { 1.604 + EmulateRequestFinished(aConsumer, aStatus); 1.605 + } 1.606 + 1.607 + // Make sure we don't give callbacks to a consumer that isn't interested in 1.608 + // them any more. 1.609 + imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get()); 1.610 + if (aConsumer->NotificationsDeferred() && runnable) { 1.611 + runnable->RemoveProxy(aConsumer); 1.612 + aConsumer->SetNotificationsDeferred(false); 1.613 + } 1.614 + 1.615 + return removed; 1.616 +} 1.617 + 1.618 +bool 1.619 +imgStatusTracker::FirstConsumerIs(imgRequestProxy* aConsumer) 1.620 +{ 1.621 + MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); 1.622 + ProxyArray::ForwardIterator iter(mConsumers); 1.623 + while (iter.HasMore()) { 1.624 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.625 + if (proxy) { 1.626 + return proxy.get() == aConsumer; 1.627 + } 1.628 + } 1.629 + return false; 1.630 +} 1.631 + 1.632 +void 1.633 +imgStatusTracker::RecordCancel() 1.634 +{ 1.635 + if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) 1.636 + mImageStatus = imgIRequest::STATUS_ERROR; 1.637 +} 1.638 + 1.639 +void 1.640 +imgStatusTracker::RecordLoaded() 1.641 +{ 1.642 + NS_ABORT_IF_FALSE(mImage, "RecordLoaded called before we have an Image"); 1.643 + mState |= stateRequestStarted | stateHasSize | stateRequestStopped; 1.644 + mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE; 1.645 + mHadLastPart = true; 1.646 +} 1.647 + 1.648 +void 1.649 +imgStatusTracker::RecordDecoded() 1.650 +{ 1.651 + NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image"); 1.652 + mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped; 1.653 + mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE; 1.654 + mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; 1.655 +} 1.656 + 1.657 +void 1.658 +imgStatusTracker::RecordStartDecode() 1.659 +{ 1.660 + NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image"); 1.661 + mState |= stateDecodeStarted; 1.662 + mImageStatus |= imgIRequest::STATUS_DECODE_STARTED; 1.663 +} 1.664 + 1.665 +void 1.666 +imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy) 1.667 +{ 1.668 + MOZ_ASSERT(NS_IsMainThread()); 1.669 + if (!aProxy->NotificationsDeferred()) 1.670 + aProxy->OnStartDecode(); 1.671 +} 1.672 + 1.673 +void 1.674 +imgStatusTracker::RecordStartContainer(imgIContainer* aContainer) 1.675 +{ 1.676 + NS_ABORT_IF_FALSE(mImage, 1.677 + "RecordStartContainer called before we have an Image"); 1.678 + NS_ABORT_IF_FALSE(mImage == aContainer, 1.679 + "RecordStartContainer called with wrong Image"); 1.680 + mState |= stateHasSize; 1.681 + mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE; 1.682 +} 1.683 + 1.684 +void 1.685 +imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy) 1.686 +{ 1.687 + MOZ_ASSERT(NS_IsMainThread()); 1.688 + if (!aProxy->NotificationsDeferred()) 1.689 + aProxy->OnStartContainer(); 1.690 +} 1.691 + 1.692 +void 1.693 +imgStatusTracker::RecordStartFrame() 1.694 +{ 1.695 + mInvalidRect.SetEmpty(); 1.696 +} 1.697 + 1.698 +// No SendStartFrame since it's not observed below us. 1.699 + 1.700 +void 1.701 +imgStatusTracker::RecordStopFrame() 1.702 +{ 1.703 + NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image"); 1.704 + mState |= stateFrameStopped; 1.705 + mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE; 1.706 +} 1.707 + 1.708 +void 1.709 +imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy) 1.710 +{ 1.711 + MOZ_ASSERT(NS_IsMainThread()); 1.712 + if (!aProxy->NotificationsDeferred()) 1.713 + aProxy->OnStopFrame(); 1.714 +} 1.715 + 1.716 +void 1.717 +imgStatusTracker::RecordStopDecode(nsresult aStatus) 1.718 +{ 1.719 + NS_ABORT_IF_FALSE(mImage, 1.720 + "RecordStopDecode called before we have an Image"); 1.721 + mState |= stateDecodeStopped; 1.722 + 1.723 + if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) { 1.724 + mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE; 1.725 + mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; 1.726 + mHasBeenDecoded = true; 1.727 + // If we weren't successful, clear all success status bits and set error. 1.728 + } else { 1.729 + mImageStatus = imgIRequest::STATUS_ERROR; 1.730 + } 1.731 +} 1.732 + 1.733 +void 1.734 +imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, 1.735 + nsresult aStatus) 1.736 +{ 1.737 + MOZ_ASSERT(NS_IsMainThread()); 1.738 + if (!aProxy->NotificationsDeferred()) 1.739 + aProxy->OnStopDecode(); 1.740 +} 1.741 + 1.742 +void 1.743 +imgStatusTracker::RecordDiscard() 1.744 +{ 1.745 + NS_ABORT_IF_FALSE(mImage, 1.746 + "RecordDiscard called before we have an Image"); 1.747 + // Clear the state bits we no longer deserve. 1.748 + uint32_t stateBitsToClear = stateDecodeStopped; 1.749 + mState &= ~stateBitsToClear; 1.750 + 1.751 + // Clear the status bits we no longer deserve. 1.752 + uint32_t statusBitsToClear = imgIRequest::STATUS_DECODE_STARTED | 1.753 + imgIRequest::STATUS_FRAME_COMPLETE | 1.754 + imgIRequest::STATUS_DECODE_COMPLETE; 1.755 + mImageStatus &= ~statusBitsToClear; 1.756 +} 1.757 + 1.758 +void 1.759 +imgStatusTracker::SendDiscard(imgRequestProxy* aProxy) 1.760 +{ 1.761 + MOZ_ASSERT(NS_IsMainThread()); 1.762 + if (!aProxy->NotificationsDeferred()) 1.763 + aProxy->OnDiscard(); 1.764 +} 1.765 + 1.766 + 1.767 +void 1.768 +imgStatusTracker::RecordUnlockedDraw() 1.769 +{ 1.770 + NS_ABORT_IF_FALSE(mImage, 1.771 + "RecordUnlockedDraw called before we have an Image"); 1.772 +} 1.773 + 1.774 +void 1.775 +imgStatusTracker::RecordImageIsAnimated() 1.776 +{ 1.777 + NS_ABORT_IF_FALSE(mImage, 1.778 + "RecordImageIsAnimated called before we have an Image"); 1.779 + mState |= stateImageIsAnimated; 1.780 +} 1.781 + 1.782 +void 1.783 +imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy) 1.784 +{ 1.785 + MOZ_ASSERT(NS_IsMainThread()); 1.786 + if (!aProxy->NotificationsDeferred()) 1.787 + aProxy->OnImageIsAnimated(); 1.788 +} 1.789 + 1.790 +void 1.791 +imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy) 1.792 +{ 1.793 + MOZ_ASSERT(NS_IsMainThread()); 1.794 + if (!aProxy->NotificationsDeferred()) 1.795 + aProxy->OnUnlockedDraw(); 1.796 +} 1.797 + 1.798 +void 1.799 +imgStatusTracker::OnUnlockedDraw() 1.800 +{ 1.801 + MOZ_ASSERT(NS_IsMainThread()); 1.802 + RecordUnlockedDraw(); 1.803 + ProxyArray::ForwardIterator iter(mConsumers); 1.804 + while (iter.HasMore()) { 1.805 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.806 + if (proxy) { 1.807 + SendUnlockedDraw(proxy); 1.808 + } 1.809 + } 1.810 +} 1.811 + 1.812 +void 1.813 +imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect) 1.814 +{ 1.815 + NS_ABORT_IF_FALSE(mImage, 1.816 + "RecordFrameChanged called before we have an Image"); 1.817 + mInvalidRect = mInvalidRect.Union(*aDirtyRect); 1.818 +} 1.819 + 1.820 +void 1.821 +imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy, 1.822 + const nsIntRect* aDirtyRect) 1.823 +{ 1.824 + MOZ_ASSERT(NS_IsMainThread()); 1.825 + if (!aProxy->NotificationsDeferred()) 1.826 + aProxy->OnFrameUpdate(aDirtyRect); 1.827 +} 1.828 + 1.829 +/* non-virtual sort-of-nsIRequestObserver methods */ 1.830 +void 1.831 +imgStatusTracker::RecordStartRequest() 1.832 +{ 1.833 + // We're starting a new load, so clear any status and state bits indicating 1.834 + // load/decode 1.835 + mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL; 1.836 + mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE; 1.837 + mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE; 1.838 + mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; 1.839 + mImageStatus &= ~imgIRequest::STATUS_DECODE_COMPLETE; 1.840 + mState &= ~stateRequestStarted; 1.841 + mState &= ~stateDecodeStarted; 1.842 + mState &= ~stateDecodeStopped; 1.843 + mState &= ~stateRequestStopped; 1.844 + mState &= ~stateBlockingOnload; 1.845 + mState &= ~stateImageIsAnimated; 1.846 + 1.847 + mState |= stateRequestStarted; 1.848 +} 1.849 + 1.850 +void 1.851 +imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy) 1.852 +{ 1.853 + MOZ_ASSERT(NS_IsMainThread()); 1.854 + if (!aProxy->NotificationsDeferred()) 1.855 + aProxy->OnStartRequest(); 1.856 +} 1.857 + 1.858 +void 1.859 +imgStatusTracker::OnStartRequest() 1.860 +{ 1.861 + MOZ_ASSERT(NS_IsMainThread()); 1.862 + RecordStartRequest(); 1.863 + ProxyArray::ForwardIterator iter(mConsumers); 1.864 + while (iter.HasMore()) { 1.865 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.866 + if (proxy) { 1.867 + SendStartRequest(proxy); 1.868 + } 1.869 + } 1.870 +} 1.871 + 1.872 +void 1.873 +imgStatusTracker::RecordStopRequest(bool aLastPart, 1.874 + nsresult aStatus) 1.875 +{ 1.876 + mHadLastPart = aLastPart; 1.877 + mState |= stateRequestStopped; 1.878 + 1.879 + // If we were successful in loading, note that the image is complete. 1.880 + if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) 1.881 + mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE; 1.882 + else 1.883 + mImageStatus = imgIRequest::STATUS_ERROR; 1.884 +} 1.885 + 1.886 +void 1.887 +imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, 1.888 + bool aLastPart, 1.889 + nsresult aStatus) 1.890 +{ 1.891 + MOZ_ASSERT(NS_IsMainThread()); 1.892 + if (!aProxy->NotificationsDeferred()) { 1.893 + aProxy->OnStopRequest(aLastPart); 1.894 + } 1.895 +} 1.896 + 1.897 +class OnStopRequestEvent : public nsRunnable 1.898 +{ 1.899 +public: 1.900 + OnStopRequestEvent(imgStatusTracker* aTracker, 1.901 + bool aLastPart, 1.902 + nsresult aStatus) 1.903 + : mTracker(aTracker) 1.904 + , mLastPart(aLastPart) 1.905 + , mStatus(aStatus) 1.906 + { 1.907 + MOZ_ASSERT(!NS_IsMainThread(), "Should be created off the main thread"); 1.908 + MOZ_ASSERT(aTracker, "aTracker should not be null"); 1.909 + } 1.910 + 1.911 + NS_IMETHOD Run() 1.912 + { 1.913 + MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); 1.914 + MOZ_ASSERT(mTracker, "mTracker should not be null"); 1.915 + mTracker->OnStopRequest(mLastPart, mStatus); 1.916 + return NS_OK; 1.917 + } 1.918 +private: 1.919 + nsRefPtr<imgStatusTracker> mTracker; 1.920 + bool mLastPart; 1.921 + nsresult mStatus; 1.922 +}; 1.923 + 1.924 +void 1.925 +imgStatusTracker::OnStopRequest(bool aLastPart, 1.926 + nsresult aStatus) 1.927 +{ 1.928 + if (!NS_IsMainThread()) { 1.929 + NS_DispatchToMainThread( 1.930 + new OnStopRequestEvent(this, aLastPart, aStatus)); 1.931 + return; 1.932 + } 1.933 + bool preexistingError = mImageStatus == imgIRequest::STATUS_ERROR; 1.934 + 1.935 + RecordStopRequest(aLastPart, aStatus); 1.936 + /* notify the kids */ 1.937 + ProxyArray::ForwardIterator srIter(mConsumers); 1.938 + while (srIter.HasMore()) { 1.939 + nsRefPtr<imgRequestProxy> proxy = srIter.GetNext().get(); 1.940 + if (proxy) { 1.941 + SendStopRequest(proxy, aLastPart, aStatus); 1.942 + } 1.943 + } 1.944 + 1.945 + if (NS_FAILED(aStatus) && !preexistingError) { 1.946 + FireFailureNotification(); 1.947 + } 1.948 +} 1.949 + 1.950 +void 1.951 +imgStatusTracker::OnDiscard() 1.952 +{ 1.953 + MOZ_ASSERT(NS_IsMainThread()); 1.954 + RecordDiscard(); 1.955 + 1.956 + /* notify the kids */ 1.957 + ProxyArray::ForwardIterator iter(mConsumers); 1.958 + while (iter.HasMore()) { 1.959 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.960 + if (proxy) { 1.961 + SendDiscard(proxy); 1.962 + } 1.963 + } 1.964 +} 1.965 + 1.966 +void 1.967 +imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect) 1.968 +{ 1.969 + MOZ_ASSERT(NS_IsMainThread()); 1.970 + RecordFrameChanged(aDirtyRect); 1.971 + 1.972 + /* notify the kids */ 1.973 + ProxyArray::ForwardIterator iter(mConsumers); 1.974 + while (iter.HasMore()) { 1.975 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.976 + if (proxy) { 1.977 + SendFrameChanged(proxy, aDirtyRect); 1.978 + } 1.979 + } 1.980 +} 1.981 + 1.982 +void 1.983 +imgStatusTracker::OnStopFrame() 1.984 +{ 1.985 + MOZ_ASSERT(NS_IsMainThread()); 1.986 + RecordStopFrame(); 1.987 + 1.988 + /* notify the kids */ 1.989 + ProxyArray::ForwardIterator iter(mConsumers); 1.990 + while (iter.HasMore()) { 1.991 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.992 + if (proxy) { 1.993 + SendStopFrame(proxy); 1.994 + } 1.995 + } 1.996 +} 1.997 + 1.998 +void 1.999 +imgStatusTracker::OnDataAvailable() 1.1000 +{ 1.1001 + if (!NS_IsMainThread()) { 1.1002 + // Note: SetHasImage calls Image::Lock and Image::IncrementAnimationCounter 1.1003 + // so subsequent calls or dispatches which Unlock or Decrement~ should 1.1004 + // be issued after this to avoid race conditions. 1.1005 + NS_DispatchToMainThread( 1.1006 + NS_NewRunnableMethod(this, &imgStatusTracker::OnDataAvailable)); 1.1007 + return; 1.1008 + } 1.1009 + // Notify any imgRequestProxys that are observing us that we have an Image. 1.1010 + ProxyArray::ForwardIterator iter(mConsumers); 1.1011 + while (iter.HasMore()) { 1.1012 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.1013 + if (proxy) { 1.1014 + proxy->SetHasImage(); 1.1015 + } 1.1016 + } 1.1017 +} 1.1018 + 1.1019 +void 1.1020 +imgStatusTracker::RecordBlockOnload() 1.1021 +{ 1.1022 + MOZ_ASSERT(!(mState & stateBlockingOnload)); 1.1023 + mState |= stateBlockingOnload; 1.1024 +} 1.1025 + 1.1026 +void 1.1027 +imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy) 1.1028 +{ 1.1029 + MOZ_ASSERT(NS_IsMainThread()); 1.1030 + if (!aProxy->NotificationsDeferred()) { 1.1031 + aProxy->BlockOnload(); 1.1032 + } 1.1033 +} 1.1034 + 1.1035 +void 1.1036 +imgStatusTracker::RecordUnblockOnload() 1.1037 +{ 1.1038 + mState &= ~stateBlockingOnload; 1.1039 +} 1.1040 + 1.1041 +void 1.1042 +imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy) 1.1043 +{ 1.1044 + MOZ_ASSERT(NS_IsMainThread()); 1.1045 + if (!aProxy->NotificationsDeferred()) { 1.1046 + aProxy->UnblockOnload(); 1.1047 + } 1.1048 +} 1.1049 + 1.1050 +void 1.1051 +imgStatusTracker::MaybeUnblockOnload() 1.1052 +{ 1.1053 + if (!NS_IsMainThread()) { 1.1054 + NS_DispatchToMainThread( 1.1055 + NS_NewRunnableMethod(this, &imgStatusTracker::MaybeUnblockOnload)); 1.1056 + return; 1.1057 + } 1.1058 + if (!(mState & stateBlockingOnload)) { 1.1059 + return; 1.1060 + } 1.1061 + 1.1062 + RecordUnblockOnload(); 1.1063 + 1.1064 + ProxyArray::ForwardIterator iter(mConsumers); 1.1065 + while (iter.HasMore()) { 1.1066 + nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); 1.1067 + if (proxy) { 1.1068 + SendUnblockOnload(proxy); 1.1069 + } 1.1070 + } 1.1071 +} 1.1072 + 1.1073 +void 1.1074 +imgStatusTracker::RecordError() 1.1075 +{ 1.1076 + mImageStatus = imgIRequest::STATUS_ERROR; 1.1077 +} 1.1078 + 1.1079 +void 1.1080 +imgStatusTracker::FireFailureNotification() 1.1081 +{ 1.1082 + MOZ_ASSERT(NS_IsMainThread()); 1.1083 + 1.1084 + // Some kind of problem has happened with image decoding. 1.1085 + // Report the URI to net:failed-to-process-uri-conent observers. 1.1086 + if (mImage) { 1.1087 + // Should be on main thread, so ok to create a new nsIURI. 1.1088 + nsCOMPtr<nsIURI> uri; 1.1089 + { 1.1090 + nsRefPtr<ImageURL> threadsafeUriData = mImage->GetURI(); 1.1091 + uri = threadsafeUriData ? threadsafeUriData->ToIURI() : nullptr; 1.1092 + } 1.1093 + if (uri) { 1.1094 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.1095 + if (os) { 1.1096 + os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); 1.1097 + } 1.1098 + } 1.1099 + } 1.1100 +}