Revision control

Copy as Markdown

Other Tools

/* 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 thin_vec::thin_vec;
use nserror::nsresult;
use std::ptr;
use xpcom::interfaces::{nsIMsgIncomingServer, nsIObserverService};
use xpcom::{RefCounted, RefPtr, components};
use crate::{
MESSENGER_STRING_BUNDLE, UserInteractiveServer, get_formatted_string, get_string_bundle,
register_alert,
};
/// Handle a possible connection error that came from the given
/// [`nsIMsgIncomingServer`].
///
/// If the error matches a known connection error, the user is shown an alert
/// notification/modal. Otherwise, this does nothing.
///
/// # Safety
///
/// The `incoming_server` argument must point to a valid object or be the null
/// pointer. In the latter case, this function will return
/// [`nserror::NS_ERROR_NULL_POINTER`].
pub unsafe extern "C" fn maybe_handle_connection_error_from_incoming_server(
error: nsresult,
incoming_server: *const nsIMsgIncomingServer,
) -> nsresult {
if incoming_server.is_null() {
return nserror::NS_ERROR_NULL_POINTER;
}
// SAFETY: We have already ensured the provided pointer isn't null, and the
// function's call contract implies consumers should ensure it's valid.
// `RefPtr::from_raw` only returns `None` if the pointer is null, and we
// have already ensured all of our pointers are non-null, so unwrapping
// shouldn't panic here.
let incoming_server = unsafe { RefPtr::from_raw(incoming_server).unwrap() };
match maybe_handle_connection_error(error, incoming_server) {
Ok(_) => nserror::NS_OK,
Err(status) => status,
}
}
/// Handles an error that might represent a connection error.
///
/// If the error matches a known connection error, the user is shown an alert
/// notification/modal. Otherwise, this does nothing.
pub fn maybe_handle_connection_error<ServerT>(
error: nsresult,
server: RefPtr<ServerT>,
) -> Result<(), nsresult>
where
ServerT: UserInteractiveServer + RefCounted,
{
// Check if we can map the error to a user-facing message.
let message_name = match error {
nserror::NS_ERROR_UNKNOWN_HOST | nserror::NS_ERROR_UNKNOWN_PROXY_HOST => {
c"unknownHostError"
}
nserror::NS_ERROR_CONNECTION_REFUSED | nserror::NS_ERROR_PROXY_CONNECTION_REFUSED => {
c"connectionRefusedError"
}
nserror::NS_ERROR_NET_TIMEOUT => c"netTimeoutError",
nserror::NS_ERROR_NET_RESET => c"netResetError",
nserror::NS_ERROR_NET_INTERRUPT => c"netInterruptError",
nserror::NS_ERROR_NET_ERROR_RESPONSE => c"errorResponseError",
// We couldn't find a message to show the user, in which case we bail
// early and let the consumer handle the error as usual.
_ => return Ok(()),
};
let bundle = get_string_bundle(MESSENGER_STRING_BUNDLE)?;
let name = server.host_name()?;
let message = get_formatted_string(&bundle, message_name, thin_vec![name])?;
let uri = server.uri()?;
register_alert(message, uri)
}
pub fn report_connection_success<ServerT>(server: RefPtr<ServerT>) -> Result<(), nsresult>
where
ServerT: UserInteractiveServer + RefCounted,
{
let obs_svc: RefPtr<nsIObserverService> = components::Observer::service()?;
let uri = server.uri()?;
// SAFETY: uri is a valid object, `aTopic` is constructed inline, and `someData` is optional
unsafe {
obs_svc
.NotifyObservers(
uri.coerce(),
c"server-connection-succeeded".as_ptr(),
ptr::null(),
)
.to_result()
}
}