Skip to content

feat(web): implement image decoding throttling for HTML images - #186032

Merged
auto-submit[bot] merged 7 commits into
flutter:masterfrom
harryterkelsen:web-image-decode-with-createImageBitmap
May 7, 2026
Merged

auto-submit[bot] merged 7 commits into
flutter:masterfrom
harryterkelsen:web-image-decode-with-createImageBitmap

Conversation

@harryterkelsen

Copy link
Copy Markdown
Contributor

Introduces a centralized ImageDecodingManager to coordinate the concurrency and memory footprint of HTMLImageElement.decode() calls. This prevents resource exhaustion and silent crashes on browsers like iOS Safari when handling high volumes of large image assets.

  • Added ImageDecodingManager to enforce safety limits (20 decodes / 128MB).
  • Refactored HtmlImageElementCodec to use a multi-phase throttled decode.
  • Implemented aggressive resource reclamation by clearing img.src on disposal.
  • Added unit tests for the manager and updated codec integration tests.

Largely alleviates #152709

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Introduces a centralized ImageDecodingManager to coordinate the concurrency
and memory footprint of HTMLImageElement.decode() calls. This prevents
resource exhaustion and silent crashes on browsers like iOS Safari when
handling high volumes of large image assets.

- Added ImageDecodingManager to enforce safety limits (20 decodes / 128MB).
- Refactored HtmlImageElementCodec to use a multi-phase throttled decode.
- Implemented aggressive resource reclamation by clearing img.src on disposal.
- Added unit tests for the manager and updated codec integration tests.
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 5, 2026
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically labels May 5, 2026
@harryterkelsen
harryterkelsen requested a review from mdebbar May 5, 2026 00:02

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an ImageDecodingManager to throttle HTMLImageElement.decode() calls, addressing resource exhaustion and crashes on browsers like iOS Safari. The HtmlImageElementCodec is refactored into a multi-phase asynchronous process that manages concurrency and memory limits through a centralized FIFO queue. Feedback focuses on improving resource reclamation by clearing image sources during failures and enhancing error reporting by wrapping decoding operations in descriptive exceptions.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/html_image_element_codec.dart Outdated
@github-actions github-actions Bot removed the CICD Run CI/CD label May 5, 2026
mdebbar
mdebbar previously approved these changes May 5, 2026
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/html_image_element_codec.dart Outdated
@github-actions github-actions Bot removed the CICD Run CI/CD label May 5, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label May 5, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 7, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label May 7, 2026
@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label May 7, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue May 7, 2026
Merged via the queue into flutter:master with commit a05715c May 7, 2026
200 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 7, 2026
jhbruhn added a commit to jhbruhn/zugvogel that referenced this pull request Aug 21, 2026
…n Chrome

0ec9641 hat dieses Repo auf 3.47.1 gehoben, "in lockstep with both apps".
Das war die falsche Richtung: 3.47 hört auf Firefox und Safari (iPhone
eingeschlossen) still auf, Bilder zu zeichnen — ein Thumbnail rendert
einmal und bleibt bei jedem späteren Repaint leer, bis die Seite neu
geladen wird. CachedFileImage ist die Stelle, an der es auftaucht, also
erben es beide Apps, und nichts in diesem Repo kann es reparieren.

Also fordern die Pubspecs hier wieder `^3.44.0`. Das ist der Punkt und
nicht bloß Kosmetik: solange sie ^3.47.0 fordern, bricht der NÄCHSTE
Pin-Bump den Build jeder App, die auf 3.44 zurück ist — federfall entkam
nur, weil sein Pin älter ist als 0ec9641, und eiermann entkam gar nicht.

Vier Stellen, alle zusammen: die zwei Pubspecs, CI, und die
Versionszeilen in README und CLAUDE.md. Dazu zurück, was 0ec9641 am Code
geändert hatte: auf 3.44 sind die Animationsfutures nicht
`@awaitNotRequired`, also greift wieder `discarded_futures` und die vier
`unawaited(...)` müssen stehen (mit `dart:async` in offline_notice.dart).
Gemessen: ohne sie meldet `flutter analyze` genau diese vier Stellen.

Die AUSFÜHRLICHE Analyse des Fehlers in CLAUDE.md — skwasm gegen
CanvasKit, flutter.js' wasmAllowList, flutter/flutter#186032 und der Fix
in #188573 — stammt aus einer parallelen Sitzung. Ihr Commit ging bei
einem Reset in diesem Arbeitsbaum verloren; der Text ist aus dem noch
vorhandenen Objekt wiederhergestellt und steht hier unverändert, bis auf
den Schlussabsatz: die Falle, die er beschreibt ("die nächste Pin-Bump
bricht federfalls Build"), ist genau die, die dieser Commit zumacht.

Verifiziert auf 3.44.3: analyze sauber, 127 + 68 + 50 + 48 Tests grün,
`dart format --set-exit-if-changed` sauber.

Ziel bleibt 3.48 (flutter/flutter#188573). Auf 3.47 wird nicht gewartet.

Co-Authored-By: Claude Opus 5 <[email protected]>
jhbruhn added a commit to jhbruhn/federfall that referenced this pull request Aug 21, 2026
This reverts commit 7da167f.

3.47 silently loses images on Firefox and Safari — iPhone included. A
thumbnail paints once and then draws nothing on any later repaint, until a
full page reload. Chrome is unaffected, which is what made it easy to miss.

Two gates decide it, and Firefox/Safari fail both. `--wasm` ships skwasm plus
a CanvasKit fallback, and flutter.js's default wasmAllowList is
{blink:true, gecko:false, webkit:false} — so only Chrome runs skwasm, which
copies decoded bytes eagerly into an ImageBitmap and owns them. Everything
else falls back to CanvasKit, where an image decoded from bytes is a LAZY
SkImage over the <img> element the codec made, re-uploading its texture on
every paint. flutter/flutter#186032 then taught the engine to reclaim that
element aggressively — `ImageElementImageSource._doClose()` went from a
deliberate no-op ("let the browser garbage collect it") to
`imageElement.src = ''`. Its stated purpose was to stop iOS Safari crashing
on many large images, so the regression lands squarely in the case the change
was written for.

Confirmed by A/B, not by reading: the same commit built `--wasm --release`
against the same backend, served with the same COOP/COEP headers, breaks on
3.47.1 and works on 3.44.3. Three app-side workarounds were tried and all
failed — clamping the decode below the 200x200 thumb, raising the ImageCache
ceilings, and waiting out the new 30s decode timeout — so this is not
something federfall can hold at arm's length. The exact defect is still
unpinned; a lazy texture whose source is freed on refcount-zero is the
suspect, but ImageBitmapImageSource frees its source the same way and is fine
on both versions, so that story is incomplete.

3.48 fixes it. flutter/flutter#188573 moves image decoding and lifetime onto
the shared frontend skwasm already used, and 3.48.0-0.2.pre renders correctly
in Firefox on the same harness. It is a refactor, not a targeted fix, so a
3.47.2 backport is not worth waiting for — and 3.47 offered nothing this app
wanted anyway, only higher Java and iOS floors. So: back to 3.44.3, and go
straight to 3.48 when it is stable (the cadence puts that around November).

Co-Authored-By: Claude Opus 5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-web Web applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants