Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/imgLoader.h
Line
Count
Source (jump to first uncovered line)
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
  nsrefcnt AddRef()
50
0
  {
51
0
    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
0
  }
57
58
  nsrefcnt Release()
59
0
  {
60
0
    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
0
    }
69
0
    return mRefCnt;
70
0
  }
71
72
  uint32_t GetDataSize() const
73
0
  {
74
0
    return mDataSize;
75
0
  }
76
  void SetDataSize(uint32_t aDataSize)
77
0
  {
78
0
    int32_t oldsize = mDataSize;
79
0
    mDataSize = aDataSize;
80
0
    UpdateCache(mDataSize - oldsize);
81
0
  }
82
83
  int32_t GetTouchedTime() const
84
0
  {
85
0
    return mTouchedTime;
86
0
  }
87
  void SetTouchedTime(int32_t time)
88
0
  {
89
0
    mTouchedTime = time;
90
0
    Touch(/* updateTime = */ false);
91
0
  }
92
93
  uint32_t GetLoadTime() const
94
0
  {
95
0
    return mLoadTime;
96
0
  }
97
98
  void UpdateLoadTime();
99
100
  int32_t GetExpiryTime() const
101
0
  {
102
0
    return mExpiryTime;
103
0
  }
104
  void SetExpiryTime(int32_t aExpiryTime)
105
0
  {
106
0
    mExpiryTime = aExpiryTime;
107
0
    Touch();
108
0
  }
109
110
  bool GetMustValidate() const
111
0
  {
112
0
    return mMustValidate;
113
0
  }
114
  void SetMustValidate(bool aValidate)
115
0
  {
116
0
    mMustValidate = aValidate;
117
0
    Touch();
118
0
  }
119
120
  already_AddRefed<imgRequest> GetRequest() const
121
0
  {
122
0
    RefPtr<imgRequest> req = mRequest;
123
0
    return req.forget();
124
0
  }
125
126
  bool Evicted() const
127
0
  {
128
0
    return mEvicted;
129
0
  }
130
131
  nsExpirationState* GetExpirationState()
132
0
  {
133
0
    return &mExpirationState;
134
0
  }
135
136
  bool HasNoProxies() const
137
0
  {
138
0
    return mHasNoProxies;
139
0
  }
140
141
  bool ForcePrincipalCheck() const
142
0
  {
143
0
    return mForcePrincipalCheck;
144
0
  }
145
146
  imgLoader* Loader() const
147
0
  {
148
0
    return mLoader;
149
0
  }
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
0
  {
158
0
    mEvicted = evict;
159
0
  }
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
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
  inline static bool CompareCacheEntries(const RefPtr<imgCacheEntry>& one,
365
                                         const RefPtr<imgCacheEntry>& two)
366
0
  {
367
0
    if (!one) {
368
0
      return false;
369
0
    }
370
0
    if (!two) {
371
0
      return true;
372
0
    }
373
0
374
0
    const double sizeweight = 1.0 - sCacheTimeWeight;
375
0
376
0
    // We want large, old images to be evicted first (depending on their
377
0
    // relative weights). Since a larger time is actually newer, we subtract
378
0
    // 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
0
384
0
    return oneweight < twoweight;
385
0
  }
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
                     bool* aNewChannelCreated,
417
                     imgRequestProxy** aProxyRequest,
418
                     nsIPrincipal* aLoadingPrincipal,
419
                     int32_t aCORSMode);
420
421
  bool ValidateRequestWithNewChannel(imgRequest* request, nsIURI* aURI,
422
                                     nsIURI* aInitialDocumentURI,
423
                                     nsIURI* aReferrerURI,
424
                                     ReferrerPolicy aReferrerPolicy,
425
                                     nsILoadGroup* aLoadGroup,
426
                                     imgINotificationObserver* aObserver,
427
                                     nsISupports* aCX,
428
                                     nsIDocument* aLoadingDocument,
429
                                     nsLoadFlags aLoadFlags,
430
                                     nsContentPolicyType aContentPolicyType,
431
                                     imgRequestProxy** aProxyRequest,
432
                                     nsIPrincipal* aLoadingPrincipal,
433
                                     int32_t aCORSMode,
434
                                     bool* aNewChannelCreated);
435
436
  nsresult CreateNewProxyForRequest(imgRequest* aRequest,
437
                                    nsILoadGroup* aLoadGroup,
438
                                    nsIDocument* aLoadingDocument,
439
                                    imgINotificationObserver* aObserver,
440
                                    nsLoadFlags aLoadFlags,
441
                                    imgRequestProxy** _retval);
442
443
  void ReadAcceptHeaderPref();
444
445
  nsresult EvictEntries(imgCacheTable& aCacheToClear);
446
  nsresult EvictEntries(imgCacheQueue& aQueueToClear);
447
448
  imgCacheTable& GetCache(bool aForChrome);
449
  imgCacheTable& GetCache(const ImageCacheKey& aKey);
450
  imgCacheQueue& GetCacheQueue(bool aForChrome);
451
  imgCacheQueue& GetCacheQueue(const ImageCacheKey& aKey);
