[Wallet] Properly cleanup js providers when no longer needed (#36816)

This commit is contained in:
Anton P
2026-06-03 17:21:32 +07:00
committed by GitHub
parent e350982a37
commit b57858103c
8 changed files with 98 additions and 33 deletions
@@ -12,6 +12,7 @@
#include "brave/components/brave_wallet/renderer/resource_helper.h"
#include "brave/components/brave_wallet/renderer/v8_helper.h"
#include "components/grit/brave_components_resources.h"
#include "content/public/common/isolated_world_ids.h"
#include "gin/converter.h"
#include "gin/object_template_builder.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
@@ -30,8 +31,25 @@ constexpr char kCardano[] = "cardano";
constexpr char kBrave[] = "brave";
} // namespace
// content::RenderFrameObserver
void JSCardanoProvider::Cleanup() {
// No longer need that provider object. Reset mojo connection, clean bound v8
// references, stop tracking the render frame.
cardano_provider_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
Dispose();
}
void JSCardanoProvider::WillReleaseScriptContext(v8::Local<v8::Context> context,
int32_t world_id) {
if (world_id != content::ISOLATED_WORLD_ID_GLOBAL) {
return;
}
Cleanup();
}
void JSCardanoProvider::OnDestruct() {
Cleanup();
}
std::vector<std::string> JSCardanoProvider::GetSupportedExtensions() {
@@ -59,6 +59,11 @@ class JSCardanoProvider final : public gin::Wrappable<JSCardanoProvider>,
std::string GetIcon();
std::string GetApiVersion();
void Cleanup();
// content::RenderFrameObserver
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int32_t world_id) override;
void OnDestruct() override;
mojo::Remote<mojom::CardanoProvider> cardano_provider_;
@@ -9,6 +9,7 @@
#include "base/containers/to_value_list.h"
#include "components/grit/brave_components_strings.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/renderer/v8_value_converter.h"
#include "gin/converter.h"
#include "gin/object_template_builder.h"
@@ -48,8 +49,27 @@ v8::Local<v8::Value> ConvertError(
return content::V8ValueConverter::Create()->ToV8Value(error_value, context);
}
// content::RenderFrameObserver
void JSCardanoWalletApi::OnDestruct() {}
void JSCardanoWalletApi::Cleanup() {
// No longer need that provider object. Reset mojo connection, clean bound v8
// references, stop tracking the render frame.
cardano_api_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
Dispose();
}
void JSCardanoWalletApi::WillReleaseScriptContext(
v8::Local<v8::Context> context,
int32_t world_id) {
if (world_id != content::ISOLATED_WORLD_ID_GLOBAL) {
return;
}
Cleanup();
}
void JSCardanoWalletApi::OnDestruct() {
Cleanup();
}
// gin::Wrappable<JSCardanoProvider>
gin::ObjectTemplateBuilder JSCardanoWalletApi::GetObjectTemplateBuilder(
@@ -51,6 +51,12 @@ class JSCardanoWalletApi final : public gin::Wrappable<JSCardanoWalletApi>,
private:
bool EnsureConnected();
void Cleanup();
// content::RenderFrameObserver
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int32_t world_id) override;
void OnDestruct() override;
void HandleStringResult(v8::Global<v8::Context> global_context,
@@ -121,30 +121,33 @@ JSEthereumProvider::JSEthereumProvider(content::RenderFrame* render_frame)
JSEthereumProvider::~JSEthereumProvider() = default;
void JSEthereumProvider::OnDestruct() {}
void JSEthereumProvider::Cleanup() {
// No longer need that provider object. Reset mojo connection, clean bound v8
// references, stop tracking the render frame.
receiver_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
Dispose();
}
void JSEthereumProvider::OnDestruct() {
Cleanup();
}
void JSEthereumProvider::WillReleaseScriptContext(v8::Local<v8::Context>,
int32_t world_id) {
if (world_id != content::ISOLATED_WORLD_ID_GLOBAL) {
return;
}
// Close mojo connection from browser to renderer.
receiver_.reset();
script_context_released_ = true;
Cleanup();
}
void JSEthereumProvider::DidDispatchDOMContentLoadedEvent() {
if (script_context_released_) {
return;
}
ConnectEvent();
}
void JSEthereumProvider::DidFinishLoad() {
if (script_context_released_) {
return;
}
// These used to be called synchronously by `JSEthereumProvider::Install`
// which appeared to cause rare crashes with certain extensions' behavior. See
// https://github.com/brave/brave-browser/issues/45694 for details.
@@ -80,6 +80,8 @@ class JSEthereumProvider final : public gin::Wrappable<JSEthereumProvider>,
mojo::Remote<mojom::EthereumProvider> ethereum_provider_;
};
void Cleanup();
// content::RenderFrameObserver
void OnDestruct() override;
void WillReleaseScriptContext(v8::Local<v8::Context>,
@@ -132,7 +134,6 @@ class JSEthereumProvider final : public gin::Wrappable<JSEthereumProvider>,
mojo::Remote<mojom::EthereumProvider> ethereum_provider_;
mojo::Receiver<mojom::EventsListener> receiver_{this};
bool is_connected_ = false;
bool script_context_released_ = false;
std::string chain_id_;
std::string first_allowed_account_;
std::string uuid_;
@@ -102,9 +102,6 @@ JSSolanaProvider::JSSolanaProvider(content::RenderFrame* render_frame)
JSSolanaProvider::~JSSolanaProvider() = default;
void JSSolanaProvider::OnDestruct() {
}
// static
void JSSolanaProvider::Install(bool allow_overwrite_window_solana,
content::RenderFrame* render_frame) {
@@ -239,13 +236,25 @@ void JSSolanaProvider::DisconnectEvent() {
FireEvent(kDisconnectEvent, std::vector<v8::Local<v8::Value>>());
}
void JSSolanaProvider::Cleanup() {
// No longer need that provider object. Reset mojo connection, clean bound v8
// references, stop tracking the render frame.
receiver_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
Dispose();
}
void JSSolanaProvider::OnDestruct() {
Cleanup();
}
void JSSolanaProvider::WillReleaseScriptContext(v8::Local<v8::Context>,
int32_t world_id) {
if (world_id != content::ISOLATED_WORLD_ID_GLOBAL) {
return;
}
// Close mojo connection from browser to renderer
receiver_.reset();
Cleanup();
}
bool JSSolanaProvider::EnsureConnected() {
@@ -325,10 +334,9 @@ v8::Local<v8::Promise> JSSolanaProvider::Connect(gin::Arguments* arguments) {
v8::Global<v8::Promise::Resolver>(isolate, resolver.ToLocalChecked()));
solana_provider_->Connect(
std::move(arg),
// We don't need weak ptr for mojo bindings when owning mojo::Remote
base::BindOnce(&JSSolanaProvider::OnConnect, base::Unretained(this),
std::move(global_context), std::move(promise_resolver),
isolate));
base::BindOnce(&JSSolanaProvider::OnConnect,
weak_ptr_factory_.GetWeakPtr(), std::move(global_context),
std::move(promise_resolver), isolate));
return resolver.ToLocalChecked()->GetPromise();
}
@@ -403,7 +411,7 @@ v8::Local<v8::Promise> JSSolanaProvider::SignAndSendTransaction(
solana_provider_->SignAndSendTransaction(
std::move(param), std::move(send_options),
base::BindOnce(&JSSolanaProvider::OnSignAndSendTransaction,
base::Unretained(this), std::move(global_context),
weak_ptr_factory_.GetWeakPtr(), std::move(global_context),
std::move(promise_resolver), isolate));
return resolver.ToLocalChecked()->GetPromise();
@@ -451,9 +459,9 @@ v8::Local<v8::Promise> JSSolanaProvider::SignMessage(
v8::Global<v8::Promise::Resolver>(isolate, resolver.ToLocalChecked()));
solana_provider_->SignMessage(
blob_msg->GetBlob(), display_str,
base::BindOnce(&JSSolanaProvider::OnSignMessage, base::Unretained(this),
std::move(global_context), std::move(promise_resolver),
isolate));
base::BindOnce(&JSSolanaProvider::OnSignMessage,
weak_ptr_factory_.GetWeakPtr(), std::move(global_context),
std::move(promise_resolver), isolate));
return resolver.ToLocalChecked()->GetPromise();
}
@@ -499,9 +507,9 @@ v8::Local<v8::Promise> JSSolanaProvider::Request(gin::Arguments* arguments) {
v8::Global<v8::Promise::Resolver>(isolate, resolver.ToLocalChecked()));
solana_provider_->Request(
std::move(arg_dict),
base::BindOnce(&JSSolanaProvider::OnRequest, base::Unretained(this),
std::move(global_context), std::move(promise_resolver),
isolate, *method));
base::BindOnce(&JSSolanaProvider::OnRequest,
weak_ptr_factory_.GetWeakPtr(), std::move(global_context),
std::move(promise_resolver), isolate, *method));
return resolver.ToLocalChecked()->GetPromise();
}
@@ -539,7 +547,7 @@ v8::Local<v8::Promise> JSSolanaProvider::SignTransaction(
solana_provider_->SignTransaction(
std::move(param),
base::BindOnce(&JSSolanaProvider::OnSignTransaction,
base::Unretained(this), std::move(global_context),
weak_ptr_factory_.GetWeakPtr(), std::move(global_context),
std::move(promise_resolver), isolate));
return resolver.ToLocalChecked()->GetPromise();
@@ -587,7 +595,7 @@ v8::Local<v8::Promise> JSSolanaProvider::SignAllTransactions(
solana_provider_->SignAllTransactions(
std::move(params),
base::BindOnce(&JSSolanaProvider::OnSignAllTransactions,
base::Unretained(this), std::move(global_context),
weak_ptr_factory_.GetWeakPtr(), std::move(global_context),
std::move(promise_resolver), isolate));
return resolver.ToLocalChecked()->GetPromise();
@@ -11,6 +11,7 @@
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
@@ -47,6 +48,8 @@ class JSSolanaProvider final : public gin::Wrappable<JSSolanaProvider>,
void DisconnectEvent() override;
private:
void Cleanup();
// RenderFrameObserver implementation.
void OnDestruct() override;
void WillReleaseScriptContext(v8::Local<v8::Context>,
@@ -187,6 +190,7 @@ class JSSolanaProvider final : public gin::Wrappable<JSSolanaProvider>,
std::unique_ptr<content::V8ValueConverter> v8_value_converter_;
mojo::Remote<mojom::SolanaProvider> solana_provider_;
mojo::Receiver<mojom::SolanaEventsListener> receiver_{this};
base::WeakPtrFactory<JSSolanaProvider> weak_ptr_factory_{this};
};
} // namespace brave_wallet