servo/
webview_delegate.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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, FilterPattern,
12    GamepadHapticEffectType, InputMethodType, KeyboardEvent, LoadStatus, MediaSessionEvent,
13    Notification, PermissionFeature, RgbColor, ScreenGeometry, SelectElementOptionOrOptgroup,
14    SimpleDialog, TraversalId, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
15};
16use ipc_channel::ipc::IpcSender;
17use serde::Serialize;
18use url::Url;
19use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
20
21use crate::responders::ServoErrorSender;
22use crate::{ConstellationProxy, WebView};
23
24/// A request to navigate a [`WebView`] or one of its inner frames. This can be handled
25/// asynchronously. If not handled, the request will automatically be allowed.
26pub struct NavigationRequest {
27    pub url: Url,
28    pub(crate) pipeline_id: PipelineId,
29    pub(crate) constellation_proxy: ConstellationProxy,
30    pub(crate) response_sent: bool,
31}
32
33impl NavigationRequest {
34    pub fn allow(mut self) {
35        self.constellation_proxy
36            .send(EmbedderToConstellationMessage::AllowNavigationResponse(
37                self.pipeline_id,
38                true,
39            ));
40        self.response_sent = true;
41    }
42
43    pub fn deny(mut self) {
44        self.constellation_proxy
45            .send(EmbedderToConstellationMessage::AllowNavigationResponse(
46                self.pipeline_id,
47                false,
48            ));
49        self.response_sent = true;
50    }
51}
52
53impl Drop for NavigationRequest {
54    fn drop(&mut self) {
55        if !self.response_sent {
56            self.constellation_proxy
57                .send(EmbedderToConstellationMessage::AllowNavigationResponse(
58                    self.pipeline_id,
59                    true,
60                ));
61        }
62    }
63}
64
65/// Sends a response over an IPC channel, or a default response on [`Drop`] if no response was sent.
66pub(crate) struct IpcResponder<T: Serialize> {
67    response_sender: GenericSender<T>,
68    response_sent: bool,
69    /// Always present, except when taken by [`Drop`].
70    default_response: Option<T>,
71}
72
73impl<T: Serialize> IpcResponder<T> {
74    pub(crate) fn new(response_sender: GenericSender<T>, default_response: T) -> Self {
75        Self {
76            response_sender,
77            response_sent: false,
78            default_response: Some(default_response),
79        }
80    }
81
82    pub(crate) fn send(&mut self, response: T) -> SendResult {
83        let result = self.response_sender.send(response);
84        self.response_sent = true;
85        result
86    }
87
88    pub(crate) fn into_inner(self) -> GenericSender<T> {
89        self.response_sender.clone()
90    }
91}
92
93impl<T: Serialize> Drop for IpcResponder<T> {
94    fn drop(&mut self) {
95        if !self.response_sent {
96            let response = self
97                .default_response
98                .take()
99                .expect("Guaranteed by inherent impl");
100            // Don’t notify embedder about send errors for the default response,
101            // since they didn’t send anything and probably don’t care.
102            let _ = self.response_sender.send(response);
103        }
104    }
105}
106
107/// A permissions request for a [`WebView`] The embedder should allow or deny the request,
108/// either by reading a cached value or querying the user for permission via the user
109/// interface.
110pub struct PermissionRequest {
111    pub(crate) requested_feature: PermissionFeature,
112    pub(crate) allow_deny_request: AllowOrDenyRequest,
113}
114
115impl PermissionRequest {
116    pub fn feature(&self) -> PermissionFeature {
117        self.requested_feature
118    }
119
120    pub fn allow(self) {
121        self.allow_deny_request.allow();
122    }
123
124    pub fn deny(self) {
125        self.allow_deny_request.deny();
126    }
127}
128
129pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>, ServoErrorSender);
130
131impl AllowOrDenyRequest {
132    pub(crate) fn new(
133        response_sender: GenericSender<AllowOrDeny>,
134        default_response: AllowOrDeny,
135        error_sender: ServoErrorSender,
136    ) -> Self {
137        Self(
138            IpcResponder::new(response_sender, default_response),
139            error_sender,
140        )
141    }
142
143    pub fn allow(mut self) {
144        if let Err(error) = self.0.send(AllowOrDeny::Allow) {
145            self.1.raise_response_send_error(error);
146        }
147    }
148
149    pub fn deny(mut self) {
150        if let Err(error) = self.0.send(AllowOrDeny::Deny) {
151            self.1.raise_response_send_error(error);
152        }
153    }
154}
155
156/// A request to authenticate a [`WebView`] navigation. Embedders may choose to prompt
157/// the user to enter credentials or simply ignore this request (in which case credentials
158/// will not be used).
159pub struct AuthenticationRequest {
160    pub(crate) url: Url,
161    pub(crate) for_proxy: bool,
162    pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
163    pub(crate) error_sender: ServoErrorSender,
164}
165
166impl AuthenticationRequest {
167    pub(crate) fn new(
168        url: Url,
169        for_proxy: bool,
170        response_sender: GenericSender<Option<AuthenticationResponse>>,
171        error_sender: ServoErrorSender,
172    ) -> Self {
173        Self {
174            url,
175            for_proxy,
176            responder: IpcResponder::new(response_sender, None),
177            error_sender,
178        }
179    }
180
181    /// The URL of the request that triggered this authentication.
182    pub fn url(&self) -> &Url {
183        &self.url
184    }
185    /// Whether or not this authentication request is associated with a proxy server authentication.
186    pub fn for_proxy(&self) -> bool {
187        self.for_proxy
188    }
189    /// Respond to the [`AuthenticationRequest`] with the given username and password.
190    pub fn authenticate(mut self, username: String, password: String) {
191        if let Err(error) = self
192            .responder
193            .send(Some(AuthenticationResponse { username, password }))
194        {
195            self.error_sender.raise_response_send_error(error);
196        }
197    }
198}
199
200/// Information related to the loading of a web resource. These are created for all HTTP requests.
201/// The client may choose to intercept the load of web resources and send an alternate response
202/// by calling [`WebResourceLoad::intercept`].
203pub struct WebResourceLoad {
204    pub request: WebResourceRequest,
205    pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
206    pub(crate) error_sender: ServoErrorSender,
207}
208
209impl WebResourceLoad {
210    pub(crate) fn new(
211        web_resource_request: WebResourceRequest,
212        response_sender: GenericSender<WebResourceResponseMsg>,
213        error_sender: ServoErrorSender,
214    ) -> Self {
215        Self {
216            request: web_resource_request,
217            responder: IpcResponder::new(response_sender, WebResourceResponseMsg::DoNotIntercept),
218            error_sender,
219        }
220    }
221
222    /// The [`WebResourceRequest`] associated with this [`WebResourceLoad`].
223    pub fn request(&self) -> &WebResourceRequest {
224        &self.request
225    }
226    /// Intercept this [`WebResourceLoad`] and control the response via the returned
227    /// [`InterceptedWebResourceLoad`].
228    pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad {
229        if let Err(error) = self.responder.send(WebResourceResponseMsg::Start(response)) {
230            self.error_sender.raise_response_send_error(error);
231        }
232        InterceptedWebResourceLoad {
233            request: self.request.clone(),
234            response_sender: self.responder.into_inner(),
235            finished: false,
236            error_sender: self.error_sender,
237        }
238    }
239}
240
241/// An intercepted web resource load. This struct allows the client to send an alternative response
242/// after calling [`WebResourceLoad::intercept`]. In order to send chunks of body data, the client
243/// must call [`InterceptedWebResourceLoad::send_body_data`]. When the interception is complete, the client
244/// should call [`InterceptedWebResourceLoad::finish`]. If neither `finish()` or `cancel()` are called,
245/// this interception will automatically be finished when dropped.
246pub struct InterceptedWebResourceLoad {
247    pub request: WebResourceRequest,
248    pub(crate) response_sender: GenericSender<WebResourceResponseMsg>,
249    pub(crate) finished: bool,
250    pub(crate) error_sender: ServoErrorSender,
251}
252
253impl InterceptedWebResourceLoad {
254    /// Send a chunk of response body data. It's possible to make subsequent calls to
255    /// this method when streaming body data.
256    pub fn send_body_data(&self, data: Vec<u8>) {
257        if let Err(error) = self
258            .response_sender
259            .send(WebResourceResponseMsg::SendBodyData(data))
260        {
261            self.error_sender.raise_response_send_error(error);
262        }
263    }
264    /// Finish this [`InterceptedWebResourceLoad`] and complete the response.
265    pub fn finish(mut self) {
266        if let Err(error) = self
267            .response_sender
268            .send(WebResourceResponseMsg::FinishLoad)
269        {
270            self.error_sender.raise_response_send_error(error);
271        }
272        self.finished = true;
273    }
274    /// Cancel this [`InterceptedWebResourceLoad`], which will trigger a network error.
275    pub fn cancel(mut self) {
276        if let Err(error) = self
277            .response_sender
278            .send(WebResourceResponseMsg::CancelLoad)
279        {
280            self.error_sender.raise_response_send_error(error);
281        }
282        self.finished = true;
283    }
284}
285
286impl Drop for InterceptedWebResourceLoad {
287    fn drop(&mut self) {
288        if !self.finished {
289            if let Err(error) = self
290                .response_sender
291                .send(WebResourceResponseMsg::FinishLoad)
292            {
293                self.error_sender.raise_response_send_error(error);
294            }
295        }
296    }
297}
298
299/// The controls of an interactive form element.
300pub enum FormControl {
301    /// The picker of a `<select>` element.
302    SelectElement(SelectElement),
303    /// The picker of a `<input type=color>` element.
304    ColorPicker(ColorPicker),
305}
306
307/// Represents a dialog triggered by clicking a `<select>` element.
308pub struct SelectElement {
309    pub(crate) options: Vec<SelectElementOptionOrOptgroup>,
310    pub(crate) selected_option: Option<usize>,
311    pub(crate) position: DeviceIntRect,
312    pub(crate) responder: IpcResponder<Option<usize>>,
313}
314
315impl SelectElement {
316    pub(crate) fn new(
317        options: Vec<SelectElementOptionOrOptgroup>,
318        selected_option: Option<usize>,
319        position: DeviceIntRect,
320        ipc_sender: GenericSender<Option<usize>>,
321    ) -> Self {
322        Self {
323            options,
324            selected_option,
325            position,
326            responder: IpcResponder::new(ipc_sender, None),
327        }
328    }
329
330    /// Return the area occupied by the `<select>` element that triggered the prompt.
331    ///
332    /// The embedder should use this value to position the prompt that is shown to the user.
333    pub fn position(&self) -> DeviceIntRect {
334        self.position
335    }
336
337    /// Consecutive `<option>` elements outside of an `<optgroup>` will be combined
338    /// into a single anonymous group, whose [`label`](SelectElementGroup::label) is `None`.
339    pub fn options(&self) -> &[SelectElementOptionOrOptgroup] {
340        &self.options
341    }
342
343    /// Mark a single option as selected.
344    ///
345    /// If there is already a selected option and the `<select>` element does not
346    /// support selecting multiple options, then the previous option will be unselected.
347    pub fn select(&mut self, id: Option<usize>) {
348        self.selected_option = id;
349    }
350
351    pub fn selected_option(&self) -> Option<usize> {
352        self.selected_option
353    }
354
355    /// Resolve the prompt with the options that have been selected by calling [select] previously.
356    pub fn submit(mut self) {
357        let _ = self.responder.send(self.selected_option);
358    }
359}
360
361/// Represents a dialog triggered by clicking a `<input type=color>` element.
362pub struct ColorPicker {
363    pub(crate) current_color: RgbColor,
364    pub(crate) position: DeviceIntRect,
365    pub(crate) responder: IpcResponder<Option<RgbColor>>,
366    pub(crate) error_sender: ServoErrorSender,
367}
368
369impl ColorPicker {
370    pub(crate) fn new(
371        current_color: RgbColor,
372        position: DeviceIntRect,
373        ipc_sender: GenericSender<Option<RgbColor>>,
374        error_sender: ServoErrorSender,
375    ) -> Self {
376        Self {
377            current_color,
378            position,
379            responder: IpcResponder::new(ipc_sender, None),
380            error_sender,
381        }
382    }
383
384    /// Get the area occupied by the `<input>` element that triggered the prompt.
385    ///
386    /// The embedder should use this value to position the prompt that is shown to the user.
387    pub fn position(&self) -> DeviceIntRect {
388        self.position
389    }
390
391    /// Get the color that was selected before the prompt was opened.
392    pub fn current_color(&self) -> RgbColor {
393        self.current_color
394    }
395
396    pub fn select(&mut self, color: Option<RgbColor>) {
397        if let Err(error) = self.responder.send(color) {
398            self.error_sender.raise_response_send_error(error);
399        }
400    }
401}
402
403pub trait WebViewDelegate {
404    /// Get the [`ScreenGeometry`] for this [`WebView`]. If this is unimplemented or returns `None`
405    /// the screen will have the size of the [`WebView`]'s `RenderingContext` and `WebView` will be
406    /// considered to be positioned at the screen's origin.
407    fn screen_geometry(&self, _webview: WebView) -> Option<ScreenGeometry> {
408        None
409    }
410    /// The URL of the currently loaded page in this [`WebView`] has changed. The new
411    /// URL can accessed via [`WebView::url`].
412    fn notify_url_changed(&self, _webview: WebView, _url: Url) {}
413    /// The page title of the currently loaded page in this [`WebView`] has changed. The new
414    /// title can accessed via [`WebView::page_title`].
415    fn notify_page_title_changed(&self, _webview: WebView, _title: Option<String>) {}
416    /// The status text of the currently loaded page in this [`WebView`] has changed. The new
417    /// status text can accessed via [`WebView::status_text`].
418    fn notify_status_text_changed(&self, _webview: WebView, _status: Option<String>) {}
419    /// This [`WebView`] has either become focused or lost focus. Whether or not the
420    /// [`WebView`] is focused can be accessed via [`WebView::focused`].
421    fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {}
422    /// This [`WebView`] has either started to animate or stopped animating. When a
423    /// [`WebView`] is animating, it is up to the embedding application ensure that
424    /// `Servo::spin_event_loop` is called at regular intervals in order to update the
425    /// painted contents of the [`WebView`].
426    fn notify_animating_changed(&self, _webview: WebView, _animating: bool) {}
427    /// The `LoadStatus` of the currently loading or loaded page in this [`WebView`] has changed. The new
428    /// status can accessed via [`WebView::load_status`].
429    fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {}
430    /// The [`Cursor`] of the currently loaded page in this [`WebView`] has changed. The new
431    /// cursor can accessed via [`WebView::cursor`].
432    fn notify_cursor_changed(&self, _webview: WebView, _: Cursor) {}
433    /// The favicon of the currently loaded page in this [`WebView`] has changed. The new
434    /// favicon [`Image`] can accessed via [`WebView::favicon`].
435    fn notify_favicon_changed(&self, _webview: WebView) {}
436    /// Notify the embedder that it needs to present a new frame.
437    fn notify_new_frame_ready(&self, _webview: WebView) {}
438    /// The navigation history of this [`WebView`] has changed. The navigation history is represented
439    /// as a `Vec<Url>` and `_current` denotes the current index in the history. New navigations,
440    /// back navigation, and forward navigation modify this index.
441    fn notify_history_changed(&self, _webview: WebView, _entries: Vec<Url>, _current: usize) {}
442    /// A history traversal operation is complete.
443    fn notify_traversal_complete(&self, _webview: WebView, _: TraversalId) {}
444    /// Page content has closed this [`WebView`] via `window.close()`. It's the embedder's
445    /// responsibility to remove the [`WebView`] from the interface when this notification
446    /// occurs.
447    fn notify_closed(&self, _webview: WebView) {}
448
449    /// A keyboard event has been sent to Servo, but remains unprocessed. This allows the
450    /// embedding application to handle key events while first letting the [`WebView`]
451    /// have an opportunity to handle it first. Apart from builtin keybindings, page
452    /// content may expose custom keybindings as well.
453    fn notify_keyboard_event(&self, _webview: WebView, _: KeyboardEvent) {}
454    /// A pipeline in the webview panicked. First string is the reason, second one is the backtrace.
455    fn notify_crashed(&self, _webview: WebView, _reason: String, _backtrace: Option<String>) {}
456    /// Notifies the embedder about media session events
457    /// (i.e. when there is metadata for the active media session, playback state changes...).
458    fn notify_media_session_event(&self, _webview: WebView, _event: MediaSessionEvent) {}
459    /// A notification that the [`WebView`] has entered or exited fullscreen mode. This is an
460    /// opportunity for the embedder to transition the containing window into or out of fullscreen
461    /// mode and to show or hide extra UI elements. Regardless of how the notification is handled,
462    /// the page will enter or leave fullscreen state internally according to the [Fullscreen
463    /// API](https://fullscreen.spec.whatwg.org/).
464    fn notify_fullscreen_state_changed(&self, _webview: WebView, _: bool) {}
465
466    /// Whether or not to allow a [`WebView`] to load a URL in its main frame or one of its
467    /// nested `<iframe>`s. [`NavigationRequest`]s are accepted by default.
468    fn request_navigation(&self, _webview: WebView, _navigation_request: NavigationRequest) {}
469    /// Whether or not to allow a [`WebView`]  to unload a `Document` in its main frame or one
470    /// of its nested `<iframe>`s. By default, unloads are allowed.
471    fn request_unload(&self, _webview: WebView, _unload_request: AllowOrDenyRequest) {}
472    /// Move the window to a point.
473    fn request_move_to(&self, _webview: WebView, _: DeviceIntPoint) {}
474    /// Try to resize the window that contains this [`WebView`] to the provided outer
475    /// size. These resize requests can come from page content. Servo will ensure that the
476    /// values are greater than zero, but it is up to the embedder to limit the maximum
477    /// size. For instance, a reasonable limitation might be that the final size is no
478    /// larger than the screen size.
479    fn request_resize_to(&self, _webview: WebView, _requested_outer_size: DeviceIntSize) {}
480    /// Whether or not to allow script to open a new `WebView`. If not handled by the
481    /// embedder, these requests are automatically denied.
482    fn request_open_auxiliary_webview(&self, _parent_webview: WebView) -> Option<WebView> {
483        None
484    }
485
486    /// Content in a [`WebView`] is requesting permission to access a feature requiring
487    /// permission from the user. The embedder should allow or deny the request, either by
488    /// reading a cached value or querying the user for permission via the user interface.
489    fn request_permission(&self, _webview: WebView, _: PermissionRequest) {}
490
491    fn request_authentication(
492        &self,
493        _webview: WebView,
494        _authentication_request: AuthenticationRequest,
495    ) {
496    }
497
498    /// Show the user a [simple dialog](https://html.spec.whatwg.org/multipage/#simple-dialogs) (`alert()`, `confirm()`,
499    /// or `prompt()`). Since their messages are controlled by web content, they should be presented to the user in a
500    /// way that makes them impossible to mistake for browser UI.
501    /// TODO: This API needs to be reworked to match the new model of how responses are sent.
502    fn show_simple_dialog(&self, _webview: WebView, dialog: SimpleDialog) {
503        // Return the DOM-specified default value for when we **cannot show simple dialogs**.
504        let _ = match dialog {
505            SimpleDialog::Alert {
506                response_sender, ..
507            } => response_sender.send(Default::default()),
508            SimpleDialog::Confirm {
509                response_sender, ..
510            } => response_sender.send(Default::default()),
511            SimpleDialog::Prompt {
512                response_sender, ..
513            } => response_sender.send(Default::default()),
514        };
515    }
516
517    /// Show a context menu to the user
518    fn show_context_menu(
519        &self,
520        _webview: WebView,
521        result_sender: GenericSender<ContextMenuResult>,
522        _: Option<String>,
523        _: Vec<String>,
524    ) {
525        let _ = result_sender.send(ContextMenuResult::Ignored);
526    }
527
528    /// Open dialog to select bluetooth device.
529    /// TODO: This API needs to be reworked to match the new model of how responses are sent.
530    fn show_bluetooth_device_dialog(
531        &self,
532        _webview: WebView,
533        _: Vec<String>,
534        response_sender: GenericSender<Option<String>>,
535    ) {
536        let _ = response_sender.send(None);
537    }
538
539    /// Open file dialog to select files. Set boolean flag to true allows to select multiple files.
540    fn show_file_selection_dialog(
541        &self,
542        _webview: WebView,
543        _filter_pattern: Vec<FilterPattern>,
544        _allow_select_mutiple: bool,
545        response_sender: GenericSender<Option<Vec<PathBuf>>>,
546    ) {
547        let _ = response_sender.send(None);
548    }
549
550    /// Request to present an IME to the user when an editable element is focused.
551    /// If `type` is [`InputMethodType::Text`], then the `text` parameter specifies
552    /// the pre-existing text content and the zero-based index into the string
553    /// of the insertion point.
554    fn show_ime(
555        &self,
556        _webview: WebView,
557        _type: InputMethodType,
558        _text: Option<(String, i32)>,
559        _multiline: bool,
560        _position: DeviceIntRect,
561    ) {
562    }
563
564    /// Request to hide the IME when the editable element is blurred.
565    fn hide_ime(&self, _webview: WebView) {}
566
567    /// Request that the embedder show UI elements for form controls that are not integrated
568    /// into page content, such as dropdowns for `<select>` elements.
569    fn show_form_control(&self, _webview: WebView, _form_control: FormControl) {}
570
571    /// Request to play a haptic effect on a connected gamepad.
572    fn play_gamepad_haptic_effect(
573        &self,
574        _webview: WebView,
575        _: usize,
576        _: GamepadHapticEffectType,
577        _: IpcSender<bool>,
578    ) {
579    }
580    /// Request to stop a haptic effect on a connected gamepad.
581    fn stop_gamepad_haptic_effect(&self, _webview: WebView, _: usize, _: IpcSender<bool>) {}
582
583    /// Triggered when this [`WebView`] will load a web (HTTP/HTTPS) resource. The load may be
584    /// intercepted and alternate contents can be loaded by the client by calling
585    /// [`WebResourceLoad::intercept`]. If not handled, the load will continue as normal.
586    ///
587    /// Note: This delegate method is called for all resource loads associated with a [`WebView`].
588    /// For loads not associated with a [`WebView`], such as those for service workers, Servo
589    /// will call [`crate::ServoDelegate::load_web_resource`].
590    fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
591
592    /// Request to display a notification.
593    fn show_notification(&self, _webview: WebView, _notification: Notification) {}
594}
595
596pub(crate) struct DefaultWebViewDelegate;
597impl WebViewDelegate for DefaultWebViewDelegate {}
598
599#[test]
600fn test_allow_deny_request() {
601    use base::generic_channel;
602
603    use crate::ServoErrorChannel;
604
605    for default_response in [AllowOrDeny::Allow, AllowOrDeny::Deny] {
606        // Explicit allow yields allow and nothing else
607        let errors = ServoErrorChannel::default();
608        let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
609        let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
610        request.allow();
611        assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Allow));
612        assert_eq!(receiver.try_recv().ok(), None);
613        assert!(errors.try_recv().is_none());
614
615        // Explicit deny yields deny and nothing else
616        let errors = ServoErrorChannel::default();
617        let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
618        let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
619        request.deny();
620        assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Deny));
621        assert_eq!(receiver.try_recv().ok(), None);
622        assert!(errors.try_recv().is_none());
623
624        // No response yields default response and nothing else
625        let errors = ServoErrorChannel::default();
626        let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
627        let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
628        drop(request);
629        assert_eq!(receiver.try_recv().ok(), Some(default_response));
630        assert_eq!(receiver.try_recv().ok(), None);
631        assert!(errors.try_recv().is_none());
632
633        // Explicit allow when receiver disconnected yields error
634        let errors = ServoErrorChannel::default();
635        let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
636        let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
637        drop(receiver);
638        request.allow();
639        assert!(errors.try_recv().is_some());
640
641        // Explicit deny when receiver disconnected yields error
642        let errors = ServoErrorChannel::default();
643        let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
644        let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
645        drop(receiver);
646        request.deny();
647        assert!(errors.try_recv().is_some());
648
649        // No response when receiver disconnected yields no error
650        let errors = ServoErrorChannel::default();
651        let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
652        let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
653        drop(receiver);
654        drop(request);
655        assert!(errors.try_recv().is_none());
656    }
657}
658
659#[test]
660fn test_authentication_request() {
661    use base::generic_channel;
662
663    use crate::ServoErrorChannel;
664
665    let url = Url::parse("https://example.com").expect("Guaranteed by argument");
666
667    // Explicit response yields that response and nothing else
668    let errors = ServoErrorChannel::default();
669    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
670    let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
671    request.authenticate("diffie".to_owned(), "hunter2".to_owned());
672    assert_eq!(
673        receiver.try_recv().ok(),
674        Some(Some(AuthenticationResponse {
675            username: "diffie".to_owned(),
676            password: "hunter2".to_owned(),
677        }))
678    );
679    assert_eq!(receiver.try_recv().ok(), None);
680    assert!(errors.try_recv().is_none());
681
682    // No response yields None response and nothing else
683    let errors = ServoErrorChannel::default();
684    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
685    let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
686    drop(request);
687    assert_eq!(receiver.try_recv().ok(), Some(None));
688    assert_eq!(receiver.try_recv().ok(), None);
689    assert!(errors.try_recv().is_none());
690
691    // Explicit response when receiver disconnected yields error
692    let errors = ServoErrorChannel::default();
693    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
694    let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
695    drop(receiver);
696    request.authenticate("diffie".to_owned(), "hunter2".to_owned());
697    assert!(errors.try_recv().is_some());
698
699    // No response when receiver disconnected yields no error
700    let errors = ServoErrorChannel::default();
701    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
702    let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
703    drop(receiver);
704    drop(request);
705    assert!(errors.try_recv().is_none());
706}
707
708#[test]
709fn test_web_resource_load() {
710    use base::generic_channel;
711    use http::{HeaderMap, Method, StatusCode};
712
713    use crate::ServoErrorChannel;
714
715    let web_resource_request = || WebResourceRequest {
716        method: Method::GET,
717        headers: HeaderMap::default(),
718        url: Url::parse("https://example.com").expect("Guaranteed by argument"),
719        is_for_main_frame: false,
720        is_redirect: false,
721    };
722    let web_resource_response = || {
723        WebResourceResponse::new(Url::parse("https://diffie.test").expect("Guaranteed by argument"))
724            .status_code(StatusCode::IM_A_TEAPOT)
725    };
726
727    // Explicit intercept with explicit cancel yields Start and Cancel and nothing else
728    let errors = ServoErrorChannel::default();
729    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
730    let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
731    request.intercept(web_resource_response()).cancel();
732    assert!(matches!(
733        receiver.try_recv(),
734        Ok(WebResourceResponseMsg::Start(_))
735    ));
736    assert!(matches!(
737        receiver.try_recv(),
738        Ok(WebResourceResponseMsg::CancelLoad)
739    ));
740    assert!(matches!(receiver.try_recv(), Err(_)));
741    assert!(errors.try_recv().is_none());
742
743    // Explicit intercept with no further action yields Start and FinishLoad and nothing else
744    let errors = ServoErrorChannel::default();
745    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
746    let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
747    drop(request.intercept(web_resource_response()));
748    assert!(matches!(
749        receiver.try_recv(),
750        Ok(WebResourceResponseMsg::Start(_))
751    ));
752    assert!(matches!(
753        receiver.try_recv(),
754        Ok(WebResourceResponseMsg::FinishLoad)
755    ));
756    assert!(matches!(receiver.try_recv(), Err(_)));
757    assert!(errors.try_recv().is_none());
758
759    // No response yields DoNotIntercept and nothing else
760    let errors = ServoErrorChannel::default();
761    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
762    let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
763    drop(request);
764    assert!(matches!(
765        receiver.try_recv(),
766        Ok(WebResourceResponseMsg::DoNotIntercept)
767    ));
768    assert!(matches!(receiver.try_recv(), Err(_)));
769    assert!(errors.try_recv().is_none());
770
771    // Explicit intercept with explicit cancel when receiver disconnected yields error
772    let errors = ServoErrorChannel::default();
773    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
774    let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
775    drop(receiver);
776    request.intercept(web_resource_response()).cancel();
777    assert!(errors.try_recv().is_some());
778
779    // Explicit intercept with no further action when receiver disconnected yields error
780    let errors = ServoErrorChannel::default();
781    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
782    let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
783    drop(receiver);
784    drop(request.intercept(web_resource_response()));
785    assert!(errors.try_recv().is_some());
786
787    // No response when receiver disconnected yields no error
788    let errors = ServoErrorChannel::default();
789    let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel");
790    let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
791    drop(receiver);
792    drop(request);
793    assert!(errors.try_recv().is_none());
794}