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