diff --git a/components/brave_wallet/browser/solana_provider_impl.cc b/components/brave_wallet/browser/solana_provider_impl.cc index aff3712255d..99e8f8a1750 100644 --- a/components/brave_wallet/browser/solana_provider_impl.cc +++ b/components/brave_wallet/browser/solana_provider_impl.cc @@ -14,7 +14,9 @@ namespace brave_wallet { SolanaProviderImpl::SolanaProviderImpl() = default; SolanaProviderImpl::~SolanaProviderImpl() = default; -void SolanaProviderImpl::Connect(ConnectCallback callback) { +void SolanaProviderImpl::Connect(absl::optional arg, + ConnectCallback callback) { + // TODO(darkdh): handle onlyIfTrusted when it exists // NOTIMPLEMENTED(); // std::move(callback).Run(mojom::SolanaProviderError::kInternalError, "", // ""); @@ -26,4 +28,9 @@ void SolanaProviderImpl::Disconnect() { NOTIMPLEMENTED(); } +void SolanaProviderImpl::IsConnected(IsConnectedCallback callback) { + NOTIMPLEMENTED(); + std::move(callback).Run(false); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/solana_provider_impl.h b/components/brave_wallet/browser/solana_provider_impl.h index 08653c42310..d590c28148b 100644 --- a/components/brave_wallet/browser/solana_provider_impl.h +++ b/components/brave_wallet/browser/solana_provider_impl.h @@ -17,8 +17,10 @@ class SolanaProviderImpl final : public mojom::SolanaProvider { SolanaProviderImpl(const SolanaProviderImpl&) = delete; SolanaProviderImpl& operator=(const SolanaProviderImpl&) = delete; - void Connect(ConnectCallback callback) override; + void Connect(absl::optional arg, + ConnectCallback callback) override; void Disconnect() override; + void IsConnected(IsConnectedCallback callback) override; private: }; diff --git a/components/brave_wallet/common/brave_wallet.mojom b/components/brave_wallet/common/brave_wallet.mojom index 7010ac8e85a..ac418038f33 100644 --- a/components/brave_wallet/common/brave_wallet.mojom +++ b/components/brave_wallet/common/brave_wallet.mojom @@ -123,8 +123,13 @@ interface BraveWalletProvider { }; interface SolanaProvider { - Connect() => (SolanaProviderError error, string message, string public_key); + // Optional {onlyIfTrusted: true}, when that flag is enable, we will only + // connect and emit a connect event if the application is trusted. + Connect(mojo_base.mojom.DictionaryValue? arg) + => (SolanaProviderError error, string message, string public_key); Disconnect(); + [Sync] + IsConnected() => (bool is_connected); // SignTransaction // SignAllTransactions // SignMessage, hex or utf8 encoding diff --git a/components/brave_wallet/renderer/BUILD.gn b/components/brave_wallet/renderer/BUILD.gn index 94ca8f719fc..1a3f61db570 100644 --- a/components/brave_wallet/renderer/BUILD.gn +++ b/components/brave_wallet/renderer/BUILD.gn @@ -4,6 +4,8 @@ source_set("renderer") { "brave_wallet_js_handler.h", "js_solana_provider.cc", "js_solana_provider.h", + "v8_helper.cc", + "v8_helper.h", ] deps = [ diff --git a/components/brave_wallet/renderer/brave_wallet_js_handler.cc b/components/brave_wallet/renderer/brave_wallet_js_handler.cc index 1016f398d4b..94d6e95f276 100644 --- a/components/brave_wallet/renderer/brave_wallet_js_handler.cc +++ b/components/brave_wallet/renderer/brave_wallet_js_handler.cc @@ -18,6 +18,7 @@ #include "brave/components/brave_wallet/common/hex_utils.h" #include "brave/components/brave_wallet/common/value_conversion_utils.h" #include "brave/components/brave_wallet/common/web3_provider_constants.h" +#include "brave/components/brave_wallet/renderer/v8_helper.h" #include "brave/components/brave_wallet/resources/grit/brave_wallet_script_generated.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/v8_value_converter.h" @@ -55,55 +56,6 @@ std::string LoadDataResource(const int id) { return std::string(resource_bundle.GetRawDataResource(id)); } -v8::MaybeLocal GetProperty(v8::Local context, - v8::Local object, - const std::u16string& name) { - v8::Isolate* isolate = context->GetIsolate(); - v8::Local name_str = - gin::ConvertToV8(isolate, name).As(); - v8::Local object_obj; - if (!object->ToObject(context).ToLocal(&object_obj)) { - return v8::MaybeLocal(); - } - - return object_obj->Get(context, name_str); -} - -void CallMethodOfObject(blink::WebLocalFrame* web_frame, - const std::u16string& object_name, - const std::u16string& method_name, - base::Value arguments) { - if (web_frame->IsProvisional()) - return; - v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); - v8::Local context = web_frame->MainWorldScriptContext(); - v8::Context::Scope context_scope(context); - v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), - v8::MicrotasksScope::kDoNotRunMicrotasks); - v8::Local object; - v8::Local method; - if (!GetProperty(context, context->Global(), object_name).ToLocal(&object) || - !GetProperty(context, object, method_name).ToLocal(&method)) { - return; - } - - // Without the IsFunction test here JS blocking from content settings - // will trigger a DCHECK crash. - if (method.IsEmpty() || !method->IsFunction()) { - return; - } - - std::vector> args; - for (auto const& argument : arguments.GetList()) { - args.push_back( - content::V8ValueConverter::Create()->ToV8Value(&argument, context)); - } - - web_frame->ExecuteMethodAndReturnValue(v8::Local::Cast(method), - object, static_cast(args.size()), - args.data()); -} - } // namespace namespace brave_wallet { @@ -616,9 +568,18 @@ void BraveWalletJSHandler::InjectInitScript() { void BraveWalletJSHandler::FireEvent(const std::string& event, base::Value event_args) { - base::Value args = base::Value(base::Value::Type::LIST); - args.Append(event); - args.Append(std::move(event_args)); + base::Value args_list = base::Value(base::Value::Type::LIST); + args_list.Append(event); + args_list.Append(std::move(event_args)); + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + v8::Local context = + render_frame_->GetWebFrame()->MainWorldScriptContext(); + + std::vector> args; + args.push_back( + content::V8ValueConverter::Create()->ToV8Value(&args_list, context)); CallMethodOfObject(render_frame_->GetWebFrame(), u"ethereum", u"emit", std::move(args)); } diff --git a/components/brave_wallet/renderer/js_solana_provider.cc b/components/brave_wallet/renderer/js_solana_provider.cc index e34a8e68f71..28a4e8579a3 100644 --- a/components/brave_wallet/renderer/js_solana_provider.cc +++ b/components/brave_wallet/renderer/js_solana_provider.cc @@ -7,71 +7,25 @@ #include #include -#include #include "base/notreached.h" #include "brave/components/brave_wallet/common/brave_wallet_response_helpers.h" +#include "brave/components/brave_wallet/common/web3_provider_constants.h" +#include "brave/components/brave_wallet/renderer/v8_helper.h" #include "content/public/renderer/v8_value_converter.h" #include "gin/handle.h" #include "gin/object_template_builder.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h" #include "third_party/blink/public/web/web_local_frame.h" -#include "v8/include/v8-function.h" #include "v8/include/v8-microtask-queue.h" namespace brave_wallet { -namespace { - -// TODO: move these to helper -v8::MaybeLocal GetProperty(v8::Local context, - v8::Local object, - const std::u16string& name) { - v8::Isolate* isolate = context->GetIsolate(); - v8::Local name_str = - gin::ConvertToV8(isolate, name).As(); - v8::Local object_obj; - if (!object->ToObject(context).ToLocal(&object_obj)) { - return v8::MaybeLocal(); - } - - return object_obj->Get(context, name_str); -} -v8::MaybeLocal CallMethodOfObject( - blink::WebLocalFrame* web_frame, - const std::u16string& object_name, - const std::u16string& method_name, - std::vector>&& args) { - if (web_frame->IsProvisional()) - return v8::Local(); - v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); - v8::Local context = web_frame->MainWorldScriptContext(); - v8::Context::Scope context_scope(context); - v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), - v8::MicrotasksScope::kDoNotRunMicrotasks); - v8::Local object; - v8::Local method; - if (!GetProperty(context, context->Global(), object_name).ToLocal(&object) || - !GetProperty(context, object, method_name).ToLocal(&method)) { - return v8::Local(); - } - - // Without the IsFunction test here JS blocking from content settings - // will trigger a DCHECK crash. - if (method.IsEmpty() || !method->IsFunction()) { - return v8::Local(); - } - - return web_frame->CallFunctionEvenIfScriptDisabled( - v8::Local::Cast(method), object, - static_cast(args.size()), args.data()); -} - -} // namespace - JSSolanaProvider::JSSolanaProvider(bool use_native_wallet, content::RenderFrame* render_frame) - : use_native_wallet_(use_native_wallet), render_frame_(render_frame) { + : use_native_wallet_(use_native_wallet), + render_frame_(render_frame), + v8_value_converter_(content::V8ValueConverter::Create()) { EnsureConnected(); } JSSolanaProvider::~JSSolanaProvider() = default; @@ -138,12 +92,14 @@ bool JSSolanaProvider::GetIsPhantom(gin::Arguments* arguments) { } bool JSSolanaProvider::GetIsConnected(gin::Arguments* arguments) { - NOTIMPLEMENTED(); - return false; + bool is_connected = false; + if (!solana_provider_->IsConnected(&is_connected)) { + return false; + } + return is_connected; } v8::Local JSSolanaProvider::Connect(gin::Arguments* arguments) { - // TODO: support onlyIfTrusted if (!EnsureConnected()) return v8::Local(); @@ -154,13 +110,33 @@ v8::Local JSSolanaProvider::Connect(gin::Arguments* arguments) { return v8::Local(); } + // Get base::Value arg to pass + absl::optional arg = absl::nullopt; + v8::Local v8_arg; + if (arguments->Length() > 1 || + (arguments->Length() == 1 && !arguments->GetNext(&v8_arg))) { + arguments->ThrowError(); + return v8::Local(); + } + if (!v8_arg.IsEmpty()) { + std::unique_ptr arg_value = + v8_value_converter_->FromV8Value(v8_arg, isolate->GetCurrentContext()); + if (!arg_value || !arg_value->is_dict()) { + arguments->ThrowError(); + return v8::Local(); + } + arg = std::move(*arg_value); + } + auto global_context( v8::Global(isolate, isolate->GetCurrentContext())); auto promise_resolver( v8::Global(isolate, resolver.ToLocalChecked())); - solana_provider_->Connect(base::BindOnce( - &JSSolanaProvider::OnConnect, weak_ptr_factory_.GetWeakPtr(), - std::move(global_context), std::move(promise_resolver), isolate)); + solana_provider_->Connect( + std::move(arg), + base::BindOnce(&JSSolanaProvider::OnConnect, + weak_ptr_factory_.GetWeakPtr(), std::move(global_context), + std::move(promise_resolver), isolate)); return resolver.ToLocalChecked()->GetPromise(); } @@ -178,13 +154,7 @@ v8::Local JSSolanaProvider::Disconnect(gin::Arguments* arguments) { std::ignore = resolver.ToLocalChecked()->Resolve(isolate->GetCurrentContext(), v8::Undefined(isolate)); - // emit disconnect - std::vector> args; - const base::Value event("disconnect"); - args.push_back(content::V8ValueConverter::Create()->ToV8Value( - &event, isolate->GetCurrentContext())); - CallMethodOfObject(render_frame_->GetWebFrame(), u"solana", u"emit", - std::move(args)); + FireEvent(kDisconnectEvent, std::vector>()); return resolver.ToLocalChecked()->GetPromise(); } @@ -219,6 +189,19 @@ v8::Local JSSolanaProvider::SignAllTransaction( return v8::Local(); } +void JSSolanaProvider::FireEvent( + const std::string& event, + std::vector>&& event_args) { + v8::Local context = + render_frame_->GetWebFrame()->MainWorldScriptContext(); + std::vector> args; + const base::Value event_value(event); + args.push_back(v8_value_converter_->ToV8Value(&event_value, context)); + args.insert(args.end(), event_args.begin(), event_args.end()); + CallMethodOfObject(render_frame_->GetWebFrame(), u"solana", u"emit", + std::move(args)); +} + void JSSolanaProvider::OnConnect( v8::Global global_context, v8::Global promise_resolver, @@ -232,13 +215,11 @@ void JSSolanaProvider::OnConnect( v8::Local context = global_context.Get(isolate); v8::Local result; v8::Local v8_public_key; - std::unique_ptr v8_value_converter = - content::V8ValueConverter::Create(); if (error == mojom::SolanaProviderError::kSuccess) { // use @solana/web3.js and create publicKey from base58 string const base::Value public_key_value(public_key); std::vector> args; - args.push_back(v8_value_converter->ToV8Value(&public_key_value, context)); + args.push_back(v8_value_converter_->ToV8Value(&public_key_value, context)); v8::MaybeLocal public_key_result = CallMethodOfObject(render_frame_->GetWebFrame(), u"solana", @@ -249,19 +230,16 @@ void JSSolanaProvider::OnConnect( } else { std::unique_ptr formed_response = GetProviderErrorDictionary(error, error_message); - result = v8_value_converter->ToV8Value(formed_response.get(), context); + result = v8_value_converter_->ToV8Value(formed_response.get(), context); } SendResponse(std::move(global_context), std::move(promise_resolver), isolate, std::move(result), error == mojom::SolanaProviderError::kSuccess); - // emit connect + std::vector> args; - const base::Value event("connect"); - args.push_back(v8_value_converter->ToV8Value(&event, context)); args.push_back(std::move(v8_public_key)); - CallMethodOfObject(render_frame_->GetWebFrame(), u"solana", u"emit", - std::move(args)); + FireEvent(kConnectEvent, std::move(args)); } void JSSolanaProvider::SendResponse( diff --git a/components/brave_wallet/renderer/js_solana_provider.h b/components/brave_wallet/renderer/js_solana_provider.h index d6f779d5309..3234d8a624c 100644 --- a/components/brave_wallet/renderer/js_solana_provider.h +++ b/components/brave_wallet/renderer/js_solana_provider.h @@ -8,11 +8,13 @@ #include #include +#include #include "base/memory/raw_ptr.h" #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/v8_value_converter.h" #include "gin/arguments.h" #include "gin/wrappable.h" #include "mojo/public/cpp/bindings/remote.h" @@ -56,6 +58,8 @@ class JSSolanaProvider final : public gin::Wrappable { v8::Local SignAllTransaction(gin::Arguments* arguments); // TODO: fire accountChanged event + void FireEvent(const std::string& event, + std::vector>&& event_args); void OnConnect(v8::Global global_context, v8::Global promise_resolver, @@ -72,6 +76,7 @@ class JSSolanaProvider final : public gin::Wrappable { bool use_native_wallet_ = false; raw_ptr render_frame_ = nullptr; + std::unique_ptr v8_value_converter_; mojo::Remote solana_provider_; base::WeakPtrFactory weak_ptr_factory_{this}; }; diff --git a/components/brave_wallet/renderer/v8_helper.cc b/components/brave_wallet/renderer/v8_helper.cc new file mode 100644 index 00000000000..9822649b84a --- /dev/null +++ b/components/brave_wallet/renderer/v8_helper.cc @@ -0,0 +1,59 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_wallet/renderer/v8_helper.h" + +#include "gin/converter.h" +#include "third_party/blink/public/web/web_local_frame.h" +#include "v8/include/v8-function.h" +#include "v8/include/v8-microtask-queue.h" + +namespace brave_wallet { + +v8::MaybeLocal GetProperty(v8::Local context, + v8::Local object, + const std::u16string& name) { + v8::Isolate* isolate = context->GetIsolate(); + v8::Local name_str = + gin::ConvertToV8(isolate, name).As(); + v8::Local object_obj; + if (!object->ToObject(context).ToLocal(&object_obj)) { + return v8::MaybeLocal(); + } + + return object_obj->Get(context, name_str); +} + +v8::MaybeLocal CallMethodOfObject( + blink::WebLocalFrame* web_frame, + const std::u16string& object_name, + const std::u16string& method_name, + std::vector>&& args) { + if (web_frame->IsProvisional()) + return v8::Local(); + v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); + v8::Local context = web_frame->MainWorldScriptContext(); + v8::Context::Scope context_scope(context); + v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), + v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local object; + v8::Local method; + if (!GetProperty(context, context->Global(), object_name).ToLocal(&object) || + !GetProperty(context, object, method_name).ToLocal(&method)) { + return v8::Local(); + } + + // Without the IsFunction test here JS blocking from content settings + // will trigger a DCHECK crash. + if (method.IsEmpty() || !method->IsFunction()) { + return v8::Local(); + } + + return web_frame->CallFunctionEvenIfScriptDisabled( + v8::Local::Cast(method), object, + static_cast(args.size()), args.data()); +} + +} // namespace brave_wallet diff --git a/components/brave_wallet/renderer/v8_helper.h b/components/brave_wallet/renderer/v8_helper.h new file mode 100644 index 00000000000..b08d1c9438d --- /dev/null +++ b/components/brave_wallet/renderer/v8_helper.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_WALLET_RENDERER_V8_HELPER_H_ +#define BRAVE_COMPONENTS_BRAVE_WALLET_RENDERER_V8_HELPER_H_ + +#include +#include + +#include "v8/include/v8-context.h" +#include "v8/include/v8-local-handle.h" +#include "v8/include/v8-value.h" + +namespace blink { +class WebLocalFrame; +} // namespace blink + +namespace brave_wallet { + +v8::MaybeLocal GetProperty(v8::Local context, + v8::Local object, + const std::u16string& name); + +v8::MaybeLocal CallMethodOfObject( + blink::WebLocalFrame* web_frame, + const std::u16string& object_name, + const std::u16string& method_name, + std::vector>&& args); + +} // namespace brave_wallet + +#endif // BRAVE_COMPONENTS_BRAVE_WALLET_RENDERER_V8_HELPER_H_