Skip to content

Commit

Permalink
feat(dialog): add HTTP basic authentication (#263)
Browse files Browse the repository at this point in the history
* feat(dialog): add HTTP basic authentication

* fix(prompt): pipeline not refresh after prompt removed when navigation
  • Loading branch information
pewsheen authored Jan 7, 2025
1 parent 0415a3c commit da288df
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 8 deletions.
79 changes: 79 additions & 0 deletions resources/components/prompt/http_basic_auth.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<html>
<head>
<link
rel="stylesheet"
type="text/css"
href="verso://resources/components/prompt/prompt.css"
/>
<style>
.field {
padding-bottom: 4px;
}
</style>
</head>
<body>
<div class="dialog">
<div class="msg">
<p id="msg"></p>
<div class="field">
<input
type="text"
id="username"
placeholder="Username"
aria-label="username"
/>
</div>
<div class="field">
<input
type="password"
id="password"
placeholder="Password"
aria-label="password"
/>
</div>
</div>
<div class="btn-group">
<button onclick="sendToVersoAndClose('cancel')">Cancel</button>
<button onclick="sendToVersoAndClose('signin')">Sign In</button>
</div>
</div>
</body>
<script>
const msgEl = document.getElementById('msg');
const usernameEl = document.getElementById('username');
const passwordEl = document.getElementById('password');

const params = URL.parse(window.location.href).searchParams;

// Set dialog message
msgEl.textContent = 'Sign in';
// TODO: add target host and check if it's secure
// const host = params.get('host');
// if (host) {
// msgEl.textContent = `Sign in to ${host}`;
// } else {
// msgEl.textContent = 'Sign in';
// }

function sendToVersoAndClose(action) {
const auth = {
username: null,
password: null,
};

if (action === 'signin') {
auth.username = usernameEl.value;
auth.password = passwordEl.value;
}

// Use as an IPC between Verso and WebView
window.alert(
JSON.stringify({
action,
auth,
})
);
window.close();
}
</script>
</html>
51 changes: 50 additions & 1 deletion src/webview/prompt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use base::id::WebViewId;
use compositing_traits::ConstellationMsg;
use crossbeam_channel::Sender;
use embedder_traits::{PermissionRequest, PromptResult};
use embedder_traits::{PermissionRequest, PromptCredentialsInput, PromptResult};
use ipc_channel::ipc::IpcSender;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
Expand All @@ -28,6 +28,10 @@ enum PromptType {
///
/// <https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt>
Input(String, Option<String>),
/// HTTP basic authentication dialog (username / password)
///
/// <https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic>
HttpBasicAuth,
}

/// Prompt Sender, used to send prompt result back to the caller
Expand All @@ -41,6 +45,8 @@ pub enum PromptSender {
InputSender(IpcSender<Option<String>>),
/// Yes/No Permission sender
PermissionSender(IpcSender<PermissionRequest>),
/// HTTP basic authentication sender
HttpBasicAuthSender(IpcSender<PromptCredentialsInput>),
}

/// Prompt input result send from prompt dialog to backend
Expand All @@ -60,6 +66,21 @@ pub struct PromptInputResult {
pub value: String,
}

/// Prompt input result send from prompt dialog to backend
/// - action: "signin" / "cancel"
/// - auth: { username: string, password: string }
///
/// Behavior:
/// - **signin**: return { username: string, password: string }
/// - **cancel**: return { username: null, password: null }
#[derive(Debug, Serialize, Deserialize)]
pub struct HttpBasicAuthInputResult {
/// User action: "signin" / "cancel"
pub action: String,
/// User input value
pub auth: PromptCredentialsInput,
}

/// Prompt Dialog
#[derive(Clone)]
pub struct PromptDialog {
Expand Down Expand Up @@ -196,6 +217,31 @@ impl PromptDialog {
self.show(sender, rect, PromptType::Input(message, default_value));
}

/// Show input prompt
///
/// After you call `input(..)`, you must call `sender()` to get prompt sender,
/// then send user interaction result back to caller.
///
/// ## Example
///
/// ```rust
/// if let Some(PromptSender::HttpBasicAuthSender(sender)) = prompt.sender() {
/// let _ = sender.send(PromptCredentialsInput {
/// username: "user".to_string(),
/// password: "password".to_string(),
/// });
/// }
/// ```
pub fn http_basic_auth(
&mut self,
sender: &Sender<ConstellationMsg>,
rect: DeviceIntRect,
prompt_sender: IpcSender<PromptCredentialsInput>,
) {
self.prompt_sender = Some(PromptSender::HttpBasicAuthSender(prompt_sender));
self.show(sender, rect, PromptType::HttpBasicAuth);
}

fn show(
&mut self,
sender: &Sender<ConstellationMsg>,
Expand Down Expand Up @@ -227,6 +273,9 @@ impl PromptDialog {
}
url
}
PromptType::HttpBasicAuth => {
format!("verso://resources/components/prompt/http_basic_auth.html")
}
};
ServoUrl::parse(&url).unwrap()
}
Expand Down
39 changes: 33 additions & 6 deletions src/webview/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use base::id::{BrowsingContextId, WebViewId};
use compositing_traits::ConstellationMsg;
use crossbeam_channel::Sender;
use embedder_traits::{
CompositorEventVariant, EmbedderMsg, PermissionPrompt, PermissionRequest, PromptDefinition,
PromptResult,
CompositorEventVariant, EmbedderMsg, PermissionPrompt, PermissionRequest,
PromptCredentialsInput, PromptDefinition, PromptResult,
};
use ipc_channel::ipc;
use script_traits::{
Expand All @@ -19,7 +19,7 @@ use crate::{
compositor::IOCompositor,
tab::{TabActivateRequest, TabCloseRequest, TabCreateResponse},
verso::send_to_constellation,
webview::prompt::{PromptDialog, PromptInputResult, PromptSender},
webview::prompt::{HttpBasicAuthInputResult, PromptDialog, PromptInputResult, PromptSender},
window::Window,
};

Expand Down Expand Up @@ -73,7 +73,7 @@ impl Window {
message: EmbedderMsg,
sender: &Sender<ConstellationMsg>,
clipboard: Option<&mut Clipboard>,
_compositor: &mut IOCompositor,
compositor: &mut IOCompositor,
) {
log::trace!("Verso WebView {webview_id:?} is handling Embedder message: {message:?}",);
match message {
Expand Down Expand Up @@ -156,6 +156,8 @@ impl Window {
}
EmbedderMsg::HistoryChanged(list, index) => {
self.close_prompt_dialog(webview_id);
compositor.send_root_pipeline_display_list(self);

self.tab_manager
.set_history(webview_id, list.clone(), index);
let url = list.get(index).unwrap();
Expand Down Expand Up @@ -204,8 +206,8 @@ impl Window {
PromptDefinition::Input(message, default_value, prompt_sender) => {
prompt.input(sender, rect, message, Some(default_value), prompt_sender);
}
PromptDefinition::Credentials(_) => {
todo!();
PromptDefinition::Credentials(prompt_sender) => {
prompt.http_basic_auth(sender, rect, prompt_sender);
}
}

Expand Down Expand Up @@ -558,6 +560,31 @@ impl Window {
};
let _ = sender.send(result);
}
PromptSender::HttpBasicAuthSender(sender) => {
let canceled_auth = PromptCredentialsInput {
username: None,
password: None,
};

if let Ok(HttpBasicAuthInputResult { action, auth }) =
serde_json::from_str::<HttpBasicAuthInputResult>(&msg)
{
match action.as_str() {
"signin" => {
let _ = sender.send(auth);
}
"cancel" => {
let _ = sender.send(canceled_auth);
}
_ => {
let _ = sender.send(canceled_auth);
}
};
} else {
log::error!("prompt result message invalid: {msg}");
let _ = sender.send(canceled_auth);
}
}
}

let _ = ignored_prompt_sender.send(());
Expand Down
10 changes: 9 additions & 1 deletion src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use std::cell::Cell;
use base::id::WebViewId;
use compositing_traits::ConstellationMsg;
use crossbeam_channel::Sender;
use embedder_traits::{Cursor, EmbedderMsg, PermissionRequest, PromptResult};
use embedder_traits::{
Cursor, EmbedderMsg, PermissionRequest, PromptCredentialsInput, PromptResult,
};
use euclid::{Point2D, Size2D};
use glutin::{
config::{ConfigTemplateBuilder, GlConfig},
Expand Down Expand Up @@ -904,6 +906,12 @@ impl Window {
PromptSender::PermissionSender(sender) => {
let _ = sender.send(PermissionRequest::Denied);
}
PromptSender::HttpBasicAuthSender(sender) => {
let _ = sender.send(PromptCredentialsInput {
username: None,
password: None,
});
}
}
}
}
Expand Down

0 comments on commit da288df

Please sign in to comment.