Embedder Prompt API

This commit is contained in:
Paul Rouget 2019-12-09 10:26:47 +01:00
parent 5f55cd5d71
commit 51f15a055e
17 changed files with 447 additions and 110 deletions

View file

@ -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));
}

View file

@ -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) {}

View file

@ -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.

View file

@ -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())
}
}

View file

@ -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();