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

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

mercurial