summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/DataStorage.h
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/DataStorage.h')
-rw-r--r--security/manager/ssl/DataStorage.h202
1 files changed, 202 insertions, 0 deletions
diff --git a/security/manager/ssl/DataStorage.h b/security/manager/ssl/DataStorage.h
new file mode 100644
index 000000000..5e3182a40
--- /dev/null
+++ b/security/manager/ssl/DataStorage.h
@@ -0,0 +1,202 @@
+/* -*- 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 mozilla_DataStorage_h
+#define mozilla_DataStorage_h
+
+#include "mozilla/Monitor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsIObserver.h"
+#include "nsIThread.h"
+#include "nsITimer.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+namespace dom {
+class DataStorageItem;
+}
+
+/**
+ * DataStorage is a threadsafe, generic, narrow string-based hash map that
+ * persists data on disk and additionally handles temporary and private data.
+ * However, if used in a context where there is no profile directory, data
+ * will not be persisted.
+ *
+ * Its lifecycle is as follows:
+ * - Allocate with a filename (this is or will eventually be a file in the
+ * profile directory, if the profile exists).
+ * - Call Init() from the main thread. This spins off an asynchronous read
+ * of the backing file.
+ * - Eventually observers of the topic "data-storage-ready" will be notified
+ * with the backing filename as the data in the notification when this
+ * has completed.
+ * - Should the profile directory not be available, (e.g. in xpcshell),
+ * DataStorage will not initially read any persistent data. The
+ * "data-storage-ready" event will still be emitted. This follows semantics
+ * similar to the permission manager and allows tests that test
+ * unrelated components to proceed without a profile.
+ * - When any persistent data changes, a timer is initialized that will
+ * eventually asynchronously write all persistent data to the backing file.
+ * When this happens, observers will be notified with the topic
+ * "data-storage-written" and the backing filename as the data.
+ * It is possible to receive a "data-storage-written" event while there exist
+ * pending persistent data changes. However, those changes will cause the
+ * timer to be reinitialized and another "data-storage-written" event will
+ * be sent.
+ * - When DataStorage observes the topic "profile-before-change" in
+ * anticipation of shutdown, all persistent data is synchronously written to
+ * the backing file. The worker thread responsible for these writes is then
+ * disabled to prevent further writes to that file (the delayed-write timer
+ * is cancelled when this happens).
+ * - For testing purposes, the preference "test.datastorage.write_timer_ms" can
+ * be set to cause the asynchronous writing of data to happen more quickly.
+ * - To prevent unbounded memory and disk use, the number of entries in each
+ * table is limited to 1024. Evictions are handled in by a modified LRU scheme
+ * (see implementation comments).
+ * - NB: Instances of DataStorage have long lifetimes because they are strong
+ * observers of events and won't go away until the observer service does.
+ *
+ * For each key/value:
+ * - The key must be a non-empty string containing no instances of '\t' or '\n'
+ * (this is a limitation of how the data is stored and will be addressed in
+ * the future).
+ * - The key must have a length no more than 256.
+ * - The value must not contain '\n' and must have a length no more than 1024.
+ * (the length limits are to prevent unbounded disk and memory usage)
+ */
+
+/**
+ * Data that is DataStorage_Persistent is saved on disk. DataStorage_Temporary
+ * and DataStorage_Private are not saved. DataStorage_Private is meant to
+ * only be set and accessed from private contexts. It will be cleared upon
+ * observing the event "last-pb-context-exited".
+ */
+enum DataStorageType {
+ DataStorage_Persistent,
+ DataStorage_Temporary,
+ DataStorage_Private
+};
+
+class DataStorage : public nsIObserver
+{
+ typedef dom::DataStorageItem DataStorageItem;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ // If there is a profile directory, there is or will eventually be a file
+ // by the name specified by aFilename there.
+ static already_AddRefed<DataStorage> Get(const nsString& aFilename);
+ static already_AddRefed<DataStorage> GetIfExists(const nsString& aFilename);
+
+ // Initializes the DataStorage. Must be called before using.
+ // aDataWillPersist returns whether or not data can be persistently saved.
+ nsresult Init(/*out*/bool& aDataWillPersist);
+ // Given a key and a type of data, returns a value. Returns an empty string if
+ // the key is not present for that type of data. If Get is called before the
+ // "data-storage-ready" event is observed, it will block. NB: It is not
+ // currently possible to differentiate between missing data and data that is
+ // the empty string.
+ nsCString Get(const nsCString& aKey, DataStorageType aType);
+ // Give a key, value, and type of data, adds an entry as appropriate.
+ // Updates existing entries.
+ nsresult Put(const nsCString& aKey, const nsCString& aValue,
+ DataStorageType aType);
+ // Given a key and type of data, removes an entry if present.
+ void Remove(const nsCString& aKey, DataStorageType aType);
+ // Removes all entries of all types of data.
+ nsresult Clear();
+
+ // Read all of the data items.
+ void GetAll(InfallibleTArray<DataStorageItem>* aItems);
+
+private:
+ explicit DataStorage(const nsString& aFilename);
+ virtual ~DataStorage();
+
+ class Writer;
+ class Reader;
+
+ class Entry
+ {
+ public:
+ Entry();
+ bool UpdateScore();
+
+ uint32_t mScore;
+ int32_t mLastAccessed; // the last accessed time in days since the epoch
+ nsCString mValue;
+ };
+
+ // Utility class for scanning tables for an entry to evict.
+ class KeyAndEntry
+ {
+ public:
+ nsCString mKey;
+ Entry mEntry;
+ };
+
+ typedef nsDataHashtable<nsCStringHashKey, Entry> DataStorageTable;
+ typedef nsRefPtrHashtable<nsStringHashKey, DataStorage> DataStorages;
+
+ void WaitForReady();
+ nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
+ nsresult AsyncReadData(bool& aHaveProfileDir,
+ const MutexAutoLock& aProofOfLock);
+ nsresult AsyncSetTimer(const MutexAutoLock& aProofOfLock);
+ nsresult DispatchShutdownTimer(const MutexAutoLock& aProofOfLock);
+
+ static nsresult ValidateKeyAndValue(const nsCString& aKey,
+ const nsCString& aValue);
+ static void TimerCallback(nsITimer* aTimer, void* aClosure);
+ void SetTimer();
+ void ShutdownTimer();
+ void NotifyObservers(const char* aTopic);
+
+ bool GetInternal(const nsCString& aKey, Entry* aEntry, DataStorageType aType,
+ const MutexAutoLock& aProofOfLock);
+ nsresult PutInternal(const nsCString& aKey, Entry& aEntry,
+ DataStorageType aType,
+ const MutexAutoLock& aProofOfLock);
+ void MaybeEvictOneEntry(DataStorageType aType,
+ const MutexAutoLock& aProofOfLock);
+ DataStorageTable& GetTableForType(DataStorageType aType,
+ const MutexAutoLock& aProofOfLock);
+
+ void ReadAllFromTable(DataStorageType aType,
+ InfallibleTArray<DataStorageItem>* aItems,
+ const MutexAutoLock& aProofOfLock);
+
+ Mutex mMutex; // This mutex protects access to the following members:
+ DataStorageTable mPersistentDataTable;
+ DataStorageTable mTemporaryDataTable;
+ DataStorageTable mPrivateDataTable;
+ nsCOMPtr<nsIThread> mWorkerThread;
+ nsCOMPtr<nsIFile> mBackingFile;
+ nsCOMPtr<nsITimer> mTimer; // All uses after init must be on the worker thread
+ uint32_t mTimerDelay; // in milliseconds
+ bool mPendingWrite; // true if a write is needed but hasn't been dispatched
+ bool mShuttingDown;
+ bool mInitCalled; // Indicates that Init() has been called.
+ // (End list of members protected by mMutex)
+
+ Monitor mReadyMonitor; // Do not acquire this at the same time as mMutex.
+ bool mReady; // Indicates that saved data has been read and Get can proceed.
+
+ const nsString mFilename;
+
+ static StaticAutoPtr<DataStorages> sDataStorages;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_DataStorage_h