| 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 chrome = chrome || {}; | 5 var chrome = chrome || {}; |
| 6 (function () { | 6 (function () { |
| 7 native function GetChromeHidden(); | 7 native function GetChromeHidden(); |
| 8 native function AttachEvent(eventName); | 8 native function AttachEvent(eventName); |
| 9 native function DetachEvent(eventName); | 9 native function DetachEvent(eventName); |
| 10 native function Print(); | 10 native function Print(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 } | 44 } |
| 45 }; | 45 }; |
| 46 | 46 |
| 47 this.parse = function(thing) { | 47 this.parse = function(thing) { |
| 48 return $jsonParse(thing); | 48 return $jsonParse(thing); |
| 49 }; | 49 }; |
| 50 })(); | 50 })(); |
| 51 | 51 |
| 52 // Event object. If opt_eventName is provided, this object represents | 52 // Event object. If opt_eventName is provided, this object represents |
| 53 // the unique instance of that named event, and dispatching an event | 53 // the unique instance of that named event, and dispatching an event |
| 54 // with that name will route through this object's listeners. | 54 // with that name will route through this object's listeners. Note that |
| 55 // opt_eventName is required for events that support rules. |
| 55 // | 56 // |
| 56 // Example: | 57 // Example: |
| 57 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); | 58 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); |
| 58 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); | 59 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| 59 // chromeHidden.Event.dispatch("tab-changed", "hi"); | 60 // chromeHidden.Event.dispatch("tab-changed", "hi"); |
| 60 // will result in an alert dialog that says 'hi'. | 61 // will result in an alert dialog that says 'hi'. |
| 61 chrome.Event = function(opt_eventName, opt_argSchemas) { | 62 // |
| 63 // If opt_eventOptions exists, it is a dictionary that contains the boolean |
| 64 // entries "supportsListeners" and "supportsRules". |
| 65 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { |
| 62 this.eventName_ = opt_eventName; | 66 this.eventName_ = opt_eventName; |
| 63 this.listeners_ = []; | 67 this.listeners_ = []; |
| 68 this.eventOptions_ = opt_eventOptions || |
| 69 {"supportsListeners": true, "supportsRules": false}; |
| 70 |
| 71 if (this.eventOptions_.supportsRules && !opt_eventName) |
| 72 throw new Error("Events that support rules require an event name."); |
| 64 | 73 |
| 65 // Validate event parameters if we are in debug. | 74 // Validate event parameters if we are in debug. |
| 66 if (opt_argSchemas && | 75 if (opt_argSchemas && |
| 67 chromeHidden.validateCallbacks && | 76 chromeHidden.validateCallbacks && |
| 68 chromeHidden.validate) { | 77 chromeHidden.validate) { |
| 69 | 78 |
| 70 this.validate_ = function(args) { | 79 this.validate_ = function(args) { |
| 71 try { | 80 try { |
| 72 chromeHidden.validate(args, opt_argSchemas); | 81 chromeHidden.validate(args, opt_argSchemas); |
| 73 } catch (exception) { | 82 } catch (exception) { |
| 74 return "Event validation error during " + opt_eventName + " -- " + | 83 return "Event validation error during " + opt_eventName + " -- " + |
| 75 exception; | 84 exception; |
| 76 } | 85 } |
| 77 }; | 86 }; |
| 87 } else { |
| 88 this.validate_ = function() {} |
| 78 } | 89 } |
| 79 }; | 90 }; |
| 80 | 91 |
| 81 // A map of event names to the event object that is registered to that name. | 92 // A map of event names to the event object that is registered to that name. |
| 82 var attachedNamedEvents = {}; | 93 var attachedNamedEvents = {}; |
| 83 | 94 |
| 84 // An array of all attached event objects, used for detaching on unload. | 95 // An array of all attached event objects, used for detaching on unload. |
| 85 var allAttachedEvents = []; | 96 var allAttachedEvents = []; |
| 86 | 97 |
| 87 // A map of functions that massage event arguments before they are dispatched. | 98 // A map of functions that massage event arguments before they are dispatched. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 }; | 131 }; |
| 121 | 132 |
| 122 // Test if a named event has any listeners. | 133 // Test if a named event has any listeners. |
| 123 chromeHidden.Event.hasListener = function(name) { | 134 chromeHidden.Event.hasListener = function(name) { |
| 124 return (attachedNamedEvents[name] && | 135 return (attachedNamedEvents[name] && |
| 125 attachedNamedEvents[name].listeners_.length > 0); | 136 attachedNamedEvents[name].listeners_.length > 0); |
| 126 }; | 137 }; |
| 127 | 138 |
| 128 // Registers a callback to be called when this event is dispatched. | 139 // Registers a callback to be called when this event is dispatched. |
| 129 chrome.Event.prototype.addListener = function(cb) { | 140 chrome.Event.prototype.addListener = function(cb) { |
| 141 if (!this.eventOptions_.supportsListeners) |
| 142 throw new Error("This event does not support listeners."); |
| 130 if (this.listeners_.length == 0) { | 143 if (this.listeners_.length == 0) { |
| 131 this.attach_(); | 144 this.attach_(); |
| 132 } | 145 } |
| 133 this.listeners_.push(cb); | 146 this.listeners_.push(cb); |
| 134 }; | 147 }; |
| 135 | 148 |
| 136 // Unregisters a callback. | 149 // Unregisters a callback. |
| 137 chrome.Event.prototype.removeListener = function(cb) { | 150 chrome.Event.prototype.removeListener = function(cb) { |
| 151 if (!this.eventOptions_.supportsListeners) |
| 152 throw new Error("This event does not support listeners."); |
| 138 var idx = this.findListener_(cb); | 153 var idx = this.findListener_(cb); |
| 139 if (idx == -1) { | 154 if (idx == -1) { |
| 140 return; | 155 return; |
| 141 } | 156 } |
| 142 | 157 |
| 143 this.listeners_.splice(idx, 1); | 158 this.listeners_.splice(idx, 1); |
| 144 if (this.listeners_.length == 0) { | 159 if (this.listeners_.length == 0) { |
| 145 this.detach_(); | 160 this.detach_(); |
| 146 } | 161 } |
| 147 }; | 162 }; |
| 148 | 163 |
| 149 // Test if the given callback is registered for this event. | 164 // Test if the given callback is registered for this event. |
| 150 chrome.Event.prototype.hasListener = function(cb) { | 165 chrome.Event.prototype.hasListener = function(cb) { |
| 166 if (!this.eventOptions_.supportsListeners) |
| 167 throw new Error("This event does not support listeners."); |
| 151 return this.findListener_(cb) > -1; | 168 return this.findListener_(cb) > -1; |
| 152 }; | 169 }; |
| 153 | 170 |
| 154 // Test if any callbacks are registered for this event. | 171 // Test if any callbacks are registered for this event. |
| 155 chrome.Event.prototype.hasListeners = function(cb) { | 172 chrome.Event.prototype.hasListeners = function() { |
| 173 if (!this.eventOptions_.supportsListeners) |
| 174 throw new Error("This event does not support listeners."); |
| 156 return this.listeners_.length > 0; | 175 return this.listeners_.length > 0; |
| 157 }; | 176 }; |
| 158 | 177 |
| 159 // Returns the index of the given callback if registered, or -1 if not | 178 // Returns the index of the given callback if registered, or -1 if not |
| 160 // found. | 179 // found. |
| 161 chrome.Event.prototype.findListener_ = function(cb) { | 180 chrome.Event.prototype.findListener_ = function(cb) { |
| 162 for (var i = 0; i < this.listeners_.length; i++) { | 181 for (var i = 0; i < this.listeners_.length; i++) { |
| 163 if (this.listeners_[i] == cb) { | 182 if (this.listeners_[i] == cb) { |
| 164 return i; | 183 return i; |
| 165 } | 184 } |
| 166 } | 185 } |
| 167 | 186 |
| 168 return -1; | 187 return -1; |
| 169 }; | 188 }; |
| 170 | 189 |
| 171 // Dispatches this event object to all listeners, passing all supplied | 190 // Dispatches this event object to all listeners, passing all supplied |
| 172 // arguments to this function each listener. | 191 // arguments to this function each listener. |
| 173 chrome.Event.prototype.dispatch = function(varargs) { | 192 chrome.Event.prototype.dispatch = function(varargs) { |
| 193 if (!this.eventOptions_.supportsListeners) |
| 194 throw new Error("This event does not support listeners."); |
| 174 var args = Array.prototype.slice.call(arguments); | 195 var args = Array.prototype.slice.call(arguments); |
| 175 if (this.validate_) { | 196 var validationErrors = this.validate_(args); |
| 176 var validationErrors = this.validate_(args); | 197 if (validationErrors) { |
| 177 if (validationErrors) { | 198 return validationErrors; |
| 178 return validationErrors; | |
| 179 } | |
| 180 } | 199 } |
| 181 for (var i = 0; i < this.listeners_.length; i++) { | 200 for (var i = 0; i < this.listeners_.length; i++) { |
| 182 try { | 201 try { |
| 183 this.listeners_[i].apply(null, args); | 202 this.listeners_[i].apply(null, args); |
| 184 } catch (e) { | 203 } catch (e) { |
| 185 console.error("Error in event handler for '" + this.eventName_ + | 204 console.error("Error in event handler for '" + this.eventName_ + |
| 186 "': " + e.stack); | 205 "': " + e.stack); |
| 187 } | 206 } |
| 188 } | 207 } |
| 189 }; | 208 }; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 220 | 239 |
| 221 delete attachedNamedEvents[this.eventName_]; | 240 delete attachedNamedEvents[this.eventName_]; |
| 222 }; | 241 }; |
| 223 | 242 |
| 224 chrome.Event.prototype.destroy_ = function() { | 243 chrome.Event.prototype.destroy_ = function() { |
| 225 this.listeners_ = []; | 244 this.listeners_ = []; |
| 226 this.validate_ = []; | 245 this.validate_ = []; |
| 227 this.detach_(); | 246 this.detach_(); |
| 228 }; | 247 }; |
| 229 | 248 |
| 249 chrome.Event.prototype.addRules = function(rules, opt_cb) { |
| 250 if (!this.eventOptions_.supportsRules) |
| 251 throw new Error("This event does not support rules."); |
| 252 if (!chrome.experimental || !chrome.experimental.declarative) { |
| 253 throw new Error("You must have access to the experimental.declarative " + |
| 254 "API to support rules in events"); |
| 255 } |
| 256 chrome.experimental.declarative.addRules(this.eventName_, rules, opt_cb); |
| 257 } |
| 258 |
| 259 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { |
| 260 if (!this.eventOptions_.supportsRules) |
| 261 throw new Error("This event does not support rules."); |
| 262 if (!chrome.experimental || !chrome.experimental.declarative) { |
| 263 throw new Error("You must have access to the experimental.declarative " + |
| 264 "API to support rules in events"); |
| 265 } |
| 266 chrome.experimental.declarative.removeRules( |
| 267 this.eventName_, ruleIdentifiers, opt_cb); |
| 268 } |
| 269 |
| 270 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { |
| 271 if (!this.eventOptions_.supportsRules) |
| 272 throw new Error("This event does not support rules."); |
| 273 if (!chrome.experimental || !chrome.experimental.declarative) { |
| 274 throw new Error("You must have access to the experimental.declarative " + |
| 275 "API to support rules in events"); |
| 276 } |
| 277 chrome.experimental.declarative.getRules( |
| 278 this.eventName_, ruleIdentifiers, cb); |
| 279 } |
| 280 |
| 230 // Special load events: we don't use the DOM unload because that slows | 281 // Special load events: we don't use the DOM unload because that slows |
| 231 // down tab shutdown. On the other hand, onUnload might not always fire, | 282 // down tab shutdown. On the other hand, onUnload might not always fire, |
| 232 // since Chrome will terminate renderers on shutdown (SuddenTermination). | 283 // since Chrome will terminate renderers on shutdown (SuddenTermination). |
| 233 chromeHidden.onLoad = new chrome.Event(); | 284 chromeHidden.onLoad = new chrome.Event(); |
| 234 chromeHidden.onUnload = new chrome.Event(); | 285 chromeHidden.onUnload = new chrome.Event(); |
| 235 | 286 |
| 236 chromeHidden.dispatchOnLoad = | 287 chromeHidden.dispatchOnLoad = |
| 237 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); | 288 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); |
| 238 | 289 |
| 239 chromeHidden.dispatchOnUnload = function() { | 290 chromeHidden.dispatchOnUnload = function() { |
| 240 chromeHidden.onUnload.dispatch(); | 291 chromeHidden.onUnload.dispatch(); |
| 241 for (var i = 0; i < allAttachedEvents.length; ++i) { | 292 for (var i = 0; i < allAttachedEvents.length; ++i) { |
| 242 var event = allAttachedEvents[i]; | 293 var event = allAttachedEvents[i]; |
| 243 if (event) | 294 if (event) |
| 244 event.detach_(); | 295 event.detach_(); |
| 245 } | 296 } |
| 246 }; | 297 }; |
| 247 | 298 |
| 248 chromeHidden.dispatchError = function(msg) { | 299 chromeHidden.dispatchError = function(msg) { |
| 249 console.error(msg); | 300 console.error(msg); |
| 250 }; | 301 }; |
| 251 })(); | 302 })(); |
| OLD | NEW |