tor-browser

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

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 }