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