PIR: Fix shared webview being destroyed in PIR debug runs#7755
PIR: Fix shared webview being destroyed in PIR debug runs#7755
Conversation
| logcat { "PIR-SCAN: Start thread=${Thread.currentThread().name}, profile=$profileQuery and step=$step" } | ||
| runners[0].startOn(webView, profileQuery, listOf(step)) | ||
| runners[0].stop() | ||
| // don't call stop() here to avoid destroying the WebView as it's reused for all steps |
There was a problem hiding this comment.
Shared webview still destroyed on next run
Medium Severity
Removing per-step stop() leaves the debug PirActionsRunner alive in runners, so later cleanup (cleanPreviousRun/cleanRunners) still calls stop() and destroy() on the externally owned webView. This shifts destruction to the next job instead of delegating lifecycle ownership to the activity.
Additional Locations (1)
| logcat { "PIR-SCAN: Start thread=${Thread.currentThread().name}, profile=$profileQuery and step=$step" } | ||
| runners[0].startOn(webView, profileQuery, listOf(step)) | ||
| runners[0].stop() | ||
| // don't call stop() here to avoid destroying the WebView as it's reused for all steps |
There was a problem hiding this comment.
Debug runner retains shared WebView reference
Medium Severity
With stop() removed, the debug PirActionsRunner keeps detachedWebView pointing to the activity-owned webView after completion, and the runner stays in runners. This can retain the activity Context and WebView resources past the debug session, creating a long-lived memory/resource leak until another cleanup path runs.
Additional Locations (1)
| logcat { "PIR-SCAN: Start thread=${Thread.currentThread().name}, profile=$profileQuery and step=$step" } | ||
| runners[0].startOn(webView, profileQuery, listOf(step)) | ||
| runners[0].stop() | ||
| // don't call stop() here to avoid destroying the WebView as it's reused for all steps |
There was a problem hiding this comment.
Runner coroutine not canceled after debug completion
Medium Severity
Skipping runners[0].stop() leaves the final engineJob from startOn active after debug runs. awaitResult() keeps collecting sideEffect until stop() cancels it, so the runner keeps a live coroutine in app scope after completion, which can leak work and keep stale runner state alive.


Task/Issue URL: https://app.asana.com/1/137249556945/task/1212923242657599
Description
Shared WebView that is passed to a PIR runner was destroyed between jobs. This delegates the cleanup to the owner of the WebView (activity) instead of to the runner.
Steps to test this PR
QA optional - can run debug scans or opt-outs
UI changes
No UI changes
Note
Low Risk
Small, debug-only control-flow change that avoids calling runner cleanup which destroys the shared WebView; low blast radius outside debug runs.
Overview
Prevents PIR debug scan/opt-out flows from destroying the caller-provided (shared)
WebViewbetween sequential steps.In
RealPirScan.debugExecuteandRealPirOptOut.debugExecute, removes the per-steprunners[0].stop()call (with an explanatory comment), leaving WebView lifecycle/cleanup to the debug WebView owner while still cleaning WebView data at the end of the run.Written by Cursor Bugbot for commit a3343a9. This will update automatically on new commits. Configure here.