api_binding.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  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.h"
  5. #include <algorithm>
  6. #include "base/bind.h"
  7. #include "base/check.h"
  8. #include "base/ranges/algorithm.h"
  9. #include "base/strings/strcat.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/values.h"
  13. #include "extensions/renderer/bindings/api_binding_hooks.h"
  14. #include "extensions/renderer/bindings/api_binding_types.h"
  15. #include "extensions/renderer/bindings/api_binding_util.h"
  16. #include "extensions/renderer/bindings/api_event_handler.h"
  17. #include "extensions/renderer/bindings/api_invocation_errors.h"
  18. #include "extensions/renderer/bindings/api_request_handler.h"
  19. #include "extensions/renderer/bindings/api_signature.h"
  20. #include "extensions/renderer/bindings/api_type_reference_map.h"
  21. #include "extensions/renderer/bindings/binding_access_checker.h"
  22. #include "extensions/renderer/bindings/declarative_event.h"
  23. #include "gin/arguments.h"
  24. #include "gin/handle.h"
  25. #include "gin/per_context_data.h"
  26. namespace extensions {
  27. namespace {
  28. // Returns the name of the enum value for use in JavaScript; JS enum entries use
  29. // SCREAMING_STYLE.
  30. std::string GetJSEnumEntryName(const std::string& original) {
  31. // The webstorePrivate API has an empty enum value for a result.
  32. // TODO(devlin): Work with the webstore team to see if we can move them off
  33. // this - they also already have a "success" result that they can use.
  34. // See crbug.com/709120.
  35. if (original.empty())
  36. return original;
  37. std::string result;
  38. // If the original starts with a digit, prefix it with an underscore.
  39. if (base::IsAsciiDigit(original[0]))
  40. result.push_back('_');
  41. // Given 'myEnum-Foo':
  42. for (size_t i = 0; i < original.size(); ++i) {
  43. // Add an underscore between camelcased items:
  44. // 'myEnum-Foo' -> 'mY_Enum-Foo'
  45. if (i > 0 && base::IsAsciiLower(original[i - 1]) &&
  46. base::IsAsciiUpper(original[i])) {
  47. result.push_back('_');
  48. result.push_back(original[i]);
  49. } else if (original[i] == '-') { // 'mY_Enum-Foo' -> 'mY_Enum_Foo'
  50. result.push_back('_');
  51. } else { // 'mY_Enum_Foo' -> 'MY_ENUM_FOO'
  52. result.push_back(base::ToUpperASCII(original[i]));
  53. }
  54. }
  55. return result;
  56. }
  57. std::unique_ptr<APISignature> GetAPISignatureFromDictionary(
  58. const base::Value* dict,
  59. BindingAccessChecker* access_checker,
  60. const std::string& api_name) {
  61. const base::Value* params =
  62. dict->FindKeyOfType("parameters", base::Value::Type::LIST);
  63. CHECK(params);
  64. const base::Value* returns_async =
  65. dict->FindKeyOfType("returns_async", base::Value::Type::DICTIONARY);
  66. return APISignature::CreateFromValues(*params, returns_async, access_checker,
  67. api_name, false /*is_event_signature*/);
  68. }
  69. void RunAPIBindingHandlerCallback(
  70. const v8::FunctionCallbackInfo<v8::Value>& info) {
  71. gin::Arguments args(info);
  72. if (!binding::IsContextValidOrThrowError(args.isolate()->GetCurrentContext()))
  73. return;
  74. v8::Local<v8::External> external;
  75. CHECK(args.GetData(&external));
  76. auto* callback = static_cast<APIBinding::HandlerCallback*>(external->Value());
  77. callback->Run(&args);
  78. }
  79. } // namespace
  80. struct APIBinding::MethodData {
  81. MethodData(std::string full_name, const APISignature* signature)
  82. : full_name(std::move(full_name)), signature(signature) {}
  83. // The fully-qualified name of this api (e.g. runtime.sendMessage instead of
  84. // sendMessage).
  85. const std::string full_name;
  86. // The expected API signature.
  87. const APISignature* signature;
  88. // The callback used by the v8 function.
  89. APIBinding::HandlerCallback callback;
  90. };
  91. // TODO(devlin): Maybe separate EventData into two classes? Rules, actions, and
  92. // conditions should never be present on vanilla events.
  93. struct APIBinding::EventData {
  94. EventData(std::string exposed_name,
  95. std::string full_name,
  96. bool supports_filters,
  97. bool supports_rules,
  98. bool supports_lazy_listeners,
  99. int max_listeners,
  100. bool notify_on_change,
  101. std::vector<std::string> actions,
  102. std::vector<std::string> conditions,
  103. APIBinding* binding)
  104. : exposed_name(std::move(exposed_name)),
  105. full_name(std::move(full_name)),
  106. supports_filters(supports_filters),
  107. supports_rules(supports_rules),
  108. supports_lazy_listeners(supports_lazy_listeners),
  109. max_listeners(max_listeners),
  110. notify_on_change(notify_on_change),
  111. actions(std::move(actions)),
  112. conditions(std::move(conditions)),
  113. binding(binding) {}
  114. // The name of the event on the API object (e.g. onCreated).
  115. std::string exposed_name;
  116. // The fully-specified name of the event (e.g. tabs.onCreated).
  117. std::string full_name;
  118. // Whether the event supports filters.
  119. bool supports_filters;
  120. // Whether the event supports rules.
  121. bool supports_rules;
  122. // Whether the event supports lazy listeners.
  123. bool supports_lazy_listeners;
  124. // The maximum number of listeners this event supports.
  125. int max_listeners;
  126. // Whether to notify the browser of listener changes.
  127. bool notify_on_change;
  128. // The associated actions and conditions for declarative events.
  129. std::vector<std::string> actions;
  130. std::vector<std::string> conditions;
  131. // The associated APIBinding. This raw pointer is safe because the
  132. // EventData is only accessed from the callbacks associated with the
  133. // APIBinding, and both the APIBinding and APIEventHandler are owned by the
  134. // same object (the APIBindingsSystem).
  135. APIBinding* binding;
  136. };
  137. struct APIBinding::CustomPropertyData {
  138. CustomPropertyData(const std::string& type_name,
  139. const std::string& property_name,
  140. const base::ListValue* property_values,
  141. const CreateCustomType& create_custom_type)
  142. : type_name(type_name),
  143. property_name(property_name),
  144. property_values(property_values),
  145. create_custom_type(create_custom_type) {}
  146. // The type of the property, e.g. 'storage.StorageArea'.
  147. std::string type_name;
  148. // The name of the property on the object, e.g. 'local' for
  149. // chrome.storage.local.
  150. std::string property_name;
  151. // Values curried into this particular type from the schema.
  152. const base::ListValue* property_values;
  153. CreateCustomType create_custom_type;
  154. };
  155. APIBinding::APIBinding(const std::string& api_name,
  156. const base::ListValue* function_definitions,
  157. const base::ListValue* type_definitions,
  158. const base::ListValue* event_definitions,
  159. const base::DictionaryValue* property_definitions,
  160. CreateCustomType create_custom_type,
  161. OnSilentRequest on_silent_request,
  162. std::unique_ptr<APIBindingHooks> binding_hooks,
  163. APITypeReferenceMap* type_refs,
  164. APIRequestHandler* request_handler,
  165. APIEventHandler* event_handler,
  166. BindingAccessChecker* access_checker)
  167. : api_name_(api_name),
  168. property_definitions_(property_definitions),
  169. create_custom_type_(std::move(create_custom_type)),
  170. on_silent_request_(std::move(on_silent_request)),
  171. binding_hooks_(std::move(binding_hooks)),
  172. type_refs_(type_refs),
  173. request_handler_(request_handler),
  174. event_handler_(event_handler),
  175. access_checker_(access_checker) {
  176. // TODO(devlin): It might make sense to instantiate the object_template_
  177. // directly here, which would avoid the need to hold on to
  178. // |property_definitions_| and |enums_|. However, there are *some* cases where
  179. // we don't immediately stamp out an API from the template following
  180. // construction.
  181. if (function_definitions) {
  182. for (const auto& func : function_definitions->GetList()) {
  183. const base::DictionaryValue* func_dict = nullptr;
  184. CHECK(func.GetAsDictionary(&func_dict));
  185. std::string name;
  186. CHECK(func_dict->GetString("name", &name));
  187. std::string full_name =
  188. base::StringPrintf("%s.%s", api_name_.c_str(), name.c_str());
  189. auto signature =
  190. GetAPISignatureFromDictionary(func_dict, access_checker, full_name);
  191. methods_[name] = std::make_unique<MethodData>(full_name, signature.get());
  192. type_refs->AddAPIMethodSignature(full_name, std::move(signature));
  193. }
  194. }
  195. if (type_definitions) {
  196. for (const auto& type : type_definitions->GetList()) {
  197. const base::DictionaryValue* type_dict = nullptr;
  198. CHECK(type.GetAsDictionary(&type_dict));
  199. std::string id;
  200. CHECK(type_dict->GetString("id", &id));
  201. auto argument_spec = std::make_unique<ArgumentSpec>(*type_dict);
  202. const std::set<std::string>& enum_values = argument_spec->enum_values();
  203. if (!enum_values.empty()) {
  204. // Type names may be prefixed by the api name. If so, remove the prefix.
  205. absl::optional<std::string> stripped_id;
  206. if (base::StartsWith(id, api_name_, base::CompareCase::SENSITIVE))
  207. stripped_id = id.substr(api_name_.size() + 1); // +1 for trailing '.'
  208. std::vector<EnumEntry>& entries =
  209. enums_[stripped_id ? *stripped_id : id];
  210. entries.reserve(enum_values.size());
  211. for (const auto& enum_value : enum_values) {
  212. entries.push_back(
  213. std::make_pair(enum_value, GetJSEnumEntryName(enum_value)));
  214. }
  215. }
  216. type_refs->AddSpec(id, std::move(argument_spec));
  217. // Some types, like storage.StorageArea, have functions associated with
  218. // them. Cache the function signatures in the type map.
  219. const base::ListValue* type_functions = nullptr;
  220. if (type_dict->GetList("functions", &type_functions)) {
  221. for (const auto& func : type_functions->GetList()) {
  222. const base::DictionaryValue* func_dict = nullptr;
  223. CHECK(func.GetAsDictionary(&func_dict));
  224. std::string function_name;
  225. CHECK(func_dict->GetString("name", &function_name));
  226. std::string full_name =
  227. base::StringPrintf("%s.%s", id.c_str(), function_name.c_str());
  228. auto signature = GetAPISignatureFromDictionary(
  229. func_dict, access_checker, full_name);
  230. type_refs->AddTypeMethodSignature(full_name, std::move(signature));
  231. }
  232. }
  233. }
  234. }
  235. if (event_definitions) {
  236. events_.reserve(event_definitions->GetList().size());
  237. for (const auto& event : event_definitions->GetList()) {
  238. const base::DictionaryValue* event_dict = nullptr;
  239. CHECK(event.GetAsDictionary(&event_dict));
  240. std::string name;
  241. CHECK(event_dict->GetString("name", &name));
  242. std::string full_name =
  243. base::StringPrintf("%s.%s", api_name_.c_str(), name.c_str());
  244. const base::ListValue* filters = nullptr;
  245. bool supports_filters = event_dict->GetList("filters", &filters) &&
  246. !filters->GetList().empty();
  247. std::vector<std::string> rule_actions;
  248. std::vector<std::string> rule_conditions;
  249. const base::DictionaryValue* options = nullptr;
  250. bool supports_rules = false;
  251. bool notify_on_change = true;
  252. bool supports_lazy_listeners = true;
  253. int max_listeners = binding::kNoListenerMax;
  254. if (event_dict->GetDictionary("options", &options)) {
  255. // TODO(devlin): For some reason, schemas indicate supporting filters
  256. // either through having a 'filters' property *or* through having
  257. // a 'supportsFilters' property. We should clean that up.
  258. supports_filters |=
  259. options->FindBoolKey("supportsFilters").value_or(false);
  260. supports_rules = options->FindBoolKey("supportsRules").value_or(false);
  261. if (supports_rules) {
  262. absl::optional<bool> supports_listeners =
  263. options->FindBoolKey("supportsListeners");
  264. DCHECK(supports_listeners);
  265. DCHECK(!*supports_listeners)
  266. << "Events cannot support rules and listeners.";
  267. auto get_values = [options](base::StringPiece name,
  268. std::vector<std::string>* out_value) {
  269. const base::ListValue* list = nullptr;
  270. CHECK(options->GetList(name, &list));
  271. for (const auto& entry : list->GetList()) {
  272. DCHECK(entry.is_string());
  273. out_value->push_back(entry.GetString());
  274. }
  275. };
  276. get_values("actions", &rule_actions);
  277. get_values("conditions", &rule_conditions);
  278. }
  279. absl::optional<int> max_listeners_option =
  280. options->FindIntKey("maxListeners");
  281. if (max_listeners_option)
  282. max_listeners = *max_listeners_option;
  283. absl::optional<bool> unmanaged = options->FindBoolKey("unmanaged");
  284. if (unmanaged)
  285. notify_on_change = !*unmanaged;
  286. absl::optional<bool> supports_lazy_listeners_value =
  287. options->FindBoolKey("supportsLazyListeners");
  288. if (supports_lazy_listeners_value) {
  289. supports_lazy_listeners = *supports_lazy_listeners_value;
  290. DCHECK(!supports_lazy_listeners)
  291. << "Don't specify supportsLazyListeners: true; it's the default.";
  292. }
  293. }
  294. if (binding::IsResponseValidationEnabled()) {
  295. const base::Value* params =
  296. event_dict->FindKeyOfType("parameters", base::Value::Type::LIST);
  297. // NOTE: At least in tests, events may omit "parameters". It's unclear
  298. // if real schemas do, too. For now, sub in an empty list if necessary.
  299. // TODO(devlin): Track this down and CHECK(params).
  300. base::Value empty_params(base::Value::Type::LIST);
  301. std::unique_ptr<APISignature> event_signature =
  302. APISignature::CreateFromValues(
  303. params ? *params : empty_params, nullptr /*returns_async*/,
  304. access_checker, name, true /*is_event_signature*/);
  305. DCHECK(!event_signature->has_async_return());
  306. type_refs_->AddEventSignature(full_name, std::move(event_signature));
  307. }
  308. events_.push_back(std::make_unique<EventData>(
  309. std::move(name), std::move(full_name), supports_filters,
  310. supports_rules, supports_lazy_listeners, max_listeners,
  311. notify_on_change, std::move(rule_actions), std::move(rule_conditions),
  312. this));
  313. }
  314. }
  315. }
  316. APIBinding::~APIBinding() {}
  317. v8::Local<v8::Object> APIBinding::CreateInstance(
  318. v8::Local<v8::Context> context) {
  319. DCHECK(binding::IsContextValid(context));
  320. v8::Isolate* isolate = context->GetIsolate();
  321. if (object_template_.IsEmpty())
  322. InitializeTemplate(isolate);
  323. DCHECK(!object_template_.IsEmpty());
  324. v8::Local<v8::Object> object =
  325. object_template_.Get(isolate)->NewInstance(context).ToLocalChecked();
  326. // The object created from the template includes all methods, but some may
  327. // be unavailable in this context. Iterate over them and delete any that
  328. // aren't available.
  329. // TODO(devlin): Ideally, we'd only do this check on the methods that are
  330. // conditionally exposed. Or, we could have multiple templates for different
  331. // configurations, assuming there are a small number of possibilities.
  332. for (const auto& key_value : methods_) {
  333. if (!access_checker_->HasAccess(context, key_value.second->full_name)) {
  334. v8::Maybe<bool> success = object->Delete(
  335. context, gin::StringToSymbol(isolate, key_value.first));
  336. CHECK(success.IsJust());
  337. CHECK(success.FromJust());
  338. }
  339. }
  340. for (const auto& event : events_) {
  341. if (!access_checker_->HasAccess(context, event->full_name)) {
  342. v8::Maybe<bool> success = object->Delete(
  343. context, gin::StringToSymbol(isolate, event->exposed_name));
  344. CHECK(success.IsJust());
  345. CHECK(success.FromJust());
  346. }
  347. }
  348. for (const auto& property : root_properties_) {
  349. std::string full_name = base::StrCat({api_name_, ".", property});
  350. if (!access_checker_->HasAccess(context, full_name)) {
  351. v8::Maybe<bool> success =
  352. object->Delete(context, gin::StringToSymbol(isolate, property));
  353. CHECK(success.IsJust());
  354. CHECK(success.FromJust());
  355. }
  356. }
  357. binding_hooks_->InitializeInstance(context, object);
  358. return object;
  359. }
  360. void APIBinding::InitializeTemplate(v8::Isolate* isolate) {
  361. DCHECK(object_template_.IsEmpty());
  362. v8::Local<v8::ObjectTemplate> object_template =
  363. v8::ObjectTemplate::New(isolate);
  364. for (const auto& key_value : methods_) {
  365. MethodData& method = *key_value.second;
  366. DCHECK(method.callback.is_null());
  367. method.callback =
  368. base::BindRepeating(&APIBinding::HandleCall, weak_factory_.GetWeakPtr(),
  369. method.full_name, method.signature);
  370. object_template->Set(
  371. gin::StringToSymbol(isolate, key_value.first),
  372. v8::FunctionTemplate::New(isolate, &RunAPIBindingHandlerCallback,
  373. v8::External::New(isolate, &method.callback),
  374. v8::Local<v8::Signature>(), 0,
  375. v8::ConstructorBehavior::kThrow));
  376. }
  377. for (const auto& event : events_) {
  378. object_template->SetLazyDataProperty(
  379. gin::StringToSymbol(isolate, event->exposed_name),
  380. &APIBinding::GetEventObject, v8::External::New(isolate, event.get()));
  381. }
  382. for (const auto& entry : enums_) {
  383. v8::Local<v8::ObjectTemplate> enum_object =
  384. v8::ObjectTemplate::New(isolate);
  385. for (const auto& enum_entry : entry.second) {
  386. enum_object->Set(gin::StringToSymbol(isolate, enum_entry.second),
  387. gin::StringToSymbol(isolate, enum_entry.first));
  388. }
  389. object_template->Set(isolate, entry.first.c_str(), enum_object);
  390. }
  391. if (property_definitions_) {
  392. DecorateTemplateWithProperties(isolate, object_template,
  393. *property_definitions_, /*is_root=*/true);
  394. }
  395. // Allow custom bindings a chance to tweak the template, such as to add
  396. // additional properties or types.
  397. binding_hooks_->InitializeTemplate(isolate, object_template, *type_refs_);
  398. object_template_.Set(isolate, object_template);
  399. }
  400. void APIBinding::DecorateTemplateWithProperties(
  401. v8::Isolate* isolate,
  402. v8::Local<v8::ObjectTemplate> object_template,
  403. const base::DictionaryValue& properties,
  404. bool is_root) {
  405. static const char kValueKey[] = "value";
  406. for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
  407. iter.Advance()) {
  408. const base::DictionaryValue* dict = nullptr;
  409. CHECK(iter.value().GetAsDictionary(&dict));
  410. if (dict->FindBoolKey("optional")) {
  411. // TODO(devlin): What does optional even mean here? It's only used, it
  412. // seems, for lastError and inIncognitoContext, which are both handled
  413. // with custom bindings. Investigate, and remove.
  414. continue;
  415. }
  416. const base::ListValue* platforms = nullptr;
  417. // TODO(devlin): Availability should be specified in the features files,
  418. // not the API schema files.
  419. if (dict->GetList("platforms", &platforms)) {
  420. std::string this_platform = binding::GetPlatformString();
  421. auto is_this_platform = [&this_platform](const base::Value& platform) {
  422. return platform.is_string() && platform.GetString() == this_platform;
  423. };
  424. if (base::ranges::none_of(platforms->GetList(), is_this_platform))
  425. continue;
  426. }
  427. v8::Local<v8::String> v8_key = gin::StringToSymbol(isolate, iter.key());
  428. std::string ref;
  429. if (dict->GetString("$ref", &ref)) {
  430. const base::ListValue* property_values = nullptr;
  431. CHECK(dict->GetList("value", &property_values));
  432. auto property_data = std::make_unique<CustomPropertyData>(
  433. ref, iter.key(), property_values, create_custom_type_);
  434. object_template->SetLazyDataProperty(
  435. v8_key, &APIBinding::GetCustomPropertyObject,
  436. v8::External::New(isolate, property_data.get()));
  437. custom_properties_.push_back(std::move(property_data));
  438. if (is_root)
  439. root_properties_.insert(iter.key());
  440. continue;
  441. }
  442. std::string type;
  443. CHECK(dict->GetString("type", &type));
  444. if (type != "object" && !dict->FindKey(kValueKey)) {
  445. // TODO(devlin): What does a fundamental property not having a value mean?
  446. // It doesn't seem useful, and looks like it's only used by runtime.id,
  447. // which is set by custom bindings. Investigate, and remove.
  448. continue;
  449. }
  450. if (type == "integer") {
  451. absl::optional<int> val = dict->FindIntKey(kValueKey);
  452. CHECK(val);
  453. object_template->Set(v8_key, v8::Integer::New(isolate, *val));
  454. } else if (type == "boolean") {
  455. absl::optional<bool> val = dict->FindBoolKey(kValueKey);
  456. CHECK(val);
  457. object_template->Set(v8_key, v8::Boolean::New(isolate, *val));
  458. } else if (type == "string") {
  459. std::string val;
  460. CHECK(dict->GetString(kValueKey, &val)) << iter.key();
  461. object_template->Set(v8_key, gin::StringToSymbol(isolate, val));
  462. } else if (type == "object" || !ref.empty()) {
  463. v8::Local<v8::ObjectTemplate> property_template =
  464. v8::ObjectTemplate::New(isolate);
  465. const base::DictionaryValue* property_dict = nullptr;
  466. CHECK(dict->GetDictionary("properties", &property_dict));
  467. DecorateTemplateWithProperties(isolate, property_template, *property_dict,
  468. /*is_root=*/false);
  469. object_template->Set(v8_key, property_template);
  470. }
  471. if (is_root)
  472. root_properties_.insert(iter.key());
  473. }
  474. }
  475. // static
  476. bool APIBinding::enable_promise_support_for_testing = false;
  477. // static
  478. void APIBinding::GetEventObject(
  479. v8::Local<v8::Name> property,
  480. const v8::PropertyCallbackInfo<v8::Value>& info) {
  481. v8::Isolate* isolate = info.GetIsolate();
  482. v8::HandleScope handle_scope(isolate);
  483. v8::Local<v8::Context> context;
  484. if (!info.Holder()->GetCreationContext().ToLocal(&context) ||
  485. !binding::IsContextValidOrThrowError(context))
  486. return;
  487. CHECK(info.Data()->IsExternal());
  488. auto* event_data =
  489. static_cast<EventData*>(info.Data().As<v8::External>()->Value());
  490. v8::Local<v8::Value> retval;
  491. if (event_data->binding->binding_hooks_->CreateCustomEvent(
  492. context, event_data->full_name, &retval)) {
  493. // A custom event was created; our work is done.
  494. } else if (event_data->supports_rules) {
  495. gin::Handle<DeclarativeEvent> event = gin::CreateHandle(
  496. isolate, new DeclarativeEvent(
  497. event_data->full_name, event_data->binding->type_refs_,
  498. event_data->binding->request_handler_, event_data->actions,
  499. event_data->conditions, 0));
  500. retval = event.ToV8();
  501. } else {
  502. retval = event_data->binding->event_handler_->CreateEventInstance(
  503. event_data->full_name, event_data->supports_filters,
  504. event_data->supports_lazy_listeners, event_data->max_listeners,
  505. event_data->notify_on_change, context);
  506. }
  507. info.GetReturnValue().Set(retval);
  508. }
  509. void APIBinding::GetCustomPropertyObject(
  510. v8::Local<v8::Name> property_name,
  511. const v8::PropertyCallbackInfo<v8::Value>& info) {
  512. v8::Isolate* isolate = info.GetIsolate();
  513. v8::HandleScope handle_scope(isolate);
  514. v8::Local<v8::Context> context;
  515. if (!info.Holder()->GetCreationContext().ToLocal(&context) ||
  516. !binding::IsContextValid(context))
  517. return;
  518. v8::Context::Scope context_scope(context);
  519. CHECK(info.Data()->IsExternal());
  520. auto* property_data =
  521. static_cast<CustomPropertyData*>(info.Data().As<v8::External>()->Value());
  522. v8::Local<v8::Object> property = property_data->create_custom_type.Run(
  523. isolate, property_data->type_name, property_data->property_name,
  524. property_data->property_values);
  525. if (property.IsEmpty())
  526. return;
  527. info.GetReturnValue().Set(property);
  528. }
  529. void APIBinding::HandleCall(const std::string& name,
  530. const APISignature* signature,
  531. gin::Arguments* arguments) {
  532. std::string error;
  533. v8::Isolate* isolate = arguments->isolate();
  534. v8::HandleScope handle_scope(isolate);
  535. // Since this is called synchronously from the JS entry point,
  536. // GetCurrentContext() should always be correct.
  537. v8::Local<v8::Context> context = isolate->GetCurrentContext();
  538. if (!access_checker_->HasAccessOrThrowError(context, name)) {
  539. // TODO(devlin): Do we need handle this for events as well? I'm not sure the
  540. // currrent system does (though perhaps it should). Investigate.
  541. return;
  542. }
  543. std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
  544. bool invalid_invocation = false;
  545. v8::Local<v8::Function> custom_callback;
  546. binding::ResultModifierFunction result_modifier;
  547. bool updated_args = false;
  548. int old_request_id = request_handler_->last_sent_request_id();
  549. {
  550. v8::TryCatch try_catch(isolate);
  551. APIBindingHooks::RequestResult hooks_result = binding_hooks_->RunHooks(
  552. name, context, signature, &argument_list, *type_refs_);
  553. switch (hooks_result.code) {
  554. case APIBindingHooks::RequestResult::INVALID_INVOCATION:
  555. invalid_invocation = true;
  556. error = std::move(hooks_result.error);
  557. // Throw a type error below so that it's not caught by our try-catch.
  558. break;
  559. case APIBindingHooks::RequestResult::THROWN:
  560. DCHECK(try_catch.HasCaught());
  561. try_catch.ReThrow();
  562. return;
  563. case APIBindingHooks::RequestResult::CONTEXT_INVALIDATED:
  564. DCHECK(!binding::IsContextValid(context));
  565. // The context was invalidated during the course of running the custom
  566. // hooks. Bail.
  567. return;
  568. case APIBindingHooks::RequestResult::HANDLED:
  569. if (!hooks_result.return_value.IsEmpty())
  570. arguments->Return(hooks_result.return_value);
  571. // TODO(devlin): This is a pretty simplistic implementation of this,
  572. // but it's similar to the current JS logic. If we wanted to be more
  573. // correct, we could create a RequestScope object that watches outgoing
  574. // requests.
  575. if (old_request_id == request_handler_->last_sent_request_id())
  576. on_silent_request_.Run(context, name, argument_list);
  577. return; // Our work here is done.
  578. case APIBindingHooks::RequestResult::ARGUMENTS_UPDATED:
  579. updated_args = true;
  580. [[fallthrough]];
  581. case APIBindingHooks::RequestResult::NOT_HANDLED:
  582. break; // Handle in the default manner.
  583. }
  584. custom_callback = hooks_result.custom_callback;
  585. result_modifier = std::move(hooks_result.result_modifier);
  586. }
  587. if (invalid_invocation) {
  588. arguments->ThrowTypeError(api_errors::InvocationError(
  589. name, signature->GetExpectedSignature(), error));
  590. return;
  591. }
  592. APISignature::JSONParseResult parse_result;
  593. {
  594. v8::TryCatch try_catch(isolate);
  595. // If custom hooks updated the arguments post-validation, we just trust the
  596. // values the hooks provide and convert them directly. This is because some
  597. // APIs have one set of values they use for validation, and a second they
  598. // use in the implementation of the function (see, e.g.
  599. // fileSystem.getDisplayPath).
  600. // TODO(devlin): That's unfortunate. Not only does it require special casing
  601. // here, but it also means we can't auto-generate the params for the
  602. // function on the browser side.
  603. if (updated_args) {
  604. parse_result =
  605. signature->ConvertArgumentsIgnoringSchema(context, argument_list);
  606. // Converted arguments passed to us by our bindings should never fail.
  607. DCHECK(parse_result.succeeded());
  608. } else {
  609. parse_result =
  610. signature->ParseArgumentsToJSON(context, argument_list, *type_refs_);
  611. }
  612. if (try_catch.HasCaught()) {
  613. DCHECK(!parse_result.succeeded());
  614. try_catch.ReThrow();
  615. return;
  616. }
  617. }
  618. if (!parse_result.succeeded()) {
  619. arguments->ThrowTypeError(api_errors::InvocationError(
  620. name, signature->GetExpectedSignature(), *parse_result.error));
  621. return;
  622. }
  623. v8::Local<v8::Promise> promise = request_handler_->StartRequest(
  624. context, name, std::move(parse_result.arguments_list),
  625. parse_result.async_type, parse_result.callback, custom_callback,
  626. std::move(result_modifier));
  627. if (!promise.IsEmpty())
  628. arguments->Return(promise);
  629. }
  630. } // namespace extensions