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;
6use std::rc::Rc;
7
8use embedder_traits::{
9    AlertResponse, AllowOrDeny, AuthenticationResponse, BluetoothDeviceDescription,
10    ConfirmResponse, ConsoleLogLevel, ContextMenuAction, ContextMenuElementInformation,
11    ContextMenuItem, Cursor, EmbedderControlId, EmbedderControlResponse, FilePickerRequest,
12    FilterPattern, InputEventId, InputEventResult, InputMethodType, LoadStatus, MediaSessionEvent,
13    NewWebViewDetails, Notification, PermissionFeature, PromptResponse, RgbColor, ScreenGeometry,
14    SelectElementOptionOrOptgroup, SelectElementRequest, SimpleDialogRequest, TraversalId,
15    WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
16};
17use paint_api::rendering_context::RenderingContext;
18use servo_base::generic_channel::{GenericCallback, GenericSender, SendError};
19use servo_base::id::PipelineId;
20use servo_constellation_traits::EmbedderToConstellationMessage;
21use tokio::sync::mpsc::UnboundedSender as TokioSender;
22use tokio::sync::oneshot::Sender;
23use url::Url;
24use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
25
26use crate::proxies::ConstellationProxy;
27use crate::responders::{IpcResponder, OneshotSender, ServoErrorSender};
28use crate::{RegisterOrUnregister, Servo, WebView, WebViewBuilder};
29
30/// A request to navigate a [`WebView`] or one of its inner frames. This can be handled
31/// asynchronously. If not handled, the request will automatically be allowed.
32pub struct NavigationRequest {
33    pub url: Url,
34    pub(crate) pipeline_id: PipelineId,
35    pub(crate) constellation_proxy: ConstellationProxy,
36    pub(crate) response_sent: bool,
37}
38
39impl NavigationRequest {
40    pub fn allow(mut self) {
41        self.constellation_proxy
42            .send(EmbedderToConstellationMessage::AllowNavigationResponse(
43                self.pipeline_id,
44                true,
45            ));
46        self.response_sent = true;
47    }
48
49    pub fn deny(mut self) {
50        self.constellation_proxy
51            .send(EmbedderToConstellationMessage::AllowNavigationResponse(
52                self.pipeline_id,
53                false,
54            ));
55        self.response_sent = true;
56    }
57}
58
59impl Drop for NavigationRequest {
60    fn drop(&mut self) {
61        if !self.response_sent {
62            self.constellation_proxy
63                .send(EmbedderToConstellationMessage::AllowNavigationResponse(
64                    self.pipeline_id,
65                    true,
66                ));
67        }
68    }
69}
70
71/// A permissions request for a [`WebView`] The embedder should allow or deny the request,
72/// either by reading a cached value or querying the user for permission via the user
73/// interface.
74pub struct PermissionRequest {
75    pub(crate) requested_feature: PermissionFeature,
76    pub(crate) allow_deny_request: AllowOrDenyRequest,
77}
78
79impl PermissionRequest {
80    pub fn feature(&self) -> PermissionFeature {
81        self.requested_feature
82    }
83
84    pub fn allow(self) {
85        self.allow_deny_request.allow();
86    }
87
88    pub fn deny(self) {
89        self.allow_deny_request.deny();
90    }
91}
92
93pub struct AllowOrDenyRequest(IpcResponder<AllowOrDeny>, ServoErrorSender);
94
95impl AllowOrDenyRequest {
96    pub(crate) fn new(
97        response_sender: GenericSender<AllowOrDeny>,
98        default_response: AllowOrDeny,
99        error_sender: ServoErrorSender,
100    ) -> Self {
101        Self(
102            IpcResponder::new(response_sender, default_response),
103            error_sender,
104        )
105    }
106
107    pub(crate) fn new_from_callback(
108        callback: GenericCallback<AllowOrDeny>,
109        default_response: AllowOrDeny,
110        error_sender: ServoErrorSender,
111    ) -> Self {
112        Self(
113            IpcResponder::new_same_process(Box::new(callback), default_response),
114            error_sender,
115        )
116    }
117
118    pub fn allow(mut self) {
119        if let Err(error) = self.0.send(AllowOrDeny::Allow) {
120            self.1.raise_response_send_error(error);
121        }
122    }
123
124    pub fn deny(mut self) {
125        if let Err(error) = self.0.send(AllowOrDeny::Deny) {
126            self.1.raise_response_send_error(error);
127        }
128    }
129}
130
131#[derive(Clone, Debug, Eq, PartialEq)]
132pub struct ProtocolHandlerRegistration {
133    pub scheme: String,
134    pub url: Url,
135    pub register_or_unregister: RegisterOrUnregister,
136}
137
138/// A request to let the user chose a Bluetooth device.
139pub struct BluetoothDeviceSelectionRequest {
140    devices: Vec<BluetoothDeviceDescription>,
141    responder: IpcResponder<Option<String>>,
142}
143
144impl BluetoothDeviceSelectionRequest {
145    pub(crate) fn new(
146        devices: Vec<BluetoothDeviceDescription>,
147        responder: GenericSender<Option<String>>,
148    ) -> Self {
149        Self {
150            devices,
151            responder: IpcResponder::new(responder, None),
152        }
153    }
154
155    /// Set the device chosen by the user.
156    pub fn pick_device(mut self, device: &BluetoothDeviceDescription) -> Result<(), SendError> {
157        self.responder.send(Some(device.address.clone()))
158    }
159
160    /// Cancel this request.
161    pub fn cancel(mut self) -> Result<(), SendError> {
162        self.responder.send(None)
163    }
164
165    /// The set of devices that the user can chose from.
166    pub fn devices(&self) -> &Vec<BluetoothDeviceDescription> {
167        &self.devices
168    }
169}
170
171/// A request to authenticate a [`WebView`] navigation. Embedders may choose to prompt
172/// the user to enter credentials or simply ignore this request (in which case credentials
173/// will not be used).
174pub struct AuthenticationRequest {
175    pub(crate) url: Url,
176    pub(crate) for_proxy: bool,
177    pub(crate) responder: IpcResponder<Option<AuthenticationResponse>>,
178    pub(crate) error_sender: ServoErrorSender,
179}
180
181impl AuthenticationRequest {
182    pub(crate) fn new(
183        url: Url,
184        for_proxy: bool,
185        response_sender: Sender<Option<AuthenticationResponse>>,
186        error_sender: ServoErrorSender,
187    ) -> Self {
188        Self {
189            url,
190            for_proxy,
191            responder: IpcResponder::new_same_process(
192                Box::new(OneshotSender::from(response_sender)),
193                None,
194            ),
195            error_sender,
196        }
197    }
198
199    /// The URL of the request that triggered this authentication.
200    pub fn url(&self) -> &Url {
201        &self.url
202    }
203    /// Whether or not this authentication request is associated with a proxy server authentication.
204    pub fn for_proxy(&self) -> bool {
205        self.for_proxy
206    }
207    /// Respond to the [`AuthenticationRequest`] with the given username and password.
208    pub fn authenticate(mut self, username: String, password: String) {
209        if let Err(error) = self
210            .responder
211            .send(Some(AuthenticationResponse { username, password }))
212        {
213            self.error_sender.raise_response_send_error(error);
214        }
215    }
216}
217
218/// Information related to the loading of a web resource. These are created for all HTTP requests.
219/// The client may choose to intercept the load of web resources and send an alternate response
220/// by calling [`WebResourceLoad::intercept`].
221pub struct WebResourceLoad {
222    pub request: WebResourceRequest,
223    pub(crate) responder: IpcResponder<WebResourceResponseMsg>,
224    pub(crate) error_sender: ServoErrorSender,
225}
226
227impl WebResourceLoad {
228    pub(crate) fn new(
229        web_resource_request: WebResourceRequest,
230        response_sender: TokioSender<WebResourceResponseMsg>,
231        error_sender: ServoErrorSender,
232    ) -> Self {
233        Self {
234            request: web_resource_request,
235            responder: IpcResponder::new_same_process(
236                Box::new(response_sender),
237                WebResourceResponseMsg::DoNotIntercept,
238            ),
239            error_sender,
240        }
241    }
242
243    /// The [`WebResourceRequest`] associated with this [`WebResourceLoad`].
244    pub fn request(&self) -> &WebResourceRequest {
245        &self.request
246    }
247
248    /// Intercept this [`WebResourceLoad`] and control the response via the returned
249    /// [`InterceptedWebResourceLoad`].
250    pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad {
251        if let Err(error) = self.responder.send(WebResourceResponseMsg::Start(response)) {
252            self.error_sender.raise_response_send_error(error);
253        }
254        InterceptedWebResourceLoad {
255            request: self.request.clone(),
256            response_sender: self.responder,
257            finished: false,
258            error_sender: self.error_sender,
259        }
260    }
261}
262
263/// An intercepted web resource load. This struct allows the client to send an alternative response
264/// after calling [`WebResourceLoad::intercept`]. In order to send chunks of body data, the client
265/// must call [`InterceptedWebResourceLoad::send_body_data`]. When the interception is complete, the client
266/// should call [`InterceptedWebResourceLoad::finish`]. If neither `finish()` or `cancel()` are called,
267/// this interception will automatically be finished when dropped.
268pub struct InterceptedWebResourceLoad {
269    pub request: WebResourceRequest,
270    pub(crate) response_sender: IpcResponder<WebResourceResponseMsg>,
271    pub(crate) finished: bool,
272    pub(crate) error_sender: ServoErrorSender,
273}
274
275impl InterceptedWebResourceLoad {
276    /// Send a chunk of response body data. It's possible to make subsequent calls to
277    /// this method when streaming body data.
278    pub fn send_body_data(&mut self, data: Vec<u8>) {
279        if let Err(error) = self
280            .response_sender
281            .send(WebResourceResponseMsg::SendBodyData(data))
282        {
283            self.error_sender.raise_response_send_error(error);
284        }
285    }
286    /// Finish this [`InterceptedWebResourceLoad`] and complete the response.
287    pub fn finish(mut self) {
288        if let Err(error) = self
289            .response_sender
290            .send(WebResourceResponseMsg::FinishLoad)
291        {
292            self.error_sender.raise_response_send_error(error);
293        }
294        self.finished = true;
295    }
296    /// Cancel this [`InterceptedWebResourceLoad`], which will trigger a network error.
297    pub fn cancel(mut self) {
298        if let Err(error) = self
299            .response_sender
300            .send(WebResourceResponseMsg::CancelLoad)
301        {
302            self.error_sender.raise_response_send_error(error);
303        }
304        self.finished = true;
305    }
306}
307
308impl Drop for InterceptedWebResourceLoad {
309    fn drop(&mut self) {
310        if !self.finished {
311            if let Err(error) = self
312                .response_sender
313                .send(WebResourceResponseMsg::FinishLoad)
314            {
315                self.error_sender.raise_response_send_error(error);
316            }
317        }
318    }
319}
320
321/// The controls of an interactive form element.
322pub enum EmbedderControl {
323    /// The picker of a `<select>` element.
324    SelectElement(SelectElement),
325    /// The picker of a `<input type=color>` element.
326    ColorPicker(ColorPicker),
327    /// The picker of a `<input type=file>` element.
328    FilePicker(FilePicker),
329    /// Request to present an input method (IME) interface to the user when an
330    /// editable element is focused.
331    InputMethod(InputMethodControl),
332    /// A [simple dialog](https://html.spec.whatwg.org/multipage/#simple-dialogs) initiated by
333    /// script (`alert()`, `confirm()`, or `prompt()`). Since their messages are controlled by web
334    /// content, they should be presented to the user in a way that makes them impossible to
335    /// mistake for browser UI.
336    SimpleDialog(SimpleDialog),
337    /// A context menu. This can be triggered by things like right-clicking on web content.
338    /// The menu that is actually shown the user may be customized, but custom menu entries
339    /// must be handled by the embedder.
340    ContextMenu(ContextMenu),
341}
342
343impl EmbedderControl {
344    pub fn id(&self) -> EmbedderControlId {
345        match self {
346            EmbedderControl::SelectElement(select_element) => select_element.id,
347            EmbedderControl::ColorPicker(color_picker) => color_picker.id,
348            EmbedderControl::FilePicker(file_picker) => file_picker.id,
349            EmbedderControl::InputMethod(input_method) => input_method.id,
350            EmbedderControl::SimpleDialog(simple_dialog) => simple_dialog.id(),
351            EmbedderControl::ContextMenu(context_menu) => context_menu.id,
352        }
353    }
354}
355
356/// Represents a context menu opened on web content.
357pub struct ContextMenu {
358    pub(crate) id: EmbedderControlId,
359    pub(crate) position: DeviceIntRect,
360    pub(crate) items: Vec<ContextMenuItem>,
361    pub(crate) element_info: ContextMenuElementInformation,
362    pub(crate) response_sent: bool,
363    pub(crate) constellation_proxy: ConstellationProxy,
364}
365
366impl ContextMenu {
367    /// Return the [`EmbedderControlId`] associated with this element.
368    pub fn id(&self) -> EmbedderControlId {
369        self.id
370    }
371
372    /// Return the area occupied by the element on which this context menu was triggered.
373    ///
374    /// The embedder should use this value to position the prompt that is shown to the user.
375    pub fn position(&self) -> DeviceIntRect {
376        self.position
377    }
378
379    /// A [`ContextMenuElementInformation`] giving details about the element that this [`ContextMenu`]
380    /// was activated on.
381    pub fn element_info(&self) -> &ContextMenuElementInformation {
382        &self.element_info
383    }
384
385    /// Resolve the context menu by activating the given context menu action.
386    pub fn items(&self) -> &[ContextMenuItem] {
387        &self.items
388    }
389
390    /// Resolve the context menu by activating the given context menu action.
391    pub fn select(mut self, action: ContextMenuAction) {
392        self.constellation_proxy
393            .send(EmbedderToConstellationMessage::EmbedderControlResponse(
394                self.id,
395                EmbedderControlResponse::ContextMenu(Some(action)),
396            ));
397        self.response_sent = true;
398    }
399
400    /// Tell Servo that the context menu was dismissed with no selection.
401    pub fn dismiss(mut self) {
402        self.constellation_proxy
403            .send(EmbedderToConstellationMessage::EmbedderControlResponse(
404                self.id,
405                EmbedderControlResponse::ContextMenu(None),
406            ));
407        self.response_sent = true;
408    }
409}
410
411impl Drop for ContextMenu {
412    fn drop(&mut self) {
413        if !self.response_sent {
414            self.constellation_proxy
415                .send(EmbedderToConstellationMessage::EmbedderControlResponse(
416                    self.id,
417                    EmbedderControlResponse::ContextMenu(None),
418                ));
419        }
420    }
421}
422
423/// Represents a dialog triggered by clicking a `<select>` element.
424pub struct SelectElement {
425    pub(crate) id: EmbedderControlId,
426    pub(crate) position: DeviceIntRect,
427    pub(crate) constellation_proxy: ConstellationProxy,
428    pub(crate) select_element_request: SelectElementRequest,
429    pub(crate) response_sent: bool,
430}
431
432impl SelectElement {
433    /// Return the [`EmbedderControlId`] associated with this element.
434    pub fn id(&self) -> EmbedderControlId {
435        self.id
436    }
437
438    /// Return the area occupied by the `<select>` element that triggered the prompt.
439    ///
440    /// The embedder should use this value to position the prompt that is shown to the user.
441    pub fn position(&self) -> DeviceIntRect {
442        self.position
443    }
444
445    /// Consecutive `<option>` elements outside of an `<optgroup>` will be combined
446    /// into a single anonymous group without a label.
447    pub fn options(&self) -> &[SelectElementOptionOrOptgroup] {
448        &self.select_element_request.options
449    }
450
451    /// Set the options that are selected.
452    ///
453    /// If other options have previously been selected, this set of options
454    /// will replace them.
455    pub fn select(&mut self, selected_options: Vec<usize>) {
456        self.select_element_request.selected_options = selected_options
457    }
458
459    pub fn selected_options(&self) -> Vec<usize> {
460        self.select_element_request.selected_options.clone()
461    }
462
463    pub fn allow_select_multiple(&self) -> bool {
464        self.select_element_request.allow_select_multiple
465    }
466
467    /// Resolve the prompt with the options that have been selected by calling [`Self::select`] previously.
468    pub fn submit(mut self) {
469        self.response_sent = true;
470        self.constellation_proxy
471            .send(EmbedderToConstellationMessage::EmbedderControlResponse(
472                self.id,
473                EmbedderControlResponse::SelectElement(self.selected_options()),
474            ));
475    }
476}
477
478impl Drop for SelectElement {
479    fn drop(&mut self) {
480        if !self.response_sent {
481            self.constellation_proxy
482                .send(EmbedderToConstellationMessage::EmbedderControlResponse(
483                    self.id,
484                    EmbedderControlResponse::SelectElement(self.selected_options()),
485                ));
486        }
487    }
488}
489
490/// Represents a dialog triggered by clicking a `<input type=color>` element.
491pub struct ColorPicker {
492    pub(crate) id: EmbedderControlId,
493    pub(crate) current_color: Option<RgbColor>,
494    pub(crate) position: DeviceIntRect,
495    pub(crate) constellation_proxy: ConstellationProxy,
496    pub(crate) response_sent: bool,
497}
498
499impl ColorPicker {
500    /// Return the [`EmbedderControlId`] associated with this element.
501    pub fn id(&self) -> EmbedderControlId {
502        self.id
503    }
504
505    /// Get the area occupied by the `<input>` element that triggered the prompt.
506    ///
507    /// The embedder should use this value to position the prompt that is shown to the user.
508    pub fn position(&self) -> DeviceIntRect {
509        self.position
510    }
511
512    /// Get the currently selected color for this [`ColorPicker`]. This is initially the selected color
513    /// before the picker is opened.
514    pub fn current_color(&self) -> Option<RgbColor> {
515        self.current_color
516    }
517
518    pub fn select(&mut self, color: Option<RgbColor>) {
519        self.current_color = color;
520    }
521
522    /// Resolve the prompt with the options that have been selected by calling [`Self::select`] previously.
523    pub fn submit(mut self) {
524        self.response_sent = true;
525        self.constellation_proxy
526            .send(EmbedderToConstellationMessage::EmbedderControlResponse(
527                self.id,
528                EmbedderControlResponse::ColorPicker(self.current_color),
529            ));
530    }
531}
532
533impl Drop for ColorPicker {
534    fn drop(&mut self) {
535        if !self.response_sent {
536            self.constellation_proxy
537                .send(EmbedderToConstellationMessage::EmbedderControlResponse(
538                    self.id,
539                    EmbedderControlResponse::ColorPicker(self.current_color),
540                ));
541        }
542    }
543}
544
545/// Represents a dialog triggered by clicking a `<input type=color>` element.
546pub struct FilePicker {
547    pub(crate) id: EmbedderControlId,
548    pub(crate) file_picker_request: FilePickerRequest,
549    pub(crate) response_sender: Option<Sender<Option<Vec<PathBuf>>>>,
550}
551
552impl FilePicker {
553    /// Return the [`EmbedderControlId`] associated with this element.
554    pub fn id(&self) -> EmbedderControlId {
555        self.id
556    }
557
558    pub fn filter_patterns(&self) -> &[FilterPattern] {
559        &self.file_picker_request.filter_patterns
560    }
561
562    pub fn allow_select_multiple(&self) -> bool {
563        self.file_picker_request.allow_select_multiple
564    }
565
566    /// Get the currently selected files in this [`FilePicker`]. This is initially the files that
567    /// were previously selected before the picker is opened.
568    pub fn current_paths(&self) -> &[PathBuf] {
569        &self.file_picker_request.current_paths
570    }
571
572    pub fn select(&mut self, paths: &[PathBuf]) {
573        self.file_picker_request.current_paths = paths.to_owned();
574    }
575
576    /// Resolve the prompt with the options that have been selected by calling [`Self::select`] previously.
577    pub fn submit(mut self) {
578        if let Some(sender) = self.response_sender.take() {
579            let _ = sender.send(Some(std::mem::take(
580                &mut self.file_picker_request.current_paths,
581            )));
582        }
583    }
584
585    /// Tell Servo that the file picker was dismissed with no selection.
586    pub fn dismiss(mut self) {
587        if let Some(sender) = self.response_sender.take() {
588            let _ = sender.send(None);
589        }
590    }
591}
592
593impl Drop for FilePicker {
594    fn drop(&mut self) {
595        if let Some(sender) = self.response_sender.take() {
596            let _ = sender.send(None);
597        }
598    }
599}
600
601/// Represents a request to enable the system input method interface.
602pub struct InputMethodControl {
603    pub(crate) id: EmbedderControlId,
604    pub(crate) input_method_type: InputMethodType,
605    pub(crate) text: String,
606    pub(crate) insertion_point: Option<u32>,
607    pub(crate) position: DeviceIntRect,
608    pub(crate) multiline: bool,
609    pub(crate) allow_virtual_keyboard: bool,
610}
611
612impl InputMethodControl {
613    /// Return the [`EmbedderControlId`] associated with this element.
614    pub fn id(&self) -> EmbedderControlId {
615        self.id
616    }
617
618    /// Return the type of input method that initated this request.
619    pub fn input_method_type(&self) -> InputMethodType {
620        self.input_method_type
621    }
622
623    /// Return the current string value of the input field.
624    pub fn text(&self) -> String {
625        self.text.clone()
626    }
627
628    /// The current zero-based insertion point / cursor position if it is within the field or `None`
629    /// if it is not.
630    pub fn insertion_point(&self) -> Option<u32> {
631        self.insertion_point
632    }
633
634    /// Get the area occupied by the `<input>` element that triggered the input method.
635    ///
636    /// The embedder should use this value to position the input method interface that is
637    /// shown to the user.
638    pub fn position(&self) -> DeviceIntRect {
639        self.position
640    }
641
642    /// Whether or not this field is a multiline field.
643    pub fn multiline(&self) -> bool {
644        self.multiline
645    }
646
647    /// Whether the virtual keyboard should be shown for this input method event.
648    /// This is currently true for input method events that happen after the user has
649    /// interacted with page contents via an input event.
650    pub fn allow_virtual_keyboard(&self) -> bool {
651        self.allow_virtual_keyboard
652    }
653}
654
655/// [Simple dialogs](https://html.spec.whatwg.org/multipage/#simple-dialogs) are synchronous dialogs
656/// that can be opened by web content. Since their messages are controlled by web content, they
657/// should be presented to the user in a way that makes them impossible to mistake for browser UI.
658pub enum SimpleDialog {
659    Alert(AlertDialog),
660    Confirm(ConfirmDialog),
661    Prompt(PromptDialog),
662}
663
664impl SimpleDialog {
665    pub fn message(&self) -> &str {
666        match self {
667            SimpleDialog::Alert(alert_dialog) => alert_dialog.message(),
668            SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.message(),
669            SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.message(),
670        }
671    }
672
673    pub fn confirm(self) {
674        match self {
675            SimpleDialog::Alert(alert_dialog) => alert_dialog.confirm(),
676            SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.confirm(),
677            SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.confirm(),
678        }
679    }
680
681    pub fn dismiss(self) {
682        match self {
683            SimpleDialog::Alert(alert_dialog) => alert_dialog.confirm(),
684            SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.dismiss(),
685            SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.dismiss(),
686        }
687    }
688}
689
690impl SimpleDialog {
691    fn id(&self) -> EmbedderControlId {
692        match self {
693            SimpleDialog::Alert(alert_dialog) => alert_dialog.id,
694            SimpleDialog::Confirm(confirm_dialog) => confirm_dialog.id,
695            SimpleDialog::Prompt(prompt_dialog) => prompt_dialog.id,
696        }
697    }
698}
699
700impl From<SimpleDialogRequest> for SimpleDialog {
701    fn from(simple_dialog_request: SimpleDialogRequest) -> Self {
702        match simple_dialog_request {
703            SimpleDialogRequest::Alert {
704                id,
705                message,
706                response_sender,
707            } => Self::Alert(AlertDialog {
708                id,
709                message,
710                response_sender,
711                response_sent: false,
712            }),
713            SimpleDialogRequest::Confirm {
714                id,
715                message,
716                response_sender,
717            } => Self::Confirm(ConfirmDialog {
718                id,
719                message,
720                response_sender,
721                response_sent: false,
722            }),
723            SimpleDialogRequest::Prompt {
724                id,
725                message,
726                default,
727                response_sender,
728            } => Self::Prompt(PromptDialog {
729                id,
730                message,
731                current_value: default,
732                response_sender,
733                response_sent: false,
734            }),
735        }
736    }
737}
738
739/// [`alert()`](https://html.spec.whatwg.org/multipage/#dom-alert).
740///
741/// The confirm dialog is expected to be represented by a message and an "Ok" button.
742/// Pressing "Ok" always causes the DOM API to return `undefined`.
743pub struct AlertDialog {
744    id: EmbedderControlId,
745    message: String,
746    response_sender: GenericSender<AlertResponse>,
747    response_sent: bool,
748}
749
750impl Drop for AlertDialog {
751    fn drop(&mut self) {
752        if !self.response_sent {
753            let _ = self.response_sender.send(AlertResponse::Ok);
754        }
755    }
756}
757
758impl AlertDialog {
759    pub fn message(&self) -> &str {
760        &self.message
761    }
762
763    /// This should be called when the dialog button is pressed.
764    pub fn confirm(self) {
765        // The result will be send via the `Drop` implementation.
766    }
767}
768
769/// [`confirm()`](https://html.spec.whatwg.org/multipage/#dom-confirm).
770///
771/// The confirm dialog is expected to be represented by a message and "Ok" and "Cancel"
772/// buttons. When "Ok" is selected `true` is sent as a response to the DOM API, while
773/// "Cancel" will send `false`.
774pub struct ConfirmDialog {
775    id: EmbedderControlId,
776    message: String,
777    response_sender: GenericSender<ConfirmResponse>,
778    response_sent: bool,
779}
780
781impl ConfirmDialog {
782    pub fn message(&self) -> &str {
783        &self.message
784    }
785
786    /// This should be called when the dialog "Cancel" button is pressed.
787    pub fn dismiss(mut self) {
788        let _ = self.response_sender.send(ConfirmResponse::Cancel);
789        self.response_sent = true;
790    }
791
792    /// This should be called when the dialog "Ok" button is pressed.
793    pub fn confirm(mut self) {
794        let _ = self.response_sender.send(ConfirmResponse::Ok);
795        self.response_sent = true;
796    }
797}
798
799impl Drop for ConfirmDialog {
800    fn drop(&mut self) {
801        if !self.response_sent {
802            let _ = self.response_sender.send(ConfirmResponse::Cancel);
803        }
804    }
805}
806
807/// A [`prompt()`](https://html.spec.whatwg.org/multipage/#dom-prompt).
808///
809/// The prompt dialog is expected to be represented by a mesage, a text entry field, and
810/// an "Ok" and "Cancel" buttons. When "Ok" is selected the current prompt value is sent
811/// as the response to the DOM API. A default value may be sent with the [`PromptDialog`],
812/// which be be retrieved by calling [`Self::current_value`]. Before calling [`Self::confirm`]
813/// or as the prompt field changes, the embedder is expected to call
814/// [`Self::set_current_value`].
815pub struct PromptDialog {
816    id: EmbedderControlId,
817    message: String,
818    current_value: String,
819    response_sender: GenericSender<PromptResponse>,
820    response_sent: bool,
821}
822
823impl Drop for PromptDialog {
824    fn drop(&mut self) {
825        if !self.response_sent {
826            let _ = self.response_sender.send(PromptResponse::Cancel);
827        }
828    }
829}
830
831impl PromptDialog {
832    pub fn message(&self) -> &str {
833        &self.message
834    }
835
836    pub fn current_value(&self) -> &str {
837        &self.current_value
838    }
839
840    pub fn set_current_value(&mut self, new_value: &str) {
841        self.current_value = new_value.to_owned()
842    }
843
844    /// This should be called when the dialog "Cancel" button is pressed.
845    pub fn dismiss(mut self) {
846        let _ = self.response_sender.send(PromptResponse::Cancel);
847        self.response_sent = true;
848    }
849
850    /// This should be called when the dialog "Ok" button is pressed, the current prompt value will
851    /// be sent to web content.
852    pub fn confirm(mut self) {
853        let _ = self
854            .response_sender
855            .send(PromptResponse::Ok(self.current_value.clone()));
856        self.response_sent = true;
857    }
858}
859
860pub struct CreateNewWebViewRequest {
861    pub(crate) servo: Servo,
862    pub(crate) responder: IpcResponder<Option<NewWebViewDetails>>,
863}
864
865impl CreateNewWebViewRequest {
866    pub fn builder(self, rendering_context: Rc<dyn RenderingContext>) -> WebViewBuilder {
867        WebViewBuilder::new_for_create_request(&self.servo, rendering_context, self.responder)
868    }
869}
870
871pub trait WebViewDelegate {
872    /// Get the [`ScreenGeometry`] for this [`WebView`]. If this is unimplemented or returns `None`
873    /// the screen will have the size of the [`WebView`]'s `RenderingContext` and `WebView` will be
874    /// considered to be positioned at the screen's origin.
875    fn screen_geometry(&self, _webview: WebView) -> Option<ScreenGeometry> {
876        None
877    }
878    /// The URL of the currently loaded page in this [`WebView`] has changed. The new
879    /// URL can accessed via [`WebView::url`].
880    fn notify_url_changed(&self, _webview: WebView, _url: Url) {}
881    /// The page title of the currently loaded page in this [`WebView`] has changed. The new
882    /// title can accessed via [`WebView::page_title`].
883    fn notify_page_title_changed(&self, _webview: WebView, _title: Option<String>) {}
884    /// The status text of the currently loaded page in this [`WebView`] has changed. The new
885    /// status text can accessed via [`WebView::status_text`].
886    fn notify_status_text_changed(&self, _webview: WebView, _status: Option<String>) {}
887    /// This [`WebView`] has either become focused or lost focus. Whether or not the
888    /// [`WebView`] is focused can be accessed via [`WebView::focused`].
889    fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {}
890    /// This [`WebView`] has either started to animate or stopped animating. When a
891    /// [`WebView`] is animating, it is up to the embedding application ensure that
892    /// `Servo::spin_event_loop` is called at regular intervals in order to update the
893    /// painted contents of the [`WebView`].
894    fn notify_animating_changed(&self, _webview: WebView, _animating: bool) {}
895    /// The `LoadStatus` of the currently loading or loaded page in this [`WebView`] has changed. The new
896    /// status can accessed via [`WebView::load_status`].
897    fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {}
898    /// The [`Cursor`] of the currently loaded page in this [`WebView`] has changed. The new
899    /// cursor can accessed via [`WebView::cursor`].
900    fn notify_cursor_changed(&self, _webview: WebView, _cursor: Cursor) {}
901    /// The favicon of the currently loaded page in this [`WebView`] has changed. The new
902    /// favicon [`Image`](embedder_traits::Image) can accessed via [`WebView::favicon`].
903    fn notify_favicon_changed(&self, _webview: WebView) {}
904    /// Notify the embedder that it needs to present a new frame.
905    fn notify_new_frame_ready(&self, _webview: WebView) {}
906    /// The navigation history of this [`WebView`] has changed. The navigation history is represented
907    /// as a `Vec<Url>` and `_current` denotes the current index in the history. New navigations,
908    /// back navigation, and forward navigation modify this index.
909    fn notify_history_changed(&self, _webview: WebView, _entries: Vec<Url>, _current: usize) {}
910    /// A history traversal operation is complete.
911    fn notify_traversal_complete(&self, _webview: WebView, _traversal_id: TraversalId) {}
912    /// Page content has closed this [`WebView`] via `window.close()`. It's the embedder's
913    /// responsibility to remove the [`WebView`] from the interface when this notification
914    /// occurs.
915    fn notify_closed(&self, _webview: WebView) {}
916
917    /// An input event passed to this [`WebView`] via [`WebView::notify_input_event`] has been handled
918    /// by Servo. This allows post-procesing of input events, such as chaining up unhandled events
919    /// to parent UI elements.
920    fn notify_input_event_handled(
921        &self,
922        _webview: WebView,
923        _event_id: InputEventId,
924        _result: InputEventResult,
925    ) {
926    }
927    /// A pipeline in the webview panicked. First string is the reason, second one is the backtrace.
928    fn notify_crashed(&self, _webview: WebView, _reason: String, _backtrace: Option<String>) {}
929    /// Notifies the embedder about media session events
930    /// (i.e. when there is metadata for the active media session, playback state changes...).
931    fn notify_media_session_event(&self, _webview: WebView, _event: MediaSessionEvent) {}
932    /// A notification that the [`WebView`] has entered or exited fullscreen mode. This is an
933    /// opportunity for the embedder to transition the containing window into or out of fullscreen
934    /// mode and to show or hide extra UI elements. Regardless of how the notification is handled,
935    /// the page will enter or leave fullscreen state internally according to the [Fullscreen
936    /// API](https://fullscreen.spec.whatwg.org/).
937    fn notify_fullscreen_state_changed(&self, _webview: WebView, _is_fullscreen: bool) {}
938
939    /// Whether or not to allow a [`WebView`] to load a URL in its main frame or one of its
940    /// nested `<iframe>`s. [`NavigationRequest`]s are accepted by default.
941    fn request_navigation(&self, _webview: WebView, _navigation_request: NavigationRequest) {}
942    /// Whether or not to allow a [`WebView`]  to unload a `Document` in its main frame or one
943    /// of its nested `<iframe>`s. By default, unloads are allowed.
944    fn request_unload(&self, _webview: WebView, _unload_request: AllowOrDenyRequest) {}
945    /// Move the window to a point.
946    fn request_move_to(&self, _webview: WebView, _point: DeviceIntPoint) {}
947    /// Whether or not to allow a [`WebView`] to (un)register a protocol handler (e.g. `mailto:`).
948    /// Typically an embedder application will show a permissions prompt when this happens
949    /// to confirm a protocol handler is allowed. By default, requests are denied.
950    /// For more information, see the specification:
951    /// <https://html.spec.whatwg.org/multipage/#custom-handlers>
952    fn request_protocol_handler(
953        &self,
954        _webview: WebView,
955        _protocol_handler_registration: ProtocolHandlerRegistration,
956        _allow_deny_request: AllowOrDenyRequest,
957    ) {
958    }
959    /// Try to resize the window that contains this [`WebView`] to the provided outer
960    /// size. These resize requests can come from page content. Servo will ensure that the
961    /// values are greater than zero, but it is up to the embedder to limit the maximum
962    /// size. For instance, a reasonable limitation might be that the final size is no
963    /// larger than the screen size.
964    fn request_resize_to(&self, _webview: WebView, _requested_outer_size: DeviceIntSize) {}
965    /// This method is called when web content makes a request to open a new
966    /// `WebView`, such as via the [`window.open`] DOM API. If this request is
967    /// ignored, no new `WebView` will be opened. Embedders can handle this method by
968    /// using the provided [`CreateNewWebViewRequest`] to build a new `WebView`.
969    ///
970    /// ```rust
971    /// fn request_create_new(&self, parent_webview: WebView, request: CreateNewWebViewRequest) {
972    ///     let webview = request
973    ///         .builder(self.rendering_context())
974    ///         .delegate(parent_webview.delegate())
975    ///         .build();
976    ///     self.register_webview(webview);
977    /// }
978    /// ```
979    ///
980    /// **Important:** It is important to keep a live handle to the new `WebView` in the application or
981    /// it will be immediately destroyed.
982    ///
983    /// [`window.open`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
984    fn request_create_new(&self, _parent_webview: WebView, _request: CreateNewWebViewRequest) {}
985    /// Content in a [`WebView`] is requesting permission to access a feature requiring
986    /// permission from the user. The embedder should allow or deny the request, either by
987    /// reading a cached value or querying the user for permission via the user interface.
988    fn request_permission(&self, _webview: WebView, _request: PermissionRequest) {}
989
990    fn request_authentication(
991        &self,
992        _webview: WebView,
993        _authentication_request: AuthenticationRequest,
994    ) {
995    }
996
997    /// Open dialog to select bluetooth device.
998    fn show_bluetooth_device_dialog(
999        &self,
1000        _webview: WebView,
1001        _request: BluetoothDeviceSelectionRequest,
1002    ) {
1003    }
1004
1005    /// Request that the embedder show UI elements for form controls that are not integrated
1006    /// into page content, such as dropdowns for `<select>` elements.
1007    fn show_embedder_control(&self, _webview: WebView, _embedder_control: EmbedderControl) {}
1008
1009    /// Request that the embedder hide and ignore a previous [`EmbedderControl`] request, if it hasn’t
1010    /// already responded to it.
1011    ///
1012    /// After this point, any further responses to that request will be ignored.
1013    fn hide_embedder_control(&self, _webview: WebView, _control_id: EmbedderControlId) {}
1014
1015    /// Triggered when this [`WebView`] will load a web (HTTP/HTTPS) resource. The load may be
1016    /// intercepted and alternate contents can be loaded by the client by calling
1017    /// [`WebResourceLoad::intercept`]. If not handled, the load will continue as normal.
1018    ///
1019    /// Note: This delegate method is called for all resource loads associated with a [`WebView`].
1020    /// For loads not associated with a [`WebView`], such as those for service workers, Servo
1021    /// will call [`crate::ServoDelegate::load_web_resource`].
1022    fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
1023
1024    /// Request to display a notification.
1025    fn show_notification(&self, _webview: WebView, _notification: Notification) {}
1026
1027    /// A console message was logged by content in this [`WebView`].
1028    /// <https://developer.mozilla.org/en-US/docs/Web/API/Console_API>
1029    fn show_console_message(&self, _webview: WebView, _level: ConsoleLogLevel, _message: String) {}
1030
1031    /// There is a new accessibility tree update from this [`WebView`].
1032    ///
1033    /// Generally the impl should send this update to an AccessKit adapter, but it may need to queue
1034    /// the update for later, if the [graft node] for this [`WebView`] has not yet been created
1035    /// *and* your impl is unable to create it (and send that update to AccessKit) before sending
1036    /// this update to AccessKit. For more details, see [`WebView::set_accessibility_active`].
1037    fn notify_accessibility_tree_update(
1038        &self,
1039        _webview: WebView,
1040        _tree_update: accesskit::TreeUpdate,
1041    ) {
1042    }
1043}
1044
1045pub(crate) struct DefaultWebViewDelegate;
1046impl WebViewDelegate for DefaultWebViewDelegate {}
1047
1048#[cfg(test)]
1049mod test {
1050    use super::*;
1051
1052    #[test]
1053    fn test_allow_deny_request() {
1054        use servo_base::generic_channel;
1055
1056        use crate::responders::ServoErrorChannel;
1057
1058        for default_response in [AllowOrDeny::Allow, AllowOrDeny::Deny] {
1059            // Explicit allow yields allow and nothing else
1060            let errors = ServoErrorChannel::default();
1061            let (sender, receiver) =
1062                generic_channel::channel().expect("Failed to create IPC channel");
1063            let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
1064            request.allow();
1065            assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Allow));
1066            assert_eq!(receiver.try_recv().ok(), None);
1067            assert!(errors.try_recv().is_none());
1068
1069            // Explicit deny yields deny and nothing else
1070            let errors = ServoErrorChannel::default();
1071            let (sender, receiver) =
1072                generic_channel::channel().expect("Failed to create IPC channel");
1073            let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
1074            request.deny();
1075            assert_eq!(receiver.try_recv().ok(), Some(AllowOrDeny::Deny));
1076            assert_eq!(receiver.try_recv().ok(), None);
1077            assert!(errors.try_recv().is_none());
1078
1079            // No response yields default response and nothing else
1080            let errors = ServoErrorChannel::default();
1081            let (sender, receiver) =
1082                generic_channel::channel().expect("Failed to create IPC channel");
1083            let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
1084            drop(request);
1085            assert_eq!(receiver.try_recv().ok(), Some(default_response));
1086            assert_eq!(receiver.try_recv().ok(), None);
1087            assert!(errors.try_recv().is_none());
1088
1089            // Explicit allow when receiver disconnected yields error
1090            let errors = ServoErrorChannel::default();
1091            let (sender, receiver) =
1092                generic_channel::channel().expect("Failed to create IPC channel");
1093            let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
1094            drop(receiver);
1095            request.allow();
1096            assert!(errors.try_recv().is_some());
1097
1098            // Explicit deny when receiver disconnected yields error
1099            let errors = ServoErrorChannel::default();
1100            let (sender, receiver) =
1101                generic_channel::channel().expect("Failed to create IPC channel");
1102            let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
1103            drop(receiver);
1104            request.deny();
1105            assert!(errors.try_recv().is_some());
1106
1107            // No response when receiver disconnected yields no error
1108            let errors = ServoErrorChannel::default();
1109            let (sender, receiver) =
1110                generic_channel::channel().expect("Failed to create IPC channel");
1111            let request = AllowOrDenyRequest::new(sender, default_response, errors.sender());
1112            drop(receiver);
1113            drop(request);
1114            assert!(errors.try_recv().is_none());
1115        }
1116    }
1117
1118    #[test]
1119    fn test_authentication_request() {
1120        use crate::responders::ServoErrorChannel;
1121
1122        let url = Url::parse("https://example.com").expect("Guaranteed by argument");
1123
1124        // Explicit response yields that response and nothing else
1125        let errors = ServoErrorChannel::default();
1126        let (sender, mut receiver) = tokio::sync::oneshot::channel();
1127        let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
1128        request.authenticate("diffie".to_owned(), "hunter2".to_owned());
1129        assert_eq!(
1130            receiver.try_recv().ok(),
1131            Some(Some(AuthenticationResponse {
1132                username: "diffie".to_owned(),
1133                password: "hunter2".to_owned(),
1134            }))
1135        );
1136        assert_eq!(receiver.try_recv().ok(), None);
1137        assert!(errors.try_recv().is_none());
1138
1139        // No response yields None response and nothing else
1140        let errors = ServoErrorChannel::default();
1141        let (sender, mut receiver) = tokio::sync::oneshot::channel();
1142        let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
1143        drop(request);
1144        assert_eq!(receiver.try_recv().ok(), Some(None));
1145        assert_eq!(receiver.try_recv().ok(), None);
1146        assert!(errors.try_recv().is_none());
1147
1148        // Explicit response when receiver disconnected yields error
1149        let errors = ServoErrorChannel::default();
1150        let (sender, receiver) = tokio::sync::oneshot::channel();
1151        let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
1152        drop(receiver);
1153        request.authenticate("diffie".to_owned(), "hunter2".to_owned());
1154        assert!(errors.try_recv().is_some());
1155
1156        // No response when receiver disconnected yields no error
1157        let errors = ServoErrorChannel::default();
1158        let (sender, receiver) = tokio::sync::oneshot::channel();
1159        let request = AuthenticationRequest::new(url.clone(), false, sender, errors.sender());
1160        drop(receiver);
1161        drop(request);
1162        assert!(errors.try_recv().is_none());
1163    }
1164
1165    #[test]
1166    fn test_web_resource_load() {
1167        use http::{HeaderMap, Method, StatusCode};
1168
1169        use crate::responders::ServoErrorChannel;
1170
1171        let web_resource_request = || WebResourceRequest {
1172            method: Method::GET,
1173            headers: HeaderMap::default(),
1174            url: Url::parse("https://example.com").expect("Guaranteed by argument"),
1175            is_for_main_frame: false,
1176            is_redirect: false,
1177        };
1178        let web_resource_response = || {
1179            WebResourceResponse::new(
1180                Url::parse("https://diffie.test").expect("Guaranteed by argument"),
1181            )
1182            .status_code(StatusCode::IM_A_TEAPOT)
1183        };
1184
1185        // Explicit intercept with explicit cancel yields Start and Cancel and nothing else
1186        let errors = ServoErrorChannel::default();
1187        let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel();
1188        let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
1189        request.intercept(web_resource_response()).cancel();
1190        assert!(matches!(
1191            receiver.try_recv(),
1192            Ok(WebResourceResponseMsg::Start(_))
1193        ));
1194        assert!(matches!(
1195            receiver.try_recv(),
1196            Ok(WebResourceResponseMsg::CancelLoad)
1197        ));
1198        assert!(matches!(receiver.try_recv(), Err(_)));
1199        assert!(errors.try_recv().is_none());
1200
1201        // Explicit intercept with no further action yields Start and FinishLoad and nothing else
1202        let errors = ServoErrorChannel::default();
1203        let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel();
1204        let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
1205        drop(request.intercept(web_resource_response()));
1206        assert!(matches!(
1207            receiver.try_recv(),
1208            Ok(WebResourceResponseMsg::Start(_))
1209        ));
1210        assert!(matches!(
1211            receiver.try_recv(),
1212            Ok(WebResourceResponseMsg::FinishLoad)
1213        ));
1214        assert!(matches!(receiver.try_recv(), Err(_)));
1215        assert!(errors.try_recv().is_none());
1216
1217        // No response yields DoNotIntercept and nothing else
1218        let errors = ServoErrorChannel::default();
1219        let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel();
1220        let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
1221        drop(request);
1222        assert!(matches!(
1223            receiver.try_recv(),
1224            Ok(WebResourceResponseMsg::DoNotIntercept)
1225        ));
1226        assert!(matches!(receiver.try_recv(), Err(_)));
1227        assert!(errors.try_recv().is_none());
1228
1229        // Explicit intercept with explicit cancel when receiver disconnected yields error
1230        let errors = ServoErrorChannel::default();
1231        let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
1232        let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
1233        drop(receiver);
1234        request.intercept(web_resource_response()).cancel();
1235        assert!(errors.try_recv().is_some());
1236
1237        // Explicit intercept with no further action when receiver disconnected yields error
1238        let errors = ServoErrorChannel::default();
1239        let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
1240        let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
1241        drop(receiver);
1242        drop(request.intercept(web_resource_response()));
1243        assert!(errors.try_recv().is_some());
1244
1245        // No response when receiver disconnected yields no error
1246        let errors = ServoErrorChannel::default();
1247        let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
1248        let request = WebResourceLoad::new(web_resource_request(), sender, errors.sender());
1249        drop(receiver);
1250        drop(request);
1251        assert!(errors.try_recv().is_none());
1252    }
1253}