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