LCOV - code coverage report
Current view: top level - image - ProgressTracker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 69 259 26.6 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "ImageLogging.h"
       8             : #include "ProgressTracker.h"
       9             : 
      10             : #include "imgIContainer.h"
      11             : #include "imgINotificationObserver.h"
      12             : #include "imgIRequest.h"
      13             : #include "Image.h"
      14             : #include "nsNetUtil.h"
      15             : #include "nsIObserverService.h"
      16             : 
      17             : #include "mozilla/Assertions.h"
      18             : #include "mozilla/Services.h"
      19             : #include "mozilla/SystemGroup.h"
      20             : 
      21             : using mozilla::WeakPtr;
      22             : 
      23             : namespace mozilla {
      24             : namespace image {
      25             : 
      26             : static void
      27           0 : CheckProgressConsistency(Progress aOldProgress, Progress aNewProgress, bool aIsMultipart)
      28             : {
      29             :   // Check preconditions for every progress bit.
      30             : 
      31             :   // Error's do not get propagated from the tracker for each image part to the
      32             :   // tracker for the multipart image because we don't want one bad part to
      33             :   // prevent the remaining parts from showing. So we need to consider whether
      34             :   // this is a tracker for a multipart image for these assertions to work.
      35             : 
      36           0 :   if (aNewProgress & FLAG_SIZE_AVAILABLE) {
      37             :     // No preconditions.
      38             :   }
      39           0 :   if (aNewProgress & FLAG_DECODE_COMPLETE) {
      40           0 :     MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE);
      41           0 :     MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_FRAME_COMPLETE | FLAG_HAS_ERROR));
      42             :   }
      43           0 :   if (aNewProgress & FLAG_FRAME_COMPLETE) {
      44           0 :     MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE);
      45             :   }
      46           0 :   if (aNewProgress & FLAG_LOAD_COMPLETE) {
      47           0 :     MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_SIZE_AVAILABLE | FLAG_HAS_ERROR));
      48             :   }
      49             :   if (aNewProgress & FLAG_IS_ANIMATED) {
      50             :     // No preconditions; like FLAG_HAS_TRANSPARENCY, we should normally never
      51             :     // discover this *after* FLAG_SIZE_AVAILABLE, but unfortunately some corrupt
      52             :     // GIFs may fool us.
      53             :   }
      54             :   if (aNewProgress & FLAG_HAS_TRANSPARENCY) {
      55             :     // XXX We'd like to assert that transparency is only set during metadata
      56             :     // decode but we don't have any way to assert that until bug 1254892 is fixed.
      57             :   }
      58           0 :   if (aNewProgress & FLAG_LAST_PART_COMPLETE) {
      59           0 :     MOZ_ASSERT(aNewProgress & FLAG_LOAD_COMPLETE);
      60             :   }
      61             :   if (aNewProgress & FLAG_HAS_ERROR) {
      62             :     // No preconditions.
      63             :   }
      64           0 : }
      65             : 
      66           0 : ProgressTracker::ProgressTracker()
      67             :   : mMutex("ProgressTracker::mMutex")
      68             :   , mImage(nullptr)
      69           0 :   , mEventTarget(WrapNotNull(nsCOMPtr<nsIEventTarget>(SystemGroup::EventTargetFor(TaskCategory::Other))))
      70             :   , mObserversWithTargets(0)
      71           0 :   , mObservers(new ObserverTable)
      72             :   , mProgress(NoProgress)
      73           0 :   , mIsMultipart(false)
      74           0 : { }
      75             : 
      76             : void
      77           0 : ProgressTracker::SetImage(Image* aImage)
      78             : {
      79           0 :   MutexAutoLock lock(mMutex);
      80           0 :   MOZ_ASSERT(aImage, "Setting null image");
      81           0 :   MOZ_ASSERT(!mImage, "Setting image when we already have one");
      82           0 :   mImage = aImage;
      83           0 : }
      84             : 
      85             : void
      86           0 : ProgressTracker::ResetImage()
      87             : {
      88           0 :   MutexAutoLock lock(mMutex);
      89           0 :   MOZ_ASSERT(mImage, "Resetting image when it's already null!");
      90           0 :   mImage = nullptr;
      91           0 : }
      92             : 
      93             : uint32_t
      94           0 : ProgressTracker::GetImageStatus() const
      95             : {
      96           0 :   uint32_t status = imgIRequest::STATUS_NONE;
      97             : 
      98             :   // Translate our current state to a set of imgIRequest::STATE_* flags.
      99           0 :   if (mProgress & FLAG_SIZE_AVAILABLE) {
     100           0 :     status |= imgIRequest::STATUS_SIZE_AVAILABLE;
     101             :   }
     102           0 :   if (mProgress & FLAG_DECODE_COMPLETE) {
     103           0 :     status |= imgIRequest::STATUS_DECODE_COMPLETE;
     104             :   }
     105           0 :   if (mProgress & FLAG_FRAME_COMPLETE) {
     106           0 :     status |= imgIRequest::STATUS_FRAME_COMPLETE;
     107             :   }
     108           0 :   if (mProgress & FLAG_LOAD_COMPLETE) {
     109           0 :     status |= imgIRequest::STATUS_LOAD_COMPLETE;
     110             :   }
     111           0 :   if (mProgress & FLAG_IS_ANIMATED) {
     112           0 :     status |= imgIRequest::STATUS_IS_ANIMATED;
     113             :   }
     114           0 :   if (mProgress & FLAG_HAS_TRANSPARENCY) {
     115           0 :     status |= imgIRequest::STATUS_HAS_TRANSPARENCY;
     116             :   }
     117           0 :   if (mProgress & FLAG_HAS_ERROR) {
     118           0 :     status |= imgIRequest::STATUS_ERROR;
     119             :   }
     120             : 
     121           0 :   return status;
     122             : }
     123             : 
     124             : // A helper class to allow us to call SyncNotify asynchronously.
     125           0 : class AsyncNotifyRunnable : public Runnable
     126             : {
     127             :   public:
     128           0 :     AsyncNotifyRunnable(ProgressTracker* aTracker,
     129             :                         IProgressObserver* aObserver)
     130           0 :      : Runnable("ProgressTracker::AsyncNotifyRunnable")
     131           0 :      , mTracker(aTracker)
     132             :     {
     133           0 :       MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
     134           0 :       MOZ_ASSERT(aTracker, "aTracker should not be null");
     135           0 :       MOZ_ASSERT(aObserver, "aObserver should not be null");
     136           0 :       mObservers.AppendElement(aObserver);
     137           0 :     }
     138             : 
     139           0 :     NS_IMETHOD Run() override
     140             :     {
     141           0 :       MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
     142           0 :       MOZ_ASSERT(mTracker, "mTracker should not be null");
     143           0 :       for (uint32_t i = 0; i < mObservers.Length(); ++i) {
     144           0 :         mObservers[i]->ClearPendingNotify();
     145           0 :         mTracker->SyncNotify(mObservers[i]);
     146             :       }
     147             : 
     148           0 :       mTracker->mRunnable = nullptr;
     149           0 :       return NS_OK;
     150             :     }
     151             : 
     152             :     void AddObserver(IProgressObserver* aObserver)
     153             :     {
     154           0 :       mObservers.AppendElement(aObserver);
     155             :     }
     156             : 
     157             :     void RemoveObserver(IProgressObserver* aObserver)
     158             :     {
     159           0 :       mObservers.RemoveElement(aObserver);
     160             :     }
     161             : 
     162             :   private:
     163             :     friend class ProgressTracker;
     164             : 
     165             :     RefPtr<ProgressTracker> mTracker;
     166             :     nsTArray<RefPtr<IProgressObserver>> mObservers;
     167             : };
     168             : 
     169             : void
     170           0 : ProgressTracker::Notify(IProgressObserver* aObserver)
     171             : {
     172           0 :   MOZ_ASSERT(NS_IsMainThread());
     173             : 
     174           0 :   if (aObserver->NotificationsDeferred()) {
     175             :     // There is a pending notification, or the observer isn't ready yet.
     176             :     return;
     177             :   }
     178             : 
     179           0 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     180           0 :     RefPtr<Image> image = GetImage();
     181             :     LOG_FUNC_WITH_PARAM(gImgLog,
     182           0 :                         "ProgressTracker::Notify async", "uri", image);
     183             :   }
     184             : 
     185           1 :   aObserver->MarkPendingNotify();
     186             : 
     187             :   // If we have an existing runnable that we can use, we just append this
     188             :   // observer to its list of observers to be notified. This ensures we don't
     189             :   // unnecessarily delay onload.
     190             :   AsyncNotifyRunnable* runnable =
     191           2 :     static_cast<AsyncNotifyRunnable*>(mRunnable.get());
     192             : 
     193           0 :   if (runnable) {
     194             :     runnable->AddObserver(aObserver);
     195             :   } else {
     196           1 :     mRunnable = new AsyncNotifyRunnable(this, aObserver);
     197           3 :     mEventTarget->Dispatch(mRunnable, NS_DISPATCH_NORMAL);
     198             :   }
     199             : }
     200             : 
     201             : // A helper class to allow us to call SyncNotify asynchronously for a given,
     202             : // fixed, state.
     203           0 : class AsyncNotifyCurrentStateRunnable : public Runnable
     204             : {
     205             :   public:
     206           0 :     AsyncNotifyCurrentStateRunnable(ProgressTracker* aProgressTracker,
     207             :                                     IProgressObserver* aObserver)
     208           0 :       : Runnable("image::AsyncNotifyCurrentStateRunnable")
     209             :       , mProgressTracker(aProgressTracker)
     210           0 :       , mObserver(aObserver)
     211             :     {
     212           0 :       MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
     213           0 :       MOZ_ASSERT(mProgressTracker, "mProgressTracker should not be null");
     214           0 :       MOZ_ASSERT(mObserver, "mObserver should not be null");
     215           0 :       mImage = mProgressTracker->GetImage();
     216           0 :     }
     217             : 
     218           0 :     NS_IMETHOD Run() override
     219             :     {
     220           0 :       MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
     221           0 :       mObserver->ClearPendingNotify();
     222             : 
     223           0 :       mProgressTracker->SyncNotify(mObserver);
     224           0 :       return NS_OK;
     225             :     }
     226             : 
     227             :   private:
     228             :     RefPtr<ProgressTracker> mProgressTracker;
     229             :     RefPtr<IProgressObserver> mObserver;
     230             : 
     231             :     // We have to hold on to a reference to the tracker's image, just in case
     232             :     // it goes away while we're in the event queue.
     233             :     RefPtr<Image> mImage;
     234             : };
     235             : 
     236             : void
     237           0 : ProgressTracker::NotifyCurrentState(IProgressObserver* aObserver)
     238             : {
     239           0 :   MOZ_ASSERT(NS_IsMainThread());
     240             : 
     241           0 :   if (aObserver->NotificationsDeferred()) {
     242             :     // There is a pending notification, or the observer isn't ready yet.
     243           0 :     return;
     244             :   }
     245             : 
     246           0 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     247           0 :     RefPtr<Image> image = GetImage();
     248             :     LOG_FUNC_WITH_PARAM(gImgLog,
     249           0 :                         "ProgressTracker::NotifyCurrentState", "uri", image);
     250             :   }
     251             : 
     252           0 :   aObserver->MarkPendingNotify();
     253             : 
     254             :   nsCOMPtr<nsIRunnable> ev = new AsyncNotifyCurrentStateRunnable(this,
     255           0 :                                                                  aObserver);
     256           0 :   mEventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
     257             : }
     258             : 
     259             : /**
     260             :  * ImageObserverNotifier is a helper type that abstracts over the difference
     261             :  * between sending notifications to all of the observers in an ObserverTable,
     262             :  * and sending them to a single observer. This allows the same notification code
     263             :  * to be used for both cases.
     264             :  */
     265             : template <typename T> struct ImageObserverNotifier;
     266             : 
     267             : template <>
     268             : struct MOZ_STACK_CLASS ImageObserverNotifier<const ObserverTable*>
     269             : {
     270             :   explicit ImageObserverNotifier(const ObserverTable* aObservers,
     271             :                                  bool aIgnoreDeferral = false)
     272          61 :     : mObservers(aObservers)
     273          61 :     , mIgnoreDeferral(aIgnoreDeferral)
     274             :   { }
     275             : 
     276             :   template <typename Lambda>
     277         135 :   void operator()(Lambda aFunc)
     278             :   {
     279        1195 :     for (auto iter = mObservers->ConstIter(); !iter.Done(); iter.Next()) {
     280         790 :       RefPtr<IProgressObserver> observer = iter.Data().get();
     281        1185 :       if (observer &&
     282         738 :           (mIgnoreDeferral || !observer->NotificationsDeferred())) {
     283         395 :         aFunc(observer);
     284             :       }
     285             :     }
     286         135 :   }
     287             : 
     288             : private:
     289             :   const ObserverTable* mObservers;
     290             :   const bool mIgnoreDeferral;
     291             : };
     292             : 
     293             : template <>
     294             : struct MOZ_STACK_CLASS ImageObserverNotifier<IProgressObserver*>
     295             : {
     296             :   explicit ImageObserverNotifier(IProgressObserver* aObserver)
     297          40 :     : mObserver(aObserver)
     298             :   { }
     299             : 
     300             :   template <typename Lambda>
     301          12 :   void operator()(Lambda aFunc)
     302             :   {
     303          12 :     if (mObserver && !mObserver->NotificationsDeferred()) {
     304          12 :       aFunc(mObserver);
     305             :     }
     306          12 :   }
     307             : 
     308             : private:
     309             :   IProgressObserver* mObserver;
     310             : };
     311             : 
     312             : template <typename T> void
     313           0 : SyncNotifyInternal(const T& aObservers,
     314             :                    bool aHasImage,
     315             :                    Progress aProgress,
     316             :                    const nsIntRect& aDirtyRect)
     317             : {
     318           0 :   MOZ_ASSERT(NS_IsMainThread());
     319             : 
     320             :   typedef imgINotificationObserver I;
     321         170 :   ImageObserverNotifier<T> notify(aObservers);
     322             : 
     323          85 :   if (aProgress & FLAG_SIZE_AVAILABLE) {
     324          76 :     notify([](IProgressObserver* aObs) { aObs->Notify(I::SIZE_AVAILABLE); });
     325             :   }
     326             : 
     327          85 :   if (aHasImage) {
     328             :     // OnFrameUpdate
     329             :     // If there's any content in this frame at all (always true for
     330             :     // vector images, true for raster images that have decoded at
     331             :     // least one frame) then send OnFrameUpdate.
     332          94 :     if (!aDirtyRect.IsEmpty()) {
     333           0 :       notify([&](IProgressObserver* aObs) {
     334          89 :         aObs->Notify(I::FRAME_UPDATE, &aDirtyRect);
     335           0 :       });
     336             :     }
     337             : 
     338          47 :     if (aProgress & FLAG_FRAME_COMPLETE) {
     339           0 :       notify([](IProgressObserver* aObs) { aObs->Notify(I::FRAME_COMPLETE); });
     340             :     }
     341             : 
     342          47 :     if (aProgress & FLAG_HAS_TRANSPARENCY) {
     343          76 :       notify([](IProgressObserver* aObs) { aObs->Notify(I::HAS_TRANSPARENCY); });
     344             :     }
     345             : 
     346           0 :     if (aProgress & FLAG_IS_ANIMATED) {
     347           0 :       notify([](IProgressObserver* aObs) { aObs->Notify(I::IS_ANIMATED); });
     348             :     }
     349             :   }
     350             : 
     351           0 :   if (aProgress & FLAG_DECODE_COMPLETE) {
     352          18 :     MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
     353          70 :     notify([](IProgressObserver* aObs) { aObs->Notify(I::DECODE_COMPLETE); });
     354             :   }
     355             : 
     356          85 :   if (aProgress & FLAG_LOAD_COMPLETE) {
     357          76 :     notify([=](IProgressObserver* aObs) {
     358           0 :       aObs->OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE);
     359           0 :     });
     360             :   }
     361          85 : }
     362             : 
     363             : void
     364           0 : ProgressTracker::SyncNotifyProgress(Progress aProgress,
     365             :                                     const nsIntRect& aInvalidRect
     366             :                                                   /* = nsIntRect() */)
     367             : {
     368           0 :   MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
     369             : 
     370           0 :   Progress progress = Difference(aProgress);
     371           0 :   CheckProgressConsistency(mProgress, mProgress | progress, mIsMultipart);
     372             : 
     373             :   // Apply the changes.
     374          45 :   mProgress |= progress;
     375             : 
     376             :   // Send notifications.
     377          90 :   mObservers.Read([&](const ObserverTable* aTable) {
     378          45 :     SyncNotifyInternal(aTable, HasImage(), progress, aInvalidRect);
     379         135 :   });
     380             : 
     381          45 :   if (progress & FLAG_HAS_ERROR) {
     382           0 :     FireFailureNotification();
     383             :   }
     384          45 : }
     385             : 
     386             : void
     387          40 : ProgressTracker::SyncNotify(IProgressObserver* aObserver)
     388             : {
     389           0 :   MOZ_ASSERT(NS_IsMainThread());
     390             : 
     391           0 :   RefPtr<Image> image = GetImage();
     392          80 :   LOG_SCOPE_WITH_PARAM(gImgLog,
     393             :                        "ProgressTracker::SyncNotify", "uri", image);
     394             : 
     395          40 :   nsIntRect rect;
     396           0 :   if (image) {
     397             :     int32_t width, height;
     398           4 :     if (NS_FAILED(image->GetWidth(&width)) ||
     399           0 :         NS_FAILED(image->GetHeight(&height))) {
     400             :       // Either the image has no intrinsic size, or it has an error.
     401           0 :       rect = GetMaxSizedIntRect();
     402             :     } else {
     403           0 :       rect.SizeTo(width, height);
     404             :     }
     405             :   }
     406             : 
     407           0 :   SyncNotifyInternal(aObserver, !!image, mProgress, rect);
     408          40 : }
     409             : 
     410             : void
     411           4 : ProgressTracker::EmulateRequestFinished(IProgressObserver* aObserver)
     412             : {
     413           0 :   MOZ_ASSERT(NS_IsMainThread(),
     414             :              "SyncNotifyState and mObservers are not threadsafe");
     415           0 :   RefPtr<IProgressObserver> kungFuDeathGrip(aObserver);
     416             : 
     417           4 :   if (!(mProgress & FLAG_LOAD_COMPLETE)) {
     418           0 :     aObserver->OnLoadComplete(true);
     419             :   }
     420           0 : }
     421             : 
     422             : already_AddRefed<nsIEventTarget>
     423          13 : ProgressTracker::GetEventTarget() const
     424             : {
     425           0 :   MutexAutoLock lock(mMutex);
     426          39 :   nsCOMPtr<nsIEventTarget> target = mEventTarget;
     427          26 :   return target.forget();
     428             : }
     429             : 
     430             : void
     431          56 : ProgressTracker::AddObserver(IProgressObserver* aObserver)
     432             : {
     433          56 :   MOZ_ASSERT(NS_IsMainThread());
     434           0 :   RefPtr<IProgressObserver> observer = aObserver;
     435             : 
     436         112 :   nsCOMPtr<nsIEventTarget> target = observer->GetEventTarget();
     437           0 :   if (target) {
     438          56 :     if (mObserversWithTargets == 0) {
     439             :       // On the first observer with a target (i.e. listener), always accept its
     440             :       // event target; this may be for a specific DocGroup, or it may be the
     441             :       // unlabelled main thread target.
     442           0 :       MutexAutoLock lock(mMutex);
     443           0 :       mEventTarget = WrapNotNull(target);
     444           0 :     } else if (mEventTarget.get() != target.get()) {
     445             :       // If a subsequent observer comes in with a different target, we need to
     446             :       // switch to use the unlabelled main thread target, if we haven't already.
     447           0 :       MutexAutoLock lock(mMutex);
     448           0 :       nsCOMPtr<nsIEventTarget> mainTarget(do_GetMainThread());
     449           0 :       mEventTarget = WrapNotNull(mainTarget);
     450             :     }
     451           0 :     ++mObserversWithTargets;
     452             :   }
     453             : 
     454           0 :   mObservers.Write([=](ObserverTable* aTable) {
     455           0 :     MOZ_ASSERT(!aTable->Get(observer, nullptr),
     456             :                "Adding duplicate entry for image observer");
     457             : 
     458         168 :     WeakPtr<IProgressObserver> weakPtr = observer.get();
     459           0 :     aTable->Put(observer, weakPtr);
     460           0 :   });
     461             : 
     462          56 :   MOZ_ASSERT(mObserversWithTargets <= ObserverCount());
     463          56 : }
     464             : 
     465             : bool
     466           0 : ProgressTracker::RemoveObserver(IProgressObserver* aObserver)
     467             : {
     468           0 :   MOZ_ASSERT(NS_IsMainThread());
     469          16 :   RefPtr<IProgressObserver> observer = aObserver;
     470             : 
     471             :   // Remove the observer from the list.
     472           0 :   bool removed = mObservers.Write([observer](ObserverTable* aTable) {
     473          16 :     return aTable->Remove(observer);
     474          16 :   });
     475             : 
     476             :   // Sometimes once an image is decoded, and all of its observers removed, a new
     477             :   // document may request the same image. Thus we need to clear our event target
     478             :   // state when the last observer is removed, so that we select the most
     479             :   // appropriate event target when a new observer is added. Since the event
     480             :   // target may have changed (e.g. due to the scheduler group going away before
     481             :   // we were removed), so we should be cautious comparing this target against
     482             :   // anything at this stage.
     483           0 :   if (removed) {
     484           8 :     nsCOMPtr<nsIEventTarget> target = observer->GetEventTarget();
     485           0 :     if (target) {
     486           0 :       MOZ_ASSERT(mObserversWithTargets > 0);
     487           4 :       --mObserversWithTargets;
     488             : 
     489           0 :       if (mObserversWithTargets == 0) {
     490           0 :         MutexAutoLock lock(mMutex);
     491           0 :         nsCOMPtr<nsIEventTarget> target(SystemGroup::EventTargetFor(TaskCategory::Other));
     492           0 :         mEventTarget = WrapNotNull(target);
     493             :       }
     494             :     }
     495             : 
     496           4 :     MOZ_ASSERT(mObserversWithTargets <= ObserverCount());
     497             :   }
     498             : 
     499             :   // Observers can get confused if they don't get all the proper teardown
     500             :   // notifications. Part ways on good terms.
     501           0 :   if (removed && !aObserver->NotificationsDeferred()) {
     502           0 :     EmulateRequestFinished(aObserver);
     503             :   }
     504             : 
     505             :   // Make sure we don't give callbacks to an observer that isn't interested in
     506             :   // them any more.
     507             :   AsyncNotifyRunnable* runnable =
     508           0 :     static_cast<AsyncNotifyRunnable*>(mRunnable.get());
     509             : 
     510           8 :   if (aObserver->NotificationsDeferred() && runnable) {
     511           0 :     runnable->RemoveObserver(aObserver);
     512           0 :     aObserver->ClearPendingNotify();
     513             :   }
     514             : 
     515           8 :   return removed;
     516             : }
     517             : 
     518             : uint32_t
     519           0 : ProgressTracker::ObserverCount() const
     520             : {
     521         136 :   MOZ_ASSERT(NS_IsMainThread());
     522         136 :   return mObservers.Read([](const ObserverTable* aTable) {
     523         136 :     return aTable->Count();
     524         272 :   });
     525             : }
     526             : 
     527             : void
     528           0 : ProgressTracker::OnUnlockedDraw()
     529             : {
     530           0 :   MOZ_ASSERT(NS_IsMainThread());
     531           0 :   mObservers.Read([](const ObserverTable* aTable) {
     532           0 :     ImageObserverNotifier<const ObserverTable*> notify(aTable);
     533           0 :     notify([](IProgressObserver* aObs) {
     534           0 :       aObs->Notify(imgINotificationObserver::UNLOCKED_DRAW);
     535           0 :     });
     536           0 :   });
     537           0 : }
     538             : 
     539             : void
     540           0 : ProgressTracker::ResetForNewRequest()
     541             : {
     542           0 :   MOZ_ASSERT(NS_IsMainThread());
     543           0 :   mProgress = NoProgress;
     544           0 : }
     545             : 
     546             : void
     547           0 : ProgressTracker::OnDiscard()
     548             : {
     549           0 :   MOZ_ASSERT(NS_IsMainThread());
     550           0 :   mObservers.Read([](const ObserverTable* aTable) {
     551           0 :     ImageObserverNotifier<const ObserverTable*> notify(aTable);
     552           0 :     notify([](IProgressObserver* aObs) {
     553           0 :       aObs->Notify(imgINotificationObserver::DISCARD);
     554           0 :     });
     555           0 :   });
     556           0 : }
     557             : 
     558             : void
     559           0 : ProgressTracker::OnImageAvailable()
     560             : {
     561           0 :   MOZ_ASSERT(NS_IsMainThread());
     562             :   // Notify any imgRequestProxys that are observing us that we have an Image.
     563          32 :   mObservers.Read([](const ObserverTable* aTable) {
     564             :     ImageObserverNotifier<const ObserverTable*>
     565          16 :       notify(aTable, /* aIgnoreDeferral = */ true);
     566           0 :     notify([](IProgressObserver* aObs) {
     567           0 :       aObs->SetHasImage();
     568           0 :     });
     569           0 :   });
     570           0 : }
     571             : 
     572             : void
     573           0 : ProgressTracker::FireFailureNotification()
     574             : {
     575           0 :   MOZ_ASSERT(NS_IsMainThread());
     576             : 
     577             :   // Some kind of problem has happened with image decoding.
     578             :   // Report the URI to net:failed-to-process-uri-conent observers.
     579           0 :   RefPtr<Image> image = GetImage();
     580           0 :   if (image) {
     581             :     // Should be on main thread, so ok to create a new nsIURI.
     582           0 :     nsCOMPtr<nsIURI> uri = image->GetURI();
     583           0 :     if (uri) {
     584           0 :       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     585           0 :       if (os) {
     586           0 :         os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
     587             :       }
     588             :     }
     589             :   }
     590           0 : }
     591             : 
     592             : } // namespace image
     593             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952