LCOV - code coverage report
Current view: top level - layout/style - ImageLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 170 259 65.6 %
Date: 2017-07-14 16:53:18 Functions: 20 27 74.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       3             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : /* A class that handles style system image loads (other image loads are handled
       6             :  * by the nodes in the content tree).
       7             :  */
       8             : 
       9             : #include "mozilla/css/ImageLoader.h"
      10             : #include "nsAutoPtr.h"
      11             : #include "nsContentUtils.h"
      12             : #include "nsLayoutUtils.h"
      13             : #include "nsError.h"
      14             : #include "nsDisplayList.h"
      15             : #include "FrameLayerBuilder.h"
      16             : #include "nsSVGEffects.h"
      17             : #include "imgIContainer.h"
      18             : #include "Image.h"
      19             : 
      20             : namespace mozilla {
      21             : namespace css {
      22             : 
      23             : void
      24           0 : ImageLoader::DropDocumentReference()
      25             : {
      26             :   // It's okay if GetPresContext returns null here (due to the presshell pointer
      27             :   // on the document being null) as that means the presshell has already
      28             :   // been destroyed, and it also calls ClearFrames when it is destroyed.
      29           0 :   ClearFrames(GetPresContext());
      30             : 
      31           0 :   for (auto it = mImages.Iter(); !it.Done(); it.Next()) {
      32           0 :     ImageLoader::Image* image = it.Get()->GetKey();
      33           0 :     imgIRequest* request = image->mRequests.GetWeak(mDocument);
      34           0 :     if (request) {
      35           0 :       request->CancelAndForgetObserver(NS_BINDING_ABORTED);
      36             :     }
      37           0 :     image->mRequests.Remove(mDocument);
      38             :   }
      39           0 :   mImages.Clear();
      40             : 
      41           0 :   mDocument = nullptr;
      42           0 : }
      43             : 
      44             : void
      45          12 : ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
      46             :                                      nsIFrame* aFrame)
      47             : {
      48          24 :   nsCOMPtr<imgINotificationObserver> observer;
      49          12 :   aRequest->GetNotificationObserver(getter_AddRefs(observer));
      50          12 :   if (!observer) {
      51             :     // The request has already been canceled, so ignore it.  This is ok because
      52             :     // we're not going to get any more notifications from a canceled request.
      53           0 :     return;
      54             :   }
      55             : 
      56          12 :   MOZ_ASSERT(observer == this);
      57             : 
      58          12 :   FrameSet* frameSet = nullptr;
      59          12 :   if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
      60           1 :     NS_ASSERTION(frameSet, "This should never be null!");
      61             :   }
      62             : 
      63          12 :   if (!frameSet) {
      64          22 :     nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
      65             : 
      66          11 :     mRequestToFrameMap.Put(aRequest, newFrameSet);
      67          11 :     frameSet = newFrameSet.forget();
      68             : 
      69          11 :     nsPresContext* presContext = GetPresContext();
      70          11 :     if (presContext) {
      71             :       nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
      72             :                                                     aRequest,
      73          11 :                                                     nullptr);
      74             :     }
      75             :   }
      76             : 
      77          12 :   RequestSet* requestSet = nullptr;
      78          12 :   if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
      79           2 :     NS_ASSERTION(requestSet, "This should never be null");
      80             :   }
      81             : 
      82          12 :   if (!requestSet) {
      83          20 :     nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
      84             : 
      85          10 :     mFrameToRequestMap.Put(aFrame, newRequestSet);
      86          10 :     requestSet = newRequestSet.forget();
      87          10 :     aFrame->SetHasImageRequest(true);
      88             :   }
      89             : 
      90             :   // Add these to the sets, but only if they're not already there.
      91          12 :   uint32_t i = frameSet->IndexOfFirstElementGt(aFrame);
      92          12 :   if (i == 0 || aFrame != frameSet->ElementAt(i-1)) {
      93          12 :     frameSet->InsertElementAt(i, aFrame);
      94             :   }
      95          12 :   i = requestSet->IndexOfFirstElementGt(aRequest);
      96          12 :   if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
      97          12 :     requestSet->InsertElementAt(i, aRequest);
      98             :   }
      99             : }
     100             : 
     101             : void
     102         141 : ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
     103             : {
     104         141 :   NS_ASSERTION(aImage, "This should never be null!");
     105             : 
     106         141 :   bool found = false;
     107         141 :   aImage->mRequests.GetWeak(mDocument, &found);
     108         141 :   if (found) {
     109             :     // This document already has a request.
     110         282 :     return;
     111             :   }
     112             : 
     113           0 :   imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
     114           0 :   if (!canonicalRequest) {
     115             :     // The image was blocked or something.
     116           0 :     return;
     117             :   }
     118             : 
     119           0 :   RefPtr<imgRequestProxy> request;
     120             : 
     121             :   // Ignore errors here.  If cloning fails for some reason we'll put a null
     122             :   // entry in the hash and we won't keep trying to clone.
     123           0 :   mInClone = true;
     124           0 :   canonicalRequest->Clone(this, getter_AddRefs(request));
     125           0 :   mInClone = false;
     126             : 
     127           0 :   aImage->mRequests.Put(mDocument, request);
     128             : 
     129           0 :   AddImage(aImage);
     130             : }
     131             : 
     132             : void
     133           0 : ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
     134             : {
     135           0 :   RemoveImage(aImage);
     136           0 : }
     137             : 
     138             : void
     139           2 : ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest,
     140             :                                          nsIFrame*    aFrame)
     141             : {
     142             : #ifdef DEBUG
     143             :   {
     144           4 :     nsCOMPtr<imgINotificationObserver> observer;
     145           2 :     aRequest->GetNotificationObserver(getter_AddRefs(observer));
     146           2 :     MOZ_ASSERT(!observer || observer == this);
     147             :   }
     148             : #endif
     149             : 
     150           2 :   if (auto entry = mRequestToFrameMap.Lookup(aRequest)) {
     151           2 :     FrameSet* frameSet = entry.Data();
     152           2 :     MOZ_ASSERT(frameSet, "This should never be null");
     153           2 :     frameSet->RemoveElementSorted(aFrame);
     154           2 :     if (frameSet->IsEmpty()) {
     155           1 :       nsPresContext* presContext = GetPresContext();
     156           1 :       if (presContext) {
     157           1 :         nsLayoutUtils::DeregisterImageRequest(presContext, aRequest, nullptr);
     158             :       }
     159           1 :       entry.Remove();
     160             :     }
     161             :   }
     162           2 : }
     163             : 
     164             : void
     165           0 : ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest,
     166             :                                          nsIFrame*    aFrame)
     167             : {
     168           0 :   if (auto entry = mFrameToRequestMap.Lookup(aFrame)) {
     169           0 :     RequestSet* requestSet = entry.Data();
     170           0 :     MOZ_ASSERT(requestSet, "This should never be null");
     171           0 :     requestSet->RemoveElementSorted(aRequest);
     172           0 :     if (requestSet->IsEmpty()) {
     173           0 :       aFrame->SetHasImageRequest(false);
     174           0 :       entry.Remove();
     175             :     }
     176             :   }
     177           0 : }
     178             : 
     179             : void
     180           0 : ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
     181             :                                           nsIFrame*    aFrame)
     182             : {
     183           0 :   MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?");
     184           0 :   RemoveRequestToFrameMapping(aRequest, aFrame);
     185           0 :   RemoveFrameToRequestMapping(aRequest, aFrame);
     186           0 : }
     187             : 
     188             : void
     189           2 : ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
     190             : {
     191           2 :   MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?");
     192           4 :   nsAutoPtr<RequestSet> requestSet;
     193           2 :   mFrameToRequestMap.Remove(aFrame, &requestSet);
     194           2 :   aFrame->SetHasImageRequest(false);
     195           2 :   if (MOZ_UNLIKELY(!requestSet)) {
     196           0 :     MOZ_ASSERT_UNREACHABLE("HasImageRequest was lying");
     197             :     return;
     198             :   }
     199           4 :   for (imgIRequest* request : *requestSet) {
     200           2 :     RemoveRequestToFrameMapping(request, aFrame);
     201             :   }
     202           2 : }
     203             : 
     204             : void
     205           0 : ImageLoader::SetAnimationMode(uint16_t aMode)
     206             : {
     207           0 :   NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
     208             :                aMode == imgIContainer::kDontAnimMode ||
     209             :                aMode == imgIContainer::kLoopOnceAnimMode,
     210             :                "Wrong Animation Mode is being set!");
     211             : 
     212           0 :   for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
     213           0 :     auto request = static_cast<imgIRequest*>(iter.Key());
     214             : 
     215             : #ifdef DEBUG
     216             :     {
     217           0 :       nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
     218           0 :       NS_ASSERTION(debugRequest == request, "This is bad");
     219             :     }
     220             : #endif
     221             : 
     222           0 :     nsCOMPtr<imgIContainer> container;
     223           0 :     request->GetImage(getter_AddRefs(container));
     224           0 :     if (!container) {
     225           0 :       continue;
     226             :     }
     227             : 
     228             :     // This can fail if the image is in error, and we don't care.
     229           0 :     container->SetAnimationMode(aMode);
     230             :   }
     231           0 : }
     232             : 
     233             : void
     234           4 : ImageLoader::ClearFrames(nsPresContext* aPresContext)
     235             : {
     236           4 :   for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
     237           0 :     auto request = static_cast<imgIRequest*>(iter.Key());
     238             : 
     239             : #ifdef DEBUG
     240             :     {
     241           0 :       nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
     242           0 :       NS_ASSERTION(debugRequest == request, "This is bad");
     243             :     }
     244             : #endif
     245             : 
     246           0 :     if (aPresContext) {
     247             :       nsLayoutUtils::DeregisterImageRequest(aPresContext,
     248             :                                             request,
     249           0 :                                             nullptr);
     250             :     }
     251             :   }
     252             : 
     253           4 :   mRequestToFrameMap.Clear();
     254           4 :   mFrameToRequestMap.Clear();
     255           4 : }
     256             : 
     257             : void
     258          40 : ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
     259             :                        nsIURI* aReferrer, ImageLoader::Image* aImage)
     260             : {
     261          40 :   NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
     262             : 
     263          40 :   aImage->mRequests.Put(nullptr, nullptr);
     264             : 
     265          40 :   if (!aURI) {
     266           0 :     return;
     267             :   }
     268             : 
     269          80 :   RefPtr<imgRequestProxy> request;
     270         120 :   nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument,
     271             :                                           aOriginPrincipal, aReferrer,
     272          40 :                                           mDocument->GetReferrerPolicy(),
     273             :                                           nullptr, nsIRequest::LOAD_NORMAL,
     274          80 :                                           NS_LITERAL_STRING("css"),
     275         160 :                                           getter_AddRefs(request));
     276             : 
     277          40 :   if (NS_FAILED(rv) || !request) {
     278           0 :     return;
     279             :   }
     280             : 
     281          80 :   RefPtr<imgRequestProxy> clonedRequest;
     282          40 :   mInClone = true;
     283          40 :   rv = request->Clone(this, getter_AddRefs(clonedRequest));
     284          40 :   mInClone = false;
     285             : 
     286          40 :   if (NS_FAILED(rv)) {
     287           0 :     return;
     288             :   }
     289             : 
     290          40 :   aImage->mRequests.Put(nullptr, request);
     291          40 :   aImage->mRequests.Put(mDocument, clonedRequest);
     292             : 
     293          40 :   AddImage(aImage);
     294             : }
     295             : 
     296             : void
     297          40 : ImageLoader::AddImage(ImageLoader::Image* aImage)
     298             : {
     299          40 :   NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
     300          40 :   mImages.PutEntry(aImage);
     301          40 : }
     302             : 
     303             : void
     304           0 : ImageLoader::RemoveImage(ImageLoader::Image* aImage)
     305             : {
     306           0 :   NS_ASSERTION(mImages.Contains(aImage), "Huh?");
     307           0 :   mImages.RemoveEntry(aImage);
     308           0 : }
     309             : 
     310             : nsPresContext*
     311          52 : ImageLoader::GetPresContext()
     312             : {
     313          52 :   if (!mDocument) {
     314           0 :     return nullptr;
     315             :   }
     316             : 
     317          52 :   nsIPresShell* shell = mDocument->GetShell();
     318          52 :   if (!shell) {
     319           0 :     return nullptr;
     320             :   }
     321             : 
     322          52 :   return shell->GetPresContext();
     323             : }
     324             : 
     325          15 : void InvalidateImagesCallback(nsIFrame* aFrame,
     326             :                               DisplayItemData* aItem)
     327             : {
     328          15 :   nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey());
     329          15 :   uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type);
     330             : 
     331          15 :   if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) {
     332           2 :     return;
     333             :   }
     334             : 
     335          13 :   if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
     336           0 :     printf_stderr("Invalidating display item(type=%d) based on frame %p \
     337           0 :       because it might contain an invalidated image\n", type, aFrame);
     338             :   }
     339          13 :   aItem->Invalidate();
     340          13 :   aFrame->SchedulePaint();
     341             : }
     342             : 
     343             : void
     344          15 : ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint)
     345             : {
     346          15 :   NS_ASSERTION(aFrameSet, "Must have a frame set");
     347          15 :   NS_ASSERTION(mDocument, "Should have returned earlier!");
     348             : 
     349          15 :   FrameSet::size_type length = aFrameSet->Length();
     350          30 :   for (FrameSet::size_type i = 0; i < length; i++) {
     351          15 :     nsIFrame* frame = aFrameSet->ElementAt(i);
     352             : 
     353          15 :     if (frame->StyleVisibility()->IsVisible()) {
     354          15 :       if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
     355             :         // Tables don't necessarily build border/background display items
     356             :         // for the individual table part frames, so IterateRetainedDataFor
     357             :         // might not find the right display item.
     358           0 :         frame->InvalidateFrame();
     359             :       } else {
     360          15 :         FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
     361             : 
     362             :         // Update ancestor rendering observers (-moz-element etc)
     363          15 :         nsIFrame *f = frame;
     364         267 :         while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
     365         126 :           nsSVGEffects::InvalidateDirectRenderingObservers(f);
     366         126 :           f = nsLayoutUtils::GetCrossDocParentFrame(f);
     367             :         }
     368             : 
     369          15 :         if (aForcePaint) {
     370           6 :           frame->SchedulePaint();
     371             :         }
     372             :       }
     373             :     }
     374             :   }
     375          15 : }
     376             : 
     377         739 : NS_IMPL_ADDREF(ImageLoader)
     378         684 : NS_IMPL_RELEASE(ImageLoader)
     379             : 
     380         376 : NS_INTERFACE_MAP_BEGIN(ImageLoader)
     381         376 :   NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
     382         108 :   NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
     383           0 : NS_INTERFACE_MAP_END
     384             : 
     385             : NS_IMETHODIMP
     386         254 : ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
     387             : {
     388         254 :   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     389          80 :     nsCOMPtr<imgIContainer> image;
     390          40 :     aRequest->GetImage(getter_AddRefs(image));
     391          40 :     return OnSizeAvailable(aRequest, image);
     392             :   }
     393             : 
     394         214 :   if (aType == imgINotificationObserver::IS_ANIMATED) {
     395           2 :     return OnImageIsAnimated(aRequest);
     396             :   }
     397             : 
     398         212 :   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
     399          35 :     return OnFrameComplete(aRequest);
     400             :   }
     401             : 
     402         177 :   if (aType == imgINotificationObserver::FRAME_UPDATE) {
     403          62 :     return OnFrameUpdate(aRequest);
     404             :   }
     405             : 
     406         115 :   if (aType == imgINotificationObserver::DECODE_COMPLETE) {
     407          70 :     nsCOMPtr<imgIContainer> image;
     408          35 :     aRequest->GetImage(getter_AddRefs(image));
     409          35 :     if (image && mDocument) {
     410          35 :       image->PropagateUseCounters(mDocument);
     411             :     }
     412             :   }
     413             : 
     414         115 :   return NS_OK;
     415             : }
     416             : 
     417             : nsresult
     418          40 : ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
     419             : {
     420          40 :   nsPresContext* presContext = GetPresContext();
     421          40 :   if (!presContext) {
     422           0 :     return NS_OK;
     423             :   }
     424             : 
     425          40 :   aImage->SetAnimationMode(presContext->ImageAnimationMode());
     426             : 
     427          40 :   return NS_OK;
     428             : }
     429             : 
     430             : nsresult
     431           2 : ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
     432             : {
     433           2 :   if (!mDocument) {
     434           0 :     return NS_OK;
     435             :   }
     436             : 
     437           2 :   FrameSet* frameSet = nullptr;
     438           2 :   if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
     439           2 :     return NS_OK;
     440             :   }
     441             : 
     442             :   // Register with the refresh driver now that we are aware that
     443             :   // we are animated.
     444           0 :   nsPresContext* presContext = GetPresContext();
     445           0 :   if (presContext) {
     446             :     nsLayoutUtils::RegisterImageRequest(presContext,
     447             :                                         aRequest,
     448           0 :                                         nullptr);
     449             :   }
     450             : 
     451           0 :   return NS_OK;
     452             : }
     453             : 
     454             : nsresult
     455          35 : ImageLoader::OnFrameComplete(imgIRequest* aRequest)
     456             : {
     457          35 :   if (!mDocument || mInClone) {
     458           0 :     return NS_OK;
     459             :   }
     460             : 
     461          35 :   FrameSet* frameSet = nullptr;
     462          35 :   if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
     463          29 :     return NS_OK;
     464             :   }
     465             : 
     466           6 :   NS_ASSERTION(frameSet, "This should never be null!");
     467             : 
     468             :   // Since we just finished decoding a frame, we always want to paint, in case
     469             :   // we're now able to paint an image that we couldn't paint before (and hence
     470             :   // that we don't have retained data for).
     471           6 :   DoRedraw(frameSet, /* aForcePaint = */ true);
     472             : 
     473           6 :   return NS_OK;
     474             : }
     475             : 
     476             : nsresult
     477          62 : ImageLoader::OnFrameUpdate(imgIRequest* aRequest)
     478             : {
     479          62 :   if (!mDocument || mInClone) {
     480           0 :     return NS_OK;
     481             :   }
     482             : 
     483          62 :   FrameSet* frameSet = nullptr;
     484          62 :   if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
     485          53 :     return NS_OK;
     486             :   }
     487             : 
     488           9 :   NS_ASSERTION(frameSet, "This should never be null!");
     489             : 
     490           9 :   DoRedraw(frameSet, /* aForcePaint = */ false);
     491             : 
     492           9 :   return NS_OK;
     493             : }
     494             : 
     495             : NS_IMETHODIMP
     496          54 : ImageLoader::BlockOnload(imgIRequest* aRequest)
     497             : {
     498          54 :   if (!mDocument) {
     499           0 :     return NS_OK;
     500             :   }
     501             : 
     502          54 :   mDocument->BlockOnload();
     503             : 
     504          54 :   return NS_OK;
     505             : }
     506             : 
     507             : NS_IMETHODIMP
     508          54 : ImageLoader::UnblockOnload(imgIRequest* aRequest)
     509             : {
     510          54 :   if (!mDocument) {
     511           0 :     return NS_OK;
     512             :   }
     513             : 
     514          54 :   mDocument->UnblockOnload(false);
     515             : 
     516          54 :   return NS_OK;
     517             : }
     518             : 
     519             : void
     520           0 : ImageLoader::FlushUseCounters()
     521             : {
     522           0 :   for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) {
     523           0 :     nsPtrHashKey<Image>* key = iter.Get();
     524           0 :     ImageLoader::Image* image = key->GetKey();
     525             : 
     526           0 :     imgIRequest* request = image->mRequests.GetWeak(mDocument);
     527             : 
     528           0 :     nsCOMPtr<imgIContainer> container;
     529           0 :     request->GetImage(getter_AddRefs(container));
     530           0 :     if (container) {
     531           0 :       static_cast<image::Image*>(container.get())->ReportUseCounters();
     532             :     }
     533             :   }
     534           0 : }
     535             : 
     536             : } // namespace css
     537             : } // namespace mozilla

Generated by: LCOV version 1.13