diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/base/http-sfv/Cargo.toml | 15 | ||||
-rw-r--r-- | netwerk/base/http-sfv/SFVService.cpp | 39 | ||||
-rw-r--r-- | netwerk/base/http-sfv/SFVService.h | 14 | ||||
-rw-r--r-- | netwerk/base/http-sfv/moz.build | 17 | ||||
-rw-r--r-- | netwerk/base/http-sfv/nsIStructuredFieldValues.idl | 290 | ||||
-rw-r--r-- | netwerk/base/http-sfv/src/lib.rs | 873 |
6 files changed, 1248 insertions, 0 deletions
diff --git a/netwerk/base/http-sfv/Cargo.toml b/netwerk/base/http-sfv/Cargo.toml new file mode 100644 index 0000000000..4625cdf974 --- /dev/null +++ b/netwerk/base/http-sfv/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "http_sfv" +version = "0.1.0" +authors = ["barabass <yalyna.ts@gmail.com>"] +edition = "2018" +license = "MPL-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nserror = { path = "../../../xpcom/rust/nserror" } +nsstring = { path = "../../../xpcom/rust/nsstring" } +sfv = "0.9.1" +xpcom = { path = "../../../xpcom/rust/xpcom" } +thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } diff --git a/netwerk/base/http-sfv/SFVService.cpp b/netwerk/base/http-sfv/SFVService.cpp new file mode 100644 index 0000000000..9759993e7f --- /dev/null +++ b/netwerk/base/http-sfv/SFVService.cpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPtr.h" +#include "nsCOMPtr.h" +#include "SFVService.h" + +// This anonymous namespace prevents outside C++ code from improperly accessing +// these implementation details. +namespace { +extern "C" { +// Implemented in Rust. +void new_sfv_service(nsISFVService** result); +} + +static mozilla::StaticRefPtr<nsISFVService> sService; +} // namespace + +namespace mozilla::net { + +already_AddRefed<nsISFVService> GetSFVService() { + nsCOMPtr<nsISFVService> service; + + if (sService) { + service = sService; + } else { + new_sfv_service(getter_AddRefs(service)); + sService = service; + mozilla::ClearOnShutdown(&sService); + } + + return service.forget(); +} + +} // namespace mozilla::net diff --git a/netwerk/base/http-sfv/SFVService.h b/netwerk/base/http-sfv/SFVService.h new file mode 100644 index 0000000000..4016951609 --- /dev/null +++ b/netwerk/base/http-sfv/SFVService.h @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "nsIStructuredFieldValues.h" +namespace mozilla { +namespace net { + +already_AddRefed<nsISFVService> GetSFVService(); + +} // namespace net +} // namespace mozilla diff --git a/netwerk/base/http-sfv/moz.build b/netwerk/base/http-sfv/moz.build new file mode 100644 index 0000000000..ed857c91a8 --- /dev/null +++ b/netwerk/base/http-sfv/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + "nsIStructuredFieldValues.idl", +] + +XPIDL_MODULE = "http-sfv" + +EXPORTS.mozilla.net += ["SFVService.h"] + +SOURCES += ["SFVService.cpp"] + +FINAL_LIBRARY = "xul" diff --git a/netwerk/base/http-sfv/nsIStructuredFieldValues.idl b/netwerk/base/http-sfv/nsIStructuredFieldValues.idl new file mode 100644 index 0000000000..3f02b33953 --- /dev/null +++ b/netwerk/base/http-sfv/nsIStructuredFieldValues.idl @@ -0,0 +1,290 @@ +/* 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/. */ + +#include "nsISupports.idl" + +/** + * Conceptually, there are three types of structured field (header) values: + * + * Item - can be an Integer, Decimal, String, Token, Byte Sequence, or Boolean. + * It can have associated Parameters. + * List - array of zero or more members, each of which can be an Item or an InnerList, + * both of which can be Parameterized. + * Dictionary - ordered map of name-value pairs, where the names are short textual strings + * and the values are Items or arrays of Items (represented with InnerList), + * both of which can be Parameterized. There can be zero or more members, + * and their names are unique in the scope of the Dictionary they occur within. + * + * + * There's also a few primitive types used to construct structured field values: + * - BareItem used as Item's value or as a parameter value in Parameters. + * - Parameters are an ordered map of key-value pairs that are associated with an Item or InnerList. + * The keys are unique within the scope the Parameters they occur within, and the values are BareItem. + * - InnerList is an array of zero or more Items. Can have Parameters. + * - ListEntry represents either Item or InnerList as a member of List or as member-value in Dictionary. + */ + + + +/** + * nsISFVBareItem is a building block for Item header value (nsISFVItem) and Parameters (nsISFVParams). + * It can be of type BOOL, STRING, DECIMAL, INTEGER, TOKEN, BYTE_SEQUENCE. + * Each type is represented by its own interface which is used to create + * a bare item of that type. + */ +[scriptable, builtinclass, uuid(7072853f-215b-4a8a-92e5-9732bccc377b)] +interface nsISFVBareItem : nsISupports { + const long BOOL = 1; + const long STRING = 2; + const long DECIMAL = 3; + const long INTEGER = 4; + const long TOKEN = 5; + const long BYTE_SEQUENCE = 6; + + /** + * Returns value associated with type of bare item. + * Used to identify type of bare item without querying for interface + * (like nsISFVString, etc). + */ + readonly attribute long type; +}; + +[scriptable, builtinclass, uuid(843eea44-990a-422c-bbf2-2aa4ba9ee4d2)] +interface nsISFVInteger : nsISFVBareItem { + attribute long long value; +}; + +[scriptable, builtinclass, uuid(df6a0787-7caa-4fef-b145-08c1104c2fde)] +interface nsISFVString : nsISFVBareItem { + attribute ACString value; +}; + +[scriptable, builtinclass, uuid(d263c6d7-4123-4c39-a121-ccf874a19012)] +interface nsISFVBool : nsISFVBareItem { + attribute boolean value; +}; + +[scriptable, builtinclass, uuid(1098da8b-b4df-4526-b985-53dbd4160ad2)] +interface nsISFVDecimal : nsISFVBareItem { + attribute double value; +}; + +[scriptable, builtinclass, uuid(8ad33d52-b9b2-4a17-8aa8-991250fc1214)] +interface nsISFVToken : nsISFVBareItem { + attribute ACString value; +}; + +[scriptable, builtinclass, uuid(887eaef0-19fe-42bc-9a42-9ff773aa8fea)] +interface nsISFVByteSeq : nsISFVBareItem { + attribute ACString value; +}; + + +/** + * nsISFVParams represents parameters, key-value pairs of ACString and nsISFVBareItem, + * which parametrize Item type header or InnerList type withing List type header. + */ +[scriptable, builtinclass, uuid(b1a397d7-3333-43e7-993a-fbe8ab90ee96)] +interface nsISFVParams : nsISupports { + /** + * Return value (nsISFVBareItem) stored for key, if it is present + * + * @throws NS_ERROR_UNEXPECTED if the key does not exist in parameters. + */ + nsISFVBareItem get(in ACString key); + + /** + * Insert a new key-value pair. + * If an equivalent key already exists: the key remains and retains in its place in the order, + * its corresponding value is updated with the new value. + * + * @throws NS_ERROR_UNEXPECTED if supplied item does not implement nsISFVBareItem interface. + */ + void set(in ACString key, in nsISFVBareItem item); + + /** + * Remove the key-value pair equivalent to key. + * + * @throws NS_ERROR_UNEXPECTED upon attempt to delete key that does not exist in parameters. + */ + void delete(in ACString key); + + /** + * Returns array of keys. + */ + Array<ACString> keys(); +}; + +/** + * nsISFVParametrizable is implemented for types that + * can be parametrized with nsISFVParams + */ +[scriptable, builtinclass, uuid(6c0399f8-01de-4b25-b339-68e35e8d2e49)] +interface nsISFVParametrizable : nsISupports { + readonly attribute nsISFVParams params; +}; + +/** + * nsISFVItemOrInnerList represents member in nsISFVList + * or member-value in nsISFVDictionary. + * nsISFVItemOrInnerList is implemented for + * nsISFVItem or nsISFVInnerList, both of which are used + * to create nsISFVList and nsISFVDictionary. + */ +[scriptable, builtinclass, uuid(99ac1b56-b5b3-44e7-ad96-db7444aae4b2)] +interface nsISFVItemOrInnerList : nsISFVParametrizable { +}; + +/** + * nsISFVSerialize indicates that object can be serialized into ACString. + */ +[scriptable, builtinclass, uuid(28b9215d-c131-413c-9482-0004a371a5ec)] +interface nsISFVSerialize : nsISupports { + ACString serialize(); +}; + +/** + * nsISFVItem represents Item structured header value. + */ +[scriptable, builtinclass, uuid(abe8826b-6af7-4e54-bd2c-46ab231700ce)] +interface nsISFVItem : nsISFVItemOrInnerList { + readonly attribute nsISFVBareItem value; + ACString serialize(); +}; + +/** + * nsISFVInnerList can be used as a member of nsISFVList + * or a member-value of nsISFVDictionary. + */ +[scriptable, builtinclass, uuid(b2e52be2-8488-41b2-9ee2-3c48d92d095c)] +interface nsISFVInnerList : nsISFVItemOrInnerList { + attribute Array<nsISFVItem> items; +}; + +/** + * nsISFVList represents List structured header value. + */ +[scriptable, builtinclass, uuid(02bb92a6-d1de-449c-b54f-d137f30c613d)] +interface nsISFVList : nsISFVSerialize { + /** + * Returns array of members. + * QueryInterface can be used on a member to get more specific type. + */ + attribute Array<nsISFVItemOrInnerList> members; + + /** + * In case when header value is split across lines, it's possible + * this method parses supplied line and merges it with members of existing object. + */ + void parseMore(in ACString header); +}; + +/** + * nsISFVDictionary represents nsISFVDictionary structured header value. + */ +[scriptable, builtinclass, uuid(6642a7fe-7026-4eba-b730-05e230ee3437)] +interface nsISFVDictionary : nsISFVSerialize { + + /** + * Return value (nsISFVItemOrInnerList) stored for key, if it is present. + * QueryInterface can be used on a value to get more specific type. + * + * @throws NS_ERROR_UNEXPECTED if the key does not exist in parameters. + */ + nsISFVItemOrInnerList get(in ACString key); + + /** + * Insert a new key-value pair. + * If an equivalent key already exists: the key remains and retains in its place in the order, + * its corresponding value is updated with the new value. + * + * @throws NS_ERROR_UNEXPECTED if supplied item does not implement nsISFVItemOrInnerList interface. + */ + void set(in ACString key, in nsISFVItemOrInnerList member_value); + + /** + * Remove the key-value pair equivalent to key. + * + * @throws NS_ERROR_UNEXPECTED upon attempt to delete key that does not exist in parameters. + */ + void delete(in ACString key); + + /** + * Returns array of keys. + */ + Array<ACString> keys(); + + /** + * In case when header value is split across lines, it's possible + * this method parses supplied line and merges it with members of existing object. + */ + void parseMore(in ACString header); +}; + + +/** + * nsISFVService provides a set of functions for working with HTTP header value as an object. + * It exposes functions for creating object from string containing header value, + * as well as individual components for manual structured header object creation. + */ +[scriptable, builtinclass, uuid(049f4be1-2f22-4438-a8da-518552ed390c)] +interface nsISFVService: nsISupports +{ + /** + * Parses provided string into Dictionary header value (nsISFVDictionary). + * + * @throws NS_ERROR_FAILURE if parsing fails. + */ + nsISFVDictionary parseDictionary(in ACString header); + + /** + * Parses provided string into List header value (nsISFVList). + * + * @throws NS_ERROR_FAILURE if parsing fails. + */ + nsISFVList parseList(in ACString header); + + /** + * Parses provided string into Item header value (nsISFVItem). + * + * @throws NS_ERROR_FAILURE if parsing fails. + */ + nsISFVItem parseItem(in ACString header); + + /** + * The following functions create bare item of specific type. + */ + nsISFVInteger newInteger(in long long value); + nsISFVBool newBool(in bool value); + nsISFVDecimal newDecimal(in double value); + nsISFVString newString(in ACString value); + nsISFVByteSeq newByteSequence(in ACString value); + nsISFVToken newToken(in ACString value); + + /** + * Creates nsISFVParams with no parameters. In other words, it's an empty map byt default. + */ + nsISFVParams newParameters(); + + /** + * Creates nsISFVInnerList from nsISFVItem array and nsISFVParams. + */ + nsISFVInnerList newInnerList(in Array<nsISFVItem> items, in nsISFVParams params); + + /** + * Creates nsISFVItem, which represents Item header value, from nsISFVBareItem and associated nsISFVParams. + */ + nsISFVItem newItem(in nsISFVBareItem value, in nsISFVParams params); + + /** + * Creates nsISFVList, which represents List header value, from array of nsISFVItemOrInnerList. + * nsISFVItemOrInnerList represens either Item (nsISFVItem) or Inner List (nsISFVInnerList). + */ + nsISFVList newList(in Array<nsISFVItemOrInnerList> members); + + /** + * Creates nsISFVDictionary representing Dictionary header value. It is empty by default. + */ + nsISFVDictionary newDictionary(); +}; diff --git a/netwerk/base/http-sfv/src/lib.rs b/netwerk/base/http-sfv/src/lib.rs new file mode 100644 index 0000000000..fe669f5f3f --- /dev/null +++ b/netwerk/base/http-sfv/src/lib.rs @@ -0,0 +1,873 @@ +/* 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> = SFVService::new(); + RefPtr::new(service.coerce::<nsISFVService>()).forget(&mut *result); +} + +#[xpcom(implement(nsISFVService), atomic)] +struct SFVService {} + +impl SFVService { + fn new() -> RefPtr<SFVService> { + SFVService::allocate(InitSFVService {}) + } + + xpcom_method!(parse_dictionary => ParseDictionary(header: *const nsACString) -> *const nsISFVDictionary); + fn parse_dictionary(&self, header: &nsACString) -> Result<RefPtr<nsISFVDictionary>, 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::<nsISFVDictionary>())) + } + + xpcom_method!(parse_list => ParseList(field_value: *const nsACString) -> *const nsISFVList); + fn parse_list(&self, header: &nsACString) -> Result<RefPtr<nsISFVList>, 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::<nsISFVList>())) + } + + xpcom_method!(parse_item => ParseItem(header: *const nsACString) -> *const nsISFVItem); + fn parse_item(&self, header: &nsACString) -> Result<RefPtr<nsISFVItem>, 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<RefPtr<nsISFVInteger>, nsresult> { + Ok(RefPtr::new( + SFVInteger::new(value).coerce::<nsISFVInteger>(), + )) + } + + xpcom_method!(new_decimal => NewDecimal(value: f64) -> *const nsISFVDecimal); + fn new_decimal(&self, value: f64) -> Result<RefPtr<nsISFVDecimal>, nsresult> { + Ok(RefPtr::new( + SFVDecimal::new(value).coerce::<nsISFVDecimal>(), + )) + } + + xpcom_method!(new_bool => NewBool(value: bool) -> *const nsISFVBool); + fn new_bool(&self, value: bool) -> Result<RefPtr<nsISFVBool>, nsresult> { + Ok(RefPtr::new(SFVBool::new(value).coerce::<nsISFVBool>())) + } + + xpcom_method!(new_string => NewString(value: *const nsACString) -> *const nsISFVString); + fn new_string(&self, value: &nsACString) -> Result<RefPtr<nsISFVString>, nsresult> { + Ok(RefPtr::new(SFVString::new(value).coerce::<nsISFVString>())) + } + + xpcom_method!(new_token => NewToken(value: *const nsACString) -> *const nsISFVToken); + fn new_token(&self, value: &nsACString) -> Result<RefPtr<nsISFVToken>, nsresult> { + Ok(RefPtr::new(SFVToken::new(value).coerce::<nsISFVToken>())) + } + + xpcom_method!(new_byte_sequence => NewByteSequence(value: *const nsACString) -> *const nsISFVByteSeq); + fn new_byte_sequence(&self, value: &nsACString) -> Result<RefPtr<nsISFVByteSeq>, nsresult> { + Ok(RefPtr::new( + SFVByteSeq::new(value).coerce::<nsISFVByteSeq>(), + )) + } + + xpcom_method!(new_parameters => NewParameters() -> *const nsISFVParams); + fn new_parameters(&self) -> Result<RefPtr<nsISFVParams>, nsresult> { + Ok(RefPtr::new(SFVParams::new().coerce::<nsISFVParams>())) + } + + xpcom_method!(new_item => NewItem(value: *const nsISFVBareItem, params: *const nsISFVParams) -> *const nsISFVItem); + fn new_item( + &self, + value: &nsISFVBareItem, + params: &nsISFVParams, + ) -> Result<RefPtr<nsISFVItem>, nsresult> { + Ok(RefPtr::new( + SFVItem::new(value, params).coerce::<nsISFVItem>(), + )) + } + + xpcom_method!(new_inner_list => NewInnerList(items: *const thin_vec::ThinVec<Option<RefPtr<nsISFVItem>>>, params: *const nsISFVParams) -> *const nsISFVInnerList); + fn new_inner_list( + &self, + items: &thin_vec::ThinVec<Option<RefPtr<nsISFVItem>>>, + params: &nsISFVParams, + ) -> Result<RefPtr<nsISFVInnerList>, nsresult> { + let items = items + .iter() + .cloned() + .map(|item| item.ok_or(NS_ERROR_NULL_POINTER)) + .collect::<Result<Vec<_>, nsresult>>()?; + Ok(RefPtr::new( + SFVInnerList::new(items, params).coerce::<nsISFVInnerList>(), + )) + } + + xpcom_method!(new_list => NewList(members: *const thin_vec::ThinVec<Option<RefPtr<nsISFVItemOrInnerList>>>) -> *const nsISFVList); + fn new_list( + &self, + members: &thin_vec::ThinVec<Option<RefPtr<nsISFVItemOrInnerList>>>, + ) -> Result<RefPtr<nsISFVList>, nsresult> { + let members = members + .iter() + .cloned() + .map(|item| item.ok_or(NS_ERROR_NULL_POINTER)) + .collect::<Result<Vec<_>, nsresult>>()?; + Ok(RefPtr::new(SFVList::new(members).coerce::<nsISFVList>())) + } + + xpcom_method!(new_dictionary => NewDictionary() -> *const nsISFVDictionary); + fn new_dictionary(&self) -> Result<RefPtr<nsISFVDictionary>, nsresult> { + Ok(RefPtr::new( + SFVDictionary::new().coerce::<nsISFVDictionary>(), + )) + } +} + +#[xpcom(implement(nsISFVInteger, nsISFVBareItem), nonatomic)] +struct SFVInteger { + value: RefCell<i64>, +} + +impl SFVInteger { + fn new(value: i64) -> RefPtr<SFVInteger> { + SFVInteger::allocate(InitSFVInteger { + value: RefCell::new(value), + }) + } + + xpcom_method!(get_value => GetValue() -> i64); + fn get_value(&self) -> Result<i64, nsresult> { + 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<i32, nsresult> { + 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<bool>, +} + +impl SFVBool { + fn new(value: bool) -> RefPtr<SFVBool> { + SFVBool::allocate(InitSFVBool { + value: RefCell::new(value), + }) + } + + xpcom_method!(get_value => GetValue() -> bool); + fn get_value(&self) -> Result<bool, nsresult> { + 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<i32, nsresult> { + 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<nsCString>, +} + +impl SFVString { + fn new(value: &nsACString) -> RefPtr<SFVString> { + SFVString::allocate(InitSFVString { + value: RefCell::new(nsCString::from(value)), + }) + } + + xpcom_method!( + get_value => GetValue( + ) -> nsACString + ); + + fn get_value(&self) -> Result<nsCString, nsresult> { + 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<i32, nsresult> { + 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<nsCString>, +} + +impl SFVToken { + fn new(value: &nsACString) -> RefPtr<SFVToken> { + SFVToken::allocate(InitSFVToken { + value: RefCell::new(nsCString::from(value)), + }) + } + + xpcom_method!( + get_value => GetValue( + ) -> nsACString + ); + + fn get_value(&self) -> Result<nsCString, nsresult> { + 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<i32, nsresult> { + 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<nsCString>, +} + +impl SFVByteSeq { + fn new(value: &nsACString) -> RefPtr<SFVByteSeq> { + SFVByteSeq::allocate(InitSFVByteSeq { + value: RefCell::new(nsCString::from(value)), + }) + } + + xpcom_method!( + get_value => GetValue( + ) -> nsACString + ); + + fn get_value(&self) -> Result<nsCString, nsresult> { + 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<i32, nsresult> { + 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<f64>, +} + +impl SFVDecimal { + fn new(value: f64) -> RefPtr<SFVDecimal> { + SFVDecimal::allocate(InitSFVDecimal { + value: RefCell::new(value), + }) + } + + xpcom_method!( + get_value => GetValue( + ) -> f64 + ); + + fn get_value(&self) -> Result<f64, nsresult> { + 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<i32, nsresult> { + Ok(nsISFVBareItem::DECIMAL) + } + + fn from_bare_item_interface(obj: &nsISFVBareItem) -> &Self { + unsafe { ::std::mem::transmute(obj) } + } +} + +#[xpcom(implement(nsISFVParams), nonatomic)] +struct SFVParams { + params: RefCell<Parameters>, +} + +impl SFVParams { + fn new() -> RefPtr<SFVParams> { + SFVParams::allocate(InitSFVParams { + params: RefCell::new(Parameters::new()), + }) + } + + xpcom_method!( + get => Get(key: *const nsACString) -> *const nsISFVBareItem + ); + + fn get(&self, key: &nsACString) -> Result<RefPtr<nsISFVBareItem>, 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<nsCString> + ); + fn keys(&self) -> Result<thin_vec::ThinVec<nsCString>, nsresult> { + let keys = self.params.borrow(); + let keys = keys + .keys() + .map(nsCString::from) + .collect::<ThinVec<nsCString>>(); + Ok(keys) + } + + fn from_interface(obj: &nsISFVParams) -> &Self { + unsafe { ::std::mem::transmute(obj) } + } +} + +#[xpcom(implement(nsISFVItem, nsISFVItemOrInnerList), nonatomic)] +struct SFVItem { + value: RefPtr<nsISFVBareItem>, + params: RefPtr<nsISFVParams>, +} + +impl SFVItem { + fn new(value: &nsISFVBareItem, params: &nsISFVParams) -> RefPtr<SFVItem> { + SFVItem::allocate(InitSFVItem { + value: RefPtr::new(value), + params: RefPtr::new(params), + }) + } + + xpcom_method!( + get_value => GetValue( + ) -> *const nsISFVBareItem + ); + + fn get_value(&self) -> Result<RefPtr<nsISFVBareItem>, nsresult> { + Ok(self.value.clone()) + } + + xpcom_method!( + get_params => GetParams( + ) -> *const nsISFVParams + ); + fn get_params(&self) -> Result<RefPtr<nsISFVParams>, nsresult> { + Ok(self.params.clone()) + } + + xpcom_method!( + serialize => Serialize() -> nsACString + ); + fn serialize(&self) -> Result<nsCString, nsresult> { + 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<Vec<RefPtr<nsISFVItem>>>, + params: RefPtr<nsISFVParams>, +} + +impl SFVInnerList { + fn new(items: Vec<RefPtr<nsISFVItem>>, params: &nsISFVParams) -> RefPtr<SFVInnerList> { + SFVInnerList::allocate(InitSFVInnerList { + items: RefCell::new(items), + params: RefPtr::new(params), + }) + } + + xpcom_method!( + get_items => GetItems() -> thin_vec::ThinVec<Option<RefPtr<nsISFVItem>>> + ); + + fn get_items(&self) -> Result<thin_vec::ThinVec<Option<RefPtr<nsISFVItem>>>, 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<Option<RefPtr<nsISFVItem>>>, + ) -> nsresult { + if value.is_null() { + return NS_ERROR_NULL_POINTER; + } + match (*value) + .iter() + .map(|v| v.clone().ok_or(NS_ERROR_NULL_POINTER)) + .collect::<Result<Vec<_>, 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<RefPtr<nsISFVParams>, 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<Vec<RefPtr<nsISFVItemOrInnerList>>>, +} + +impl SFVList { + fn new(members: Vec<RefPtr<nsISFVItemOrInnerList>>) -> RefPtr<SFVList> { + SFVList::allocate(InitSFVList { + members: RefCell::new(members), + }) + } + + xpcom_method!( + get_members => GetMembers() -> thin_vec::ThinVec<Option<RefPtr<nsISFVItemOrInnerList>>> + ); + + fn get_members( + &self, + ) -> Result<thin_vec::ThinVec<Option<RefPtr<nsISFVItemOrInnerList>>>, nsresult> { + Ok(self.members.borrow().iter().cloned().map(Some).collect()) + } + + #[allow(non_snake_case)] + unsafe fn SetMembers( + &self, + value: *const thin_vec::ThinVec<Option<RefPtr<nsISFVItemOrInnerList>>>, + ) -> nsresult { + if value.is_null() { + return NS_ERROR_NULL_POINTER; + } + match (*value) + .iter() + .map(|v| v.clone().ok_or(NS_ERROR_NULL_POINTER)) + .collect::<Result<Vec<_>, 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<nsCString, nsresult> { + 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<Dictionary>, +} + +impl SFVDictionary { + fn new() -> RefPtr<SFVDictionary> { + SFVDictionary::allocate(InitSFVDictionary { + value: RefCell::new(Dictionary::new()), + }) + } + + xpcom_method!( + get => Get(key: *const nsACString) -> *const nsISFVItemOrInnerList + ); + + fn get(&self, key: &nsACString) -> Result<RefPtr<nsISFVItemOrInnerList>, 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<nsCString> + ); + fn keys(&self) -> Result<thin_vec::ThinVec<nsCString>, nsresult> { + let members = self.value.borrow(); + let keys = members + .keys() + .map(nsCString::from) + .collect::<ThinVec<nsCString>>(); + 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<nsCString, nsresult> { + let serialized = self + .value + .borrow() + .serialize_value() + .map_err(|_| NS_ERROR_FAILURE)?; + Ok(nsCString::from(serialized)) + } +} + +fn bare_item_from_interface(obj: &nsISFVBareItem) -> Result<BareItem, nsresult> { + let obj = obj + .query_interface::<nsISFVBareItem>() + .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<Parameters, nsresult> { + let params = SFVParams::from_interface(obj).params.borrow(); + Ok(params.clone()) +} + +fn item_from_interface(obj: &nsISFVItem) -> Result<Item, nsresult> { + 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<InnerList, nsresult> { + let sfv_inner_list = SFVInnerList::from_interface(obj); + + let mut inner_list_items: Vec<Item> = 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<ListEntry, nsresult> { + if let Some(nsi_item) = obj.query_interface::<nsISFVItem>() { + let item = item_from_interface(nsi_item.deref())?; + Ok(ListEntry::Item(item)) + } else if let Some(nsi_inner_list) = obj.query_interface::<nsISFVInnerList>() { + 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<RefPtr<nsISFVBareItem>, nsresult> { + let bare_item = match bare_item { + BareItem::Boolean(val) => RefPtr::new(SFVBool::new(*val).coerce::<nsISFVBareItem>()), + BareItem::String(val) => { + RefPtr::new(SFVString::new(&nsCString::from(val)).coerce::<nsISFVBareItem>()) + } + BareItem::Token(val) => { + RefPtr::new(SFVToken::new(&nsCString::from(val)).coerce::<nsISFVBareItem>()) + } + BareItem::ByteSeq(val) => RefPtr::new( + SFVByteSeq::new(&nsCString::from(String::from_utf8(val.to_vec()).unwrap())) + .coerce::<nsISFVBareItem>(), + ), + BareItem::Decimal(val) => { + let val = val + .to_string() + .parse::<f64>() + .map_err(|_| NS_ERROR_UNEXPECTED)?; + RefPtr::new(SFVDecimal::new(val).coerce::<nsISFVBareItem>()) + } + BareItem::Integer(val) => RefPtr::new(SFVInteger::new(*val).coerce::<nsISFVBareItem>()), + }; + + Ok(bare_item) +} + +fn interface_from_item(item: &Item) -> Result<RefPtr<nsISFVItem>, 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::<nsISFVItem>(), + )) +} + +fn interface_from_params(params: &Parameters) -> Result<RefPtr<nsISFVParams>, 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::<nsISFVParams>())) +} + +fn interface_from_list_entry( + member: &ListEntry, +) -> Result<RefPtr<nsISFVItemOrInnerList>, 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::<nsISFVItemOrInnerList>(), + )) + } + 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::<nsISFVItemOrInnerList>(), + )) + } + } +} |