Skip to content

Commit

Permalink
JavascriptBinding - Add JavascriptBindingSettings.JavascriptBindingAp…
Browse files Browse the repository at this point in the history
…iEnabled property (#3571)

- The Javascript methods that CefSharp provides in relation to JavaScript Binding are
  created using a Global (window) Object. Settings this property allows you to disable
  the creation of this object. Features like EvaluateScriptAsPromiseAsync that rely on
  the creation of this object will no longer function.
  • Loading branch information
amaitland authored Jun 4, 2021
1 parent a3f9140 commit 0386220
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 46 deletions.
89 changes: 47 additions & 42 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ namespace CefSharp
}
}
}

_jsBindingApiEnabled = extraInfo->GetBool("JavascriptBindingApiEnabled");
}

if (extraInfo->HasKey("JsBindingPropertyName") || extraInfo->HasKey("JsBindingPropertyNameCamelCase"))
Expand Down Expand Up @@ -137,50 +139,53 @@ namespace CefSharp
}
}

//TODO: Look at adding some sort of javascript mapping layer to reduce the code duplication
auto global = context->GetGlobal();
auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier());
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, browserWrapper));
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)
if (_jsBindingApiEnabled)
{
auto cefSharpObj = CefV8Value::CreateObject(NULL, NULL);
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);
}
//TODO: Look at adding some sort of javascript mapping layer to reduce the code duplication
auto global = context->GetGlobal();
auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier());
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, browserWrapper));
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 cefSharpObj = CefV8Value::CreateObject(NULL, NULL);
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 (createCefSharpObjCamelCase)
{
auto cefSharpObjCamelCase = CefV8Value::CreateObject(NULL, NULL);
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);
if (createCefSharpObjCamelCase)
{
auto cefSharpObjCamelCase = CefV8Value::CreateObject(NULL, NULL);
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);
}
}

//Send a message to the browser processing signaling that OnContextCreated has been called
Expand Down
1 change: 1 addition & 0 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace CefSharp
gcroot<ConcurrentDictionary<int, CefBrowserWrapper^>^> _browserWrappers;
bool _focusedNodeChangedEnabled;
bool _legacyBindingEnabled;
bool _jsBindingApiEnabled = true;

// The property names used to call bound objects
CefString _jsBindingPropertyName;
Expand Down
11 changes: 7 additions & 4 deletions CefSharp.Core.Runtime/ManagedCefBrowserAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ namespace CefSharp
}

auto objectRepository = _javaScriptObjectRepository;
auto objectRepositorySettings = objectRepository->Settings;

//It's no longer possible to change these settings
objectRepository->Settings->Freeze();
objectRepositorySettings->Freeze();

CefRefPtr<CefDictionaryValue> extraInfo = CefDictionaryValue::Create();
auto legacyBindingEnabled = false;

if (objectRepository->Settings->LegacyBindingEnabled)
if (objectRepositorySettings->LegacyBindingEnabled)
{
auto legacyBoundObjects = objectRepository->GetLegacyBoundObjects();

Expand All @@ -67,9 +68,9 @@ namespace CefSharp

extraInfo->SetBool("LegacyBindingEnabled", legacyBindingEnabled);

if (!String::IsNullOrEmpty(objectRepository->Settings->JavascriptBindingApiGlobalObjectName))
if (!String::IsNullOrEmpty(objectRepositorySettings->JavascriptBindingApiGlobalObjectName))
{
auto globalObjName = objectRepository->Settings->JavascriptBindingApiGlobalObjectName;
auto globalObjName = objectRepositorySettings->JavascriptBindingApiGlobalObjectName;

if (StringCheck::IsFirstCharacterLowercase(globalObjName))
{
Expand All @@ -81,6 +82,8 @@ namespace CefSharp
}
}

extraInfo->SetBool("JavascriptBindingApiEnabled", objectRepositorySettings->JavascriptBindingApiEnabled);

CefRefPtr<CefRequestContext> requestCtx;

if (requestContext != nullptr)
Expand Down
1 change: 1 addition & 0 deletions CefSharp.Test/CefSharpFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private void CefInitialize()
//HTML5 databases such as localStorage will only persist across sessions if a cache path is specified.
settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Tests\\Cache");
settings.RootCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Tests");
//settings.CefCommandLineArgs.Add("renderer-startup-dialog");

Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null);
}
Expand Down
48 changes: 48 additions & 0 deletions CefSharp.Test/JavascriptBinding/IntegrationTestFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,54 @@ public async Task JsBindingGlobalObjectNameCustomValueExecuteIsObjectCachedSucce
}
}

[Fact]
public async Task JsBindingGlobalApiDisabled()
{
using (var browser = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false))
{
var settings = browser.JavascriptObjectRepository.Settings;
settings.JavascriptBindingApiEnabled = false;

//To modify the settings we need to defer browser creation slightly
browser.CreateBrowser();

await browser.LoadPageAsync();

var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'");
var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'");

Assert.True(response1.Success);
Assert.True((bool)response1.Result);

Assert.True(response2.Success);
Assert.True((bool)response2.Result);
}
}

[Fact]
public async Task JsBindingGlobalApiEnabled()
{
using (var browser = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false))
{
var settings = browser.JavascriptObjectRepository.Settings;
settings.JavascriptBindingApiEnabled = true;

//To modify the settings we need to defer browser creation slightly
browser.CreateBrowser();

await browser.LoadPageAsync();

var response1 = await browser.EvaluateScriptAsync("typeof window.cefSharp === 'undefined'");
var response2 = await browser.EvaluateScriptAsync("typeof window.CefSharp === 'undefined'");

Assert.True(response1.Success);
Assert.False((bool)response1.Result);

Assert.True(response2.Success);
Assert.False((bool)response2.Result);
}
}

[Theory]
[InlineData("CefSharp.RenderProcessId")]
[InlineData("cefSharp.renderProcessId")]
Expand Down
18 changes: 18 additions & 0 deletions CefSharp/JavascriptBinding/JavascriptBindingSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ public class JavascriptBindingSettings : FreezableBase
private bool alwaysInterceptAsynchronously;
private bool legacyBindingEnabled;
private string jsBindingGlobalObjectName;
private bool jsBindingApiEnabled = true;

/// <summary>
/// The Javascript methods that CefSharp provides in relation to JavaScript Binding are
/// created using a Global (window) Object. Settings this property allows you to disable
/// the creation of this object. Features like EvaluateScriptAsPromiseAsync that rely on
/// the creation of this object will no longer function.
/// </summary>
public bool JavascriptBindingApiEnabled
{
get { return jsBindingApiEnabled; }
set
{
ThrowIfFrozen();

jsBindingApiEnabled = value;
}
}

/// <summary>
/// The Javascript methods that CefSharp provides in relation to JavaScript Binding are
Expand Down

0 comments on commit 0386220

Please sign in to comment.