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