You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The editor's dependency fetch has no total-duration bound and no cancellation path. An editor the user has backed out of can hold its WKWebView until the network gives up on its own.
No cooperative cancellation.grep -rn "Task.isCancelled\|checkCancellation\|withTaskCancellationHandler" over Sources/Services/ and Stores/EditorAssetLibrary.swift returns nothing. Even with a handle, cancellation only ever took effect because URLSession's own methods are cancellation-aware.
No request timeout.EditorViewController.swift:215-218 builds EditorHTTPClient(urlSession: URLSession.shared, authHeader:); requestTimeout defaults to nil (EditorHTTPClient.swift:101) and configureRequest only sets timeoutInterval when non-nil (:186-188). So the only ceilings are URLSession.shared's defaults: 60 s of inactivity per request, and a 7-day resource timeout.
How it bites
Cold cache, a connection that is slow but not dead (hotel wifi, congested cellular, captive portal). The user opens a post, watches the spinner, backs out. The editor, its WKWebView, its WKWebViewConfiguration, the GutenbergEditorController and the EditorService all stay alive until the fetch unwinds — up to ~60 s. Repeated attempts stack. When the fetch finally fails, the host receives didFailToLoad for an editor it dismissed a minute ago.
Why the obvious fix is probably wrong
Setting requestTimeout looks like the answer and likely isn't. EditorHTTPClient.swift:163-172 already documents the hazard: it is an inactivity timer, and a short value "would also fire during the silent window while WordPress synchronously generates image sub-sizes inside POST /wp/v2/media, orphaning the attachment server-side and duplicating it on retry." That comment calls out "no total-duration cap" as the deliberate current design, mirroring Android.
A total-duration deadline on EditorService.prepare() is the better-shaped knob.
The parent problem
The reason nothing can cancel this is that the endpoint is terminal. self.error is written in exactly one place (EditorViewController.swift:386) and never cleared; displayError (:1033-1043) renders a ContentUnavailableView with no retry action; hideError() (:1046) has zero callers anywhere in ios/.
Make the load restartable — a retry affordance on the error view, plus a public reload() for hosts — and the calculus inverts. A wrong cancellation costs a restart instead of the session, which is exactly the condition #649 names for adopting its ancestor-walk teardown detector. That is the fix worth doing; the deadline is the stopgap.
The editor's dependency fetch has no total-duration bound and no cancellation path. An editor the user has backed out of can hold its
WKWebViewuntil the network gives up on its own.Detail
Three things compose:
dependencyTaskHandleand theviewDidDisappearcancel, deliberately — see that PR for why.deinitis unreachable while the task runs, becauseawait self?.prepareEditor()holds a strongselfacross the call.grep -rn "Task.isCancelled\|checkCancellation\|withTaskCancellationHandler"overSources/Services/andStores/EditorAssetLibrary.swiftreturns nothing. Even with a handle, cancellation only ever took effect becauseURLSession's own methods are cancellation-aware.EditorViewController.swift:215-218buildsEditorHTTPClient(urlSession: URLSession.shared, authHeader:);requestTimeoutdefaults tonil(EditorHTTPClient.swift:101) andconfigureRequestonly setstimeoutIntervalwhen non-nil (:186-188). So the only ceilings areURLSession.shared's defaults: 60 s of inactivity per request, and a 7-day resource timeout.How it bites
Cold cache, a connection that is slow but not dead (hotel wifi, congested cellular, captive portal). The user opens a post, watches the spinner, backs out. The editor, its
WKWebView, itsWKWebViewConfiguration, theGutenbergEditorControllerand theEditorServiceall stay alive until the fetch unwinds — up to ~60 s. Repeated attempts stack. When the fetch finally fails, the host receivesdidFailToLoadfor an editor it dismissed a minute ago.Why the obvious fix is probably wrong
Setting
requestTimeoutlooks like the answer and likely isn't.EditorHTTPClient.swift:163-172already documents the hazard: it is an inactivity timer, and a short value "would also fire during the silent window while WordPress synchronously generates image sub-sizes insidePOST /wp/v2/media, orphaning the attachment server-side and duplicating it on retry." That comment calls out "no total-duration cap" as the deliberate current design, mirroring Android.A total-duration deadline on
EditorService.prepare()is the better-shaped knob.The parent problem
The reason nothing can cancel this is that the endpoint is terminal.
self.erroris written in exactly one place (EditorViewController.swift:386) and never cleared;displayError(:1033-1043) renders aContentUnavailableViewwith no retry action;hideError()(:1046) has zero callers anywhere inios/.Make the load restartable — a retry affordance on the error view, plus a public
reload()for hosts — and the calculus inverts. A wrong cancellation costs a restart instead of the session, which is exactly the condition #649 names for adopting its ancestor-walk teardown detector. That is the fix worth doing; the deadline is the stopgap.Found while reviewing #651.