use std::{
mem::ManuallyDrop,
ptr::{self, NonNull},
};
use ohos_web_sys::{
ArkWeb_ResourceHandler, ArkWeb_ResourceRequest, ArkWeb_SchemeHandler,
OH_ArkWebSchemeHandler_GetUserData, OH_ArkWebSchemeHandler_SetOnRequestStart,
OH_ArkWebSchemeHandler_SetOnRequestStop, OH_ArkWebSchemeHandler_SetUserData,
OH_ArkWeb_CreateSchemeHandler, OH_ArkWeb_DestroySchemeHandler,
};
use crate::{ResourceHandle, ResourceRequest};
pub struct CustomProtocolHandlerContext {
request: Option<Box<dyn FnMut(ResourceRequest, ResourceHandle) -> bool>>,
response: Option<Box<dyn FnMut(ResourceRequest)>>,
}
pub struct CustomProtocolHandler {
raw: NonNull<ArkWeb_SchemeHandler>,
}
impl CustomProtocolHandler {
pub fn new() -> Self {
let mut raw = ptr::null_mut();
unsafe {
OH_ArkWeb_CreateSchemeHandler(&mut raw);
#[cfg(debug_assertions)]
assert!(!raw.is_null(), "Failed to create scheme handler");
Self {
raw: NonNull::new_unchecked(raw),
}
}
}
pub fn raw(&self) -> *mut ArkWeb_SchemeHandler {
self.raw.as_ptr()
}
pub fn on_request_start<F>(&self, mut handler: F)
where
F: FnMut(ResourceRequest, ResourceHandle) -> bool,
{
let user_data_raw = unsafe { OH_ArkWebSchemeHandler_GetUserData(self.raw.as_ptr()) };
let static_on_request_start = unsafe {
std::mem::transmute::<
Box<dyn FnMut(ResourceRequest, ResourceHandle) -> bool>,
Box<dyn FnMut(ResourceRequest, ResourceHandle) -> bool + 'static>,
>(Box::new(move |request, handle| handler(request, handle)))
};
match user_data_raw.is_null() {
false => {
let mut user_data = unsafe {
ManuallyDrop::new(Box::from_raw(
user_data_raw as *mut CustomProtocolHandlerContext,
))
};
user_data.request = Some(Box::new(static_on_request_start));
}
true => {
let user_data = Box::new(CustomProtocolHandlerContext {
request: Some(Box::new(static_on_request_start)),
response: None,
});
let user_data_raw = Box::into_raw(user_data);
unsafe {
OH_ArkWebSchemeHandler_SetUserData(self.raw.as_ptr(), user_data_raw as _);
}
}
}
unsafe {
let ret =
OH_ArkWebSchemeHandler_SetOnRequestStart(self.raw.as_ptr(), Some(on_request_start));
#[cfg(debug_assertions)]
assert!(ret == 0, "Failed to set on request start");
}
}
pub fn on_request_stop<F>(&self, mut handler: F)
where
F: FnMut(ResourceRequest),
{
let user_data_raw = unsafe { OH_ArkWebSchemeHandler_GetUserData(self.raw.as_ptr()) };
let static_on_request_stop = unsafe {
std::mem::transmute::<
Box<dyn FnMut(ResourceRequest)>,
Box<dyn FnMut(ResourceRequest) + 'static>,
>(Box::new(move |handle| handler(handle)))
};
match user_data_raw.is_null() {
false => {
let mut user_data = unsafe {
ManuallyDrop::new(Box::from_raw(
user_data_raw as *mut CustomProtocolHandlerContext,
))
};
user_data.response = Some(Box::new(static_on_request_stop));
}
true => {
let user_data = Box::new(CustomProtocolHandlerContext {
request: None,
response: Some(Box::new(static_on_request_stop)),
});
let user_data_raw = Box::into_raw(user_data);
unsafe {
OH_ArkWebSchemeHandler_SetUserData(self.raw.as_ptr(), user_data_raw as _);
}
}
}
unsafe {
let ret =
OH_ArkWebSchemeHandler_SetOnRequestStop(self.raw.as_ptr(), Some(on_request_stop));
#[cfg(debug_assertions)]
assert!(ret == 0, "Failed to set on request stop");
}
}
}
impl Drop for CustomProtocolHandler {
fn drop(&mut self) {
unsafe {
OH_ArkWeb_DestroySchemeHandler(self.raw.as_ptr());
}
}
}
extern "C" fn on_request_start(
schema_handle: *const ArkWeb_SchemeHandler,
resource_request: *mut ArkWeb_ResourceRequest,
resource_handler: *const ArkWeb_ResourceHandler,
intercept: *mut bool,
) {
let user_data_raw = unsafe { OH_ArkWebSchemeHandler_GetUserData(schema_handle) };
if user_data_raw.is_null() {
unsafe {
*intercept = false;
}
return;
}
let mut user_data = unsafe {
ManuallyDrop::new(Box::from_raw(
user_data_raw as *mut CustomProtocolHandlerContext,
))
};
if let Some(request) = &mut user_data.request {
let ret = request(
ResourceRequest::new(resource_request),
ResourceHandle::new(resource_handler as _),
);
unsafe {
*intercept = ret;
}
} else {
unsafe {
*intercept = false;
}
}
}
extern "C" fn on_request_stop(
schema_handle: *const ArkWeb_SchemeHandler,
resource_request: *const ArkWeb_ResourceRequest,
) {
let user_data_raw = unsafe { OH_ArkWebSchemeHandler_GetUserData(schema_handle) };
if user_data_raw.is_null() {
return;
}
let mut user_data = unsafe {
ManuallyDrop::new(Box::from_raw(
user_data_raw as *mut CustomProtocolHandlerContext,
))
};
if let Some(response) = &mut user_data.response {
response(ResourceRequest::new(resource_request as _));
}
}