1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
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 _nsCacheService_h_
8 #define _nsCacheService_h_
9 
10 #include "nsICacheService.h"
11 #include "nsCacheSession.h"
12 #include "nsCacheDevice.h"
13 #include "nsCacheEntry.h"
14 #include "nsThreadUtils.h"
15 #include "nsICacheListener.h"
16 #include "nsIMemoryReporter.h"
17 
18 #include "prthread.h"
19 #include "nsIObserver.h"
20 #include "nsString.h"
21 #include "nsTArray.h"
22 #include "nsRefPtrHashtable.h"
23 #include "mozilla/CondVar.h"
24 #include "mozilla/Mutex.h"
25 #include "mozilla/Telemetry.h"
26 
27 class nsCacheRequest;
28 class nsCacheProfilePrefObserver;
29 class nsDiskCacheDevice;
30 class nsMemoryCacheDevice;
31 class nsOfflineCacheDevice;
32 class nsCacheServiceAutoLock;
33 class nsITimer;
34 class mozIStorageService;
35 
36 
37 /******************************************************************************
38  * nsNotifyDoomListener
39  *****************************************************************************/
40 
41 class nsNotifyDoomListener : public mozilla::Runnable {
42 public:
nsNotifyDoomListener(nsICacheListener * listener,nsresult status)43     nsNotifyDoomListener(nsICacheListener *listener,
44                          nsresult status)
45         : mListener(listener)      // transfers reference
46         , mStatus(status)
47     {}
48 
Run()49     NS_IMETHOD Run() override
50     {
51         mListener->OnCacheEntryDoomed(mStatus);
52         NS_RELEASE(mListener);
53         return NS_OK;
54     }
55 
56 private:
57     nsICacheListener *mListener;
58     nsresult          mStatus;
59 };
60 
61 /******************************************************************************
62  *  nsCacheService
63  ******************************************************************************/
64 
65 class nsCacheService final : public nsICacheServiceInternal,
66                              public nsIMemoryReporter
67 {
68     virtual ~nsCacheService();
69 
70 public:
71     NS_DECL_THREADSAFE_ISUPPORTS
72     NS_DECL_NSICACHESERVICE
73     NS_DECL_NSICACHESERVICEINTERNAL
74     NS_DECL_NSIMEMORYREPORTER
75 
76     nsCacheService();
77 
78     // Define a Create method to be used with a factory:
79     static nsresult
80     Create(nsISupports* outer, const nsIID& iid, void* *result);
81 
82 
83     /**
84      * Methods called by nsCacheSession
85      */
86     static nsresult  OpenCacheEntry(nsCacheSession *           session,
87                                     const nsACString &         key,
88                                     nsCacheAccessMode          accessRequested,
89                                     bool                       blockingMode,
90                                     nsICacheListener *         listener,
91                                     nsICacheEntryDescriptor ** result);
92 
93     static nsresult  EvictEntriesForSession(nsCacheSession *   session);
94 
95     static nsresult  IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
96                                                bool *              result);
97 
98     static nsresult  DoomEntry(nsCacheSession   *session,
99                                const nsACString &key,
100                                nsICacheListener *listener);
101 
102     /**
103      * Methods called by nsCacheEntryDescriptor
104      */
105 
106     static void      CloseDescriptor(nsCacheEntryDescriptor * descriptor);
107 
108     static nsresult  GetFileForEntry(nsCacheEntry *         entry,
109                                      nsIFile **             result);
110 
111     static nsresult  OpenInputStreamForEntry(nsCacheEntry *     entry,
112                                              nsCacheAccessMode  mode,
113                                              uint32_t           offset,
114                                              nsIInputStream **  result);
115 
116     static nsresult  OpenOutputStreamForEntry(nsCacheEntry *     entry,
117                                               nsCacheAccessMode  mode,
118                                               uint32_t           offset,
119                                               nsIOutputStream ** result);
120 
121     static nsresult  OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize);
122 
123     static nsresult  SetCacheElement(nsCacheEntry * entry, nsISupports * element);
124 
125     static nsresult  ValidateEntry(nsCacheEntry * entry);
126 
127     static int32_t   CacheCompressionLevel();
128 
129     static bool      GetClearingEntries();
130 
131     static void      GetCacheBaseDirectoty(nsIFile ** result);
132     static void      GetDiskCacheDirectory(nsIFile ** result);
133     static void      GetAppCacheDirectory(nsIFile ** result);
134 
135     /**
136      * Methods called by any cache classes
137      */
138 
139     static
GlobalInstance()140     nsCacheService * GlobalInstance()   { return gService; }
141 
142     static nsresult  DoomEntry(nsCacheEntry * entry);
143 
144     static bool      IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy policy);
145 
146     /**
147      * Called by disk cache to notify us to use the new max smart size
148      */
149     static void      MarkStartingFresh();
150 
151     /**
152      * Methods called by nsApplicationCacheService
153      */
154 
155     nsresult GetOfflineDevice(nsOfflineCacheDevice ** aDevice);
156 
157     /**
158      * Creates an offline cache device that works over a specific profile directory.
159      * A tool to preload offline cache for profiles different from the current
160      * application's profile directory.
161      */
162     nsresult GetCustomOfflineDevice(nsIFile *aProfileDir,
163                                     int32_t aQuota,
164                                     nsOfflineCacheDevice **aDevice);
165 
166     // This method may be called to release an object while the cache service
167     // lock is being held.  If a non-null target is specified and the target
168     // does not correspond to the current thread, then the release will be
169     // proxied to the specified target.  Otherwise, the object will be added to
170     // the list of objects to be released when the cache service is unlocked.
171     static void      ReleaseObject_Locked(nsISupports *    object,
172                                           nsIEventTarget * target = nullptr);
173 
174     static nsresult DispatchToCacheIOThread(nsIRunnable* event);
175 
176     // Calling this method will block the calling thread until all pending
177     // events on the cache-io thread has finished. The calling thread must
178     // hold the cache-lock
179     static nsresult SyncWithCacheIOThread();
180 
181 
182     /**
183      * Methods called by nsCacheProfilePrefObserver
184      */
185     static void      OnProfileShutdown();
186     static void      OnProfileChanged();
187 
188     static void      SetDiskCacheEnabled(bool    enabled);
189     // Sets the disk cache capacity (in kilobytes)
190     static void      SetDiskCacheCapacity(int32_t  capacity);
191     // Set max size for a disk-cache entry (in KB). -1 disables limit up to
192     // 1/8th of disk cache size
193     static void      SetDiskCacheMaxEntrySize(int32_t  maxSize);
194     // Set max size for a memory-cache entry (in kilobytes). -1 disables
195     // limit up to 90% of memory cache size
196     static void      SetMemoryCacheMaxEntrySize(int32_t  maxSize);
197 
198     static void      SetOfflineCacheEnabled(bool    enabled);
199     // Sets the offline cache capacity (in kilobytes)
200     static void      SetOfflineCacheCapacity(int32_t  capacity);
201 
202     static void      SetMemoryCache();
203 
204     static void      SetCacheCompressionLevel(int32_t level);
205 
206     // Starts smart cache size computation if disk device is available
207     static nsresult  SetDiskSmartSize();
208 
209     static void      MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
210                                            nsIFile *aNewCacheDir,
211                                            const char *aCacheSubdir);
212 
213     nsresult         Init();
214     void             Shutdown();
215 
IsInitialized()216     static bool      IsInitialized()
217     {
218       if (!gService) {
219           return false;
220       }
221       return gService->mInitialized;
222     }
223 
AssertOwnsLock()224     static void      AssertOwnsLock()
225     { gService->mLock.AssertCurrentThreadOwns(); }
226 
227     static void      LeavePrivateBrowsing();
228     bool             IsDoomListEmpty();
229 
230     typedef bool (*DoomCheckFn)(nsCacheEntry* entry);
231 
232     // Accessors to the disabled functionality
233     nsresult CreateSessionInternal(const char *          clientID,
234                                    nsCacheStoragePolicy  storagePolicy,
235                                    bool                  streamBased,
236                                    nsICacheSession     **result);
237     nsresult VisitEntriesInternal(nsICacheVisitor *visitor);
238     nsresult EvictEntriesInternal(nsCacheStoragePolicy storagePolicy);
239 
240 private:
241     friend class nsCacheServiceAutoLock;
242     friend class nsOfflineCacheDevice;
243     friend class nsProcessRequestEvent;
244     friend class nsSetSmartSizeEvent;
245     friend class nsBlockOnCacheThreadEvent;
246     friend class nsSetDiskSmartSizeCallback;
247     friend class nsDoomEvent;
248     friend class nsDisableOldMaxSmartSizePrefEvent;
249     friend class nsDiskCacheMap;
250     friend class nsAsyncDoomEvent;
251     friend class nsCacheEntryDescriptor;
252 
253     /**
254      * Internal Methods
255      */
256 
257     static void      Lock();
258     static void      Lock(::mozilla::Telemetry::ID mainThreadLockerID);
259     static void      Unlock();
260     void             LockAcquired();
261     void             LockReleased();
262 
263     nsresult         CreateDiskDevice();
264     nsresult         CreateOfflineDevice();
265     nsresult         CreateCustomOfflineDevice(nsIFile *aProfileDir,
266                                                int32_t aQuota,
267                                                nsOfflineCacheDevice **aDevice);
268     nsresult         CreateMemoryDevice();
269 
270     nsresult         RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice);
271 
272     nsresult         CreateRequest(nsCacheSession *   session,
273                                    const nsACString & clientKey,
274                                    nsCacheAccessMode  accessRequested,
275                                    bool               blockingMode,
276                                    nsICacheListener * listener,
277                                    nsCacheRequest **  request);
278 
279     nsresult         DoomEntry_Internal(nsCacheEntry * entry,
280                                         bool doProcessPendingRequests);
281 
282     nsresult         EvictEntriesForClient(const char *          clientID,
283                                            nsCacheStoragePolicy  storagePolicy);
284 
285     // Notifies request listener asynchronously on the request's thread, and
286     // releases the descriptor on the request's thread.  If this method fails,
287     // the descriptor is not released.
288     nsresult         NotifyListener(nsCacheRequest *          request,
289                                     nsICacheEntryDescriptor * descriptor,
290                                     nsCacheAccessMode         accessGranted,
291                                     nsresult                  error);
292 
293     nsresult         ActivateEntry(nsCacheRequest * request,
294                                    nsCacheEntry ** entry,
295                                    nsCacheEntry ** doomedEntry);
296 
297     nsCacheDevice *  EnsureEntryHasDevice(nsCacheEntry * entry);
298 
299     nsCacheEntry *   SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision);
300 
301     void             DeactivateEntry(nsCacheEntry * entry);
302 
303     nsresult         ProcessRequest(nsCacheRequest *           request,
304                                     bool                       calledFromOpenCacheEntry,
305                                     nsICacheEntryDescriptor ** result);
306 
307     nsresult         ProcessPendingRequests(nsCacheEntry * entry);
308 
309     void             ClearDoomList(void);
310     void             DoomActiveEntries(DoomCheckFn check);
311     void             CloseAllStreams();
312     void             FireClearNetworkCacheStoredAnywhereNotification();
313 
314     void LogCacheStatistics();
315 
316     nsresult         SetDiskSmartSize_Locked();
317 
318     /**
319      *  Data Members
320      */
321 
322     static nsCacheService *         gService;  // there can be only one...
323 
324     nsCOMPtr<mozIStorageService>    mStorageService;
325 
326     nsCacheProfilePrefObserver *    mObserver;
327 
328     mozilla::Mutex                  mLock;
329     mozilla::CondVar                mCondVar;
330     bool                            mNotified;
331 
332     mozilla::Mutex                  mTimeStampLock;
333     mozilla::TimeStamp              mLockAcquiredTimeStamp;
334 
335     nsCOMPtr<nsIThread>             mCacheIOThread;
336 
337     nsTArray<nsISupports*>          mDoomedObjects;
338     nsCOMPtr<nsITimer>              mSmartSizeTimer;
339 
340     bool                            mInitialized;
341     bool                            mClearingEntries;
342 
343     bool                            mEnableMemoryDevice;
344     bool                            mEnableDiskDevice;
345     bool                            mEnableOfflineDevice;
346 
347     nsMemoryCacheDevice *           mMemoryDevice;
348     nsDiskCacheDevice *             mDiskDevice;
349     nsOfflineCacheDevice *          mOfflineDevice;
350 
351     nsRefPtrHashtable<nsStringHashKey, nsOfflineCacheDevice> mCustomOfflineDevices;
352 
353     nsCacheEntryHashTable           mActiveEntries;
354     PRCList                         mDoomedEntries;
355 
356     // stats
357 
358     uint32_t                        mTotalEntries;
359     uint32_t                        mCacheHits;
360     uint32_t                        mCacheMisses;
361     uint32_t                        mMaxKeyLength;
362     uint32_t                        mMaxDataSize;
363     uint32_t                        mMaxMetaSize;
364 
365     // Unexpected error totals
366     uint32_t                        mDeactivateFailures;
367     uint32_t                        mDeactivatedUnboundEntries;
368 };
369 
370 /******************************************************************************
371  *  nsCacheServiceAutoLock
372  ******************************************************************************/
373 
374 #define LOCK_TELEM(x) \
375   (::mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_##x)
376 
377 // Instantiate this class to acquire the cache service lock for a particular
378 // execution scope.
379 class nsCacheServiceAutoLock {
380 public:
nsCacheServiceAutoLock()381     nsCacheServiceAutoLock() {
382         nsCacheService::Lock();
383     }
nsCacheServiceAutoLock(mozilla::Telemetry::ID mainThreadLockerID)384     explicit nsCacheServiceAutoLock(mozilla::Telemetry::ID mainThreadLockerID) {
385         nsCacheService::Lock(mainThreadLockerID);
386     }
~nsCacheServiceAutoLock()387     ~nsCacheServiceAutoLock() {
388         nsCacheService::Unlock();
389     }
390 };
391 
392 #endif // _nsCacheService_h_
393