/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_NULL_POINTER, NS_ERROR_UNEXPECTED, NS_OK}; use nsstring::{nsACString, nsCString}; use sfv::Parser; use sfv::SerializeValue; use sfv::{ BareItem, Decimal, Dictionary, FromPrimitive, InnerList, Item, List, ListEntry, Parameters, ParseMore, }; use std::cell::RefCell; use std::ops::Deref; use thin_vec::ThinVec; use xpcom::interfaces::{ nsISFVBareItem, nsISFVBool, nsISFVByteSeq, nsISFVDecimal, nsISFVDictionary, nsISFVInnerList, nsISFVInteger, nsISFVItem, nsISFVItemOrInnerList, nsISFVList, nsISFVParams, nsISFVService, nsISFVString, nsISFVToken, }; use xpcom::{xpcom, xpcom_method, RefPtr, XpCom}; #[no_mangle] pub unsafe extern "C" fn new_sfv_service(result: *mut *const nsISFVService) { let service: RefPtr = SFVService::new(); RefPtr::new(service.coerce::()).forget(&mut *result); } #[xpcom(implement(nsISFVService), atomic)] struct SFVService {} impl SFVService { fn new() -> RefPtr { SFVService::allocate(InitSFVService {}) } xpcom_method!(parse_dictionary => ParseDictionary(header: *const nsACString) -> *const nsISFVDictionary); fn parse_dictionary(&self, header: &nsACString) -> Result, nsresult> { let parsed_dict = Parser::parse_dictionary(&header).map_err(|_| NS_ERROR_FAILURE)?; let sfv_dict = SFVDictionary::new(); sfv_dict.value.replace(parsed_dict); Ok(RefPtr::new(sfv_dict.coerce::())) } xpcom_method!(parse_list => ParseList(field_value: *const nsACString) -> *const nsISFVList); fn parse_list(&self, header: &nsACString) -> Result, nsresult> { let parsed_list = Parser::parse_list(&header).map_err(|_| NS_ERROR_FAILURE)?; let mut nsi_members = Vec::new(); for item_or_inner_list in parsed_list.iter() { nsi_members.push(interface_from_list_entry(item_or_inner_list)?) } let sfv_list = SFVList::allocate(InitSFVList { members: RefCell::new(nsi_members), }); Ok(RefPtr::new(sfv_list.coerce::())) } xpcom_method!(parse_item => ParseItem(header: *const nsACString) -> *const nsISFVItem); fn parse_item(&self, header: &nsACString) -> Result, nsresult> { let parsed_item = Parser::parse_item(&header).map_err(|_| NS_ERROR_FAILURE)?; interface_from_item(&parsed_item) } xpcom_method!(new_integer => NewInteger(value: i64) -> *const nsISFVInteger); fn new_integer(&self, value: i64) -> Result, nsresult> { Ok(RefPtr::new( SFVInteger::new(value).coerce::(), )) } xpcom_method!(new_decimal => NewDecimal(value: f64) -> *const nsISFVDecimal); fn new_decimal(&self, value: f64) -> Result, nsresult> { Ok(RefPtr::new( SFVDecimal::new(value).coerce::(), )) } xpcom_method!(new_bool => NewBool(value: bool) -> *const nsISFVBool); fn new_bool(&self, value: bool) -> Result, nsresult> { Ok(RefPtr::new(SFVBool::new(value).coerce::())) } xpcom_method!(new_string => NewString(value: *const nsACString) -> *const nsISFVString); fn new_string(&self, value: &nsACString) -> Result, nsresult> { Ok(RefPtr::new(SFVString::new(value).coerce::())) } xpcom_method!(new_token => NewToken(value: *const nsACString) -> *const nsISFVToken); fn new_token(&self, value: &nsACString) -> Result, nsresult> { Ok(RefPtr::new(SFVToken::new(value).coerce::())) } xpcom_method!(new_byte_sequence => NewByteSequence(value: *const nsACString) -> *const nsISFVByteSeq); fn new_byte_sequence(&self, value: &nsACString) -> Result, nsresult> { Ok(RefPtr::new( SFVByteSeq::new(value).coerce::(), )) } xpcom_method!(new_parameters => NewParameters() -> *const nsISFVParams); fn new_parameters(&self) -> Result, nsresult> { Ok(RefPtr::new(SFVParams::new().coerce::())) } xpcom_method!(new_item => NewItem(value: *const nsISFVBareItem, params: *const nsISFVParams) -> *const nsISFVItem); fn new_item( &self, value: &nsISFVBareItem, params: &nsISFVParams, ) -> Result, nsresult> { Ok(RefPtr::new( SFVItem::new(value, params).coerce::(), )) } xpcom_method!(new_inner_list => NewInnerList(items: *const thin_vec::ThinVec>>, params: *const nsISFVParams) -> *const nsISFVInnerList); fn new_inner_list( &self, items: &thin_vec::ThinVec>>, params: &nsISFVParams, ) -> Result, nsresult> { let items = items .iter() .cloned() .map(|item| item.ok_or(NS_ERROR_NULL_POINTER)) .collect::, nsresult>>()?; Ok(RefPtr::new( SFVInnerList::new(items, params).coerce::(), )) } xpcom_method!(new_list => NewList(members: *const thin_vec::ThinVec>>) -> *const nsISFVList); fn new_list( &self, members: &thin_vec::ThinVec>>, ) -> Result, nsresult> { let members = members .iter() .cloned() .map(|item| item.ok_or(NS_ERROR_NULL_POINTER)) .collect::, nsresult>>()?; Ok(RefPtr::new(SFVList::new(members).coerce::())) } xpcom_method!(new_dictionary => NewDictionary() -> *const nsISFVDictionary); fn new_dictionary(&self) -> Result, nsresult> { Ok(RefPtr::new( SFVDictionary::new().coerce::(), )) } } #[xpcom(implement(nsISFVInteger, nsISFVBareItem), nonatomic)] struct SFVInteger { value: RefCell, } impl SFVInteger { fn new(value: i64) -> RefPtr { SFVInteger::allocate(InitSFVInteger { value: RefCell::new(value), }) } xpcom_method!(get_value => GetValue() -> i64); fn get_value(&self) -> Result { Ok(*self.value.borrow()) } xpcom_method!(set_value => SetValue(value: i64)); fn set_value(&self, value: i64) -> Result<(), nsresult> { self.value.replace(value); Ok(()) } xpcom_method!(get_type => GetType() -> i32); fn get_type(&self) -> Result { Ok(nsISFVBareItem::INTEGER) } fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVBool, nsISFVBareItem), nonatomic)] struct SFVBool { value: RefCell, } impl SFVBool { fn new(value: bool) -> RefPtr { SFVBool::allocate(InitSFVBool { value: RefCell::new(value), }) } xpcom_method!(get_value => GetValue() -> bool); fn get_value(&self) -> Result { Ok(*self.value.borrow()) } xpcom_method!(set_value => SetValue(value: bool)); fn set_value(&self, value: bool) -> Result<(), nsresult> { self.value.replace(value); Ok(()) } xpcom_method!(get_type => GetType() -> i32); fn get_type(&self) -> Result { Ok(nsISFVBareItem::BOOL) } fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVString, nsISFVBareItem), nonatomic)] struct SFVString { value: RefCell, } impl SFVString { fn new(value: &nsACString) -> RefPtr { SFVString::allocate(InitSFVString { value: RefCell::new(nsCString::from(value)), }) } xpcom_method!( get_value => GetValue( ) -> nsACString ); fn get_value(&self) -> Result { Ok(self.value.borrow().clone()) } xpcom_method!( set_value => SetValue(value: *const nsACString) ); fn set_value(&self, value: &nsACString) -> Result<(), nsresult> { self.value.borrow_mut().assign(value); Ok(()) } xpcom_method!(get_type => GetType() -> i32); fn get_type(&self) -> Result { Ok(nsISFVBareItem::STRING) } fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVToken, nsISFVBareItem), nonatomic)] struct SFVToken { value: RefCell, } impl SFVToken { fn new(value: &nsACString) -> RefPtr { SFVToken::allocate(InitSFVToken { value: RefCell::new(nsCString::from(value)), }) } xpcom_method!( get_value => GetValue( ) -> nsACString ); fn get_value(&self) -> Result { Ok(self.value.borrow().clone()) } xpcom_method!( set_value => SetValue(value: *const nsACString) ); fn set_value(&self, value: &nsACString) -> Result<(), nsresult> { self.value.borrow_mut().assign(value); Ok(()) } xpcom_method!(get_type => GetType() -> i32); fn get_type(&self) -> Result { Ok(nsISFVBareItem::TOKEN) } fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVByteSeq, nsISFVBareItem), nonatomic)] struct SFVByteSeq { value: RefCell, } impl SFVByteSeq { fn new(value: &nsACString) -> RefPtr { SFVByteSeq::allocate(InitSFVByteSeq { value: RefCell::new(nsCString::from(value)), }) } xpcom_method!( get_value => GetValue( ) -> nsACString ); fn get_value(&self) -> Result { Ok(self.value.borrow().clone()) } xpcom_method!( set_value => SetValue(value: *const nsACString) ); fn set_value(&self, value: &nsACString) -> Result<(), nsresult> { self.value.borrow_mut().assign(value); Ok(()) } xpcom_method!(get_type => GetType() -> i32); fn get_type(&self) -> Result { Ok(nsISFVBareItem::BYTE_SEQUENCE) } fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVDecimal, nsISFVBareItem), nonatomic)] struct SFVDecimal { value: RefCell, } impl SFVDecimal { fn new(value: f64) -> RefPtr { SFVDecimal::allocate(InitSFVDecimal { value: RefCell::new(value), }) } xpcom_method!( get_value => GetValue( ) -> f64 ); fn get_value(&self) -> Result { Ok(*self.value.borrow()) } xpcom_method!( set_value => SetValue(value: f64) ); fn set_value(&self, value: f64) -> Result<(), nsresult> { self.value.replace(value); Ok(()) } xpcom_method!(get_type => GetType() -> i32); fn get_type(&self) -> Result { Ok(nsISFVBareItem::DECIMAL) } fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVParams), nonatomic)] struct SFVParams { params: RefCell, } impl SFVParams { fn new() -> RefPtr { SFVParams::allocate(InitSFVParams { params: RefCell::new(Parameters::new()), }) } xpcom_method!( get => Get(key: *const nsACString) -> *const nsISFVBareItem ); fn get(&self, key: &nsACString) -> Result, nsresult> { let key = key.to_utf8(); let params = self.params.borrow(); let param_val = params.get(key.as_ref()); match param_val { Some(val) => interface_from_bare_item(val), None => return Err(NS_ERROR_UNEXPECTED), } } xpcom_method!( set => Set(key: *const nsACString, item: *const nsISFVBareItem) ); fn set(&self, key: &nsACString, item: &nsISFVBareItem) -> Result<(), nsresult> { let key = key.to_utf8().into_owned(); let bare_item = bare_item_from_interface(item)?; self.params.borrow_mut().insert(key, bare_item); Ok(()) } xpcom_method!( delete => Delete(key: *const nsACString) ); fn delete(&self, key: &nsACString) -> Result<(), nsresult> { let key = key.to_utf8(); let mut params = self.params.borrow_mut(); if !params.contains_key(key.as_ref()) { return Err(NS_ERROR_UNEXPECTED); } // Keeps only entries that don't match key params.retain(|k, _| k != key.as_ref()); Ok(()) } xpcom_method!( keys => Keys() -> thin_vec::ThinVec ); fn keys(&self) -> Result, nsresult> { let keys = self.params.borrow(); let keys = keys .keys() .map(nsCString::from) .collect::>(); Ok(keys) } fn from_interface(obj: &nsISFVParams) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVItem, nsISFVItemOrInnerList), nonatomic)] struct SFVItem { value: RefPtr, params: RefPtr, } impl SFVItem { fn new(value: &nsISFVBareItem, params: &nsISFVParams) -> RefPtr { SFVItem::allocate(InitSFVItem { value: RefPtr::new(value), params: RefPtr::new(params), }) } xpcom_method!( get_value => GetValue( ) -> *const nsISFVBareItem ); fn get_value(&self) -> Result, nsresult> { Ok(self.value.clone()) } xpcom_method!( get_params => GetParams( ) -> *const nsISFVParams ); fn get_params(&self) -> Result, nsresult> { Ok(self.params.clone()) } xpcom_method!( serialize => Serialize() -> nsACString ); fn serialize(&self) -> Result { let bare_item = bare_item_from_interface(self.value.deref())?; let params = params_from_interface(self.params.deref())?; let serialized = Item::with_params(bare_item, params) .serialize_value() .map_err(|_| NS_ERROR_FAILURE)?; Ok(nsCString::from(serialized)) } fn from_interface(obj: &nsISFVItem) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVInnerList, nsISFVItemOrInnerList), nonatomic)] struct SFVInnerList { items: RefCell>>, params: RefPtr, } impl SFVInnerList { fn new(items: Vec>, params: &nsISFVParams) -> RefPtr { SFVInnerList::allocate(InitSFVInnerList { items: RefCell::new(items), params: RefPtr::new(params), }) } xpcom_method!( get_items => GetItems() -> thin_vec::ThinVec>> ); fn get_items(&self) -> Result>>, nsresult> { let items = self.items.borrow().iter().cloned().map(Some).collect(); Ok(items) } #[allow(non_snake_case)] unsafe fn SetItems( &self, value: *const thin_vec::ThinVec>>, ) -> nsresult { if value.is_null() { return NS_ERROR_NULL_POINTER; } match (*value) .iter() .map(|v| v.clone().ok_or(NS_ERROR_NULL_POINTER)) .collect::, nsresult>>() { Ok(value) => *self.items.borrow_mut() = value, Err(rv) => return rv, } NS_OK } xpcom_method!( get_params => GetParams( ) -> *const nsISFVParams ); fn get_params(&self) -> Result, nsresult> { Ok(self.params.clone()) } fn from_interface(obj: &nsISFVInnerList) -> &Self { unsafe { ::std::mem::transmute(obj) } } } #[xpcom(implement(nsISFVList, nsISFVSerialize), nonatomic)] struct SFVList { members: RefCell>>, } impl SFVList { fn new(members: Vec>) -> RefPtr { SFVList::allocate(InitSFVList { members: RefCell::new(members), }) } xpcom_method!( get_members => GetMembers() -> thin_vec::ThinVec>> ); fn get_members( &self, ) -> Result>>, nsresult> { Ok(self.members.borrow().iter().cloned().map(Some).collect()) } #[allow(non_snake_case)] unsafe fn SetMembers( &self, value: *const thin_vec::ThinVec>>, ) -> nsresult { if value.is_null() { return NS_ERROR_NULL_POINTER; } match (*value) .iter() .map(|v| v.clone().ok_or(NS_ERROR_NULL_POINTER)) .collect::, nsresult>>() { Ok(value) => *self.members.borrow_mut() = value, Err(rv) => return rv, } NS_OK } xpcom_method!( parse_more => ParseMore(header: *const nsACString) ); fn parse_more(&self, header: &nsACString) -> Result<(), nsresult> { // create List from SFVList to call parse_more on it let mut list = List::new(); let members = self.members.borrow().clone(); for interface_entry in members.iter() { let item_or_inner_list = list_entry_from_interface(interface_entry)?; list.push(item_or_inner_list); } let _ = list.parse_more(&header).map_err(|_| NS_ERROR_FAILURE)?; // replace SFVList's members with new_members let mut new_members = Vec::new(); for item_or_inner_list in list.iter() { new_members.push(interface_from_list_entry(item_or_inner_list)?) } self.members.replace(new_members); Ok(()) } xpcom_method!( serialize => Serialize() -> nsACString ); fn serialize(&self) -> Result { let mut list = List::new(); let members = self.members.borrow().clone(); for interface_entry in members.iter() { let item_or_inner_list = list_entry_from_interface(interface_entry)?; list.push(item_or_inner_list); } let serialized = list.serialize_value().map_err(|_| NS_ERROR_FAILURE)?; Ok(nsCString::from(serialized)) } } #[xpcom(implement(nsISFVDictionary, nsISFVSerialize), nonatomic)] struct SFVDictionary { value: RefCell, } impl SFVDictionary { fn new() -> RefPtr { SFVDictionary::allocate(InitSFVDictionary { value: RefCell::new(Dictionary::new()), }) } xpcom_method!( get => Get(key: *const nsACString) -> *const nsISFVItemOrInnerList ); fn get(&self, key: &nsACString) -> Result, nsresult> { let key = key.to_utf8(); let value = self.value.borrow(); let member_value = value.get(key.as_ref()); match member_value { Some(member) => interface_from_list_entry(member), None => return Err(NS_ERROR_UNEXPECTED), } } xpcom_method!( set => Set(key: *const nsACString, item: *const nsISFVItemOrInnerList) ); fn set(&self, key: &nsACString, member_value: &nsISFVItemOrInnerList) -> Result<(), nsresult> { let key = key.to_utf8().into_owned(); let value = list_entry_from_interface(member_value)?; self.value.borrow_mut().insert(key, value); Ok(()) } xpcom_method!( delete => Delete(key: *const nsACString) ); fn delete(&self, key: &nsACString) -> Result<(), nsresult> { let key = key.to_utf8(); let mut params = self.value.borrow_mut(); if !params.contains_key(key.as_ref()) { return Err(NS_ERROR_UNEXPECTED); } // Keeps only entries that don't match key params.retain(|k, _| k != key.as_ref()); Ok(()) } xpcom_method!( keys => Keys() -> thin_vec::ThinVec ); fn keys(&self) -> Result, nsresult> { let members = self.value.borrow(); let keys = members .keys() .map(nsCString::from) .collect::>(); Ok(keys) } xpcom_method!( parse_more => ParseMore(header: *const nsACString) ); fn parse_more(&self, header: &nsACString) -> Result<(), nsresult> { let _ = self .value .borrow_mut() .parse_more(&header) .map_err(|_| NS_ERROR_FAILURE)?; Ok(()) } xpcom_method!( serialize => Serialize() -> nsACString ); fn serialize(&self) -> Result { let serialized = self .value .borrow() .serialize_value() .map_err(|_| NS_ERROR_FAILURE)?; Ok(nsCString::from(serialized)) } } fn bare_item_from_interface(obj: &nsISFVBareItem) -> Result { let obj = obj .query_interface::() .ok_or(NS_ERROR_UNEXPECTED)?; let mut obj_type: i32 = -1; unsafe { obj.deref().GetType(&mut obj_type); } match obj_type { nsISFVBareItem::BOOL => { let item_value = SFVBool::from_bare_item_interface(obj.deref()).get_value()?; Ok(BareItem::Boolean(item_value)) } nsISFVBareItem::STRING => { let string_itm = SFVString::from_bare_item_interface(obj.deref()).get_value()?; let item_value = (*string_itm.to_utf8()).to_string(); Ok(BareItem::String(item_value)) } nsISFVBareItem::TOKEN => { let token_itm = SFVToken::from_bare_item_interface(obj.deref()).get_value()?; let item_value = (*token_itm.to_utf8()).to_string(); Ok(BareItem::Token(item_value)) } nsISFVBareItem::INTEGER => { let item_value = SFVInteger::from_bare_item_interface(obj.deref()).get_value()?; Ok(BareItem::Integer(item_value)) } nsISFVBareItem::DECIMAL => { let item_value = SFVDecimal::from_bare_item_interface(obj.deref()).get_value()?; let decimal: Decimal = Decimal::from_f64(item_value).ok_or(NS_ERROR_UNEXPECTED)?; Ok(BareItem::Decimal(decimal)) } nsISFVBareItem::BYTE_SEQUENCE => { let token_itm = SFVByteSeq::from_bare_item_interface(obj.deref()).get_value()?; let item_value: String = (*token_itm.to_utf8()).to_string(); Ok(BareItem::ByteSeq(item_value.into_bytes())) } _ => return Err(NS_ERROR_UNEXPECTED), } } fn params_from_interface(obj: &nsISFVParams) -> Result { let params = SFVParams::from_interface(obj).params.borrow(); Ok(params.clone()) } fn item_from_interface(obj: &nsISFVItem) -> Result { let sfv_item = SFVItem::from_interface(obj); let bare_item = bare_item_from_interface(sfv_item.value.deref())?; let parameters = params_from_interface(sfv_item.params.deref())?; Ok(Item::with_params(bare_item, parameters)) } fn inner_list_from_interface(obj: &nsISFVInnerList) -> Result { let sfv_inner_list = SFVInnerList::from_interface(obj); let mut inner_list_items: Vec = vec![]; for item in sfv_inner_list.items.borrow().iter() { let item = item_from_interface(item)?; inner_list_items.push(item); } let inner_list_params = params_from_interface(sfv_inner_list.params.deref())?; Ok(InnerList::with_params(inner_list_items, inner_list_params)) } fn list_entry_from_interface(obj: &nsISFVItemOrInnerList) -> Result { if let Some(nsi_item) = obj.query_interface::() { let item = item_from_interface(nsi_item.deref())?; Ok(ListEntry::Item(item)) } else if let Some(nsi_inner_list) = obj.query_interface::() { let inner_list = inner_list_from_interface(nsi_inner_list.deref())?; Ok(ListEntry::InnerList(inner_list)) } else { return Err(NS_ERROR_UNEXPECTED); } } fn interface_from_bare_item(bare_item: &BareItem) -> Result, nsresult> { let bare_item = match bare_item { BareItem::Boolean(val) => RefPtr::new(SFVBool::new(*val).coerce::()), BareItem::String(val) => { RefPtr::new(SFVString::new(&nsCString::from(val)).coerce::()) } BareItem::Token(val) => { RefPtr::new(SFVToken::new(&nsCString::from(val)).coerce::()) } BareItem::ByteSeq(val) => RefPtr::new( SFVByteSeq::new(&nsCString::from(String::from_utf8(val.to_vec()).unwrap())) .coerce::(), ), BareItem::Decimal(val) => { let val = val .to_string() .parse::() .map_err(|_| NS_ERROR_UNEXPECTED)?; RefPtr::new(SFVDecimal::new(val).coerce::()) } BareItem::Integer(val) => RefPtr::new(SFVInteger::new(*val).coerce::()), }; Ok(bare_item) } fn interface_from_item(item: &Item) -> Result, nsresult> { let nsi_bare_item = interface_from_bare_item(&item.bare_item)?; let nsi_params = interface_from_params(&item.params)?; Ok(RefPtr::new( SFVItem::new(&nsi_bare_item, &nsi_params).coerce::(), )) } fn interface_from_params(params: &Parameters) -> Result, nsresult> { let sfv_params = SFVParams::new(); for (key, value) in params.iter() { sfv_params .params .borrow_mut() .insert(key.clone(), value.clone()); } Ok(RefPtr::new(sfv_params.coerce::())) } fn interface_from_list_entry( member: &ListEntry, ) -> Result, nsresult> { match member { ListEntry::Item(item) => { let nsi_bare_item = interface_from_bare_item(&item.bare_item)?; let nsi_params = interface_from_params(&item.params)?; Ok(RefPtr::new( SFVItem::new(&nsi_bare_item, &nsi_params).coerce::(), )) } ListEntry::InnerList(inner_list) => { let mut nsi_inner_list = Vec::new(); for item in inner_list.items.iter() { let nsi_item = interface_from_item(item)?; nsi_inner_list.push(nsi_item); } let nsi_params = interface_from_params(&inner_list.params)?; Ok(RefPtr::new( SFVInnerList::new(nsi_inner_list, &nsi_params).coerce::(), )) } } }