fix: avoid NPE when canceling a JavaScript invocation of a closed UI - #25095
Conversation
Invocations owned by an invisible component are retained in the UI's queue and get a detach listener registered for them. Registering that listener installs a handler on the invocation itself, and the handler stays attached to the invocation for the rest of its lifetime, since there is no way to unsubscribe it. A component that keeps the PendingJavaScriptResult and cancels it after being reused in another UI therefore runs the handler installed by the closed UI, which dereferences its cleared session. Return early when the UI no longer has a session, as its invocation queue has already been released by then. Also release the retained invocations when the UI is closed, so that the queue and the detach listener registrations on the state nodes do not outlive the UI when a detach listener fails and prevents the ones after it from running. Fixes #25092
|
|
Needs manual pick to 24.10 and 24.9 because of JUnit 4 |
|
@claude review this pull request |
|
Claude finished @mshabarov's task in 1m 46s —— View job PR Review: avoid NPE when canceling a JavaScript invocation of a closed UI
SummaryThe fix is correct and well-targeted. It addresses the NPE in two complementary ways, both consistent with the existing lifecycle design:
The test coverage is thorough and reproduces the reported failure modes precisely (retained-invocation cancel, throwing detach listener, and element reuse across UIs). Correctness verificationOrdering in No double-processing on normal close. Detaching the root node ( ConcurrentModification is handled. Post-close callback is safe. After release, Observation (non-blocking): potential memory leak of the closed
|
…25095) (CP: 25.1) (#25212) This PR cherry-picks changes from the original PR #25095 to branch 25.1. --- #### Original PR description > Invocations owned by an invisible component are retained in the UI's queue and get a detach listener registered for them. Registering that listener installs a handler on the invocation itself, and the handler stays attached to the invocation for the rest of its lifetime, since there is no way to unsubscribe it. > > A component that keeps the PendingJavaScriptResult and cancels it after being reused in another UI therefore runs the handler installed by the closed UI, which dereferences its cleared session. Return early when the UI no longer has a session, as its invocation queue has already been released by then. > > Also release the retained invocations when the UI is closed, so that the queue and the detach listener registrations on the state nodes do not outlive the UI when a detach listener fails and prevents the ones after it from running. > > Fixes #25092 Co-authored-by: Marco Collovati <marco@vaadin.com>
…25095) (CP: 25.2) (#25211) This PR cherry-picks changes from the original PR #25095 to branch 25.2. --- #### Original PR description > Invocations owned by an invisible component are retained in the UI's queue and get a detach listener registered for them. Registering that listener installs a handler on the invocation itself, and the handler stays attached to the invocation for the rest of its lifetime, since there is no way to unsubscribe it. > > A component that keeps the PendingJavaScriptResult and cancels it after being reused in another UI therefore runs the handler installed by the closed UI, which dereferences its cleared session. Return early when the UI no longer has a session, as its invocation queue has already been released by then. > > Also release the retained invocations when the UI is closed, so that the queue and the detach listener registrations on the state nodes do not outlive the UI when a detach listener fails and prevents the ones after it from running. > > Fixes #25092 Co-authored-by: Marco Collovati <marco@vaadin.com>



Invocations owned by an invisible component are retained in the UI's queue and get a detach listener registered for them. Registering that listener installs a handler on the invocation itself, and the handler stays attached to the invocation for the rest of its lifetime, since there is no way to unsubscribe it.
A component that keeps the PendingJavaScriptResult and cancels it after being reused in another UI therefore runs the handler installed by the closed UI, which dereferences its cleared session. Return early when the UI no longer has a session, as its invocation queue has already been released by then.
Also release the retained invocations when the UI is closed, so that the queue and the detach listener registrations on the state nodes do not outlive the UI when a detach listener fails and prevents the ones after it from running.
Fixes #25092