summaryrefslogtreecommitdiffstats
path: root/netwerk/cache/nsCacheService.h
blob: 2f9ca28af06a1929455b9c410612ca308ab95ea3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef _nsCacheService_h_
#define _nsCacheService_h_

#include "nsICacheService.h"
#include "nsCacheSession.h"
#include "nsCacheDevice.h"
#include "nsCacheEntry.h"
#include "nsThreadUtils.h"
#include "nsICacheListener.h"

#include "prthread.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
#include "mozilla/Telemetry.h"

class nsCacheRequest;
class nsCacheProfilePrefObserver;
class nsOfflineCacheDevice;
class nsCacheServiceAutoLock;
class nsITimer;
class mozIStorageService;

/******************************************************************************
 * nsNotifyDoomListener
 *****************************************************************************/

class nsNotifyDoomListener : public mozilla::Runnable {
 public:
  nsNotifyDoomListener(nsICacheListener* listener, nsresult status)
      : mozilla::Runnable("nsNotifyDoomListener"),
        mListener(listener)  // transfers reference
        ,
        mStatus(status) {}

  NS_IMETHOD Run() override {
    mListener->OnCacheEntryDoomed(mStatus);
    NS_RELEASE(mListener);
    return NS_OK;
  }

 private:
  nsICacheListener* mListener;
  nsresult mStatus;
};

/******************************************************************************
 *  nsCacheService
 ******************************************************************************/

class nsCacheService final : public nsICacheServiceInternal {
  virtual ~nsCacheService();

 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSICACHESERVICE
  NS_DECL_NSICACHESERVICEINTERNAL

  nsCacheService();

  // Define a Create method to be used with a factory:
  static nsresult Create(nsISupports* outer, const nsIID& iid, void** result);

  /**
   * Methods called by nsCacheSession
   */
  static nsresult OpenCacheEntry(nsCacheSession* session, const nsACString& key,
                                 nsCacheAccessMode accessRequested,
                                 bool blockingMode, nsICacheListener* listener,
                                 nsICacheEntryDescriptor** result);

  static nsresult EvictEntriesForSession(nsCacheSession* session);

