ImageFactory.cpp (8006B)
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/. */ 6 7 #include "ImageFactory.h" 8 9 #include <algorithm> 10 11 #include "nsIChannel.h" 12 #include "nsIFileChannel.h" 13 #include "nsIObserverService.h" 14 #include "nsIFile.h" 15 #include "nsMimeTypes.h" 16 #include "nsIRequest.h" 17 18 #include "MultipartImage.h" 19 #include "RasterImage.h" 20 #include "VectorImage.h" 21 #include "Image.h" 22 #include "mozilla/MediaFragmentURIParser.h" 23 #include "nsContentUtils.h" 24 25 #include "mozilla/SchedulerGroup.h" 26 #include "mozilla/StaticPrefs_image.h" 27 #include "mozilla/ProfilerMarkers.h" 28 29 namespace mozilla { 30 namespace image { 31 32 /*static*/ 33 void ImageFactory::Initialize() {} 34 35 static uint32_t ComputeImageFlags(nsIURI* uri, const nsCString& aMimeType, 36 bool isMultiPart) { 37 // We default to the static globals. 38 bool isDiscardable = StaticPrefs::image_mem_discardable(); 39 bool doDecodeImmediately = StaticPrefs::image_decode_immediately_enabled(); 40 41 // We want UI to be as snappy as possible and not to flicker. Disable 42 // discarding for chrome URLS. 43 if (uri->SchemeIs("chrome")) { 44 isDiscardable = false; 45 } 46 47 // We don't want resources like the "loading" icon to be discardable either. 48 if (uri->SchemeIs("resource")) { 49 isDiscardable = false; 50 } 51 52 // For multipart/x-mixed-replace, we basically want a direct channel to the 53 // decoder. Disable everything for this case. 54 if (isMultiPart) { 55 isDiscardable = false; 56 } 57 58 // We have all the information we need. 59 uint32_t imageFlags = Image::INIT_FLAG_NONE; 60 if (isDiscardable) { 61 imageFlags |= Image::INIT_FLAG_DISCARDABLE; 62 } 63 if (doDecodeImmediately) { 64 imageFlags |= Image::INIT_FLAG_DECODE_IMMEDIATELY; 65 } 66 if (isMultiPart) { 67 imageFlags |= Image::INIT_FLAG_TRANSIENT; 68 } 69 70 // Synchronously decode metadata (including size) if we have a data URI since 71 // the data is immediately available. 72 if (uri->SchemeIs("data")) { 73 imageFlags |= Image::INIT_FLAG_SYNC_LOAD; 74 } 75 76 return imageFlags; 77 } 78 79 #ifdef DEBUG 80 static void NotifyImageLoading(nsIURI* aURI) { 81 if (!NS_IsMainThread()) { 82 nsCOMPtr<nsIURI> uri(aURI); 83 nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction( 84 "NotifyImageLoading", [uri]() -> void { NotifyImageLoading(uri); }); 85 NS_DispatchToMainThread(ev.forget()); 86 return; 87 } 88 89 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 90 NS_WARNING_ASSERTION(obs, "Can't get an observer service handle"); 91 if (obs) { 92 nsAutoCString spec; 93 aURI->GetSpec(spec); 94 obs->NotifyObservers(nullptr, "image-loading", 95 NS_ConvertUTF8toUTF16(spec).get()); 96 } 97 } 98 #endif 99 100 /* static */ 101 already_AddRefed<Image> ImageFactory::CreateImage( 102 nsIRequest* aRequest, ProgressTracker* aProgressTracker, 103 const nsCString& aMimeType, nsIURI* aURI, bool aIsMultiPart, 104 uint64_t aInnerWindowId) { 105 // Compute the image's initialization flags. 106 uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart); 107 108 #ifdef DEBUG 109 // Record the image load for startup performance testing. 110 if (aURI->SchemeIs("resource") || aURI->SchemeIs("chrome")) { 111 NotifyImageLoading(aURI); 112 } 113 #endif 114 115 if (profiler_thread_is_being_profiled_for_markers()) { 116 static const size_t sMaxTruncatedLength = 1024; 117 PROFILER_MARKER_TEXT( 118 "Image Load", GRAPHICS, MarkerInnerWindowId(aInnerWindowId), 119 nsContentUtils::TruncatedURLForDisplay(aURI, sMaxTruncatedLength)); 120 } 121 122 // Select the type of image to create based on MIME type. 123 if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) { 124 return CreateVectorImage(aRequest, aProgressTracker, aMimeType, aURI, 125 imageFlags, aInnerWindowId); 126 } else { 127 return CreateRasterImage(aRequest, aProgressTracker, aMimeType, aURI, 128 imageFlags, aInnerWindowId); 129 } 130 } 131 132 // Marks an image as having an error before returning it. 133 template <typename T> 134 static already_AddRefed<Image> BadImage(const char* aMessage, 135 RefPtr<T>& aImage) { 136 aImage->SetHasError(); 137 return aImage.forget(); 138 } 139 140 /* static */ 141 already_AddRefed<Image> ImageFactory::CreateAnonymousImage( 142 const nsCString& aMimeType, uint32_t aSizeHint /* = 0 */) { 143 nsresult rv; 144 145 RefPtr<RasterImage> newImage = new RasterImage(); 146 147 RefPtr<ProgressTracker> newTracker = new ProgressTracker(); 148 newTracker->SetImage(newImage); 149 newImage->SetProgressTracker(newTracker); 150 151 rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD); 152 if (NS_FAILED(rv)) { 153 return BadImage("RasterImage::Init failed", newImage); 154 } 155 156 rv = newImage->SetSourceSizeHint(aSizeHint); 157 if (NS_FAILED(rv)) { 158 return BadImage("RasterImage::SetSourceSizeHint failed", newImage); 159 } 160 161 return newImage.forget(); 162 } 163 164 /* static */ 165 already_AddRefed<MultipartImage> ImageFactory::CreateMultipartImage( 166 Image* aFirstPart, ProgressTracker* aProgressTracker) { 167 MOZ_ASSERT(aFirstPart); 168 MOZ_ASSERT(aProgressTracker); 169 170 RefPtr<MultipartImage> newImage = new MultipartImage(aFirstPart); 171 aProgressTracker->SetImage(newImage); 172 newImage->SetProgressTracker(aProgressTracker); 173 174 newImage->Init(); 175 176 return newImage.forget(); 177 } 178 179 int32_t SaturateToInt32(int64_t val) { 180 if (val > INT_MAX) { 181 return INT_MAX; 182 } 183 if (val < INT_MIN) { 184 return INT_MIN; 185 } 186 187 return static_cast<int32_t>(val); 188 } 189 190 uint32_t GetContentSize(nsIRequest* aRequest) { 191 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); 192 if (channel) { 193 int64_t size; 194 nsresult rv = channel->GetContentLength(&size); 195 if (NS_SUCCEEDED(rv)) { 196 return std::max(SaturateToInt32(size), 0); 197 } 198 } 199 200 // Use the file size as a size hint for file channels. 201 nsCOMPtr<nsIFileChannel> fileChannel(do_QueryInterface(aRequest)); 202 if (fileChannel) { 203 nsCOMPtr<nsIFile> file; 204 nsresult rv = fileChannel->GetFile(getter_AddRefs(file)); 205 if (NS_SUCCEEDED(rv)) { 206 int64_t filesize; 207 rv = file->GetFileSize(&filesize); 208 if (NS_SUCCEEDED(rv)) { 209 return std::max(SaturateToInt32(filesize), 0); 210 } 211 } 212 } 213 214 // Fallback - neither http nor file. We'll use dynamic allocation. 215 return 0; 216 } 217 218 /* static */ 219 already_AddRefed<Image> ImageFactory::CreateRasterImage( 220 nsIRequest* aRequest, ProgressTracker* aProgressTracker, 221 const nsCString& aMimeType, nsIURI* aURI, uint32_t aImageFlags, 222 uint64_t aInnerWindowId) { 223 MOZ_ASSERT(aProgressTracker); 224 225 nsresult rv; 226 227 RefPtr<RasterImage> newImage = new RasterImage(aURI); 228 aProgressTracker->SetImage(newImage); 229 newImage->SetProgressTracker(aProgressTracker); 230 231 rv = newImage->Init(aMimeType.get(), aImageFlags); 232 if (NS_FAILED(rv)) { 233 return BadImage("RasterImage::Init failed", newImage); 234 } 235 236 newImage->SetInnerWindowID(aInnerWindowId); 237 238 rv = newImage->SetSourceSizeHint(GetContentSize(aRequest)); 239 if (NS_FAILED(rv)) { 240 return BadImage("RasterImage::SetSourceSizeHint failed", newImage); 241 } 242 243 return newImage.forget(); 244 } 245 246 /* static */ 247 already_AddRefed<Image> ImageFactory::CreateVectorImage( 248 nsIRequest* aRequest, ProgressTracker* aProgressTracker, 249 const nsCString& aMimeType, nsIURI* aURI, uint32_t aImageFlags, 250 uint64_t aInnerWindowId) { 251 MOZ_ASSERT(aProgressTracker); 252 253 nsresult rv; 254 255 RefPtr<VectorImage> newImage = new VectorImage(aURI); 256 aProgressTracker->SetImage(newImage); 257 newImage->SetProgressTracker(aProgressTracker); 258 259 rv = newImage->Init(aMimeType.get(), aImageFlags); 260 if (NS_FAILED(rv)) { 261 return BadImage("VectorImage::Init failed", newImage); 262 } 263 264 newImage->SetInnerWindowID(aInnerWindowId); 265 266 rv = newImage->OnStartRequest(aRequest); 267 if (NS_FAILED(rv)) { 268 return BadImage("VectorImage::OnStartRequest failed", newImage); 269 } 270 271 return newImage.forget(); 272 } 273 274 } // namespace image 275 } // namespace mozilla