Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(913)

Side by Side Diff: extensions/renderer/api_binding_unittest.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_binding.h"
6 #include "base/bind.h"
7 #include "base/memory/ptr_util.h"
8 #include "base/stl_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "extensions/renderer/api_binding_hooks.h"
12 #include "extensions/renderer/api_binding_hooks_test_delegate.h"
13 #include "extensions/renderer/api_binding_test.h"
14 #include "extensions/renderer/api_binding_test_util.h"
15 #include "extensions/renderer/api_event_handler.h"
16 #include "extensions/renderer/api_invocation_errors.h"
17 #include "extensions/renderer/api_request_handler.h"
18 #include "extensions/renderer/api_type_reference_map.h"
19 #include "gin/arguments.h"
20 #include "gin/converter.h"
21 #include "gin/public/context_holder.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
24 #include "v8/include/v8.h"
25
26 namespace extensions {
27
28 using namespace api_errors;
29
30 namespace {
31
32 const char kBindingName[] = "test";
33
34 // Function spec; we use single quotes for readability and then replace them.
35 const char kFunctions[] =
36 "[{"
37 " 'name': 'oneString',"
38 " 'parameters': [{"
39 " 'type': 'string',"
40 " 'name': 'str'"
41 " }]"
42 "}, {"
43 " 'name': 'stringAndInt',"
44 " 'parameters': [{"
45 " 'type': 'string',"
46 " 'name': 'str'"
47 " }, {"
48 " 'type': 'integer',"
49 " 'name': 'int'"
50 " }]"
51 "}, {"
52 " 'name': 'oneObject',"
53 " 'parameters': [{"
54 " 'type': 'object',"
55 " 'name': 'foo',"
56 " 'properties': {"
57 " 'prop1': {'type': 'string'},"
58 " 'prop2': {'type': 'string', 'optional': true}"
59 " }"
60 " }]"
61 "}, {"
62 " 'name': 'intAndCallback',"
63 " 'parameters': [{"
64 " 'name': 'int',"
65 " 'type': 'integer'"
66 " }, {"
67 " 'name': 'callback',"
68 " 'type': 'function'"
69 " }]"
70 "}]";
71
72 bool AllowAllFeatures(v8::Local<v8::Context> context, const std::string& name) {
73 return true;
74 }
75
76 void OnEventListenersChanged(const std::string& event_name,
77 binding::EventListenersChanged change,
78 const base::DictionaryValue* filter,
79 bool was_manual,
80 v8::Local<v8::Context> context) {}
81
82 } // namespace
83
84 class APIBindingUnittest : public APIBindingTest {
85 public:
86 void OnFunctionCall(std::unique_ptr<APIRequestHandler::Request> request,
87 v8::Local<v8::Context> context) {
88 last_request_ = std::move(request);
89 }
90
91 protected:
92 APIBindingUnittest()
93 : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {}
94 void SetUp() override {
95 APIBindingTest::SetUp();
96 request_handler_ = base::MakeUnique<APIRequestHandler>(
97 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
98 base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
99 APILastError(APILastError::GetParent(),
100 APILastError::AddConsoleError()));
101 }
102
103 void TearDown() override {
104 DisposeAllContexts();
105 request_handler_.reset();
106 event_handler_.reset();
107 binding_.reset();
108 APIBindingTest::TearDown();
109 }
110
111 void OnWillDisposeContext(v8::Local<v8::Context> context) override {
112 event_handler_->InvalidateContext(context);
113 request_handler_->InvalidateContext(context);
114 }
115
116 void SetFunctions(const char* functions) {
117 binding_functions_ = ListValueFromString(functions);
118 ASSERT_TRUE(binding_functions_);
119 }
120
121 void SetEvents(const char* events) {
122 binding_events_ = ListValueFromString(events);
123 ASSERT_TRUE(binding_events_);
124 }
125
126 void SetTypes(const char* types) {
127 binding_types_ = ListValueFromString(types);
128 ASSERT_TRUE(binding_types_);
129 }
130
131 void SetProperties(const char* properties) {
132 binding_properties_ = DictionaryValueFromString(properties);
133 ASSERT_TRUE(binding_properties_);
134 }
135
136 void SetHooks(std::unique_ptr<APIBindingHooks> hooks) {
137 binding_hooks_ = std::move(hooks);
138 ASSERT_TRUE(binding_hooks_);
139 }
140
141 void SetHooksDelegate(
142 std::unique_ptr<APIBindingHooksDelegate> hooks_delegate) {
143 binding_hooks_delegate_ = std::move(hooks_delegate);
144 ASSERT_TRUE(binding_hooks_delegate_);
145 }
146
147 void SetCreateCustomType(const APIBinding::CreateCustomType& callback) {
148 create_custom_type_ = callback;
149 }
150
151 void SetAvailabilityCallback(
152 const APIBinding::AvailabilityCallback& callback) {
153 availability_callback_ = callback;
154 }
155
156 void InitializeBinding() {
157 if (!binding_hooks_) {
158 binding_hooks_ = base::MakeUnique<APIBindingHooks>(
159 kBindingName, binding::RunJSFunctionSync());
160 }
161 if (binding_hooks_delegate_)
162 binding_hooks_->SetDelegate(std::move(binding_hooks_delegate_));
163 if (!availability_callback_)
164 availability_callback_ = base::Bind(&AllowAllFeatures);
165 event_handler_ = base::MakeUnique<APIEventHandler>(
166 base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
167 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
168 base::Bind(&OnEventListenersChanged));
169 binding_ = base::MakeUnique<APIBinding>(
170 kBindingName, binding_functions_.get(), binding_types_.get(),
171 binding_events_.get(), binding_properties_.get(), create_custom_type_,
172 availability_callback_, std::move(binding_hooks_), &type_refs_,
173 request_handler_.get(), event_handler_.get());
174 EXPECT_EQ(!binding_types_.get(), type_refs_.empty());
175 }
176
177 void ExpectPass(v8::Local<v8::Object> object,
178 const std::string& script_source,
179 const std::string& expected_json_arguments_single_quotes,
180 bool expect_callback) {
181 ExpectPass(MainContext(), object, script_source,
182 expected_json_arguments_single_quotes, expect_callback);
183 }
184
185 void ExpectPass(v8::Local<v8::Context> context,
186 v8::Local<v8::Object> object,
187 const std::string& script_source,
188 const std::string& expected_json_arguments_single_quotes,
189 bool expect_callback) {
190 RunTest(context, object, script_source, true,
191 ReplaceSingleQuotes(expected_json_arguments_single_quotes),
192 expect_callback, std::string());
193 }
194
195 void ExpectFailure(v8::Local<v8::Object> object,
196 const std::string& script_source,
197 const std::string& expected_error) {
198 RunTest(MainContext(), object, script_source, false, std::string(), false,
199 "Uncaught TypeError: " + expected_error);
200 }
201
202 void ExpectThrow(v8::Local<v8::Object> object,
203 const std::string& script_source,
204 const std::string& expected_error) {
205 RunTest(MainContext(), object, script_source, false, std::string(), false,
206 "Uncaught Error: " + expected_error);
207 }
208
209 bool HandlerWasInvoked() const { return last_request_ != nullptr; }
210 const APIRequestHandler::Request* last_request() const {
211 return last_request_.get();
212 }
213 void reset_last_request() { last_request_.reset(); }
214 APIBinding* binding() { return binding_.get(); }
215 APIEventHandler* event_handler() { return event_handler_.get(); }
216 APIRequestHandler* request_handler() { return request_handler_.get(); }
217 const APITypeReferenceMap& type_refs() const { return type_refs_; }
218
219 private:
220 void RunTest(v8::Local<v8::Context> context,
221 v8::Local<v8::Object> object,
222 const std::string& script_source,
223 bool should_pass,
224 const std::string& expected_json_arguments,
225 bool expect_callback,
226 const std::string& expected_error);
227
228 std::unique_ptr<APIRequestHandler::Request> last_request_;
229 std::unique_ptr<APIBinding> binding_;
230 std::unique_ptr<APIEventHandler> event_handler_;
231 std::unique_ptr<APIRequestHandler> request_handler_;
232 APITypeReferenceMap type_refs_;
233
234 std::unique_ptr<base::ListValue> binding_functions_;
235 std::unique_ptr<base::ListValue> binding_events_;
236 std::unique_ptr<base::ListValue> binding_types_;
237 std::unique_ptr<base::DictionaryValue> binding_properties_;
238 std::unique_ptr<APIBindingHooks> binding_hooks_;
239 std::unique_ptr<APIBindingHooksDelegate> binding_hooks_delegate_;
240 APIBinding::CreateCustomType create_custom_type_;
241 APIBinding::AvailabilityCallback availability_callback_;
242
243 DISALLOW_COPY_AND_ASSIGN(APIBindingUnittest);
244 };
245
246 void APIBindingUnittest::RunTest(v8::Local<v8::Context> context,
247 v8::Local<v8::Object> object,
248 const std::string& script_source,
249 bool should_pass,
250 const std::string& expected_json_arguments,
251 bool expect_callback,
252 const std::string& expected_error) {
253 EXPECT_FALSE(last_request_);
254 std::string wrapped_script_source =
255 base::StringPrintf("(function(obj) { %s })", script_source.c_str());
256
257 v8::Local<v8::Function> func =
258 FunctionFromString(context, wrapped_script_source);
259 ASSERT_FALSE(func.IsEmpty());
260
261 v8::Local<v8::Value> argv[] = {object};
262
263 if (should_pass) {
264 RunFunction(func, context, 1, argv);
265 ASSERT_TRUE(last_request_) << script_source;
266 EXPECT_EQ(expected_json_arguments,
267 ValueToString(*last_request_->arguments));
268 EXPECT_EQ(expect_callback, last_request_->has_callback) << script_source;
269 } else {
270 RunFunctionAndExpectError(func, context, 1, argv, expected_error);
271 EXPECT_FALSE(last_request_);
272 }
273
274 last_request_.reset();
275 }
276
277 TEST_F(APIBindingUnittest, TestEmptyAPI) {
278 InitializeBinding();
279
280 v8::HandleScope handle_scope(isolate());
281 v8::Local<v8::Context> context = MainContext();
282
283 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
284 EXPECT_EQ(
285 0u,
286 binding_object->GetOwnPropertyNames(context).ToLocalChecked()->Length());
287 }
288
289 // Tests the basic call -> request flow of the API binding (ensuring that
290 // functions are set up correctly and correctly enforced).
291 TEST_F(APIBindingUnittest, TestBasicAPICalls) {
292 SetFunctions(kFunctions);
293 InitializeBinding();
294
295 v8::HandleScope handle_scope(isolate());
296 v8::Local<v8::Context> context = MainContext();
297
298 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
299
300 // Argument parsing is tested primarily in APISignature and ArgumentSpec
301 // tests, so do a few quick sanity checks...
302 ExpectPass(binding_object, "obj.oneString('foo');", "['foo']", false);
303 ExpectFailure(
304 binding_object, "obj.oneString(1);",
305 InvocationError(
306 "test.oneString", "string str",
307 ArgumentError("str", InvalidType(kTypeString, kTypeInteger))));
308 ExpectPass(binding_object, "obj.stringAndInt('foo', 1)", "['foo',1]", false);
309 ExpectFailure(
310 binding_object, "obj.stringAndInt(1)",
311 InvocationError(
312 "test.stringAndInt", "string str, integer int",
313 ArgumentError("str", InvalidType(kTypeString, kTypeInteger))));
314 ExpectPass(binding_object, "obj.intAndCallback(1, function() {})", "[1]",
315 true);
316 ExpectFailure(
317 binding_object, "obj.intAndCallback(function() {})",
318 InvocationError(
319 "test.intAndCallback", "integer int, function callback",
320 ArgumentError("int", InvalidType(kTypeInteger, kTypeFunction))));
321
322 // ...And an interesting case (throwing an error during parsing).
323 ExpectThrow(binding_object,
324 "obj.oneObject({ get prop1() { throw new Error('Badness'); } });",
325 "Badness");
326 }
327
328 // Test that "forIOThread" property in a function schema is respected.
329 TEST_F(APIBindingUnittest, IOThreadCalls) {
330 const char kFunctions[] =
331 "[{"
332 " 'name' : 'uiFunc1',"
333 " 'parameters' : []"
334 "}, {"
335 " 'name' : 'uiFunc2',"
336 " 'parameters' : [],"
337 " 'forIOThread' : false"
338 "}, {"
339 " 'name' : 'ioFunc',"
340 " 'parameters' : [],"
341 " 'forIOThread' : true"
342 "}]";
343 SetFunctions(kFunctions);
344 InitializeBinding();
345
346 v8::HandleScope handle_scope(isolate());
347 v8::Local<v8::Context> context = MainContext();
348 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
349
350 struct {
351 const char* func_name;
352 binding::RequestThread thread;
353 } test_cases[] = {
354 {"uiFunc1", binding::RequestThread::UI},
355 {"uiFunc2", binding::RequestThread::UI},
356 {"ioFunc", binding::RequestThread::IO},
357 };
358 const char kFunctionCall[] = "(function(obj) { obj.%s(); })";
359 v8::Local<v8::Value> argv[] = {binding_object};
360
361 for (const auto& test_case : test_cases) {
362 SCOPED_TRACE(base::StringPrintf("Testing case-%s", test_case.func_name));
363 v8::Local<v8::Function> func = FunctionFromString(
364 context, base::StringPrintf(kFunctionCall, test_case.func_name));
365 RunFunction(func, context, arraysize(argv), argv);
366 ASSERT_TRUE(last_request());
367 EXPECT_EQ(test_case.thread, last_request()->thread);
368 reset_last_request();
369 }
370 }
371
372 // Test that enum values are properly exposed on the binding object.
373 TEST_F(APIBindingUnittest, EnumValues) {
374 const char kTypes[] =
375 "[{"
376 " 'id': 'first',"
377 " 'type': 'string',"
378 " 'enum': ['alpha', 'camelCase', 'Hyphen-ated',"
379 " 'SCREAMING', 'nums123', '42nums']"
380 "}, {"
381 " 'id': 'last',"
382 " 'type': 'string',"
383 " 'enum': [{'name': 'omega'}]"
384 "}]";
385
386 SetTypes(kTypes);
387 InitializeBinding();
388
389 v8::HandleScope handle_scope(isolate());
390 v8::Local<v8::Context> context = MainContext();
391
392 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
393
394 const char kExpected[] =
395 "{'ALPHA':'alpha','CAMEL_CASE':'camelCase','HYPHEN_ATED':'Hyphen-ated',"
396 "'NUMS123':'nums123','SCREAMING':'SCREAMING','_42NUMS':'42nums'}";
397 EXPECT_EQ(ReplaceSingleQuotes(kExpected),
398 GetStringPropertyFromObject(binding_object, context, "first"));
399 EXPECT_EQ(ReplaceSingleQuotes("{'OMEGA':'omega'}"),
400 GetStringPropertyFromObject(binding_object, context, "last"));
401 }
402
403 // Test that empty enum entries are (unfortunately) allowed.
404 TEST_F(APIBindingUnittest, EnumWithEmptyEntry) {
405 const char kTypes[] =
406 "[{"
407 " 'id': 'enumWithEmpty',"
408 " 'type': 'string',"
409 " 'enum': [{'name': ''}, {'name': 'other'}]"
410 "}]";
411
412 SetTypes(kTypes);
413 InitializeBinding();
414
415 v8::HandleScope handle_scope(isolate());
416 v8::Local<v8::Context> context = MainContext();
417
418 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
419
420 EXPECT_EQ(
421 "{\"\":\"\",\"OTHER\":\"other\"}",
422 GetStringPropertyFromObject(binding_object, context, "enumWithEmpty"));
423 }
424
425 // Test that type references are correctly set up in the API.
426 TEST_F(APIBindingUnittest, TypeRefsTest) {
427 const char kTypes[] =
428 "[{"
429 " 'id': 'refObj',"
430 " 'type': 'object',"
431 " 'properties': {"
432 " 'prop1': {'type': 'string'},"
433 " 'prop2': {'type': 'integer', 'optional': true}"
434 " }"
435 "}, {"
436 " 'id': 'refEnum',"
437 " 'type': 'string',"
438 " 'enum': ['alpha', 'beta']"
439 "}]";
440 const char kRefFunctions[] =
441 "[{"
442 " 'name': 'takesRefObj',"
443 " 'parameters': [{"
444 " 'name': 'o',"
445 " '$ref': 'refObj'"
446 " }]"
447 "}, {"
448 " 'name': 'takesRefEnum',"
449 " 'parameters': [{"
450 " 'name': 'e',"
451 " '$ref': 'refEnum'"
452 " }]"
453 "}]";
454
455 SetFunctions(kRefFunctions);
456 SetTypes(kTypes);
457 InitializeBinding();
458 EXPECT_EQ(2u, type_refs().size());
459 EXPECT_TRUE(type_refs().GetSpec("refObj"));
460 EXPECT_TRUE(type_refs().GetSpec("refEnum"));
461
462 v8::HandleScope handle_scope(isolate());
463 v8::Local<v8::Context> context = MainContext();
464
465 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
466
467 // Parsing in general is tested in APISignature and ArgumentSpec tests, but
468 // we test that the binding a) correctly finds the definitions, and b) accepts
469 // properties from the API object.
470 ExpectPass(binding_object, "obj.takesRefObj({prop1: 'foo'})",
471 "[{'prop1':'foo'}]", false);
472 ExpectFailure(
473 binding_object, "obj.takesRefObj({prop1: 'foo', prop2: 'a'})",
474 InvocationError(
475 "test.takesRefObj", "refObj o",
476 ArgumentError(
477 "o",
478 PropertyError("prop2", InvalidType(kTypeInteger, kTypeString)))));
479 ExpectPass(binding_object, "obj.takesRefEnum('alpha')", "['alpha']", false);
480 ExpectPass(binding_object, "obj.takesRefEnum(obj.refEnum.BETA)", "['beta']",
481 false);
482 ExpectFailure(
483 binding_object, "obj.takesRefEnum('gamma')",
484 InvocationError("test.takesRefEnum", "refEnum e",
485 ArgumentError("e", InvalidEnumValue({"alpha", "beta"}))));
486 }
487
488 TEST_F(APIBindingUnittest, RestrictedAPIs) {
489 const char kFunctions[] =
490 "[{"
491 " 'name': 'allowedOne',"
492 " 'parameters': []"
493 "}, {"
494 " 'name': 'allowedTwo',"
495 " 'parameters': []"
496 "}, {"
497 " 'name': 'restrictedOne',"
498 " 'parameters': []"
499 "}, {"
500 " 'name': 'restrictedTwo',"
501 " 'parameters': []"
502 "}]";
503 SetFunctions(kFunctions);
504 const char kEvents[] =
505 "[{'name': 'allowedEvent'}, {'name': 'restrictedEvent'}]";
506 SetEvents(kEvents);
507 auto is_available = [](v8::Local<v8::Context> context,
508 const std::string& name) {
509 std::set<std::string> allowed = {"test.allowedOne", "test.allowedTwo",
510 "test.allowedEvent"};
511 std::set<std::string> restricted = {
512 "test.restrictedOne", "test.restrictedTwo", "test.restrictedEvent"};
513 EXPECT_TRUE(allowed.count(name) || restricted.count(name)) << name;
514 return allowed.count(name) != 0;
515 };
516 SetAvailabilityCallback(base::Bind(is_available));
517
518 InitializeBinding();
519
520 v8::HandleScope handle_scope(isolate());
521 v8::Local<v8::Context> context = MainContext();
522
523 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
524
525 auto is_defined = [&binding_object, context](const std::string& name) {
526 v8::Local<v8::Value> val =
527 GetPropertyFromObject(binding_object, context, name);
528 EXPECT_FALSE(val.IsEmpty());
529 return !val->IsUndefined() && !val->IsNull();
530 };
531
532 EXPECT_TRUE(is_defined("allowedOne"));
533 EXPECT_TRUE(is_defined("allowedTwo"));
534 EXPECT_TRUE(is_defined("allowedEvent"));
535 EXPECT_FALSE(is_defined("restrictedOne"));
536 EXPECT_FALSE(is_defined("restrictedTwo"));
537 EXPECT_FALSE(is_defined("restrictedEvent"));
538 }
539
540 // Tests that events specified in the API are created as properties of the API
541 // object.
542 TEST_F(APIBindingUnittest, TestEventCreation) {
543 SetEvents(
544 R"([
545 {'name': 'onFoo'},
546 {'name': 'onBar'},
547 {'name': 'onBaz', 'options': {'maxListeners': 1}}
548 ])");
549 SetFunctions(kFunctions);
550 InitializeBinding();
551
552 v8::HandleScope handle_scope(isolate());
553 v8::Local<v8::Context> context = MainContext();
554
555 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
556
557 // Event behavior is tested in the APIEventHandler unittests as well as the
558 // APIBindingsSystem tests, so we really only need to check that the events
559 // are being initialized on the object.
560 v8::Maybe<bool> has_on_foo =
561 binding_object->Has(context, gin::StringToV8(isolate(), "onFoo"));
562 EXPECT_TRUE(has_on_foo.IsJust());
563 EXPECT_TRUE(has_on_foo.FromJust());
564
565 v8::Maybe<bool> has_on_bar =
566 binding_object->Has(context, gin::StringToV8(isolate(), "onBar"));
567 EXPECT_TRUE(has_on_bar.IsJust());
568 EXPECT_TRUE(has_on_bar.FromJust());
569
570 v8::Maybe<bool> has_on_baz =
571 binding_object->Has(context, gin::StringToV8(isolate(), "onBaz"));
572 EXPECT_TRUE(has_on_baz.IsJust());
573 EXPECT_TRUE(has_on_baz.FromJust());
574
575 // Test that the maxListeners property is correctly used.
576 v8::Local<v8::Function> add_listener = FunctionFromString(
577 context, "(function(e) { e.addListener(function() {}); })");
578 v8::Local<v8::Value> args[] = {
579 GetPropertyFromObject(binding_object, context, "onBaz")};
580 RunFunction(add_listener, context, arraysize(args), args);
581 EXPECT_EQ(1u, event_handler()->GetNumEventListenersForTesting("test.onBaz",
582 context));
583 RunFunctionAndExpectError(add_listener, context, arraysize(args), args,
584 "Uncaught TypeError: Too many listeners.");
585 EXPECT_EQ(1u, event_handler()->GetNumEventListenersForTesting("test.onBaz",
586 context));
587
588 v8::Maybe<bool> has_nonexistent_event = binding_object->Has(
589 context, gin::StringToV8(isolate(), "onNonexistentEvent"));
590 EXPECT_TRUE(has_nonexistent_event.IsJust());
591 EXPECT_FALSE(has_nonexistent_event.FromJust());
592 }
593
594 TEST_F(APIBindingUnittest, TestProperties) {
595 SetProperties(
596 "{"
597 " 'prop1': { 'value': 17, 'type': 'integer' },"
598 " 'prop2': {"
599 " 'type': 'object',"
600 " 'properties': {"
601 " 'subprop1': { 'value': 'some value', 'type': 'string' },"
602 " 'subprop2': { 'value': true, 'type': 'boolean' }"
603 " }"
604 " }"
605 "}");
606 InitializeBinding();
607
608 v8::HandleScope handle_scope(isolate());
609 v8::Local<v8::Context> context = MainContext();
610 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
611 EXPECT_EQ("17",
612 GetStringPropertyFromObject(binding_object, context, "prop1"));
613 EXPECT_EQ(R"({"subprop1":"some value","subprop2":true})",
614 GetStringPropertyFromObject(binding_object, context, "prop2"));
615 }
616
617 TEST_F(APIBindingUnittest, TestRefProperties) {
618 SetProperties(
619 "{"
620 " 'alpha': {"
621 " '$ref': 'AlphaRef',"
622 " 'value': ['a']"
623 " },"
624 " 'beta': {"
625 " '$ref': 'BetaRef',"
626 " 'value': ['b']"
627 " }"
628 "}");
629 auto create_custom_type = [](v8::Isolate* isolate,
630 const std::string& type_name,
631 const std::string& property_name,
632 const base::ListValue* property_values) {
633 v8::Local<v8::Context> context = isolate->GetCurrentContext();
634 v8::Local<v8::Object> result = v8::Object::New(isolate);
635 if (type_name == "AlphaRef") {
636 EXPECT_EQ("alpha", property_name);
637 EXPECT_EQ("[\"a\"]", ValueToString(*property_values));
638 result
639 ->Set(context, gin::StringToSymbol(isolate, "alphaProp"),
640 gin::StringToV8(isolate, "alphaVal"))
641 .ToChecked();
642 } else if (type_name == "BetaRef") {
643 EXPECT_EQ("beta", property_name);
644 EXPECT_EQ("[\"b\"]", ValueToString(*property_values));
645 result
646 ->Set(context, gin::StringToSymbol(isolate, "betaProp"),
647 gin::StringToV8(isolate, "betaVal"))
648 .ToChecked();
649 } else {
650 EXPECT_TRUE(false) << type_name;
651 }
652 return result;
653 };
654
655 SetCreateCustomType(base::Bind(create_custom_type));
656
657 InitializeBinding();
658
659 v8::HandleScope handle_scope(isolate());
660 v8::Local<v8::Context> context = MainContext();
661 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
662 EXPECT_EQ(R"({"alphaProp":"alphaVal"})",
663 GetStringPropertyFromObject(binding_object, context, "alpha"));
664 EXPECT_EQ(
665 R"({"betaProp":"betaVal"})",
666 GetStringPropertyFromObject(binding_object, context, "beta"));
667 }
668
669 TEST_F(APIBindingUnittest, TestDisposedContext) {
670 SetFunctions(kFunctions);
671 InitializeBinding();
672
673 v8::HandleScope handle_scope(isolate());
674 v8::Local<v8::Context> context = MainContext();
675
676 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
677
678 v8::Local<v8::Function> func =
679 FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
680 v8::Local<v8::Value> argv[] = {binding_object};
681 DisposeContext(context);
682 RunFunction(func, context, arraysize(argv), argv);
683 EXPECT_FALSE(HandlerWasInvoked());
684 // This test passes if this does not crash, even under AddressSanitizer
685 // builds.
686 }
687
688 TEST_F(APIBindingUnittest, MultipleContexts) {
689 v8::HandleScope handle_scope(isolate());
690 v8::Local<v8::Context> context_a = MainContext();
691 v8::Local<v8::Context> context_b = AddContext();
692
693 SetFunctions(kFunctions);
694 InitializeBinding();
695
696 v8::Local<v8::Object> binding_object_a = binding()->CreateInstance(context_a);
697 v8::Local<v8::Object> binding_object_b = binding()->CreateInstance(context_b);
698
699 ExpectPass(context_a, binding_object_a, "obj.oneString('foo');", "['foo']",
700 false);
701 ExpectPass(context_b, binding_object_b, "obj.oneString('foo');", "['foo']",
702 false);
703 DisposeContext(context_a);
704 ExpectPass(context_b, binding_object_b, "obj.oneString('foo');", "['foo']",
705 false);
706 }
707
708 // Tests adding custom hooks for an API method.
709 TEST_F(APIBindingUnittest, TestCustomHooks) {
710 SetFunctions(kFunctions);
711
712 // Register a hook for the test.oneString method.
713 auto hooks = base::MakeUnique<APIBindingHooksTestDelegate>();
714 bool did_call = false;
715 auto hook = [](bool* did_call, const APISignature* signature,
716 v8::Local<v8::Context> context,
717 std::vector<v8::Local<v8::Value>>* arguments,
718 const APITypeReferenceMap& ref_map) {
719 *did_call = true;
720 APIBindingHooks::RequestResult result(
721 APIBindingHooks::RequestResult::HANDLED);
722 if (arguments->size() != 1u) { // ASSERT* messes with the return type.
723 EXPECT_EQ(1u, arguments->size());
724 return result;
725 }
726 EXPECT_EQ("foo", gin::V8ToString(arguments->at(0)));
727 return result;
728 };
729 hooks->AddHandler("test.oneString", base::Bind(hook, &did_call));
730 SetHooksDelegate(std::move(hooks));
731
732 InitializeBinding();
733
734 v8::HandleScope handle_scope(isolate());
735 v8::Local<v8::Context> context = MainContext();
736
737 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
738
739 // First try calling the oneString() method, which has a custom hook
740 // installed.
741 v8::Local<v8::Function> func =
742 FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
743 v8::Local<v8::Value> args[] = {binding_object};
744 RunFunction(func, context, 1, args);
745 EXPECT_TRUE(did_call);
746
747 // Other methods, like stringAndInt(), should behave normally.
748 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
749 false);
750 }
751
752 TEST_F(APIBindingUnittest, TestJSCustomHook) {
753 // Register a hook for the test.oneString method.
754 auto hooks = base::MakeUnique<APIBindingHooks>(
755 kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle));
756
757 v8::HandleScope handle_scope(isolate());
758 v8::Local<v8::Context> context = MainContext();
759 {
760 const char kRegisterHook[] =
761 "(function(hooks) {\n"
762 " hooks.setHandleRequest('oneString', function() {\n"
763 " this.requestArguments = Array.from(arguments);\n"
764 " });\n"
765 "})";
766 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
767 v8::Local<v8::Function> function =
768 FunctionFromString(context, kRegisterHook);
769 v8::Local<v8::Value> args[] = {js_hooks};
770 RunFunctionOnGlobal(function, context, arraysize(args), args);
771 }
772
773 SetFunctions(kFunctions);
774 SetHooks(std::move(hooks));
775 InitializeBinding();
776
777 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
778
779 // First try calling with an invalid invocation. An error should be raised and
780 // the hook should never have been called, since the arguments didn't match.
781 ExpectFailure(
782 binding_object, "obj.oneString(1);",
783 InvocationError(
784 "test.oneString", "string str",
785 ArgumentError("str", InvalidType(kTypeString, kTypeInteger))));
786 v8::Local<v8::Value> property =
787 GetPropertyFromObject(context->Global(), context, "requestArguments");
788 ASSERT_FALSE(property.IsEmpty());
789 EXPECT_TRUE(property->IsUndefined());
790
791 // Try calling the oneString() method with valid arguments. The hook should
792 // be called.
793 v8::Local<v8::Function> func =
794 FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
795 v8::Local<v8::Value> args[] = {binding_object};
796 RunFunction(func, context, 1, args);
797
798 EXPECT_EQ("[\"foo\"]", GetStringPropertyFromObject(
799 context->Global(), context, "requestArguments"));
800
801 // Other methods, like stringAndInt(), should behave normally.
802 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
803 false);
804 }
805
806 // Tests the updateArgumentsPreValidate hook.
807 TEST_F(APIBindingUnittest, TestUpdateArgumentsPreValidate) {
808 // Register a hook for the test.oneString method.
809 auto hooks = base::MakeUnique<APIBindingHooks>(
810 kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle));
811
812 v8::HandleScope handle_scope(isolate());
813 v8::Local<v8::Context> context = MainContext();
814 const char kRegisterHook[] =
815 "(function(hooks) {\n"
816 " hooks.setUpdateArgumentsPreValidate('oneString', function() {\n"
817 " this.requestArguments = Array.from(arguments);\n"
818 " if (this.requestArguments[0] === true)\n"
819 " return ['hooked']\n"
820 " return this.requestArguments\n"
821 " });\n"
822 "})";
823 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
824 v8::Local<v8::Function> function = FunctionFromString(context, kRegisterHook);
825 v8::Local<v8::Value> args[] = {js_hooks};
826 RunFunctionOnGlobal(function, context, arraysize(args), args);
827
828 SetHooks(std::move(hooks));
829 SetFunctions(kFunctions);
830 InitializeBinding();
831
832 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
833
834 // Call the method with a hook. Since the hook updates arguments before
835 // validation, we should be able to pass in invalid arguments and still
836 // have the hook called.
837 ExpectFailure(
838 binding_object, "obj.oneString(false);",
839 InvocationError(
840 "test.oneString", "string str",
841 ArgumentError("str", InvalidType(kTypeString, kTypeBoolean))));
842 EXPECT_EQ("[false]", GetStringPropertyFromObject(
843 context->Global(), context, "requestArguments"));
844
845 ExpectPass(binding_object, "obj.oneString(true);", "['hooked']", false);
846 EXPECT_EQ("[true]", GetStringPropertyFromObject(
847 context->Global(), context, "requestArguments"));
848
849 // Other methods, like stringAndInt(), should behave normally.
850 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
851 false);
852 }
853
854 // Tests the updateArgumentsPreValidate hook.
855 TEST_F(APIBindingUnittest, TestThrowInUpdateArgumentsPreValidate) {
856 auto run_js_and_allow_error = [](v8::Local<v8::Function> function,
857 v8::Local<v8::Context> context,
858 int argc,
859 v8::Local<v8::Value> argv[]) {
860 v8::MaybeLocal<v8::Value> maybe_result =
861 function->Call(context, context->Global(), argc, argv);
862 v8::Global<v8::Value> result;
863 v8::Local<v8::Value> local;
864 if (maybe_result.ToLocal(&local))
865 result.Reset(context->GetIsolate(), local);
866 return result;
867 };
868
869 // Register a hook for the test.oneString method.
870 auto hooks = base::MakeUnique<APIBindingHooks>(
871 kBindingName, base::Bind(run_js_and_allow_error));
872
873 v8::HandleScope handle_scope(isolate());
874 v8::Local<v8::Context> context = MainContext();
875 {
876 const char kRegisterHook[] =
877 "(function(hooks) {\n"
878 " hooks.setUpdateArgumentsPreValidate('oneString', function() {\n"
879 " throw new Error('Custom Hook Error');\n"
880 " });\n"
881 "})";
882 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
883 v8::Local<v8::Function> function =
884 FunctionFromString(context, kRegisterHook);
885 v8::Local<v8::Value> args[] = {js_hooks};
886 RunFunctionOnGlobal(function, context, arraysize(args), args);
887 }
888
889 SetHooks(std::move(hooks));
890 SetFunctions(kFunctions);
891 InitializeBinding();
892
893 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
894
895 v8::Local<v8::Function> function =
896 FunctionFromString(context,
897 "(function(obj) { return obj.oneString('ping'); })");
898 v8::Local<v8::Value> args[] = {binding_object};
899 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
900 arraysize(args), args,
901 "Uncaught Error: Custom Hook Error");
902
903 // Other methods, like stringAndInt(), should behave normally.
904 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
905 false);
906 }
907
908 // Tests that custom JS hooks can return results synchronously.
909 TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) {
910 // Register a hook for the test.oneString method.
911 auto hooks = base::MakeUnique<APIBindingHooks>(
912 kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle));
913
914 v8::HandleScope handle_scope(isolate());
915 v8::Local<v8::Context> context = MainContext();
916 {
917 const char kRegisterHook[] =
918 "(function(hooks) {\n"
919 " hooks.setHandleRequest('oneString', str => {\n"
920 " return str + ' pong';\n"
921 " });\n"
922 "})";
923 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
924 v8::Local<v8::Function> function =
925 FunctionFromString(context, kRegisterHook);
926 v8::Local<v8::Value> args[] = {js_hooks};
927 RunFunctionOnGlobal(function, context, arraysize(args), args);
928 }
929
930 SetHooks(std::move(hooks));
931 SetFunctions(kFunctions);
932 InitializeBinding();
933
934 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
935
936 v8::Local<v8::Function> function =
937 FunctionFromString(context,
938 "(function(obj) { return obj.oneString('ping'); })");
939 v8::Local<v8::Value> args[] = {binding_object};
940 v8::Local<v8::Value> result =
941 RunFunction(function, context, arraysize(args), args);
942 ASSERT_FALSE(result.IsEmpty());
943 std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
944 ASSERT_TRUE(json_result);
945 EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
946 }
947
948 // Tests that JS custom hooks can throw exceptions for bad invocations.
949 TEST_F(APIBindingUnittest, TestThrowingFromCustomJSHook) {
950 // Our testing handlers for running functions expect a pre-determined success
951 // or failure. Since we're testing throwing exceptions here, we need a way of
952 // running that allows exceptions to be thrown, but we still expect most JS
953 // calls to succeed.
954 // TODO(devlin): This is a bit clunky. If we need to do this enough, we could
955 // figure out a different solution, like having a stack object for allowing
956 // errors/exceptions. But given this is the only place we need it so far, this
957 // is sufficient.
958 auto run_js_and_expect_error = [](v8::Local<v8::Function> function,
959 v8::Local<v8::Context> context,
960 int argc,
961 v8::Local<v8::Value> argv[]) {
962 v8::MaybeLocal<v8::Value> maybe_result =
963 function->Call(context, context->Global(), argc, argv);
964 v8::Global<v8::Value> result;
965 v8::Local<v8::Value> local;
966 if (maybe_result.ToLocal(&local))
967 result.Reset(context->GetIsolate(), local);
968 return result;
969 };
970 // Register a hook for the test.oneString method.
971 auto hooks = base::MakeUnique<APIBindingHooks>(
972 kBindingName, base::Bind(run_js_and_expect_error));
973
974 v8::HandleScope handle_scope(isolate());
975 v8::Local<v8::Context> context = MainContext();
976 {
977 const char kRegisterHook[] =
978 "(function(hooks) {\n"
979 " hooks.setHandleRequest('oneString', str => {\n"
980 " throw new Error('Custom Hook Error');\n"
981 " });\n"
982 "})";
983 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
984 v8::Local<v8::Function> function =
985 FunctionFromString(context, kRegisterHook);
986 v8::Local<v8::Value> args[] = {js_hooks};
987 RunFunctionOnGlobal(function, context, arraysize(args), args);
988 }
989
990 SetHooks(std::move(hooks));
991 SetFunctions(kFunctions);
992 InitializeBinding();
993
994 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
995
996 v8::Local<v8::Function> function =
997 FunctionFromString(context,
998 "(function(obj) { return obj.oneString('ping'); })");
999 v8::Local<v8::Value> args[] = {binding_object};
1000 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
1001 arraysize(args), args,
1002 "Uncaught Error: Custom Hook Error");
1003 }
1004
1005 // Tests that native custom hooks can return results synchronously, or throw
1006 // exceptions for bad invocations.
1007 TEST_F(APIBindingUnittest,
1008 TestReturningResultAndThrowingExceptionFromCustomNativeHook) {
1009 v8::HandleScope handle_scope(isolate());
1010 v8::Local<v8::Context> context = MainContext();
1011
1012 // Register a hook for the test.oneString method.
1013 auto hooks = base::MakeUnique<APIBindingHooksTestDelegate>();
1014 bool did_call = false;
1015 auto hook = [](bool* did_call, const APISignature* signature,
1016 v8::Local<v8::Context> context,
1017 std::vector<v8::Local<v8::Value>>* arguments,
1018 const APITypeReferenceMap& ref_map) {
1019 APIBindingHooks::RequestResult result(
1020 APIBindingHooks::RequestResult::HANDLED);
1021 if (arguments->size() != 1u) { // ASSERT* messes with the return type.
1022 EXPECT_EQ(1u, arguments->size());
1023 return result;
1024 }
1025 v8::Isolate* isolate = context->GetIsolate();
1026 std::string arg_value = gin::V8ToString(arguments->at(0));
1027 if (arg_value == "throw") {
1028 isolate->ThrowException(v8::Exception::Error(
1029 gin::StringToV8(isolate, "Custom Hook Error")));
1030 result.code = APIBindingHooks::RequestResult::THROWN;
1031 return result;
1032 }
1033 result.return_value =
1034 gin::StringToV8(context->GetIsolate(), arg_value + " pong");
1035 return result;
1036 };
1037 hooks->AddHandler("test.oneString", base::Bind(hook, &did_call));
1038
1039 SetHooksDelegate(std::move(hooks));
1040 SetFunctions(kFunctions);
1041 InitializeBinding();
1042
1043 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
1044
1045 {
1046 // Test an invocation that we expect to throw an exception.
1047 v8::Local<v8::Function> function =
1048 FunctionFromString(
1049 context, "(function(obj) { return obj.oneString('throw'); })");
1050 v8::Local<v8::Value> args[] = {binding_object};
1051 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
1052 arraysize(args), args,
1053 "Uncaught Error: Custom Hook Error");
1054 }
1055
1056 {
1057 // Test an invocation we expect to succeed.
1058 v8::Local<v8::Function> function =
1059 FunctionFromString(context,
1060 "(function(obj) { return obj.oneString('ping'); })");
1061 v8::Local<v8::Value> args[] = {binding_object};
1062 v8::Local<v8::Value> result =
1063 RunFunction(function, context, arraysize(args), args);
1064 ASSERT_FALSE(result.IsEmpty());
1065 std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
1066 ASSERT_TRUE(json_result);
1067 EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
1068 }
1069 }
1070
1071 // Tests the updateArgumentsPostValidate hook.
1072 TEST_F(APIBindingUnittest, TestUpdateArgumentsPostValidate) {
1073 // Register a hook for the test.oneString method.
1074 auto hooks = base::MakeUnique<APIBindingHooks>(
1075 kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle));
1076
1077 v8::HandleScope handle_scope(isolate());
1078 v8::Local<v8::Context> context = MainContext();
1079 {
1080 const char kRegisterHook[] =
1081 "(function(hooks) {\n"
1082 " hooks.setUpdateArgumentsPostValidate('oneString', function() {\n"
1083 " this.requestArguments = Array.from(arguments);\n"
1084 " return ['pong'];\n"
1085 " });\n"
1086 "})";
1087 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
1088 v8::Local<v8::Function> function =
1089 FunctionFromString(context, kRegisterHook);
1090 v8::Local<v8::Value> args[] = {js_hooks};
1091 RunFunctionOnGlobal(function, context, arraysize(args), args);
1092 }
1093
1094 SetHooks(std::move(hooks));
1095 SetFunctions(kFunctions);
1096 InitializeBinding();
1097
1098 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
1099
1100 // Try calling the method with an invalid signature. Since it's invalid, we
1101 // should never enter the hook.
1102 ExpectFailure(
1103 binding_object, "obj.oneString(false);",
1104 InvocationError(
1105 "test.oneString", "string str",
1106 ArgumentError("str", InvalidType(kTypeString, kTypeBoolean))));
1107 EXPECT_EQ("undefined", GetStringPropertyFromObject(
1108 context->Global(), context, "requestArguments"));
1109
1110 // Call the method with a valid signature. The hook should be entered and
1111 // manipulate the arguments.
1112 ExpectPass(binding_object, "obj.oneString('ping');", "['pong']", false);
1113 EXPECT_EQ("[\"ping\"]", GetStringPropertyFromObject(
1114 context->Global(), context, "requestArguments"));
1115
1116 // Other methods, like stringAndInt(), should behave normally.
1117 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);",
1118 "['foo',42]", false);
1119 }
1120
1121 // Tests using setUpdateArgumentsPostValidate to return a list of arguments
1122 // that violates the function schema. Sadly, this should succeed. :(
1123 // See comment in api_binding.cc.
1124 TEST_F(APIBindingUnittest, TestUpdateArgumentsPostValidateViolatingSchema) {
1125 // Register a hook for the test.oneString method.
1126 auto hooks = base::MakeUnique<APIBindingHooks>(
1127 kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle));
1128
1129 v8::HandleScope handle_scope(isolate());
1130 v8::Local<v8::Context> context = MainContext();
1131 {
1132 const char kRegisterHook[] =
1133 "(function(hooks) {\n"
1134 " hooks.setUpdateArgumentsPostValidate('oneString', function() {\n"
1135 " return [{}];\n"
1136 " });\n"
1137 "})";
1138 v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
1139 v8::Local<v8::Function> function =
1140 FunctionFromString(context, kRegisterHook);
1141 v8::Local<v8::Value> args[] = {js_hooks};
1142 RunFunctionOnGlobal(function, context, arraysize(args), args);
1143 }
1144
1145 SetHooks(std::move(hooks));
1146 SetFunctions(kFunctions);
1147 InitializeBinding();
1148
1149 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
1150
1151 // Call the method with a valid signature. The hook should be entered and
1152 // manipulate the arguments.
1153 ExpectPass(binding_object, "obj.oneString('ping');", "[{}]", false);
1154 }
1155
1156 // Test that user gestures are properly recorded when calling APIs.
1157 TEST_F(APIBindingUnittest, TestUserGestures) {
1158 SetFunctions(kFunctions);
1159 InitializeBinding();
1160
1161 v8::HandleScope handle_scope(isolate());
1162 v8::Local<v8::Context> context = MainContext();
1163
1164 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
1165
1166 v8::Local<v8::Function> function =
1167 FunctionFromString(context, "(function(obj) { obj.oneString('foo');})");
1168 ASSERT_FALSE(function.IsEmpty());
1169
1170 v8::Local<v8::Value> argv[] = {binding_object};
1171 RunFunction(function, context, arraysize(argv), argv);
1172 ASSERT_TRUE(last_request());
1173 EXPECT_FALSE(last_request()->has_user_gesture);
1174 reset_last_request();
1175
1176 blink::WebScopedUserGesture user_gesture(nullptr);
1177 RunFunction(function, context, arraysize(argv), argv);
1178 ASSERT_TRUE(last_request());
1179 EXPECT_TRUE(last_request()->has_user_gesture);
1180
1181 reset_last_request();
1182 }
1183
1184 TEST_F(APIBindingUnittest, FilteredEvents) {
1185 const char kEvents[] =
1186 "[{"
1187 " 'name': 'unfilteredOne',"
1188 " 'parameters': []"
1189 "}, {"
1190 " 'name': 'unfilteredTwo',"
1191 " 'filters': [],"
1192 " 'parameters': []"
1193 "}, {"
1194 " 'name': 'unfilteredThree',"
1195 " 'options': {'supportsFilters': false},"
1196 " 'parameters': []"
1197 "}, {"
1198 " 'name': 'filteredOne',"
1199 " 'options': {'supportsFilters': true},"
1200 " 'parameters': []"
1201 "}, {"
1202 " 'name': 'filteredTwo',"
1203 " 'filters': ["
1204 " {'name': 'url', 'type': 'array', 'items': {'type': 'any'}}"
1205 " ],"
1206 " 'parameters': []"
1207 "}]";
1208 SetEvents(kEvents);
1209 InitializeBinding();
1210
1211 v8::HandleScope handle_scope(isolate());
1212 v8::Local<v8::Context> context = MainContext();
1213
1214 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
1215
1216 const char kAddFilteredListener[] =
1217 "(function(evt) {\n"
1218 " evt.addListener(function() {},\n"
1219 " {url: [{pathContains: 'simple2.html'}]});\n"
1220 "})";
1221 v8::Local<v8::Function> function =
1222 FunctionFromString(context, kAddFilteredListener);
1223 ASSERT_FALSE(function.IsEmpty());
1224
1225 auto check_supports_filters = [context, binding_object, function](
1226 base::StringPiece name,
1227 bool expect_supports) {
1228 SCOPED_TRACE(name);
1229 v8::Local<v8::Value> event =
1230 GetPropertyFromObject(binding_object, context, name);
1231 v8::Local<v8::Value> args[] = {event};
1232 if (expect_supports) {
1233 RunFunction(function, context, context->Global(), arraysize(args), args);
1234 } else {
1235 RunFunctionAndExpectError(
1236 function, context, context->Global(), arraysize(args), args,
1237 "Uncaught TypeError: This event does not support filters");
1238 }
1239 };
1240
1241 check_supports_filters("unfilteredOne", false);
1242 check_supports_filters("unfilteredTwo", false);
1243 check_supports_filters("unfilteredThree", false);
1244 check_supports_filters("filteredOne", true);
1245 check_supports_filters("filteredTwo", true);
1246 }
1247
1248 TEST_F(APIBindingUnittest, HooksTemplateInitializer) {
1249 SetFunctions(kFunctions);
1250
1251 // Register a hook for the test.oneString method.
1252 auto hooks = base::MakeUnique<APIBindingHooksTestDelegate>();
1253 auto hook = [](v8::Isolate* isolate,
1254 v8::Local<v8::ObjectTemplate> object_template,
1255 const APITypeReferenceMap& type_refs) {
1256 object_template->Set(gin::StringToSymbol(isolate, "hookedProperty"),
1257 gin::ConvertToV8(isolate, 42));
1258 };
1259 hooks->SetTemplateInitializer(base::Bind(hook));
1260 SetHooksDelegate(std::move(hooks));
1261
1262 InitializeBinding();
1263
1264 v8::HandleScope handle_scope(isolate());
1265 v8::Local<v8::Context> context = MainContext();
1266
1267 v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
1268
1269 // The extra property should be present on the binding object.
1270 EXPECT_EQ("42", GetStringPropertyFromObject(binding_object, context,
1271 "hookedProperty"));
1272 // Sanity check: other values should still be there.
1273 EXPECT_EQ("function",
1274 GetStringPropertyFromObject(binding_object, context, "oneString"));
1275 }
1276
1277 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698