image/src/imgStatusTracker.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "imgStatusTracker.h"
michael@0 8
michael@0 9 #include "imgIContainer.h"
michael@0 10 #include "imgRequestProxy.h"
michael@0 11 #include "imgDecoderObserver.h"
michael@0 12 #include "Image.h"
michael@0 13 #include "ImageLogging.h"
michael@0 14 #include "nsNetUtil.h"
michael@0 15 #include "nsIObserverService.h"
michael@0 16
michael@0 17 #include "mozilla/Assertions.h"
michael@0 18 #include "mozilla/Services.h"
michael@0 19
michael@0 20 using namespace mozilla::image;
michael@0 21 using mozilla::WeakPtr;
michael@0 22
michael@0 23 class imgStatusTrackerObserver : public imgDecoderObserver
michael@0 24 {
michael@0 25 public:
michael@0 26 imgStatusTrackerObserver(imgStatusTracker* aTracker)
michael@0 27 : mTracker(aTracker->asWeakPtr())
michael@0 28 {
michael@0 29 MOZ_ASSERT(aTracker);
michael@0 30 }
michael@0 31
michael@0 32 void SetTracker(imgStatusTracker* aTracker)
michael@0 33 {
michael@0 34 MOZ_ASSERT(aTracker);
michael@0 35 mTracker = aTracker->asWeakPtr();
michael@0 36 }
michael@0 37
michael@0 38 /** imgDecoderObserver methods **/
michael@0 39
michael@0 40 virtual void OnStartDecode() MOZ_OVERRIDE
michael@0 41 {
michael@0 42 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode");
michael@0 43 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 44 if (!tracker) { return; }
michael@0 45 tracker->RecordStartDecode();
michael@0 46 if (!tracker->IsMultipart()) {
michael@0 47 tracker->RecordBlockOnload();
michael@0 48 }
michael@0 49 }
michael@0 50
michael@0 51 virtual void OnStartRequest() MOZ_OVERRIDE
michael@0 52 {
michael@0 53 NS_NOTREACHED("imgStatusTrackerObserver(imgDecoderObserver)::OnStartRequest");
michael@0 54 }
michael@0 55
michael@0 56 virtual void OnStartContainer() MOZ_OVERRIDE
michael@0 57 {
michael@0 58 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer");
michael@0 59 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 60 if (!tracker) { return; }
michael@0 61 nsRefPtr<Image> image = tracker->GetImage();;
michael@0 62 tracker->RecordStartContainer(image);
michael@0 63 }
michael@0 64
michael@0 65 virtual void OnStartFrame() MOZ_OVERRIDE
michael@0 66 {
michael@0 67 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartFrame");
michael@0 68 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 69 if (!tracker) { return; }
michael@0 70 tracker->RecordStartFrame();
michael@0 71 }
michael@0 72
michael@0 73 virtual void FrameChanged(const nsIntRect* dirtyRect) MOZ_OVERRIDE
michael@0 74 {
michael@0 75 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged");
michael@0 76 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 77 if (!tracker) { return; }
michael@0 78 tracker->RecordFrameChanged(dirtyRect);
michael@0 79 }
michael@0 80
michael@0 81 virtual void OnStopFrame() MOZ_OVERRIDE
michael@0 82 {
michael@0 83 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame");
michael@0 84 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 85 if (!tracker) { return; }
michael@0 86 tracker->RecordStopFrame();
michael@0 87 tracker->RecordUnblockOnload();
michael@0 88 }
michael@0 89
michael@0 90 virtual void OnStopDecode(nsresult aStatus) MOZ_OVERRIDE
michael@0 91 {
michael@0 92 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode");
michael@0 93 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 94 if (!tracker) { return; }
michael@0 95 tracker->RecordStopDecode(aStatus);
michael@0 96
michael@0 97 // This is really hacky. We need to handle the case where we start decoding,
michael@0 98 // block onload, but then hit an error before we get to our first frame.
michael@0 99 tracker->RecordUnblockOnload();
michael@0 100 }
michael@0 101
michael@0 102 virtual void OnStopRequest(bool aLastPart, nsresult aStatus) MOZ_OVERRIDE
michael@0 103 {
michael@0 104 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopRequest");
michael@0 105 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 106 if (!tracker) { return; }
michael@0 107 tracker->RecordStopRequest(aLastPart, aStatus);
michael@0 108 }
michael@0 109
michael@0 110 virtual void OnDiscard() MOZ_OVERRIDE
michael@0 111 {
michael@0 112 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDiscard");
michael@0 113 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 114 if (!tracker) { return; }
michael@0 115 tracker->RecordDiscard();
michael@0 116 }
michael@0 117
michael@0 118 virtual void OnUnlockedDraw() MOZ_OVERRIDE
michael@0 119 {
michael@0 120 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw");
michael@0 121 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 122 if (!tracker) { return; }
michael@0 123 NS_ABORT_IF_FALSE(tracker->HasImage(),
michael@0 124 "OnUnlockedDraw callback before we've created our image");
michael@0 125 tracker->RecordUnlockedDraw();
michael@0 126 }
michael@0 127
michael@0 128 virtual void OnImageIsAnimated() MOZ_OVERRIDE
michael@0 129 {
michael@0 130 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnImageIsAnimated");
michael@0 131 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 132 if (!tracker) { return; }
michael@0 133 tracker->RecordImageIsAnimated();
michael@0 134 }
michael@0 135
michael@0 136 virtual void OnError() MOZ_OVERRIDE
michael@0 137 {
michael@0 138 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnError");
michael@0 139 nsRefPtr<imgStatusTracker> tracker = mTracker.get();
michael@0 140 if (!tracker) { return; }
michael@0 141 tracker->RecordError();
michael@0 142 }
michael@0 143
michael@0 144 protected:
michael@0 145 virtual ~imgStatusTrackerObserver() {}
michael@0 146
michael@0 147 private:
michael@0 148 WeakPtr<imgStatusTracker> mTracker;
michael@0 149 };
michael@0 150
michael@0 151 // imgStatusTracker methods
michael@0 152
michael@0 153 imgStatusTracker::imgStatusTracker(Image* aImage)
michael@0 154 : mImage(aImage),
michael@0 155 mState(0),
michael@0 156 mImageStatus(imgIRequest::STATUS_NONE),
michael@0 157 mIsMultipart(false),
michael@0 158 mHadLastPart(false),
michael@0 159 mHasBeenDecoded(false)
michael@0 160 {
michael@0 161 mTrackerObserver = new imgStatusTrackerObserver(this);
michael@0 162 }
michael@0 163
michael@0 164 // Private, used only by CloneForRecording.
michael@0 165 imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther)
michael@0 166 : mImage(aOther.mImage),
michael@0 167 mState(aOther.mState),
michael@0 168 mImageStatus(aOther.mImageStatus),
michael@0 169 mIsMultipart(aOther.mIsMultipart),
michael@0 170 mHadLastPart(aOther.mHadLastPart),
michael@0 171 mHasBeenDecoded(aOther.mHasBeenDecoded)
michael@0 172 // Note: we explicitly don't copy several fields:
michael@0 173 // - mRequestRunnable, because it won't be nulled out when the
michael@0 174 // mRequestRunnable's Run function eventually gets called.
michael@0 175 // - mProperties, because we don't need it and it'd just point at the same
michael@0 176 // object
michael@0 177 // - mConsumers, because we don't need to talk to consumers
michael@0 178 // - mInvalidRect, because the point of it is to be fired off and reset
michael@0 179 {
michael@0 180 mTrackerObserver = new imgStatusTrackerObserver(this);
michael@0 181 }
michael@0 182
michael@0 183 imgStatusTracker::~imgStatusTracker()
michael@0 184 {}
michael@0 185
michael@0 186 imgStatusTrackerInit::imgStatusTrackerInit(mozilla::image::Image* aImage,
michael@0 187 imgStatusTracker* aTracker)
michael@0 188 {
michael@0 189 MOZ_ASSERT(aImage);
michael@0 190
michael@0 191 if (aTracker) {
michael@0 192 mTracker = aTracker;
michael@0 193 mTracker->SetImage(aImage);
michael@0 194 } else {
michael@0 195 mTracker = new imgStatusTracker(aImage);
michael@0 196 }
michael@0 197 aImage->SetStatusTracker(mTracker);
michael@0 198 MOZ_ASSERT(mTracker);
michael@0 199 }
michael@0 200
michael@0 201 imgStatusTrackerInit::~imgStatusTrackerInit()
michael@0 202 {
michael@0 203 mTracker->ResetImage();
michael@0 204 }
michael@0 205
michael@0 206 void
michael@0 207 imgStatusTracker::SetImage(Image* aImage)
michael@0 208 {
michael@0 209 NS_ABORT_IF_FALSE(aImage, "Setting null image");
michael@0 210 NS_ABORT_IF_FALSE(!mImage, "Setting image when we already have one");
michael@0 211 mImage = aImage;
michael@0 212 }
michael@0 213
michael@0 214 void
michael@0 215 imgStatusTracker::ResetImage()
michael@0 216 {
michael@0 217 NS_ABORT_IF_FALSE(mImage, "Resetting image when it's already null!");
michael@0 218 mImage = nullptr;
michael@0 219 }
michael@0 220
michael@0 221 bool
michael@0 222 imgStatusTracker::IsLoading() const
michael@0 223 {
michael@0 224 // Checking for whether OnStopRequest has fired allows us to say we're
michael@0 225 // loading before OnStartRequest gets called, letting the request properly
michael@0 226 // get removed from the cache in certain cases.
michael@0 227 return !(mState & stateRequestStopped);
michael@0 228 }
michael@0 229
michael@0 230 uint32_t
michael@0 231 imgStatusTracker::GetImageStatus() const
michael@0 232 {
michael@0 233 return mImageStatus;
michael@0 234 }
michael@0 235
michael@0 236 // A helper class to allow us to call SyncNotify asynchronously.
michael@0 237 class imgRequestNotifyRunnable : public nsRunnable
michael@0 238 {
michael@0 239 public:
michael@0 240 imgRequestNotifyRunnable(imgStatusTracker* aTracker,
michael@0 241 imgRequestProxy* aRequestProxy)
michael@0 242 : mTracker(aTracker)
michael@0 243 {
michael@0 244 MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
michael@0 245 MOZ_ASSERT(aRequestProxy, "aRequestProxy should not be null");
michael@0 246 MOZ_ASSERT(aTracker, "aTracker should not be null");
michael@0 247 mProxies.AppendElement(aRequestProxy);
michael@0 248 }
michael@0 249
michael@0 250 NS_IMETHOD Run()
michael@0 251 {
michael@0 252 MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
michael@0 253 MOZ_ASSERT(mTracker, "mTracker should not be null");
michael@0 254 for (uint32_t i = 0; i < mProxies.Length(); ++i) {
michael@0 255 mProxies[i]->SetNotificationsDeferred(false);
michael@0 256 mTracker->SyncNotify(mProxies[i]);
michael@0 257 }
michael@0 258
michael@0 259 mTracker->mRequestRunnable = nullptr;
michael@0 260 return NS_OK;
michael@0 261 }
michael@0 262
michael@0 263 void AddProxy(imgRequestProxy* aRequestProxy)
michael@0 264 {
michael@0 265 mProxies.AppendElement(aRequestProxy);
michael@0 266 }
michael@0 267
michael@0 268 void RemoveProxy(imgRequestProxy* aRequestProxy)
michael@0 269 {
michael@0 270 mProxies.RemoveElement(aRequestProxy);
michael@0 271 }
michael@0 272
michael@0 273 private:
michael@0 274 friend class imgStatusTracker;
michael@0 275
michael@0 276 nsRefPtr<imgStatusTracker> mTracker;
michael@0 277 nsTArray< nsRefPtr<imgRequestProxy> > mProxies;
michael@0 278 };
michael@0 279
michael@0 280 void
michael@0 281 imgStatusTracker::Notify(imgRequestProxy* proxy)
michael@0 282 {
michael@0 283 MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
michael@0 284 #ifdef PR_LOGGING
michael@0 285 if (mImage && mImage->GetURI()) {
michael@0 286 nsRefPtr<ImageURL> uri(mImage->GetURI());
michael@0 287 nsAutoCString spec;
michael@0 288 uri->GetSpec(spec);
michael@0 289 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get());
michael@0 290 } else {
michael@0 291 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", "<unknown>");
michael@0 292 }
michael@0 293 #endif
michael@0 294
michael@0 295 proxy->SetNotificationsDeferred(true);
michael@0 296
michael@0 297 // If we have an existing runnable that we can use, we just append this proxy
michael@0 298 // to its list of proxies to be notified. This ensures we don't unnecessarily
michael@0 299 // delay onload.
michael@0 300 imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get());
michael@0 301 if (runnable) {
michael@0 302 runnable->AddProxy(proxy);
michael@0 303 } else {
michael@0 304 mRequestRunnable = new imgRequestNotifyRunnable(this, proxy);
michael@0 305 NS_DispatchToCurrentThread(mRequestRunnable);
michael@0 306 }
michael@0 307 }
michael@0 308
michael@0 309 // A helper class to allow us to call SyncNotify asynchronously for a given,
michael@0 310 // fixed, state.
michael@0 311 class imgStatusNotifyRunnable : public nsRunnable
michael@0 312 {
michael@0 313 public:
michael@0 314 imgStatusNotifyRunnable(imgStatusTracker* statusTracker,
michael@0 315 imgRequestProxy* requestproxy)
michael@0 316 : mStatusTracker(statusTracker), mProxy(requestproxy)
michael@0 317 {
michael@0 318 MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
michael@0 319 MOZ_ASSERT(requestproxy, "requestproxy cannot be null");
michael@0 320 MOZ_ASSERT(statusTracker, "status should not be null");
michael@0 321 mImage = statusTracker->GetImage();
michael@0 322 }
michael@0 323
michael@0 324 NS_IMETHOD Run()
michael@0 325 {
michael@0 326 MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
michael@0 327 mProxy->SetNotificationsDeferred(false);
michael@0 328
michael@0 329 mStatusTracker->SyncNotify(mProxy);
michael@0 330 return NS_OK;
michael@0 331 }
michael@0 332
michael@0 333 private:
michael@0 334 nsRefPtr<imgStatusTracker> mStatusTracker;
michael@0 335 // We have to hold on to a reference to the tracker's image, just in case
michael@0 336 // it goes away while we're in the event queue.
michael@0 337 nsRefPtr<Image> mImage;
michael@0 338 nsRefPtr<imgRequestProxy> mProxy;
michael@0 339 };
michael@0 340
michael@0 341 void
michael@0 342 imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy)
michael@0 343 {
michael@0 344 MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
michael@0 345 #ifdef PR_LOGGING
michael@0 346 nsRefPtr<ImageURL> uri;
michael@0 347 proxy->GetURI(getter_AddRefs(uri));
michael@0 348 nsAutoCString spec;
michael@0 349 uri->GetSpec(spec);
michael@0 350 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::NotifyCurrentState", "uri", spec.get());
michael@0 351 #endif
michael@0 352
michael@0 353 proxy->SetNotificationsDeferred(true);
michael@0 354
michael@0 355 // We don't keep track of
michael@0 356 nsCOMPtr<nsIRunnable> ev = new imgStatusNotifyRunnable(this, proxy);
michael@0 357 NS_DispatchToCurrentThread(ev);
michael@0 358 }
michael@0 359
michael@0 360 #define NOTIFY_IMAGE_OBSERVERS(func) \
michael@0 361 do { \
michael@0 362 ProxyArray::ForwardIterator iter(proxies); \
michael@0 363 while (iter.HasMore()) { \
michael@0 364 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); \
michael@0 365 if (proxy && !proxy->NotificationsDeferred()) { \
michael@0 366 proxy->func; \
michael@0 367 } \
michael@0 368 } \
michael@0 369 } while (false);
michael@0 370
michael@0 371 /* static */ void
michael@0 372 imgStatusTracker::SyncNotifyState(ProxyArray& proxies,
michael@0 373 bool hasImage, uint32_t state,
michael@0 374 nsIntRect& dirtyRect, bool hadLastPart)
michael@0 375 {
michael@0 376 MOZ_ASSERT(NS_IsMainThread());
michael@0 377 // OnStartRequest
michael@0 378 if (state & stateRequestStarted)
michael@0 379 NOTIFY_IMAGE_OBSERVERS(OnStartRequest());
michael@0 380
michael@0 381 // OnStartContainer
michael@0 382 if (state & stateHasSize)
michael@0 383 NOTIFY_IMAGE_OBSERVERS(OnStartContainer());
michael@0 384
michael@0 385 // OnStartDecode
michael@0 386 if (state & stateDecodeStarted)
michael@0 387 NOTIFY_IMAGE_OBSERVERS(OnStartDecode());
michael@0 388
michael@0 389 // BlockOnload
michael@0 390 if (state & stateBlockingOnload)
michael@0 391 NOTIFY_IMAGE_OBSERVERS(BlockOnload());
michael@0 392
michael@0 393 if (hasImage) {
michael@0 394 // OnFrameUpdate
michael@0 395 // If there's any content in this frame at all (always true for
michael@0 396 // vector images, true for raster images that have decoded at
michael@0 397 // least one frame) then send OnFrameUpdate.
michael@0 398 if (!dirtyRect.IsEmpty())
michael@0 399 NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect));
michael@0 400
michael@0 401 if (state & stateFrameStopped)
michael@0 402 NOTIFY_IMAGE_OBSERVERS(OnStopFrame());
michael@0 403
michael@0 404 // OnImageIsAnimated
michael@0 405 if (state & stateImageIsAnimated)
michael@0 406 NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated());
michael@0 407 }
michael@0 408
michael@0 409 if (state & stateDecodeStopped) {
michael@0 410 NS_ABORT_IF_FALSE(hasImage, "stopped decoding without ever having an image?");
michael@0 411 NOTIFY_IMAGE_OBSERVERS(OnStopDecode());
michael@0 412 }
michael@0 413
michael@0 414 if (state & stateRequestStopped) {
michael@0 415 NOTIFY_IMAGE_OBSERVERS(OnStopRequest(hadLastPart));
michael@0 416 }
michael@0 417 }
michael@0 418
michael@0 419 ImageStatusDiff
michael@0 420 imgStatusTracker::Difference(imgStatusTracker* aOther) const
michael@0 421 {
michael@0 422 MOZ_ASSERT(aOther, "aOther cannot be null");
michael@0 423 ImageStatusDiff diff;
michael@0 424 diff.diffState = ~mState & aOther->mState & ~stateRequestStarted;
michael@0 425 diff.diffImageStatus = ~mImageStatus & aOther->mImageStatus;
michael@0 426 diff.unblockedOnload = mState & stateBlockingOnload && !(aOther->mState & stateBlockingOnload);
michael@0 427 diff.unsetDecodeStarted = mImageStatus & imgIRequest::STATUS_DECODE_STARTED
michael@0 428 && !(aOther->mImageStatus & imgIRequest::STATUS_DECODE_STARTED);
michael@0 429 diff.foundError = (mImageStatus != imgIRequest::STATUS_ERROR)
michael@0 430 && (aOther->mImageStatus == imgIRequest::STATUS_ERROR);
michael@0 431
michael@0 432 MOZ_ASSERT(!mIsMultipart || aOther->mIsMultipart, "mIsMultipart should be monotonic");
michael@0 433 diff.foundIsMultipart = !mIsMultipart && aOther->mIsMultipart;
michael@0 434 diff.foundLastPart = !mHadLastPart && aOther->mHadLastPart;
michael@0 435
michael@0 436 diff.gotDecoded = !mHasBeenDecoded && aOther->mHasBeenDecoded;
michael@0 437
michael@0 438 // Only record partial invalidations if we haven't been decoded before.
michael@0 439 // When images are re-decoded after discarding, we don't want to display
michael@0 440 // partially decoded versions to the user.
michael@0 441 const uint32_t combinedStatus = mImageStatus | aOther->mImageStatus;
michael@0 442 const bool doInvalidations = !(mHasBeenDecoded || aOther->mHasBeenDecoded)
michael@0 443 || combinedStatus & imgIRequest::STATUS_ERROR
michael@0 444 || combinedStatus & imgIRequest::STATUS_DECODE_COMPLETE;
michael@0 445
michael@0 446 // Record and reset the invalid rectangle.
michael@0 447 // XXX(seth): We shouldn't be resetting anything here; see bug 910441.
michael@0 448 if (doInvalidations) {
michael@0 449 diff.invalidRect = aOther->mInvalidRect;
michael@0 450 aOther->mInvalidRect.SetEmpty();
michael@0 451 }
michael@0 452
michael@0 453 return diff;
michael@0 454 }
michael@0 455
michael@0 456 ImageStatusDiff
michael@0 457 imgStatusTracker::DecodeStateAsDifference() const
michael@0 458 {
michael@0 459 ImageStatusDiff diff;
michael@0 460 diff.diffState = mState & ~stateRequestStarted;
michael@0 461
michael@0 462 // All other ImageStatusDiff fields are intentionally left at their default
michael@0 463 // values; we only want to notify decode state changes.
michael@0 464
michael@0 465 return diff;
michael@0 466 }
michael@0 467
michael@0 468 void
michael@0 469 imgStatusTracker::ApplyDifference(const ImageStatusDiff& aDiff)
michael@0 470 {
michael@0 471 LOG_SCOPE(GetImgLog(), "imgStatusTracker::ApplyDifference");
michael@0 472
michael@0 473 // We must not modify or notify for the start-load state, which happens from Necko callbacks.
michael@0 474 uint32_t loadState = mState & stateRequestStarted;
michael@0 475
michael@0 476 // Synchronize our state.
michael@0 477 mState |= aDiff.diffState | loadState;
michael@0 478 if (aDiff.unblockedOnload)
michael@0 479 mState &= ~stateBlockingOnload;
michael@0 480
michael@0 481 mIsMultipart = mIsMultipart || aDiff.foundIsMultipart;
michael@0 482 mHadLastPart = mHadLastPart || aDiff.foundLastPart;
michael@0 483 mHasBeenDecoded = mHasBeenDecoded || aDiff.gotDecoded;
michael@0 484
michael@0 485 // Update the image status. There are some subtle points which are handled below.
michael@0 486 mImageStatus |= aDiff.diffImageStatus;
michael@0 487
michael@0 488 // Unset bits which can get unset as part of the decoding process.
michael@0 489 if (aDiff.unsetDecodeStarted)
michael@0 490 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
michael@0 491
michael@0 492 // The error state is sticky and overrides all other bits.
michael@0 493 if (mImageStatus & imgIRequest::STATUS_ERROR)
michael@0 494 mImageStatus = imgIRequest::STATUS_ERROR;
michael@0 495 }
michael@0 496
michael@0 497 void
michael@0 498 imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff)
michael@0 499 {
michael@0 500 MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
michael@0 501 LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference");
michael@0 502
michael@0 503 nsIntRect invalidRect = mInvalidRect.Union(diff.invalidRect);
michael@0 504
michael@0 505 SyncNotifyState(mConsumers, !!mImage, diff.diffState, invalidRect, mHadLastPart);
michael@0 506
michael@0 507 mInvalidRect.SetEmpty();
michael@0 508
michael@0 509 if (diff.unblockedOnload) {
michael@0 510 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 511 while (iter.HasMore()) {
michael@0 512 // Hold on to a reference to this proxy, since notifying the state can
michael@0 513 // cause it to disappear.
michael@0 514 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 515
michael@0 516 if (proxy && !proxy->NotificationsDeferred()) {
michael@0 517 SendUnblockOnload(proxy);
michael@0 518 }
michael@0 519 }
michael@0 520 }
michael@0 521
michael@0 522 if (diff.foundError) {
michael@0 523 FireFailureNotification();
michael@0 524 }
michael@0 525 }
michael@0 526
michael@0 527 already_AddRefed<imgStatusTracker>
michael@0 528 imgStatusTracker::CloneForRecording()
michael@0 529 {
michael@0 530 // Grab a ref to this to ensure it isn't deleted.
michael@0 531 nsRefPtr<imgStatusTracker> thisStatusTracker = this;
michael@0 532 nsRefPtr<imgStatusTracker> clone = new imgStatusTracker(*thisStatusTracker);
michael@0 533 return clone.forget();
michael@0 534 }
michael@0 535
michael@0 536 void
michael@0 537 imgStatusTracker::SyncNotify(imgRequestProxy* proxy)
michael@0 538 {
michael@0 539 MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
michael@0 540 #ifdef PR_LOGGING
michael@0 541 nsRefPtr<ImageURL> uri;
michael@0 542 proxy->GetURI(getter_AddRefs(uri));
michael@0 543 nsAutoCString spec;
michael@0 544 uri->GetSpec(spec);
michael@0 545 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgStatusTracker::SyncNotify", "uri", spec.get());
michael@0 546 #endif
michael@0 547
michael@0 548 nsIntRect r;
michael@0 549 if (mImage) {
michael@0 550 // XXX - Should only send partial rects here, but that needs to
michael@0 551 // wait until we fix up the observer interface
michael@0 552 r = mImage->FrameRect(imgIContainer::FRAME_CURRENT);
michael@0 553 }
michael@0 554
michael@0 555 ProxyArray array;
michael@0 556 array.AppendElement(proxy->asWeakPtr());
michael@0 557 SyncNotifyState(array, !!mImage, mState, r, mHadLastPart);
michael@0 558 }
michael@0 559
michael@0 560 void
michael@0 561 imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy,
michael@0 562 nsresult aStatus)
michael@0 563 {
michael@0 564 MOZ_ASSERT(NS_IsMainThread(),
michael@0 565 "SyncNotifyState and mConsumers are not threadsafe");
michael@0 566 nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy);
michael@0 567
michael@0 568 // In certain cases the request might not have started yet.
michael@0 569 // We still need to fulfill the contract.
michael@0 570 if (!(mState & stateRequestStarted)) {
michael@0 571 aProxy->OnStartRequest();
michael@0 572 }
michael@0 573
michael@0 574 if (mState & stateBlockingOnload) {
michael@0 575 aProxy->UnblockOnload();
michael@0 576 }
michael@0 577
michael@0 578 if (!(mState & stateRequestStopped)) {
michael@0 579 aProxy->OnStopRequest(true);
michael@0 580 }
michael@0 581 }
michael@0 582
michael@0 583 void
michael@0 584 imgStatusTracker::AddConsumer(imgRequestProxy* aConsumer)
michael@0 585 {
michael@0 586 MOZ_ASSERT(NS_IsMainThread());
michael@0 587 mConsumers.AppendElementUnlessExists(aConsumer->asWeakPtr());
michael@0 588 }
michael@0 589
michael@0 590 // XXX - The last argument should go away.
michael@0 591 bool
michael@0 592 imgStatusTracker::RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus)
michael@0 593 {
michael@0 594 MOZ_ASSERT(NS_IsMainThread());
michael@0 595 // Remove the proxy from the list.
michael@0 596 bool removed = mConsumers.RemoveElement(aConsumer);
michael@0 597
michael@0 598 // Consumers can get confused if they don't get all the proper teardown
michael@0 599 // notifications. Part ways on good terms.
michael@0 600 if (removed && !aConsumer->NotificationsDeferred()) {
michael@0 601 EmulateRequestFinished(aConsumer, aStatus);
michael@0 602 }
michael@0 603
michael@0 604 // Make sure we don't give callbacks to a consumer that isn't interested in
michael@0 605 // them any more.
michael@0 606 imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get());
michael@0 607 if (aConsumer->NotificationsDeferred() && runnable) {
michael@0 608 runnable->RemoveProxy(aConsumer);
michael@0 609 aConsumer->SetNotificationsDeferred(false);
michael@0 610 }
michael@0 611
michael@0 612 return removed;
michael@0 613 }
michael@0 614
michael@0 615 bool
michael@0 616 imgStatusTracker::FirstConsumerIs(imgRequestProxy* aConsumer)
michael@0 617 {
michael@0 618 MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
michael@0 619 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 620 while (iter.HasMore()) {
michael@0 621 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 622 if (proxy) {
michael@0 623 return proxy.get() == aConsumer;
michael@0 624 }
michael@0 625 }
michael@0 626 return false;
michael@0 627 }
michael@0 628
michael@0 629 void
michael@0 630 imgStatusTracker::RecordCancel()
michael@0 631 {
michael@0 632 if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
michael@0 633 mImageStatus = imgIRequest::STATUS_ERROR;
michael@0 634 }
michael@0 635
michael@0 636 void
michael@0 637 imgStatusTracker::RecordLoaded()
michael@0 638 {
michael@0 639 NS_ABORT_IF_FALSE(mImage, "RecordLoaded called before we have an Image");
michael@0 640 mState |= stateRequestStarted | stateHasSize | stateRequestStopped;
michael@0 641 mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE;
michael@0 642 mHadLastPart = true;
michael@0 643 }
michael@0 644
michael@0 645 void
michael@0 646 imgStatusTracker::RecordDecoded()
michael@0 647 {
michael@0 648 NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image");
michael@0 649 mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped;
michael@0 650 mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE;
michael@0 651 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
michael@0 652 }
michael@0 653
michael@0 654 void
michael@0 655 imgStatusTracker::RecordStartDecode()
michael@0 656 {
michael@0 657 NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image");
michael@0 658 mState |= stateDecodeStarted;
michael@0 659 mImageStatus |= imgIRequest::STATUS_DECODE_STARTED;
michael@0 660 }
michael@0 661
michael@0 662 void
michael@0 663 imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy)
michael@0 664 {
michael@0 665 MOZ_ASSERT(NS_IsMainThread());
michael@0 666 if (!aProxy->NotificationsDeferred())
michael@0 667 aProxy->OnStartDecode();
michael@0 668 }
michael@0 669
michael@0 670 void
michael@0 671 imgStatusTracker::RecordStartContainer(imgIContainer* aContainer)
michael@0 672 {
michael@0 673 NS_ABORT_IF_FALSE(mImage,
michael@0 674 "RecordStartContainer called before we have an Image");
michael@0 675 NS_ABORT_IF_FALSE(mImage == aContainer,
michael@0 676 "RecordStartContainer called with wrong Image");
michael@0 677 mState |= stateHasSize;
michael@0 678 mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
michael@0 679 }
michael@0 680
michael@0 681 void
michael@0 682 imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy)
michael@0 683 {
michael@0 684 MOZ_ASSERT(NS_IsMainThread());
michael@0 685 if (!aProxy->NotificationsDeferred())
michael@0 686 aProxy->OnStartContainer();
michael@0 687 }
michael@0 688
michael@0 689 void
michael@0 690 imgStatusTracker::RecordStartFrame()
michael@0 691 {
michael@0 692 mInvalidRect.SetEmpty();
michael@0 693 }
michael@0 694
michael@0 695 // No SendStartFrame since it's not observed below us.
michael@0 696
michael@0 697 void
michael@0 698 imgStatusTracker::RecordStopFrame()
michael@0 699 {
michael@0 700 NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image");
michael@0 701 mState |= stateFrameStopped;
michael@0 702 mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
michael@0 703 }
michael@0 704
michael@0 705 void
michael@0 706 imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy)
michael@0 707 {
michael@0 708 MOZ_ASSERT(NS_IsMainThread());
michael@0 709 if (!aProxy->NotificationsDeferred())
michael@0 710 aProxy->OnStopFrame();
michael@0 711 }
michael@0 712
michael@0 713 void
michael@0 714 imgStatusTracker::RecordStopDecode(nsresult aStatus)
michael@0 715 {
michael@0 716 NS_ABORT_IF_FALSE(mImage,
michael@0 717 "RecordStopDecode called before we have an Image");
michael@0 718 mState |= stateDecodeStopped;
michael@0 719
michael@0 720 if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) {
michael@0 721 mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE;
michael@0 722 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
michael@0 723 mHasBeenDecoded = true;
michael@0 724 // If we weren't successful, clear all success status bits and set error.
michael@0 725 } else {
michael@0 726 mImageStatus = imgIRequest::STATUS_ERROR;
michael@0 727 }
michael@0 728 }
michael@0 729
michael@0 730 void
michael@0 731 imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy,
michael@0 732 nsresult aStatus)
michael@0 733 {
michael@0 734 MOZ_ASSERT(NS_IsMainThread());
michael@0 735 if (!aProxy->NotificationsDeferred())
michael@0 736 aProxy->OnStopDecode();
michael@0 737 }
michael@0 738
michael@0 739 void
michael@0 740 imgStatusTracker::RecordDiscard()
michael@0 741 {
michael@0 742 NS_ABORT_IF_FALSE(mImage,
michael@0 743 "RecordDiscard called before we have an Image");
michael@0 744 // Clear the state bits we no longer deserve.
michael@0 745 uint32_t stateBitsToClear = stateDecodeStopped;
michael@0 746 mState &= ~stateBitsToClear;
michael@0 747
michael@0 748 // Clear the status bits we no longer deserve.
michael@0 749 uint32_t statusBitsToClear = imgIRequest::STATUS_DECODE_STARTED |
michael@0 750 imgIRequest::STATUS_FRAME_COMPLETE |
michael@0 751 imgIRequest::STATUS_DECODE_COMPLETE;
michael@0 752 mImageStatus &= ~statusBitsToClear;
michael@0 753 }
michael@0 754
michael@0 755 void
michael@0 756 imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
michael@0 757 {
michael@0 758 MOZ_ASSERT(NS_IsMainThread());
michael@0 759 if (!aProxy->NotificationsDeferred())
michael@0 760 aProxy->OnDiscard();
michael@0 761 }
michael@0 762
michael@0 763
michael@0 764 void
michael@0 765 imgStatusTracker::RecordUnlockedDraw()
michael@0 766 {
michael@0 767 NS_ABORT_IF_FALSE(mImage,
michael@0 768 "RecordUnlockedDraw called before we have an Image");
michael@0 769 }
michael@0 770
michael@0 771 void
michael@0 772 imgStatusTracker::RecordImageIsAnimated()
michael@0 773 {
michael@0 774 NS_ABORT_IF_FALSE(mImage,
michael@0 775 "RecordImageIsAnimated called before we have an Image");
michael@0 776 mState |= stateImageIsAnimated;
michael@0 777 }
michael@0 778
michael@0 779 void
michael@0 780 imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy)
michael@0 781 {
michael@0 782 MOZ_ASSERT(NS_IsMainThread());
michael@0 783 if (!aProxy->NotificationsDeferred())
michael@0 784 aProxy->OnImageIsAnimated();
michael@0 785 }
michael@0 786
michael@0 787 void
michael@0 788 imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy)
michael@0 789 {
michael@0 790 MOZ_ASSERT(NS_IsMainThread());
michael@0 791 if (!aProxy->NotificationsDeferred())
michael@0 792 aProxy->OnUnlockedDraw();
michael@0 793 }
michael@0 794
michael@0 795 void
michael@0 796 imgStatusTracker::OnUnlockedDraw()
michael@0 797 {
michael@0 798 MOZ_ASSERT(NS_IsMainThread());
michael@0 799 RecordUnlockedDraw();
michael@0 800 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 801 while (iter.HasMore()) {
michael@0 802 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 803 if (proxy) {
michael@0 804 SendUnlockedDraw(proxy);
michael@0 805 }
michael@0 806 }
michael@0 807 }
michael@0 808
michael@0 809 void
michael@0 810 imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect)
michael@0 811 {
michael@0 812 NS_ABORT_IF_FALSE(mImage,
michael@0 813 "RecordFrameChanged called before we have an Image");
michael@0 814 mInvalidRect = mInvalidRect.Union(*aDirtyRect);
michael@0 815 }
michael@0 816
michael@0 817 void
michael@0 818 imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy,
michael@0 819 const nsIntRect* aDirtyRect)
michael@0 820 {
michael@0 821 MOZ_ASSERT(NS_IsMainThread());
michael@0 822 if (!aProxy->NotificationsDeferred())
michael@0 823 aProxy->OnFrameUpdate(aDirtyRect);
michael@0 824 }
michael@0 825
michael@0 826 /* non-virtual sort-of-nsIRequestObserver methods */
michael@0 827 void
michael@0 828 imgStatusTracker::RecordStartRequest()
michael@0 829 {
michael@0 830 // We're starting a new load, so clear any status and state bits indicating
michael@0 831 // load/decode
michael@0 832 mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
michael@0 833 mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
michael@0 834 mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
michael@0 835 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
michael@0 836 mImageStatus &= ~imgIRequest::STATUS_DECODE_COMPLETE;
michael@0 837 mState &= ~stateRequestStarted;
michael@0 838 mState &= ~stateDecodeStarted;
michael@0 839 mState &= ~stateDecodeStopped;
michael@0 840 mState &= ~stateRequestStopped;
michael@0 841 mState &= ~stateBlockingOnload;
michael@0 842 mState &= ~stateImageIsAnimated;
michael@0 843
michael@0 844 mState |= stateRequestStarted;
michael@0 845 }
michael@0 846
michael@0 847 void
michael@0 848 imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy)
michael@0 849 {
michael@0 850 MOZ_ASSERT(NS_IsMainThread());
michael@0 851 if (!aProxy->NotificationsDeferred())
michael@0 852 aProxy->OnStartRequest();
michael@0 853 }
michael@0 854
michael@0 855 void
michael@0 856 imgStatusTracker::OnStartRequest()
michael@0 857 {
michael@0 858 MOZ_ASSERT(NS_IsMainThread());
michael@0 859 RecordStartRequest();
michael@0 860 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 861 while (iter.HasMore()) {
michael@0 862 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 863 if (proxy) {
michael@0 864 SendStartRequest(proxy);
michael@0 865 }
michael@0 866 }
michael@0 867 }
michael@0 868
michael@0 869 void
michael@0 870 imgStatusTracker::RecordStopRequest(bool aLastPart,
michael@0 871 nsresult aStatus)
michael@0 872 {
michael@0 873 mHadLastPart = aLastPart;
michael@0 874 mState |= stateRequestStopped;
michael@0 875
michael@0 876 // If we were successful in loading, note that the image is complete.
michael@0 877 if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR)
michael@0 878 mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
michael@0 879 else
michael@0 880 mImageStatus = imgIRequest::STATUS_ERROR;
michael@0 881 }
michael@0 882
michael@0 883 void
michael@0 884 imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy,
michael@0 885 bool aLastPart,
michael@0 886 nsresult aStatus)
michael@0 887 {
michael@0 888 MOZ_ASSERT(NS_IsMainThread());
michael@0 889 if (!aProxy->NotificationsDeferred()) {
michael@0 890 aProxy->OnStopRequest(aLastPart);
michael@0 891 }
michael@0 892 }
michael@0 893
michael@0 894 class OnStopRequestEvent : public nsRunnable
michael@0 895 {
michael@0 896 public:
michael@0 897 OnStopRequestEvent(imgStatusTracker* aTracker,
michael@0 898 bool aLastPart,
michael@0 899 nsresult aStatus)
michael@0 900 : mTracker(aTracker)
michael@0 901 , mLastPart(aLastPart)
michael@0 902 , mStatus(aStatus)
michael@0 903 {
michael@0 904 MOZ_ASSERT(!NS_IsMainThread(), "Should be created off the main thread");
michael@0 905 MOZ_ASSERT(aTracker, "aTracker should not be null");
michael@0 906 }
michael@0 907
michael@0 908 NS_IMETHOD Run()
michael@0 909 {
michael@0 910 MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
michael@0 911 MOZ_ASSERT(mTracker, "mTracker should not be null");
michael@0 912 mTracker->OnStopRequest(mLastPart, mStatus);
michael@0 913 return NS_OK;
michael@0 914 }
michael@0 915 private:
michael@0 916 nsRefPtr<imgStatusTracker> mTracker;
michael@0 917 bool mLastPart;
michael@0 918 nsresult mStatus;
michael@0 919 };
michael@0 920
michael@0 921 void
michael@0 922 imgStatusTracker::OnStopRequest(bool aLastPart,
michael@0 923 nsresult aStatus)
michael@0 924 {
michael@0 925 if (!NS_IsMainThread()) {
michael@0 926 NS_DispatchToMainThread(
michael@0 927 new OnStopRequestEvent(this, aLastPart, aStatus));
michael@0 928 return;
michael@0 929 }
michael@0 930 bool preexistingError = mImageStatus == imgIRequest::STATUS_ERROR;
michael@0 931
michael@0 932 RecordStopRequest(aLastPart, aStatus);
michael@0 933 /* notify the kids */
michael@0 934 ProxyArray::ForwardIterator srIter(mConsumers);
michael@0 935 while (srIter.HasMore()) {
michael@0 936 nsRefPtr<imgRequestProxy> proxy = srIter.GetNext().get();
michael@0 937 if (proxy) {
michael@0 938 SendStopRequest(proxy, aLastPart, aStatus);
michael@0 939 }
michael@0 940 }
michael@0 941
michael@0 942 if (NS_FAILED(aStatus) && !preexistingError) {
michael@0 943 FireFailureNotification();
michael@0 944 }
michael@0 945 }
michael@0 946
michael@0 947 void
michael@0 948 imgStatusTracker::OnDiscard()
michael@0 949 {
michael@0 950 MOZ_ASSERT(NS_IsMainThread());
michael@0 951 RecordDiscard();
michael@0 952
michael@0 953 /* notify the kids */
michael@0 954 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 955 while (iter.HasMore()) {
michael@0 956 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 957 if (proxy) {
michael@0 958 SendDiscard(proxy);
michael@0 959 }
michael@0 960 }
michael@0 961 }
michael@0 962
michael@0 963 void
michael@0 964 imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect)
michael@0 965 {
michael@0 966 MOZ_ASSERT(NS_IsMainThread());
michael@0 967 RecordFrameChanged(aDirtyRect);
michael@0 968
michael@0 969 /* notify the kids */
michael@0 970 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 971 while (iter.HasMore()) {
michael@0 972 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 973 if (proxy) {
michael@0 974 SendFrameChanged(proxy, aDirtyRect);
michael@0 975 }
michael@0 976 }
michael@0 977 }
michael@0 978
michael@0 979 void
michael@0 980 imgStatusTracker::OnStopFrame()
michael@0 981 {
michael@0 982 MOZ_ASSERT(NS_IsMainThread());
michael@0 983 RecordStopFrame();
michael@0 984
michael@0 985 /* notify the kids */
michael@0 986 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 987 while (iter.HasMore()) {
michael@0 988 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 989 if (proxy) {
michael@0 990 SendStopFrame(proxy);
michael@0 991 }
michael@0 992 }
michael@0 993 }
michael@0 994
michael@0 995 void
michael@0 996 imgStatusTracker::OnDataAvailable()
michael@0 997 {
michael@0 998 if (!NS_IsMainThread()) {
michael@0 999 // Note: SetHasImage calls Image::Lock and Image::IncrementAnimationCounter
michael@0 1000 // so subsequent calls or dispatches which Unlock or Decrement~ should
michael@0 1001 // be issued after this to avoid race conditions.
michael@0 1002 NS_DispatchToMainThread(
michael@0 1003 NS_NewRunnableMethod(this, &imgStatusTracker::OnDataAvailable));
michael@0 1004 return;
michael@0 1005 }
michael@0 1006 // Notify any imgRequestProxys that are observing us that we have an Image.
michael@0 1007 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 1008 while (iter.HasMore()) {
michael@0 1009 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 1010 if (proxy) {
michael@0 1011 proxy->SetHasImage();
michael@0 1012 }
michael@0 1013 }
michael@0 1014 }
michael@0 1015
michael@0 1016 void
michael@0 1017 imgStatusTracker::RecordBlockOnload()
michael@0 1018 {
michael@0 1019 MOZ_ASSERT(!(mState & stateBlockingOnload));
michael@0 1020 mState |= stateBlockingOnload;
michael@0 1021 }
michael@0 1022
michael@0 1023 void
michael@0 1024 imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy)
michael@0 1025 {
michael@0 1026 MOZ_ASSERT(NS_IsMainThread());
michael@0 1027 if (!aProxy->NotificationsDeferred()) {
michael@0 1028 aProxy->BlockOnload();
michael@0 1029 }
michael@0 1030 }
michael@0 1031
michael@0 1032 void
michael@0 1033 imgStatusTracker::RecordUnblockOnload()
michael@0 1034 {
michael@0 1035 mState &= ~stateBlockingOnload;
michael@0 1036 }
michael@0 1037
michael@0 1038 void
michael@0 1039 imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy)
michael@0 1040 {
michael@0 1041 MOZ_ASSERT(NS_IsMainThread());
michael@0 1042 if (!aProxy->NotificationsDeferred()) {
michael@0 1043 aProxy->UnblockOnload();
michael@0 1044 }
michael@0 1045 }
michael@0 1046
michael@0 1047 void
michael@0 1048 imgStatusTracker::MaybeUnblockOnload()
michael@0 1049 {
michael@0 1050 if (!NS_IsMainThread()) {
michael@0 1051 NS_DispatchToMainThread(
michael@0 1052 NS_NewRunnableMethod(this, &imgStatusTracker::MaybeUnblockOnload));
michael@0 1053 return;
michael@0 1054 }
michael@0 1055 if (!(mState & stateBlockingOnload)) {
michael@0 1056 return;
michael@0 1057 }
michael@0 1058
michael@0 1059 RecordUnblockOnload();
michael@0 1060
michael@0 1061 ProxyArray::ForwardIterator iter(mConsumers);
michael@0 1062 while (iter.HasMore()) {
michael@0 1063 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
michael@0 1064 if (proxy) {
michael@0 1065 SendUnblockOnload(proxy);
michael@0 1066 }
michael@0 1067 }
michael@0 1068 }
michael@0 1069
michael@0 1070 void
michael@0 1071 imgStatusTracker::RecordError()
michael@0 1072 {
michael@0 1073 mImageStatus = imgIRequest::STATUS_ERROR;
michael@0 1074 }
michael@0 1075
michael@0 1076 void
michael@0 1077 imgStatusTracker::FireFailureNotification()
michael@0 1078 {
michael@0 1079 MOZ_ASSERT(NS_IsMainThread());
michael@0 1080
michael@0 1081 // Some kind of problem has happened with image decoding.
michael@0 1082 // Report the URI to net:failed-to-process-uri-conent observers.
michael@0 1083 if (mImage) {
michael@0 1084 // Should be on main thread, so ok to create a new nsIURI.
michael@0 1085 nsCOMPtr<nsIURI> uri;
michael@0 1086 {
michael@0 1087 nsRefPtr<ImageURL> threadsafeUriData = mImage->GetURI();
michael@0 1088 uri = threadsafeUriData ? threadsafeUriData->ToIURI() : nullptr;
michael@0 1089 }
michael@0 1090 if (uri) {
michael@0 1091 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
michael@0 1092 if (os) {
michael@0 1093 os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
michael@0 1094 }
michael@0 1095 }
michael@0 1096 }
michael@0 1097 }

mercurial