Skip to content

onBeforeDownload never fires despite Chromium correctly detecting the download (ERR_ABORTED) #18

Description

@Thrameos

Summary

CefDownloadHandler.onBeforeDownload never fires when navigating directly to a URL serving a Content-Disposition: attachment response -- even though Chromium correctly detects it as a download and aborts the navigation (onLoadError fires with ERR_ABORTED, the normal/expected signal for a download-triggered navigation). Confirmed on this fork's current CEF version (146.0.10+g8219561+chromium-146.0.7680.179).

Repro

Added java/tests/junittests/CefDownloadItemTest.java (currently @Disabled with a link to this issue -- remove @Disabled to re-run it):

  1. Serve a resource via a custom CefResourceHandler with a Content-Disposition: attachment; filename="test.bin" response header.
  2. Register a CefDownloadHandler.
  3. Navigate the browser directly to that URL as its initial load (avoids any user-gesture-requirement concerns a JS-synthesized click might raise, see issue JCEF doesn't expose CefExecuteJavaScriptWithUserGestureForTests, blocking real onBeforePopup test coverage #11).
  4. Confirmed via debug instrumentation: onLoadError fires with ERR_ABORTED (Chromium did detect and intercept the download), but onBeforeDownload never fires. Test hangs for the full 30s watchdog timeout -- a clean, bounded failure, not an unrecoverable hang.

Most likely cause (not confirmed as the fix, but a strong lead)

This fork's TestSetupExtension (the shared test-harness CefApp initialization used by every test in the suite) never sets CefSettings.root_cache_path -- there's a startup warning about exactly this: "Please customize CefSettings.root_cache_path for your application. Use of the default value may lead to unintended process singleton behavior." The download manager may not be fully initialized against the resulting ephemeral/in-memory profile.

This wasn't confirmed by testing with a real root_cache_path, because CefSettings.root_cache_path can only be set once, globally, at CefApp.getInstance() startup -- this fork's entire test suite shares one CefApp singleton via TestSetupExtension, so there's no way to vary it per-test without running a separate JVM process per test (same limitation independently documented for CefSettings.background_color, upstream issue chromiumembedded#362).

Suggested next steps

  • Confirm the root_cache_path theory directly: run a small standalone (non-suite) repro with a real root_cache_path set and see if onBeforeDownload then fires.
  • If confirmed, this may not be a JCEF/CEF bug at all -- just an environment-configuration requirement worth documenting more prominently (the existing warning is easy to miss/ignore since nothing else in a typical embedding app visibly breaks without it).

Found via

This fork's coverage-expansion effort (tracked in #5) -- CefDownloadItem_N.cpp/download_handler.cpp/CefBeforeDownloadCallback_N.cpp/CefDownloadItemCallback_N.cpp are a real, currently 0%-covered chunk per a real gcovr measurement.

Activity

  1. added a commit that references this issue on Aug 29, 2026
  2. Thrameos commented on Aug 30, 2026

    @Thrameos
    OwnerAuthor

    Root-caused and fixed -- not a JCEF/CEF bug, and not the `root_cache_path` issue originally suspected. `onBeforeDownload` does fire correctly; the original test held onto the `CefDownloadItem` object and called `isValid()`/`getURL()`/etc. on it after `awaitCompletion()` returned (i.e. after `terminateTest()` had already begun tearing down the browser). `CefDownloadItem` is a scoped/temporary object per its own javadoc ("Do not call any other methods if `isValid()` returns false") -- same class of test-writing mistake this fork's suite hit before with `CefContextMenuParams`/`CefRequest` params passed into other callbacks (see #17's fix): capture primitives inside the callback, don't hold the object for later use.

    Confirmed via a standalone diagnostic (enabling verbose CEF logging and checking the callback fired) before rewriting the real test. The rewritten test goes further than just confirming the fix -- it calls `CefBeforeDownloadCallback.Continue()` with a real temp file path so the download actually completes, exercises `CefDownloadItemCallback`'s `pause()`/`resume()` from `onDownloadUpdated()`, and verifies the downloaded file's real content on disk.

    Coverage impact: `download_handler.cpp` 0% → 94%, `CefDownloadItem_N.cpp` 0% → 84%, `CefBeforeDownloadCallback_N.cpp` 0% → 91%, `CefDownloadItemCallback_N.cpp` 0% → 40% (`cancel()` not exercised -- the test lets the download complete rather than cancelling it). See `CefDownloadItemTest.java` in the fork.

  3. added 5 commits that reference this issue on Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions