load.rs (3605B)
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 futures_channel::oneshot; 6 use nserror::{nsresult, NS_OK, NS_SUCCESS_ADOPTED_DATA}; 7 use nsstring::{nsACString, nsCStringLike}; 8 use std::{ 9 cell::Cell, 10 ffi::c_void, 11 io::{self, Error, ErrorKind}, 12 ptr, 13 }; 14 use xpcom::{ 15 interfaces::{nsIStreamLoader, nsIStreamLoaderObserver, nsISupports}, 16 xpcom, 17 }; 18 19 unsafe fn boxed_slice_from_raw(ptr: *mut u8, len: usize) -> Box<[u8]> { 20 if ptr.is_null() { 21 // It is undefined behaviour to create a `Box<[u8]>` with a null pointer, 22 // so avoid that case. 23 assert_eq!(len, 0); 24 Box::new([]) 25 } else { 26 Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, len)) 27 } 28 } 29 30 #[xpcom(implement(nsIStreamLoaderObserver), nonatomic)] 31 struct StreamLoaderObserver { 32 sender: Cell<Option<oneshot::Sender<Result<Box<[u8]>, nsresult>>>>, 33 } 34 35 impl StreamLoaderObserver { 36 #[allow(non_snake_case)] 37 unsafe fn OnStreamComplete( 38 &self, 39 _loader: *const nsIStreamLoader, 40 _ctxt: *const nsISupports, 41 status: nsresult, 42 result_length: u32, 43 result: *const u8, 44 ) -> nsresult { 45 let sender = match self.sender.take() { 46 Some(sender) => sender, 47 None => return NS_OK, 48 }; 49 50 if status.failed() { 51 sender.send(Err(status)).expect("Failed to send data"); 52 return NS_OK; 53 } 54 55 // safety: take ownership of the data passed in. This is OK because we 56 // have configured Rust and C++ to use the same allocator, and our 57 // caller won't free the `result` pointer if we return 58 // NS_SUCCESS_ADOPTED_DATA. 59 sender 60 .send(Ok(boxed_slice_from_raw( 61 result as *mut u8, 62 result_length as usize, 63 ))) 64 .expect("Failed to send data"); 65 NS_SUCCESS_ADOPTED_DATA 66 } 67 } 68 69 extern "C" { 70 fn L10nRegistryLoad( 71 path: *const nsACString, 72 observer: *const nsIStreamLoaderObserver, 73 ) -> nsresult; 74 75 fn L10nRegistryLoadSync( 76 aPath: *const nsACString, 77 aData: *mut *mut c_void, 78 aSize: *mut u64, 79 ) -> nsresult; 80 } 81 82 pub async fn load_async(path: impl nsCStringLike) -> io::Result<Box<[u8]>> { 83 let (sender, receiver) = oneshot::channel::<Result<Box<[u8]>, nsresult>>(); 84 let observer = StreamLoaderObserver::allocate(InitStreamLoaderObserver { 85 sender: Cell::new(Some(sender)), 86 }); 87 unsafe { 88 L10nRegistryLoad(&*path.adapt(), observer.coerce()) 89 .to_result() 90 .map_err(|err| Error::new(ErrorKind::Other, err))?; 91 } 92 receiver 93 .await 94 .expect("Failed to receive from observer.") 95 .map_err(|err| Error::new(ErrorKind::Other, err)) 96 } 97 98 pub fn load_sync(path: impl nsCStringLike) -> io::Result<Box<[u8]>> { 99 let mut data_ptr: *mut c_void = ptr::null_mut(); 100 let mut data_length: u64 = 0; 101 unsafe { 102 L10nRegistryLoadSync(&*path.adapt(), &mut data_ptr, &mut data_length) 103 .to_result() 104 .map_err(|err| Error::new(ErrorKind::Other, err))?; 105 106 // The call succeeded, meaning `data_ptr` and `size` have been filled in with owning pointers to actual data payloads (or null). 107 // If we get a null, return a successful read of the empty file. 108 Ok(boxed_slice_from_raw( 109 data_ptr as *mut u8, 110 data_length as usize, 111 )) 112 } 113 }