1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #ifndef CacheFile__h__
6 #define CacheFile__h__
7 
8 #include "CacheFileChunk.h"
9 #include "CacheFileIOManager.h"
10 #include "CacheFileMetadata.h"
11 #include "nsRefPtrHashtable.h"
12 #include "nsClassHashtable.h"
13 #include "mozilla/Mutex.h"
14 
15 class nsIInputStream;
16 class nsIOutputStream;
17 class nsICacheEntryMetaDataVisitor;
18 
19 namespace mozilla {
20 namespace net {
21 
22 class CacheFileInputStream;
23 class CacheFileOutputStream;
24 class CacheOutputCloseListener;
25 class MetadataWriteTimer;
26 
27 #define CACHEFILELISTENER_IID \
28 { /* 95e7f284-84ba-48f9-b1fc-3a7336b4c33c */       \
29   0x95e7f284,                                      \
30   0x84ba,                                          \
31   0x48f9,                                          \
32   {0xb1, 0xfc, 0x3a, 0x73, 0x36, 0xb4, 0xc3, 0x3c} \
33 }
34 
35 class CacheFileListener : public nsISupports
36 {
37 public:
38   NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILELISTENER_IID)
39 
40   NS_IMETHOD OnFileReady(nsresult aResult, bool aIsNew) = 0;
41   NS_IMETHOD OnFileDoomed(nsresult aResult) = 0;
42 };
43 
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileListener,CACHEFILELISTENER_IID)44 NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileListener, CACHEFILELISTENER_IID)
45 
46 
47 class CacheFile final : public CacheFileChunkListener
48                       , public CacheFileIOListener
49                       , public CacheFileMetadataListener
50 {
51 public:
52   NS_DECL_THREADSAFE_ISUPPORTS
53 
54   CacheFile();
55 
56   nsresult Init(const nsACString &aKey,
57                 bool aCreateNew,
58                 bool aMemoryOnly,
59                 bool aSkipSizeCheck,
60                 bool aPriority,
61                 bool aPinned,
62                 CacheFileListener *aCallback);
63 
64   NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override;
65   NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) override;
66   NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
67                               CacheFileChunk *aChunk) override;
68   NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk) override;
69 
70   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
71   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
72                            nsresult aResult) override;
73   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override;
74   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
75   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override;
76   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override;
77   virtual bool IsKilled() override;
78 
79   NS_IMETHOD OnMetadataRead(nsresult aResult) override;
80   NS_IMETHOD OnMetadataWritten(nsresult aResult) override;
81 
82   NS_IMETHOD OpenInputStream(nsICacheEntry *aCacheEntryHandle, nsIInputStream **_retval);
83   NS_IMETHOD OpenAlternativeInputStream(nsICacheEntry *aCacheEntryHandle,
84                                         const char *aAltDataType, nsIInputStream **_retval);
85   NS_IMETHOD OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval);
86   NS_IMETHOD OpenAlternativeOutputStream(CacheOutputCloseListener *aCloseListener,
87                                          const char *aAltDataType, nsIOutputStream **_retval);
88   NS_IMETHOD SetMemoryOnly();
89   NS_IMETHOD Doom(CacheFileListener *aCallback);
90 
91   void Kill() { mKill = true; }
92   nsresult   ThrowMemoryCachedData();
93 
94   nsresult GetAltDataSize(int64_t *aSize);
95 
96   // metadata forwarders
97   nsresult GetElement(const char *aKey, char **_retval);
98   nsresult SetElement(const char *aKey, const char *aValue);
99   nsresult VisitMetaData(nsICacheEntryMetaDataVisitor *aVisitor);
100   nsresult ElementsSize(uint32_t *_retval);
101   nsresult SetExpirationTime(uint32_t aExpirationTime);
102   nsresult GetExpirationTime(uint32_t *_retval);
103   nsresult SetFrecency(uint32_t aFrecency);
104   nsresult GetFrecency(uint32_t *_retval);
105   nsresult GetLastModified(uint32_t *_retval);
106   nsresult GetLastFetched(uint32_t *_retval);
107   nsresult GetFetchCount(uint32_t *_retval);
108   nsresult GetDiskStorageSizeInKB(uint32_t *aDiskStorageSize);
109   // Called by upper layers to indicated the entry has been fetched,
110   // i.e. delivered to the consumer.
111   nsresult OnFetched();
112 
113   bool DataSize(int64_t* aSize);
114   void Key(nsACString& aKey) { aKey = mKey; }
115   bool IsDoomed();
116   bool IsPinned() const { return mPinned; }
117   bool IsWriteInProgress();
118 
119   // Memory reporting
120   size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
121   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
122 
123 private:
124   friend class CacheFileIOManager;
125   friend class CacheFileChunk;
126   friend class CacheFileInputStream;
127   friend class CacheFileOutputStream;
128   friend class CacheFileAutoLock;
129   friend class MetadataWriteTimer;
130 
131   virtual ~CacheFile();
132 
133   void     Lock();
134   void     Unlock();
135   void     AssertOwnsLock() const;
136   void     ReleaseOutsideLock(RefPtr<nsISupports> aObject);
137 
138   enum ECallerType {
139     READER    = 0,
140     WRITER    = 1,
141     PRELOADER = 2
142   };
143 
144   nsresult DoomLocked(CacheFileListener *aCallback);
145 
146   nsresult GetChunkLocked(uint32_t aIndex, ECallerType aCaller,
147                           CacheFileChunkListener *aCallback,
148                           CacheFileChunk **_retval);
149 
150   void     PreloadChunks(uint32_t aIndex);
151   bool     ShouldCacheChunk(uint32_t aIndex);
152   bool     MustKeepCachedChunk(uint32_t aIndex);
153 
154   nsresult DeactivateChunk(CacheFileChunk *aChunk);
155   void     RemoveChunkInternal(CacheFileChunk *aChunk, bool aCacheChunk);
156 
157   bool     OutputStreamExists(bool aAlternativeData);
158   // Returns number of bytes that are available and can be read by input stream
159   // without waiting for the data. The amount is counted from the start of
160   // aIndex chunk and it is guaranteed that this data won't be released by
161   // CleanUpCachedChunks().
162   int64_t  BytesFromChunk(uint32_t aIndex, bool aAlternativeData);
163   nsresult Truncate(int64_t aOffset);
164 
165   nsresult RemoveInput(CacheFileInputStream *aInput, nsresult aStatus);
166   nsresult RemoveOutput(CacheFileOutputStream *aOutput, nsresult aStatus);
167   nsresult NotifyChunkListener(CacheFileChunkListener *aCallback,
168                                nsIEventTarget *aTarget,
169                                nsresult aResult,
170                                uint32_t aChunkIdx,
171                                CacheFileChunk *aChunk);
172   nsresult QueueChunkListener(uint32_t aIndex,
173                               CacheFileChunkListener *aCallback);
174   nsresult NotifyChunkListeners(uint32_t aIndex, nsresult aResult,
175                                 CacheFileChunk *aChunk);
176   bool     HaveChunkListeners(uint32_t aIndex);
177   void     NotifyListenersAboutOutputRemoval();
178 
179   bool IsDirty();
180   void WriteMetadataIfNeeded();
181   void WriteMetadataIfNeededLocked(bool aFireAndForget = false);
182   void PostWriteTimer();
183 
184   void CleanUpCachedChunks();
185 
186   nsresult PadChunkWithZeroes(uint32_t aChunkIdx);
187 
188   void SetError(nsresult aStatus);
189 
190   nsresult InitIndexEntry();
191 
192   mozilla::Mutex mLock;
193   bool           mOpeningFile;
194   bool           mReady;
195   bool           mMemoryOnly;
196   bool           mSkipSizeCheck;
197   bool           mOpenAsMemoryOnly;
198   bool           mPinned;
199   bool           mPriority;
200   bool           mDataAccessed;
201   bool           mDataIsDirty;
202   bool           mWritingMetadata;
203   bool           mPreloadWithoutInputStreams;
204   uint32_t       mPreloadChunkCount;
205   nsresult       mStatus;
206   int64_t        mDataSize; // Size of the whole data including eventual
207                             // alternative data represenation.
208   int64_t        mAltDataOffset; // If there is alternative data present, it
209                                  // contains size of the original data, i.e.
210                                  // offset where alternative data starts.
211                                  // Otherwise it is -1.
212   nsCString      mKey;
213 
214   RefPtr<CacheFileHandle>      mHandle;
215   RefPtr<CacheFileMetadata>    mMetadata;
216   nsCOMPtr<CacheFileListener>  mListener;
217   nsCOMPtr<CacheFileIOListener>   mDoomAfterOpenListener;
218   Atomic<bool, Relaxed>        mKill;
219 
220   nsRefPtrHashtable<nsUint32HashKey, CacheFileChunk> mChunks;
221   nsClassHashtable<nsUint32HashKey, ChunkListeners> mChunkListeners;
222   nsRefPtrHashtable<nsUint32HashKey, CacheFileChunk> mCachedChunks;
223   // We can truncate data only if there is no input/output stream beyond the
224   // truncate position, so only unused chunks can be thrown away. But it can
225   // happen that we need to throw away a chunk that is still in mChunks (i.e.
226   // an active chunk) because deactivation happens with a small delay. We cannot
227   // delete such chunk immediately but we need to ensure that such chunk won't
228   // be returned by GetChunkLocked, so we move this chunk into mDiscardedChunks
229   // and mark it as discarded.
230   nsTArray<RefPtr<CacheFileChunk> > mDiscardedChunks;
231 
232   nsTArray<CacheFileInputStream*> mInputs;
233   CacheFileOutputStream          *mOutput;
234 
235   nsTArray<RefPtr<nsISupports>> mObjsToRelease;
236 };
237 
238 class CacheFileAutoLock {
239 public:
CacheFileAutoLock(CacheFile * aFile)240   explicit CacheFileAutoLock(CacheFile *aFile)
241     : mFile(aFile)
242     , mLocked(true)
243   {
244     mFile->Lock();
245   }
~CacheFileAutoLock()246   ~CacheFileAutoLock()
247   {
248     if (mLocked)
249       mFile->Unlock();
250   }
Lock()251   void Lock()
252   {
253     MOZ_ASSERT(!mLocked);
254     mFile->Lock();
255     mLocked = true;
256   }
Unlock()257   void Unlock()
258   {
259     MOZ_ASSERT(mLocked);
260     mFile->Unlock();
261     mLocked = false;
262   }
263 
264 private:
265   RefPtr<CacheFile> mFile;
266   bool mLocked;
267 };
268 
269 } // namespace net
270 } // namespace mozilla
271 
272 #endif
273