mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Embedder Prompt API
This commit is contained in:
parent
5f55cd5d71
commit
51f15a055e
17 changed files with 447 additions and 110 deletions
|
@ -106,6 +106,37 @@ impl EmbedderReceiver {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum PromptDefinition {
|
||||
/// Show a message.
|
||||
Alert(String, IpcSender<()>),
|
||||
/// Ask a Ok/Cancel question.
|
||||
OkCancel(String, IpcSender<PromptResult>),
|
||||
/// Ask a Yes/No question.
|
||||
YesNo(String, IpcSender<PromptResult>),
|
||||
/// Ask the user to enter text.
|
||||
Input(String, String, IpcSender<Option<String>>),
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum EmbedderMsg {
|
||||
/// A status message to be displayed by the browser chrome.
|
||||
|
@ -116,8 +147,8 @@ pub enum EmbedderMsg {
|
|||
MoveTo(DeviceIntPoint),
|
||||
/// Resize the window to size
|
||||
ResizeTo(DeviceIntSize),
|
||||
// Show an alert message.
|
||||
Alert(String, IpcSender<()>),
|
||||
/// Show dialog to user
|
||||
Prompt(PromptDefinition, PromptOrigin),
|
||||
/// Wether or not to allow a pipeline to load a url.
|
||||
AllowNavigationRequest(PipelineId, ServoUrl),
|
||||
/// Whether or not to allow script to open a new tab/browser
|
||||
|
@ -174,7 +205,7 @@ impl Debug for EmbedderMsg {
|
|||
EmbedderMsg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
|
||||
EmbedderMsg::MoveTo(..) => write!(f, "MoveTo"),
|
||||
EmbedderMsg::ResizeTo(..) => write!(f, "ResizeTo"),
|
||||
EmbedderMsg::Alert(..) => write!(f, "Alert"),
|
||||
EmbedderMsg::Prompt(..) => write!(f, "Prompt"),
|
||||
EmbedderMsg::AllowUnload(..) => write!(f, "AllowUnload"),
|
||||
EmbedderMsg::AllowNavigationRequest(..) => write!(f, "AllowNavigationRequest"),
|
||||
EmbedderMsg::Keyboard(..) => write!(f, "Keyboard"),
|
||||
|
|
|
@ -55,8 +55,8 @@
|
|||
// user prompts
|
||||
void alert(DOMString message);
|
||||
void alert();
|
||||
//boolean confirm(optional DOMString message = "");
|
||||
//DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
|
||||
boolean confirm(optional DOMString message = "");
|
||||
DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
|
||||
//void print();
|
||||
//any showModalDialog(DOMString url, optional any argument);
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ use crossbeam_channel::{unbounded, Sender, TryRecvError};
|
|||
use cssparser::{Parser, ParserInput, SourceLocation};
|
||||
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::{EmbedderMsg, EventLoopWaker};
|
||||
use embedder_traits::{EmbedderMsg, EventLoopWaker, PromptDefinition, PromptOrigin, PromptResult};
|
||||
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
|
||||
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
|
||||
use ipc_channel::ipc::{channel, IpcSender};
|
||||
|
@ -620,11 +620,32 @@ impl WindowMethods for Window {
|
|||
}
|
||||
let (sender, receiver) =
|
||||
ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
|
||||
let msg = EmbedderMsg::Alert(s.to_string(), sender);
|
||||
let prompt = PromptDefinition::Alert(s.to_string(), sender);
|
||||
let msg = EmbedderMsg::Prompt(prompt, PromptOrigin::Untrusted);
|
||||
self.send_to_embedder(msg);
|
||||
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(prompt, PromptOrigin::Untrusted);
|
||||
self.send_to_embedder(msg);
|
||||
receiver.recv().unwrap() == PromptResult::Primary
|
||||
}
|
||||
|
||||
// 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(prompt, PromptOrigin::Untrusted);
|
||||
self.send_to_embedder(msg);
|
||||
receiver.recv().unwrap().map(|s| s.into())
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-window-stop
|
||||
fn Stop(&self) {
|
||||
// TODO: Cancel ongoing navigation.
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::window_trait::{WindowPortsMethods, LINE_HEIGHT};
|
|||
use euclid::{Point2D, Vector2D};
|
||||
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
|
||||
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
|
||||
use servo::embedder_traits::{EmbedderMsg, FilterPattern};
|
||||
use servo::embedder_traits::{EmbedderMsg, FilterPattern, PromptDefinition, PromptOrigin, PromptResult};
|
||||
use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
|
||||
use servo::msg::constellation_msg::TraversalDirection;
|
||||
use servo::net_traits::pub_domains::is_reg_domain;
|
||||
|
@ -24,7 +24,7 @@ use std::mem;
|
|||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tinyfiledialogs::{self, MessageBoxIcon};
|
||||
use tinyfiledialogs::{self, MessageBoxIcon, OkCancel, YesNo};
|
||||
|
||||
pub struct Browser<Window: WindowPortsMethods + ?Sized> {
|
||||
current_url: Option<ServoUrl>,
|
||||
|
@ -299,23 +299,78 @@ where
|
|||
EmbedderMsg::ResizeTo(size) => {
|
||||
self.window.set_inner_size(size);
|
||||
},
|
||||
EmbedderMsg::Alert(message, sender) => {
|
||||
if !opts::get().headless {
|
||||
let _ = thread::Builder::new()
|
||||
EmbedderMsg::Prompt(definition, origin) => {
|
||||
let res = if opts::get().headless {
|
||||
match definition {
|
||||
PromptDefinition::Alert(_message, sender) => {
|
||||
sender.send(())
|
||||
}
|
||||
PromptDefinition::YesNo(_message, sender) => {
|
||||
sender.send(PromptResult::Primary)
|
||||
}
|
||||
PromptDefinition::OkCancel(_message, sender) => {
|
||||
sender.send(PromptResult::Primary)
|
||||
}
|
||||
PromptDefinition::Input(_message, default, sender) => {
|
||||
sender.send(Some(default.to_owned()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
thread::Builder::new()
|
||||
.name("display alert dialog".to_owned())
|
||||
.spawn(move || {
|
||||
tinyfiledialogs::message_box_ok(
|
||||
"Alert!",
|
||||
&tiny_dialog_escape(&message),
|
||||
MessageBoxIcon::Warning,
|
||||
);
|
||||
match definition {
|
||||
PromptDefinition::Alert(mut message, sender) => {
|
||||
if origin == PromptOrigin::Untrusted {
|
||||
message = tiny_dialog_escape(&message);
|
||||
}
|
||||
tinyfiledialogs::message_box_ok(
|
||||
"Alert!",
|
||||
&message,
|
||||
MessageBoxIcon::Warning,
|
||||
);
|
||||
sender.send(())
|
||||
}
|
||||
PromptDefinition::YesNo(mut message, sender) => {
|
||||
if origin == PromptOrigin::Untrusted {
|
||||
message = tiny_dialog_escape(&message);
|
||||
}
|
||||
let result = tinyfiledialogs::message_box_yes_no(
|
||||
"", &message, MessageBoxIcon::Warning, YesNo::No,
|
||||
);
|
||||
sender.send(match result {
|
||||
YesNo::Yes => PromptResult::Primary,
|
||||
YesNo::No => PromptResult::Secondary,
|
||||
})
|
||||
}
|
||||
PromptDefinition::OkCancel(mut message, sender) => {
|
||||
if origin == PromptOrigin::Untrusted {
|
||||
message = tiny_dialog_escape(&message);
|
||||
}
|
||||
let result = tinyfiledialogs::message_box_ok_cancel(
|
||||
"", &message, MessageBoxIcon::Warning, OkCancel::Cancel,
|
||||
);
|
||||
sender.send(match result {
|
||||
OkCancel::Ok => PromptResult::Primary,
|
||||
OkCancel::Cancel => PromptResult::Secondary,
|
||||
})
|
||||
}
|
||||
PromptDefinition::Input(mut message, mut default, sender) => {
|
||||
if origin == PromptOrigin::Untrusted {
|
||||
message = tiny_dialog_escape(&message);
|
||||
default = tiny_dialog_escape(&default);
|
||||
}
|
||||
let result = tinyfiledialogs::input_box("", &message, &default);
|
||||
sender.send(result)
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
.expect("Thread spawning failed");
|
||||
}
|
||||
if let Err(e) = sender.send(()) {
|
||||
let reason = format!("Failed to send Alert response: {}", e);
|
||||
.expect("Thread spawning failed")
|
||||
};
|
||||
if let Err(e) = res {
|
||||
let reason = format!("Failed to send Prompt response: {}", e);
|
||||
self.event_queue
|
||||
.push(WindowEvent::SendError(browser_id, reason));
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@ use servo::keyboard_types::Key;
|
|||
use servo::servo_url::ServoUrl;
|
||||
use servo::webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel};
|
||||
use simpleservo::{self, deinit, gl_glue, MouseButton, ServoGlue, SERVO};
|
||||
use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOptions};
|
||||
use simpleservo::{
|
||||
Coordinates, EventLoopWaker, HostTrait, InitOptions, PromptResult, VRInitOptions,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::Cell;
|
||||
use std::ffi::CStr;
|
||||
|
@ -365,7 +367,25 @@ impl HostTrait for HostCallbacks {
|
|||
MakeCurrent(self.disp, self.surf, self.surf, self.ctxt);
|
||||
}
|
||||
|
||||
fn on_alert(&self, _message: String) {}
|
||||
fn prompt_alert(&self, message: String, _trusted: bool) {
|
||||
warn!("Prompt Alert: {}", message);
|
||||
}
|
||||
|
||||
fn prompt_ok_cancel(&self, message: String, _trusted: bool) -> PromptResult {
|
||||
warn!("Prompt not implemented. Cancelled. {}", message);
|
||||
PromptResult::Secondary
|
||||
}
|
||||
|
||||
fn prompt_yes_no(&self, message: String, _trusted: bool) -> PromptResult {
|
||||
warn!("Prompt not implemented. Cancelled. {}", message);
|
||||
PromptResult::Secondary
|
||||
}
|
||||
|
||||
fn prompt_input(&self, message: String, default: String, _trusted: bool) -> Option<String> {
|
||||
warn!("Input prompt not implemented. {}", message);
|
||||
Some(default)
|
||||
}
|
||||
|
||||
fn on_load_started(&self) {}
|
||||
fn on_load_ended(&self) {}
|
||||
fn on_title_changed(&self, _title: String) {}
|
||||
|
|
|
@ -7,7 +7,7 @@ extern crate log;
|
|||
|
||||
pub mod gl_glue;
|
||||
|
||||
pub use servo::embedder_traits::MediaSessionPlaybackState;
|
||||
pub use servo::embedder_traits::{MediaSessionPlaybackState, PromptResult};
|
||||
pub use servo::script_traits::{MediaSessionActionType, MouseButton};
|
||||
|
||||
use getopts::Options;
|
||||
|
@ -16,7 +16,7 @@ use servo::compositing::windowing::{
|
|||
WindowMethods,
|
||||
};
|
||||
use servo::embedder_traits::resources::{self, Resource, ResourceReaderMethods};
|
||||
use servo::embedder_traits::{EmbedderMsg, MediaSessionEvent};
|
||||
use servo::embedder_traits::{EmbedderMsg, MediaSessionEvent, PromptDefinition, PromptOrigin};
|
||||
use servo::euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
|
||||
use servo::keyboard_types::{Key, KeyState, KeyboardEvent};
|
||||
use servo::msg::constellation_msg::TraversalDirection;
|
||||
|
@ -92,8 +92,14 @@ pub trait HostTrait {
|
|||
/// Will be called before drawing.
|
||||
/// Time to make the targetted GL context current.
|
||||
fn make_current(&self);
|
||||
/// javascript window.alert()
|
||||
fn on_alert(&self, msg: String);
|
||||
/// Show alert.
|
||||
fn prompt_alert(&self, msg: String, trusted: bool);
|
||||
/// Ask Yes/No question.
|
||||
fn prompt_yes_no(&self, msg: String, trusted: bool) -> PromptResult;
|
||||
/// Ask Ok/Cancel question.
|
||||
fn prompt_ok_cancel(&self, msg: String, trusted: bool) -> PromptResult;
|
||||
/// Ask for string
|
||||
fn prompt_input(&self, msg: String, default: String, trusted: bool) -> Option<String>;
|
||||
/// Page starts loading.
|
||||
/// "Reload button" should be disabled.
|
||||
/// "Stop button" should be enabled.
|
||||
|
@ -540,10 +546,27 @@ impl ServoGlue {
|
|||
EmbedderMsg::AllowUnload(sender) => {
|
||||
let _ = sender.send(true);
|
||||
},
|
||||
EmbedderMsg::Alert(message, sender) => {
|
||||
info!("Alert: {}", message);
|
||||
self.callbacks.host_callbacks.on_alert(message);
|
||||
let _ = sender.send(());
|
||||
EmbedderMsg::Prompt(definition, origin) => {
|
||||
let cb = &self.callbacks.host_callbacks;
|
||||
let trusted = origin == PromptOrigin::Trusted;
|
||||
let res = match definition {
|
||||
PromptDefinition::Alert(message, sender) => {
|
||||
sender.send(cb.prompt_alert(message, trusted))
|
||||
},
|
||||
PromptDefinition::OkCancel(message, sender) => {
|
||||
sender.send(cb.prompt_ok_cancel(message, trusted))
|
||||
},
|
||||
PromptDefinition::YesNo(message, sender) => {
|
||||
sender.send(cb.prompt_yes_no(message, trusted))
|
||||
},
|
||||
PromptDefinition::Input(message, default, sender) => {
|
||||
sender.send(cb.prompt_input(message, default, trusted))
|
||||
},
|
||||
};
|
||||
if let Err(e) = res {
|
||||
let reason = format!("Failed to send Prompt response: {}", e);
|
||||
self.events.push(WindowEvent::SendError(browser_id, reason));
|
||||
}
|
||||
},
|
||||
EmbedderMsg::AllowOpeningBrowser(response_chan) => {
|
||||
// Note: would be a place to handle pop-ups config.
|
||||
|
|
|
@ -18,7 +18,7 @@ use log::LevelFilter;
|
|||
use simpleservo::{self, gl_glue, ServoGlue, SERVO};
|
||||
use simpleservo::{
|
||||
Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionActionType,
|
||||
MediaSessionPlaybackState, MouseButton, VRInitOptions,
|
||||
MediaSessionPlaybackState, MouseButton, PromptResult, VRInitOptions,
|
||||
};
|
||||
use std::ffi::{CStr, CString};
|
||||
#[cfg(target_os = "windows")]
|
||||
|
@ -205,7 +205,6 @@ where
|
|||
pub struct CHostCallbacks {
|
||||
pub flush: extern "C" fn(),
|
||||
pub make_current: extern "C" fn(),
|
||||
pub on_alert: extern "C" fn(message: *const c_char),
|
||||
pub on_load_started: extern "C" fn(),
|
||||
pub on_load_ended: extern "C" fn(),
|
||||
pub on_title_changed: extern "C" fn(title: *const c_char),
|
||||
|
@ -222,6 +221,14 @@ pub struct CHostCallbacks {
|
|||
pub on_media_session_playback_state_change: extern "C" fn(state: CMediaSessionPlaybackState),
|
||||
pub on_media_session_set_position_state:
|
||||
extern "C" fn(duration: f64, position: f64, playback_rate: f64),
|
||||
pub prompt_alert: extern "C" fn(message: *const c_char, trusted: bool),
|
||||
pub prompt_ok_cancel: extern "C" fn(message: *const c_char, trusted: bool) -> CPromptResult,
|
||||
pub prompt_yes_no: extern "C" fn(message: *const c_char, trusted: bool) -> CPromptResult,
|
||||
pub prompt_input: extern "C" fn(
|
||||
message: *const c_char,
|
||||
default: *const c_char,
|
||||
trusted: bool,
|
||||
) -> *const c_char,
|
||||
}
|
||||
|
||||
/// Servo options
|
||||
|
@ -255,6 +262,23 @@ impl CMouseButton {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum CPromptResult {
|
||||
Dismissed,
|
||||
Primary,
|
||||
Secondary,
|
||||
}
|
||||
|
||||
impl CPromptResult {
|
||||
pub fn convert(&self) -> PromptResult {
|
||||
match self {
|
||||
CPromptResult::Primary => PromptResult::Primary,
|
||||
CPromptResult::Secondary => PromptResult::Secondary,
|
||||
CPromptResult::Dismissed => PromptResult::Dismissed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum CMediaSessionPlaybackState {
|
||||
None = 1,
|
||||
|
@ -698,12 +722,6 @@ impl HostTrait for HostCallbacks {
|
|||
(self.0.make_current)();
|
||||
}
|
||||
|
||||
fn on_alert(&self, message: String) {
|
||||
debug!("on_alert");
|
||||
let message = CString::new(message).expect("Can't create string");
|
||||
(self.0.on_alert)(message.as_ptr());
|
||||
}
|
||||
|
||||
fn on_load_started(&self) {
|
||||
debug!("on_load_started");
|
||||
(self.0.on_load_started)();
|
||||
|
@ -797,4 +815,35 @@ impl HostTrait for HostCallbacks {
|
|||
);
|
||||
(self.0.on_media_session_set_position_state)(duration, position, playback_rate);
|
||||
}
|
||||
|
||||
fn prompt_alert(&self, message: String, trusted: bool) {
|
||||
debug!("prompt_alert");
|
||||
let message = CString::new(message).expect("Can't create string");
|
||||
(self.0.prompt_alert)(message.as_ptr(), trusted);
|
||||
}
|
||||
|
||||
fn prompt_ok_cancel(&self, message: String, trusted: bool) -> PromptResult {
|
||||
debug!("prompt_ok_cancel");
|
||||
let message = CString::new(message).expect("Can't create string");
|
||||
(self.0.prompt_ok_cancel)(message.as_ptr(), trusted).convert()
|
||||
}
|
||||
|
||||
fn prompt_yes_no(&self, message: String, trusted: bool) -> PromptResult {
|
||||
debug!("prompt_yes_no");
|
||||
let message = CString::new(message).expect("Can't create string");
|
||||
(self.0.prompt_yes_no)(message.as_ptr(), trusted).convert()
|
||||
}
|
||||
|
||||
fn prompt_input(&self, message: String, default: String, trusted: bool) -> Option<String> {
|
||||
debug!("prompt_input");
|
||||
let message = CString::new(message).expect("Can't create string");
|
||||
let default = CString::new(default).expect("Can't create string");
|
||||
let raw_contents = (self.0.prompt_input)(message.as_ptr(), default.as_ptr(), trusted);
|
||||
if raw_contents.is_null() {
|
||||
return None;
|
||||
}
|
||||
let c_str = unsafe { CStr::from_ptr(raw_contents) };
|
||||
let contents_str = c_str.to_str().expect("Can't create str");
|
||||
Some(contents_str.to_owned())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ use libc::{dup2, pipe, read};
|
|||
use log::Level;
|
||||
use simpleservo::{self, gl_glue, ServoGlue, SERVO};
|
||||
use simpleservo::{
|
||||
Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionPlaybackState, VRInitOptions,
|
||||
Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionPlaybackState, PromptResult,
|
||||
VRInitOptions,
|
||||
};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr::{null, null_mut};
|
||||
|
@ -394,8 +395,8 @@ impl HostTrait for HostCallbacks {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
fn on_alert(&self, message: String) {
|
||||
debug!("on_alert");
|
||||
fn prompt_alert(&self, message: String, _trusted: bool) {
|
||||
debug!("prompt_alert");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
let s = match new_string(&env, &message) {
|
||||
Ok(s) => s,
|
||||
|
@ -411,6 +412,21 @@ impl HostTrait for HostCallbacks {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
fn prompt_ok_cancel(&self, message: String, _trusted: bool) -> PromptResult {
|
||||
warn!("Prompt not implemented. Cancelled. {}", message);
|
||||
PromptResult::Secondary
|
||||
}
|
||||
|
||||
fn prompt_yes_no(&self, message: String, _trusted: bool) -> PromptResult {
|
||||
warn!("Prompt not implemented. Cancelled. {}", message);
|
||||
PromptResult::Secondary
|
||||
}
|
||||
|
||||
fn prompt_input(&self, message: String, default: String, _trusted: bool) -> Option<String> {
|
||||
warn!("Input prompt not implemented. {}", message);
|
||||
Some(default)
|
||||
}
|
||||
|
||||
fn on_load_started(&self) {
|
||||
debug!("on_load_started");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
|
|
|
@ -13,10 +13,6 @@ void on_history_changed(bool back, bool forward) {
|
|||
|
||||
void on_shutdown_complete() { sServo->Delegate().OnServoShutdownComplete(); }
|
||||
|
||||
void on_alert(const char *message) {
|
||||
sServo->Delegate().OnServoAlert(char2hstring(message));
|
||||
}
|
||||
|
||||
void on_title_changed(const char *title) {
|
||||
sServo->Delegate().OnServoTitleChanged(char2hstring(title));
|
||||
}
|
||||
|
@ -67,6 +63,30 @@ void on_media_session_playback_state_change(
|
|||
return sServo->Delegate().OnServoMediaSessionPlaybackStateChange(state);
|
||||
}
|
||||
|
||||
void prompt_alert(const char *message, bool trusted) {
|
||||
sServo->Delegate().OnServoPromptAlert(char2hstring(message), trusted);
|
||||
}
|
||||
|
||||
Servo::PromptResult prompt_ok_cancel(const char *message, bool trusted) {
|
||||
return sServo->Delegate().OnServoPromptOkCancel(char2hstring(message),
|
||||
trusted);
|
||||
}
|
||||
|
||||
Servo::PromptResult prompt_yes_no(const char *message, bool trusted) {
|
||||
return sServo->Delegate().OnServoPromptYesNo(char2hstring(message), trusted);
|
||||
}
|
||||
|
||||
const char *prompt_input(const char *message, const char *default,
|
||||
bool trusted) {
|
||||
auto input = sServo->Delegate().OnServoPromptInput(
|
||||
char2hstring(message), char2hstring(default), trusted);
|
||||
if (input.has_value()) {
|
||||
return *hstring2char(*input);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
|
||||
float dpi, ServoDelegate &aDelegate)
|
||||
: mWindowHeight(height), mWindowWidth(width), mDelegate(aDelegate) {
|
||||
|
@ -109,7 +129,6 @@ Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
|
|||
capi::CHostCallbacks c;
|
||||
c.flush = &flush;
|
||||
c.make_current = &make_current;
|
||||
c.on_alert = &on_alert;
|
||||
c.on_load_started = &on_load_started;
|
||||
c.on_load_ended = &on_load_ended;
|
||||
c.on_title_changed = &on_title_changed;
|
||||
|
@ -124,6 +143,10 @@ Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height,
|
|||
c.on_media_session_metadata = &on_media_session_metadata;
|
||||
c.on_media_session_playback_state_change =
|
||||
&on_media_session_playback_state_change;
|
||||
c.prompt_alert = &prompt_alert;
|
||||
c.prompt_ok_cancel = &prompt_ok_cancel;
|
||||
c.prompt_yes_no = &prompt_yes_no;
|
||||
c.prompt_input = &prompt_input;
|
||||
|
||||
capi::register_panic_handler(&on_panic);
|
||||
|
||||
|
|
|
@ -19,29 +19,7 @@ extern "C" {
|
|||
hstring char2hstring(const char *);
|
||||
std::unique_ptr<char *> hstring2char(hstring);
|
||||
|
||||
class ServoDelegate {
|
||||
public:
|
||||
// Called from any thread
|
||||
virtual void WakeUp() = 0;
|
||||
// Called from GL thread
|
||||
virtual void OnServoLoadStarted() = 0;
|
||||
virtual void OnServoLoadEnded() = 0;
|
||||
virtual void OnServoHistoryChanged(bool, bool) = 0;
|
||||
virtual void OnServoShutdownComplete() = 0;
|
||||
virtual void OnServoTitleChanged(hstring) = 0;
|
||||
virtual void OnServoAlert(hstring) = 0;
|
||||
virtual void OnServoURLChanged(hstring) = 0;
|
||||
virtual bool OnServoAllowNavigation(hstring) = 0;
|
||||
virtual void OnServoAnimatingChanged(bool) = 0;
|
||||
virtual void OnServoIMEStateChanged(bool) = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0;
|
||||
virtual void OnServoMediaSessionPlaybackStateChange(int) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ServoDelegate(){};
|
||||
};
|
||||
class ServoDelegate;
|
||||
|
||||
class Servo {
|
||||
public:
|
||||
|
@ -50,6 +28,7 @@ public:
|
|||
ServoDelegate &Delegate() { return mDelegate; }
|
||||
|
||||
typedef capi::CMouseButton MouseButton;
|
||||
typedef capi::CPromptResult PromptResult;
|
||||
typedef capi::CMediaSessionActionType MediaSessionActionType;
|
||||
typedef capi::CMediaSessionPlaybackState MediaSessionPlaybackState;
|
||||
|
||||
|
@ -100,6 +79,33 @@ private:
|
|||
GLsizei mWindowHeight;
|
||||
};
|
||||
|
||||
class ServoDelegate {
|
||||
public:
|
||||
// Called from any thread
|
||||
virtual void WakeUp() = 0;
|
||||
// Called from GL thread
|
||||
virtual void OnServoLoadStarted() = 0;
|
||||
virtual void OnServoLoadEnded() = 0;
|
||||
virtual void OnServoHistoryChanged(bool, bool) = 0;
|
||||
virtual void OnServoShutdownComplete() = 0;
|
||||
virtual void OnServoTitleChanged(hstring) = 0;
|
||||
virtual void OnServoURLChanged(hstring) = 0;
|
||||
virtual bool OnServoAllowNavigation(hstring) = 0;
|
||||
virtual void OnServoAnimatingChanged(bool) = 0;
|
||||
virtual void OnServoIMEStateChanged(bool) = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0;
|
||||
virtual void OnServoMediaSessionPlaybackStateChange(int) = 0;
|
||||
virtual void OnServoPromptAlert(hstring, bool) = 0;
|
||||
virtual Servo::PromptResult OnServoPromptOkCancel(hstring, bool) = 0;
|
||||
virtual Servo::PromptResult OnServoPromptYesNo(hstring, bool) = 0;
|
||||
virtual std::optional<hstring> OnServoPromptInput(hstring, hstring, bool) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ServoDelegate(){};
|
||||
};
|
||||
|
||||
// This is sad. We need a static pointer to Servo because we use function
|
||||
// pointer as callback in Servo, and these functions need a way to get
|
||||
// the Servo instance. See https://github.com/servo/servo/issues/22967
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
using namespace std::placeholders;
|
||||
using namespace winrt::Windows::Graphics::Display;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Popups;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::System;
|
||||
|
@ -69,6 +70,8 @@ void ServoControl::OnLoaded(IInspectable const &, RoutedEventArgs const &) {
|
|||
Panel().SizeChanged(std::bind(&ServoControl::OnSurfaceResized, this, _1, _2));
|
||||
InitializeConditionVariable(&mGLCondVar);
|
||||
InitializeCriticalSection(&mGLLock);
|
||||
InitializeConditionVariable(&mDialogCondVar);
|
||||
InitializeCriticalSection(&mDialogLock);
|
||||
CreateRenderSurface();
|
||||
StartRenderLoop();
|
||||
}
|
||||
|
@ -285,7 +288,7 @@ void ServoControl::TryLoadUri(hstring input) {
|
|||
RunOnGLThread([=] {
|
||||
if (!mServo->LoadUri(input)) {
|
||||
RunOnUIThread([=] {
|
||||
Windows::UI::Popups::MessageDialog msg{L"URI not valid"};
|
||||
MessageDialog msg{L"URI not valid"};
|
||||
msg.ShowAsync();
|
||||
});
|
||||
}
|
||||
|
@ -389,20 +392,15 @@ void ServoControl::OnServoShutdownComplete() {
|
|||
LeaveCriticalSection(&mGLLock);
|
||||
}
|
||||
|
||||
void ServoControl::OnServoAlert(hstring message) {
|
||||
// FIXME: make this sync
|
||||
RunOnUIThread([=] {
|
||||
Windows::UI::Popups::MessageDialog msg{message};
|
||||
msg.ShowAsync();
|
||||
});
|
||||
}
|
||||
|
||||
void ServoControl::OnServoTitleChanged(hstring title) {
|
||||
RunOnUIThread([=] { mOnTitleChangedEvent(*this, title); });
|
||||
}
|
||||
|
||||
void ServoControl::OnServoURLChanged(hstring url) {
|
||||
RunOnUIThread([=] { mOnURLChangedEvent(*this, url); });
|
||||
RunOnUIThread([=] {
|
||||
mCurrentUrl = url;
|
||||
mOnURLChangedEvent(*this, url);
|
||||
});
|
||||
}
|
||||
|
||||
void ServoControl::Flush() {
|
||||
|
@ -448,6 +446,113 @@ void ServoControl::OnServoMediaSessionPlaybackStateChange(int state) {
|
|||
RunOnUIThread([=] { mOnMediaSessionPlaybackStateChangeEvent(*this, state); });
|
||||
}
|
||||
|
||||
std::tuple<Controls::ContentDialogResult, std::optional<hstring>>
|
||||
ServoControl::PromptSync(hstring title, hstring message, hstring primaryButton,
|
||||
std::optional<hstring> secondaryButton,
|
||||
std::optional<hstring> input) {
|
||||
|
||||
bool showing = true;
|
||||
Controls::ContentDialogResult retButton = Controls::ContentDialogResult::None;
|
||||
std::optional<hstring> retString = {};
|
||||
|
||||
EnterCriticalSection(&mDialogLock);
|
||||
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::High, [&] {
|
||||
auto dialog = Controls::ContentDialog();
|
||||
dialog.IsPrimaryButtonEnabled(true);
|
||||
dialog.PrimaryButtonText(primaryButton);
|
||||
|
||||
if (secondaryButton.has_value()) {
|
||||
dialog.IsPrimaryButtonEnabled(true);
|
||||
dialog.SecondaryButtonText(*secondaryButton);
|
||||
} else {
|
||||
dialog.IsPrimaryButtonEnabled(false);
|
||||
}
|
||||
|
||||
auto titleBlock = Controls::TextBlock();
|
||||
titleBlock.Text(title);
|
||||
|
||||
auto messageBlock = Controls::TextBlock();
|
||||
messageBlock.TextWrapping(TextWrapping::Wrap);
|
||||
messageBlock.Text(message);
|
||||
Controls::StackPanel stack = Controls::StackPanel();
|
||||
stack.Children().Append(titleBlock);
|
||||
stack.Children().Append(messageBlock);
|
||||
|
||||
dialog.Content(stack);
|
||||
|
||||
auto textbox = Controls::TextBox();
|
||||
textbox.KeyDown([=](auto sender, auto args) {
|
||||
if (args.Key() == Windows::System::VirtualKey::Enter) {
|
||||
dialog.Hide();
|
||||
}
|
||||
});
|
||||
if (input.has_value()) {
|
||||
textbox.Text(*input);
|
||||
stack.Children().Append(textbox);
|
||||
}
|
||||
|
||||
dialog.Closed([&, textbox](Controls::ContentDialog d, auto closed) {
|
||||
EnterCriticalSection(&mDialogLock);
|
||||
retButton = closed.Result();
|
||||
showing = false;
|
||||
if (retButton == Controls::ContentDialogResult::Primary &&
|
||||
input.has_value()) {
|
||||
retString = hstring(textbox.Text());
|
||||
}
|
||||
LeaveCriticalSection(&mDialogLock);
|
||||
WakeConditionVariable(&mDialogCondVar);
|
||||
});
|
||||
dialog.ShowAsync();
|
||||
});
|
||||
|
||||
while (showing) {
|
||||
SleepConditionVariableCS(&mDialogCondVar, &mDialogLock, INFINITE);
|
||||
}
|
||||
LeaveCriticalSection(&mDialogLock);
|
||||
|
||||
return {retButton, retString};
|
||||
}
|
||||
|
||||
void ServoControl::OnServoPromptAlert(winrt::hstring message, bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
PromptSync(title, message, L"OK", {}, {});
|
||||
}
|
||||
|
||||
servo::Servo::PromptResult
|
||||
ServoControl::OnServoPromptOkCancel(winrt::hstring message, bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
auto [button, string] = PromptSync(title, message, L"OK", L"Cancel", {});
|
||||
if (button == Controls::ContentDialogResult::Primary) {
|
||||
return servo::Servo::PromptResult::Primary;
|
||||
} else if (button == Controls::ContentDialogResult::Secondary) {
|
||||
return servo::Servo::PromptResult::Secondary;
|
||||
} else {
|
||||
return servo::Servo::PromptResult::Dismissed;
|
||||
}
|
||||
}
|
||||
|
||||
servo::Servo::PromptResult
|
||||
ServoControl::OnServoPromptYesNo(winrt::hstring message, bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
auto [button, string] = PromptSync(title, message, L"Yes", L"No", {});
|
||||
if (button == Controls::ContentDialogResult::Primary) {
|
||||
return servo::Servo::PromptResult::Primary;
|
||||
} else if (button == Controls::ContentDialogResult::Secondary) {
|
||||
return servo::Servo::PromptResult::Secondary;
|
||||
} else {
|
||||
return servo::Servo::PromptResult::Dismissed;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<hstring> ServoControl::OnServoPromptInput(winrt::hstring message,
|
||||
winrt::hstring default,
|
||||
bool trusted) {
|
||||
auto title = trusted ? L"" : mCurrentUrl + L" says:";
|
||||
auto [button, string] = PromptSync(title, message, L"Ok", L"Cancel", default);
|
||||
return string;
|
||||
}
|
||||
|
||||
template <typename Callable> void ServoControl::RunOnUIThread(Callable cb) {
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::High, cb);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,6 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
|
|||
virtual void OnServoHistoryChanged(bool, bool);
|
||||
virtual void OnServoShutdownComplete();
|
||||
virtual void OnServoTitleChanged(winrt::hstring);
|
||||
virtual void OnServoAlert(winrt::hstring);
|
||||
virtual void OnServoURLChanged(winrt::hstring);
|
||||
virtual void Flush();
|
||||
virtual void MakeCurrent();
|
||||
|
@ -108,6 +107,12 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
|
|||
virtual void OnServoMediaSessionMetadata(winrt::hstring, winrt::hstring,
|
||||
winrt::hstring);
|
||||
virtual void OnServoMediaSessionPlaybackStateChange(int);
|
||||
virtual void OnServoPromptAlert(winrt::hstring, bool);
|
||||
virtual servo::Servo::PromptResult OnServoPromptOkCancel(winrt::hstring,
|
||||
bool);
|
||||
virtual servo::Servo::PromptResult OnServoPromptYesNo(winrt::hstring, bool);
|
||||
virtual std::optional<hstring> OnServoPromptInput(winrt::hstring,
|
||||
winrt::hstring, bool);
|
||||
|
||||
private:
|
||||
winrt::event<Windows::Foundation::EventHandler<hstring>> mOnURLChangedEvent;
|
||||
|
@ -121,8 +126,18 @@ private:
|
|||
winrt::event<Windows::Foundation::EventHandler<int>>
|
||||
mOnMediaSessionPlaybackStateChangeEvent;
|
||||
|
||||
CRITICAL_SECTION mDialogLock;
|
||||
CONDITION_VARIABLE mDialogCondVar;
|
||||
|
||||
std::tuple<Windows::UI::Xaml::Controls::ContentDialogResult,
|
||||
std::optional<hstring>>
|
||||
PromptSync(hstring title, hstring message, hstring primaryButton,
|
||||
std::optional<hstring> secondaryButton,
|
||||
std::optional<hstring> input);
|
||||
|
||||
float mDPI = 1;
|
||||
hstring mInitialURL = DEFAULT_URL;
|
||||
hstring mCurrentUrl = L"";
|
||||
bool mTransient = false;
|
||||
|
||||
Windows::UI::Xaml::Controls::SwapChainPanel ServoControl::Panel();
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
[Window method: blur]
|
||||
expected: FAIL
|
||||
|
||||
[Window method: confirm]
|
||||
expected: FAIL
|
||||
|
||||
[Window method: prompt]
|
||||
expected: FAIL
|
||||
|
||||
[Window method: print]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1471,9 +1471,6 @@
|
|||
[Document interface: operation queryCommandEnabled(DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: window must inherit property "prompt(DOMString, DOMString)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: window must inherit property "onsecuritypolicyviolation" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1486,9 +1483,6 @@
|
|||
[Document interface: calling queryCommandSupported(DOMString) on new Document() with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: calling confirm(DOMString) on window with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: operation focus()]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1504,9 +1498,6 @@
|
|||
[Document interface: iframe.contentDocument must inherit property "linkColor" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: operation prompt(DOMString, DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: documentWithHandlers must inherit property "dir" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1525,9 +1516,6 @@
|
|||
[Document interface: iframe.contentDocument must inherit property "queryCommandValue(DOMString)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: window must inherit property "confirm(DOMString)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: operation queryCommandSupported(DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1657,9 +1645,6 @@
|
|||
[Window interface: window must inherit property "focus()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: calling prompt(DOMString, DOMString) on window with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: new Document() must inherit property "onauxclick" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1696,9 +1681,6 @@
|
|||
[Document interface: operation execCommand(DOMString, boolean, DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: operation confirm(DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: window must inherit property "toolbar" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[iframe_sandbox_block_modals-2.html]
|
||||
expected: TIMEOUT
|
||||
[Frames without `allow-modals` should not be able to open modal dialogs]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[iframe_sandbox_block_modals-3.html]
|
||||
expected: TIMEOUT
|
||||
[Frames without `allow-modals` should not be able to open modal dialogs]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[iframe_sandbox_popups_escaping-3.html]
|
||||
type: testharness
|
||||
expected: CRASH
|
||||
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue