Skip to content

Fix CefDownloadHandler.onBeforeDownload's Chrome-style cancel trap - #48

Merged
Thrameos merged 2 commits into
masterfrom
backport/download-handler-return-false-crash-contract
Sep 6, 2026
Merged

Thrameos merged 2 commits into
masterfrom
backport/download-handler-return-false-crash-contract

Conversation

@Thrameos

@Thrameos Thrameos commented Sep 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • This project runs CEF's Chrome-style runtime, so returning false from onBeforeDownload did not cancel the download -- it fell through to Chrome's own download-shelf default handling, which this project's embedding does not implement. If the browser that started the download was torn down before that deferred handling ran, CEF's own ChooseDownloadPath dereferenced an already-gone callback and crashed the process with an internal CHECK failure. Root-caused via gdb while doing coverage work on a separate branch (see plan/tasks/20260905-26-download-shelf-check-crash.md for the full backtrace and CEF source citations).
  • Revised per review feedback ("should we implement the missing feature rather than documenting the defect?"): implementing CEF's actual Chrome-style download shelf isn't viable here -- it's an internal Chrome-UI subsystem with no public hook for embedders, and this project's Java/AWT/OSR embedding doesn't use the CEF Views UI framework it would need anyway. But the underlying ask -- "make returning false actually work as a cancel" -- is fixable at the JNI boundary: native/download_handler.cpp's OnBeforeDownload now always returns true to CEF and treats a false return from Java's onBeforeDownload as "drop the callback un-run", the same already-safe cancel path a true+drop-callback implementation already used, just applied uniformly instead of leaving a plain return false as a latent crash trap.
  • Updated CefDownloadHandler.onBeforeDownload's javadoc for the new, simpler contract: false now always safely cancels, no runtime-style caveat needed.
  • Replaced the original doc-only regression test with one that exercises the actual fix (rejectingDownloadByReturningFalseDoesNotCrash -- returns false directly, asserts no crash and no file written), and kept a second test covering the older true+drop-callback path (rejectingDownloadByReturningTrueAndDroppingCallbackDoesNotCrash).

Test plan

  • Full clean rebuild (native ninja -C jcef_build jcef under -Werror + tools/compile.sh linux64)
  • Both CefDownloadItemTest regression tests pass; full local suite 125/125 passing
  • Post-suite Aborted (core dumped) after the JUnit summary is the already-tracked, pre-existing GH Release build: SIGSEGV in libc.so.6 during JVM shutdown after all tests pass #10 shutdown SIGSEGV, unrelated to this change
  • CI (build/test/coverage jobs)

🤖 Generated with Claude Code

https://claude.ai/code/session_01HBFmQs6JDypYP5qdC99yDq

… contract

Root-caused during task 26 (see plan/tasks/20260905-26-download-shelf-check-crash.md):
this project runs CEF's Chrome-style runtime, so returning false from
onBeforeDownload does NOT cancel the download -- it falls through to
Chrome's own download-shelf default handling, which this project's
embedding does not implement. If the browser that started the download
is torn down before that deferred handling runs, CEF's own
ChooseDownloadPath dereferences an already-gone callback and crashes the
process with an internal CHECK failure. The only safe way to cancel is
to return true and drop |callback| un-run.

This bug pattern was found in a future-branch-only coverage test
(CefBrowserApiGapCoverageTest, not present on this branch) and doesn't
exist anywhere in this branch's current onBeforeDownload implementations
-- there's no code-level regression to fix here. What's worth
backporting on its own is protecting this branch's future users/tests
from the same trap: a javadoc warning on the actual contract, plus a
regression test that locks in the documented-safe cancel path (return
true, drop the callback) actually completes cleanly with no crash and
writes no target file. Deliberately does not attempt to reproduce the
crash itself -- it's inherently timing-dependent on browser teardown
racing a posted UI-thread task, and a real process crash is not
something to leave as a routine CI test.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HBFmQs6JDypYP5qdC99yDq
@codecov

