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

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

mercurial