mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1837267 - introduce XPCOM interface nsIDataStorage for DataStorage r=jschanck,necko-reviewers,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D180267
This commit is contained in:
parent
4d8a23d28f
commit
2edebb46f1
@ -201,7 +201,7 @@ void AltSvcMapping::ProcessHeader(
|
||||
}
|
||||
}
|
||||
|
||||
AltSvcMapping::AltSvcMapping(DataStorage* storage, int32_t epoch,
|
||||
AltSvcMapping::AltSvcMapping(nsIDataStorage* storage, int32_t epoch,
|
||||
const nsACString& originScheme,
|
||||
const nsACString& originHost, int32_t originPort,
|
||||
const nsACString& username, bool privateBrowsing,
|
||||
@ -294,8 +294,9 @@ int32_t AltSvcMapping::TTL() { return mExpiresAt - NowInSeconds(); }
|
||||
|
||||
void AltSvcMapping::SyncString(const nsCString& str) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mStorage->Put(HashKey(), str,
|
||||
mPrivate ? DataStorage_Private : DataStorage_Persistent);
|
||||
(void)mStorage->Put(HashKey(), str,
|
||||
mPrivate ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent);
|
||||
}
|
||||
|
||||
void AltSvcMapping::Sync() {
|
||||
@ -316,8 +317,9 @@ void AltSvcMapping::Sync() {
|
||||
return;
|
||||
}
|
||||
|
||||
mStorage->Put(HashKey(), value,
|
||||
mPrivate ? DataStorage_Private : DataStorage_Persistent);
|
||||
(void)mStorage->Put(HashKey(), value,
|
||||
mPrivate ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent);
|
||||
}
|
||||
|
||||
void AltSvcMapping::SetValidated(bool val) {
|
||||
@ -403,7 +405,7 @@ void AltSvcMapping::Serialize(nsCString& out) {
|
||||
// Add code to serialize new members here!
|
||||
}
|
||||
|
||||
AltSvcMapping::AltSvcMapping(DataStorage* storage, int32_t epoch,
|
||||
AltSvcMapping::AltSvcMapping(nsIDataStorage* storage, int32_t epoch,
|
||||
const nsCString& str)
|
||||
: mStorage(storage), mStorageEpoch(epoch) {
|
||||
mValidated = false;
|
||||
@ -869,20 +871,22 @@ void AltSvcCache::EnsureStorageInited() {
|
||||
auto initTask = [&]() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// DataStorage gives synchronous access to a memory based hash table
|
||||
// nsIDataStorage gives synchronous access to a memory based hash table
|
||||
// that is backed by disk where those writes are done asynchronously
|
||||
// on another thread
|
||||
mStorage = DataStorage::Get(DataStorageClass::AlternateServices);
|
||||
if (!mStorage) {
|
||||
nsCOMPtr<nsIDataStorageManager> dataStorageManager(
|
||||
do_GetService("@mozilla.org/security/datastoragemanager;1"));
|
||||
if (!dataStorageManager) {
|
||||
LOG(("AltSvcCache::EnsureStorageInited WARN NO STORAGE MANAGER\n"));
|
||||
return;
|
||||
}
|
||||
nsresult rv = dataStorageManager->Get(
|
||||
nsIDataStorageManager::AlternateServices, getter_AddRefs(mStorage));
|
||||
if (NS_FAILED(rv) || !mStorage) {
|
||||
LOG(("AltSvcCache::EnsureStorageInited WARN NO STORAGE\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(mStorage->Init())) {
|
||||
mStorage = nullptr;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
mStorageEpoch = NowInSeconds();
|
||||
};
|
||||
@ -910,50 +914,70 @@ already_AddRefed<AltSvcMapping> AltSvcCache::LookupMapping(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread() && !mStorage->IsReady()) {
|
||||
LOG(("AltSvcCache::LookupMapping %p skip when storage is not ready\n",
|
||||
this));
|
||||
return nullptr;
|
||||
if (NS_IsMainThread()) {
|
||||
bool isReady;
|
||||
nsresult rv = mStorage->IsReady(&isReady);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("AltSvcCache::LookupMapping %p mStorage->IsReady failed\n", this));
|
||||
return nullptr;
|
||||
}
|
||||
if (!isReady) {
|
||||
LOG(("AltSvcCache::LookupMapping %p skip when storage is not ready\n",
|
||||
this));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCString val(mStorage->Get(
|
||||
key, privateBrowsing ? DataStorage_Private : DataStorage_Persistent));
|
||||
if (val.IsEmpty()) {
|
||||
nsAutoCString val;
|
||||
nsresult rv =
|
||||
mStorage->Get(key,
|
||||
privateBrowsing ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent,
|
||||
val);
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
LOG(("AltSvcCache::LookupMapping %p mStorage->Get failed \n", this));
|
||||
return nullptr;
|
||||
}
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE || val.IsEmpty()) {
|
||||
LOG(("AltSvcCache::LookupMapping %p MISS\n", this));
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<AltSvcMapping> rv = new AltSvcMapping(mStorage, mStorageEpoch, val);
|
||||
if (!rv->Validated() && (rv->StorageEpoch() != mStorageEpoch)) {
|
||||
RefPtr<AltSvcMapping> mapping =
|
||||
new AltSvcMapping(mStorage, mStorageEpoch, val);
|
||||
if (!mapping->Validated() && (mapping->StorageEpoch() != mStorageEpoch)) {
|
||||
// this was an in progress validation abandoned in a different session
|
||||
// rare edge case will not detect session change - that's ok as only impact
|
||||
// will be loss of alt-svc to this origin for this session.
|
||||
LOG(("AltSvcCache::LookupMapping %p invalid hit - MISS\n", this));
|
||||
mStorage->Remove(
|
||||
key, rv->Private() ? DataStorage_Private : DataStorage_Persistent);
|
||||
(void)mStorage->Remove(key, mapping->Private()
|
||||
? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (rv->IsHttp3() &&
|
||||
if (mapping->IsHttp3() &&
|
||||
(!nsHttpHandler::IsHttp3Enabled() ||
|
||||
!gHttpHandler->IsHttp3VersionSupported(rv->NPNToken()) ||
|
||||
gHttpHandler->IsHttp3Excluded(rv->AlternateHost()))) {
|
||||
!gHttpHandler->IsHttp3VersionSupported(mapping->NPNToken()) ||
|
||||
gHttpHandler->IsHttp3Excluded(mapping->AlternateHost()))) {
|
||||
// If Http3 is disabled or the version not supported anymore, remove the
|
||||
// mapping.
|
||||
mStorage->Remove(
|
||||
key, rv->Private() ? DataStorage_Private : DataStorage_Persistent);
|
||||
(void)mStorage->Remove(key, mapping->Private()
|
||||
? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (rv->TTL() <= 0) {
|
||||
if (mapping->TTL() <= 0) {
|
||||
LOG(("AltSvcCache::LookupMapping %p expired hit - MISS\n", this));
|
||||
mStorage->Remove(
|
||||
key, rv->Private() ? DataStorage_Private : DataStorage_Persistent);
|
||||
(void)mStorage->Remove(key, mapping->Private()
|
||||
? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(rv->Private() == privateBrowsing);
|
||||
LOG(("AltSvcCache::LookupMapping %p HIT %p\n", this, rv.get()));
|
||||
return rv.forget();
|
||||
MOZ_ASSERT(mapping->Private() == privateBrowsing);
|
||||
LOG(("AltSvcCache::LookupMapping %p HIT %p\n", this, mapping.get()));
|
||||
return mapping.forget();
|
||||
}
|
||||
|
||||
// This is only used for testing!
|
||||
@ -1255,18 +1279,26 @@ void AltSvcCache::ClearHostMapping(nsHttpConnectionInfo* ci) {
|
||||
void AltSvcCache::ClearAltServiceMappings() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mStorage) {
|
||||
mStorage->Clear();
|
||||
(void)mStorage->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult AltSvcCache::GetAltSvcCacheKeys(nsTArray<nsCString>& value) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (gHttpHandler->AllowAltSvc() && mStorage) {
|
||||
nsTArray<DataStorageItem> items;
|
||||
mStorage->GetAll(&items);
|
||||
nsTArray<RefPtr<nsIDataStorageItem>> items;
|
||||
nsresult rv = mStorage->GetAll(items);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (const auto& item : items) {
|
||||
value.AppendElement(item.key);
|
||||
nsAutoCString key;
|
||||
rv = item->GetKey(key);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
value.AppendElement(key);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -23,9 +23,9 @@ https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06
|
||||
#ifndef mozilla_net_AlternateServices_h
|
||||
#define mozilla_net_AlternateServices_h
|
||||
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIDataStorage.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISpeculativeConnect.h"
|
||||
@ -47,7 +47,7 @@ class AltSvcMapping {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AltSvcMapping)
|
||||
|
||||
private: // ctor from ProcessHeader
|
||||
AltSvcMapping(DataStorage* storage, int32_t storageEpoch,
|
||||
AltSvcMapping(nsIDataStorage* storage, int32_t storageEpoch,
|
||||
const nsACString& originScheme, const nsACString& originHost,
|
||||
int32_t originPort, const nsACString& username,
|
||||
bool privateBrowsing, uint32_t expiresAt,
|
||||
@ -56,7 +56,7 @@ class AltSvcMapping {
|
||||
const OriginAttributes& originAttributes, bool aIsHttp3);
|
||||
|
||||
public:
|
||||
AltSvcMapping(DataStorage* storage, int32_t storageEpoch,
|
||||
AltSvcMapping(nsIDataStorage* storage, int32_t storageEpoch,
|
||||
const nsCString& str);
|
||||
|
||||
static void ProcessHeader(
|
||||
@ -107,7 +107,7 @@ class AltSvcMapping {
|
||||
private:
|
||||
virtual ~AltSvcMapping() = default;
|
||||
void SyncString(const nsCString& str);
|
||||
RefPtr<DataStorage> mStorage;
|
||||
nsCOMPtr<nsIDataStorage> mStorage;
|
||||
int32_t mStorageEpoch;
|
||||
void Serialize(nsCString& out);
|
||||
|
||||
@ -200,7 +200,7 @@ class AltSvcCache {
|
||||
void ClearHostMapping(const nsACString& host, int32_t port,
|
||||
const OriginAttributes& originAttributes);
|
||||
void ClearHostMapping(nsHttpConnectionInfo* ci);
|
||||
DataStorage* GetStoragePtr() { return mStorage.get(); }
|
||||
nsIDataStorage* GetStoragePtr() { return mStorage.get(); }
|
||||
int32_t StorageEpoch() { return mStorageEpoch; }
|
||||
nsresult GetAltSvcCacheKeys(nsTArray<nsCString>& value);
|
||||
|
||||
@ -208,7 +208,7 @@ class AltSvcCache {
|
||||
void EnsureStorageInited();
|
||||
already_AddRefed<AltSvcMapping> LookupMapping(const nsCString& key,
|
||||
bool privateBrowsing);
|
||||
RefPtr<DataStorage> mStorage;
|
||||
nsCOMPtr<nsIDataStorage> mStorage;
|
||||
int32_t mStorageEpoch{0};
|
||||
};
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsISafeOutputStream.h"
|
||||
#include "nsISerialEventTarget.h"
|
||||
@ -49,38 +48,73 @@ static const int64_t sOneDayInMicroseconds =
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DataStorageMemoryReporter final : public nsIMemoryReporter {
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
|
||||
~DataStorageMemoryReporter() = default;
|
||||
NS_IMPL_ISUPPORTS(DataStorageManager, nsIDataStorageManager)
|
||||
NS_IMPL_ISUPPORTS(DataStorageItem, nsIDataStorageItem)
|
||||
NS_IMPL_ISUPPORTS(DataStorage, nsIDataStorage, nsIMemoryReporter, nsIObserver)
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) final {
|
||||
nsTArray<nsString> fileNames;
|
||||
#define DATA_STORAGE(_) \
|
||||
fileNames.AppendElement(NS_LITERAL_STRING_FROM_CSTRING(#_ ".txt"));
|
||||
#include "mozilla/DataStorageList.h"
|
||||
#undef DATA_STORAGE
|
||||
for (const auto& file : fileNames) {
|
||||
RefPtr<DataStorage> ds = DataStorage::GetFromRawFileName(file);
|
||||
size_t amount = ds->SizeOfIncludingThis(MallocSizeOf);
|
||||
nsPrintfCString path("explicit/data-storage/%s",
|
||||
NS_ConvertUTF16toUTF8(file).get());
|
||||
Unused << aHandleReport->Callback(
|
||||
""_ns, path, KIND_HEAP, UNITS_BYTES, amount,
|
||||
"Memory used by PSM data storage cache."_ns, aData);
|
||||
}
|
||||
return NS_OK;
|
||||
NS_IMETHODIMP
|
||||
DataStorageManager::Get(nsIDataStorageManager::DataStorage aDataStorage,
|
||||
nsIDataStorage** aResult) {
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
};
|
||||
nsAutoString filename;
|
||||
switch (aDataStorage) {
|
||||
case nsIDataStorageManager::AlternateServices:
|
||||
if (mAlternateServicesCreated) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
mAlternateServicesCreated = true;
|
||||
filename.Assign(u"AlternateServices.txt"_ns);
|
||||
break;
|
||||
case nsIDataStorageManager::ClientAuthRememberList:
|
||||
if (mClientAuthRememberListCreated) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
mClientAuthRememberListCreated = true;
|
||||
filename.Assign(u"ClientAuthRememberList.txt"_ns);
|
||||
break;
|
||||
case nsIDataStorageManager::SiteSecurityServiceState:
|
||||
if (mSiteSecurityServiceStateCreated) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
mSiteSecurityServiceStateCreated = true;
|
||||
filename.Assign(u"SiteSecurityServiceState.txt"_ns);
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
RefPtr<mozilla::DataStorage> dataStorage(new mozilla::DataStorage(filename));
|
||||
nsresult rv = dataStorage->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsIMemoryReporter> memoryReporter(dataStorage.get());
|
||||
RegisterStrongMemoryReporter(memoryReporter);
|
||||
*aResult = dataStorage.forget().take();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(DataStorageMemoryReporter, nsIMemoryReporter)
|
||||
NS_IMETHODIMP
|
||||
DataStorageItem::GetKey(nsACString& aKey) {
|
||||
aKey.Assign(key);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(DataStorage, nsIObserver)
|
||||
NS_IMETHODIMP
|
||||
DataStorageItem::GetValue(nsACString& aValue) {
|
||||
aValue.Assign(value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::StaticAutoPtr<DataStorage::DataStorages> DataStorage::sDataStorages;
|
||||
NS_IMETHODIMP
|
||||
DataStorageItem::GetType(nsIDataStorage::DataType* aType) {
|
||||
if (!aType) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*aType = type;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DataStorage::DataStorage(const nsString& aFilename)
|
||||
: mMutex("DataStorage::mMutex"),
|
||||
@ -92,42 +126,6 @@ DataStorage::DataStorage(const nsString& aFilename)
|
||||
mReady(false),
|
||||
mFilename(aFilename) {}
|
||||
|
||||
// static
|
||||
already_AddRefed<DataStorage> DataStorage::Get(DataStorageClass aFilename) {
|
||||
switch (aFilename) {
|
||||
#define DATA_STORAGE(_) \
|
||||
case DataStorageClass::_: \
|
||||
return GetFromRawFileName(NS_LITERAL_STRING_FROM_CSTRING(#_ ".txt"));
|
||||
#include "mozilla/DataStorageList.h"
|
||||
#undef DATA_STORAGE
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid DataStorage type passed?");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DataStorage> DataStorage::GetFromRawFileName(
|
||||
const nsString& aFilename) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sDataStorages) {
|
||||
sDataStorages = new DataStorages();
|
||||
ClearOnShutdown(&sDataStorages);
|
||||
}
|
||||
return do_AddRef(sDataStorages->LookupOrInsertWith(
|
||||
aFilename, [&] { return RefPtr{new DataStorage(aFilename)}; }));
|
||||
}
|
||||
|
||||
size_t DataStorage::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
size_t sizeOfExcludingThis =
|
||||
mPersistentDataTable.ShallowSizeOfExcludingThis(aMallocSizeOf) +
|
||||
mTemporaryDataTable.ShallowSizeOfExcludingThis(aMallocSizeOf) +
|
||||
mPrivateDataTable.ShallowSizeOfExcludingThis(aMallocSizeOf) +
|
||||
mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
return aMallocSizeOf(this) + sizeOfExcludingThis;
|
||||
}
|
||||
|
||||
nsresult DataStorage::Init() {
|
||||
// Don't access the observer service or preferences off the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
@ -157,15 +155,6 @@ nsresult DataStorage::Init() {
|
||||
|
||||
mInitCalled = true;
|
||||
|
||||
static bool memoryReporterRegistered = false;
|
||||
if (!memoryReporterRegistered) {
|
||||
nsresult rv = RegisterStrongMemoryReporter(new DataStorageMemoryReporter());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
memoryReporterRegistered = true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget> target;
|
||||
nsresult rv = NS_CreateBackgroundTaskQueue(
|
||||
"DataStorage::mBackgroundTaskQueue", getter_AddRefs(target));
|
||||
@ -316,8 +305,8 @@ DataStorage::Reader::Run() {
|
||||
// The value must not contain '\n' and must have a length no more than 1024.
|
||||
// The length limits are to prevent unbounded memory and disk usage.
|
||||
/* static */
|
||||
nsresult DataStorage::ValidateKeyAndValue(const nsCString& aKey,
|
||||
const nsCString& aValue) {
|
||||
nsresult DataStorage::ValidateKeyAndValue(const nsACString& aKey,
|
||||
const nsACString& aValue) {
|
||||
if (aKey.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
@ -439,9 +428,14 @@ nsresult DataStorage::AsyncReadData(const MutexAutoLock& /*aProofOfLock*/) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool DataStorage::IsReady() {
|
||||
NS_IMETHODIMP
|
||||
DataStorage::IsReady(bool* aReady) {
|
||||
if (!aReady) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
MonitorAutoLock readyLock(mReadyMonitor);
|
||||
return mReady;
|
||||
*aReady = mReady;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void DataStorage::WaitForReady() {
|
||||
@ -454,14 +448,16 @@ void DataStorage::WaitForReady() {
|
||||
MOZ_ASSERT(mReady);
|
||||
}
|
||||
|
||||
nsCString DataStorage::Get(const nsCString& aKey, DataStorageType aType) {
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Get(const nsACString& aKey, nsIDataStorage::DataType aType,
|
||||
nsACString& aValue) {
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
Entry entry;
|
||||
bool foundValue = GetInternal(aKey, &entry, aType, lock);
|
||||
if (!foundValue) {
|
||||
return ""_ns;
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// If we're here, we found a value. Maybe update its score.
|
||||
@ -469,11 +465,12 @@ nsCString DataStorage::Get(const nsCString& aKey, DataStorageType aType) {
|
||||
PutInternal(aKey, entry, aType, lock);
|
||||
}
|
||||
|
||||
return entry.mValue;
|
||||
aValue.Assign(entry.mValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool DataStorage::GetInternal(const nsCString& aKey, Entry* aEntry,
|
||||
DataStorageType aType,
|
||||
bool DataStorage::GetInternal(const nsACString& aKey, Entry* aEntry,
|
||||
nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
DataStorageTable& table = GetTableForType(aType, aProofOfLock);
|
||||
bool foundValue = table.Get(aKey, aEntry);
|
||||
@ -481,40 +478,41 @@ bool DataStorage::GetInternal(const nsCString& aKey, Entry* aEntry,
|
||||
}
|
||||
|
||||
DataStorage::DataStorageTable& DataStorage::GetTableForType(
|
||||
DataStorageType aType, const MutexAutoLock& /*aProofOfLock*/) {
|
||||
nsIDataStorage::DataType aType, const MutexAutoLock& /*aProofOfLock*/) {
|
||||
switch (aType) {
|
||||
case DataStorage_Persistent:
|
||||
case nsIDataStorage::DataType::Persistent:
|
||||
return mPersistentDataTable;
|
||||
case DataStorage_Temporary:
|
||||
case nsIDataStorage::DataType::Temporary:
|
||||
return mTemporaryDataTable;
|
||||
case DataStorage_Private:
|
||||
case nsIDataStorage::DataType::Private:
|
||||
return mPrivateDataTable;
|
||||
}
|
||||
|
||||
MOZ_CRASH("given bad DataStorage storage type");
|
||||
}
|
||||
|
||||
void DataStorage::ReadAllFromTable(DataStorageType aType,
|
||||
nsTArray<DataStorageItem>* aItems,
|
||||
void DataStorage::ReadAllFromTable(nsIDataStorage::DataType aType,
|
||||
nsTArray<RefPtr<nsIDataStorageItem>>& aItems,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
for (auto iter = GetTableForType(aType, aProofOfLock).Iter(); !iter.Done();
|
||||
iter.Next()) {
|
||||
DataStorageItem* item = aItems->AppendElement();
|
||||
item->key = iter.Key();
|
||||
item->value = iter.Data().mValue;
|
||||
item->type = aType;
|
||||
nsCOMPtr<nsIDataStorageItem> item(
|
||||
new DataStorageItem(iter.Key(), iter.Data().mValue, aType));
|
||||
aItems.AppendElement(item);
|
||||
}
|
||||
}
|
||||
|
||||
void DataStorage::GetAll(nsTArray<DataStorageItem>* aItems) {
|
||||
NS_IMETHODIMP
|
||||
DataStorage::GetAll(nsTArray<RefPtr<nsIDataStorageItem>>& aItems) {
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
aItems->SetCapacity(mPersistentDataTable.Count() +
|
||||
mTemporaryDataTable.Count() + mPrivateDataTable.Count());
|
||||
ReadAllFromTable(DataStorage_Persistent, aItems, lock);
|
||||
ReadAllFromTable(DataStorage_Temporary, aItems, lock);
|
||||
ReadAllFromTable(DataStorage_Private, aItems, lock);
|
||||
aItems.SetCapacity(mPersistentDataTable.Count() +
|
||||
mTemporaryDataTable.Count() + mPrivateDataTable.Count());
|
||||
ReadAllFromTable(nsIDataStorage::DataType::Persistent, aItems, lock);
|
||||
ReadAllFromTable(nsIDataStorage::DataType::Temporary, aItems, lock);
|
||||
ReadAllFromTable(nsIDataStorage::DataType::Private, aItems, lock);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Limit the number of entries per table. This is to prevent unbounded
|
||||
@ -524,7 +522,7 @@ void DataStorage::GetAll(nsTArray<DataStorageItem>* aItems) {
|
||||
// (this is the same as saying evict the entry with the lowest score,
|
||||
// except for when there are multiple entries with the lowest score,
|
||||
// in which case one of them is evicted - which one is not specified).
|
||||
void DataStorage::MaybeEvictOneEntry(DataStorageType aType,
|
||||
void DataStorage::MaybeEvictOneEntry(nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
DataStorageTable& table = GetTableForType(aType, aProofOfLock);
|
||||
if (table.Count() >= sMaxDataEntries) {
|
||||
@ -552,8 +550,9 @@ void DataStorage::MaybeEvictOneEntry(DataStorageType aType,
|
||||
}
|
||||
}
|
||||
|
||||
nsresult DataStorage::Put(const nsCString& aKey, const nsCString& aValue,
|
||||
DataStorageType aType) {
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Put(const nsACString& aKey, const nsACString& aValue,
|
||||
nsIDataStorage::DataType aType) {
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
@ -574,14 +573,14 @@ nsresult DataStorage::Put(const nsCString& aKey, const nsCString& aValue,
|
||||
return PutInternal(aKey, entry, aType, lock);
|
||||
}
|
||||
|
||||
nsresult DataStorage::PutInternal(const nsCString& aKey, Entry& aEntry,
|
||||
DataStorageType aType,
|
||||
nsresult DataStorage::PutInternal(const nsACString& aKey, Entry& aEntry,
|
||||
nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock) {
|
||||
mMutex.AssertCurrentThreadOwns();
|
||||
DataStorageTable& table = GetTableForType(aType, aProofOfLock);
|
||||
table.InsertOrUpdate(aKey, aEntry);
|
||||
|
||||
if (aType == DataStorage_Persistent) {
|
||||
if (aType == nsIDataStorage::DataType::Persistent) {
|
||||
mPendingWrite = true;
|
||||
ArmTimer(aProofOfLock);
|
||||
}
|
||||
@ -589,17 +588,19 @@ nsresult DataStorage::PutInternal(const nsCString& aKey, Entry& aEntry,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void DataStorage::Remove(const nsCString& aKey, DataStorageType aType) {
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Remove(const nsACString& aKey, nsIDataStorage::DataType aType) {
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
DataStorageTable& table = GetTableForType(aType, lock);
|
||||
table.Remove(aKey);
|
||||
|
||||
if (aType == DataStorage_Persistent) {
|
||||
if (aType == nsIDataStorage::DataType::Persistent) {
|
||||
mPendingWrite = true;
|
||||
ArmTimer(lock);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class DataStorage::Writer final : public Runnable {
|
||||
@ -715,7 +716,8 @@ nsresult DataStorage::AsyncWriteData(const MutexAutoLock& /*aProofOfLock*/) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult DataStorage::Clear() {
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Clear() {
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
mPersistentDataTable.Clear();
|
||||
@ -781,6 +783,27 @@ void DataStorage::ShutdownTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// DataStorage::nsIMemoryReporter
|
||||
//------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataStorage::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
size_t sizeOfExcludingThis =
|
||||
mPersistentDataTable.ShallowSizeOfExcludingThis(MallocSizeOf) +
|
||||
mTemporaryDataTable.ShallowSizeOfExcludingThis(MallocSizeOf) +
|
||||
mPrivateDataTable.ShallowSizeOfExcludingThis(MallocSizeOf) +
|
||||
mFilename.SizeOfExcludingThisIfUnshared(MallocSizeOf);
|
||||
size_t amount = MallocSizeOf(this) + sizeOfExcludingThis;
|
||||
nsPrintfCString path("explicit/data-storage/%s",
|
||||
NS_ConvertUTF16toUTF8(mFilename).get());
|
||||
return aHandleReport->Callback(""_ns, path, KIND_HEAP, UNITS_BYTES, amount,
|
||||
"Memory used by PSM data storage cache."_ns,
|
||||
aData);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// DataStorage::nsIObserver
|
||||
//------------------------------------------------------------
|
||||
|
@ -14,15 +14,14 @@
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsTHashMap.h"
|
||||
#include "nsIDataStorage.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class psm_DataStorageTest;
|
||||
|
||||
namespace mozilla {
|
||||
class DataStorageMemoryReporter;
|
||||
class TaskQueue;
|
||||
|
||||
/**
|
||||
@ -77,78 +76,57 @@ class TaskQueue;
|
||||
* (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
|
||||
};
|
||||
|
||||
struct DataStorageItem final {
|
||||
nsCString key;
|
||||
nsCString value;
|
||||
DataStorageType type;
|
||||
};
|
||||
|
||||
enum class DataStorageClass {
|
||||
#define DATA_STORAGE(_) _,
|
||||
#include "mozilla/DataStorageList.h"
|
||||
#undef DATA_STORAGE
|
||||
};
|
||||
|
||||
class DataStorage : public nsIObserver {
|
||||
class DataStorageManager final : public nsIDataStorageManager {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDATASTORAGEMANAGER
|
||||
|
||||
private:
|
||||
~DataStorageManager() = default;
|
||||
|
||||
bool mAlternateServicesCreated = false;
|
||||
bool mClientAuthRememberListCreated = false;
|
||||
bool mSiteSecurityServiceStateCreated = false;
|
||||
};
|
||||
|
||||
class DataStorageItem final : public nsIDataStorageItem {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDATASTORAGEITEM
|
||||
|
||||
DataStorageItem(const nsACString& aKey, const nsACString& aValue,
|
||||
nsIDataStorage::DataType aType)
|
||||
: key(aKey), value(aValue), type(aType) {}
|
||||
|
||||
private:
|
||||
~DataStorageItem() = default;
|
||||
|
||||
nsAutoCString key;
|
||||
nsAutoCString value;
|
||||
nsIDataStorage::DataType type;
|
||||
};
|
||||
|
||||
class DataStorage final : public nsIDataStorage,
|
||||
public nsIMemoryReporter,
|
||||
public nsIObserver {
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDATASTORAGE
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
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(DataStorageClass aFilename);
|
||||
|
||||
explicit DataStorage(const nsString& aFilename);
|
||||
// Initializes the DataStorage. Must be called before using.
|
||||
nsresult Init();
|
||||
|
||||
// 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(nsTArray<DataStorageItem>* aItems);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
// Return true if this data storage is ready to be used.
|
||||
bool IsReady();
|
||||
private:
|
||||
~DataStorage() = default;
|
||||
|
||||
void ArmTimer(const MutexAutoLock& aProofOfLock);
|
||||
void ShutdownTimer();
|
||||
|
||||
private:
|
||||
explicit DataStorage(const nsString& aFilename);
|
||||
virtual ~DataStorage() = default;
|
||||
|
||||
static already_AddRefed<DataStorage> GetFromRawFileName(
|
||||
const nsString& aFilename);
|
||||
|
||||
friend class ::psm_DataStorageTest;
|
||||
friend class mozilla::DataStorageMemoryReporter;
|
||||
|
||||
class Writer;
|
||||
class Reader;
|
||||
|
||||
@ -176,23 +154,24 @@ class DataStorage : public nsIObserver {
|
||||
nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
|
||||
nsresult AsyncReadData(const MutexAutoLock& aProofOfLock);
|
||||
|
||||
static nsresult ValidateKeyAndValue(const nsCString& aKey,
|
||||
const nsCString& aValue);
|
||||
static nsresult ValidateKeyAndValue(const nsACString& aKey,
|
||||
const nsACString& aValue);
|
||||
static void TimerCallback(nsITimer* aTimer, void* aClosure);
|
||||
void NotifyObservers(const char* aTopic);
|
||||
|
||||
bool GetInternal(const nsCString& aKey, Entry* aEntry, DataStorageType aType,
|
||||
bool GetInternal(const nsACString& aKey, Entry* aEntry,
|
||||
nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
nsresult PutInternal(const nsCString& aKey, Entry& aEntry,
|
||||
DataStorageType aType,
|
||||
nsresult PutInternal(const nsACString& aKey, Entry& aEntry,
|
||||
nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
void MaybeEvictOneEntry(DataStorageType aType,
|
||||
void MaybeEvictOneEntry(nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
DataStorageTable& GetTableForType(DataStorageType aType,
|
||||
DataStorageTable& GetTableForType(nsIDataStorage::DataType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
void ReadAllFromTable(DataStorageType aType,
|
||||
nsTArray<DataStorageItem>* aItems,
|
||||
void ReadAllFromTable(nsIDataStorage::DataType aType,
|
||||
nsTArray<RefPtr<nsIDataStorageItem>>& aItems,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
Mutex mMutex; // This mutex protects access to the following members:
|
||||
|
@ -1,18 +0,0 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
// This is the list of well-known PSM DataStorage classes that Gecko uses.
|
||||
// These are key value data stores that are backed by a simple text-based
|
||||
// storage in the profile directory.
|
||||
//
|
||||
// Please note that it is crucial for performance reasons for the number of
|
||||
// these classes to remain low. If you need to add to this list, you may
|
||||
// need to update the algorithm in DataStorage::SetCachedStorageEntries()
|
||||
// to something faster.
|
||||
|
||||
DATA_STORAGE(AlternateServices)
|
||||
DATA_STORAGE(ClientAuthRememberList)
|
||||
DATA_STORAGE(SiteSecurityServiceState)
|
@ -140,4 +140,10 @@ Classes = [
|
||||
'type': 'mozilla::psm::CRLiteTimestamp',
|
||||
'headers': ['/security/certverifier/CRLiteTimestamp.h'],
|
||||
},
|
||||
{
|
||||
'cid': '{71b49926-fd4e-43e2-ab8d-d9b049413c0b}',
|
||||
'contract_ids': ['@mozilla.org/security/datastoragemanager;1'],
|
||||
'type': 'mozilla::DataStorageManager',
|
||||
'headers': ['/security/manager/ssl//DataStorage.h'],
|
||||
},
|
||||
]
|
||||
|
@ -26,6 +26,7 @@ XPIDL_SOURCES += [
|
||||
"nsIClientAuthRememberService.idl",
|
||||
"nsIContentSignatureVerifier.idl",
|
||||
"nsICryptoHash.idl",
|
||||
"nsIDataStorage.idl",
|
||||
"nsINSSComponent.idl",
|
||||
"nsINSSErrorsService.idl",
|
||||
"nsINSSVersion.idl",
|
||||
@ -83,8 +84,6 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"DataStorage.h",
|
||||
"DataStorageList.h",
|
||||
"PublicSSL.h",
|
||||
]
|
||||
|
||||
|
@ -7,12 +7,12 @@
|
||||
#include "nsClientAuthRemember.h"
|
||||
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsINSSComponent.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsIDataStorage.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPromiseFlatString.h"
|
||||
@ -70,21 +70,31 @@ nsresult nsClientAuthRememberService::Init() {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
mClientAuthRememberList =
|
||||
mozilla::DataStorage::Get(DataStorageClass::ClientAuthRememberList);
|
||||
nsresult rv = mClientAuthRememberList->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsCOMPtr<nsIDataStorageManager> dataStorageManager(
|
||||
do_GetService("@mozilla.org/security/datastoragemanager;1"));
|
||||
if (!dataStorageManager) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv =
|
||||
dataStorageManager->Get(nsIDataStorageManager::ClientAuthRememberList,
|
||||
getter_AddRefs(mClientAuthRememberList));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!mClientAuthRememberList) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) {
|
||||
mClientAuthRememberList->Remove(PromiseFlatCString(key),
|
||||
mozilla::DataStorage_Persistent);
|
||||
|
||||
nsresult rv = mClientAuthRememberList->Remove(
|
||||
PromiseFlatCString(key), nsIDataStorage::DataType::Persistent);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
|
||||
if (!nssComponent) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
@ -95,13 +105,31 @@ nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) {
|
||||
NS_IMETHODIMP
|
||||
nsClientAuthRememberService::GetDecisions(
|
||||
nsTArray<RefPtr<nsIClientAuthRememberRecord>>& results) {
|
||||
nsTArray<DataStorageItem> decisions;
|
||||
mClientAuthRememberList->GetAll(&decisions);
|
||||
nsTArray<RefPtr<nsIDataStorageItem>> decisions;
|
||||
nsresult rv = mClientAuthRememberList->GetAll(decisions);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (const DataStorageItem& decision : decisions) {
|
||||
if (decision.type == DataStorageType::DataStorage_Persistent) {
|
||||
for (const auto& decision : decisions) {
|
||||
nsIDataStorage::DataType type;
|
||||
rv = decision->GetType(&type);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (type == nsIDataStorage::DataType::Persistent) {
|
||||
nsAutoCString key;
|
||||
rv = decision->GetKey(key);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsAutoCString value;
|
||||
rv = decision->GetValue(value);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
RefPtr<nsIClientAuthRememberRecord> tmp =
|
||||
new nsClientAuthRemember(decision.key, decision.value);
|
||||
new nsClientAuthRemember(key, value);
|
||||
|
||||
results.AppendElement(tmp);
|
||||
}
|
||||
@ -112,7 +140,10 @@ nsClientAuthRememberService::GetDecisions(
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClientAuthRememberService::ClearRememberedDecisions() {
|
||||
mClientAuthRememberList->Clear();
|
||||
nsresult rv = mClientAuthRememberList->Clear();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
|
||||
if (!nssComponent) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
@ -128,19 +159,40 @@ nsClientAuthRememberService::DeleteDecisionsByHost(
|
||||
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
DataStorageType storageType = GetDataStorageType(attrs);
|
||||
nsIDataStorage::DataType storageType = GetDataStorageType(attrs);
|
||||
|
||||
nsTArray<DataStorageItem> decisions;
|
||||
mClientAuthRememberList->GetAll(&decisions);
|
||||
nsTArray<RefPtr<nsIDataStorageItem>> decisions;
|
||||
nsresult rv = mClientAuthRememberList->GetAll(decisions);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (const DataStorageItem& decision : decisions) {
|
||||
if (decision.type == storageType) {
|
||||
for (const auto& decision : decisions) {
|
||||
nsIDataStorage::DataType type;
|
||||
nsresult rv = decision->GetType(&type);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (type == storageType) {
|
||||
nsAutoCString key;
|
||||
rv = decision->GetKey(key);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsAutoCString value;
|
||||
rv = decision->GetValue(value);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
RefPtr<nsIClientAuthRememberRecord> tmp =
|
||||
new nsClientAuthRemember(decision.key, decision.value);
|
||||
new nsClientAuthRemember(key, value);
|
||||
nsAutoCString asciiHost;
|
||||
tmp->GetAsciiHost(asciiHost);
|
||||
if (asciiHost.Equals(aHostName)) {
|
||||
mClientAuthRememberList->Remove(decision.key, decision.type);
|
||||
rv = mClientAuthRememberList->Remove(key, type);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,25 +292,44 @@ void nsClientAuthRememberService::Migrate() {
|
||||
if (migrated) {
|
||||
return;
|
||||
}
|
||||
nsTArray<DataStorageItem> decisions;
|
||||
mClientAuthRememberList->GetAll(&decisions);
|
||||
migrated = true;
|
||||
nsTArray<RefPtr<nsIDataStorageItem>> decisions;
|
||||
nsresult rv = mClientAuthRememberList->GetAll(decisions);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
for (const auto& decision : decisions) {
|
||||
if (decision.type != DataStorage_Persistent) {
|
||||
nsIDataStorage::DataType type;
|
||||
if (NS_FAILED(decision->GetType(&type))) {
|
||||
continue;
|
||||
}
|
||||
RefPtr<nsClientAuthRemember> entry(
|
||||
new nsClientAuthRemember(decision.key, decision.value));
|
||||
if (type != nsIDataStorage::DataType::Persistent) {
|
||||
continue;
|
||||
}
|
||||
nsAutoCString key;
|
||||
if (NS_FAILED(decision->GetKey(key))) {
|
||||
continue;
|
||||
}
|
||||
nsAutoCString value;
|
||||
if (NS_FAILED(decision->GetValue(value))) {
|
||||
continue;
|
||||
}
|
||||
RefPtr<nsClientAuthRemember> entry(new nsClientAuthRemember(key, value));
|
||||
nsAutoCString newKey;
|
||||
if (NS_FAILED(entry->GetEntryKey(newKey))) {
|
||||
continue;
|
||||
}
|
||||
if (newKey != decision.key) {
|
||||
mClientAuthRememberList->Remove(decision.key, DataStorage_Persistent);
|
||||
(void)mClientAuthRememberList->Put(newKey, decision.value,
|
||||
DataStorage_Persistent);
|
||||
if (newKey != key) {
|
||||
if (NS_FAILED(mClientAuthRememberList->Remove(
|
||||
key, nsIDataStorage::DataType::Persistent))) {
|
||||
continue;
|
||||
}
|
||||
if (NS_FAILED(mClientAuthRememberList->Put(
|
||||
newKey, value, nsIDataStorage::DataType::Persistent))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
migrated = true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -285,10 +356,14 @@ nsClientAuthRememberService::HasRememberedDecision(
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
DataStorageType storageType = GetDataStorageType(aOriginAttributes);
|
||||
nsIDataStorage::DataType storageType = GetDataStorageType(aOriginAttributes);
|
||||
|
||||
nsCString listEntry = mClientAuthRememberList->Get(entryKey, storageType);
|
||||
if (!listEntry.IsEmpty()) {
|
||||
nsAutoCString listEntry;
|
||||
rv = mClientAuthRememberList->Get(entryKey, storageType, listEntry);
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
return rv;
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) && !listEntry.IsEmpty()) {
|
||||
if (!listEntry.Equals(nsClientAuthRemember::SentinelValue)) {
|
||||
aCertDBKey = listEntry;
|
||||
}
|
||||
@ -331,7 +406,7 @@ nsresult nsClientAuthRememberService::AddEntryToList(
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
DataStorageType storageType = GetDataStorageType(aOriginAttributes);
|
||||
nsIDataStorage::DataType storageType = GetDataStorageType(aOriginAttributes);
|
||||
|
||||
nsCString tmpDbKey(aDBKey);
|
||||
rv = mClientAuthRememberList->Put(entryKey, tmpDbKey, storageType);
|
||||
@ -354,10 +429,10 @@ bool nsClientAuthRememberService::IsPrivateBrowsingKey(
|
||||
return OriginAttributes::IsPrivateBrowsing(suffix);
|
||||
}
|
||||
|
||||
DataStorageType nsClientAuthRememberService::GetDataStorageType(
|
||||
nsIDataStorage::DataType nsClientAuthRememberService::GetDataStorageType(
|
||||
const OriginAttributes& aOriginAttributes) {
|
||||
if (aOriginAttributes.mPrivateBrowsingId > 0) {
|
||||
return DataStorage_Private;
|
||||
return nsIDataStorage::DataType::Private;
|
||||
}
|
||||
return DataStorage_Persistent;
|
||||
return nsIDataStorage::DataType::Persistent;
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
#include <utility>
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsIClientAuthRememberService.h"
|
||||
#include "nsIDataStorage.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsString.h"
|
||||
@ -83,10 +83,10 @@ class nsClientAuthRememberService final : public nsIClientAuthRememberService {
|
||||
protected:
|
||||
~nsClientAuthRememberService() = default;
|
||||
|
||||
static mozilla::DataStorageType GetDataStorageType(
|
||||
static nsIDataStorage::DataType GetDataStorageType(
|
||||
const OriginAttributes& aOriginAttributes);
|
||||
|
||||
RefPtr<mozilla::DataStorage> mClientAuthRememberList;
|
||||
nsCOMPtr<nsIDataStorage> mClientAuthRememberList;
|
||||
|
||||
nsresult AddEntryToList(const nsACString& aHost,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
|
64
security/manager/ssl/nsIDataStorage.idl
Normal file
64
security/manager/ssl/nsIDataStorage.idl
Normal file
@ -0,0 +1,64 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDataStorage;
|
||||
interface nsIDataStorageItem;
|
||||
|
||||
[scriptable, uuid(71b49926-fd4e-43e2-ab8d-d9b049413c0b)]
|
||||
interface nsIDataStorageManager : nsISupports {
|
||||
cenum DataStorage : 8 {
|
||||
AlternateServices,
|
||||
ClientAuthRememberList,
|
||||
SiteSecurityServiceState,
|
||||
};
|
||||
|
||||
nsIDataStorage get(in nsIDataStorageManager_DataStorage dataStorage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(fcbb5ec4-7134-4069-91c6-9378eff51e03)]
|
||||
interface nsIDataStorage : nsISupports {
|
||||
/**
|
||||
* Data that is Persistent is saved on disk. Temporary and Private are not
|
||||
* saved. Private is meant to only be set and accessed from private contexts.
|
||||
* It will be cleared upon observing the event "last-pb-context-exited".
|
||||
*/
|
||||
cenum DataType : 8 {
|
||||
Persistent,
|
||||
Temporary,
|
||||
Private,
|
||||
};
|
||||
|
||||
// Given a key and a type of data, returns a value. Returns
|
||||
// NS_ERROR_NOT_AVAILABLE 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.
|
||||
ACString get(in ACString key, in nsIDataStorage_DataType type);
|
||||
|
||||
// Give a key, value, and type of data, adds an entry as appropriate.
|
||||
// Updates existing entries.
|
||||
void put(in ACString key, in ACString value, in nsIDataStorage_DataType type);
|
||||
|
||||
// Given a key and type of data, removes an entry if present.
|
||||
void remove(in ACString key, in nsIDataStorage_DataType type);
|
||||
|
||||
// Removes all entries of all types of data.
|
||||
void clear();
|
||||
|
||||
// Returns true if this data storage is ready to be used.
|
||||
bool isReady();
|
||||
|
||||
// Read all of the data items.
|
||||
Array<nsIDataStorageItem> getAll();
|
||||
};
|
||||
|
||||
[scriptable, uuid(4501f984-0e3a-4199-a67e-7753649e93f1)]
|
||||
interface nsIDataStorageItem : nsISupports {
|
||||
readonly attribute ACString key;
|
||||
readonly attribute ACString value;
|
||||
readonly attribute nsIDataStorage_DataType type;
|
||||
};
|
@ -13,7 +13,6 @@
|
||||
#include "mozilla/Tokenizer.h"
|
||||
#include "mozilla/dom/PContent.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "nsArrayEnumerator.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISocketProvider.h"
|
||||
@ -190,12 +189,20 @@ nsresult nsSiteSecurityService::Init() {
|
||||
mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
|
||||
mozilla::Preferences::AddStrongObserver(this,
|
||||
"test.currentTimeOffsetSeconds");
|
||||
mSiteStateStorage =
|
||||
mozilla::DataStorage::Get(DataStorageClass::SiteSecurityServiceState);
|
||||
nsresult rv = mSiteStateStorage->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsCOMPtr<nsIDataStorageManager> dataStorageManager(
|
||||
do_GetService("@mozilla.org/security/datastoragemanager;1"));
|
||||
if (!dataStorageManager) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv =
|
||||
dataStorageManager->Get(nsIDataStorageManager::SiteSecurityServiceState,
|
||||
getter_AddRefs(mSiteStateStorage));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!mSiteStateStorage) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -315,21 +322,33 @@ nsresult nsSiteSecurityService::SetHSTSState(
|
||||
siteState.ToString(stateString);
|
||||
SSSLOG(("SSS: setting state for %s", hostname.get()));
|
||||
bool isPrivate = aOriginAttributes.mPrivateBrowsingId > 0;
|
||||
mozilla::DataStorageType storageType = isPrivate
|
||||
? mozilla::DataStorage_Private
|
||||
: mozilla::DataStorage_Persistent;
|
||||
nsIDataStorage::DataType storageType =
|
||||
isPrivate ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent;
|
||||
SSSLOG(("SSS: storing HSTS site entry for %s", hostname.get()));
|
||||
nsCString value;
|
||||
GetWithMigration(hostname, aOriginAttributes, storageType, value);
|
||||
nsAutoCString value;
|
||||
nsresult rv =
|
||||
GetWithMigration(hostname, aOriginAttributes, storageType, value);
|
||||
// If this fails for a reason other than nothing by that key exists,
|
||||
// propagate the failure.
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
return rv;
|
||||
}
|
||||
// This is an entirely new entry.
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
nsAutoCString storageKey;
|
||||
GetStorageKey(hostname, aOriginAttributes, storageKey);
|
||||
return mSiteStateStorage->Put(storageKey, stateString, storageType);
|
||||
}
|
||||
// Otherwise, only update the backing storage if the currently-stored state
|
||||
// is different. In the case of expiration time, "different" means "is
|
||||
// different by more than a day".
|
||||
SiteHSTSState curSiteState(hostname, aOriginAttributes, value);
|
||||
// Only update the backing storage if the currently-stored state is
|
||||
// different. In the case of expiration time, "different" means "is different
|
||||
// by more than a day".
|
||||
if (curSiteState.mHSTSState != siteState.mHSTSState ||
|
||||
curSiteState.mHSTSIncludeSubdomains != siteState.mHSTSIncludeSubdomains ||
|
||||
AbsoluteDifference(curSiteState.mHSTSExpireTime,
|
||||
siteState.mHSTSExpireTime) > sOneDayInMilliseconds) {
|
||||
nsresult rv =
|
||||
rv =
|
||||
PutWithMigration(hostname, aOriginAttributes, storageType, stateString);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
@ -346,9 +365,9 @@ nsresult nsSiteSecurityService::SetHSTSState(
|
||||
nsresult nsSiteSecurityService::MarkHostAsNotHSTS(
|
||||
const nsAutoCString& aHost, const OriginAttributes& aOriginAttributes) {
|
||||
bool isPrivate = aOriginAttributes.mPrivateBrowsingId > 0;
|
||||
mozilla::DataStorageType storageType = isPrivate
|
||||
? mozilla::DataStorage_Private
|
||||
: mozilla::DataStorage_Persistent;
|
||||
nsIDataStorage::DataType storageType =
|
||||
isPrivate ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent;
|
||||
if (GetPreloadStatus(aHost)) {
|
||||
SSSLOG(("SSS: storing knockout entry for %s", aHost.get()));
|
||||
SiteHSTSState siteState(aHost, aOriginAttributes, 0,
|
||||
@ -419,17 +438,31 @@ nsresult nsSiteSecurityService::ResetStateInternal(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<DataStorageItem> items;
|
||||
mSiteStateStorage->GetAll(&items);
|
||||
nsTArray<RefPtr<nsIDataStorageItem>> items;
|
||||
rv = mSiteStateStorage->GetAll(items);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
for (const auto& item : items) {
|
||||
static const nsLiteralCString kHPKPKeySuffix = ":HPKP"_ns;
|
||||
if (StringEndsWith(item.key, kHPKPKeySuffix)) {
|
||||
mSiteStateStorage->Remove(item.key, DataStorage_Persistent);
|
||||
nsAutoCString key;
|
||||
rv = item->GetKey(key);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsAutoCString value;
|
||||
rv = item->GetValue(value);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (StringEndsWith(key, kHPKPKeySuffix)) {
|
||||
(void)mSiteStateStorage->Remove(key,
|
||||
nsIDataStorage::DataType::Persistent);
|
||||
continue;
|
||||
}
|
||||
size_t suffixLength =
|
||||
StringEndsWith(item.key, kHSTSKeySuffix) ? kHSTSKeySuffix.Length() : 0;
|
||||
nsCString origin(StringHead(item.key, item.key.Length() - suffixLength));
|
||||
StringEndsWith(key, kHSTSKeySuffix) ? kHSTSKeySuffix.Length() : 0;
|
||||
nsCString origin(StringHead(key, key.Length() - suffixLength));
|
||||
nsAutoCString itemHostname;
|
||||
OriginAttributes itemOriginAttributes;
|
||||
if (!itemOriginAttributes.PopulateFromOrigin(origin, itemHostname)) {
|
||||
@ -460,9 +493,9 @@ nsresult nsSiteSecurityService::ResetStateInternal(
|
||||
void nsSiteSecurityService::ResetStateForExactDomain(
|
||||
const nsCString& aHostname, const OriginAttributes& aOriginAttributes) {
|
||||
bool isPrivate = aOriginAttributes.mPrivateBrowsingId > 0;
|
||||
mozilla::DataStorageType storageType = isPrivate
|
||||
? mozilla::DataStorage_Private
|
||||
: mozilla::DataStorage_Persistent;
|
||||
nsIDataStorage::DataType storageType =
|
||||
isPrivate ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent;
|
||||
RemoveWithMigration(aHostname, aOriginAttributes, storageType);
|
||||
}
|
||||
|
||||
@ -738,48 +771,55 @@ bool nsSiteSecurityService::GetPreloadStatus(const nsACString& aHost,
|
||||
return found;
|
||||
}
|
||||
|
||||
void nsSiteSecurityService::GetWithMigration(
|
||||
nsresult nsSiteSecurityService::GetWithMigration(
|
||||
const nsACString& aHostname, const OriginAttributes& aOriginAttributes,
|
||||
DataStorageType aDataStorageType, nsCString& aValue) {
|
||||
nsIDataStorage::DataType aDataStorageType, nsACString& aValue) {
|
||||
// First see if this entry exists and has already been migrated.
|
||||
nsAutoCString storageKey;
|
||||
GetStorageKey(aHostname, aOriginAttributes, storageKey);
|
||||
aValue = mSiteStateStorage->Get(storageKey, aDataStorageType);
|
||||
if (!aValue.IsEmpty()) {
|
||||
return;
|
||||
nsresult rv = mSiteStateStorage->Get(storageKey, aDataStorageType, aValue);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
return rv;
|
||||
}
|
||||
// Otherwise, it potentially needs to be migrated, if it's persistent data.
|
||||
if (aDataStorageType != DataStorage_Persistent) {
|
||||
return;
|
||||
if (aDataStorageType != nsIDataStorage::DataType::Persistent) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
nsAutoCString oldStorageKey;
|
||||
GetOldStorageKey(aHostname, aOriginAttributes, oldStorageKey);
|
||||
aValue = mSiteStateStorage->Get(oldStorageKey, DataStorage_Persistent);
|
||||
if (aValue.IsEmpty()) {
|
||||
return;
|
||||
rv = mSiteStateStorage->Get(oldStorageKey,
|
||||
nsIDataStorage::DataType::Persistent, aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// If there was a value, remove the old entry, insert a new one with the new
|
||||
// key, and return the value.
|
||||
mSiteStateStorage->Remove(oldStorageKey, DataStorage_Persistent);
|
||||
nsresult rv =
|
||||
mSiteStateStorage->Put(storageKey, aValue, DataStorage_Persistent);
|
||||
// DataStorage::Put only fails if the key or value is too long. Since the new
|
||||
// key is shorter than the old key, and since the old key and value were
|
||||
// already present in storage, this should never fail.
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
(void)rv;
|
||||
rv = mSiteStateStorage->Remove(oldStorageKey,
|
||||
nsIDataStorage::DataType::Persistent);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return mSiteStateStorage->Put(storageKey, aValue,
|
||||
nsIDataStorage::DataType::Persistent);
|
||||
}
|
||||
|
||||
nsresult nsSiteSecurityService::PutWithMigration(
|
||||
const nsACString& aHostname, const OriginAttributes& aOriginAttributes,
|
||||
DataStorageType aDataStorageType, const nsCString& aStateString) {
|
||||
nsIDataStorage::DataType aDataStorageType, const nsACString& aStateString) {
|
||||
// Only persistent data needs migrating.
|
||||
if (aDataStorageType == DataStorage_Persistent) {
|
||||
if (aDataStorageType == nsIDataStorage::DataType::Persistent) {
|
||||
// Since the intention is to overwrite the previously-stored data anyway,
|
||||
// the old entry can be removed.
|
||||
nsAutoCString oldStorageKey;
|
||||
GetOldStorageKey(aHostname, aOriginAttributes, oldStorageKey);
|
||||
mSiteStateStorage->Remove(oldStorageKey, DataStorage_Persistent);
|
||||
nsresult rv = mSiteStateStorage->Remove(
|
||||
oldStorageKey, nsIDataStorage::DataType::Persistent);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString storageKey;
|
||||
@ -787,19 +827,23 @@ nsresult nsSiteSecurityService::PutWithMigration(
|
||||
return mSiteStateStorage->Put(storageKey, aStateString, aDataStorageType);
|
||||
}
|
||||
|
||||
void nsSiteSecurityService::RemoveWithMigration(
|
||||
nsresult nsSiteSecurityService::RemoveWithMigration(
|
||||
const nsACString& aHostname, const OriginAttributes& aOriginAttributes,
|
||||
DataStorageType aDataStorageType) {
|
||||
nsIDataStorage::DataType aDataStorageType) {
|
||||
// Only persistent data needs migrating.
|
||||
if (aDataStorageType == DataStorage_Persistent) {
|
||||
if (aDataStorageType == nsIDataStorage::DataType::Persistent) {
|
||||
nsAutoCString oldStorageKey;
|
||||
GetOldStorageKey(aHostname, aOriginAttributes, oldStorageKey);
|
||||
mSiteStateStorage->Remove(oldStorageKey, DataStorage_Persistent);
|
||||
nsresult rv = mSiteStateStorage->Remove(
|
||||
oldStorageKey, nsIDataStorage::DataType::Persistent);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString storageKey;
|
||||
GetStorageKey(aHostname, aOriginAttributes, storageKey);
|
||||
mSiteStateStorage->Remove(storageKey, aDataStorageType);
|
||||
return mSiteStateStorage->Remove(storageKey, aDataStorageType);
|
||||
}
|
||||
|
||||
// Allows us to determine if we have an HSTS entry for a given host (and, if
|
||||
@ -808,9 +852,11 @@ void nsSiteSecurityService::RemoveWithMigration(
|
||||
// the host which we wish to deteming HSTS information on,
|
||||
// aRequireIncludeSubdomains specifies whether we require includeSubdomains
|
||||
// to be set on the entry (with the other parameters being as per IsSecureHost).
|
||||
bool nsSiteSecurityService::HostHasHSTSEntry(
|
||||
nsresult nsSiteSecurityService::HostHasHSTSEntry(
|
||||
const nsAutoCString& aHost, bool aRequireIncludeSubdomains,
|
||||
const OriginAttributes& aOriginAttributes, bool* aResult) {
|
||||
const OriginAttributes& aOriginAttributes, bool& aHostHasHSTSEntry,
|
||||
bool* aResult) {
|
||||
aHostHasHSTSEntry = false;
|
||||
// First we check for an entry in site security storage. If that entry exists,
|
||||
// we don't want to check in the preload lists. We only want to use the
|
||||
// stored value if it is not a knockout entry, however.
|
||||
@ -818,48 +864,62 @@ bool nsSiteSecurityService::HostHasHSTSEntry(
|
||||
// on the host, because the knockout entry indicates "we have no information
|
||||
// regarding the security status of this host".
|
||||
bool isPrivate = aOriginAttributes.mPrivateBrowsingId > 0;
|
||||
mozilla::DataStorageType storageType = isPrivate
|
||||
? mozilla::DataStorage_Private
|
||||
: mozilla::DataStorage_Persistent;
|
||||
nsIDataStorage::DataType storageType =
|
||||
isPrivate ? nsIDataStorage::DataType::Private
|
||||
: nsIDataStorage::DataType::Persistent;
|
||||
SSSLOG(("Seeking HSTS entry for %s", aHost.get()));
|
||||
nsCString value;
|
||||
GetWithMigration(aHost, aOriginAttributes, storageType, value);
|
||||
SiteHSTSState siteState(aHost, aOriginAttributes, value);
|
||||
if (siteState.mHSTSState != SecurityPropertyUnset) {
|
||||
SSSLOG(("Found HSTS entry for %s", aHost.get()));
|
||||
bool expired = siteState.IsExpired();
|
||||
if (!expired) {
|
||||
SSSLOG(("Entry for %s is not expired", aHost.get()));
|
||||
if (siteState.mHSTSState == SecurityPropertySet) {
|
||||
*aResult =
|
||||
aRequireIncludeSubdomains ? siteState.mHSTSIncludeSubdomains : true;
|
||||
return true;
|
||||
nsAutoCString value;
|
||||
nsresult rv = GetWithMigration(aHost, aOriginAttributes, storageType, value);
|
||||
// If this fails for a reason other than nothing by that key exists,
|
||||
// propagate the failure.
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
return rv;
|
||||
}
|
||||
bool checkPreloadList = true;
|
||||
// If something by that key does exist, decode and process that information.
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
SiteHSTSState siteState(aHost, aOriginAttributes, value);
|
||||
if (siteState.mHSTSState != SecurityPropertyUnset) {
|
||||
SSSLOG(("Found HSTS entry for %s", aHost.get()));
|
||||
bool expired = siteState.IsExpired();
|
||||
if (!expired) {
|
||||
SSSLOG(("Entry for %s is not expired", aHost.get()));
|
||||
if (siteState.mHSTSState == SecurityPropertySet) {
|
||||
*aResult = aRequireIncludeSubdomains
|
||||
? siteState.mHSTSIncludeSubdomains
|
||||
: true;
|
||||
aHostHasHSTSEntry = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expired) {
|
||||
SSSLOG(("Entry %s is expired - checking for preload state", aHost.get()));
|
||||
if (!GetPreloadStatus(aHost)) {
|
||||
SSSLOG(("No static preload - removing expired entry"));
|
||||
nsAutoCString storageKey;
|
||||
GetStorageKey(aHost, aOriginAttributes, storageKey);
|
||||
mSiteStateStorage->Remove(storageKey, storageType);
|
||||
if (expired) {
|
||||
SSSLOG(
|
||||
("Entry %s is expired - checking for preload state", aHost.get()));
|
||||
if (!GetPreloadStatus(aHost)) {
|
||||
SSSLOG(("No static preload - removing expired entry"));
|
||||
nsAutoCString storageKey;
|
||||
GetStorageKey(aHost, aOriginAttributes, storageKey);
|
||||
rv = mSiteStateStorage->Remove(storageKey, storageType);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
return false;
|
||||
checkPreloadList = false;
|
||||
}
|
||||
|
||||
bool includeSubdomains = false;
|
||||
|
||||
// Finally look in the static preload list.
|
||||
if (siteState.mHSTSState == SecurityPropertyUnset &&
|
||||
GetPreloadStatus(aHost, &includeSubdomains)) {
|
||||
if (checkPreloadList && GetPreloadStatus(aHost, &includeSubdomains)) {
|
||||
SSSLOG(("%s is a preloaded HSTS host", aHost.get()));
|
||||
*aResult = aRequireIncludeSubdomains ? includeSubdomains : true;
|
||||
return true;
|
||||
aHostHasHSTSEntry = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsSiteSecurityService::IsSecureHost(
|
||||
@ -880,7 +940,13 @@ nsresult nsSiteSecurityService::IsSecureHost(
|
||||
PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
|
||||
|
||||
// First check the exact host.
|
||||
if (HostHasHSTSEntry(host, false, aOriginAttributes, aResult)) {
|
||||
bool hostHasHSTSEntry = false;
|
||||
nsresult rv = HostHasHSTSEntry(host, false, aOriginAttributes,
|
||||
hostHasHSTSEntry, aResult);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (hostHasHSTSEntry) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -902,7 +968,13 @@ nsresult nsSiteSecurityService::IsSecureHost(
|
||||
// that the entry includes subdomains.
|
||||
nsAutoCString subdomainString(subdomain);
|
||||
|
||||
if (HostHasHSTSEntry(subdomainString, true, aOriginAttributes, aResult)) {
|
||||
hostHasHSTSEntry = false;
|
||||
rv = HostHasHSTSEntry(subdomainString, true, aOriginAttributes,
|
||||
hostHasHSTSEntry, aResult);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (hostHasHSTSEntry) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@
|
||||
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/Dafsa.h"
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDataStorage.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsISiteSecurityService.h"
|
||||
#include "nsString.h"
|
||||
@ -126,10 +126,10 @@ class nsSiteSecurityService : public nsISiteSecurityService,
|
||||
nsISiteSecurityService::ResetStateBy aScope);
|
||||
void ResetStateForExactDomain(const nsCString& aHostname,
|
||||
const OriginAttributes& aOriginAttributes);
|
||||
bool HostHasHSTSEntry(const nsAutoCString& aHost,
|
||||
bool aRequireIncludeSubdomains,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
bool* aResult);
|
||||
nsresult HostHasHSTSEntry(const nsAutoCString& aHost,
|
||||
bool aRequireIncludeSubdomains,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
bool& aHostHasHSTSEntry, bool* aResult);
|
||||
bool GetPreloadStatus(
|
||||
const nsACString& aHost,
|
||||
/*optional out*/ bool* aIncludeSubdomains = nullptr) const;
|
||||
@ -137,21 +137,21 @@ class nsSiteSecurityService : public nsISiteSecurityService,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
bool* aResult);
|
||||
|
||||
void GetWithMigration(const nsACString& aHostname,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
mozilla::DataStorageType aDataStorageType,
|
||||
nsCString& aValue);
|
||||
nsresult GetWithMigration(const nsACString& aHostname,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
nsIDataStorage::DataType aDataStorageType,
|
||||
nsACString& aValue);
|
||||
nsresult PutWithMigration(const nsACString& aHostname,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
mozilla::DataStorageType aDataStorageType,
|
||||
const nsCString& aStateString);
|
||||
void RemoveWithMigration(const nsACString& aHostname,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
mozilla::DataStorageType aDataStorageType);
|
||||
nsIDataStorage::DataType aDataStorageType,
|
||||
const nsACString& aStateString);
|
||||
nsresult RemoveWithMigration(const nsACString& aHostname,
|
||||
const OriginAttributes& aOriginAttributes,
|
||||
nsIDataStorage::DataType aDataStorageType);
|
||||
|
||||
bool mUsePreloadList;
|
||||
int64_t mPreloadListTimeOffset;
|
||||
RefPtr<mozilla::DataStorage> mSiteStateStorage;
|
||||
nsCOMPtr<nsIDataStorage> mSiteStateStorage;
|
||||
const mozilla::Dafsa mDafsa;
|
||||
};
|
||||
|
||||
|
@ -1,201 +0,0 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "prtime.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class psm_DataStorageTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const ::testing::TestInfo* const testInfo =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
NS_ConvertUTF8toUTF16 testName(testInfo->name());
|
||||
storage = DataStorage::GetFromRawFileName(testName);
|
||||
storage->Init();
|
||||
}
|
||||
|
||||
RefPtr<DataStorage> storage;
|
||||
};
|
||||
|
||||
constexpr auto testKey = "test"_ns;
|
||||
constexpr auto testValue = "value"_ns;
|
||||
constexpr auto privateTestValue = "private"_ns;
|
||||
|
||||
TEST_F(psm_DataStorageTest, GetPutRemove) {
|
||||
// Test Put/Get on Persistent data
|
||||
EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
|
||||
// Don't re-use testKey / testValue here, to make sure that this works as
|
||||
// expected with objects that have the same semantic value but are not
|
||||
// literally the same object.
|
||||
nsCString result = storage->Get("test"_ns, DataStorage_Persistent);
|
||||
EXPECT_STREQ("value", result.get());
|
||||
|
||||
// Get on Temporary/Private data with the same key should give nothing
|
||||
result = storage->Get(testKey, DataStorage_Temporary);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
result = storage->Get(testKey, DataStorage_Private);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
|
||||
// Put with Temporary/Private data shouldn't affect Persistent data
|
||||
constexpr auto temporaryTestValue = "temporary"_ns;
|
||||
EXPECT_EQ(NS_OK,
|
||||
storage->Put(testKey, temporaryTestValue, DataStorage_Temporary));
|
||||
EXPECT_EQ(NS_OK,
|
||||
storage->Put(testKey, privateTestValue, DataStorage_Private));
|
||||
result = storage->Get(testKey, DataStorage_Temporary);
|
||||
EXPECT_STREQ("temporary", result.get());
|
||||
result = storage->Get(testKey, DataStorage_Private);
|
||||
EXPECT_STREQ("private", result.get());
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("value", result.get());
|
||||
|
||||
// Put of a previously-present key overwrites it (if of the same type)
|
||||
constexpr auto newValue = "new"_ns;
|
||||
EXPECT_EQ(NS_OK, storage->Put(testKey, newValue, DataStorage_Persistent));
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("new", result.get());
|
||||
|
||||
// Removal should work
|
||||
storage->Remove(testKey, DataStorage_Temporary);
|
||||
result = storage->Get(testKey, DataStorage_Temporary);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
// But removing one type shouldn't affect the others
|
||||
result = storage->Get(testKey, DataStorage_Private);
|
||||
EXPECT_STREQ("private", result.get());
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("new", result.get());
|
||||
// Test removing the other types as well
|
||||
storage->Remove(testKey, DataStorage_Private);
|
||||
result = storage->Get(testKey, DataStorage_Private);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
storage->Remove(testKey, DataStorage_Persistent);
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(psm_DataStorageTest, InputValidation) {
|
||||
// Keys may not have tabs or newlines
|
||||
EXPECT_EQ(NS_ERROR_INVALID_ARG,
|
||||
storage->Put("key\thas tab"_ns, testValue, DataStorage_Persistent));
|
||||
nsCString result = storage->Get("key\thas tab"_ns, DataStorage_Persistent);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
EXPECT_EQ(NS_ERROR_INVALID_ARG, storage->Put("key has\nnewline"_ns, testValue,
|
||||
DataStorage_Persistent));
|
||||
result = storage->Get("keyhas\nnewline"_ns, DataStorage_Persistent);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
// Values may not have newlines
|
||||
EXPECT_EQ(NS_ERROR_INVALID_ARG, storage->Put(testKey, "value\nhas newline"_ns,
|
||||
DataStorage_Persistent));
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
// Values may have tabs
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
EXPECT_EQ(NS_OK, storage->Put(testKey, "val\thas tab; this is ok"_ns,
|
||||
DataStorage_Persistent));
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("val\thas tab; this is ok", result.get());
|
||||
|
||||
nsCString longKey("a");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
longKey.Append(longKey);
|
||||
}
|
||||
// A key of length 256 will work
|
||||
EXPECT_EQ(NS_OK, storage->Put(longKey, testValue, DataStorage_Persistent));
|
||||
result = storage->Get(longKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("value", result.get());
|
||||
longKey.AppendLiteral("a");
|
||||
// A key longer than that will not work
|
||||
EXPECT_EQ(NS_ERROR_INVALID_ARG,
|
||||
storage->Put(longKey, testValue, DataStorage_Persistent));
|
||||
result = storage->Get(longKey, DataStorage_Persistent);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
|
||||
nsCString longValue("a");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
longValue.Append(longValue);
|
||||
}
|
||||
// A value of length 1024 will work
|
||||
EXPECT_EQ(NS_OK, storage->Put(testKey, longValue, DataStorage_Persistent));
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ(longValue.get(), result.get());
|
||||
longValue.AppendLiteral("a");
|
||||
// A value longer than that will not work
|
||||
storage->Remove(testKey, DataStorage_Persistent);
|
||||
EXPECT_EQ(NS_ERROR_INVALID_ARG,
|
||||
storage->Put(testKey, longValue, DataStorage_Persistent));
|
||||
result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(psm_DataStorageTest, Eviction) {
|
||||
// Eviction is on a per-table basis. Tables shouldn't affect each other.
|
||||
EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
|
||||
for (int i = 0; i < 1025; i++) {
|
||||
EXPECT_EQ(NS_OK,
|
||||
storage->Put(nsPrintfCString("%d", i), nsPrintfCString("%d", i),
|
||||
DataStorage_Temporary));
|
||||
nsCString result =
|
||||
storage->Get(nsPrintfCString("%d", i), DataStorage_Temporary);
|
||||
EXPECT_STREQ(nsPrintfCString("%d", i).get(), result.get());
|
||||
}
|
||||
// We don't know which entry got evicted, but we can count them.
|
||||
int entries = 0;
|
||||
for (int i = 0; i < 1025; i++) {
|
||||
nsCString result =
|
||||
storage->Get(nsPrintfCString("%d", i), DataStorage_Temporary);
|
||||
if (!result.IsEmpty()) {
|
||||
entries++;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(entries, 1024);
|
||||
nsCString result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("value", result.get());
|
||||
}
|
||||
|
||||
TEST_F(psm_DataStorageTest, ClearPrivateData) {
|
||||
EXPECT_EQ(NS_OK,
|
||||
storage->Put(testKey, privateTestValue, DataStorage_Private));
|
||||
nsCString result = storage->Get(testKey, DataStorage_Private);
|
||||
EXPECT_STREQ("private", result.get());
|
||||
storage->Observe(nullptr, "last-pb-context-exited", nullptr);
|
||||
result = storage->Get(testKey, DataStorage_Private);
|
||||
EXPECT_TRUE(result.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(psm_DataStorageTest, Shutdown) {
|
||||
EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
|
||||
nsCString result = storage->Get(testKey, DataStorage_Persistent);
|
||||
EXPECT_STREQ("value", result.get());
|
||||
// Get "now" (in days) close to when the data was last touched, so we won't
|
||||
// get intermittent failures with the day not matching.
|
||||
int64_t microsecondsPerDay = 24 * 60 * 60 * int64_t(PR_USEC_PER_SEC);
|
||||
int32_t nowInDays = int32_t(PR_Now() / microsecondsPerDay);
|
||||
// Simulate shutdown.
|
||||
storage->Observe(nullptr, "profile-before-change", nullptr);
|
||||
nsCOMPtr<nsIFile> backingFile;
|
||||
EXPECT_EQ(NS_OK, NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(backingFile)));
|
||||
const ::testing::TestInfo* const testInfo =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
NS_ConvertUTF8toUTF16 testName(testInfo->name());
|
||||
EXPECT_EQ(NS_OK, backingFile->Append(testName));
|
||||
nsCOMPtr<nsIInputStream> fileInputStream;
|
||||
EXPECT_EQ(NS_OK, NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
|
||||
backingFile));
|
||||
nsCString data;
|
||||
EXPECT_EQ(NS_OK, NS_ConsumeStream(fileInputStream, UINT32_MAX, data));
|
||||
// The data will be of the form 'test\t0\t<days since the epoch>\tvalue'
|
||||
EXPECT_STREQ(nsPrintfCString("test\t0\t%d\tvalue\n", nowInDays).get(),
|
||||
data.get());
|
||||
}
|
@ -7,7 +7,6 @@
|
||||
SOURCES += [
|
||||
"CertDBTest.cpp",
|
||||
"CoseTest.cpp",
|
||||
"DataStorageTest.cpp",
|
||||
"DeserializeCertTest.cpp",
|
||||
"HMACTest.cpp",
|
||||
"MD4Test.cpp",
|
||||
|
73
security/manager/ssl/tests/unit/test_data_storage.js
Normal file
73
security/manager/ssl/tests/unit/test_data_storage.js
Normal file
@ -0,0 +1,73 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
do_get_profile(); // must be done before instantiating nsIDataStorageManager
|
||||
|
||||
let dataStorageManager = Cc[
|
||||
"@mozilla.org/security/datastoragemanager;1"
|
||||
].getService(Ci.nsIDataStorageManager);
|
||||
let dataStorage = dataStorageManager.get(
|
||||
Ci.nsIDataStorageManager.ClientAuthRememberList
|
||||
);
|
||||
|
||||
add_task(function test_data_storage() {
|
||||
// Test putting a simple key/value pair.
|
||||
dataStorage.put("test", "value", Ci.nsIDataStorage.Persistent);
|
||||
Assert.equal(dataStorage.get("test", Ci.nsIDataStorage.Persistent), "value");
|
||||
|
||||
// Test that getting a value with the same key but of a different type throws.
|
||||
Assert.throws(
|
||||
() => dataStorage.get("test", Ci.nsIDataStorage.Temporary),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"getting a value of a type that hasn't been set yet should throw"
|
||||
);
|
||||
Assert.throws(
|
||||
() => dataStorage.get("test", Ci.nsIDataStorage.Private),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"getting a value of a type that hasn't been set yet should throw"
|
||||
);
|
||||
|
||||
// Put with Temporary/Private data shouldn't affect Persistent data
|
||||
dataStorage.put("test", "temporary", Ci.nsIDataStorage.Temporary);
|
||||
Assert.equal(
|
||||
dataStorage.get("test", Ci.nsIDataStorage.Temporary),
|
||||
"temporary"
|
||||
);
|
||||
dataStorage.put("test", "private", Ci.nsIDataStorage.Private);
|
||||
Assert.equal(dataStorage.get("test", Ci.nsIDataStorage.Private), "private");
|
||||
Assert.equal(dataStorage.get("test", Ci.nsIDataStorage.Persistent), "value");
|
||||
|
||||
// Put of a previously-present key overwrites it (if of the same type)
|
||||
dataStorage.put("test", "new", Ci.nsIDataStorage.Persistent);
|
||||
Assert.equal(dataStorage.get("test", Ci.nsIDataStorage.Persistent), "new");
|
||||
|
||||
// Removal should work
|
||||
dataStorage.remove("test", Ci.nsIDataStorage.Persistent);
|
||||
Assert.throws(
|
||||
() => dataStorage.get("test", Ci.nsIDataStorage.Persistent),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"getting a removed value should throw"
|
||||
);
|
||||
// But removing one type shouldn't affect the others
|
||||
Assert.equal(
|
||||
dataStorage.get("test", Ci.nsIDataStorage.Temporary),
|
||||
"temporary"
|
||||
);
|
||||
Assert.equal(dataStorage.get("test", Ci.nsIDataStorage.Private), "private");
|
||||
// Test removing the other types as well
|
||||
dataStorage.remove("test", Ci.nsIDataStorage.Temporary);
|
||||
dataStorage.remove("test", Ci.nsIDataStorage.Private);
|
||||
Assert.throws(
|
||||
() => dataStorage.get("test", Ci.nsIDataStorage.Temporary),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"getting a removed value should throw"
|
||||
);
|
||||
Assert.throws(
|
||||
() => dataStorage.get("test", Ci.nsIDataStorage.Private),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"getting a removed value should throw"
|
||||
);
|
||||
});
|
@ -118,6 +118,7 @@ tags = remote-settings psm
|
||||
# Requires hard-coded debug-only data
|
||||
skip-if = !debug
|
||||
run-sequentially = hardcoded ports
|
||||
[test_data_storage.js]
|
||||
[test_db_format_pref_new.js]
|
||||
# Android always has and always will use the new format, so
|
||||
# this test doesn't apply.
|
||||
|
Loading…
Reference in New Issue
Block a user