OLD | NEW |
(Empty) | |
| 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 |
| 5 #include "extensions/renderer/api_event_handler.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/memory/ptr_util.h" |
| 9 #include "base/strings/stringprintf.h" |
| 10 #include "base/values.h" |
| 11 #include "extensions/renderer/api_binding_test_util.h" |
| 12 #include "gin/converter.h" |
| 13 #include "gin/public/context_holder.h" |
| 14 #include "gin/public/isolate_holder.h" |
| 15 #include "gin/test/v8_test.h" |
| 16 #include "gin/try_catch.h" |
| 17 |
| 18 namespace extensions { |
| 19 |
| 20 class APIEventHandlerTest : public gin::V8Test { |
| 21 protected: |
| 22 APIEventHandlerTest() {} |
| 23 ~APIEventHandlerTest() override {} |
| 24 |
| 25 void SetUp() override { |
| 26 gin::V8Test::SetUp(); |
| 27 v8::HandleScope handle_scope(instance_->isolate()); |
| 28 holder_ = base::MakeUnique<gin::ContextHolder>(instance_->isolate()); |
| 29 holder_->SetContext( |
| 30 v8::Local<v8::Context>::New(instance_->isolate(), context_)); |
| 31 } |
| 32 |
| 33 void TearDown() override { |
| 34 holder_.reset(); |
| 35 gin::V8Test::TearDown(); |
| 36 } |
| 37 |
| 38 void CallFunctionOnObject(v8::Local<v8::Context> context, |
| 39 v8::Local<v8::Object> object, |
| 40 const std::string& script_source) { |
| 41 std::string wrapped_script_source = |
| 42 base::StringPrintf("(function(obj) { %s })", script_source.c_str()); |
| 43 v8::Local<v8::Function> func = |
| 44 FunctionFromString(context, wrapped_script_source); |
| 45 ASSERT_FALSE(func.IsEmpty()); |
| 46 |
| 47 v8::Local<v8::Value> argv[] = {object}; |
| 48 RunFunction(func, context, object, 1, argv); |
| 49 } |
| 50 |
| 51 private: |
| 52 std::unique_ptr<gin::ContextHolder> holder_; |
| 53 |
| 54 DISALLOW_COPY_AND_ASSIGN(APIEventHandlerTest); |
| 55 }; |
| 56 |
| 57 // Tests adding, removing, and querying event listeners by calling the |
| 58 // associated methods on the JS object. |
| 59 TEST_F(APIEventHandlerTest, AddingRemovingAndQueryingEventListeners) { |
| 60 const char kEventName[] = "alpha"; |
| 61 v8::Isolate* isolate = instance_->isolate(); |
| 62 v8::HandleScope handle_scope(instance_->isolate()); |
| 63 v8::Local<v8::Context> context = |
| 64 v8::Local<v8::Context>::New(instance_->isolate(), context_); |
| 65 |
| 66 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 67 v8::Local<v8::Object> event = |
| 68 handler.CreateEventInstance(kEventName, context); |
| 69 ASSERT_FALSE(event.IsEmpty()); |
| 70 |
| 71 EXPECT_TRUE(handler.GetEventListenersForTesting(kEventName, context).empty()); |
| 72 |
| 73 const char kListenerFunction[] = "(function() {})"; |
| 74 v8::Local<v8::Function> listener_function = |
| 75 FunctionFromString(context, kListenerFunction); |
| 76 ASSERT_FALSE(listener_function.IsEmpty()); |
| 77 |
| 78 const char kAddListenerFunction[] = |
| 79 "(function(event, listener) { event.addListener(listener); })"; |
| 80 v8::Local<v8::Function> add_listener_function = |
| 81 FunctionFromString(context, kAddListenerFunction); |
| 82 |
| 83 { |
| 84 v8::Local<v8::Value> argv[] = {event, listener_function}; |
| 85 RunFunction(add_listener_function, context, arraysize(argv), argv); |
| 86 } |
| 87 // There should only be one listener on the event. |
| 88 EXPECT_EQ(1u, |
| 89 handler.GetEventListenersForTesting(kEventName, context).size()); |
| 90 |
| 91 { |
| 92 v8::Local<v8::Value> argv[] = {event, listener_function}; |
| 93 RunFunction(add_listener_function, context, arraysize(argv), argv); |
| 94 } |
| 95 // Trying to add the same listener again should be a no-op. |
| 96 EXPECT_EQ(1u, |
| 97 handler.GetEventListenersForTesting(kEventName, context).size()); |
| 98 |
| 99 // Test hasListener returns true for a listener that is present. |
| 100 const char kHasListenerFunction[] = |
| 101 "(function(event, listener) { return event.hasListener(listener); })"; |
| 102 v8::Local<v8::Function> has_listener_function = |
| 103 FunctionFromString(context, kHasListenerFunction); |
| 104 { |
| 105 v8::Local<v8::Value> argv[] = {event, listener_function}; |
| 106 v8::Local<v8::Value> result = |
| 107 RunFunction(has_listener_function, context, arraysize(argv), argv); |
| 108 bool has_listener = false; |
| 109 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener)); |
| 110 EXPECT_TRUE(has_listener); |
| 111 } |
| 112 |
| 113 // Test that hasListener returns false for a listener that isn't present. |
| 114 { |
| 115 v8::Local<v8::Function> not_a_listener = |
| 116 FunctionFromString(context, "(function() {})"); |
| 117 v8::Local<v8::Value> argv[] = {event, not_a_listener}; |
| 118 v8::Local<v8::Value> result = |
| 119 RunFunction(has_listener_function, context, arraysize(argv), argv); |
| 120 bool has_listener = false; |
| 121 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listener)); |
| 122 EXPECT_FALSE(has_listener); |
| 123 } |
| 124 |
| 125 // Test hasListeners returns true |
| 126 const char kHasListenersFunction[] = |
| 127 "(function(event) { return event.hasListeners(); })"; |
| 128 v8::Local<v8::Function> has_listeners_function = |
| 129 FunctionFromString(context, kHasListenersFunction); |
| 130 { |
| 131 v8::Local<v8::Value> argv[] = {event}; |
| 132 v8::Local<v8::Value> result = |
| 133 RunFunction(has_listeners_function, context, arraysize(argv), argv); |
| 134 bool has_listeners = false; |
| 135 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners)); |
| 136 EXPECT_TRUE(has_listeners); |
| 137 } |
| 138 |
| 139 const char kRemoveListenerFunction[] = |
| 140 "(function(event, listener) { event.removeListener(listener); })"; |
| 141 v8::Local<v8::Function> remove_listener_function = |
| 142 FunctionFromString(context, kRemoveListenerFunction); |
| 143 { |
| 144 v8::Local<v8::Value> argv[] = {event, listener_function}; |
| 145 RunFunction(remove_listener_function, context, arraysize(argv), argv); |
| 146 } |
| 147 EXPECT_TRUE(handler.GetEventListenersForTesting(kEventName, context).empty()); |
| 148 |
| 149 { |
| 150 v8::Local<v8::Value> argv[] = {event}; |
| 151 v8::Local<v8::Value> result = |
| 152 RunFunction(has_listener_function, context, arraysize(argv), argv); |
| 153 bool has_listeners = false; |
| 154 EXPECT_TRUE(gin::Converter<bool>::FromV8(isolate, result, &has_listeners)); |
| 155 EXPECT_FALSE(has_listeners); |
| 156 } |
| 157 } |
| 158 |
| 159 // Tests listening for and firing different events. |
| 160 TEST_F(APIEventHandlerTest, FiringEvents) { |
| 161 const char kAlphaName[] = "alpha"; |
| 162 const char kBetaName[] = "beta"; |
| 163 v8::HandleScope handle_scope(instance_->isolate()); |
| 164 v8::Local<v8::Context> context = |
| 165 v8::Local<v8::Context>::New(instance_->isolate(), context_); |
| 166 |
| 167 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 168 v8::Local<v8::Object> alpha_event = |
| 169 handler.CreateEventInstance(kAlphaName, context); |
| 170 v8::Local<v8::Object> beta_event = |
| 171 handler.CreateEventInstance(kBetaName, context); |
| 172 ASSERT_FALSE(alpha_event.IsEmpty()); |
| 173 ASSERT_FALSE(beta_event.IsEmpty()); |
| 174 |
| 175 const char kAlphaListenerFunction1[] = |
| 176 "(function() {\n" |
| 177 " if (!this.alphaCount1) this.alphaCount1 = 0;\n" |
| 178 " ++this.alphaCount1;\n" |
| 179 "});\n"; |
| 180 v8::Local<v8::Function> alpha_listener1 = |
| 181 FunctionFromString(context, kAlphaListenerFunction1); |
| 182 const char kAlphaListenerFunction2[] = |
| 183 "(function() {\n" |
| 184 " if (!this.alphaCount2) this.alphaCount2 = 0;\n" |
| 185 " ++this.alphaCount2;\n" |
| 186 "});\n"; |
| 187 v8::Local<v8::Function> alpha_listener2 = |
| 188 FunctionFromString(context, kAlphaListenerFunction2); |
| 189 const char kBetaListenerFunction[] = |
| 190 "(function() {\n" |
| 191 " if (!this.betaCount) this.betaCount = 0;\n" |
| 192 " ++this.betaCount;\n" |
| 193 "});\n"; |
| 194 v8::Local<v8::Function> beta_listener = |
| 195 FunctionFromString(context, kBetaListenerFunction); |
| 196 ASSERT_FALSE(alpha_listener1.IsEmpty()); |
| 197 ASSERT_FALSE(alpha_listener2.IsEmpty()); |
| 198 ASSERT_FALSE(beta_listener.IsEmpty()); |
| 199 |
| 200 { |
| 201 const char kAddListenerFunction[] = |
| 202 "(function(event, listener) { event.addListener(listener); })"; |
| 203 v8::Local<v8::Function> add_listener_function = |
| 204 FunctionFromString(context, kAddListenerFunction); |
| 205 { |
| 206 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener1}; |
| 207 RunFunction(add_listener_function, context, arraysize(argv), argv); |
| 208 } |
| 209 { |
| 210 v8::Local<v8::Value> argv[] = {alpha_event, alpha_listener2}; |
| 211 RunFunction(add_listener_function, context, arraysize(argv), argv); |
| 212 } |
| 213 { |
| 214 v8::Local<v8::Value> argv[] = {beta_event, beta_listener}; |
| 215 RunFunction(add_listener_function, context, arraysize(argv), argv); |
| 216 } |
| 217 } |
| 218 |
| 219 EXPECT_EQ(2u, |
| 220 handler.GetEventListenersForTesting(kAlphaName, context).size()); |
| 221 EXPECT_EQ(1u, handler.GetEventListenersForTesting(kBetaName, context).size()); |
| 222 |
| 223 auto get_fired_count = [&context](const char* name) { |
| 224 v8::Local<v8::Value> res = |
| 225 GetPropertyFromObject(context->Global(), context, name); |
| 226 if (res->IsUndefined()) |
| 227 return 0; |
| 228 int32_t count = 0; |
| 229 EXPECT_TRUE( |
| 230 gin::Converter<int32_t>::FromV8(context->GetIsolate(), res, &count)) |
| 231 << name; |
| 232 return count; |
| 233 }; |
| 234 |
| 235 EXPECT_EQ(0, get_fired_count("alphaCount1")); |
| 236 EXPECT_EQ(0, get_fired_count("alphaCount2")); |
| 237 EXPECT_EQ(0, get_fired_count("betaCount")); |
| 238 |
| 239 handler.FireEventInContext(kAlphaName, context, base::ListValue()); |
| 240 EXPECT_EQ(2u, |
| 241 handler.GetEventListenersForTesting(kAlphaName, context).size()); |
| 242 EXPECT_EQ(1u, handler.GetEventListenersForTesting(kBetaName, context).size()); |
| 243 |
| 244 EXPECT_EQ(1, get_fired_count("alphaCount1")); |
| 245 EXPECT_EQ(1, get_fired_count("alphaCount2")); |
| 246 EXPECT_EQ(0, get_fired_count("betaCount")); |
| 247 |
| 248 handler.FireEventInContext(kAlphaName, context, base::ListValue()); |
| 249 EXPECT_EQ(2, get_fired_count("alphaCount1")); |
| 250 EXPECT_EQ(2, get_fired_count("alphaCount2")); |
| 251 EXPECT_EQ(0, get_fired_count("betaCount")); |
| 252 |
| 253 handler.FireEventInContext(kBetaName, context, base::ListValue()); |
| 254 EXPECT_EQ(2, get_fired_count("alphaCount1")); |
| 255 EXPECT_EQ(2, get_fired_count("alphaCount2")); |
| 256 EXPECT_EQ(1, get_fired_count("betaCount")); |
| 257 } |
| 258 |
| 259 // Tests firing events with arguments. |
| 260 TEST_F(APIEventHandlerTest, EventArguments) { |
| 261 v8::Isolate* isolate = instance_->isolate(); |
| 262 v8::HandleScope handle_scope(isolate); |
| 263 v8::Local<v8::Context> context = |
| 264 v8::Local<v8::Context>::New(isolate, context_); |
| 265 |
| 266 const char kEventName[] = "alpha"; |
| 267 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 268 v8::Local<v8::Object> event = |
| 269 handler.CreateEventInstance(kEventName, context); |
| 270 ASSERT_FALSE(event.IsEmpty()); |
| 271 |
| 272 const char kListenerFunction[] = |
| 273 "(function() { this.eventArgs = Array.from(arguments); })"; |
| 274 v8::Local<v8::Function> listener_function = |
| 275 FunctionFromString(context, kListenerFunction); |
| 276 ASSERT_FALSE(listener_function.IsEmpty()); |
| 277 |
| 278 { |
| 279 const char kAddListenerFunction[] = |
| 280 "(function(event, listener) { event.addListener(listener); })"; |
| 281 v8::Local<v8::Function> add_listener_function = |
| 282 FunctionFromString(context, kAddListenerFunction); |
| 283 v8::Local<v8::Value> argv[] = {event, listener_function}; |
| 284 RunFunction(add_listener_function, context, arraysize(argv), argv); |
| 285 } |
| 286 |
| 287 const char kArguments[] = "['foo',1,{'prop1':'bar'}]"; |
| 288 std::unique_ptr<base::ListValue> event_args = ListValueFromString(kArguments); |
| 289 ASSERT_TRUE(event_args); |
| 290 handler.FireEventInContext(kEventName, context, *event_args); |
| 291 |
| 292 std::unique_ptr<base::Value> result = |
| 293 GetBaseValuePropertyFromObject(context->Global(), context, "eventArgs"); |
| 294 ASSERT_TRUE(result); |
| 295 EXPECT_EQ(ReplaceSingleQuotes(kArguments), ValueToString(*result)); |
| 296 } |
| 297 |
| 298 // Test dispatching events to multiple contexts. |
| 299 TEST_F(APIEventHandlerTest, MultipleContexts) { |
| 300 v8::Isolate* isolate = instance_->isolate(); |
| 301 v8::HandleScope handle_scope(instance_->isolate()); |
| 302 |
| 303 v8::Local<v8::Context> context_a = |
| 304 v8::Local<v8::Context>::New(isolate, context_); |
| 305 v8::Local<v8::Context> context_b = v8::Context::New(isolate); |
| 306 gin::ContextHolder holder_b(isolate); |
| 307 holder_b.SetContext(context_b); |
| 308 |
| 309 const char kEventName[] = "onFoo"; |
| 310 |
| 311 APIEventHandler handler(base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 312 |
| 313 v8::Local<v8::Function> listener_a = FunctionFromString( |
| 314 context_a, "(function(arg) { this.eventArgs = arg + 'alpha'; })"); |
| 315 ASSERT_FALSE(listener_a.IsEmpty()); |
| 316 v8::Local<v8::Function> listener_b = FunctionFromString( |
| 317 context_b, "(function(arg) { this.eventArgs = arg + 'beta'; })"); |
| 318 ASSERT_FALSE(listener_b.IsEmpty()); |
| 319 |
| 320 // Create two instances of the same event in different contexts. |
| 321 v8::Local<v8::Object> event_a = |
| 322 handler.CreateEventInstance(kEventName, context_a); |
| 323 ASSERT_FALSE(event_a.IsEmpty()); |
| 324 v8::Local<v8::Object> event_b = |
| 325 handler.CreateEventInstance(kEventName, context_b); |
| 326 ASSERT_FALSE(event_b.IsEmpty()); |
| 327 |
| 328 // Add two separate listeners to the event, one in each context. |
| 329 const char kAddListenerFunction[] = |
| 330 "(function(event, listener) { event.addListener(listener); })"; |
| 331 { |
| 332 v8::Local<v8::Function> add_listener_a = |
| 333 FunctionFromString(context_a, kAddListenerFunction); |
| 334 v8::Local<v8::Value> argv[] = {event_a, listener_a}; |
| 335 RunFunction(add_listener_a, context_a, arraysize(argv), argv); |
| 336 } |
| 337 EXPECT_EQ(1u, |
| 338 handler.GetEventListenersForTesting(kEventName, context_a).size()); |
| 339 EXPECT_EQ(0u, |
| 340 handler.GetEventListenersForTesting(kEventName, context_b).size()); |
| 341 |
| 342 { |
| 343 v8::Local<v8::Function> add_listener_b = |
| 344 FunctionFromString(context_b, kAddListenerFunction); |
| 345 v8::Local<v8::Value> argv[] = {event_b, listener_b}; |
| 346 RunFunction(add_listener_b, context_b, arraysize(argv), argv); |
| 347 } |
| 348 EXPECT_EQ(1u, |
| 349 handler.GetEventListenersForTesting(kEventName, context_a).size()); |
| 350 EXPECT_EQ(1u, |
| 351 handler.GetEventListenersForTesting(kEventName, context_b).size()); |
| 352 |
| 353 // Dispatch the event in context_a - the listener in context_b should not be |
| 354 // notified. |
| 355 std::unique_ptr<base::ListValue> arguments_a = |
| 356 ListValueFromString("['result_a:']"); |
| 357 ASSERT_TRUE(arguments_a); |
| 358 |
| 359 handler.FireEventInContext(kEventName, context_a, *arguments_a); |
| 360 { |
| 361 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject( |
| 362 context_a->Global(), context_a, "eventArgs"); |
| 363 ASSERT_TRUE(result_a); |
| 364 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a)); |
| 365 } |
| 366 { |
| 367 v8::Local<v8::Value> result_b = |
| 368 GetPropertyFromObject(context_b->Global(), context_b, "eventArgs"); |
| 369 ASSERT_FALSE(result_b.IsEmpty()); |
| 370 EXPECT_TRUE(result_b->IsUndefined()); |
| 371 } |
| 372 |
| 373 // Dispatch the event in context_b - the listener in context_a should not be |
| 374 // notified. |
| 375 std::unique_ptr<base::ListValue> arguments_b = |
| 376 ListValueFromString("['result_b:']"); |
| 377 ASSERT_TRUE(arguments_b); |
| 378 handler.FireEventInContext(kEventName, context_b, *arguments_b); |
| 379 { |
| 380 std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject( |
| 381 context_a->Global(), context_a, "eventArgs"); |
| 382 ASSERT_TRUE(result_a); |
| 383 EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a)); |
| 384 } |
| 385 { |
| 386 std::unique_ptr<base::Value> result_b = GetBaseValuePropertyFromObject( |
| 387 context_b->Global(), context_b, "eventArgs"); |
| 388 ASSERT_TRUE(result_b); |
| 389 EXPECT_EQ("\"result_b:beta\"", ValueToString(*result_b)); |
| 390 } |
| 391 } |
| 392 |
| 393 } // namespace extensions |
OLD | NEW |