Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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(); |
| 11 //native function AddRules(rules); | |
| 12 //native function RemoveRules(rules); | |
| 11 | 13 |
| 12 var chromeHidden = GetChromeHidden(); | 14 var chromeHidden = GetChromeHidden(); |
| 13 | 15 |
| 14 // Local implementation of JSON.parse & JSON.stringify that protect us | 16 // Local implementation of JSON.parse & JSON.stringify that protect us |
| 15 // from being clobbered by an extension. | 17 // from being clobbered by an extension. |
| 16 // | 18 // |
| 17 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass | 19 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass |
| 18 // Values directly over IPC without serializing to strings and use | 20 // Values directly over IPC without serializing to strings and use |
| 19 // JSONValueConverter. | 21 // JSONValueConverter. |
| 20 chromeHidden.JSON = new (function() { | 22 chromeHidden.JSON = new (function() { |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 51 | 53 |
| 52 // Event object. If opt_eventName is provided, this object represents | 54 // Event object. If opt_eventName is provided, this object represents |
| 53 // the unique instance of that named event, and dispatching an event | 55 // the unique instance of that named event, and dispatching an event |
| 54 // with that name will route through this object's listeners. | 56 // with that name will route through this object's listeners. |
| 55 // | 57 // |
| 56 // Example: | 58 // Example: |
| 57 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); | 59 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); |
| 58 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); | 60 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| 59 // chromeHidden.Event.dispatch("tab-changed", "hi"); | 61 // chromeHidden.Event.dispatch("tab-changed", "hi"); |
| 60 // will result in an alert dialog that says 'hi'. | 62 // will result in an alert dialog that says 'hi'. |
| 61 chrome.Event = function(opt_eventName, opt_argSchemas) { | 63 // |
| 64 // If opt_eventOptions exists, it is a dictionary that contains the boolean | |
| 65 // entries "supports_listeners" and "supports_rules". | |
| 66 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions, | |
| 67 opt_typesAPI) { | |
| 62 this.eventName_ = opt_eventName; | 68 this.eventName_ = opt_eventName; |
| 63 this.listeners_ = []; | 69 this.listeners_ = []; |
| 70 this.eventOptions_ = opt_eventOptions || | |
| 71 {'supports_listeners': true, 'supports_rules': false}; | |
|
Aaron Boodman
2012/01/19 23:09:46
Nit: don't need the single quotes in the object li
Matt Perry
2012/01/20 00:07:55
also, use camelCase for dict keys
battre
2012/01/24 20:36:16
Done.
battre
2012/01/24 20:36:16
Done.
| |
| 72 if (opt_typesAPI) { | |
| 73 this.sendRequest_ = opt_typesAPI.sendRequest; | |
| 74 this.apiDefinitions_ = opt_typesAPI.apiDefinitions; | |
| 75 } else { | |
| 76 this.sendRequest_ = function() {}; | |
| 77 this.apiDefinitions_ = {}; | |
| 78 } | |
| 64 | 79 |
| 65 // Validate event parameters if we are in debug. | 80 // Validate event parameters if we are in debug. |
| 66 if (opt_argSchemas && | 81 if (opt_argSchemas && |
| 67 chromeHidden.validateCallbacks && | 82 chromeHidden.validateCallbacks && |
| 68 chromeHidden.validate) { | 83 chromeHidden.validate) { |
| 69 | 84 |
| 70 this.validate_ = function(args) { | 85 this.validate_ = function(args) { |
| 71 try { | 86 try { |
| 72 chromeHidden.validate(args, opt_argSchemas); | 87 chromeHidden.validate(args, opt_argSchemas); |
| 73 } catch (exception) { | 88 } catch (exception) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 }; | 135 }; |
| 121 | 136 |
| 122 // Test if a named event has any listeners. | 137 // Test if a named event has any listeners. |
| 123 chromeHidden.Event.hasListener = function(name) { | 138 chromeHidden.Event.hasListener = function(name) { |
| 124 return (attachedNamedEvents[name] && | 139 return (attachedNamedEvents[name] && |
| 125 attachedNamedEvents[name].listeners_.length > 0); | 140 attachedNamedEvents[name].listeners_.length > 0); |
| 126 }; | 141 }; |
| 127 | 142 |
| 128 // Registers a callback to be called when this event is dispatched. | 143 // Registers a callback to be called when this event is dispatched. |
| 129 chrome.Event.prototype.addListener = function(cb) { | 144 chrome.Event.prototype.addListener = function(cb) { |
| 145 if (!this.eventOptions_.supports_listeners) | |
| 146 throw new Error("This event does not support listeners."); | |
| 130 if (this.listeners_.length == 0) { | 147 if (this.listeners_.length == 0) { |
| 131 this.attach_(); | 148 this.attach_(); |
| 132 } | 149 } |
| 133 this.listeners_.push(cb); | 150 this.listeners_.push(cb); |
| 134 }; | 151 }; |
| 135 | 152 |
| 136 // Unregisters a callback. | 153 // Unregisters a callback. |
| 137 chrome.Event.prototype.removeListener = function(cb) { | 154 chrome.Event.prototype.removeListener = function(cb) { |
| 155 if (!this.eventOptions_.supports_listeners) | |
| 156 throw new Error("This event does not support listeners."); | |
| 138 var idx = this.findListener_(cb); | 157 var idx = this.findListener_(cb); |
| 139 if (idx == -1) { | 158 if (idx == -1) { |
| 140 return; | 159 return; |
| 141 } | 160 } |
| 142 | 161 |
| 143 this.listeners_.splice(idx, 1); | 162 this.listeners_.splice(idx, 1); |
| 144 if (this.listeners_.length == 0) { | 163 if (this.listeners_.length == 0) { |
| 145 this.detach_(); | 164 this.detach_(); |
| 146 } | 165 } |
| 147 }; | 166 }; |
| 148 | 167 |
| 149 // Test if the given callback is registered for this event. | 168 // Test if the given callback is registered for this event. |
| 150 chrome.Event.prototype.hasListener = function(cb) { | 169 chrome.Event.prototype.hasListener = function(cb) { |
| 170 if (!this.eventOptions_.supports_listeners) | |
| 171 throw new Error("This event does not support listeners."); | |
| 151 return this.findListener_(cb) > -1; | 172 return this.findListener_(cb) > -1; |
| 152 }; | 173 }; |
| 153 | 174 |
| 154 // Test if any callbacks are registered for this event. | 175 // Test if any callbacks are registered for this event. |
| 155 chrome.Event.prototype.hasListeners = function(cb) { | 176 chrome.Event.prototype.hasListeners = function(cb) { |
|
Aaron Boodman
2012/01/19 23:09:46
whoops: the cb param here not required.
battre
2012/01/24 20:36:16
Done.
| |
| 177 if (!this.eventOptions_.supports_listeners) | |
| 178 throw new Error("This event does not support listeners."); | |
| 156 return this.listeners_.length > 0; | 179 return this.listeners_.length > 0; |
| 157 }; | 180 }; |
| 158 | 181 |
| 159 // Returns the index of the given callback if registered, or -1 if not | 182 // Returns the index of the given callback if registered, or -1 if not |
| 160 // found. | 183 // found. |
| 161 chrome.Event.prototype.findListener_ = function(cb) { | 184 chrome.Event.prototype.findListener_ = function(cb) { |
| 162 for (var i = 0; i < this.listeners_.length; i++) { | 185 for (var i = 0; i < this.listeners_.length; i++) { |
| 163 if (this.listeners_[i] == cb) { | 186 if (this.listeners_[i] == cb) { |
| 164 return i; | 187 return i; |
| 165 } | 188 } |
| 166 } | 189 } |
| 167 | 190 |
| 168 return -1; | 191 return -1; |
| 169 }; | 192 }; |
| 170 | 193 |
| 171 // Dispatches this event object to all listeners, passing all supplied | 194 // Dispatches this event object to all listeners, passing all supplied |
| 172 // arguments to this function each listener. | 195 // arguments to this function each listener. |
| 173 chrome.Event.prototype.dispatch = function(varargs) { | 196 chrome.Event.prototype.dispatch = function(varargs) { |
| 197 if (!this.eventOptions_.supports_listeners) | |
| 198 throw new Error("This event does not support listeners."); | |
| 174 var args = Array.prototype.slice.call(arguments); | 199 var args = Array.prototype.slice.call(arguments); |
| 175 if (this.validate_) { | 200 if (this.validate_) { |
| 176 var validationErrors = this.validate_(args); | 201 var validationErrors = this.validate_(args); |
| 177 if (validationErrors) { | 202 if (validationErrors) { |
| 178 return validationErrors; | 203 return validationErrors; |
| 179 } | 204 } |
| 180 } | 205 } |
| 181 for (var i = 0; i < this.listeners_.length; i++) { | 206 for (var i = 0; i < this.listeners_.length; i++) { |
| 182 try { | 207 try { |
| 183 this.listeners_[i].apply(null, args); | 208 this.listeners_[i].apply(null, args); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 | 245 |
| 221 delete attachedNamedEvents[this.eventName_]; | 246 delete attachedNamedEvents[this.eventName_]; |
| 222 }; | 247 }; |
| 223 | 248 |
| 224 chrome.Event.prototype.destroy_ = function() { | 249 chrome.Event.prototype.destroy_ = function() { |
| 225 this.listeners_ = []; | 250 this.listeners_ = []; |
| 226 this.validate_ = []; | 251 this.validate_ = []; |
| 227 this.detach_(); | 252 this.detach_(); |
| 228 }; | 253 }; |
| 229 | 254 |
| 255 chrome.Event.prototype.getFunctionDefinition_ = | |
| 256 function(namespace, functionName) { | |
| 257 var filterNamespace = function(val) {return val.namespace === namespace;}; | |
| 258 var apiSchema = this.apiDefinitions_.filter(filterNamespace)[0]; | |
| 259 var filterFunctionName = function (val) {return val.name === functionName;}; | |
| 260 return apiSchema.functions.filter(filterFunctionName)[0]; | |
| 261 } | |
| 262 | |
| 263 chrome.Event.prototype.addRules = function(rules, opt_cb) { | |
| 264 if (!this.eventOptions_.supports_rules) | |
|
Aaron Boodman
2012/01/19 23:09:46
nit: maybe just put supports_rules_ on |this|?
battre
2012/01/24 20:36:16
hm... I think having this as a separate dictionary
| |
| 265 throw new Error("This event does not support listeners."); | |
|
Aaron Boodman
2012/01/19 23:09:46
s/listener/rule/
battre
2012/01/24 20:36:16
Done.
| |
| 266 // TODO: wrap callback in such a way that | |
| 267 // - the rules are stored | |
| 268 // - rule IDs are updated | |
| 269 | |
| 270 // Validate conditions and actions against specific schemas of this | |
| 271 // event object type. | |
| 272 var conditionsSchema = this.eventOptions_.conditions; | |
| 273 var actionsSchema = this.eventOptions_.actions; | |
| 274 if (!conditionsSchema || !actionsSchema) { | |
| 275 throw new Error("Error in API specification."); | |
| 276 } | |
| 277 rules.forEach(function(rule) { | |
| 278 chromeHidden.validate([rule.conditions], [conditionsSchema]); | |
| 279 chromeHidden.validate([rule.actions], [actionsSchema]); | |
|
Aaron Boodman
2012/01/19 23:09:46
Interesting :) I hadn't considered that we could j
| |
| 280 }) | |
| 281 | |
| 282 var functionDef = | |
| 283 this.getFunctionDefinition_("experimental.declarative", "addRules"); | |
|
Aaron Boodman
2012/01/19 23:09:46
I think it might be easier to just use raw IPC her
battre
2012/01/19 23:45:48
I started off with this approach. Here is the deal
Matt Perry
2012/01/20 00:07:55
This seems reasonable to me. No sense rolling cust
battre
2012/01/24 20:36:16
We don't have access to these functions, they are
| |
| 284 return this.sendRequest_.call(this, | |
| 285 "experimental.declarative.addRules", | |
| 286 [this.eventName_, rules, opt_cb], | |
| 287 functionDef.parameters); | |
| 288 } | |
| 289 | |
| 290 chrome.Event.prototype.removeRules = function(cb) { | |
| 291 if (!this.eventOptions_.supports_rules) | |
| 292 throw new Error("This event does not support listeners."); | |
| 293 // TODO(battre): Remove rules. | |
| 294 } | |
| 295 | |
| 296 chrome.Event.prototype.getRules = function(rule_identifiers, cb) { | |
| 297 if (!this.eventOptions_.supports_rules) | |
| 298 throw new Error("This event does not support listeners."); | |
| 299 // TODO(battre): Get rules. | |
| 300 cb.apply(this, []); | |
| 301 } | |
| 302 | |
| 230 // Special load events: we don't use the DOM unload because that slows | 303 // 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, | 304 // down tab shutdown. On the other hand, onUnload might not always fire, |
| 232 // since Chrome will terminate renderers on shutdown (SuddenTermination). | 305 // since Chrome will terminate renderers on shutdown (SuddenTermination). |
| 233 chromeHidden.onLoad = new chrome.Event(); | 306 chromeHidden.onLoad = new chrome.Event(); |
| 234 chromeHidden.onUnload = new chrome.Event(); | 307 chromeHidden.onUnload = new chrome.Event(); |
| 235 | 308 |
| 236 chromeHidden.dispatchOnLoad = function(extensionId, isExtensionProcess, | 309 chromeHidden.dispatchOnLoad = function(extensionId, isExtensionProcess, |
| 237 isIncognitoContext) { | 310 isIncognitoContext) { |
| 238 chromeHidden.onLoad.dispatch(extensionId, isExtensionProcess, | 311 chromeHidden.onLoad.dispatch(extensionId, isExtensionProcess, |
| 239 isIncognitoContext); | 312 isIncognitoContext); |
| 240 }; | 313 }; |
| 241 | 314 |
| 242 chromeHidden.dispatchOnUnload = function() { | 315 chromeHidden.dispatchOnUnload = function() { |
| 243 chromeHidden.onUnload.dispatch(); | 316 chromeHidden.onUnload.dispatch(); |
| 244 for (var i = 0; i < allAttachedEvents.length; ++i) { | 317 for (var i = 0; i < allAttachedEvents.length; ++i) { |
| 245 var event = allAttachedEvents[i]; | 318 var event = allAttachedEvents[i]; |
| 246 if (event) | 319 if (event) |
| 247 event.detach_(); | 320 event.detach_(); |
| 248 } | 321 } |
| 322 // TODO(battre): Remove all rules | |
| 249 }; | 323 }; |
| 250 | 324 |
| 251 chromeHidden.dispatchError = function(msg) { | 325 chromeHidden.dispatchError = function(msg) { |
| 252 console.error(msg); | 326 console.error(msg); |
| 253 }; | 327 }; |
| 254 })(); | 328 })(); |
| OLD | NEW |