Skip to content

Commit cdc2b11

Browse files
committed
JavaScript Binding - Cache objects on a per browser basis
Issue #2306
1 parent 5cf0185 commit cdc2b11

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
@@ -77,20 +77,7 @@ namespace CefSharp
7777
{
7878
auto javascriptObjects = DeserializeJsObjects(objects, 0);
7979

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

@@ -113,6 +100,8 @@ namespace CefSharp
113100
_onBrowserDestroyed->Invoke(wrapper);
114101
delete wrapper;
115102
}
103+
104+
_javascriptObjectCache->ClearCache(browser->GetIdentifier());
116105
};
117106

118107
void CefAppUnmanagedWrapper::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
@@ -130,9 +119,11 @@ namespace CefSharp
130119

131120
if (_legacyBindingEnabled)
132121
{
133-
if (_javascriptObjects->Count > 0 && rootObject != nullptr)
122+
auto values = _javascriptObjectCache->GetCacheValues(browser->GetIdentifier());
123+
124+
if (values->Count > 0 && rootObject != nullptr)
134125
{
135-
rootObject->Bind(_javascriptObjects->Values, context->GetGlobal());
126+
rootObject->Bind(values, context->GetGlobal());
136127
}
137128
}
138129

@@ -142,13 +133,14 @@ namespace CefSharp
142133
auto global = context->GetGlobal();
143134
auto browserWrapper = FindBrowserWrapper(browser->GetIdentifier());
144135
auto processId = System::Diagnostics::Process::GetCurrentProcess()->Id;
136+
auto objectCache = _javascriptObjectCache->GetCache(browser->GetIdentifier());
145137

146138
//TODO: JSB: Split functions into their own classes
147139
//Browser wrapper is only used for BindObjectAsync
148-
auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, _javascriptObjects, browserWrapper));
149-
auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(_javascriptObjects));
150-
auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(_javascriptObjects));
151-
auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(_javascriptObjects));
140+
auto bindObjAsyncFunction = CefV8Value::CreateFunction(kBindObjectAsync, new BindObjectAsyncHandler(_registerBoundObjectRegistry, objectCache, browserWrapper));
141+
auto unBindObjFunction = CefV8Value::CreateFunction(kDeleteBoundObject, new RegisterBoundObjectHandler(objectCache));
142+
auto removeObjectFromCacheFunction = CefV8Value::CreateFunction(kRemoveObjectFromCache, new RegisterBoundObjectHandler(objectCache));
143+
auto isObjectCachedFunction = CefV8Value::CreateFunction(kIsObjectCached, new RegisterBoundObjectHandler(objectCache));
152144
auto postMessageFunction = CefV8Value::CreateFunction(kPostMessage, new JavascriptPostMessageHandler(rootObject == nullptr ? nullptr : rootObject->CallbackRegistry));
153145
auto promiseHandlerFunction = CefV8Value::CreateFunction(kSendEvalScriptResponse, new JavascriptPromiseHandler());
154146

@@ -621,15 +613,7 @@ namespace CefSharp
621613
auto javascriptObjects = DeserializeJsObjects(argList, 1);
622614

623615
//Caching of JavascriptObjects
624-
//TODO: JSB Should caching be configurable? On a per object basis?
625-
for each (JavascriptObject ^ obj in Enumerable::OfType<JavascriptObject^>(javascriptObjects))
626-
{
627-
if (_javascriptObjects->ContainsKey(obj->JavascriptName))
628-
{
629-
_javascriptObjects->Remove(obj->JavascriptName);
630-
}
631-
_javascriptObjects->Add(obj->JavascriptName, obj);
632-
}
616+
_javascriptObjectCache->InsertOrUpdate(browser->GetIdentifier(), javascriptObjects);
633617

634618
auto rootObject = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier());
635619

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)