LCOV - code coverage report
Current view: top level - image - Image.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 20 163 12.3 %
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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "Image.h"
       7             : #include "Layers.h"               // for LayerManager
       8             : #include "nsRefreshDriver.h"
       9             : #include "nsContentUtils.h"
      10             : #include "mozilla/SizeOfState.h"
      11             : #include "mozilla/TimeStamp.h"
      12             : #include "mozilla/Tuple.h"        // for Tie
      13             : 
      14             : namespace mozilla {
      15             : namespace image {
      16             : 
      17             : ///////////////////////////////////////////////////////////////////////////////
      18             : // Memory Reporting
      19             : ///////////////////////////////////////////////////////////////////////////////
      20             : 
      21           0 : ImageMemoryCounter::ImageMemoryCounter(Image* aImage,
      22             :                                        SizeOfState& aState,
      23           0 :                                        bool aIsUsed)
      24           0 :   : mIsUsed(aIsUsed)
      25             : {
      26           0 :   MOZ_ASSERT(aImage);
      27             : 
      28             :   // Extract metadata about the image.
      29           0 :   nsCOMPtr<nsIURI> imageURL(aImage->GetURI());
      30           0 :   if (imageURL) {
      31           0 :     imageURL->GetSpec(mURI);
      32             :   }
      33             : 
      34           0 :   int32_t width = 0;
      35           0 :   int32_t height = 0;
      36           0 :   aImage->GetWidth(&width);
      37           0 :   aImage->GetHeight(&height);
      38           0 :   mIntrinsicSize.SizeTo(width, height);
      39             : 
      40           0 :   mType = aImage->GetType();
      41             : 
      42             :   // Populate memory counters for source and decoded data.
      43           0 :   mValues.SetSource(aImage->SizeOfSourceWithComputedFallback(aState));
      44           0 :   aImage->CollectSizeOfSurfaces(mSurfaces, aState.mMallocSizeOf);
      45             : 
      46             :   // Compute totals.
      47           0 :   for (const SurfaceMemoryCounter& surfaceCounter : mSurfaces) {
      48           0 :     mValues += surfaceCounter.Values();
      49             :   }
      50           0 : }
      51             : 
      52             : 
      53             : ///////////////////////////////////////////////////////////////////////////////
      54             : // Image Base Types
      55             : ///////////////////////////////////////////////////////////////////////////////
      56             : 
      57             : bool
      58           0 : ImageResource::GetSpecTruncatedTo1k(nsCString& aSpec) const
      59             : {
      60             :   static const size_t sMaxTruncatedLength = 1024;
      61             : 
      62           0 :   mURI->GetSpec(aSpec);
      63           0 :   if (sMaxTruncatedLength >= aSpec.Length()) {
      64             :     return true;
      65             :   }
      66             : 
      67           0 :   aSpec.Truncate(sMaxTruncatedLength);
      68           0 :   return false;
      69             : }
      70             : 
      71             : void
      72           0 : ImageResource::SetCurrentImage(ImageContainer* aContainer,
      73             :                                SourceSurface* aSurface,
      74             :                                bool aInTransaction)
      75             : {
      76           0 :   MOZ_ASSERT(NS_IsMainThread());
      77           0 :   MOZ_ASSERT(aContainer);
      78             : 
      79           0 :   if (!aSurface) {
      80             :     // The OS threw out some or all of our buffer. We'll need to wait for the
      81             :     // redecode (which was automatically triggered by GetFrame) to complete.
      82           0 :     return;
      83             :   }
      84             : 
      85             :   // |image| holds a reference to a SourceSurface which in turn holds a lock on
      86             :   // the current frame's data buffer, ensuring that it doesn't get freed as
      87             :   // long as the layer system keeps this ImageContainer alive.
      88           0 :   RefPtr<layers::Image> image = new layers::SourceSurfaceImage(aSurface);
      89             : 
      90             :   // We can share the producer ID with other containers because it is only
      91             :   // used internally to validate the frames given to a particular container
      92             :   // so that another object cannot add its own. Similarly the frame ID is
      93             :   // only used internally to ensure it is always increasing, and skipping
      94             :   // IDs from an individual container's perspective is acceptable.
      95           0 :   AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
      96           0 :   imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
      97           0 :                                                          mLastFrameID++,
      98           0 :                                                          mImageProducerID));
      99             : 
     100           0 :   if (aInTransaction) {
     101           0 :     aContainer->SetCurrentImagesInTransaction(imageList);
     102             :   } else {
     103           0 :     aContainer->SetCurrentImages(imageList);
     104             :   }
     105             : }
     106             : 
     107             : already_AddRefed<ImageContainer>
     108           0 : ImageResource::GetImageContainerImpl(LayerManager* aManager,
     109             :                                      const IntSize& aSize,
     110             :                                      const Maybe<SVGImageContext>& aSVGContext,
     111             :                                      uint32_t aFlags)
     112             : {
     113           0 :   MOZ_ASSERT(NS_IsMainThread());
     114           0 :   MOZ_ASSERT(aManager);
     115           0 :   MOZ_ASSERT((aFlags & ~(FLAG_SYNC_DECODE |
     116             :                          FLAG_SYNC_DECODE_IF_FAST |
     117             :                          FLAG_ASYNC_NOTIFY |
     118             :                          FLAG_HIGH_QUALITY_SCALING))
     119             :                == FLAG_NONE,
     120             :              "Unsupported flag passed to GetImageContainer");
     121             : 
     122           0 :   IntSize size = GetImageContainerSize(aManager, aSize, aFlags);
     123           0 :   if (size.IsEmpty()) {
     124             :     return nullptr;
     125             :   }
     126             : 
     127           0 :   if (mAnimationConsumers == 0) {
     128           0 :     SendOnUnlockedDraw(aFlags);
     129             :   }
     130             : 
     131             :   uint32_t flags = (aFlags & ~(FLAG_SYNC_DECODE |
     132           0 :                                FLAG_SYNC_DECODE_IF_FAST)) | FLAG_ASYNC_NOTIFY;
     133           0 :   RefPtr<layers::ImageContainer> container;
     134           0 :   ImageContainerEntry* entry = nullptr;
     135           0 :   int i = mImageContainers.Length() - 1;
     136           0 :   for (; i >= 0; --i) {
     137           0 :     entry = &mImageContainers[i];
     138           0 :     container = entry->mContainer.get();
     139           0 :     if (size == entry->mSize && flags == entry->mFlags &&
     140           0 :         aSVGContext == entry->mSVGContext) {
     141             :       // Lack of a container is handled below.
     142             :       break;
     143           0 :     } else if (!container) {
     144             :       // Stop tracking if our weak pointer to the image container was freed.
     145           0 :       mImageContainers.RemoveElementAt(i);
     146             :     } else {
     147             :       // It isn't a match, but still valid. Forget the container so we don't
     148             :       // try to reuse it below.
     149           0 :       container = nullptr;
     150             :     }
     151             :   }
     152             : 
     153           0 :   if (container) {
     154           0 :     switch (entry->mLastDrawResult) {
     155             :       case ImgDrawResult::SUCCESS:
     156             :       case ImgDrawResult::BAD_IMAGE:
     157             :       case ImgDrawResult::BAD_ARGS:
     158             :         return container.forget();
     159             :       case ImgDrawResult::NOT_READY:
     160             :       case ImgDrawResult::INCOMPLETE:
     161             :       case ImgDrawResult::TEMPORARY_ERROR:
     162             :         // Temporary conditions where we need to rerequest the frame to recover.
     163             :         break;
     164             :       case ImgDrawResult::WRONG_SIZE:
     165             :         // Unused by GetFrameInternal
     166             :       default:
     167           0 :         MOZ_ASSERT_UNREACHABLE("Unhandled ImgDrawResult type!");
     168             :         return container.forget();
     169             :     }
     170             :   }
     171             : 
     172             : #ifdef DEBUG
     173           0 :   NotifyDrawingObservers();
     174             : #endif
     175             : 
     176             :   ImgDrawResult drawResult;
     177           0 :   IntSize bestSize;
     178           0 :   RefPtr<SourceSurface> surface;
     179           0 :   Tie(drawResult, bestSize, surface) =
     180           0 :     GetFrameInternal(size, aSVGContext, FRAME_CURRENT,
     181           0 :                      aFlags | FLAG_ASYNC_NOTIFY);
     182             : 
     183             :   // The requested size might be refused by the surface cache (i.e. due to
     184             :   // factor-of-2 mode). In that case we don't want to create an entry for this
     185             :   // specific size, but rather re-use the entry for the substituted size.
     186           0 :   if (bestSize != size) {
     187           0 :     MOZ_ASSERT(!bestSize.IsEmpty());
     188             : 
     189             :     // We can only remove the entry if we no longer have a container, because if
     190             :     // there are strong references to it remaining, we need to still update it
     191             :     // in UpdateImageContainer.
     192           0 :     if (i >= 0 && !container) {
     193           0 :       mImageContainers.RemoveElementAt(i);
     194             :     }
     195             : 
     196             :     // Forget about the stale container, if any. This lets the entry creation
     197             :     // logic do its job below, if it turns out there is no existing best entry
     198             :     // or the best entry doesn't have a container.
     199           0 :     container = nullptr;
     200             : 
     201             :     // We need to do the entry search again for the new size. We skip pruning
     202             :     // because we did this above once already, but ImageContainer is threadsafe,
     203             :     // so there is a remote possibility it got freed.
     204           0 :     i = mImageContainers.Length() - 1;
     205           0 :     for (; i >= 0; --i) {
     206           0 :       entry = &mImageContainers[i];
     207           0 :       if (bestSize == entry->mSize && flags == entry->mFlags &&
     208           0 :           aSVGContext == entry->mSVGContext) {
     209           0 :         container = entry->mContainer.get();
     210           0 :         if (container) {
     211           0 :           switch (entry->mLastDrawResult) {
     212             :             case ImgDrawResult::SUCCESS:
     213             :             case ImgDrawResult::BAD_IMAGE:
     214             :             case ImgDrawResult::BAD_ARGS:
     215             :               return container.forget();
     216             :             case ImgDrawResult::NOT_READY:
     217             :             case ImgDrawResult::INCOMPLETE:
     218             :             case ImgDrawResult::TEMPORARY_ERROR:
     219             :               // Temporary conditions where we need to rerequest the frame to
     220             :               // recover. We have already done so!
     221             :               break;
     222             :            case ImgDrawResult::WRONG_SIZE:
     223             :               // Unused by GetFrameInternal
     224             :             default:
     225           0 :               MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!");
     226             :               return container.forget();
     227             :           }
     228             :         }
     229             :         break;
     230             :       }
     231             :     }
     232             :   }
     233             : 
     234           0 :   if (!container) {
     235             :     // We need a new ImageContainer, so create one.
     236           0 :     container = LayerManager::CreateImageContainer();
     237             : 
     238           0 :     if (i >= 0) {
     239           0 :       entry->mContainer = container;
     240             :     } else {
     241           0 :       entry = mImageContainers.AppendElement(
     242           0 :         ImageContainerEntry(bestSize, aSVGContext, container.get(), flags));
     243             :     }
     244             :   }
     245             : 
     246           0 :   SetCurrentImage(container, surface, true);
     247           0 :   entry->mLastDrawResult = drawResult;
     248             :   return container.forget();
     249             : }
     250             : 
     251             : void
     252           3 : ImageResource::UpdateImageContainer()
     253             : {
     254           3 :   MOZ_ASSERT(NS_IsMainThread());
     255             : 
     256           0 :   for (int i = mImageContainers.Length() - 1; i >= 0; --i) {
     257           0 :     ImageContainerEntry& entry = mImageContainers[i];
     258           0 :     RefPtr<ImageContainer> container = entry.mContainer.get();
     259           0 :     if (container) {
     260           0 :       IntSize bestSize;
     261           0 :       RefPtr<SourceSurface> surface;
     262           0 :       Tie(entry.mLastDrawResult, bestSize, surface) =
     263           0 :         GetFrameInternal(entry.mSize, entry.mSVGContext,
     264           0 :                          FRAME_CURRENT, entry.mFlags);
     265             : 
     266             :       // It is possible that this is a factor-of-2 substitution. Since we
     267             :       // managed to convert the weak reference into a strong reference, that
     268             :       // means that an imagelib user still is holding onto the container. thus
     269             :       // we cannot consolidate and must keep updating the duplicate container.
     270           0 :       SetCurrentImage(container, surface, false);
     271             :     } else {
     272             :       // Stop tracking if our weak pointer to the image container was freed.
     273           0 :       mImageContainers.RemoveElementAt(i);
     274             :     }
     275             :   }
     276           3 : }
     277             : 
     278             : void
     279           0 : ImageResource::ReleaseImageContainer()
     280             : {
     281           0 :   MOZ_ASSERT(NS_IsMainThread());
     282           0 :   mImageContainers.Clear();
     283           0 : }
     284             : 
     285             : // Constructor
     286          20 : ImageResource::ImageResource(nsIURI* aURI) :
     287             :   mURI(aURI),
     288             :   mInnerWindowId(0),
     289             :   mAnimationConsumers(0),
     290             :   mAnimationMode(kNormalAnimMode),
     291             :   mInitialized(false),
     292             :   mAnimating(false),
     293             :   mError(false),
     294          20 :   mImageProducerID(ImageContainer::AllocateProducerID()),
     295           0 :   mLastFrameID(0)
     296           0 : { }
     297             : 
     298          12 : ImageResource::~ImageResource()
     299             : {
     300             :   // Ask our ProgressTracker to drop its weak reference to us.
     301           0 :   mProgressTracker->ResetImage();
     302           4 : }
     303             : 
     304             : void
     305           0 : ImageResource::IncrementAnimationConsumers()
     306             : {
     307          20 :   MOZ_ASSERT(NS_IsMainThread(), "Main thread only to encourage serialization "
     308             :                                 "with DecrementAnimationConsumers");
     309           0 :   mAnimationConsumers++;
     310          20 : }
     311             : 
     312             : void
     313           2 : ImageResource::DecrementAnimationConsumers()
     314             : {
     315           0 :   MOZ_ASSERT(NS_IsMainThread(), "Main thread only to encourage serialization "
     316             :                                 "with IncrementAnimationConsumers");
     317           0 :   MOZ_ASSERT(mAnimationConsumers >= 1,
     318             :              "Invalid no. of animation consumers!");
     319           2 :   mAnimationConsumers--;
     320           2 : }
     321             : 
     322             : nsresult
     323           0 : ImageResource::GetAnimationModeInternal(uint16_t* aAnimationMode)
     324             : {
     325           0 :   if (mError) {
     326             :     return NS_ERROR_FAILURE;
     327             :   }
     328             : 
     329           0 :   NS_ENSURE_ARG_POINTER(aAnimationMode);
     330             : 
     331           0 :   *aAnimationMode = mAnimationMode;
     332           0 :   return NS_OK;
     333             : }
     334             : 
     335             : nsresult
     336          17 : ImageResource::SetAnimationModeInternal(uint16_t aAnimationMode)
     337             : {
     338          17 :   if (mError) {
     339             :     return NS_ERROR_FAILURE;
     340             :   }
     341             : 
     342          17 :   NS_ASSERTION(aAnimationMode == kNormalAnimMode ||
     343             :                aAnimationMode == kDontAnimMode ||
     344             :                aAnimationMode == kLoopOnceAnimMode,
     345             :                "Wrong Animation Mode is being set!");
     346             : 
     347           0 :   mAnimationMode = aAnimationMode;
     348             : 
     349          17 :   return NS_OK;
     350             : }
     351             : 
     352             : bool
     353           0 : ImageResource::HadRecentRefresh(const TimeStamp& aTime)
     354             : {
     355             :   // Our threshold for "recent" is 1/2 of the default refresh-driver interval.
     356             :   // This ensures that we allow for frame rates at least as fast as the
     357             :   // refresh driver's default rate.
     358             :   static TimeDuration recentThreshold =
     359           0 :       TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval() / 2.0);
     360             : 
     361           0 :   if (!mLastRefreshTime.IsNull() &&
     362           0 :       aTime - mLastRefreshTime < recentThreshold) {
     363             :     return true;
     364             :   }
     365             : 
     366             :   // else, we can proceed with a refresh.
     367             :   // But first, update our last refresh time:
     368           0 :   mLastRefreshTime = aTime;
     369           0 :   return false;
     370             : }
     371             : 
     372             : void
     373          13 : ImageResource::EvaluateAnimation()
     374             : {
     375          13 :   if (!mAnimating && ShouldAnimate()) {
     376           0 :     nsresult rv = StartAnimation();
     377           0 :     mAnimating = NS_SUCCEEDED(rv);
     378          13 :   } else if (mAnimating && !ShouldAnimate()) {
     379           0 :     StopAnimation();
     380             :   }
     381           0 : }
     382             : 
     383             : void
     384           0 : ImageResource::SendOnUnlockedDraw(uint32_t aFlags)
     385             : {
     386           0 :   if (!mProgressTracker) {
     387             :     return;
     388             :   }
     389             : 
     390           0 :   if (!(aFlags & FLAG_ASYNC_NOTIFY)) {
     391           0 :     mProgressTracker->OnUnlockedDraw();
     392             :   } else {
     393           0 :     NotNull<RefPtr<ImageResource>> image = WrapNotNull(this);
     394           0 :     nsCOMPtr<nsIEventTarget> eventTarget = mProgressTracker->GetEventTarget();
     395           0 :     nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction(
     396           0 :       "image::ImageResource::SendOnUnlockedDraw", [=]() -> void {
     397           0 :         RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
     398           0 :         if (tracker) {
     399           0 :           tracker->OnUnlockedDraw();
     400             :         }
     401           0 :       });
     402           0 :     eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
     403             :   }
     404             : }
     405             : 
     406             : #ifdef DEBUG
     407             : void
     408           0 : ImageResource::NotifyDrawingObservers()
     409             : {
     410           0 :   if (!mURI || !NS_IsMainThread()) {
     411           0 :     return;
     412             :   }
     413             : 
     414           0 :   bool match = false;
     415           0 :   if ((NS_FAILED(mURI->SchemeIs("resource", &match)) || !match) &&
     416          44 :       (NS_FAILED(mURI->SchemeIs("chrome", &match)) || !match)) {
     417             :     return;
     418             :   }
     419             : 
     420             :   // Record the image drawing for startup performance testing.
     421             :   nsCOMPtr<nsIURI> uri = mURI;
     422             :   nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
     423             :     "image::ImageResource::NotifyDrawingObservers", [uri]() {
     424             :       nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     425             :       NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
     426             :       if (obs) {
     427             :         nsAutoCString spec;
     428             :         uri->GetSpec(spec);
     429             :         obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
     430             :       }
     431             :     }));
     432             : }
     433             : #endif
     434             : 
     435             : } // namespace image
     436             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952