declarative_event.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2017 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/declarative_event.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include "base/values.h"
  8. #include "extensions/renderer/bindings/api_event_listeners.h"
  9. #include "extensions/renderer/bindings/api_request_handler.h"
  10. #include "extensions/renderer/bindings/api_signature.h"
  11. #include "extensions/renderer/bindings/api_type_reference_map.h"
  12. #include "extensions/renderer/bindings/argument_spec.h"
  13. #include "gin/object_template_builder.h"
  14. #include "gin/per_context_data.h"
  15. namespace extensions {
  16. namespace {
  17. // Builds an ArgumentSpec that accepts the given |choices| as references.
  18. std::unique_ptr<ArgumentSpec> BuildChoicesSpec(
  19. const std::vector<std::string>& choices_list) {
  20. auto item_type = std::make_unique<ArgumentSpec>(ArgumentType::CHOICES);
  21. std::vector<std::unique_ptr<ArgumentSpec>> choices;
  22. choices.reserve(choices_list.size());
  23. for (const std::string& value : choices_list) {
  24. auto choice = std::make_unique<ArgumentSpec>(ArgumentType::REF);
  25. choice->set_ref(value);
  26. choices.push_back(std::move(choice));
  27. }
  28. item_type->set_choices(std::move(choices));
  29. return item_type;
  30. }
  31. // Builds the ArgumentSpec for a events.Rule type, given a list of actions and
  32. // conditions. It's insufficient to use the specification in events.Rule, since
  33. // that provides argument types of "any" for actions and conditions, allowing
  34. // individual APIs to specify them further. Alternatively, we could lookup the
  35. // events.Rule spec and only override the actions and conditions properties,
  36. // but that doesn't seem any less contrived and requires JSON parsing and
  37. // complex spec initialization.
  38. // TODO(devlin): Another target for generating these specs. Currently, the
  39. // custom JS bindings do something similar, so this is no worse off, but that
  40. // doesn't make it more desirable.
  41. std::unique_ptr<ArgumentSpec> BuildRulesSpec(
  42. const std::vector<std::string>& actions_list,
  43. const std::vector<std::string>& conditions_list) {
  44. auto rule_spec = std::make_unique<ArgumentSpec>(ArgumentType::OBJECT);
  45. ArgumentSpec::PropertiesMap properties;
  46. {
  47. auto id_spec = std::make_unique<ArgumentSpec>(ArgumentType::STRING);
  48. id_spec->set_optional(true);
  49. properties["id"] = std::move(id_spec);
  50. }
  51. {
  52. auto tags_spec = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
  53. tags_spec->set_list_element_type(
  54. std::make_unique<ArgumentSpec>(ArgumentType::STRING));
  55. tags_spec->set_optional(true);
  56. properties["tags"] = std::move(tags_spec);
  57. }
  58. {
  59. auto actions_spec = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
  60. actions_spec->set_list_element_type(BuildChoicesSpec(actions_list));
  61. properties["actions"] = std::move(actions_spec);
  62. }
  63. {
  64. auto conditions_spec = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
  65. conditions_spec->set_list_element_type(BuildChoicesSpec(conditions_list));
  66. properties["conditions"] = std::move(conditions_spec);
  67. }
  68. {
  69. auto priority_spec = std::make_unique<ArgumentSpec>(ArgumentType::INTEGER);
  70. priority_spec->set_optional(true);
  71. properties["priority"] = std::move(priority_spec);
  72. }
  73. rule_spec->set_properties(std::move(properties));
  74. return rule_spec;
  75. }
  76. // Builds the signature for events.addRules using a specific rule.
  77. std::unique_ptr<APISignature> BuildAddRulesSignature(
  78. const std::string& rule_name) {
  79. std::vector<std::unique_ptr<ArgumentSpec>> params;
  80. params.push_back(std::make_unique<ArgumentSpec>(ArgumentType::STRING));
  81. params.push_back(std::make_unique<ArgumentSpec>(ArgumentType::INTEGER));
  82. {
  83. auto rules = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
  84. auto ref = std::make_unique<ArgumentSpec>(ArgumentType::REF);
  85. ref->set_ref(rule_name);
  86. rules->set_list_element_type(std::move(ref));
  87. params.push_back(std::move(rules));
  88. }
  89. auto returns_async = std::make_unique<APISignature::ReturnsAsync>();
  90. returns_async->optional = true;
  91. return std::make_unique<APISignature>(
  92. std::move(params), std::move(returns_async), nullptr /*access_checker*/);
  93. }
  94. } // namespace
  95. gin::WrapperInfo DeclarativeEvent::kWrapperInfo = {gin::kEmbedderNativeGin};
  96. DeclarativeEvent::DeclarativeEvent(
  97. const std::string& name,
  98. APITypeReferenceMap* type_refs,
  99. APIRequestHandler* request_handler,
  100. const std::vector<std::string>& actions_list,
  101. const std::vector<std::string>& conditions_list,
  102. int webview_instance_id)
  103. : event_name_(name),
  104. type_refs_(type_refs),
  105. request_handler_(request_handler),
  106. webview_instance_id_(webview_instance_id) {
  107. // In declarative events, the specification of the rules can change. This only
  108. // matters for the events.addRules function. Check whether or not a
  109. // specialized version for this event exists, and, if not, create it.
  110. std::string add_rules_name = name + ".addRules";
  111. if (!type_refs->HasTypeMethodSignature(add_rules_name)) {
  112. // Create the specific rules spec and cache it under this type. This will
  113. // result in e.g. declarativeContent.onPageChanged.Rule, since the Rule
  114. // schema is only used for this event.
  115. std::unique_ptr<ArgumentSpec> rules_spec =
  116. BuildRulesSpec(actions_list, conditions_list);
  117. std::string rule_type_name = name + ".Rule";
  118. type_refs->AddSpec(rule_type_name, std::move(rules_spec));
  119. // Build a custom signature for the method, since this would be different
  120. // than adding rules for a different event.
  121. std::unique_ptr<APISignature> rules_signature =
  122. BuildAddRulesSignature(rule_type_name);
  123. type_refs->AddTypeMethodSignature(add_rules_name,
  124. std::move(rules_signature));
  125. }
  126. }
  127. DeclarativeEvent::~DeclarativeEvent() {}
  128. gin::ObjectTemplateBuilder DeclarativeEvent::GetObjectTemplateBuilder(
  129. v8::Isolate* isolate) {
  130. return Wrappable<DeclarativeEvent>::GetObjectTemplateBuilder(isolate)
  131. .SetMethod("addRules", &DeclarativeEvent::AddRules)
  132. .SetMethod("removeRules", &DeclarativeEvent::RemoveRules)
  133. .SetMethod("getRules", &DeclarativeEvent::GetRules);
  134. }
  135. const char* DeclarativeEvent::GetTypeName() {
  136. // NOTE(devlin): Currently, our documentation does not differentiate between
  137. // "normal" events and declarative events. Use "Event" here so that developers
  138. // don't think there's separate documentation to look for.
  139. return "Event";
  140. }
  141. void DeclarativeEvent::AddRules(gin::Arguments* arguments) {
  142. // When adding rules, we use the signature we built for this event (e.g.
  143. // declarativeContent.onPageChanged.addRules).
  144. HandleFunction(event_name_ + ".addRules", "events.addRules", arguments);
  145. }
  146. void DeclarativeEvent::RemoveRules(gin::Arguments* arguments) {
  147. // The signatures for removeRules are always the same (they don't use the
  148. // event's Rule schema).
  149. HandleFunction("events.Event.removeRules", "events.removeRules", arguments);
  150. }
  151. void DeclarativeEvent::GetRules(gin::Arguments* arguments) {
  152. // The signatures for getRules are always the same (they don't use the
  153. // event's Rule schema).
  154. HandleFunction("events.Event.getRules", "events.getRules", arguments);
  155. }
  156. void DeclarativeEvent::HandleFunction(const std::string& signature_name,
  157. const std::string& request_name,
  158. gin::Arguments* arguments) {
  159. v8::Isolate* isolate = arguments->isolate();
  160. v8::HandleScope handle_scope(isolate);
  161. v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
  162. std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
  163. // The events API has two undocumented parameters for each function: the name
  164. // of the event, and the "webViewInstanceId". Currently, stub 0 for webview
  165. // instance id.
  166. argument_list.insert(argument_list.begin(),
  167. {gin::StringToSymbol(isolate, event_name_),
  168. v8::Integer::New(isolate, webview_instance_id_)});
  169. const APISignature* signature =
  170. type_refs_->GetTypeMethodSignature(signature_name);
  171. DCHECK(signature);
  172. APISignature::JSONParseResult parse_result =
  173. signature->ParseArgumentsToJSON(context, argument_list, *type_refs_);
  174. if (!parse_result.succeeded()) {
  175. arguments->ThrowTypeError("Invalid invocation");
  176. return;
  177. }
  178. // We don't currently support promise based requests through DeclarativeEvent.
  179. DCHECK_NE(binding::AsyncResponseType::kPromise, parse_result.async_type);
  180. request_handler_->StartRequest(
  181. context, request_name, std::move(parse_result.arguments_list),
  182. parse_result.async_type, parse_result.callback, v8::Local<v8::Function>(),
  183. binding::ResultModifierFunction());
  184. }
  185. } // namespace extensions