1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/imgLoader.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,497 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef imgLoader_h__ 1.11 +#define imgLoader_h__ 1.12 + 1.13 +#include "mozilla/Attributes.h" 1.14 + 1.15 +#include "imgILoader.h" 1.16 +#include "imgICache.h" 1.17 +#include "nsWeakReference.h" 1.18 +#include "nsIContentSniffer.h" 1.19 +#include "nsRefPtrHashtable.h" 1.20 +#include "nsExpirationTracker.h" 1.21 +#include "nsAutoPtr.h" 1.22 +#include "imgRequest.h" 1.23 +#include "nsIProgressEventSink.h" 1.24 +#include "nsIChannel.h" 1.25 +#include "mozIThirdPartyUtil.h" 1.26 +#include "nsIThreadRetargetableStreamListener.h" 1.27 +#include "imgIRequest.h" 1.28 + 1.29 +class imgLoader; 1.30 +class imgRequestProxy; 1.31 +class imgINotificationObserver; 1.32 +class nsILoadGroup; 1.33 +class imgCacheExpirationTracker; 1.34 +class imgMemoryReporter; 1.35 +class nsIChannelPolicy; 1.36 + 1.37 +namespace mozilla { 1.38 +namespace image { 1.39 +class ImageURL; 1.40 +} 1.41 +} 1.42 + 1.43 +class imgCacheEntry 1.44 +{ 1.45 +public: 1.46 + imgCacheEntry(imgLoader* loader, imgRequest *request, bool aForcePrincipalCheck); 1.47 + ~imgCacheEntry(); 1.48 + 1.49 + nsrefcnt AddRef() 1.50 + { 1.51 + NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); 1.52 + NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry addref isn't thread-safe!"); 1.53 + ++mRefCnt; 1.54 + NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this)); 1.55 + return mRefCnt; 1.56 + } 1.57 + 1.58 + nsrefcnt Release() 1.59 + { 1.60 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.61 + NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry release isn't thread-safe!"); 1.62 + --mRefCnt; 1.63 + NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry"); 1.64 + if (mRefCnt == 0) { 1.65 + mRefCnt = 1; /* stabilize */ 1.66 + delete this; 1.67 + return 0; 1.68 + } 1.69 + return mRefCnt; 1.70 + } 1.71 + 1.72 + uint32_t GetDataSize() const 1.73 + { 1.74 + return mDataSize; 1.75 + } 1.76 + void SetDataSize(uint32_t aDataSize) 1.77 + { 1.78 + int32_t oldsize = mDataSize; 1.79 + mDataSize = aDataSize; 1.80 + UpdateCache(mDataSize - oldsize); 1.81 + } 1.82 + 1.83 + int32_t GetTouchedTime() const 1.84 + { 1.85 + return mTouchedTime; 1.86 + } 1.87 + void SetTouchedTime(int32_t time) 1.88 + { 1.89 + mTouchedTime = time; 1.90 + Touch(/* updateTime = */ false); 1.91 + } 1.92 + 1.93 + int32_t GetExpiryTime() const 1.94 + { 1.95 + return mExpiryTime; 1.96 + } 1.97 + void SetExpiryTime(int32_t aExpiryTime) 1.98 + { 1.99 + mExpiryTime = aExpiryTime; 1.100 + Touch(); 1.101 + } 1.102 + 1.103 + bool GetMustValidate() const 1.104 + { 1.105 + return mMustValidate; 1.106 + } 1.107 + void SetMustValidate(bool aValidate) 1.108 + { 1.109 + mMustValidate = aValidate; 1.110 + Touch(); 1.111 + } 1.112 + 1.113 + already_AddRefed<imgRequest> GetRequest() const 1.114 + { 1.115 + nsRefPtr<imgRequest> req = mRequest; 1.116 + return req.forget(); 1.117 + } 1.118 + 1.119 + bool Evicted() const 1.120 + { 1.121 + return mEvicted; 1.122 + } 1.123 + 1.124 + nsExpirationState *GetExpirationState() 1.125 + { 1.126 + return &mExpirationState; 1.127 + } 1.128 + 1.129 + bool HasNoProxies() const 1.130 + { 1.131 + return mHasNoProxies; 1.132 + } 1.133 + 1.134 + bool ForcePrincipalCheck() const 1.135 + { 1.136 + return mForcePrincipalCheck; 1.137 + } 1.138 + 1.139 + imgLoader* Loader() const 1.140 + { 1.141 + return mLoader; 1.142 + } 1.143 + 1.144 +private: // methods 1.145 + friend class imgLoader; 1.146 + friend class imgCacheQueue; 1.147 + void Touch(bool updateTime = true); 1.148 + void UpdateCache(int32_t diff = 0); 1.149 + void SetEvicted(bool evict) 1.150 + { 1.151 + mEvicted = evict; 1.152 + } 1.153 + void SetHasNoProxies(bool hasNoProxies); 1.154 + 1.155 + // Private, unimplemented copy constructor. 1.156 + imgCacheEntry(const imgCacheEntry &); 1.157 + 1.158 +private: // data 1.159 + nsAutoRefCnt mRefCnt; 1.160 + NS_DECL_OWNINGTHREAD 1.161 + 1.162 + imgLoader* mLoader; 1.163 + nsRefPtr<imgRequest> mRequest; 1.164 + uint32_t mDataSize; 1.165 + int32_t mTouchedTime; 1.166 + int32_t mExpiryTime; 1.167 + nsExpirationState mExpirationState; 1.168 + bool mMustValidate : 1; 1.169 + bool mEvicted : 1; 1.170 + bool mHasNoProxies : 1; 1.171 + bool mForcePrincipalCheck : 1; 1.172 +}; 1.173 + 1.174 +#include <vector> 1.175 + 1.176 +#define NS_IMGLOADER_CID \ 1.177 +{ /* 9f6a0d2e-1dd1-11b2-a5b8-951f13c846f7 */ \ 1.178 + 0x9f6a0d2e, \ 1.179 + 0x1dd1, \ 1.180 + 0x11b2, \ 1.181 + {0xa5, 0xb8, 0x95, 0x1f, 0x13, 0xc8, 0x46, 0xf7} \ 1.182 +} 1.183 + 1.184 +class imgCacheQueue 1.185 +{ 1.186 +public: 1.187 + imgCacheQueue(); 1.188 + void Remove(imgCacheEntry *); 1.189 + void Push(imgCacheEntry *); 1.190 + void MarkDirty(); 1.191 + bool IsDirty(); 1.192 + already_AddRefed<imgCacheEntry> Pop(); 1.193 + void Refresh(); 1.194 + uint32_t GetSize() const; 1.195 + void UpdateSize(int32_t diff); 1.196 + uint32_t GetNumElements() const; 1.197 + typedef std::vector<nsRefPtr<imgCacheEntry> > queueContainer; 1.198 + typedef queueContainer::iterator iterator; 1.199 + typedef queueContainer::const_iterator const_iterator; 1.200 + 1.201 + iterator begin(); 1.202 + const_iterator begin() const; 1.203 + iterator end(); 1.204 + const_iterator end() const; 1.205 + 1.206 +private: 1.207 + queueContainer mQueue; 1.208 + bool mDirty; 1.209 + uint32_t mSize; 1.210 +}; 1.211 + 1.212 +class imgLoader : public imgILoader, 1.213 + public nsIContentSniffer, 1.214 + public imgICache, 1.215 + public nsSupportsWeakReference, 1.216 + public nsIObserver 1.217 +{ 1.218 +public: 1.219 + typedef mozilla::image::ImageURL ImageURL; 1.220 + typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable; 1.221 + 1.222 + NS_DECL_ISUPPORTS 1.223 + NS_DECL_IMGILOADER 1.224 + NS_DECL_NSICONTENTSNIFFER 1.225 + NS_DECL_IMGICACHE 1.226 + NS_DECL_NSIOBSERVER 1.227 + 1.228 + static imgLoader* Singleton(); 1.229 + static imgLoader* PBSingleton(); 1.230 + 1.231 + imgLoader(); 1.232 + virtual ~imgLoader(); 1.233 + 1.234 + nsresult Init(); 1.235 + 1.236 + static imgLoader* Create() 1.237 + { 1.238 + // Unfortunately, we rely on XPCOM module init happening 1.239 + // before imgLoader creation. For now, it's easier 1.240 + // to just call CallCreateInstance() which will init 1.241 + // the image module instead of calling new imgLoader 1.242 + // directly. 1.243 + imgILoader *loader; 1.244 + CallCreateInstance("@mozilla.org/image/loader;1", &loader); 1.245 + // There's only one imgLoader implementation so we 1.246 + // can safely cast to it. 1.247 + return static_cast<imgLoader*>(loader); 1.248 + } 1.249 + 1.250 + static already_AddRefed<imgLoader> GetInstance(); 1.251 + 1.252 + nsresult LoadImage(nsIURI *aURI, 1.253 + nsIURI *aInitialDocumentURI, 1.254 + nsIURI *aReferrerURI, 1.255 + nsIPrincipal* aLoadingPrincipal, 1.256 + nsILoadGroup *aLoadGroup, 1.257 + imgINotificationObserver *aObserver, 1.258 + nsISupports *aCX, 1.259 + nsLoadFlags aLoadFlags, 1.260 + nsISupports *aCacheKey, 1.261 + nsIChannelPolicy *aPolicy, 1.262 + const nsAString& initiatorType, 1.263 + imgRequestProxy **_retval); 1.264 + nsresult LoadImageWithChannel(nsIChannel *channel, 1.265 + imgINotificationObserver *aObserver, 1.266 + nsISupports *aCX, 1.267 + nsIStreamListener **listener, 1.268 + imgRequestProxy **_retval); 1.269 + 1.270 + static nsresult GetMimeTypeFromContent(const char* aContents, uint32_t aLength, nsACString& aContentType); 1.271 + // exported for use by mimei.cpp in libxul sdk builds 1.272 + static NS_EXPORT_(bool) SupportImageWithMimeType(const char* aMimeType); 1.273 + 1.274 + static void GlobalInit(); // for use by the factory 1.275 + static void Shutdown(); // for use by the factory 1.276 + 1.277 + nsresult ClearChromeImageCache(); 1.278 + nsresult ClearImageCache(); 1.279 + void MinimizeCaches(); 1.280 + 1.281 + nsresult InitCache(); 1.282 + 1.283 + nsAutoCString GetCacheKey(nsIURI *firstPartyIsolationURI, 1.284 + nsIURI* uri, 1.285 + bool *isIsolated); 1.286 + nsAutoCString GetCacheKey(nsIURI *firstPartyIsolationURI, 1.287 + ImageURL *imgURI, 1.288 + bool *isIsolated); 1.289 + bool RemoveFromCache(ImageURL *aKey); 1.290 + bool RemoveFromCache(nsAutoCString key, 1.291 + imgCacheTable &cache, 1.292 + imgCacheQueue &queue); 1.293 + bool RemoveFromCache(imgCacheEntry *entry); 1.294 + 1.295 + bool PutIntoCache(nsAutoCString key, imgCacheEntry *entry); 1.296 + 1.297 + 1.298 + // Returns true if we should prefer evicting cache entry |two| over cache 1.299 + // entry |one|. 1.300 + // This mixes units in the worst way, but provides reasonable results. 1.301 + inline static bool CompareCacheEntries(const nsRefPtr<imgCacheEntry> &one, 1.302 + const nsRefPtr<imgCacheEntry> &two) 1.303 + { 1.304 + if (!one) 1.305 + return false; 1.306 + if (!two) 1.307 + return true; 1.308 + 1.309 + const double sizeweight = 1.0 - sCacheTimeWeight; 1.310 + 1.311 + // We want large, old images to be evicted first (depending on their 1.312 + // relative weights). Since a larger time is actually newer, we subtract 1.313 + // time's weight, so an older image has a larger weight. 1.314 + double oneweight = double(one->GetDataSize()) * sizeweight - 1.315 + double(one->GetTouchedTime()) * sCacheTimeWeight; 1.316 + double twoweight = double(two->GetDataSize()) * sizeweight - 1.317 + double(two->GetTouchedTime()) * sCacheTimeWeight; 1.318 + 1.319 + return oneweight < twoweight; 1.320 + } 1.321 + 1.322 + void VerifyCacheSizes(); 1.323 + 1.324 + // The image loader maintains a hash table of all imgCacheEntries. However, 1.325 + // only some of them will be evicted from the cache: those who have no 1.326 + // imgRequestProxies watching their imgRequests. 1.327 + // 1.328 + // Once an imgRequest has no imgRequestProxies, it should notify us by 1.329 + // calling HasNoObservers(), and null out its cache entry pointer. 1.330 + // 1.331 + // Upon having a proxy start observing again, it should notify us by calling 1.332 + // HasObservers(). The request's cache entry will be re-set before this 1.333 + // happens, by calling imgRequest::SetCacheEntry() when an entry with no 1.334 + // observers is re-requested. 1.335 + bool SetHasNoProxies(ImageURL *imgURI, imgCacheEntry *entry); 1.336 + bool SetHasProxies(nsIURI *firstPartyIsolationURI, ImageURL *imgURI); 1.337 + 1.338 +private: // methods 1.339 + 1.340 + bool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aURI, 1.341 + nsIURI *aFirstPartyIsolationURI, nsIURI *aReferrerURI, 1.342 + nsILoadGroup *aLoadGroup, 1.343 + imgINotificationObserver *aObserver, nsISupports *aCX, 1.344 + nsLoadFlags aLoadFlags, bool aCanMakeNewChannel, 1.345 + imgRequestProxy **aProxyRequest, 1.346 + nsIChannelPolicy *aPolicy, 1.347 + nsIPrincipal* aLoadingPrincipal, 1.348 + int32_t aCORSMode); 1.349 + 1.350 + bool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI, 1.351 + nsIURI *aInitialDocumentURI, 1.352 + nsIURI *aReferrerURI, 1.353 + nsILoadGroup *aLoadGroup, 1.354 + imgINotificationObserver *aObserver, 1.355 + nsISupports *aCX, nsLoadFlags aLoadFlags, 1.356 + imgRequestProxy **aProxyRequest, 1.357 + nsIChannelPolicy *aPolicy, 1.358 + nsIPrincipal* aLoadingPrincipal, 1.359 + int32_t aCORSMode); 1.360 + 1.361 + nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, 1.362 + imgINotificationObserver *aObserver, 1.363 + nsLoadFlags aLoadFlags, imgRequestProxy **_retval); 1.364 + 1.365 + void ReadAcceptHeaderPref(); 1.366 + 1.367 + nsresult EvictEntries(imgCacheTable &aCacheToClear); 1.368 + nsresult EvictEntries(imgCacheQueue &aQueueToClear); 1.369 + 1.370 + imgCacheTable &GetCache(nsIURI *aURI); 1.371 + imgCacheQueue &GetCacheQueue(nsIURI *aURI); 1.372 + imgCacheTable &GetCache(ImageURL *aURI); 1.373 + imgCacheQueue &GetCacheQueue(ImageURL *aURI); 1.374 + void CacheEntriesChanged(ImageURL *aURI, int32_t sizediff = 0); 1.375 + void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue); 1.376 + bool RemoveMatchingUrlsFromCache(nsIURI *aImgURI); 1.377 + 1.378 +private: // data 1.379 + friend class imgCacheEntry; 1.380 + friend class imgMemoryReporter; 1.381 + friend class imgRequest; 1.382 + 1.383 + imgCacheTable mCache; 1.384 + imgCacheQueue mCacheQueue; 1.385 + 1.386 + imgCacheTable mChromeCache; 1.387 + imgCacheQueue mChromeCacheQueue; 1.388 + 1.389 + static double sCacheTimeWeight; 1.390 + static uint32_t sCacheMaxSize; 1.391 + static imgMemoryReporter* sMemReporter; 1.392 + 1.393 + static nsCOMPtr<mozIThirdPartyUtil> sThirdPartyUtilSvc; 1.394 + nsCString mAcceptHeader; 1.395 + 1.396 + nsAutoPtr<imgCacheExpirationTracker> mCacheTracker; 1.397 + bool mRespectPrivacy; 1.398 +}; 1.399 + 1.400 + 1.401 + 1.402 +/** 1.403 + * proxy stream listener class used to handle multipart/x-mixed-replace 1.404 + */ 1.405 + 1.406 +#include "nsCOMPtr.h" 1.407 +#include "nsIStreamListener.h" 1.408 +#include "nsIThreadRetargetableStreamListener.h" 1.409 + 1.410 +class ProxyListener : public nsIStreamListener 1.411 + , public nsIThreadRetargetableStreamListener 1.412 +{ 1.413 +public: 1.414 + ProxyListener(nsIStreamListener *dest); 1.415 + virtual ~ProxyListener(); 1.416 + 1.417 + /* additional members */ 1.418 + NS_DECL_ISUPPORTS 1.419 + NS_DECL_NSISTREAMLISTENER 1.420 + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 1.421 + NS_DECL_NSIREQUESTOBSERVER 1.422 + 1.423 +private: 1.424 + nsCOMPtr<nsIStreamListener> mDestListener; 1.425 +}; 1.426 + 1.427 +/** 1.428 + * A class that implements nsIProgressEventSink and forwards all calls to it to 1.429 + * the original notification callbacks of the channel. Also implements 1.430 + * nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls, 1.431 + * and forwards everything else to the channel's notification callbacks. 1.432 + */ 1.433 +class nsProgressNotificationProxy MOZ_FINAL 1.434 + : public nsIProgressEventSink 1.435 + , public nsIChannelEventSink 1.436 + , public nsIInterfaceRequestor 1.437 +{ 1.438 + public: 1.439 + nsProgressNotificationProxy(nsIChannel* channel, 1.440 + imgIRequest* proxy) 1.441 + : mImageRequest(proxy) { 1.442 + channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks)); 1.443 + } 1.444 + 1.445 + NS_DECL_ISUPPORTS 1.446 + NS_DECL_NSIPROGRESSEVENTSINK 1.447 + NS_DECL_NSICHANNELEVENTSINK 1.448 + NS_DECL_NSIINTERFACEREQUESTOR 1.449 + private: 1.450 + ~nsProgressNotificationProxy() {} 1.451 + 1.452 + nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks; 1.453 + nsCOMPtr<nsIRequest> mImageRequest; 1.454 +}; 1.455 + 1.456 +/** 1.457 + * validate checker 1.458 + */ 1.459 + 1.460 +#include "nsCOMArray.h" 1.461 + 1.462 +class imgCacheValidator : public nsIStreamListener, 1.463 + public nsIThreadRetargetableStreamListener, 1.464 + public nsIChannelEventSink, 1.465 + public nsIInterfaceRequestor, 1.466 + public nsIAsyncVerifyRedirectCallback 1.467 +{ 1.468 +public: 1.469 + imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader, 1.470 + imgRequest *request, void *aContext, bool forcePrincipalCheckForCacheEntry); 1.471 + virtual ~imgCacheValidator(); 1.472 + 1.473 + void AddProxy(imgRequestProxy *aProxy); 1.474 + 1.475 + NS_DECL_ISUPPORTS 1.476 + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 1.477 + NS_DECL_NSISTREAMLISTENER 1.478 + NS_DECL_NSIREQUESTOBSERVER 1.479 + NS_DECL_NSICHANNELEVENTSINK 1.480 + NS_DECL_NSIINTERFACEREQUESTOR 1.481 + NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK 1.482 + 1.483 +private: 1.484 + nsCOMPtr<nsIStreamListener> mDestListener; 1.485 + nsRefPtr<nsProgressNotificationProxy> mProgressProxy; 1.486 + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; 1.487 + nsCOMPtr<nsIChannel> mRedirectChannel; 1.488 + 1.489 + nsRefPtr<imgRequest> mRequest; 1.490 + nsCOMArray<imgIRequest> mProxies; 1.491 + 1.492 + nsRefPtr<imgRequest> mNewRequest; 1.493 + nsRefPtr<imgCacheEntry> mNewEntry; 1.494 + 1.495 + void *mContext; 1.496 + 1.497 + imgLoader* mImgLoader; 1.498 +}; 1.499 + 1.500 +#endif // imgLoader_h__