Skip to content

Commit 794cb8b

Browse files
committed
feat: add permission_handler example, Hash derive, Display impl for PermissionKind/PermissionResponse
Signed-off-by: F0RLE <lrshka.klim7766@gmail.com>
1 parent f5e3439 commit 794cb8b

File tree

2 files changed

+177
-2
lines changed

2 files changed

+177
-2
lines changed

examples/permission_handler.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
//! Example demonstrating the permission handler API.
6+
//!
7+
//! This example creates a webview with a simple HTML page that requests
8+
//! camera, microphone, and geolocation permissions. The permission handler
9+
//! logs each request and applies a per-kind policy.
10+
11+
use tao::{
12+
event::{Event, WindowEvent},
13+
event_loop::{ControlFlow, EventLoop},
14+
window::WindowBuilder,
15+
};
16+
use wry::{PermissionKind, PermissionResponse, WebViewBuilder};
17+
18+
const HTML: &str = r#"
19+
<!DOCTYPE html>
20+
<html>
21+
<head>
22+
<meta charset="utf-8">
23+
<title>Permission Handler Example</title>
24+
<style>
25+
body { font-family: system-ui, sans-serif; padding: 2rem; background: #1a1a2e; color: #eee; }
26+
button { padding: 0.75rem 1.5rem; margin: 0.5rem; border: none; border-radius: 8px;
27+
background: #0f3460; color: #fff; cursor: pointer; font-size: 1rem; }
28+
button:hover { background: #16213e; }
29+
#log { margin-top: 1rem; padding: 1rem; background: #16213e; border-radius: 8px;
30+
font-family: monospace; white-space: pre-wrap; min-height: 100px; }
31+
</style>
32+
</head>
33+
<body>
34+
<h1>🔐 Permission Handler Demo</h1>
35+
<p>Click a button to trigger a permission request. Check the terminal for handler output.</p>
36+
<div>
37+
<button onclick="requestCamera()">📷 Request Camera</button>
38+
<button onclick="requestMicrophone()">🎤 Request Microphone</button>
39+
<button onclick="requestGeolocation()">📍 Request Geolocation</button>
40+
<button onclick="requestScreen()">🖥️ Request Screen Capture</button>
41+
</div>
42+
<div id="log">Waiting for permission requests...</div>
43+
<script>
44+
function log(msg) {
45+
document.getElementById('log').textContent += '\n' + msg;
46+
}
47+
async function requestCamera() {
48+
try {
49+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
50+
log('✅ Camera granted');
51+
stream.getTracks().forEach(t => t.stop());
52+
} catch (e) { log('❌ Camera denied: ' + e.message); }
53+
}
54+
async function requestMicrophone() {
55+
try {
56+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
57+
log('✅ Microphone granted');
58+
stream.getTracks().forEach(t => t.stop());
59+
} catch (e) { log('❌ Microphone denied: ' + e.message); }
60+
}
61+
function requestGeolocation() {
62+
navigator.geolocation.getCurrentPosition(
63+
pos => log('✅ Geolocation granted: ' + pos.coords.latitude + ', ' + pos.coords.longitude),
64+
err => log('❌ Geolocation denied: ' + err.message)
65+
);
66+
}
67+
async function requestScreen() {
68+
try {
69+
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
70+
log('✅ Screen capture granted');
71+
stream.getTracks().forEach(t => t.stop());
72+
} catch (e) { log('❌ Screen capture denied: ' + e.message); }
73+
}
74+
</script>
75+
</body>
76+
</html>
77+
"#;
78+
79+
fn main() -> wry::Result<()> {
80+
let event_loop = EventLoop::new();
81+
let window = WindowBuilder::new()
82+
.with_title("Permission Handler Example")
83+
.build(&event_loop)
84+
.unwrap();
85+
86+
let builder = WebViewBuilder::new()
87+
.with_html(HTML)
88+
.with_permission_handler(|kind| {
89+
println!("[permission] requested: {kind:?}");
90+
91+
let response = match kind {
92+
// Allow media capture
93+
PermissionKind::Camera => PermissionResponse::Allow,
94+
PermissionKind::Microphone => PermissionResponse::Allow,
95+
PermissionKind::DisplayCapture => PermissionResponse::Allow,
96+
97+
// Let the system handle geolocation (shows native prompt)
98+
PermissionKind::Geolocation => PermissionResponse::Prompt,
99+
100+
// Deny everything else
101+
_ => PermissionResponse::Deny,
102+
};
103+
104+
println!("[permission] response: {response:?}");
105+
response
106+
});
107+
108+
#[cfg(any(
109+
target_os = "windows",
110+
target_os = "macos",
111+
target_os = "ios",
112+
target_os = "android"
113+
))]
114+
let _webview = builder.build(&window)?;
115+
#[cfg(not(any(
116+
target_os = "windows",
117+
target_os = "macos",
118+
target_os = "ios",
119+
target_os = "android"
120+
)))]
121+
let _webview = {
122+
use tao::platform::unix::WindowExtUnix;
123+
use wry::WebViewBuilderExtUnix;
124+
let vbox = window.default_vbox().unwrap();
125+
builder.build_gtk(vbox)?
126+
};
127+
128+
event_loop.run(move |event, _, control_flow| {
129+
*control_flow = ControlFlow::Wait;
130+
131+
if let Event::WindowEvent {
132+
event: WindowEvent::CloseRequested,
133+
..
134+
} = event
135+
{
136+
*control_flow = ControlFlow::Exit;
137+
}
138+
});
139+
}

