image/src/imgStatusTracker.cpp

changeset 0
6474c204b198
     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 +}

mercurial