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