HtmlScreenshot: headless Chrome launches use a private, temporary user-data-dir - #320
HtmlScreenshot: headless Chrome launches use a private, temporary user-data-dir#320hesong12 wants to merge 1 commit into
Conversation
Every headless launch previously used Chrome's own default profile resolution, so a screenshot could contend with a Chrome window the host already has open, and it could not run at all under a sandbox that denies writes to the default profile directory. Point every headless launch (DumpDom, CaptureChromeSized, TryChrome, RunChromeCapture) at a fresh --user-data-dir under the temp directory and delete it once that launch exits.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 540c91059a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return (p.ExitCode == 0, errTask.GetAwaiter().GetResult()); | ||
| } | ||
| catch { return (false, null); } | ||
| finally { CleanupChromeUserDataDir(userDataDir); } |
There was a problem hiding this comment.
Wait for Chrome to exit before deleting its profile
When a Chrome capture times out, both this method and the RunBinary path call Kill(true) and return immediately; process termination is asynchronous, so this finally can attempt Directory.Delete while Chrome or its children still hold files in the profile. On platforms such as Windows the deletion then fails, and because CleanupChromeUserDataDir suppresses the exception, repeated timed-out captures permanently accumulate these supposedly temporary directories. Wait for the killed process tree to exit before running the cleanup.
Useful? React with 👍 / 👎.
Files:
src/officecli/Core/HtmlScreenshot.cs(DumpDom,CaptureChromeSized,TryChrome,RunChromeCapture)Summary
Every headless Chrome launch in this file (
DumpDom,CaptureChromeSized,TryChrome,RunChromeCapture) previously relied on Chrome's own defaultprofile-directory resolution. Two consequences:
on the same default profile.
profile directory (a real constraint for hosts that run OfficeCLI inside
a restricted exec sandbox).
This PR adds a fresh, private
--user-data-dirunder the OS temp directoryto all four headless launch sites, and deletes it once that specific launch
exits:
Each call site: generate the dir path before building the argument list, add
--user-data-dir={dir}alongside the existing--headless=newflags, anddelete it in a
finallyaround the existing try/catch (or a newtry/finally where the site had none) so it's cleaned up on every exit path
— success, failure, or the existing timeout-kill branch.
Why
Same sandboxed-host motivation as the two sibling PRs: the host needing this
runs OfficeCLI's headless-screenshot path inside a restricted environment
and cannot guarantee the default Chrome profile directory is writable or
uncontended.
Validation
Toolchain: same scratch .NET 10 SDK as the sibling PRs.
dotnet build src/officecli -c Debugsucceeds.Ran a screenshot while another chrome-family process already had the real
default profile directory (
~/Library/Application Support/Google/Chrome)open (a background
--headless=newinstance against the unmodifieddefault profile, holding its
SingletonSocket), confirming this branch'sscreenshot still succeeds:
officecli create t.docx officecli add t.docx /body --type paragraph --prop text=hello officecli close t.docx officecli view t.docx screenshot -o t.png --json # → {"success":true,"data":".../t.png", ...}Result PNG: 1600×1200, 8-bit RGB, 9296 bytes — see attached
U3-screenshot.png(identical render to the sibling PR's baseline "hello" document, confirming
no regression).
Directly observed the fix taking effect (not just "it happened to work"):
polled the OS temp directory once a second while the screenshot ran and
caught the profile directory mid-flight —
— matching the
officecli-chrome-<guid>pattern inNewChromeUserDataDir(),confirming the flag is actually being passed and used, not merely present in
the diff. After the command completed normally:
Note on the "before" comparison: the concurrent-headless-instance repro
above did not reproduce a hard failure on the unmodified
mainbranchwith this Chrome version (149.0.7827.22 / 151.0.7922.138) — a second
--headless=newinstance against an already-open default profile completedwithout contention on this build. The fix is still correct and worth
landing on its own merits (verified via direct observation of the isolated
profile directory's creation and cleanup above): it removes any dependency
on the host's default-profile availability at all, which is what makes
screenshotting work under a sandbox that denies writes to that directory —
the harder and more relevant constraint for the sandboxed host this change
is for. Did not attempt a foreground (non-headless, visibly-windowed) Chrome
instance for this test, to avoid interacting with the tester's real,
in-use browser session.
Compatibility
Additive/isolating only — every headless launch site behaves exactly as
before except for the added, self-cleaning profile directory. No public API
change;
HasChromeFamily()/FindChrome()are untouched.