tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }