1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/ImageFactory.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,287 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <algorithm> 1.11 + 1.12 +#include "mozilla/Preferences.h" 1.13 +#include "mozilla/Likely.h" 1.14 + 1.15 +#include "nsIHttpChannel.h" 1.16 +#include "nsIFileChannel.h" 1.17 +#include "nsIFile.h" 1.18 +#include "nsMimeTypes.h" 1.19 +#include "nsIRequest.h" 1.20 + 1.21 +#include "RasterImage.h" 1.22 +#include "VectorImage.h" 1.23 +#include "Image.h" 1.24 +#include "nsMediaFragmentURIParser.h" 1.25 +#include "nsContentUtils.h" 1.26 +#include "nsIScriptSecurityManager.h" 1.27 + 1.28 +#include "ImageFactory.h" 1.29 +#include "gfxPrefs.h" 1.30 + 1.31 +namespace mozilla { 1.32 +namespace image { 1.33 + 1.34 +// Global preferences related to image containers. 1.35 +static bool gInitializedPrefCaches = false; 1.36 +static bool gDecodeOnDraw = false; 1.37 +static bool gDiscardable = false; 1.38 +static bool gEnableMozSampleSize = false; 1.39 + 1.40 +/*static*/ void 1.41 +ImageFactory::Initialize() 1.42 +{ 1.43 + MOZ_ASSERT(NS_IsMainThread()); 1.44 + if (!gInitializedPrefCaches) { 1.45 + // Initialize the graphics preferences 1.46 + gfxPrefs::GetSingleton(); 1.47 + Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable"); 1.48 + Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw"); 1.49 + Preferences::AddBoolVarCache(&gEnableMozSampleSize, "image.mozsamplesize.enabled"); 1.50 + gInitializedPrefCaches = true; 1.51 + } 1.52 +} 1.53 + 1.54 +static uint32_t 1.55 +ComputeImageFlags(ImageURL* uri, bool isMultiPart) 1.56 +{ 1.57 + nsresult rv; 1.58 + 1.59 + // We default to the static globals. 1.60 + bool isDiscardable = gDiscardable; 1.61 + bool doDecodeOnDraw = gDecodeOnDraw; 1.62 + 1.63 + // We want UI to be as snappy as possible and not to flicker. Disable discarding 1.64 + // and decode-on-draw for chrome URLS. 1.65 + bool isChrome = false; 1.66 + rv = uri->SchemeIs("chrome", &isChrome); 1.67 + if (NS_SUCCEEDED(rv) && isChrome) 1.68 + isDiscardable = doDecodeOnDraw = false; 1.69 + 1.70 + // We don't want resources like the "loading" icon to be discardable or 1.71 + // decode-on-draw either. 1.72 + bool isResource = false; 1.73 + rv = uri->SchemeIs("resource", &isResource); 1.74 + if (NS_SUCCEEDED(rv) && isResource) 1.75 + isDiscardable = doDecodeOnDraw = false; 1.76 + 1.77 + // For multipart/x-mixed-replace, we basically want a direct channel to the 1.78 + // decoder. Disable both for this case as well. 1.79 + if (isMultiPart) 1.80 + isDiscardable = doDecodeOnDraw = false; 1.81 + 1.82 + // We have all the information we need. 1.83 + uint32_t imageFlags = Image::INIT_FLAG_NONE; 1.84 + if (isDiscardable) 1.85 + imageFlags |= Image::INIT_FLAG_DISCARDABLE; 1.86 + if (doDecodeOnDraw) 1.87 + imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW; 1.88 + if (isMultiPart) 1.89 + imageFlags |= Image::INIT_FLAG_MULTIPART; 1.90 + 1.91 + return imageFlags; 1.92 +} 1.93 + 1.94 +/* static */ bool 1.95 +ImageFactory::CanRetargetOnDataAvailable(ImageURL* aURI, bool aIsMultiPart) 1.96 +{ 1.97 + // We can't retarget OnDataAvailable safely in cases where we aren't storing 1.98 + // source data (and thus need to sync decode in ODA) because allocating frames 1.99 + // off-main-thread is currently not possible and we don't have a workaround in 1.100 + // place yet. (See bug 967985.) For now, we detect those cases and refuse to 1.101 + // retarget. When the problem is fixed, this function can be removed. 1.102 + 1.103 + if (aIsMultiPart) { 1.104 + return false; 1.105 + } 1.106 + 1.107 + uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart); 1.108 + if (!(imageFlags & Image::INIT_FLAG_DISCARDABLE) && 1.109 + !(imageFlags & Image::INIT_FLAG_DECODE_ON_DRAW)) { 1.110 + return false; 1.111 + } 1.112 + 1.113 + return true; 1.114 +} 1.115 + 1.116 +/* static */ already_AddRefed<Image> 1.117 +ImageFactory::CreateImage(nsIRequest* aRequest, 1.118 + imgStatusTracker* aStatusTracker, 1.119 + const nsCString& aMimeType, 1.120 + ImageURL* aURI, 1.121 + bool aIsMultiPart, 1.122 + uint32_t aInnerWindowId) 1.123 +{ 1.124 + MOZ_ASSERT(gInitializedPrefCaches, 1.125 + "Pref observers should have been initialized already"); 1.126 + 1.127 + // Compute the image's initialization flags. 1.128 + uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart); 1.129 + 1.130 + // Select the type of image to create based on MIME type. 1.131 + if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) { 1.132 + return CreateVectorImage(aRequest, aStatusTracker, aMimeType, 1.133 + aURI, imageFlags, aInnerWindowId); 1.134 + } else { 1.135 + return CreateRasterImage(aRequest, aStatusTracker, aMimeType, 1.136 + aURI, imageFlags, aInnerWindowId); 1.137 + } 1.138 +} 1.139 + 1.140 +// Marks an image as having an error before returning it. Used with macros like 1.141 +// NS_ENSURE_SUCCESS, since we guarantee to always return an image even if an 1.142 +// error occurs, but callers need to be able to tell that this happened. 1.143 +template <typename T> 1.144 +static already_AddRefed<Image> 1.145 +BadImage(nsRefPtr<T>& image) 1.146 +{ 1.147 + image->SetHasError(); 1.148 + return image.forget(); 1.149 +} 1.150 + 1.151 +/* static */ already_AddRefed<Image> 1.152 +ImageFactory::CreateAnonymousImage(const nsCString& aMimeType) 1.153 +{ 1.154 + nsresult rv; 1.155 + 1.156 + nsRefPtr<RasterImage> newImage = new RasterImage(); 1.157 + 1.158 + rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_NONE); 1.159 + NS_ENSURE_SUCCESS(rv, BadImage(newImage)); 1.160 + 1.161 + return newImage.forget(); 1.162 +} 1.163 + 1.164 +int32_t 1.165 +SaturateToInt32(int64_t val) 1.166 +{ 1.167 + if (val > INT_MAX) 1.168 + return INT_MAX; 1.169 + if (val < INT_MIN) 1.170 + return INT_MIN; 1.171 + 1.172 + return static_cast<int32_t>(val); 1.173 +} 1.174 + 1.175 +uint32_t 1.176 +GetContentSize(nsIRequest* aRequest) 1.177 +{ 1.178 + nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); 1.179 + if (channel) { 1.180 + int64_t size; 1.181 + nsresult rv = channel->GetContentLength(&size); 1.182 + if (NS_SUCCEEDED(rv)) { 1.183 + return std::max(SaturateToInt32(size), 0); 1.184 + } 1.185 + } 1.186 + 1.187 + // Use the file size as a size hint for file channels. 1.188 + nsCOMPtr<nsIFileChannel> fileChannel(do_QueryInterface(aRequest)); 1.189 + if (fileChannel) { 1.190 + nsCOMPtr<nsIFile> file; 1.191 + nsresult rv = fileChannel->GetFile(getter_AddRefs(file)); 1.192 + if (NS_SUCCEEDED(rv)) { 1.193 + int64_t filesize; 1.194 + rv = file->GetFileSize(&filesize); 1.195 + if (NS_SUCCEEDED(rv)) { 1.196 + return std::max(SaturateToInt32(filesize), 0); 1.197 + } 1.198 + } 1.199 + } 1.200 + 1.201 + // Fallback - neither http nor file. We'll use dynamic allocation. 1.202 + return 0; 1.203 +} 1.204 + 1.205 +/* static */ already_AddRefed<Image> 1.206 +ImageFactory::CreateRasterImage(nsIRequest* aRequest, 1.207 + imgStatusTracker* aStatusTracker, 1.208 + const nsCString& aMimeType, 1.209 + ImageURL* aURI, 1.210 + uint32_t aImageFlags, 1.211 + uint32_t aInnerWindowId) 1.212 +{ 1.213 + nsresult rv; 1.214 + 1.215 + nsRefPtr<RasterImage> newImage = new RasterImage(aStatusTracker, aURI); 1.216 + 1.217 + rv = newImage->Init(aMimeType.get(), aImageFlags); 1.218 + NS_ENSURE_SUCCESS(rv, BadImage(newImage)); 1.219 + 1.220 + newImage->SetInnerWindowID(aInnerWindowId); 1.221 + 1.222 + uint32_t len = GetContentSize(aRequest); 1.223 + 1.224 + // Pass anything usable on so that the RasterImage can preallocate 1.225 + // its source buffer. 1.226 + if (len > 0) { 1.227 + uint32_t sizeHint = std::min<uint32_t>(len, 20000000); // Bound by something reasonable 1.228 + rv = newImage->SetSourceSizeHint(sizeHint); 1.229 + if (NS_FAILED(rv)) { 1.230 + // Flush memory, try to get some back, and try again. 1.231 + rv = nsMemory::HeapMinimize(true); 1.232 + nsresult rv2 = newImage->SetSourceSizeHint(sizeHint); 1.233 + // If we've still failed at this point, things are going downhill. 1.234 + if (NS_FAILED(rv) || NS_FAILED(rv2)) { 1.235 + NS_WARNING("About to hit OOM in imagelib!"); 1.236 + } 1.237 + } 1.238 + } 1.239 + 1.240 + nsAutoCString ref; 1.241 + aURI->GetRef(ref); 1.242 + mozilla::net::nsMediaFragmentURIParser parser(ref); 1.243 + if (parser.HasResolution()) { 1.244 + newImage->SetRequestedResolution(parser.GetResolution()); 1.245 + } 1.246 + 1.247 + if (parser.HasSampleSize()) { 1.248 + /* Get our principal */ 1.249 + nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); 1.250 + nsCOMPtr<nsIPrincipal> principal; 1.251 + if (chan) { 1.252 + nsContentUtils::GetSecurityManager()->GetChannelPrincipal(chan, 1.253 + getter_AddRefs(principal)); 1.254 + } 1.255 + 1.256 + if ((principal && 1.257 + principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED) || 1.258 + gEnableMozSampleSize) { 1.259 + newImage->SetRequestedSampleSize(parser.GetSampleSize()); 1.260 + } 1.261 + } 1.262 + 1.263 + return newImage.forget(); 1.264 +} 1.265 + 1.266 +/* static */ already_AddRefed<Image> 1.267 +ImageFactory::CreateVectorImage(nsIRequest* aRequest, 1.268 + imgStatusTracker* aStatusTracker, 1.269 + const nsCString& aMimeType, 1.270 + ImageURL* aURI, 1.271 + uint32_t aImageFlags, 1.272 + uint32_t aInnerWindowId) 1.273 +{ 1.274 + nsresult rv; 1.275 + 1.276 + nsRefPtr<VectorImage> newImage = new VectorImage(aStatusTracker, aURI); 1.277 + 1.278 + rv = newImage->Init(aMimeType.get(), aImageFlags); 1.279 + NS_ENSURE_SUCCESS(rv, BadImage(newImage)); 1.280 + 1.281 + newImage->SetInnerWindowID(aInnerWindowId); 1.282 + 1.283 + rv = newImage->OnStartRequest(aRequest, nullptr); 1.284 + NS_ENSURE_SUCCESS(rv, BadImage(newImage)); 1.285 + 1.286 + return newImage.forget(); 1.287 +} 1.288 + 1.289 +} // namespace image 1.290 +} // namespace mozilla