tor-browser

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

error.rs (7077B)


      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 //! Types needed to marshal [`server`](crate::server) errors back to C++ in Firefox. The main type
      6 //! of this module is [`ErrorBuffer`](crate::server::ErrorBuffer).
      7 
      8 use std::{
      9    borrow::Cow,
     10    error::Error,
     11    fmt::{self, Display, Formatter},
     12    os::raw::c_char,
     13    ptr,
     14 };
     15 
     16 use nsstring::nsCString;
     17 use serde::{Deserialize, Serialize};
     18 use wgc::id;
     19 use wgt::error::{ErrorType, WebGpuError};
     20 
     21 /// A non-owning representation of `mozilla::webgpu::ErrorBuffer` in C++, passed as an argument to
     22 /// other functions in [this module](self).
     23 ///
     24 /// C++ callers of Rust functions (presumably in `WebGPUParent.cpp`) that expect one of these
     25 /// structs can create a `mozilla::webgpu::ErrorBuffer` object, and call its `ToFFI` method to
     26 /// construct a value of this type, available to C++ as `mozilla::webgpu::ffi::WGPUErrorBuffer`. If
     27 /// we catch a `Result::Err` in other functions of [this module](self), the error is converted to
     28 /// this type.
     29 #[repr(C)]
     30 pub struct ErrorBuffer {
     31    /// The type of error that `string` is associated with. If this location is set to
     32    /// [`ErrorBufferType::None`] after being passed as an argument to a function in [this module](self),
     33    /// then the remaining fields are guaranteed to not have been altered by that function from
     34    /// their original state.
     35    r#type: *mut ErrorBufferType,
     36    /// The (potentially truncated) message associated with this error. A fixed-capacity,
     37    /// null-terminated UTF-8 string buffer owned by C++.
     38    ///
     39    /// When we convert WGPU errors to this type, we render the error as a string, copying into
     40    /// `message` up to `capacity - 1`, and null-terminate it.
     41    message: *mut c_char,
     42    message_capacity: usize,
     43    device_id: *mut wgc::id::DeviceId,
     44 }
     45 
     46 impl ErrorBuffer {
     47    /// Fill this buffer with the textual representation of `error`.
     48    ///
     49    /// If the error message is too long, truncate it to `self.capacity`. In either case, the error
     50    /// message is always terminated by a zero byte.
     51    ///
     52    /// Note that there is no explicit indication of the message's length, only the terminating zero
     53    /// byte. If the textual form of `error` itself includes a zero byte (as Rust strings can), then
     54    /// the C++ code receiving this error message has no way to distinguish that from the
     55    /// terminating zero byte, and will see the message as shorter than it is.
     56    pub(crate) fn init(&mut self, error: impl WebGpuError, device_id: wgc::id::DeviceId) {
     57        let err_ty = match error.webgpu_error_type() {
     58            ErrorType::Internal => ErrorBufferType::Internal,
     59            ErrorType::OutOfMemory => ErrorBufferType::OutOfMemory,
     60            ErrorType::Validation => ErrorBufferType::Validation,
     61            ErrorType::DeviceLost => return, // will be surfaced via callback
     62        };
     63        // SAFETY: We presume the pointer provided by the caller is safe to write to.
     64        unsafe { *self.r#type = err_ty };
     65 
     66        unsafe { *self.device_id = device_id };
     67 
     68        let message = error_to_string(&error);
     69 
     70        assert_ne!(self.message_capacity, 0);
     71        // Since we need to store a nul terminator after the content, the
     72        // content length must always be strictly less than the buffer's
     73        // capacity.
     74        let length = if message.len() >= self.message_capacity {
     75            // Thanks to the structure of UTF-8, `std::is_char_boundary` is
     76            // O(1), so this should examine a few bytes at most.
     77            //
     78            // The largest value in this range is `self.message_capacity - 1`,
     79            // which is a safe length.
     80            let truncated_length = (0..self.message_capacity)
     81                .rfind(|&offset| message.is_char_boundary(offset))
     82                .unwrap_or(0);
     83            log::warn!(
     84                "Error message's length {} reached capacity {}, truncating to {}",
     85                message.len(),
     86                self.message_capacity,
     87                truncated_length,
     88            );
     89            truncated_length
     90        } else {
     91            message.len()
     92        };
     93        unsafe {
     94            ptr::copy_nonoverlapping(message.as_ptr(), self.message as *mut u8, length);
     95            *self.message.add(length) = 0;
     96        }
     97    }
     98 }
     99 
    100 pub struct OwnedErrorBuffer {
    101    device_id: Option<id::DeviceId>,
    102    ty: ErrorBufferType,
    103    message: nsCString,
    104 }
    105 
    106 impl OwnedErrorBuffer {
    107    pub fn new() -> Self {
    108        Self {
    109            device_id: None,
    110            ty: ErrorBufferType::None,
    111            message: nsCString::new(),
    112        }
    113    }
    114 
    115    pub(crate) fn init(&mut self, error: impl WebGpuError, device_id: id::DeviceId) {
    116        assert!(self.device_id.is_none());
    117 
    118        let ty = match error.webgpu_error_type() {
    119            ErrorType::Internal => ErrorBufferType::Internal,
    120            ErrorType::OutOfMemory => ErrorBufferType::OutOfMemory,
    121            ErrorType::Validation => ErrorBufferType::Validation,
    122            ErrorType::DeviceLost => return, // will be surfaced via callback
    123        };
    124 
    125        self.device_id = Some(device_id);
    126        self.ty = ty;
    127 
    128        let message = error_to_string(&error);
    129        self.message = nsCString::from(message);
    130    }
    131 
    132    pub(crate) fn get_inner_data(&self) -> Option<(id::DeviceId, ErrorBufferType, &nsCString)> {
    133        Some((self.device_id?, self.ty, &self.message))
    134    }
    135 }
    136 
    137 pub fn error_to_string(error: impl Error) -> String {
    138    use std::fmt::Write;
    139    let mut message = format!("{}", error);
    140    let mut e = error.source();
    141    while let Some(source) = e {
    142        write!(message, ", caused by: {}", source).unwrap();
    143        e = source.source();
    144    }
    145    message
    146 }
    147 
    148 /// Corresponds to an optional discriminant of [`GPUError`] type in the WebGPU API. Strongly
    149 /// correlates to [`GPUErrorFilter`]s.
    150 ///
    151 /// [`GPUError`]: https://gpuweb.github.io/gpuweb/#gpuerror
    152 /// [`GPUErrorFilter`]: https://gpuweb.github.io/gpuweb/#enumdef-gpuerrorfilter
    153 #[repr(u8)]
    154 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
    155 pub(crate) enum ErrorBufferType {
    156    None = 0,
    157    DeviceLost = 1,
    158    Internal = 2,
    159    OutOfMemory = 3,
    160    Validation = 4,
    161 }
    162 
    163 /// Representation an error whose error message is already rendered as a [`&str`], and has no error
    164 /// sources. Used for convenience in [`server`](crate::server) code.
    165 #[derive(Clone, Debug)]
    166 pub(crate) struct ErrMsg {
    167    pub(crate) message: Cow<'static, str>,
    168    pub(crate) r#type: ErrorType,
    169 }
    170 
    171 impl ErrMsg {
    172    pub fn oom() -> Self {
    173        Self {
    174            message: "Out of memory".into(),
    175            r#type: ErrorType::OutOfMemory,
    176        }
    177    }
    178 }
    179 
    180 impl Display for ErrMsg {
    181    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    182        let Self { message, r#type: _ } = self;
    183        write!(f, "{message}")
    184    }
    185 }
    186 
    187 impl Error for ErrMsg {}
    188 
    189 impl WebGpuError for ErrMsg {
    190    fn webgpu_error_type(&self) -> ErrorType {
    191        self.r#type
    192    }
    193 }