| 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 eventNatives = requireNative('event_natives'); |
| 6 // TODO(cduvall/kalman): json_schema shouldn't put things on chromeHidden. | 6 var logging = requireNative('logging'); |
| 7 require('json_schema'); | 7 var onUnload = require('on_unload'); |
| 8 var eventBindingsNatives = requireNative('event_bindings'); | 8 var schemaRegistry = requireNative('schema_registry'); |
| 9 var AttachEvent = eventBindingsNatives.AttachEvent; | |
| 10 var DetachEvent = eventBindingsNatives.DetachEvent; | |
| 11 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; | |
| 12 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; | |
| 13 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; | |
| 14 var forEach = require('utils').forEach; | |
| 15 var sendRequest = require('sendRequest').sendRequest; | 9 var sendRequest = require('sendRequest').sendRequest; |
| 16 var utils = require('utils'); | 10 var utils = require('utils'); |
| 17 var validate = require('schemaUtils').validate; | 11 var validate = require('schemaUtils').validate; |
| 18 | 12 |
| 19 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); | |
| 20 var chrome = requireNative('chrome').GetChrome(); | |
| 21 var schemaRegistry = requireNative('schema_registry'); | |
| 22 | |
| 23 // Schemas for the rule-style functions on the events API that | 13 // Schemas for the rule-style functions on the events API that |
| 24 // only need to be generated occasionally, so populate them lazily. | 14 // only need to be generated occasionally, so populate them lazily. |
| 25 var ruleFunctionSchemas = { | 15 var ruleFunctionSchemas = { |
| 26 // These values are set lazily: | 16 // These values are set lazily: |
| 27 // addRules: {}, | 17 // addRules: {}, |
| 28 // getRules: {}, | 18 // getRules: {}, |
| 29 // removeRules: {} | 19 // removeRules: {} |
| 30 }; | 20 }; |
| 31 | 21 |
| 32 // This function ensures that |ruleFunctionSchemas| is populated. | 22 // This function ensures that |ruleFunctionSchemas| is populated. |
| (...skipping 23 matching lines...) Expand all Loading... |
| 56 | 46 |
| 57 // Handles adding/removing/dispatching listeners for unfiltered events. | 47 // Handles adding/removing/dispatching listeners for unfiltered events. |
| 58 var UnfilteredAttachmentStrategy = function(event) { | 48 var UnfilteredAttachmentStrategy = function(event) { |
| 59 this.event_ = event; | 49 this.event_ = event; |
| 60 }; | 50 }; |
| 61 | 51 |
| 62 UnfilteredAttachmentStrategy.prototype.onAddedListener = | 52 UnfilteredAttachmentStrategy.prototype.onAddedListener = |
| 63 function(listener) { | 53 function(listener) { |
| 64 // Only attach / detach on the first / last listener removed. | 54 // Only attach / detach on the first / last listener removed. |
| 65 if (this.event_.listeners_.length == 0) | 55 if (this.event_.listeners_.length == 0) |
| 66 AttachEvent(this.event_.eventName_); | 56 eventNatives.AttachEvent(this.event_.eventName_); |
| 67 }; | 57 }; |
| 68 | 58 |
| 69 UnfilteredAttachmentStrategy.prototype.onRemovedListener = | 59 UnfilteredAttachmentStrategy.prototype.onRemovedListener = |
| 70 function(listener) { | 60 function(listener) { |
| 71 if (this.event_.listeners_.length == 0) | 61 if (this.event_.listeners_.length == 0) |
| 72 this.detach(true); | 62 this.detach(true); |
| 73 }; | 63 }; |
| 74 | 64 |
| 75 UnfilteredAttachmentStrategy.prototype.detach = function(manual) { | 65 UnfilteredAttachmentStrategy.prototype.detach = function(manual) { |
| 76 DetachEvent(this.event_.eventName_, manual); | 66 eventNatives.DetachEvent(this.event_.eventName_, manual); |
| 77 }; | 67 }; |
| 78 | 68 |
| 79 UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { | 69 UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { |
| 80 return this.event_.listeners_; | 70 return this.event_.listeners_; |
| 81 }; | 71 }; |
| 82 | 72 |
| 83 var FilteredAttachmentStrategy = function(event) { | 73 var FilteredAttachmentStrategy = function(event) { |
| 84 this.event_ = event; | 74 this.event_ = event; |
| 85 this.listenerMap_ = {}; | 75 this.listenerMap_ = {}; |
| 86 }; | 76 }; |
| 87 | 77 |
| 88 FilteredAttachmentStrategy.idToEventMap = {}; | 78 FilteredAttachmentStrategy.idToEventMap = {}; |
| 89 | 79 |
| 90 FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) { | 80 FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) { |
| 91 var id = AttachFilteredEvent(this.event_.eventName_, | 81 var id = eventNatives.AttachFilteredEvent(this.event_.eventName_, |
| 92 listener.filters || {}); | 82 listener.filters || {}); |
| 93 if (id == -1) | 83 if (id == -1) |
| 94 throw new Error("Can't add listener"); | 84 throw new Error("Can't add listener"); |
| 95 listener.id = id; | 85 listener.id = id; |
| 96 this.listenerMap_[id] = listener; | 86 this.listenerMap_[id] = listener; |
| 97 FilteredAttachmentStrategy.idToEventMap[id] = this.event_; | 87 FilteredAttachmentStrategy.idToEventMap[id] = this.event_; |
| 98 }; | 88 }; |
| 99 | 89 |
| 100 FilteredAttachmentStrategy.prototype.onRemovedListener = function(listener) { | 90 FilteredAttachmentStrategy.prototype.onRemovedListener = function(listener) { |
| 101 this.detachListener(listener, true); | 91 this.detachListener(listener, true); |
| 102 }; | 92 }; |
| 103 | 93 |
| 104 FilteredAttachmentStrategy.prototype.detachListener = | 94 FilteredAttachmentStrategy.prototype.detachListener = |
| 105 function(listener, manual) { | 95 function(listener, manual) { |
| 106 if (listener.id == undefined) | 96 if (listener.id == undefined) |
| 107 throw new Error("listener.id undefined - '" + listener + "'"); | 97 throw new Error("listener.id undefined - '" + listener + "'"); |
| 108 var id = listener.id; | 98 var id = listener.id; |
| 109 delete this.listenerMap_[id]; | 99 delete this.listenerMap_[id]; |
| 110 delete FilteredAttachmentStrategy.idToEventMap[id]; | 100 delete FilteredAttachmentStrategy.idToEventMap[id]; |
| 111 DetachFilteredEvent(id, manual); | 101 eventNatives.DetachFilteredEvent(id, manual); |
| 112 }; | 102 }; |
| 113 | 103 |
| 114 FilteredAttachmentStrategy.prototype.detach = function(manual) { | 104 FilteredAttachmentStrategy.prototype.detach = function(manual) { |
| 115 for (var i in this.listenerMap_) | 105 for (var i in this.listenerMap_) |
| 116 this.detachListener(this.listenerMap_[i], manual); | 106 this.detachListener(this.listenerMap_[i], manual); |
| 117 }; | 107 }; |
| 118 | 108 |
| 119 FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { | 109 FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { |
| 120 var result = []; | 110 var result = []; |
| 121 for (var i = 0; i < ids.length; i++) | 111 for (var i = 0; i < ids.length; i++) |
| 122 result.push(this.listenerMap_[ids[i]]); | 112 result.push(this.listenerMap_[ids[i]]); |
| 123 return result; | 113 return result; |
| 124 }; | 114 }; |
| 125 | 115 |
| 126 chromeHidden.parseEventOptions = function(opt_eventOptions) { | 116 function parseEventOptions(opt_eventOptions) { |
| 127 function merge(dest, src) { | 117 function merge(dest, src) { |
| 128 for (var k in src) { | 118 for (var k in src) { |
| 129 if (!dest.hasOwnProperty(k)) { | 119 if (!dest.hasOwnProperty(k)) { |
| 130 dest[k] = src[k]; | 120 dest[k] = src[k]; |
| 131 } | 121 } |
| 132 } | 122 } |
| 133 } | 123 } |
| 134 | 124 |
| 135 var options = opt_eventOptions || {}; | 125 var options = opt_eventOptions || {}; |
| 136 merge(options, | 126 merge(options, |
| 137 {supportsFilters: false, | 127 {supportsFilters: false, |
| 138 supportsListeners: true, | 128 supportsListeners: true, |
| 139 supportsRules: false, | 129 supportsRules: false, |
| 140 }); | 130 }); |
| 141 return options; | 131 return options; |
| 142 }; | 132 }; |
| 143 | 133 |
| 144 // Event object. If opt_eventName is provided, this object represents | 134 // Event object. If opt_eventName is provided, this object represents |
| 145 // the unique instance of that named event, and dispatching an event | 135 // the unique instance of that named event, and dispatching an event |
| 146 // with that name will route through this object's listeners. Note that | 136 // with that name will route through this object's listeners. Note that |
| 147 // opt_eventName is required for events that support rules. | 137 // opt_eventName is required for events that support rules. |
| 148 // | 138 // |
| 149 // Example: | 139 // Example: |
| 150 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); | 140 // var Event = require('event_bindings').Event; |
| 141 // chrome.tabs.onChanged = new Event("tab-changed"); |
| 151 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); | 142 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| 152 // chromeHidden.Event.dispatch("tab-changed", "hi"); | 143 // Event.dispatch("tab-changed", "hi"); |
| 153 // will result in an alert dialog that says 'hi'. | 144 // will result in an alert dialog that says 'hi'. |
| 154 // | 145 // |
| 155 // If opt_eventOptions exists, it is a dictionary that contains the boolean | 146 // If opt_eventOptions exists, it is a dictionary that contains the boolean |
| 156 // entries "supportsListeners" and "supportsRules". | 147 // entries "supportsListeners" and "supportsRules". |
| 157 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { | 148 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { |
| 158 this.eventName_ = opt_eventName; | 149 this.eventName_ = opt_eventName; |
| 159 this.listeners_ = []; | 150 this.listeners_ = []; |
| 160 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); | 151 this.eventOptions_ = parseEventOptions(opt_eventOptions); |
| 161 | 152 |
| 162 if (this.eventOptions_.supportsRules && !opt_eventName) | 153 if (this.eventOptions_.supportsRules && !opt_eventName) |
| 163 throw new Error("Events that support rules require an event name."); | 154 throw new Error("Events that support rules require an event name."); |
| 164 | 155 |
| 165 if (this.eventOptions_.supportsFilters) { | 156 if (this.eventOptions_.supportsFilters) { |
| 166 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); | 157 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); |
| 167 } else { | 158 } else { |
| 168 this.attachmentStrategy_ = new UnfilteredAttachmentStrategy(this); | 159 this.attachmentStrategy_ = new UnfilteredAttachmentStrategy(this); |
| 169 } | 160 } |
| 170 | 161 |
| 171 // Validate event arguments (the data that is passed to the callbacks) | 162 // Validate event arguments (the data that is passed to the callbacks) |
| 172 // if we are in debug. | 163 // if we are in debug. |
| 173 if (opt_argSchemas && | 164 if (opt_argSchemas && logging.DCHECK_IS_ON()) { |
| 174 chromeHidden.validateCallbacks) { | |
| 175 | |
| 176 this.validateEventArgs_ = function(args) { | 165 this.validateEventArgs_ = function(args) { |
| 177 try { | 166 try { |
| 178 validate(args, opt_argSchemas); | 167 validate(args, opt_argSchemas); |
| 179 } catch (exception) { | 168 } catch (exception) { |
| 180 return "Event validation error during " + opt_eventName + " -- " + | 169 return "Event validation error during " + opt_eventName + " -- " + |
| 181 exception; | 170 exception; |
| 182 } | 171 } |
| 183 }; | 172 }; |
| 184 } else { | 173 } else { |
| 185 this.validateEventArgs_ = function() {} | 174 this.validateEventArgs_ = function() {} |
| 186 } | 175 } |
| 187 }; | 176 }; |
| 188 | 177 |
| 189 | |
| 190 chromeHidden.Event = {}; | |
| 191 | |
| 192 // callback is a function(args, dispatch). args are the args we receive from | 178 // callback is a function(args, dispatch). args are the args we receive from |
| 193 // dispatchEvent(), and dispatch is a function(args) that dispatches args to | 179 // dispatchEvent(), and dispatch is a function(args) that dispatches args to |
| 194 // its listeners. | 180 // its listeners. |
| 195 chromeHidden.Event.registerArgumentMassager = function(name, callback) { | 181 function registerArgumentMassager(name, callback) { |
| 196 if (eventArgumentMassagers[name]) | 182 if (eventArgumentMassagers[name]) |
| 197 throw new Error("Massager already registered for event: " + name); | 183 throw new Error("Massager already registered for event: " + name); |
| 198 eventArgumentMassagers[name] = callback; | 184 eventArgumentMassagers[name] = callback; |
| 199 }; | 185 } |
| 200 | 186 |
| 201 // Dispatches a named event with the given argument array. The args array is | 187 // Dispatches a named event with the given argument array. The args array is |
| 202 // the list of arguments that will be sent to the event callback. | 188 // the list of arguments that will be sent to the event callback. |
| 203 chromeHidden.Event.dispatchEvent = function(name, args, filteringInfo) { | 189 function dispatchEvent(name, args, filteringInfo) { |
| 204 var listenerIDs = null; | 190 var listenerIDs = null; |
| 205 | 191 |
| 206 if (filteringInfo) | 192 if (filteringInfo) |
| 207 listenerIDs = MatchAgainstEventFilter(name, filteringInfo); | 193 listenerIDs = eventNatives.MatchAgainstEventFilter(name, filteringInfo); |
| 208 | 194 |
| 209 var event = attachedNamedEvents[name]; | 195 var event = attachedNamedEvents[name]; |
| 210 if (!event) | 196 if (!event) |
| 211 return; | 197 return; |
| 212 | 198 |
| 213 var dispatchArgs = function(args) { | 199 var dispatchArgs = function(args) { |
| 214 var result = event.dispatch_(args, listenerIDs); | 200 var result = event.dispatch_(args, listenerIDs); |
| 215 if (result) | 201 if (result) |
| 216 DCHECK(!result.validationErrors, result.validationErrors); | 202 logging.DCHECK(!result.validationErrors, result.validationErrors); |
| 217 return result; | 203 return result; |
| 218 }; | 204 }; |
| 219 | 205 |
| 220 if (eventArgumentMassagers[name]) | 206 if (eventArgumentMassagers[name]) |
| 221 eventArgumentMassagers[name](args, dispatchArgs); | 207 eventArgumentMassagers[name](args, dispatchArgs); |
| 222 else | 208 else |
| 223 dispatchArgs(args); | 209 dispatchArgs(args); |
| 224 }; | 210 } |
| 225 | |
| 226 // Test if a named event has any listeners. | |
| 227 chromeHidden.Event.hasListener = function(name) { | |
| 228 return (attachedNamedEvents[name] && | |
| 229 attachedNamedEvents[name].listeners_.length > 0); | |
| 230 }; | |
| 231 | 211 |
| 232 // Registers a callback to be called when this event is dispatched. | 212 // Registers a callback to be called when this event is dispatched. |
| 233 Event.prototype.addListener = function(cb, filters) { | 213 Event.prototype.addListener = function(cb, filters) { |
| 234 if (!this.eventOptions_.supportsListeners) | 214 if (!this.eventOptions_.supportsListeners) |
| 235 throw new Error("This event does not support listeners."); | 215 throw new Error("This event does not support listeners."); |
| 236 if (this.eventOptions_.maxListeners && | 216 if (this.eventOptions_.maxListeners && |
| 237 this.getListenerCount() >= this.eventOptions_.maxListeners) | 217 this.getListenerCount() >= this.eventOptions_.maxListeners) |
| 238 throw new Error("Too many listeners for " + this.eventName_); | 218 throw new Error("Too many listeners for " + this.eventName_); |
| 239 if (filters) { | 219 if (filters) { |
| 240 if (!this.eventOptions_.supportsFilters) | 220 if (!this.eventOptions_.supportsFilters) |
| 241 throw new Error("This event does not support filters."); | 221 throw new Error("This event does not support filters."); |
| 242 if (filters.url && !(filters.url instanceof Array)) | 222 if (filters.url && !(filters.url instanceof Array)) |
| 243 throw new Error("filters.url should be an array"); | 223 throw new Error("filters.url should be an array"); |
| 244 } | 224 } |
| 245 var listener = {callback: cb, filters: filters}; | 225 var listener = {callback: cb, filters: filters}; |
| 246 this.attach_(listener); | 226 this.attach_(listener); |
| 247 this.listeners_.push(listener); | 227 this.listeners_.push(listener); |
| 248 }; | 228 }; |
| 249 | 229 |
| 250 Event.prototype.attach_ = function(listener) { | 230 Event.prototype.attach_ = function(listener) { |
| 251 this.attachmentStrategy_.onAddedListener(listener); | 231 this.attachmentStrategy_.onAddedListener(listener); |
| 252 if (this.listeners_.length == 0) { | 232 if (this.listeners_.length == 0) { |
| 253 allAttachedEvents[allAttachedEvents.length] = this; | 233 allAttachedEvents[allAttachedEvents.length] = this; |
| 254 if (!this.eventName_) | 234 if (!this.eventName_) |
| 255 return; | 235 return; |
| 256 | 236 |
| 257 if (attachedNamedEvents[this.eventName_]) { | 237 if (attachedNamedEvents[this.eventName_]) { |
| 258 throw new Error("chrome.Event '" + this.eventName_ + | 238 throw new Error("Event '" + this.eventName_ + "' is already attached."); |
| 259 "' is already attached."); | |
| 260 } | 239 } |
| 261 | 240 |
| 262 attachedNamedEvents[this.eventName_] = this; | 241 attachedNamedEvents[this.eventName_] = this; |
| 263 } | 242 } |
| 264 }; | 243 }; |
| 265 | 244 |
| 266 // Unregisters a callback. | 245 // Unregisters a callback. |
| 267 Event.prototype.removeListener = function(cb) { | 246 Event.prototype.removeListener = function(cb) { |
| 268 if (!this.eventOptions_.supportsListeners) | 247 if (!this.eventOptions_.supportsListeners) |
| 269 throw new Error("This event does not support listeners."); | 248 throw new Error("This event does not support listeners."); |
| 270 var idx = this.findListener_(cb); | 249 var idx = this.findListener_(cb); |
| 271 if (idx == -1) { | 250 if (idx == -1) { |
| 272 return; | 251 return; |
| 273 } | 252 } |
| 274 | 253 |
| 275 var removedListener = this.listeners_.splice(idx, 1)[0]; | 254 var removedListener = this.listeners_.splice(idx, 1)[0]; |
| 276 this.attachmentStrategy_.onRemovedListener(removedListener); | 255 this.attachmentStrategy_.onRemovedListener(removedListener); |
| 277 | 256 |
| 278 if (this.listeners_.length == 0) { | 257 if (this.listeners_.length == 0) { |
| 279 var i = allAttachedEvents.indexOf(this); | 258 var i = allAttachedEvents.indexOf(this); |
| 280 if (i >= 0) | 259 if (i >= 0) |
| 281 delete allAttachedEvents[i]; | 260 delete allAttachedEvents[i]; |
| 282 if (!this.eventName_) | 261 if (!this.eventName_) |
| 283 return; | 262 return; |
| 284 | 263 |
| 285 if (!attachedNamedEvents[this.eventName_]) { | 264 if (!attachedNamedEvents[this.eventName_]) { |
| 286 throw new Error("chrome.Event '" + this.eventName_ + | 265 throw new Error("Event '" + this.eventName_ + "' is not attached."); |
| 287 "' is not attached."); | |
| 288 } | 266 } |
| 289 | 267 |
| 290 delete attachedNamedEvents[this.eventName_]; | 268 delete attachedNamedEvents[this.eventName_]; |
| 291 } | 269 } |
| 292 }; | 270 }; |
| 293 | 271 |
| 294 // Test if the given callback is registered for this event. | 272 // Test if the given callback is registered for this event. |
| 295 Event.prototype.hasListener = function(cb) { | 273 Event.prototype.hasListener = function(cb) { |
| 296 if (!this.eventOptions_.supportsListeners) | 274 if (!this.eventOptions_.supportsListeners) |
| 297 throw new Error("This event does not support listeners."); | 275 throw new Error("This event does not support listeners."); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 336 var listeners = | 314 var listeners = |
| 337 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); | 315 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); |
| 338 | 316 |
| 339 var results = []; | 317 var results = []; |
| 340 for (var i = 0; i < listeners.length; i++) { | 318 for (var i = 0; i < listeners.length; i++) { |
| 341 try { | 319 try { |
| 342 var result = this.dispatchToListener(listeners[i].callback, args); | 320 var result = this.dispatchToListener(listeners[i].callback, args); |
| 343 if (result !== undefined) | 321 if (result !== undefined) |
| 344 results.push(result); | 322 results.push(result); |
| 345 } catch (e) { | 323 } catch (e) { |
| 346 console.error("Error in event handler for '" + this.eventName_ + | 324 var errorMessage = "Error in event handler"; |
| 347 "': " + e.message + ' ' + e.stack); | 325 if (this.eventName_) |
| 326 errorMessage += " for " + this.eventName_; |
| 327 errorMessage += ": " + e; |
| 328 console.error(errorMessage); |
| 348 } | 329 } |
| 349 } | 330 } |
| 350 if (results.length) | 331 if (results.length) |
| 351 return {results: results}; | 332 return {results: results}; |
| 352 } | 333 } |
| 353 | 334 |
| 354 // Can be overridden to support custom dispatching. | 335 // Can be overridden to support custom dispatching. |
| 355 Event.prototype.dispatchToListener = function(callback, args) { | 336 Event.prototype.dispatchToListener = function(callback, args) { |
| 356 return callback.apply(null, args); | 337 return callback.apply(null, args); |
| 357 } | 338 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 // Validate conditions and actions against specific schemas of this | 373 // Validate conditions and actions against specific schemas of this |
| 393 // event object type. | 374 // event object type. |
| 394 // |rules| is an array of JSON objects that follow the Rule type of the | 375 // |rules| is an array of JSON objects that follow the Rule type of the |
| 395 // declarative extension APIs. |conditions| is an array of JSON type | 376 // declarative extension APIs. |conditions| is an array of JSON type |
| 396 // identifiers that are allowed to occur in the conditions attribute of each | 377 // identifiers that are allowed to occur in the conditions attribute of each |
| 397 // rule. Likewise, |actions| is an array of JSON type identifiers that are | 378 // rule. Likewise, |actions| is an array of JSON type identifiers that are |
| 398 // allowed to occur in the actions attribute of each rule. | 379 // allowed to occur in the actions attribute of each rule. |
| 399 function validateRules(rules, conditions, actions) { | 380 function validateRules(rules, conditions, actions) { |
| 400 var conditionsSchema = buildArrayOfChoicesSchema(conditions); | 381 var conditionsSchema = buildArrayOfChoicesSchema(conditions); |
| 401 var actionsSchema = buildArrayOfChoicesSchema(actions); | 382 var actionsSchema = buildArrayOfChoicesSchema(actions); |
| 402 forEach(rules, function(i, rule) { | 383 for (var i = 0; i < rules.length; ++i) { |
| 384 var rule = rules[i]; |
| 403 validate([rule.conditions], [conditionsSchema]); | 385 validate([rule.conditions], [conditionsSchema]); |
| 404 validate([rule.actions], [actionsSchema]); | 386 validate([rule.actions], [actionsSchema]); |
| 405 }) | 387 } |
| 406 }; | 388 }; |
| 407 | 389 |
| 408 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) { | 390 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) { |
| 409 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' + | 391 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' + |
| 410 'actions in the API specification.'); | 392 'actions in the API specification.'); |
| 411 } | 393 } |
| 412 | 394 |
| 413 validateRules(rules, | 395 validateRules(rules, |
| 414 this.eventOptions_.conditions, | 396 this.eventOptions_.conditions, |
| 415 this.eventOptions_.actions); | 397 this.eventOptions_.actions); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 443 // We remove the first parameter from the validation to give the user more | 425 // We remove the first parameter from the validation to give the user more |
| 444 // meaningful error messages. | 426 // meaningful error messages. |
| 445 validate([ruleIdentifiers, cb], | 427 validate([ruleIdentifiers, cb], |
| 446 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); | 428 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); |
| 447 | 429 |
| 448 sendRequest("events.getRules", | 430 sendRequest("events.getRules", |
| 449 [this.eventName_, ruleIdentifiers, cb], | 431 [this.eventName_, ruleIdentifiers, cb], |
| 450 ruleFunctionSchemas.getRules.parameters); | 432 ruleFunctionSchemas.getRules.parameters); |
| 451 } | 433 } |
| 452 | 434 |
| 453 // Special load events: we don't use the DOM unload because that slows | 435 onUnload.addListener(function() { |
| 454 // down tab shutdown. On the other hand, onUnload might not always fire, | |
| 455 // since Chrome will terminate renderers on shutdown (SuddenTermination). | |
| 456 chromeHidden.onUnload = new Event(); | |
| 457 | |
| 458 chromeHidden.dispatchOnUnload = function() { | |
| 459 chromeHidden.onUnload.dispatch(); | |
| 460 chromeHidden.wasUnloaded = true; | |
| 461 | |
| 462 for (var i = 0; i < allAttachedEvents.length; ++i) { | 436 for (var i = 0; i < allAttachedEvents.length; ++i) { |
| 463 var event = allAttachedEvents[i]; | 437 var event = allAttachedEvents[i]; |
| 464 if (event) | 438 if (event) |
| 465 event.detach_(); | 439 event.detach_(); |
| 466 } | 440 } |
| 467 }; | 441 }); |
| 468 | 442 |
| 469 chrome.Event = Event; | 443 exports.Event = Event; |
| 444 exports.dispatchEvent = dispatchEvent; |
| 445 exports.parseEventOptions = parseEventOptions; |
| 446 exports.registerArgumentMassager = registerArgumentMassager; |
| OLD | NEW |