Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate ethereum provider to gin::Wrappable #15498

Merged
merged 3 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions browser/brave_wallet/send_transaction_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -807,30 +807,4 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest,
true);
}

IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest,
EnsurePropertiesCantBeDeleted) {
GURL url =
https_server_for_files()->GetURL("a.com", "/send_transaction.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(WaitForLoadStop(web_contents()));
ASSERT_EQ(EvalJs(web_contents(), "ensurePropertiesCantBeDeleted()",
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
.ExtractBool(),
true);
}

IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest,
EnsurePropertiesCantBeDeletedNoOverwrite) {
brave_wallet_service_->SetDefaultEthereumWallet(
mojom::DefaultWallet::BraveWallet);
GURL url =
https_server_for_files()->GetURL("a.com", "/send_transaction.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(WaitForLoadStop(web_contents()));
ASSERT_EQ(EvalJs(web_contents(), "ensurePropertiesCantBeDeleted()",
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
.ExtractBool(),
true);
}

} // namespace brave_wallet
310 changes: 118 additions & 192 deletions components/brave_wallet/renderer/js_ethereum_provider.cc

Large diffs are not rendered by default.

81 changes: 42 additions & 39 deletions components/brave_wallet/renderer/js_ethereum_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,66 +10,71 @@
#include <string>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.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"
#include "gin/arguments.h"
#include "gin/wrappable.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "v8/include/v8.h"

namespace brave_wallet {

class JSEthereumProvider : public mojom::EventsListener {
class JSEthereumProvider final : public gin::Wrappable<JSEthereumProvider>,
public content::RenderFrameObserver,
public mojom::EventsListener {
public:
explicit JSEthereumProvider(content::RenderFrame* render_frame,
bool brave_use_native_wallet);
~JSEthereumProvider() override;
static gin::WrapperInfo kWrapperInfo;

void AddJavaScriptObjectToFrame(v8::Local<v8::Context> context,
bool allow_overwrite_window_ethereum,
bool is_main_world);
void FireEvent(const std::string& event, base::Value event_args);
void ConnectEvent();
void OnGetChainId(const std::string& chain_id);
void DisconnectEvent(const std::string& message);
JSEthereumProvider(const JSEthereumProvider&) = delete;
JSEthereumProvider& operator=(const JSEthereumProvider&) = delete;

static void Install(bool allow_overwrite_window_ethereum_provider,
content::RenderFrame* render_frame);

// gin::WrappableBase
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
const char* GetTypeName() override;

// mojom::EventsListener
void AccountsChangedEvent(const std::vector<std::string>& accounts) override;
void ChainChangedEvent(const std::string& chain_id) override;

private:
void BindFunctionsToObject(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Object> ethereum_object,
v8::Local<v8::Object> metamask_object);
void UpdateAndBindJSProperties();
void UpdateAndBindJSProperties(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Object> ethereum_obj);

// Adds a function to the provided object.
template <typename Sig>
void BindFunctionToObject(v8::Isolate* isolate,
v8::Local<v8::Object> javascript_object,
const std::string& name,
const base::RepeatingCallback<Sig>& callback);
void CreateEthereumObject(v8::Isolate* isolate,
v8::Local<v8::Context> context,
bool allow_overwrite_window_ethereum);
explicit JSEthereumProvider(content::RenderFrame* render_frame);
~JSEthereumProvider() override;

// content::RenderFrameObserver
void OnDestruct() override {}
void WillReleaseScriptContext(v8::Local<v8::Context>,
int32_t world_id) override;

bool EnsureConnected();
void InjectInitScript(bool is_main_world);

void FireEvent(const std::string& event, base::Value event_args);
void OnGetChainId(const std::string& chain_id);
void ConnectEvent();
void DisconnectEvent(const std::string& message);

bool GetIsBraveWallet();
bool GetIsMetaMask();
std::string GetChainId();
v8::Local<v8::Value> GetNetworkVersion(v8::Isolate* isolate);
v8::Local<v8::Value> GetSelectedAddress(v8::Isolate* isolate);

// Functions to be called from JS
v8::Local<v8::Promise> Request(v8::Isolate* isolate,
v8::Local<v8::Value> input);
v8::Local<v8::Value> IsConnected();
v8::Local<v8::Promise> Enable();
v8::Local<v8::Promise> IsUnlocked();
v8::Local<v8::Promise> Send(gin::Arguments* args);
v8::Local<v8::Value> IsConnected(v8::Isolate* isolate);
v8::Local<v8::Promise> Enable(v8::Isolate* isolate);
v8::Local<v8::Promise> IsUnlocked(v8::Isolate* isolate);
v8::Local<v8::Promise> SendMethod(gin::Arguments* args);
void SendAsync(gin::Arguments* args);
bool ProxyDeletePropertyHandler(gin::Arguments* args);

void OnRequestOrSendAsync(v8::Global<v8::Context> global_context,
std::unique_ptr<v8::Global<v8::Function>> callback,
Expand All @@ -95,11 +100,9 @@ class JSEthereumProvider : public mojom::EventsListener {
base::Value formed_response,
bool success);

raw_ptr<content::RenderFrame> render_frame_ = nullptr;
bool brave_use_native_wallet_;
mojo::Remote<mojom::EthereumProvider> ethereum_provider_;
mojo::Receiver<mojom::EventsListener> receiver_{this};
bool is_connected_;
bool is_connected_ = false;
std::string chain_id_;
std::string first_allowed_account_;
base::WeakPtrFactory<JSEthereumProvider> weak_ptr_factory_{this};
Expand Down
6 changes: 3 additions & 3 deletions components/brave_wallet/renderer/js_solana_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ void JSSolanaProvider::Install(bool allow_overwrite_window_solana,
for (const std::string& method :
{"connect", "disconnect", "signAndSendTransaction", "signMessage",
"request", "signTransaction", "signAllTransactions"}) {
SetOwnPropertyNonWritable(
context, provider_value->ToObject(context).ToLocalChecked(),
gin::StringToV8(isolate, method));
SetOwnPropertyWritable(context,
provider_value->ToObject(context).ToLocalChecked(),
gin::StringToV8(isolate, method), false);
}

blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
Expand Down
9 changes: 5 additions & 4 deletions components/brave_wallet/renderer/v8_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,15 @@ void SetProviderNonWritable(v8::Local<v8::Context> context,
global->DefineProperty(context, provider_name, desc).Check();
}

void SetOwnPropertyNonWritable(v8::Local<v8::Context> context,
v8::Local<v8::Object> provider_object,
v8::Local<v8::String> property_name) {
void SetOwnPropertyWritable(v8::Local<v8::Context> context,
v8::Local<v8::Object> provider_object,
v8::Local<v8::String> property_name,
bool writable) {
v8::Local<v8::Value> property;
if (!provider_object->Get(context, property_name).ToLocal(&property))
return;

v8::PropertyDescriptor desc(property, false);
v8::PropertyDescriptor desc(property, writable);
provider_object->DefineProperty(context, property_name, desc).Check();
}

Expand Down
7 changes: 4 additions & 3 deletions components/brave_wallet/renderer/v8_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ void SetProviderNonWritable(v8::Local<v8::Context> context,
v8::Local<v8::String> provider_name,
bool is_enumerable);

void SetOwnPropertyNonWritable(v8::Local<v8::Context> context,
v8::Local<v8::Object> provider_object,
v8::Local<v8::String> property_name);
void SetOwnPropertyWritable(v8::Local<v8::Context> context,
v8::Local<v8::Object> provider_object,
v8::Local<v8::String> property_name,
bool writable);

} // namespace brave_wallet

Expand Down
28 changes: 0 additions & 28 deletions components/brave_wallet/resources/ethereum_provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,4 @@
writable: false
}
})

var alreadyLogged = false
var logweb3Warning = () => {
if (!alreadyLogged) {
console.warn('You are accessing the window.web3 shim. This object is deprecated, please use window.ethereum instead.')
alreadyLogged = true
}
}
const web3Shim = {
__isMetaMaskShim__: true,
currentProvider: window.ethereum
}
const web3Proxy = new Proxy(web3Shim, {
get: (...args) => {
logweb3Warning()
return Reflect.get(...args)
},
set: (...args) => {
logweb3Warning()
return Reflect.set(...args)
}
})
$Object.defineProperty(window, 'web3', {
value: web3Proxy,
enumerable: false,
configurable: true,
writable: true,
})
})()
46 changes: 11 additions & 35 deletions renderer/brave_wallet/brave_wallet_render_frame_observer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,33 +55,6 @@ bool BraveWalletRenderFrameObserver::CanCreateProvider() {
return true;
}

void BraveWalletRenderFrameObserver::DidCreateScriptContext(
v8::Local<v8::Context> context,
int32_t world_id) {
if (!CanCreateProvider())
return;

auto dynamic_params = get_dynamic_params_callback_.Run();
if (!dynamic_params.brave_use_native_ethereum_wallet) {
js_ethereum_provider_.reset();
return;
}

bool is_main_world = world_id == content::ISOLATED_WORLD_ID_GLOBAL;
if (render_frame()->GetWebFrame()->GetDocument().IsDOMFeaturePolicyEnabled(
render_frame()->GetWebFrame()->MainWorldScriptContext(),
"ethereum")) {
if (!js_ethereum_provider_) {
js_ethereum_provider_ = std::make_unique<JSEthereumProvider>(
render_frame(), dynamic_params.brave_use_native_ethereum_wallet);
}
js_ethereum_provider_->AddJavaScriptObjectToFrame(
context, dynamic_params.allow_overwrite_window_ethereum_provider,
is_main_world);
js_ethereum_provider_->ConnectEvent();
}
}

void BraveWalletRenderFrameObserver::DidFinishLoad() {
#if !BUILDFLAG(IS_ANDROID)
// Only record P3A for desktop and valid HTTP/HTTPS pages
Expand All @@ -95,18 +68,13 @@ void BraveWalletRenderFrameObserver::DidFinishLoad() {
#endif
}

void BraveWalletRenderFrameObserver::WillReleaseScriptContext(
v8::Local<v8::Context>,
int32_t world_id) {
js_ethereum_provider_.reset();
}

void BraveWalletRenderFrameObserver::DidClearWindowObject() {
if (!CanCreateProvider())
return;

auto dynamic_params = get_dynamic_params_callback_.Run();
if (!dynamic_params.brave_use_native_solana_wallet) {
if (!dynamic_params.brave_use_native_solana_wallet &&
!dynamic_params.brave_use_native_ethereum_wallet) {
return;
}

Expand All @@ -119,11 +87,19 @@ void BraveWalletRenderFrameObserver::DidClearWindowObject() {
if (context.IsEmpty())
return;

if (dynamic_params.brave_use_native_ethereum_wallet &&
web_frame->GetDocument().IsDOMFeaturePolicyEnabled(context, "ethereum")) {
JSEthereumProvider::Install(
dynamic_params.allow_overwrite_window_ethereum_provider,
render_frame());
}

if (base::FeatureList::IsEnabled(
brave_wallet::features::kBraveWalletSolanaFeature) &&
base::FeatureList::IsEnabled(
brave_wallet::features::kBraveWalletSolanaProviderFeature) &&
web_frame->GetDocument().IsDOMFeaturePolicyEnabled(context, "solana")) {
web_frame->GetDocument().IsDOMFeaturePolicyEnabled(context, "solana") &&
dynamic_params.brave_use_native_solana_wallet) {
JSSolanaProvider::Install(
dynamic_params.allow_overwrite_window_solana_provider, render_frame());
}
Expand Down
7 changes: 0 additions & 7 deletions renderer/brave_wallet/brave_wallet_render_frame_observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ class BraveWalletRenderFrameObserver : public content::RenderFrameObserver {
void DidStartNavigation(
const GURL& url,
absl::optional<blink::WebNavigationType> navigation_type) override;
void DidCreateScriptContext(v8::Local<v8::Context> context,
int32_t world_id) override;
void WillReleaseScriptContext(v8::Local<v8::Context>,
int32_t world_id) override;
void DidClearWindowObject() override;

void DidFinishLoad() override;
Expand All @@ -50,9 +46,6 @@ class BraveWalletRenderFrameObserver : public content::RenderFrameObserver {
bool IsPageValid();
bool CanCreateProvider();

// Handle to "handler" JavaScript object functionality.
std::unique_ptr<JSEthereumProvider> js_ethereum_provider_;

GURL url_;
GetDynamicParamsCallback get_dynamic_params_callback_;

Expand Down
19 changes: 14 additions & 5 deletions renderer/test/js_ethereum_provider_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,21 @@ IN_PROC_BROWSER_TEST_F(JSEthereumProviderBrowserTest, NonWritable) {
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY);
EXPECT_EQ(base::Value(true), result.value) << result.error;
}
{
auto result =
EvalJs(web_contents(), NonWriteableScriptMethod("ethereum", "send"),
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY);
EXPECT_EQ(base::Value(false), result.value) << result.error;
}

// window._metamask.isUnlocked()
auto result =
EvalJs(web_contents(),
NonWriteableScriptMethod("ethereum._metamask", "isUnlocked"),
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY);
EXPECT_EQ(base::Value(true), result.value) << result.error;
{
auto result =
EvalJs(web_contents(),
NonWriteableScriptMethod("ethereum._metamask", "isUnlocked"),
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY);
EXPECT_EQ(base::Value(true), result.value) << result.error;
}
}

// See https://github.com/brave/brave-browser/issues/22213 for details
Expand Down
8 changes: 0 additions & 8 deletions test/data/brave-wallet/send_transaction.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,6 @@
window.ethereum._metamask.isUnlocked().then(result =>
window.domAutomationController.send(result))
}
function ensurePropertiesCantBeDeleted() {
// window.ethereum properties cannot be deleted AND they return true
// this is for web-compat, for example https://wallet.polygon.technology/
// fails otherwise as it uses a library which tries to delete
// window.ethereum.sendAsync and then uses it. Seriously.
window.domAutomationController.send(delete window.ethereum.sendAsync === true &&
typeof(window.ethereum.sendAsync) === 'function')
}
</script>

<body>
Expand Down