Measured before changing anything, and nothing about loading changed, because the evidence does not point at Wonder Ink. Same lab, same page, timed by each engine until the sign-in form exists: Wonder Ink 2.86-3.00 s cold and 2.20-2.49 s warm, Chrome 2.79-3.48 s and 1.96-2.15 s. The site is the weight — 119-160 requests and 3.2 MB for a sign-in form, and its console reports loading the Google Maps API twice. Memory about 610 MB for one tab, 115 MB per further tab; idle CPU about 1%. The lab draws in software for both engines, so it cannot show the one likely cause it has no hardware for: QtWebEngine failing to use a classroom's GPU where Chrome succeeds. classroom-wonderink --diagnose, run from the maintenance terminal in the teacher's session, prints the engine versions, the scale, the WebGL renderer (a driver, or software), and the sign-in page's time from a cold cache, in a throwaway profile beside any running Wonder Ink. Co-Authored-By: Claude Opus 5.5 <[email protected]>
541 lines
24 KiB
Markdown
541 lines
24 KiB
Markdown
# Wonder Ink
|
||
|
||
Wonder Ink is a lesson application, not a browser: its own window, its own
|
||
profile, and no way from it into the rest of the web.
|
||
|
||
Since 0.2.1 it has tabs.
|
||
|
||
## Why it stopped being Chrome
|
||
|
||
It used to be `google-chrome --app=https://app.wonderink.org/`. App mode
|
||
gives exactly the window a classroom wants — no tab strip, no address bar,
|
||
reads as a program — and teacher feedback then found the thing app mode
|
||
takes away is the thing they need. A lesson is prepared across several
|
||
tabs: the lesson, its video, the activity.
|
||
|
||
Chrome cannot be asked for "tabs but no omnibox". App mode has no tabs. A
|
||
normal window has tabs and an address bar, and no policy, flag or
|
||
extension removes the address bar from it. Tabbed web apps come closest,
|
||
need a `display_override` in a manifest on a site that is not ours, and
|
||
push off-origin links out into a normal browser window — which is the
|
||
whole of what this design exists to prevent.
|
||
|
||
So the tab strip is the classroom's own, in `classroom-wonderink`, and the
|
||
engine is Chromium either way.
|
||
|
||
## Why QtWebEngine
|
||
|
||
`python3-pyqt6.qtwebengine`, from Debian, updated with the rest of the
|
||
system.
|
||
|
||
The usual and correct objection is codecs, so it was measured before it
|
||
was chosen. On Debian 13, QtWebEngine 6.8.2 (Chromium 122):
|
||
|
||
| | |
|
||
| --- | --- |
|
||
| `canPlayType` H.264, AAC, MP3, VP8, VP9 | all "probably" |
|
||
| `MediaSource.isTypeSupported` avc1, mp4a, vp9 | all true |
|
||
| a 720p H.264/AAC file | loaded, played to the end, 1280×720 |
|
||
| `app.wonderink.org` | loads; the sign-in is an ordinary email and password form, not a Google flow that would mind an embedded browser |
|
||
|
||
Two further things the shell does for the same reason:
|
||
|
||
* **The user agent** has QtWebEngine's own token stripped, leaving a plain
|
||
Chrome string. The engine really is that Chromium, and a site gating on
|
||
the UA should not see something it has never heard of.
|
||
* **Widevine** is handed to it from Chrome's own bundle when Chrome is
|
||
installed, which on a classroom machine it always is. A lesson whose
|
||
video service wants DRM is not stopped by the engine change. Absent, the
|
||
flag is not passed and nothing else is affected.
|
||
|
||
## What a teacher gets
|
||
|
||
[ Lesson ] [ Video ] [ Activity ]
|
||
|
||
The strip appears only when there is more than one tab, so a room with one
|
||
lesson open looks exactly like the application this replaced. Each tab has
|
||
a close button. Closing the last one closes Wonder Ink; closing any other
|
||
leaves the rest alone.
|
||
|
||
A tab can only come from the lesson: a link the site opens in a new
|
||
window, a middle click, a Ctrl+click, "open in a new tab" in the menu.
|
||
There is no address bar, no search, and no button that makes a blank tab.
|
||
That is the seam the whole design turns on — Wonder Ink can take a teacher
|
||
anywhere it needs to, including a video CDN nobody told us about, and a
|
||
teacher cannot take themselves anywhere.
|
||
|
||
**There is deliberately no allowlist of domains.** A list would have to be
|
||
right about every service a lesson might ever embed, and the day it is
|
||
wrong a teacher is standing in front of a class.
|
||
|
||
The right-click menu is built rather than filtered, so a future Qt cannot
|
||
grow view-source or save-page back into it without somebody noticing in
|
||
the code: back, forward, reload, open in a new tab when there is a link,
|
||
cut/copy/paste in a text field, print.
|
||
|
||
Keys: Ctrl+W closes a tab, Alt+Left and Alt+Right navigate, Ctrl+P prints,
|
||
Ctrl+Tab switches. Ctrl+T, Ctrl+N and Ctrl+L are deliberately unbound.
|
||
|
||
## Worksheets
|
||
|
||
Some of Wonder Ink's lesson links are a download rather than a page: the
|
||
server sends the PDF as an attachment, so there is nothing for a tab to
|
||
show and the file simply lands on disk.
|
||
|
||
In this room that was the same as it not arriving at all. There is no file
|
||
manager, no application launcher and no downloads bar, on purpose, and a
|
||
teacher is not going to open a maintenance terminal to print a Christmas
|
||
tree. 0.2.1 downloaded the worksheet, said so in a notification, and left
|
||
it somewhere unreachable.
|
||
|
||
So a worksheet that finishes downloading is opened in a tab:
|
||
|
||
1. a teacher clicks a worksheet link;
|
||
2. Wonder Ink downloads it into `~/Downloads/Wonder Ink`;
|
||
3. when the download **completes**, and the file really is a PDF, it
|
||
opens as another tab, named after the file;
|
||
4. the teacher reads it, scrolls it, prints it to the room's printer;
|
||
5. closing that tab leaves the lesson exactly where it was.
|
||
|
||
Rendering is the engine's own PDF viewer — the same PDFium that Chrome
|
||
uses — so there is no second rendering library in the room and no
|
||
separate application. `QtWebEngineSettings.PdfViewerEnabled` was already
|
||
on for remote PDFs; what 0.2.2 adds is opening a *local* one, which was
|
||
verified against a real completed file rather than assumed from the
|
||
remote case.
|
||
|
||
### What is refused, and why
|
||
|
||
A completed download is not the same as a usable worksheet, and each of
|
||
these was a real case in the lab:
|
||
|
||
| | |
|
||
| --- | --- |
|
||
| an interrupted download | the notification says it did not finish and to try again. Nothing opens |
|
||
| HTML that the server called `.pdf` | refused on its first five bytes, not on its name. Without this the teacher gets a blank tab and no reason |
|
||
| an empty or unreadable file | the same |
|
||
| anything that is not a PDF | saved, and said so, exactly as before. Nothing is opened and nothing is executed |
|
||
|
||
The terminal state is read from `stateChanged`, not `isFinishedChanged`.
|
||
Measured: an interrupted download never emits `isFinishedChanged` at all,
|
||
so the first version of this said nothing whatsoever about a worksheet
|
||
that failed to arrive — which is the one case a teacher most needs told
|
||
about.
|
||
|
||
### Which local files Wonder Ink will open
|
||
|
||
Exactly the ones it opened itself, and nothing else.
|
||
|
||
Opening a worksheet adds that **one resolved path** to a list. The page's
|
||
`acceptNavigationRequest` refuses every `file:` URL that is not on that
|
||
list, so a lesson cannot navigate to one, a link cannot point at one and a
|
||
redirect cannot reach one. Chromium already refuses to let a remote page
|
||
reach `file:` at all — verified in the lab, where a link, a
|
||
`location.href` and a link to a worksheet that *had* been downloaded but
|
||
not opened by the shell were all refused — so this is the second lock
|
||
rather than the only one. It is there because the first one is somebody
|
||
else's.
|
||
|
||
Alongside it, two Qt defaults are turned off rather than left implicit:
|
||
`LocalContentCanAccessFileUrls` (on by default, and would let a local
|
||
document read other local documents) and `LocalContentCanAccessRemoteUrls`.
|
||
|
||
Printing has no **Print to File** and no file chooser of any kind: the
|
||
preview (below) offers the room's CUPS printers and nothing else.
|
||
|
||
### What a teacher still cannot reach
|
||
|
||
**Anything that is not a PDF.** A lesson handing out a Word document, a
|
||
spreadsheet, a zip of images or a PowerPoint still downloads, still says
|
||
so, and still lands somewhere a teacher has no way to open. This release
|
||
deliberately fixes only the worksheet case, because that is the one the
|
||
room actually hit and because "open whatever arrives" is how an appliance
|
||
stops being one.
|
||
|
||
The obvious small follow-ups, in the order they are worth doing:
|
||
|
||
1. **Presentations.** Already planned separately, and the larger of the
|
||
two jobs — a PowerPoint needs a renderer, not just a viewer.
|
||
2. **A worksheets list.** Not a file manager: one Wonder Ink tab, showing
|
||
the PDFs already in `~/Downloads/Wonder Ink`, each opening in a tab the
|
||
same way a fresh download does. It reads one directory, offers no
|
||
navigation out of it, and needs no new permission — the paths it offers
|
||
are ones the shell would open anyway. That covers "a teacher closed the
|
||
worksheet tab and wants it back", which is the next thing that will be
|
||
asked for.
|
||
|
||
Neither is in this release.
|
||
|
||
### Where Wonder Ink's downloads end, and Chrome's begin
|
||
|
||
They never meet, and not because anything inspects a filename.
|
||
|
||
The download handler is connected to **Wonder Ink's own profile**, so it
|
||
only ever sees downloads that started in a Wonder Ink tab. Chrome is a
|
||
separate application with a separate profile and its own download
|
||
handling, and nothing here changes, watches or intercepts it. There is no
|
||
directory watcher anywhere in the classroom: a PDF that Chrome downloads
|
||
is Chrome's business and stays that way.
|
||
|
||
`~/Downloads/Wonder Ink` is a directory of its own inside the ordinary
|
||
Downloads folder. That is partly tidiness and mostly the security
|
||
argument: it is the only place a worksheet can come from.
|
||
|
||
## Printing
|
||
|
||
### The crash (0.2.2, Little Waders)
|
||
|
||
Wonder Ink closed completely every time a teacher confirmed a print job:
|
||
`SIGSEGV in QPrinter::resolution()`, twice, both through QtWebEngine's
|
||
print-completion path.
|
||
|
||
`QWebEngineView.print(printer)` is asynchronous. In Qt 6.8.2
|
||
(`qwebengineview.cpp`) it stores the raw `QPrinter*` and returns; when
|
||
Chromium has rendered the page, `didPrintPage()` calls
|
||
`currentPrinter->resolution()` and hands the printer to a worker thread.
|
||
0.2.2 made the printer a local variable in `print_view()`, so PyQt deleted
|
||
it when the function returned and Qt read freed memory. Reproduced in the
|
||
lab with the 0.2.2 code: `QPrinter::resolution()` ←
|
||
`QWebEnginePagePrivate::didPrintPage` ← `WebContentsAdapter::didPrintPage`,
|
||
the Little Waders backtrace, and no job in CUPS.
|
||
|
||
Keeping the printer alive until `printFinished` stops the crash — that was
|
||
built and measured first — and it is still not a working print path:
|
||
|
||
| Measured, QtWebEngine 6.8.2, lab queue built from Elmer's PPD | Qt `view.print()` | `printToPdf()` + `lp` |
|
||
| --- | --- | --- |
|
||
| A three-page lesson | 24.6 MB of 300 dpi pictures, no text, A4 on a test queue | 18 KB vector PDF with its text, on Letter, 0.43 s through Elmer's filters |
|
||
| A worksheet PDF tab | **one blank page** | the worksheet file itself |
|
||
| A PDF a site shows inline | — | `printToPdf` never answers (`CreatePreviewDocument got 0 page count`) — so it is downloaded and printed as a file |
|
||
| A queue refusing jobs | reported as **printed** | "Refusing did not take the job: Destination … is not accepting jobs." |
|
||
| Black and white on Elmer | — | IPP `print-color-mode` is **ignored** by the generic PCL 6 driver ("Unknown option", prints colour); `ColorModel=Grayscale` gives `pxlmono` |
|
||
|
||
Qt's path also mishandles three things its own source makes plain: a
|
||
second `print()` on a page that is still printing is refused with no
|
||
`printFinished`, the worker keeps the printer after the tab has gone, and
|
||
quitting destroys the printer under that worker.
|
||
|
||
So 0.2.6.1 does not use Qt printing at all. **Print** makes a PDF — a
|
||
worksheet already is one; a lesson page is rendered by the engine onto the
|
||
printer's own paper with Chrome-like 10 mm margins — shows it, and hands it
|
||
to CUPS with `lp`, which is how Chrome prints in the same rooms. Nothing
|
||
asynchronous ever holds a printer, so closing a tab or Wonder Ink cannot
|
||
break a job: once `lp` returns, the job is CUPS's.
|
||
|
||
### The preview
|
||
|
||
One dialog, for lessons and worksheets alike:
|
||
|
||
* the document as it will print, a whole page at a time, ‹ › to turn;
|
||
* **Printer** — the CUPS destinations, the system default preselected
|
||
(Elmer, in every room);
|
||
* **Pages** — all, or some: `2`, `1-3`, `1, 3, 5-6`, checked against the
|
||
document with the reason given if wrong;
|
||
* **Copies** — 1 to 50;
|
||
* **Colour** — colour or black and white, only where the printer's driver
|
||
has a grey mode, and asked for in that driver's own terms;
|
||
* **Cancel** sends nothing; **Print** returns to the lesson once CUPS has
|
||
the job, and a notification says where it went. A refused job keeps the
|
||
preview open with CUPS's reason.
|
||
|
||
Choosing a printer with different paper lays a lesson page out again. A
|
||
page that closes its own tab while the preview is open is reported, not
|
||
printed. Print pressed again while a preview is open does nothing, and so
|
||
does a page calling `window.print()` meanwhile. The PDF viewer's own print
|
||
button opens the same preview.
|
||
|
||
Temporary PDFs live in the teacher's `$XDG_RUNTIME_DIR` and are removed
|
||
when the preview closes.
|
||
|
||
`tools/lab/wonderink-print-check` runs all of this against the installed
|
||
command in the lab, and judges every scenario by what CUPS actually
|
||
received. `tests/test-classroom-printing.sh` covers the CUPS side at every
|
||
build. **Neither is paper.** The first print through the real Elmer is a
|
||
job for somebody standing next to it.
|
||
|
||
### Any other error
|
||
|
||
An exception escaping any Qt handler makes PyQt6 abort the whole process —
|
||
measured: a mistake in one slot and the window vanishes. Wonder Ink now
|
||
logs it to the journal and carries on.
|
||
|
||
## What stayed the same
|
||
|
||
* **A separate profile from the ordinary browser.** A teacher's Wonder Ink
|
||
sign-in and the room's shared YouTube account are different sessions in
|
||
different storage that cannot reach each other. The browser is still
|
||
Chrome and nothing in this change touches it.
|
||
* **Cookies live only as long as the window.** This is what the Chrome
|
||
policy's `CookiesSessionOnlyForUrls` did for wonderink.org, and it is
|
||
the reason closing Wonder Ink ends the teacher's session rather than
|
||
leaving the next person signed in as the last one. The whole profile
|
||
rather than one domain now, because this profile only ever goes where a
|
||
lesson sends it. If a room ever wants sign-ins to survive, it is the one
|
||
line `setPersistentCookiesPolicy` in `classroom-wonderink`.
|
||
* **The old Chrome profile is left where it is**, at
|
||
`~/.local/share/ekklesia-classroom/wonderink`. The new one is
|
||
`wonderink-shell` beside it, so rolling back to 0.2.0 finds the old one
|
||
intact.
|
||
* **One scale for the room** — see *How large a lesson is*, below.
|
||
|
||
## How large a lesson is
|
||
|
||
Teachers found lessons in Wonder Ink too small next to the same site in
|
||
the ordinary browser. Measured in the lab (Plasma 6 on KWin, Debian 13) by
|
||
asking each page for `window.devicePixelRatio` and its width in CSS pixels,
|
||
with the display scale set the way a room sets it (`kscreen-doctor` and
|
||
`UI_SCALE`):
|
||
|
||
| Display scale | Wonder Ink 0.2.6 | Chrome, as `classroom-browser` starts it |
|
||
| --- | --- | --- |
|
||
| 100% | dpr 1, 1920 px wide | dpr 1, 1920 px wide |
|
||
| 150% | dpr 1.5, 1280 px wide | **dpr 2.25**, 854 px wide |
|
||
| 200% | dpr 2, 960 px wide | **dpr 4**, 500 px wide |
|
||
|
||
Wonder Ink was right by the desktop's measure: it follows the display
|
||
scale exactly, like the panel and every dialog. **Chrome is scaled twice.**
|
||
Under Plasma it follows the compositor's scale itself, and
|
||
`classroom-browser` also forces `--force-device-scale-factor=UI_SCALE` — a
|
||
flag from the sway session, where Chrome was measured *not* following the
|
||
compositor. So in any room above 100%, every page in the browser is
|
||
`UI_SCALE` times larger than the desktop around it, and that is the size
|
||
teachers read lessons at.
|
||
|
||
0.2.6.1 does not change the browser — it is the size the rooms know, and
|
||
changing it everywhere is its own decision. Wonder Ink's pages start at
|
||
the browser's size instead: **page zoom = `UI_SCALE`**, the room's own
|
||
clamped number, which is exactly 1.0 in a room at 100%. Wonder Ink's
|
||
window, tabs and dialogs are not zoomed; Qt already scales them once,
|
||
correctly. Measured after the change: at 150% the lesson reports dpr 2.25
|
||
and 853 px, Chrome dpr 2.25 and 854 px.
|
||
|
||
**The teacher's own size** goes on top: Ctrl+Plus (and Ctrl+=, and the
|
||
keypad's), Ctrl+Minus, Ctrl+0 for normal, Ctrl+wheel, or the right-click
|
||
menu. Chrome's steps, 50% to 300% of the room's normal size, one level for
|
||
every tab including new ones. It is remembered in
|
||
`~/.config/ekklesia-classroom/wonderink.conf` as a single `LEVEL=` line —
|
||
its own file, nowhere near the engine profile, so remembering a size can
|
||
never mean remembering a sign-in — and relative to the room, so an
|
||
administrator changing `UI_SCALE` later still gets the right result.
|
||
|
||
Measured along the way:
|
||
|
||
* the engine keeps a tab's zoom across every navigation, same site or not,
|
||
so it is set once when the tab is made and never re-applied;
|
||
* zoom does not change what prints: the same lesson is 3 pages at 100%,
|
||
200% and 400%;
|
||
* **page zoom does not reach a worksheet tab.** Chromium's PDF viewer
|
||
follows the display scale like the window, fits the page to its width,
|
||
and has its own − / + zoom in its toolbar, which is what a teacher uses
|
||
there;
|
||
* two Qt actions on one key are ambiguous and Qt does *neither* — Ctrl+Minus
|
||
did nothing while it was bound once by name and again as KDE's standard
|
||
zoom-out key. One action per command now carries all its keys, which
|
||
also fixed Ctrl+Shift+Tab (bound twice the same way since 0.2.1).
|
||
|
||
`tools/lab/wonderink-zoom-check` asks the installed command all of this.
|
||
The lab's injected keyboard does not reach its nested session, so the
|
||
check delivers keys through Qt's own key path to the focused page; a
|
||
physical Ctrl+Plus on a classroom keyboard is a hardware check.
|
||
|
||
## Why lessons load slowly
|
||
|
||
Measured before anything was changed, and nothing about loading was
|
||
changed, because the evidence does not point at Wonder Ink.
|
||
|
||
In the lab, on the same connection, the same page, timed by each
|
||
engine's own Navigation Timing until the sign-in form exists (the site
|
||
is a 3.7 KB shell that builds itself in JavaScript, so its load event is
|
||
not the moment a teacher can use it):
|
||
|
||
| Until the sign-in page is usable | Wonder Ink | Chrome |
|
||
| --- | --- | --- |
|
||
| cold cache, 3 runs | 2.86 – 3.00 s | 2.79 – 3.48 s |
|
||
| warm cache, 3 runs | 2.20 – 2.49 s | 1.96 – 2.15 s |
|
||
| window on screen after launch | 0.33 s | 0.42 s |
|
||
|
||
* **The site is the weight:** 119 – 160 requests and 3.2 MB for a sign-in
|
||
form, about 430 KB warm. Its console says it loads the Google Maps
|
||
JavaScript API *twice*, synchronously. That is Wonder Ink's to fix.
|
||
* Wonder Ink is about a quarter of a second slower warm: it revalidates
|
||
the page shell (62 – 87 ms to first byte) where Chrome serves it from
|
||
its cache. Not the "very long time" teachers describe.
|
||
* Memory: about 610 MB for one tab and 115 MB more for each further tab —
|
||
eight tabs, 1.4 GB — one renderer each. Idle CPU about 1%: nothing
|
||
spins in the background.
|
||
* The network: from the lab, 0.14 – 0.22 s to first byte; from the
|
||
development host, 0.42 – 0.44 s. Cloudflare in front, uncached.
|
||
|
||
What the lab **cannot** show is the machines: it draws in software for
|
||
both engines. A classroom whose graphics fail in QtWebEngine but work in
|
||
Chrome would look exactly like the complaint. So there is a command for a
|
||
technician, run from the maintenance terminal inside the teacher's
|
||
session (Meta+Ctrl+Alt+Shift+K):
|
||
|
||
classroom-wonderink --diagnose
|
||
|
||
It prints the engine's versions, the scale, **what WebGL reports as the
|
||
renderer** — a real driver's name, or "none … drawing in software" — and
|
||
times the sign-in page from a cold cache in a throwaway profile, beside a
|
||
running Wonder Ink without touching it. Compare its time with the lab's
|
||
2.9 s, and its renderer with Chrome's on the same machine (open
|
||
`chrome://gpu` there).
|
||
|
||
|
||
|
||
One future device is a laptop that will not have Wonder Ink at all.
|
||
|
||
WONDERINK_ENABLED=false
|
||
|
||
in `/etc/ekklesia-classroom/device.conf`, then
|
||
`sudo classroom-desktop-layout` and a logout. The launcher comes off the
|
||
panel, the command refuses to start and says why, and nothing Wonder Ink
|
||
owns runs. The browser, Parent Call and the disc player are untouched, and
|
||
the panel stays balanced because the applications are centred rather than
|
||
placed.
|
||
|
||
The setting is read the same way the disc player's line is written: one
|
||
marked line in the packaged layout, edited by `classroom-desktop-layout`.
|
||
Absent, it means yes — every room that has never heard of the setting is a
|
||
room with Wonder Ink.
|
||
|
||
## Present view video
|
||
|
||
**Fixed in 0.2.1.** The investigation below is kept because it is what
|
||
found the cause, and because the second and third candidates in it were
|
||
never ruled out and may still matter.
|
||
|
||
Candidate 1 — the autoplay gesture chain — is the one the new shell can
|
||
act on, and does. Chrome refuses to begin audible playback that no click
|
||
asked for, and in Chrome the only lever was `AutoplayAllowlist` on the
|
||
policy that governs the ordinary browser as well; allowing autoplay
|
||
everywhere on a classroom machine was a real change and was deliberately
|
||
not made on a guess.
|
||
|
||
Wonder Ink is no longer governed by that policy. Its profile is Wonder
|
||
Ink and nothing else, so `PlaybackRequiresUserGesture` is turned off for
|
||
that profile alone and reaches no other page in the room. The ordinary
|
||
browser is a separate application with a separate policy and is not
|
||
touched.
|
||
|
||
Measured both ways in the lab, with a page that starts an *unmuted* video
|
||
from a script with no user gesture — which is what Present view does:
|
||
|
||
| | |
|
||
| --- | --- |
|
||
| the setting at Chrome's default | `AUTOPLAY BLOCKED: NotAllowedError` — the exact error predicted below |
|
||
| the setting as shipped | `AUTOPLAY OK muted=false`, video playing |
|
||
|
||
That is the lab reproducing the failure and then not reproducing it. It
|
||
is **not** confirmation that Present view itself is fixed, because the
|
||
lab has no Wonder Ink account: if Present view still stops after this,
|
||
candidates 2 and 3 below are where to look next.
|
||
|
||
### The observation
|
||
|
||
On the classroom endpoint:
|
||
|
||
- Vimeo videos embedded in Wonder Ink **play correctly** in the normal
|
||
lesson view.
|
||
- The **same** videos in Wonder Ink's **Present** view stop immediately
|
||
when Play is pressed.
|
||
|
||
### What this rules out
|
||
|
||
The system's video capability is fine. The same video, the same host,
|
||
the same browser and the same codecs work a click earlier in the same
|
||
session. Nothing is missing from the machine.
|
||
|
||
**No codecs or media packages were installed for this, and none should
|
||
be.** Installing packages to fix a problem that is not a missing package
|
||
would leave the classroom carrying software it does not need, and would
|
||
not fix Present view.
|
||
|
||
Nothing in the system was changed for this.
|
||
|
||
### The decisive test
|
||
|
||
Run it once at the endpoint. This separates "Wonder Ink" from "the
|
||
classroom's Chrome configuration" in one step:
|
||
|
||
```
|
||
sudo mv /etc/opt/chrome/policies /etc/opt/chrome/policies.off
|
||
google-chrome --user-data-dir=/tmp/plain-chrome
|
||
```
|
||
|
||
That is a stock Chrome with a clean profile and no classroom policy at
|
||
all. Sign in, open the lesson, press Present, press Play.
|
||
|
||
- **Still fails** — it is Wonder Ink, and nothing here can fix it.
|
||
Report it to them with the console output below.
|
||
- **Works** — something in the classroom configuration is responsible,
|
||
and the next section is where to look.
|
||
|
||
Afterwards:
|
||
|
||
```
|
||
sudo mv /etc/opt/chrome/policies.off /etc/opt/chrome/policies
|
||
rm -rf /tmp/plain-chrome
|
||
```
|
||
|
||
### If it turns out to be this end
|
||
|
||
Two candidates, in order of likelihood. **Neither has been applied.**
|
||
|
||
#### 1. The autoplay gesture chain
|
||
|
||
Chrome refuses to start audible playback without a user gesture. If
|
||
Present view starts the player programmatically after its own view
|
||
transition, the gesture from the Present click may not carry, and
|
||
Chrome rejects the play. That would look exactly like this: fine when
|
||
you click the player directly, dead when the view starts it for you.
|
||
|
||
This is stock Chrome behaviour, not a classroom setting — a plain Chrome
|
||
on any machine would do the same, which is what the decisive test above
|
||
checks.
|
||
|
||
Diagnostic: open DevTools (F12) in Present view and press Play. A
|
||
rejected autoplay logs:
|
||
|
||
```
|
||
NotAllowedError: play() failed because the user didn't interact
|
||
with the document first.
|
||
```
|
||
|
||
If that appears, the fix is a scoped policy, not a global one:
|
||
|
||
```json
|
||
"AutoplayAllowlist": ["https://app.wonderink.org"]
|
||
```
|
||
|
||
`AutoplayAllowlist` is recognised by Chrome 152 — verified against the
|
||
installed binary. It has deliberately **not** been added, because
|
||
allowing autoplay everywhere on a classroom machine is a real change and
|
||
should follow evidence, not a guess.
|
||
|
||
*Since 0.2.1 this no longer applies to Wonder Ink, which is not a Chrome
|
||
window any more and allows its own lesson to start a video without
|
||
changing anything about the browser. See the top of this section. It
|
||
still applies to the ordinary browser, where nothing has changed.*
|
||
|
||
#### 2. A popup window that sway tiles
|
||
|
||
sway tiles new windows rather than floating them. If Present view opens
|
||
a second browser window, it gets a tiled geometry the site did not
|
||
expect, and a player that is resized or re-parented mid-start can stop.
|
||
|
||
Diagnostic, while Present view is open:
|
||
|
||
```
|
||
swaymsg -t get_tree | grep -c google-chrome
|
||
```
|
||
|
||
More than one Chrome window means this is worth pursuing.
|
||
|
||
#### 3. Not the classroom policies
|
||
|
||
For completeness, so nobody re-audits them: the managed policy in
|
||
`/etc/opt/chrome/policies/managed/` sets first-run, sign-in, password
|
||
and homepage behaviour only. None of it touches media, autoplay,
|
||
iframes, fullscreen or site permissions.
|