1use std::path::PathBuf;
6
7use base::generic_channel::{GenericSender, SendResult};
8use base::id::PipelineId;
9use constellation_traits::EmbedderToConstellationMessage;
10use embedder_traits::{
11 AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, EmbedderControlId,
12 EmbedderControlResponse, FilePickerRequest, FilterPattern, GamepadHapticEffectType,
13 InputEventId, InputEventResult, InputMethodType, LoadStatus, MediaSessionEvent, Notification,
14 PermissionFeature, RgbColor, ScreenGeometry, SelectElementOptionOrOptgroup, SimpleDialog,
15 TraversalId, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
16};
17use ipc_channel::ipc::IpcSender;
18use serde::Serialize;
19use url::Url;
20use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
21
22use crate::responders::ServoErrorSender;
23use crate::{ConstellationProxy, WebView};
24
25pub struct NavigationRequest {
28 pub url: Url,
29 pub(crate) pipeline_id: PipelineId,
30 pub(crate) constellation_proxy: ConstellationProxy,
31 pub(crate) response_sent: bool,
32}
33
34impl NavigationRequest {
35 pub fn allow(mut self) {
36 self.constellation_proxy
37 .send(EmbedderToConstellationMessage::AllowNavigationResponse(
38 self.pipeline_id,
39 true,
40 ));
41 self.response_sent = true;
42 }
43
44 pub fn deny(mut self) {
45 self.constellation_proxy
46 .send(EmbedderToConstellationMessage::AllowNavigationResponse(
47 self.pipeline_id,
48 false,
49 ));
50 self.response_sent = true;
51 }
52}
53
54impl Drop for NavigationRequest {
55 fn drop(&mut self) {
56 if !self.response_sent {
57 self.constellation_proxy
58 .send(EmbedderToConstellationMessage::AllowNavigationResponse(
59 self.pipeline_id,
60 true,
61 ));
62 }
63 }
64}
65
66pub(crate) struct IpcResponder<T: Serialize> {
68 response_sender: GenericSender<T>,
69 response_sent: bool,
70 default_response: Option<T>,
72}
73
74impl<T: Serialize> IpcResponder<T> {
75 pub(crate) fn new(response_sender: GenericSender<T>, default_response: T) -> Self {
76 Self {
77 response_sender,
78 response_sent: false,
79 default_response: Some(default_response),
80 }
81 }
82
83 pub(crate) fn send(&mut self, response: T) -> SendResult {
84 let result = self.response_sender.send(response);
85 self.response_sent = true;
86 result
87 }
88
89 pub(crate) fn into_inner(self) -> GenericSender<T> {
90 self.response_sender.clone()
91 }
92}
93
94impl<T: Serialize> Drop for IpcResponder<T> {
95 fn drop(&mut self) {
96 if !self.response_sent {
97 let response = self
98 .default_response
99 .take()
100 .expect("Guaranteed by inherent impl");
101 let _ = self.response_sender.send(response);
104 }
105 }
106}
107
108pub struct PermissionRequest {
112 pub(crate) requested_feature: PermissionFeature,
113 pub(crate) allow_deny_request: AllowOrDenyRequest,
114}
115
116impl PermissionRequest {
117 pub fn feature(&self) -> PermissionFeature {
118 self.requested_feature
119 }
120
121 pub fn allow(self) {
122 self.allow_deny_request.allow();
123 }
124
125 pub fn deny(self) {
126 self.allow_deny_request.deny();
127 }
128}
129
130pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>, ServoErrorSender);
131
132impl AllowOrDenyRequest {
133 pub(crate) fn new(
134 response_sender: GenericSender<AllowOrDeny>,
135 default_response: AllowOrDeny,
136 error_sender: ServoErrorSender,
137 ) -> Self {
138 Self(
139 IpcResponder::new(response_sender, default_response),
140 error_sender,
141 )
142 }
143
144 pub fn allow(mut self) {
145 if let Err(error) = self.0.send(AllowOrDeny::Allow) {
146 self.1.raise_response_send_error(error);
147 }
148 }
149
150 pub fn deny(mut self) {
151 if let Err(error) = self.0.send(AllowOrDeny::Deny) {
152 self.1.raise_response_send_error(error);
153 }
154 }
155}
156
157pub struct AuthenticationRequest {
161 pub(crate) url: Url,
162 pub(crate) for_proxy: bool,
163 pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
164 pub(crate) error_sender: ServoErrorSender,
165}
166
167impl AuthenticationRequest {
168 pub(crate) fn new(
169 url: Url,
170 for_proxy: bool,
171 response_sender: GenericSender<Option<AuthenticationResponse>>,
172 error_sender: ServoErrorSender,
173 ) -> Self {
174 Self {
175 url,
176 for_proxy,
177 responder: IpcResponder::new(response_sender, None),
178 error_sender,
179 }
180 }
181
182 pub fn url(&self) -> &Url {
184 &self.url
185 }
186 pub fn for_proxy(&self) -> bool {
188 self.for_proxy
189 }
190 pub fn authenticate(mut self, username: String, password: String) {
192 if let Err(error) = self
193 .responder
194 .send(Some(AuthenticationResponse { username, password }))
195 {
196 self.error_sender.raise_response_send_error(error);
197 }
198 }
199}
200
201pub struct WebResourceLoad {
205 pub request: WebResourceRequest,
206 pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
207 pub(crate) error_sender: ServoErrorSender,
208}
209
210impl WebResourceLoad {
211 pub(crate) fn new(
212 web_resource_request: WebResourceRequest,
213 response_sender: GenericSender<WebResourceResponseMsg>,
214 error_sender: ServoErrorSender,
215 ) -> Self {
216 Self {
217 request: web_resource_request,
218 responder: IpcResponder::new(response_sender, WebResourceResponseMsg::DoNotIntercept),
219 error_sender,
220 }
221 }
222
223 pub fn request(&self) -> &WebResourceRequest {
225 &self.request
226 }
227 pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad {
230 if let Err(error) = self.responder.send(WebResourceResponseMsg::Start(response)) {
231 self.error_sender.raise_response_send_error(error);
232 }
233 InterceptedWebResourceLoad {
234 request: self.request.clone(),
235 response_sender: self.responder.into_inner(),
236 finished: false,
237 error_sender: self.error_sender,
238 }
239 }
240}
241
242pub struct InterceptedWebResourceLoad {
248 pub request: WebResourceRequest,
249 pub(crate) response_sender: GenericSender<WebResourceResponseMsg>,
250 pub(crate) finished: bool,
251 pub(crate) error_sender: ServoErrorSender,
252}
253
254impl InterceptedWebResourceLoad {
255 pub fn send_body_data(&self, data: Vec<u8>) {
258 if let Err(error) = self
259 .response_sender
260 .send(WebResourceResponseMsg::SendBodyData(data))
261 {
262 self.error_sender.raise_response_send_error(error);
263 }
264 }
265 pub fn finish(mut self) {
267 if let Err(error) = self
268 .response_sender
269 .send(WebResourceResponseMsg::FinishLoad)
270 {
271 self.error_sender.raise_response_send_error(error);
272 }
273 self.finished = true;
274 }
275 pub fn cancel(mut self) {
277 if let Err(error) = self
278 .response_sender
279 .send(WebResourceResponseMsg::CancelLoad)
280 {
281 self.error_sender.raise_response_send_error(error);
282 }
283 self.finished = true;
284 }
285}
286
287impl Drop for InterceptedWebResourceLoad {
288 fn drop(&mut self) {
289 if !self.finished {
290 if let Err(error) = self
291 .response_sender
292 .send(WebResourceResponseMsg::FinishLoad)
293 {
294 self.error_sender.raise_response_send_error(error);
295 }
296 }
297 }
298}
299
300pub enum EmbedderControl {
302 SelectElement(SelectElement),
304 ColorPicker(ColorPicker),
306 FilePicker(FilePicker),
308 InputMethod(InputMethodControl),
311 SimpleDialog(SimpleDialog),
316}
317
318impl EmbedderControl {
319 pub fn id(&self) -> EmbedderControlId {
320 match self {
321 EmbedderControl::SelectElement(select_element) => select_element.id,
322 EmbedderControl::ColorPicker(color_picker) => color_picker.id,
323 EmbedderControl::FilePicker(file_picker) => file_picker.id,
324 EmbedderControl::InputMethod(input_method) => input_method.id,
325 EmbedderControl::SimpleDialog(simple_dialog) => simple_dialog.id(),
326 }
327 }
328}
329pub struct SelectElement {
331 pub(crate) id: EmbedderControlId,
332 pub(crate) options: Vec<SelectElementOptionOrOptgroup>,
333 pub(crate) selected_option: Option<usize>,
334 pub(crate) position: DeviceIntRect,
335 pub(crate) constellation_proxy: ConstellationProxy,
336 pub(crate) response_sent: bool,
337}
338
339impl SelectElement {
340 pub fn id(&self) -> EmbedderControlId {
342 self.id
343 }
344
345 pub fn position(&self) -> DeviceIntRect {
349 self.position
350 }
351
352 pub fn options(&self) -> &[SelectElementOptionOrOptgroup] {
355 &self.options
356 }
357
358 pub fn select(&mut self, id: Option<usize>) {
363 self.selected_option = id;
364 }
365
366 pub fn selected_option(&self) -> Option<usize> {
367 self.selected_option
368 }
369
370 pub fn submit(mut self) {
372 self.response_sent = true;
373 self.constellation_proxy
374 .send(EmbedderToConstellationMessage::EmbedderControlResponse(
375 self.id,
376 EmbedderControlResponse::SelectElement(self.selected_option()),
377 ));
378 }
379}
380
381impl Drop for SelectElement {
382 fn drop(&mut self) {
383 if !self.response_sent {
384 self.constellation_proxy
385 .send(EmbedderToConstellationMessage::EmbedderControlResponse(
386 self.id,
387 EmbedderControlResponse::SelectElement(self.selected_option()),
388 ));
389 }
390 }
391}
392
393pub struct ColorPicker {
395 pub(crate) id: EmbedderControlId,
396 pub(crate) current_color: Option<RgbColor>,
397 pub(crate) position: DeviceIntRect,
398 pub(crate) constellation_proxy: ConstellationProxy,
399 pub(crate) response_sent: bool,
400}
401
402impl ColorPicker {
403 pub fn id(&self) -> EmbedderControlId {
405 self.id
406 }
407
408 pub fn position(&self) -> DeviceIntRect {
412 self.position
413 }
414
415 pub fn current_color(&self) -> Option<RgbColor> {
418 self.current_color
419 }
420
421 pub fn select(&mut self, color: Option<RgbColor>) {
422 self.current_color = color;
423 }
424
425 pub fn submit(mut self) {
427 self.response_sent = true;
428 self.constellation_proxy
429 .send(EmbedderToConstellationMessage::EmbedderControlResponse(
430 self.id,
431 EmbedderControlResponse::ColorPicker(self.current_color),
432 ));
433 }
434}
435
436impl Drop for ColorPicker {
437 fn drop(&mut self) {
438 if !self.response_sent {
439 self.constellation_proxy
440 .send(EmbedderToConstellationMessage::EmbedderControlResponse(
441 self.id,
442 EmbedderControlResponse::ColorPicker(self.current_color),
443 ));
444 }
445 }
446}
447
448pub struct FilePicker {
450 pub(crate) id: EmbedderControlId,
451 pub(crate) file_picker_request: FilePickerRequest,
452 pub(crate) response_sender: GenericSender<Option<Vec<PathBuf>>>,
453 pub(crate) response_sent: bool,
454}
455
456impl FilePicker {
457 pub fn id(&self) -> EmbedderControlId {
459 self.id
460 }
461
462 pub fn filter_patterns(&self) -> &[FilterPattern] {
463 &self.file_picker_request.filter_patterns
464 }
465
466 pub fn allow_select_multiple(&self) -> bool {
467 self.file_picker_request.allow_select_multiple
468 }
469
470 pub fn current_paths(&self) -> &[PathBuf] {
473 &self.file_picker_request.current_paths
474 }
475
476 pub fn select(&mut self, paths: &[PathBuf]) {
477 self.file_picker_request.current_paths = paths.to_owned();
478 }
479
480 pub fn submit(mut self) {
482 let _ = self.response_sender.send(Some(std::mem::take(
483 &mut self.file_picker_request.current_paths,
484 )));
485 self.response_sent = true;
486 }
487
488 pub fn dismiss(mut self) {
490 let _ = self.response_sender.send(None);
491 self.response_sent = true;
492 }
493}
494
495impl Drop for FilePicker {
496 fn drop(&mut self) {
497 if !self.response_sent {
498 let _ = self.response_sender.send(None);
499 }
500 }
501}
502
503pub struct InputMethodControl {
505 pub(crate) id: EmbedderControlId,
506 pub(crate) input_method_type: InputMethodType,
507 pub(crate) text: String,
508 pub(crate) insertion_point: Option<u32>,
509 pub(crate) position: DeviceIntRect,
510 pub(crate) multiline: bool,
511}
512
513impl InputMethodControl {
514 pub fn input_method_type(&self) -> InputMethodType {
516 self.input_method_type
517 }
518
519 pub fn text(&self) -> String {
521 self.text.clone()
522 }
523
524 pub fn insertion_point(&self) -> Option<u32> {
527 self.insertion_point
528 }
529
530 pub fn position(&self) -> DeviceIntRect {
535 self.position
536 }
537
538 pub fn multiline(&self) -> bool {
540 self.multiline
541 }
542}
543
544pub trait WebViewDelegate {
545 fn screen_geometry(&self, _webview: WebView) -> Option<ScreenGeometry> {
549 None
550 }
551 fn notify_url_changed(&self, _webview: WebView, _url: Url) {}
554 fn notify_page_title_changed(&self, _webview: WebView, _title: Option<String>) {}
557 fn notify_status_text_changed(&self, _webview: WebView, _status: Option<String>) {}
560 fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {}
563 fn notify_animating_changed(&self, _webview: WebView, _animating: bool) {}
568 fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {}
571 fn notify_cursor_changed(&self, _webview: WebView, _: Cursor) {}
574 fn notify_favicon_changed(&self, _webview: WebView) {}
577 fn notify_new_frame_ready(&self, _webview: WebView) {}
579 fn notify_history_changed(&self, _webview: WebView, _entries: Vec<Url>, _current: usize) {}
583 fn notify_traversal_complete(&self, _webview: WebView, _: TraversalId) {}
585 fn notify_closed(&self, _webview: WebView) {}
589
590 fn notify_input_event_handled(&self, _webview: WebView, _: InputEventId, _: InputEventResult) {}
594 fn notify_crashed(&self, _webview: WebView, _reason: String, _backtrace: Option<String>) {}
596 fn notify_media_session_event(&self, _webview: WebView, _event: MediaSessionEvent) {}
599 fn notify_fullscreen_state_changed(&self, _webview: WebView, _: bool) {}
605
606 fn request_navigation(&self, _webview: WebView, _navigation_request: NavigationRequest) {}
609 fn request_unload(&self, _webview: WebView, _unload_request: AllowOrDenyRequest) {}
612 fn request_move_to(&self, _webview: WebView, _: DeviceIntPoint) {}
614 fn request_resize_to(&self, _webview: WebView, _requested_outer_size: DeviceIntSize) {}
620 fn request_open_auxiliary_webview(&self, _parent_webview: WebView) -> Option<WebView> {
623 None
624 }
625
626 fn request_permission(&self, _webview: WebView, _: PermissionRequest) {}
630
631 fn request_authentication(
632 &self,
633 _webview: WebView,
634 _authentication_request: AuthenticationRequest,
635 ) {
636 }
637
638 fn show_context_menu(
640 &self,
641 _webview: WebView,
642 result_sender: GenericSender<ContextMenuResult>,
643 _: Option<String>,
644 _: Vec<String>,
645 ) {
646 let _ = result_sender.send(ContextMenuResult::Ignored);
647 }
648
649 fn show_bluetooth_device_dialog(
652 &self,
653 _webview: WebView,
654 _: Vec<String>,
655 response_sender: GenericSender<Option<String>>,
656 ) {
657 let _ = response_sender.send(None);
658 }
659
660 fn show_embedder_control(&self, _webview: WebView, embedder_control: EmbedderControl) {
663 let EmbedderControl::SimpleDialog(simple_dialog) = embedder_control else {
664 return;
665 };
666 let _ = match simple_dialog {
668 SimpleDialog::Alert {
669 response_sender, ..
670 } => response_sender.send(Default::default()),
671 SimpleDialog::Confirm {
672 response_sender, ..
673 } => response_sender.send(Default::default()),
674 SimpleDialog::Prompt {
675 response_sender, ..
676 } => response_sender.send(Default::default()),
677 };
678 }
679
680 fn hide_embedder_control(&self, _webview: WebView, _control_id: EmbedderControlId) {}
685
686 fn play_gamepad_haptic_effect(
688 &self,
689 _webview: WebView,
690 _: usize,
691 _: GamepadHapticEffectType,
692 _: IpcSender<bool>,
693 ) {
694 }
695 fn stop_gamepad_haptic_effect(&self, _webview: WebView, _: usize, _: IpcSender<bool>) {}
697
698 fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
706
707 fn show_notification(&self, _webview: WebView, _notification: Notification) {}
709}
710
711pub(crate) struct DefaultWebViewDelegate;
712impl WebViewDelegate for DefaultWebViewDelegate {}
713
714#[cfg(test)]
715mod test {
716 use super::*;
717
718 #[test]
719 fn test_allow_deny_request() {
720 use base::generic_channel;
721
722 use crate::ServoErrorChannel;
723
724 for default_response in [AllowOrDeny::Allow, AllowOrDeny::Deny] {
725 let errors = ServoErrorChannel::default();
727 let (sender, receiver) =
728 generic_channel::channel().expect("Failed to create IPC channel");
729 let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
730 request.allow();
731 assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Allow));
732 assert_eq!(receiver.try_recv().ok(), None);
733 assert!(errors.try_recv().is_none());
734
735 let errors = ServoErrorChannel::default();
737 let (sender, receiver) =
738 generic_channel::channel().expect("Failed to create IPC channel");
739 let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
740 request.deny();
741 assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Deny));
742 assert_eq!(receiver.try_recv().ok(), None);
743 assert!(errors.try_recv().is_none());
744
745 let errors = ServoErrorChannel::default();
747 let (sender, receiver) =
748 generic_channel::channel().expect("Failed to create IPC channel");
749 let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
750 drop(request);
751 assert_eq!(receiver.try_recv().ok(), Some(default_response));
752 assert_eq!(receiver.try_recv().ok(), None);
753 assert!(errors.try_recv().is_none());
754
755 let errors = ServoErrorChannel::default();
757 let (sender, receiver) =
758 generic_channel::channel().expect("Failed to create IPC channel");
759 let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
760 drop(receiver);
761 request.allow();
762 assert!(errors.try_recv().is_some());
763
764 let errors = ServoErrorChannel::default();
766 let (sender, receiver) =
767 generic_channel::channel().expect("Failed to create IPC channel");
768 let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
769 drop(receiver);
770 request.deny();
771 assert!(errors.try_recv().is_some());
772
773 let errors = ServoErrorChannel::default();
775 let (sender, receiver) =
776 generic_channel::channel().expect("Failed to create IPC channel");
777 let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
778 drop(receiver);
779 drop(request);
780 assert!(errors.try_recv().is_none());
781 }
782 }
783
784 #[test]
785 fn test_authentication_request() {
786 use base::generic_channel;
787
788 use crate::ServoErrorChannel;
789
790 let url = Url::parse("https://example.com").expect("Guaranteed by argument");
791
792 let errors = ServoErrorChannel::default();
794 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
795 let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
796 request.authenticate("diffie".to_owned(), "hunter2".to_owned());
797 assert_eq!(
798 receiver.try_recv().ok(),
799 Some(Some(AuthenticationResponse {
800 username: "diffie".to_owned(),
801 password: "hunter2".to_owned(),
802 }))
803 );
804 assert_eq!(receiver.try_recv().ok(), None);
805 assert!(errors.try_recv().is_none());
806
807 let errors = ServoErrorChannel::default();
809 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
810 let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
811 drop(request);
812 assert_eq!(receiver.try_recv().ok(), Some(None));
813 assert_eq!(receiver.try_recv().ok(), None);
814 assert!(errors.try_recv().is_none());
815
816 let errors = ServoErrorChannel::default();
818 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
819 let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
820 drop(receiver);
821 request.authenticate("diffie".to_owned(), "hunter2".to_owned());
822 assert!(errors.try_recv().is_some());
823
824 let errors = ServoErrorChannel::default();
826 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
827 let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
828 drop(receiver);
829 drop(request);
830 assert!(errors.try_recv().is_none());
831 }
832
833 #[test]
834 fn test_web_resource_load() {
835 use base::generic_channel;
836 use http::{HeaderMap, Method, StatusCode};
837
838 use crate::ServoErrorChannel;
839
840 let web_resource_request = || WebResourceRequest {
841 method: Method::GET,
842 headers: HeaderMap::default(),
843 url: Url::parse("https://example.com").expect("Guaranteed by argument"),
844 is_for_main_frame: false,
845 is_redirect: false,
846 };
847 let web_resource_response = || {
848 WebResourceResponse::new(
849 Url::parse("https://diffie.test").expect("Guaranteed by argument"),
850 )
851 .status_code(StatusCode::IM_A_TEAPOT)
852 };
853
854 let errors = ServoErrorChannel::default();
856 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
857 let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
858 request.intercept(web_resource_response()).cancel();
859 assert!(matches!(
860 receiver.try_recv(),
861 Ok(WebResourceResponseMsg::Start(_))
862 ));
863 assert!(matches!(
864 receiver.try_recv(),
865 Ok(WebResourceResponseMsg::CancelLoad)
866 ));
867 assert!(matches!(receiver.try_recv(), Err(_)));
868 assert!(errors.try_recv().is_none());
869
870 let errors = ServoErrorChannel::default();
872 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
873 let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
874 drop(request.intercept(web_resource_response()));
875 assert!(matches!(
876 receiver.try_recv(),
877 Ok(WebResourceResponseMsg::Start(_))
878 ));
879 assert!(matches!(
880 receiver.try_recv(),
881 Ok(WebResourceResponseMsg::FinishLoad)
882 ));
883 assert!(matches!(receiver.try_recv(), Err(_)));
884 assert!(errors.try_recv().is_none());
885
886 let errors = ServoErrorChannel::default();
888 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
889 let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
890 drop(request);
891 assert!(matches!(
892 receiver.try_recv(),
893 Ok(WebResourceResponseMsg::DoNotIntercept)
894 ));
895 assert!(matches!(receiver.try_recv(), Err(_)));
896 assert!(errors.try_recv().is_none());
897
898 let errors = ServoErrorChannel::default();
900 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
901 let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
902 drop(receiver);
903 request.intercept(web_resource_response()).cancel();
904 assert!(errors.try_recv().is_some());
905
906 let errors = ServoErrorChannel::default();
908 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
909 let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
910 drop(receiver);
911 drop(request.intercept(web_resource_response()));
912 assert!(errors.try_recv().is_some());
913
914 let errors = ServoErrorChannel::default();
916 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
917 let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
918 drop(receiver);
919 drop(request);
920 assert!(errors.try_recv().is_none());
921 }
922}