tor-browser

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

lib.rs (8117B)


      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 extern crate libc;
      6 extern crate nserror;
      7 extern crate nsstring;
      8 extern crate xpcom;
      9 
     10 mod bag;
     11 
     12 use std::{
     13    borrow::Cow,
     14    convert::{TryFrom, TryInto},
     15 };
     16 
     17 use libc::c_double;
     18 use nserror::{nsresult, NS_ERROR_CANNOT_CONVERT_DATA, NS_OK};
     19 use nsstring::{nsACString, nsAString, nsCString, nsString};
     20 use xpcom::{getter_addrefs, interfaces::nsIVariant, RefPtr};
     21 
     22 pub use crate::bag::HashPropertyBag;
     23 
     24 extern "C" {
     25    fn NS_GetDataType(variant: *const nsIVariant) -> u16;
     26    fn NS_NewStorageNullVariant(result: *mut *const nsIVariant);
     27    fn NS_NewStorageBooleanVariant(value: bool, result: *mut *const nsIVariant);
     28    fn NS_NewStorageIntegerVariant(value: i64, result: *mut *const nsIVariant);
     29    fn NS_NewStorageFloatVariant(value: c_double, result: *mut *const nsIVariant);
     30    fn NS_NewStorageTextVariant(value: *const nsAString, result: *mut *const nsIVariant);
     31    fn NS_NewStorageUTF8TextVariant(value: *const nsACString, result: *mut *const nsIVariant);
     32 }
     33 
     34 // These are the relevant parts of the nsXPTTypeTag enum in xptinfo.h,
     35 // which nsIVariant.idl reflects into the nsIDataType struct class and uses
     36 // to constrain the values of nsIVariant::dataType.
     37 #[repr(u16)]
     38 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
     39 pub enum DataType {
     40    Int32 = 2,
     41    Int64 = 3,
     42    Double = 9,
     43    Bool = 10,
     44    Void = 13,
     45    CharStr = 15,
     46    WCharStr = 16,
     47    StringSizeIs = 20,
     48    WStringSizeIs = 21,
     49    Utf8String = 24,
     50    CString = 25,
     51    AString = 26,
     52    EmptyArray = 254,
     53    Empty = 255,
     54 }
     55 
     56 impl TryFrom<u16> for DataType {
     57    type Error = nsresult;
     58 
     59    /// Converts a raw type tag for an `nsIVariant` into a `DataType` variant.
     60    /// Returns `NS_ERROR_CANNOT_CONVERT_DATA` if the type isn't one that we
     61    /// support.
     62    fn try_from(raw: u16) -> Result<Self, Self::Error> {
     63        Ok(match raw {
     64            2 => DataType::Int32,
     65            3 => DataType::Int64,
     66            9 => DataType::Double,
     67            10 => DataType::Bool,
     68            13 => DataType::Void,
     69            15 => DataType::CharStr,
     70            16 => DataType::WCharStr,
     71            20 => DataType::StringSizeIs,
     72            21 => DataType::WStringSizeIs,
     73            24 => DataType::Utf8String,
     74            25 => DataType::CString,
     75            26 => DataType::AString,
     76            254 => DataType::EmptyArray,
     77            255 => DataType::Empty,
     78            _ => Err(NS_ERROR_CANNOT_CONVERT_DATA)?,
     79        })
     80    }
     81 }
     82 
     83 /// Extension methods implemented on `nsIVariant` types, to make them easier
     84 /// to work with.
     85 pub trait NsIVariantExt {
     86    /// Returns the raw type tag for this variant. Call
     87    /// `DataType::try_from()` on this tag to turn it into a `DataType`.
     88    fn get_data_type(&self) -> u16;
     89 
     90    /// Tries to clone this variant, failing with `NS_ERROR_CANNOT_CONVERT_DATA`
     91    /// if its type is unsupported.
     92    fn try_clone(&self) -> Result<RefPtr<nsIVariant>, nsresult>;
     93 }
     94 
     95 impl NsIVariantExt for nsIVariant {
     96    fn get_data_type(&self) -> u16 {
     97        unsafe { NS_GetDataType(self) }
     98    }
     99 
    100    fn try_clone(&self) -> Result<RefPtr<nsIVariant>, nsresult> {
    101        Ok(match self.get_data_type().try_into()? {
    102            DataType::Bool => bool::from_variant(self)?.into_variant(),
    103            DataType::Int32 => i32::from_variant(self)?.into_variant(),
    104            DataType::Int64 => i64::from_variant(self)?.into_variant(),
    105            DataType::Double => f64::from_variant(self)?.into_variant(),
    106            DataType::AString | DataType::WCharStr | DataType::WStringSizeIs => {
    107                nsString::from_variant(self)?.into_variant()
    108            }
    109            DataType::CString
    110            | DataType::CharStr
    111            | DataType::StringSizeIs
    112            | DataType::Utf8String => nsCString::from_variant(self)?.into_variant(),
    113            DataType::Void | DataType::EmptyArray | DataType::Empty => ().into_variant(),
    114        })
    115    }
    116 }
    117 
    118 pub trait VariantType {
    119    fn type_name() -> Cow<'static, str>;
    120    fn into_variant(self) -> RefPtr<nsIVariant>;
    121    fn from_variant(variant: &nsIVariant) -> Result<Self, nsresult>
    122    where
    123        Self: Sized;
    124 }
    125 
    126 /// Implements traits to convert between variants and their types.
    127 macro_rules! variant {
    128    ($typ:ident, $constructor:ident, $getter:ident) => {
    129        impl VariantType for $typ {
    130            fn type_name() -> Cow<'static, str> {
    131                stringify!($typ).into()
    132            }
    133            fn into_variant(self) -> RefPtr<nsIVariant> {
    134                // getter_addrefs returns a Result<RefPtr<T>, nsresult>,
    135                // but we know that our $constructor is infallible, so we can
    136                // safely unwrap and return the RefPtr.
    137                getter_addrefs(|p| {
    138                    unsafe { $constructor(self.into(), p) };
    139                    NS_OK
    140                })
    141                .unwrap()
    142            }
    143            fn from_variant(variant: &nsIVariant) -> Result<$typ, nsresult> {
    144                let mut result = $typ::default();
    145                let rv = unsafe { variant.$getter(&mut result) };
    146                if rv.succeeded() {
    147                    Ok(result)
    148                } else {
    149                    Err(rv)
    150                }
    151            }
    152        }
    153    };
    154    (* $typ:ident, $constructor:ident, $getter:ident) => {
    155        impl VariantType for $typ {
    156            fn type_name() -> Cow<'static, str> {
    157                stringify!($typ).into()
    158            }
    159            fn into_variant(self) -> RefPtr<nsIVariant> {
    160                // getter_addrefs returns a Result<RefPtr<T>, nsresult>,
    161                // but we know that our $constructor is infallible, so we can
    162                // safely unwrap and return the RefPtr.
    163                getter_addrefs(|p| {
    164                    unsafe { $constructor(&*self, p) };
    165                    NS_OK
    166                })
    167                .unwrap()
    168            }
    169            fn from_variant(variant: &nsIVariant) -> Result<$typ, nsresult> {
    170                let mut result = $typ::new();
    171                let rv = unsafe { variant.$getter(&mut *result) };
    172                if rv.succeeded() {
    173                    Ok(result)
    174                } else {
    175                    Err(rv)
    176                }
    177            }
    178        }
    179    };
    180 }
    181 
    182 // The unit type (()) is a reasonable equivalation of the null variant.
    183 // The macro can't produce its implementations of VariantType, however,
    184 // so we implement them concretely.
    185 impl VariantType for () {
    186    fn type_name() -> Cow<'static, str> {
    187        "()".into()
    188    }
    189    fn into_variant(self) -> RefPtr<nsIVariant> {
    190        // getter_addrefs returns a Result<RefPtr<T>, nsresult>,
    191        // but we know that NS_NewStorageNullVariant is infallible, so we can
    192        // safely unwrap and return the RefPtr.
    193        getter_addrefs(|p| {
    194            unsafe { NS_NewStorageNullVariant(p) };
    195            NS_OK
    196        })
    197        .unwrap()
    198    }
    199    fn from_variant(_variant: &nsIVariant) -> Result<Self, nsresult> {
    200        Ok(())
    201    }
    202 }
    203 
    204 impl<T> VariantType for Option<T>
    205 where
    206    T: VariantType,
    207 {
    208    fn type_name() -> Cow<'static, str> {
    209        format!("Option<{}>", T::type_name()).into()
    210    }
    211    fn into_variant(self) -> RefPtr<nsIVariant> {
    212        match self {
    213            Some(v) => v.into_variant(),
    214            None => ().into_variant(),
    215        }
    216    }
    217    fn from_variant(variant: &nsIVariant) -> Result<Self, nsresult> {
    218        Ok(match variant.get_data_type().try_into() {
    219            Ok(DataType::Void) | Ok(DataType::EmptyArray) | Ok(DataType::Empty) => None,
    220            _ => Some(VariantType::from_variant(variant)?),
    221        })
    222    }
    223 }
    224 
    225 variant!(bool, NS_NewStorageBooleanVariant, GetAsBool);
    226 variant!(i32, NS_NewStorageIntegerVariant, GetAsInt32);
    227 variant!(i64, NS_NewStorageIntegerVariant, GetAsInt64);
    228 variant!(f64, NS_NewStorageFloatVariant, GetAsDouble);
    229 variant!(*nsString, NS_NewStorageTextVariant, GetAsAString);
    230 variant!(*nsCString, NS_NewStorageUTF8TextVariant, GetAsAUTF8String);