Delegate permission prompt dialog formatting to embedders

This commit is contained in:
Iulian Gabriel Radu 2020-02-28 11:44:33 +02:00
parent 675b36dde5
commit f75d547c61
3 changed files with 76 additions and 25 deletions

View file

@ -185,9 +185,8 @@ pub enum EmbedderMsg {
GetSelectedBluetoothDevice(Vec<String>, IpcSender<Option<String>>), GetSelectedBluetoothDevice(Vec<String>, IpcSender<Option<String>>),
/// Open file dialog to select files. Set boolean flag to true allows to select multiple files. /// Open file dialog to select files. Set boolean flag to true allows to select multiple files.
SelectFiles(Vec<FilterPattern>, bool, IpcSender<Option<Vec<String>>>), SelectFiles(Vec<FilterPattern>, bool, IpcSender<Option<Vec<String>>>),
/// Open yes/no message for user to allow permission specified by first String. /// Open interface to request permission specified by prompt.
/// With dialog title specified by second String. PromptPermission(PermissionPrompt, IpcSender<PermissionRequest>),
PromptPermission(String, String, IpcSender<PermissionRequest>),
/// Request to present an IME to the user when an editable element is focused. /// Request to present an IME to the user when an editable element is focused.
ShowIME(InputMethodType), ShowIME(InputMethodType),
/// Request to hide the IME when the editable element is blurred. /// Request to hide the IME when the editable element is blurred.
@ -304,7 +303,30 @@ pub enum MediaSessionEvent {
SetPositionState(MediaPositionState), SetPositionState(MediaPositionState),
} }
// Status for prompting user for permission. /// Enum with variants that match the DOM PermissionName enum
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum PermissionName {
Geolocation,
Notifications,
Push,
Midi,
Camera,
Microphone,
Speaker,
DeviceInfo,
BackgroundSync,
Bluetooth,
PersistentStorage,
}
/// Information required to display a permission prompt
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum PermissionPrompt {
Insecure(PermissionName),
Request(PermissionName),
}
/// Status for prompting user for permission.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub enum PermissionRequest { pub enum PermissionRequest {
Granted, Granted,

View file

@ -19,7 +19,7 @@ use crate::dom::promise::Promise;
use crate::realms::{AlreadyInRealm, InRealm}; use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::JSContext; use crate::script_runtime::JSContext;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::{EmbedderMsg, PermissionRequest}; use embedder_traits::{self, EmbedderMsg, PermissionPrompt, PermissionRequest};
use ipc_channel::ipc; use ipc_channel::ipc;
use js::conversions::ConversionResult; use js::conversions::ConversionResult;
use js::jsapi::JSObject; use js::jsapi::JSObject;
@ -27,11 +27,6 @@ use js::jsval::{ObjectValue, UndefinedValue};
use servo_config::pref; use servo_config::pref;
use std::rc::Rc; use std::rc::Rc;
const DIALOG_TITLE: &'static str = "Permission request dialog";
const NONSECURE_DIALOG_MESSAGE: &'static str = "feature is only safe to use in secure context,\
but servo can't guarantee\n that the current context is secure. Do you want to proceed and grant permission?";
const REQUEST_DIALOG_MESSAGE: &'static str = "Do you want to grant permission for";
pub trait PermissionAlgorithm { pub trait PermissionAlgorithm {
type Descriptor; type Descriptor;
type Status; type Status;
@ -256,10 +251,11 @@ impl PermissionAlgorithm for Permissions {
// Step 3. // Step 3.
PermissionState::Prompt => { PermissionState::Prompt => {
let perm_name = status.get_query(); let perm_name = status.get_query();
let prompt =
PermissionPrompt::Request(embedder_traits::PermissionName::from(perm_name));
// https://w3c.github.io/permissions/#request-permission-to-use (Step 3 - 4) // https://w3c.github.io/permissions/#request-permission-to-use (Step 3 - 4)
let globalscope = GlobalScope::current().expect("No current global object"); let globalscope = GlobalScope::current().expect("No current global object");
let prompt = format!("{} {} ?", REQUEST_DIALOG_MESSAGE, perm_name.clone());
let state = prompt_user_from_embedder(prompt, &globalscope); let state = prompt_user_from_embedder(prompt, &globalscope);
globalscope globalscope
.permission_state_invocation_results() .permission_state_invocation_results()
@ -305,8 +301,10 @@ pub fn get_descriptor_permission_state(
.borrow_mut() .borrow_mut()
.remove(&permission_name.to_string()); .remove(&permission_name.to_string());
let prompt = format!("The {} {}", permission_name, NONSECURE_DIALOG_MESSAGE); prompt_user_from_embedder(
prompt_user_from_embedder(prompt, &globalscope) PermissionPrompt::Insecure(embedder_traits::PermissionName::from(permission_name)),
&globalscope,
)
} }
}; };
@ -357,13 +355,9 @@ fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool {
} }
} }
fn prompt_user_from_embedder(prompt: String, gs: &GlobalScope) -> PermissionState { fn prompt_user_from_embedder(prompt: PermissionPrompt, gs: &GlobalScope) -> PermissionState {
let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!"); let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
gs.send_to_embedder(EmbedderMsg::PromptPermission( gs.send_to_embedder(EmbedderMsg::PromptPermission(prompt, sender));
prompt,
DIALOG_TITLE.to_string(),
sender,
));
match receiver.recv() { match receiver.recv() {
Ok(PermissionRequest::Granted) => PermissionState::Granted, Ok(PermissionRequest::Granted) => PermissionState::Granted,
@ -377,3 +371,23 @@ fn prompt_user_from_embedder(prompt: String, gs: &GlobalScope) -> PermissionStat
}, },
} }
} }
impl From<PermissionName> for embedder_traits::PermissionName {
fn from(permission_name: PermissionName) -> Self {
match permission_name {
PermissionName::Geolocation => embedder_traits::PermissionName::Geolocation,
PermissionName::Notifications => embedder_traits::PermissionName::Notifications,
PermissionName::Push => embedder_traits::PermissionName::Push,
PermissionName::Midi => embedder_traits::PermissionName::Midi,
PermissionName::Camera => embedder_traits::PermissionName::Camera,
PermissionName::Microphone => embedder_traits::PermissionName::Microphone,
PermissionName::Speaker => embedder_traits::PermissionName::Speaker,
PermissionName::Device_info => embedder_traits::PermissionName::DeviceInfo,
PermissionName::Background_sync => embedder_traits::PermissionName::BackgroundSync,
PermissionName::Bluetooth => embedder_traits::PermissionName::Bluetooth,
PermissionName::Persistent_storage => {
embedder_traits::PermissionName::PersistentStorage
},
}
}
}

