blob: 0e12afa6138280697ef70c3d3bab98b7500bad1c [file] [log] [blame]
[email protected]701a94e2014-04-17 04:37:371// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/renderer/binding_generating_native_handler.h"
6
rdevlin.croninc6be58d62016-10-06 01:35:547#include "base/metrics/histogram_macros.h"
Avi Drissman197295ae2018-12-25 20:28:348#include "base/stl_util.h"
rdevlin.croninc6be58d62016-10-06 01:35:549#include "base/timer/elapsed_timer.h"
bashi61ca3c72015-06-26 00:40:1010#include "extensions/renderer/script_context.h"
11#include "extensions/renderer/v8_helpers.h"
jbroman5967a862017-04-27 16:52:4512#include "gin/data_object_builder.h"
[email protected]701a94e2014-04-17 04:37:3713
14namespace extensions {
15
Peter Kastingf301da92019-09-23 08:12:5016using v8_helpers::GetProperty;
bashi61ca3c72015-06-26 00:40:1017
[email protected]701a94e2014-04-17 04:37:3718BindingGeneratingNativeHandler::BindingGeneratingNativeHandler(
bashi61ca3c72015-06-26 00:40:1019 ScriptContext* context,
[email protected]701a94e2014-04-17 04:37:3720 const std::string& api_name,
21 const std::string& bind_to)
bashi61ca3c72015-06-26 00:40:1022 : context_(context), api_name_(api_name), bind_to_(bind_to) {}
[email protected]701a94e2014-04-17 04:37:3723
Devlin Cronind9ea8342018-01-27 06:00:0424void BindingGeneratingNativeHandler::Initialize() {}
25
Devlin Cronin6fed7f02018-01-31 22:38:2026bool BindingGeneratingNativeHandler::IsInitialized() {
27 // There's no initialization to do, so just always return true.
28 return true;
29}
30
tfarinaf85316f2015-04-29 17:03:4031v8::Local<v8::Object> BindingGeneratingNativeHandler::NewInstance() {
rdevlin.croninc6be58d62016-10-06 01:35:5432 base::ElapsedTimer timer;
bashi61ca3c72015-06-26 00:40:1033 // This long sequence of commands effectively runs the JavaScript code,
34 // such that result[bind_to] is the compiled schema for |api_name|:
35 //
36 // var result = {};
37 // result[bind_to] = require('binding').Binding.create(api_name).generate();
38 // return result;
39 //
40 // Unfortunately using the v8 APIs makes that quite verbose.
41 // Each stage is marked with the code it executes.
42 v8::Isolate* isolate = context_->isolate();
[email protected]701a94e2014-04-17 04:37:3743 v8::EscapableHandleScope scope(isolate);
bashi61ca3c72015-06-26 00:40:1044
45 // Convert |api_name| and |bind_to| into their v8::Strings to pass
46 // through the v8 APIs.
47 v8::Local<v8::String> v8_api_name;
Peter Kastingf301da92019-09-23 08:12:5048 if (!v8_helpers::ToV8String(isolate, api_name_, &v8_api_name)) {
bashi61ca3c72015-06-26 00:40:1049 NOTREACHED();
50 return v8::Local<v8::Object>();
[email protected]701a94e2014-04-17 04:37:3751 }
bashi61ca3c72015-06-26 00:40:1052
53 v8::Local<v8::Context> v8_context = context_->v8_context();
54
55 // require('binding');
56 v8::Local<v8::Object> binding_module;
57 if (!context_->module_system()->Require("binding").ToLocal(&binding_module)) {
58 NOTREACHED();
59 return v8::Local<v8::Object>();
60 }
61
62 // require('binding').Binding;
63 v8::Local<v8::Value> binding_value;
64 v8::Local<v8::Object> binding;
65 if (!GetProperty(v8_context, binding_module, "Binding", &binding_value) ||
66 !binding_value->ToObject(v8_context).ToLocal(&binding)) {
67 NOTREACHED();
68 return v8::Local<v8::Object>();
69 }
70
71 // require('binding').Binding.create;
72 v8::Local<v8::Value> create_binding_value;
73 if (!GetProperty(v8_context, binding, "create", &create_binding_value) ||
74 !create_binding_value->IsFunction()) {
75 NOTREACHED();
76 return v8::Local<v8::Object>();
77 }
78 v8::Local<v8::Function> create_binding =
79 create_binding_value.As<v8::Function>();
80
81 // require('Binding').Binding.create(api_name);
bashi61ca3c72015-06-26 00:40:1082 v8::Local<v8::Object> binding_instance;
Devlin Croninbcf34be2017-12-12 21:37:5683 {
84 v8::Local<v8::Value> argv[] = {v8_api_name};
85 v8::Local<v8::Value> binding_instance_value;
86 v8::MicrotasksScope microtasks_scope(
87 v8_context->GetIsolate(), v8::MicrotasksScope::kDoNotRunMicrotasks);
88 // TODO(devlin): We should not be using v8::Function::Call() directly here.
89 // Instead, we should use JSRunner once it's used outside native bindings.
Avi Drissman197295ae2018-12-25 20:28:3490 if (!create_binding->Call(v8_context, binding, base::size(argv), argv)
Devlin Croninbcf34be2017-12-12 21:37:5691 .ToLocal(&binding_instance_value) ||
92 !binding_instance_value->ToObject(v8_context)
93 .ToLocal(&binding_instance)) {
94 NOTREACHED();
95 return v8::Local<v8::Object>();
96 }
bashi61ca3c72015-06-26 00:40:1097 }
98
99 // require('binding').Binding.create(api_name).generate;
100 v8::Local<v8::Value> generate_value;
101 if (!GetProperty(v8_context, binding_instance, "generate", &generate_value) ||
102 !generate_value->IsFunction()) {
103 NOTREACHED();
104 return v8::Local<v8::Object>();
105 }
106 v8::Local<v8::Function> generate = generate_value.As<v8::Function>();
107
108 // require('binding').Binding.create(api_name).generate();
bashi61ca3c72015-06-26 00:40:10109 v8::Local<v8::Value> compiled_schema;
Devlin Croninbcf34be2017-12-12 21:37:56110 {
111 v8::MicrotasksScope microtasks_scope(
112 v8_context->GetIsolate(), v8::MicrotasksScope::kDoNotRunMicrotasks);
113 // TODO(devlin): We should not be using v8::Function::Call() directly here.
114 // Instead, we should use JSRunner once it's used outside native bindings.
115 if (!generate->Call(v8_context, binding_instance, 0, nullptr)
116 .ToLocal(&compiled_schema)) {
117 NOTREACHED();
118 return v8::Local<v8::Object>();
119 }
bashi61ca3c72015-06-26 00:40:10120 }
121
122 // var result = {};
123 // result[bind_to] = ...;
jbroman5967a862017-04-27 16:52:45124 v8::Local<v8::Object> object =
125 gin::DataObjectBuilder(isolate).Set(bind_to_, compiled_schema).Build();
rdevlin.croninc6be58d62016-10-06 01:35:54126
127 // Log UMA with microsecond accuracy*; maxes at 10 seconds.
128 // *Obviously, limited by our TimeTicks implementation, but as close as
129 // possible.
130 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ApiBindingObjectGenerationTime",
131 timer.Elapsed().InMicroseconds(),
132 1, 10000000, 100);
bashi61ca3c72015-06-26 00:40:10133 // return result;
[email protected]701a94e2014-04-17 04:37:37134 return scope.Escape(object);
135}
136
137} // namespace extensions