use super::super::{
error::{callback_context, callback_error},
text::{read_task_mem_message, read_task_mem_uri},
};
use super::{
registration::Registrations,
state::{Shared, observe},
};
use serde_json::json;
use std::rc::Rc;
use webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_PROCESS_FAILED_KIND;
use webview2_com::{
ContentLoadingEventHandler, DOMContentLoadedEventHandler, NavigationCompletedEventHandler,
NavigationStartingEventHandler, ProcessFailedEventHandler,
WebResourceResponseReceivedEventHandler,
};
use windows::core::{BOOL, HSTRING, PWSTR};
pub(super) fn install(tokens: &mut Registrations, shared: &Shared) -> windows::core::Result<()> {
navigation(tokens, shared)?;
content(tokens, shared)?;
resources(tokens, shared)?;
process(tokens, shared)
}
fn navigation(tokens: &mut Registrations, shared: &Shared) -> windows::core::Result<()> {
let weak = Rc::downgrade(shared);
let handler = NavigationStartingEventHandler::create(Box::new(move |_, args| {
observe(&weak, "NavigationStarting", |state| {
let args =
args.ok_or_else(|| callback_error("Startup navigation arguments missing"))?;
let mut id = 0;
unsafe {
args.NavigationId(&mut id)
.map_err(|error| callback_context("Startup NavigationId", error))?;
}
let mut raw = PWSTR::null();
unsafe {
args.Uri(&mut raw)
.map_err(|error| callback_context("Startup NavigationStarting.Uri", error))?;
}
let uri = unsafe { read_task_mem_uri(raw) }?;
state.navigation_started(id, &uri);
state.record(&json!({"kind":"navigation_starting","navigation_id":id.to_string()}));
Ok(())
})
}));
let mut token = 0;
unsafe {
tokens
.webview
.add_NavigationStarting(&handler, &mut token)
.map_err(|error| callback_context("Startup add_NavigationStarting", error))?;
}
tokens.starting = Some((token, handler));
let weak = Rc::downgrade(shared);
let handler = NavigationCompletedEventHandler::create(Box::new(move |_, args| {
observe(&weak, "NavigationCompleted", |state| {
let args =
args.ok_or_else(|| callback_error("Startup completion arguments missing"))?;
let mut success = BOOL(0);
let mut id = 0;
unsafe {
args.IsSuccess(&mut success).map_err(|error| {
callback_context("Startup NavigationCompleted.IsSuccess", error)
})?;
args.NavigationId(&mut id)
.map_err(|error| callback_context("Startup NavigationId", error))?;
}
let current = state.navigation_completed(id, success.as_bool());
state.record(&json!({"kind":"navigation_completed","success":success.as_bool(),"navigation_id":id.to_string(),"current":current}));
Ok(())
})
}));
unsafe {
tokens
.webview
.add_NavigationCompleted(&handler, &mut token)
.map_err(|error| callback_context("Startup add_NavigationCompleted", error))?;
}
tokens.completed = Some((token, handler));
Ok(())
}
fn content(tokens: &mut Registrations, shared: &Shared) -> windows::core::Result<()> {
let weak = Rc::downgrade(shared);
let handler = ContentLoadingEventHandler::create(Box::new(move |_, args| {
observe(&weak, "ContentLoading", |state| {
let args = args.ok_or_else(|| callback_error("Startup content arguments missing"))?;
let mut error_page = BOOL(0);
let mut id = 0;
unsafe {
args.IsErrorPage(&mut error_page).map_err(|error| {
callback_context("Startup ContentLoading.IsErrorPage", error)
})?;
args.NavigationId(&mut id)
.map_err(|error| callback_context("Startup NavigationId", error))?;
}
state.record(&json!({"kind":"content_loading","error_page":error_page.as_bool(),"navigation_id":id.to_string()}));
Ok(())
})
}));
let mut token = 0;
unsafe {
tokens
.webview
.add_ContentLoading(&handler, &mut token)
.map_err(|error| callback_context("Startup add_ContentLoading", error))?;
}
tokens.content = Some((token, handler));
let weak = Rc::downgrade(shared);
let handler = DOMContentLoadedEventHandler::create(Box::new(move |_, args| {
observe(&weak, "DOMContentLoaded", |state| {
let args = args.ok_or_else(|| callback_error("Startup DOM arguments missing"))?;
let mut id = 0;
unsafe {
args.NavigationId(&mut id)
.map_err(|error| callback_context("Startup NavigationId", error))?;
}
let current = state.dom_loaded(id);
state.record(&json!({"kind":"dom_content_loaded","navigation_id":id.to_string(),"current":current}));
Ok(())
})
}));
unsafe {
tokens
.resources
.add_DOMContentLoaded(&handler, &mut token)
.map_err(|error| callback_context("Startup add_DOMContentLoaded", error))?;
}
tokens.dom = Some((token, handler));
Ok(())
}
fn resources(tokens: &mut Registrations, shared: &Shared) -> windows::core::Result<()> {
let weak = Rc::downgrade(shared);
let handler = WebResourceResponseReceivedEventHandler::create(Box::new(move |_, args| {
observe(&weak, "WebResourceResponseReceived", |state| {
let args = args.ok_or_else(|| callback_error("Startup response arguments missing"))?;
let (request, response) = unsafe {
(
args.Request().map_err(|error| {
callback_context("Startup WebResourceResponseReceived.Request", error)
})?,
args.Response().map_err(|error| {
callback_context("Startup WebResourceResponseReceived.Response", error)
})?,
)
};
let mut raw = PWSTR::null();
unsafe {
request
.Uri(&mut raw)
.map_err(|error| callback_context("Startup WebResourceRequest.Uri", error))?;
}
let uri = unsafe { read_task_mem_uri(raw) }?;
let mut status = 0;
unsafe {
response.StatusCode(&mut status).map_err(|error| {
callback_context("Startup WebResourceResponseView.StatusCode", error)
})?;
}
let headers = unsafe {
response.Headers().map_err(|error| {
callback_context("Startup WebResourceResponseView.Headers", error)
})?
};
let name = HSTRING::from("Content-Type");
let mut present = BOOL(0);
unsafe {
headers.Contains(&name, &mut present).map_err(|error| {
callback_context("Startup HttpResponseHeaders.Contains(Content-Type)", error)
})?;
}
let mime = if present.as_bool() {
let mut raw = PWSTR::null();
unsafe {
headers.GetHeader(&name, &mut raw).map_err(|error| {
callback_context(
"Startup HttpResponseHeaders.GetHeader(Content-Type)",
error,
)
})?;
}
Some(unsafe { read_task_mem_message(raw) }?)
} else {
None
};
state.record(
&json!({"kind":"resource_response","uri":uri,"status":status,"content_type":mime}),
);
Ok(())
})
}));
let mut token = 0;
unsafe {
tokens
.resources
.add_WebResourceResponseReceived(&handler, &mut token)
.map_err(|error| callback_context("Startup add_WebResourceResponseReceived", error))?;
}
tokens.response = Some((token, handler));
Ok(())
}
fn process(tokens: &mut Registrations, shared: &Shared) -> windows::core::Result<()> {
let weak = Rc::downgrade(shared);
let handler = ProcessFailedEventHandler::create(Box::new(move |_, args| {
observe(&weak, "ProcessFailed", |state| {
let args = args.ok_or_else(|| callback_error("Startup process arguments missing"))?;
let mut kind = COREWEBVIEW2_PROCESS_FAILED_KIND(0);
unsafe {
args.ProcessFailedKind(&mut kind)
.map_err(|error| callback_context("Startup ProcessFailedKind", error))?;
}
state.record(&json!({"kind":"process_failed","process_kind":kind.0}));
Ok(())
})
}));
let mut token = 0;
unsafe {
tokens
.webview
.add_ProcessFailed(&handler, &mut token)
.map_err(|error| callback_context("Startup add_ProcessFailed", error))?;
}
tokens.process = Some((token, handler));
Ok(())
}