declarative_event_unittest.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 <memory>
  6. #include "base/bind.h"
  7. #include "base/strings/stringprintf.h"
  8. #include "base/values.h"
  9. #include "extensions/common/extension_api.h"
  10. #include "extensions/renderer/bindings/api_binding_test.h"
  11. #include "extensions/renderer/bindings/api_binding_test_util.h"
  12. #include "extensions/renderer/bindings/api_bindings_system.h"
  13. #include "extensions/renderer/bindings/api_bindings_system_unittest.h"
  14. #include "extensions/renderer/bindings/api_last_error.h"
  15. #include "extensions/renderer/bindings/api_request_handler.h"
  16. #include "extensions/renderer/bindings/api_type_reference_map.h"
  17. #include "extensions/renderer/bindings/argument_spec.h"
  18. #include "extensions/renderer/bindings/test_interaction_provider.h"
  19. #include "gin/handle.h"
  20. namespace extensions {
  21. namespace {
  22. const char kDeclarativeAPIName[] = "alpha";
  23. const char kDeclarativeAPISpec[] =
  24. "{"
  25. " 'types': [{"
  26. " 'id': 'alpha.objRef',"
  27. " 'type': 'object',"
  28. " 'properties': {"
  29. " 'prop1': {'type': 'string'},"
  30. " 'prop2': {'type': 'integer', 'optional': true}"
  31. " }"
  32. " }, {"
  33. " 'id': 'alpha.enumRef',"
  34. " 'type': 'string',"
  35. " 'enum': ['cat', 'dog']"
  36. " }],"
  37. " 'events': [{"
  38. " 'name': 'declarativeEvent',"
  39. " 'options': {"
  40. " 'supportsRules': true,"
  41. " 'supportsListeners': false,"
  42. " 'actions': ['alpha.enumRef'],"
  43. " 'conditions': ['alpha.objRef']"
  44. " }"
  45. " }]"
  46. "}";
  47. } // namespace
  48. class DeclarativeEventTest : public APIBindingTest {
  49. public:
  50. DeclarativeEventTest()
  51. : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {}
  52. DeclarativeEventTest(const DeclarativeEventTest&) = delete;
  53. DeclarativeEventTest& operator=(const DeclarativeEventTest&) = delete;
  54. ~DeclarativeEventTest() override {}
  55. void OnRequest(std::unique_ptr<APIRequestHandler::Request> request,
  56. v8::Local<v8::Context> context) {
  57. last_request_ = std::move(request);
  58. }
  59. APITypeReferenceMap* type_refs() { return &type_refs_; }
  60. APIRequestHandler* request_handler() { return request_handler_.get(); }
  61. APIRequestHandler::Request* last_request() { return last_request_.get(); }
  62. void reset_last_request() { last_request_.reset(); }
  63. private:
  64. void SetUp() override {
  65. APIBindingTest::SetUp();
  66. {
  67. auto action1 = std::make_unique<ArgumentSpec>(ArgumentType::STRING);
  68. action1->set_enum_values({"actionA"});
  69. type_refs_.AddSpec("action1", std::move(action1));
  70. auto action2 = std::make_unique<ArgumentSpec>(ArgumentType::STRING);
  71. action2->set_enum_values({"actionB"});
  72. type_refs_.AddSpec("action2", std::move(action2));
  73. }
  74. {
  75. auto condition = std::make_unique<ArgumentSpec>(ArgumentType::OBJECT);
  76. auto prop = std::make_unique<ArgumentSpec>(ArgumentType::STRING);
  77. ArgumentSpec::PropertiesMap props;
  78. props["url"] = std::move(prop);
  79. condition->set_properties(std::move(props));
  80. type_refs_.AddSpec("condition", std::move(condition));
  81. }
  82. interaction_provider_ = std::make_unique<TestInteractionProvider>();
  83. request_handler_ = std::make_unique<APIRequestHandler>(
  84. base::BindRepeating(&DeclarativeEventTest::OnRequest,
  85. base::Unretained(this)),
  86. APILastError(APILastError::GetParent(), binding::AddConsoleError()),
  87. nullptr, interaction_provider_.get());
  88. }
  89. void TearDown() override {
  90. request_handler_.reset();
  91. interaction_provider_.reset();
  92. APIBindingTest::TearDown();
  93. }
  94. APITypeReferenceMap type_refs_;
  95. std::unique_ptr<TestInteractionProvider> interaction_provider_;
  96. std::unique_ptr<APIRequestHandler> request_handler_;
  97. std::unique_ptr<APIRequestHandler::Request> last_request_;
  98. };
  99. // Test that the rules schema behaves properly. This is designed to be more of
  100. // a sanity check than a comprehensive test, since it mostly delegates the logic
  101. // out to ArgumentSpec.
  102. TEST_F(DeclarativeEventTest, TestRulesSchema) {
  103. v8::HandleScope handle_scope(isolate());
  104. v8::Local<v8::Context> context = MainContext();
  105. gin::Handle<DeclarativeEvent> emitter = gin::CreateHandle(
  106. context->GetIsolate(),
  107. new DeclarativeEvent("declEvent", type_refs(), request_handler(),
  108. {"action1", "action2"}, {"condition"}, 0));
  109. v8::Local<v8::Value> emitter_value = emitter.ToV8();
  110. const char kAddRules[] =
  111. "(function(event) {\n"
  112. " var rules = %s;\n"
  113. " event.addRules(rules);\n"
  114. "})";
  115. {
  116. const char kGoodRules[] =
  117. "[{id: 'rule', tags: ['tag'],"
  118. " actions: ['actionA'],"
  119. " conditions: [{url: 'example.com'}],"
  120. " priority: 1}]";
  121. v8::Local<v8::Function> function =
  122. FunctionFromString(context, base::StringPrintf(kAddRules, kGoodRules));
  123. v8::Local<v8::Value> args[] = {emitter_value};
  124. RunFunctionOnGlobal(function, context, std::size(args), args);
  125. EXPECT_TRUE(last_request());
  126. reset_last_request();
  127. }
  128. {
  129. // Invalid action.
  130. const char kBadRules1[] =
  131. "[{id: 'rule', tags: ['tag'],"
  132. " actions: ['notAnAction'],"
  133. " conditions: [{url: 'example.com'}],"
  134. " priority: 1}]";
  135. // Missing required property: conditions.
  136. const char kBadRules2[] =
  137. "[{id: 'rule', tags: ['tag'],"
  138. " actions: ['actionA'],"
  139. " priority: 1}]";
  140. // Missing required property: actions.
  141. const char kBadRules3[] =
  142. "[{id: 'rule', tags: ['tag'],"
  143. " conditions: [{url: 'example.com'}],"
  144. " priority: 1}]";
  145. for (const char* rules : {kBadRules1, kBadRules2, kBadRules3}) {
  146. v8::Local<v8::Function> function =
  147. FunctionFromString(context, base::StringPrintf(kAddRules, rules));
  148. v8::Local<v8::Value> args[] = {emitter_value};
  149. RunFunctionAndExpectError(function, context, std::size(args), args,
  150. "Uncaught TypeError: Invalid invocation");
  151. EXPECT_FALSE(last_request()) << rules;
  152. reset_last_request();
  153. }
  154. }
  155. }
  156. class DeclarativeEventWithSchemaTest : public APIBindingsSystemTest {
  157. protected:
  158. DeclarativeEventWithSchemaTest() {}
  159. ~DeclarativeEventWithSchemaTest() override {}
  160. std::vector<FakeSpec> GetAPIs() override {
  161. // events.removeRules and events.getRules are specified in the events.json
  162. // schema, so we need to load that.
  163. base::StringPiece events_schema =
  164. ExtensionAPI::GetSharedInstance()->GetSchemaStringPiece("events");
  165. return {{kDeclarativeAPIName, kDeclarativeAPISpec},
  166. {"events", events_schema.data()}};
  167. }
  168. };
  169. // Test all methods of declarative events.
  170. TEST_F(DeclarativeEventWithSchemaTest, TestAllMethods) {
  171. v8::HandleScope handle_scope(isolate());
  172. v8::Local<v8::Context> context = MainContext();
  173. v8::Local<v8::Object> api = bindings_system()->CreateAPIInstance(
  174. kDeclarativeAPIName, context, nullptr);
  175. ASSERT_FALSE(api.IsEmpty());
  176. v8::Local<v8::Object> declarative_event;
  177. ASSERT_TRUE(GetPropertyFromObjectAs(api, context, "declarativeEvent",
  178. &declarative_event));
  179. v8::Local<v8::Function> add_rules;
  180. ASSERT_TRUE(GetPropertyFromObjectAs(declarative_event, context, "addRules",
  181. &add_rules));
  182. v8::Local<v8::Value> args[] = {api};
  183. {
  184. const char kAddRules[] =
  185. R"((function(obj) {
  186. obj.declarativeEvent.addRules(
  187. [{
  188. id: 'rule',
  189. conditions: [{prop1: 'property'}],
  190. actions: ['cat'],
  191. }]);
  192. }))";
  193. v8::Local<v8::Function> add_rules_func =
  194. FunctionFromString(context, kAddRules);
  195. RunFunctionOnGlobal(add_rules_func, context, std::size(args), args);
  196. ValidateLastRequest("events.addRules",
  197. "['alpha.declarativeEvent',0,"
  198. "[{'actions':['cat'],"
  199. "'conditions':[{'prop1':'property'}],"
  200. "'id':'rule'}]]");
  201. reset_last_request();
  202. }
  203. {
  204. const char kRemoveRules[] =
  205. "(function(obj) {\n"
  206. " obj.declarativeEvent.removeRules(['rule']);\n"
  207. "})";
  208. v8::Local<v8::Function> remove_rules =
  209. FunctionFromString(context, kRemoveRules);
  210. RunFunctionOnGlobal(remove_rules, context, std::size(args), args);
  211. ValidateLastRequest("events.removeRules",
  212. "['alpha.declarativeEvent',0,['rule']]");
  213. reset_last_request();
  214. }
  215. {
  216. const char kGetRules[] =
  217. "(function(obj) {\n"
  218. " obj.declarativeEvent.getRules(function() {});\n"
  219. "})";
  220. v8::Local<v8::Function> remove_rules =
  221. FunctionFromString(context, kGetRules);
  222. RunFunctionOnGlobal(remove_rules, context, std::size(args), args);
  223. ValidateLastRequest("events.getRules", "['alpha.declarativeEvent',0,null]");
  224. reset_last_request();
  225. }
  226. }
  227. } // namespace extensions