BindObjectAsync reports successful binding, but sync-bound object is undefined on window after navigation #5233
Unanswered
SwapnojHyland
asked this question in
Q&A
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Environment
Application Context
We are embedding CefSharp in a WPF desktop application that hosts a
data viewer component— it renders server-side
HTML pages inside a
ChromiumWebBrowsercontrol.The typical user flow that triggers the issue:
On the C# side, the object is registered via
JavascriptObjectRepository.Register(name, obj, isAsync: ...)and thenCefSharp.BindObjectAsync('name')is triggered usingIFrame.EvaluateScriptAsync— the Promise object is returned as the result but not awaited on the C# side.
We also tried properly awaiting the Promise by switching to
IFrame.EvaluateScriptAsPromiseAsyncwith the body"await CefSharp.BindObjectAsync('name'); return true;"— this did not resolvethe issue. The binding still reported success but the object remained absent from
window. The loaded HTML page then references the bound object by name; onfailure
window[syncObjectName]isundefinedand aReferenceErroris thrown.The
about:blankintermediate navigation is intentional (visual clear duringthe async round-trip) but we confirmed that removing it does not fix the issue —
CefSharp itself generates
about:blankV8 contexts during navigation, so therace persists regardless.
Problem
After upgrading from CefSharp v144.0.120 to v146.0.70 / v146.0.100, a C#
object registered via
JavascriptObjectRepository.Register()and bound usingCefSharp.BindObjectAsync()reliably fails to appear onwindowwhen theDevTools panel is open — specifically from the 2nd or subsequent page
navigation onward. The Binding API reports
{Success:true, Count:1}everytime, but when a subsequent
EvaluateScriptAsynccall tries to use the object,it throws:
(
about:blank:1:64is CefSharp's internal source location label forEvaluateScriptAsynccalls — it does not indicate an actual about:blank page.)The same object registered with
isAsync=trueworks correctly — always presenton
window, DevTools open or not.Minimal reproduction steps
isAsync=falseundefinedWhat we observed
BindObjectAsyncreturnsSuccess=truein ~2ms → cache-hit path, notan IPC round-trip (~30ms would indicate full IPC path)
window[objName]isundefinedimmediately after and staysundefinedindefinitely (polled for 10 seconds, 50 retries × 200ms)
isAsync=trueresolves the issue completely and immediatelyRoot cause hypothesis (from source reading)
We believe three things combine to cause this:
1. The failure is consistently reproducible when DevTools is open.
Opening DevTools is the reliable trigger — the issue does not occur without it.
We suspect something in the DevTools inspector isolated world lifecycle interacts
with
OnContextReleased/TryRemovein a way that evicts the newly-createdrootObjectWrapper, but we haven't been able to confirm the exact ordering changewithout access to a debug CEF build. We'd welcome any insight into what DevTools
changes about the V8 context lifecycle in Chromium 122 vs 120.
2.
_browserProcess = null→Bind()silently skips the sync object.JavascriptRootObjectWrapper::Bind()in theisAsync=false(#ifndef NETCOREAPP)branch:
The evicted wrapper's
_browserProcessis null (itsOnBrowserDestroyedalreadyran). The object is never placed on
window. C# calling code receives no error.3. Cache-hit path in
BindObjectAsyncHandler::Executereports false-positivesuccess.
From BindObjectAsyncHandler.h (cache-hit branch, ~line 150):
cachedObjects->Count > 0is always true after the first page load (objectdescriptor is cached), so the promise always resolves
Success:truewithoutverifying
windowpresence. This is a false positive that hides the failureentirely from C# code.
Why only 2nd+ refresh with DevTools:
First refresh: DevTools hasn't fully initialized its
addScriptToEvaluateOnNewDocumenthook yet — inspectorOnContextReleasedstill fires before the new main world's
OnContextCreated. Safe order. Passes.2nd+ refresh: Inspector isolated world lifecycle is now fully synchronized with
page navigation. Fires after new main world
OnContextCreated. Reliablytriggers the eviction race.
Questions for maintainers
Is our understanding of the Chromium M122
OnContextReleasedordering changecorrect? Was this a deliberate CEF change or an unintended side-effect?
Is there a planned CefSharp-level workaround until CEF Chromium Site Isolation - Legacy Binding Test failure #3867 is resolved —
e.g. skipping
TryRemoveinOnContextReleasedwhen the frame'srootObjectWrapperis currently referenced by a liveBindObjectAsyncHandler?Would it be reasonable for the cache-hit path in
BindObjectAsyncHandler::Executeto verify actual window presence after
Bind()— i.e. checkglobal->HasValue(objectName)for each cached object before resolvingSuccess:true? That would at least surface the failure to C# rather thanreturning a false positive.
For applications that cannot migrate to
isAsync=true(WCF sync call semanticsrequired), is passing
{ignoreCache:true}toCefSharp.BindObjectAsync()aviable workaround to force the full IPC path on every bind call, thereby
bypassing the evicted-wrapper issue?
Can you confirm why
isAsync=trueis immune to this issue across all threeversions (144.0.120, 146.0.70, 146.0.100)? Our reading of the source suggests
JavascriptAsyncObjectWrapper::Bind()callsv8Value->SetValue()unconditionallywith no
_browserProcessdependency, so it places the proxy onwindowregardless of
rootObjectWrapperlifecycle state. If that is correct andarchitecturally guaranteed to remain stable, it would give us confidence to
ship
isAsync=trueas a permanent fix rather than treating it as a temporaryworkaround pending an upstream CEF fix.
Workaround found
Changing registration to
isAsync=truefully resolves the issue. JavaScriptcallers already used async/await so no behavior change was needed on the JS side.
Noting this in case it helps other users hitting the same symptom.
References
isSameContextguard added toOnContextCreated;OnContextReleasedguard explicitly not possible (AV crash confirmed in PR discussion)
browserWrapper null → window.CefSharp not injectedracewindow.CefSharpinjection gap (different symptom; does not fixthe
isAsync=falseDevTools eviction bug described above)Beta Was this translation helpful? Give feedback.
All reactions