imgLoader.h 18 KB

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