View file

@ -9,6 +9,7 @@ use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent}; use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
use servo::embedder_traits::{ use servo::embedder_traits::{
EmbedderMsg, FilterPattern, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, EmbedderMsg, FilterPattern, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult,
PermissionPrompt,
}; };
use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId; use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
use servo::msg::constellation_msg::TraversalDirection; use servo::msg::constellation_msg::TraversalDirection;
@ -493,8 +494,8 @@ where
self.event_queue.push(WindowEvent::SendError(None, reason)); self.event_queue.push(WindowEvent::SendError(None, reason));
}; };
}, },
EmbedderMsg::PromptPermission(message, dialog_title, sender) => { EmbedderMsg::PromptPermission(prompt, sender) => {
let permission_state = prompt_user(&message, &dialog_title); let permission_state = prompt_user(prompt);
let _ = sender.send(permission_state); let _ = sender.send(permission_state);
} }
EmbedderMsg::ShowIME(_kind) => { EmbedderMsg::ShowIME(_kind) => {
@ -520,13 +521,27 @@ where
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn prompt_user(prompt: &str, dialog_title: &str) -> PermissionRequest { fn prompt_user(prompt: PermissionPrompt) -> PermissionRequest {
if opts::get().headless { if opts::get().headless {
return PermissionRequest::Denied; return PermissionRequest::Denied;
} }
let message = match prompt {
PermissionPrompt::Request(permission_name) => {
format!("Do you want to grant permission for {:?}?", permission_name)
},
PermissionPrompt::Insecure(permission_name) => {
format!(
"The {:?} feature is only safe to use in secure context, but servo can't guarantee\n\
that the current context is secure. Do you want to proceed and grant permission?",
permission_name
)
},
};
match tinyfiledialogs::message_box_yes_no( match tinyfiledialogs::message_box_yes_no(
dialog_title, "Permission request dialog",
prompt, &message,
MessageBoxIcon::Question, MessageBoxIcon::Question,
YesNo::No, YesNo::No,
) { ) {
@ -536,7 +551,7 @@ fn prompt_user(prompt: &str, dialog_title: &str) -> PermissionRequest {
} }
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
fn prompt_user(_prompt: &str, _dialog_title: &str) -> PermissionRequest { fn prompt_user(_prompt: PermissionPrompt) -> PermissionRequest {
// TODO popup only supported on linux // TODO popup only supported on linux
PermissionRequest::Denied PermissionRequest::Denied
} }