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