Skip to main content

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