libservo: Clean up interfaces for alert()/confirm()/prompt() (#35579)

Signed-off-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
Delan Azabani 2025-02-27 10:49:08 +08:00 committed by GitHub
parent 03e953e22c
commit 276f6a3ba7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 278 additions and 222 deletions

View file

@ -201,7 +201,7 @@ mod from_script {
Self::ChangePageTitle(..) => target_variant!("ChangePageTitle"),
Self::MoveTo(..) => target_variant!("MoveTo"),
Self::ResizeTo(..) => target_variant!("ResizeTo"),
Self::Prompt(..) => target_variant!("Prompt"),
Self::ShowSimpleDialog(..) => target_variant!("ShowSimpleDialog"),
Self::RequestAuthentication(..) => target_variant!("RequestAuthentication"),
Self::ShowContextMenu(..) => target_variant!("ShowContextMenu"),
Self::AllowNavigationRequest(..) => target_variant!("AllowNavigationRequest"),

View file

@ -26,8 +26,8 @@ use cssparser::{Parser, ParserInput, SourceLocation};
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
use dom_struct::dom_struct;
use embedder_traits::{
EmbedderMsg, PromptDefinition, PromptOrigin, PromptResult, Theme, WebDriverJSError,
WebDriverJSResult,
AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, SimpleDialog, Theme,
WebDriverJSError, WebDriverJSResult,
};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
@ -736,30 +736,43 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
}
let (sender, receiver) =
ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
let prompt = PromptDefinition::Alert(s.to_string(), sender);
let msg = EmbedderMsg::Prompt(self.webview_id(), prompt, PromptOrigin::Untrusted);
let dialog = SimpleDialog::Alert {
message: s.to_string(),
response_sender: sender,
};
let msg = EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog);
self.send_to_embedder(msg);
receiver.recv().unwrap();
let AlertResponse::Ok = receiver.recv().unwrap();
}
// https://html.spec.whatwg.org/multipage/#dom-confirm
fn Confirm(&self, s: DOMString) -> bool {
let (sender, receiver) =
ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
let prompt = PromptDefinition::OkCancel(s.to_string(), sender);
let msg = EmbedderMsg::Prompt(self.webview_id(), prompt, PromptOrigin::Untrusted);
let dialog = SimpleDialog::Confirm {
message: s.to_string(),
response_sender: sender,
};
let msg = EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog);
self.send_to_embedder(msg);
receiver.recv().unwrap() == PromptResult::Primary
receiver.recv().unwrap() == ConfirmResponse::Ok
}
// https://html.spec.whatwg.org/multipage/#dom-prompt
fn Prompt(&self, message: DOMString, default: DOMString) -> Option<DOMString> {
let (sender, receiver) =
ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
let prompt = PromptDefinition::Input(message.to_string(), default.to_string(), sender);
let msg = EmbedderMsg::Prompt(self.webview_id(), prompt, PromptOrigin::Untrusted);
let dialog = SimpleDialog::Prompt {
message: message.to_string(),
default: default.to_string(),
response_sender: sender,
};
let msg = EmbedderMsg::ShowSimpleDialog(self.webview_id(), dialog);
self.send_to_embedder(msg);
receiver.recv().unwrap().map(|s| s.into())
match receiver.recv().unwrap() {
PromptResponse::Ok(input) => Some(input.into()),
PromptResponse::Cancel => None,
}
}
// https://html.spec.whatwg.org/multipage/#dom-window-stop

View file

