Summary
If GutenbergView never finishes loading (e.g. an upstream 404 on wp-block-editor/v1/settings, network failure mid-init, or any other case where the JS bridge never becomes available), the editor activity gets stuck — back press doesn't dismiss it, the "saving" progress dialog stays open, and the only way out is to force-stop the app.
Repro
Surfaced while testing #22579 against a WP.com Atomic site (automatticwidgets.wpcomstaging.com, Storefront theme) where wp-block-editor/v1/settings returns 404 and GutenbergView stalls at progress 81/100.
- Open the editor on a site where the underlying GBKit init can't complete
- Wait for the editor to stall (visible: progress bar never advances past loading)
- Tap the X / back button
- Nothing happens — saving dialog appears and the activity never finishes
Root cause
GutenbergKitEditorFragment.getTitleAndContent blocks on CountDownLatch.await() with no timeout:
val latch = CountDownLatch(1)
gutenbergView.getTitleAndContent(originalContent, object : TitleAndContentCallback {
override fun onResult(title, content) {
result[0] = Pair(title, content)
latch.countDown()
}
}, completeComposition)
val finalResult = try {
latch.await() // never returns if the bridge never replies
result[0]
} catch (e: InterruptedException) { ... }
The save-on-exit flow (savePostAndOptionallyFinish → updateAndSavePostAsyncOnEditorExit → updateAndSavePostAsync → updateFromEditor → getTitleAndContent) sits on this latch waiting for TitleAndContentCallback.onResult to fire from JS. When GutenbergView hasn't finished loading, the bridge call is dropped silently — GBKit internally guards on its private isEditorLoaded field and logs "You can't change the editor content until it has loaded". latch.countDown() never fires; the completion lambda that would call storePostViewModel.finish(it) never runs; the activity never finishes.
Introduced incidentally by #22764 ("Upgrade GutenbergKit from v0.11.1 to v0.15.2"), which switched the bridge call from sync to async-with-latch.
Proposed fix
Two layers, both host-side. GBKit doesn't currently expose a public ready-state property — only the setEditorDidBecomeAvailable {} callback.
- Track ready state in the fragment. Flip a local
editorReady flag to true inside the setEditorDidBecomeAvailable {} listener we already register. Short-circuit getTitleAndContent to return Pair("", "") when the flag is false.
- Bound the latch. Replace
latch.await() with latch.await(N, TimeUnit.SECONDS) (5s is probably reasonable) as a belt-and-braces guard for any race window. On timeout, return the same Pair("", "") fallback.
Net behaviour: editor stall → empty title/content on exit → no save attempted → activity finishes immediately. Honest outcome for a session where nothing was loaded.
Related
Summary
If
GutenbergViewnever finishes loading (e.g. an upstream 404 onwp-block-editor/v1/settings, network failure mid-init, or any other case where the JS bridge never becomes available), the editor activity gets stuck — back press doesn't dismiss it, the "saving" progress dialog stays open, and the only way out is to force-stop the app.Repro
Surfaced while testing #22579 against a WP.com Atomic site (
automatticwidgets.wpcomstaging.com, Storefront theme) wherewp-block-editor/v1/settingsreturns 404 andGutenbergViewstalls at progress 81/100.Root cause
GutenbergKitEditorFragment.getTitleAndContentblocks onCountDownLatch.await()with no timeout:The save-on-exit flow (
savePostAndOptionallyFinish→updateAndSavePostAsyncOnEditorExit→updateAndSavePostAsync→updateFromEditor→getTitleAndContent) sits on this latch waiting forTitleAndContentCallback.onResultto fire from JS. WhenGutenbergViewhasn't finished loading, the bridge call is dropped silently — GBKit internally guards on its privateisEditorLoadedfield and logs"You can't change the editor content until it has loaded".latch.countDown()never fires; the completion lambda that would callstorePostViewModel.finish(it)never runs; the activity never finishes.Introduced incidentally by #22764 ("Upgrade GutenbergKit from v0.11.1 to v0.15.2"), which switched the bridge call from sync to async-with-latch.
Proposed fix
Two layers, both host-side. GBKit doesn't currently expose a public ready-state property — only the
setEditorDidBecomeAvailable {}callback.editorReadyflag totrueinside thesetEditorDidBecomeAvailable {}listener we already register. Short-circuitgetTitleAndContentto returnPair("", "")when the flag is false.latch.await()withlatch.await(N, TimeUnit.SECONDS)(5s is probably reasonable) as a belt-and-braces guard for any race window. On timeout, return the samePair("", "")fallback.Net behaviour: editor stall → empty title/content on exit → no save attempted → activity finishes immediately. Honest outcome for a session where nothing was loaded.
Related