fix: do not leak IPC or context bridge promises (#23321) (#23339)

This commit is contained in:
Samuel Attard
2020-04-30 09:51:51 -07:00
committed by GitHub
parent b2fb14fd48
commit 479fc57cef
2 changed files with 59 additions and 22 deletions

View File

@@ -4,6 +4,7 @@
#include "shell/renderer/api/electron_api_context_bridge.h"
#include <memory>
#include <set>
#include <string>
#include <utility>
@@ -197,18 +198,32 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
v8::Context::Scope destination_scope(destination_context);
{
auto source_promise = v8::Local<v8::Promise>::Cast(value);
auto* proxied_promise = new util::Promise<v8::Local<v8::Value>>(
destination_context->GetIsolate());
// Make the promise a shared_ptr so that when the original promise is
// freed the proxy promise is correctly freed as well instead of being
// left dangling
std::shared_ptr<util::Promise<v8::Local<v8::Value>>> proxied_promise(
new util::Promise<v8::Local<v8::Value>>(
destination_context->GetIsolate()));
v8::Local<v8::Promise> proxied_promise_handle =
proxied_promise->GetHandle();
v8::Global<v8::Context> global_then_source_context(
source_context->GetIsolate(), source_context);
v8::Global<v8::Context> global_then_destination_context(
destination_context->GetIsolate(), destination_context);
global_then_source_context.SetWeak();
global_then_destination_context.SetWeak();
auto then_cb = base::BindOnce(
[](util::Promise<v8::Local<v8::Value>>* proxied_promise,
[](std::shared_ptr<util::Promise<v8::Local<v8::Value>>>
proxied_promise,
v8::Isolate* isolate,
v8::Global<v8::Context> global_source_context,
v8::Global<v8::Context> global_destination_context,
context_bridge::RenderFrameFunctionStore* store,
v8::Local<v8::Value> result) {
if (global_source_context.IsEmpty() ||
global_destination_context.IsEmpty())
return;
context_bridge::ObjectCache object_cache;
auto val =
PassValueToOtherContext(global_source_context.Get(isolate),
@@ -216,20 +231,28 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
result, store, &object_cache, false, 0);
if (!val.IsEmpty())
proxied_promise->Resolve(val.ToLocalChecked());
delete proxied_promise;
},
proxied_promise, destination_context->GetIsolate(),
v8::Global<v8::Context>(source_context->GetIsolate(), source_context),
v8::Global<v8::Context>(destination_context->GetIsolate(),
destination_context),
store);
std::move(global_then_source_context),
std::move(global_then_destination_context), store);
v8::Global<v8::Context> global_catch_source_context(
source_context->GetIsolate(), source_context);
v8::Global<v8::Context> global_catch_destination_context(
destination_context->GetIsolate(), destination_context);
global_catch_source_context.SetWeak();
global_catch_destination_context.SetWeak();
auto catch_cb = base::BindOnce(
[](util::Promise<v8::Local<v8::Value>>* proxied_promise,
[](std::shared_ptr<util::Promise<v8::Local<v8::Value>>>
proxied_promise,
v8::Isolate* isolate,
v8::Global<v8::Context> global_source_context,
v8::Global<v8::Context> global_destination_context,
context_bridge::RenderFrameFunctionStore* store,
v8::Local<v8::Value> result) {
if (global_source_context.IsEmpty() ||
global_destination_context.IsEmpty())
return;
context_bridge::ObjectCache object_cache;
auto val =
PassValueToOtherContext(global_source_context.Get(isolate),
@@ -237,13 +260,10 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
result, store, &object_cache, false, 0);
if (!val.IsEmpty())
proxied_promise->Reject(val.ToLocalChecked());
delete proxied_promise;
},
proxied_promise, destination_context->GetIsolate(),
v8::Global<v8::Context>(source_context->GetIsolate(), source_context),
v8::Global<v8::Context>(destination_context->GetIsolate(),
destination_context),
store);
std::move(global_catch_source_context),
std::move(global_catch_destination_context), store);
ignore_result(source_promise->Then(
source_context,
@@ -371,7 +391,8 @@ v8::Local<v8::Value> ProxyFunctionWrapper(
!mate::ConvertFromV8(args->isolate(), message->Get(),
&error_message)) {
error_message =
"An unknown exception occurred in the isolated context, an error "
"An unknown exception occurred in the isolated context, an "
"error "
"occurred but a valid exception was not thrown.";
}
}

View File

@@ -7,6 +7,7 @@
#include "base/task/post_task.h"
#include "base/values.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "gin/dictionary.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
@@ -33,7 +34,8 @@ RenderFrame* GetCurrentRenderFrame() {
return RenderFrame::FromWebFrame(frame);
}
class IPCRenderer : public gin::Wrappable<IPCRenderer> {
class IPCRenderer : public gin::Wrappable<IPCRenderer>,
public content::RenderFrameObserver {
public:
static gin::WrapperInfo kWrapperInfo;
@@ -41,19 +43,32 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
return gin::CreateHandle(isolate, new IPCRenderer(isolate));
}
explicit IPCRenderer(v8::Isolate* isolate) {
explicit IPCRenderer(v8::Isolate* isolate)
: content::RenderFrameObserver(GetCurrentRenderFrame()) {
RenderFrame* render_frame = GetCurrentRenderFrame();
DCHECK(render_frame);
weak_context_ =
v8::Global<v8::Context>(isolate, isolate->GetCurrentContext());
weak_context_.SetWeak();
render_frame->GetRemoteInterfaces()->GetInterface(
mojo::MakeRequest(&electron_browser_ptr_));
}
void OnDestruct() override { electron_browser_ptr_.reset(); }
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int32_t world_id) override {
if (weak_context_.IsEmpty() ||
weak_context_.Get(context->GetIsolate()) == context)
electron_browser_ptr_.reset();
}
// gin::Wrappable:
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return gin::Wrappable<IPCRenderer>::GetObjectTemplateBuilder(isolate)
.SetMethod("send", &IPCRenderer::Send)
.SetMethod("send", &IPCRenderer::SendMessage)
.SetMethod("sendSync", &IPCRenderer::SendSync)
.SetMethod("sendTo", &IPCRenderer::SendTo)
.SetMethod("sendToHost", &IPCRenderer::SendToHost)
@@ -63,10 +78,10 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
const char* GetTypeName() override { return "IPCRenderer"; }
private:
void Send(v8::Isolate* isolate,
bool internal,
const std::string& channel,
v8::Local<v8::Value> arguments) {
void SendMessage(v8::Isolate* isolate,
bool internal,
const std::string& channel,
v8::Local<v8::Value> arguments) {
blink::CloneableMessage message;
if (!mate::ConvertFromV8(isolate, arguments, &message)) {
return;
@@ -134,6 +149,7 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
return result;
}
v8::Global<v8::Context> weak_context_;
electron::mojom::ElectronBrowserPtr electron_browser_ptr_;
};