perf: do not double-proxy methods being return over the contextBridge (#28285)
This commit is contained in:
parent
89df6b98da
commit
7918ddb026
2 changed files with 33 additions and 1 deletions
|
@ -41,6 +41,8 @@ namespace context_bridge {
|
||||||
const char* const kProxyFunctionPrivateKey = "electron_contextBridge_proxy_fn";
|
const char* const kProxyFunctionPrivateKey = "electron_contextBridge_proxy_fn";
|
||||||
const char* const kSupportsDynamicPropertiesPrivateKey =
|
const char* const kSupportsDynamicPropertiesPrivateKey =
|
||||||
"electron_contextBridge_supportsDynamicProperties";
|
"electron_contextBridge_supportsDynamicProperties";
|
||||||
|
const char* const kOriginalFunctionPrivateKey =
|
||||||
|
"electron_contextBridge_original_fn";
|
||||||
|
|
||||||
} // namespace context_bridge
|
} // namespace context_bridge
|
||||||
|
|
||||||
|
@ -179,9 +181,26 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
|
||||||
// the global handle at the right time.
|
// the global handle at the right time.
|
||||||
if (value->IsFunction()) {
|
if (value->IsFunction()) {
|
||||||
auto func = v8::Local<v8::Function>::Cast(value);
|
auto func = v8::Local<v8::Function>::Cast(value);
|
||||||
|
v8::MaybeLocal<v8::Value> maybe_original_fn = GetPrivate(
|
||||||
|
source_context, func, context_bridge::kOriginalFunctionPrivateKey);
|
||||||
|
|
||||||
{
|
{
|
||||||
v8::Context::Scope destination_scope(destination_context);
|
v8::Context::Scope destination_scope(destination_context);
|
||||||
|
v8::Local<v8::Value> proxy_func;
|
||||||
|
|
||||||
|
// If this function has already been sent over the bridge,
|
||||||
|
// then it is being sent _back_ over the bridge and we can
|
||||||
|
// simply return the original method here for performance reasons
|
||||||
|
|
||||||
|
// For safety reasons we check if the destination context is the
|
||||||
|
// creation context of the original method. If it's not we proceed
|
||||||
|
// with the proxy logic
|
||||||
|
if (maybe_original_fn.ToLocal(&proxy_func) && proxy_func->IsFunction() &&
|
||||||
|
proxy_func.As<v8::Object>()->CreationContext() ==
|
||||||
|
destination_context) {
|
||||||
|
return v8::MaybeLocal<v8::Value>(proxy_func);
|
||||||
|
}
|
||||||
|
|
||||||
v8::Local<v8::Object> state =
|
v8::Local<v8::Object> state =
|
||||||
v8::Object::New(destination_context->GetIsolate());
|
v8::Object::New(destination_context->GetIsolate());
|
||||||
SetPrivate(destination_context, state,
|
SetPrivate(destination_context, state,
|
||||||
|
@ -190,10 +209,12 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
|
||||||
context_bridge::kSupportsDynamicPropertiesPrivateKey,
|
context_bridge::kSupportsDynamicPropertiesPrivateKey,
|
||||||
gin::ConvertToV8(destination_context->GetIsolate(),
|
gin::ConvertToV8(destination_context->GetIsolate(),
|
||||||
support_dynamic_properties));
|
support_dynamic_properties));
|
||||||
v8::Local<v8::Value> proxy_func;
|
|
||||||
if (!v8::Function::New(destination_context, ProxyFunctionWrapper, state)
|
if (!v8::Function::New(destination_context, ProxyFunctionWrapper, state)
|
||||||
.ToLocal(&proxy_func))
|
.ToLocal(&proxy_func))
|
||||||
return v8::MaybeLocal<v8::Value>();
|
return v8::MaybeLocal<v8::Value>();
|
||||||
|
SetPrivate(destination_context, proxy_func.As<v8::Object>(),
|
||||||
|
context_bridge::kOriginalFunctionPrivateKey, func);
|
||||||
object_cache->CacheProxiedObject(value, proxy_func);
|
object_cache->CacheProxiedObject(value, proxy_func);
|
||||||
return v8::MaybeLocal<v8::Value>(proxy_func);
|
return v8::MaybeLocal<v8::Value>(proxy_func);
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,6 +353,17 @@ describe('contextBridge', () => {
|
||||||
expect(result).equal('return-value');
|
expect(result).equal('return-value');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not double-proxy functions when they are returned to their origin side of the bridge', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', (fn: any) => fn);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings(async (root: any) => {
|
||||||
|
const fn = () => null;
|
||||||
|
return root.example(fn) === fn;
|
||||||
|
});
|
||||||
|
expect(result).equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should proxy methods that are callable multiple times', async () => {
|
it('should proxy methods that are callable multiple times', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue