1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsHashPropertyBag.h"
8
9 #include <utility>
10
11 #include "mozilla/Attributes.h"
12 #include "mozilla/SimpleEnumerator.h"
13 #include "nsArray.h"
14 #include "nsArrayEnumerator.h"
15 #include "nsIProperty.h"
16 #include "nsIVariant.h"
17 #include "nsThreadUtils.h"
18 #include "nsVariant.h"
19
20 using mozilla::MakeRefPtr;
21 using mozilla::SimpleEnumerator;
22 using mozilla::Unused;
23
24 extern "C" {
25
26 // This function uses C linkage because it's exposed to Rust to support the
27 // `HashPropertyBag` wrapper in the `storage_variant` crate.
NS_NewHashPropertyBag(nsIWritablePropertyBag ** aBag)28 void NS_NewHashPropertyBag(nsIWritablePropertyBag** aBag) {
29 MakeRefPtr<nsHashPropertyBag>().forget(aBag);
30 }
31
32 } // extern "C"
33
34 /*
35 * nsHashPropertyBagBase implementation.
36 */
37
38 NS_IMETHODIMP
HasKey(const nsAString & aName,bool * aResult)39 nsHashPropertyBagBase::HasKey(const nsAString& aName, bool* aResult) {
40 *aResult = mPropertyHash.Get(aName, nullptr);
41 return NS_OK;
42 }
43
44 NS_IMETHODIMP
Get(const nsAString & aName,nsIVariant ** aResult)45 nsHashPropertyBagBase::Get(const nsAString& aName, nsIVariant** aResult) {
46 if (!mPropertyHash.Get(aName, aResult)) {
47 *aResult = nullptr;
48 }
49
50 return NS_OK;
51 }
52
53 NS_IMETHODIMP
GetProperty(const nsAString & aName,nsIVariant ** aResult)54 nsHashPropertyBagBase::GetProperty(const nsAString& aName,
55 nsIVariant** aResult) {
56 bool isFound = mPropertyHash.Get(aName, aResult);
57 if (!isFound) {
58 return NS_ERROR_FAILURE;
59 }
60
61 return NS_OK;
62 }
63
64 NS_IMETHODIMP
SetProperty(const nsAString & aName,nsIVariant * aValue)65 nsHashPropertyBagBase::SetProperty(const nsAString& aName, nsIVariant* aValue) {
66 if (NS_WARN_IF(!aValue)) {
67 return NS_ERROR_INVALID_ARG;
68 }
69
70 mPropertyHash.InsertOrUpdate(aName, aValue);
71
72 return NS_OK;
73 }
74
75 NS_IMETHODIMP
DeleteProperty(const nsAString & aName)76 nsHashPropertyBagBase::DeleteProperty(const nsAString& aName) {
77 return mPropertyHash.Remove(aName) ? NS_OK : NS_ERROR_FAILURE;
78 }
79
80 //
81 // nsSimpleProperty class and impl; used for GetEnumerator
82 //
83
84 class nsSimpleProperty final : public nsIProperty {
85 ~nsSimpleProperty() = default;
86
87 public:
nsSimpleProperty(const nsAString & aName,nsIVariant * aValue)88 nsSimpleProperty(const nsAString& aName, nsIVariant* aValue)
89 : mName(aName), mValue(aValue) {}
90
91 NS_DECL_ISUPPORTS
92 NS_DECL_NSIPROPERTY
93 protected:
94 nsString mName;
95 nsCOMPtr<nsIVariant> mValue;
96 };
97
NS_IMPL_ISUPPORTS(nsSimpleProperty,nsIProperty)98 NS_IMPL_ISUPPORTS(nsSimpleProperty, nsIProperty)
99
100 NS_IMETHODIMP
101 nsSimpleProperty::GetName(nsAString& aName) {
102 aName.Assign(mName);
103 return NS_OK;
104 }
105
106 NS_IMETHODIMP
GetValue(nsIVariant ** aValue)107 nsSimpleProperty::GetValue(nsIVariant** aValue) {
108 NS_IF_ADDREF(*aValue = mValue);
109 return NS_OK;
110 }
111
112 // end nsSimpleProperty
113
114 NS_IMETHODIMP
GetEnumerator(nsISimpleEnumerator ** aResult)115 nsHashPropertyBagBase::GetEnumerator(nsISimpleEnumerator** aResult) {
116 nsCOMPtr<nsIMutableArray> propertyArray = nsArray::Create();
117 if (!propertyArray) {
118 return NS_ERROR_OUT_OF_MEMORY;
119 }
120
121 for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
122 const nsAString& key = iter.Key();
123 nsIVariant* data = iter.UserData();
124 nsSimpleProperty* sprop = new nsSimpleProperty(key, data);
125 propertyArray->AppendElement(sprop);
126 }
127
128 return NS_NewArrayEnumerator(aResult, propertyArray, NS_GET_IID(nsIProperty));
129 }
130
131 #define IMPL_GETSETPROPERTY_AS(Name, Type) \
132 NS_IMETHODIMP \
133 nsHashPropertyBagBase::GetPropertyAs##Name(const nsAString& prop, \
134 Type* _retval) { \
135 nsIVariant* v = mPropertyHash.GetWeak(prop); \
136 if (!v) return NS_ERROR_NOT_AVAILABLE; \
137 return v->GetAs##Name(_retval); \
138 } \
139 \
140 NS_IMETHODIMP \
141 nsHashPropertyBagBase::SetPropertyAs##Name(const nsAString& prop, \
142 Type value) { \
143 nsCOMPtr<nsIWritableVariant> var = new nsVariant(); \
144 var->SetAs##Name(value); \
145 return SetProperty(prop, var); \
146 }
147
IMPL_GETSETPROPERTY_AS(Int32,int32_t)148 IMPL_GETSETPROPERTY_AS(Int32, int32_t)
149 IMPL_GETSETPROPERTY_AS(Uint32, uint32_t)
150 IMPL_GETSETPROPERTY_AS(Int64, int64_t)
151 IMPL_GETSETPROPERTY_AS(Uint64, uint64_t)
152 IMPL_GETSETPROPERTY_AS(Double, double)
153 IMPL_GETSETPROPERTY_AS(Bool, bool)
154
155 NS_IMETHODIMP
156 nsHashPropertyBagBase::GetPropertyAsAString(const nsAString& aProp,
157 nsAString& aResult) {
158 nsIVariant* v = mPropertyHash.GetWeak(aProp);
159 if (!v) {
160 return NS_ERROR_NOT_AVAILABLE;
161 }
162 return v->GetAsAString(aResult);
163 }
164
165 NS_IMETHODIMP
GetPropertyAsACString(const nsAString & aProp,nsACString & aResult)166 nsHashPropertyBagBase::GetPropertyAsACString(const nsAString& aProp,
167 nsACString& aResult) {
168 nsIVariant* v = mPropertyHash.GetWeak(aProp);
169 if (!v) {
170 return NS_ERROR_NOT_AVAILABLE;
171 }
172 return v->GetAsACString(aResult);
173 }
174
175 NS_IMETHODIMP
GetPropertyAsAUTF8String(const nsAString & aProp,nsACString & aResult)176 nsHashPropertyBagBase::GetPropertyAsAUTF8String(const nsAString& aProp,
177 nsACString& aResult) {
178 nsIVariant* v = mPropertyHash.GetWeak(aProp);
179 if (!v) {
180 return NS_ERROR_NOT_AVAILABLE;
181 }
182 return v->GetAsAUTF8String(aResult);
183 }
184
185 NS_IMETHODIMP
GetPropertyAsInterface(const nsAString & aProp,const nsIID & aIID,void ** aResult)186 nsHashPropertyBagBase::GetPropertyAsInterface(const nsAString& aProp,
187 const nsIID& aIID,
188 void** aResult) {
189 nsIVariant* v = mPropertyHash.GetWeak(aProp);
190 if (!v) {
191 return NS_ERROR_NOT_AVAILABLE;
192 }
193 nsCOMPtr<nsISupports> val;
194 nsresult rv = v->GetAsISupports(getter_AddRefs(val));
195 if (NS_FAILED(rv)) {
196 return rv;
197 }
198 if (!val) {
199 // We have a value, but it's null
200 *aResult = nullptr;
201 return NS_OK;
202 }
203 return val->QueryInterface(aIID, aResult);
204 }
205
206 NS_IMETHODIMP
SetPropertyAsAString(const nsAString & aProp,const nsAString & aValue)207 nsHashPropertyBagBase::SetPropertyAsAString(const nsAString& aProp,
208 const nsAString& aValue) {
209 nsCOMPtr<nsIWritableVariant> var = new nsVariant();
210 var->SetAsAString(aValue);
211 return SetProperty(aProp, var);
212 }
213
214 NS_IMETHODIMP
SetPropertyAsACString(const nsAString & aProp,const nsACString & aValue)215 nsHashPropertyBagBase::SetPropertyAsACString(const nsAString& aProp,
216 const nsACString& aValue) {
217 nsCOMPtr<nsIWritableVariant> var = new nsVariant();
218 var->SetAsACString(aValue);
219 return SetProperty(aProp, var);
220 }
221
222 NS_IMETHODIMP
SetPropertyAsAUTF8String(const nsAString & aProp,const nsACString & aValue)223 nsHashPropertyBagBase::SetPropertyAsAUTF8String(const nsAString& aProp,
224 const nsACString& aValue) {
225 nsCOMPtr<nsIWritableVariant> var = new nsVariant();
226 var->SetAsAUTF8String(aValue);
227 return SetProperty(aProp, var);
228 }
229
230 NS_IMETHODIMP
SetPropertyAsInterface(const nsAString & aProp,nsISupports * aValue)231 nsHashPropertyBagBase::SetPropertyAsInterface(const nsAString& aProp,
232 nsISupports* aValue) {
233 nsCOMPtr<nsIWritableVariant> var = new nsVariant();
234 var->SetAsISupports(aValue);
235 return SetProperty(aProp, var);
236 }
237
CopyFrom(const nsHashPropertyBagBase * aOther)238 void nsHashPropertyBagBase::CopyFrom(const nsHashPropertyBagBase* aOther) {
239 for (const auto& entry : aOther->mPropertyHash) {
240 SetProperty(entry.GetKey(), entry.GetWeak());
241 }
242 }
243
CopyFrom(nsIPropertyBag * aOther)244 void nsHashPropertyBagBase::CopyFrom(nsIPropertyBag* aOther) {
245 CopyFrom(this, aOther);
246 }
247
CopyFrom(nsIWritablePropertyBag * aTo,nsIPropertyBag * aFrom)248 /* static */ void nsHashPropertyBagBase::CopyFrom(nsIWritablePropertyBag* aTo,
249 nsIPropertyBag* aFrom) {
250 if (aTo && aFrom) {
251 nsCOMPtr<nsISimpleEnumerator> enumerator;
252 if (NS_SUCCEEDED(aFrom->GetEnumerator(getter_AddRefs(enumerator)))) {
253 for (auto& property : SimpleEnumerator<nsIProperty>(enumerator)) {
254 nsString name;
255 nsCOMPtr<nsIVariant> value;
256 Unused << NS_WARN_IF(NS_FAILED(property->GetName(name)));
257 Unused << NS_WARN_IF(
258 NS_FAILED(property->GetValue(getter_AddRefs(value))));
259 Unused << NS_WARN_IF(
260 NS_FAILED(aTo->SetProperty(std::move(name), value)));
261 }
262 } else {
263 NS_WARNING("Unable to copy nsIPropertyBag");
264 }
265 }
266 }
267
operator ()(const nsIID & aIID,void ** aInstancePtr) const268 nsresult nsGetProperty::operator()(const nsIID& aIID,
269 void** aInstancePtr) const {
270 nsresult rv;
271
272 if (mPropBag) {
273 rv = mPropBag->GetPropertyAsInterface(mPropName, aIID, aInstancePtr);
274 } else {
275 rv = NS_ERROR_NULL_POINTER;
276 *aInstancePtr = 0;
277 }
278
279 if (mErrorPtr) {
280 *mErrorPtr = rv;
281 }
282 return rv;
283 }
284
285 /*
286 * nsHashPropertyBag implementation.
287 */
288
289 NS_IMPL_ADDREF(nsHashPropertyBag)
290 NS_IMPL_RELEASE(nsHashPropertyBag)
291
292 NS_INTERFACE_MAP_BEGIN(nsHashPropertyBag)
293 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag)
294 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag, nsIWritablePropertyBag)
295 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWritablePropertyBag)
296 NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2)
297 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2)
298 NS_INTERFACE_MAP_END
299
300 /*
301 * We need to ensure that the hashtable is destroyed on the main thread, as
302 * the nsIVariant values are main-thread only objects.
303 */
304 class ProxyHashtableDestructor final : public mozilla::Runnable {
305 public:
306 using HashtableType = nsInterfaceHashtable<nsStringHashKey, nsIVariant>;
ProxyHashtableDestructor(HashtableType && aTable)307 explicit ProxyHashtableDestructor(HashtableType&& aTable)
308 : mozilla::Runnable("ProxyHashtableDestructor"),
309 mPropertyHash(std::move(aTable)) {}
310
311 NS_IMETHODIMP
Run()312 Run() override {
313 MOZ_ASSERT(NS_IsMainThread());
314 HashtableType table(std::move(mPropertyHash));
315 return NS_OK;
316 }
317
318 private:
319 HashtableType mPropertyHash;
320 };
321
~nsHashPropertyBag()322 nsHashPropertyBag::~nsHashPropertyBag() {
323 if (!NS_IsMainThread()) {
324 RefPtr<ProxyHashtableDestructor> runnable =
325 new ProxyHashtableDestructor(std::move(mPropertyHash));
326 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
327 }
328 }
329
330 /*
331 * nsHashPropertyBagCC implementation.
332 */
333
334 NS_IMPL_CYCLE_COLLECTION(nsHashPropertyBagCC, mPropertyHash)
335
336 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHashPropertyBagCC)
337 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHashPropertyBagCC)
338
339 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHashPropertyBagCC)
340 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag)
341 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag, nsIWritablePropertyBag)
342 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWritablePropertyBag)
343 NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2)
344 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2)
345 NS_INTERFACE_MAP_END
346