@ -708,11 +708,11 @@ impl Servo {
webview.delegate().request_resize_to(webview, size);
}
},
EmbedderMsg::Prompt(webview_id, prompt_definition, prompt_origin) => {
EmbedderMsg::ShowSimpleDialog(webview_id, prompt_definition) => {
if let Some(webview) = self.get_webview_handle(webview_id) {
webview
.delegate()
.show_prompt(webview, prompt_definition, prompt_origin);
.show_simple_dialog(webview, prompt_definition);
}
},
EmbedderMsg::ShowContextMenu(webview_id, ipc_sender, title, items) => {

View file

@ -9,8 +9,7 @@ use compositing_traits::ConstellationMsg;
use embedder_traits::{
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, PermissionFeature,
PromptDefinition, PromptOrigin, WebResourceRequest, WebResourceResponse,
WebResourceResponseMsg,
SimpleDialog, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
};
use ipc_channel::ipc::IpcSender;
use keyboard_types::KeyboardEvent;
@ -338,17 +337,25 @@ pub trait WebViewDelegate {
) {
}
/// Show dialog to user
/// Show the user a [simple dialog](https://html.spec.whatwg.org/multipage/#simple-dialogs) (`alert()`, `confirm()`,
/// or `prompt()`). Since their messages are controlled by web content, they should be presented to the user in a
/// way that makes them impossible to mistake for browser UI.
/// TODO: This API needs to be reworked to match the new model of how responses are sent.
fn show_prompt(&self, _webview: WebView, prompt: PromptDefinition, _: PromptOrigin) {
let _ = match prompt {
PromptDefinition::Alert(_, response_sender) => response_sender.send(()),
PromptDefinition::OkCancel(_, response_sender) => {
response_sender.send(embedder_traits::PromptResult::Dismissed)
},
PromptDefinition::Input(_, _, response_sender) => response_sender.send(None),
fn show_simple_dialog(&self, _webview: WebView, dialog: SimpleDialog) {
// Return the DOM-specified default value for when we **cannot show simple dialogs**.
let _ = match dialog {
SimpleDialog::Alert {
response_sender, ..
} => response_sender.send(Default::default()),
SimpleDialog::Confirm {
response_sender, ..
} => response_sender.send(Default::default()),
SimpleDialog::Prompt {
response_sender, ..
} => response_sender.send(Default::default()),
};
}
/// Show a context menu to the user
fn show_context_menu(
&self,

View file

@ -123,14 +123,30 @@ pub enum ContextMenuResult {
Selected(usize),
}
/// [Simple dialogs](https://html.spec.whatwg.org/multipage/#simple-dialogs) are synchronous dialogs
/// that can be opened by web content. Since their messages are controlled by web content, they
/// should be presented to the user in a way that makes them impossible to mistake for browser UI.
#[derive(Deserialize, Serialize)]
pub enum PromptDefinition {
/// Show a message.
Alert(String, IpcSender<()>),
/// Ask a Ok/Cancel question.
OkCancel(String, IpcSender<PromptResult>),
/// Ask the user to enter text.
Input(String, String, IpcSender<Option<String>>),
pub enum SimpleDialog {
/// [`alert()`](https://html.spec.whatwg.org/multipage/#dom-alert).
/// TODO: Include details about the document origin.
Alert {
message: String,
response_sender: IpcSender<AlertResponse>,
},
/// [`confirm()`](https://html.spec.whatwg.org/multipage/#dom-confirm).
/// TODO: Include details about the document origin.
Confirm {
message: String,
response_sender: IpcSender<ConfirmResponse>,
},
/// [`prompt()`](https://html.spec.whatwg.org/multipage/#dom-prompt).
/// TODO: Include details about the document origin.
Prompt {
message: String,
default: String,
response_sender: IpcSender<PromptResponse>,
},
}
#[derive(Debug, Default, Deserialize, Serialize)]
@ -142,22 +158,52 @@ pub struct AuthenticationResponse {
}
#[derive(Deserialize, PartialEq, Serialize)]
pub enum PromptOrigin {
/// Prompt is triggered from content (window.prompt/alert/confirm/…).
/// Prompt message is unknown.
Untrusted,
/// Prompt is triggered from Servo (ask for permission, show error,…).
Trusted,
pub enum AlertResponse {
/// The user chose Ok, or the dialog was otherwise dismissed or ignored.
Ok,
}
impl Default for AlertResponse {
fn default() -> Self {
// Per <https://html.spec.whatwg.org/multipage/#dom-alert>,
// if we **cannot show simple dialogs**, including cases where the user or user agent decides to ignore
// all modal dialogs, we need to return (which represents Ok).
Self::Ok
}
}
#[derive(Deserialize, PartialEq, Serialize)]
pub enum PromptResult {
/// Prompt was closed by clicking on the primary button (ok/yes)
Primary,
/// Prompt was closed by clicking on the secondary button (cancel/no)
Secondary,
/// Prompt was dismissed
Dismissed,
pub enum ConfirmResponse {
/// The user chose Ok.
Ok,
/// The user chose Cancel, or the dialog was otherwise dismissed or ignored.
Cancel,
}
impl Default for ConfirmResponse {
fn default() -> Self {
// Per <https://html.spec.whatwg.org/multipage/#dom-confirm>,
// if we **cannot show simple dialogs**, including cases where the user or user agent decides to ignore
// all modal dialogs, we need to return false (which represents Cancel), not true (Ok).
Self::Cancel
}
}
#[derive(Deserialize, PartialEq, Serialize)]
pub enum PromptResponse {
/// The user chose Ok, with the given input.
Ok(String),
/// The user chose Cancel, or the dialog was otherwise dismissed or ignored.
Cancel,
}
impl Default for PromptResponse {
fn default() -> Self {
// Per <https://html.spec.whatwg.org/multipage/#dom-prompt>,
// if we **cannot show simple dialogs**, including cases where the user or user agent decides to ignore
// all modal dialogs, we need to return null (which represents Cancel), not the default input.
Self::Cancel
}
}
/// A response to a request to allow or deny an action.
@ -177,8 +223,10 @@ pub enum EmbedderMsg {
MoveTo(WebViewId, DeviceIntPoint),
/// Resize the window to size
ResizeTo(WebViewId, DeviceIntSize),
/// Show dialog to user
Prompt(WebViewId, PromptDefinition, PromptOrigin),
/// Show the user a [simple dialog](https://html.spec.whatwg.org/multipage/#simple-dialogs) (`alert()`, `confirm()`,
/// or `prompt()`). Since their messages are controlled by web content, they should be presented to the user in a
/// way that makes them impossible to mistake for browser UI.
ShowSimpleDialog(WebViewId, SimpleDialog),
/// Request authentication for a load or navigation from the embedder.
RequestAuthentication(
WebViewId,
@ -280,7 +328,7 @@ impl Debug for EmbedderMsg {
EmbedderMsg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
EmbedderMsg::MoveTo(..) => write!(f, "MoveTo"),
EmbedderMsg::ResizeTo(..) => write!(f, "ResizeTo"),
EmbedderMsg::Prompt(..) => write!(f, "Prompt"),
EmbedderMsg::ShowSimpleDialog(..) => write!(f, "ShowSimpleDialog"),
EmbedderMsg::RequestAuthentication(..) => write!(f, "RequestAuthentication"),
EmbedderMsg::AllowUnload(..) => write!(f, "AllowUnload"),
EmbedderMsg::AllowNavigationRequest(..) => write!(f, "AllowNavigationRequest"),