mirror of
https://github.com/electron/electron.git
synced 2026-05-02 03:00:22 -04:00
refactor: attach translator holder via v8::Function data slot (#51120)
refactor: attach translator holder via v8::Function data slot (#50867)
(cherry picked from commit bfa5c93332)
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
#include "shell/common/gin_helper/callback.h"
|
||||
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "gin/dictionary.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "shell/common/process_util.h"
|
||||
|
||||
namespace gin_helper {
|
||||
@@ -31,42 +31,28 @@ struct TranslatorHolder {
|
||||
delete data.GetParameter();
|
||||
}
|
||||
|
||||
static gin::DeprecatedWrapperInfo kWrapperInfo;
|
||||
|
||||
v8::Global<v8::External> handle;
|
||||
Translator translator;
|
||||
bool one_time = false;
|
||||
bool called = false;
|
||||
};
|
||||
|
||||
gin::DeprecatedWrapperInfo TranslatorHolder::kWrapperInfo = {
|
||||
gin::kEmbedderNativeGin};
|
||||
void CallTranslator(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
gin::Arguments args(info);
|
||||
auto* holder =
|
||||
static_cast<TranslatorHolder*>(info.Data().As<v8::External>()->Value(
|
||||
v8::kExternalPointerTypeTagDefault));
|
||||
|
||||
void CallTranslator(v8::Local<v8::External> external,
|
||||
v8::Local<v8::Object> state,
|
||||
gin::Arguments* args) {
|
||||
// Whether the callback should only be called once.
|
||||
v8::Isolate* isolate = args->isolate();
|
||||
auto context = isolate->GetCurrentContext();
|
||||
bool one_time =
|
||||
state->Has(context, gin::StringToSymbol(isolate, "oneTime")).ToChecked();
|
||||
|
||||
// Check if the callback has already been called.
|
||||
if (one_time) {
|
||||
auto called_symbol = gin::StringToSymbol(isolate, "called");
|
||||
if (state->Has(context, called_symbol).ToChecked()) {
|
||||
args->ThrowTypeError("One-time callback was called more than once");
|
||||
return;
|
||||
} else {
|
||||
state->Set(context, called_symbol, v8::True(isolate)).ToChecked();
|
||||
}
|
||||
if (holder->one_time && holder->called) {
|
||||
args.ThrowTypeError("One-time callback was called more than once");
|
||||
return;
|
||||
}
|
||||
holder->called = true;
|
||||
|
||||
auto* holder = static_cast<TranslatorHolder*>(
|
||||
external->Value(v8::kExternalPointerTypeTagDefault));
|
||||
holder->translator.Run(args);
|
||||
holder->translator.Run(&args);
|
||||
|
||||
// Free immediately for one-time callback.
|
||||
if (one_time)
|
||||
delete holder;
|
||||
if (holder->one_time)
|
||||
holder->translator.Reset();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -120,41 +106,11 @@ v8::Local<v8::Function> SafeV8Function::NewHandle(v8::Isolate* isolate) const {
|
||||
v8::Local<v8::Value> CreateFunctionFromTranslator(v8::Isolate* isolate,
|
||||
const Translator& translator,
|
||||
bool one_time) {
|
||||
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
|
||||
auto* wrapper_info = &TranslatorHolder::kWrapperInfo;
|
||||
v8::Local<v8::FunctionTemplate> constructor =
|
||||
data->DeprecatedGetFunctionTemplate(wrapper_info);
|
||||
// The FunctionTemplate is cached.
|
||||
if (constructor.IsEmpty()) {
|
||||
constructor =
|
||||
CreateFunctionTemplate(isolate, base::BindRepeating(&CallTranslator));
|
||||
data->DeprecatedSetFunctionTemplate(wrapper_info, constructor);
|
||||
}
|
||||
|
||||
auto* holder = new TranslatorHolder(isolate);
|
||||
holder->translator = translator;
|
||||
auto state = gin::Dictionary::CreateEmpty(isolate);
|
||||
if (one_time)
|
||||
state.Set("oneTime", true);
|
||||
holder->one_time = one_time;
|
||||
auto context = isolate->GetCurrentContext();
|
||||
return BindFunctionWith(
|
||||
isolate, context, constructor->GetFunction(context).ToLocalChecked(),
|
||||
holder->handle.Get(isolate), gin::ConvertToV8(isolate, state));
|
||||
}
|
||||
|
||||
// func.bind(func, arg1).
|
||||
// NB(zcbenz): Using C++11 version crashes VS.
|
||||
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
||||
v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Function> func,
|
||||
v8::Local<v8::Value> arg1,
|
||||
v8::Local<v8::Value> arg2) {
|
||||
v8::MaybeLocal<v8::Value> bind =
|
||||
func->Get(context, gin::StringToV8(isolate, "bind"));
|
||||
CHECK(!bind.IsEmpty());
|
||||
v8::Local<v8::Function> bind_func = bind.ToLocalChecked().As<v8::Function>();
|
||||
v8::Local<v8::Value> converted[] = {func, arg1, arg2};
|
||||
return bind_func->Call(context, func, std::size(converted), converted)
|
||||
return v8::Function::New(context, CallTranslator, holder->handle.Get(isolate))
|
||||
.ToLocalChecked();
|
||||
}
|
||||
|
||||
|
||||
@@ -118,11 +118,6 @@ using Translator = base::RepeatingCallback<void(gin::Arguments* args)>;
|
||||
v8::Local<v8::Value> CreateFunctionFromTranslator(v8::Isolate* isolate,
|
||||
const Translator& translator,
|
||||
bool one_time);
|
||||
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
||||
v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Function> func,
|
||||
v8::Local<v8::Value> arg1,
|
||||
v8::Local<v8::Value> arg2);
|
||||
|
||||
// Calls callback with Arguments.
|
||||
template <typename Sig>
|
||||
|
||||
@@ -372,6 +372,33 @@ describe('contextBridge', () => {
|
||||
expect(result).to.equal(123);
|
||||
});
|
||||
|
||||
it('should proxy promises correctly when Function.prototype has been overridden in the main world', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.exposeInMainWorld('example', {
|
||||
getPromise: () => Promise.resolve('proxied-ok')
|
||||
});
|
||||
});
|
||||
const result = await callWithBindings((root: any) => {
|
||||
return new Promise(resolve => {
|
||||
let observed = false;
|
||||
const original = Function.prototype.bind;
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Function.prototype.bind = new Proxy(original, {
|
||||
apply (target, thisArg, args) {
|
||||
observed = true;
|
||||
return Reflect.apply(target, thisArg, args);
|
||||
}
|
||||
});
|
||||
root.example.getPromise().then((v: string) => {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Function.prototype.bind = original;
|
||||
resolve({ observed, value: v });
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(result).to.deep.equal({ observed: false, value: 'proxied-ok' });
|
||||
});
|
||||
|
||||
it('should proxy methods', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.exposeInMainWorld('example', {
|
||||
|
||||
Reference in New Issue
Block a user