diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp index dfbdb75781..97530bc0d3 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp @@ -77,20 +77,7 @@ namespace CefSharp { auto javascriptObjects = DeserializeJsObjects(objects, 0); - for each (JavascriptObject ^ obj in Enumerable::OfType(javascriptObjects)) - { - //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. - //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)) - { - _javascriptObjects->Remove(obj->JavascriptName); - } - _javascriptObjects->Add(obj->JavascriptName, obj); - } + _javascriptObjectCache->InsertOrUpdate(browser->GetIdentifier(), javascriptObjects); } } @@ -113,6 +100,8 @@ namespace CefSharp _onBrowserDestroyed->Invoke(wrapper); delete wrapper; } + + _javascriptObjectCache->ClearCache(browser->GetIdentifier()); }; void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) @@ -130,9 +119,11 @@ namespace CefSharp if (_legacyBindingEnabled) { - if (_javascriptObjects->Count > 0 && rootObject != nullptr) + auto values = _javascriptObjectCache->GetCacheValues(browser->GetIdentifier()); + + if (values->Count > 0 && rootObject != nullptr) { - rootObject->Bind(_javascriptObjects->Values, context->GetGlobal()); + rootObject->Bind(values, context->GetGlobal()); } } @@ -142,13 +133,14 @@ namespace CefSharp auto global = context->GetGlobal(); auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier()); auto processId = System::Diagnostics::Process::GetCurrentProcess()->Id; + auto objectCache = _javascriptObjectCache->GetCache(browser->GetIdentifier()); //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 bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, objectCache, browserWrapper)); + auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(objectCache)); + auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(objectCache)); + auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(objectCache)); auto postMessageFunction = CefV8Value::CreateFunction(kPostMessage, new JavascriptPostMessageHandler(rootObject == nullptr ? nullptr : rootObject->CallbackRegistry)); auto promiseHandlerFunction = CefV8Value::CreateFunction(kSendEvalScriptResponse, new JavascriptPromiseHandler()); @@ -621,15 +613,7 @@ namespace CefSharp auto javascriptObjects = DeserializeJsObjects(argList, 1); //Caching of JavascriptObjects - //TODO: JSB Should caching be configurable? On a per object basis? - for each (JavascriptObject ^ obj in Enumerable::OfType(javascriptObjects)) - { - if (_javascriptObjects->ContainsKey(obj->JavascriptName)) - { - _javascriptObjects->Remove(obj->JavascriptName); - } - _javascriptObjects->Add(obj->JavascriptName, obj); - } + _javascriptObjectCache->InsertOrUpdate(browser->GetIdentifier(), javascriptObjects); auto rootObject = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier()); diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h index b202f31aa8..814896f8da 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h @@ -35,7 +35,7 @@ namespace CefSharp CefString _jsBindingPropertyNameCamelCase; // The serialized registered object data waiting to be used. - gcroot^> _javascriptObjects; + gcroot _javascriptObjectCache; gcroot _registerBoundObjectRegistry; @@ -49,7 +49,7 @@ namespace CefSharp _onBrowserDestroyed = onBrowserDestroyed; _browserWrappers = gcnew ConcurrentDictionary(); _focusedNodeChangedEnabled = enableFocusedNodeChanged; - _javascriptObjects = gcnew Dictionary(); + _javascriptObjectCache = gcnew JavaScriptObjectCache(); _registerBoundObjectRegistry = gcnew RegisterBoundObjectRegistry(); _legacyBindingEnabled = false; _jsBindingPropertyName = "CefSharp"; diff --git a/CefSharp/RenderProcess/JavaScriptObjectCache.cs b/CefSharp/RenderProcess/JavaScriptObjectCache.cs new file mode 100644 index 0000000000..2e6eae258f --- /dev/null +++ b/CefSharp/RenderProcess/JavaScriptObjectCache.cs @@ -0,0 +1,94 @@ +// Copyright © 2023 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Collections.Generic; +using CefSharp.Internals; + +namespace CefSharp.RenderProcess +{ + /// + /// JavaScriptObjectCache is used in the RenderProcess to cache + /// JavaScript bound objects at a CefBrowser level + /// + public class JavaScriptObjectCache + { + private readonly Dictionary> cache + = new Dictionary>(); + + /// + /// Remove the Browser specific Cache + /// + /// browser Id + public void ClearCache(int browserId) + { + cache.Remove(browserId); + } + + /// + /// Insert or Update the within the Cache + /// + /// browser id + /// JavaScript object + /// + public void InsertOrUpdate(int browserId, IList javascriptObjects) + { + var dict = GetCacheInternal(browserId); + + foreach (var obj in javascriptObjects) + { + if (dict.ContainsKey(obj.Name)) + { + dict.Remove(obj.Name); + } + + dict.Add(obj.Name, obj); + } + } + + /// + /// Gets a collection of s + /// for the given + /// + /// browser Id + /// Collection of current bound objects for the browser + /// + public ICollection GetCacheValues(int browserId) + { + if (cache.TryGetValue(browserId, out var dict)) + { + return dict.Values; + } + + return new List(); + } + + /// + /// Gets the browser specific cache (dictionary) based on it's Id + /// + /// browser Id + /// Dictionary of cache 's. + /// + public Dictionary GetCache(int browserId) + { + var dict = GetCacheInternal(browserId); + + return dict; + } + + private Dictionary GetCacheInternal(int browserId) + { + Dictionary dict; + + if (!cache.TryGetValue(browserId, out dict)) + { + dict = new Dictionary(); + + cache.Add(browserId, dict); + } + + return dict; + } + } +}