ImageFactory.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. #include "ImageFactory.h"
  7. #include <algorithm>
  8. #include "mozilla/Likely.h"
  9. #include "nsIHttpChannel.h"
  10. #include "nsIFileChannel.h"
  11. #include "nsIFile.h"
  12. #include "nsMimeTypes.h"
  13. #include "nsIRequest.h"
  14. #include "MultipartImage.h"
  15. #include "RasterImage.h"
  16. #include "VectorImage.h"
  17. #include "Image.h"
  18. #include "nsMediaFragmentURIParser.h"
  19. #include "nsContentUtils.h"
  20. #include "nsIScriptSecurityManager.h"
  21. #include "gfxPrefs.h"
  22. namespace mozilla {
  23. namespace image {
  24. /*static*/ void
  25. ImageFactory::Initialize()
  26. { }
  27. static uint32_t
  28. ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
  29. {
  30. nsresult rv;
  31. // We default to the static globals.
  32. bool isDiscardable = gfxPrefs::ImageMemDiscardable();
  33. bool doDecodeImmediately = gfxPrefs::ImageDecodeImmediatelyEnabled();
  34. // We want UI to be as snappy as possible and not to flicker. Disable
  35. // discarding for chrome URLS.
  36. bool isChrome = false;
  37. rv = uri->SchemeIs("chrome", &isChrome);
  38. if (NS_SUCCEEDED(rv) && isChrome) {
  39. isDiscardable = false;
  40. }
  41. // We don't want resources like the "loading" icon to be discardable either.
  42. bool isResource = false;
  43. rv = uri->SchemeIs("resource", &isResource);
  44. if (NS_SUCCEEDED(rv) && isResource) {
  45. isDiscardable = false;
  46. }
  47. // For multipart/x-mixed-replace, we basically want a direct channel to the
  48. // decoder. Disable everything for this case.
  49. if (isMultiPart) {
  50. isDiscardable = false;
  51. }
  52. // We have all the information we need.
  53. uint32_t imageFlags = Image::INIT_FLAG_NONE;
  54. if (isDiscardable) {
  55. imageFlags |= Image::INIT_FLAG_DISCARDABLE;
  56. }
  57. if (doDecodeImmediately) {
  58. imageFlags |= Image::INIT_FLAG_DECODE_IMMEDIATELY;
  59. }
  60. if (isMultiPart) {
  61. imageFlags |= Image::INIT_FLAG_TRANSIENT;
  62. }
  63. return imageFlags;
  64. }
  65. /* static */ already_AddRefed<Image>
  66. ImageFactory::CreateImage(nsIRequest* aRequest,
  67. ProgressTracker* aProgressTracker,
  68. const nsCString& aMimeType,
  69. ImageURL* aURI,
  70. bool aIsMultiPart,
  71. uint32_t aInnerWindowId)
  72. {
  73. MOZ_ASSERT(gfxPrefs::SingletonExists(),
  74. "Pref observers should have been initialized already");
  75. // Compute the image's initialization flags.
  76. uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
  77. // Select the type of image to create based on MIME type.
  78. if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
  79. return CreateVectorImage(aRequest, aProgressTracker, aMimeType,
  80. aURI, imageFlags, aInnerWindowId);
  81. } else {
  82. return CreateRasterImage(aRequest, aProgressTracker, aMimeType,
  83. aURI, imageFlags, aInnerWindowId);
  84. }
  85. }
  86. // Marks an image as having an error before returning it.
  87. template <typename T>
  88. static already_AddRefed<Image>
  89. BadImage(const char* aMessage, RefPtr<T>& aImage)
  90. {
  91. aImage->SetHasError();
  92. return aImage.forget();
  93. }
  94. /* static */ already_AddRefed<Image>
  95. ImageFactory::CreateAnonymousImage(const nsCString& aMimeType)
  96. {
  97. nsresult rv;
  98. RefPtr<RasterImage> newImage = new RasterImage();
  99. RefPtr<ProgressTracker> newTracker = new ProgressTracker();
  100. newTracker->SetImage(newImage);
  101. newImage->SetProgressTracker(newTracker);
  102. rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD);
  103. if (NS_FAILED(rv)) {
  104. return BadImage("RasterImage::Init failed", newImage);
  105. }
  106. return newImage.forget();
  107. }
  108. /* static */ already_AddRefed<MultipartImage>
  109. ImageFactory::CreateMultipartImage(Image* aFirstPart,
  110. ProgressTracker* aProgressTracker)
  111. {
  112. MOZ_ASSERT(aFirstPart);
  113. MOZ_ASSERT(aProgressTracker);
  114. RefPtr<MultipartImage> newImage = new MultipartImage(aFirstPart);
  115. aProgressTracker->SetImage(newImage);
  116. newImage->SetProgressTracker(aProgressTracker);
  117. newImage->Init();
  118. return newImage.forget();
  119. }
  120. int32_t
  121. SaturateToInt32(int64_t val)
  122. {
  123. if (val > INT_MAX) {
  124. return INT_MAX;
  125. }
  126. if (val < INT_MIN) {
  127. return INT_MIN;
  128. }
  129. return static_cast<int32_t>(val);
  130. }
  131. uint32_t
  132. GetContentSize(nsIRequest* aRequest)
  133. {
  134. nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  135. if (channel) {
  136. int64_t size;
  137. nsresult rv = channel->GetContentLength(&size);
  138. if (NS_SUCCEEDED(rv)) {
  139. return std::max(SaturateToInt32(size), 0);
  140. }
  141. }
  142. // Use the file size as a size hint for file channels.
  143. nsCOMPtr<nsIFileChannel> fileChannel(do_QueryInterface(aRequest));
  144. if (fileChannel) {
  145. nsCOMPtr<nsIFile> file;
  146. nsresult rv = fileChannel->GetFile(getter_AddRefs(file));
  147. if (NS_SUCCEEDED(rv)) {
  148. int64_t filesize;
  149. rv = file->GetFileSize(&filesize);
  150. if (NS_SUCCEEDED(rv)) {
  151. return std::max(SaturateToInt32(filesize), 0);
  152. }
  153. }
  154. }
  155. // Fallback - neither http nor file. We'll use dynamic allocation.
  156. return 0;
  157. }
  158. /* static */ already_AddRefed<Image>
  159. ImageFactory::CreateRasterImage(nsIRequest* aRequest,
  160. ProgressTracker* aProgressTracker,
  161. const nsCString& aMimeType,
  162. ImageURL* aURI,
  163. uint32_t aImageFlags,
  164. uint32_t aInnerWindowId)
  165. {
  166. MOZ_ASSERT(aProgressTracker);
  167. nsresult rv;
  168. RefPtr<RasterImage> newImage = new RasterImage(aURI);
  169. aProgressTracker->SetImage(newImage);
  170. newImage->SetProgressTracker(aProgressTracker);
  171. nsAutoCString ref;
  172. aURI->GetRef(ref);
  173. net::nsMediaFragmentURIParser parser(ref);
  174. if (parser.HasSampleSize()) {
  175. /* Get our principal */
  176. nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
  177. nsCOMPtr<nsIPrincipal> principal;
  178. if (chan) {
  179. nsContentUtils::GetSecurityManager()
  180. ->GetChannelResultPrincipal(chan, getter_AddRefs(principal));
  181. }
  182. if ((principal &&
  183. principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED) ||
  184. gfxPrefs::ImageMozSampleSizeEnabled()) {
  185. newImage->SetRequestedSampleSize(parser.GetSampleSize());
  186. }
  187. }
  188. rv = newImage->Init(aMimeType.get(), aImageFlags);
  189. if (NS_FAILED(rv)) {
  190. return BadImage("RasterImage::Init failed", newImage);
  191. }
  192. newImage->SetInnerWindowID(aInnerWindowId);
  193. uint32_t len = GetContentSize(aRequest);
  194. // Pass anything usable on so that the RasterImage can preallocate
  195. // its source buffer.
  196. if (len > 0) {
  197. // Bound by something reasonable
  198. uint32_t sizeHint = std::min<uint32_t>(len, 20000000);
  199. rv = newImage->SetSourceSizeHint(sizeHint);
  200. if (NS_FAILED(rv)) {
  201. // Flush memory, try to get some back, and try again.
  202. rv = nsMemory::HeapMinimize(true);
  203. nsresult rv2 = newImage->SetSourceSizeHint(sizeHint);
  204. // If we've still failed at this point, things are going downhill.
  205. if (NS_FAILED(rv) || NS_FAILED(rv2)) {
  206. NS_WARNING("About to hit OOM in imagelib!");
  207. }
  208. }
  209. }
  210. return newImage.forget();
  211. }
  212. /* static */ already_AddRefed<Image>
  213. ImageFactory::CreateVectorImage(nsIRequest* aRequest,
  214. ProgressTracker* aProgressTracker,
  215. const nsCString& aMimeType,
  216. ImageURL* aURI,
  217. uint32_t aImageFlags,
  218. uint32_t aInnerWindowId)
  219. {
  220. MOZ_ASSERT(aProgressTracker);
  221. nsresult rv;
  222. RefPtr<VectorImage> newImage = new VectorImage(aURI);
  223. aProgressTracker->SetImage(newImage);
  224. newImage->SetProgressTracker(aProgressTracker);
  225. rv = newImage->Init(aMimeType.get(), aImageFlags);
  226. if (NS_FAILED(rv)) {
  227. return BadImage("VectorImage::Init failed", newImage);
  228. }
  229. newImage->SetInnerWindowID(aInnerWindowId);
  230. rv = newImage->OnStartRequest(aRequest, nullptr);
  231. if (NS_FAILED(rv)) {
  232. return BadImage("VectorImage::OnStartRequest failed", newImage);
  233. }
  234. return newImage.forget();
  235. }
  236. } // namespace image
  237. } // namespace mozilla