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

Side by Side Diff: chrome/renderer/resources/extensions/event.js

Issue 15841013: Make miscellaneous_bindings and event_bindings Required as needed. Previously (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 var DCHECK = requireNative('logging').DCHECK; 5 var eventNatives = requireNative('event_natives');
6 // TODO(cduvall/kalman): json_schema shouldn't put things on chromeHidden. 6 var logging = requireNative('logging');
7 require('json_schema'); 7 var onUnload = require('on_unload');
8 var eventBindingsNatives = requireNative('event_bindings'); 8 var schemaRegistry = requireNative('schema_registry');
9 var AttachEvent = eventBindingsNatives.AttachEvent;
10 var DetachEvent = eventBindingsNatives.DetachEvent;
11 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent;
12 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent;
13 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter;
14 var forEach = require('utils').forEach;
15 var sendRequest = require('sendRequest').sendRequest; 9 var sendRequest = require('sendRequest').sendRequest;
16 var utils = require('utils'); 10 var utils = require('utils');
17 var validate = require('schemaUtils').validate; 11 var validate = require('schemaUtils').validate;
18 12
19 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
20 var chrome = requireNative('chrome').GetChrome();
21 var schemaRegistry = requireNative('schema_registry');
22
23 // Schemas for the rule-style functions on the events API that 13 // Schemas for the rule-style functions on the events API that
24 // only need to be generated occasionally, so populate them lazily. 14 // only need to be generated occasionally, so populate them lazily.
25 var ruleFunctionSchemas = { 15 var ruleFunctionSchemas = {
26 // These values are set lazily: 16 // These values are set lazily:
27 // addRules: {}, 17 // addRules: {},
28 // getRules: {}, 18 // getRules: {},
29 // removeRules: {} 19 // removeRules: {}
30 }; 20 };
31 21
32 // This function ensures that |ruleFunctionSchemas| is populated. 22 // This function ensures that |ruleFunctionSchemas| is populated.
(...skipping 23 matching lines...) Expand all
56 46
57 // Handles adding/removing/dispatching listeners for unfiltered events. 47 // Handles adding/removing/dispatching listeners for unfiltered events.
58 var UnfilteredAttachmentStrategy = function(event) { 48 var UnfilteredAttachmentStrategy = function(event) {
59 this.event_ = event; 49 this.event_ = event;
60 }; 50 };
61 51
62 UnfilteredAttachmentStrategy.prototype.onAddedListener = 52 UnfilteredAttachmentStrategy.prototype.onAddedListener =
63 function(listener) { 53 function(listener) {
64 // Only attach / detach on the first / last listener removed. 54 // Only attach / detach on the first / last listener removed.
65 if (this.event_.listeners_.length == 0) 55 if (this.event_.listeners_.length == 0)
66 AttachEvent(this.event_.eventName_); 56 eventNatives.AttachEvent(this.event_.eventName_);
67 }; 57 };
68 58
69 UnfilteredAttachmentStrategy.prototype.onRemovedListener = 59 UnfilteredAttachmentStrategy.prototype.onRemovedListener =
70 function(listener) { 60 function(listener) {
71 if (this.event_.listeners_.length == 0) 61 if (this.event_.listeners_.length == 0)
72 this.detach(true); 62 this.detach(true);
73 }; 63 };
74 64
75 UnfilteredAttachmentStrategy.prototype.detach = function(manual) { 65 UnfilteredAttachmentStrategy.prototype.detach = function(manual) {
76 DetachEvent(this.event_.eventName_, manual); 66 eventNatives.DetachEvent(this.event_.eventName_, manual);
77 }; 67 };
78 68
79 UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { 69 UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
80 return this.event_.listeners_; 70 return this.event_.listeners_;
81 }; 71 };
82 72
83 var FilteredAttachmentStrategy = function(event) { 73 var FilteredAttachmentStrategy = function(event) {
84 this.event_ = event; 74 this.event_ = event;
85 this.listenerMap_ = {}; 75 this.listenerMap_ = {};
86 }; 76 };
87 77
88 FilteredAttachmentStrategy.idToEventMap = {}; 78 FilteredAttachmentStrategy.idToEventMap = {};
89 79
90 FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) { 80 FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) {
91 var id = AttachFilteredEvent(this.event_.eventName_, 81 var id = eventNatives.AttachFilteredEvent(this.event_.eventName_,
92 listener.filters || {}); 82 listener.filters || {});
93 if (id == -1) 83 if (id == -1)
94 throw new Error("Can't add listener"); 84 throw new Error("Can't add listener");
95 listener.id = id; 85 listener.id = id;
96 this.listenerMap_[id] = listener; 86 this.listenerMap_[id] = listener;
97 FilteredAttachmentStrategy.idToEventMap[id] = this.event_; 87 FilteredAttachmentStrategy.idToEventMap[id] = this.event_;
98 }; 88 };
99 89
100 FilteredAttachmentStrategy.prototype.onRemovedListener = function(listener) { 90 FilteredAttachmentStrategy.prototype.onRemovedListener = function(listener) {
101 this.detachListener(listener, true); 91 this.detachListener(listener, true);
102 }; 92 };
103 93
104 FilteredAttachmentStrategy.prototype.detachListener = 94 FilteredAttachmentStrategy.prototype.detachListener =
105 function(listener, manual) { 95 function(listener, manual) {
106 if (listener.id == undefined) 96 if (listener.id == undefined)
107 throw new Error("listener.id undefined - '" + listener + "'"); 97 throw new Error("listener.id undefined - '" + listener + "'");
108 var id = listener.id; 98 var id = listener.id;
109 delete this.listenerMap_[id]; 99 delete this.listenerMap_[id];
110 delete FilteredAttachmentStrategy.idToEventMap[id]; 100 delete FilteredAttachmentStrategy.idToEventMap[id];
111 DetachFilteredEvent(id, manual); 101 eventNatives.DetachFilteredEvent(id, manual);
112 }; 102 };
113 103
114 FilteredAttachmentStrategy.prototype.detach = function(manual) { 104 FilteredAttachmentStrategy.prototype.detach = function(manual) {
115 for (var i in this.listenerMap_) 105 for (var i in this.listenerMap_)
116 this.detachListener(this.listenerMap_[i], manual); 106 this.detachListener(this.listenerMap_[i], manual);
117 }; 107 };
118 108
119 FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { 109 FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
120 var result = []; 110 var result = [];
121 for (var i = 0; i < ids.length; i++) 111 for (var i = 0; i < ids.length; i++)
122 result.push(this.listenerMap_[ids[i]]); 112 result.push(this.listenerMap_[ids[i]]);
123 return result; 113 return result;
124 }; 114 };
125 115
126 chromeHidden.parseEventOptions = function(opt_eventOptions) { 116 function parseEventOptions(opt_eventOptions) {
127 function merge(dest, src) { 117 function merge(dest, src) {
128 for (var k in src) { 118 for (var k in src) {
129 if (!dest.hasOwnProperty(k)) { 119 if (!dest.hasOwnProperty(k)) {
130 dest[k] = src[k]; 120 dest[k] = src[k];
131 } 121 }
132 } 122 }
133 } 123 }
134 124
135 var options = opt_eventOptions || {}; 125 var options = opt_eventOptions || {};
136 merge(options, 126 merge(options,
137 {supportsFilters: false, 127 {supportsFilters: false,
138 supportsListeners: true, 128 supportsListeners: true,
139 supportsRules: false, 129 supportsRules: false,
140 }); 130 });
141 return options; 131 return options;
142 }; 132 };
143 133
144 // Event object. If opt_eventName is provided, this object represents 134 // Event object. If opt_eventName is provided, this object represents
145 // the unique instance of that named event, and dispatching an event 135 // the unique instance of that named event, and dispatching an event
146 // with that name will route through this object's listeners. Note that 136 // with that name will route through this object's listeners. Note that
147 // opt_eventName is required for events that support rules. 137 // opt_eventName is required for events that support rules.
148 // 138 //
149 // Example: 139 // Example:
150 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); 140 // var Event = require('event_bindings').Event;
141 // chrome.tabs.onChanged = new Event("tab-changed");
151 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); 142 // chrome.tabs.onChanged.addListener(function(data) { alert(data); });
152 // chromeHidden.Event.dispatch("tab-changed", "hi"); 143 // Event.dispatch("tab-changed", "hi");
153 // will result in an alert dialog that says 'hi'. 144 // will result in an alert dialog that says 'hi'.
154 // 145 //
155 // If opt_eventOptions exists, it is a dictionary that contains the boolean 146 // If opt_eventOptions exists, it is a dictionary that contains the boolean
156 // entries "supportsListeners" and "supportsRules". 147 // entries "supportsListeners" and "supportsRules".
157 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { 148 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) {
158 this.eventName_ = opt_eventName; 149 this.eventName_ = opt_eventName;
159 this.listeners_ = []; 150 this.listeners_ = [];
160 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); 151 this.eventOptions_ = parseEventOptions(opt_eventOptions);
161 152
162 if (this.eventOptions_.supportsRules && !opt_eventName) 153 if (this.eventOptions_.supportsRules && !opt_eventName)
163 throw new Error("Events that support rules require an event name."); 154 throw new Error("Events that support rules require an event name.");
164 155
165 if (this.eventOptions_.supportsFilters) { 156 if (this.eventOptions_.supportsFilters) {
166 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); 157 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this);
167 } else { 158 } else {
168 this.attachmentStrategy_ = new UnfilteredAttachmentStrategy(this); 159 this.attachmentStrategy_ = new UnfilteredAttachmentStrategy(this);
169 } 160 }
170 161
171 // Validate event arguments (the data that is passed to the callbacks) 162 // Validate event arguments (the data that is passed to the callbacks)
172 // if we are in debug. 163 // if we are in debug.
173 if (opt_argSchemas && 164 if (opt_argSchemas && logging.DCHECK_IS_ON()) {
174 chromeHidden.validateCallbacks) {
175
176 this.validateEventArgs_ = function(args) { 165 this.validateEventArgs_ = function(args) {
177 try { 166 try {
178 validate(args, opt_argSchemas); 167 validate(args, opt_argSchemas);
179 } catch (exception) { 168 } catch (exception) {
180 return "Event validation error during " + opt_eventName + " -- " + 169 return "Event validation error during " + opt_eventName + " -- " +
181 exception; 170 exception;
182 } 171 }
183 }; 172 };
184 } else { 173 } else {
185 this.validateEventArgs_ = function() {} 174 this.validateEventArgs_ = function() {}
186 } 175 }
187 }; 176 };
188 177
189
190 chromeHidden.Event = {};
191
192 // callback is a function(args, dispatch). args are the args we receive from 178 // callback is a function(args, dispatch). args are the args we receive from
193 // dispatchEvent(), and dispatch is a function(args) that dispatches args to 179 // dispatchEvent(), and dispatch is a function(args) that dispatches args to
194 // its listeners. 180 // its listeners.
195 chromeHidden.Event.registerArgumentMassager = function(name, callback) { 181 function registerArgumentMassager(name, callback) {
196 if (eventArgumentMassagers[name]) 182 if (eventArgumentMassagers[name])
197 throw new Error("Massager already registered for event: " + name); 183 throw new Error("Massager already registered for event: " + name);
198 eventArgumentMassagers[name] = callback; 184 eventArgumentMassagers[name] = callback;
199 }; 185 }
200 186
201 // Dispatches a named event with the given argument array. The args array is 187 // Dispatches a named event with the given argument array. The args array is
202 // the list of arguments that will be sent to the event callback. 188 // the list of arguments that will be sent to the event callback.
203 chromeHidden.Event.dispatchEvent = function(name, args, filteringInfo) { 189 function dispatchEvent(name, args, filteringInfo) {
204 var listenerIDs = null; 190 var listenerIDs = null;
205 191
206 if (filteringInfo) 192 if (filteringInfo)
207 listenerIDs = MatchAgainstEventFilter(name, filteringInfo); 193 listenerIDs = eventNatives.MatchAgainstEventFilter(name, filteringInfo);
208 194
209 var event = attachedNamedEvents[name]; 195 var event = attachedNamedEvents[name];
210 if (!event) 196 if (!event)
211 return; 197 return;
212 198
213 var dispatchArgs = function(args) { 199 var dispatchArgs = function(args) {
214 var result = event.dispatch_(args, listenerIDs); 200 var result = event.dispatch_(args, listenerIDs);
215 if (result) 201 if (result)
216 DCHECK(!result.validationErrors, result.validationErrors); 202 logging.DCHECK(!result.validationErrors, result.validationErrors);
217 return result; 203 return result;
218 }; 204 };
219 205
220 if (eventArgumentMassagers[name]) 206 if (eventArgumentMassagers[name])
221 eventArgumentMassagers[name](args, dispatchArgs); 207 eventArgumentMassagers[name](args, dispatchArgs);
222 else 208 else
223 dispatchArgs(args); 209 dispatchArgs(args);
224 }; 210 }
225
226 // Test if a named event has any listeners.
227 chromeHidden.Event.hasListener = function(name) {
228 return (attachedNamedEvents[name] &&
229 attachedNamedEvents[name].listeners_.length > 0);
230 };
231 211
232 // Registers a callback to be called when this event is dispatched. 212 // Registers a callback to be called when this event is dispatched.
233 Event.prototype.addListener = function(cb, filters) { 213 Event.prototype.addListener = function(cb, filters) {
234 if (!this.eventOptions_.supportsListeners) 214 if (!this.eventOptions_.supportsListeners)
235 throw new Error("This event does not support listeners."); 215 throw new Error("This event does not support listeners.");
236 if (this.eventOptions_.maxListeners && 216 if (this.eventOptions_.maxListeners &&
237 this.getListenerCount() >= this.eventOptions_.maxListeners) 217 this.getListenerCount() >= this.eventOptions_.maxListeners)
238 throw new Error("Too many listeners for " + this.eventName_); 218 throw new Error("Too many listeners for " + this.eventName_);
239 if (filters) { 219 if (filters) {
240 if (!this.eventOptions_.supportsFilters) 220 if (!this.eventOptions_.supportsFilters)
241 throw new Error("This event does not support filters."); 221 throw new Error("This event does not support filters.");
242 if (filters.url && !(filters.url instanceof Array)) 222 if (filters.url && !(filters.url instanceof Array))
243 throw new Error("filters.url should be an array"); 223 throw new Error("filters.url should be an array");
244 } 224 }
245 var listener = {callback: cb, filters: filters}; 225 var listener = {callback: cb, filters: filters};
246 this.attach_(listener); 226 this.attach_(listener);
247 this.listeners_.push(listener); 227 this.listeners_.push(listener);
248 }; 228 };
249 229
250 Event.prototype.attach_ = function(listener) { 230 Event.prototype.attach_ = function(listener) {
251 this.attachmentStrategy_.onAddedListener(listener); 231 this.attachmentStrategy_.onAddedListener(listener);
252 if (this.listeners_.length == 0) { 232 if (this.listeners_.length == 0) {
253 allAttachedEvents[allAttachedEvents.length] = this; 233 allAttachedEvents[allAttachedEvents.length] = this;
254 if (!this.eventName_) 234 if (!this.eventName_)
255 return; 235 return;
256 236
257 if (attachedNamedEvents[this.eventName_]) { 237 if (attachedNamedEvents[this.eventName_]) {
258 throw new Error("chrome.Event '" + this.eventName_ + 238 throw new Error("Event '" + this.eventName_ + "' is already attached.");
259 "' is already attached.");
260 } 239 }
261 240
262 attachedNamedEvents[this.eventName_] = this; 241 attachedNamedEvents[this.eventName_] = this;
263 } 242 }
264 }; 243 };
265 244
266 // Unregisters a callback. 245 // Unregisters a callback.
267 Event.prototype.removeListener = function(cb) { 246 Event.prototype.removeListener = function(cb) {
268 if (!this.eventOptions_.supportsListeners) 247 if (!this.eventOptions_.supportsListeners)
269 throw new Error("This event does not support listeners."); 248 throw new Error("This event does not support listeners.");
270 var idx = this.findListener_(cb); 249 var idx = this.findListener_(cb);
271 if (idx == -1) { 250 if (idx == -1) {
272 return; 251 return;
273 } 252 }
274 253
275 var removedListener = this.listeners_.splice(idx, 1)[0]; 254 var removedListener = this.listeners_.splice(idx, 1)[0];
276 this.attachmentStrategy_.onRemovedListener(removedListener); 255 this.attachmentStrategy_.onRemovedListener(removedListener);
277 256
278 if (this.listeners_.length == 0) { 257 if (this.listeners_.length == 0) {
279 var i = allAttachedEvents.indexOf(this); 258 var i = allAttachedEvents.indexOf(this);
280 if (i >= 0) 259 if (i >= 0)
281 delete allAttachedEvents[i]; 260 delete allAttachedEvents[i];
282 if (!this.eventName_) 261 if (!this.eventName_)
283 return; 262 return;
284 263
285 if (!attachedNamedEvents[this.eventName_]) { 264 if (!attachedNamedEvents[this.eventName_]) {
286 throw new Error("chrome.Event '" + this.eventName_ + 265 throw new Error("Event '" + this.eventName_ + "' is not attached.");
287 "' is not attached.");
288 } 266 }
289 267
290 delete attachedNamedEvents[this.eventName_]; 268 delete attachedNamedEvents[this.eventName_];
291 } 269 }
292 }; 270 };
293 271
294 // Test if the given callback is registered for this event. 272 // Test if the given callback is registered for this event.
295 Event.prototype.hasListener = function(cb) { 273 Event.prototype.hasListener = function(cb) {
296 if (!this.eventOptions_.supportsListeners) 274 if (!this.eventOptions_.supportsListeners)
297 throw new Error("This event does not support listeners."); 275 throw new Error("This event does not support listeners.");
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 var listeners = 314 var listeners =
337 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); 315 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice();
338 316
339 var results = []; 317 var results = [];
340 for (var i = 0; i < listeners.length; i++) { 318 for (var i = 0; i < listeners.length; i++) {
341 try { 319 try {
342 var result = this.dispatchToListener(listeners[i].callback, args); 320 var result = this.dispatchToListener(listeners[i].callback, args);
343 if (result !== undefined) 321 if (result !== undefined)
344 results.push(result); 322 results.push(result);
345 } catch (e) { 323 } catch (e) {
346 console.error("Error in event handler for '" + this.eventName_ + 324 var errorMessage = "Error in event handler";
347 "': " + e.message + ' ' + e.stack); 325 if (this.eventName_)
326 errorMessage += " for " + this.eventName_;
327 errorMessage += ": " + e;
328 console.error(errorMessage);
348 } 329 }
349 } 330 }
350 if (results.length) 331 if (results.length)
351 return {results: results}; 332 return {results: results};
352 } 333 }
353 334
354 // Can be overridden to support custom dispatching. 335 // Can be overridden to support custom dispatching.
355 Event.prototype.dispatchToListener = function(callback, args) { 336 Event.prototype.dispatchToListener = function(callback, args) {
356 return callback.apply(null, args); 337 return callback.apply(null, args);
357 } 338 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 // Validate conditions and actions against specific schemas of this 373 // Validate conditions and actions against specific schemas of this
393 // event object type. 374 // event object type.
394 // |rules| is an array of JSON objects that follow the Rule type of the 375 // |rules| is an array of JSON objects that follow the Rule type of the
395 // declarative extension APIs. |conditions| is an array of JSON type 376 // declarative extension APIs. |conditions| is an array of JSON type
396 // identifiers that are allowed to occur in the conditions attribute of each 377 // identifiers that are allowed to occur in the conditions attribute of each
397 // rule. Likewise, |actions| is an array of JSON type identifiers that are 378 // rule. Likewise, |actions| is an array of JSON type identifiers that are
398 // allowed to occur in the actions attribute of each rule. 379 // allowed to occur in the actions attribute of each rule.
399 function validateRules(rules, conditions, actions) { 380 function validateRules(rules, conditions, actions) {
400 var conditionsSchema = buildArrayOfChoicesSchema(conditions); 381 var conditionsSchema = buildArrayOfChoicesSchema(conditions);
401 var actionsSchema = buildArrayOfChoicesSchema(actions); 382 var actionsSchema = buildArrayOfChoicesSchema(actions);
402 forEach(rules, function(i, rule) { 383 for (var i = 0; i < rules.length; ++i) {
384 var rule = rules[i];
403 validate([rule.conditions], [conditionsSchema]); 385 validate([rule.conditions], [conditionsSchema]);
404 validate([rule.actions], [actionsSchema]); 386 validate([rule.actions], [actionsSchema]);
405 }) 387 }
406 }; 388 };
407 389
408 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) { 390 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) {
409 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' + 391 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' +
410 'actions in the API specification.'); 392 'actions in the API specification.');
411 } 393 }
412 394
413 validateRules(rules, 395 validateRules(rules,
414 this.eventOptions_.conditions, 396 this.eventOptions_.conditions,
415 this.eventOptions_.actions); 397 this.eventOptions_.actions);
(...skipping 27 matching lines...) Expand all
443 // We remove the first parameter from the validation to give the user more 425 // We remove the first parameter from the validation to give the user more
444 // meaningful error messages. 426 // meaningful error messages.
445 validate([ruleIdentifiers, cb], 427 validate([ruleIdentifiers, cb],
446 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); 428 ruleFunctionSchemas.getRules.parameters.slice().splice(1));
447 429
448 sendRequest("events.getRules", 430 sendRequest("events.getRules",
449 [this.eventName_, ruleIdentifiers, cb], 431 [this.eventName_, ruleIdentifiers, cb],
450 ruleFunctionSchemas.getRules.parameters); 432 ruleFunctionSchemas.getRules.parameters);
451 } 433 }
452 434
453 // Special load events: we don't use the DOM unload because that slows 435 onUnload.addListener(function() {
454 // down tab shutdown. On the other hand, onUnload might not always fire,
455 // since Chrome will terminate renderers on shutdown (SuddenTermination).
456 chromeHidden.onUnload = new Event();
457
458 chromeHidden.dispatchOnUnload = function() {
459 chromeHidden.onUnload.dispatch();
460 chromeHidden.wasUnloaded = true;
461
462 for (var i = 0; i < allAttachedEvents.length; ++i) { 436 for (var i = 0; i < allAttachedEvents.length; ++i) {
463 var event = allAttachedEvents[i]; 437 var event = allAttachedEvents[i];
464 if (event) 438 if (event)
465 event.detach_(); 439 event.detach_();
466 } 440 }
467 }; 441 });
468 442
469 chrome.Event = Event; 443 exports.Event = Event;
444 exports.dispatchEvent = dispatchEvent;
445 exports.parseEventOptions = parseEventOptions;
446 exports.registerArgumentMassager = registerArgumentMassager;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698