1. Pass connect arg to mojom

2. Implement IsConnected
3. Move CallMethodOfObject and GetProperty to helper
This commit is contained in:
Anthony Tseng
2022-03-28 17:22:15 -07:00
parent 7444bd68ff
commit a3e822d8ab
9 changed files with 181 additions and 128 deletions
@@ -14,7 +14,9 @@ namespace brave_wallet {
SolanaProviderImpl::SolanaProviderImpl() = default;
SolanaProviderImpl::~SolanaProviderImpl() = default;
void SolanaProviderImpl::Connect(ConnectCallback callback) {
void SolanaProviderImpl::Connect(absl::optional<base::Value> 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
@@ -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<base::Value> arg,
ConnectCallback callback) override;
void Disconnect() override;
void IsConnected(IsConnectedCallback callback) override;
private:
};
@@ -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
@@ -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 = [
@@ -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<v8::Value> GetProperty(v8::Local<v8::Context> context,
v8::Local<v8::Value> object,
const std::u16string& name) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::String> name_str =
gin::ConvertToV8(isolate, name).As<v8::String>();
v8::Local<v8::Object> object_obj;
if (!object->ToObject(context).ToLocal(&object_obj)) {
return v8::MaybeLocal<v8::Value>();
}
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<v8::Context> context = web_frame->MainWorldScriptContext();
v8::Context::Scope context_scope(context);
v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Value> object;
v8::Local<v8::Value> 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<v8::Local<v8::Value>> args;
for (auto const& argument : arguments.GetList()) {
args.push_back(
content::V8ValueConverter::Create()->ToV8Value(&argument, context));
}
web_frame->ExecuteMethodAndReturnValue(v8::Local<v8::Function>::Cast(method),
object, static_cast<int>(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<v8::Context> context =
render_frame_->GetWebFrame()->MainWorldScriptContext();
std::vector<v8::Local<v8::Value>> args;
args.push_back(
content::V8ValueConverter::Create()->ToV8Value(&args_list, context));
CallMethodOfObject(render_frame_->GetWebFrame(), u"ethereum", u"emit",
std::move(args));
}
@@ -7,71 +7,25 @@
#include <tuple>
#include <utility>
#include <vector>
#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<v8::Value> GetProperty(v8::Local<v8::Context> context,
v8::Local<v8::Value> object,
const std::u16string& name) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::String> name_str =
gin::ConvertToV8(isolate, name).As<v8::String>();
v8::Local<v8::Object> object_obj;
if (!object->ToObject(context).ToLocal(&object_obj)) {
return v8::MaybeLocal<v8::Value>();
}
return object_obj->Get(context, name_str);
}
v8::MaybeLocal<v8::Value> CallMethodOfObject(
blink::WebLocalFrame* web_frame,
const std::u16string& object_name,
const std::u16string& method_name,
std::vector<v8::Local<v8::Value>>&& args) {
if (web_frame->IsProvisional())
return v8::Local<v8::Value>();
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
v8::Local<v8::Context> context = web_frame->MainWorldScriptContext();
v8::Context::Scope context_scope(context);
v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Value> object;
v8::Local<v8::Value> method;
if (!GetProperty(context, context->Global(), object_name).ToLocal(&object) ||
!GetProperty(context, object, method_name).ToLocal(&method)) {
return v8::Local<v8::Value>();
}
// Without the IsFunction test here JS blocking from content settings
// will trigger a DCHECK crash.
if (method.IsEmpty() || !method->IsFunction()) {
return v8::Local<v8::Value>();
}
return web_frame->CallFunctionEvenIfScriptDisabled(
v8::Local<v8::Function>::Cast(method), object,
static_cast<int>(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<v8::Promise> JSSolanaProvider::Connect(gin::Arguments* arguments) {
// TODO: support onlyIfTrusted
if (!EnsureConnected())
return v8::Local<v8::Promise>();
@@ -154,13 +110,33 @@ v8::Local<v8::Promise> JSSolanaProvider::Connect(gin::Arguments* arguments) {
return v8::Local<v8::Promise>();
}
// Get base::Value arg to pass
absl::optional<base::Value> arg = absl::nullopt;
v8::Local<v8::Value> v8_arg;
if (arguments->Length() > 1 ||
(arguments->Length() == 1 && !arguments->GetNext(&v8_arg))) {
arguments->ThrowError();
return v8::Local<v8::Promise>();
}
if (!v8_arg.IsEmpty()) {
std::unique_ptr<base::Value> arg_value =
v8_value_converter_->FromV8Value(v8_arg, isolate->GetCurrentContext());
if (!arg_value || !arg_value->is_dict()) {
arguments->ThrowError();
return v8::Local<v8::Promise>();
}
arg = std::move(*arg_value);
}
auto global_context(
v8::Global<v8::Context>(isolate, isolate->GetCurrentContext()));
auto promise_resolver(
v8::Global<v8::Promise::Resolver>(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<v8::Promise> JSSolanaProvider::Disconnect(gin::Arguments* arguments) {
std::ignore = resolver.ToLocalChecked()->Resolve(isolate->GetCurrentContext(),
v8::Undefined(isolate));
// emit disconnect
std::vector<v8::Local<v8::Value>> 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<v8::Local<v8::Value>>());
return resolver.ToLocalChecked()->GetPromise();
}
@@ -219,6 +189,19 @@ v8::Local<v8::Promise> JSSolanaProvider::SignAllTransaction(
return v8::Local<v8::Promise>();
}
void JSSolanaProvider::FireEvent(
const std::string& event,
std::vector<v8::Local<v8::Value>>&& event_args) {
v8::Local<v8::Context> context =
render_frame_->GetWebFrame()->MainWorldScriptContext();
std::vector<v8::Local<v8::Value>> 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<v8::Context> global_context,
v8::Global<v8::Promise::Resolver> promise_resolver,
@@ -232,13 +215,11 @@ void JSSolanaProvider::OnConnect(
v8::Local<v8::Context> context = global_context.Get(isolate);
v8::Local<v8::Value> result;
v8::Local<v8::Value> v8_public_key;
std::unique_ptr<content::V8ValueConverter> 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<v8::Local<v8::Value>> 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<v8::Value> public_key_result =
CallMethodOfObject(render_frame_->GetWebFrame(), u"solana",
@@ -249,19 +230,16 @@ void JSSolanaProvider::OnConnect(
} else {
std::unique_ptr<base::Value> 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<v8::Local<v8::Value>> 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(
@@ -8,11 +8,13 @@
#include <memory>
#include <string>
#include <vector>
#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<JSSolanaProvider> {
v8::Local<v8::Promise> SignAllTransaction(gin::Arguments* arguments);
// TODO: fire accountChanged event
void FireEvent(const std::string& event,
std::vector<v8::Local<v8::Value>>&& event_args);
void OnConnect(v8::Global<v8::Context> global_context,
v8::Global<v8::Promise::Resolver> promise_resolver,
@@ -72,6 +76,7 @@ class JSSolanaProvider final : public gin::Wrappable<JSSolanaProvider> {
bool use_native_wallet_ = false;
raw_ptr<content::RenderFrame> render_frame_ = nullptr;
std::unique_ptr<content::V8ValueConverter> v8_value_converter_;
mojo::Remote<mojom::SolanaProvider> solana_provider_;
base::WeakPtrFactory<JSSolanaProvider> weak_ptr_factory_{this};
};
@@ -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<v8::Value> GetProperty(v8::Local<v8::Context> context,
v8::Local<v8::Value> object,
const std::u16string& name) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::String> name_str =
gin::ConvertToV8(isolate, name).As<v8::String>();
v8::Local<v8::Object> object_obj;
if (!object->ToObject(context).ToLocal(&object_obj)) {
return v8::MaybeLocal<v8::Value>();
}
return object_obj->Get(context, name_str);
}
v8::MaybeLocal<v8::Value> CallMethodOfObject(
blink::WebLocalFrame* web_frame,
const std::u16string& object_name,
const std::u16string& method_name,
std::vector<v8::Local<v8::Value>>&& args) {
if (web_frame->IsProvisional())
return v8::Local<v8::Value>();
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
v8::Local<v8::Context> context = web_frame->MainWorldScriptContext();
v8::Context::Scope context_scope(context);
v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Value> object;
v8::Local<v8::Value> method;
if (!GetProperty(context, context->Global(), object_name).ToLocal(&object) ||
!GetProperty(context, object, method_name).ToLocal(&method)) {
return v8::Local<v8::Value>();
}
// Without the IsFunction test here JS blocking from content settings
// will trigger a DCHECK crash.
if (method.IsEmpty() || !method->IsFunction()) {
return v8::Local<v8::Value>();
}
return web_frame->CallFunctionEvenIfScriptDisabled(
v8::Local<v8::Function>::Cast(method), object,
static_cast<int>(args.size()), args.data());
}
} // namespace brave_wallet
@@ -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 <string>
#include <vector>
#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<v8::Value> GetProperty(v8::Local<v8::Context> context,
v8::Local<v8::Value> object,
const std::u16string& name);
v8::MaybeLocal<v8::Value> CallMethodOfObject(
blink::WebLocalFrame* web_frame,
const std::u16string& object_name,
const std::u16string& method_name,
std::vector<v8::Local<v8::Value>>&& args);
} // namespace brave_wallet
#endif // BRAVE_COMPONENTS_BRAVE_WALLET_RENDERER_V8_HELPER_H_