refactor: port window.open and window.opener to use ctx bridge instead of hole punching (#23235)

* refactor: port window.open and window.opener to use ctx bridge instead of hole punching

* refactor: only run the isolated init bundle when webview is enabled
This commit is contained in:
Samuel Attard 2020-04-27 12:46:04 -07:00 committed by GitHub
parent c68589f212
commit abe5cf398c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 178 additions and 85 deletions

View file

@ -146,6 +146,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
v8::Local<v8::Value> value,
context_bridge::RenderFrameFunctionStore* store,
context_bridge::ObjectCache* object_cache,
bool support_dynamic_properties,
int recursion_depth) {
if (recursion_depth >= kMaxRecursion) {
v8::Context::Scope source_scope(source_context);
@ -179,7 +180,8 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
{
v8::Local<v8::Value> proxy_func = gin_helper::CallbackToV8Leaked(
destination_context->GetIsolate(),
base::BindRepeating(&ProxyFunctionWrapper, store, func_id));
base::BindRepeating(&ProxyFunctionWrapper, store, func_id,
support_dynamic_properties));
FunctionLifeMonitor::BindTo(destination_context->GetIsolate(),
v8::Local<v8::Object>::Cast(proxy_func),
store->GetWeakPtr(), func_id);
@ -209,7 +211,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
auto val =
PassValueToOtherContext(global_source_context.Get(isolate),
global_destination_context.Get(isolate),
result, store, &object_cache, 0);
result, store, &object_cache, false, 0);
if (!val.IsEmpty())
proxied_promise->Resolve(val.ToLocalChecked());
delete proxied_promise;
@ -230,7 +232,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
auto val =
PassValueToOtherContext(global_source_context.Get(isolate),
global_destination_context.Get(isolate),
result, store, &object_cache, 0);
result, store, &object_cache, false, 0);
if (!val.IsEmpty())
proxied_promise->Reject(val.ToLocalChecked());
delete proxied_promise;
@ -276,7 +278,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
auto value_for_array = PassValueToOtherContext(
source_context, destination_context,
arr->Get(source_context, i).ToLocalChecked(), store, object_cache,
recursion_depth + 1);
support_dynamic_properties, recursion_depth + 1);
if (value_for_array.IsEmpty())
return v8::MaybeLocal<v8::Value>();
@ -293,9 +295,9 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
// Proxy all objects
if (IsPlainObject(value)) {
auto object_value = v8::Local<v8::Object>::Cast(value);
auto passed_value =
CreateProxyForAPI(object_value, source_context, destination_context,
store, object_cache, recursion_depth + 1);
auto passed_value = CreateProxyForAPI(
object_value, source_context, destination_context, store, object_cache,
support_dynamic_properties, recursion_depth + 1);
if (passed_value.IsEmpty())
return v8::MaybeLocal<v8::Value>();
return v8::MaybeLocal<v8::Value>(passed_value.ToLocalChecked());
@ -324,6 +326,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
v8::Local<v8::Value> ProxyFunctionWrapper(
context_bridge::RenderFrameFunctionStore* store,
size_t func_id,
bool support_dynamic_properties,
gin_helper::Arguments* args) {
// Context the proxy function was called from
v8::Local<v8::Context> calling_context = args->isolate()->GetCurrentContext();
@ -343,7 +346,8 @@ v8::Local<v8::Value> ProxyFunctionWrapper(
for (auto value : original_args) {
auto arg = PassValueToOtherContext(calling_context, func_owning_context,
value, store, &object_cache, 0);
value, store, &object_cache,
support_dynamic_properties, 0);
if (arg.IsEmpty())
return v8::Undefined(args->isolate());
proxied_args.push_back(arg.ToLocalChecked());
@ -381,9 +385,10 @@ v8::Local<v8::Value> ProxyFunctionWrapper(
if (maybe_return_value.IsEmpty())
return v8::Undefined(args->isolate());
auto ret = PassValueToOtherContext(func_owning_context, calling_context,
maybe_return_value.ToLocalChecked(),
store, &object_cache, 0);
auto ret =
PassValueToOtherContext(func_owning_context, calling_context,
maybe_return_value.ToLocalChecked(), store,
&object_cache, support_dynamic_properties, 0);
if (ret.IsEmpty())
return v8::Undefined(args->isolate());
return ret.ToLocalChecked();
@ -396,6 +401,7 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
const v8::Local<v8::Context>& destination_context,
context_bridge::RenderFrameFunctionStore* store,
context_bridge::ObjectCache* object_cache,
bool support_dynamic_properties,
int recursion_depth) {
gin_helper::Dictionary api(source_context->GetIsolate(), api_object);
v8::Context::Scope destination_context_scope(destination_context);
@ -420,13 +426,54 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
if (!gin::ConvertFromV8(api.isolate(), key, &key_str)) {
continue;
}
if (support_dynamic_properties) {
v8::Context::Scope source_context_scope(source_context);
auto maybe_desc = api.GetHandle()->GetOwnPropertyDescriptor(
source_context, v8::Local<v8::Name>::Cast(key));
v8::Local<v8::Value> desc_value;
if (!maybe_desc.ToLocal(&desc_value) || !desc_value->IsObject())
continue;
gin_helper::Dictionary desc(api.isolate(), desc_value.As<v8::Object>());
if (desc.Has("get") || desc.Has("set")) {
v8::Local<v8::Value> getter;
v8::Local<v8::Value> setter;
desc.Get("get", &getter);
desc.Get("set", &setter);
{
v8::Context::Scope destination_context_scope(destination_context);
v8::Local<v8::Value> getter_proxy;
v8::Local<v8::Value> setter_proxy;
if (!getter.IsEmpty()) {
if (!PassValueToOtherContext(source_context, destination_context,
getter, store, object_cache, false,
1)
.ToLocal(&getter_proxy))
continue;
}
if (!setter.IsEmpty()) {
if (!PassValueToOtherContext(source_context, destination_context,
setter, store, object_cache, false,
1)
.ToLocal(&setter_proxy))
continue;
}
v8::PropertyDescriptor desc(getter_proxy, setter_proxy);
ignore_result(proxy.GetHandle()->DefineProperty(
destination_context, gin::StringToV8(api.isolate(), key_str),
desc));
}
continue;
}
}
v8::Local<v8::Value> value;
if (!api.Get(key_str, &value))
continue;
auto passed_value =
PassValueToOtherContext(source_context, destination_context, value,
store, object_cache, recursion_depth + 1);
auto passed_value = PassValueToOtherContext(
source_context, destination_context, value, store, object_cache,
support_dynamic_properties, recursion_depth + 1);
if (passed_value.IsEmpty())
return v8::MaybeLocal<v8::Object>();
proxy.Set(key_str, passed_value.ToLocalChecked());
@ -472,8 +519,9 @@ void ExposeAPIInMainWorld(const std::string& key,
context_bridge::ObjectCache object_cache;
v8::Context::Scope main_context_scope(main_context);
{
v8::MaybeLocal<v8::Object> maybe_proxy = CreateProxyForAPI(
api_object, isolated_context, main_context, store, &object_cache, 0);
v8::MaybeLocal<v8::Object> maybe_proxy =
CreateProxyForAPI(api_object, isolated_context, main_context, store,
&object_cache, false, 0);
if (maybe_proxy.IsEmpty())
return;
auto proxy = maybe_proxy.ToLocalChecked();
@ -493,13 +541,14 @@ gin_helper::Dictionary TraceKeyPath(const gin_helper::Dictionary& start,
return current;
}
void OverrideGlobalMethodFromIsolatedWorld(
void OverrideGlobalValueFromIsolatedWorld(
const std::vector<std::string>& key_path,
v8::Local<v8::Function> method) {
v8::Local<v8::Object> value,
bool support_dynamic_properties) {
if (key_path.size() == 0)
return;
auto* render_frame = GetRenderFrame(method);
auto* render_frame = GetRenderFrame(value);
CHECK(render_frame);
context_bridge::RenderFrameFunctionStore* store =
GetOrCreateStore(render_frame);
@ -515,9 +564,9 @@ void OverrideGlobalMethodFromIsolatedWorld(
{
v8::Context::Scope main_context_scope(main_context);
context_bridge::ObjectCache object_cache;
v8::MaybeLocal<v8::Value> maybe_proxy =
PassValueToOtherContext(method->CreationContext(), main_context, method,
store, &object_cache, 1);
v8::MaybeLocal<v8::Value> maybe_proxy = PassValueToOtherContext(
value->CreationContext(), main_context, value, store, &object_cache,
support_dynamic_properties, 1);
DCHECK(!maybe_proxy.IsEmpty());
auto proxy = maybe_proxy.ToLocalChecked();
@ -555,14 +604,14 @@ bool OverrideGlobalPropertyFromIsolatedWorld(
if (!getter->IsNullOrUndefined()) {
v8::MaybeLocal<v8::Value> maybe_getter_proxy =
PassValueToOtherContext(getter->CreationContext(), main_context,
getter, store, &object_cache, 1);
getter, store, &object_cache, false, 1);
DCHECK(!maybe_getter_proxy.IsEmpty());
getter_proxy = maybe_getter_proxy.ToLocalChecked();
}
if (!setter->IsNullOrUndefined() && setter->IsObject()) {
v8::MaybeLocal<v8::Value> maybe_setter_proxy =
PassValueToOtherContext(getter->CreationContext(), main_context,
setter, store, &object_cache, 1);
setter, store, &object_cache, false, 1);
DCHECK(!maybe_setter_proxy.IsEmpty());
setter_proxy = maybe_setter_proxy.ToLocalChecked();
}
@ -597,8 +646,8 @@ void Initialize(v8::Local<v8::Object> exports,
v8::Isolate* isolate = context->GetIsolate();
gin_helper::Dictionary dict(isolate, exports);
dict.SetMethod("exposeAPIInMainWorld", &electron::api::ExposeAPIInMainWorld);
dict.SetMethod("_overrideGlobalMethodFromIsolatedWorld",
&electron::api::OverrideGlobalMethodFromIsolatedWorld);
dict.SetMethod("_overrideGlobalValueFromIsolatedWorld",
&electron::api::OverrideGlobalValueFromIsolatedWorld);
dict.SetMethod("_overrideGlobalPropertyFromIsolatedWorld",
&electron::api::OverrideGlobalPropertyFromIsolatedWorld);
dict.SetMethod("_isCalledFromMainWorld",