OLD | NEW |
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 eventBindingsNatives = requireNative('event_bindings'); | 6 var eventBindingsNatives = requireNative('event_bindings'); |
6 var AttachEvent = eventBindingsNatives.AttachEvent; | 7 var AttachEvent = eventBindingsNatives.AttachEvent; |
7 var DetachEvent = eventBindingsNatives.DetachEvent; | 8 var DetachEvent = eventBindingsNatives.DetachEvent; |
8 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; | 9 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; |
9 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; | 10 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; |
10 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; | 11 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; |
11 var sendRequest = require('sendRequest').sendRequest; | 12 var sendRequest = require('sendRequest').sendRequest; |
12 var utils = require('utils'); | 13 var utils = require('utils'); |
13 var validate = require('schemaUtils').validate; | 14 var validate = require('schemaUtils').validate; |
14 | 15 |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 exception; | 202 exception; |
202 } | 203 } |
203 }; | 204 }; |
204 } else { | 205 } else { |
205 this.validateEventArgs_ = function() {} | 206 this.validateEventArgs_ = function() {} |
206 } | 207 } |
207 }; | 208 }; |
208 | 209 |
209 chromeHidden.Event = {}; | 210 chromeHidden.Event = {}; |
210 | 211 |
211 chromeHidden.Event.registerArgumentMassager = function(name, fn) { | 212 // callback is a function(args, dispatch). args are the args we receive from |
| 213 // dispatchJSON(), and dispatch is a function(args) that dispatches args to |
| 214 // its listeners. |
| 215 chromeHidden.Event.registerArgumentMassager = function(name, callback) { |
212 if (eventArgumentMassagers[name]) | 216 if (eventArgumentMassagers[name]) |
213 throw new Error("Massager already registered for event: " + name); | 217 throw new Error("Massager already registered for event: " + name); |
214 eventArgumentMassagers[name] = fn; | 218 eventArgumentMassagers[name] = callback; |
215 }; | 219 }; |
216 | 220 |
217 // Dispatches a named event with the given JSON array, which is deserialized | 221 // Dispatches a named event with the given JSON array, which is deserialized |
218 // before dispatch. The JSON array is the list of arguments that will be | 222 // before dispatch. The JSON array is the list of arguments that will be |
219 // sent with the event callback. | 223 // sent with the event callback. |
220 chromeHidden.Event.dispatchJSON = function(name, args, filteringInfo) { | 224 chromeHidden.Event.dispatchJSON = function(name, args, filteringInfo) { |
221 var listenerIDs = null; | 225 var listenerIDs = null; |
222 | 226 |
223 if (filteringInfo) { | 227 if (filteringInfo) |
224 listenerIDs = MatchAgainstEventFilter(name, filteringInfo); | 228 listenerIDs = MatchAgainstEventFilter(name, filteringInfo); |
225 } | |
226 if (attachedNamedEvents[name]) { | |
227 if (args) { | |
228 // TODO(asargent): This is an antiquity. Until all callers of | |
229 // dispatchJSON use actual values, this must remain here to catch the | |
230 // cases where a caller has hard-coded a JSON string to pass in. | |
231 if (typeof(args) == "string") { | |
232 args = chromeHidden.JSON.parse(args); | |
233 } | |
234 if (eventArgumentMassagers[name]) | |
235 eventArgumentMassagers[name](args); | |
236 } | |
237 | 229 |
238 var event = attachedNamedEvents[name]; | 230 var event = attachedNamedEvents[name]; |
239 var result; | 231 if (!event) |
240 // TODO(koz): We have to do this differently for unfiltered events (which | 232 return; |
241 // have listenerIDs = null) because some bindings write over | |
242 // event.dispatch (eg: experimental.app.custom_bindings.js) and so expect | |
243 // events to go through it. These places need to be fixed so that they | |
244 // expect a listenerIDs parameter. | |
245 if (listenerIDs) | |
246 result = event.dispatch_(args, listenerIDs); | |
247 else | |
248 result = event.dispatch.apply(event, args); | |
249 if (result && result.validationErrors) | |
250 return result.validationErrors; | |
251 } | |
252 }; | |
253 | 233 |
254 // Dispatches a named event with the given arguments, supplied as an array. | 234 // TODO(asargent): This is an antiquity. Until all callers of |
255 chromeHidden.Event.dispatch = function(name, args) { | 235 // dispatchJSON use actual values, this must remain here to catch the |
256 if (attachedNamedEvents[name]) { | 236 // cases where a caller has hard-coded a JSON string to pass in. |
257 attachedNamedEvents[name].dispatch.apply( | 237 if (typeof(args) == "string") |
258 attachedNamedEvents[name], args); | 238 args = chromeHidden.JSON.parse(args); |
259 } | 239 |
| 240 var dispatchArgs = function(args) { |
| 241 result = event.dispatch_(args, listenerIDs); |
| 242 if (result) |
| 243 DCHECK(!result.validationErrors, result.validationErrors); |
| 244 }; |
| 245 |
| 246 if (eventArgumentMassagers[name]) |
| 247 eventArgumentMassagers[name](args, dispatchArgs); |
| 248 else |
| 249 dispatchArgs(args); |
260 }; | 250 }; |
261 | 251 |
262 // Test if a named event has any listeners. | 252 // Test if a named event has any listeners. |
263 chromeHidden.Event.hasListener = function(name) { | 253 chromeHidden.Event.hasListener = function(name) { |
264 return (attachedNamedEvents[name] && | 254 return (attachedNamedEvents[name] && |
265 attachedNamedEvents[name].listeners_.length > 0); | 255 attachedNamedEvents[name].listeners_.length > 0); |
266 }; | 256 }; |
267 | 257 |
268 // Registers a callback to be called when this event is dispatched. | 258 // Registers a callback to be called when this event is dispatched. |
269 chrome.Event.prototype.addListener = function(cb, filters) { | 259 chrome.Event.prototype.addListener = function(cb, filters) { |
270 if (!this.eventOptions_.supportsListeners) | 260 if (!this.eventOptions_.supportsListeners) |
271 throw new Error("This event does not support listeners."); | 261 throw new Error("This event does not support listeners."); |
| 262 if (this.eventOptions_.maxListeners && |
| 263 this.getListenerCount() >= this.eventOptions_.maxListeners) |
| 264 throw new Error("Too many listeners for " + this.eventName_); |
272 if (filters) { | 265 if (filters) { |
273 if (!this.eventOptions_.supportsFilters) | 266 if (!this.eventOptions_.supportsFilters) |
274 throw new Error("This event does not support filters."); | 267 throw new Error("This event does not support filters."); |
275 if (filters.url && !(filters.url instanceof Array)) | 268 if (filters.url && !(filters.url instanceof Array)) |
276 throw new Error("filters.url should be an array"); | 269 throw new Error("filters.url should be an array"); |
277 } | 270 } |
278 var listener = {callback: cb, filters: filters}; | 271 var listener = {callback: cb, filters: filters}; |
279 this.attach_(listener); | 272 this.attach_(listener); |
280 this.listeners_.push(listener); | 273 this.listeners_.push(listener); |
281 }; | 274 }; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 | 319 |
327 // Test if the given callback is registered for this event. | 320 // Test if the given callback is registered for this event. |
328 chrome.Event.prototype.hasListener = function(cb) { | 321 chrome.Event.prototype.hasListener = function(cb) { |
329 if (!this.eventOptions_.supportsListeners) | 322 if (!this.eventOptions_.supportsListeners) |
330 throw new Error("This event does not support listeners."); | 323 throw new Error("This event does not support listeners."); |
331 return this.findListener_(cb) > -1; | 324 return this.findListener_(cb) > -1; |
332 }; | 325 }; |
333 | 326 |
334 // Test if any callbacks are registered for this event. | 327 // Test if any callbacks are registered for this event. |
335 chrome.Event.prototype.hasListeners = function() { | 328 chrome.Event.prototype.hasListeners = function() { |
| 329 return this.getListenerCount() > 0; |
| 330 }; |
| 331 |
| 332 // Return the number of listeners on this event. |
| 333 chrome.Event.prototype.getListenerCount = function() { |
336 if (!this.eventOptions_.supportsListeners) | 334 if (!this.eventOptions_.supportsListeners) |
337 throw new Error("This event does not support listeners."); | 335 throw new Error("This event does not support listeners."); |
338 return this.listeners_.length > 0; | 336 return this.listeners_.length; |
339 }; | 337 }; |
340 | 338 |
341 // Returns the index of the given callback if registered, or -1 if not | 339 // Returns the index of the given callback if registered, or -1 if not |
342 // found. | 340 // found. |
343 chrome.Event.prototype.findListener_ = function(cb) { | 341 chrome.Event.prototype.findListener_ = function(cb) { |
344 for (var i = 0; i < this.listeners_.length; i++) { | 342 for (var i = 0; i < this.listeners_.length; i++) { |
345 if (this.listeners_[i].callback == cb) { | 343 if (this.listeners_[i].callback == cb) { |
346 return i; | 344 return i; |
347 } | 345 } |
348 } | 346 } |
349 | 347 |
350 return -1; | 348 return -1; |
351 }; | 349 }; |
352 | 350 |
353 chrome.Event.prototype.dispatch_ = function(args, listenerIDs) { | 351 chrome.Event.prototype.dispatch_ = function(args, listenerIDs) { |
354 if (!this.eventOptions_.supportsListeners) | 352 if (!this.eventOptions_.supportsListeners) |
355 throw new Error("This event does not support listeners."); | 353 throw new Error("This event does not support listeners."); |
356 var validationErrors = this.validateEventArgs_(args); | 354 var validationErrors = this.validateEventArgs_(args); |
357 if (validationErrors) { | 355 if (validationErrors) { |
358 console.error(validationErrors); | 356 console.error(validationErrors); |
359 return {validationErrors: validationErrors}; | 357 return {validationErrors: validationErrors}; |
360 } | 358 } |
361 | 359 |
362 var listeners = this.attachmentStrategy_.getListenersByIDs(listenerIDs); | 360 var listeners = this.attachmentStrategy_.getListenersByIDs(listenerIDs); |
363 | 361 |
364 var results = []; | 362 var results = []; |
365 for (var i = 0; i < listeners.length; i++) { | 363 for (var i = 0; i < listeners.length; i++) { |
366 try { | 364 try { |
367 var result = listeners[i].callback.apply(null, args); | 365 var result = this.dispatchToListener(listeners[i].callback, args); |
368 if (result !== undefined) | 366 if (result !== undefined) |
369 results.push(result); | 367 results.push(result); |
370 } catch (e) { | 368 } catch (e) { |
371 console.error("Error in event handler for '" + this.eventName_ + | 369 console.error("Error in event handler for '" + this.eventName_ + |
372 "': " + e.message + ' ' + e.stack); | 370 "': " + e.message + ' ' + e.stack); |
373 } | 371 } |
374 } | 372 } |
375 if (results.length) | 373 if (results.length) |
376 return {results: results}; | 374 return {results: results}; |
377 } | 375 } |
378 | 376 |
| 377 // Can be overridden to support custom dispatching. |
| 378 chrome.Event.prototype.dispatchToListener = function(callback, args) { |
| 379 return callback.apply(null, args); |
| 380 } |
| 381 |
379 // Dispatches this event object to all listeners, passing all supplied | 382 // Dispatches this event object to all listeners, passing all supplied |
380 // arguments to this function each listener. | 383 // arguments to this function each listener. |
381 chrome.Event.prototype.dispatch = function(varargs) { | 384 chrome.Event.prototype.dispatch = function(varargs) { |
382 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); | 385 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); |
383 }; | 386 }; |
384 | 387 |
385 // Detaches this event object from its name. | 388 // Detaches this event object from its name. |
386 chrome.Event.prototype.detach_ = function() { | 389 chrome.Event.prototype.detach_ = function() { |
387 this.attachmentStrategy_.detach(false); | 390 this.attachmentStrategy_.detach(false); |
388 }; | 391 }; |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 if (event) | 476 if (event) |
474 event.detach_(); | 477 event.detach_(); |
475 } | 478 } |
476 }; | 479 }; |
477 | 480 |
478 chromeHidden.dispatchError = function(msg) { | 481 chromeHidden.dispatchError = function(msg) { |
479 console.error(msg); | 482 console.error(msg); |
480 }; | 483 }; |
481 | 484 |
482 exports.Event = chrome.Event; | 485 exports.Event = chrome.Event; |
OLD | NEW |