Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 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 mozilla_DataStorage_h
8 : #define mozilla_DataStorage_h
9 :
10 : #include "mozilla/Atomics.h"
11 : #include "mozilla/MemoryReporting.h"
12 : #include "mozilla/Monitor.h"
13 : #include "mozilla/Mutex.h"
14 : #include "mozilla/StaticPtr.h"
15 : #include "nsCOMPtr.h"
16 : #include "nsDataHashtable.h"
17 : #include "nsIObserver.h"
18 : #include "nsIThread.h"
19 : #include "nsITimer.h"
20 : #include "nsRefPtrHashtable.h"
21 : #include "nsString.h"
22 :
23 : class psm_DataStorageTest;
24 :
25 : namespace mozilla {
26 : class DataStorageMemoryReporter;
27 :
28 : namespace dom {
29 : class ContentChild;
30 : class DataStorageEntry;
31 : class DataStorageItem;
32 : }
33 :
34 : /**
35 : * DataStorage is a threadsafe, generic, narrow string-based hash map that
36 : * persists data on disk and additionally handles temporary and private data.
37 : * However, if used in a context where there is no profile directory, data
38 : * will not be persisted.
39 : *
40 : * Its lifecycle is as follows:
41 : * - Allocate with a filename (this is or will eventually be a file in the
42 : * profile directory, if the profile exists).
43 : * - Call Init() from the main thread. This spins off an asynchronous read
44 : * of the backing file.
45 : * - Eventually observers of the topic "data-storage-ready" will be notified
46 : * with the backing filename as the data in the notification when this
47 : * has completed.
48 : * - Should the profile directory not be available, (e.g. in xpcshell),
49 : * DataStorage will not initially read any persistent data. The
50 : * "data-storage-ready" event will still be emitted. This follows semantics
51 : * similar to the permission manager and allows tests that test
52 : * unrelated components to proceed without a profile.
53 : * - When any persistent data changes, a timer is initialized that will
54 : * eventually asynchronously write all persistent data to the backing file.
55 : * When this happens, observers will be notified with the topic
56 : * "data-storage-written" and the backing filename as the data.
57 : * It is possible to receive a "data-storage-written" event while there exist
58 : * pending persistent data changes. However, those changes will cause the
59 : * timer to be reinitialized and another "data-storage-written" event will
60 : * be sent.
61 : * - When DataStorage observes the topic "profile-before-change" in
62 : * anticipation of shutdown, all persistent data is synchronously written to
63 : * the backing file. The worker thread responsible for these writes is then
64 : * disabled to prevent further writes to that file (the delayed-write timer
65 : * is cancelled when this happens).
66 : * - For testing purposes, the preference "test.datastorage.write_timer_ms" can
67 : * be set to cause the asynchronous writing of data to happen more quickly.
68 : * - To prevent unbounded memory and disk use, the number of entries in each
69 : * table is limited to 1024. Evictions are handled in by a modified LRU scheme
70 : * (see implementation comments).
71 : * - NB: Instances of DataStorage have long lifetimes because they are strong
72 : * observers of events and won't go away until the observer service does.
73 : *
74 : * For each key/value:
75 : * - The key must be a non-empty string containing no instances of '\t' or '\n'
76 : * (this is a limitation of how the data is stored and will be addressed in
77 : * the future).
78 : * - The key must have a length no more than 256.
79 : * - The value must not contain '\n' and must have a length no more than 1024.
80 : * (the length limits are to prevent unbounded disk and memory usage)
81 : */
82 :
83 : /**
84 : * Data that is DataStorage_Persistent is saved on disk. DataStorage_Temporary
85 : * and DataStorage_Private are not saved. DataStorage_Private is meant to
86 : * only be set and accessed from private contexts. It will be cleared upon
87 : * observing the event "last-pb-context-exited".
88 : */
89 : enum DataStorageType {
90 : DataStorage_Persistent,
91 : DataStorage_Temporary,
92 : DataStorage_Private
93 : };
94 :
95 : enum class DataStorageClass {
96 : #define DATA_STORAGE(_) _,
97 : #include "mozilla/DataStorageList.h"
98 : #undef DATA_STORAGE
99 : };
100 :
101 : class DataStorage : public nsIObserver
102 : {
103 : typedef dom::DataStorageItem DataStorageItem;
104 :
105 : public:
106 : NS_DECL_THREADSAFE_ISUPPORTS
107 : NS_DECL_NSIOBSERVER
108 :
109 : // If there is a profile directory, there is or will eventually be a file
110 : // by the name specified by aFilename there.
111 : static already_AddRefed<DataStorage> Get(DataStorageClass aFilename);
112 : static already_AddRefed<DataStorage> GetIfExists(DataStorageClass aFilename);
113 :
114 : // Initializes the DataStorage. Must be called before using.
115 : // aDataWillPersist returns whether or not data can be persistently saved.
116 : // aItems is used in the content process to initialize a cache of the items
117 : // received from the parent process over IPC. nullptr must be passed for the
118 : // parent process.
119 : nsresult Init(/*out*/bool& aDataWillPersist,
120 : const InfallibleTArray<mozilla::dom::DataStorageItem>*
121 : aItems = nullptr);
122 : // Given a key and a type of data, returns a value. Returns an empty string if
123 : // the key is not present for that type of data. If Get is called before the
124 : // "data-storage-ready" event is observed, it will block. NB: It is not
125 : // currently possible to differentiate between missing data and data that is
126 : // the empty string.
127 : nsCString Get(const nsCString& aKey, DataStorageType aType);
128 : // Give a key, value, and type of data, adds an entry as appropriate.
129 : // Updates existing entries.
130 : nsresult Put(const nsCString& aKey, const nsCString& aValue,
131 : DataStorageType aType);
132 : // Given a key and type of data, removes an entry if present.
133 : void Remove(const nsCString& aKey, DataStorageType aType);
134 : // Removes all entries of all types of data.
135 : nsresult Clear();
136 :
137 : // Read all file names that we know about.
138 : static void GetAllFileNames(nsTArray<nsString>& aItems);
139 :
140 : // Read all child process data that we know about.
141 : static void GetAllChildProcessData(nsTArray<mozilla::dom::DataStorageEntry>& aEntries);
142 :
143 : // Read all of the data items.
144 : void GetAll(InfallibleTArray<DataStorageItem>* aItems);
145 :
146 : // Set the cached copy of our DataStorage entries in the content process.
147 : static void SetCachedStorageEntries(const InfallibleTArray<mozilla::dom::DataStorageEntry>& aEntries);
148 :
149 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
150 :
151 : private:
152 : explicit DataStorage(const nsString& aFilename);
153 : virtual ~DataStorage();
154 :
155 : static already_AddRefed<DataStorage> GetFromRawFileName(const nsString& aFilename);
156 :
157 : friend class ::psm_DataStorageTest;
158 : friend class mozilla::dom::ContentChild;
159 : friend class mozilla::DataStorageMemoryReporter;
160 :
161 : class Writer;
162 : class Reader;
163 :
164 22 : class Entry
165 : {
166 : public:
167 : Entry();
168 : bool UpdateScore();
169 :
170 : uint32_t mScore;
171 : int32_t mLastAccessed; // the last accessed time in days since the epoch
172 : nsCString mValue;
173 : };
174 :
175 : // Utility class for scanning tables for an entry to evict.
176 0 : class KeyAndEntry
177 : {
178 : public:
179 : nsCString mKey;
180 : Entry mEntry;
181 : };
182 :
183 : typedef nsDataHashtable<nsCStringHashKey, Entry> DataStorageTable;
184 : typedef nsRefPtrHashtable<nsStringHashKey, DataStorage> DataStorages;
185 :
186 : void WaitForReady();
187 : nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
188 : nsresult AsyncReadData(bool& aHaveProfileDir,
189 : const MutexAutoLock& aProofOfLock);
190 : nsresult AsyncSetTimer(const MutexAutoLock& aProofOfLock);
191 : nsresult DispatchShutdownTimer(const MutexAutoLock& aProofOfLock);
192 :
193 : static nsresult ValidateKeyAndValue(const nsCString& aKey,
194 : const nsCString& aValue);
195 : static void TimerCallback(nsITimer* aTimer, void* aClosure);
196 : void SetTimer();
197 : void ShutdownTimer();
198 : void NotifyObservers(const char* aTopic);
199 :
200 : bool GetInternal(const nsCString& aKey, Entry* aEntry, DataStorageType aType,
201 : const MutexAutoLock& aProofOfLock);
202 : nsresult PutInternal(const nsCString& aKey, Entry& aEntry,
203 : DataStorageType aType,
204 : const MutexAutoLock& aProofOfLock);
205 : void MaybeEvictOneEntry(DataStorageType aType,
206 : const MutexAutoLock& aProofOfLock);
207 : DataStorageTable& GetTableForType(DataStorageType aType,
208 : const MutexAutoLock& aProofOfLock);
209 :
210 : void ReadAllFromTable(DataStorageType aType,
211 : InfallibleTArray<DataStorageItem>* aItems,
212 : const MutexAutoLock& aProofOfLock);
213 :
214 : Mutex mMutex; // This mutex protects access to the following members:
215 : DataStorageTable mPersistentDataTable;
216 : DataStorageTable mTemporaryDataTable;
217 : DataStorageTable mPrivateDataTable;
218 : nsCOMPtr<nsIThread> mWorkerThread;
219 : nsCOMPtr<nsIFile> mBackingFile;
220 : nsCOMPtr<nsITimer> mTimer; // All uses after init must be on the worker thread
221 : uint32_t mTimerDelay; // in milliseconds
222 : bool mPendingWrite; // true if a write is needed but hasn't been dispatched
223 : bool mShuttingDown;
224 : // (End list of members protected by mMutex)
225 :
226 : mozilla::Atomic<bool> mInitCalled; // Indicates that Init() has been called.
227 :
228 : Monitor mReadyMonitor; // Do not acquire this at the same time as mMutex.
229 : bool mReady; // Indicates that saved data has been read and Get can proceed.
230 :
231 : const nsString mFilename;
232 :
233 : static StaticAutoPtr<DataStorages> sDataStorages;
234 : };
235 :
236 : } // namespace mozilla
237 :
238 : #endif // mozilla_DataStorage_h
|