image/src/ImageFactory.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <algorithm>
     9 #include "mozilla/Preferences.h"
    10 #include "mozilla/Likely.h"
    12 #include "nsIHttpChannel.h"
    13 #include "nsIFileChannel.h"
    14 #include "nsIFile.h"
    15 #include "nsMimeTypes.h"
    16 #include "nsIRequest.h"
    18 #include "RasterImage.h"
    19 #include "VectorImage.h"
    20 #include "Image.h"
    21 #include "nsMediaFragmentURIParser.h"
    22 #include "nsContentUtils.h"
    23 #include "nsIScriptSecurityManager.h"
    25 #include "ImageFactory.h"
    26 #include "gfxPrefs.h"
    28 namespace mozilla {
    29 namespace image {
    31 // Global preferences related to image containers.
    32 static bool gInitializedPrefCaches = false;
    33 static bool gDecodeOnDraw = false;
    34 static bool gDiscardable = false;
    35 static bool gEnableMozSampleSize = false;
    37 /*static*/ void
    38 ImageFactory::Initialize()
    39 {
    40   MOZ_ASSERT(NS_IsMainThread());
    41   if (!gInitializedPrefCaches) {
    42     // Initialize the graphics preferences
    43     gfxPrefs::GetSingleton();
    44     Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable");
    45     Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw");
    46     Preferences::AddBoolVarCache(&gEnableMozSampleSize, "image.mozsamplesize.enabled");
    47     gInitializedPrefCaches = true;
    48   }
    49 }
    51 static uint32_t
    52 ComputeImageFlags(ImageURL* uri, bool isMultiPart)
    53 {
    54   nsresult rv;
    56   // We default to the static globals.
    57   bool isDiscardable = gDiscardable;
    58   bool doDecodeOnDraw = gDecodeOnDraw;
    60   // We want UI to be as snappy as possible and not to flicker. Disable discarding
    61   // and decode-on-draw for chrome URLS.
    62   bool isChrome = false;
    63   rv = uri->SchemeIs("chrome", &isChrome);
    64   if (NS_SUCCEEDED(rv) && isChrome)
    65     isDiscardable = doDecodeOnDraw = false;
    67   // We don't want resources like the "loading" icon to be discardable or
    68   // decode-on-draw either.
    69   bool isResource = false;
    70   rv = uri->SchemeIs("resource", &isResource);
    71   if (NS_SUCCEEDED(rv) && isResource)
    72     isDiscardable = doDecodeOnDraw = false;
    74   // For multipart/x-mixed-replace, we basically want a direct channel to the
    75   // decoder. Disable both for this case as well.
    76   if (isMultiPart)
    77     isDiscardable = doDecodeOnDraw = false;
    79   // We have all the information we need.
    80   uint32_t imageFlags = Image::INIT_FLAG_NONE;
    81   if (isDiscardable)
    82     imageFlags |= Image::INIT_FLAG_DISCARDABLE;
    83   if (doDecodeOnDraw)
    84     imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
    85   if (isMultiPart)
    86     imageFlags |= Image::INIT_FLAG_MULTIPART;
    88   return imageFlags;
    89 }
    91 /* static */ bool
    92 ImageFactory::CanRetargetOnDataAvailable(ImageURL* aURI, bool aIsMultiPart)
    93 {
    94   // We can't retarget OnDataAvailable safely in cases where we aren't storing
    95   // source data (and thus need to sync decode in ODA) because allocating frames
    96   // off-main-thread is currently not possible and we don't have a workaround in
    97   // place yet. (See bug 967985.) For now, we detect those cases and refuse to
    98   // retarget. When the problem is fixed, this function can be removed.
   100   if (aIsMultiPart) {
   101     return false;
   102   }
   104   uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart);
   105   if (!(imageFlags & Image::INIT_FLAG_DISCARDABLE) &&
   106       !(imageFlags & Image::INIT_FLAG_DECODE_ON_DRAW)) {
   107     return false;
   108   }
   110   return true;
   111 }
   113 /* static */ already_AddRefed<Image>
   114 ImageFactory::CreateImage(nsIRequest* aRequest,
   115                           imgStatusTracker* aStatusTracker,
   116                           const nsCString& aMimeType,
   117                           ImageURL* aURI,
   118                           bool aIsMultiPart,
   119                           uint32_t aInnerWindowId)
   120 {
   121   MOZ_ASSERT(gInitializedPrefCaches,
   122              "Pref observers should have been initialized already");
   124   // Compute the image's initialization flags.
   125   uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart);
   127   // Select the type of image to create based on MIME type.
   128   if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
   129     return CreateVectorImage(aRequest, aStatusTracker, aMimeType,
   130                              aURI, imageFlags, aInnerWindowId);
   131   } else {
   132     return CreateRasterImage(aRequest, aStatusTracker, aMimeType,
   133                              aURI, imageFlags, aInnerWindowId);
   134   }
   135 }
   137 // Marks an image as having an error before returning it. Used with macros like
   138 // NS_ENSURE_SUCCESS, since we guarantee to always return an image even if an
   139 // error occurs, but callers need to be able to tell that this happened.
   140 template <typename T>
   141 static already_AddRefed<Image>
   142 BadImage(nsRefPtr<T>& image)
   143 {
   144   image->SetHasError();
   145   return image.forget();
   146 }
   148 /* static */ already_AddRefed<Image>
   149 ImageFactory::CreateAnonymousImage(const nsCString& aMimeType)
   150 {
   151   nsresult rv;
   153   nsRefPtr<RasterImage> newImage = new RasterImage();
   155   rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_NONE);
   156   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
   158   return newImage.forget();
   159 }
   161 int32_t
   162 SaturateToInt32(int64_t val)
   163 {
   164   if (val > INT_MAX)
   165     return INT_MAX;
   166   if (val < INT_MIN)
   167     return INT_MIN;
   169   return static_cast<int32_t>(val);
   170 }
   172 uint32_t
   173 GetContentSize(nsIRequest* aRequest)
   174 {
   175   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   176   if (channel) {
   177     int64_t size;
   178     nsresult rv = channel->GetContentLength(&size);
   179     if (NS_SUCCEEDED(rv)) {
   180       return std::max(SaturateToInt32(size), 0);
   181     }
   182   }
   184   // Use the file size as a size hint for file channels.
   185   nsCOMPtr<nsIFileChannel> fileChannel(do_QueryInterface(aRequest));
   186   if (fileChannel) {
   187     nsCOMPtr<nsIFile> file;
   188     nsresult rv = fileChannel->GetFile(getter_AddRefs(file));
   189     if (NS_SUCCEEDED(rv)) {
   190       int64_t filesize;
   191       rv = file->GetFileSize(&filesize);
   192       if (NS_SUCCEEDED(rv)) {
   193         return std::max(SaturateToInt32(filesize), 0);
   194       }
   195     }
   196   }
   198   // Fallback - neither http nor file. We'll use dynamic allocation.
   199   return 0;
   200 }
   202 /* static */ already_AddRefed<Image>
   203 ImageFactory::CreateRasterImage(nsIRequest* aRequest,
   204                                 imgStatusTracker* aStatusTracker,
   205                                 const nsCString& aMimeType,
   206                                 ImageURL* aURI,
   207                                 uint32_t aImageFlags,
   208                                 uint32_t aInnerWindowId)
   209 {
   210   nsresult rv;
   212   nsRefPtr<RasterImage> newImage = new RasterImage(aStatusTracker, aURI);
   214   rv = newImage->Init(aMimeType.get(), aImageFlags);
   215   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
   217   newImage->SetInnerWindowID(aInnerWindowId);
   219   uint32_t len = GetContentSize(aRequest);
   221   // Pass anything usable on so that the RasterImage can preallocate
   222   // its source buffer.
   223   if (len > 0) {
   224     uint32_t sizeHint = std::min<uint32_t>(len, 20000000); // Bound by something reasonable
   225     rv = newImage->SetSourceSizeHint(sizeHint);
   226     if (NS_FAILED(rv)) {
   227       // Flush memory, try to get some back, and try again.
   228       rv = nsMemory::HeapMinimize(true);
   229       nsresult rv2 = newImage->SetSourceSizeHint(sizeHint);
   230       // If we've still failed at this point, things are going downhill.
   231       if (NS_FAILED(rv) || NS_FAILED(rv2)) {
   232         NS_WARNING("About to hit OOM in imagelib!");
   233       }
   234     }
   235   }
   237   nsAutoCString ref;
   238   aURI->GetRef(ref);
   239   mozilla::net::nsMediaFragmentURIParser parser(ref);
   240   if (parser.HasResolution()) {
   241     newImage->SetRequestedResolution(parser.GetResolution());
   242   }
   244   if (parser.HasSampleSize()) {
   245       /* Get our principal */
   246       nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   247       nsCOMPtr<nsIPrincipal> principal;
   248       if (chan) {
   249         nsContentUtils::GetSecurityManager()->GetChannelPrincipal(chan,
   250                                                                   getter_AddRefs(principal));
   251       }
   253       if ((principal &&
   254            principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED) ||
   255           gEnableMozSampleSize) {
   256         newImage->SetRequestedSampleSize(parser.GetSampleSize());
   257       }
   258   }
   260   return newImage.forget();
   261 }
   263 /* static */ already_AddRefed<Image>
   264 ImageFactory::CreateVectorImage(nsIRequest* aRequest,
   265                                 imgStatusTracker* aStatusTracker,
   266                                 const nsCString& aMimeType,
   267                                 ImageURL* aURI,
   268                                 uint32_t aImageFlags,
   269                                 uint32_t aInnerWindowId)
   270 {
   271   nsresult rv;
   273   nsRefPtr<VectorImage> newImage = new VectorImage(aStatusTracker, aURI);
   275   rv = newImage->Init(aMimeType.get(), aImageFlags);
   276   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
   278   newImage->SetInnerWindowID(aInnerWindowId);
   280   rv = newImage->OnStartRequest(aRequest, nullptr);
   281   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
   283   return newImage.forget();
   284 }
   286 } // namespace image
   287 } // namespace mozilla

mercurial