Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(kiosk-browser): remove uses of non-trivial APIs #5637

Merged

Conversation

eventualbuddha
Copy link
Collaborator

@eventualbuddha eventualbuddha commented Nov 26, 2024

Overview

Refs #4531

Removes all uses of the following kiosk-browser APIs:

  • captureScreenshot + saveAs (replaced with dev-dock/backend API + gnome-screenshot) this was restored because we need a way to capture the web rendering area and not the entire screen
  • showOpenDialog (selecting election package to load in dev is removed) this was restored because we still need to pick election definitions
  • showSaveDialog ("Save As" in a few places is removed)
  • saveAs ("Save As" in a few places is removed, and writing the screenshot is now done on the backend)

kiosk.quit and kiosk.log remain unchanged.

Demo Video or Screenshot

Edit 11/27: The video below shows the screenshot flow using a wry-based version of kiosk-browser. This no longer works with the reversion to using kiosk.captureScreenshot, but the flow outlined in the video is the same.

CleanShot.2024-11-25.at.16.51.12.mp4
Click to view `wry` based app code
use clap::Parser;
use serde::Deserialize;
use tao::{
    event::{ElementState, Event, StartCause, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    keyboard::ModifiersState,
    window::{Fullscreen, WindowBuilder},
};
use wry::WebViewBuilderExtUnix;
use wry::{http::Uri, WebViewBuilder};

#[derive(Debug, Parser)]
struct Options {
    #[clap(short, long, default_value = "false")]
    allow_devtools: bool,

    #[clap(default_value = "http://localhost:3000/")]
    url: Uri,
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
enum IpcCommand {
    /// Log a message to stdout.
    #[serde(rename = "log")]
    Log { message: String },

    /// Quit the application.
    #[serde(rename = "quit")]
    Quit,
}

const INIT_JS: &str = "
    globalThis.kiosk = Object.freeze({
        quit() {
            window.ipc.postMessage(JSON.stringify({ type: 'quit' }));
        },

        log(message) {
            window.ipc.postMessage(JSON.stringify({ type: 'log', message }));
        },
    });
";

fn main() -> wry::Result<()> {
    let options = Options::parse();
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new()
        .with_fullscreen(Some(Fullscreen::Borderless(None)))
        .with_title("kiosk-browser")
        .build(&event_loop)
        .unwrap();
    let builder = WebViewBuilder::new()
        .with_url(options.url.to_string())
        .with_initialization_script(INIT_JS)
        // allow devtools if the flag is set
        .with_devtools(options.allow_devtools)
        // deny clipboard access
        .with_clipboard(false)
        // deny downloads
        .with_download_started_handler(|_, _| false)
        // do not record browsing history, cookies, cache, etc.
        .with_incognito(true)
        // handle IPC messages, i.e. `kiosk.log` et al
        .with_ipc_handler(
            |ipc| match serde_json::from_str::<IpcCommand>(&ipc.body()) {
                Ok(IpcCommand::Log { message }) => println!("{message}"),
                Ok(IpcCommand::Quit) => std::process::exit(0),
                Err(err) => {
                    eprintln!(
                        "failed to parse IPC message: {body}\nError: {err}",
                        body = ipc.body()
                    );
                }
            },
        );

    let webview = {
        use tao::platform::unix::WindowExtUnix;
        let vbox = window.default_vbox().unwrap();
        builder.build_gtk(vbox)?
    };

    let mut modifiers = Default::default();
    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;

        match event {
            Event::NewEvents(StartCause::Init) => println!("kiosk-browser has started!"),
            Event::WindowEvent {
                event: WindowEvent::ModifiersChanged(new_modifiers),
                ..
            } => {
                modifiers = new_modifiers;
            }
            Event::WindowEvent {
                event: WindowEvent::KeyboardInput { event, .. },
                ..
            } => match (modifiers, event.state, event.text) {
                (ModifiersState::CONTROL, ElementState::Released, Some("w")) => {
                    println!("Ctrl+W pressed, closing the window");
                    *control_flow = ControlFlow::Exit;
                }
                (ModifiersState::CONTROL, ElementState::Released, Some("r")) => {
                    println!("Ctrl+R pressed, reloading the page");
                    webview.evaluate_script("location.reload()").unwrap();
                }
                (
                    ModifiersState::CONTROL | ModifiersState::SHIFT,
                    ElementState::Released,
                    Some("i"),
                ) => {
                    if options.allow_devtools {
                        println!("Ctrl+Shift+I pressed, toggling devtools");
                        if webview.is_devtools_open() {
                            webview.close_devtools();
                        } else {
                            webview.open_devtools();
                        }
                    }
                }
                _ => {
                    // ignore
                }
            },
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => *control_flow = ControlFlow::Exit,
            _ => (),
        }
    });
}

Testing Plan

Tested in the original kiosk-browser and the new prototype wry-based kiosk-browser (see above). Standard browser usage remains unchanged since it doesn't have these APIs anyway.

@eventualbuddha eventualbuddha linked an issue Nov 26, 2024 that may be closed by this pull request
17 tasks
Closes #4531

Removes all uses of the following `kiosk-browser` APIs:
- `captureScreenshot` + `saveAs` (replaced with `dev-dock/backend` API + `gnome-screenshot`)
- `showOpenDialog` (selecting election package to load in dev is removed)
- `showSaveDialog` ("Save As" in a few places is removed)

We still use `kiosk.quit` and `kiosk.log`, but these can be trivially reimplemented.
@eventualbuddha eventualbuddha force-pushed the 4531-tech-improvement-stop-using-kiosk-browser-apis branch from fbb013d to f1813fb Compare November 26, 2024 01:05
Copy link
Collaborator

@jonahkagan jonahkagan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I called out two important dev dock features removed in this PR that I think we need to maintain or find another way to implement. Happy to help research/brainstorm alternatives

libs/dev-dock/backend/src/dev_dock_api.ts Show resolved Hide resolved
libs/dev-dock/frontend/src/dev_dock.tsx Outdated Show resolved Hide resolved
This is more web-native than `Buffer`, which is pretty NodeJS-specific. It'd be nice to move in this direction over time.
Changes it to use `Uint8Array` instead of `Buffer` and to post the data for the backend to write, so we can at least remove the `kiosk.saveAs` API.
@eventualbuddha eventualbuddha merged commit 5e8e3f3 into main Dec 2, 2024
62 checks passed
@eventualbuddha eventualbuddha deleted the 4531-tech-improvement-stop-using-kiosk-browser-apis branch December 2, 2024 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

tech improvement: stop using kiosk-browser APIs
2 participants