libservo: Refactor ipc-channel default response logic (#35624)

Signed-off-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
Delan Azabani 2025-02-24 23:40:09 +08:00 committed by GitHub
parent e887e621f8
commit fe509bb82f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 90 additions and 71 deletions

2
Cargo.lock generated
View file

@ -4330,6 +4330,7 @@ dependencies = [
"arboard", "arboard",
"background_hang_monitor", "background_hang_monitor",
"base", "base",
"bincode",
"bluetooth", "bluetooth",
"bluetooth_traits", "bluetooth_traits",
"canvas", "canvas",
@ -4366,6 +4367,7 @@ dependencies = [
"script", "script",
"script_layout_interface", "script_layout_interface",
"script_traits", "script_traits",
"serde",
"servo-media", "servo-media",
"servo-media-dummy", "servo-media-dummy",
"servo-media-gstreamer", "servo-media-gstreamer",

View file

@ -58,6 +58,7 @@ webgpu = [
[dependencies] [dependencies]
background_hang_monitor = { path = "../background_hang_monitor" } background_hang_monitor = { path = "../background_hang_monitor" }
base = { workspace = true } base = { workspace = true }
bincode = { workspace = true }
bluetooth = { path = "../bluetooth", optional = true } bluetooth = { path = "../bluetooth", optional = true }
bluetooth_traits = { workspace = true, optional = true } bluetooth_traits = { workspace = true, optional = true }
canvas = { path = "../canvas", default-features = false } canvas = { path = "../canvas", default-features = false }
@ -91,6 +92,7 @@ profile_traits = { workspace = true }
script = { path = "../script" } script = { path = "../script" }
script_layout_interface = { workspace = true } script_layout_interface = { workspace = true }
script_traits = { workspace = true } script_traits = { workspace = true }
serde = { workspace = true }
servo-media = { workspace = true } servo-media = { workspace = true }
servo-media-dummy = { workspace = true } servo-media-dummy = { workspace = true }
servo-media-gstreamer = { workspace = true, optional = true } servo-media-gstreamer = { workspace = true, optional = true }

View file

@ -766,11 +766,7 @@ impl Servo {
}, },
EmbedderMsg::AllowUnload(webview_id, response_sender) => { EmbedderMsg::AllowUnload(webview_id, response_sender) => {
if let Some(webview) = self.get_webview_handle(webview_id) { if let Some(webview) = self.get_webview_handle(webview_id) {
let request = AllowOrDenyRequest { let request = AllowOrDenyRequest::new(response_sender, AllowOrDeny::Allow);
response_sender,
response_sent: false,
default_response: AllowOrDeny::Allow,
};
webview.delegate().request_unload(webview, request); webview.delegate().request_unload(webview, request);
} }
}, },
@ -836,11 +832,7 @@ impl Servo {
web_resource_request, web_resource_request,
response_sender, response_sender,
) => { ) => {
let web_resource_load = WebResourceLoad { let web_resource_load = WebResourceLoad::new(web_resource_request, response_sender);
request: web_resource_request,
response_sender,
intercepted: false,
};
match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) { match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
Some(webview) => webview Some(webview) => webview
.delegate() .delegate()
@ -880,12 +872,8 @@ impl Servo {
} }
}, },
EmbedderMsg::RequestAuthentication(webview_id, url, for_proxy, response_sender) => { EmbedderMsg::RequestAuthentication(webview_id, url, for_proxy, response_sender) => {
let authentication_request = AuthenticationRequest { let authentication_request =
url: url.into_url(), AuthenticationRequest::new(url.into_url(), for_proxy, response_sender);
for_proxy,
response_sender,
response_sent: false,
};
if let Some(webview) = self.get_webview_handle(webview_id) { if let Some(webview) = self.get_webview_handle(webview_id) {
webview webview
.delegate() .delegate()
@ -896,11 +884,10 @@ impl Servo {
if let Some(webview) = self.get_webview_handle(webview_id) { if let Some(webview) = self.get_webview_handle(webview_id) {
let permission_request = PermissionRequest { let permission_request = PermissionRequest {
requested_feature, requested_feature,
allow_deny_request: AllowOrDenyRequest { allow_deny_request: AllowOrDenyRequest::new(
response_sender, response_sender,
response_sent: false, AllowOrDeny::Deny,
default_response: AllowOrDeny::Deny, ),
},
}; };
webview webview
.delegate() .delegate()
@ -942,11 +929,7 @@ impl Servo {
EmbedderMsg::RequestDevtoolsConnection(response_sender) => { EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
self.delegate().request_devtools_connection( self.delegate().request_devtools_connection(
self, self,
AllowOrDenyRequest { AllowOrDenyRequest::new(response_sender, AllowOrDeny::Deny),
response_sender,
response_sent: false,
default_response: AllowOrDeny::Deny,
},
); );
}, },
EmbedderMsg::PlayGamepadHapticEffect( EmbedderMsg::PlayGamepadHapticEffect(

View file

@ -14,6 +14,7 @@ use embedder_traits::{
}; };
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use keyboard_types::KeyboardEvent; use keyboard_types::KeyboardEvent;
use serde::Serialize;
use url::Url; use url::Url;
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
@ -60,6 +61,46 @@ impl Drop for NavigationRequest {
} }
} }
/// Sends a response over an IPC channel, or a default response on [`Drop`] if no response was sent.
pub(crate) struct IpcResponder<T: Serialize> {
response_sender: IpcSender<T>,
response_sent: bool,
/// Always present, except when taken by [`Drop`].
default_response: Option<T>,
}
impl<T: Serialize> IpcResponder<T> {
pub(crate) fn new(response_sender: IpcSender<T>, default_response: T) -> Self {
Self {
response_sender,
response_sent: false,
default_response: Some(default_response),
}
}
pub(crate) fn send(&mut self, response: T) -> bincode::Result<()> {
let result = self.response_sender.send(response);
self.response_sent = true;
result
}
pub(crate) fn into_inner(self) -> IpcSender<T> {
self.response_sender.clone()
}
}
impl<T: Serialize> Drop for IpcResponder<T> {
fn drop(&mut self) {
if !self.response_sent {
let response = self
.default_response
.take()
.expect("Guaranteed by inherent impl");
let _ = self.response_sender.send(response);
}
}
}
/// A permissions request for a [`WebView`] The embedder should allow or deny the request, /// A permissions request for a [`WebView`] The embedder should allow or deny the request,
/// either by reading a cached value or querying the user for permission via the user /// either by reading a cached value or querying the user for permission via the user
/// interface. /// interface.
@ -82,29 +123,22 @@ impl PermissionRequest {
} }
} }
pub struct AllowOrDenyRequest { pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>);
pub(crate) response_sender: IpcSender<AllowOrDeny>,
pub(crate) response_sent: bool,
pub(crate) default_response: AllowOrDeny,
}
impl AllowOrDenyRequest { impl AllowOrDenyRequest {
pub(crate) fn new(
response_sender: IpcSender<AllowOrDeny>,
default_response: AllowOrDeny,
) -> Self {
Self(IpcResponder::new(response_sender, default_response))
}
pub fn allow(mut self) { pub fn allow(mut self) {
let _ = self.response_sender.send(AllowOrDeny::Allow); let _ = self.0.send(AllowOrDeny::Allow);
self.response_sent = true;
} }
pub fn deny(mut self) { pub fn deny(mut self) {
let _ = self.response_sender.send(AllowOrDeny::Deny); let _ = self.0.send(AllowOrDeny::Deny);
self.response_sent = true;
}
}
impl Drop for AllowOrDenyRequest {
fn drop(&mut self) {
if !self.response_sent {
let _ = self.response_sender.send(self.default_response);
}
} }
} }
@ -114,11 +148,22 @@ impl Drop for AllowOrDenyRequest {
pub struct AuthenticationRequest { pub struct AuthenticationRequest {
pub(crate) url: Url, pub(crate) url: Url,
pub(crate) for_proxy: bool, pub(crate) for_proxy: bool,
pub(crate) response_sender: IpcSender<Option<AuthenticationResponse>>, pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
pub(crate) response_sent: bool,
} }
impl AuthenticationRequest { impl AuthenticationRequest {
pub(crate) fn new(
url: Url,
for_proxy: bool,
response_sender: IpcSender<Option<AuthenticationResponse>>,
) -> Self {
Self {
url,
for_proxy,
responder: IpcResponder::new(response_sender, None),
}
}
/// The URL of the request that triggered this authentication. /// The URL of the request that triggered this authentication.
pub fn url(&self) -> &Url { pub fn url(&self) -> &Url {
&self.url &self.url
@ -130,17 +175,8 @@ impl AuthenticationRequest {
/// Respond to the [`AuthenticationRequest`] with the given username and password. /// Respond to the [`AuthenticationRequest`] with the given username and password.
pub fn authenticate(mut self, username: String, password: String) { pub fn authenticate(mut self, username: String, password: String) {
let _ = self let _ = self
.response_sender .responder
.send(Some(AuthenticationResponse { username, password })); .send(Some(AuthenticationResponse { username, password }));
self.response_sent = true;
}
}
impl Drop for AuthenticationRequest {
fn drop(&mut self) {
if !self.response_sent {
let _ = self.response_sender.send(None);
}
} }
} }
@ -149,11 +185,20 @@ impl Drop for AuthenticationRequest {
/// by calling [`WebResourceLoad::intercept`]. /// by calling [`WebResourceLoad::intercept`].
pub struct WebResourceLoad { pub struct WebResourceLoad {
pub request: WebResourceRequest, pub request: WebResourceRequest,
pub(crate) response_sender: IpcSender<WebResourceResponseMsg>, pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
pub(crate) intercepted: bool,
} }
impl WebResourceLoad { impl WebResourceLoad {
pub(crate) fn new(
web_resource_request: WebResourceRequest,
response_sender: IpcSender<WebResourceResponseMsg>,
) -> Self {
Self {
request: web_resource_request,
responder: IpcResponder::new(response_sender, WebResourceResponseMsg::DoNotIntercept),
}
}
/// The [`WebResourceRequest`] associated with this [`WebResourceLoad`]. /// The [`WebResourceRequest`] associated with this [`WebResourceLoad`].
pub fn request(&self) -> &WebResourceRequest { pub fn request(&self) -> &WebResourceRequest {
&self.request &self.request
@ -161,28 +206,15 @@ impl WebResourceLoad {
/// Intercept this [`WebResourceLoad`] and control the response via the returned /// Intercept this [`WebResourceLoad`] and control the response via the returned
/// [`InterceptedWebResourceLoad`]. /// [`InterceptedWebResourceLoad`].
pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad { pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad {
let _ = self let _ = self.responder.send(WebResourceResponseMsg::Start(response));
.response_sender
.send(WebResourceResponseMsg::Start(response));
self.intercepted = true;
InterceptedWebResourceLoad { InterceptedWebResourceLoad {
request: self.request.clone(), request: self.request.clone(),
response_sender: self.response_sender.clone(), response_sender: self.responder.into_inner(),
finished: false, finished: false,
} }
} }
} }
impl Drop for WebResourceLoad {
fn drop(&mut self) {
if !self.intercepted {
let _ = self
.response_sender
.send(WebResourceResponseMsg::DoNotIntercept);
}
}
}
/// An intercepted web resource load. This struct allows the client to send an alternative response /// An intercepted web resource load. This struct allows the client to send an alternative response
/// after calling [`WebResourceLoad::intercept`]. In order to send chunks of body data, the client /// after calling [`WebResourceLoad::intercept`]. In order to send chunks of body data, the client
/// must call [`InterceptedWebResourceLoad::send_body_data`]. When the interception is complete, the client /// must call [`InterceptedWebResourceLoad::send_body_data`]. When the interception is complete, the client