/src/mozilla-central/image/imgLoader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
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 | | // Undefine windows version of LoadImage because our code uses that name. |
8 | | #undef LoadImage |
9 | | |
10 | | #include "ImageLogging.h" |
11 | | #include "imgLoader.h" |
12 | | |
13 | | #include "mozilla/Attributes.h" |
14 | | #include "mozilla/ClearOnShutdown.h" |
15 | | #include "mozilla/Move.h" |
16 | | #include "mozilla/NullPrincipal.h" |
17 | | #include "mozilla/Preferences.h" |
18 | | #include "mozilla/ChaosMode.h" |
19 | | #include "mozilla/LoadInfo.h" |
20 | | #include "mozilla/Telemetry.h" |
21 | | |
22 | | #include "nsImageModule.h" |
23 | | #include "imgRequestProxy.h" |
24 | | |
25 | | #include "nsCOMPtr.h" |
26 | | |
27 | | #include "nsContentPolicyUtils.h" |
28 | | #include "nsContentUtils.h" |
29 | | #include "nsNetUtil.h" |
30 | | #include "nsNetCID.h" |
31 | | #include "nsIProtocolHandler.h" |
32 | | #include "nsMimeTypes.h" |
33 | | #include "nsStreamUtils.h" |
34 | | #include "nsIHttpChannel.h" |
35 | | #include "nsICacheInfoChannel.h" |
36 | | #include "nsIClassOfService.h" |
37 | | #include "nsIInterfaceRequestor.h" |
38 | | #include "nsIInterfaceRequestorUtils.h" |
39 | | #include "nsIProgressEventSink.h" |
40 | | #include "nsIChannelEventSink.h" |
41 | | #include "nsIAsyncVerifyRedirectCallback.h" |
42 | | #include "nsIFileURL.h" |
43 | | #include "nsIFile.h" |
44 | | #include "nsCRT.h" |
45 | | #include "nsINetworkPredictor.h" |
46 | | #include "nsReadableUtils.h" |
47 | | #include "mozilla/dom/ContentParent.h" |
48 | | #include "mozilla/dom/nsMixedContentBlocker.h" |
49 | | |
50 | | #include "nsIApplicationCache.h" |
51 | | #include "nsIApplicationCacheContainer.h" |
52 | | |
53 | | #include "nsIMemoryReporter.h" |
54 | | #include "DecoderFactory.h" |
55 | | #include "Image.h" |
56 | | #include "gfxPrefs.h" |
57 | | #include "prtime.h" |
58 | | |
59 | | // we want to explore making the document own the load group |
60 | | // so we can associate the document URI with the load group. |
61 | | // until this point, we have an evil hack: |
62 | | #include "nsIHttpChannelInternal.h" |
63 | | #include "nsILoadContext.h" |
64 | | #include "nsILoadGroupChild.h" |
65 | | #include "nsIDocShell.h" |
66 | | |
67 | | using namespace mozilla; |
68 | | using namespace mozilla::dom; |
69 | | using namespace mozilla::image; |
70 | | using namespace mozilla::net; |
71 | | |
72 | | MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf) |
73 | | |
74 | | class imgMemoryReporter final : public nsIMemoryReporter |
75 | | { |
76 | 0 | ~imgMemoryReporter() = default; |
77 | | |
78 | | public: |
79 | | NS_DECL_ISUPPORTS |
80 | | |
81 | | NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, |
82 | | nsISupports* aData, bool aAnonymize) override |
83 | 0 | { |
84 | 0 | nsTArray<ImageMemoryCounter> chrome; |
85 | 0 | nsTArray<ImageMemoryCounter> content; |
86 | 0 | nsTArray<ImageMemoryCounter> uncached; |
87 | 0 |
|
88 | 0 | for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) { |
89 | 0 | for (auto iter = mKnownLoaders[i]->mChromeCache.Iter(); !iter.Done(); iter.Next()) { |
90 | 0 | imgCacheEntry* entry = iter.UserData(); |
91 | 0 | RefPtr<imgRequest> req = entry->GetRequest(); |
92 | 0 | RecordCounterForRequest(req, &chrome, !entry->HasNoProxies()); |
93 | 0 | } |
94 | 0 | for (auto iter = mKnownLoaders[i]->mCache.Iter(); !iter.Done(); iter.Next()) { |
95 | 0 | imgCacheEntry* entry = iter.UserData(); |
96 | 0 | RefPtr<imgRequest> req = entry->GetRequest(); |
97 | 0 | RecordCounterForRequest(req, &content, !entry->HasNoProxies()); |
98 | 0 | } |
99 | 0 | MutexAutoLock lock(mKnownLoaders[i]->mUncachedImagesMutex); |
100 | 0 | for (auto iter = mKnownLoaders[i]->mUncachedImages.Iter(); |
101 | 0 | !iter.Done(); |
102 | 0 | iter.Next()) { |
103 | 0 | nsPtrHashKey<imgRequest>* entry = iter.Get(); |
104 | 0 | RefPtr<imgRequest> req = entry->GetKey(); |
105 | 0 | RecordCounterForRequest(req, &uncached, req->HasConsumers()); |
106 | 0 | } |
107 | 0 | } |
108 | 0 |
|
109 | 0 | // Note that we only need to anonymize content image URIs. |
110 | 0 |
|
111 | 0 | ReportCounterArray(aHandleReport, aData, chrome, "images/chrome"); |
112 | 0 |
|
113 | 0 | ReportCounterArray(aHandleReport, aData, content, "images/content", |
114 | 0 | aAnonymize); |
115 | 0 |
|
116 | 0 | // Uncached images may be content or chrome, so anonymize them. |
117 | 0 | ReportCounterArray(aHandleReport, aData, uncached, "images/uncached", |
118 | 0 | aAnonymize); |
119 | 0 |
|
120 | 0 | return NS_OK; |
121 | 0 | } |
122 | | |
123 | | static int64_t ImagesContentUsedUncompressedDistinguishedAmount() |
124 | 0 | { |
125 | 0 | size_t n = 0; |
126 | 0 | for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length(); |
127 | 0 | i++) { |
128 | 0 | for (auto iter = imgLoader::sMemReporter->mKnownLoaders[i]->mCache.Iter(); |
129 | 0 | !iter.Done(); |
130 | 0 | iter.Next()) { |
131 | 0 | imgCacheEntry* entry = iter.UserData(); |
132 | 0 | if (entry->HasNoProxies()) { |
133 | 0 | continue; |
134 | 0 | } |
135 | 0 | |
136 | 0 | RefPtr<imgRequest> req = entry->GetRequest(); |
137 | 0 | RefPtr<image::Image> image = req->GetImage(); |
138 | 0 | if (!image) { |
139 | 0 | continue; |
140 | 0 | } |
141 | 0 | |
142 | 0 | // Both this and EntryImageSizes measure images/content/raster/used/decoded |
143 | 0 | // memory. This function's measurement is secondary -- the result doesn't |
144 | 0 | // go in the "explicit" tree -- so we use moz_malloc_size_of instead of |
145 | 0 | // ImagesMallocSizeOf to prevent DMD from seeing it reported twice. |
146 | 0 | SizeOfState state(moz_malloc_size_of); |
147 | 0 | ImageMemoryCounter counter(image, state, /* aIsUsed = */ true); |
148 | 0 |
|
149 | 0 | n += counter.Values().DecodedHeap(); |
150 | 0 | n += counter.Values().DecodedNonHeap(); |
151 | 0 | } |
152 | 0 | } |
153 | 0 | return n; |
154 | 0 | } |
155 | | |
156 | | void RegisterLoader(imgLoader* aLoader) |
157 | 0 | { |
158 | 0 | mKnownLoaders.AppendElement(aLoader); |
159 | 0 | } |
160 | | |
161 | | void UnregisterLoader(imgLoader* aLoader) |
162 | 0 | { |
163 | 0 | mKnownLoaders.RemoveElement(aLoader); |
164 | 0 | } |
165 | | |
166 | | private: |
167 | | nsTArray<imgLoader*> mKnownLoaders; |
168 | | |
169 | | struct MemoryTotal |
170 | | { |
171 | | MemoryTotal& operator+=(const ImageMemoryCounter& aImageCounter) |
172 | 0 | { |
173 | 0 | if (aImageCounter.Type() == imgIContainer::TYPE_RASTER) { |
174 | 0 | if (aImageCounter.IsUsed()) { |
175 | 0 | mUsedRasterCounter += aImageCounter.Values(); |
176 | 0 | } else { |
177 | 0 | mUnusedRasterCounter += aImageCounter.Values(); |
178 | 0 | } |
179 | 0 | } else if (aImageCounter.Type() == imgIContainer::TYPE_VECTOR) { |
180 | 0 | if (aImageCounter.IsUsed()) { |
181 | 0 | mUsedVectorCounter += aImageCounter.Values(); |
182 | 0 | } else { |
183 | 0 | mUnusedVectorCounter += aImageCounter.Values(); |
184 | 0 | } |
185 | 0 | } else { |
186 | 0 | MOZ_CRASH("Unexpected image type"); |
187 | 0 | } |
188 | 0 |
|
189 | 0 | return *this; |
190 | 0 | } |
191 | | |
192 | 0 | const MemoryCounter& UsedRaster() const { return mUsedRasterCounter; } |
193 | 0 | const MemoryCounter& UnusedRaster() const { return mUnusedRasterCounter; } |
194 | 0 | const MemoryCounter& UsedVector() const { return mUsedVectorCounter; } |
195 | 0 | const MemoryCounter& UnusedVector() const { return mUnusedVectorCounter; } |
196 | | |
197 | | private: |
198 | | MemoryCounter mUsedRasterCounter; |
199 | | MemoryCounter mUnusedRasterCounter; |
200 | | MemoryCounter mUsedVectorCounter; |
201 | | MemoryCounter mUnusedVectorCounter; |
202 | | }; |
203 | | |
204 | | // Reports all images of a single kind, e.g. all used chrome images. |
205 | | void ReportCounterArray(nsIHandleReportCallback* aHandleReport, |
206 | | nsISupports* aData, |
207 | | nsTArray<ImageMemoryCounter>& aCounterArray, |
208 | | const char* aPathPrefix, |
209 | | bool aAnonymize = false) |
210 | 0 | { |
211 | 0 | MemoryTotal summaryTotal; |
212 | 0 | MemoryTotal nonNotableTotal; |
213 | 0 |
|
214 | 0 | // Report notable images, and compute total and non-notable aggregate sizes. |
215 | 0 | for (uint32_t i = 0; i < aCounterArray.Length(); i++) { |
216 | 0 | ImageMemoryCounter& counter = aCounterArray[i]; |
217 | 0 |
|
218 | 0 | if (aAnonymize) { |
219 | 0 | counter.URI().Truncate(); |
220 | 0 | counter.URI().AppendPrintf("<anonymized-%u>", i); |
221 | 0 | } else { |
222 | 0 | // The URI could be an extremely long data: URI. Truncate if needed. |
223 | 0 | static const size_t max = 256; |
224 | 0 | if (counter.URI().Length() > max) { |
225 | 0 | counter.URI().Truncate(max); |
226 | 0 | counter.URI().AppendLiteral(" (truncated)"); |
227 | 0 | } |
228 | 0 | counter.URI().ReplaceChar('/', '\\'); |
229 | 0 | } |
230 | 0 |
|
231 | 0 | summaryTotal += counter; |
232 | 0 |
|
233 | 0 | if (counter.IsNotable()) { |
234 | 0 | ReportImage(aHandleReport, aData, aPathPrefix, counter); |
235 | 0 | } else { |
236 | 0 | nonNotableTotal += counter; |
237 | 0 | } |
238 | 0 | } |
239 | 0 |
|
240 | 0 | // Report non-notable images in aggregate. |
241 | 0 | ReportTotal(aHandleReport, aData, /* aExplicit = */ true, |
242 | 0 | aPathPrefix, "<non-notable images>/", nonNotableTotal); |
243 | 0 |
|
244 | 0 | // Report a summary in aggregate, outside of the explicit tree. |
245 | 0 | ReportTotal(aHandleReport, aData, /* aExplicit = */ false, |
246 | 0 | aPathPrefix, "", summaryTotal); |
247 | 0 | } |
248 | | |
249 | | static void ReportImage(nsIHandleReportCallback* aHandleReport, |
250 | | nsISupports* aData, |
251 | | const char* aPathPrefix, |
252 | | const ImageMemoryCounter& aCounter) |
253 | 0 | { |
254 | 0 | nsAutoCString pathPrefix(NS_LITERAL_CSTRING("explicit/")); |
255 | 0 | pathPrefix.Append(aPathPrefix); |
256 | 0 | pathPrefix.Append(aCounter.Type() == imgIContainer::TYPE_RASTER |
257 | 0 | ? "/raster/" |
258 | 0 | : "/vector/"); |
259 | 0 | pathPrefix.Append(aCounter.IsUsed() ? "used/" : "unused/"); |
260 | 0 | pathPrefix.AppendLiteral("image("); |
261 | 0 | pathPrefix.AppendInt(aCounter.IntrinsicSize().width); |
262 | 0 | pathPrefix.AppendLiteral("x"); |
263 | 0 | pathPrefix.AppendInt(aCounter.IntrinsicSize().height); |
264 | 0 | pathPrefix.AppendLiteral(", "); |
265 | 0 |
|
266 | 0 | if (aCounter.URI().IsEmpty()) { |
267 | 0 | pathPrefix.AppendLiteral("<unknown URI>"); |
268 | 0 | } else { |
269 | 0 | pathPrefix.Append(aCounter.URI()); |
270 | 0 | } |
271 | 0 |
|
272 | 0 | pathPrefix.AppendLiteral(")/"); |
273 | 0 |
|
274 | 0 | ReportSurfaces(aHandleReport, aData, pathPrefix, aCounter); |
275 | 0 |
|
276 | 0 | ReportSourceValue(aHandleReport, aData, pathPrefix, aCounter.Values()); |
277 | 0 | } |
278 | | |
279 | | static void ReportSurfaces(nsIHandleReportCallback* aHandleReport, |
280 | | nsISupports* aData, |
281 | | const nsACString& aPathPrefix, |
282 | | const ImageMemoryCounter& aCounter) |
283 | 0 | { |
284 | 0 | for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) { |
285 | 0 | nsAutoCString surfacePathPrefix(aPathPrefix); |
286 | 0 | if (counter.IsLocked()) { |
287 | 0 | surfacePathPrefix.AppendLiteral("locked/"); |
288 | 0 | } else { |
289 | 0 | surfacePathPrefix.AppendLiteral("unlocked/"); |
290 | 0 | } |
291 | 0 | if (counter.IsFactor2()) { |
292 | 0 | surfacePathPrefix.AppendLiteral("factor2/"); |
293 | 0 | } |
294 | 0 | if (counter.CannotSubstitute()) { |
295 | 0 | surfacePathPrefix.AppendLiteral("cannot_substitute/"); |
296 | 0 | } |
297 | 0 | surfacePathPrefix.AppendLiteral("surface("); |
298 | 0 | surfacePathPrefix.AppendInt(counter.Key().Size().width); |
299 | 0 | surfacePathPrefix.AppendLiteral("x"); |
300 | 0 | surfacePathPrefix.AppendInt(counter.Key().Size().height); |
301 | 0 |
|
302 | 0 | if (counter.Values().ExternalHandles() > 0) { |
303 | 0 | surfacePathPrefix.AppendLiteral(", external:"); |
304 | 0 | surfacePathPrefix.AppendInt(uint32_t(counter.Values().ExternalHandles())); |
305 | 0 | } |
306 | 0 |
|
307 | 0 | if (counter.Type() == SurfaceMemoryCounterType::NORMAL) { |
308 | 0 | PlaybackType playback = counter.Key().Playback(); |
309 | 0 | surfacePathPrefix.Append(playback == PlaybackType::eAnimated |
310 | 0 | ? " (animation)" |
311 | 0 | : ""); |
312 | 0 |
|
313 | 0 | if (counter.Key().Flags() != DefaultSurfaceFlags()) { |
314 | 0 | surfacePathPrefix.AppendLiteral(", flags:"); |
315 | 0 | surfacePathPrefix.AppendInt(uint32_t(counter.Key().Flags()), |
316 | 0 | /* aRadix = */ 16); |
317 | 0 | } |
318 | 0 |
|
319 | 0 | if (counter.Key().SVGContext()) { |
320 | 0 | const SVGImageContext& context = counter.Key().SVGContext().ref(); |
321 | 0 | surfacePathPrefix.AppendLiteral(", svgContext:[ "); |
322 | 0 | if (context.GetViewportSize()) { |
323 | 0 | const CSSIntSize& size = context.GetViewportSize().ref(); |
324 | 0 | surfacePathPrefix.AppendLiteral("viewport=("); |
325 | 0 | surfacePathPrefix.AppendInt(size.width); |
326 | 0 | surfacePathPrefix.AppendLiteral("x"); |
327 | 0 | surfacePathPrefix.AppendInt(size.height); |
328 | 0 | surfacePathPrefix.AppendLiteral(") "); |
329 | 0 | } |
330 | 0 | if (context.GetPreserveAspectRatio()) { |
331 | 0 | nsAutoString aspect; |
332 | 0 | context.GetPreserveAspectRatio()->ToString(aspect); |
333 | 0 | surfacePathPrefix.AppendLiteral("preserveAspectRatio=("); |
334 | 0 | LossyAppendUTF16toASCII(aspect, surfacePathPrefix); |
335 | 0 | surfacePathPrefix.AppendLiteral(") "); |
336 | 0 | } |
337 | 0 | if (context.GetContextPaint()) { |
338 | 0 | const SVGEmbeddingContextPaint* paint = context.GetContextPaint(); |
339 | 0 | surfacePathPrefix.AppendLiteral("contextPaint=("); |
340 | 0 | if (paint->GetFill()) { |
341 | 0 | surfacePathPrefix.AppendLiteral(" fill="); |
342 | 0 | surfacePathPrefix.AppendInt(paint->GetFill()->ToABGR(), 16); |
343 | 0 | } |
344 | 0 | if (paint->GetFillOpacity()) { |
345 | 0 | surfacePathPrefix.AppendLiteral(" fillOpa="); |
346 | 0 | surfacePathPrefix.AppendFloat(paint->GetFillOpacity()); |
347 | 0 | } |
348 | 0 | if (paint->GetStroke()) { |
349 | 0 | surfacePathPrefix.AppendLiteral(" stroke="); |
350 | 0 | surfacePathPrefix.AppendInt(paint->GetStroke()->ToABGR(), 16); |
351 | 0 | } |
352 | 0 | if (paint->GetStrokeOpacity()) { |
353 | 0 | surfacePathPrefix.AppendLiteral(" strokeOpa="); |
354 | 0 | surfacePathPrefix.AppendFloat(paint->GetStrokeOpacity()); |
355 | 0 | } |
356 | 0 | surfacePathPrefix.AppendLiteral(" ) "); |
357 | 0 | } |
358 | 0 | surfacePathPrefix.AppendLiteral("]"); |
359 | 0 | } |
360 | 0 | } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING) { |
361 | 0 | surfacePathPrefix.AppendLiteral(", compositing frame"); |
362 | 0 | } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING_PREV) { |
363 | 0 | surfacePathPrefix.AppendLiteral(", compositing prev frame"); |
364 | 0 | } else { |
365 | 0 | MOZ_ASSERT_UNREACHABLE("Unknown counter type"); |
366 | 0 | } |
367 | 0 |
|
368 | 0 | surfacePathPrefix.AppendLiteral(")/"); |
369 | 0 |
|
370 | 0 | ReportValues(aHandleReport, aData, surfacePathPrefix, counter.Values()); |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | | static void ReportTotal(nsIHandleReportCallback* aHandleReport, |
375 | | nsISupports* aData, |
376 | | bool aExplicit, |
377 | | const char* aPathPrefix, |
378 | | const char* aPathInfix, |
379 | | const MemoryTotal& aTotal) |
380 | 0 | { |
381 | 0 | nsAutoCString pathPrefix; |
382 | 0 | if (aExplicit) { |
383 | 0 | pathPrefix.AppendLiteral("explicit/"); |
384 | 0 | } |
385 | 0 | pathPrefix.Append(aPathPrefix); |
386 | 0 |
|
387 | 0 | nsAutoCString rasterUsedPrefix(pathPrefix); |
388 | 0 | rasterUsedPrefix.AppendLiteral("/raster/used/"); |
389 | 0 | rasterUsedPrefix.Append(aPathInfix); |
390 | 0 | ReportValues(aHandleReport, aData, rasterUsedPrefix, aTotal.UsedRaster()); |
391 | 0 |
|
392 | 0 | nsAutoCString rasterUnusedPrefix(pathPrefix); |
393 | 0 | rasterUnusedPrefix.AppendLiteral("/raster/unused/"); |
394 | 0 | rasterUnusedPrefix.Append(aPathInfix); |
395 | 0 | ReportValues(aHandleReport, aData, rasterUnusedPrefix, |
396 | 0 | aTotal.UnusedRaster()); |
397 | 0 |
|
398 | 0 | nsAutoCString vectorUsedPrefix(pathPrefix); |
399 | 0 | vectorUsedPrefix.AppendLiteral("/vector/used/"); |
400 | 0 | vectorUsedPrefix.Append(aPathInfix); |
401 | 0 | ReportValues(aHandleReport, aData, vectorUsedPrefix, aTotal.UsedVector()); |
402 | 0 |
|
403 | 0 | nsAutoCString vectorUnusedPrefix(pathPrefix); |
404 | 0 | vectorUnusedPrefix.AppendLiteral("/vector/unused/"); |
405 | 0 | vectorUnusedPrefix.Append(aPathInfix); |
406 | 0 | ReportValues(aHandleReport, aData, vectorUnusedPrefix, |
407 | 0 | aTotal.UnusedVector()); |
408 | 0 | } |
409 | | |
410 | | static void ReportValues(nsIHandleReportCallback* aHandleReport, |
411 | | nsISupports* aData, |
412 | | const nsACString& aPathPrefix, |
413 | | const MemoryCounter& aCounter) |
414 | 0 | { |
415 | 0 | ReportSourceValue(aHandleReport, aData, aPathPrefix, aCounter); |
416 | 0 |
|
417 | 0 | ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix, |
418 | 0 | "decoded-heap", |
419 | 0 | "Decoded image data which is stored on the heap.", |
420 | 0 | aCounter.DecodedHeap()); |
421 | 0 |
|
422 | 0 | ReportValue(aHandleReport, aData, KIND_NONHEAP, aPathPrefix, |
423 | 0 | "decoded-nonheap", |
424 | 0 | "Decoded image data which isn't stored on the heap.", |
425 | 0 | aCounter.DecodedNonHeap()); |
426 | 0 | } |
427 | | |
428 | | static void ReportSourceValue(nsIHandleReportCallback* aHandleReport, |
429 | | nsISupports* aData, |
430 | | const nsACString& aPathPrefix, |
431 | | const MemoryCounter& aCounter) |
432 | 0 | { |
433 | 0 | ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix, |
434 | 0 | "source", |
435 | 0 | "Raster image source data and vector image documents.", |
436 | 0 | aCounter.Source()); |
437 | 0 | } |
438 | | |
439 | | static void ReportValue(nsIHandleReportCallback* aHandleReport, |
440 | | nsISupports* aData, |
441 | | int32_t aKind, |
442 | | const nsACString& aPathPrefix, |
443 | | const char* aPathSuffix, |
444 | | const char* aDescription, |
445 | | size_t aValue) |
446 | 0 | { |
447 | 0 | if (aValue == 0) { |
448 | 0 | return; |
449 | 0 | } |
450 | 0 | |
451 | 0 | nsAutoCString desc(aDescription); |
452 | 0 | nsAutoCString path(aPathPrefix); |
453 | 0 | path.Append(aPathSuffix); |
454 | 0 |
|
455 | 0 | aHandleReport->Callback(EmptyCString(), path, aKind, UNITS_BYTES, |
456 | 0 | aValue, desc, aData); |
457 | 0 | } |
458 | | |
459 | | static void RecordCounterForRequest(imgRequest* aRequest, |
460 | | nsTArray<ImageMemoryCounter>* aArray, |
461 | | bool aIsUsed) |
462 | 0 | { |
463 | 0 | RefPtr<image::Image> image = aRequest->GetImage(); |
464 | 0 | if (!image) { |
465 | 0 | return; |
466 | 0 | } |
467 | 0 | |
468 | 0 | SizeOfState state(ImagesMallocSizeOf); |
469 | 0 | ImageMemoryCounter counter(image, state, aIsUsed); |
470 | 0 |
|
471 | 0 | aArray->AppendElement(std::move(counter)); |
472 | 0 | } |
473 | | }; |
474 | | |
475 | | NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter) |
476 | | |
477 | | NS_IMPL_ISUPPORTS(nsProgressNotificationProxy, |
478 | | nsIProgressEventSink, |
479 | | nsIChannelEventSink, |
480 | | nsIInterfaceRequestor) |
481 | | |
482 | | NS_IMETHODIMP |
483 | | nsProgressNotificationProxy::OnProgress(nsIRequest* request, |
484 | | nsISupports* ctxt, |
485 | | int64_t progress, |
486 | | int64_t progressMax) |
487 | 0 | { |
488 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
489 | 0 | request->GetLoadGroup(getter_AddRefs(loadGroup)); |
490 | 0 |
|
491 | 0 | nsCOMPtr<nsIProgressEventSink> target; |
492 | 0 | NS_QueryNotificationCallbacks(mOriginalCallbacks, |
493 | 0 | loadGroup, |
494 | 0 | NS_GET_IID(nsIProgressEventSink), |
495 | 0 | getter_AddRefs(target)); |
496 | 0 | if (!target) { |
497 | 0 | return NS_OK; |
498 | 0 | } |
499 | 0 | return target->OnProgress(mImageRequest, ctxt, progress, progressMax); |
500 | 0 | } |
501 | | |
502 | | NS_IMETHODIMP |
503 | | nsProgressNotificationProxy::OnStatus(nsIRequest* request, |
504 | | nsISupports* ctxt, |
505 | | nsresult status, |
506 | | const char16_t* statusArg) |
507 | 0 | { |
508 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
509 | 0 | request->GetLoadGroup(getter_AddRefs(loadGroup)); |
510 | 0 |
|
511 | 0 | nsCOMPtr<nsIProgressEventSink> target; |
512 | 0 | NS_QueryNotificationCallbacks(mOriginalCallbacks, |
513 | 0 | loadGroup, |
514 | 0 | NS_GET_IID(nsIProgressEventSink), |
515 | 0 | getter_AddRefs(target)); |
516 | 0 | if (!target) { |
517 | 0 | return NS_OK; |
518 | 0 | } |
519 | 0 | return target->OnStatus(mImageRequest, ctxt, status, statusArg); |
520 | 0 | } |
521 | | |
522 | | NS_IMETHODIMP |
523 | | nsProgressNotificationProxy:: |
524 | | AsyncOnChannelRedirect(nsIChannel* oldChannel, |
525 | | nsIChannel* newChannel, |
526 | | uint32_t flags, |
527 | | nsIAsyncVerifyRedirectCallback* cb) |
528 | 0 | { |
529 | 0 | // Tell the original original callbacks about it too |
530 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
531 | 0 | newChannel->GetLoadGroup(getter_AddRefs(loadGroup)); |
532 | 0 | nsCOMPtr<nsIChannelEventSink> target; |
533 | 0 | NS_QueryNotificationCallbacks(mOriginalCallbacks, |
534 | 0 | loadGroup, |
535 | 0 | NS_GET_IID(nsIChannelEventSink), |
536 | 0 | getter_AddRefs(target)); |
537 | 0 | if (!target) { |
538 | 0 | cb->OnRedirectVerifyCallback(NS_OK); |
539 | 0 | return NS_OK; |
540 | 0 | } |
541 | 0 | |
542 | 0 | // Delegate to |target| if set, reusing |cb| |
543 | 0 | return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb); |
544 | 0 | } |
545 | | |
546 | | NS_IMETHODIMP |
547 | | nsProgressNotificationProxy::GetInterface(const nsIID& iid, |
548 | | void** result) |
549 | 0 | { |
550 | 0 | if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) { |
551 | 0 | *result = static_cast<nsIProgressEventSink*>(this); |
552 | 0 | NS_ADDREF_THIS(); |
553 | 0 | return NS_OK; |
554 | 0 | } |
555 | 0 | if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) { |
556 | 0 | *result = static_cast<nsIChannelEventSink*>(this); |
557 | 0 | NS_ADDREF_THIS(); |
558 | 0 | return NS_OK; |
559 | 0 | } |
560 | 0 | if (mOriginalCallbacks) { |
561 | 0 | return mOriginalCallbacks->GetInterface(iid, result); |
562 | 0 | } |
563 | 0 | return NS_NOINTERFACE; |
564 | 0 | } |
565 | | |
566 | | static void |
567 | | NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, imgLoader* aLoader, |
568 | | const ImageCacheKey& aKey, |
569 | | imgRequest** aRequest, imgCacheEntry** aEntry) |
570 | 0 | { |
571 | 0 | RefPtr<imgRequest> request = new imgRequest(aLoader, aKey); |
572 | 0 | RefPtr<imgCacheEntry> entry = |
573 | 0 | new imgCacheEntry(aLoader, request, aForcePrincipalCheckForCacheEntry); |
574 | 0 | aLoader->AddToUncachedImages(request); |
575 | 0 | request.forget(aRequest); |
576 | 0 | entry.forget(aEntry); |
577 | 0 | } |
578 | | |
579 | | static bool |
580 | | ShouldRevalidateEntry(imgCacheEntry* aEntry, |
581 | | nsLoadFlags aFlags, |
582 | | bool aHasExpired) |
583 | 0 | { |
584 | 0 | bool bValidateEntry = false; |
585 | 0 |
|
586 | 0 | if (aFlags & nsIRequest::LOAD_BYPASS_CACHE) { |
587 | 0 | return false; |
588 | 0 | } |
589 | 0 | |
590 | 0 | if (aFlags & nsIRequest::VALIDATE_ALWAYS) { |
591 | 0 | bValidateEntry = true; |
592 | 0 | } else if (aEntry->GetMustValidate()) { |
593 | 0 | bValidateEntry = true; |
594 | 0 | } else if (aHasExpired) { |
595 | 0 | // The cache entry has expired... Determine whether the stale cache |
596 | 0 | // entry can be used without validation... |
597 | 0 | if (aFlags & (nsIRequest::VALIDATE_NEVER | |
598 | 0 | nsIRequest::VALIDATE_ONCE_PER_SESSION)) { |
599 | 0 | // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache |
600 | 0 | // entries to be used unless they have been explicitly marked to |
601 | 0 | // indicate that revalidation is necessary. |
602 | 0 | bValidateEntry = false; |
603 | 0 |
|
604 | 0 | } else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) { |
605 | 0 | // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise, |
606 | 0 | // the entry must be revalidated. |
607 | 0 | bValidateEntry = true; |
608 | 0 | } |
609 | 0 | } |
610 | 0 |
|
611 | 0 | return bValidateEntry; |
612 | 0 | } |
613 | | |
614 | | /* Call content policies on cached images that went through a redirect */ |
615 | | static bool |
616 | | ShouldLoadCachedImage(imgRequest* aImgRequest, |
617 | | nsISupports* aLoadingContext, |
618 | | nsIPrincipal* aTriggeringPrincipal, |
619 | | nsContentPolicyType aPolicyType, |
620 | | bool aSendCSPViolationReports) |
621 | 0 | { |
622 | 0 | /* Call content policies on cached images - Bug 1082837 |
623 | 0 | * Cached images are keyed off of the first uri in a redirect chain. |
624 | 0 | * Hence content policies don't get a chance to test the intermediate hops |
625 | 0 | * or the final desitnation. Here we test the final destination using |
626 | 0 | * mFinalURI off of the imgRequest and passing it into content policies. |
627 | 0 | * For Mixed Content Blocker, we do an additional check to determine if any |
628 | 0 | * of the intermediary hops went through an insecure redirect with the |
629 | 0 | * mHadInsecureRedirect flag |
630 | 0 | */ |
631 | 0 | bool insecureRedirect = aImgRequest->HadInsecureRedirect(); |
632 | 0 | nsCOMPtr<nsIURI> contentLocation; |
633 | 0 | aImgRequest->GetFinalURI(getter_AddRefs(contentLocation)); |
634 | 0 | nsresult rv; |
635 | 0 |
|
636 | 0 | nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aLoadingContext); |
637 | 0 | nsCOMPtr<nsIPrincipal> loadingPrincipal = requestingNode |
638 | 0 | ? requestingNode->NodePrincipal() |
639 | 0 | : aTriggeringPrincipal; |
640 | 0 | // If there is no context and also no triggeringPrincipal, then we use a fresh |
641 | 0 | // nullPrincipal as the loadingPrincipal because we can not create a loadinfo |
642 | 0 | // without a valid loadingPrincipal. |
643 | 0 | if (!loadingPrincipal) { |
644 | 0 | loadingPrincipal = NullPrincipal::CreateWithoutOriginAttributes(); |
645 | 0 | } |
646 | 0 |
|
647 | 0 | nsCOMPtr<nsILoadInfo> secCheckLoadInfo = |
648 | 0 | new LoadInfo(loadingPrincipal, |
649 | 0 | aTriggeringPrincipal, |
650 | 0 | requestingNode, |
651 | 0 | nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, |
652 | 0 | aPolicyType); |
653 | 0 |
|
654 | 0 | secCheckLoadInfo->SetSendCSPViolationEvents(aSendCSPViolationReports); |
655 | 0 |
|
656 | 0 | int16_t decision = nsIContentPolicy::REJECT_REQUEST; |
657 | 0 | rv = NS_CheckContentLoadPolicy(contentLocation, |
658 | 0 | secCheckLoadInfo, |
659 | 0 | EmptyCString(), //mime guess |
660 | 0 | &decision, |
661 | 0 | nsContentUtils::GetContentPolicy()); |
662 | 0 | if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) { |
663 | 0 | return false; |
664 | 0 | } |
665 | 0 | |
666 | 0 | // We call all Content Policies above, but we also have to call mcb |
667 | 0 | // individually to check the intermediary redirect hops are secure. |
668 | 0 | if (insecureRedirect) { |
669 | 0 | // Bug 1314356: If the image ended up in the cache upgraded by HSTS and the page |
670 | 0 | // uses upgrade-inscure-requests it had an insecure redirect (http->https). |
671 | 0 | // We need to invalidate the image and reload it because mixed content blocker |
672 | 0 | // only bails if upgrade-insecure-requests is set on the doc and the resource |
673 | 0 | // load is http: which would result in an incorrect mixed content warning. |
674 | 0 | nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aLoadingContext); |
675 | 0 | if (docShell) { |
676 | 0 | nsIDocument* document = docShell->GetDocument(); |
677 | 0 | if (document && document->GetUpgradeInsecureRequests(false)) { |
678 | 0 | return false; |
679 | 0 | } |
680 | 0 | } |
681 | 0 | |
682 | 0 | if (!nsContentUtils::IsSystemPrincipal(aTriggeringPrincipal)) { |
683 | 0 | // Set the requestingLocation from the aTriggeringPrincipal. |
684 | 0 | nsCOMPtr<nsIURI> requestingLocation; |
685 | 0 | if (aTriggeringPrincipal) { |
686 | 0 | rv = aTriggeringPrincipal->GetURI(getter_AddRefs(requestingLocation)); |
687 | 0 | NS_ENSURE_SUCCESS(rv, false); |
688 | 0 | } |
689 | 0 |
|
690 | 0 | // reset the decision for mixed content blocker check |
691 | 0 | decision = nsIContentPolicy::REJECT_REQUEST; |
692 | 0 | rv = nsMixedContentBlocker::ShouldLoad(insecureRedirect, |
693 | 0 | aPolicyType, |
694 | 0 | contentLocation, |
695 | 0 | requestingLocation, |
696 | 0 | aLoadingContext, |
697 | 0 | EmptyCString(), //mime guess |
698 | 0 | aTriggeringPrincipal, |
699 | 0 | &decision); |
700 | 0 | if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) { |
701 | 0 | return false; |
702 | 0 | } |
703 | 0 | } |
704 | 0 | } |
705 | 0 | |
706 | 0 | return true; |
707 | 0 | } |
708 | | |
709 | | // Returns true if this request is compatible with the given CORS mode on the |
710 | | // given loading principal, and false if the request may not be reused due |
711 | | // to CORS. Also checks the Referrer Policy, since requests with different |
712 | | // referrers/policies may generate different responses. |
713 | | static bool |
714 | | ValidateSecurityInfo(imgRequest* request, bool forcePrincipalCheck, |
715 | | int32_t corsmode, nsIPrincipal* triggeringPrincipal, |
716 | | nsISupports* aCX, nsContentPolicyType aPolicyType, |
717 | | ReferrerPolicy referrerPolicy) |
718 | 0 | { |
719 | 0 | // If the entry's Referrer Policy doesn't match, we can't use this request. |
720 | 0 | // XXX: this will return false if an image has different referrer attributes, |
721 | 0 | // i.e. we currently don't use the cached image but reload the image with |
722 | 0 | // the new referrer policy bug 1174921 |
723 | 0 | if (referrerPolicy != request->GetReferrerPolicy()) { |
724 | 0 | return false; |
725 | 0 | } |
726 | 0 | |
727 | 0 | // If the entry's CORS mode doesn't match, or the CORS mode matches but the |
728 | 0 | // document principal isn't the same, we can't use this request. |
729 | 0 | if (request->GetCORSMode() != corsmode) { |
730 | 0 | return false; |
731 | 0 | } |
732 | 0 | if (request->GetCORSMode() != imgIRequest::CORS_NONE || |
733 | 0 | forcePrincipalCheck) { |
734 | 0 | nsCOMPtr<nsIPrincipal> otherprincipal = request->GetTriggeringPrincipal(); |
735 | 0 |
|
736 | 0 | // If we previously had a principal, but we don't now, we can't use this |
737 | 0 | // request. |
738 | 0 | if (otherprincipal && !triggeringPrincipal) { |
739 | 0 | return false; |
740 | 0 | } |
741 | 0 | |
742 | 0 | if (otherprincipal && triggeringPrincipal) { |
743 | 0 | bool equals = false; |
744 | 0 | otherprincipal->Equals(triggeringPrincipal, &equals); |
745 | 0 | if (!equals) { |
746 | 0 | return false; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | } |
750 | 0 | |
751 | 0 | // Content Policy Check on Cached Images |
752 | 0 | return ShouldLoadCachedImage(request, aCX, triggeringPrincipal, aPolicyType, |
753 | 0 | /* aSendCSPViolationReports */ false); |
754 | 0 | } |
755 | | |
756 | | static nsresult |
757 | | NewImageChannel(nsIChannel** aResult, |
758 | | // If aForcePrincipalCheckForCacheEntry is true, then we will |
759 | | // force a principal check even when not using CORS before |
760 | | // assuming we have a cache hit on a cache entry that we |
761 | | // create for this channel. This is an out param that should |
762 | | // be set to true if this channel ends up depending on |
763 | | // aTriggeringPrincipal and false otherwise. |
764 | | bool* aForcePrincipalCheckForCacheEntry, |
765 | | nsIURI* aURI, |
766 | | nsIURI* aInitialDocumentURI, |
767 | | int32_t aCORSMode, |
768 | | nsIURI* aReferringURI, |
769 | | ReferrerPolicy aReferrerPolicy, |
770 | | nsILoadGroup* aLoadGroup, |
771 | | const nsCString& aAcceptHeader, |
772 | | nsLoadFlags aLoadFlags, |
773 | | nsContentPolicyType aPolicyType, |
774 | | nsIPrincipal* aTriggeringPrincipal, |
775 | | nsISupports* aRequestingContext, |
776 | | bool aRespectPrivacy) |
777 | 0 | { |
778 | 0 | MOZ_ASSERT(aResult); |
779 | 0 |
|
780 | 0 | nsresult rv; |
781 | 0 | nsCOMPtr<nsIHttpChannel> newHttpChannel; |
782 | 0 |
|
783 | 0 | nsCOMPtr<nsIInterfaceRequestor> callbacks; |
784 | 0 |
|
785 | 0 | if (aLoadGroup) { |
786 | 0 | // Get the notification callbacks from the load group for the new channel. |
787 | 0 | // |
788 | 0 | // XXX: This is not exactly correct, because the network request could be |
789 | 0 | // referenced by multiple windows... However, the new channel needs |
790 | 0 | // something. So, using the 'first' notification callbacks is better |
791 | 0 | // than nothing... |
792 | 0 | // |
793 | 0 | aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); |
794 | 0 | } |
795 | 0 |
|
796 | 0 | // Pass in a nullptr loadgroup because this is the underlying network |
797 | 0 | // request. This request may be referenced by several proxy image requests |
798 | 0 | // (possibly in different documents). |
799 | 0 | // If all of the proxy requests are canceled then this request should be |
800 | 0 | // canceled too. |
801 | 0 | // |
802 | 0 | aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI; |
803 | 0 |
|
804 | 0 | nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aRequestingContext); |
805 | 0 |
|
806 | 0 | nsSecurityFlags securityFlags = |
807 | 0 | aCORSMode == imgIRequest::CORS_NONE |
808 | 0 | ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
809 | 0 | : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; |
810 | 0 | if (aCORSMode == imgIRequest::CORS_ANONYMOUS) { |
811 | 0 | securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; |
812 | 0 | } else if (aCORSMode == imgIRequest::CORS_USE_CREDENTIALS) { |
813 | 0 | securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; |
814 | 0 | } |
815 | 0 | securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; |
816 | 0 |
|
817 | 0 | // Note we are calling NS_NewChannelWithTriggeringPrincipal() here with a |
818 | 0 | // node and a principal. This is for things like background images that are |
819 | 0 | // specified by user stylesheets, where the document is being styled, but |
820 | 0 | // the principal is that of the user stylesheet. |
821 | 0 | if (requestingNode && aTriggeringPrincipal) { |
822 | 0 | rv = NS_NewChannelWithTriggeringPrincipal(aResult, |
823 | 0 | aURI, |
824 | 0 | requestingNode, |
825 | 0 | aTriggeringPrincipal, |
826 | 0 | securityFlags, |
827 | 0 | aPolicyType, |
828 | 0 | nullptr, // PerformanceStorage |
829 | 0 | nullptr, // loadGroup |
830 | 0 | callbacks, |
831 | 0 | aLoadFlags); |
832 | 0 |
|
833 | 0 | if (NS_FAILED(rv)) { |
834 | 0 | return rv; |
835 | 0 | } |
836 | 0 | |
837 | 0 | if (aPolicyType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) { |
838 | 0 | // If this is a favicon loading, we will use the originAttributes from the |
839 | 0 | // triggeringPrincipal as the channel's originAttributes. This allows the favicon |
840 | 0 | // loading from XUL will use the correct originAttributes. |
841 | 0 |
|
842 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo(); |
843 | 0 | if (loadInfo) { |
844 | 0 | rv = |
845 | 0 | loadInfo->SetOriginAttributes(aTriggeringPrincipal->OriginAttributesRef()); |
846 | 0 | } |
847 | 0 | } |
848 | 0 | } else { |
849 | 0 | // either we are loading something inside a document, in which case |
850 | 0 | // we should always have a requestingNode, or we are loading something |
851 | 0 | // outside a document, in which case the triggeringPrincipal and |
852 | 0 | // triggeringPrincipal should always be the systemPrincipal. |
853 | 0 | // However, there are exceptions: one is Notifications which create a |
854 | 0 | // channel in the parent prcoess in which case we can't get a requestingNode. |
855 | 0 | rv = NS_NewChannel(aResult, |
856 | 0 | aURI, |
857 | 0 | nsContentUtils::GetSystemPrincipal(), |
858 | 0 | securityFlags, |
859 | 0 | aPolicyType, |
860 | 0 | nullptr, // PerformanceStorage |
861 | 0 | nullptr, // loadGroup |
862 | 0 | callbacks, |
863 | 0 | aLoadFlags); |
864 | 0 |
|
865 | 0 | if (NS_FAILED(rv)) { |
866 | 0 | return rv; |
867 | 0 | } |
868 | 0 | |
869 | 0 | // Use the OriginAttributes from the loading principal, if one is available, |
870 | 0 | // and adjust the private browsing ID based on what kind of load the caller |
871 | 0 | // has asked us to perform. |
872 | 0 | OriginAttributes attrs; |
873 | 0 | if (aTriggeringPrincipal) { |
874 | 0 | attrs = aTriggeringPrincipal->OriginAttributesRef(); |
875 | 0 | } |
876 | 0 | attrs.mPrivateBrowsingId = aRespectPrivacy ? 1 : 0; |
877 | 0 |
|
878 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo(); |
879 | 0 | if (loadInfo) { |
880 | 0 | rv = loadInfo->SetOriginAttributes(attrs); |
881 | 0 | } |
882 | 0 | } |
883 | 0 |
|
884 | 0 | if (NS_FAILED(rv)) { |
885 | 0 | return rv; |
886 | 0 | } |
887 | 0 | |
888 | 0 | // only inherit if we have a principal |
889 | 0 | *aForcePrincipalCheckForCacheEntry = |
890 | 0 | aTriggeringPrincipal && |
891 | 0 | nsContentUtils::ChannelShouldInheritPrincipal( |
892 | 0 | aTriggeringPrincipal, |
893 | 0 | aURI, |
894 | 0 | /* aInheritForAboutBlank */ false, |
895 | 0 | /* aForceInherit */ false); |
896 | 0 |
|
897 | 0 | // Initialize HTTP-specific attributes |
898 | 0 | newHttpChannel = do_QueryInterface(*aResult); |
899 | 0 | if (newHttpChannel) { |
900 | 0 | rv = newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), |
901 | 0 | aAcceptHeader, |
902 | 0 | false); |
903 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
904 | 0 |
|
905 | 0 | nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = |
906 | 0 | do_QueryInterface(newHttpChannel); |
907 | 0 | NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED); |
908 | 0 | rv = httpChannelInternal->SetDocumentURI(aInitialDocumentURI); |
909 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
910 | 0 | rv = newHttpChannel->SetReferrerWithPolicy(aReferringURI, aReferrerPolicy); |
911 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
912 | 0 | } |
913 | 0 |
|
914 | 0 | // Image channels are loaded by default with reduced priority. |
915 | 0 | nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult); |
916 | 0 | if (p) { |
917 | 0 | uint32_t priority = nsISupportsPriority::PRIORITY_LOW; |
918 | 0 |
|
919 | 0 | if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) { |
920 | 0 | ++priority; // further reduce priority for background loads |
921 | 0 | } |
922 | 0 |
|
923 | 0 | p->AdjustPriority(priority); |
924 | 0 | } |
925 | 0 |
|
926 | 0 | // Create a new loadgroup for this new channel, using the old group as |
927 | 0 | // the parent. The indirection keeps the channel insulated from cancels, |
928 | 0 | // but does allow a way for this revalidation to be associated with at |
929 | 0 | // least one base load group for scheduling/caching purposes. |
930 | 0 |
|
931 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); |
932 | 0 | nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup); |
933 | 0 | if (childLoadGroup) { |
934 | 0 | childLoadGroup->SetParentLoadGroup(aLoadGroup); |
935 | 0 | } |
936 | 0 | (*aResult)->SetLoadGroup(loadGroup); |
937 | 0 |
|
938 | 0 | return NS_OK; |
939 | 0 | } |
940 | | |
941 | | /* static */ uint32_t |
942 | | imgCacheEntry::SecondsFromPRTime(PRTime prTime) |
943 | 0 | { |
944 | 0 | return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC)); |
945 | 0 | } |
946 | | |
947 | | imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest* request, |
948 | | bool forcePrincipalCheck) |
949 | | : mLoader(loader), |
950 | | mRequest(request), |
951 | | mDataSize(0), |
952 | | mTouchedTime(SecondsFromPRTime(PR_Now())), |
953 | | mLoadTime(SecondsFromPRTime(PR_Now())), |
954 | | mExpiryTime(0), |
955 | | mMustValidate(false), |
956 | | // We start off as evicted so we don't try to update the cache. PutIntoCache |
957 | | // will set this to false. |
958 | | mEvicted(true), |
959 | | mHasNoProxies(true), |
960 | | mForcePrincipalCheck(forcePrincipalCheck) |
961 | 0 | { } |
962 | | |
963 | | imgCacheEntry::~imgCacheEntry() |
964 | 0 | { |
965 | 0 | LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()"); |
966 | 0 | } |
967 | | |
968 | | void |
969 | | imgCacheEntry::Touch(bool updateTime /* = true */) |
970 | 0 | { |
971 | 0 | LOG_SCOPE(gImgLog, "imgCacheEntry::Touch"); |
972 | 0 |
|
973 | 0 | if (updateTime) { |
974 | 0 | mTouchedTime = SecondsFromPRTime(PR_Now()); |
975 | 0 | } |
976 | 0 |
|
977 | 0 | UpdateCache(); |
978 | 0 | } |
979 | | |
980 | | void |
981 | | imgCacheEntry::UpdateCache(int32_t diff /* = 0 */) |
982 | 0 | { |
983 | 0 | // Don't update the cache if we've been removed from it or it doesn't care |
984 | 0 | // about our size or usage. |
985 | 0 | if (!Evicted() && HasNoProxies()) { |
986 | 0 | mLoader->CacheEntriesChanged(mRequest->IsChrome(), diff); |
987 | 0 | } |
988 | 0 | } |
989 | | |
990 | | void imgCacheEntry::UpdateLoadTime() |
991 | 0 | { |
992 | 0 | mLoadTime = SecondsFromPRTime(PR_Now()); |
993 | 0 | } |
994 | | |
995 | | void |
996 | | imgCacheEntry::SetHasNoProxies(bool hasNoProxies) |
997 | 0 | { |
998 | 0 | if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) { |
999 | 0 | if (hasNoProxies) { |
1000 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true", |
1001 | 0 | "uri", mRequest->CacheKey().URI()); |
1002 | 0 | } else { |
1003 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false", |
1004 | 0 | "uri", mRequest->CacheKey().URI()); |
1005 | 0 | } |
1006 | 0 | } |
1007 | 0 |
|
1008 | 0 | mHasNoProxies = hasNoProxies; |
1009 | 0 | } |
1010 | | |
1011 | | imgCacheQueue::imgCacheQueue() |
1012 | | : mDirty(false), |
1013 | | mSize(0) |
1014 | 0 | { } |
1015 | | |
1016 | | void |
1017 | | imgCacheQueue::UpdateSize(int32_t diff) |
1018 | 0 | { |
1019 | 0 | mSize += diff; |
1020 | 0 | } |
1021 | | |
1022 | | uint32_t |
1023 | | imgCacheQueue::GetSize() const |
1024 | 0 | { |
1025 | 0 | return mSize; |
1026 | 0 | } |
1027 | | |
1028 | | #include <algorithm> |
1029 | | using namespace std; |
1030 | | |
1031 | | void |
1032 | | imgCacheQueue::Remove(imgCacheEntry* entry) |
1033 | 0 | { |
1034 | 0 | uint64_t index = mQueue.IndexOf(entry); |
1035 | 0 | if (index == queueContainer::NoIndex) { |
1036 | 0 | return; |
1037 | 0 | } |
1038 | 0 | |
1039 | 0 | mSize -= mQueue[index]->GetDataSize(); |
1040 | 0 |
|
1041 | 0 | // If the queue is clean and this is the first entry, |
1042 | 0 | // then we can efficiently remove the entry without |
1043 | 0 | // dirtying the sort order. |
1044 | 0 | if (!IsDirty() && index == 0) { |
1045 | 0 | std::pop_heap(mQueue.begin(), mQueue.end(), |
1046 | 0 | imgLoader::CompareCacheEntries); |
1047 | 0 | mQueue.RemoveLastElement(); |
1048 | 0 | return; |
1049 | 0 | } |
1050 | 0 | |
1051 | 0 | // Remove from the middle of the list. This potentially |
1052 | 0 | // breaks the binary heap sort order. |
1053 | 0 | mQueue.RemoveElementAt(index); |
1054 | 0 |
|
1055 | 0 | // If we only have one entry or the queue is empty, though, |
1056 | 0 | // then the sort order is still effectively good. Simply |
1057 | 0 | // refresh the list to clear the dirty flag. |
1058 | 0 | if (mQueue.Length() <= 1) { |
1059 | 0 | Refresh(); |
1060 | 0 | return; |
1061 | 0 | } |
1062 | 0 | |
1063 | 0 | // Otherwise we must mark the queue dirty and potentially |
1064 | 0 | // trigger an expensive sort later. |
1065 | 0 | MarkDirty(); |
1066 | 0 | } |
1067 | | |
1068 | | void |
1069 | | imgCacheQueue::Push(imgCacheEntry* entry) |
1070 | 0 | { |
1071 | 0 | mSize += entry->GetDataSize(); |
1072 | 0 |
|
1073 | 0 | RefPtr<imgCacheEntry> refptr(entry); |
1074 | 0 | mQueue.AppendElement(std::move(refptr)); |
1075 | 0 | // If we're not dirty already, then we can efficiently add this to the |
1076 | 0 | // binary heap immediately. This is only O(log n). |
1077 | 0 | if (!IsDirty()) { |
1078 | 0 | std::push_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries); |
1079 | 0 | } |
1080 | 0 | } |
1081 | | |
1082 | | already_AddRefed<imgCacheEntry> |
1083 | | imgCacheQueue::Pop() |
1084 | 0 | { |
1085 | 0 | if (mQueue.IsEmpty()) { |
1086 | 0 | return nullptr; |
1087 | 0 | } |
1088 | 0 | if (IsDirty()) { |
1089 | 0 | Refresh(); |
1090 | 0 | } |
1091 | 0 |
|
1092 | 0 | std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries); |
1093 | 0 | RefPtr<imgCacheEntry> entry = mQueue.PopLastElement(); |
1094 | 0 |
|
1095 | 0 | mSize -= entry->GetDataSize(); |
1096 | 0 | return entry.forget(); |
1097 | 0 | } |
1098 | | |
1099 | | void |
1100 | | imgCacheQueue::Refresh() |
1101 | 0 | { |
1102 | 0 | // Resort the list. This is an O(3 * n) operation and best avoided |
1103 | 0 | // if possible. |
1104 | 0 | std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries); |
1105 | 0 | mDirty = false; |
1106 | 0 | } |
1107 | | |
1108 | | void |
1109 | | imgCacheQueue::MarkDirty() |
1110 | 0 | { |
1111 | 0 | mDirty = true; |
1112 | 0 | } |
1113 | | |
1114 | | bool |
1115 | | imgCacheQueue::IsDirty() |
1116 | 0 | { |
1117 | 0 | return mDirty; |
1118 | 0 | } |
1119 | | |
1120 | | uint32_t |
1121 | | imgCacheQueue::GetNumElements() const |
1122 | 0 | { |
1123 | 0 | return mQueue.Length(); |
1124 | 0 | } |
1125 | | |
1126 | | bool |
1127 | | imgCacheQueue::Contains(imgCacheEntry* aEntry) const |
1128 | 0 | { |
1129 | 0 | return mQueue.Contains(aEntry); |
1130 | 0 | } |
1131 | | |
1132 | | imgCacheQueue::iterator |
1133 | | imgCacheQueue::begin() |
1134 | 0 | { |
1135 | 0 | return mQueue.begin(); |
1136 | 0 | } |
1137 | | |
1138 | | imgCacheQueue::const_iterator |
1139 | | imgCacheQueue::begin() const |
1140 | 0 | { |
1141 | 0 | return mQueue.begin(); |
1142 | 0 | } |
1143 | | |
1144 | | imgCacheQueue::iterator |
1145 | | imgCacheQueue::end() |
1146 | 0 | { |
1147 | 0 | return mQueue.end(); |
1148 | 0 | } |
1149 | | |
1150 | | imgCacheQueue::const_iterator |
1151 | | imgCacheQueue::end() const |
1152 | 0 | { |
1153 | 0 | return mQueue.end(); |
1154 | 0 | } |
1155 | | |
1156 | | nsresult |
1157 | | imgLoader::CreateNewProxyForRequest(imgRequest* aRequest, |
1158 | | nsILoadGroup* aLoadGroup, |
1159 | | nsIDocument* aLoadingDocument, |
1160 | | imgINotificationObserver* aObserver, |
1161 | | nsLoadFlags aLoadFlags, |
1162 | | imgRequestProxy** _retval) |
1163 | 0 | { |
1164 | 0 | LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest", |
1165 | 0 | "imgRequest", aRequest); |
1166 | 0 |
|
1167 | 0 | /* XXX If we move decoding onto separate threads, we should save off the |
1168 | 0 | calling thread here and pass it off to |proxyRequest| so that it call |
1169 | 0 | proxy calls to |aObserver|. |
1170 | 0 | */ |
1171 | 0 |
|
1172 | 0 | RefPtr<imgRequestProxy> proxyRequest = new imgRequestProxy(); |
1173 | 0 |
|
1174 | 0 | /* It is important to call |SetLoadFlags()| before calling |Init()| because |
1175 | 0 | |Init()| adds the request to the loadgroup. |
1176 | 0 | */ |
1177 | 0 | proxyRequest->SetLoadFlags(aLoadFlags); |
1178 | 0 |
|
1179 | 0 | nsCOMPtr<nsIURI> uri; |
1180 | 0 | aRequest->GetURI(getter_AddRefs(uri)); |
1181 | 0 |
|
1182 | 0 | // init adds itself to imgRequest's list of observers |
1183 | 0 | nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, aLoadingDocument, |
1184 | 0 | uri, aObserver); |
1185 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1186 | 0 | return rv; |
1187 | 0 | } |
1188 | 0 | |
1189 | 0 | proxyRequest.forget(_retval); |
1190 | 0 | return NS_OK; |
1191 | 0 | } |
1192 | | |
1193 | | class imgCacheExpirationTracker final |
1194 | | : public nsExpirationTracker<imgCacheEntry, 3> |
1195 | | { |
1196 | | enum { TIMEOUT_SECONDS = 10 }; |
1197 | | public: |
1198 | | imgCacheExpirationTracker(); |
1199 | | |
1200 | | protected: |
1201 | | void NotifyExpired(imgCacheEntry* entry) override; |
1202 | | }; |
1203 | | |
1204 | | imgCacheExpirationTracker::imgCacheExpirationTracker() |
1205 | | : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000, |
1206 | | "imgCacheExpirationTracker", |
1207 | | SystemGroup::EventTargetFor(TaskCategory::Other)) |
1208 | 0 | { } |
1209 | | |
1210 | | void |
1211 | | imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry) |
1212 | 0 | { |
1213 | 0 | // Hold on to a reference to this entry, because the expiration tracker |
1214 | 0 | // mechanism doesn't. |
1215 | 0 | RefPtr<imgCacheEntry> kungFuDeathGrip(entry); |
1216 | 0 |
|
1217 | 0 | if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) { |
1218 | 0 | RefPtr<imgRequest> req = entry->GetRequest(); |
1219 | 0 | if (req) { |
1220 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, |
1221 | 0 | "imgCacheExpirationTracker::NotifyExpired", |
1222 | 0 | "entry", req->CacheKey().URI()); |
1223 | 0 | } |
1224 | 0 | } |
1225 | 0 |
|
1226 | 0 | // We can be called multiple times on the same entry. Don't do work multiple |
1227 | 0 | // times. |
1228 | 0 | if (!entry->Evicted()) { |
1229 | 0 | entry->Loader()->RemoveFromCache(entry); |
1230 | 0 | } |
1231 | 0 |
|
1232 | 0 | entry->Loader()->VerifyCacheSizes(); |
1233 | 0 | } |
1234 | | |
1235 | | |
1236 | | /////////////////////////////////////////////////////////////////////////////// |
1237 | | // imgLoader |
1238 | | /////////////////////////////////////////////////////////////////////////////// |
1239 | | |
1240 | | double imgLoader::sCacheTimeWeight; |
1241 | | uint32_t imgLoader::sCacheMaxSize; |
1242 | | imgMemoryReporter* imgLoader::sMemReporter; |
1243 | | |
1244 | | NS_IMPL_ISUPPORTS(imgLoader, imgILoader, nsIContentSniffer, imgICache, |
1245 | | nsISupportsWeakReference, nsIObserver) |
1246 | | |
1247 | | static imgLoader* gNormalLoader = nullptr; |
1248 | | static imgLoader* gPrivateBrowsingLoader = nullptr; |
1249 | | |
1250 | | /* static */ already_AddRefed<imgLoader> |
1251 | | imgLoader::CreateImageLoader() |
1252 | 0 | { |
1253 | 0 | // In some cases, such as xpctests, XPCOM modules are not automatically |
1254 | 0 | // initialized. We need to make sure that our module is initialized before |
1255 | 0 | // we hand out imgLoader instances and code starts using them. |
1256 | 0 | mozilla::image::EnsureModuleInitialized(); |
1257 | 0 |
|
1258 | 0 | RefPtr<imgLoader> loader = new imgLoader(); |
1259 | 0 | loader->Init(); |
1260 | 0 |
|
1261 | 0 | return loader.forget(); |
1262 | 0 | } |
1263 | | |
1264 | | imgLoader* |
1265 | | imgLoader::NormalLoader() |
1266 | 0 | { |
1267 | 0 | if (!gNormalLoader) { |
1268 | 0 | gNormalLoader = CreateImageLoader().take(); |
1269 | 0 | } |
1270 | 0 | return gNormalLoader; |
1271 | 0 | } |
1272 | | |
1273 | | imgLoader* |
1274 | | imgLoader::PrivateBrowsingLoader() |
1275 | 0 | { |
1276 | 0 | if (!gPrivateBrowsingLoader) { |
1277 | 0 | gPrivateBrowsingLoader = CreateImageLoader().take(); |
1278 | 0 | gPrivateBrowsingLoader->RespectPrivacyNotifications(); |
1279 | 0 | } |
1280 | 0 | return gPrivateBrowsingLoader; |
1281 | 0 | } |
1282 | | |
1283 | | imgLoader::imgLoader() |
1284 | | : mUncachedImagesMutex("imgLoader::UncachedImages"), mRespectPrivacy(false) |
1285 | 0 | { |
1286 | 0 | sMemReporter->AddRef(); |
1287 | 0 | sMemReporter->RegisterLoader(this); |
1288 | 0 | } |
1289 | | |
1290 | | imgLoader::~imgLoader() |
1291 | 0 | { |
1292 | 0 | ClearChromeImageCache(); |
1293 | 0 | ClearImageCache(); |
1294 | 0 | { |
1295 | 0 | // If there are any of our imgRequest's left they are in the uncached |
1296 | 0 | // images set, so clear their pointer to us. |
1297 | 0 | MutexAutoLock lock(mUncachedImagesMutex); |
1298 | 0 | for (auto iter = mUncachedImages.Iter(); !iter.Done(); iter.Next()) { |
1299 | 0 | nsPtrHashKey<imgRequest>* entry = iter.Get(); |
1300 | 0 | RefPtr<imgRequest> req = entry->GetKey(); |
1301 | 0 | req->ClearLoader(); |
1302 | 0 | } |
1303 | 0 | } |
1304 | 0 | sMemReporter->UnregisterLoader(this); |
1305 | 0 | sMemReporter->Release(); |
1306 | 0 | } |
1307 | | |
1308 | | void |
1309 | | imgLoader::VerifyCacheSizes() |
1310 | 0 | { |
1311 | | #ifdef DEBUG |
1312 | | if (!mCacheTracker) { |
1313 | | return; |
1314 | | } |
1315 | | |
1316 | | uint32_t cachesize = mCache.Count() + mChromeCache.Count(); |
1317 | | uint32_t queuesize = |
1318 | | mCacheQueue.GetNumElements() + mChromeCacheQueue.GetNumElements(); |
1319 | | uint32_t trackersize = 0; |
1320 | | for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(mCacheTracker.get()); |
1321 | | it.Next(); ){ |
1322 | | trackersize++; |
1323 | | } |
1324 | | MOZ_ASSERT(queuesize == trackersize, "Queue and tracker sizes out of sync!"); |
1325 | | MOZ_ASSERT(queuesize <= cachesize, "Queue has more elements than cache!"); |
1326 | | #endif |
1327 | | } |
1328 | | |
1329 | | imgLoader::imgCacheTable& |
1330 | | imgLoader::GetCache(bool aForChrome) |
1331 | 0 | { |
1332 | 0 | return aForChrome ? mChromeCache : mCache; |
1333 | 0 | } |
1334 | | |
1335 | | imgLoader::imgCacheTable& |
1336 | | imgLoader::GetCache(const ImageCacheKey& aKey) |
1337 | 0 | { |
1338 | 0 | return GetCache(aKey.IsChrome()); |
1339 | 0 | } |
1340 | | |
1341 | | imgCacheQueue& |
1342 | | imgLoader::GetCacheQueue(bool aForChrome) |
1343 | 0 | { |
1344 | 0 | return aForChrome ? mChromeCacheQueue : mCacheQueue; |
1345 | 0 |
|
1346 | 0 | } |
1347 | | |
1348 | | imgCacheQueue& |
1349 | | imgLoader::GetCacheQueue(const ImageCacheKey& aKey) |
1350 | 0 | { |
1351 | 0 | return GetCacheQueue(aKey.IsChrome()); |
1352 | 0 |
|
1353 | 0 | } |
1354 | | |
1355 | | void imgLoader::GlobalInit() |
1356 | 0 | { |
1357 | 0 | sCacheTimeWeight = gfxPrefs::ImageCacheTimeWeight() / 1000.0; |
1358 | 0 | int32_t cachesize = gfxPrefs::ImageCacheSize(); |
1359 | 0 | sCacheMaxSize = cachesize > 0 ? cachesize : 0; |
1360 | 0 |
|
1361 | 0 | sMemReporter = new imgMemoryReporter(); |
1362 | 0 | RegisterStrongMemoryReporter(sMemReporter); |
1363 | 0 | RegisterImagesContentUsedUncompressedDistinguishedAmount( |
1364 | 0 | imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount); |
1365 | 0 |
|
1366 | 0 | Telemetry::ScalarSet(Telemetry::ScalarID::IMAGES_WEBP_PROBE_OBSERVED, false); |
1367 | 0 | Telemetry::ScalarSet(Telemetry::ScalarID::IMAGES_WEBP_CONTENT_OBSERVED, false); |
1368 | 0 | } |
1369 | | |
1370 | | void imgLoader::ShutdownMemoryReporter() |
1371 | 0 | { |
1372 | 0 | UnregisterImagesContentUsedUncompressedDistinguishedAmount(); |
1373 | 0 | UnregisterStrongMemoryReporter(sMemReporter); |
1374 | 0 | } |
1375 | | |
1376 | | nsresult |
1377 | | imgLoader::InitCache() |
1378 | 0 | { |
1379 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
1380 | 0 | if (!os) { |
1381 | 0 | return NS_ERROR_FAILURE; |
1382 | 0 | } |
1383 | 0 | |
1384 | 0 | os->AddObserver(this, "memory-pressure", false); |
1385 | 0 | os->AddObserver(this, "chrome-flush-skin-caches", false); |
1386 | 0 | os->AddObserver(this, "chrome-flush-caches", false); |
1387 | 0 | os->AddObserver(this, "last-pb-context-exited", false); |
1388 | 0 | os->AddObserver(this, "profile-before-change", false); |
1389 | 0 | os->AddObserver(this, "xpcom-shutdown", false); |
1390 | 0 |
|
1391 | 0 | mCacheTracker = MakeUnique<imgCacheExpirationTracker>(); |
1392 | 0 |
|
1393 | 0 | return NS_OK; |
1394 | 0 | } |
1395 | | |
1396 | | nsresult |
1397 | | imgLoader::Init() |
1398 | 0 | { |
1399 | 0 | InitCache(); |
1400 | 0 |
|
1401 | 0 | ReadAcceptHeaderPref(); |
1402 | 0 |
|
1403 | 0 | Preferences::AddWeakObserver(this, "image.http.accept"); |
1404 | 0 |
|
1405 | 0 | return NS_OK; |
1406 | 0 | } |
1407 | | |
1408 | | NS_IMETHODIMP |
1409 | | imgLoader::RespectPrivacyNotifications() |
1410 | 0 | { |
1411 | 0 | mRespectPrivacy = true; |
1412 | 0 | return NS_OK; |
1413 | 0 | } |
1414 | | |
1415 | | NS_IMETHODIMP |
1416 | | imgLoader::Observe(nsISupports* aSubject, const char* aTopic, |
1417 | | const char16_t* aData) |
1418 | 0 | { |
1419 | 0 | // We listen for pref change notifications... |
1420 | 0 | if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { |
1421 | 0 | if (!NS_strcmp(aData, u"image.http.accept")) { |
1422 | 0 | ReadAcceptHeaderPref(); |
1423 | 0 | } |
1424 | 0 |
|
1425 | 0 | } else if (strcmp(aTopic, "memory-pressure") == 0) { |
1426 | 0 | MinimizeCaches(); |
1427 | 0 | } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 || |
1428 | 0 | strcmp(aTopic, "chrome-flush-caches") == 0) { |
1429 | 0 | MinimizeCaches(); |
1430 | 0 | ClearChromeImageCache(); |
1431 | 0 | } else if (strcmp(aTopic, "last-pb-context-exited") == 0) { |
1432 | 0 | if (mRespectPrivacy) { |
1433 | 0 | ClearImageCache(); |
1434 | 0 | ClearChromeImageCache(); |
1435 | 0 | } |
1436 | 0 | } else if (strcmp(aTopic, "profile-before-change") == 0) { |
1437 | 0 | mCacheTracker = nullptr; |
1438 | 0 | } else if (strcmp(aTopic, "xpcom-shutdown") == 0) { |
1439 | 0 | mCacheTracker = nullptr; |
1440 | 0 | ShutdownMemoryReporter(); |
1441 | 0 |
|
1442 | 0 | } else { |
1443 | 0 | // (Nothing else should bring us here) |
1444 | 0 | MOZ_ASSERT(0, "Invalid topic received"); |
1445 | 0 | } |
1446 | 0 |
|
1447 | 0 | return NS_OK; |
1448 | 0 | } |
1449 | | |
1450 | | void imgLoader::ReadAcceptHeaderPref() |
1451 | 0 | { |
1452 | 0 | nsAutoCString accept; |
1453 | 0 | nsresult rv = Preferences::GetCString("image.http.accept", accept); |
1454 | 0 | if (NS_SUCCEEDED(rv)) { |
1455 | 0 | mAcceptHeader = accept; |
1456 | 0 | } else { |
1457 | 0 | mAcceptHeader = |
1458 | 0 | IMAGE_PNG "," IMAGE_WILDCARD ";q=0.8," ANY_WILDCARD ";q=0.5"; |
1459 | 0 | } |
1460 | 0 | } |
1461 | | |
1462 | | NS_IMETHODIMP |
1463 | | imgLoader::ClearCache(bool chrome) |
1464 | 0 | { |
1465 | 0 | if (XRE_IsParentProcess()) { |
1466 | 0 | bool privateLoader = this == gPrivateBrowsingLoader; |
1467 | 0 | for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { |
1468 | 0 | Unused << cp->SendClearImageCache(privateLoader, chrome); |
1469 | 0 | } |
1470 | 0 | } |
1471 | 0 |
|
1472 | 0 | if (chrome) { |
1473 | 0 | return ClearChromeImageCache(); |
1474 | 0 | } |
1475 | 0 | return ClearImageCache(); |
1476 | 0 |
|
1477 | 0 | } |
1478 | | |
1479 | | NS_IMETHODIMP |
1480 | | imgLoader::RemoveEntriesFromPrincipal(nsIPrincipal* aPrincipal) |
1481 | 0 | { |
1482 | 0 | nsAutoString origin; |
1483 | 0 | nsresult rv = nsContentUtils::GetUTFOrigin(aPrincipal, origin); |
1484 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1485 | 0 | return rv; |
1486 | 0 | } |
1487 | 0 | |
1488 | 0 | AutoTArray<RefPtr<imgCacheEntry>, 128> entriesToBeRemoved; |
1489 | 0 |
|
1490 | 0 | imgCacheTable& cache = GetCache(nsContentUtils::IsSystemPrincipal(aPrincipal)); |
1491 | 0 | for (auto iter = cache.Iter(); !iter.Done(); iter.Next()) { |
1492 | 0 | auto& key = iter.Key(); |
1493 | 0 |
|
1494 | 0 | if (key.OriginAttributesRef() != BasePrincipal::Cast(aPrincipal)->OriginAttributesRef()) { |
1495 | 0 | continue; |
1496 | 0 | } |
1497 | 0 | |
1498 | 0 | nsAutoString imageOrigin; |
1499 | 0 | nsresult rv = nsContentUtils::GetUTFOrigin(key.URI(), imageOrigin); |
1500 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1501 | 0 | continue; |
1502 | 0 | } |
1503 | 0 | |
1504 | 0 | if (imageOrigin == origin) { |
1505 | 0 | entriesToBeRemoved.AppendElement(iter.Data()); |
1506 | 0 | } |
1507 | 0 | } |
1508 | 0 |
|
1509 | 0 | for (auto& entry : entriesToBeRemoved) { |
1510 | 0 | if (!RemoveFromCache(entry)) { |
1511 | 0 | NS_WARNING("Couldn't remove an entry from the cache in RemoveEntriesFromPrincipal()\n"); |
1512 | 0 | } |
1513 | 0 | } |
1514 | 0 |
|
1515 | 0 | return NS_OK; |
1516 | 0 | } |
1517 | | |
1518 | | NS_IMETHODIMP |
1519 | | imgLoader::RemoveEntry(nsIURI* aURI, |
1520 | | nsIDocument* aDoc) |
1521 | 0 | { |
1522 | 0 | if (aURI) { |
1523 | 0 | OriginAttributes attrs; |
1524 | 0 | if (aDoc) { |
1525 | 0 | nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal(); |
1526 | 0 | if (principal) { |
1527 | 0 | attrs = principal->OriginAttributesRef(); |
1528 | 0 | } |
1529 | 0 | } |
1530 | 0 |
|
1531 | 0 | nsresult rv = NS_OK; |
1532 | 0 | ImageCacheKey key(aURI, attrs, aDoc, rv); |
1533 | 0 | if (NS_SUCCEEDED(rv) && RemoveFromCache(key)) { |
1534 | 0 | return NS_OK; |
1535 | 0 | } |
1536 | 0 | } |
1537 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1538 | 0 | } |
1539 | | |
1540 | | NS_IMETHODIMP |
1541 | | imgLoader::FindEntryProperties(nsIURI* uri, |
1542 | | nsIDocument* aDoc, |
1543 | | nsIProperties** _retval) |
1544 | 0 | { |
1545 | 0 | *_retval = nullptr; |
1546 | 0 |
|
1547 | 0 | OriginAttributes attrs; |
1548 | 0 | if (aDoc) { |
1549 | 0 | nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal(); |
1550 | 0 | if (principal) { |
1551 | 0 | attrs = principal->OriginAttributesRef(); |
1552 | 0 | } |
1553 | 0 | } |
1554 | 0 |
|
1555 | 0 | nsresult rv; |
1556 | 0 | ImageCacheKey key(uri, attrs, aDoc, rv); |
1557 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1558 | 0 | imgCacheTable& cache = GetCache(key); |
1559 | 0 |
|
1560 | 0 | RefPtr<imgCacheEntry> entry; |
1561 | 0 | if (cache.Get(key, getter_AddRefs(entry)) && entry) { |
1562 | 0 | if (mCacheTracker && entry->HasNoProxies()) { |
1563 | 0 | mCacheTracker->MarkUsed(entry); |
1564 | 0 | } |
1565 | 0 |
|
1566 | 0 | RefPtr<imgRequest> request = entry->GetRequest(); |
1567 | 0 | if (request) { |
1568 | 0 | nsCOMPtr<nsIProperties> properties = request->Properties(); |
1569 | 0 | properties.forget(_retval); |
1570 | 0 | } |
1571 | 0 | } |
1572 | 0 |
|
1573 | 0 | return NS_OK; |
1574 | 0 | } |
1575 | | |
1576 | | NS_IMETHODIMP_(void) |
1577 | | imgLoader::ClearCacheForControlledDocument(nsIDocument* aDoc) |
1578 | 0 | { |
1579 | 0 | MOZ_ASSERT(aDoc); |
1580 | 0 | AutoTArray<RefPtr<imgCacheEntry>, 128> entriesToBeRemoved; |
1581 | 0 | imgCacheTable& cache = GetCache(false); |
1582 | 0 | for (auto iter = cache.Iter(); !iter.Done(); iter.Next()) { |
1583 | 0 | auto& key = iter.Key(); |
1584 | 0 | if (key.ControlledDocument() == aDoc) { |
1585 | 0 | entriesToBeRemoved.AppendElement(iter.Data()); |
1586 | 0 | } |
1587 | 0 | } |
1588 | 0 | for (auto& entry : entriesToBeRemoved) { |
1589 | 0 | if (!RemoveFromCache(entry)) { |
1590 | 0 | NS_WARNING("Couldn't remove an entry from the cache in ClearCacheForControlledDocument()\n"); |
1591 | 0 | } |
1592 | 0 | } |
1593 | 0 | } |
1594 | | |
1595 | | void |
1596 | | imgLoader::Shutdown() |
1597 | 0 | { |
1598 | 0 | NS_IF_RELEASE(gNormalLoader); |
1599 | 0 | gNormalLoader = nullptr; |
1600 | 0 | NS_IF_RELEASE(gPrivateBrowsingLoader); |
1601 | 0 | gPrivateBrowsingLoader = nullptr; |
1602 | 0 | } |
1603 | | |
1604 | | nsresult |
1605 | | imgLoader::ClearChromeImageCache() |
1606 | 0 | { |
1607 | 0 | return EvictEntries(mChromeCache); |
1608 | 0 | } |
1609 | | |
1610 | | nsresult |
1611 | | imgLoader::ClearImageCache() |
1612 | 0 | { |
1613 | 0 | return EvictEntries(mCache); |
1614 | 0 | } |
1615 | | |
1616 | | void |
1617 | | imgLoader::MinimizeCaches() |
1618 | 0 | { |
1619 | 0 | EvictEntries(mCacheQueue); |
1620 | 0 | EvictEntries(mChromeCacheQueue); |
1621 | 0 | } |
1622 | | |
1623 | | bool |
1624 | | imgLoader::PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* entry) |
1625 | 0 | { |
1626 | 0 | imgCacheTable& cache = GetCache(aKey); |
1627 | 0 |
|
1628 | 0 | LOG_STATIC_FUNC_WITH_PARAM(gImgLog, |
1629 | 0 | "imgLoader::PutIntoCache", "uri", aKey.URI()); |
1630 | 0 |
|
1631 | 0 | // Check to see if this request already exists in the cache. If so, we'll |
1632 | 0 | // replace the old version. |
1633 | 0 | RefPtr<imgCacheEntry> tmpCacheEntry; |
1634 | 0 | if (cache.Get(aKey, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) { |
1635 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
1636 | 0 | ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", |
1637 | 0 | nullptr)); |
1638 | 0 | RefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest(); |
1639 | 0 |
|
1640 | 0 | // If it already exists, and we're putting the same key into the cache, we |
1641 | 0 | // should remove the old version. |
1642 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
1643 | 0 | ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element", |
1644 | 0 | nullptr)); |
1645 | 0 |
|
1646 | 0 | RemoveFromCache(aKey); |
1647 | 0 | } else { |
1648 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
1649 | 0 | ("[this=%p] imgLoader::PutIntoCache --" |
1650 | 0 | " Element NOT already in the cache", nullptr)); |
1651 | 0 | } |
1652 | 0 |
|
1653 | 0 | cache.Put(aKey, entry); |
1654 | 0 |
|
1655 | 0 | // We can be called to resurrect an evicted entry. |
1656 | 0 | if (entry->Evicted()) { |
1657 | 0 | entry->SetEvicted(false); |
1658 | 0 | } |
1659 | 0 |
|
1660 | 0 | // If we're resurrecting an entry with no proxies, put it back in the |
1661 | 0 | // tracker and queue. |
1662 | 0 | if (entry->HasNoProxies()) { |
1663 | 0 | nsresult addrv = NS_OK; |
1664 | 0 |
|
1665 | 0 | if (mCacheTracker) { |
1666 | 0 | addrv = mCacheTracker->AddObject(entry); |
1667 | 0 | } |
1668 | 0 |
|
1669 | 0 | if (NS_SUCCEEDED(addrv)) { |
1670 | 0 | imgCacheQueue& queue = GetCacheQueue(aKey); |
1671 | 0 | queue.Push(entry); |
1672 | 0 | } |
1673 | 0 | } |
1674 | 0 |
|
1675 | 0 | RefPtr<imgRequest> request = entry->GetRequest(); |
1676 | 0 | request->SetIsInCache(true); |
1677 | 0 | RemoveFromUncachedImages(request); |
1678 | 0 |
|
1679 | 0 | return true; |
1680 | 0 | } |
1681 | | |
1682 | | bool |
1683 | | imgLoader::SetHasNoProxies(imgRequest* aRequest, imgCacheEntry* aEntry) |
1684 | 0 | { |
1685 | 0 | LOG_STATIC_FUNC_WITH_PARAM(gImgLog, |
1686 | 0 | "imgLoader::SetHasNoProxies", "uri", |
1687 | 0 | aRequest->CacheKey().URI()); |
1688 | 0 |
|
1689 | 0 | aEntry->SetHasNoProxies(true); |
1690 | 0 |
|
1691 | 0 | if (aEntry->Evicted()) { |
1692 | 0 | return false; |
1693 | 0 | } |
1694 | 0 | |
1695 | 0 | imgCacheQueue& queue = GetCacheQueue(aRequest->IsChrome()); |
1696 | 0 |
|
1697 | 0 | nsresult addrv = NS_OK; |
1698 | 0 |
|
1699 | 0 | if (mCacheTracker) { |
1700 | 0 | addrv = mCacheTracker->AddObject(aEntry); |
1701 | 0 | } |
1702 | 0 |
|
1703 | 0 | if (NS_SUCCEEDED(addrv)) { |
1704 | 0 | queue.Push(aEntry); |
1705 | 0 | } |
1706 | 0 |
|
1707 | 0 | imgCacheTable& cache = GetCache(aRequest->IsChrome()); |
1708 | 0 | CheckCacheLimits(cache, queue); |
1709 | 0 |
|
1710 | 0 | return true; |
1711 | 0 | } |
1712 | | |
1713 | | bool |
1714 | | imgLoader::SetHasProxies(imgRequest* aRequest) |
1715 | 0 | { |
1716 | 0 | VerifyCacheSizes(); |
1717 | 0 |
|
1718 | 0 | const ImageCacheKey& key = aRequest->CacheKey(); |
1719 | 0 | imgCacheTable& cache = GetCache(key); |
1720 | 0 |
|
1721 | 0 | LOG_STATIC_FUNC_WITH_PARAM(gImgLog, |
1722 | 0 | "imgLoader::SetHasProxies", "uri", key.URI()); |
1723 | 0 |
|
1724 | 0 | RefPtr<imgCacheEntry> entry; |
1725 | 0 | if (cache.Get(key, getter_AddRefs(entry)) && entry) { |
1726 | 0 | // Make sure the cache entry is for the right request |
1727 | 0 | RefPtr<imgRequest> entryRequest = entry->GetRequest(); |
1728 | 0 | if (entryRequest == aRequest && entry->HasNoProxies()) { |
1729 | 0 | imgCacheQueue& queue = GetCacheQueue(key); |
1730 | 0 | queue.Remove(entry); |
1731 | 0 |
|
1732 | 0 | if (mCacheTracker) { |
1733 | 0 | mCacheTracker->RemoveObject(entry); |
1734 | 0 | } |
1735 | 0 |
|
1736 | 0 | entry->SetHasNoProxies(false); |
1737 | 0 |
|
1738 | 0 | return true; |
1739 | 0 | } |
1740 | 0 | } |
1741 | 0 |
|
1742 | 0 | return false; |
1743 | 0 | } |
1744 | | |
1745 | | void |
1746 | | imgLoader::CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff /* = 0 */) |
1747 | 0 | { |
1748 | 0 | imgCacheQueue& queue = GetCacheQueue(aForChrome); |
1749 | 0 | // We only need to dirty the queue if there is any sorting |
1750 | 0 | // taking place. Empty or single-entry lists can't become |
1751 | 0 | // dirty. |
1752 | 0 | if (queue.GetNumElements() > 1) { |
1753 | 0 | queue.MarkDirty(); |
1754 | 0 | } |
1755 | 0 | queue.UpdateSize(aSizeDiff); |
1756 | 0 | } |
1757 | | |
1758 | | void |
1759 | | imgLoader::CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue) |
1760 | 0 | { |
1761 | 0 | if (queue.GetNumElements() == 0) { |
1762 | 0 | NS_ASSERTION(queue.GetSize() == 0, |
1763 | 0 | "imgLoader::CheckCacheLimits -- incorrect cache size"); |
1764 | 0 | } |
1765 | 0 |
|
1766 | 0 | // Remove entries from the cache until we're back at our desired max size. |
1767 | 0 | while (queue.GetSize() > sCacheMaxSize) { |
1768 | 0 | // Remove the first entry in the queue. |
1769 | 0 | RefPtr<imgCacheEntry> entry(queue.Pop()); |
1770 | 0 |
|
1771 | 0 | NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer"); |
1772 | 0 |
|
1773 | 0 | if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) { |
1774 | 0 | RefPtr<imgRequest> req = entry->GetRequest(); |
1775 | 0 | if (req) { |
1776 | 0 | LOG_STATIC_FUNC_WITH_PARAM(gImgLog, |
1777 | 0 | "imgLoader::CheckCacheLimits", |
1778 | 0 | "entry", req->CacheKey().URI()); |
1779 | 0 | } |
1780 | 0 | } |
1781 | 0 |
|
1782 | 0 | if (entry) { |
1783 | 0 | // We just popped this entry from the queue, so pass AlreadyRemoved |
1784 | 0 | // to avoid searching the queue again in RemoveFromCache. |
1785 | 0 | RemoveFromCache(entry, QueueState::AlreadyRemoved); |
1786 | 0 | } |
1787 | 0 | } |
1788 | 0 | } |
1789 | | |
1790 | | bool |
1791 | | imgLoader::ValidateRequestWithNewChannel(imgRequest* request, |
1792 | | nsIURI* aURI, |
1793 | | nsIURI* aInitialDocumentURI, |
1794 | | nsIURI* aReferrerURI, |
1795 | | ReferrerPolicy aReferrerPolicy, |
1796 | | nsILoadGroup* aLoadGroup, |
1797 | | imgINotificationObserver* aObserver, |
1798 | | nsISupports* aCX, |
1799 | | nsIDocument* aLoadingDocument, |
1800 | | nsLoadFlags aLoadFlags, |
1801 | | nsContentPolicyType aLoadPolicyType, |
1802 | | imgRequestProxy** aProxyRequest, |
1803 | | nsIPrincipal* aTriggeringPrincipal, |
1804 | | int32_t aCORSMode, |
1805 | | bool* aNewChannelCreated) |
1806 | 0 | { |
1807 | 0 | // now we need to insert a new channel request object inbetween the real |
1808 | 0 | // request and the proxy that basically delays loading the image until it |
1809 | 0 | // gets a 304 or figures out that this needs to be a new request |
1810 | 0 |
|
1811 | 0 | nsresult rv; |
1812 | 0 |
|
1813 | 0 | // If we're currently in the middle of validating this request, just hand |
1814 | 0 | // back a proxy to it; the required work will be done for us. |
1815 | 0 | if (request->GetValidator()) { |
1816 | 0 | rv = CreateNewProxyForRequest(request, aLoadGroup, aLoadingDocument, |
1817 | 0 | aObserver, aLoadFlags, aProxyRequest); |
1818 | 0 | if (NS_FAILED(rv)) { |
1819 | 0 | return false; |
1820 | 0 | } |
1821 | 0 | |
1822 | 0 | if (*aProxyRequest) { |
1823 | 0 | imgRequestProxy* proxy = static_cast<imgRequestProxy*>(*aProxyRequest); |
1824 | 0 |
|
1825 | 0 | // We will send notifications from imgCacheValidator::OnStartRequest(). |
1826 | 0 | // In the mean time, we must defer notifications because we are added to |
1827 | 0 | // the imgRequest's proxy list, and we can get extra notifications |
1828 | 0 | // resulting from methods such as StartDecoding(). See bug 579122. |
1829 | 0 | proxy->MarkValidating(); |
1830 | 0 |
|
1831 | 0 | // Attach the proxy without notifying |
1832 | 0 | request->GetValidator()->AddProxy(proxy); |
1833 | 0 | } |
1834 | 0 |
|
1835 | 0 | return NS_SUCCEEDED(rv); |
1836 | 0 |
|
1837 | 0 | } |
1838 | 0 | // We will rely on Necko to cache this request when it's possible, and to |
1839 | 0 | // tell imgCacheValidator::OnStartRequest whether the request came from its |
1840 | 0 | // cache. |
1841 | 0 | nsCOMPtr<nsIChannel> newChannel; |
1842 | 0 | bool forcePrincipalCheck; |
1843 | 0 | rv = NewImageChannel(getter_AddRefs(newChannel), |
1844 | 0 | &forcePrincipalCheck, |
1845 | 0 | aURI, |
1846 | 0 | aInitialDocumentURI, |
1847 | 0 | aCORSMode, |
1848 | 0 | aReferrerURI, |
1849 | 0 | aReferrerPolicy, |
1850 | 0 | aLoadGroup, |
1851 | 0 | mAcceptHeader, |
1852 | 0 | aLoadFlags, |
1853 | 0 | aLoadPolicyType, |
1854 | 0 | aTriggeringPrincipal, |
1855 | 0 | aCX, |
1856 | 0 | mRespectPrivacy); |
1857 | 0 | if (NS_FAILED(rv)) { |
1858 | 0 | return false; |
1859 | 0 | } |
1860 | 0 | |
1861 | 0 | if (aNewChannelCreated) { |
1862 | 0 | *aNewChannelCreated = true; |
1863 | 0 | } |
1864 | 0 |
|
1865 | 0 | RefPtr<imgRequestProxy> req; |
1866 | 0 | rv = CreateNewProxyForRequest(request, aLoadGroup, aLoadingDocument, |
1867 | 0 | aObserver, aLoadFlags, getter_AddRefs(req)); |
1868 | 0 | if (NS_FAILED(rv)) { |
1869 | 0 | return false; |
1870 | 0 | } |
1871 | 0 | |
1872 | 0 | // Make sure that OnStatus/OnProgress calls have the right request set... |
1873 | 0 | RefPtr<nsProgressNotificationProxy> progressproxy = |
1874 | 0 | new nsProgressNotificationProxy(newChannel, req); |
1875 | 0 | if (!progressproxy) { |
1876 | 0 | return false; |
1877 | 0 | } |
1878 | 0 | |
1879 | 0 | RefPtr<imgCacheValidator> hvc = |
1880 | 0 | new imgCacheValidator(progressproxy, this, request, aCX, |
1881 | 0 | forcePrincipalCheck); |
1882 | 0 |
|
1883 | 0 | // Casting needed here to get past multiple inheritance. |
1884 | 0 | nsCOMPtr<nsIStreamListener> listener = |
1885 | 0 | do_QueryInterface(static_cast<nsIThreadRetargetableStreamListener*>(hvc)); |
1886 | 0 | NS_ENSURE_TRUE(listener, false); |
1887 | 0 |
|
1888 | 0 | // We must set the notification callbacks before setting up the |
1889 | 0 | // CORS listener, because that's also interested inthe |
1890 | 0 | // notification callbacks. |
1891 | 0 | newChannel->SetNotificationCallbacks(hvc); |
1892 | 0 |
|
1893 | 0 | request->SetValidator(hvc); |
1894 | 0 |
|
1895 | 0 | // We will send notifications from imgCacheValidator::OnStartRequest(). |
1896 | 0 | // In the mean time, we must defer notifications because we are added to |
1897 | 0 | // the imgRequest's proxy list, and we can get extra notifications |
1898 | 0 | // resulting from methods such as StartDecoding(). See bug 579122. |
1899 | 0 | req->MarkValidating(); |
1900 | 0 |
|
1901 | 0 | // Add the proxy without notifying |
1902 | 0 | hvc->AddProxy(req); |
1903 | 0 |
|
1904 | 0 | mozilla::net::PredictorLearn(aURI, aInitialDocumentURI, |
1905 | 0 | nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup); |
1906 | 0 | rv = newChannel->AsyncOpen2(listener); |
1907 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1908 | 0 | req->CancelAndForgetObserver(rv); |
1909 | 0 | return false; |
1910 | 0 | } |
1911 | 0 | |
1912 | 0 | req.forget(aProxyRequest); |
1913 | 0 | return true; |
1914 | 0 | } |
1915 | | |
1916 | | bool |
1917 | | imgLoader::ValidateEntry(imgCacheEntry* aEntry, |
1918 | | nsIURI* aURI, |
1919 | | nsIURI* aInitialDocumentURI, |
1920 | | nsIURI* aReferrerURI, |
1921 | | ReferrerPolicy aReferrerPolicy, |
1922 | | nsILoadGroup* aLoadGroup, |
1923 | | imgINotificationObserver* aObserver, |
1924 | | nsISupports* aCX, |
1925 | | nsIDocument* aLoadingDocument, |
1926 | | nsLoadFlags aLoadFlags, |
1927 | | nsContentPolicyType aLoadPolicyType, |
1928 | | bool aCanMakeNewChannel, |
1929 | | bool* aNewChannelCreated, |
1930 | | imgRequestProxy** aProxyRequest, |
1931 | | nsIPrincipal* aTriggeringPrincipal, |
1932 | | int32_t aCORSMode) |
1933 | 0 | { |
1934 | 0 | LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry"); |
1935 | 0 |
|
1936 | 0 | // If the expiration time is zero, then the request has not gotten far enough |
1937 | 0 | // to know when it will expire. |
1938 | 0 | uint32_t expiryTime = aEntry->GetExpiryTime(); |
1939 | 0 | bool hasExpired = expiryTime != 0 && |
1940 | 0 | expiryTime <= imgCacheEntry::SecondsFromPRTime(PR_Now()); |
1941 | 0 |
|
1942 | 0 | nsresult rv; |
1943 | 0 |
|
1944 | 0 | // Special treatment for file URLs - aEntry has expired if file has changed |
1945 | 0 | nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI)); |
1946 | 0 | if (fileUrl) { |
1947 | 0 | uint32_t lastModTime = aEntry->GetLoadTime(); |
1948 | 0 |
|
1949 | 0 | nsCOMPtr<nsIFile> theFile; |
1950 | 0 | rv = fileUrl->GetFile(getter_AddRefs(theFile)); |
1951 | 0 | if (NS_SUCCEEDED(rv)) { |
1952 | 0 | PRTime fileLastMod; |
1953 | 0 | rv = theFile->GetLastModifiedTime(&fileLastMod); |
1954 | 0 | if (NS_SUCCEEDED(rv)) { |
1955 | 0 | // nsIFile uses millisec, NSPR usec |
1956 | 0 | fileLastMod *= 1000; |
1957 | 0 | hasExpired = |
1958 | 0 | imgCacheEntry::SecondsFromPRTime((PRTime)fileLastMod) > lastModTime; |
1959 | 0 | } |
1960 | 0 | } |
1961 | 0 | } |
1962 | 0 |
|
1963 | 0 | RefPtr<imgRequest> request(aEntry->GetRequest()); |
1964 | 0 |
|
1965 | 0 | if (!request) { |
1966 | 0 | return false; |
1967 | 0 | } |
1968 | 0 | |
1969 | 0 | if (!ValidateSecurityInfo(request, aEntry->ForcePrincipalCheck(), |
1970 | 0 | aCORSMode, aTriggeringPrincipal, |
1971 | 0 | aCX, aLoadPolicyType, aReferrerPolicy)) |
1972 | 0 | return false; |
1973 | 0 | |
1974 | 0 | // data URIs are immutable and by their nature can't leak data, so we can |
1975 | 0 | // just return true in that case. Doing so would mean that shift-reload |
1976 | 0 | // doesn't reload data URI documents/images though (which is handy for |
1977 | 0 | // debugging during gecko development) so we make an exception in that case. |
1978 | 0 | nsAutoCString scheme; |
1979 | 0 | aURI->GetScheme(scheme); |
1980 | 0 | if (scheme.EqualsLiteral("data") && |
1981 | 0 | !(aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)) { |
1982 | 0 | return true; |
1983 | 0 | } |
1984 | 0 | |
1985 | 0 | bool validateRequest = false; |
1986 | 0 |
|
1987 | 0 | // If the request's loadId is the same as the aCX, then it is ok to use |
1988 | 0 | // this one because it has already been validated for this context. |
1989 | 0 | // |
1990 | 0 | // XXX: nullptr seems to be a 'special' key value that indicates that NO |
1991 | 0 | // validation is required. |
1992 | 0 | // |
1993 | 0 | void *key = (void*) aCX; |
1994 | 0 | if (request->LoadId() != key) { |
1995 | 0 | // If we would need to revalidate this entry, but we're being told to |
1996 | 0 | // bypass the cache, we don't allow this entry to be used. |
1997 | 0 | if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) { |
1998 | 0 | return false; |
1999 | 0 | } |
2000 | 0 | |
2001 | 0 | if (MOZ_UNLIKELY(ChaosMode::isActive(ChaosFeature::ImageCache))) { |
2002 | 0 | if (ChaosMode::randomUint32LessThan(4) < 1) { |
2003 | 0 | return false; |
2004 | 0 | } |
2005 | 0 | } |
2006 | 0 | |
2007 | 0 | // Determine whether the cache aEntry must be revalidated... |
2008 | 0 | validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired); |
2009 | 0 |
|
2010 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2011 | 0 | ("imgLoader::ValidateEntry validating cache entry. " |
2012 | 0 | "validateRequest = %d", validateRequest)); |
2013 | 0 | } else if (!key && MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) { |
2014 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2015 | 0 | ("imgLoader::ValidateEntry BYPASSING cache validation for %s " |
2016 | 0 | "because of NULL LoadID", aURI->GetSpecOrDefault().get())); |
2017 | 0 | } |
2018 | 0 |
|
2019 | 0 | // We can't use a cached request if it comes from a different |
2020 | 0 | // application cache than this load is expecting. |
2021 | 0 | nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer; |
2022 | 0 | nsCOMPtr<nsIApplicationCache> requestAppCache; |
2023 | 0 | nsCOMPtr<nsIApplicationCache> groupAppCache; |
2024 | 0 | if ((appCacheContainer = do_GetInterface(request->GetRequest()))) { |
2025 | 0 | appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache)); |
2026 | 0 | } |
2027 | 0 | if ((appCacheContainer = do_QueryInterface(aLoadGroup))) { |
2028 | 0 | appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache)); |
2029 | 0 | } |
2030 | 0 |
|
2031 | 0 | if (requestAppCache != groupAppCache) { |
2032 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2033 | 0 | ("imgLoader::ValidateEntry - Unable to use cached imgRequest " |
2034 | 0 | "[request=%p] because of mismatched application caches\n", |
2035 | 0 | address_of(request))); |
2036 | 0 | return false; |
2037 | 0 | } |
2038 | 0 |
|
2039 | 0 | if (validateRequest && aCanMakeNewChannel) { |
2040 | 0 | LOG_SCOPE(gImgLog, |
2041 | 0 | "imgLoader::ValidateRequest |cache hit| must validate"); |
2042 | 0 |
|
2043 | 0 | return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI, |
2044 | 0 | aReferrerURI, aReferrerPolicy, |
2045 | 0 | aLoadGroup, aObserver, |
2046 | 0 | aCX, aLoadingDocument, |
2047 | 0 | aLoadFlags, aLoadPolicyType, |
2048 | 0 | aProxyRequest, aTriggeringPrincipal, |
2049 | 0 | aCORSMode, aNewChannelCreated); |
2050 | 0 | } |
2051 | 0 |
|
2052 | 0 | return !validateRequest; |
2053 | 0 | } |
2054 | | |
2055 | | bool |
2056 | | imgLoader::RemoveFromCache(const ImageCacheKey& aKey) |
2057 | 0 | { |
2058 | 0 | LOG_STATIC_FUNC_WITH_PARAM(gImgLog, |
2059 | 0 | "imgLoader::RemoveFromCache", "uri", aKey.URI()); |
2060 | 0 |
|
2061 | 0 | imgCacheTable& cache = GetCache(aKey); |
2062 | 0 | imgCacheQueue& queue = GetCacheQueue(aKey); |
2063 | 0 |
|
2064 | 0 | RefPtr<imgCacheEntry> entry; |
2065 | 0 | cache.Remove(aKey, getter_AddRefs(entry)); |
2066 | 0 | if (entry) { |
2067 | 0 | MOZ_ASSERT(!entry->Evicted(), "Evicting an already-evicted cache entry!"); |
2068 | 0 |
|
2069 | 0 | // Entries with no proxies are in the tracker. |
2070 | 0 | if (entry->HasNoProxies()) { |
2071 | 0 | if (mCacheTracker) { |
2072 | 0 | mCacheTracker->RemoveObject(entry); |
2073 | 0 | } |
2074 | 0 | queue.Remove(entry); |
2075 | 0 | } |
2076 | 0 |
|
2077 | 0 | entry->SetEvicted(true); |
2078 | 0 |
|
2079 | 0 | RefPtr<imgRequest> request = entry->GetRequest(); |
2080 | 0 | request->SetIsInCache(false); |
2081 | 0 | AddToUncachedImages(request); |
2082 | 0 |
|
2083 | 0 | return true; |
2084 | 0 | } |
2085 | 0 | return false; |
2086 | 0 | } |
2087 | | |
2088 | | bool |
2089 | | imgLoader::RemoveFromCache(imgCacheEntry* entry, QueueState aQueueState) |
2090 | 0 | { |
2091 | 0 | LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry"); |
2092 | 0 |
|
2093 | 0 | RefPtr<imgRequest> request = entry->GetRequest(); |
2094 | 0 | if (request) { |
2095 | 0 | const ImageCacheKey& key = request->CacheKey(); |
2096 | 0 | imgCacheTable& cache = GetCache(key); |
2097 | 0 | imgCacheQueue& queue = GetCacheQueue(key); |
2098 | 0 |
|
2099 | 0 | LOG_STATIC_FUNC_WITH_PARAM(gImgLog, |
2100 | 0 | "imgLoader::RemoveFromCache", "entry's uri", |
2101 | 0 | key.URI()); |
2102 | 0 |
|
2103 | 0 | cache.Remove(key); |
2104 | 0 |
|
2105 | 0 | if (entry->HasNoProxies()) { |
2106 | 0 | LOG_STATIC_FUNC(gImgLog, |
2107 | 0 | "imgLoader::RemoveFromCache removing from tracker"); |
2108 | 0 | if (mCacheTracker) { |
2109 | 0 | mCacheTracker->RemoveObject(entry); |
2110 | 0 | } |
2111 | 0 | // Only search the queue to remove the entry if its possible it might |
2112 | 0 | // be in the queue. If we know its not in the queue this would be |
2113 | 0 | // wasted work. |
2114 | 0 | MOZ_ASSERT_IF(aQueueState == QueueState::AlreadyRemoved, |
2115 | 0 | !queue.Contains(entry)); |
2116 | 0 | if (aQueueState == QueueState::MaybeExists) { |
2117 | 0 | queue.Remove(entry); |
2118 | 0 | } |
2119 | 0 | } |
2120 | 0 |
|
2121 | 0 | entry->SetEvicted(true); |
2122 | 0 | request->SetIsInCache(false); |
2123 | 0 | AddToUncachedImages(request); |
2124 | 0 |
|
2125 | 0 | return true; |
2126 | 0 | } |
2127 | 0 |
|
2128 | 0 | return false; |
2129 | 0 | } |
2130 | | |
2131 | | nsresult |
2132 | | imgLoader::EvictEntries(imgCacheTable& aCacheToClear) |
2133 | 0 | { |
2134 | 0 | LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries table"); |
2135 | 0 |
|
2136 | 0 | // We have to make a temporary, since RemoveFromCache removes the element |
2137 | 0 | // from the queue, invalidating iterators. |
2138 | 0 | nsTArray<RefPtr<imgCacheEntry> > entries; |
2139 | 0 | for (auto iter = aCacheToClear.Iter(); !iter.Done(); iter.Next()) { |
2140 | 0 | RefPtr<imgCacheEntry>& data = iter.Data(); |
2141 | 0 | entries.AppendElement(data); |
2142 | 0 | } |
2143 | 0 |
|
2144 | 0 | for (uint32_t i = 0; i < entries.Length(); ++i) { |
2145 | 0 | if (!RemoveFromCache(entries[i])) { |
2146 | 0 | return NS_ERROR_FAILURE; |
2147 | 0 | } |
2148 | 0 | } |
2149 | 0 |
|
2150 | 0 | MOZ_ASSERT(aCacheToClear.Count() == 0); |
2151 | 0 |
|
2152 | 0 | return NS_OK; |
2153 | 0 | } |
2154 | | |
2155 | | nsresult |
2156 | | imgLoader::EvictEntries(imgCacheQueue& aQueueToClear) |
2157 | 0 | { |
2158 | 0 | LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries queue"); |
2159 | 0 |
|
2160 | 0 | // We have to make a temporary, since RemoveFromCache removes the element |
2161 | 0 | // from the queue, invalidating iterators. |
2162 | 0 | nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements()); |
2163 | 0 | for (auto i = aQueueToClear.begin(); i != aQueueToClear.end(); ++i) { |
2164 | 0 | entries.AppendElement(*i); |
2165 | 0 | } |
2166 | 0 |
|
2167 | 0 | // Iterate in reverse order to minimize array copying. |
2168 | 0 | for (auto& entry : entries) { |
2169 | 0 | if (!RemoveFromCache(entry)) { |
2170 | 0 | return NS_ERROR_FAILURE; |
2171 | 0 | } |
2172 | 0 | } |
2173 | 0 |
|
2174 | 0 | MOZ_ASSERT(aQueueToClear.GetNumElements() == 0); |
2175 | 0 |
|
2176 | 0 | return NS_OK; |
2177 | 0 | } |
2178 | | |
2179 | | void |
2180 | | imgLoader::AddToUncachedImages(imgRequest* aRequest) |
2181 | 0 | { |
2182 | 0 | MutexAutoLock lock(mUncachedImagesMutex); |
2183 | 0 | mUncachedImages.PutEntry(aRequest); |
2184 | 0 | } |
2185 | | |
2186 | | void |
2187 | | imgLoader::RemoveFromUncachedImages(imgRequest* aRequest) |
2188 | 0 | { |
2189 | 0 | MutexAutoLock lock(mUncachedImagesMutex); |
2190 | 0 | mUncachedImages.RemoveEntry(aRequest); |
2191 | 0 | } |
2192 | | |
2193 | | |
2194 | 0 | #define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \ |
2195 | 0 | nsIRequest::LOAD_FROM_CACHE) |
2196 | | |
2197 | 0 | #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \ |
2198 | 0 | nsIRequest::VALIDATE_NEVER | \ |
2199 | 0 | nsIRequest::VALIDATE_ONCE_PER_SESSION) |
2200 | | |
2201 | | NS_IMETHODIMP |
2202 | | imgLoader::LoadImageXPCOM(nsIURI* aURI, |
2203 | | nsIURI* aInitialDocumentURI, |
2204 | | nsIURI* aReferrerURI, |
2205 | | const nsAString& aReferrerPolicy, |
2206 | | nsIPrincipal* aTriggeringPrincipal, |
2207 | | nsILoadGroup* aLoadGroup, |
2208 | | imgINotificationObserver* aObserver, |
2209 | | nsISupports* aCX, |
2210 | | nsLoadFlags aLoadFlags, |
2211 | | nsISupports* aCacheKey, |
2212 | | nsContentPolicyType aContentPolicyType, |
2213 | | imgIRequest** _retval) |
2214 | 0 | { |
2215 | 0 | // Optional parameter, so defaults to 0 (== TYPE_INVALID) |
2216 | 0 | if (!aContentPolicyType) { |
2217 | 0 | aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE; |
2218 | 0 | } |
2219 | 0 | imgRequestProxy* proxy; |
2220 | 0 | ReferrerPolicy refpol = ReferrerPolicyFromString(aReferrerPolicy); |
2221 | 0 | nsCOMPtr<nsINode> node = do_QueryInterface(aCX); |
2222 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX); |
2223 | 0 | nsresult rv = LoadImage(aURI, |
2224 | 0 | aInitialDocumentURI, |
2225 | 0 | aReferrerURI, |
2226 | 0 | refpol, |
2227 | 0 | aTriggeringPrincipal, |
2228 | 0 | 0, |
2229 | 0 | aLoadGroup, |
2230 | 0 | aObserver, |
2231 | 0 | node, |
2232 | 0 | doc, |
2233 | 0 | aLoadFlags, |
2234 | 0 | aCacheKey, |
2235 | 0 | aContentPolicyType, |
2236 | 0 | EmptyString(), |
2237 | 0 | /* aUseUrgentStartForChannel */ false, |
2238 | 0 | &proxy); |
2239 | 0 | *_retval = proxy; |
2240 | 0 | return rv; |
2241 | 0 | } |
2242 | | |
2243 | | nsresult |
2244 | | imgLoader::LoadImage(nsIURI* aURI, |
2245 | | nsIURI* aInitialDocumentURI, |
2246 | | nsIURI* aReferrerURI, |
2247 | | ReferrerPolicy aReferrerPolicy, |
2248 | | nsIPrincipal* aTriggeringPrincipal, |
2249 | | uint64_t aRequestContextID, |
2250 | | nsILoadGroup* aLoadGroup, |
2251 | | imgINotificationObserver* aObserver, |
2252 | | nsINode *aContext, |
2253 | | nsIDocument* aLoadingDocument, |
2254 | | nsLoadFlags aLoadFlags, |
2255 | | nsISupports* aCacheKey, |
2256 | | nsContentPolicyType aContentPolicyType, |
2257 | | const nsAString& initiatorType, |
2258 | | bool aUseUrgentStartForChannel, |
2259 | | imgRequestProxy** _retval) |
2260 | 0 | { |
2261 | 0 | VerifyCacheSizes(); |
2262 | 0 |
|
2263 | 0 | NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer"); |
2264 | 0 |
|
2265 | 0 | if (!aURI) { |
2266 | 0 | return NS_ERROR_NULL_POINTER; |
2267 | 0 | } |
2268 | 0 | |
2269 | 0 | LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", aURI); |
2270 | 0 |
|
2271 | 0 | *_retval = nullptr; |
2272 | 0 |
|
2273 | 0 | RefPtr<imgRequest> request; |
2274 | 0 |
|
2275 | 0 | nsresult rv; |
2276 | 0 | nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; |
2277 | 0 |
|
2278 | | #ifdef DEBUG |
2279 | | bool isPrivate = false; |
2280 | | |
2281 | | if (aLoadingDocument) { |
2282 | | isPrivate = nsContentUtils::IsInPrivateBrowsing(aLoadingDocument); |
2283 | | } else if (aLoadGroup) { |
2284 | | isPrivate = nsContentUtils::IsInPrivateBrowsing(aLoadGroup); |
2285 | | } |
2286 | | MOZ_ASSERT(isPrivate == mRespectPrivacy); |
2287 | | |
2288 | | if (aLoadingDocument) { |
2289 | | // The given load group should match that of the document if given. If |
2290 | | // that isn't the case, then we need to add more plumbing to ensure we |
2291 | | // block the document as well. |
2292 | | nsCOMPtr<nsILoadGroup> docLoadGroup = |
2293 | | aLoadingDocument->GetDocumentLoadGroup(); |
2294 | | MOZ_ASSERT(docLoadGroup == aLoadGroup); |
2295 | | } |
2296 | | #endif |
2297 | |
|
2298 | 0 | // Get the default load flags from the loadgroup (if possible)... |
2299 | 0 | if (aLoadGroup) { |
2300 | 0 | aLoadGroup->GetLoadFlags(&requestFlags); |
2301 | 0 |
|
2302 | 0 | // If we are trying to load a thumbnail via the moz-page-thumb:// protocol, load |
2303 | 0 | // it directly from the cache to prevent re-decoding the image. See Bug 1373258 |
2304 | 0 | // TODO: Bug 1406134 |
2305 | 0 | bool isThumbnailScheme = false; |
2306 | 0 | if (NS_SUCCEEDED(aURI->SchemeIs("moz-page-thumb", &isThumbnailScheme)) && isThumbnailScheme) { |
2307 | 0 | requestFlags |= nsIRequest::LOAD_FROM_CACHE; |
2308 | 0 | } |
2309 | 0 | } |
2310 | 0 | // |
2311 | 0 | // Merge the default load flags with those passed in via aLoadFlags. |
2312 | 0 | // Currently, *only* the caching, validation and background load flags |
2313 | 0 | // are merged... |
2314 | 0 | // |
2315 | 0 | // The flags in aLoadFlags take precedence over the default flags! |
2316 | 0 | // |
2317 | 0 | if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) { |
2318 | 0 | // Override the default caching flags... |
2319 | 0 | requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) | |
2320 | 0 | (aLoadFlags & LOAD_FLAGS_CACHE_MASK); |
2321 | 0 | } |
2322 | 0 | if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) { |
2323 | 0 | // Override the default validation flags... |
2324 | 0 | requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) | |
2325 | 0 | (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK); |
2326 | 0 | } |
2327 | 0 | if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) { |
2328 | 0 | // Propagate background loading... |
2329 | 0 | requestFlags |= nsIRequest::LOAD_BACKGROUND; |
2330 | 0 | } |
2331 | 0 |
|
2332 | 0 | int32_t corsmode = imgIRequest::CORS_NONE; |
2333 | 0 | if (aLoadFlags & imgILoader::LOAD_CORS_ANONYMOUS) { |
2334 | 0 | corsmode = imgIRequest::CORS_ANONYMOUS; |
2335 | 0 | } else if (aLoadFlags & imgILoader::LOAD_CORS_USE_CREDENTIALS) { |
2336 | 0 | corsmode = imgIRequest::CORS_USE_CREDENTIALS; |
2337 | 0 | } |
2338 | 0 |
|
2339 | 0 | RefPtr<imgCacheEntry> entry; |
2340 | 0 |
|
2341 | 0 | // Look in the cache for our URI, and then validate it. |
2342 | 0 | // XXX For now ignore aCacheKey. We will need it in the future |
2343 | 0 | // for correctly dealing with image load requests that are a result |
2344 | 0 | // of post data. |
2345 | 0 | OriginAttributes attrs; |
2346 | 0 | if (aTriggeringPrincipal) { |
2347 | 0 | attrs = aTriggeringPrincipal->OriginAttributesRef(); |
2348 | 0 | } |
2349 | 0 | ImageCacheKey key(aURI, attrs, aLoadingDocument, rv); |
2350 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2351 | 0 | imgCacheTable& cache = GetCache(key); |
2352 | 0 |
|
2353 | 0 | if (cache.Get(key, getter_AddRefs(entry)) && entry) { |
2354 | 0 | bool newChannelCreated = false; |
2355 | 0 | if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI, |
2356 | 0 | aReferrerPolicy, aLoadGroup, aObserver, aLoadingDocument, |
2357 | 0 | aLoadingDocument, requestFlags, aContentPolicyType, true, |
2358 | 0 | &newChannelCreated, _retval, aTriggeringPrincipal, |
2359 | 0 | corsmode)) { |
2360 | 0 | request = entry->GetRequest(); |
2361 | 0 |
|
2362 | 0 | // If this entry has no proxies, its request has no reference to the |
2363 | 0 | // entry. |
2364 | 0 | if (entry->HasNoProxies()) { |
2365 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, |
2366 | 0 | "imgLoader::LoadImage() adding proxyless entry", "uri", key.URI()); |
2367 | 0 | MOZ_ASSERT(!request->HasCacheEntry(), |
2368 | 0 | "Proxyless entry's request has cache entry!"); |
2369 | 0 | request->SetCacheEntry(entry); |
2370 | 0 |
|
2371 | 0 | if (mCacheTracker && entry->GetExpirationState()->IsTracked()) { |
2372 | 0 | mCacheTracker->MarkUsed(entry); |
2373 | 0 | } |
2374 | 0 | } |
2375 | 0 |
|
2376 | 0 | entry->Touch(); |
2377 | 0 |
|
2378 | 0 | if (!newChannelCreated) { |
2379 | 0 | // This is ugly but it's needed to report CSP violations. We have 3 |
2380 | 0 | // scenarios: |
2381 | 0 | // - we don't have cache. We are not in this if() stmt. A new channel is |
2382 | 0 | // created and that triggers the CSP checks. |
2383 | 0 | // - We have a cache entry and this is blocked by CSP directives. |
2384 | 0 | DebugOnly<bool> shouldLoad = |
2385 | 0 | ShouldLoadCachedImage(request, aLoadingDocument, aTriggeringPrincipal, |
2386 | 0 | aContentPolicyType, |
2387 | 0 | /* aSendCSPViolationReports */ true); |
2388 | 0 | MOZ_ASSERT(shouldLoad); |
2389 | 0 | } |
2390 | 0 | } else { |
2391 | 0 | // We can't use this entry. We'll try to load it off the network, and if |
2392 | 0 | // successful, overwrite the old entry in the cache with a new one. |
2393 | 0 | entry = nullptr; |
2394 | 0 | } |
2395 | 0 | } |
2396 | 0 |
|
2397 | 0 | // Keep the channel in this scope, so we can adjust its notificationCallbacks |
2398 | 0 | // later when we create the proxy. |
2399 | 0 | nsCOMPtr<nsIChannel> newChannel; |
2400 | 0 | // If we didn't get a cache hit, we need to load from the network. |
2401 | 0 | if (!request) { |
2402 | 0 | LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|"); |
2403 | 0 |
|
2404 | 0 | bool forcePrincipalCheck; |
2405 | 0 | rv = NewImageChannel(getter_AddRefs(newChannel), |
2406 | 0 | &forcePrincipalCheck, |
2407 | 0 | aURI, |
2408 | 0 | aInitialDocumentURI, |
2409 | 0 | corsmode, |
2410 | 0 | aReferrerURI, |
2411 | 0 | aReferrerPolicy, |
2412 | 0 | aLoadGroup, |
2413 | 0 | mAcceptHeader, |
2414 | 0 | requestFlags, |
2415 | 0 | aContentPolicyType, |
2416 | 0 | aTriggeringPrincipal, |
2417 | 0 | aContext, |
2418 | 0 | mRespectPrivacy); |
2419 | 0 | if (NS_FAILED(rv)) { |
2420 | 0 | return NS_ERROR_FAILURE; |
2421 | 0 | } |
2422 | 0 | |
2423 | 0 | MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy); |
2424 | 0 |
|
2425 | 0 | NewRequestAndEntry(forcePrincipalCheck, this, key, |
2426 | 0 | getter_AddRefs(request), |
2427 | 0 | getter_AddRefs(entry)); |
2428 | 0 |
|
2429 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2430 | 0 | ("[this=%p] imgLoader::LoadImage -- Created new imgRequest" |
2431 | 0 | " [request=%p]\n", this, request.get())); |
2432 | 0 |
|
2433 | 0 | nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(newChannel)); |
2434 | 0 | if (cos) { |
2435 | 0 | if (aUseUrgentStartForChannel) { |
2436 | 0 | cos->AddClassFlags(nsIClassOfService::UrgentStart); |
2437 | 0 | } |
2438 | 0 |
|
2439 | 0 | if (nsContentUtils::IsTailingEnabled() && |
2440 | 0 | aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) { |
2441 | 0 | cos->AddClassFlags(nsIClassOfService::Throttleable | |
2442 | 0 | nsIClassOfService::Tail); |
2443 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(newChannel)); |
2444 | 0 | if (httpChannel) { |
2445 | 0 | Unused << httpChannel->SetRequestContextID(aRequestContextID); |
2446 | 0 | } |
2447 | 0 | } |
2448 | 0 | } |
2449 | 0 |
|
2450 | 0 | nsCOMPtr<nsILoadGroup> channelLoadGroup; |
2451 | 0 | newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup)); |
2452 | 0 | rv = request->Init(aURI, aURI, /* aHadInsecureRedirect = */ false, |
2453 | 0 | channelLoadGroup, newChannel, entry, aLoadingDocument, |
2454 | 0 | aTriggeringPrincipal, corsmode, aReferrerPolicy); |
2455 | 0 | if (NS_FAILED(rv)) { |
2456 | 0 | return NS_ERROR_FAILURE; |
2457 | 0 | } |
2458 | 0 | |
2459 | 0 | // Add the initiator type for this image load |
2460 | 0 | nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel); |
2461 | 0 | if (timedChannel) { |
2462 | 0 | timedChannel->SetInitiatorType(initiatorType); |
2463 | 0 | } |
2464 | 0 |
|
2465 | 0 | // create the proxy listener |
2466 | 0 | nsCOMPtr<nsIStreamListener> listener = new ProxyListener(request.get()); |
2467 | 0 |
|
2468 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2469 | 0 | ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen2()\n", |
2470 | 0 | this)); |
2471 | 0 |
|
2472 | 0 | mozilla::net::PredictorLearn(aURI, aInitialDocumentURI, |
2473 | 0 | nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup); |
2474 | 0 |
|
2475 | 0 | nsresult openRes = newChannel->AsyncOpen2(listener); |
2476 | 0 |
|
2477 | 0 | if (NS_FAILED(openRes)) { |
2478 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2479 | 0 | ("[this=%p] imgLoader::LoadImage -- AsyncOpen2() failed: 0x%" PRIx32 "\n", |
2480 | 0 | this, static_cast<uint32_t>(openRes))); |
2481 | 0 | request->CancelAndAbort(openRes); |
2482 | 0 | return openRes; |
2483 | 0 | } |
2484 | 0 |
|
2485 | 0 | // Try to add the new request into the cache. |
2486 | 0 | PutIntoCache(key, entry); |
2487 | 0 | } else { |
2488 | 0 | LOG_MSG_WITH_PARAM(gImgLog, |
2489 | 0 | "imgLoader::LoadImage |cache hit|", "request", request); |
2490 | 0 | } |
2491 | 0 |
|
2492 | 0 |
|
2493 | 0 | // If we didn't get a proxy when validating the cache entry, we need to |
2494 | 0 | // create one. |
2495 | 0 | if (!*_retval) { |
2496 | 0 | // ValidateEntry() has three return values: "Is valid," "might be valid -- |
2497 | 0 | // validating over network", and "not valid." If we don't have a _retval, |
2498 | 0 | // we know ValidateEntry is not validating over the network, so it's safe |
2499 | 0 | // to SetLoadId here because we know this request is valid for this context. |
2500 | 0 | // |
2501 | 0 | // Note, however, that this doesn't guarantee the behaviour we want (one |
2502 | 0 | // URL maps to the same image on a page) if we load the same image in a |
2503 | 0 | // different tab (see bug 528003), because its load id will get re-set, and |
2504 | 0 | // that'll cause us to validate over the network. |
2505 | 0 | request->SetLoadId(aLoadingDocument); |
2506 | 0 |
|
2507 | 0 | LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request."); |
2508 | 0 | rv = CreateNewProxyForRequest(request, aLoadGroup, aLoadingDocument, |
2509 | 0 | aObserver, requestFlags, _retval); |
2510 | 0 | if (NS_FAILED(rv)) { |
2511 | 0 | return rv; |
2512 | 0 | } |
2513 | 0 | |
2514 | 0 | imgRequestProxy* proxy = *_retval; |
2515 | 0 |
|
2516 | 0 | // Make sure that OnStatus/OnProgress calls have the right request set, if |
2517 | 0 | // we did create a channel here. |
2518 | 0 | if (newChannel) { |
2519 | 0 | nsCOMPtr<nsIInterfaceRequestor> requestor( |
2520 | 0 | new nsProgressNotificationProxy(newChannel, proxy)); |
2521 | 0 | if (!requestor) { |
2522 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2523 | 0 | } |
2524 | 0 | newChannel->SetNotificationCallbacks(requestor); |
2525 | 0 | } |
2526 | 0 |
|
2527 | 0 | // Note that it's OK to add here even if the request is done. If it is, |
2528 | 0 | // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and |
2529 | 0 | // the proxy will be removed from the loadgroup. |
2530 | 0 | proxy->AddToLoadGroup(); |
2531 | 0 |
|
2532 | 0 | // If we're loading off the network, explicitly don't notify our proxy, |
2533 | 0 | // because necko (or things called from necko, such as imgCacheValidator) |
2534 | 0 | // are going to call our notifications asynchronously, and we can't make it |
2535 | 0 | // further asynchronous because observers might rely on imagelib completing |
2536 | 0 | // its work between the channel's OnStartRequest and OnStopRequest. |
2537 | 0 | if (!newChannel) { |
2538 | 0 | proxy->NotifyListener(); |
2539 | 0 | } |
2540 | 0 |
|
2541 | 0 | return rv; |
2542 | 0 | } |
2543 | 0 |
|
2544 | 0 | NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value"); |
2545 | 0 |
|
2546 | 0 | return NS_OK; |
2547 | 0 | } |
2548 | | |
2549 | | NS_IMETHODIMP |
2550 | | imgLoader::LoadImageWithChannelXPCOM(nsIChannel* channel, |
2551 | | imgINotificationObserver* aObserver, |
2552 | | nsISupports* aCX, |
2553 | | nsIStreamListener** listener, |
2554 | | imgIRequest** _retval) |
2555 | 0 | { |
2556 | 0 | nsresult result; |
2557 | 0 | imgRequestProxy* proxy; |
2558 | 0 | result = LoadImageWithChannel(channel, |
2559 | 0 | aObserver, |
2560 | 0 | aCX, |
2561 | 0 | listener, |
2562 | 0 | &proxy); |
2563 | 0 | *_retval = proxy; |
2564 | 0 | return result; |
2565 | 0 | } |
2566 | | |
2567 | | nsresult |
2568 | | imgLoader::LoadImageWithChannel(nsIChannel* channel, |
2569 | | imgINotificationObserver* aObserver, |
2570 | | nsISupports* aCX, |
2571 | | nsIStreamListener** listener, |
2572 | | imgRequestProxy** _retval) |
2573 | 0 | { |
2574 | 0 | NS_ASSERTION(channel, |
2575 | 0 | "imgLoader::LoadImageWithChannel -- NULL channel pointer"); |
2576 | 0 |
|
2577 | 0 | MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy); |
2578 | 0 |
|
2579 | 0 | LOG_SCOPE(gImgLog, "imgLoader::LoadImageWithChannel"); |
2580 | 0 | RefPtr<imgRequest> request; |
2581 | 0 |
|
2582 | 0 | nsCOMPtr<nsIURI> uri; |
2583 | 0 | channel->GetURI(getter_AddRefs(uri)); |
2584 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX); |
2585 | 0 |
|
2586 | 0 | NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE); |
2587 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); |
2588 | 0 |
|
2589 | 0 | OriginAttributes attrs; |
2590 | 0 | if (loadInfo) { |
2591 | 0 | attrs = loadInfo->GetOriginAttributes(); |
2592 | 0 | } |
2593 | 0 |
|
2594 | 0 | nsresult rv; |
2595 | 0 | ImageCacheKey key(uri, attrs, doc, rv); |
2596 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2597 | 0 |
|
2598 | 0 | nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; |
2599 | 0 | channel->GetLoadFlags(&requestFlags); |
2600 | 0 |
|
2601 | 0 | // If we are trying to load a thumbnail via the moz-page-thumb:// protocol, load |
2602 | 0 | // it directly from the cache to prevent re-decoding the image. See Bug 1373258 |
2603 | 0 | // TODO: Bug 1406134 |
2604 | 0 | bool isThumbnailScheme = false; |
2605 | 0 | if (NS_SUCCEEDED(uri->SchemeIs("moz-page-thumb", &isThumbnailScheme)) && isThumbnailScheme) { |
2606 | 0 | requestFlags |= nsIRequest::LOAD_FROM_CACHE; |
2607 | 0 | } |
2608 | 0 |
|
2609 | 0 | RefPtr<imgCacheEntry> entry; |
2610 | 0 |
|
2611 | 0 | if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) { |
2612 | 0 | RemoveFromCache(key); |
2613 | 0 | } else { |
2614 | 0 | // Look in the cache for our URI, and then validate it. |
2615 | 0 | // XXX For now ignore aCacheKey. We will need it in the future |
2616 | 0 | // for correctly dealing with image load requests that are a result |
2617 | 0 | // of post data. |
2618 | 0 | imgCacheTable& cache = GetCache(key); |
2619 | 0 | if (cache.Get(key, getter_AddRefs(entry)) && entry) { |
2620 | 0 | // We don't want to kick off another network load. So we ask |
2621 | 0 | // ValidateEntry to only do validation without creating a new proxy. If |
2622 | 0 | // it says that the entry isn't valid any more, we'll only use the entry |
2623 | 0 | // we're getting if the channel is loading from the cache anyways. |
2624 | 0 | // |
2625 | 0 | // XXX -- should this be changed? it's pretty much verbatim from the old |
2626 | 0 | // code, but seems nonsensical. |
2627 | 0 | // |
2628 | 0 | // Since aCanMakeNewChannel == false, we don't need to pass content policy |
2629 | 0 | // type/principal/etc |
2630 | 0 |
|
2631 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); |
2632 | 0 | // if there is a loadInfo, use the right contentType, otherwise |
2633 | 0 | // default to the internal image type |
2634 | 0 | nsContentPolicyType policyType = loadInfo |
2635 | 0 | ? loadInfo->InternalContentPolicyType() |
2636 | 0 | : nsIContentPolicy::TYPE_INTERNAL_IMAGE; |
2637 | 0 |
|
2638 | 0 | if (ValidateEntry(entry, uri, nullptr, nullptr, RP_Unset, |
2639 | 0 | nullptr, aObserver, aCX, doc, requestFlags, |
2640 | 0 | policyType, false, nullptr, nullptr, |
2641 | 0 | nullptr, imgIRequest::CORS_NONE)) { |
2642 | 0 | request = entry->GetRequest(); |
2643 | 0 | } else { |
2644 | 0 | nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(channel)); |
2645 | 0 | bool bUseCacheCopy; |
2646 | 0 |
|
2647 | 0 | if (cacheChan) { |
2648 | 0 | cacheChan->IsFromCache(&bUseCacheCopy); |
2649 | 0 | } else { |
2650 | 0 | bUseCacheCopy = false; |
2651 | 0 | } |
2652 | 0 |
|
2653 | 0 | if (!bUseCacheCopy) { |
2654 | 0 | entry = nullptr; |
2655 | 0 | } else { |
2656 | 0 | request = entry->GetRequest(); |
2657 | 0 | } |
2658 | 0 | } |
2659 | 0 |
|
2660 | 0 | if (request && entry) { |
2661 | 0 | // If this entry has no proxies, its request has no reference to |
2662 | 0 | // the entry. |
2663 | 0 | if (entry->HasNoProxies()) { |
2664 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, |
2665 | 0 | "imgLoader::LoadImageWithChannel() adding proxyless entry", |
2666 | 0 | "uri", key.URI()); |
2667 | 0 | MOZ_ASSERT(!request->HasCacheEntry(), |
2668 | 0 | "Proxyless entry's request has cache entry!"); |
2669 | 0 | request->SetCacheEntry(entry); |
2670 | 0 |
|
2671 | 0 | if (mCacheTracker && entry->GetExpirationState()->IsTracked()) { |
2672 | 0 | mCacheTracker->MarkUsed(entry); |
2673 | 0 | } |
2674 | 0 | } |
2675 | 0 | } |
2676 | 0 | } |
2677 | 0 | } |
2678 | 0 |
|
2679 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
2680 | 0 | channel->GetLoadGroup(getter_AddRefs(loadGroup)); |
2681 | 0 |
|
2682 | | #ifdef DEBUG |
2683 | | if (doc) { |
2684 | | // The load group of the channel should always match that of the |
2685 | | // document if given. If that isn't the case, then we need to add more |
2686 | | // plumbing to ensure we block the document as well. |
2687 | | nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup(); |
2688 | | MOZ_ASSERT(docLoadGroup == loadGroup); |
2689 | | } |
2690 | | #endif |
2691 | |
|
2692 | 0 | // Filter out any load flags not from nsIRequest |
2693 | 0 | requestFlags &= nsIRequest::LOAD_REQUESTMASK; |
2694 | 0 |
|
2695 | 0 | rv = NS_OK; |
2696 | 0 | if (request) { |
2697 | 0 | // we have this in our cache already.. cancel the current (document) load |
2698 | 0 |
|
2699 | 0 | // this should fire an OnStopRequest |
2700 | 0 | channel->Cancel(NS_ERROR_PARSED_DATA_CACHED); |
2701 | 0 |
|
2702 | 0 | *listener = nullptr; // give them back a null nsIStreamListener |
2703 | 0 |
|
2704 | 0 | rv = CreateNewProxyForRequest(request, loadGroup, doc, aObserver, |
2705 | 0 | requestFlags, _retval); |
2706 | 0 | static_cast<imgRequestProxy*>(*_retval)->NotifyListener(); |
2707 | 0 | } else { |
2708 | 0 | // We use originalURI here to fulfil the imgIRequest contract on GetURI. |
2709 | 0 | nsCOMPtr<nsIURI> originalURI; |
2710 | 0 | channel->GetOriginalURI(getter_AddRefs(originalURI)); |
2711 | 0 |
|
2712 | 0 | // XXX(seth): We should be able to just use |key| here, except that |key| is |
2713 | 0 | // constructed above with the *current URI* and not the *original URI*. I'm |
2714 | 0 | // pretty sure this is a bug, and it's preventing us from ever getting a |
2715 | 0 | // cache hit in LoadImageWithChannel when redirects are involved. |
2716 | 0 | ImageCacheKey originalURIKey(originalURI, attrs, doc, rv); |
2717 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2718 | 0 |
|
2719 | 0 | // Default to doing a principal check because we don't know who |
2720 | 0 | // started that load and whether their principal ended up being |
2721 | 0 | // inherited on the channel. |
2722 | 0 | NewRequestAndEntry(/* aForcePrincipalCheckForCacheEntry = */ true, |
2723 | 0 | this, originalURIKey, |
2724 | 0 | getter_AddRefs(request), |
2725 | 0 | getter_AddRefs(entry)); |
2726 | 0 |
|
2727 | 0 | // No principal specified here, because we're not passed one. |
2728 | 0 | // In LoadImageWithChannel, the redirects that may have been |
2729 | 0 | // assoicated with this load would have gone through necko. |
2730 | 0 | // We only have the final URI in ImageLib and hence don't know |
2731 | 0 | // if the request went through insecure redirects. But if it did, |
2732 | 0 | // the necko cache should have handled that (since all necko cache hits |
2733 | 0 | // including the redirects will go through content policy). Hence, we |
2734 | 0 | // can set aHadInsecureRedirect to false here. |
2735 | 0 | rv = request->Init(originalURI, uri, /* aHadInsecureRedirect = */ false, |
2736 | 0 | channel, channel, entry, aCX, nullptr, |
2737 | 0 | imgIRequest::CORS_NONE, RP_Unset); |
2738 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2739 | 0 |
|
2740 | 0 | RefPtr<ProxyListener> pl = |
2741 | 0 | new ProxyListener(static_cast<nsIStreamListener*>(request.get())); |
2742 | 0 | pl.forget(listener); |
2743 | 0 |
|
2744 | 0 | // Try to add the new request into the cache. |
2745 | 0 | PutIntoCache(originalURIKey, entry); |
2746 | 0 |
|
2747 | 0 | rv = CreateNewProxyForRequest(request, loadGroup, doc, aObserver, |
2748 | 0 | requestFlags, _retval); |
2749 | 0 |
|
2750 | 0 | // Explicitly don't notify our proxy, because we're loading off the |
2751 | 0 | // network, and necko (or things called from necko, such as |
2752 | 0 | // imgCacheValidator) are going to call our notifications asynchronously, |
2753 | 0 | // and we can't make it further asynchronous because observers might rely |
2754 | 0 | // on imagelib completing its work between the channel's OnStartRequest and |
2755 | 0 | // OnStopRequest. |
2756 | 0 | } |
2757 | 0 |
|
2758 | 0 | if (NS_FAILED(rv)) { |
2759 | 0 | return rv; |
2760 | 0 | } |
2761 | 0 | |
2762 | 0 | (*_retval)->AddToLoadGroup(); |
2763 | 0 | return rv; |
2764 | 0 | } |
2765 | | |
2766 | | bool |
2767 | | imgLoader::SupportImageWithMimeType(const char* aMimeType, |
2768 | | AcceptedMimeTypes aAccept |
2769 | | /* = AcceptedMimeTypes::IMAGES */) |
2770 | 0 | { |
2771 | 0 | nsAutoCString mimeType(aMimeType); |
2772 | 0 | ToLowerCase(mimeType); |
2773 | 0 |
|
2774 | 0 | if (aAccept == AcceptedMimeTypes::IMAGES_AND_DOCUMENTS && |
2775 | 0 | mimeType.EqualsLiteral("image/svg+xml")) { |
2776 | 0 | return true; |
2777 | 0 | } |
2778 | 0 | |
2779 | 0 | DecoderType type = DecoderFactory::GetDecoderType(mimeType.get()); |
2780 | 0 | return type != DecoderType::UNKNOWN; |
2781 | 0 | } |
2782 | | |
2783 | | NS_IMETHODIMP |
2784 | | imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest, |
2785 | | const uint8_t* aContents, |
2786 | | uint32_t aLength, |
2787 | | nsACString& aContentType) |
2788 | 0 | { |
2789 | 0 | return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType); |
2790 | 0 | } |
2791 | | |
2792 | | /* static */ |
2793 | | nsresult |
2794 | | imgLoader::GetMimeTypeFromContent(const char* aContents, |
2795 | | uint32_t aLength, |
2796 | | nsACString& aContentType) |
2797 | 0 | { |
2798 | 0 | /* Is it a GIF? */ |
2799 | 0 | if (aLength >= 6 && (!strncmp(aContents, "GIF87a", 6) || |
2800 | 0 | !strncmp(aContents, "GIF89a", 6))) { |
2801 | 0 | aContentType.AssignLiteral(IMAGE_GIF); |
2802 | 0 |
|
2803 | 0 | /* or a PNG? */ |
2804 | 0 | } else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 && |
2805 | 0 | (unsigned char)aContents[1]==0x50 && |
2806 | 0 | (unsigned char)aContents[2]==0x4E && |
2807 | 0 | (unsigned char)aContents[3]==0x47 && |
2808 | 0 | (unsigned char)aContents[4]==0x0D && |
2809 | 0 | (unsigned char)aContents[5]==0x0A && |
2810 | 0 | (unsigned char)aContents[6]==0x1A && |
2811 | 0 | (unsigned char)aContents[7]==0x0A)) { |
2812 | 0 | aContentType.AssignLiteral(IMAGE_PNG); |
2813 | 0 |
|
2814 | 0 | /* maybe a JPEG (JFIF)? */ |
2815 | 0 | /* JFIF files start with SOI APP0 but older files can start with SOI DQT |
2816 | 0 | * so we test for SOI followed by any marker, i.e. FF D8 FF |
2817 | 0 | * this will also work for SPIFF JPEG files if they appear in the future. |
2818 | 0 | * |
2819 | 0 | * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00) |
2820 | 0 | */ |
2821 | 0 | } else if (aLength >= 3 && |
2822 | 0 | ((unsigned char)aContents[0])==0xFF && |
2823 | 0 | ((unsigned char)aContents[1])==0xD8 && |
2824 | 0 | ((unsigned char)aContents[2])==0xFF) { |
2825 | 0 | aContentType.AssignLiteral(IMAGE_JPEG); |
2826 | 0 |
|
2827 | 0 | /* or how about ART? */ |
2828 | 0 | /* ART begins with JG (4A 47). Major version offset 2. |
2829 | 0 | * Minor version offset 3. Offset 4 must be nullptr. |
2830 | 0 | */ |
2831 | 0 | } else if (aLength >= 5 && |
2832 | 0 | ((unsigned char) aContents[0])==0x4a && |
2833 | 0 | ((unsigned char) aContents[1])==0x47 && |
2834 | 0 | ((unsigned char) aContents[4])==0x00 ) { |
2835 | 0 | aContentType.AssignLiteral(IMAGE_ART); |
2836 | 0 |
|
2837 | 0 | } else if (aLength >= 2 && !strncmp(aContents, "BM", 2)) { |
2838 | 0 | aContentType.AssignLiteral(IMAGE_BMP); |
2839 | 0 |
|
2840 | 0 | // ICOs always begin with a 2-byte 0 followed by a 2-byte 1. |
2841 | 0 | // CURs begin with 2-byte 0 followed by 2-byte 2. |
2842 | 0 | } else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) || |
2843 | 0 | !memcmp(aContents, "\000\000\002\000", 4))) { |
2844 | 0 | aContentType.AssignLiteral(IMAGE_ICO); |
2845 | 0 |
|
2846 | 0 | // WebPs always begin with RIFF, a 32-bit length, and WEBP. |
2847 | 0 | } else if (aLength >= 12 && !memcmp(aContents, "RIFF", 4) && |
2848 | 0 | !memcmp(aContents + 8, "WEBP", 4)) { |
2849 | 0 | aContentType.AssignLiteral(IMAGE_WEBP); |
2850 | 0 |
|
2851 | 0 | } else { |
2852 | 0 | /* none of the above? I give up */ |
2853 | 0 | return NS_ERROR_NOT_AVAILABLE; |
2854 | 0 | } |
2855 | 0 | |
2856 | 0 | return NS_OK; |
2857 | 0 | } |
2858 | | |
2859 | | /** |
2860 | | * proxy stream listener class used to handle multipart/x-mixed-replace |
2861 | | */ |
2862 | | |
2863 | | #include "nsIRequest.h" |
2864 | | #include "nsIStreamConverterService.h" |
2865 | | |
2866 | | NS_IMPL_ISUPPORTS(ProxyListener, |
2867 | | nsIStreamListener, |
2868 | | nsIThreadRetargetableStreamListener, |
2869 | | nsIRequestObserver) |
2870 | | |
2871 | | ProxyListener::ProxyListener(nsIStreamListener* dest) : |
2872 | | mDestListener(dest) |
2873 | 0 | { |
2874 | 0 | /* member initializers and constructor code */ |
2875 | 0 | } |
2876 | | |
2877 | | ProxyListener::~ProxyListener() |
2878 | 0 | { |
2879 | 0 | /* destructor code */ |
2880 | 0 | } |
2881 | | |
2882 | | |
2883 | | /** nsIRequestObserver methods **/ |
2884 | | |
2885 | | NS_IMETHODIMP |
2886 | | ProxyListener::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt) |
2887 | 0 | { |
2888 | 0 | if (!mDestListener) { |
2889 | 0 | return NS_ERROR_FAILURE; |
2890 | 0 | } |
2891 | 0 | |
2892 | 0 | nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); |
2893 | 0 | if (channel) { |
2894 | 0 | // We need to set the initiator type for the image load |
2895 | 0 | nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel); |
2896 | 0 | if (timedChannel) { |
2897 | 0 | nsAutoString type; |
2898 | 0 | timedChannel->GetInitiatorType(type); |
2899 | 0 | if (type.IsEmpty()) { |
2900 | 0 | timedChannel->SetInitiatorType(NS_LITERAL_STRING("img")); |
2901 | 0 | } |
2902 | 0 | } |
2903 | 0 |
|
2904 | 0 | nsAutoCString contentType; |
2905 | 0 | nsresult rv = channel->GetContentType(contentType); |
2906 | 0 |
|
2907 | 0 | if (!contentType.IsEmpty()) { |
2908 | 0 | /* If multipart/x-mixed-replace content, we'll insert a MIME decoder |
2909 | 0 | in the pipeline to handle the content and pass it along to our |
2910 | 0 | original listener. |
2911 | 0 | */ |
2912 | 0 | if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) { |
2913 | 0 |
|
2914 | 0 | nsCOMPtr<nsIStreamConverterService> convServ( |
2915 | 0 | do_GetService("@mozilla.org/streamConverters;1", &rv)); |
2916 | 0 | if (NS_SUCCEEDED(rv)) { |
2917 | 0 | nsCOMPtr<nsIStreamListener> toListener(mDestListener); |
2918 | 0 | nsCOMPtr<nsIStreamListener> fromListener; |
2919 | 0 |
|
2920 | 0 | rv = convServ->AsyncConvertData("multipart/x-mixed-replace", |
2921 | 0 | "*/*", |
2922 | 0 | toListener, |
2923 | 0 | nullptr, |
2924 | 0 | getter_AddRefs(fromListener)); |
2925 | 0 | if (NS_SUCCEEDED(rv)) { |
2926 | 0 | mDestListener = fromListener; |
2927 | 0 | } |
2928 | 0 | } |
2929 | 0 | } |
2930 | 0 | } |
2931 | 0 | } |
2932 | 0 |
|
2933 | 0 | return mDestListener->OnStartRequest(aRequest, ctxt); |
2934 | 0 | } |
2935 | | |
2936 | | NS_IMETHODIMP |
2937 | | ProxyListener::OnStopRequest(nsIRequest* aRequest, |
2938 | | nsISupports* ctxt, |
2939 | | nsresult status) |
2940 | 0 | { |
2941 | 0 | if (!mDestListener) { |
2942 | 0 | return NS_ERROR_FAILURE; |
2943 | 0 | } |
2944 | 0 | |
2945 | 0 | return mDestListener->OnStopRequest(aRequest, ctxt, status); |
2946 | 0 | } |
2947 | | |
2948 | | /** nsIStreamListener methods **/ |
2949 | | |
2950 | | NS_IMETHODIMP |
2951 | | ProxyListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt, |
2952 | | nsIInputStream* inStr, uint64_t sourceOffset, |
2953 | | uint32_t count) |
2954 | 0 | { |
2955 | 0 | if (!mDestListener) { |
2956 | 0 | return NS_ERROR_FAILURE; |
2957 | 0 | } |
2958 | 0 | |
2959 | 0 | return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, |
2960 | 0 | sourceOffset, count); |
2961 | 0 | } |
2962 | | |
2963 | | /** nsThreadRetargetableStreamListener methods **/ |
2964 | | NS_IMETHODIMP |
2965 | | ProxyListener::CheckListenerChain() |
2966 | 0 | { |
2967 | 0 | NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!"); |
2968 | 0 | nsresult rv = NS_OK; |
2969 | 0 | nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = |
2970 | 0 | do_QueryInterface(mDestListener, &rv); |
2971 | 0 | if (retargetableListener) { |
2972 | 0 | rv = retargetableListener->CheckListenerChain(); |
2973 | 0 | } |
2974 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
2975 | 0 | ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%" PRIx32 "]", |
2976 | 0 | (NS_SUCCEEDED(rv) ? "success" : "failure"), |
2977 | 0 | this, (nsIStreamListener*)mDestListener, static_cast<uint32_t>(rv))); |
2978 | 0 | return rv; |
2979 | 0 | } |
2980 | | |
2981 | | /** |
2982 | | * http validate class. check a channel for a 304 |
2983 | | */ |
2984 | | |
2985 | | NS_IMPL_ISUPPORTS(imgCacheValidator, nsIStreamListener, nsIRequestObserver, |
2986 | | nsIThreadRetargetableStreamListener, |
2987 | | nsIChannelEventSink, nsIInterfaceRequestor, |
2988 | | nsIAsyncVerifyRedirectCallback) |
2989 | | |
2990 | | imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress, |
2991 | | imgLoader* loader, imgRequest* request, |
2992 | | nsISupports* aContext, |
2993 | | bool forcePrincipalCheckForCacheEntry) |
2994 | | : mProgressProxy(progress), |
2995 | | mRequest(request), |
2996 | | mContext(aContext), |
2997 | | mImgLoader(loader), |
2998 | | mHadInsecureRedirect(false) |
2999 | 0 | { |
3000 | 0 | NewRequestAndEntry(forcePrincipalCheckForCacheEntry, loader, |
3001 | 0 | mRequest->CacheKey(), |
3002 | 0 | getter_AddRefs(mNewRequest), |
3003 | 0 | getter_AddRefs(mNewEntry)); |
3004 | 0 | } |
3005 | | |
3006 | | imgCacheValidator::~imgCacheValidator() |
3007 | 0 | { |
3008 | 0 | if (mRequest) { |
3009 | 0 | // If something went wrong, and we never unblocked the requests waiting on |
3010 | 0 | // validation, now is our last chance. We will cancel the new request and |
3011 | 0 | // switch the waiting proxies to it. |
3012 | 0 | UpdateProxies(/* aCancelRequest */ true, /* aSyncNotify */ false); |
3013 | 0 | } |
3014 | 0 | } |
3015 | | |
3016 | | void |
3017 | | imgCacheValidator::AddProxy(imgRequestProxy* aProxy) |
3018 | 0 | { |
3019 | 0 | // aProxy needs to be in the loadgroup since we're validating from |
3020 | 0 | // the network. |
3021 | 0 | aProxy->AddToLoadGroup(); |
3022 | 0 |
|
3023 | 0 | mProxies.AppendElement(aProxy); |
3024 | 0 | } |
3025 | | |
3026 | | void |
3027 | | imgCacheValidator::RemoveProxy(imgRequestProxy* aProxy) |
3028 | 0 | { |
3029 | 0 | mProxies.RemoveElement(aProxy); |
3030 | 0 | } |
3031 | | |
3032 | | void |
3033 | | imgCacheValidator::UpdateProxies(bool aCancelRequest, bool aSyncNotify) |
3034 | 0 | { |
3035 | 0 | MOZ_ASSERT(mRequest); |
3036 | 0 |
|
3037 | 0 | // Clear the validator before updating the proxies. The notifications may |
3038 | 0 | // clone an existing request, and its state could be inconsistent. |
3039 | 0 | mRequest->SetValidator(nullptr); |
3040 | 0 | mRequest = nullptr; |
3041 | 0 |
|
3042 | 0 | // If an error occurred, we will want to cancel the new request, and make the |
3043 | 0 | // validating proxies point to it. Any proxies still bound to the original |
3044 | 0 | // request which are not validating should remain untouched. |
3045 | 0 | if (aCancelRequest) { |
3046 | 0 | MOZ_ASSERT(mNewRequest); |
3047 | 0 | mNewRequest->CancelAndAbort(NS_BINDING_ABORTED); |
3048 | 0 | } |
3049 | 0 |
|
3050 | 0 | // We have finished validating the request, so we can safely take ownership |
3051 | 0 | // of the proxy list. imgRequestProxy::SyncNotifyListener can mutate the list |
3052 | 0 | // if imgRequestProxy::CancelAndForgetObserver is called by its owner. Note |
3053 | 0 | // that any potential notifications should still be suppressed in |
3054 | 0 | // imgRequestProxy::ChangeOwner because we haven't cleared the validating |
3055 | 0 | // flag yet, and thus they will remain deferred. |
3056 | 0 | AutoTArray<RefPtr<imgRequestProxy>, 4> proxies(std::move(mProxies)); |
3057 | 0 |
|
3058 | 0 | for (auto& proxy : proxies) { |
3059 | 0 | // First update the state of all proxies before notifying any of them |
3060 | 0 | // to ensure a consistent state (e.g. in case the notification causes |
3061 | 0 | // other proxies to be touched indirectly.) |
3062 | 0 | MOZ_ASSERT(proxy->IsValidating()); |
3063 | 0 | MOZ_ASSERT(proxy->NotificationsDeferred(), |
3064 | 0 | "Proxies waiting on cache validation should be " |
3065 | 0 | "deferring notifications!"); |
3066 | 0 | if (mNewRequest) { |
3067 | 0 | proxy->ChangeOwner(mNewRequest); |
3068 | 0 | } |
3069 | 0 | proxy->ClearValidating(); |
3070 | 0 | } |
3071 | 0 |
|
3072 | 0 | mNewRequest = nullptr; |
3073 | 0 | mNewEntry = nullptr; |
3074 | 0 |
|
3075 | 0 | for (auto& proxy : proxies) { |
3076 | 0 | if (aSyncNotify) { |
3077 | 0 | // Notify synchronously, because the caller knows we are already in an |
3078 | 0 | // asynchronously-called function (e.g. OnStartRequest). |
3079 | 0 | proxy->SyncNotifyListener(); |
3080 | 0 | } else { |
3081 | 0 | // Notify asynchronously, because the caller does not know our current |
3082 | 0 | // call state (e.g. ~imgCacheValidator). |
3083 | 0 | proxy->NotifyListener(); |
3084 | 0 | } |
3085 | 0 | } |
3086 | 0 | } |
3087 | | |
3088 | | /** nsIRequestObserver methods **/ |
3089 | | |
3090 | | NS_IMETHODIMP |
3091 | | imgCacheValidator::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt) |
3092 | 0 | { |
3093 | 0 | // We may be holding on to a document, so ensure that it's released. |
3094 | 0 | nsCOMPtr<nsISupports> context = mContext.forget(); |
3095 | 0 |
|
3096 | 0 | // If for some reason we don't still have an existing request (probably |
3097 | 0 | // because OnStartRequest got delivered more than once), just bail. |
3098 | 0 | if (!mRequest) { |
3099 | 0 | MOZ_ASSERT_UNREACHABLE("OnStartRequest delivered more than once?"); |
3100 | 0 | aRequest->Cancel(NS_BINDING_ABORTED); |
3101 | 0 | return NS_ERROR_FAILURE; |
3102 | 0 | } |
3103 | 0 |
|
3104 | 0 | // If this request is coming from cache and has the same URI as our |
3105 | 0 | // imgRequest, the request all our proxies are pointing at is valid, and all |
3106 | 0 | // we have to do is tell them to notify their listeners. |
3107 | 0 | nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(aRequest)); |
3108 | 0 | nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); |
3109 | 0 | if (cacheChan && channel && !mRequest->CacheChanged(aRequest)) { |
3110 | 0 | bool isFromCache = false; |
3111 | 0 | cacheChan->IsFromCache(&isFromCache); |
3112 | 0 |
|
3113 | 0 | nsCOMPtr<nsIURI> channelURI; |
3114 | 0 | channel->GetURI(getter_AddRefs(channelURI)); |
3115 | 0 |
|
3116 | 0 | nsCOMPtr<nsIURI> finalURI; |
3117 | 0 | mRequest->GetFinalURI(getter_AddRefs(finalURI)); |
3118 | 0 |
|
3119 | 0 | bool sameURI = false; |
3120 | 0 | if (channelURI && finalURI) { |
3121 | 0 | channelURI->Equals(finalURI, &sameURI); |
3122 | 0 | } |
3123 | 0 |
|
3124 | 0 | if (isFromCache && sameURI) { |
3125 | 0 | // We don't need to load this any more. |
3126 | 0 | aRequest->Cancel(NS_BINDING_ABORTED); |
3127 | 0 | mNewRequest = nullptr; |
3128 | 0 |
|
3129 | 0 | // Clear the validator before updating the proxies. The notifications may |
3130 | 0 | // clone an existing request, and its state could be inconsistent. |
3131 | 0 | mRequest->SetLoadId(context); |
3132 | 0 | UpdateProxies(/* aCancelRequest */ false, /* aSyncNotify */ true); |
3133 | 0 | return NS_OK; |
3134 | 0 | } |
3135 | 0 | } |
3136 | 0 | |
3137 | 0 | // We can't load out of cache. We have to create a whole new request for the |
3138 | 0 | // data that's coming in off the channel. |
3139 | 0 | nsCOMPtr<nsIURI> uri; |
3140 | 0 | mRequest->GetURI(getter_AddRefs(uri)); |
3141 | 0 |
|
3142 | 0 | LOG_MSG_WITH_PARAM(gImgLog, |
3143 | 0 | "imgCacheValidator::OnStartRequest creating new request", |
3144 | 0 | "uri", uri); |
3145 | 0 |
|
3146 | 0 | int32_t corsmode = mRequest->GetCORSMode(); |
3147 | 0 | ReferrerPolicy refpol = mRequest->GetReferrerPolicy(); |
3148 | 0 | nsCOMPtr<nsIPrincipal> triggeringPrincipal = mRequest->GetTriggeringPrincipal(); |
3149 | 0 |
|
3150 | 0 | // Doom the old request's cache entry |
3151 | 0 | mRequest->RemoveFromCache(); |
3152 | 0 |
|
3153 | 0 | // We use originalURI here to fulfil the imgIRequest contract on GetURI. |
3154 | 0 | nsCOMPtr<nsIURI> originalURI; |
3155 | 0 | channel->GetOriginalURI(getter_AddRefs(originalURI)); |
3156 | 0 | nsresult rv = |
3157 | 0 | mNewRequest->Init(originalURI, uri, mHadInsecureRedirect, aRequest, channel, |
3158 | 0 | mNewEntry, context, triggeringPrincipal, corsmode, refpol); |
3159 | 0 | if (NS_FAILED(rv)) { |
3160 | 0 | UpdateProxies(/* aCancelRequest */ true, /* aSyncNotify */ true); |
3161 | 0 | return rv; |
3162 | 0 | } |
3163 | 0 | |
3164 | 0 | mDestListener = new ProxyListener(mNewRequest); |
3165 | 0 |
|
3166 | 0 | // Try to add the new request into the cache. Note that the entry must be in |
3167 | 0 | // the cache before the proxies' ownership changes, because adding a proxy |
3168 | 0 | // changes the caching behaviour for imgRequests. |
3169 | 0 | mImgLoader->PutIntoCache(mNewRequest->CacheKey(), mNewEntry); |
3170 | 0 | UpdateProxies(/* aCancelRequest */ false, /* aSyncNotify */ true); |
3171 | 0 | return mDestListener->OnStartRequest(aRequest, ctxt); |
3172 | 0 | } |
3173 | | |
3174 | | NS_IMETHODIMP |
3175 | | imgCacheValidator::OnStopRequest(nsIRequest* aRequest, |
3176 | | nsISupports* ctxt, |
3177 | | nsresult status) |
3178 | 0 | { |
3179 | 0 | // Be sure we've released the document that we may have been holding on to. |
3180 | 0 | mContext = nullptr; |
3181 | 0 |
|
3182 | 0 | if (!mDestListener) { |
3183 | 0 | return NS_OK; |
3184 | 0 | } |
3185 | 0 | |
3186 | 0 | return mDestListener->OnStopRequest(aRequest, ctxt, status); |
3187 | 0 | } |
3188 | | |
3189 | | /** nsIStreamListener methods **/ |
3190 | | |
3191 | | |
3192 | | NS_IMETHODIMP |
3193 | | imgCacheValidator::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt, |
3194 | | nsIInputStream* inStr, |
3195 | | uint64_t sourceOffset, uint32_t count) |
3196 | 0 | { |
3197 | 0 | if (!mDestListener) { |
3198 | 0 | // XXX see bug 113959 |
3199 | 0 | uint32_t _retval; |
3200 | 0 | inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &_retval); |
3201 | 0 | return NS_OK; |
3202 | 0 | } |
3203 | 0 | |
3204 | 0 | return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, |
3205 | 0 | count); |
3206 | 0 | } |
3207 | | |
3208 | | /** nsIThreadRetargetableStreamListener methods **/ |
3209 | | |
3210 | | NS_IMETHODIMP |
3211 | | imgCacheValidator::CheckListenerChain() |
3212 | 0 | { |
3213 | 0 | NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!"); |
3214 | 0 | nsresult rv = NS_OK; |
3215 | 0 | nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = |
3216 | 0 | do_QueryInterface(mDestListener, &rv); |
3217 | 0 | if (retargetableListener) { |
3218 | 0 | rv = retargetableListener->CheckListenerChain(); |
3219 | 0 | } |
3220 | 0 | MOZ_LOG(gImgLog, LogLevel::Debug, |
3221 | 0 | ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %" PRId32 "=%s", |
3222 | 0 | this, static_cast<uint32_t>(rv), NS_SUCCEEDED(rv) ? "succeeded" : "failed")); |
3223 | 0 | return rv; |
3224 | 0 | } |
3225 | | |
3226 | | /** nsIInterfaceRequestor methods **/ |
3227 | | |
3228 | | NS_IMETHODIMP |
3229 | | imgCacheValidator::GetInterface(const nsIID& aIID, void** aResult) |
3230 | 0 | { |
3231 | 0 | if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { |
3232 | 0 | return QueryInterface(aIID, aResult); |
3233 | 0 | } |
3234 | 0 | |
3235 | 0 | return mProgressProxy->GetInterface(aIID, aResult); |
3236 | 0 | } |
3237 | | |
3238 | | // These functions are materially the same as the same functions in imgRequest. |
3239 | | // We duplicate them because we're verifying whether cache loads are necessary, |
3240 | | // not unconditionally loading. |
3241 | | |
3242 | | /** nsIChannelEventSink methods **/ |
3243 | | NS_IMETHODIMP |
3244 | | imgCacheValidator:: |
3245 | | AsyncOnChannelRedirect(nsIChannel* oldChannel, |
3246 | | nsIChannel* newChannel, |
3247 | | uint32_t flags, |
3248 | | nsIAsyncVerifyRedirectCallback* callback) |
3249 | 0 | { |
3250 | 0 | // Note all cache information we get from the old channel. |
3251 | 0 | mNewRequest->SetCacheValidation(mNewEntry, oldChannel); |
3252 | 0 |
|
3253 | 0 | // If the previous URI is a non-HTTPS URI, record that fact for later use by |
3254 | 0 | // security code, which needs to know whether there is an insecure load at any |
3255 | 0 | // point in the redirect chain. |
3256 | 0 | nsCOMPtr<nsIURI> oldURI; |
3257 | 0 | bool isHttps = false; |
3258 | 0 | bool isChrome = false; |
3259 | 0 | bool schemeLocal = false; |
3260 | 0 | if (NS_FAILED(oldChannel->GetURI(getter_AddRefs(oldURI))) || |
3261 | 0 | NS_FAILED(oldURI->SchemeIs("https", &isHttps)) || |
3262 | 0 | NS_FAILED(oldURI->SchemeIs("chrome", &isChrome)) || |
3263 | 0 | NS_FAILED(NS_URIChainHasFlags(oldURI, |
3264 | 0 | nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, |
3265 | 0 | &schemeLocal)) || |
3266 | 0 | (!isHttps && !isChrome && !schemeLocal)) { |
3267 | 0 | mHadInsecureRedirect = true; |
3268 | 0 | } |
3269 | 0 |
|
3270 | 0 | // Prepare for callback |
3271 | 0 | mRedirectCallback = callback; |
3272 | 0 | mRedirectChannel = newChannel; |
3273 | 0 |
|
3274 | 0 | return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags, |
3275 | 0 | this); |
3276 | 0 | } |
3277 | | |
3278 | | NS_IMETHODIMP |
3279 | | imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult) |
3280 | 0 | { |
3281 | 0 | // If we've already been told to abort, just do so. |
3282 | 0 | if (NS_FAILED(aResult)) { |
3283 | 0 | mRedirectCallback->OnRedirectVerifyCallback(aResult); |
3284 | 0 | mRedirectCallback = nullptr; |
3285 | 0 | mRedirectChannel = nullptr; |
3286 | 0 | return NS_OK; |
3287 | 0 | } |
3288 | 0 | |
3289 | 0 | // make sure we have a protocol that returns data rather than opens |
3290 | 0 | // an external application, e.g. mailto: |
3291 | 0 | nsCOMPtr<nsIURI> uri; |
3292 | 0 | mRedirectChannel->GetURI(getter_AddRefs(uri)); |
3293 | 0 | bool doesNotReturnData = false; |
3294 | 0 | NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, |
3295 | 0 | &doesNotReturnData); |
3296 | 0 |
|
3297 | 0 | nsresult result = NS_OK; |
3298 | 0 |
|
3299 | 0 | if (doesNotReturnData) { |
3300 | 0 | result = NS_ERROR_ABORT; |
3301 | 0 | } |
3302 | 0 |
|
3303 | 0 | mRedirectCallback->OnRedirectVerifyCallback(result); |
3304 | 0 | mRedirectCallback = nullptr; |
3305 | 0 | mRedirectChannel = nullptr; |
3306 | 0 | return NS_OK; |
3307 | 0 | } |