| 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 // TODO(cduvall/kalman): json_schema shouldn't put things on chromeHidden. | |
| 7 require('json_schema'); | |
| 8 var eventBindingsNatives = requireNative('event_bindings'); | 6 var eventBindingsNatives = requireNative('event_bindings'); |
| 9 var AttachEvent = eventBindingsNatives.AttachEvent; | 7 var AttachEvent = eventBindingsNatives.AttachEvent; |
| 10 var DetachEvent = eventBindingsNatives.DetachEvent; | 8 var DetachEvent = eventBindingsNatives.DetachEvent; |
| 11 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; | 9 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; |
| 12 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; | 10 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; |
| 13 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; | 11 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; |
| 14 var sendRequest = require('sendRequest').sendRequest; | 12 var sendRequest = require('sendRequest').sendRequest; |
| 15 var utils = require('utils'); | 13 var utils = require('utils'); |
| 16 var validate = require('schemaUtils').validate; | 14 var validate = require('schemaUtils').validate; |
| 17 | 15 |
| 18 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); | 16 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); |
| 19 var chrome = requireNative('chrome').GetChrome(); | 17 var GetExtensionAPIDefinition = |
| 20 var schemaRegistry = requireNative('schema_registry'); | 18 requireNative('apiDefinitions').GetExtensionAPIDefinition; |
| 21 | 19 |
| 22 // Schemas for the rule-style functions on the events API that | 20 // Schemas for the rule-style functions on the events API that |
| 23 // only need to be generated occasionally, so populate them lazily. | 21 // only need to be generated occasionally, so populate them lazily. |
| 24 var ruleFunctionSchemas = { | 22 var ruleFunctionSchemas = { |
| 25 // These values are set lazily: | 23 // These values are set lazily: |
| 26 // addRules: {}, | 24 // addRules: {}, |
| 27 // getRules: {}, | 25 // getRules: {}, |
| 28 // removeRules: {} | 26 // removeRules: {} |
| 29 }; | 27 }; |
| 30 | 28 |
| 31 // This function ensures that |ruleFunctionSchemas| is populated. | 29 // This function ensures that |ruleFunctionSchemas| is populated. |
| 32 function ensureRuleSchemasLoaded() { | 30 function ensureRuleSchemasLoaded() { |
| 33 if (ruleFunctionSchemas.addRules) | 31 if (ruleFunctionSchemas.addRules) |
| 34 return; | 32 return; |
| 35 var eventsSchema = schemaRegistry.GetSchema("events"); | 33 var eventsSchema = GetExtensionAPIDefinition("events")[0]; |
| 36 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); | 34 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); |
| 37 | 35 |
| 38 ruleFunctionSchemas.addRules = | 36 ruleFunctionSchemas.addRules = |
| 39 utils.lookup(eventType.functions, 'name', 'addRules'); | 37 utils.lookup(eventType.functions, 'name', 'addRules'); |
| 40 ruleFunctionSchemas.getRules = | 38 ruleFunctionSchemas.getRules = |
| 41 utils.lookup(eventType.functions, 'name', 'getRules'); | 39 utils.lookup(eventType.functions, 'name', 'getRules'); |
| 42 ruleFunctionSchemas.removeRules = | 40 ruleFunctionSchemas.removeRules = |
| 43 utils.lookup(eventType.functions, 'name', 'removeRules'); | 41 utils.lookup(eventType.functions, 'name', 'removeRules'); |
| 44 } | 42 } |
| 45 | 43 |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 // opt_eventName is required for events that support rules. | 144 // opt_eventName is required for events that support rules. |
| 147 // | 145 // |
| 148 // Example: | 146 // Example: |
| 149 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); | 147 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); |
| 150 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); | 148 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| 151 // chromeHidden.Event.dispatch("tab-changed", "hi"); | 149 // chromeHidden.Event.dispatch("tab-changed", "hi"); |
| 152 // will result in an alert dialog that says 'hi'. | 150 // will result in an alert dialog that says 'hi'. |
| 153 // | 151 // |
| 154 // If opt_eventOptions exists, it is a dictionary that contains the boolean | 152 // If opt_eventOptions exists, it is a dictionary that contains the boolean |
| 155 // entries "supportsListeners" and "supportsRules". | 153 // entries "supportsListeners" and "supportsRules". |
| 156 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { | 154 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { |
| 157 this.eventName_ = opt_eventName; | 155 this.eventName_ = opt_eventName; |
| 158 this.listeners_ = []; | 156 this.listeners_ = []; |
| 159 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); | 157 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); |
| 160 | 158 |
| 161 if (this.eventOptions_.supportsRules && !opt_eventName) | 159 if (this.eventOptions_.supportsRules && !opt_eventName) |
| 162 throw new Error("Events that support rules require an event name."); | 160 throw new Error("Events that support rules require an event name."); |
| 163 | 161 |
| 164 if (this.eventOptions_.supportsFilters) { | 162 if (this.eventOptions_.supportsFilters) { |
| 165 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); | 163 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); |
| 166 } else { | 164 } else { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 dispatchArgs(args); | 219 dispatchArgs(args); |
| 222 }; | 220 }; |
| 223 | 221 |
| 224 // Test if a named event has any listeners. | 222 // Test if a named event has any listeners. |
| 225 chromeHidden.Event.hasListener = function(name) { | 223 chromeHidden.Event.hasListener = function(name) { |
| 226 return (attachedNamedEvents[name] && | 224 return (attachedNamedEvents[name] && |
| 227 attachedNamedEvents[name].listeners_.length > 0); | 225 attachedNamedEvents[name].listeners_.length > 0); |
| 228 }; | 226 }; |
| 229 | 227 |
| 230 // Registers a callback to be called when this event is dispatched. | 228 // Registers a callback to be called when this event is dispatched. |
| 231 Event.prototype.addListener = function(cb, filters) { | 229 chrome.Event.prototype.addListener = function(cb, filters) { |
| 232 if (!this.eventOptions_.supportsListeners) | 230 if (!this.eventOptions_.supportsListeners) |
| 233 throw new Error("This event does not support listeners."); | 231 throw new Error("This event does not support listeners."); |
| 234 if (this.eventOptions_.maxListeners && | 232 if (this.eventOptions_.maxListeners && |
| 235 this.getListenerCount() >= this.eventOptions_.maxListeners) | 233 this.getListenerCount() >= this.eventOptions_.maxListeners) |
| 236 throw new Error("Too many listeners for " + this.eventName_); | 234 throw new Error("Too many listeners for " + this.eventName_); |
| 237 if (filters) { | 235 if (filters) { |
| 238 if (!this.eventOptions_.supportsFilters) | 236 if (!this.eventOptions_.supportsFilters) |
| 239 throw new Error("This event does not support filters."); | 237 throw new Error("This event does not support filters."); |
| 240 if (filters.url && !(filters.url instanceof Array)) | 238 if (filters.url && !(filters.url instanceof Array)) |
| 241 throw new Error("filters.url should be an array"); | 239 throw new Error("filters.url should be an array"); |
| 242 } | 240 } |
| 243 var listener = {callback: cb, filters: filters}; | 241 var listener = {callback: cb, filters: filters}; |
| 244 this.attach_(listener); | 242 this.attach_(listener); |
| 245 this.listeners_.push(listener); | 243 this.listeners_.push(listener); |
| 246 }; | 244 }; |
| 247 | 245 |
| 248 Event.prototype.attach_ = function(listener) { | 246 chrome.Event.prototype.attach_ = function(listener) { |
| 249 this.attachmentStrategy_.onAddedListener(listener); | 247 this.attachmentStrategy_.onAddedListener(listener); |
| 250 if (this.listeners_.length == 0) { | 248 if (this.listeners_.length == 0) { |
| 251 allAttachedEvents[allAttachedEvents.length] = this; | 249 allAttachedEvents[allAttachedEvents.length] = this; |
| 252 if (!this.eventName_) | 250 if (!this.eventName_) |
| 253 return; | 251 return; |
| 254 | 252 |
| 255 if (attachedNamedEvents[this.eventName_]) { | 253 if (attachedNamedEvents[this.eventName_]) { |
| 256 throw new Error("chrome.Event '" + this.eventName_ + | 254 throw new Error("chrome.Event '" + this.eventName_ + |
| 257 "' is already attached."); | 255 "' is already attached."); |
| 258 } | 256 } |
| 259 | 257 |
| 260 attachedNamedEvents[this.eventName_] = this; | 258 attachedNamedEvents[this.eventName_] = this; |
| 261 } | 259 } |
| 262 }; | 260 }; |
| 263 | 261 |
| 264 // Unregisters a callback. | 262 // Unregisters a callback. |
| 265 Event.prototype.removeListener = function(cb) { | 263 chrome.Event.prototype.removeListener = function(cb) { |
| 266 if (!this.eventOptions_.supportsListeners) | 264 if (!this.eventOptions_.supportsListeners) |
| 267 throw new Error("This event does not support listeners."); | 265 throw new Error("This event does not support listeners."); |
| 268 var idx = this.findListener_(cb); | 266 var idx = this.findListener_(cb); |
| 269 if (idx == -1) { | 267 if (idx == -1) { |
| 270 return; | 268 return; |
| 271 } | 269 } |
| 272 | 270 |
| 273 var removedListener = this.listeners_.splice(idx, 1)[0]; | 271 var removedListener = this.listeners_.splice(idx, 1)[0]; |
| 274 this.attachmentStrategy_.onRemovedListener(removedListener); | 272 this.attachmentStrategy_.onRemovedListener(removedListener); |
| 275 | 273 |
| 276 if (this.listeners_.length == 0) { | 274 if (this.listeners_.length == 0) { |
| 277 var i = allAttachedEvents.indexOf(this); | 275 var i = allAttachedEvents.indexOf(this); |
| 278 if (i >= 0) | 276 if (i >= 0) |
| 279 delete allAttachedEvents[i]; | 277 delete allAttachedEvents[i]; |
| 280 if (!this.eventName_) | 278 if (!this.eventName_) |
| 281 return; | 279 return; |
| 282 | 280 |
| 283 if (!attachedNamedEvents[this.eventName_]) { | 281 if (!attachedNamedEvents[this.eventName_]) { |
| 284 throw new Error("chrome.Event '" + this.eventName_ + | 282 throw new Error("chrome.Event '" + this.eventName_ + |
| 285 "' is not attached."); | 283 "' is not attached."); |
| 286 } | 284 } |
| 287 | 285 |
| 288 delete attachedNamedEvents[this.eventName_]; | 286 delete attachedNamedEvents[this.eventName_]; |
| 289 } | 287 } |
| 290 }; | 288 }; |
| 291 | 289 |
| 292 // Test if the given callback is registered for this event. | 290 // Test if the given callback is registered for this event. |
| 293 Event.prototype.hasListener = function(cb) { | 291 chrome.Event.prototype.hasListener = function(cb) { |
| 294 if (!this.eventOptions_.supportsListeners) | 292 if (!this.eventOptions_.supportsListeners) |
| 295 throw new Error("This event does not support listeners."); | 293 throw new Error("This event does not support listeners."); |
| 296 return this.findListener_(cb) > -1; | 294 return this.findListener_(cb) > -1; |
| 297 }; | 295 }; |
| 298 | 296 |
| 299 // Test if any callbacks are registered for this event. | 297 // Test if any callbacks are registered for this event. |
| 300 Event.prototype.hasListeners = function() { | 298 chrome.Event.prototype.hasListeners = function() { |
| 301 return this.getListenerCount() > 0; | 299 return this.getListenerCount() > 0; |
| 302 }; | 300 }; |
| 303 | 301 |
| 304 // Return the number of listeners on this event. | 302 // Return the number of listeners on this event. |
| 305 Event.prototype.getListenerCount = function() { | 303 chrome.Event.prototype.getListenerCount = function() { |
| 306 if (!this.eventOptions_.supportsListeners) | 304 if (!this.eventOptions_.supportsListeners) |
| 307 throw new Error("This event does not support listeners."); | 305 throw new Error("This event does not support listeners."); |
| 308 return this.listeners_.length; | 306 return this.listeners_.length; |
| 309 }; | 307 }; |
| 310 | 308 |
| 311 // Returns the index of the given callback if registered, or -1 if not | 309 // Returns the index of the given callback if registered, or -1 if not |
| 312 // found. | 310 // found. |
| 313 Event.prototype.findListener_ = function(cb) { | 311 chrome.Event.prototype.findListener_ = function(cb) { |
| 314 for (var i = 0; i < this.listeners_.length; i++) { | 312 for (var i = 0; i < this.listeners_.length; i++) { |
| 315 if (this.listeners_[i].callback == cb) { | 313 if (this.listeners_[i].callback == cb) { |
| 316 return i; | 314 return i; |
| 317 } | 315 } |
| 318 } | 316 } |
| 319 | 317 |
| 320 return -1; | 318 return -1; |
| 321 }; | 319 }; |
| 322 | 320 |
| 323 Event.prototype.dispatch_ = function(args, listenerIDs) { | 321 chrome.Event.prototype.dispatch_ = function(args, listenerIDs) { |
| 324 if (!this.eventOptions_.supportsListeners) | 322 if (!this.eventOptions_.supportsListeners) |
| 325 throw new Error("This event does not support listeners."); | 323 throw new Error("This event does not support listeners."); |
| 326 var validationErrors = this.validateEventArgs_(args); | 324 var validationErrors = this.validateEventArgs_(args); |
| 327 if (validationErrors) { | 325 if (validationErrors) { |
| 328 console.error(validationErrors); | 326 console.error(validationErrors); |
| 329 return {validationErrors: validationErrors}; | 327 return {validationErrors: validationErrors}; |
| 330 } | 328 } |
| 331 | 329 |
| 332 // Make a copy of the listeners in case the listener list is modified | 330 // Make a copy of the listeners in case the listener list is modified |
| 333 // while dispatching the event. | 331 // while dispatching the event. |
| 334 var listeners = | 332 var listeners = |
| 335 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); | 333 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); |
| 336 | 334 |
| 337 var results = []; | 335 var results = []; |
| 338 for (var i = 0; i < listeners.length; i++) { | 336 for (var i = 0; i < listeners.length; i++) { |
| 339 try { | 337 try { |
| 340 var result = this.dispatchToListener(listeners[i].callback, args); | 338 var result = this.dispatchToListener(listeners[i].callback, args); |
| 341 if (result !== undefined) | 339 if (result !== undefined) |
| 342 results.push(result); | 340 results.push(result); |
| 343 } catch (e) { | 341 } catch (e) { |
| 344 console.error("Error in event handler for '" + this.eventName_ + | 342 console.error("Error in event handler for '" + this.eventName_ + |
| 345 "': " + e.message + ' ' + e.stack); | 343 "': " + e.message + ' ' + e.stack); |
| 346 } | 344 } |
| 347 } | 345 } |
| 348 if (results.length) | 346 if (results.length) |
| 349 return {results: results}; | 347 return {results: results}; |
| 350 } | 348 } |
| 351 | 349 |
| 352 // Can be overridden to support custom dispatching. | 350 // Can be overridden to support custom dispatching. |
| 353 Event.prototype.dispatchToListener = function(callback, args) { | 351 chrome.Event.prototype.dispatchToListener = function(callback, args) { |
| 354 return callback.apply(null, args); | 352 return callback.apply(null, args); |
| 355 } | 353 } |
| 356 | 354 |
| 357 // Dispatches this event object to all listeners, passing all supplied | 355 // Dispatches this event object to all listeners, passing all supplied |
| 358 // arguments to this function each listener. | 356 // arguments to this function each listener. |
| 359 Event.prototype.dispatch = function(varargs) { | 357 chrome.Event.prototype.dispatch = function(varargs) { |
| 360 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); | 358 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); |
| 361 }; | 359 }; |
| 362 | 360 |
| 363 // Detaches this event object from its name. | 361 // Detaches this event object from its name. |
| 364 Event.prototype.detach_ = function() { | 362 chrome.Event.prototype.detach_ = function() { |
| 365 this.attachmentStrategy_.detach(false); | 363 this.attachmentStrategy_.detach(false); |
| 366 }; | 364 }; |
| 367 | 365 |
| 368 Event.prototype.destroy_ = function() { | 366 chrome.Event.prototype.destroy_ = function() { |
| 369 this.listeners_ = []; | 367 this.listeners_ = []; |
| 370 this.validateEventArgs_ = []; | 368 this.validateEventArgs_ = []; |
| 371 this.detach_(false); | 369 this.detach_(false); |
| 372 }; | 370 }; |
| 373 | 371 |
| 374 Event.prototype.addRules = function(rules, opt_cb) { | 372 chrome.Event.prototype.addRules = function(rules, opt_cb) { |
| 375 if (!this.eventOptions_.supportsRules) | 373 if (!this.eventOptions_.supportsRules) |
| 376 throw new Error("This event does not support rules."); | 374 throw new Error("This event does not support rules."); |
| 377 | 375 |
| 378 // Takes a list of JSON datatype identifiers and returns a schema fragment | 376 // Takes a list of JSON datatype identifiers and returns a schema fragment |
| 379 // that verifies that a JSON object corresponds to an array of only these | 377 // that verifies that a JSON object corresponds to an array of only these |
| 380 // data types. | 378 // data types. |
| 381 function buildArrayOfChoicesSchema(typesList) { | 379 function buildArrayOfChoicesSchema(typesList) { |
| 382 return { | 380 return { |
| 383 'type': 'array', | 381 'type': 'array', |
| 384 'items': { | 382 'items': { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 414 | 412 |
| 415 ensureRuleSchemasLoaded(); | 413 ensureRuleSchemasLoaded(); |
| 416 // We remove the first parameter from the validation to give the user more | 414 // We remove the first parameter from the validation to give the user more |
| 417 // meaningful error messages. | 415 // meaningful error messages. |
| 418 validate([rules, opt_cb], | 416 validate([rules, opt_cb], |
| 419 ruleFunctionSchemas.addRules.parameters.slice().splice(1)); | 417 ruleFunctionSchemas.addRules.parameters.slice().splice(1)); |
| 420 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], | 418 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], |
| 421 ruleFunctionSchemas.addRules.parameters); | 419 ruleFunctionSchemas.addRules.parameters); |
| 422 } | 420 } |
| 423 | 421 |
| 424 Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { | 422 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { |
| 425 if (!this.eventOptions_.supportsRules) | 423 if (!this.eventOptions_.supportsRules) |
| 426 throw new Error("This event does not support rules."); | 424 throw new Error("This event does not support rules."); |
| 427 ensureRuleSchemasLoaded(); | 425 ensureRuleSchemasLoaded(); |
| 428 // We remove the first parameter from the validation to give the user more | 426 // We remove the first parameter from the validation to give the user more |
| 429 // meaningful error messages. | 427 // meaningful error messages. |
| 430 validate([ruleIdentifiers, opt_cb], | 428 validate([ruleIdentifiers, opt_cb], |
| 431 ruleFunctionSchemas.removeRules.parameters.slice().splice(1)); | 429 ruleFunctionSchemas.removeRules.parameters.slice().splice(1)); |
| 432 sendRequest("events.removeRules", | 430 sendRequest("events.removeRules", |
| 433 [this.eventName_, ruleIdentifiers, opt_cb], | 431 [this.eventName_, ruleIdentifiers, opt_cb], |
| 434 ruleFunctionSchemas.removeRules.parameters); | 432 ruleFunctionSchemas.removeRules.parameters); |
| 435 } | 433 } |
| 436 | 434 |
| 437 Event.prototype.getRules = function(ruleIdentifiers, cb) { | 435 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { |
| 438 if (!this.eventOptions_.supportsRules) | 436 if (!this.eventOptions_.supportsRules) |
| 439 throw new Error("This event does not support rules."); | 437 throw new Error("This event does not support rules."); |
| 440 ensureRuleSchemasLoaded(); | 438 ensureRuleSchemasLoaded(); |
| 441 // We remove the first parameter from the validation to give the user more | 439 // We remove the first parameter from the validation to give the user more |
| 442 // meaningful error messages. | 440 // meaningful error messages. |
| 443 validate([ruleIdentifiers, cb], | 441 validate([ruleIdentifiers, cb], |
| 444 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); | 442 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); |
| 445 | 443 |
| 446 sendRequest("events.getRules", | 444 sendRequest("events.getRules", |
| 447 [this.eventName_, ruleIdentifiers, cb], | 445 [this.eventName_, ruleIdentifiers, cb], |
| 448 ruleFunctionSchemas.getRules.parameters); | 446 ruleFunctionSchemas.getRules.parameters); |
| 449 } | 447 } |
| 450 | 448 |
| 451 // Special load events: we don't use the DOM unload because that slows | 449 // Special load events: we don't use the DOM unload because that slows |
| 452 // down tab shutdown. On the other hand, onUnload might not always fire, | 450 // down tab shutdown. On the other hand, onUnload might not always fire, |
| 453 // since Chrome will terminate renderers on shutdown (SuddenTermination). | 451 // since Chrome will terminate renderers on shutdown (SuddenTermination). |
| 454 chromeHidden.onLoad = new Event(); | 452 chromeHidden.onLoad = new chrome.Event(); |
| 455 chromeHidden.onUnload = new Event(); | 453 chromeHidden.onUnload = new chrome.Event(); |
| 456 | 454 |
| 457 chromeHidden.dispatchOnLoad = | 455 chromeHidden.dispatchOnLoad = |
| 458 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); | 456 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); |
| 459 | 457 |
| 460 chromeHidden.dispatchOnUnload = function() { | 458 chromeHidden.dispatchOnUnload = function() { |
| 461 chromeHidden.onUnload.dispatch(); | 459 chromeHidden.onUnload.dispatch(); |
| 462 chromeHidden.wasUnloaded = true; | 460 chromeHidden.wasUnloaded = true; |
| 463 | 461 |
| 464 for (var i = 0; i < allAttachedEvents.length; ++i) { | 462 for (var i = 0; i < allAttachedEvents.length; ++i) { |
| 465 var event = allAttachedEvents[i]; | 463 var event = allAttachedEvents[i]; |
| 466 if (event) | 464 if (event) |
| 467 event.detach_(); | 465 event.detach_(); |
| 468 } | 466 } |
| 469 }; | 467 }; |
| 470 | 468 |
| 471 chromeHidden.dispatchError = function(msg) { | 469 chromeHidden.dispatchError = function(msg) { |
| 472 console.error(msg); | 470 console.error(msg); |
| 473 }; | 471 }; |
| 474 | 472 |
| 475 chrome.Event = Event; | 473 exports.Event = chrome.Event; |
| OLD | NEW |