| 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 DCHECK = requireNative('logging').DCHECK; |
| 6 require('json_schema'); |
| 6 var eventBindingsNatives = requireNative('event_bindings'); | 7 var eventBindingsNatives = requireNative('event_bindings'); |
| 7 var AttachEvent = eventBindingsNatives.AttachEvent; | 8 var AttachEvent = eventBindingsNatives.AttachEvent; |
| 8 var DetachEvent = eventBindingsNatives.DetachEvent; | 9 var DetachEvent = eventBindingsNatives.DetachEvent; |
| 9 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; | 10 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; |
| 10 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; | 11 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; |
| 11 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; | 12 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; |
| 12 var sendRequest = require('sendRequest').sendRequest; | 13 var sendRequest = require('sendRequest').sendRequest; |
| 13 var utils = require('utils'); | 14 var utils = require('utils'); |
| 14 var validate = require('schemaUtils').validate; | 15 var validate = require('schemaUtils').validate; |
| 15 | 16 |
| 16 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); | 17 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); |
| 17 var GetExtensionAPIDefinition = | 18 var chrome = requireNative('chrome').GetChrome(); |
| 18 requireNative('apiDefinitions').GetExtensionAPIDefinition; | 19 var schemaRegistry = requireNative('schema_registry'); |
| 19 | 20 |
| 20 // Schemas for the rule-style functions on the events API that | 21 // Schemas for the rule-style functions on the events API that |
| 21 // only need to be generated occasionally, so populate them lazily. | 22 // only need to be generated occasionally, so populate them lazily. |
| 22 var ruleFunctionSchemas = { | 23 var ruleFunctionSchemas = { |
| 23 // These values are set lazily: | 24 // These values are set lazily: |
| 24 // addRules: {}, | 25 // addRules: {}, |
| 25 // getRules: {}, | 26 // getRules: {}, |
| 26 // removeRules: {} | 27 // removeRules: {} |
| 27 }; | 28 }; |
| 28 | 29 |
| 29 // This function ensures that |ruleFunctionSchemas| is populated. | 30 // This function ensures that |ruleFunctionSchemas| is populated. |
| 30 function ensureRuleSchemasLoaded() { | 31 function ensureRuleSchemasLoaded() { |
| 31 if (ruleFunctionSchemas.addRules) | 32 if (ruleFunctionSchemas.addRules) |
| 32 return; | 33 return; |
| 33 var eventsSchema = GetExtensionAPIDefinition("events")[0]; | 34 var eventsSchema = schemaRegistry.GetSchema("events"); |
| 34 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); | 35 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); |
| 35 | 36 |
| 36 ruleFunctionSchemas.addRules = | 37 ruleFunctionSchemas.addRules = |
| 37 utils.lookup(eventType.functions, 'name', 'addRules'); | 38 utils.lookup(eventType.functions, 'name', 'addRules'); |
| 38 ruleFunctionSchemas.getRules = | 39 ruleFunctionSchemas.getRules = |
| 39 utils.lookup(eventType.functions, 'name', 'getRules'); | 40 utils.lookup(eventType.functions, 'name', 'getRules'); |
| 40 ruleFunctionSchemas.removeRules = | 41 ruleFunctionSchemas.removeRules = |
| 41 utils.lookup(eventType.functions, 'name', 'removeRules'); | 42 utils.lookup(eventType.functions, 'name', 'removeRules'); |
| 42 } | 43 } |
| 43 | 44 |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 // opt_eventName is required for events that support rules. | 183 // opt_eventName is required for events that support rules. |
| 183 // | 184 // |
| 184 // Example: | 185 // Example: |
| 185 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); | 186 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); |
| 186 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); | 187 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| 187 // chromeHidden.Event.dispatch("tab-changed", "hi"); | 188 // chromeHidden.Event.dispatch("tab-changed", "hi"); |
| 188 // will result in an alert dialog that says 'hi'. | 189 // will result in an alert dialog that says 'hi'. |
| 189 // | 190 // |
| 190 // If opt_eventOptions exists, it is a dictionary that contains the boolean | 191 // If opt_eventOptions exists, it is a dictionary that contains the boolean |
| 191 // entries "supportsListeners" and "supportsRules". | 192 // entries "supportsListeners" and "supportsRules". |
| 192 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { | 193 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { |
| 193 this.eventName_ = opt_eventName; | 194 this.eventName_ = opt_eventName; |
| 194 this.listeners_ = []; | 195 this.listeners_ = []; |
| 195 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); | 196 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); |
| 196 | 197 |
| 197 if (this.eventOptions_.supportsRules && !opt_eventName) | 198 if (this.eventOptions_.supportsRules && !opt_eventName) |
| 198 throw new Error("Events that support rules require an event name."); | 199 throw new Error("Events that support rules require an event name."); |
| 199 | 200 |
| 200 if (this.eventOptions_.supportsFilters) { | 201 if (this.eventOptions_.supportsFilters) { |
| 201 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); | 202 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); |
| 202 } else { | 203 } else { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 dispatchArgs(args); | 258 dispatchArgs(args); |
| 258 }; | 259 }; |
| 259 | 260 |
| 260 // Test if a named event has any listeners. | 261 // Test if a named event has any listeners. |
| 261 chromeHidden.Event.hasListener = function(name) { | 262 chromeHidden.Event.hasListener = function(name) { |
| 262 return (attachedNamedEvents[name] && | 263 return (attachedNamedEvents[name] && |
| 263 attachedNamedEvents[name].listeners_.length > 0); | 264 attachedNamedEvents[name].listeners_.length > 0); |
| 264 }; | 265 }; |
| 265 | 266 |
| 266 // Registers a callback to be called when this event is dispatched. | 267 // Registers a callback to be called when this event is dispatched. |
| 267 chrome.Event.prototype.addListener = function(cb, filters) { | 268 Event.prototype.addListener = function(cb, filters) { |
| 268 if (!this.eventOptions_.supportsListeners) | 269 if (!this.eventOptions_.supportsListeners) |
| 269 throw new Error("This event does not support listeners."); | 270 throw new Error("This event does not support listeners."); |
| 270 if (this.eventOptions_.maxListeners && | 271 if (this.eventOptions_.maxListeners && |
| 271 this.getListenerCount() >= this.eventOptions_.maxListeners) | 272 this.getListenerCount() >= this.eventOptions_.maxListeners) |
| 272 throw new Error("Too many listeners for " + this.eventName_); | 273 throw new Error("Too many listeners for " + this.eventName_); |
| 273 if (filters) { | 274 if (filters) { |
| 274 if (!this.eventOptions_.supportsFilters) | 275 if (!this.eventOptions_.supportsFilters) |
| 275 throw new Error("This event does not support filters."); | 276 throw new Error("This event does not support filters."); |
| 276 if (filters.url && !(filters.url instanceof Array)) | 277 if (filters.url && !(filters.url instanceof Array)) |
| 277 throw new Error("filters.url should be an array"); | 278 throw new Error("filters.url should be an array"); |
| 278 } | 279 } |
| 279 var listener = {callback: cb, filters: filters}; | 280 var listener = {callback: cb, filters: filters}; |
| 280 this.attach_(listener); | 281 this.attach_(listener); |
| 281 this.listeners_.push(listener); | 282 this.listeners_.push(listener); |
| 282 }; | 283 }; |
| 283 | 284 |
| 284 chrome.Event.prototype.attach_ = function(listener) { | 285 Event.prototype.attach_ = function(listener) { |
| 285 this.attachmentStrategy_.onAddedListener(listener); | 286 this.attachmentStrategy_.onAddedListener(listener); |
| 286 if (this.listeners_.length == 0) { | 287 if (this.listeners_.length == 0) { |
| 287 allAttachedEvents[allAttachedEvents.length] = this; | 288 allAttachedEvents[allAttachedEvents.length] = this; |
| 288 if (!this.eventName_) | 289 if (!this.eventName_) |
| 289 return; | 290 return; |
| 290 | 291 |
| 291 if (attachedNamedEvents[this.eventName_]) { | 292 if (attachedNamedEvents[this.eventName_]) { |
| 292 throw new Error("chrome.Event '" + this.eventName_ + | 293 throw new Error("chrome.Event '" + this.eventName_ + |
| 293 "' is already attached."); | 294 "' is already attached."); |
| 294 } | 295 } |
| 295 | 296 |
| 296 attachedNamedEvents[this.eventName_] = this; | 297 attachedNamedEvents[this.eventName_] = this; |
| 297 } | 298 } |
| 298 }; | 299 }; |
| 299 | 300 |
| 300 // Unregisters a callback. | 301 // Unregisters a callback. |
| 301 chrome.Event.prototype.removeListener = function(cb) { | 302 Event.prototype.removeListener = function(cb) { |
| 302 if (!this.eventOptions_.supportsListeners) | 303 if (!this.eventOptions_.supportsListeners) |
| 303 throw new Error("This event does not support listeners."); | 304 throw new Error("This event does not support listeners."); |
| 304 var idx = this.findListener_(cb); | 305 var idx = this.findListener_(cb); |
| 305 if (idx == -1) { | 306 if (idx == -1) { |
| 306 return; | 307 return; |
| 307 } | 308 } |
| 308 | 309 |
| 309 var removedListener = this.listeners_.splice(idx, 1)[0]; | 310 var removedListener = this.listeners_.splice(idx, 1)[0]; |
| 310 this.attachmentStrategy_.onRemovedListener(removedListener); | 311 this.attachmentStrategy_.onRemovedListener(removedListener); |
| 311 | 312 |
| 312 if (this.listeners_.length == 0) { | 313 if (this.listeners_.length == 0) { |
| 313 var i = allAttachedEvents.indexOf(this); | 314 var i = allAttachedEvents.indexOf(this); |
| 314 if (i >= 0) | 315 if (i >= 0) |
| 315 delete allAttachedEvents[i]; | 316 delete allAttachedEvents[i]; |
| 316 if (!this.eventName_) | 317 if (!this.eventName_) |
| 317 return; | 318 return; |
| 318 | 319 |
| 319 if (!attachedNamedEvents[this.eventName_]) { | 320 if (!attachedNamedEvents[this.eventName_]) { |
| 320 throw new Error("chrome.Event '" + this.eventName_ + | 321 throw new Error("chrome.Event '" + this.eventName_ + |
| 321 "' is not attached."); | 322 "' is not attached."); |
| 322 } | 323 } |
| 323 | 324 |
| 324 delete attachedNamedEvents[this.eventName_]; | 325 delete attachedNamedEvents[this.eventName_]; |
| 325 } | 326 } |
| 326 }; | 327 }; |
| 327 | 328 |
| 328 // Test if the given callback is registered for this event. | 329 // Test if the given callback is registered for this event. |
| 329 chrome.Event.prototype.hasListener = function(cb) { | 330 Event.prototype.hasListener = function(cb) { |
| 330 if (!this.eventOptions_.supportsListeners) | 331 if (!this.eventOptions_.supportsListeners) |
| 331 throw new Error("This event does not support listeners."); | 332 throw new Error("This event does not support listeners."); |
| 332 return this.findListener_(cb) > -1; | 333 return this.findListener_(cb) > -1; |
| 333 }; | 334 }; |
| 334 | 335 |
| 335 // Test if any callbacks are registered for this event. | 336 // Test if any callbacks are registered for this event. |
| 336 chrome.Event.prototype.hasListeners = function() { | 337 Event.prototype.hasListeners = function() { |
| 337 return this.getListenerCount() > 0; | 338 return this.getListenerCount() > 0; |
| 338 }; | 339 }; |
| 339 | 340 |
| 340 // Return the number of listeners on this event. | 341 // Return the number of listeners on this event. |
| 341 chrome.Event.prototype.getListenerCount = function() { | 342 Event.prototype.getListenerCount = function() { |
| 342 if (!this.eventOptions_.supportsListeners) | 343 if (!this.eventOptions_.supportsListeners) |
| 343 throw new Error("This event does not support listeners."); | 344 throw new Error("This event does not support listeners."); |
| 344 return this.listeners_.length; | 345 return this.listeners_.length; |
| 345 }; | 346 }; |
| 346 | 347 |
| 347 // Returns the index of the given callback if registered, or -1 if not | 348 // Returns the index of the given callback if registered, or -1 if not |
| 348 // found. | 349 // found. |
| 349 chrome.Event.prototype.findListener_ = function(cb) { | 350 Event.prototype.findListener_ = function(cb) { |
| 350 for (var i = 0; i < this.listeners_.length; i++) { | 351 for (var i = 0; i < this.listeners_.length; i++) { |
| 351 if (this.listeners_[i].callback == cb) { | 352 if (this.listeners_[i].callback == cb) { |
| 352 return i; | 353 return i; |
| 353 } | 354 } |
| 354 } | 355 } |
| 355 | 356 |
| 356 return -1; | 357 return -1; |
| 357 }; | 358 }; |
| 358 | 359 |
| 359 chrome.Event.prototype.dispatch_ = function(args, listenerIDs) { | 360 Event.prototype.dispatch_ = function(args, listenerIDs) { |
| 360 if (!this.eventOptions_.supportsListeners) | 361 if (!this.eventOptions_.supportsListeners) |
| 361 throw new Error("This event does not support listeners."); | 362 throw new Error("This event does not support listeners."); |
| 362 var validationErrors = this.validateEventArgs_(args); | 363 var validationErrors = this.validateEventArgs_(args); |
| 363 if (validationErrors) { | 364 if (validationErrors) { |
| 364 console.error(validationErrors); | 365 console.error(validationErrors); |
| 365 return {validationErrors: validationErrors}; | 366 return {validationErrors: validationErrors}; |
| 366 } | 367 } |
| 367 | 368 |
| 368 // Make a copy of the listeners in case the listener list is modified | 369 // Make a copy of the listeners in case the listener list is modified |
| 369 // while dispatching the event. | 370 // while dispatching the event. |
| 370 var listeners = | 371 var listeners = |
| 371 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); | 372 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); |
| 372 | 373 |
| 373 var results = []; | 374 var results = []; |
| 374 for (var i = 0; i < listeners.length; i++) { | 375 for (var i = 0; i < listeners.length; i++) { |
| 375 try { | 376 try { |
| 376 var result = this.dispatchToListener(listeners[i].callback, args); | 377 var result = this.dispatchToListener(listeners[i].callback, args); |
| 377 if (result !== undefined) | 378 if (result !== undefined) |
| 378 results.push(result); | 379 results.push(result); |
| 379 } catch (e) { | 380 } catch (e) { |
| 380 console.error("Error in event handler for '" + this.eventName_ + | 381 console.error("Error in event handler for '" + this.eventName_ + |
| 381 "': " + e.message + ' ' + e.stack); | 382 "': " + e.message + ' ' + e.stack); |
| 382 } | 383 } |
| 383 } | 384 } |
| 384 if (results.length) | 385 if (results.length) |
| 385 return {results: results}; | 386 return {results: results}; |
| 386 } | 387 } |
| 387 | 388 |
| 388 // Can be overridden to support custom dispatching. | 389 // Can be overridden to support custom dispatching. |
| 389 chrome.Event.prototype.dispatchToListener = function(callback, args) { | 390 Event.prototype.dispatchToListener = function(callback, args) { |
| 390 return callback.apply(null, args); | 391 return callback.apply(null, args); |
| 391 } | 392 } |
| 392 | 393 |
| 393 // Dispatches this event object to all listeners, passing all supplied | 394 // Dispatches this event object to all listeners, passing all supplied |
| 394 // arguments to this function each listener. | 395 // arguments to this function each listener. |
| 395 chrome.Event.prototype.dispatch = function(varargs) { | 396 Event.prototype.dispatch = function(varargs) { |
| 396 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); | 397 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); |
| 397 }; | 398 }; |
| 398 | 399 |
| 399 // Detaches this event object from its name. | 400 // Detaches this event object from its name. |
| 400 chrome.Event.prototype.detach_ = function() { | 401 Event.prototype.detach_ = function() { |
| 401 this.attachmentStrategy_.detach(false); | 402 this.attachmentStrategy_.detach(false); |
| 402 }; | 403 }; |
| 403 | 404 |
| 404 chrome.Event.prototype.destroy_ = function() { | 405 Event.prototype.destroy_ = function() { |
| 405 this.listeners_ = []; | 406 this.listeners_ = []; |
| 406 this.validateEventArgs_ = []; | 407 this.validateEventArgs_ = []; |
| 407 this.detach_(false); | 408 this.detach_(false); |
| 408 }; | 409 }; |
| 409 | 410 |
| 410 chrome.Event.prototype.addRules = function(rules, opt_cb) { | 411 Event.prototype.addRules = function(rules, opt_cb) { |
| 411 if (!this.eventOptions_.supportsRules) | 412 if (!this.eventOptions_.supportsRules) |
| 412 throw new Error("This event does not support rules."); | 413 throw new Error("This event does not support rules."); |
| 413 | 414 |
| 414 // Takes a list of JSON datatype identifiers and returns a schema fragment | 415 // Takes a list of JSON datatype identifiers and returns a schema fragment |
| 415 // that verifies that a JSON object corresponds to an array of only these | 416 // that verifies that a JSON object corresponds to an array of only these |
| 416 // data types. | 417 // data types. |
| 417 function buildArrayOfChoicesSchema(typesList) { | 418 function buildArrayOfChoicesSchema(typesList) { |
| 418 return { | 419 return { |
| 419 'type': 'array', | 420 'type': 'array', |
| 420 'items': { | 421 'items': { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 450 | 451 |
| 451 ensureRuleSchemasLoaded(); | 452 ensureRuleSchemasLoaded(); |
| 452 // We remove the first parameter from the validation to give the user more | 453 // We remove the first parameter from the validation to give the user more |
| 453 // meaningful error messages. | 454 // meaningful error messages. |
| 454 validate([rules, opt_cb], | 455 validate([rules, opt_cb], |
| 455 ruleFunctionSchemas.addRules.parameters.slice().splice(1)); | 456 ruleFunctionSchemas.addRules.parameters.slice().splice(1)); |
| 456 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], | 457 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], |
| 457 ruleFunctionSchemas.addRules.parameters); | 458 ruleFunctionSchemas.addRules.parameters); |
| 458 } | 459 } |
| 459 | 460 |
| 460 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { | 461 Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { |
| 461 if (!this.eventOptions_.supportsRules) | 462 if (!this.eventOptions_.supportsRules) |
| 462 throw new Error("This event does not support rules."); | 463 throw new Error("This event does not support rules."); |
| 463 ensureRuleSchemasLoaded(); | 464 ensureRuleSchemasLoaded(); |
| 464 // We remove the first parameter from the validation to give the user more | 465 // We remove the first parameter from the validation to give the user more |
| 465 // meaningful error messages. | 466 // meaningful error messages. |
| 466 validate([ruleIdentifiers, opt_cb], | 467 validate([ruleIdentifiers, opt_cb], |
| 467 ruleFunctionSchemas.removeRules.parameters.slice().splice(1)); | 468 ruleFunctionSchemas.removeRules.parameters.slice().splice(1)); |
| 468 sendRequest("events.removeRules", | 469 sendRequest("events.removeRules", |
| 469 [this.eventName_, ruleIdentifiers, opt_cb], | 470 [this.eventName_, ruleIdentifiers, opt_cb], |
| 470 ruleFunctionSchemas.removeRules.parameters); | 471 ruleFunctionSchemas.removeRules.parameters); |
| 471 } | 472 } |
| 472 | 473 |
| 473 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { | 474 Event.prototype.getRules = function(ruleIdentifiers, cb) { |
| 474 if (!this.eventOptions_.supportsRules) | 475 if (!this.eventOptions_.supportsRules) |
| 475 throw new Error("This event does not support rules."); | 476 throw new Error("This event does not support rules."); |
| 476 ensureRuleSchemasLoaded(); | 477 ensureRuleSchemasLoaded(); |
| 477 // We remove the first parameter from the validation to give the user more | 478 // We remove the first parameter from the validation to give the user more |
| 478 // meaningful error messages. | 479 // meaningful error messages. |
| 479 validate([ruleIdentifiers, cb], | 480 validate([ruleIdentifiers, cb], |
| 480 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); | 481 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); |
| 481 | 482 |
| 482 sendRequest("events.getRules", | 483 sendRequest("events.getRules", |
| 483 [this.eventName_, ruleIdentifiers, cb], | 484 [this.eventName_, ruleIdentifiers, cb], |
| 484 ruleFunctionSchemas.getRules.parameters); | 485 ruleFunctionSchemas.getRules.parameters); |
| 485 } | 486 } |
| 486 | 487 |
| 487 // Special load events: we don't use the DOM unload because that slows | 488 // Special load events: we don't use the DOM unload because that slows |
| 488 // down tab shutdown. On the other hand, onUnload might not always fire, | 489 // down tab shutdown. On the other hand, onUnload might not always fire, |
| 489 // since Chrome will terminate renderers on shutdown (SuddenTermination). | 490 // since Chrome will terminate renderers on shutdown (SuddenTermination). |
| 490 chromeHidden.onLoad = new chrome.Event(); | 491 chromeHidden.onLoad = new Event(); |
| 491 chromeHidden.onUnload = new chrome.Event(); | 492 chromeHidden.onUnload = new Event(); |
| 492 | 493 |
| 493 chromeHidden.dispatchOnLoad = | 494 chromeHidden.dispatchOnLoad = |
| 494 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); | 495 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); |
| 495 | 496 |
| 496 chromeHidden.dispatchOnUnload = function() { | 497 chromeHidden.dispatchOnUnload = function() { |
| 497 chromeHidden.onUnload.dispatch(); | 498 chromeHidden.onUnload.dispatch(); |
| 498 for (var i = 0; i < allAttachedEvents.length; ++i) { | 499 for (var i = 0; i < allAttachedEvents.length; ++i) { |
| 499 var event = allAttachedEvents[i]; | 500 var event = allAttachedEvents[i]; |
| 500 if (event) | 501 if (event) |
| 501 event.detach_(); | 502 event.detach_(); |
| 502 } | 503 } |
| 503 }; | 504 }; |
| 504 | 505 |
| 505 chromeHidden.dispatchError = function(msg) { | 506 chromeHidden.dispatchError = function(msg) { |
| 506 console.error(msg); | 507 console.error(msg); |
| 507 }; | 508 }; |
| 508 | 509 |
| 509 exports.Event = chrome.Event; | 510 chrome.Event = Event; |
| OLD | NEW |