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