src/lib.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ pub struct NewWindowFeatures {
538538
/// Permission types that can be requested by the webview.
539539
///
540540
/// See [`WebViewBuilder::with_permission_handler`].
541-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
541+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
542542
#[non_exhaustive]
543543
pub enum PermissionKind {
544544
/// Microphone access permission.
@@ -593,10 +593,35 @@ pub enum PermissionKind {
593593
Other,
594594
}
595595

596+
impl std::fmt::Display for PermissionKind {
597+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598+
match self {
599+
Self::Microphone => write!(f, "microphone"),
600+
Self::Camera => write!(f, "camera"),
601+
Self::Geolocation => write!(f, "geolocation"),
602+
Self::Notifications => write!(f, "notifications"),
603+
Self::ClipboardRead => write!(f, "clipboard-read"),
604+
Self::DisplayCapture => write!(f, "display-capture"),
605+
Self::Midi => write!(f, "midi"),
606+
Self::Nfc => write!(f, "nfc"),
607+
Self::Bluetooth => write!(f, "bluetooth"),
608+
Self::Sensors => write!(f, "sensors"),
609+
Self::MediaKeySystemAccess => write!(f, "media-key-system-access"),
610+
Self::LocalFonts => write!(f, "local-fonts"),
611+
Self::WindowManagement => write!(f, "window-management"),
612+
Self::PointerLock => write!(f, "pointer-lock"),
613+
Self::AutomaticDownloads => write!(f, "automatic-downloads"),
614+
Self::FileSystemAccess => write!(f, "file-system-access"),
615+
Self::Autoplay => write!(f, "autoplay"),
616+
Self::Other => write!(f, "other"),
617+
}
618+
}
619+
}
620+
596621
/// Response for permission requests.
597622
///
598623
/// See [`WebViewBuilder::with_permission_handler`].
599-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
624+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
600625
pub enum PermissionResponse {
601626
/// Grant the permission.
602627
Allow,
@@ -609,6 +634,17 @@ pub enum PermissionResponse {
609634
Prompt,
610635
}
611636

637+
impl std::fmt::Display for PermissionResponse {
638+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
639+
match self {
640+
Self::Allow => write!(f, "allow"),
641+
Self::Deny => write!(f, "deny"),
642+
Self::Default => write!(f, "default"),
643+
Self::Prompt => write!(f, "prompt"),
644+
}
645+
}
646+
}
647+
612648
/// An id for a webview
613649
pub type WebViewId<'a> = &'a str;
614650

0 commit comments

Comments
 (0)