Skip to content

Commit

Permalink
JavaScript Binding - Cache objects on a per browser basis
Browse files Browse the repository at this point in the history
Issue #2306
  • Loading branch information
amaitland committed Apr 12, 2023
1 parent 5cf0185 commit cdc2b11
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 31 deletions.
42 changes: 13 additions & 29 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,7 @@ namespace CefSharp
{
auto javascriptObjects = DeserializeJsObjects(objects, 0);

for each (JavascriptObject ^ obj in Enumerable::OfType<JavascriptObject^>(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);
}
}

Expand All @@ -113,6 +100,8 @@ namespace CefSharp
_onBrowserDestroyed->Invoke(wrapper);
delete wrapper;
}

_javascriptObjectCache->ClearCache(browser->GetIdentifier());
};

void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
Expand All @@ -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());
}
}

Expand All @@ -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());

Expand Down Expand Up @@ -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<JavascriptObject^>(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());

Expand Down
4 changes: 2 additions & 2 deletions CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace CefSharp
CefString _jsBindingPropertyNameCamelCase;

// The serialized registered object data waiting to be used.
gcroot<Dictionary<String^, JavascriptObject^>^> _javascriptObjects;
gcroot<JavaScriptObjectCache^> _javascriptObjectCache;

gcroot<RegisterBoundObjectRegistry^> _registerBoundObjectRegistry;

Expand All @@ -49,7 +49,7 @@ namespace CefSharp
_onBrowserDestroyed = onBrowserDestroyed;
_browserWrappers = gcnew ConcurrentDictionary<int, CefBrowserWrapper^>();
_focusedNodeChangedEnabled = enableFocusedNodeChanged;
_javascriptObjects = gcnew Dictionary<String^, JavascriptObject^>();
_javascriptObjectCache = gcnew JavaScriptObjectCache();
_registerBoundObjectRegistry = gcnew RegisterBoundObjectRegistry();
_legacyBindingEnabled = false;
_jsBindingPropertyName = "CefSharp";
Expand Down
94 changes: 94 additions & 0 deletions CefSharp/RenderProcess/JavaScriptObjectCache.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// JavaScriptObjectCache is used in the RenderProcess to cache
/// JavaScript bound objects at a CefBrowser level
/// </summary>
public class JavaScriptObjectCache
{
private readonly Dictionary<int, Dictionary<string, JavascriptObject>> cache
= new Dictionary<int, Dictionary<string, JavascriptObject>>();

/// <summary>
/// Remove the Browser specific Cache
/// </summary>
/// <param name="browserId">browser Id</param>
public void ClearCache(int browserId)
{
cache.Remove(browserId);
}

/// <summary>
/// Insert or Update the <paramref name="javascriptObject"/> within the Cache
/// </summary>
/// <param name="browserId">browser id</param>
/// <param name="javascriptObject">JavaScript object</param>
/// <exception cref="InvalidOperationException"></exception>
public void InsertOrUpdate(int browserId, IList<JavascriptObject> javascriptObjects)
{
var dict = GetCacheInternal(browserId);

foreach (var obj in javascriptObjects)
{
if (dict.ContainsKey(obj.Name))
{
dict.Remove(obj.Name);
}

dict.Add(obj.Name, obj);
}
}

/// <summary>
/// Gets a collection of <see cref="JavascriptObject"/>s
/// for the given <paramref name="browserId"/>
/// </summary>
/// <param name="browserId">browser Id</param>
/// <returns>Collection of current bound objects for the browser</returns>
/// <exception cref="InvalidOperationException"></exception>
public ICollection<JavascriptObject> GetCacheValues(int browserId)
{
if (cache.TryGetValue(browserId, out var dict))
{
return dict.Values;
}

return new List<JavascriptObject>();
}

/// <summary>
/// Gets the browser specific cache (dictionary) based on it's Id
/// </summary>
/// <param name="browserId">browser Id</param>
/// <returns>Dictionary of cache <see cref="JavascriptObject"/>'s.</returns>
/// <exception cref="InvalidOperationException"></exception>
public Dictionary<string, JavascriptObject> GetCache(int browserId)
{
var dict = GetCacheInternal(browserId);

return dict;
}

private Dictionary<string, JavascriptObject> GetCacheInternal(int browserId)
{
Dictionary<string, JavascriptObject> dict;

if (!cache.TryGetValue(browserId, out dict))
{
dict = new Dictionary<string, JavascriptObject>();

cache.Add(browserId, dict);
}

return dict;
}
}
}

0 comments on commit cdc2b11

Please sign in to comment.