Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 79 additions & 8 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "Wrapper\Browser.h"
#include "..\CefSharp.Core.Runtime\Internals\Messaging\Messages.h"
#include "..\CefSharp.Core.Runtime\Internals\Serialization\Primitives.h"
#include <include/cef_parser.h>

using namespace System;
using namespace System::Diagnostics;
Expand Down Expand Up @@ -87,7 +88,7 @@ namespace CefSharp
//Using LegacyBinding with multiple ChromiumWebBrowser instances that share the same
//render process and using LegacyBinding will cause problems for the limited caching implementation
//that exists at the moment, for now we'll remove an object if already exists, same behaviour
//as the new binding method.
//as the new binding method.
//TODO: This should be removed when https://github.com/cefsharp/CefSharp/issues/2306
//Is complete as objects will be stored at the browser level
if (_javascriptObjects->ContainsKey(obj->JavascriptName))
Expand All @@ -98,16 +99,33 @@ namespace CefSharp
}
}
}
}

_jsBindingApiEnabled = extraInfo->GetBool("JavascriptBindingApiEnabled");
if (extraInfo->HasKey("JavascriptBindingApiEnabled"))
{
wrapper->JavascriptBindingApiEnabled = extraInfo->GetBool("JavascriptBindingApiEnabled");
}

if (extraInfo->HasKey("JavascriptBindingApiHasAllowOrigins"))
{
wrapper->JavascriptBindingApiHasAllowOrigins = extraInfo->GetBool("JavascriptBindingApiHasAllowOrigins");

if (extraInfo->HasKey("JsBindingPropertyName") || extraInfo->HasKey("JsBindingPropertyNameCamelCase"))
if (wrapper->JavascriptBindingApiHasAllowOrigins)
{
//TODO: Create constant for these and legacy binding strings above
_jsBindingPropertyName = extraInfo->GetString("JsBindingPropertyName");
_jsBindingPropertyNameCamelCase = extraInfo->GetString("JsBindingPropertyNameCamelCase");
auto allowOrigins = extraInfo->GetList("JavascriptBindingApiAllowOrigins");
if (allowOrigins.get() && allowOrigins->IsValid())
{
wrapper->JavascriptBindingApiAllowOrigins = allowOrigins->Copy();
}
}
}

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");
}
}

void CefAppUnmanagedWrapper::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
Expand Down Expand Up @@ -147,11 +165,12 @@ namespace CefSharp
}
}

if (_jsBindingApiEnabled)
auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier());

if (browserWrapper != nullptr && browserWrapper->JavascriptBindingApiEnabled && IsJavascriptBindingApiAllowed(frame))
{
//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
Expand Down Expand Up @@ -328,6 +347,58 @@ namespace CefSharp
return rootObject;
}

bool CefAppUnmanagedWrapper::IsJavascriptBindingApiAllowed(CefRefPtr<CefFrame> frame)
{
auto browserWrapper = FindBrowserWrapper(frame->GetBrowser()->GetIdentifier());

if (browserWrapper == nullptr || !browserWrapper->JavascriptBindingApiHasAllowOrigins)
{
return true;
}

auto allowOrigins = browserWrapper->JavascriptBindingApiAllowOrigins;
if (!allowOrigins.get())
{
return false;
}

auto frameUrl = frame->GetURL();

CefURLParts frameUrlParts;

if (CefParseURL(frameUrl, frameUrlParts))
{
auto originStr = frameUrlParts.origin.str;
auto originLen = frameUrlParts.origin.length;

if (originLen > 0 && originStr[originLen - 1] == L'/')
{
originLen--;
}

auto frameUrlOrigin = CefString(originStr, originLen);

auto size = static_cast<int>(allowOrigins->GetSize());

for (int i = 0; i < size; i++)
{
auto origin = allowOrigins->GetString(i);
auto frameOriginPtr = reinterpret_cast<const wchar_t*>(frameUrlOrigin.c_str());
auto allowedOriginPtr = reinterpret_cast<const wchar_t*>(origin.c_str());

if (frameOriginPtr != nullptr && allowedOriginPtr != nullptr)
{
if (_wcsicmp(frameOriginPtr, allowedOriginPtr) == 0)
{
return true;
}
}
}
}

return false;
}

