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