1// Copyright 2014 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/common/extension_urls.h"
6#include "extensions/renderer/module_system_test.h"
7#include "grit/extensions_renderer_resources.h"
8
9namespace extensions {
10namespace {
11
12class EventUnittest : public ModuleSystemTest {
13  virtual void SetUp() OVERRIDE {
14    ModuleSystemTest::SetUp();
15
16    env()->RegisterModule(kEventBindings, IDR_EVENT_BINDINGS_JS);
17    env()->RegisterModule("json_schema", IDR_JSON_SCHEMA_JS);
18    env()->RegisterModule(kSchemaUtils, IDR_SCHEMA_UTILS_JS);
19    env()->RegisterModule("uncaught_exception_handler",
20                          IDR_UNCAUGHT_EXCEPTION_HANDLER_JS);
21    env()->RegisterModule("unload_event", IDR_UNLOAD_EVENT_JS);
22    env()->RegisterModule("utils", IDR_UTILS_JS);
23
24    // Mock out the native handler for event_bindings. These mocks will fail if
25    // any invariants maintained by the real event_bindings are broken.
26    env()->OverrideNativeHandler(
27        "event_natives",
28        "var assert = requireNative('assert');"
29        "var attachedListeners = exports.attachedListeners = {};"
30        "var attachedFilteredListeners = "
31        "    exports.attachedFilteredListeners = {};"
32        "var nextId = 0;"
33        "var idToName = {};"
34        "exports.AttachEvent = function(eventName) {"
35        "  assert.AssertFalse(!!attachedListeners[eventName]);"
36        "  attachedListeners[eventName] = 1;"
37        "};"
38        "exports.DetachEvent = function(eventName) {"
39        "  assert.AssertTrue(!!attachedListeners[eventName]);"
40        "  delete attachedListeners[eventName];"
41        "};"
42        "exports.IsEventAttached = function(eventName) {"
43        "  return !!attachedListeners[eventName];"
44        "};"
45        "exports.AttachFilteredEvent = function(name, filters) {"
46        "  var id = nextId++;"
47        "  idToName[id] = name;"
48        "  attachedFilteredListeners[name] ="
49        "    attachedFilteredListeners[name] || [];"
50        "  attachedFilteredListeners[name][id] = filters;"
51        "  return id;"
52        "};"
53        "exports.DetachFilteredEvent = function(id, manual) {"
54        "  var i = attachedFilteredListeners[idToName[id]].indexOf(id);"
55        "  attachedFilteredListeners[idToName[id]].splice(i, 1);"
56        "};"
57        "exports.HasFilteredListener = function(name) {"
58        "  return attachedFilteredListeners[name].length;"
59        "};");
60    env()->OverrideNativeHandler("sendRequest",
61                                 "exports.sendRequest = function() {};");
62    env()->OverrideNativeHandler(
63        "apiDefinitions",
64        "exports.GetExtensionAPIDefinitionsForTest = function() {};");
65    env()->OverrideNativeHandler("logging", "exports.DCHECK = function() {};");
66    env()->OverrideNativeHandler("schema_registry",
67                                 "exports.GetSchema = function() {};");
68  }
69};
70
71TEST_F(EventUnittest, TestNothing) {
72  ExpectNoAssertionsMade();
73}
74
75TEST_F(EventUnittest, AddRemoveTwoListeners) {
76  ModuleSystem::NativesEnabledScope natives_enabled_scope(
77      env()->module_system());
78  env()->RegisterModule(
79      "test",
80      "var assert = requireNative('assert');"
81      "var Event = require('event_bindings').Event;"
82      "var eventNatives = requireNative('event_natives');"
83      "var myEvent = new Event('named-event');"
84      "var cb1 = function() {};"
85      "var cb2 = function() {};"
86      "myEvent.addListener(cb1);"
87      "myEvent.addListener(cb2);"
88      "myEvent.removeListener(cb1);"
89      "assert.AssertTrue(!!eventNatives.attachedListeners['named-event']);"
90      "myEvent.removeListener(cb2);"
91      "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
92  env()->module_system()->Require("test");
93}
94
95TEST_F(EventUnittest, OnUnloadDetachesAllListeners) {
96  ModuleSystem::NativesEnabledScope natives_enabled_scope(
97      env()->module_system());
98  env()->RegisterModule(
99      "test",
100      "var assert = requireNative('assert');"
101      "var Event = require('event_bindings').Event;"
102      "var eventNatives = requireNative('event_natives');"
103      "var myEvent = new Event('named-event');"
104      "var cb1 = function() {};"
105      "var cb2 = function() {};"
106      "myEvent.addListener(cb1);"
107      "myEvent.addListener(cb2);"
108      "require('unload_event').dispatch();"
109      "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
110  env()->module_system()->Require("test");
111}
112
113TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) {
114  ModuleSystem::NativesEnabledScope natives_enabled_scope(
115      env()->module_system());
116  env()->RegisterModule(
117      "test",
118      "var assert = requireNative('assert');"
119      "var Event = require('event_bindings').Event;"
120      "var eventNatives = requireNative('event_natives');"
121      "var myEvent = new Event('named-event');"
122      "var cb1 = function() {};"
123      "myEvent.addListener(cb1);"
124      "myEvent.addListener(cb1);"
125      "require('unload_event').dispatch();"
126      "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
127  env()->module_system()->Require("test");
128}
129
130TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) {
131  ModuleSystem::NativesEnabledScope natives_enabled_scope(
132      env()->module_system());
133  env()->RegisterModule(
134      "test",
135      "var Event = require('event_bindings').Event;"
136      "var eventOpts = {supportsRules: true};"
137      "var assert = requireNative('assert');"
138      "var caught = false;"
139      "try {"
140      "  var myEvent = new Event(undefined, undefined, eventOpts);"
141      "} catch (e) {"
142      "  caught = true;"
143      "}"
144      "assert.AssertTrue(caught);");
145  env()->module_system()->Require("test");
146}
147
148TEST_F(EventUnittest, NamedEventDispatch) {
149  ModuleSystem::NativesEnabledScope natives_enabled_scope(
150      env()->module_system());
151  env()->RegisterModule(
152      "test",
153      "var Event = require('event_bindings').Event;"
154      "var dispatchEvent = require('event_bindings').dispatchEvent;"
155      "var assert = requireNative('assert');"
156      "var e = new Event('myevent');"
157      "var called = false;"
158      "e.addListener(function() { called = true; });"
159      "dispatchEvent('myevent', []);"
160      "assert.AssertTrue(called);");
161  env()->module_system()->Require("test");
162}
163
164TEST_F(EventUnittest, AddListenerWithFiltersThrowsErrorByDefault) {
165  ModuleSystem::NativesEnabledScope natives_enabled_scope(
166      env()->module_system());
167  env()->RegisterModule("test",
168                        "var Event = require('event_bindings').Event;"
169                        "var assert = requireNative('assert');"
170                        "var e = new Event('myevent');"
171                        "var filter = [{"
172                        "  url: {hostSuffix: 'google.com'},"
173                        "}];"
174                        "var caught = false;"
175                        "try {"
176                        "  e.addListener(function() {}, filter);"
177                        "} catch (e) {"
178                        "  caught = true;"
179                        "}"
180                        "assert.AssertTrue(caught);");
181  env()->module_system()->Require("test");
182}
183
184TEST_F(EventUnittest, FilteredEventsAttachment) {
185  ModuleSystem::NativesEnabledScope natives_enabled_scope(
186      env()->module_system());
187  env()->RegisterModule(
188      "test",
189      "var Event = require('event_bindings').Event;"
190      "var assert = requireNative('assert');"
191      "var bindings = requireNative('event_natives');"
192      "var eventOpts = {supportsListeners: true, supportsFilters: true};"
193      "var e = new Event('myevent', undefined, eventOpts);"
194      "var cb = function() {};"
195      "var filters = {url: [{hostSuffix: 'google.com'}]};"
196      "e.addListener(cb, filters);"
197      "assert.AssertTrue(bindings.HasFilteredListener('myevent'));"
198      "e.removeListener(cb);"
199      "assert.AssertFalse(bindings.HasFilteredListener('myevent'));");
200  env()->module_system()->Require("test");
201}
202
203TEST_F(EventUnittest, DetachFilteredEvent) {
204  ModuleSystem::NativesEnabledScope natives_enabled_scope(
205      env()->module_system());
206  env()->RegisterModule(
207      "test",
208      "var Event = require('event_bindings').Event;"
209      "var assert = requireNative('assert');"
210      "var bindings = requireNative('event_natives');"
211      "var eventOpts = {supportsListeners: true, supportsFilters: true};"
212      "var e = new Event('myevent', undefined, eventOpts);"
213      "var cb1 = function() {};"
214      "var cb2 = function() {};"
215      "var filters = {url: [{hostSuffix: 'google.com'}]};"
216      "e.addListener(cb1, filters);"
217      "e.addListener(cb2, filters);"
218      "privates(e).impl.detach_();"
219      "assert.AssertFalse(bindings.HasFilteredListener('myevent'));");
220  env()->module_system()->Require("test");
221}
222
223TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) {
224  ModuleSystem::NativesEnabledScope natives_enabled_scope(
225      env()->module_system());
226  env()->RegisterModule(
227      "test",
228      "var Event = require('event_bindings').Event;"
229      "var assert = requireNative('assert');"
230      "var bindings = requireNative('event_natives');"
231      "var eventOpts = {supportsListeners: true, supportsFilters: true};"
232      "var e = new Event('myevent', undefined, eventOpts);"
233      "var cb = function() {};"
234      "var filters = {url: [{hostSuffix: 'google.com'}]};"
235      "e.addListener(cb, filters);"
236      "e.addListener(cb, filters);"
237      "assert.AssertTrue(bindings.HasFilteredListener('myevent'));"
238      "e.removeListener(cb);"
239      "assert.AssertTrue(bindings.HasFilteredListener('myevent'));"
240      "e.removeListener(cb);"
241      "assert.AssertFalse(bindings.HasFilteredListener('myevent'));");
242  env()->module_system()->Require("test");
243}
244
245TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) {
246  ModuleSystem::NativesEnabledScope natives_enabled_scope(
247      env()->module_system());
248  env()->RegisterModule(
249      "test",
250      "var Event = require('event_bindings').Event;"
251      "var assert = requireNative('assert');"
252      "var eventOpts = {supportsListeners: true, supportsFilters: true};"
253      "var e = new Event('myevent', undefined, eventOpts);"
254      "var cb = function() {};"
255      "var filters = {url: {hostSuffix: 'google.com'}};"
256      "var caught = false;"
257      "try {"
258      "  e.addListener(cb, filters);"
259      "} catch (e) {"
260      "  caught = true;"
261      "}"
262      "assert.AssertTrue(caught);");
263  env()->module_system()->Require("test");
264}
265
266TEST_F(EventUnittest, MaxListeners) {
267  ModuleSystem::NativesEnabledScope natives_enabled_scope(
268      env()->module_system());
269  env()->RegisterModule(
270      "test",
271      "var Event = require('event_bindings').Event;"
272      "var assert = requireNative('assert');"
273      "var eventOpts = {supportsListeners: true, maxListeners: 1};"
274      "var e = new Event('myevent', undefined, eventOpts);"
275      "var cb = function() {};"
276      "var caught = false;"
277      "try {"
278      "  e.addListener(cb);"
279      "} catch (e) {"
280      "  caught = true;"
281      "}"
282      "assert.AssertTrue(!caught);"
283      "try {"
284      "  e.addListener(cb);"
285      "} catch (e) {"
286      "  caught = true;"
287      "}"
288      "assert.AssertTrue(caught);");
289  env()->module_system()->Require("test");
290}
291
292}  // namespace
293}  // namespace extensions
294