feat(android): add HTML-to-Bitmap renderer#470
Merged
Conversation
e9b6fb7 to
feb1c4f
Compare
88d07a8 to
932e7ed
Compare
feb1c4f to
9642d40
Compare
4 tasks
9642d40 to
bcf5343
Compare
3b091fd to
3bfc76e
Compare
3 tasks
f685d27 to
9a51157
Compare
Adds HtmlToBitmapRenderer, an off-screen WebView wrapper that loads an HTML string and snapshots the laid-out content to a Bitmap. Mirrors the iOS HTMLWebViewRenderer MVP: no caching, no pooling — each call creates and destroys its own WebView. A follow-up will layer caching and WebView reuse on top. All WebView interaction is marshalled onto the main thread internally, so callers can invoke the suspending API from any dispatcher.
After onPageFinished, poll Array.from(document.images).every(i => i.complete) at 50ms intervals up to a 4s soft timeout. Catches the common case where <img> tags with srcset, lazy decoding, or async DOM insertion are still in flight when window.load fires. On timeout, proceed with whatever has loaded rather than fail the render — a partial thumbnail is better than none. Same race exists on iOS; identical JS would close it there.
Adds JUnit instrumented tests that exercise the renderer against a real Android WebView on an emulator or device — the unit-test layer can't stand in for this because Robolectric's WebView shadow doesn't raster. Runs in CI via the `:android: Test Android Library Instrumented` step on the `mac-metal` queue. Each test writes its rendered PNG under the app's external cache dir and logs the absolute path under the `GBKRendererTest` tag so failures can be inspected with `adb pull` + an image viewer.
Without a parent window, Chromium-backed WebViews render into a GPU texture that WebView.draw(canvas) can't reach, producing blank bitmaps for CSS-only content. Force LAYER_TYPE_SOFTWARE and add a short post-layout settle so the software layer actually paints before pixels are read.
9a51157 to
fc4fe18
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Groundwork for rendering block patterns as thumbnails in the native Android inserter. Introduces
HtmlToBitmapRenderer: an off-screenWebViewthat loads an HTML string and snapshots the laid-out content to aBitmapviaCanvas.draw(). Lays out at the viewport width in device pixels, awaitsonPageFinished, pollsdocument.images.every(i => i.complete)so async-loaded images aren't missing from the snapshot, queriesdocument.documentElement.scrollHeight, re-lays out to content height, then draws into a scaled bitmap. Never upscales.Mirrors the iOS
HTMLWebViewRendererMVP underios/Sources/GutenbergKit/Sources/Views/HTMLPreview/. No consumer yet — theBlockInserterDialogwiring that turns patterns into previews lands separately.Design notes
WebViewin afinally. Pooling is a deliberate follow-up — iOS pools becauseWKWebViewallocation is expensive, but we want to see the simpler model exercised before adding a pool.setLayerType(LAYER_TYPE_SOFTWARE)forces the WebView to raster onto a software canvas rather than a GPU texture (whichWebView.draw()can't reach off-screen). After re-layout wedelay(POST_LAYOUT_SETTLE_MS)because there's no parent-window invalidation cycle to drive a repaint.Known gaps
WebViewdoesn't actually render. Coverage comes from theandroidTest/instrumented tests, which run in CI via the:android: Test Android Library Instrumentedstep (ci(android): run Gutenberg-module instrumented tests #472).Test plan
./gradlew :Gutenberg:detekt :Gutenberg:lintDebug :Gutenberg:compileDebugKotlin :Gutenberg:test—BUILD SUCCESSFUL./gradlew :Gutenberg:connectedAndroidTest):android: Test Android Library Instrumentedstep passes on this PRFollow-ups
CachingHtmlToBitmapRenderer+BlobCache/FileBlobCache) on top of the renderer.BlockInserterDialogso the renderer is exercised end-to-end.