layout/style/ImageLoader.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     5 /* A class that handles style system image loads (other image loads are handled
     6  * by the nodes in the content tree).
     7  */
     9 #include "mozilla/css/ImageLoader.h"
    10 #include "nsContentUtils.h"
    11 #include "nsLayoutUtils.h"
    12 #include "nsError.h"
    13 #include "nsDisplayList.h"
    14 #include "FrameLayerBuilder.h"
    15 #include "nsSVGEffects.h"
    16 #include "imgIContainer.h"
    18 namespace mozilla {
    19 namespace css {
    21 /* static */ PLDHashOperator
    22 ImageLoader::SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
    23                                         void* aClosure)
    24 {
    25   imgIRequest* request = static_cast<imgIRequest*>(aKey);
    27   uint16_t* mode = static_cast<uint16_t*>(aClosure);
    29 #ifdef DEBUG
    30   {
    31     nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(aKey);
    32     NS_ASSERTION(debugRequest == request, "This is bad");
    33   }
    34 #endif
    36   nsCOMPtr<imgIContainer> container;
    37   request->GetImage(getter_AddRefs(container));
    38   if (!container) {
    39     return PL_DHASH_NEXT;
    40   }
    42   // This can fail if the image is in error, and we don't care.
    43   container->SetAnimationMode(*mode);
    45   return PL_DHASH_NEXT;
    46 }
    48 static PLDHashOperator
    49 ClearImageHashSet(nsPtrHashKey<ImageLoader::Image>* aKey, void* aClosure)
    50 {
    51   nsIDocument* doc = static_cast<nsIDocument*>(aClosure);
    52   ImageLoader::Image* image = aKey->GetKey();
    54   imgIRequest* request = image->mRequests.GetWeak(doc);
    55   if (request) {
    56     request->CancelAndForgetObserver(NS_BINDING_ABORTED);
    57   }
    59   image->mRequests.Remove(doc);
    61   return PL_DHASH_REMOVE;
    62 }
    64 void
    65 ImageLoader::DropDocumentReference()
    66 {
    67   ClearFrames();
    68   mImages.EnumerateEntries(&ClearImageHashSet, mDocument);
    69   mDocument = nullptr;
    70 }
    72 void
    73 ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
    74                                      nsIFrame* aFrame)
    75 {
    76   nsCOMPtr<imgINotificationObserver> observer;
    77   aRequest->GetNotificationObserver(getter_AddRefs(observer));
    78   if (!observer) {
    79     // The request has already been canceled, so ignore it.  This is ok because
    80     // we're not going to get any more notifications from a canceled request.
    81     return;
    82   }
    84   MOZ_ASSERT(observer == this);
    86   FrameSet* frameSet = nullptr;
    87   if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
    88     NS_ASSERTION(frameSet, "This should never be null!");
    89   }
    91   if (!frameSet) {
    92     nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
    94     mRequestToFrameMap.Put(aRequest, newFrameSet);
    95     frameSet = newFrameSet.forget();
    97     nsPresContext* presContext = GetPresContext();
    98     if (presContext) {
    99       nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
   100                                                     aRequest,
   101                                                     nullptr);
   102     }
   103   }
   105   RequestSet* requestSet = nullptr;
   106   if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
   107     NS_ASSERTION(requestSet, "This should never be null");
   108   }
   110   if (!requestSet) {
   111     nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
   113     mFrameToRequestMap.Put(aFrame, newRequestSet);
   114     requestSet = newRequestSet.forget();
   115   }
   117   // Add these to the sets, but only if they're not already there.
   118   uint32_t i = frameSet->IndexOfFirstElementGt(aFrame);
   119   if (i == 0 || aFrame != frameSet->ElementAt(i-1)) {
   120     frameSet->InsertElementAt(i, aFrame);
   121   }
   122   i = requestSet->IndexOfFirstElementGt(aRequest);
   123   if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
   124     requestSet->InsertElementAt(i, aRequest);
   125   }
   126 }
   128 void
   129 ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
   130 {
   131   NS_ASSERTION(aImage, "This should never be null!");
   133   bool found = false;
   134   aImage->mRequests.GetWeak(mDocument, &found);
   135   if (found) {
   136     // This document already has a request.
   137     return;
   138   }
   140   imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
   141   if (!canonicalRequest) {
   142     // The image was blocked or something.
   143     return;
   144   }
   146   nsRefPtr<imgRequestProxy> request;
   148   // Ignore errors here.  If cloning fails for some reason we'll put a null
   149   // entry in the hash and we won't keep trying to clone.
   150   mInClone = true;
   151   canonicalRequest->Clone(this, getter_AddRefs(request));
   152   mInClone = false;
   154   aImage->mRequests.Put(mDocument, request);
   156   AddImage(aImage);
   157 }
   159 void
   160 ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
   161 {
   162   RemoveImage(aImage);
   163 }
   165 void
   166 ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
   167                                           nsIFrame* aFrame)
   168 {
   169   FrameSet* frameSet = nullptr;
   170   RequestSet* requestSet = nullptr;
   172 #ifdef DEBUG
   173   {
   174     nsCOMPtr<imgINotificationObserver> observer;
   175     aRequest->GetNotificationObserver(getter_AddRefs(observer));
   176     MOZ_ASSERT(!observer || observer == this);
   177   }
   178 #endif
   180   mRequestToFrameMap.Get(aRequest, &frameSet);
   181   mFrameToRequestMap.Get(aFrame, &requestSet);
   183   if (frameSet) {
   184     frameSet->RemoveElementSorted(aFrame);
   185   }
   186   if (requestSet) {
   187     requestSet->RemoveElementSorted(aRequest);
   188   }
   190   if (frameSet && !frameSet->Length()) {
   191     mRequestToFrameMap.Remove(aRequest);
   193     nsPresContext* presContext = GetPresContext();
   194     if (presContext) {
   195       nsLayoutUtils::DeregisterImageRequest(presContext,
   196                                             aRequest,
   197                                             nullptr);
   198     }
   199   }
   201   if (requestSet && !requestSet->Length()) {
   202     mFrameToRequestMap.Remove(aFrame);
   203   }
   204 }
   206 void
   207 ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
   208 {
   209   RequestSet* requestSet = nullptr;
   210   if (!mFrameToRequestMap.Get(aFrame, &requestSet)) {
   211     return;
   212   }
   214   NS_ASSERTION(requestSet, "This should never be null");
   216   RequestSet frozenRequestSet(*requestSet);
   217   for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) {
   218     imgIRequest* request = frozenRequestSet.ElementAt(i - 1);
   220     DisassociateRequestFromFrame(request, aFrame);
   221   }
   222 }
   224 void
   225 ImageLoader::SetAnimationMode(uint16_t aMode)
   226 {
   227   NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
   228                aMode == imgIContainer::kDontAnimMode ||
   229                aMode == imgIContainer::kLoopOnceAnimMode,
   230                "Wrong Animation Mode is being set!");
   232   mRequestToFrameMap.EnumerateRead(SetAnimationModeEnumerator, &aMode);
   233 }
   235 void
   236 ImageLoader::ClearFrames()
   237 {
   238   mRequestToFrameMap.Clear();
   239   mFrameToRequestMap.Clear();
   240 }
   242 void
   243 ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
   244                        nsIURI* aReferrer, ImageLoader::Image* aImage)
   245 {
   246   NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
   248   aImage->mRequests.Put(nullptr, nullptr);
   250   if (!aURI) {
   251     return;
   252   }
   254   if (!nsContentUtils::CanLoadImage(aURI, mDocument, mDocument,
   255                                     aOriginPrincipal)) {
   256     return;
   257   }
   259   nsRefPtr<imgRequestProxy> request;
   260   nsContentUtils::LoadImage(aURI, mDocument, aOriginPrincipal, aReferrer,
   261                             nullptr, nsIRequest::LOAD_NORMAL,
   262                             NS_LITERAL_STRING("css"),
   263                             getter_AddRefs(request));
   265   if (!request) {
   266     return;
   267   }
   269   nsRefPtr<imgRequestProxy> clonedRequest;
   270   mInClone = true;
   271   nsresult rv = request->Clone(this, getter_AddRefs(clonedRequest));
   272   mInClone = false;
   274   if (NS_FAILED(rv)) {
   275     return;
   276   }
   278   aImage->mRequests.Put(nullptr, request);
   279   aImage->mRequests.Put(mDocument, clonedRequest);
   281   AddImage(aImage);
   282 }
   284 void
   285 ImageLoader::AddImage(ImageLoader::Image* aImage)
   286 {
   287   NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
   288   if (!mImages.PutEntry(aImage)) {
   289     NS_RUNTIMEABORT("OOM");
   290   }
   291 }
   293 void
   294 ImageLoader::RemoveImage(ImageLoader::Image* aImage)
   295 {
   296   NS_ASSERTION(mImages.Contains(aImage), "Huh?");
   297   mImages.RemoveEntry(aImage);
   298 }
   300 nsPresContext*
   301 ImageLoader::GetPresContext()
   302 {
   303   if (!mDocument) {
   304     return nullptr;
   305   }
   307   nsIPresShell* shell = mDocument->GetShell();
   308   if (!shell) {
   309     return nullptr;
   310   }
   312   return shell->GetPresContext();
   313 }
   315 void InvalidateImagesCallback(nsIFrame* aFrame, 
   316                               FrameLayerBuilder::DisplayItemData* aItem)
   317 {
   318   nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey());
   319   uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type);
   321   if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) {
   322     return;
   323   }
   325   aItem->Invalidate();
   327   // Update ancestor rendering observers (-moz-element etc)
   328   nsIFrame *f = aFrame;
   329   while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
   330     nsSVGEffects::InvalidateDirectRenderingObservers(f);
   331     f = nsLayoutUtils::GetCrossDocParentFrame(f);
   332   }
   333 }
   335 void
   336 ImageLoader::DoRedraw(FrameSet* aFrameSet)
   337 {
   338   NS_ASSERTION(aFrameSet, "Must have a frame set");
   339   NS_ASSERTION(mDocument, "Should have returned earlier!");
   341   FrameSet::size_type length = aFrameSet->Length();
   342   for (FrameSet::size_type i = 0; i < length; i++) {
   343     nsIFrame* frame = aFrameSet->ElementAt(i);
   345     if (frame->StyleVisibility()->IsVisible()) {
   346       if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
   347         // Tables don't necessarily build border/background display items
   348         // for the individual table part frames, so IterateRetainedDataFor
   349         // might not find the right display item.
   350         frame->InvalidateFrame();
   351       } else {
   352         FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
   353         frame->SchedulePaint();
   354       }
   355     }
   356   }
   357 }
   359 NS_IMPL_ADDREF(ImageLoader)
   360 NS_IMPL_RELEASE(ImageLoader)
   362 NS_INTERFACE_MAP_BEGIN(ImageLoader)
   363   NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
   364   NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
   365 NS_INTERFACE_MAP_END
   367 NS_IMETHODIMP
   368 ImageLoader::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
   369 {
   370   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   371     nsCOMPtr<imgIContainer> image;
   372     aRequest->GetImage(getter_AddRefs(image));
   373     return OnStartContainer(aRequest, image);
   374   }
   376   if (aType == imgINotificationObserver::IS_ANIMATED) {
   377     return OnImageIsAnimated(aRequest);
   378   }
   380   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   381     return OnStopFrame(aRequest);
   382   }
   384   if (aType == imgINotificationObserver::FRAME_UPDATE) {
   385     return FrameChanged(aRequest);
   386   }
   388   return NS_OK;
   389 }
   391 nsresult
   392 ImageLoader::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
   393 { 
   394   nsPresContext* presContext = GetPresContext();
   395   if (!presContext) {
   396     return NS_OK;
   397   }
   399   aImage->SetAnimationMode(presContext->ImageAnimationMode());
   401   return NS_OK;
   402 }
   404 nsresult
   405 ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
   406 {
   407   if (!mDocument) {
   408     return NS_OK;
   409   }
   411   FrameSet* frameSet = nullptr;
   412   if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
   413     return NS_OK;
   414   }
   416   // Register with the refresh driver now that we are aware that
   417   // we are animated.
   418   nsPresContext* presContext = GetPresContext();
   419   if (presContext) {
   420     nsLayoutUtils::RegisterImageRequest(presContext,
   421                                         aRequest,
   422                                         nullptr);
   423   }
   425   return NS_OK;
   426 }
   428 nsresult
   429 ImageLoader::OnStopFrame(imgIRequest *aRequest)
   430 {
   431   if (!mDocument || mInClone) {
   432     return NS_OK;
   433   }
   435   FrameSet* frameSet = nullptr;
   436   if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
   437     return NS_OK;
   438   }
   440   NS_ASSERTION(frameSet, "This should never be null!");
   442   DoRedraw(frameSet);
   444   return NS_OK;
   445 }
   447 nsresult
   448 ImageLoader::FrameChanged(imgIRequest *aRequest)
   449 {
   450   if (!mDocument || mInClone) {
   451     return NS_OK;
   452   }
   454   FrameSet* frameSet = nullptr;
   455   if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
   456     return NS_OK;
   457   }
   459   NS_ASSERTION(frameSet, "This should never be null!");
   461   DoRedraw(frameSet);
   463   return NS_OK;
   464 }
   466 NS_IMETHODIMP
   467 ImageLoader::BlockOnload(imgIRequest* aRequest)
   468 {
   469   if (!mDocument) {
   470     return NS_OK;
   471   }
   473   mDocument->BlockOnload();
   475   return NS_OK;
   476 }
   478 NS_IMETHODIMP
   479 ImageLoader::UnblockOnload(imgIRequest* aRequest)
   480 {
   481   if (!mDocument) {
   482     return NS_OK;
   483   }
   485   mDocument->UnblockOnload(false);
   487   return NS_OK;
   488 }
   490 } // namespace css
   491 } // namespace mozilla

mercurial