Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_image_imgLoader_h
8 : #define mozilla_image_imgLoader_h
9 :
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/Mutex.h"
12 : #include "mozilla/UniquePtr.h"
13 :
14 : #include "imgILoader.h"
15 : #include "imgICache.h"
16 : #include "nsWeakReference.h"
17 : #include "nsIContentSniffer.h"
18 : #include "nsRefPtrHashtable.h"
19 : #include "nsExpirationTracker.h"
20 : #include "ImageCacheKey.h"
21 : #include "imgRequest.h"
22 : #include "nsIProgressEventSink.h"
23 : #include "nsIChannel.h"
24 : #include "nsIThreadRetargetableStreamListener.h"
25 : #include "imgIRequest.h"
26 : #include "mozilla/net/ReferrerPolicy.h"
27 :
28 : class imgLoader;
29 : class imgRequestProxy;
30 : class imgINotificationObserver;
31 : class nsILoadGroup;
32 : class imgCacheExpirationTracker;
33 : class imgMemoryReporter;
34 :
35 : namespace mozilla {
36 : namespace image {
37 : class ImageURL;
38 : } // namespace image
39 : } // namespace mozilla
40 :
41 : class imgCacheEntry
42 : {
43 : public:
44 : imgCacheEntry(imgLoader* loader, imgRequest* request,
45 : bool aForcePrincipalCheck);
46 : ~imgCacheEntry();
47 :
48 250 : nsrefcnt AddRef()
49 : {
50 250 : NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
51 250 : NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
52 250 : ++mRefCnt;
53 250 : NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this));
54 250 : return mRefCnt;
55 : }
56 :
57 170 : nsrefcnt Release()
58 : {
59 170 : NS_PRECONDITION(0 != mRefCnt, "dup release");
60 170 : NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
61 170 : --mRefCnt;
62 170 : NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry");
63 170 : if (mRefCnt == 0) {
64 1 : mRefCnt = 1; /* stabilize */
65 1 : delete this;
66 1 : return 0;
67 : }
68 169 : return mRefCnt;
69 : }
70 :
71 82 : uint32_t GetDataSize() const
72 : {
73 82 : return mDataSize;
74 : }
75 40 : void SetDataSize(uint32_t aDataSize)
76 : {
77 40 : int32_t oldsize = mDataSize;
78 40 : mDataSize = aDataSize;
79 40 : UpdateCache(mDataSize - oldsize);
80 40 : }
81 :
82 0 : int32_t GetTouchedTime() const
83 : {
84 0 : return mTouchedTime;
85 : }
86 : void SetTouchedTime(int32_t time)
87 : {
88 : mTouchedTime = time;
89 : Touch(/* updateTime = */ false);
90 : }
91 :
92 0 : uint32_t GetLoadTime() const
93 : {
94 0 : return mLoadTime;
95 : }
96 :
97 : void UpdateLoadTime();
98 :
99 5 : int32_t GetExpiryTime() const
100 : {
101 5 : return mExpiryTime;
102 : }
103 1 : void SetExpiryTime(int32_t aExpiryTime)
104 : {
105 1 : mExpiryTime = aExpiryTime;
106 1 : Touch();
107 1 : }
108 :
109 0 : bool GetMustValidate() const
110 : {
111 0 : return mMustValidate;
112 : }
113 0 : void SetMustValidate(bool aValidate)
114 : {
115 0 : mMustValidate = aValidate;
116 0 : Touch();
117 0 : }
118 :
119 91 : already_AddRefed<imgRequest> GetRequest() const
120 : {
121 182 : RefPtr<imgRequest> req = mRequest;
122 182 : return req.forget();
123 : }
124 :
125 86 : bool Evicted() const
126 : {
127 86 : return mEvicted;
128 : }
129 :
130 123 : nsExpirationState* GetExpirationState()
131 : {
132 123 : return &mExpirationState;
133 : }
134 :
135 132 : bool HasNoProxies() const
136 : {
137 132 : return mHasNoProxies;
138 : }
139 :
140 4 : bool ForcePrincipalCheck() const
141 : {
142 4 : return mForcePrincipalCheck;
143 : }
144 :
145 0 : imgLoader* Loader() const
146 : {
147 0 : return mLoader;
148 : }
149 :
150 : private: // methods
151 : friend class imgLoader;
152 : friend class imgCacheQueue;
153 : void Touch(bool updateTime = true);
154 : void UpdateCache(int32_t diff = 0);
155 42 : void SetEvicted(bool evict)
156 : {
157 42 : mEvicted = evict;
158 42 : }
159 : void SetHasNoProxies(bool hasNoProxies);
160 :
161 : // Private, unimplemented copy constructor.
162 : imgCacheEntry(const imgCacheEntry&);
163 :
164 : private: // data
165 : nsAutoRefCnt mRefCnt;
166 : NS_DECL_OWNINGTHREAD
167 :
168 : imgLoader* mLoader;
169 : RefPtr<imgRequest> mRequest;
170 : uint32_t mDataSize;
171 : int32_t mTouchedTime;
172 : uint32_t mLoadTime;
173 : int32_t mExpiryTime;
174 : nsExpirationState mExpirationState;
175 : bool mMustValidate : 1;
176 : bool mEvicted : 1;
177 : bool mHasNoProxies : 1;
178 : bool mForcePrincipalCheck : 1;
179 : };
180 :
181 : #include <vector>
182 :
183 : #define NS_IMGLOADER_CID \
184 : { /* c1354898-e3fe-4602-88a7-c4520c21cb4e */ \
185 : 0xc1354898, \
186 : 0xe3fe, \
187 : 0x4602, \
188 : {0x88, 0xa7, 0xc4, 0x52, 0x0c, 0x21, 0xcb, 0x4e} \
189 : }
190 :
191 0 : class imgCacheQueue
192 : {
193 : public:
194 : imgCacheQueue();
195 : void Remove(imgCacheEntry*);
196 : void Push(imgCacheEntry*);
197 : void MarkDirty();
198 : bool IsDirty();
199 : already_AddRefed<imgCacheEntry> Pop();
200 : void Refresh();
201 : uint32_t GetSize() const;
202 : void UpdateSize(int32_t diff);
203 : uint32_t GetNumElements() const;
204 : typedef std::vector<RefPtr<imgCacheEntry> > queueContainer;
205 : typedef queueContainer::iterator iterator;
206 : typedef queueContainer::const_iterator const_iterator;
207 :
208 : iterator begin();
209 : const_iterator begin() const;
210 : iterator end();
211 : const_iterator end() const;
212 :
213 : private:
214 : queueContainer mQueue;
215 : bool mDirty;
216 : uint32_t mSize;
217 : };
218 :
219 : enum class AcceptedMimeTypes : uint8_t {
220 : IMAGES,
221 : IMAGES_AND_DOCUMENTS,
222 : };
223 :
224 : class imgLoader final : public imgILoader,
225 : public nsIContentSniffer,
226 : public imgICache,
227 : public nsSupportsWeakReference,
228 : public nsIObserver
229 : {
230 : virtual ~imgLoader();
231 :
232 : public:
233 : typedef mozilla::image::ImageCacheKey ImageCacheKey;
234 : typedef mozilla::image::ImageURL ImageURL;
235 : typedef nsRefPtrHashtable<nsGenericHashKey<ImageCacheKey>,
236 : imgCacheEntry> imgCacheTable;
237 : typedef nsTHashtable<nsPtrHashKey<imgRequest>> imgSet;
238 : typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
239 : typedef mozilla::Mutex Mutex;
240 :
241 : NS_DECL_ISUPPORTS
242 : NS_DECL_IMGILOADER
243 : NS_DECL_NSICONTENTSNIFFER
244 : NS_DECL_IMGICACHE
245 : NS_DECL_NSIOBSERVER
246 :
247 : /**
248 : * Get the normal image loader instance that is used by gecko code, creating
249 : * it if necessary.
250 : */
251 : static imgLoader* NormalLoader();
252 :
253 : /**
254 : * Get the Private Browsing image loader instance that is used by gecko code,
255 : * creating it if necessary.
256 : */
257 : static imgLoader* PrivateBrowsingLoader();
258 :
259 : /**
260 : * Gecko code should use NormalLoader() or PrivateBrowsingLoader() to get the
261 : * appropriate image loader.
262 : *
263 : * This constructor is public because the XPCOM module code that creates
264 : * instances of "@mozilla.org/image/loader;1" / "@mozilla.org/image/cache;1"
265 : * for nsIComponentManager.createInstance()/nsIServiceManager.getService()
266 : * calls (now only made by add-ons) needs access to it.
267 : *
268 : * XXX We would like to get rid of the nsIServiceManager.getService (and
269 : * nsIComponentManager.createInstance) method of creating imgLoader objects,
270 : * but there are add-ons that are still using it. These add-ons don't
271 : * actually do anything useful with the loaders that they create since nobody
272 : * who creates an imgLoader using this method actually QIs to imgILoader and
273 : * loads images. They all just QI to imgICache and either call clearCache()
274 : * or findEntryProperties(). Since they're doing this on an imgLoader that
275 : * has never loaded images, these calls are useless. It seems likely that
276 : * the code that is doing this is just legacy code left over from a time when
277 : * there was only one imgLoader instance for the entire process. (Nowadays
278 : * the correct method to get an imgILoader/imgICache is to call
279 : * imgITools::getImgCacheForDocument/imgITools::getImgLoaderForDocument.)
280 : * All the same, even though what these add-ons are doing is a no-op,
281 : * removing the nsIServiceManager.getService method of creating/getting an
282 : * imgLoader objects would cause an exception in these add-ons that could
283 : * break things.
284 : */
285 : imgLoader();
286 : nsresult Init();
287 :
288 : MOZ_MUST_USE nsresult LoadImage(nsIURI* aURI,
289 : nsIURI* aInitialDocumentURI,
290 : nsIURI* aReferrerURI,
291 : ReferrerPolicy aReferrerPolicy,
292 : nsIPrincipal* aLoadingPrincipal,
293 : nsILoadGroup* aLoadGroup,
294 : imgINotificationObserver* aObserver,
295 : nsINode* aContext,
296 : nsIDocument* aLoadingDocument,
297 : nsLoadFlags aLoadFlags,
298 : nsISupports* aCacheKey,
299 : nsContentPolicyType aContentPolicyType,
300 : const nsAString& initiatorType,
301 : bool aUseUrgentStartForChannel,
302 : imgRequestProxy** _retval);
303 :
304 : MOZ_MUST_USE nsresult
305 : LoadImageWithChannel(nsIChannel* channel,
306 : imgINotificationObserver* aObserver,
307 : nsISupports* aCX,
308 : nsIStreamListener** listener,
309 : imgRequestProxy** _retval);
310 :
311 : static nsresult GetMimeTypeFromContent(const char* aContents,
312 : uint32_t aLength,
313 : nsACString& aContentType);
314 :
315 : /**
316 : * Returns true if the given mime type may be interpreted as an image.
317 : *
318 : * Some MIME types may be interpreted as both images and documents. (At the
319 : * moment only "image/svg+xml" falls into this category, but there may be more
320 : * in the future.) Callers which want this function to return true for such
321 : * MIME types should pass AcceptedMimeTypes::IMAGES_AND_DOCUMENTS for
322 : * @aAccept.
323 : *
324 : * @param aMimeType The MIME type to evaluate.
325 : * @param aAcceptedMimeTypes Which kinds of MIME types to treat as images.
326 : */
327 : static bool
328 : SupportImageWithMimeType(const char* aMimeType,
329 : AcceptedMimeTypes aAccept =
330 : AcceptedMimeTypes::IMAGES);
331 :
332 : static void GlobalInit(); // for use by the factory
333 : static void Shutdown(); // for use by the factory
334 : static void ShutdownMemoryReporter();
335 :
336 : nsresult ClearChromeImageCache();
337 : nsresult ClearImageCache();
338 : void MinimizeCaches();
339 :
340 : nsresult InitCache();
341 :
342 : bool RemoveFromCache(const ImageCacheKey& aKey);
343 : bool RemoveFromCache(imgCacheEntry* entry);
344 :
345 : bool PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* aEntry);
346 :
347 : void AddToUncachedImages(imgRequest* aRequest);
348 : void RemoveFromUncachedImages(imgRequest* aRequest);
349 :
350 : // Returns true if we should prefer evicting cache entry |two| over cache
351 : // entry |one|.
352 : // This mixes units in the worst way, but provides reasonable results.
353 0 : inline static bool CompareCacheEntries(const RefPtr<imgCacheEntry>& one,
354 : const RefPtr<imgCacheEntry>& two)
355 : {
356 0 : if (!one) {
357 0 : return false;
358 : }
359 0 : if (!two) {
360 0 : return true;
361 : }
362 :
363 0 : const double sizeweight = 1.0 - sCacheTimeWeight;
364 :
365 : // We want large, old images to be evicted first (depending on their
366 : // relative weights). Since a larger time is actually newer, we subtract
367 : // time's weight, so an older image has a larger weight.
368 0 : double oneweight = double(one->GetDataSize()) * sizeweight -
369 0 : double(one->GetTouchedTime()) * sCacheTimeWeight;
370 0 : double twoweight = double(two->GetDataSize()) * sizeweight -
371 0 : double(two->GetTouchedTime()) * sCacheTimeWeight;
372 :
373 0 : return oneweight < twoweight;
374 : }
375 :
376 : void VerifyCacheSizes();
377 :
378 : // The image loader maintains a hash table of all imgCacheEntries. However,
379 : // only some of them will be evicted from the cache: those who have no
380 : // imgRequestProxies watching their imgRequests.
381 : //
382 : // Once an imgRequest has no imgRequestProxies, it should notify us by
383 : // calling HasNoObservers(), and null out its cache entry pointer.
384 : //
385 : // Upon having a proxy start observing again, it should notify us by calling
386 : // HasObservers(). The request's cache entry will be re-set before this
387 : // happens, by calling imgRequest::SetCacheEntry() when an entry with no
388 : // observers is re-requested.
389 : bool SetHasNoProxies(imgRequest* aRequest, imgCacheEntry* aEntry);
390 : bool SetHasProxies(imgRequest* aRequest);
391 :
392 : private: // methods
393 :
394 : static already_AddRefed<imgLoader> CreateImageLoader();
395 :
396 : bool ValidateEntry(imgCacheEntry* aEntry, nsIURI* aKey,
397 : nsIURI* aInitialDocumentURI, nsIURI* aReferrerURI,
398 : ReferrerPolicy aReferrerPolicy,
399 : nsILoadGroup* aLoadGroup,
400 : imgINotificationObserver* aObserver, nsISupports* aCX,
401 : nsLoadFlags aLoadFlags,
402 : nsContentPolicyType aContentPolicyType,
403 : bool aCanMakeNewChannel,
404 : imgRequestProxy** aProxyRequest,
405 : nsIPrincipal* aLoadingPrincipal,
406 : int32_t aCORSMode);
407 :
408 : bool ValidateRequestWithNewChannel(imgRequest* request, nsIURI* aURI,
409 : nsIURI* aInitialDocumentURI,
410 : nsIURI* aReferrerURI,
411 : ReferrerPolicy aReferrerPolicy,
412 : nsILoadGroup* aLoadGroup,
413 : imgINotificationObserver* aObserver,
414 : nsISupports* aCX, nsLoadFlags aLoadFlags,
415 : nsContentPolicyType aContentPolicyType,
416 : imgRequestProxy** aProxyRequest,
417 : nsIPrincipal* aLoadingPrincipal,
418 : int32_t aCORSMode);
419 :
420 : nsresult CreateNewProxyForRequest(imgRequest* aRequest,
421 : nsILoadGroup* aLoadGroup,
422 : imgINotificationObserver* aObserver,
423 : nsLoadFlags aLoadFlags,
424 : imgRequestProxy** _retval);
425 :
426 : void ReadAcceptHeaderPref();
427 :
428 : nsresult EvictEntries(imgCacheTable& aCacheToClear);
429 : nsresult EvictEntries(imgCacheQueue& aQueueToClear);
430 :
431 : imgCacheTable& GetCache(bool aForChrome);
432 : imgCacheTable& GetCache(const ImageCacheKey& aKey);
433 : imgCacheQueue& GetCacheQueue(bool aForChrome);
434 : imgCacheQueue& GetCacheQueue(const ImageCacheKey& aKey);
435 : void CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff = 0);
436 : void CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue);
437 :
438 : private: // data
439 : friend class imgCacheEntry;
440 : friend class imgMemoryReporter;
441 :
442 : imgCacheTable mCache;
443 : imgCacheQueue mCacheQueue;
444 :
445 : imgCacheTable mChromeCache;
446 : imgCacheQueue mChromeCacheQueue;
447 :
448 : // Hash set of every imgRequest for this loader that isn't in mCache or
449 : // mChromeCache. The union over all imgLoader's of mCache, mChromeCache, and
450 : // mUncachedImages should be every imgRequest that is alive. These are weak
451 : // pointers so we rely on the imgRequest destructor to remove itself.
452 : imgSet mUncachedImages;
453 : // The imgRequest can have refs to them held on non-main thread, so we need
454 : // a mutex because we modify the uncached images set from the imgRequest
455 : // destructor.
456 : Mutex mUncachedImagesMutex;
457 :
458 : static double sCacheTimeWeight;
459 : static uint32_t sCacheMaxSize;
460 : static imgMemoryReporter* sMemReporter;
461 :
462 : nsCString mAcceptHeader;
463 :
464 : mozilla::UniquePtr<imgCacheExpirationTracker> mCacheTracker;
465 : bool mRespectPrivacy;
466 : };
467 :
468 :
469 :
470 : /**
471 : * proxy stream listener class used to handle multipart/x-mixed-replace
472 : */
473 :
474 : #include "nsCOMPtr.h"
475 : #include "nsIStreamListener.h"
476 : #include "nsIThreadRetargetableStreamListener.h"
477 :
478 : class ProxyListener : public nsIStreamListener
479 : , public nsIThreadRetargetableStreamListener
480 : {
481 : public:
482 : explicit ProxyListener(nsIStreamListener* dest);
483 :
484 : /* additional members */
485 : NS_DECL_THREADSAFE_ISUPPORTS
486 : NS_DECL_NSISTREAMLISTENER
487 : NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
488 : NS_DECL_NSIREQUESTOBSERVER
489 :
490 : private:
491 : virtual ~ProxyListener();
492 :
493 : nsCOMPtr<nsIStreamListener> mDestListener;
494 : };
495 :
496 : /**
497 : * A class that implements nsIProgressEventSink and forwards all calls to it to
498 : * the original notification callbacks of the channel. Also implements
499 : * nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls,
500 : * and forwards everything else to the channel's notification callbacks.
501 : */
502 : class nsProgressNotificationProxy final
503 : : public nsIProgressEventSink
504 : , public nsIChannelEventSink
505 : , public nsIInterfaceRequestor
506 : {
507 : public:
508 41 : nsProgressNotificationProxy(nsIChannel* channel,
509 : imgIRequest* proxy)
510 41 : : mImageRequest(proxy) {
511 41 : channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks));
512 41 : }
513 :
514 : NS_DECL_ISUPPORTS
515 : NS_DECL_NSIPROGRESSEVENTSINK
516 : NS_DECL_NSICHANNELEVENTSINK
517 : NS_DECL_NSIINTERFACEREQUESTOR
518 : private:
519 41 : ~nsProgressNotificationProxy() { }
520 :
521 : nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks;
522 : nsCOMPtr<nsIRequest> mImageRequest;
523 : };
524 :
525 : /**
526 : * validate checker
527 : */
528 :
529 : #include "nsCOMArray.h"
530 :
531 : class imgCacheValidator : public nsIStreamListener,
532 : public nsIThreadRetargetableStreamListener,
533 : public nsIChannelEventSink,
534 : public nsIInterfaceRequestor,
535 : public nsIAsyncVerifyRedirectCallback
536 : {
537 : public:
538 : imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader,
539 : imgRequest* aRequest, nsISupports* aContext,
540 : bool forcePrincipalCheckForCacheEntry);
541 :
542 : void AddProxy(imgRequestProxy* aProxy);
543 :
544 : NS_DECL_THREADSAFE_ISUPPORTS
545 : NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
546 : NS_DECL_NSISTREAMLISTENER
547 : NS_DECL_NSIREQUESTOBSERVER
548 : NS_DECL_NSICHANNELEVENTSINK
549 : NS_DECL_NSIINTERFACEREQUESTOR
550 : NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
551 :
552 : private:
553 : virtual ~imgCacheValidator();
554 :
555 : nsCOMPtr<nsIStreamListener> mDestListener;
556 : RefPtr<nsProgressNotificationProxy> mProgressProxy;
557 : nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
558 : nsCOMPtr<nsIChannel> mRedirectChannel;
559 :
560 : RefPtr<imgRequest> mRequest;
561 : nsCOMArray<imgIRequest> mProxies;
562 :
563 : RefPtr<imgRequest> mNewRequest;
564 : RefPtr<imgCacheEntry> mNewEntry;
565 :
566 : nsCOMPtr<nsISupports> mContext;
567 :
568 : imgLoader* mImgLoader;
569 :
570 : bool mHadInsecureRedirect;
571 : };
572 :
573 : #endif // mozilla_image_imgLoader_h
|