bag.rs (4803B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use nserror::{nsresult, NS_ERROR_CANNOT_CONVERT_DATA, NS_OK}; 6 use nsstring::nsString; 7 use xpcom::{ 8 getter_addrefs, 9 interfaces::{nsIProperty, nsIPropertyBag, nsIWritablePropertyBag}, 10 RefPtr, XpCom, 11 }; 12 13 use crate::{NsIVariantExt, VariantType}; 14 15 extern "C" { 16 fn NS_NewHashPropertyBag(bag: *mut *const nsIWritablePropertyBag); 17 } 18 19 /// A hash property bag backed by storage variant values. 20 pub struct HashPropertyBag(RefPtr<nsIWritablePropertyBag>); 21 22 // This is safe as long as our `nsIWritablePropertyBag` is an instance of 23 // `mozilla::nsHashPropertyBag`, which is atomically reference counted, and 24 // all properties are backed by `Storage*Variant`s, all of which are 25 // thread-safe. 26 unsafe impl Send for HashPropertyBag {} 27 unsafe impl Sync for HashPropertyBag {} 28 29 impl Default for HashPropertyBag { 30 fn default() -> HashPropertyBag { 31 // This is safe to unwrap because `NS_NewHashPropertyBag` is infallible. 32 let bag = getter_addrefs(|p| { 33 unsafe { NS_NewHashPropertyBag(p) }; 34 NS_OK 35 }) 36 .unwrap(); 37 HashPropertyBag(bag) 38 } 39 } 40 41 impl HashPropertyBag { 42 /// Creates an empty property bag. 43 #[inline] 44 pub fn new() -> Self { 45 Self::default() 46 } 47 48 /// Creates a property bag from an instance of `nsIPropertyBag`, cloning its 49 /// contents. The `source` bag can only contain primitive values for which 50 /// the `VariantType` trait is implemented. Attempting to clone a bag with 51 /// unsupported types, such as arrays, interface pointers, and `jsval`s, 52 /// fails with `NS_ERROR_CANNOT_CONVERT_DATA`. 53 /// 54 /// `clone_from_bag` can be used to clone a thread-unsafe `nsIPropertyBag`, 55 /// like one passed from JavaScript via XPConnect, into one that can be 56 /// shared across threads. 57 pub fn clone_from_bag(source: &nsIPropertyBag) -> Result<Self, nsresult> { 58 let enumerator = getter_addrefs(|p| unsafe { source.GetEnumerator(p) })?; 59 let b = HashPropertyBag::new(); 60 while { 61 let mut has_more = false; 62 unsafe { enumerator.HasMoreElements(&mut has_more) }.to_result()?; 63 has_more 64 } { 65 let element = getter_addrefs(|p| unsafe { enumerator.GetNext(p) })?; 66 let property = element 67 .query_interface::<nsIProperty>() 68 .ok_or(NS_ERROR_CANNOT_CONVERT_DATA)?; 69 let mut name = nsString::new(); 70 unsafe { property.GetName(&mut *name) }.to_result()?; 71 let value = getter_addrefs(|p| unsafe { property.GetValue(p) })?; 72 unsafe { b.0.SetProperty(&*name, value.try_clone()?.coerce()) }.to_result()?; 73 } 74 Ok(b) 75 } 76 77 /// Returns the value for a property name. Fails with `NS_ERROR_FAILURE` 78 /// if the property doesn't exist, or `NS_ERROR_CANNOT_CONVERT_DATA` if the 79 /// property exists, but is not of the value type `V`. 80 pub fn get<K, V>(&self, name: K) -> Result<V, nsresult> 81 where 82 K: AsRef<str>, 83 V: VariantType, 84 { 85 getter_addrefs(|p| unsafe { self.0.GetProperty(&*nsString::from(name.as_ref()), p) }) 86 .and_then(|v| V::from_variant(v.coerce())) 87 } 88 89 /// Returns the value for a property name, or the default if not set or 90 /// not of the value type `V`. 91 #[inline] 92 pub fn get_or_default<K, V>(&self, name: K) -> V 93 where 94 K: AsRef<str>, 95 V: VariantType + Default, 96 { 97 self.get(name).unwrap_or_default() 98 } 99 100 /// Sets a property with the name to the value, overwriting any previous 101 /// value. 102 pub fn set<K, V>(&mut self, name: K, value: V) 103 where 104 K: AsRef<str>, 105 V: VariantType, 106 { 107 let v = value.into_variant(); 108 unsafe { 109 // This is safe to unwrap because 110 // `nsHashPropertyBagBase::SetProperty` only returns an error if `v` 111 // is a null pointer. 112 self.0 113 .SetProperty(&*nsString::from(name.as_ref()), v.coerce()) 114 .to_result() 115 .unwrap() 116 } 117 } 118 119 /// Deletes a property with the name. Returns `true` if the property 120 /// was previously in the bag, `false` if not. 121 pub fn delete(&mut self, name: impl AsRef<str>) -> bool { 122 unsafe { 123 self.0 124 .DeleteProperty(&*nsString::from(name.as_ref())) 125 .to_result() 126 .is_ok() 127 } 128 } 129 130 /// Returns a reference to the backing `nsIWritablePropertyBag`. 131 #[inline] 132 pub fn bag(&self) -> &nsIWritablePropertyBag { 133 &self.0 134 } 135 }