diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp index 620483497..67334e93a 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp @@ -100,15 +100,15 @@ namespace CefSharp } } - _jsBindingApiEnabled = extraInfo->GetBool("JavascriptBindingApiEnabled"); - _jsBindingApiHasAllowOrigins = extraInfo->GetBool("JavascriptBindingApiHasAllowOrigins"); + wrapper->JavascriptBindingApiEnabled = extraInfo->GetBool("JavascriptBindingApiEnabled"); + wrapper->JavascriptBindingApiHasAllowOrigins = extraInfo->GetBool("JavascriptBindingApiHasAllowOrigins"); - if (_jsBindingApiHasAllowOrigins) + if (wrapper->JavascriptBindingApiHasAllowOrigins) { auto allowOrigins = extraInfo->GetList("JavascriptBindingApiAllowOrigins"); if (allowOrigins.get() && allowOrigins->IsValid()) { - _jsBindingApiAllowOrigins = allowOrigins->Copy(); + wrapper->JavascriptBindingApiAllowOrigins = allowOrigins->Copy(); } } @@ -158,90 +158,53 @@ namespace CefSharp } } - if (_jsBindingApiEnabled) - { - auto createObjects = true; + auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier()); - if (_jsBindingApiHasAllowOrigins) + 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 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) { - createObjects = false; - - auto frameUrl = frame->GetURL(); - - CefURLParts frameUrlParts; - - if (CefParseURL(frameUrl, frameUrlParts)) - { - auto frameUrlOrigin = CefString(frameUrlParts.origin.str, frameUrlParts.origin.length); - auto clrframeUrlOrigin = StringUtils::ToClr(frameUrlOrigin); - - auto size = static_cast(_jsBindingApiAllowOrigins->GetSize()); - - for (int i = 0; i < size; i++) - { - auto origin = _jsBindingApiAllowOrigins->GetString(i); - - auto clrOrigin = StringUtils::ToClr(origin); - - auto originEqual = String::Compare(clrframeUrlOrigin, clrOrigin, StringComparison::InvariantCultureIgnoreCase); - - if (originEqual == 0) - { - createObjects = true; - - break; - } - } - } + 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 (createObjects) + if (createCefSharpObjCamelCase) { - //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(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 (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(_jsBindingPropertyNameCamelCase, cefSharpObjCamelCase, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - } + 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); } } @@ -378,6 +341,52 @@ namespace CefSharp return rootObject; } + bool CefAppUnmanagedWrapper::IsJavascriptBindingApiAllowed(CefRefPtr frame) + { + auto browserWrapper = FindBrowserWrapper(frame->GetBrowser()->GetIdentifier()); + + if (browserWrapper == nullptr || !browserWrapper->JavascriptBindingApiHasAllowOrigins) + { + return true; + } + + 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(browserWrapper->JavascriptBindingApiAllowOrigins->GetSize()); + + for (int i = 0; i < size; i++) + { + auto origin = browserWrapper->JavascriptBindingApiAllowOrigins->GetString(i); + auto frameOriginPtr = reinterpret_cast(frameUrlOrigin.c_str()); + auto allowedOriginPtr = reinterpret_cast(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; diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h index ffae4d65f..12d0c4984 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h @@ -28,9 +28,6 @@ namespace CefSharp gcroot^> _browserWrappers; bool _focusedNodeChangedEnabled; bool _legacyBindingEnabled; - bool _jsBindingApiEnabled = true; - bool _jsBindingApiHasAllowOrigins = false; - CefRefPtr _jsBindingApiAllowOrigins; // The property names used to call bound objects CefString _jsBindingPropertyName; @@ -40,6 +37,7 @@ namespace CefSharp gcroot^> _javascriptObjects; gcroot _registerBoundObjectRegistry; + bool IsJavascriptBindingApiAllowed(CefRefPtr frame); public: static const CefString kPromiseCreatorScript; @@ -71,8 +69,6 @@ namespace CefSharp } delete _onBrowserCreated; delete _onBrowserDestroyed; - - _jsBindingApiAllowOrigins = nullptr; } CefBrowserWrapper^ FindBrowserWrapper(int browserId); diff --git a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h index ef9ef6dfb..d285eae4a 100644 --- a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h @@ -28,6 +28,7 @@ namespace CefSharp { private: MCefRefPtr _cefBrowser; + MCefRefPtr _javascriptBindingApiAllowOrigins; internal: //Frame Identifier is used as Key @@ -41,11 +42,17 @@ namespace CefSharp IsPopup = cefBrowser->IsPopup(); JavascriptRootObjectWrappers = gcnew ConcurrentDictionary(); + + JavascriptBindingApiEnabled = true; + JavascriptBindingApiHasAllowOrigins = false; + JavascriptBindingApiAllowOrigins = nullptr; } !CefBrowserWrapper() { _cefBrowser = nullptr; + + _javascriptBindingApiAllowOrigins = nullptr; } ~CefBrowserWrapper() @@ -65,6 +72,13 @@ namespace CefSharp property int BrowserId; property bool IsPopup; + property bool JavascriptBindingApiEnabled; + property bool JavascriptBindingApiHasAllowOrigins; + property CefRefPtr JavascriptBindingApiAllowOrigins + { + CefRefPtr get() { return _javascriptBindingApiAllowOrigins.get(); } + void set(CefRefPtr value) { _javascriptBindingApiAllowOrigins = value.get(); } + } #ifndef NETCOREAPP // This allows us to create the WCF proxies back to our parent process. diff --git a/CefSharp/JavascriptBinding/JavascriptBindingSettings.cs b/CefSharp/JavascriptBinding/JavascriptBindingSettings.cs index cfd6b6742..50fbb120a 100644 --- a/CefSharp/JavascriptBinding/JavascriptBindingSettings.cs +++ b/CefSharp/JavascriptBinding/JavascriptBindingSettings.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using CefSharp.Internals; +using System; namespace CefSharp.JavascriptBinding { @@ -48,8 +49,17 @@ public string[] JavascriptBindingApiAllowOrigins set { ThrowIfFrozen(); - - javascriptBindingApiAllowOrigins = value; + if (value != null) + { + javascriptBindingApiAllowOrigins = Array.ConvertAll( + value, + origin => origin.EndsWith("/") ? origin.Substring(0, origin.Length - 1) : origin + ); + } + else + { + javascriptBindingApiAllowOrigins = null; + } } }