mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
libservo: Notify delegates of send errors in request objects (#35668)
* libservo: Notify delegates of send errors in request objects Signed-off-by: Delan Azabani <dazabani@igalia.com> * Remove webview error sender for simplicity Signed-off-by: Delan Azabani <dazabani@igalia.com> * Remove error sender trait, now that there is only one impl Signed-off-by: Delan Azabani <dazabani@igalia.com> * Address review feedback Signed-off-by: Delan Azabani <dazabani@igalia.com> * Add unit tests Signed-off-by: Delan Azabani <dazabani@igalia.com> --------- Signed-off-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
parent
f19dd23641
commit
a442a11330
8 changed files with 357 additions and 27 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4337,6 +4337,7 @@ dependencies = [
|
||||||
"gaol",
|
"gaol",
|
||||||
"gleam",
|
"gleam",
|
||||||
"gstreamer",
|
"gstreamer",
|
||||||
|
"http 1.3.1",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
"keyboard-types",
|
"keyboard-types",
|
||||||
"layout_thread_2020",
|
"layout_thread_2020",
|
||||||
|
|
|
@ -124,6 +124,7 @@ gaol = "0.2.1"
|
||||||
webxr = { path = "../webxr", features = ["ipc", "glwindow", "headless", "openxr-api"] }
|
webxr = { path = "../webxr", features = ["ipc", "glwindow", "headless", "openxr-api"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
http = { workspace = true }
|
||||||
libservo = { path = ".", features = ["tracing"] }
|
libservo = { path = ".", features = ["tracing"] }
|
||||||
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
|
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
mod clipboard_delegate;
|
mod clipboard_delegate;
|
||||||
mod proxies;
|
mod proxies;
|
||||||
|
mod responders;
|
||||||
mod servo_delegate;
|
mod servo_delegate;
|
||||||
mod webview;
|
mod webview;
|
||||||
mod webview_delegate;
|
mod webview_delegate;
|
||||||
|
@ -118,6 +119,7 @@ pub use {
|
||||||
pub use {bluetooth, bluetooth_traits};
|
pub use {bluetooth, bluetooth_traits};
|
||||||
|
|
||||||
use crate::proxies::ConstellationProxy;
|
use crate::proxies::ConstellationProxy;
|
||||||
|
use crate::responders::ServoErrorChannel;
|
||||||
pub use crate::servo_delegate::{ServoDelegate, ServoError};
|
pub use crate::servo_delegate::{ServoDelegate, ServoError};
|
||||||
pub use crate::webview::WebView;
|
pub use crate::webview::WebView;
|
||||||
pub use crate::webview_delegate::{
|
pub use crate::webview_delegate::{
|
||||||
|
@ -205,6 +207,7 @@ pub struct Servo {
|
||||||
/// When accessed, `Servo` will be reponsible for cleaning up the invalid `Weak`
|
/// When accessed, `Servo` will be reponsible for cleaning up the invalid `Weak`
|
||||||
/// references.
|
/// references.
|
||||||
webviews: RefCell<HashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
|
webviews: RefCell<HashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
|
||||||
|
servo_errors: ServoErrorChannel,
|
||||||
/// For single-process Servo instances, this field controls the initialization
|
/// For single-process Servo instances, this field controls the initialization
|
||||||
/// and deinitialization of the JS Engine. Multiprocess Servo instances have their
|
/// and deinitialization of the JS Engine. Multiprocess Servo instances have their
|
||||||
/// own instance that exists in the content process instead.
|
/// own instance that exists in the content process instead.
|
||||||
|
@ -536,6 +539,7 @@ impl Servo {
|
||||||
embedder_receiver,
|
embedder_receiver,
|
||||||
shutdown_state,
|
shutdown_state,
|
||||||
webviews: Default::default(),
|
webviews: Default::default(),
|
||||||
|
servo_errors: ServoErrorChannel::default(),
|
||||||
_js_engine_setup: js_engine_setup,
|
_js_engine_setup: js_engine_setup,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,6 +589,7 @@ impl Servo {
|
||||||
|
|
||||||
self.compositor.borrow_mut().perform_updates();
|
self.compositor.borrow_mut().perform_updates();
|
||||||
self.send_new_frame_ready_messages();
|
self.send_new_frame_ready_messages();
|
||||||
|
self.handle_delegate_errors();
|
||||||
self.clean_up_destroyed_webview_handles();
|
self.clean_up_destroyed_webview_handles();
|
||||||
|
|
||||||
if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
|
if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
|
||||||
|
@ -609,6 +614,12 @@ impl Servo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_delegate_errors(&self) {
|
||||||
|
while let Some(error) = self.servo_errors.try_recv() {
|
||||||
|
self.delegate().notify_error(self, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clean_up_destroyed_webview_handles(&self) {
|
fn clean_up_destroyed_webview_handles(&self) {
|
||||||
// Remove any webview handles that have been destroyed and would not be upgradable.
|
// Remove any webview handles that have been destroyed and would not be upgradable.
|
||||||
// Note that `retain` is O(capacity) because it visits empty buckets, so it may be worth
|
// Note that `retain` is O(capacity) because it visits empty buckets, so it may be worth
|
||||||
|
@ -758,7 +769,11 @@ 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::new(response_sender, AllowOrDeny::Allow);
|
let request = AllowOrDenyRequest::new(
|
||||||
|
response_sender,
|
||||||
|
AllowOrDeny::Allow,
|
||||||
|
self.servo_errors.sender(),
|
||||||
|
);
|
||||||
webview.delegate().request_unload(webview, request);
|
webview.delegate().request_unload(webview, request);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -824,12 +839,24 @@ impl Servo {
|
||||||
web_resource_request,
|
web_resource_request,
|
||||||
response_sender,
|
response_sender,
|
||||||
) => {
|
) => {
|
||||||
let web_resource_load = WebResourceLoad::new(web_resource_request, response_sender);
|
if let Some(webview) =
|
||||||
match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
|
webview_id.and_then(|webview_id| self.get_webview_handle(webview_id))
|
||||||
Some(webview) => webview
|
{
|
||||||
|
let web_resource_load = WebResourceLoad::new(
|
||||||
|
web_resource_request,
|
||||||
|
response_sender,
|
||||||
|
self.servo_errors.sender(),
|
||||||
|
);
|
||||||
|
webview
|
||||||
.delegate()
|
.delegate()
|
||||||
.load_web_resource(webview, web_resource_load),
|
.load_web_resource(webview, web_resource_load);
|
||||||
None => self.delegate().load_web_resource(web_resource_load),
|
} else {
|
||||||
|
let web_resource_load = WebResourceLoad::new(
|
||||||
|
web_resource_request,
|
||||||
|
response_sender,
|
||||||
|
self.servo_errors.sender(),
|
||||||
|
);
|
||||||
|
self.delegate().load_web_resource(web_resource_load);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EmbedderMsg::Panic(webview_id, reason, backtrace) => {
|
EmbedderMsg::Panic(webview_id, reason, backtrace) => {
|
||||||
|
@ -864,9 +891,13 @@ impl Servo {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EmbedderMsg::RequestAuthentication(webview_id, url, for_proxy, response_sender) => {
|
EmbedderMsg::RequestAuthentication(webview_id, url, for_proxy, response_sender) => {
|
||||||
let authentication_request =
|
|
||||||
AuthenticationRequest::new(url.into_url(), for_proxy, response_sender);
|
|
||||||
if let Some(webview) = self.get_webview_handle(webview_id) {
|
if let Some(webview) = self.get_webview_handle(webview_id) {
|
||||||
|
let authentication_request = AuthenticationRequest::new(
|
||||||
|
url.into_url(),
|
||||||
|
for_proxy,
|
||||||
|
response_sender,
|
||||||
|
self.servo_errors.sender(),
|
||||||
|
);
|
||||||
webview
|
webview
|
||||||
.delegate()
|
.delegate()
|
||||||
.request_authentication(webview, authentication_request);
|
.request_authentication(webview, authentication_request);
|
||||||
|
@ -879,6 +910,7 @@ impl Servo {
|
||||||
allow_deny_request: AllowOrDenyRequest::new(
|
allow_deny_request: AllowOrDenyRequest::new(
|
||||||
response_sender,
|
response_sender,
|
||||||
AllowOrDeny::Deny,
|
AllowOrDeny::Deny,
|
||||||
|
self.servo_errors.sender(),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
webview
|
webview
|
||||||
|
@ -921,7 +953,11 @@ impl Servo {
|
||||||
EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
|
EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
|
||||||
self.delegate().request_devtools_connection(
|
self.delegate().request_devtools_connection(
|
||||||
self,
|
self,
|
||||||
AllowOrDenyRequest::new(response_sender, AllowOrDeny::Deny),
|
AllowOrDenyRequest::new(
|
||||||
|
response_sender,
|
||||||
|
AllowOrDeny::Deny,
|
||||||
|
self.servo_errors.sender(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
EmbedderMsg::PlayGamepadHapticEffect(
|
EmbedderMsg::PlayGamepadHapticEffect(
|
||||||
|
|
56
components/servo/responders.rs
Normal file
56
components/servo/responders.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use crossbeam_channel::{Receiver, Sender, TryRecvError, unbounded};
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
|
use crate::ServoError;
|
||||||
|
|
||||||
|
/// Sender for errors raised by delegate request objects.
|
||||||
|
///
|
||||||
|
/// This allows errors to be raised asynchronously.
|
||||||
|
pub(crate) struct ServoErrorSender {
|
||||||
|
sender: Sender<ServoError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServoErrorSender {
|
||||||
|
pub(crate) fn raise_response_send_error(&self, error: bincode::Error) {
|
||||||
|
if let Err(error) = self.sender.send(ServoError::ResponseFailedToSend(error)) {
|
||||||
|
warn!("Failed to send Servo error: {error:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Channel for errors raised by [`WebViewDelegate`] request objects.
|
||||||
|
///
|
||||||
|
/// This allows errors to be raised asynchronously.
|
||||||
|
pub(crate) struct ServoErrorChannel {
|
||||||
|
sender: Sender<ServoError>,
|
||||||
|
receiver: Receiver<ServoError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ServoErrorChannel {
|
||||||
|
fn default() -> Self {
|
||||||
|
let (sender, receiver) = unbounded();
|
||||||
|
Self { sender, receiver }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServoErrorChannel {
|
||||||
|
pub(crate) fn sender(&self) -> ServoErrorSender {
|
||||||
|
ServoErrorSender {
|
||||||
|
sender: self.sender.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_recv(&self) -> Option<ServoError> {
|
||||||
|
match self.receiver.try_recv() {
|
||||||
|
Ok(result) => Some(result),
|
||||||
|
Err(error) => {
|
||||||
|
debug_assert_eq!(error, TryRecvError::Empty);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
use crate::Servo;
|
use crate::Servo;
|
||||||
use crate::webview_delegate::{AllowOrDenyRequest, WebResourceLoad};
|
use crate::webview_delegate::{AllowOrDenyRequest, WebResourceLoad};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
|
#[derive(Debug)]
|
||||||
pub enum ServoError {
|
pub enum ServoError {
|
||||||
/// The channel to the off-the-main-thread web engine has been lost. No further
|
/// The channel to the off-the-main-thread web engine has been lost. No further
|
||||||
/// attempts to communicate will happen. This is an unrecoverable error in Servo.
|
/// attempts to communicate will happen. This is an unrecoverable error in Servo.
|
||||||
|
@ -13,6 +13,8 @@ pub enum ServoError {
|
||||||
/// The devtools server, used to expose pages to remote web inspectors has failed
|
/// The devtools server, used to expose pages to remote web inspectors has failed
|
||||||
/// to start.
|
/// to start.
|
||||||
DevtoolsFailedToStart,
|
DevtoolsFailedToStart,
|
||||||
|
/// Failed to send response to delegate request.
|
||||||
|
ResponseFailedToSend(bincode::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ServoDelegate {
|
pub trait ServoDelegate {
|
||||||
|
|
|
@ -17,6 +17,7 @@ use serde::Serialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||||
|
|
||||||
|
use crate::responders::ServoErrorSender;
|
||||||
use crate::{ConstellationProxy, WebView};
|
use crate::{ConstellationProxy, WebView};
|
||||||
|
|
||||||
/// A request to navigate a [`WebView`] or one of its inner frames. This can be handled
|
/// A request to navigate a [`WebView`] or one of its inner frames. This can be handled
|
||||||
|
@ -95,6 +96,8 @@ impl<T: Serialize> Drop for IpcResponder<T> {
|
||||||
.default_response
|
.default_response
|
||||||
.take()
|
.take()
|
||||||
.expect("Guaranteed by inherent impl");
|
.expect("Guaranteed by inherent impl");
|
||||||
|
// Don’t notify embedder about send errors for the default response,
|
||||||
|
// since they didn’t send anything and probably don’t care.
|
||||||
let _ = self.response_sender.send(response);
|
let _ = self.response_sender.send(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,22 +125,30 @@ impl PermissionRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>);
|
pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>, ServoErrorSender);
|
||||||
|
|
||||||
impl AllowOrDenyRequest {
|
impl AllowOrDenyRequest {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
response_sender: IpcSender<AllowOrDeny>,
|
response_sender: IpcSender<AllowOrDeny>,
|
||||||
default_response: AllowOrDeny,
|
default_response: AllowOrDeny,
|
||||||
|
error_sender: ServoErrorSender,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self(IpcResponder::new(response_sender, default_response))
|
Self(
|
||||||
|
IpcResponder::new(response_sender, default_response),
|
||||||
|
error_sender,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allow(mut self) {
|
pub fn allow(mut self) {
|
||||||
let _ = self.0.send(AllowOrDeny::Allow);
|
if let Err(error) = self.0.send(AllowOrDeny::Allow) {
|
||||||
|
self.1.raise_response_send_error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deny(mut self) {
|
pub fn deny(mut self) {
|
||||||
let _ = self.0.send(AllowOrDeny::Deny);
|
if let Err(error) = self.0.send(AllowOrDeny::Deny) {
|
||||||
|
self.1.raise_response_send_error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +159,7 @@ pub struct AuthenticationRequest {
|
||||||
pub(crate) url: Url,
|
pub(crate) url: Url,
|
||||||
pub(crate) for_proxy: bool,
|
pub(crate) for_proxy: bool,
|
||||||
pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
|
pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
|
||||||
|
pub(crate) error_sender: ServoErrorSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthenticationRequest {
|
impl AuthenticationRequest {
|
||||||
|
@ -155,11 +167,13 @@ impl AuthenticationRequest {
|
||||||
url: Url,
|
url: Url,
|
||||||
for_proxy: bool,
|
for_proxy: bool,
|
||||||
response_sender: IpcSender<Option<AuthenticationResponse>>,
|
response_sender: IpcSender<Option<AuthenticationResponse>>,
|
||||||
|
error_sender: ServoErrorSender,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
url,
|
url,
|
||||||
for_proxy,
|
for_proxy,
|
||||||
responder: IpcResponder::new(response_sender, None),
|
responder: IpcResponder::new(response_sender, None),
|
||||||
|
error_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,9 +187,12 @@ 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
|
if let Err(error) = self
|
||||||
.responder
|
.responder
|
||||||
.send(Some(AuthenticationResponse { username, password }));
|
.send(Some(AuthenticationResponse { username, password }))
|
||||||
|
{
|
||||||
|
self.error_sender.raise_response_send_error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,16 +202,19 @@ impl AuthenticationRequest {
|
||||||
pub struct WebResourceLoad {
|
pub struct WebResourceLoad {
|
||||||
pub request: WebResourceRequest,
|
pub request: WebResourceRequest,
|
||||||
pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
|
pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
|
||||||
|
pub(crate) error_sender: ServoErrorSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebResourceLoad {
|
impl WebResourceLoad {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
web_resource_request: WebResourceRequest,
|
web_resource_request: WebResourceRequest,
|
||||||
response_sender: IpcSender<WebResourceResponseMsg>,
|
response_sender: IpcSender<WebResourceResponseMsg>,
|
||||||
|
error_sender: ServoErrorSender,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
request: web_resource_request,
|
request: web_resource_request,
|
||||||
responder: IpcResponder::new(response_sender, WebResourceResponseMsg::DoNotIntercept),
|
responder: IpcResponder::new(response_sender, WebResourceResponseMsg::DoNotIntercept),
|
||||||
|
error_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,11 +225,14 @@ 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.responder.send(WebResourceResponseMsg::Start(response));
|
if let Err(error) = self.responder.send(WebResourceResponseMsg::Start(response)) {
|
||||||
|
self.error_sender.raise_response_send_error(error);
|
||||||
|
}
|
||||||
InterceptedWebResourceLoad {
|
InterceptedWebResourceLoad {
|
||||||
request: self.request.clone(),
|
request: self.request.clone(),
|
||||||
response_sender: self.responder.into_inner(),
|
response_sender: self.responder.into_inner(),
|
||||||
finished: false,
|
finished: false,
|
||||||
|
error_sender: self.error_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,28 +246,38 @@ pub struct InterceptedWebResourceLoad {
|
||||||
pub request: WebResourceRequest,
|
pub request: WebResourceRequest,
|
||||||
pub(crate) response_sender: IpcSender<WebResourceResponseMsg>,
|
pub(crate) response_sender: IpcSender<WebResourceResponseMsg>,
|
||||||
pub(crate) finished: bool,
|
pub(crate) finished: bool,
|
||||||
|
pub(crate) error_sender: ServoErrorSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterceptedWebResourceLoad {
|
impl InterceptedWebResourceLoad {
|
||||||
/// Send a chunk of response body data. It's possible to make subsequent calls to
|
/// Send a chunk of response body data. It's possible to make subsequent calls to
|
||||||
/// this method when streaming body data.
|
/// this method when streaming body data.
|
||||||
pub fn send_body_data(&self, data: Vec<u8>) {
|
pub fn send_body_data(&self, data: Vec<u8>) {
|
||||||
let _ = self
|
if let Err(error) = self
|
||||||
.response_sender
|
.response_sender
|
||||||
.send(WebResourceResponseMsg::SendBodyData(data));
|
.send(WebResourceResponseMsg::SendBodyData(data))
|
||||||
|
{
|
||||||
|
self.error_sender.raise_response_send_error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Finish this [`InterceptedWebResourceLoad`] and complete the response.
|
/// Finish this [`InterceptedWebResourceLoad`] and complete the response.
|
||||||
pub fn finish(mut self) {
|
pub fn finish(mut self) {
|
||||||
let _ = self
|
if let Err(error) = self
|
||||||
.response_sender
|
.response_sender
|
||||||
.send(WebResourceResponseMsg::FinishLoad);
|
.send(WebResourceResponseMsg::FinishLoad)
|
||||||
|
{
|
||||||
|
self.error_sender.raise_response_send_error(error);
|
||||||
|
}
|
||||||
self.finished = true;
|
self.finished = true;
|
||||||
}
|
}
|
||||||
/// Cancel this [`InterceptedWebResourceLoad`], which will trigger a network error.
|
/// Cancel this [`InterceptedWebResourceLoad`], which will trigger a network error.
|
||||||
pub fn cancel(mut self) {
|
pub fn cancel(mut self) {
|
||||||
let _ = self
|
if let Err(error) = self
|
||||||
.response_sender
|
.response_sender
|
||||||
.send(WebResourceResponseMsg::CancelLoad);
|
.send(WebResourceResponseMsg::CancelLoad)
|
||||||
|
{
|
||||||
|
self.error_sender.raise_response_send_error(error);
|
||||||
|
}
|
||||||
self.finished = true;
|
self.finished = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,9 +285,12 @@ impl InterceptedWebResourceLoad {
|
||||||
impl Drop for InterceptedWebResourceLoad {
|
impl Drop for InterceptedWebResourceLoad {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.finished {
|
if !self.finished {
|
||||||
let _ = self
|
if let Err(error) = self
|
||||||
.response_sender
|
.response_sender
|
||||||
.send(WebResourceResponseMsg::FinishLoad);
|
.send(WebResourceResponseMsg::FinishLoad)
|
||||||
|
{
|
||||||
|
self.error_sender.raise_response_send_error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,3 +466,200 @@ pub trait WebViewDelegate {
|
||||||
|
|
||||||
pub(crate) struct DefaultWebViewDelegate;
|
pub(crate) struct DefaultWebViewDelegate;
|
||||||
impl WebViewDelegate for DefaultWebViewDelegate {}
|
impl WebViewDelegate for DefaultWebViewDelegate {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_allow_deny_request() {
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
|
||||||
|
use crate::ServoErrorChannel;
|
||||||
|
|
||||||
|
for default_response in [AllowOrDeny::Allow, AllowOrDeny::Deny] {
|
||||||
|
// Explicit allow yields allow and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
|
||||||
|
request.allow();
|
||||||
|
assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Allow));
|
||||||
|
assert_eq!(receiver.try_recv().ok(), None);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// Explicit deny yields deny and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
|
||||||
|
request.deny();
|
||||||
|
assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Deny));
|
||||||
|
assert_eq!(receiver.try_recv().ok(), None);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// No response yields default response and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
|
||||||
|
drop(request);
|
||||||
|
assert_eq!(receiver.try_recv().ok(), Some(default_response));
|
||||||
|
assert_eq!(receiver.try_recv().ok(), None);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// Explicit allow when receiver disconnected yields error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
request.allow();
|
||||||
|
assert!(errors.try_recv().is_some());
|
||||||
|
|
||||||
|
// Explicit deny when receiver disconnected yields error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
request.deny();
|
||||||
|
assert!(errors.try_recv().is_some());
|
||||||
|
|
||||||
|
// No response when receiver disconnected yields no error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
drop(request);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_authentication_request() {
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
|
||||||
|
use crate::ServoErrorChannel;
|
||||||
|
|
||||||
|
let url = Url::parse("https://example.com").expect("Guaranteed by argument");
|
||||||
|
|
||||||
|
// Explicit response yields that response and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
|
||||||
|
request.authenticate("diffie".to_owned(), "hunter2".to_owned());
|
||||||
|
assert_eq!(
|
||||||
|
receiver.try_recv().ok(),
|
||||||
|
Some(Some(AuthenticationResponse {
|
||||||
|
username: "diffie".to_owned(),
|
||||||
|
password: "hunter2".to_owned(),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
assert_eq!(receiver.try_recv().ok(), None);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// No response yields None response and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
|
||||||
|
drop(request);
|
||||||
|
assert_eq!(receiver.try_recv().ok(), Some(None));
|
||||||
|
assert_eq!(receiver.try_recv().ok(), None);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// Explicit response when receiver disconnected yields error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
request.authenticate("diffie".to_owned(), "hunter2".to_owned());
|
||||||
|
assert!(errors.try_recv().is_some());
|
||||||
|
|
||||||
|
// No response when receiver disconnected yields no error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
drop(request);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_web_resource_load() {
|
||||||
|
use http::{HeaderMap, Method, StatusCode};
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
|
||||||
|
use crate::ServoErrorChannel;
|
||||||
|
|
||||||
|
let web_resource_request = || WebResourceRequest {
|
||||||
|
method: Method::GET,
|
||||||
|
headers: HeaderMap::default(),
|
||||||
|
url: Url::parse("https://example.com").expect("Guaranteed by argument"),
|
||||||
|
is_for_main_frame: false,
|
||||||
|
is_redirect: false,
|
||||||
|
};
|
||||||
|
let web_resource_response = || {
|
||||||
|
WebResourceResponse::new(Url::parse("https://diffie.test").expect("Guaranteed by argument"))
|
||||||
|
.status_code(StatusCode::IM_A_TEAPOT)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Explicit intercept with explicit cancel yields Start and Cancel and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
|
||||||
|
request.intercept(web_resource_response()).cancel();
|
||||||
|
assert!(matches!(
|
||||||
|
receiver.try_recv(),
|
||||||
|
Ok(WebResourceResponseMsg::Start(_))
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
receiver.try_recv(),
|
||||||
|
Ok(WebResourceResponseMsg::CancelLoad)
|
||||||
|
));
|
||||||
|
assert!(matches!(receiver.try_recv(), Err(_)));
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// Explicit intercept with no further action yields Start and FinishLoad and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
|
||||||
|
drop(request.intercept(web_resource_response()));
|
||||||
|
assert!(matches!(
|
||||||
|
receiver.try_recv(),
|
||||||
|
Ok(WebResourceResponseMsg::Start(_))
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
receiver.try_recv(),
|
||||||
|
Ok(WebResourceResponseMsg::FinishLoad)
|
||||||
|
));
|
||||||
|
assert!(matches!(receiver.try_recv(), Err(_)));
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// No response yields DoNotIntercept and nothing else
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
|
||||||
|
drop(request);
|
||||||
|
assert!(matches!(
|
||||||
|
receiver.try_recv(),
|
||||||
|
Ok(WebResourceResponseMsg::DoNotIntercept)
|
||||||
|
));
|
||||||
|
assert!(matches!(receiver.try_recv(), Err(_)));
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
|
||||||
|
// Explicit intercept with explicit cancel when receiver disconnected yields error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
request.intercept(web_resource_response()).cancel();
|
||||||
|
assert!(errors.try_recv().is_some());
|
||||||
|
|
||||||
|
// Explicit intercept with no further action when receiver disconnected yields error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
drop(request.intercept(web_resource_response()));
|
||||||
|
assert!(errors.try_recv().is_some());
|
||||||
|
|
||||||
|
// No response when receiver disconnected yields no error
|
||||||
|
let errors = ServoErrorChannel::default();
|
||||||
|
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel");
|
||||||
|
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
|
||||||
|
drop(receiver);
|
||||||
|
drop(request);
|
||||||
|
assert!(errors.try_recv().is_none());
|
||||||
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ pub enum SimpleDialog {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
pub struct AuthenticationResponse {
|
pub struct AuthenticationResponse {
|
||||||
/// Username for http request authentication
|
/// Username for http request authentication
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -208,7 +208,7 @@ impl Default for PromptResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A response to a request to allow or deny an action.
|
/// A response to a request to allow or deny an action.
|
||||||
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
pub enum AllowOrDeny {
|
pub enum AllowOrDeny {
|
||||||
Allow,
|
Allow,
|
||||||
Deny,
|
Deny,
|
||||||
|
|
|
@ -165,6 +165,7 @@ class MachCommands(CommandBase):
|
||||||
"fonts",
|
"fonts",
|
||||||
"hyper_serde",
|
"hyper_serde",
|
||||||
"layout_2020",
|
"layout_2020",
|
||||||
|
"libservo",
|
||||||
"net",
|
"net",
|
||||||
"net_traits",
|
"net_traits",
|
||||||
"pixels",
|
"pixels",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue