1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 #include "KeyValueStorage.h"
8
9 #include "nsAppDirectoryServiceDefs.h"
10 #include "nsDirectoryServiceUtils.h"
11 #include "KeyValueStorage.h"
12 #include "nsServiceManagerUtils.h"
13 #include "nsVariant.h"
14
15 namespace mozilla {
16
17 class DatabaseCallback final : public nsIKeyValueDatabaseCallback {
18 public:
19 NS_DECL_ISUPPORTS
20
DatabaseCallback(RefPtr<nsIKeyValueDatabase> & aDatabase)21 explicit DatabaseCallback(RefPtr<nsIKeyValueDatabase>& aDatabase)
22 : mDatabase(aDatabase) {}
Resolve(nsIKeyValueDatabase * aDatabase)23 NS_IMETHOD Resolve(nsIKeyValueDatabase* aDatabase) override {
24 if (!aDatabase) {
25 mResultPromise.Reject(NS_ERROR_FAILURE, __func__);
26 }
27 mDatabase = aDatabase;
28 mResultPromise.Resolve(true, __func__);
29 return NS_OK;
30 }
Reject(const nsACString &)31 NS_IMETHOD Reject(const nsACString&) override {
32 mResultPromise.Reject(NS_ERROR_FAILURE, __func__);
33 return NS_OK;
34 }
Ensure()35 RefPtr<GenericPromise> Ensure() { return mResultPromise.Ensure(__func__); }
36
37 protected:
38 ~DatabaseCallback() = default;
39 RefPtr<nsIKeyValueDatabase>& mDatabase;
40 MozPromiseHolder<GenericPromise> mResultPromise;
41 };
42 NS_IMPL_ISUPPORTS(DatabaseCallback, nsIKeyValueDatabaseCallback);
43
Init()44 RefPtr<GenericPromise> KeyValueStorage::Init() {
45 MOZ_ASSERT(NS_IsMainThread());
46 MOZ_ASSERT(!mDatabaseName.IsEmpty());
47 nsresult rv;
48
49 nsCOMPtr<nsIFile> profileDir;
50 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
51 getter_AddRefs(profileDir));
52 if (NS_WARN_IF(NS_FAILED(rv))) {
53 return GenericPromise::CreateAndReject(rv, __func__);
54 }
55 MOZ_ASSERT(profileDir);
56
57 rv = profileDir->AppendNative(NS_LITERAL_CSTRING("mediacapabilities"));
58 if (NS_WARN_IF(NS_FAILED(rv))) {
59 return GenericPromise::CreateAndReject(rv, __func__);
60 }
61
62 rv = profileDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
63 if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
64 return GenericPromise::CreateAndReject(rv, __func__);
65 }
66
67 nsCOMPtr<nsIKeyValueService> keyValueService =
68 do_GetService("@mozilla.org/key-value-service;1", &rv);
69 if (NS_WARN_IF(NS_FAILED(rv))) {
70 return GenericPromise::CreateAndReject(rv, __func__);
71 }
72 MOZ_ASSERT(keyValueService);
73
74 auto callback = MakeRefPtr<DatabaseCallback>(mDatabase);
75
76 nsString path;
77 profileDir->GetPath(path);
78 keyValueService->GetOrCreate(callback, NS_ConvertUTF16toUTF8(path),
79 mDatabaseName);
80 return callback->Ensure();
81 }
82
83 class VoidCallback final : public nsIKeyValueVoidCallback {
84 public:
85 NS_DECL_ISUPPORTS
86
VoidCallback(const RefPtr<KeyValueStorage> & aOwner)87 explicit VoidCallback(const RefPtr<KeyValueStorage>& aOwner)
88 : nsIKeyValueVoidCallback(), mOwner(aOwner) {}
89
Resolve()90 NS_IMETHOD Resolve() override {
91 mResultPromise.Resolve(true, __func__);
92 return NS_OK;
93 }
Reject(const nsACString &)94 NS_IMETHOD Reject(const nsACString&) override {
95 mResultPromise.Reject(NS_ERROR_FAILURE, __func__);
96 return NS_OK;
97 }
Ensure(const char * aMethodName)98 RefPtr<GenericPromise> Ensure(const char* aMethodName) {
99 return mResultPromise.Ensure(aMethodName);
100 }
101
102 protected:
103 ~VoidCallback() = default;
104 MozPromiseHolder<GenericPromise> mResultPromise;
105 RefPtr<KeyValueStorage> mOwner;
106 };
107 NS_IMPL_ISUPPORTS(VoidCallback, nsIKeyValueVoidCallback);
108
Put(const nsACString & aKey,int32_t aValue)109 RefPtr<GenericPromise> KeyValueStorage::Put(const nsACString& aKey,
110 int32_t aValue) {
111 MOZ_ASSERT(NS_IsMainThread());
112 MOZ_ASSERT(mDatabase);
113 MOZ_ASSERT(!mDatabaseName.IsEmpty());
114
115 auto value = MakeRefPtr<nsVariant>();
116 nsresult rv = value->SetAsInt32(aValue);
117 if (NS_WARN_IF(NS_FAILED(rv))) {
118 return GenericPromise::CreateAndReject(rv, __func__);
119 }
120
121 auto callback = MakeRefPtr<VoidCallback>(this);
122 rv = mDatabase->Put(callback, aKey, value);
123 if (NS_WARN_IF(NS_FAILED(rv))) {
124 return GenericPromise::CreateAndReject(rv, __func__);
125 }
126 return callback->Ensure(__func__);
127 }
128
Put(const nsACString & aName,const nsACString & aKey,int32_t aValue)129 RefPtr<GenericPromise> KeyValueStorage::Put(const nsACString& aName,
130 const nsACString& aKey,
131 int32_t aValue) {
132 if (!mDatabase || !mDatabaseName.Equals(aName)) {
133 mDatabaseName = aName;
134 RefPtr<KeyValueStorage> self = this;
135 const nsCString key(aKey);
136 return Init()->Then(
137 GetCurrentThreadSerialEventTarget(), __func__,
138 [self, key, aValue](bool) { return self->Put(key, aValue); },
139 [](nsresult rv) {
140 return GenericPromise::CreateAndReject(rv, __func__);
141 });
142 }
143 return Put(aKey, aValue);
144 }
145
146 class GetValueCallback final : public nsIKeyValueVariantCallback {
147 public:
148 NS_DECL_ISUPPORTS
149
Resolve(nsIVariant * aResult)150 NS_IMETHOD Resolve(nsIVariant* aResult) override {
151 int32_t value = 0;
152 Unused << aResult->GetAsInt32(&value);
153 mResultPromise.Resolve(value, __func__);
154 return NS_OK;
155 }
Reject(const nsACString &)156 NS_IMETHOD Reject(const nsACString&) override {
157 mResultPromise.Reject(NS_ERROR_FAILURE, __func__);
158 return NS_OK;
159 }
Ensure()160 RefPtr<KeyValueStorage::GetPromise> Ensure() {
161 return mResultPromise.Ensure(__func__);
162 }
163
164 protected:
165 ~GetValueCallback() = default;
166
167 private:
168 MozPromiseHolder<KeyValueStorage::GetPromise> mResultPromise;
169 };
170 NS_IMPL_ISUPPORTS(GetValueCallback, nsIKeyValueVariantCallback);
171
Get(const nsACString & aKey)172 RefPtr<KeyValueStorage::GetPromise> KeyValueStorage::Get(
173 const nsACString& aKey) {
174 MOZ_ASSERT(NS_IsMainThread());
175 MOZ_ASSERT(mDatabase);
176 MOZ_ASSERT(!mDatabaseName.IsEmpty());
177
178 RefPtr<nsVariant> defaultValue = new nsVariant;
179 nsresult rv = defaultValue->SetAsInt32(-1);
180 if (NS_WARN_IF(NS_FAILED(rv))) {
181 return KeyValueStorage::GetPromise::CreateAndReject(rv, __func__);
182 }
183
184 auto callback = MakeRefPtr<GetValueCallback>();
185 rv = mDatabase->Get(callback, aKey, defaultValue);
186 if (NS_WARN_IF(NS_FAILED(rv))) {
187 return KeyValueStorage::GetPromise::CreateAndReject(rv, __func__);
188 }
189 return callback->Ensure();
190 }
191
Get(const nsACString & aName,const nsACString & aKey)192 RefPtr<KeyValueStorage::GetPromise> KeyValueStorage::Get(
193 const nsACString& aName, const nsACString& aKey) {
194 if (!mDatabase || !mDatabaseName.Equals(aName)) {
195 mDatabaseName = aName;
196 RefPtr<KeyValueStorage> self = this;
197 const nsCString key(aKey);
198 return Init()->Then(
199 GetCurrentThreadSerialEventTarget(), __func__,
200 [self, key](bool) { return self->Get(key); },
201 [](nsresult rv) {
202 return KeyValueStorage::GetPromise::CreateAndReject(rv, __func__);
203 });
204 }
205 return Get(aKey);
206 }
207
Clear()208 RefPtr<GenericPromise> KeyValueStorage::Clear() {
209 MOZ_ASSERT(NS_IsMainThread());
210 MOZ_ASSERT(mDatabase);
211 MOZ_ASSERT(!mDatabaseName.IsEmpty());
212
213 auto callback = MakeRefPtr<VoidCallback>(this);
214 nsresult rv = mDatabase->Clear(callback);
215 if (NS_WARN_IF(NS_FAILED(rv))) {
216 return GenericPromise::CreateAndReject(rv, __func__);
217 }
218 return callback->Ensure(__func__);
219 }
220
Clear(const nsACString & aName)221 RefPtr<GenericPromise> KeyValueStorage::Clear(const nsACString& aName) {
222 if (!mDatabase || !mDatabaseName.Equals(aName)) {
223 mDatabaseName = aName;
224 RefPtr<KeyValueStorage> self = this;
225 return Init()->Then(
226 GetCurrentThreadSerialEventTarget(), __func__,
227 [self](bool) { return self->Clear(); },
228 [](nsresult rv) {
229 return GenericPromise::CreateAndReject(rv, __func__);
230 });
231 }
232 return Clear();
233 }
234
235 } // namespace mozilla
236