452
  void CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff = 0);
453
  void CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue);
454
455
private: // data
456
  friend class imgCacheEntry;
457
  friend class imgMemoryReporter;
458
459
  imgCacheTable mCache;
460
  imgCacheQueue mCacheQueue;
461
462
  imgCacheTable mChromeCache;
463
  imgCacheQueue mChromeCacheQueue;
464
465
  // Hash set of every imgRequest for this loader that isn't in mCache or
466
  // mChromeCache. The union over all imgLoader's of mCache, mChromeCache, and
467
  // mUncachedImages should be every imgRequest that is alive. These are weak
468
  // pointers so we rely on the imgRequest destructor to remove itself.
469
  imgSet mUncachedImages;
470
  // The imgRequest can have refs to them held on non-main thread, so we need
471
  // a mutex because we modify the uncached images set from the imgRequest
472
  // destructor.
473
  Mutex mUncachedImagesMutex;
474
475
  static double sCacheTimeWeight;
476
  static uint32_t sCacheMaxSize;
477
  static imgMemoryReporter* sMemReporter;
478
479
  nsCString mAcceptHeader;
480
481
  mozilla::UniquePtr<imgCacheExpirationTracker> mCacheTracker;
482
  bool mRespectPrivacy;
483
};
484
485
486
487
/**
488
 * proxy stream listener class used to handle multipart/x-mixed-replace
489
 */
490
491
#include "nsCOMPtr.h"
492
#include "nsIStreamListener.h"
493
#include "nsIThreadRetargetableStreamListener.h"
494
495
class ProxyListener : public nsIStreamListener
496
                    , public nsIThreadRetargetableStreamListener
497
{
498
public:
499
  explicit ProxyListener(nsIStreamListener* dest);
500
501
  /* additional members */
502
  NS_DECL_THREADSAFE_ISUPPORTS
503
  NS_DECL_NSISTREAMLISTENER
504
  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
505
  NS_DECL_NSIREQUESTOBSERVER
506
507
private:
508
  virtual ~ProxyListener();
509
510
  nsCOMPtr<nsIStreamListener> mDestListener;
511
};
512
513
/**
514
 * A class that implements nsIProgressEventSink and forwards all calls to it to
515
 * the original notification callbacks of the channel. Also implements
516
 * nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls,
517
 * and forwards everything else to the channel's notification callbacks.
518
 */
519
class nsProgressNotificationProxy final
520
  : public nsIProgressEventSink
521
  , public nsIChannelEventSink
522
  , public nsIInterfaceRequestor
523
{
524
  public:
525
    nsProgressNotificationProxy(nsIChannel* channel,
526
                                imgIRequest* proxy)
527
0
        : mImageRequest(proxy) {
528
0
      channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks));
529
0
    }
530
531
    NS_DECL_ISUPPORTS
532
    NS_DECL_NSIPROGRESSEVENTSINK
533
    NS_DECL_NSICHANNELEVENTSINK
534
    NS_DECL_NSIINTERFACEREQUESTOR
535
  private:
536
0
    ~nsProgressNotificationProxy() { }
537
538
    nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks;
539
    nsCOMPtr<nsIRequest> mImageRequest;
540
};
541
542
/**
543
 * validate checker
544
 */
545
546
#include "nsCOMArray.h"
547
548
class imgCacheValidator : public nsIStreamListener,
549
                          public nsIThreadRetargetableStreamListener,
550
                          public nsIChannelEventSink,
551
                          public nsIInterfaceRequestor,
552
                          public nsIAsyncVerifyRedirectCallback
553
{
554
public:
555
  imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader,
556
                    imgRequest* aRequest, nsISupports* aContext,
557
                    bool forcePrincipalCheckForCacheEntry);
558
559
  void AddProxy(imgRequestProxy* aProxy);
560
  void RemoveProxy(imgRequestProxy* aProxy);
561
562
  NS_DECL_THREADSAFE_ISUPPORTS
563
  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
564
  NS_DECL_NSISTREAMLISTENER
565
  NS_DECL_NSIREQUESTOBSERVER
566
  NS_DECL_NSICHANNELEVENTSINK
567
  NS_DECL_NSIINTERFACEREQUESTOR
568
  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
569
570
private:
571
  void UpdateProxies(bool aCancelRequest, bool aSyncNotify);
572
  virtual ~imgCacheValidator();
573
574
  nsCOMPtr<nsIStreamListener> mDestListener;
575
  RefPtr<nsProgressNotificationProxy> mProgressProxy;
576
  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
577
  nsCOMPtr<nsIChannel> mRedirectChannel;
578
579
  RefPtr<imgRequest> mRequest;
580
  AutoTArray<RefPtr<imgRequestProxy>, 4> mProxies;
581
582
  RefPtr<imgRequest> mNewRequest;
583
  RefPtr<imgCacheEntry> mNewEntry;
584
585
  nsCOMPtr<nsISupports> mContext;
586
587
  imgLoader* mImgLoader;
588
589
  bool mHadInsecureRedirect;
590
};
591
592
#endif // mozilla_image_imgLoader_h