WIP: Run on Android System WebView 80 (Android 11's stock 83) #49

Closed
asxpi wants to merge 2 commits from asxpi/anotherim-js:fix/webview-80 into develop
First-time contributor

The app on Android System WebView 80 to 110, which covers Android 11's stock WebView (83).

What was wrong

  • It didn't start on WebView 80–91. Array.prototype.at (Chromium 92) and Array.prototype.with (110) threw before the first screen, which then showed only "another.im failed to start".
  • The lock screen leaked keyboard focus on 80–101. It relies on inert (102), so on older engines Tab reached fields and a call's Answer button behind the PIN.
  • The layout broke on 80–87. Flex gap (84), inset (87), aspect-ratio (88), :where() (88), :focus-visible inside selector lists (86) and color-mix() (111) are all missing there. The visible results:
    • spacing collapsed
    • overlays lost their position
    • round icon buttons lost their centring: a <button> there can't be a grid container
    • the --chat-* tokens in theme.css were dropped together with their :where() rule
    • mixed colours disappeared

What this changes

  1. ui/src/polyfills.ts, imported first by main.ts, adds at() and with() where missing, plus structuredClone in mock builds. test/polyfills.test.ts also scans src/ for other post-80 built-ins, so a new one fails the tests.
  2. The app shell pulls focus back to the lock where inert is missing.
  3. ui/legacy-css.ts is a Vite plugin that runs in production builds only. It rewrites the Lit css literals and the global sheets:
    • Exact rewrites: longhands for inset and margin-inline/padding-block, :focus-visible split out of selector lists, :where() unwrapped, a vh fallback before dvh, ::-webkit-scrollbar beside scrollbar-width, and a viewport @media copy of @container.
    • color-mix(…) becomes var(--cm-<hash>, color-mix(…)).
    • aspect-ratio beside a width that doesn't depend on the parent gets a height under @supports not (aspect-ratio: 1).
    • Flex gap and centring grids get margin fallbacks and flex-column fallbacks, scoped to :host-context(.no-flex-gap) and .no-button-grid.
  4. ui/src/legacy-css.ts runs in the page. It sets no-flex-gap, no-button-grid and no-focus-visible on <html> only where the engine lacks the feature. It also computes every --cm-* from the theme on :root, and recomputes on theme or accent changes.
  5. Engines that have all of this get the same styles as before. The dev server and the tests see the sources as written. build.cssTarget: 'chrome80' stops the minifier from folding inset back together.
  6. Hand fallbacks cover what the plugin can't size: the media grid, theme swatches, album tiles and picture placeholders (shotBox/clipBox/stickers).
  7. aim-webview-banner shows a note at the top of the chat list on Chromium/WebView older than 88. In the app it has an "Update…" button that opens Android System WebView on Play. Closing it is remembered per WebView version. Its strings are in all catalogues.

Tested

  • Chromium 80.0.3987.0 (snapshot r722234), headless and with a window, on make build-mock. Phone and desktop sizes, the chat list, an open chat, sheets, Preferences › Appearance and Night mode were compared side by side with Chromium 154; they match to within a few pixels.
  • The debug apk on an API 30 emulator (WebView 83.0.4103.106) renders correctly up to login. After login the emulator's arm64 translator (NDK translation 0.2.2) hits SIGILL on an fcvtzu d1, d0 in libanotherim_tacky.so. That's a gap in the emulator, not the app; a real arm64 phone runs the instruction natively. It still needs a check on a real Android 11 phone.
  • Automated checks:
    • make check STRICT=1 is clean.
    • The new tests pass: polyfills, legacy-css (12), legacy-css-runtime (5), and the app lock.
    • The full make test-ui run passed 2674 of 2687, with 8 skipped and 5 failures in timing-sensitive tests. One of those, one-client's broker sign-in, fails just as often on plain develop under load. The other four didn't fail again when their files were rerun on their own.

Known gaps on 80–87

  • color-mix() of tokens defined only on a subtree (a chat side's own tokens) or of currentColor: 4 uses, which stay unset as before.
  • aspect-ratio beside percentage widths on the call/camera video tiles, which keep their natural shape instead.
  • A few pixels of vertical rhythm here and there.

Note for the AI that merges this

This branch is based on develop at dc75e941. Before merging:

  1. Rebase onto the current develop and rerun make check STRICT=1 and make test-ui.
  2. Read every commit on develop since dc75e941 for code that needs the same treatment:
    • New JS built-ins newer than Chromium 80: e.g. toSorted, findLast, Object.hasOwn, structuredClone outside the mock, replaceAll, Promise.withResolvers, groupBy, iterator helpers, AbortSignal.timeout. Add each one to ui/src/polyfills.ts, or write it out. test/polyfills.test.ts's PAST_80 list only catches what it names, so extend it for anything new.
    • New CSS the plugin doesn't lower: :has(), :is(), CSS nesting, subgrid, @layer, light-dark(), relative colours, text-wrap, translate/scale/rotate as properties, inert used for anything other than the lock.
    • Already handled automatically: new flex gap, inset, color-mix() on root tokens, and centring grids.
    • Needs a hand fallback, like the ones in media-browser.ts, theme-picker.ts and message-row.ts:
      • aspect-ratio beside a % width
      • gap, aspect-ratio or color-mix() in inline style= strings, which the plugin can't see
      • color-mix() of tokens defined only inside a component
    • New tabindex="-1" containers focused from script are covered by sharedStyles, provided the component includes sharedStyles.
  3. Check the result in Chromium 80: make build-mock, serve build/ui-mock, and open it in the Chromium snapshot Linux_x64/722234. The pages to look at are the chat list, an open chat, a sheet, and Preferences › Appearance.
  4. New user-visible strings need entries in all catalogues (tools/i18n-extract.mjs --check --strict).

🤖 Generated with Claude Code

The app on Android System WebView 80 to 110, which covers Android 11's stock WebView (83). ## What was wrong - **It didn't start on WebView 80–91.** `Array.prototype.at` (Chromium 92) and `Array.prototype.with` (110) threw before the first screen, which then showed only "another.im failed to start". - **The lock screen leaked keyboard focus on 80–101.** It relies on `inert` (102), so on older engines Tab reached fields and a call's Answer button behind the PIN. - **The layout broke on 80–87.** Flex `gap` (84), `inset` (87), `aspect-ratio` (88), `:where()` (88), `:focus-visible` inside selector lists (86) and `color-mix()` (111) are all missing there. The visible results: - spacing collapsed - overlays lost their position - round icon buttons lost their centring: a `<button>` there can't be a grid container - the `--chat-*` tokens in `theme.css` were dropped together with their `:where()` rule - mixed colours disappeared ## What this changes 1. **`ui/src/polyfills.ts`**, imported first by `main.ts`, adds `at()` and `with()` where missing, plus `structuredClone` in mock builds. `test/polyfills.test.ts` also scans `src/` for other post-80 built-ins, so a new one fails the tests. 2. **The app shell** pulls focus back to the lock where `inert` is missing. 3. **`ui/legacy-css.ts`** is a Vite plugin that runs in production builds only. It rewrites the Lit `css` literals and the global sheets: - **Exact rewrites:** longhands for `inset` and `margin-inline`/`padding-block`, `:focus-visible` split out of selector lists, `:where()` unwrapped, a `vh` fallback before `dvh`, `::-webkit-scrollbar` beside `scrollbar-width`, and a viewport `@media` copy of `@container`. - **`color-mix(…)`** becomes `var(--cm-<hash>, color-mix(…))`. - **`aspect-ratio`** beside a width that doesn't depend on the parent gets a height under `@supports not (aspect-ratio: 1)`. - **Flex `gap` and centring grids** get margin fallbacks and flex-column fallbacks, scoped to `:host-context(.no-flex-gap)` and `.no-button-grid`. 4. **`ui/src/legacy-css.ts`** runs in the page. It sets `no-flex-gap`, `no-button-grid` and `no-focus-visible` on `<html>` only where the engine lacks the feature. It also computes every `--cm-*` from the theme on `:root`, and recomputes on theme or accent changes. 5. **Engines that have all of this get the same styles as before.** The dev server and the tests see the sources as written. `build.cssTarget: 'chrome80'` stops the minifier from folding `inset` back together. 6. **Hand fallbacks** cover what the plugin can't size: the media grid, theme swatches, album tiles and picture placeholders (`shotBox`/`clipBox`/stickers). 7. **`aim-webview-banner`** shows a note at the top of the chat list on Chromium/WebView older than 88. In the app it has an "Update…" button that opens Android System WebView on Play. Closing it is remembered per WebView version. Its strings are in all catalogues. ## Tested - **Chromium 80.0.3987.0** (snapshot r722234), headless and with a window, on `make build-mock`. Phone and desktop sizes, the chat list, an open chat, sheets, Preferences › Appearance and Night mode were compared side by side with Chromium 154; they match to within a few pixels. - **The debug apk on an API 30 emulator** (WebView 83.0.4103.106) renders correctly up to login. After login the emulator's arm64 translator (NDK translation 0.2.2) hits SIGILL on an `fcvtzu d1, d0` in `libanotherim_tacky.so`. That's a gap in the emulator, not the app; a real arm64 phone runs the instruction natively. It still needs a check on a real Android 11 phone. - **Automated checks:** - `make check STRICT=1` is clean. - The new tests pass: polyfills, `legacy-css` (12), `legacy-css-runtime` (5), and the app lock. - The full `make test-ui` run passed 2674 of 2687, with 8 skipped and 5 failures in timing-sensitive tests. One of those, `one-client`'s broker sign-in, fails just as often on plain `develop` under load. The other four didn't fail again when their files were rerun on their own. ## Known gaps on 80–87 - `color-mix()` of tokens defined only on a subtree (a chat side's own tokens) or of `currentColor`: 4 uses, which stay unset as before. - `aspect-ratio` beside percentage widths on the call/camera video tiles, which keep their natural shape instead. - A few pixels of vertical rhythm here and there. --- ## Note for the AI that merges this This branch is based on `develop` at `dc75e941`. Before merging: 1. **Rebase onto the current `develop`** and rerun `make check STRICT=1` and `make test-ui`. 2. **Read every commit on `develop` since `dc75e941`** for code that needs the same treatment: - **New JS built-ins newer than Chromium 80:** e.g. `toSorted`, `findLast`, `Object.hasOwn`, `structuredClone` outside the mock, `replaceAll`, `Promise.withResolvers`, `groupBy`, iterator helpers, `AbortSignal.timeout`. Add each one to `ui/src/polyfills.ts`, or write it out. `test/polyfills.test.ts`'s `PAST_80` list only catches what it names, so extend it for anything new. - **New CSS the plugin doesn't lower:** `:has()`, `:is()`, CSS nesting, `subgrid`, `@layer`, `light-dark()`, relative colours, `text-wrap`, `translate`/`scale`/`rotate` as properties, `inert` used for anything other than the lock. - **Already handled automatically:** new flex `gap`, `inset`, `color-mix()` on root tokens, and centring grids. - **Needs a hand fallback, like the ones in `media-browser.ts`, `theme-picker.ts` and `message-row.ts`:** - `aspect-ratio` beside a `%` width - `gap`, `aspect-ratio` or `color-mix()` in inline `style=` strings, which the plugin can't see - `color-mix()` of tokens defined only inside a component - **New `tabindex="-1"` containers** focused from script are covered by `sharedStyles`, provided the component includes `sharedStyles`. 3. **Check the result in Chromium 80:** `make build-mock`, serve `build/ui-mock`, and open it in the Chromium snapshot `Linux_x64/722234`. The pages to look at are the chat list, an open chat, a sheet, and Preferences › Appearance. 4. **New user-visible strings** need entries in all catalogues (`tools/i18n-extract.mjs --check --strict`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The bundle is lowered to ES2020 syntax, which WebView 80 parses, but
nothing supplies library methods added since. Array.prototype.at
(Chromium 92) and Array.prototype.with (110) threw before the first
screen, so Android 11's stock WebView (83) showed only "another.im
failed to start".

Add ui/src/polyfills.ts, imported first by main.ts, which defines at()
and with() where the engine lacks them, and structuredClone for the
mock backend's builds. Its test also reads the sources for calls past
Chromium 80 that it does not cover, so a new one fails the suite rather
than an old phone.

The app lock keeps Tab out of the window under its cover with `inert`
(Chromium 102). Without it the keyboard reached fields and a call's
Answer behind the PIN, so focus landing under the cover now goes back
to the lock where inert is missing.

Assisted-by: Claude:opus-5-5
The layout uses CSS newer than Android 11's stock WebView (83): flex
gap, inset, aspect-ratio, :where(), :focus-visible in selector lists and
color-mix(), among others. On WebView 80-87 spacing collapsed, overlays
lost their place, round icon buttons lost their centring (a <button>
there is no grid container), the chat tokens in theme.css were dropped
with their :where() rule, and mixed colours vanished.

Add a build-only Vite plugin, ui/legacy-css.ts, that rewrites the Lit
css literals and the global sheets: longhands for inset and the logical
shorthands, :focus-visible split out of selector lists, :where()
unwrapped, color-mix() behind a --cm-* custom property, heights for
aspect-ratio beside a width the parent does not set, and margin and
flex-column fallbacks for flex gap and centring grids under a class on
<html>. ui/src/legacy-css.ts sets those classes only where the engine
lacks the feature and computes the colour mixes from the theme on the
root, again on every theme or accent change. Engines that have it all
see the same styles as before; the dev server and the tests see the
sources as written.

The aspect-ratio boxes the plugin cannot size (media grid, theme
swatches, album tiles, picture placeholders) get hand fallbacks. The
global sheets are minified for chrome80 so inset is not folded back.
A note at the top of the chat list suggests updating Android System
WebView where it is older than 88.

Assisted-by: Claude:opus-5-5
ann closed this pull request 2026-10-27 08:31:24 +00:00

Pull request closed

Sign in to join this conversation.
No description provided.