|
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 imgLoader_h__ |
|
8 #define imgLoader_h__ |
|
9 |
|
10 #include "mozilla/Attributes.h" |
|
11 |
|
12 #include "imgILoader.h" |
|
13 #include "imgICache.h" |
|
14 #include "nsWeakReference.h" |
|
15 #include "nsIContentSniffer.h" |
|
16 #include "nsRefPtrHashtable.h" |
|
17 #include "nsExpirationTracker.h" |
|
18 #include "nsAutoPtr.h" |
|
19 #include "imgRequest.h" |
|
20 #include "nsIProgressEventSink.h" |
|
21 #include "nsIChannel.h" |
|
22 #include "mozIThirdPartyUtil.h" |
|
23 #include "nsIThreadRetargetableStreamListener.h" |
|
24 #include "imgIRequest.h" |
|
25 |
|
26 class imgLoader; |
|
27 class imgRequestProxy; |
|
28 class imgINotificationObserver; |
|
29 class nsILoadGroup; |
|
30 class imgCacheExpirationTracker; |
|
31 class imgMemoryReporter; |
|
32 class nsIChannelPolicy; |
|
33 |
|
34 namespace mozilla { |
|
35 namespace image { |
|
36 class ImageURL; |
|
37 } |
|
38 } |
|
39 |
|
40 class imgCacheEntry |
|
41 { |
|
42 public: |
|
43 imgCacheEntry(imgLoader* loader, imgRequest *request, bool aForcePrincipalCheck); |
|
44 ~imgCacheEntry(); |
|
45 |
|
46 nsrefcnt AddRef() |
|
47 { |
|
48 NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); |
|
49 NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry addref isn't thread-safe!"); |
|
50 ++mRefCnt; |
|
51 NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this)); |
|
52 return mRefCnt; |
|
53 } |
|
54 |
|
55 nsrefcnt Release() |
|
56 { |
|
57 NS_PRECONDITION(0 != mRefCnt, "dup release"); |
|
58 NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry release isn't thread-safe!"); |
|
59 --mRefCnt; |
|
60 NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry"); |
|
61 if (mRefCnt == 0) { |
|
62 mRefCnt = 1; /* stabilize */ |
|
63 delete this; |
|
64 return 0; |
|
65 } |
|
66 return mRefCnt; |
|
67 } |
|
68 |
|
69 uint32_t GetDataSize() const |
|
70 { |
|
71 return mDataSize; |
|
72 } |
|
73 void SetDataSize(uint32_t aDataSize) |
|
74 { |
|
75 int32_t oldsize = mDataSize; |
|
76 mDataSize = aDataSize; |
|
77 UpdateCache(mDataSize - oldsize); |
|
78 } |
|
79 |
|
80 int32_t GetTouchedTime() const |
|
81 { |
|
82 return mTouchedTime; |
|
83 } |
|
84 void SetTouchedTime(int32_t time) |
|
85 { |
|
86 mTouchedTime = time; |
|
87 Touch(/* updateTime = */ false); |
|
88 } |
|
89 |
|
90 int32_t GetExpiryTime() const |
|
91 { |
|
92 return mExpiryTime; |
|
93 } |
|
94 void SetExpiryTime(int32_t aExpiryTime) |
|
95 { |
|
96 mExpiryTime = aExpiryTime; |
|
97 Touch(); |
|
98 } |
|
99 |
|
100 bool GetMustValidate() const |
|
101 { |
|
102 return mMustValidate; |
|
103 } |
|
104 void SetMustValidate(bool aValidate) |
|
105 { |
|
106 mMustValidate = aValidate; |
|
107 Touch(); |
|
108 } |
|
109 |
|
110 already_AddRefed<imgRequest> GetRequest() const |
|
111 { |
|
112 nsRefPtr<imgRequest> req = mRequest; |
|
113 return req.forget(); |
|
114 } |
|
115 |
|
116 bool Evicted() const |
|
117 { |
|
118 return mEvicted; |
|
119 } |
|
120 |
|
121 nsExpirationState *GetExpirationState() |
|
122 { |
|
123 return &mExpirationState; |
|
124 } |
|
125 |
|
126 bool HasNoProxies() const |
|
127 { |
|
128 return mHasNoProxies; |
|
129 } |
|
130 |
|
131 bool ForcePrincipalCheck() const |
|
132 { |
|
133 return mForcePrincipalCheck; |
|
134 } |
|
135 |
|
136 imgLoader* Loader() const |
|
137 { |
|
138 return mLoader; |
|
139 } |
|
140 |
|
141 private: // methods |
|
142 friend class imgLoader; |
|
143 friend class imgCacheQueue; |
|
144 void Touch(bool updateTime = true); |
|
145 void UpdateCache(int32_t diff = 0); |
|
146 void SetEvicted(bool evict) |
|
147 { |
|
148 mEvicted = evict; |
|
149 } |
|
150 void SetHasNoProxies(bool hasNoProxies); |
|
151 |
|
152 // Private, unimplemented copy constructor. |
|
153 imgCacheEntry(const imgCacheEntry &); |
|
154 |
|
155 private: // data |
|
156 nsAutoRefCnt mRefCnt; |
|
157 NS_DECL_OWNINGTHREAD |
|
158 |
|
159 imgLoader* mLoader; |
|
160 nsRefPtr<imgRequest> mRequest; |
|
161 uint32_t mDataSize; |
|
162 int32_t mTouchedTime; |
|
163 int32_t mExpiryTime; |
|
164 nsExpirationState mExpirationState; |
|
165 bool mMustValidate : 1; |
|
166 bool mEvicted : 1; |
|
167 bool mHasNoProxies : 1; |
|
168 bool mForcePrincipalCheck : 1; |
|
169 }; |
|
170 |
|
171 #include <vector> |
|
172 |
|
173 #define NS_IMGLOADER_CID \ |
|
174 { /* 9f6a0d2e-1dd1-11b2-a5b8-951f13c846f7 */ \ |
|
175 0x9f6a0d2e, \ |
|
176 0x1dd1, \ |
|
177 0x11b2, \ |
|
178 {0xa5, 0xb8, 0x95, 0x1f, 0x13, 0xc8, 0x46, 0xf7} \ |
|
179 } |
|
180 |
|
181 class imgCacheQueue |
|
182 { |
|
183 public: |
|
184 imgCacheQueue(); |
|
185 void Remove(imgCacheEntry *); |
|
186 void Push(imgCacheEntry *); |
|
187 void MarkDirty(); |
|
188 bool IsDirty(); |
|
189 already_AddRefed<imgCacheEntry> Pop(); |
|
190 void Refresh(); |
|
191 uint32_t GetSize() const; |
|
192 void UpdateSize(int32_t diff); |
|
193 uint32_t GetNumElements() const; |
|
194 typedef std::vector<nsRefPtr<imgCacheEntry> > queueContainer; |
|
195 typedef queueContainer::iterator iterator; |
|
196 typedef queueContainer::const_iterator const_iterator; |
|
197 |
|
198 iterator begin(); |
|
199 const_iterator begin() const; |
|
200 iterator end(); |
|
201 const_iterator end() const; |
|
202 |
|
203 private: |
|
204 queueContainer mQueue; |
|
205 bool mDirty; |
|
206 uint32_t mSize; |
|
207 }; |
|
208 |
|
209 class imgLoader : public imgILoader, |
|
210 public nsIContentSniffer, |
|
211 public imgICache, |
|
212 public nsSupportsWeakReference, |
|
213 public nsIObserver |
|
214 { |
|
215 public: |
|
216 typedef mozilla::image::ImageURL ImageURL; |
|
217 typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable; |
|
218 |
|
219 NS_DECL_ISUPPORTS |
|
220 NS_DECL_IMGILOADER |
|
221 NS_DECL_NSICONTENTSNIFFER |
|
222 NS_DECL_IMGICACHE |
|
223 NS_DECL_NSIOBSERVER |
|
224 |
|
225 static imgLoader* Singleton(); |
|
226 static imgLoader* PBSingleton(); |
|
227 |
|
228 imgLoader(); |
|
229 virtual ~imgLoader(); |
|
230 |
|
231 nsresult Init(); |
|
232 |
|
233 static imgLoader* Create() |
|
234 { |
|
235 // Unfortunately, we rely on XPCOM module init happening |
|
236 // before imgLoader creation. For now, it's easier |
|
237 // to just call CallCreateInstance() which will init |
|
238 // the image module instead of calling new imgLoader |
|
239 // directly. |
|
240 imgILoader *loader; |
|
241 CallCreateInstance("@mozilla.org/image/loader;1", &loader); |
|
242 // There's only one imgLoader implementation so we |
|
243 // can safely cast to it. |
|
244 return static_cast<imgLoader*>(loader); |
|
245 } |
|
246 |
|
247 static already_AddRefed<imgLoader> GetInstance(); |
|
248 |
|
249 nsresult LoadImage(nsIURI *aURI, |
|
250 nsIURI *aInitialDocumentURI, |
|
251 nsIURI *aReferrerURI, |
|
252 nsIPrincipal* aLoadingPrincipal, |
|
253 nsILoadGroup *aLoadGroup, |
|
254 imgINotificationObserver *aObserver, |
|
255 nsISupports *aCX, |
|
256 nsLoadFlags aLoadFlags, |
|
257 nsISupports *aCacheKey, |
|
258 nsIChannelPolicy *aPolicy, |
|
259 const nsAString& initiatorType, |
|
260 imgRequestProxy **_retval); |
|
261 nsresult LoadImageWithChannel(nsIChannel *channel, |
|
262 imgINotificationObserver *aObserver, |
|
263 nsISupports *aCX, |
|
264 nsIStreamListener **listener, |
|
265 imgRequestProxy **_retval); |
|
266 |
|
267 static nsresult GetMimeTypeFromContent(const char* aContents, uint32_t aLength, nsACString& aContentType); |
|
268 // exported for use by mimei.cpp in libxul sdk builds |
|
269 static NS_EXPORT_(bool) SupportImageWithMimeType(const char* aMimeType); |
|
270 |
|
271 static void GlobalInit(); // for use by the factory |
|
272 static void Shutdown(); // for use by the factory |
|
273 |
|
274 nsresult ClearChromeImageCache(); |
|
275 nsresult ClearImageCache(); |
|
276 void MinimizeCaches(); |
|
277 |
|
278 nsresult InitCache(); |
|
279 |
|
280 nsAutoCString GetCacheKey(nsIURI *firstPartyIsolationURI, |
|
281 nsIURI* uri, |
|
282 bool *isIsolated); |
|
283 nsAutoCString GetCacheKey(nsIURI *firstPartyIsolationURI, |
|
284 ImageURL *imgURI, |
|
285 bool *isIsolated); |
|
286 bool RemoveFromCache(ImageURL *aKey); |
|
287 bool RemoveFromCache(nsAutoCString key, |
|
288 imgCacheTable &cache, |
|
289 imgCacheQueue &queue); |
|
290 bool RemoveFromCache(imgCacheEntry *entry); |
|
291 |
|
292 bool PutIntoCache(nsAutoCString key, imgCacheEntry *entry); |
|
293 |
|
294 |
|
295 // Returns true if we should prefer evicting cache entry |two| over cache |
|
296 // entry |one|. |
|
297 // This mixes units in the worst way, but provides reasonable results. |
|
298 inline static bool CompareCacheEntries(const nsRefPtr<imgCacheEntry> &one, |
|
299 const nsRefPtr<imgCacheEntry> &two) |
|
300 { |
|
301 if (!one) |
|
302 return false; |
|
303 if (!two) |
|
304 return true; |
|
305 |
|
306 const double sizeweight = 1.0 - sCacheTimeWeight; |
|
307 |
|
308 // We want large, old images to be evicted first (depending on their |
|
309 // relative weights). Since a larger time is actually newer, we subtract |
|
310 // time's weight, so an older image has a larger weight. |
|
311 double oneweight = double(one->GetDataSize()) * sizeweight - |
|
312 double(one->GetTouchedTime()) * sCacheTimeWeight; |
|
313 double twoweight = double(two->GetDataSize()) * sizeweight - |
|
314 double(two->GetTouchedTime()) * sCacheTimeWeight; |
|
315 |
|
316 return oneweight < twoweight; |
|
317 } |
|
318 |
|
319 void VerifyCacheSizes(); |
|
320 |
|
321 // The image loader maintains a hash table of all imgCacheEntries. However, |
|
322 // only some of them will be evicted from the cache: those who have no |
|
323 // imgRequestProxies watching their imgRequests. |
|
324 // |
|
325 // Once an imgRequest has no imgRequestProxies, it should notify us by |
|
326 // calling HasNoObservers(), and null out its cache entry pointer. |
|
327 // |
|
328 // Upon having a proxy start observing again, it should notify us by calling |
|
329 // HasObservers(). The request's cache entry will be re-set before this |
|
330 // happens, by calling imgRequest::SetCacheEntry() when an entry with no |
|
331 // observers is re-requested. |
|
332 bool SetHasNoProxies(ImageURL *imgURI, imgCacheEntry *entry); |
|
333 bool SetHasProxies(nsIURI *firstPartyIsolationURI, ImageURL *imgURI); |
|
334 |
|
335 private: // methods |
|
336 |
|
337 bool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aURI, |
|
338 nsIURI *aFirstPartyIsolationURI, nsIURI *aReferrerURI, |
|
339 nsILoadGroup *aLoadGroup, |
|
340 imgINotificationObserver *aObserver, nsISupports *aCX, |
|
341 nsLoadFlags aLoadFlags, bool aCanMakeNewChannel, |
|
342 imgRequestProxy **aProxyRequest, |
|
343 nsIChannelPolicy *aPolicy, |
|
344 nsIPrincipal* aLoadingPrincipal, |
|
345 int32_t aCORSMode); |
|
346 |
|
347 bool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI, |
|
348 nsIURI *aInitialDocumentURI, |
|
349 nsIURI *aReferrerURI, |
|
350 nsILoadGroup *aLoadGroup, |
|
351 imgINotificationObserver *aObserver, |
|
352 nsISupports *aCX, nsLoadFlags aLoadFlags, |
|
353 imgRequestProxy **aProxyRequest, |
|
354 nsIChannelPolicy *aPolicy, |
|
355 nsIPrincipal* aLoadingPrincipal, |
|
356 int32_t aCORSMode); |
|
357 |
|
358 nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, |
|
359 imgINotificationObserver *aObserver, |
|
360 nsLoadFlags aLoadFlags, imgRequestProxy **_retval); |
|
361 |
|
362 void ReadAcceptHeaderPref(); |
|
363 |
|
364 nsresult EvictEntries(imgCacheTable &aCacheToClear); |
|
365 nsresult EvictEntries(imgCacheQueue &aQueueToClear); |
|
366 |
|
367 imgCacheTable &GetCache(nsIURI *aURI); |
|
368 imgCacheQueue &GetCacheQueue(nsIURI *aURI); |
|
369 imgCacheTable &GetCache(ImageURL *aURI); |
|
370 imgCacheQueue &GetCacheQueue(ImageURL *aURI); |
|
371 void CacheEntriesChanged(ImageURL *aURI, int32_t sizediff = 0); |
|
372 void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue); |
|
373 bool RemoveMatchingUrlsFromCache(nsIURI *aImgURI); |
|
374 |
|
375 private: // data |
|
376 friend class imgCacheEntry; |
|
377 friend class imgMemoryReporter; |
|
378 friend class imgRequest; |
|
379 |
|
380 imgCacheTable mCache; |
|
381 imgCacheQueue mCacheQueue; |
|
382 |
|
383 imgCacheTable mChromeCache; |
|
384 imgCacheQueue mChromeCacheQueue; |
|
385 |
|
386 static double sCacheTimeWeight; |
|
387 static uint32_t sCacheMaxSize; |
|
388 static imgMemoryReporter* sMemReporter; |
|
389 |
|
390 static nsCOMPtr<mozIThirdPartyUtil> sThirdPartyUtilSvc; |
|
391 nsCString mAcceptHeader; |
|
392 |
|
393 nsAutoPtr<imgCacheExpirationTracker> mCacheTracker; |
|
394 bool mRespectPrivacy; |
|
395 }; |
|
396 |
|
397 |
|
398 |
|
399 /** |
|
400 * proxy stream listener class used to handle multipart/x-mixed-replace |
|
401 */ |
|
402 |
|
403 #include "nsCOMPtr.h" |
|
404 #include "nsIStreamListener.h" |
|
405 #include "nsIThreadRetargetableStreamListener.h" |
|
406 |
|
407 class ProxyListener : public nsIStreamListener |
|
408 , public nsIThreadRetargetableStreamListener |
|
409 { |
|
410 public: |
|
411 ProxyListener(nsIStreamListener *dest); |
|
412 virtual ~ProxyListener(); |
|
413 |
|
414 /* additional members */ |
|
415 NS_DECL_ISUPPORTS |
|
416 NS_DECL_NSISTREAMLISTENER |
|
417 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER |
|
418 NS_DECL_NSIREQUESTOBSERVER |
|
419 |
|
420 private: |
|
421 nsCOMPtr<nsIStreamListener> mDestListener; |
|
422 }; |
|
423 |
|
424 /** |
|
425 * A class that implements nsIProgressEventSink and forwards all calls to it to |
|
426 * the original notification callbacks of the channel. Also implements |
|
427 * nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls, |
|
428 * and forwards everything else to the channel's notification callbacks. |
|
429 */ |
|
430 class nsProgressNotificationProxy MOZ_FINAL |
|
431 : public nsIProgressEventSink |
|
432 , public nsIChannelEventSink |
|
433 , public nsIInterfaceRequestor |
|
434 { |
|
435 public: |
|
436 nsProgressNotificationProxy(nsIChannel* channel, |
|
437 imgIRequest* proxy) |
|
438 : mImageRequest(proxy) { |
|
439 channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks)); |
|
440 } |
|
441 |
|
442 NS_DECL_ISUPPORTS |
|
443 NS_DECL_NSIPROGRESSEVENTSINK |
|
444 NS_DECL_NSICHANNELEVENTSINK |
|
445 NS_DECL_NSIINTERFACEREQUESTOR |
|
446 private: |
|
447 ~nsProgressNotificationProxy() {} |
|
448 |
|
449 nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks; |
|
450 nsCOMPtr<nsIRequest> mImageRequest; |
|
451 }; |
|
452 |
|
453 /** |
|
454 * validate checker |
|
455 */ |
|
456 |
|
457 #include "nsCOMArray.h" |
|
458 |
|
459 class imgCacheValidator : public nsIStreamListener, |
|
460 public nsIThreadRetargetableStreamListener, |
|
461 public nsIChannelEventSink, |
|
462 public nsIInterfaceRequestor, |
|
463 public nsIAsyncVerifyRedirectCallback |
|
464 { |
|
465 public: |
|
466 imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader, |
|
467 imgRequest *request, void *aContext, bool forcePrincipalCheckForCacheEntry); |
|
468 virtual ~imgCacheValidator(); |
|
469 |
|
470 void AddProxy(imgRequestProxy *aProxy); |
|
471 |
|
472 NS_DECL_ISUPPORTS |
|
473 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER |
|
474 NS_DECL_NSISTREAMLISTENER |
|
475 NS_DECL_NSIREQUESTOBSERVER |
|
476 NS_DECL_NSICHANNELEVENTSINK |
|
477 NS_DECL_NSIINTERFACEREQUESTOR |
|
478 NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK |
|
479 |
|
480 private: |
|
481 nsCOMPtr<nsIStreamListener> mDestListener; |
|
482 nsRefPtr<nsProgressNotificationProxy> mProgressProxy; |
|
483 nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; |
|
484 nsCOMPtr<nsIChannel> mRedirectChannel; |
|
485 |
|
486 nsRefPtr<imgRequest> mRequest; |
|
487 nsCOMArray<imgIRequest> mProxies; |
|
488 |
|
489 nsRefPtr<imgRequest> mNewRequest; |
|
490 nsRefPtr<imgCacheEntry> mNewEntry; |
|
491 |
|
492 void *mContext; |
|
493 |
|
494 imgLoader* mImgLoader; |
|
495 }; |
|
496 |
|
497 #endif // imgLoader_h__ |