Handle page permissions - fakeivchenko/lwjwae GitHub Wiki
A page that asks for the camera or the microphone does not get an engine prompt in lwjwae. The window decides, and by default it says no, the same way on every platform. In this guide, a video call page gets the camera only when Java grants it.

The page has two buttons, one for the camera and one for the camera and the microphone, a line
that tells how the request ended, and a list of what Java decided. Here the window has a handler
that denies everything, which is also what a window without a handler does: the page asks, the
request fails with NotAllowedError, and no prompt of the engine shows, neither on Linux nor on
Windows.
Say yes to the camera
public class PermissionsGuide {
public static void main(String[] args) {
Application.launch(
WindowParameters.of("Camera", 560, 460),
window -> {
window.permissionHandler(PermissionsGuide::decide);
window.loadResource("permissions/index.html");
});
}
private static PermissionDecision decide(PermissionRequest request) {
return request.kind() == PermissionKind.CAMERA
&& request.origin().equals("https://meet.example.com")
? PermissionDecision.GRANT
: PermissionDecision.DENY;
}
}

The second click asks again, and this time the handler answers GRANT, so getUserMedia resolves
with a stream and the page shows it in a video element. The list keeps both decisions, one line
for every request that reached the handler. The pictures above come from a test camera, since the
machines that took them have none. The origin is the one of the page, app://local on Linux and
http://app.localhost on Windows, in the form of scheme://host, without the path.
Without permissionHandler, or with null, every request is denied: getUserMedia fails on the
page with NotAllowedError, as if the user had clicked Block.
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
document.querySelector("#preview").srcObject = stream;
} catch (error) {
document.querySelector("#status").textContent = error.name;
}
What reaches the handler
| Kind | Windows | macOS | Linux (GTK 3 and 4) |
|---|---|---|---|
CAMERA |
yes | yes | yes |
MICROPHONE |
yes | yes | yes |
GEOLOCATION |
yes | no hook: the system decides | always denied |
NOTIFICATIONS |
yes | no hook: the system decides | always denied |
A call that asks for the camera and the microphone together reaches the handler as two requests, and the page gets both only if the handler grants both. A capture of the screen is always denied.

Here the handler grants the camera and denies the microphone. It hears of both, one line each, and
the page gets nothing: getUserMedia({ video: true, audio: true }) fails with NotAllowedError
as a whole. The order of the two lines is the one of the engine, the microphone first on Windows.
Good to know
- The handler runs on the UI thread, so it has to answer without waiting. To ask the user, decide from what you already know, such as a setting, or open a dialog first and grant on the next call.
- A handler that throws denies the request, and the exception is reported.
- On macOS a grant is only the first step: the system then asks the user for its own permission of
the application, and the application needs
NSCameraUsageDescriptionorNSMicrophoneUsageDescriptioninInfo.plist. - The decision is not remembered between runs; the handler is asked every time.