From a7fc4ec712ff36bcddd2e348821bf83ab05f675b Mon Sep 17 00:00:00 2001 From: campersau Date: Fri, 10 Apr 2026 14:47:28 +0200 Subject: [PATCH 1/2] Move LegacyBindingEnabled / JavascriptBindingPropertyName to per browser settings --- .../CefAppUnmanagedWrapper.cpp | 116 +++++++++--------- .../CefAppUnmanagedWrapper.h | 16 ++- .../JavascriptBindingSettings.h | 8 ++ .../JavascriptBindingTests.cs | 74 ++++++++--- 4 files changed, 130 insertions(+), 84 deletions(-) diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp index 91b45bf450..05ec2fbefc 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp @@ -65,8 +65,10 @@ namespace CefSharp auto browserId = browser->GetIdentifier(); _browserWrappers->TryAdd(browserId, wrapper); - auto javascriptBindingSettings = gcnew JavascriptBindingSettings(); - _browserJavascriptBindingSettings->TryAdd(browserId, javascriptBindingSettings); + static gcroot^> factory = + gcnew Func(CefAppUnmanagedWrapper::JavascriptBindingSettingsFactory); + + auto javascriptBindingSettings = _browserJavascriptBindingSettings->GetOrAdd(browserId, factory); if (!extraInfo.get()) { @@ -78,9 +80,9 @@ namespace CefSharp //will override the _legacyBindingEnabled field if (!browser->IsPopup()) { - _legacyBindingEnabled = extraInfo->GetBool("LegacyBindingEnabled"); + javascriptBindingSettings->LegacyBindingEnabled = extraInfo->GetBool("LegacyBindingEnabled"); - if (_legacyBindingEnabled) + if (javascriptBindingSettings->LegacyBindingEnabled) { auto objects = extraInfo->GetList("LegacyBindingObjects"); if (objects.get() && objects->IsValid()) @@ -127,8 +129,8 @@ namespace CefSharp if (extraInfo->HasKey("JsBindingPropertyName") || extraInfo->HasKey("JsBindingPropertyNameCamelCase")) { //TODO: Create constant for these and legacy binding strings above - _jsBindingPropertyName = extraInfo->GetString("JsBindingPropertyName"); - _jsBindingPropertyNameCamelCase = extraInfo->GetString("JsBindingPropertyNameCamelCase"); + javascriptBindingSettings->JavascriptBindingPropertyName = StringUtils::ToClr(extraInfo->GetString("JsBindingPropertyName")); + javascriptBindingSettings->JavascriptBindingPropertyNameCamelCase = StringUtils::ToClr(extraInfo->GetString("JsBindingPropertyNameCamelCase")); } } @@ -162,64 +164,68 @@ namespace CefSharp bool isSameContext = frame->GetV8Context()->IsSame(context); if (!isSameContext) return; - auto rootObject = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier()); - - if (_legacyBindingEnabled) - { - if (_javascriptObjects->Count > 0 && rootObject != nullptr) - { - rootObject->Bind(_javascriptObjects->Values, context->GetGlobal()); - } - } JavascriptBindingSettings^ javascriptBindingSettings = nullptr; _browserJavascriptBindingSettings->TryGetValue(browser->GetIdentifier(), javascriptBindingSettings); - if (IsJavascriptBindingApiAllowed(javascriptBindingSettings, frame)) + if (!Object::ReferenceEquals(javascriptBindingSettings, nullptr)) { - //TODO: Look at adding some sort of javascript mapping layer to reduce the code duplication - auto global = context->GetGlobal(); - auto processId = System::Diagnostics::Process::GetCurrentProcess()->Id; - - //TODO: JSB: Split functions into their own classes - //Browser wrapper is only used for BindObjectAsync - auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, _javascriptObjects, rootObject)); - auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(_javascriptObjects)); - auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(_javascriptObjects)); - auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(_javascriptObjects)); - auto postMessageFunction = CefV8Value::CreateFunction(kPostMessage, new JavascriptPostMessageHandler(rootObject == nullptr ? nullptr : rootObject->CallbackRegistry)); - auto promiseHandlerFunction = CefV8Value::CreateFunction(kSendEvalScriptResponse, new JavascriptPromiseHandler()); - - //By default We'll support both CefSharp and cefSharp, for those who prefer the JS style - auto createCefSharpObj = !_jsBindingPropertyName.empty(); - auto createCefSharpObjCamelCase = !_jsBindingPropertyNameCamelCase.empty(); - - if (createCefSharpObj) + auto rootObject = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier()); + + if (javascriptBindingSettings->LegacyBindingEnabled) { - auto cefSharpObj = CefV8Value::CreateObject(nullptr, nullptr); - cefSharpObj->SetValue(kBindObjectAsync, bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObj->SetValue(kDeleteBoundObject, unBindObjFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObj->SetValue(kRemoveObjectFromCache, removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObj->SetValue(kIsObjectCached, isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObj->SetValue(kPostMessage, postMessageFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObj->SetValue(kSendEvalScriptResponse, promiseHandlerFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObj->SetValue(kRenderProcessId, CefV8Value::CreateInt(processId), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - - global->SetValue(_jsBindingPropertyName, cefSharpObj, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + if (_javascriptObjects->Count > 0 && rootObject != nullptr) + { + rootObject->Bind(_javascriptObjects->Values, context->GetGlobal()); + } } - if (createCefSharpObjCamelCase) + if (IsJavascriptBindingApiAllowed(javascriptBindingSettings, frame)) { - auto cefSharpObjCamelCase = CefV8Value::CreateObject(nullptr, nullptr); - cefSharpObjCamelCase->SetValue(kBindObjectAsyncCamelCase, bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObjCamelCase->SetValue(kDeleteBoundObjectCamelCase, unBindObjFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObjCamelCase->SetValue(kRemoveObjectFromCacheCamelCase, removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObjCamelCase->SetValue(kIsObjectCachedCamelCase, isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObjCamelCase->SetValue(kPostMessageCamelCase, postMessageFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObjCamelCase->SetValue(kSendEvalScriptResponseCamelCase, promiseHandlerFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - cefSharpObjCamelCase->SetValue(kRenderProcessIdCamelCase, CefV8Value::CreateInt(processId), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); - - global->SetValue(_jsBindingPropertyNameCamelCase, cefSharpObjCamelCase, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + //TODO: Look at adding some sort of javascript mapping layer to reduce the code duplication + auto global = context->GetGlobal(); + auto processId = System::Diagnostics::Process::GetCurrentProcess()->Id; + + //TODO: JSB: Split functions into their own classes + //Browser wrapper is only used for BindObjectAsync + auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, _javascriptObjects, rootObject)); + auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(_javascriptObjects)); + auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(_javascriptObjects)); + auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(_javascriptObjects)); + auto postMessageFunction = CefV8Value::CreateFunction(kPostMessage, new JavascriptPostMessageHandler(rootObject == nullptr ? nullptr : rootObject->CallbackRegistry)); + auto promiseHandlerFunction = CefV8Value::CreateFunction(kSendEvalScriptResponse, new JavascriptPromiseHandler()); + + //By default We'll support both CefSharp and cefSharp, for those who prefer the JS style + auto createCefSharpObj = !String::IsNullOrEmpty(javascriptBindingSettings->JavascriptBindingPropertyName); + auto createCefSharpObjCamelCase = !String::IsNullOrEmpty(javascriptBindingSettings->JavascriptBindingPropertyNameCamelCase); + + if (createCefSharpObj) + { + auto cefSharpObj = CefV8Value::CreateObject(nullptr, nullptr); + cefSharpObj->SetValue(kBindObjectAsync, bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kDeleteBoundObject, unBindObjFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kRemoveObjectFromCache, removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kIsObjectCached, isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kPostMessage, postMessageFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kSendEvalScriptResponse, promiseHandlerFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObj->SetValue(kRenderProcessId, CefV8Value::CreateInt(processId), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + + global->SetValue(StringUtils::ToNative(javascriptBindingSettings->JavascriptBindingPropertyName), cefSharpObj, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + } + + if (createCefSharpObjCamelCase) + { + auto cefSharpObjCamelCase = CefV8Value::CreateObject(nullptr, nullptr); + cefSharpObjCamelCase->SetValue(kBindObjectAsyncCamelCase, bindObjAsyncFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kDeleteBoundObjectCamelCase, unBindObjFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kRemoveObjectFromCacheCamelCase, removeObjectFromCacheFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kIsObjectCachedCamelCase, isObjectCachedFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kPostMessageCamelCase, postMessageFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kSendEvalScriptResponseCamelCase, promiseHandlerFunction, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + cefSharpObjCamelCase->SetValue(kRenderProcessIdCamelCase, CefV8Value::CreateInt(processId), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); + + global->SetValue(StringUtils::ToNative(javascriptBindingSettings->JavascriptBindingPropertyNameCamelCase), cefSharpObjCamelCase, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + } } } diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h index d0e3506d3a..a6d90755d1 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h @@ -30,17 +30,18 @@ namespace CefSharp gcroot^> _browserJavascriptBindingSettings; gcroot^> _jsRootObjectWrappersByFrameId; bool _focusedNodeChangedEnabled; - bool _legacyBindingEnabled; - - // The property names used to call bound objects - CefString _jsBindingPropertyName; - CefString _jsBindingPropertyNameCamelCase; // The serialized registered object data waiting to be used. gcroot^> _javascriptObjects; gcroot _registerBoundObjectRegistry; - bool IsJavascriptBindingApiAllowed(JavascriptBindingSettings^ javascriptBindingSettings, CefRefPtr frame); + + static bool IsJavascriptBindingApiAllowed(JavascriptBindingSettings^ javascriptBindingSettings, CefRefPtr frame); + + static JavascriptBindingSettings^ JavascriptBindingSettingsFactory(int _) + { + return gcnew JavascriptBindingSettings(); + } public: static const CefString kPromiseCreatorScript; @@ -56,9 +57,6 @@ namespace CefSharp _focusedNodeChangedEnabled = enableFocusedNodeChanged; _javascriptObjects = gcnew Dictionary(); _registerBoundObjectRegistry = gcnew RegisterBoundObjectRegistry(); - _legacyBindingEnabled = false; - _jsBindingPropertyName = "CefSharp"; - _jsBindingPropertyNameCamelCase = "cefSharp"; } ~CefAppUnmanagedWrapper() diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h b/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h index 8ef2f888bb..8947a0fddc 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h @@ -20,6 +20,9 @@ namespace CefSharp public: JavascriptBindingSettings() { + LegacyBindingEnabled = false; + JavascriptBindingPropertyName = "CefSharp"; + JavascriptBindingPropertyNameCamelCase = "cefSharp"; JavascriptBindingApiEnabled = true; JavascriptBindingApiHasAllowOrigins = false; JavascriptBindingApiAllowOrigins = nullptr; @@ -35,6 +38,11 @@ namespace CefSharp this->!JavascriptBindingSettings(); } + property bool LegacyBindingEnabled; + + property String^ JavascriptBindingPropertyName; + property String^ JavascriptBindingPropertyNameCamelCase; + property bool JavascriptBindingApiEnabled; property bool JavascriptBindingApiHasAllowOrigins; property CefRefPtr JavascriptBindingApiAllowOrigins diff --git a/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs b/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs index 76610b243e..0607be7256 100644 --- a/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs +++ b/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs @@ -84,10 +84,43 @@ public async Task ShouldWorkWhenUsingCustomGlobalObjectName() var result = await browser.EvaluateScriptAsync("bindingApiObject.isObjectCached('doesntexist') === false"); - Assert.True(result.Success); + Assert.True(result.Success, result.Message); } } + [Fact] + public async Task ShouldWorkWhenUsingCustomGlobalObjectNameInMultipleBrowsers() + { + using var browser1 = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false); + using var browser2 = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false); + using var browser3 = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false); + + browser1.JavascriptObjectRepository.Settings.JavascriptBindingApiGlobalObjectName = "bindingApiObject1"; + browser2.JavascriptObjectRepository.Settings.JavascriptBindingApiGlobalObjectName = "bindingApiObject2"; + + //To modify the settings we need to defer browser creation slightly + browser1.CreateBrowser(); + browser2.CreateBrowser(); + browser3.CreateBrowser(); + + await browser1.WaitForInitialLoadAsync(); + await browser2.WaitForInitialLoadAsync(); + await browser3.WaitForInitialLoadAsync(); + + var result1 = await browser1.EvaluateScriptAsync("typeof window.bindingApiObject1 === 'undefined'"); + var result2 = await browser2.EvaluateScriptAsync("typeof window.bindingApiObject2 === 'undefined'"); + var result3 = await browser3.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); + + Assert.True(result1.Success, result1.Message); + Assert.False((bool)result1.Result); + + Assert.True(result2.Success, result2.Message); + Assert.False((bool)result2.Result); + + Assert.True(result3.Success, result3.Message); + Assert.False((bool)result3.Result); + } + [Theory] //We'll execute twice using the different cased (camelcase naming and standard) [InlineData("CefSharp.IsObjectCached('doesntexist')")] @@ -97,10 +130,10 @@ public async Task ShouldFailWhenIsObjectCachedCalledWithInvalidObjectName(string var loadResponse = await Browser.LoadUrlAsync(CefExample.BindingApiCustomObjectNameTestUrl); Assert.True(loadResponse.Success); - + var response = await Browser.EvaluateScriptAsync(script); - Assert.True(response.Success); + Assert.True(response.Success, response.Message); Assert.False((bool)response.Result); } @@ -122,10 +155,10 @@ public async Task ShouldDisableJsBindingApi() var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.True((bool)response1.Result); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.True((bool)response2.Result); } } @@ -152,10 +185,10 @@ public async Task ShouldDisableJsBindingApiForOrigin(params string[] origins) var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.True((bool)response1.Result); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.True((bool)response2.Result); } } @@ -179,10 +212,10 @@ public async Task ShouldEnableJsBindingApiWhenOriginsListIsEmpty() var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.False((bool)response1.Result); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.False((bool)response2.Result); } } @@ -208,13 +241,14 @@ public async Task ShouldEnableJsBindingApiForOriginWithOrWithoutTrailingSlash(st var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.False((bool)response1.Result); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.False((bool)response2.Result); } } + [Theory] [InlineData(CefExample.BaseUrl + "/")] [InlineData("someorigin", CefExample.BaseUrl + "/")] @@ -239,10 +273,10 @@ public async Task ShouldEnableJsBindingApiForOrigin(params string[] origins) var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.False((bool)response1.Result); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.False((bool)response2.Result); } } @@ -265,10 +299,10 @@ public async Task ShouldEnableJsBindingApi() var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.False((bool)response1.Result); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.False((bool)response2.Result); } } @@ -282,7 +316,7 @@ public async Task ShouldReturnRenderProcessId(string script) var result = await Browser.EvaluateScriptAsync(script); - Assert.True(result.Success); + Assert.True(result.Success, result.Message); using var process = Process.GetProcessById(Assert.IsType(result.Result)); @@ -373,7 +407,7 @@ public async Task ShouldDisableJsBindingApiAfterCrossOriginNavigationToDisallowe // Binding API should be present on the allowed origin var response1 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.False((bool)response1.Result); // Navigate to a different origin that is not in the allow list @@ -382,7 +416,7 @@ public async Task ShouldDisableJsBindingApiAfterCrossOriginNavigationToDisallowe // Binding API should no longer be present on the disallowed origin var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.True((bool)response2.Result); } } @@ -404,7 +438,7 @@ public async Task ShouldKeepJsBindingApiEnabledAfterCrossOriginNavigationToAllow // Binding API should be present on the first allowed origin var response1 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response1.Success); + Assert.True(response1.Success, response1.Message); Assert.False((bool)response1.Result); // Navigate to a second origin that is also in the allow list @@ -413,7 +447,7 @@ public async Task ShouldKeepJsBindingApiEnabledAfterCrossOriginNavigationToAllow // Binding API should still be present on the second allowed origin var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'"); - Assert.True(response2.Success); + Assert.True(response2.Success, response2.Message); Assert.False((bool)response2.Result); } } From 889332fee307ef558144493d4437d347fb55f04f Mon Sep 17 00:00:00 2001 From: campersau Date: Fri, 10 Apr 2026 16:29:21 +0200 Subject: [PATCH 2/2] Stricter test --- .../JavascriptBindingSettings.h | 4 +- .../JavascriptBindingTests.cs | 51 +++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h b/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h index 8947a0fddc..a30dd30de0 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptBindingSettings.h @@ -21,9 +21,9 @@ namespace CefSharp JavascriptBindingSettings() { LegacyBindingEnabled = false; + JavascriptBindingApiEnabled = true; JavascriptBindingPropertyName = "CefSharp"; JavascriptBindingPropertyNameCamelCase = "cefSharp"; - JavascriptBindingApiEnabled = true; JavascriptBindingApiHasAllowOrigins = false; JavascriptBindingApiAllowOrigins = nullptr; } @@ -39,11 +39,11 @@ namespace CefSharp } property bool LegacyBindingEnabled; + property bool JavascriptBindingApiEnabled; property String^ JavascriptBindingPropertyName; property String^ JavascriptBindingPropertyNameCamelCase; - property bool JavascriptBindingApiEnabled; property bool JavascriptBindingApiHasAllowOrigins; property CefRefPtr JavascriptBindingApiAllowOrigins { diff --git a/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs b/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs index 0607be7256..31cb04cc4a 100644 --- a/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs +++ b/CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs @@ -107,18 +107,51 @@ public async Task ShouldWorkWhenUsingCustomGlobalObjectNameInMultipleBrowsers() await browser2.WaitForInitialLoadAsync(); await browser3.WaitForInitialLoadAsync(); - var result1 = await browser1.EvaluateScriptAsync("typeof window.bindingApiObject1 === 'undefined'"); - var result2 = await browser2.EvaluateScriptAsync("typeof window.bindingApiObject2 === 'undefined'"); - var result3 = await browser3.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); + // Assert browser1 + { + var result1 = await browser1.EvaluateScriptAsync("typeof window.bindingApiObject1 === 'undefined'"); + var result2 = await browser1.EvaluateScriptAsync("typeof window.bindingApiObject2 === 'undefined'"); + var result3 = await browser1.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); + + Assert.True(result1.Success, result1.Message); + Assert.False((bool)result1.Result); + + Assert.True(result2.Success, result2.Message); + Assert.True((bool)result2.Result); + + Assert.True(result3.Success, result3.Message); + Assert.True((bool)result3.Result); + } + // Assert browser2 + { + var result1 = await browser2.EvaluateScriptAsync("typeof window.bindingApiObject1 === 'undefined'"); + var result2 = await browser2.EvaluateScriptAsync("typeof window.bindingApiObject2 === 'undefined'"); + var result3 = await browser2.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); - Assert.True(result1.Success, result1.Message); - Assert.False((bool)result1.Result); + Assert.True(result1.Success, result1.Message); + Assert.True((bool)result1.Result); - Assert.True(result2.Success, result2.Message); - Assert.False((bool)result2.Result); + Assert.True(result2.Success, result2.Message); + Assert.False((bool)result2.Result); - Assert.True(result3.Success, result3.Message); - Assert.False((bool)result3.Result); + Assert.True(result3.Success, result3.Message); + Assert.True((bool)result3.Result); + } + // Assert browser3 + { + var result1 = await browser3.EvaluateScriptAsync("typeof window.bindingApiObject1 === 'undefined'"); + var result2 = await browser3.EvaluateScriptAsync("typeof window.bindingApiObject2 === 'undefined'"); + var result3 = await browser3.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'"); + + Assert.True(result1.Success, result1.Message); + Assert.True((bool)result1.Result); + + Assert.True(result2.Success, result2.Message); + Assert.True((bool)result2.Result); + + Assert.True(result3.Success, result3.Message); + Assert.False((bool)result3.Result); + } } [Theory]