api_binding_hooks.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. // Copyright 2016 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. #include "extensions/renderer/bindings/api_binding_hooks.h"
  5. #include "base/strings/stringprintf.h"
  6. #include "base/supports_user_data.h"
  7. #include "extensions/renderer/bindings/api_binding_hooks_delegate.h"
  8. #include "extensions/renderer/bindings/api_binding_util.h"
  9. #include "extensions/renderer/bindings/api_request_handler.h"
  10. #include "extensions/renderer/bindings/api_signature.h"
  11. #include "extensions/renderer/bindings/js_runner.h"
  12. #include "gin/arguments.h"
  13. #include "gin/data_object_builder.h"
  14. #include "gin/handle.h"
  15. #include "gin/object_template_builder.h"
  16. #include "gin/per_context_data.h"
  17. #include "gin/wrappable.h"
  18. namespace extensions {
  19. namespace {
  20. // An interface to allow for registration of custom hooks from JavaScript.
  21. // Contains registered hooks for a single API.
  22. class JSHookInterface final : public gin::Wrappable<JSHookInterface> {
  23. public:
  24. explicit JSHookInterface(const std::string& api_name)
  25. : api_name_(api_name) {}
  26. JSHookInterface(const JSHookInterface&) = delete;
  27. JSHookInterface& operator=(const JSHookInterface&) = delete;
  28. static gin::WrapperInfo kWrapperInfo;
  29. // gin::Wrappable:
  30. gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
  31. v8::Isolate* isolate) override {
  32. return Wrappable<JSHookInterface>::GetObjectTemplateBuilder(isolate)
  33. .SetMethod("setHandleRequest", &JSHookInterface::SetHandleRequest)
  34. .SetMethod("setUpdateArgumentsPreValidate",
  35. &JSHookInterface::SetUpdateArgumentsPreValidate)
  36. .SetMethod("setUpdateArgumentsPostValidate",
  37. &JSHookInterface::SetUpdateArgumentsPostValidate)
  38. .SetMethod("setCustomCallback", &JSHookInterface::SetCustomCallback);
  39. }
  40. void ClearHooks() {
  41. handle_request_hooks_.clear();
  42. pre_validation_hooks_.clear();
  43. post_validation_hooks_.clear();
  44. }
  45. v8::Local<v8::Function> GetHandleRequestHook(const std::string& method_name,
  46. v8::Isolate* isolate) const {
  47. return GetHookFromMap(handle_request_hooks_, method_name, isolate);
  48. }
  49. v8::Local<v8::Function> GetPreValidationHook(const std::string& method_name,
  50. v8::Isolate* isolate) const {
  51. return GetHookFromMap(pre_validation_hooks_, method_name, isolate);
  52. }
  53. v8::Local<v8::Function> GetPostValidationHook(const std::string& method_name,
  54. v8::Isolate* isolate) const {
  55. return GetHookFromMap(post_validation_hooks_, method_name, isolate);
  56. }
  57. v8::Local<v8::Function> GetCustomCallback(const std::string& method_name,
  58. v8::Isolate* isolate) const {
  59. return GetHookFromMap(custom_callback_hooks_, method_name, isolate);
  60. }
  61. private:
  62. using JSHooks = std::map<std::string, v8::Global<v8::Function>>;
  63. v8::Local<v8::Function> GetHookFromMap(const JSHooks& map,
  64. const std::string& method_name,
  65. v8::Isolate* isolate) const {
  66. auto iter = map.find(method_name);
  67. if (iter == map.end())
  68. return v8::Local<v8::Function>();
  69. return iter->second.Get(isolate);
  70. }
  71. void AddHookToMap(JSHooks* map,
  72. v8::Isolate* isolate,
  73. const std::string& method_name,
  74. v8::Local<v8::Function> hook) {
  75. std::string qualified_method_name =
  76. base::StringPrintf("%s.%s", api_name_.c_str(), method_name.c_str());
  77. v8::Global<v8::Function>& entry = (*map)[qualified_method_name];
  78. if (!entry.IsEmpty()) {
  79. NOTREACHED() << "Hooks can only be set once.";
  80. return;
  81. }
  82. entry.Reset(isolate, hook);
  83. }
  84. // Adds a hook to handle the implementation of the API method.
  85. void SetHandleRequest(v8::Isolate* isolate,
  86. const std::string& method_name,
  87. v8::Local<v8::Function> hook) {
  88. AddHookToMap(&handle_request_hooks_, isolate, method_name, hook);
  89. }
  90. // Adds a hook to update the arguments passed to the API method before we do
  91. // any kind of validation.
  92. void SetUpdateArgumentsPreValidate(v8::Isolate* isolate,
  93. const std::string& method_name,
  94. v8::Local<v8::Function> hook) {
  95. AddHookToMap(&pre_validation_hooks_, isolate, method_name, hook);
  96. }
  97. void SetUpdateArgumentsPostValidate(v8::Isolate* isolate,
  98. const std::string& method_name,
  99. v8::Local<v8::Function> hook) {
  100. AddHookToMap(&post_validation_hooks_, isolate, method_name, hook);
  101. }
  102. void SetCustomCallback(v8::Isolate* isolate,
  103. const std::string& method_name,
  104. v8::Local<v8::Function> hook) {
  105. AddHookToMap(&custom_callback_hooks_, isolate, method_name, hook);
  106. }
  107. std::string api_name_;
  108. JSHooks handle_request_hooks_;
  109. JSHooks pre_validation_hooks_;
  110. JSHooks post_validation_hooks_;
  111. JSHooks custom_callback_hooks_;
  112. };
  113. const char kExtensionAPIHooksPerContextKey[] = "extension_api_hooks";
  114. struct APIHooksPerContextData : public base::SupportsUserData::Data {
  115. APIHooksPerContextData(v8::Isolate* isolate) : isolate(isolate) {}
  116. ~APIHooksPerContextData() override {
  117. v8::HandleScope scope(isolate);
  118. for (const auto& pair : hook_interfaces) {
  119. // We explicitly clear the hook data map here to remove all references to
  120. // v8 objects in order to avoid cycles.
  121. JSHookInterface* hooks = nullptr;
  122. gin::Converter<JSHookInterface*>::FromV8(
  123. isolate, pair.second.Get(isolate), &hooks);
  124. CHECK(hooks);
  125. hooks->ClearHooks();
  126. }
  127. }
  128. v8::Isolate* isolate;
  129. std::map<std::string, v8::Global<v8::Object>> hook_interfaces;
  130. // For handle request hooks which need to be resolved asynchronously, we store
  131. // a map of the associated request IDs to the callbacks that will be used to
  132. // resolve them.
  133. using ActiveRequest = base::OnceCallback<void(bool, gin::Arguments*)>;
  134. std::map<int, ActiveRequest> active_requests;
  135. };
  136. gin::WrapperInfo JSHookInterface::kWrapperInfo =
  137. {gin::kEmbedderNativeGin};
  138. // Gets the v8::Object of the JSHookInterface, optionally creating it if it
  139. // doesn't exist.
  140. v8::Local<v8::Object> GetJSHookInterfaceObject(
  141. const std::string& api_name,
  142. v8::Local<v8::Context> context,
  143. bool should_create) {
  144. gin::PerContextData* per_context_data = gin::PerContextData::From(context);
  145. DCHECK(per_context_data);
  146. APIHooksPerContextData* data = static_cast<APIHooksPerContextData*>(
  147. per_context_data->GetUserData(kExtensionAPIHooksPerContextKey));
  148. if (!data) {
  149. if (!should_create)
  150. return v8::Local<v8::Object>();
  151. auto api_data =
  152. std::make_unique<APIHooksPerContextData>(context->GetIsolate());
  153. data = api_data.get();
  154. per_context_data->SetUserData(kExtensionAPIHooksPerContextKey,
  155. std::move(api_data));
  156. }
  157. auto iter = data->hook_interfaces.find(api_name);
  158. if (iter != data->hook_interfaces.end())
  159. return iter->second.Get(context->GetIsolate());
  160. if (!should_create)
  161. return v8::Local<v8::Object>();
  162. gin::Handle<JSHookInterface> hooks =
  163. gin::CreateHandle(context->GetIsolate(), new JSHookInterface(api_name));
  164. CHECK(!hooks.IsEmpty());
  165. v8::Local<v8::Object> hooks_object = hooks.ToV8().As<v8::Object>();
  166. data->hook_interfaces[api_name].Reset(context->GetIsolate(), hooks_object);
  167. return hooks_object;
  168. }
  169. // Helper function used when completing requests for handle request hooks that
  170. // had an associated asynchronous response expected.
  171. void CompleteHandleRequestHelper(
  172. const v8::FunctionCallbackInfo<v8::Value>& info,
  173. bool did_succeed) {
  174. gin::Arguments args(info);
  175. v8::Local<v8::Context> context = args.isolate()->GetCurrentContext();
  176. if (!binding::IsContextValid(context))
  177. return;
  178. int request_id = 0;
  179. bool got_request_id = args.GetData(&request_id);
  180. DCHECK(got_request_id);
  181. // The callback to complete the request is stored in a map on the
  182. // APIHooksPerContextData associated with the id of the request.
  183. gin::PerContextData* per_context_data = gin::PerContextData::From(context);
  184. DCHECK(per_context_data);
  185. APIHooksPerContextData* data = static_cast<APIHooksPerContextData*>(
  186. per_context_data->GetUserData(kExtensionAPIHooksPerContextKey));
  187. DCHECK(data) << "APIHooks PerContextData should always exist if we have an "
  188. "active request";
  189. auto iter = data->active_requests.find(request_id);
  190. if (iter == data->active_requests.end()) {
  191. // In theory there should always be an associated stored request found, but
  192. // if one of our custom bindings erroneously calls the callbacks for
  193. // completing a request more than once the associated request will have
  194. // already been removed. If that is the case we bail early.
  195. // TODO(tjudkins): Audit existing handle request custom hooks to see if this
  196. // could happen in any of them. crbug.com/1298409 seemed to indicate this
  197. // was happening, hence why we fail gracefully here to avoid a crash.
  198. NOTREACHED() << "No callback found for the specified request ID.";
  199. return;
  200. }
  201. auto callback = std::move(iter->second);
  202. data->active_requests.erase(iter);
  203. std::move(callback).Run(did_succeed, &args);
  204. }
  205. // Helper function to add a success and failure callback to the arguments passed
  206. // to handle request hooks that require an asynchronous response and add a
  207. // pending request to handle resolving it. Updates |arguments| to replace the
  208. // trailing callback with a custom handler function to resolve the request on a
  209. // success and adds another handler function to the end of |arguments| for
  210. // resolving in the case of a failure. Also adds the associated promise to the
  211. // return on |result| if this is for a promise based request.
  212. void AddSuccessAndFailureCallbacks(
  213. v8::Local<v8::Context> context,
  214. binding::AsyncResponseType async_type,
  215. APIRequestHandler& request_handler,
  216. binding::ResultModifierFunction result_modifier,
  217. base::WeakPtr<APIBindingHooks> weak_ptr,
  218. std::vector<v8::Local<v8::Value>>* arguments,
  219. APIBindingHooks::RequestResult& result) {
  220. DCHECK(!arguments->empty());
  221. // Since ParseArgumentsToV8 fills missing optional arguments with null, the
  222. // final argument should either be a function if the API was called with a
  223. // callback or null if it was left off.
  224. // Note: the response callback here can actually remain empty in the case
  225. // of an optional callback being left off in a context that doesn't support
  226. // promises.
  227. v8::Local<v8::Function> response_callback;
  228. if (async_type == binding::AsyncResponseType::kCallback) {
  229. DCHECK(arguments->back()->IsFunction());
  230. response_callback = arguments->back().As<v8::Function>();
  231. } else if (async_type == binding::AsyncResponseType::kPromise) {
  232. DCHECK(arguments->back()->IsNull());
  233. }
  234. APIRequestHandler::RequestDetails request_details =
  235. request_handler.AddPendingRequest(context, async_type, response_callback,
  236. std::move(result_modifier));
  237. DCHECK_EQ(async_type == binding::AsyncResponseType::kPromise,
  238. !request_details.promise.IsEmpty());
  239. result.return_value = request_details.promise;
  240. // We store the callbacks to complete the requests in a map on the
  241. // APIHooksPerContextData associated with the request id.
  242. v8::Local<v8::Value> v8_request_id =
  243. v8::Integer::New(context->GetIsolate(), request_details.request_id);
  244. gin::PerContextData* per_context_data = gin::PerContextData::From(context);
  245. DCHECK(per_context_data);
  246. APIHooksPerContextData* data = static_cast<APIHooksPerContextData*>(
  247. per_context_data->GetUserData(kExtensionAPIHooksPerContextKey));
  248. DCHECK(data) << "APIHooks PerContextData should always exist if we have an "
  249. "active request";
  250. data->active_requests.emplace(
  251. request_details.request_id,
  252. base::BindOnce(&APIBindingHooks::CompleteHandleRequest,
  253. std::move(weak_ptr), request_details.request_id));
  254. v8::Local<v8::Function> success_callback =
  255. v8::Function::New(
  256. context,
  257. [](const v8::FunctionCallbackInfo<v8::Value>& info) {
  258. CompleteHandleRequestHelper(info, true);
  259. },
  260. v8_request_id)
  261. .ToLocalChecked();
  262. v8::Local<v8::Function> failure_callback =
  263. v8::Function::New(
  264. context,
  265. [](const v8::FunctionCallbackInfo<v8::Value>& info) {
  266. CompleteHandleRequestHelper(info, false);
  267. },
  268. v8_request_id)
  269. .ToLocalChecked();
  270. // The success callback replaces any existing callback that may have
  271. // been at the end of the arguments and the failure callback is appended
  272. // to the end.
  273. arguments->back() = success_callback;
  274. arguments->push_back(failure_callback);
  275. }
  276. } // namespace
  277. APIBindingHooks::RequestResult::RequestResult(ResultCode code) : code(code) {}
  278. APIBindingHooks::RequestResult::RequestResult(
  279. ResultCode code,
  280. v8::Local<v8::Function> custom_callback)
  281. : code(code), custom_callback(custom_callback) {}
  282. APIBindingHooks::RequestResult::RequestResult(
  283. ResultCode code,
  284. v8::Local<v8::Function> custom_callback,
  285. binding::ResultModifierFunction result_modifier)
  286. : code(code),
  287. custom_callback(custom_callback),
  288. result_modifier(std::move(result_modifier)) {}
  289. APIBindingHooks::RequestResult::RequestResult(std::string invocation_error)
  290. : code(INVALID_INVOCATION), error(std::move(invocation_error)) {}
  291. APIBindingHooks::RequestResult::~RequestResult() = default;
  292. APIBindingHooks::RequestResult::RequestResult(RequestResult&& other) = default;
  293. APIBindingHooks::APIBindingHooks(const std::string& api_name,
  294. APIRequestHandler* request_handler)
  295. : api_name_(api_name), request_handler_(request_handler) {}
  296. APIBindingHooks::~APIBindingHooks() = default;
  297. APIBindingHooks::RequestResult APIBindingHooks::RunHooks(
  298. const std::string& method_name,
  299. v8::Local<v8::Context> context,
  300. const APISignature* signature,
  301. std::vector<v8::Local<v8::Value>>* arguments,
  302. const APITypeReferenceMap& type_refs) {
  303. binding::ResultModifierFunction result_modifier;
  304. // Easy case: a native custom hook.
  305. if (delegate_) {
  306. RequestResult result = delegate_->HandleRequest(
  307. method_name, signature, context, arguments, type_refs);
  308. // If the native hooks handled the call, set a custom callback or a result
  309. // modifier, use that.
  310. if (result.code != RequestResult::NOT_HANDLED ||
  311. !result.custom_callback.IsEmpty()) {
  312. return result;
  313. }
  314. // If the native hooks didn't handle the call but did set a result modifier,
  315. // grab it to be able to use it along with the custom hooks below.
  316. result_modifier = std::move(result.result_modifier);
  317. }
  318. // Harder case: looking up a custom hook registered on the context (since
  319. // these are JS, each context has a separate instance).
  320. v8::Local<v8::Object> hook_interface_object =
  321. GetJSHookInterfaceObject(api_name_, context, false);
  322. if (hook_interface_object.IsEmpty()) {
  323. return RequestResult(RequestResult::NOT_HANDLED, v8::Local<v8::Function>(),
  324. std::move(result_modifier));
  325. }
  326. v8::Isolate* isolate = context->GetIsolate();
  327. JSHookInterface* hook_interface = nullptr;
  328. gin::Converter<JSHookInterface*>::FromV8(
  329. isolate,
  330. hook_interface_object, &hook_interface);
  331. CHECK(hook_interface);
  332. v8::Local<v8::Function> pre_validate_hook =
  333. hook_interface->GetPreValidationHook(method_name, isolate);
  334. v8::TryCatch try_catch(isolate);
  335. if (!pre_validate_hook.IsEmpty()) {
  336. // TODO(devlin): What to do with the result of this function call? Can it
  337. // only fail in the case we've already thrown?
  338. UpdateArguments(pre_validate_hook, context, arguments);
  339. if (!binding::IsContextValid(context))
  340. return RequestResult(RequestResult::CONTEXT_INVALIDATED);
  341. if (try_catch.HasCaught()) {
  342. try_catch.ReThrow();
  343. return RequestResult(RequestResult::THROWN);
  344. }
  345. }
  346. v8::Local<v8::Function> post_validate_hook =
  347. hook_interface->GetPostValidationHook(method_name, isolate);
  348. v8::Local<v8::Function> handle_request =
  349. hook_interface->GetHandleRequestHook(method_name, isolate);
  350. v8::Local<v8::Function> custom_callback =
  351. hook_interface->GetCustomCallback(method_name, isolate);
  352. // If both the post validation hook and the handle request hook are empty,
  353. // we're done...
  354. if (post_validate_hook.IsEmpty() && handle_request.IsEmpty()) {
  355. return RequestResult(RequestResult::NOT_HANDLED, custom_callback,
  356. std::move(result_modifier));
  357. }
  358. // ... otherwise, we have to validate the arguments.
  359. APISignature::V8ParseResult parse_result =
  360. signature->ParseArgumentsToV8(context, *arguments, type_refs);
  361. if (!binding::IsContextValid(context))
  362. return RequestResult(RequestResult::CONTEXT_INVALIDATED);
  363. if (try_catch.HasCaught()) {
  364. try_catch.ReThrow();
  365. return RequestResult(RequestResult::THROWN);
  366. }
  367. if (!parse_result.succeeded())
  368. return RequestResult(std::move(*parse_result.error));
  369. arguments->swap(*parse_result.arguments);
  370. bool updated_args = false;
  371. if (!post_validate_hook.IsEmpty()) {
  372. updated_args = true;
  373. UpdateArguments(post_validate_hook, context, arguments);
  374. if (!binding::IsContextValid(context))
  375. return RequestResult(RequestResult::CONTEXT_INVALIDATED);
  376. if (try_catch.HasCaught()) {
  377. try_catch.ReThrow();
  378. return RequestResult(RequestResult::THROWN);
  379. }
  380. }
  381. if (handle_request.IsEmpty()) {
  382. RequestResult::ResultCode result = updated_args
  383. ? RequestResult::ARGUMENTS_UPDATED
  384. : RequestResult::NOT_HANDLED;
  385. return RequestResult(result, custom_callback, std::move(result_modifier));
  386. }
  387. RequestResult result(RequestResult::HANDLED, custom_callback);
  388. if (signature->has_async_return()) {
  389. AddSuccessAndFailureCallbacks(context, parse_result.async_type,
  390. *request_handler_, std::move(result_modifier),
  391. weak_factory_.GetWeakPtr(), arguments,
  392. result);
  393. }
  394. // Safe to use synchronous JS since it's in direct response to JS calling
  395. // into the binding.
  396. v8::MaybeLocal<v8::Value> v8_result =
  397. JSRunner::Get(context)->RunJSFunctionSync(
  398. handle_request, context, arguments->size(), arguments->data());
  399. if (!binding::IsContextValid(context))
  400. return RequestResult(RequestResult::CONTEXT_INVALIDATED);
  401. if (try_catch.HasCaught()) {
  402. try_catch.ReThrow();
  403. return RequestResult(RequestResult::THROWN);
  404. }
  405. if (!v8_result.ToLocalChecked()->IsUndefined()) {
  406. DCHECK(result.return_value.IsEmpty())
  407. << "A handleRequest hook cannot return a synchronous result from an "
  408. "API that supports promises.";
  409. result.return_value = v8_result.ToLocalChecked();
  410. }
  411. return result;
  412. }
  413. void APIBindingHooks::CompleteHandleRequest(int request_id,
  414. bool did_succeed,
  415. gin::Arguments* arguments) {
  416. if (did_succeed) {
  417. request_handler_->CompleteRequest(request_id, arguments->GetAll(),
  418. /*error*/ std::string());
  419. } else {
  420. CHECK(arguments->Length() == 1);
  421. v8::Local<v8::Value> error = arguments->GetAll()[0];
  422. DCHECK(error->IsString());
  423. // In the case of an error we don't respond with any arguments.
  424. std::vector<v8::Local<v8::Value>> response_list;
  425. request_handler_->CompleteRequest(
  426. request_id, response_list,
  427. gin::V8ToString(arguments->isolate(), error));
  428. }
  429. }
  430. v8::Local<v8::Object> APIBindingHooks::GetJSHookInterface(
  431. v8::Local<v8::Context> context) {
  432. return GetJSHookInterfaceObject(api_name_, context, true);
  433. }
  434. bool APIBindingHooks::CreateCustomEvent(v8::Local<v8::Context> context,
  435. const std::string& event_name,
  436. v8::Local<v8::Value>* event_out) {
  437. return delegate_ &&
  438. delegate_->CreateCustomEvent(context, event_name, event_out);
  439. }
  440. void APIBindingHooks::InitializeTemplate(
  441. v8::Isolate* isolate,
  442. v8::Local<v8::ObjectTemplate> object_template,
  443. const APITypeReferenceMap& type_refs) {
  444. if (delegate_)
  445. delegate_->InitializeTemplate(isolate, object_template, type_refs);
  446. }
  447. void APIBindingHooks::InitializeInstance(v8::Local<v8::Context> context,
  448. v8::Local<v8::Object> instance) {
  449. if (delegate_)
  450. delegate_->InitializeInstance(context, instance);
  451. }
  452. void APIBindingHooks::SetDelegate(
  453. std::unique_ptr<APIBindingHooksDelegate> delegate) {
  454. delegate_ = std::move(delegate);
  455. }
  456. bool APIBindingHooks::UpdateArguments(
  457. v8::Local<v8::Function> function,
  458. v8::Local<v8::Context> context,
  459. std::vector<v8::Local<v8::Value>>* arguments) {
  460. v8::Local<v8::Value> result;
  461. {
  462. v8::TryCatch try_catch(context->GetIsolate());
  463. // Safe to use synchronous JS since it's in direct response to JS calling
  464. // into the binding.
  465. v8::MaybeLocal<v8::Value> maybe_result =
  466. JSRunner::Get(context)->RunJSFunctionSync(
  467. function, context, arguments->size(), arguments->data());
  468. if (try_catch.HasCaught()) {
  469. try_catch.ReThrow();
  470. return false;
  471. }
  472. result = maybe_result.ToLocalChecked();
  473. }
  474. std::vector<v8::Local<v8::Value>> new_args;
  475. if (result.IsEmpty() ||
  476. !gin::Converter<std::vector<v8::Local<v8::Value>>>::FromV8(
  477. context->GetIsolate(), result, &new_args)) {
  478. return false;
  479. }
  480. arguments->swap(new_args);
  481. return true;
  482. }
  483. } // namespace extensions