  static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
                                            bool* result);

  static nsresult DoomEntry(nsCacheSession* session, const nsACString& key,
                            nsICacheListener* listener);

  /**
   * Methods called by nsCacheEntryDescriptor
   */

  static void CloseDescriptor(nsCacheEntryDescriptor* descriptor);

  static nsresult GetFileForEntry(nsCacheEntry* entry, nsIFile** result);

  static nsresult OpenInputStreamForEntry(nsCacheEntry* entry,
                                          nsCacheAccessMode mode,
                                          uint32_t offset,
                                          nsIInputStream** result);

  static nsresult OpenOutputStreamForEntry(nsCacheEntry* entry,
                                           nsCacheAccessMode mode,
                                           uint32_t offset,
                                           nsIOutputStream** result);

  static nsresult OnDataSizeChange(nsCacheEntry* entry, int32_t deltaSize);

  static nsresult SetCacheElement(nsCacheEntry* entry, nsISupports* element);

  static nsresult ValidateEntry(nsCacheEntry* entry);

  static int32_t CacheCompressionLevel();

  static bool GetClearingEntries();

  static void GetCacheBaseDirectoty(nsIFile** result);
  static void GetDiskCacheDirectory(nsIFile** result);
  static void GetAppCacheDirectory(nsIFile** result);

  /**
   * Methods called by any cache classes
   */

  static nsCacheService* GlobalInstance() { return gService; }

  static nsresult DoomEntry(nsCacheEntry* entry);

  static bool IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy policy);

  /**
   * Methods called by nsApplicationCacheService
   */

  nsresult GetOfflineDevice(nsOfflineCacheDevice** aDevice);

  /**
   * Creates an offline cache device that works over a specific profile
   * directory. A tool to preload offline cache for profiles different from the
   * current application's profile directory.
   */
  nsresult GetCustomOfflineDevice(nsIFile* aProfileDir, int32_t aQuota,
                                  nsOfflineCacheDevice** aDevice);

  // This method may be called to release an object while the cache service
  // lock is being held.  If a non-null target is specified and the target
  // does not correspond to the current thread, then the release will be
  // proxied to the specified target.  Otherwise, the object will be added to
  // the list of objects to be released when the cache service is unlocked.
  static void ReleaseObject_Locked(nsISupports* object,
                                   nsIEventTarget* target = nullptr);

  static nsresult DispatchToCacheIOThread(nsIRunnable* event);

  // Calling this method will block the calling thread until all pending
  // events on the cache-io thread has finished. The calling thread must
  // hold the cache-lock
  static nsresult SyncWithCacheIOThread();

  /**
   * Methods called by nsCacheProfilePrefObserver
   */
  static void OnProfileShutdown();
  static void OnProfileChanged();

  static void SetOfflineCacheEnabled(bool enabled);
  // Sets the offline cache capacity (in kilobytes)
  static void SetOfflineCacheCapacity(int32_t capacity);

  static void MoveOrRemoveDiskCache(nsIFile* aOldCacheDir,
                                    nsIFile* aNewCacheDir,
                                    const char* aCacheSubdir);

  nsresult Init();
  void Shutdown();

  static bool IsInitialized() {
    if (!gService) {
      return false;
    }
    return gService->mInitialized;
  }

  static void AssertOwnsLock() { gService->mLock.AssertCurrentThreadOwns(); }

  static void LeavePrivateBrowsing();
  bool IsDoomListEmpty();

  typedef bool (*DoomCheckFn)(nsCacheEntry* entry);

  // Accessors to the disabled functionality
  nsresult CreateSessionInternal(const char* clientID,
                                 nsCacheStoragePolicy storagePolicy,
                                 bool streamBased, nsICacheSession** result);
  nsresult VisitEntriesInternal(nsICacheVisitor* visitor);
  nsresult EvictEntriesInternal(nsCacheStoragePolicy storagePolicy);

 private:
  friend class nsCacheServiceAutoLock;
  friend class nsOfflineCacheDevice;
  friend class nsProcessRequestEvent;
  friend class nsBlockOnCacheThreadEvent;
  friend class nsDoomEvent;
  friend class nsAsyncDoomEvent;
  friend class nsCacheEntryDescriptor;

  /**
   * Internal Methods
   */

  static void Lock();
  static void Lock(::mozilla::Telemetry::HistogramID mainThreadLockerID);
  static void Unlock();
  void LockAcquired();
  void LockReleased();

  nsresult CreateDiskDevice();
  nsresult CreateOfflineDevice();
  nsresult CreateCustomOfflineDevice(nsIFile* aProfileDir, int32_t aQuota,
                                     nsOfflineCacheDevice** aDevice);
  nsresult CreateMemoryDevice();

  nsresult RemoveCustomOfflineDevice(nsOfflineCacheDevice* aDevice);

  nsresult CreateRequest(nsCacheSession* session, const nsACString& clientKey,
                         nsCacheAccessMode accessRequested, bool blockingMode,
                         nsICacheListener* listener, nsCacheRequest** request);

  nsresult DoomEntry_Internal(nsCacheEntry* entry,
                              bool doProcessPendingRequests);

  nsresult EvictEntriesForClient(const char* clientID,
                                 nsCacheStoragePolicy storagePolicy);

  // Notifies request listener asynchronously on the request's thread, and
  // releases the descriptor on the request's thread.  If this method fails,
  // the descriptor is not released.
  nsresult NotifyListener(nsCacheRequest* request,
                          nsICacheEntryDescriptor* descriptor,
                          nsCacheAccessMode accessGranted, nsresult error);

  nsresult ActivateEntry(nsCacheRequest* request, nsCacheEntry** entry,
                         nsCacheEntry** doomedEntry);

  nsCacheDevice* EnsureEntryHasDevice(nsCacheEntry* entry);

  nsCacheEntry* SearchCacheDevices(nsCString* key, nsCacheStoragePolicy policy,
                                   bool* collision);

  void DeactivateEntry(nsCacheEntry* entry);

  nsresult ProcessRequest(nsCacheRequest* request,
                          bool calledFromOpenCacheEntry,
                          nsICacheEntryDescriptor** result);

  nsresult ProcessPendingRequests(nsCacheEntry* entry);

  void ClearDoomList(void);
  void DoomActiveEntries(DoomCheckFn check);
  void CloseAllStreams();
  void FireClearNetworkCacheStoredAnywhereNotification();

  void LogCacheStatistics();

  /**
   *  Data Members
   */

  static nsCacheService* gService;  // there can be only one...

  nsCOMPtr<mozIStorageService> mStorageService;

  nsCacheProfilePrefObserver* mObserver;

  mozilla::Mutex mLock;
  mozilla::CondVar mCondVar;
  bool mNotified;

  mozilla::Mutex mTimeStampLock;
  mozilla::TimeStamp mLockAcquiredTimeStamp;

  nsCOMPtr<nsIThread> mCacheIOThread;

  nsTArray<nsISupports*> mDoomedObjects;

  bool mInitialized;
  bool mClearingEntries;

  bool mEnableOfflineDevice;

  nsOfflineCacheDevice* mOfflineDevice;

  nsRefPtrHashtable<nsStringHashKey, nsOfflineCacheDevice>
      mCustomOfflineDevices;

  nsCacheEntryHashTable mActiveEntries;
  PRCList mDoomedEntries;

  // stats

  uint32_t mTotalEntries;
  uint32_t mCacheHits;
  uint32_t mCacheMisses;
  uint32_t mMaxKeyLength;
  uint32_t mMaxDataSize;
  uint32_t mMaxMetaSize;

  // Unexpected error totals
  uint32_t mDeactivateFailures;
  uint32_t mDeactivatedUnboundEntries;
};

/******************************************************************************
 *  nsCacheServiceAutoLock
 ******************************************************************************/

#define LOCK_TELEM(x) \
  (::mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_##x)

// Instantiate this class to acquire the cache service lock for a particular
// execution scope.
class nsCacheServiceAutoLock {
 public:
  nsCacheServiceAutoLock() { nsCacheService::Lock(); }
  explicit nsCacheServiceAutoLock(
      mozilla::Telemetry::HistogramID mainThreadLockerID) {
    nsCacheService::Lock(mainThreadLockerID);
  }
  ~nsCacheServiceAutoLock() { nsCacheService::Unlock(); }
};

#endif  // _nsCacheService_h_