feat: settle Chrome's window, crash bubble and password prompts

Three things a volunteer should never have to see.

Sway was drawing its own title bar above Chrome's tab strip, which
already carries the window controls. Verified on a real session: Chrome
is an xdg_shell client with app_id "google-chrome" and sway was giving
it border "normal". A scoped rule removes the compositor decoration
only — Chrome keeps its tabs and address bar, stays a normal window, and
still maximises and minimises. A class rule covers an XWayland fallback.
Nothing else changes: the only other window in this classroom is VLC,
which is already borderless, so no broader rule is warranted.

"Restore pages? Chrome didn't shut down correctly" is suppressed with
--hide-crash-restore-bubble. Note the name. The switch this was going to
use, --disable-session-crashed-bubble, is not present in Chrome 152 at
all — checked against the strings of the installed binary — so it would
have silently done nothing.

That hides the prompt; the profile was still being marked as crashed,
which is the untidy half. Chrome's browser process shuts down cleanly on
SIGTERM, so a shutdown unit now gives it one and waits up to eight
seconds before the session is torn out from under it. Best effort by
construction: a shutdown that will not finish is far worse than a crash
flag.

Password prompts go off with the PasswordManagerEnabled policy, on the
path the classroom already manages. Existing profile and session data
are untouched, and website logins are unaffected: Wonder Ink and YouTube
stay signed in.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-05 00:21:02 -05:00
co-authored by Claude Opus 5
parent 890e365d81
commit 255072ac94
7 changed files with 109 additions and 0 deletions
@@ -8,6 +8,7 @@
"BrowserAddPersonEnabled": false,
"ProfilePickerOnStartupAvailability": 1,
"PasswordManagerEnabled": false,
"PasswordLeakDetectionEnabled": false,
"BackgroundModeEnabled": false,
+12
View File
@@ -24,6 +24,18 @@ exec --no-startup-id /usr/local/bin/classroom-dock
include /etc/sway/config.d/*
# Chrome draws its own tab strip with window controls in it, so sway's
# title bar above that is a second, redundant one. Verified on the
# endpoint: Chrome is an xdg_shell client with app_id "google-chrome",
# and sway was giving it border "normal". The class rule covers the case
# where Chrome falls back to XWayland.
#
# This removes the compositor's decoration only. Chrome keeps its tabs
# and address bar, stays a normal window, and still maximises and
# minimises exactly as before.
for_window [app_id="google-chrome"] border none
for_window [class="(?i)google-chrome"] border none
# The DVD player is an appliance screen, not a desktop window. VLC takes
# the whole display itself; this removes the title bar sway would
# otherwise draw above the video. classroom-dvd-session covers the
+6
View File
@@ -25,6 +25,7 @@ chmod 0755 /usr/local/bin/classroom-dvd-css-setup
chmod 0755 /usr/local/bin/classroom-notify-test
chmod 0755 /usr/local/bin/classroom-notifications
chmod 0755 /usr/local/bin/classroom-scale
chmod 0755 /usr/local/bin/classroom-shutdown
chmod 0755 /usr/local/bin/classroom-chrome-setup
chmod 0755 /usr/local/bin/classroom-provision
@@ -90,4 +91,9 @@ systemctl start ekklesia-classroom-update.timer || true
# A failure there must never fail this installation.
systemctl enable ekklesia-classroom-provision.service || true
# Gives Chrome a chance to close properly on reboot, so its profile is
# not marked as crashed. Best effort: it must never hold a shutdown up.
systemctl enable ekklesia-classroom-shutdown.service || true
systemctl start ekklesia-classroom-shutdown.service || true
exit 0
+7
View File
@@ -7,6 +7,12 @@ set -euo pipefail
# Chrome stays a full browser: Wonder Ink is only the starting page, and
# the profile in ~/.config/google-chrome is never touched here so logins
# and cookies survive reboots and updates.
#
# --hide-crash-restore-bubble is what suppresses "Restore pages? Chrome
# didn't shut down correctly" after a reboot. Note the switch name: the
# older --disable-session-crashed-bubble is not present in Chrome 152 at
# all, so it would have silently done nothing. Verified against the
# strings of the installed binary.
HOME_PAGE='https://app.wonderink.org/'
@@ -22,4 +28,5 @@ exec /usr/local/bin/classroom-window toggle \
--no-first-run \
--no-default-browser-check \
--password-store=basic \
--hide-crash-restore-bubble \
"$HOME_PAGE"
+61
View File
@@ -0,0 +1,61 @@
#!/bin/bash
set -uo pipefail
# Closes Chrome properly before the machine goes down.
#
# Chrome marks a profile as crashed when it does not get to run its own
# shutdown, which is what produces "Restore pages? Chrome didn't shut
# down correctly" on the next boot. The browser process handles SIGTERM
# by shutting down cleanly, so it is worth giving it one and waiting a
# moment before the session is torn out from under it.
#
# --hide-crash-restore-bubble already hides the prompt. This stops the
# profile being marked crashed at all, which is the tidier half of the
# same problem.
#
# Run from ekklesia-classroom-shutdown.service. Never fails: a shutdown
# that will not finish is far worse than a crash flag.
CLASSROOM_USER=classroom
WAIT_SECONDS=8
log() {
echo "[classroom-shutdown] $1"
}
browser_processes() {
# Only the browser process, not its renderers: those carry --type=
# and are Chrome's own business to reap.
local pid
for pid in $(pgrep -u "$CLASSROOM_USER" -x chrome 2>/dev/null); do
# Braces so a process exiting between pgrep and this read is
# silent: bash reports a failed redirection itself, before the
# command's own stderr redirect can catch it.
if ! { tr '\0' '\n' < "/proc/$pid/cmdline"; } 2>/dev/null |
grep -q '^--type='; then
echo "$pid"
fi
done
}
pids="$(browser_processes)"
if [[ -z "$pids" ]]; then
exit 0
fi
log "asking Chrome to close"
# shellcheck disable=SC2086
kill -TERM $pids 2>/dev/null || true
for _ in $(seq 1 $((WAIT_SECONDS * 5))); do
if [[ -z "$(browser_processes)" ]]; then
log "Chrome closed cleanly"
exit 0
fi
sleep 0.2
done
log "Chrome did not close in time; leaving it to systemd"
exit 0
@@ -0,0 +1,14 @@
[Unit]
Description=Ekklesia Classroom clean shutdown
Documentation=file:///usr/local/bin/classroom-shutdown
Before=shutdown.target reboot.target halt.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/true
ExecStop=/usr/local/bin/classroom-shutdown
TimeoutStopSec=20
[Install]
WantedBy=multi-user.target
+8
View File
@@ -125,6 +125,10 @@ install -Dm755 \
"$ROOT/scripts/classroom-scale" \
"$STAGE/usr/local/bin/classroom-scale"
install -Dm755 \
"$ROOT/scripts/classroom-shutdown" \
"$STAGE/usr/local/bin/classroom-shutdown"
install -Dm755 \
"$ROOT/scripts/classroom-chrome-setup" \
"$STAGE/usr/local/bin/classroom-chrome-setup"
@@ -203,6 +207,10 @@ install -Dm644 \
"$ROOT/systemd/provision/ekklesia-classroom-provision.service" \
"$STAGE/etc/systemd/system/ekklesia-classroom-provision.service"
install -Dm644 \
"$ROOT/systemd/shutdown/ekklesia-classroom-shutdown.service" \
"$STAGE/etc/systemd/system/ekklesia-classroom-shutdown.service"
install -Dm644 \
"$ROOT/config/icons/dvd.svg" \
"$STAGE/etc/ekklesia-classroom/icons/dvd.svg"