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/. */
7 #include "nsHashPropertyBag.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/SimpleEnumerator.h"
14 #include "nsArrayEnumerator.h"
15 #include "nsIProperty.h"
16 #include "nsIVariant.h"
17 #include "nsThreadUtils.h"
18 #include "nsVariant.h"
20 using mozilla::MakeRefPtr
;
21 using mozilla::SimpleEnumerator
;
22 using mozilla::Unused
;
26 // This function uses C linkage because it's exposed to Rust to support the
27 // `HashPropertyBag` wrapper in the `storage_variant` crate.
28 void NS_NewHashPropertyBag(nsIWritablePropertyBag
** aBag
) {
29 MakeRefPtr
<nsHashPropertyBag
>().forget(aBag
);
35 * nsHashPropertyBagBase implementation.
39 nsHashPropertyBagBase::HasKey(const nsAString
& aName
, bool* aResult
) {
40 *aResult
= mPropertyHash
.Get(aName
, nullptr);
45 nsHashPropertyBagBase::Get(const nsAString
& aName
, nsIVariant
** aResult
) {
46 if (!mPropertyHash
.Get(aName
, aResult
)) {
54 nsHashPropertyBagBase::GetProperty(const nsAString
& aName
,
55 nsIVariant
** aResult
) {
56 bool isFound
= mPropertyHash
.Get(aName
, aResult
);
58 return NS_ERROR_FAILURE
;
65 nsHashPropertyBagBase::SetProperty(const nsAString
& aName
, nsIVariant
* aValue
) {
66 if (NS_WARN_IF(!aValue
)) {
67 return NS_ERROR_INVALID_ARG
;
70 mPropertyHash
.InsertOrUpdate(aName
, aValue
);
76 nsHashPropertyBagBase::DeleteProperty(const nsAString
& aName
) {
77 return mPropertyHash
.Remove(aName
) ? NS_OK
: NS_ERROR_FAILURE
;
81 // nsSimpleProperty class and impl; used for GetEnumerator
84 class nsSimpleProperty final
: public nsIProperty
{
85 ~nsSimpleProperty() = default;
88 nsSimpleProperty(const nsAString
& aName
, nsIVariant
* aValue
)
89 : mName(aName
), mValue(aValue
) {}
95 nsCOMPtr
<nsIVariant
> mValue
;
98 NS_IMPL_ISUPPORTS(nsSimpleProperty
, nsIProperty
)
101 nsSimpleProperty::GetName(nsAString
& aName
) {
107 nsSimpleProperty::GetValue(nsIVariant
** aValue
) {
108 NS_IF_ADDREF(*aValue
= mValue
);
112 // end nsSimpleProperty
115 nsHashPropertyBagBase::GetEnumerator(nsISimpleEnumerator
** aResult
) {
116 nsCOMPtr
<nsIMutableArray
> propertyArray
= nsArray::Create();
117 if (!propertyArray
) {
118 return NS_ERROR_OUT_OF_MEMORY
;
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
);
128 return NS_NewArrayEnumerator(aResult
, propertyArray
, NS_GET_IID(nsIProperty
));
131 #define IMPL_GETSETPROPERTY_AS(Name, Type) \
133 nsHashPropertyBagBase::GetPropertyAs##Name(const nsAString& prop, \
135 nsIVariant* v = mPropertyHash.GetWeak(prop); \
136 if (!v) return NS_ERROR_NOT_AVAILABLE; \
137 return v->GetAs##Name(_retval); \
141 nsHashPropertyBagBase::SetPropertyAs##Name(const nsAString& prop, \
143 nsCOMPtr<nsIWritableVariant> var = new nsVariant(); \
144 var->SetAs##Name(value); \
145 return SetProperty(prop, var); \
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)
156 nsHashPropertyBagBase::GetPropertyAsAString(const nsAString
& aProp
,
157 nsAString
& aResult
) {
158 nsIVariant
* v
= mPropertyHash
.GetWeak(aProp
);
160 return NS_ERROR_NOT_AVAILABLE
;
162 return v
->GetAsAString(aResult
);
166 nsHashPropertyBagBase::GetPropertyAsACString(const nsAString
& aProp
,
167 nsACString
& aResult
) {
168 nsIVariant
* v
= mPropertyHash
.GetWeak(aProp
);
170 return NS_ERROR_NOT_AVAILABLE
;
172 return v
->GetAsACString(aResult
);
176 nsHashPropertyBagBase::GetPropertyAsAUTF8String(const nsAString
& aProp
,
177 nsACString
& aResult
) {
178 nsIVariant
* v
= mPropertyHash
.GetWeak(aProp
);
180 return NS_ERROR_NOT_AVAILABLE
;
182 return v
->GetAsAUTF8String(aResult
);
186 nsHashPropertyBagBase::GetPropertyAsInterface(const nsAString
& aProp
,
189 nsIVariant
* v
= mPropertyHash
.GetWeak(aProp
);
191 return NS_ERROR_NOT_AVAILABLE
;
193 nsCOMPtr
<nsISupports
> val
;
194 nsresult rv
= v
->GetAsISupports(getter_AddRefs(val
));
199 // We have a value, but it's null
203 return val
->QueryInterface(aIID
, aResult
);
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
);
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
);
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
);
231 nsHashPropertyBagBase::SetPropertyAsInterface(const nsAString
& aProp
,
232 nsISupports
* aValue
) {
233 nsCOMPtr
<nsIWritableVariant
> var
= new nsVariant();
234 var
->SetAsISupports(aValue
);
235 return SetProperty(aProp
, var
);
238 void nsHashPropertyBagBase::CopyFrom(const nsHashPropertyBagBase
* aOther
) {
239 for (const auto& entry
: aOther
->mPropertyHash
) {
240 SetProperty(entry
.GetKey(), entry
.GetWeak());
244 void nsHashPropertyBagBase::CopyFrom(nsIPropertyBag
* aOther
) {
245 CopyFrom(this, aOther
);
248 /* static */ void nsHashPropertyBagBase::CopyFrom(nsIWritablePropertyBag
* aTo
,
249 nsIPropertyBag
* aFrom
) {
251 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
252 if (NS_SUCCEEDED(aFrom
->GetEnumerator(getter_AddRefs(enumerator
)))) {
253 for (auto& property
: SimpleEnumerator
<nsIProperty
>(enumerator
)) {
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
)));
263 NS_WARNING("Unable to copy nsIPropertyBag");
268 nsresult
nsGetProperty::operator()(const nsIID
& aIID
,
269 void** aInstancePtr
) const {
273 rv
= mPropBag
->GetPropertyAsInterface(mPropName
, aIID
, aInstancePtr
);
275 rv
= NS_ERROR_NULL_POINTER
;
286 * nsHashPropertyBag implementation.
289 NS_IMPL_ADDREF(nsHashPropertyBag
)
290 NS_IMPL_RELEASE(nsHashPropertyBag
)
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
)
301 * We need to ensure that the hashtable is destroyed on the main thread, as
302 * the nsIVariant values are main-thread only objects.
304 class ProxyHashtableDestructor final
: public mozilla::Runnable
{
306 using HashtableType
= nsInterfaceHashtable
<nsStringHashKey
, nsIVariant
>;
307 explicit ProxyHashtableDestructor(HashtableType
&& aTable
)
308 : mozilla::Runnable("ProxyHashtableDestructor"),
309 mPropertyHash(std::move(aTable
)) {}
313 MOZ_ASSERT(NS_IsMainThread());
314 HashtableType
table(std::move(mPropertyHash
));
319 HashtableType mPropertyHash
;
322 nsHashPropertyBag::~nsHashPropertyBag() {
323 if (!NS_IsMainThread()) {
324 RefPtr
<ProxyHashtableDestructor
> runnable
=
325 new ProxyHashtableDestructor(std::move(mPropertyHash
));
326 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable
));
331 * nsHashPropertyBagOMT implementation
333 NS_IMPL_ADDREF(nsHashPropertyBagOMT
)
334 NS_IMPL_RELEASE(nsHashPropertyBagOMT
)
336 NS_INTERFACE_MAP_BEGIN(nsHashPropertyBagOMT
)
337 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag
)
338 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag
, nsIWritablePropertyBag
)
339 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIWritablePropertyBag
)
340 NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2
)
341 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2
)
344 nsHashPropertyBagOMT::nsHashPropertyBagOMT() {
345 // nsHashPropertyBagOMT is supposed to be used off-main thread. If you need a
346 // single threaded property bag on the main thread, you should consider using
347 // nsHashPropertyBagCC instead, to prevent leaks.
348 MOZ_ASSERT(!NS_IsMainThread());
352 * nsHashPropertyBagCC implementation.
355 NS_IMPL_CYCLE_COLLECTION(nsHashPropertyBagCC
, mPropertyHash
)
357 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHashPropertyBagCC
)
358 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHashPropertyBagCC
)
360 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHashPropertyBagCC
)
361 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag
)
362 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag
, nsIWritablePropertyBag
)
363 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIWritablePropertyBag
)
364 NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2
)
365 NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2
)