codecov Bot commented Sep 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.14%. Comparing base (be68fe1) to head (9ba26f9).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
java/tests/junittests/CefDownloadItemTest.java 90.47% 0 Missing and 4 partials ⚠️
native/download_handler.cpp 66.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master      #48      +/-   ##
============================================
+ Coverage     38.01%   38.14%   +0.13%     
- Complexity      735      736       +1     
============================================
  Files           243      243              
  Lines         14863    14906      +43     
  Branches       2449     2453       +4     
============================================
+ Hits           5650     5686      +36     
  Misses         8011     8011              
- Partials       1202     1209       +7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Thrameos Thrameos left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider revision

Comment on lines +24 to +36
* <p><b>Warning:</b> this project runs CEF's Chrome-style runtime, so
* returning false here does <i>not</i> cancel the download -- it falls
* through to Chrome's own download-shelf default handling, which this
* project's embedding does not implement. That default handling posts a
* task to the UI thread asynchronously, and if the browser that started
* the download is torn down before that task runs (a common case if a
* test or app closes the browser right after starting the download),
* the underlying CEF implementation dereferences an already-gone
* download-target callback and crashes the process with an internal
* {@code CHECK} failure. To actually cancel, return true and either
* never call {@code callback.Continue(...)}, or call it with an empty
* path.
*

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we implement the missing feature rather than documenting the defect?

@Thrameos Thrameos added the documentation Improvements or additions to documentation label Sep 6, 2026
…r real

Per review feedback on the earlier doc-only PR ("should we implement the
missing feature rather than documenting the defect?"): implementing CEF's
actual Chrome-style download shelf isn't viable here (it's an internal
Chrome-UI subsystem with no public hook for embedders, and this project's
Java/AWT/OSR embedding doesn't use CEF's Views UI framework it would need
anyway) -- but the underlying ask, "make returning false actually work as
a cancel", is fixable at the JNI boundary.

native/download_handler.cpp's OnBeforeDownload now always returns true to
CEF and treats a false return from Java's onBeforeDownload as "drop the
callback un-run" -- the same already-safe cancel path a true+drop-callback
implementation already used, just applied uniformly instead of leaving a
plain `return false` as a latent crash trap (see
plan/tasks/20260905-26-download-shelf-check-crash.md for the original
root-cause: CEF's own Chrome-style default handling for false posts a
UI-thread task that can crash with an internal CHECK if the browser is
torn down first).

Updated CefDownloadHandler.onBeforeDownload's javadoc to describe the new,
simpler contract (false always safely cancels, no runtime-style caveat).
Replaced the doc-PR's regression test with one that exercises the actual
fix (returns false directly, asserts no crash and no file written), and
kept a second test covering the older true+drop-callback path.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HBFmQs6JDypYP5qdC99yDq
@Thrameos Thrameos added bug Something isn't working and removed documentation Improvements or additions to documentation labels Sep 6, 2026
@Thrameos Thrameos changed the title Document CefDownloadHandler's Chrome-style cancel contract + regression test Fix CefDownloadHandler.onBeforeDownload's Chrome-style cancel trap Sep 6, 2026
@Thrameos

Thrameos commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Re the review comment on CefDownloadHandler.java: implemented the real fix instead of just documenting it. native/download_handler.cpp's OnBeforeDownload now normalizes a false return from Java to a safe cancel (drop the callback un-run, still return true to CEF) before it ever reaches CEF's Chrome-style default handling -- so return false; now just works, safely, instead of being a documented trap. Didn't implement the actual download shelf: it's an internal Chrome-UI subsystem with no public hook for embedders, and this project's Java/AWT/OSR embedding doesn't use the CEF Views framework it would need anyway. Full rationale in the updated PR description.

@Thrameos
Thrameos merged commit f82ead7 into master Sep 6, 2026
9 checks passed
@Thrameos
Thrameos deleted the backport/download-handler-return-false-crash-contract branch September 6, 2026 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant