Skip to content

Commit 4fc242c

Browse files
committed
JavaScript Binding - Cache objects on a per browser basis
Issue #2306
1 parent 3a749c0 commit 4fc242c

File tree

3 files changed

+109
-31
lines changed

3 files changed

+109
-31
lines changed

CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,7 @@ namespace CefSharp
8282
{
8383
auto javascriptObjects = DeserializeJsObjects(objects, 0);
8484

85-
for each (JavascriptObject ^ obj in Enumerable::OfType<JavascriptObject^>(javascriptObjects))
86-
{
87-
//Using LegacyBinding with multiple ChromiumWebBrowser instances that share the same
88-
//render process and using LegacyBinding will cause problems for the limited caching implementation
89-
//that exists at the moment, for now we'll remove an object if already exists, same behaviour
90-
//as the new binding method.
91-
//TODO: This should be removed when https://github.com/cefsharp/CefSharp/issues/2306
92-
//Is complete as objects will be stored at the browser level
93-
if (_javascriptObjects->ContainsKey(obj->JavascriptName))
94-
{
95-
_javascriptObjects->Remove(obj->JavascriptName);
96-
}
97-
_javascriptObjects->Add(obj->JavascriptName, obj);
98-
}
85+
_javascriptObjectCache->InsertOrUpdate(browser->GetIdentifier(), javascriptObjects);
9986
}
10087
}
10188

@@ -118,6 +105,8 @@ namespace CefSharp
118105
_onBrowserDestroyed->Invoke(wrapper);
119106
delete wrapper;
120107
}
108+
109+
_javascriptObjectCache->ClearCache(browser->GetIdentifier());
121110
};
122111

123112
void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
@@ -135,9 +124,11 @@ namespace CefSharp
135124

136125
if (_legacyBindingEnabled)
137126
{
138-
if (_javascriptObjects->Count > 0 && rootObject != nullptr)
127+
auto values = _javascriptObjectCache->GetCacheValues(browser->GetIdentifier());
128+
129+
if (values->Count > 0 && rootObject != nullptr)
139130
{
140-
rootObject->Bind(_javascriptObjects->Values, context->GetGlobal());
131+
rootObject->Bind(values, context->GetGlobal());
141132
}
142133
}
143134

@@ -147,13 +138,14 @@ namespace CefSharp
147138
auto global = context->GetGlobal();
148139
auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier());
149140
auto processId = System::Diagnostics::Process::GetCurrentProcess()->Id;
141+
auto objectCache = _javascriptObjectCache->GetCache(browser->GetIdentifier());
150142

151143
//TODO: JSB: Split functions into their own classes
152144
//Browser wrapper is only used for BindObjectAsync
153-
auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, _javascriptObjects, browserWrapper));
154-
auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(_javascriptObjects));
155-
auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(_javascriptObjects));
156-
auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(_javascriptObjects));
145+
auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, objectCache, browserWrapper));
146+
auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(objectCache));
147+
auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(objectCache));
148+
auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(objectCache));
157149
auto postMessageFunction = CefV8Value::CreateFunction(kPostMessage, new JavascriptPostMessageHandler(rootObject == nullptr ? nullptr : rootObject->CallbackRegistry));
158150
auto promiseHandlerFunction = CefV8Value::CreateFunction(kSendEvalScriptResponse, new JavascriptPromiseHandler());
159151

@@ -627,15 +619,7 @@ namespace CefSharp
627619
auto javascriptObjects = DeserializeJsObjects(argList, 1);
628620

629621
//Caching of JavascriptObjects
630-
//TODO: JSB Should caching be configurable? On a per object basis?
631-
for each (JavascriptObject ^ obj in Enumerable::OfType<JavascriptObject^>(javascriptObjects))
632-
{
633-
if (_javascriptObjects->ContainsKey(obj->JavascriptName))
634-
{
635-
_javascriptObjects->Remove(obj->JavascriptName);
636-
}
637-
_javascriptObjects->Add(obj->JavascriptName, obj);
638-
}
622+
_javascriptObjectCache->InsertOrUpdate(browser->GetIdentifier(), javascriptObjects);
639623

640624
auto rootObject = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier());
641625

CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace CefSharp
3535
CefString _jsBindingPropertyNameCamelCase;
3636

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

4040
gcroot<RegisterBoundObjectRegistry^> _registerBoundObjectRegistry;
4141

@@ -49,7 +49,7 @@ namespace CefSharp
4949
_onBrowserDestroyed = onBrowserDestroyed;
5050
_browserWrappers = gcnew ConcurrentDictionary<int, CefBrowserWrapper^>();
5151
_focusedNodeChangedEnabled = enableFocusedNodeChanged;
52-
_javascriptObjects = gcnew Dictionary<String^, JavascriptObject^>();
52+
_javascriptObjectCache = gcnew JavaScriptObjectCache();
5353
_registerBoundObjectRegistry = gcnew RegisterBoundObjectRegistry();
5454
_legacyBindingEnabled = false;
5555
_jsBindingPropertyName = "CefSharp";
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright © 2023 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using CefSharp.Internals;
8+
9+
namespace CefSharp.RenderProcess
10+
{
11+
/// <summary>
12+
/// JavaScriptObjectCache is used in the RenderProcess to cache
13+
/// JavaScript bound objects at a CefBrowser level
14+
/// </summary>
15+
public class JavaScriptObjectCache
16+
{
17+
private readonly Dictionary<int, Dictionary<string, JavascriptObject>> cache
18+
= new Dictionary<int, Dictionary<string, JavascriptObject>>();
19+
20+
/// <summary>
21+
/// Remove the Browser specific Cache
22+
/// </summary>
23+
/// <param name="browserId">browser Id</param>
24+
public void ClearCache(int browserId)
25+
{
26+
cache.Remove(browserId);
27+
}
28+
29+
/// <summary>
30+
/// Insert or Update the <paramref name="javascriptObject"/> within the Cache
31+
/// </summary>
32+
/// <param name="browserId">browser id</param>
33+
/// <param name="javascriptObject">JavaScript object</param>
34+
/// <exception cref="InvalidOperationException"></exception>
35+
public void InsertOrUpdate(int browserId, IList<JavascriptObject> javascriptObjects)
36+
{
37+
var dict = GetCacheInternal(browserId);
38+
39+
foreach (var obj in javascriptObjects)
40+
{
41+
if (dict.ContainsKey(obj.Name))
42+
{
43+
dict.Remove(obj.Name);
44+
}
45+
46+
dict.Add(obj.Name, obj);
47+
}
48+
}
49+
50+
/// <summary>
51+
/// Gets a collection of <see cref="JavascriptObject"/>s
52+
/// for the given <paramref name="browserId"/>
53+
/// </summary>
54+
/// <param name="browserId">browser Id</param>
55+
/// <returns>Collection of current bound objects for the browser</returns>
56+
/// <exception cref="InvalidOperationException"></exception>
57+
public ICollection<JavascriptObject> GetCacheValues(int browserId)
58+
{
59+
if (cache.TryGetValue(browserId, out var dict))
60+
{
61+
return dict.Values;
62+
}
63+
64+
return new List<JavascriptObject>();
65+
}
66+
67+
/// <summary>
68+
/// Gets the browser specific cache (dictionary) based on it's Id
69+
/// </summary>
70+
/// <param name="browserId">browser Id</param>
71+
/// <returns>Dictionary of cache <see cref="JavascriptObject"/>'s.</returns>
72+
/// <exception cref="InvalidOperationException"></exception>
73+
public Dictionary<string, JavascriptObject> GetCache(int browserId)
74+
{
75+
var dict = GetCacheInternal(browserId);
76+
77+
return dict;
78+
}
79+
80+
private Dictionary<string, JavascriptObject> GetCacheInternal(int browserId)
81+
{
82+
Dictionary<string, JavascriptObject> dict;
83+
84+
if (!cache.TryGetValue(browserId, out dict))
85+
{
86+
dict = new Dictionary<string, JavascriptObject>();
87+
88+
cache.Add(browserId, dict);
89+
}
90+
91+
return dict;
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)