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