use std::path::PathBuf;
use std::rc::Rc;
#[cfg(feature = "gamepad")]
use embedder_traits::GamepadHapticEffectType;
use embedder_traits::{
AlertResponse, AllowOrDeny, AuthenticationResponse, BluetoothDeviceDescription,
ConfirmResponse, ConsoleLogLevel, ContextMenuAction, ContextMenuElementInformation,
ContextMenuItem, Cursor, EmbedderControlId, EmbedderControlResponse, FilePickerRequest,
FilterPattern, InputEventId, InputEventResult, InputMethodType, LoadStatus, MediaSessionEvent,
NewWebViewDetails, Notification, PermissionFeature, PromptResponse, RgbColor, ScreenGeometry,
SelectElementOptionOrOptgroup, SimpleDialogRequest, TraversalId, WebResourceRequest,
WebResourceResponse, WebResourceResponseMsg,
};
use paint_api::rendering_context::RenderingContext;
use servo_base::generic_channel::{GenericSender, SendError};
use servo_base::id::PipelineId;
use servo_constellation_traits::EmbedderToConstellationMessage;
use tokio::sync::mpsc::UnboundedSender as TokioSender;
use tokio::sync::oneshot::Sender;
use url::Url;
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use crate::proxies::ConstellationProxy;
use crate::responders::{IpcResponder, OneshotSender, ServoErrorSender};
use crate::{RegisterOrUnregister, Servo, WebView, WebViewBuilder};
pub struct NavigationRequest {
pub url: Url,
pub(crate) pipeline_id: PipelineId,
pub(crate) constellation_proxy: ConstellationProxy,
pub(crate) response_sent: bool,
}
impl NavigationRequest {
pub fn allow(mut self) {
self.constellation_proxy
.send(EmbedderToConstellationMessage::AllowNavigationResponse(
self.pipeline_id,
true,
));
self.response_sent = true;
}
pub fn deny(mut self) {
self.constellation_proxy
.send(EmbedderToConstellationMessage::AllowNavigationResponse(
self.pipeline_id,
false,
));
self.response_sent = true;
}
}
impl Drop for NavigationRequest {
fn drop(&mut self) {
if !self.response_sent {
self.constellation_proxy
.send(EmbedderToConstellationMessage::AllowNavigationResponse(
self.pipeline_id,
true,
));
}
}
}
pub struct PermissionRequest {
pub(crate) requested_feature: PermissionFeature,
pub(crate) allow_deny_request: AllowOrDenyRequest,
}
impl PermissionRequest {
pub fn feature(&self) -> PermissionFeature {
self.requested_feature
}
pub fn allow(self) {
self.allow_deny_request.allow();
}
pub fn deny(self) {
self.allow_deny_request.deny();
}
}
pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>, ServoErrorSender);
impl AllowOrDenyRequest {
pub(crate) fn new(
response_sender: GenericSender<AllowOrDeny>,
default_response: AllowOrDeny,
error_sender: ServoErrorSender,
) -> Self {
Self(
IpcResponder::new(response_sender, default_response),
error_sender,
)
}
pub fn allow(mut self) {
if let Err(error) = self.0.send(AllowOrDeny::Allow) {
self.1.raise_response_send_error(error);
}
}
pub fn deny(mut self) {
if let Err(error) = self.0.send(AllowOrDeny::Deny) {
self.1.raise_response_send_error(error);
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProtocolHandlerRegistration {
pub scheme: String,
pub url: Url,
pub register_or_unregister: RegisterOrUnregister,
}
pub struct BluetoothDeviceSelectionRequest {
devices: Vec<BluetoothDeviceDescription>,
responder: IpcResponder<Option<String>>,
}
impl BluetoothDeviceSelectionRequest {
pub(crate) fn new(
devices: Vec<BluetoothDeviceDescription>,
responder: GenericSender<Option<String>>,
) -> Self {
Self {
devices,
responder: IpcResponder::new(responder, None),
}
}
pub fn pick_device(mut self, device: &BluetoothDeviceDescription) -> Result<(), SendError> {
self.responder.send(Some(device.address.clone()))
}
pub fn cancel(mut self) -> Result<(), SendError> {
self.responder.send(None)
}
pub fn devices(&self) -> &Vec<BluetoothDeviceDescription> {
&self.devices
}
}
pub struct AuthenticationRequest {
pub(crate) url: Url,
pub(crate) for_proxy: bool,
pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
pub(crate) error_sender: ServoErrorSender,
}
impl AuthenticationRequest {
pub(crate) fn new(
url: Url,
for_proxy: bool,
response_sender: Sender<Option<AuthenticationResponse>>,
error_sender: ServoErrorSender,
) -> Self {
Self {
url,
for_proxy,
responder: IpcResponder::new_same_process(
Box::new(OneshotSender::from(response_sender)),
None,
),
error_sender,
}
}
pub fn url(&self) -> &Url {
&self.url
}
pub fn for_proxy(&self) -> bool {
self.for_proxy
}
pub fn authenticate(mut self, username: String, password: String) {
if let Err(error) = self
.responder
.send(Some(AuthenticationResponse { username, password }))
{
self.error_sender.raise_response_send_error(error);
}
}
}
pub struct WebResourceLoad {
pub request: WebResourceRequest,
pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
pub(crate) error_sender: ServoErrorSender,
}
impl WebResourceLoad {
pub(crate) fn new(
web_resource_request: WebResourceRequest,
response_sender: TokioSender<WebResourceResponseMsg>,
error_sender: ServoErrorSender,
) -> Self {
Self {
request: web_resource_request,
responder: IpcResponder::new_same_process(
Box::new(response_sender),
WebResourceResponseMsg::DoNotIntercept,
),
error_sender,
}
}
pub fn request(&self) -> &WebResourceRequest {
&self.request
}
pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad {
if let Err(error) = self.responder.send(WebResourceResponseMsg::Start(response)) {
self.error_sender.raise_response_send_error(error);
}
InterceptedWebResourceLoad {
request: self.request.clone(),
response_sender: self.responder,
finished: false,
error_sender: self.error_sender,
}
}
}
pub struct InterceptedWebResourceLoad {
pub request: WebResourceRequest,
pub(crate) response_sender: IpcResponder<WebResourceResponseMsg>,
pub(crate) finished: bool,
pub(crate) error_sender: ServoErrorSender,
}
impl InterceptedWebResourceLoad {
pub fn send_body_data(&mut self, data: Vec<u8>) {
if let Err(error) = self
.response_sender
.send(WebResourceResponseMsg::SendBodyData(data))
{
self.error_sender.raise_response_send_error(error);
}
}
pub fn finish(mut self) {
if let Err(error) = self
.response_sender
.send(WebResourceResponseMsg::FinishLoad)
{
self.error_sender.raise_response_send_error(error);
}
self.finished = true;
}
pub fn cancel(mut self) {
if let Err(error) = self
.response_sender
.send(WebResourceResponseMsg::CancelLoad)
{
self.error_sender.raise_response_send_error(error);
}
self.finished = true;
}
}
impl Drop for InterceptedWebResourceLoad {
fn drop(&mut self) {
if !self.finished {
if let Err(error) = self
.response_sender
.send(WebResourceResponseMsg::FinishLoad)
{
self.error_sender.raise_response_send_error(error);
}
}
}
}
pub enum EmbedderControl {
SelectElement(SelectElement),
ColorPicker(ColorPicker),
FilePicker(FilePicker),
InputMethod(InputMethodControl),
SimpleDialog(SimpleDialog),
ContextMenu(ContextMenu),
}
impl EmbedderControl {
pub fn id(&self) -> EmbedderControlId {
match self {
EmbedderControl::SelectElement(select_element) => select_element.id,
EmbedderControl::ColorPicker(color_picker) => color_picker.id,
EmbedderControl::FilePicker(file_picker) => file_picker.id,
EmbedderControl::InputMethod(input_method) => input_method.id,
EmbedderControl::SimpleDialog(simple_dialog) => simple_dialog.id(),
EmbedderControl::ContextMenu(context_menu) => context_menu.id,
}
}
}
pub struct ContextMenu {
pub(crate) id: EmbedderControlId,
pub(crate) position: DeviceIntRect,
pub(crate) items: Vec<ContextMenuItem>,
pub(crate) element_info: ContextMenuElementInformation,
pub(crate) response_sent: bool,
pub(crate) constellation_proxy: ConstellationProxy,
}
impl ContextMenu {
pub fn id(&self) -> EmbedderControlId {
self.id
}
pub fn position(&self) -> DeviceIntRect {
self.position
}
pub fn element_info(&self) -> &ContextMenuElementInformation {
&self.element_info
}
pub fn items(&self) -> &[ContextMenuItem] {
&self.items
}
pub fn select(mut self, action: ContextMenuAction) {
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::ContextMenu(Some(action)),
));
self.response_sent = true;
}
pub fn dismiss(mut self) {
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::ContextMenu(None),
));
self.response_sent = true;
}
}
impl Drop for ContextMenu {
fn drop(&mut self) {
if !self.response_sent {
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::ContextMenu(None),
));
}
}
}
pub struct SelectElement {
pub(crate) id: EmbedderControlId,
pub(crate) options: Vec<SelectElementOptionOrOptgroup>,
pub(crate) selected_option: Option<usize>,
pub(crate) position: DeviceIntRect,
pub(crate) constellation_proxy: ConstellationProxy,
pub(crate) response_sent: bool,
}
impl SelectElement {
pub fn id(&self) -> EmbedderControlId {
self.id
}
pub fn position(&self) -> DeviceIntRect {
self.position
}
pub fn options(&self) -> &[SelectElementOptionOrOptgroup] {
&self.options
}
pub fn select(&mut self, id: Option<usize>) {
self.selected_option = id;
}
pub fn selected_option(&self) -> Option<usize> {
self.selected_option
}
pub fn submit(mut self) {
self.response_sent = true;
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::SelectElement(self.selected_option()),
));
}
}
impl Drop for SelectElement {
fn drop(&mut self) {
if !self.response_sent {
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::SelectElement(self.selected_option()),
));
}
}
}
pub struct ColorPicker {
pub(crate) id: EmbedderControlId,
pub(crate) current_color: Option<RgbColor>,
pub(crate) position: DeviceIntRect,
pub(crate) constellation_proxy: ConstellationProxy,
pub(crate) response_sent: bool,
}
impl ColorPicker {
pub fn id(&self) -> EmbedderControlId {
self.id
}
pub fn position(&self) -> DeviceIntRect {
self.position
}
pub fn current_color(&self) -> Option<RgbColor> {
self.current_color
}
pub fn select(&mut self, color: Option<RgbColor>) {
self.current_color = color;
}
pub fn submit(mut self) {
self.response_sent = true;
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::ColorPicker(self.current_color),
));
}
}
impl Drop for ColorPicker {
fn drop(&mut self) {
if !self.response_sent {
self.constellation_proxy
.send(EmbedderToConstellationMessage::EmbedderControlResponse(
self.id,
EmbedderControlResponse::ColorPicker(self.current_color),
));
}
}
}
pub struct FilePicker {
pub(crate) id: EmbedderControlId,
pub(crate) file_picker_request: FilePickerRequest,
pub(crate) response_sender: Option<Sender<Option<Vec<PathBuf>>>>,
}
impl FilePicker {
pub fn id(&self) -> EmbedderControlId {
self.id
}
pub fn filter_patterns(&self) -> &[FilterPattern] {
&self.file_picker_request.filter_patterns
}
pub fn allow_select_multiple(&self) -> bool {
self.file_picker_request.allow_select_multiple
}
pub fn current_paths(&self) -> &[PathBuf] {
&self.file_picker_request.current_paths
}
pub fn select(&mut self, paths: &[PathBuf]) {
self.file_picker_request.current_paths = paths.to_owned();
}
pub fn submit(mut self) {
if let Some(sender) = self.response_sender.take() {
let _ = sender.send(Some(std::mem::take(
&mut self.file_picker_request.current_paths,
)));
}
}
pub fn dismiss(mut self) {
if let Some(sender) = self.response_sender.take() {
let _ = sender.send(None);
}
}
}
impl Drop for FilePicker {
fn drop(&mut self) {
if let Some(sender) = self.response_sender.take() {
let _ = sender.send(None);
}
}
}
pub struct InputMethodControl {
pub(crate) id: EmbedderControlId,
pub(crate) input_method_type: InputMethodType,
pub(crate) text: String,
pub(crate) insertion_point: Option<u32>,
pub(crate) position: DeviceIntRect,
pub(crate) multiline: bool,
pub(crate) allow_virtual_keyboard: bool,
}
impl InputMethodControl {
pub fn id(&self) -> EmbedderControlId {
self.id
}
pub fn input_method_type(&self) -> InputMethodType {
self.input_method_type
}
pub fn text(&self) -> String {
self.text.clone()
}
pub fn insertion_point(&self) -> Option<u32> {
self.insertion_point
}
pub fn position(&self) -> DeviceIntRect {
self.position
}
pub fn multiline(&self) -> bool {
self.multiline
}
pub fn allow_virtual_keyboard(&self) -> bool {
self.allow_virtual_keyboard
}
}
pub enum SimpleDialog {
Alert(AlertDialog),
Confirm(ConfirmDialog),
Prompt(PromptDialog),
}
impl SimpleDialog {
pub fn message(&self) -> &str {
match self {
SimpleDialog::Alert(alert_dialog) => alert_dialog.message(),
SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.message(),
SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.message(),
}
}
pub fn confirm(self) {
match self {
SimpleDialog::Alert(alert_dialog) => alert_dialog.confirm(),
SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.confirm(),
SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.confirm(),
}
}
pub fn dismiss(self) {
match self {
SimpleDialog::Alert(alert_dialog) => alert_dialog.confirm(),
SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.dismiss(),
SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.dismiss(),
}
}
}
impl SimpleDialog {
fn id(&self) -> EmbedderControlId {
match self {
SimpleDialog::Alert(alert_dialog) => alert_dialog.id,
SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.id,
SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.id,
}
}
}
impl From<SimpleDialogRequest> for SimpleDialog {
fn from(simple_dialog_request: SimpleDialogRequest) -> Self {
match simple_dialog_request {
SimpleDialogRequest::Alert {
id,
message,
response_sender,
} => Self::Alert(AlertDialog {
id,
message,
response_sender,
response_sent: false,
}),
SimpleDialogRequest::Confirm {
id,
message,
response_sender,
} => Self::Confirm(ConfirmDialog {
id,
message,
response_sender,
response_sent: false,
}),
SimpleDialogRequest::Prompt {
id,
message,
default,
response_sender,
} => Self::Prompt(PromptDialog {
id,
message,
current_value: default,
response_sender,
response_sent: false,
}),
}
}
}
pub struct AlertDialog {
id: EmbedderControlId,
message: String,
response_sender: GenericSender<AlertResponse>,
response_sent: bool,
}
impl Drop for AlertDialog {
fn drop(&mut self) {
if !self.response_sent {
let _ = self.response_sender.send(AlertResponse::Ok);
}
}
}
impl AlertDialog {
pub fn message(&self) -> &str {
&self.message
}
pub fn confirm(self) {
}
}
pub struct ConfirmDialog {
id: EmbedderControlId,
message: String,
response_sender: GenericSender<ConfirmResponse>,
response_sent: bool,
}
impl ConfirmDialog {
pub fn message(&self) -> &str {
&self.message
}
pub fn dismiss(mut self) {
let _ = self.response_sender.send(ConfirmResponse::Cancel);
self.response_sent = true;
}
pub fn confirm(mut self) {
let _ = self.response_sender.send(ConfirmResponse::Ok);
self.response_sent = true;
}
}
impl Drop for ConfirmDialog {
fn drop(&mut self) {
if !self.response_sent {
let _ = self.response_sender.send(ConfirmResponse::Cancel);
}
}
}
pub struct PromptDialog {
id: EmbedderControlId,
message: String,
current_value: String,
response_sender: GenericSender<PromptResponse>,
response_sent: bool,
}
impl Drop for PromptDialog {
fn drop(&mut self) {
if !self.response_sent {
let _ = self.response_sender.send(PromptResponse::Cancel);
}
}
}
impl PromptDialog {
pub fn message(&self) -> &str {
&self.message
}
pub fn current_value(&self) -> &str {
&self.current_value
}
pub fn set_current_value(&mut self, new_value: &str) {
self.current_value = new_value.to_owned()
}
pub fn dismiss(mut self) {
let _ = self.response_sender.send(PromptResponse::Cancel);
self.response_sent = true;
}
pub fn confirm(mut self) {
let _ = self
.response_sender
.send(PromptResponse::Ok(self.current_value.clone()));
self.response_sent = true;
}
}
pub struct CreateNewWebViewRequest {
pub(crate) servo: Servo,
pub(crate) responder: IpcResponder<Option<NewWebViewDetails>>,
}
impl CreateNewWebViewRequest {
pub fn builder(self, rendering_context: Rc<dyn RenderingContext>) -> WebViewBuilder {
WebViewBuilder::new_for_create_request(&self.servo, rendering_context, self.responder)
}
}
pub trait WebViewDelegate {
fn screen_geometry(&self, _webview: WebView) -> Option<ScreenGeometry> {
None
}
fn notify_url_changed(&self, _webview: WebView, _url: Url) {}
fn notify_page_title_changed(&self, _webview: WebView, _title: Option<String>) {}
fn notify_status_text_changed(&self, _webview: WebView, _status: Option<String>) {}
fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {}
fn notify_animating_changed(&self, _webview: WebView, _animating: bool) {}
fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {}
fn notify_cursor_changed(&self, _webview: WebView, _: Cursor) {}
fn notify_favicon_changed(&self, _webview: WebView) {}
fn notify_new_frame_ready(&self, _webview: WebView) {}
fn notify_history_changed(&self, _webview: WebView, _entries: Vec<Url>, _current: usize) {}
fn notify_traversal_complete(&self, _webview: WebView, _: TraversalId) {}
fn notify_closed(&self, _webview: WebView) {}
fn notify_input_event_handled(&self, _webview: WebView, _: InputEventId, _: InputEventResult) {}
fn notify_crashed(&self, _webview: WebView, _reason: String, _backtrace: Option<String>) {}
fn notify_media_session_event(&self, _webview: WebView, _event: MediaSessionEvent) {}
fn notify_fullscreen_state_changed(&self, _webview: WebView, _: bool) {}
fn request_navigation(&self, _webview: WebView, _navigation_request: NavigationRequest) {}
fn request_unload(&self, _webview: WebView, _unload_request: AllowOrDenyRequest) {}
fn request_move_to(&self, _webview: WebView, _: DeviceIntPoint) {}
fn request_protocol_handler(
&self,
_webview: WebView,
_protocol_handler_registration: ProtocolHandlerRegistration,
_allow_deny_request: AllowOrDenyRequest,
) {
}
fn request_resize_to(&self, _webview: WebView, _requested_outer_size: DeviceIntSize) {}
fn request_create_new(&self, _parent_webview: WebView, _: CreateNewWebViewRequest) {}
fn request_permission(&self, _webview: WebView, _: PermissionRequest) {}
fn request_authentication(
&self,
_webview: WebView,
_authentication_request: AuthenticationRequest,
) {
}
fn show_bluetooth_device_dialog(&self, _webview: WebView, _: BluetoothDeviceSelectionRequest) {}
fn show_embedder_control(&self, _webview: WebView, _embedder_control: EmbedderControl) {}
fn hide_embedder_control(&self, _webview: WebView, _control_id: EmbedderControlId) {}
#[cfg(feature = "gamepad")]
fn play_gamepad_haptic_effect(
&self,
_webview: WebView,
_: usize,
_: GamepadHapticEffectType,
_: Box<dyn FnOnce(bool)>,
) {
}
#[cfg(feature = "gamepad")]
fn stop_gamepad_haptic_effect(&self, _webview: WebView, _: usize, _: Box<dyn FnOnce(bool)>) {}
fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
fn show_notification(&self, _webview: WebView, _notification: Notification) {}
fn show_console_message(&self, _webview: WebView, _level: ConsoleLogLevel, _message: String) {}
fn notify_accessibility_tree_update(
&self,
_webview: WebView,
_tree_update: accesskit::TreeUpdate,
) {
}
}
pub(crate) struct DefaultWebViewDelegate;
impl WebViewDelegate for DefaultWebViewDelegate {}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_allow_deny_request() {
use servo_base::generic_channel;
use crate::responders::ServoErrorChannel;
for default_response in [AllowOrDeny::Allow, AllowOrDeny::Deny] {
let errors = ServoErrorChannel::default();
let (sender, receiver) =
generic_channel::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) =
generic_channel::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) =
generic_channel::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) =
generic_channel::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) =
generic_channel::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) =
generic_channel::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 crate::responders::ServoErrorChannel;
let url = Url::parse("https://example.com").expect("Guaranteed by argument");
let errors = ServoErrorChannel::default();
let (sender, mut receiver) = tokio::sync::oneshot::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());
let errors = ServoErrorChannel::default();
let (sender, mut receiver) = tokio::sync::oneshot::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) = tokio::sync::oneshot::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());
let errors = ServoErrorChannel::default();
let (sender, receiver) = tokio::sync::oneshot::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 crate::responders::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)
};
let errors = ServoErrorChannel::default();
let (sender, mut receiver) = tokio::sync::mpsc::unbounded_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());
let errors = ServoErrorChannel::default();
let (sender, mut receiver) = tokio::sync::mpsc::unbounded_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());
let errors = ServoErrorChannel::default();
let (sender, mut receiver) = tokio::sync::mpsc::unbounded_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());
let errors = ServoErrorChannel::default();
let (sender, receiver) = tokio::sync::mpsc::unbounded_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());
let errors = ServoErrorChannel::default();
let (sender, receiver) = tokio::sync::mpsc::unbounded_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());
let errors = ServoErrorChannel::default();
let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
drop(receiver);
drop(request);
assert!(errors.try_recv().is_none());
}
}