blob: d3b5805e49d002bc3cf18ca62348d6c0ce9066a8 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/renderer/api_binding_bridge.h"
#include "base/values.h"
#include "extensions/renderer/api_binding_hooks.h"
#include "extensions/renderer/api_request_handler.h"
#include "extensions/renderer/api_signature.h"
#include "extensions/renderer/api_type_reference_map.h"
#include "gin/converter.h"
#include "gin/object_template_builder.h"
namespace extensions {
namespace {
const char kApiObjectKey[] = "extensions::bridge::api_object";
const char kHookInterfaceKey[] = "extensions::bridge::hook_object";
v8::Local<v8::Private> GetPrivatePropertyName(v8::Isolate* isolate,
const char* key) {
return v8::Private::ForApi(isolate, gin::StringToSymbol(isolate, key));
}
} // namespace
gin::WrapperInfo APIBindingBridge::kWrapperInfo = {gin::kEmbedderNativeGin};
APIBindingBridge::APIBindingBridge(const APITypeReferenceMap* type_refs,
APIRequestHandler* request_handler,
APIBindingHooks* hooks,
v8::Local<v8::Context> context,
v8::Local<v8::Value> api_object,
const std::string& extension_id,
const std::string& context_type,
const binding::RunJSFunction& run_js)
: type_refs_(type_refs),
request_handler_(request_handler),
hooks_(hooks),
extension_id_(extension_id),
context_type_(context_type),
run_js_(run_js) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Object> wrapper = GetWrapper(isolate);
v8::Maybe<bool> result = wrapper->SetPrivate(
context, GetPrivatePropertyName(isolate, kApiObjectKey), api_object);
if (!result.IsJust() || !result.FromJust()) {
NOTREACHED();
return;
}
v8::Local<v8::Object> js_hook_interface = hooks_->GetJSHookInterface(context);
result = wrapper->SetPrivate(context,
GetPrivatePropertyName(isolate,
kHookInterfaceKey),
js_hook_interface);
DCHECK(result.IsJust() && result.FromJust());
}
APIBindingBridge::~APIBindingBridge() {}
gin::ObjectTemplateBuilder APIBindingBridge::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return Wrappable<APIBindingBridge>::GetObjectTemplateBuilder(isolate)
.SetMethod("registerCustomHook", &APIBindingBridge::RegisterCustomHook)
.SetMethod("sendRequest", &APIBindingBridge::SendRequest);
}
void APIBindingBridge::RegisterCustomHook(v8::Isolate* isolate,
v8::Local<v8::Function> function) {
// The object and arguments here are meant to match those passed to the hook
// functions in binding.js.
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> hook_object = v8::Object::New(isolate);
v8::Local<v8::Object> wrapper = GetWrapper(isolate);
v8::Local<v8::Value> hook_interface =
wrapper->GetPrivate(
context, GetPrivatePropertyName(isolate, kHookInterfaceKey))
.ToLocalChecked();
v8::Maybe<bool> result = hook_object->CreateDataProperty(
context, gin::StringToSymbol(isolate, "apiFunctions"), hook_interface);
if (!result.IsJust() || !result.FromJust())
return;
v8::Local<v8::Value> api_object =
wrapper
->GetPrivate(context, GetPrivatePropertyName(isolate, kApiObjectKey))
.ToLocalChecked();
result = hook_object->CreateDataProperty(
context, gin::StringToSymbol(isolate, "compiledApi"), api_object);
if (!result.IsJust() || !result.FromJust())
return;
// TODO(devlin): The binding.js version of these hooks also has a 'schema'
// property. I wonder if we can factor that out? If not, we'll need to add it
// here.
result = hook_object->SetPrototype(context, v8::Null(isolate));
if (!result.IsJust() || !result.FromJust())
return;
v8::Local<v8::String> extension_id =
gin::StringToSymbol(isolate, extension_id_);
v8::Local<v8::String> context_type =
gin::StringToSymbol(isolate, context_type_);
v8::Local<v8::Value> args[] = {hook_object, extension_id, context_type};
run_js_.Run(function, context, arraysize(args), args);
}
void APIBindingBridge::SendRequest(
gin::Arguments* arguments,
const std::string& name,
const std::vector<v8::Local<v8::Value>>& request_args) {
v8::Isolate* isolate = arguments->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> holder;
CHECK(arguments->GetHolder(&holder));
v8::Local<v8::Context> context = holder->CreationContext();
const APISignature* signature = type_refs_->GetAPIMethodSignature(name);
DCHECK(signature);
std::unique_ptr<base::ListValue> converted_arguments;
v8::Local<v8::Function> callback;
std::string error;
CHECK(signature->ParseArgumentsToJSON(context, request_args, *type_refs_,
&converted_arguments, &callback,
&error));
request_handler_->StartRequest(context, name, std::move(converted_arguments),
callback,
hooks_->GetCustomJSCallback(name, context));
}
} // namespace extensions