CefBrowserWrapper^ CefAppUnmanagedWrapper::FindBrowserWrapper(int browserId)
{
CefBrowserWrapper^ wrapper = nullptr;
Expand Down
2 changes: 1 addition & 1 deletion CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ namespace CefSharp
gcroot<ConcurrentDictionary<String^, JavascriptRootObjectWrapper^>^> _jsRootObjectWrappersByFrameId;
bool _focusedNodeChangedEnabled;
bool _legacyBindingEnabled;
bool _jsBindingApiEnabled = true;

// The property names used to call bound objects
CefString _jsBindingPropertyName;
Expand All @@ -39,6 +38,7 @@ namespace CefSharp
gcroot<Dictionary<String^, JavascriptObject^>^> _javascriptObjects;

gcroot<RegisterBoundObjectRegistry^> _registerBoundObjectRegistry;
bool IsJavascriptBindingApiAllowed(CefRefPtr<CefFrame> frame);

public:
static const CefString kPromiseCreatorScript;
Expand Down
16 changes: 15 additions & 1 deletion CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,31 @@ namespace CefSharp
{
namespace BrowserSubprocess
{
// "Master class" for wrapping everything that the Cef Subprocess needs
// "Master class" for wrapping everything that the Cef Subprocess needs
// for ONE CefBrowser.
public ref class CefBrowserWrapper
{
private:
MCefRefPtr<CefBrowser> _cefBrowser;
MCefRefPtr<CefListValue> _javascriptBindingApiAllowOrigins;

public:
CefBrowserWrapper(const CefRefPtr<CefBrowser> &cefBrowser)
{
_cefBrowser = cefBrowser.get();
BrowserId = cefBrowser->GetIdentifier();
IsPopup = cefBrowser->IsPopup();

JavascriptBindingApiEnabled = true;
JavascriptBindingApiHasAllowOrigins = false;
JavascriptBindingApiAllowOrigins = nullptr;
}

!CefBrowserWrapper()
{
_cefBrowser = nullptr;

_javascriptBindingApiAllowOrigins = nullptr;
}

~CefBrowserWrapper()
Expand All @@ -49,6 +56,13 @@ namespace CefSharp

property int BrowserId;
property bool IsPopup;
property bool JavascriptBindingApiEnabled;
property bool JavascriptBindingApiHasAllowOrigins;
property CefRefPtr<CefListValue> JavascriptBindingApiAllowOrigins
{
CefRefPtr<CefListValue> get() { return _javascriptBindingApiAllowOrigins.get(); }
void set(CefRefPtr<CefListValue> value) { _javascriptBindingApiAllowOrigins = value.get(); }
}

#ifndef NETCOREAPP
// This allows us to create the WCF proxies back to our parent process.
Expand Down
16 changes: 16 additions & 0 deletions CefSharp.Core.Runtime/ManagedCefBrowserAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ namespace CefSharp

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

auto hasJavascriptBindingApiAllowOrigins = objectRepositorySettings->HasJavascriptBindingApiAllowOrigins();

extraInfo->SetBool("JavascriptBindingApiHasAllowOrigins", hasJavascriptBindingApiAllowOrigins);

if (hasJavascriptBindingApiAllowOrigins)
{
auto allowOriginList = CefListValue::Create();

for (int i = 0; i < objectRepositorySettings->JavascriptBindingApiAllowOrigins->Length; i++)
{
allowOriginList->SetString(i, StringUtils::ToNative(objectRepositorySettings->JavascriptBindingApiAllowOrigins[i]));
}

extraInfo->SetList("JavascriptBindingApiAllowOrigins", allowOriginList);
}

CefRefPtr<CefRequestContext> requestCtx;

if (requestContext != nullptr)
Expand Down
121 changes: 120 additions & 1 deletion CefSharp.Test/JavascriptBinding/JavascriptBindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,123 @@ public async Task ShouldDisableJsBindingApi()
}
}

[Theory]
[InlineData("notallowed")]
[InlineData("notallowed", "alsonotallowed")]
[InlineData("notallowed", "alsonotallowed", "stillnotallowed")]
public async Task ShouldDisableJsBindingApiForOrigin(params string[] origins)
{
using (var browser = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false))
{
var settings = browser.JavascriptObjectRepository.Settings;
settings.JavascriptBindingApiEnabled = true;
settings.JavascriptBindingApiAllowOrigins = origins;

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

var loadResponse = await browser.WaitForInitialLoadAsync();

Assert.True(loadResponse.Success);

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 ShouldEnableJsBindingApiWhenOriginsListIsEmpty()
{
using (var browser = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false))
{
var settings = browser.JavascriptObjectRepository.Settings;
settings.JavascriptBindingApiEnabled = true;
settings.JavascriptBindingApiAllowOrigins = new string[0];

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

var loadResponse = await browser.WaitForInitialLoadAsync();

Assert.True(loadResponse.Success);

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(CefExample.BaseUrl)]
[InlineData(CefExample.BaseUrl + "/")]
public async Task ShouldEnableJsBindingApiForOriginWithOrWithoutTrailingSlash(string configuredOrigin)
{
using (var browser = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false))
{
var settings = browser.JavascriptObjectRepository.Settings;
settings.JavascriptBindingApiEnabled = true;
settings.JavascriptBindingApiAllowOrigins = new string[] { configuredOrigin };

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

var loadResponse = await browser.WaitForInitialLoadAsync();

Assert.True(loadResponse.Success);

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(CefExample.BaseUrl + "/")]
[InlineData("someorigin", CefExample.BaseUrl + "/")]
[InlineData(CefExample.BaseUrl + "/", "someorigin")]
[InlineData("firstorigin", "secondorigin", CefExample.BaseUrl + "/")]
[InlineData("firstorigin", CefExample.BaseUrl + "/", "secondorigin")]
public async Task ShouldEnableJsBindingApiForOrigin(params string[] origins)
{
using (var browser = new ChromiumWebBrowser(CefExample.BindingApiCustomObjectNameTestUrl, automaticallyCreateBrowser: false))
{
var settings = browser.JavascriptObjectRepository.Settings;
settings.JavascriptBindingApiEnabled = true;
settings.JavascriptBindingApiAllowOrigins = origins;

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

var loadResponse = await browser.WaitForInitialLoadAsync();

Assert.True(loadResponse.Success);

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);
}
}

[Fact]
public async Task ShouldEnableJsBindingApi()
{
Expand All @@ -141,7 +258,9 @@ public async Task ShouldEnableJsBindingApi()
//To modify the settings we need to defer browser creation slightly
browser.CreateBrowser();

await browser.WaitForInitialLoadAsync();
var loadResponse = await browser.WaitForInitialLoadAsync();

Assert.True(loadResponse.Success);

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