diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index a71e3089c9..bb8a8ec37f 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -10,7 +10,9 @@ mod context_menu_handler; mod display_handler; mod life_span_handler; mod load_handler; +mod request_handler; mod resource_handler; +mod resource_request_handler; mod scheme_handler_factory; pub(super) mod render_handler; diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index 5c7d1084cd..b8221a844c 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -1,6 +1,6 @@ use cef::rc::{Rc, RcImpl}; use cef::sys::{_cef_client_t, cef_base_ref_counted_t}; -use cef::{ContextMenuHandler, DisplayHandler, ImplClient, LifeSpanHandler, LoadHandler, RenderHandler, WrapClient}; +use cef::{ContextMenuHandler, DisplayHandler, ImplClient, LifeSpanHandler, LoadHandler, RenderHandler, RequestHandler, WrapClient}; use crate::cef::CefEventHandler; use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage}; @@ -10,6 +10,7 @@ use super::display_handler::DisplayHandlerImpl; use super::life_span_handler::LifeSpanHandlerImpl; use super::load_handler::LoadHandlerImpl; use super::render_handler::RenderHandlerImpl; +use super::request_handler::RequestHandlerImpl; pub(crate) struct BrowserProcessClientImpl { object: *mut RcImpl<_cef_client_t, Self>, @@ -17,6 +18,7 @@ pub(crate) struct BrowserProcessClientImpl { load_handler: LoadHandler, render_handler: RenderHandler, display_handler: DisplayHandler, + request_handler: RequestHandler, } impl BrowserProcessClientImpl { pub(crate) fn new(event_handler: &H) -> Self { @@ -26,6 +28,7 @@ impl BrowserProcessClientImpl { load_handler: LoadHandler::new(LoadHandlerImpl::new(event_handler.duplicate())), render_handler: RenderHandler::new(RenderHandlerImpl::new(event_handler.duplicate())), display_handler: DisplayHandler::new(DisplayHandlerImpl::new(event_handler.duplicate())), + request_handler: RequestHandler::new(RequestHandlerImpl::new()), } } } @@ -73,6 +76,10 @@ impl ImplClient for BrowserProcessClientImpl { Some(self.display_handler.clone()) } + fn request_handler(&self) -> Option { + Some(self.request_handler.clone()) + } + fn context_menu_handler(&self) -> Option { Some(ContextMenuHandler::new(ContextMenuHandlerImpl::new())) } @@ -94,6 +101,7 @@ impl Clone for BrowserProcessClientImpl { load_handler: self.load_handler.clone(), render_handler: self.render_handler.clone(), display_handler: self.display_handler.clone(), + request_handler: self.request_handler.clone(), } } } diff --git a/desktop/src/cef/internal/request_handler.rs b/desktop/src/cef/internal/request_handler.rs new file mode 100644 index 0000000000..c77147ff15 --- /dev/null +++ b/desktop/src/cef/internal/request_handler.rs @@ -0,0 +1,84 @@ +use cef::rc::{Rc, RcImpl}; +use cef::sys::{_cef_request_handler_t, cef_base_ref_counted_t}; +use cef::{AuthCallback, Browser, CefString, Frame, ImplRequest, ImplRequestHandler, Request, ResourceRequestHandler, WrapRequestHandler}; +use std::ffi::c_int; + +use super::resource_request_handler::ResourceRequestHandlerImpl; +use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME}; + +pub(crate) struct RequestHandlerImpl { + object: *mut RcImpl<_cef_request_handler_t, Self>, +} + +impl RequestHandlerImpl { + pub(crate) fn new() -> Self { + Self { object: std::ptr::null_mut() } + } +} + +impl ImplRequestHandler for RequestHandlerImpl { + fn on_before_browse(&self, _browser: Option<&mut Browser>, _frame: Option<&mut Frame>, request: Option<&mut Request>, _user_gesture: c_int, _is_redirect: c_int) -> c_int { + let Some(request) = request else { return 1 }; + let url = CefString::from(&request.url()).to_string(); + if url.starts_with(&format!("{RESOURCE_SCHEME}://{RESOURCE_DOMAIN}/")) { + 0 + } else { + tracing::warn!("Blocked navigation to: {}", url); + 1 + } + } + + fn resource_request_handler( + &self, + _browser: Option<&mut Browser>, + _frame: Option<&mut Frame>, + _request: Option<&mut Request>, + _is_navigation: c_int, + _is_download: c_int, + _request_initiator: Option<&CefString>, + _disable_default_handling: Option<&mut c_int>, + ) -> Option { + Some(ResourceRequestHandler::new(ResourceRequestHandlerImpl::new())) + } + + fn auth_credentials( + &self, + _browser: Option<&mut Browser>, + _origin_url: Option<&CefString>, + _is_proxy: c_int, + _host: Option<&CefString>, + _port: c_int, + _realm: Option<&CefString>, + _scheme: Option<&CefString>, + _callback: Option<&mut AuthCallback>, + ) -> c_int { + 0 + } + + fn get_raw(&self) -> *mut _cef_request_handler_t { + self.object.cast() + } +} + +impl Clone for RequestHandlerImpl { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + } + Self { object: self.object } + } +} +impl Rc for RequestHandlerImpl { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} +impl WrapRequestHandler for RequestHandlerImpl { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_request_handler_t, Self>) { + self.object = object; + } +} diff --git a/desktop/src/cef/internal/resource_request_handler.rs b/desktop/src/cef/internal/resource_request_handler.rs new file mode 100644 index 0000000000..94d8ec47e3 --- /dev/null +++ b/desktop/src/cef/internal/resource_request_handler.rs @@ -0,0 +1,60 @@ +use cef::rc::{Rc, RcImpl}; +use cef::sys::{_cef_resource_request_handler_t, cef_base_ref_counted_t}; +use cef::{Browser, Callback, CefString, Frame, ImplRequest, ImplResourceRequestHandler, Request, ReturnValue, WrapResourceRequestHandler}; + +use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME}; + +// TODO: Deny all external requests once we stop relying on google fonts for font preview +fn is_allowed_url(url: &str) -> bool { + url.starts_with(&format!("{RESOURCE_SCHEME}://{RESOURCE_DOMAIN}/")) || url.starts_with("https://fonts.googleapis.com/css2") || url.starts_with("https://fonts.gstatic.com/") +} + +pub(crate) struct ResourceRequestHandlerImpl { + object: *mut RcImpl<_cef_resource_request_handler_t, Self>, +} + +impl ResourceRequestHandlerImpl { + pub(crate) fn new() -> Self { + Self { object: std::ptr::null_mut() } + } +} + +impl ImplResourceRequestHandler for ResourceRequestHandlerImpl { + fn on_before_resource_load(&self, _browser: Option<&mut Browser>, _frame: Option<&mut Frame>, request: Option<&mut Request>, _callback: Option<&mut Callback>) -> ReturnValue { + let Some(request) = request else { return ReturnValue::CANCEL }; + let url = CefString::from(&request.url()).to_string(); + if is_allowed_url(&url) { + ReturnValue::CONTINUE + } else { + tracing::error!("Blocked resource load: {}", url); + ReturnValue::CANCEL + } + } + + fn get_raw(&self) -> *mut _cef_resource_request_handler_t { + self.object.cast() + } +} + +impl Clone for ResourceRequestHandlerImpl { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + } + Self { object: self.object } + } +} +impl Rc for ResourceRequestHandlerImpl { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} +impl WrapResourceRequestHandler for ResourceRequestHandlerImpl { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_resource_request_handler_t, Self>) { + self.object = object; + } +}