Chromium Code Reviews| 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 eventBindingsNatives = requireNative('event_bindings'); | 5 var eventBindingsNatives = requireNative('event_bindings'); |
| 6 var AttachEvent = eventBindingsNatives.AttachEvent; | 6 var AttachEvent = eventBindingsNatives.AttachEvent; |
| 7 var DetachEvent = eventBindingsNatives.DetachEvent; | 7 var DetachEvent = eventBindingsNatives.DetachEvent; |
| 8 var Print = eventBindingsNatives.Print; | 8 var Print = eventBindingsNatives.Print; |
| 9 var sendRequest = require('sendRequest').sendRequest; | |
|
Aaron Boodman
2012/05/09 19:29:46
TODO(aa): Look at this file more closely.
| |
| 9 | 10 |
| 10 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); | 11 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); |
| 11 | 12 |
| 12 // Local implementation of JSON.parse & JSON.stringify that protect us | 13 // Local implementation of JSON.parse & JSON.stringify that protect us |
| 13 // from being clobbered by an extension. | 14 // from being clobbered by an extension. |
| 14 // | 15 // |
| 15 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass | 16 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass |
| 16 // Values directly over IPC without serializing to strings and use | 17 // Values directly over IPC without serializing to strings and use |
| 17 // JSONValueConverter. | 18 // JSONValueConverter. |
| 18 chromeHidden.JSON = new (function() { | 19 chromeHidden.JSON = new (function() { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 // opt_eventName is required for events that support rules. | 54 // opt_eventName is required for events that support rules. |
| 54 // | 55 // |
| 55 // Example: | 56 // Example: |
| 56 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); | 57 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); |
| 57 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); | 58 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| 58 // chromeHidden.Event.dispatch("tab-changed", "hi"); | 59 // chromeHidden.Event.dispatch("tab-changed", "hi"); |
| 59 // will result in an alert dialog that says 'hi'. | 60 // will result in an alert dialog that says 'hi'. |
| 60 // | 61 // |
| 61 // If opt_eventOptions exists, it is a dictionary that contains the boolean | 62 // If opt_eventOptions exists, it is a dictionary that contains the boolean |
| 62 // entries "supportsListeners" and "supportsRules". | 63 // entries "supportsListeners" and "supportsRules". |
| 63 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { | 64 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions, |
|
not at google - send to devlin
2012/05/10 09:01:25
nit: arguments should line up or fix on 1 line :(
battre
2012/05/10 16:40:29
According to the JavaScript Style Guide, the rule
| |
| 65 opt_eventsSchema) { | |
| 64 this.eventName_ = opt_eventName; | 66 this.eventName_ = opt_eventName; |
| 65 this.listeners_ = []; | 67 this.listeners_ = []; |
| 66 this.eventOptions_ = opt_eventOptions || | 68 this.eventOptions_ = opt_eventOptions || |
| 67 {"supportsListeners": true, "supportsRules": false}; | 69 {"supportsListeners": true, "supportsRules": false}; |
| 70 this.eventsSchema_ = opt_eventsSchema; | |
| 68 | 71 |
| 69 if (this.eventOptions_.supportsRules && !opt_eventName) | 72 if (this.eventOptions_.supportsRules && !opt_eventName) |
| 70 throw new Error("Events that support rules require an event name."); | 73 throw new Error("Events that support rules require an event name."); |
| 71 | 74 |
| 72 // Validate event parameters if we are in debug. | 75 // Validate event arguments (the data that is passed to the callbacks) |
| 76 // if we are in debug. | |
| 73 if (opt_argSchemas && | 77 if (opt_argSchemas && |
| 74 chromeHidden.validateCallbacks && | 78 chromeHidden.validateCallbacks && |
| 75 chromeHidden.validate) { | 79 chromeHidden.validate) { |
| 76 | 80 |
| 77 this.validate_ = function(args) { | 81 this.validateEventArgs_ = function(args) { |
| 78 try { | 82 try { |
| 79 chromeHidden.validate(args, opt_argSchemas); | 83 chromeHidden.validate(args, opt_argSchemas); |
| 80 } catch (exception) { | 84 } catch (exception) { |
| 81 return "Event validation error during " + opt_eventName + " -- " + | 85 return "Event validation error during " + opt_eventName + " -- " + |
| 82 exception; | 86 exception; |
| 83 } | 87 } |
| 84 }; | 88 }; |
| 85 } else { | 89 } else { |
| 86 this.validate_ = function() {} | 90 this.validateEventArgs_ = function() {} |
| 91 } | |
| 92 | |
| 93 if (this.eventOptions_.supportsRules) { | |
| 94 if (!this.eventsSchema_) { | |
| 95 throw new Error("Events with rules support require the schema of the " + | |
| 96 "chrome.events namespace"); | |
| 97 } | |
| 98 // Determine schemas of function parameters. | |
| 99 var eventType = chromeHidden.lookup(this.eventsSchema_.types, | |
| 100 'id', 'Event'); | |
|
not at google - send to devlin
2012/05/10 09:01:25
nit: argument lineup thing (and on the lines below
battre
2012/05/10 16:40:29
See above
| |
| 101 this.addRulesParams_ = chromeHidden.lookup(eventType.functions, | |
| 102 'name', 'addRules').parameters; | |
| 103 this.getRulesParams_ = chromeHidden.lookup(eventType.functions, | |
| 104 'name', 'getRules').parameters; | |
| 105 this.removeRulesParams_ = chromeHidden.lookup(eventType.functions, | |
| 106 'name', 'removeRules').parameters; | |
| 107 } else { | |
| 108 this.addRulesParams_ = []; | |
| 109 this.getRulesParams_ = []; | |
| 110 this.removeRulesParams_ = []; | |
| 87 } | 111 } |
| 88 }; | 112 }; |
| 89 | 113 |
| 90 // A map of event names to the event object that is registered to that name. | 114 // A map of event names to the event object that is registered to that name. |
| 91 var attachedNamedEvents = {}; | 115 var attachedNamedEvents = {}; |
| 92 | 116 |
| 93 // An array of all attached event objects, used for detaching on unload. | 117 // An array of all attached event objects, used for detaching on unload. |
| 94 var allAttachedEvents = []; | 118 var allAttachedEvents = []; |
| 95 | 119 |
| 96 // A map of functions that massage event arguments before they are dispatched. | 120 // A map of functions that massage event arguments before they are dispatched. |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 | 210 |
| 187 return -1; | 211 return -1; |
| 188 }; | 212 }; |
| 189 | 213 |
| 190 // Dispatches this event object to all listeners, passing all supplied | 214 // Dispatches this event object to all listeners, passing all supplied |
| 191 // arguments to this function each listener. | 215 // arguments to this function each listener. |
| 192 chrome.Event.prototype.dispatch = function(varargs) { | 216 chrome.Event.prototype.dispatch = function(varargs) { |
| 193 if (!this.eventOptions_.supportsListeners) | 217 if (!this.eventOptions_.supportsListeners) |
| 194 throw new Error("This event does not support listeners."); | 218 throw new Error("This event does not support listeners."); |
| 195 var args = Array.prototype.slice.call(arguments); | 219 var args = Array.prototype.slice.call(arguments); |
| 196 var validationErrors = this.validate_(args); | 220 var validationErrors = this.validateEventArgs_(args); |
| 197 if (validationErrors) { | 221 if (validationErrors) { |
| 198 return {validationErrors: validationErrors}; | 222 return {validationErrors: validationErrors}; |
| 199 } | 223 } |
| 200 var results = []; | 224 var results = []; |
| 201 for (var i = 0; i < this.listeners_.length; i++) { | 225 for (var i = 0; i < this.listeners_.length; i++) { |
| 202 try { | 226 try { |
| 203 var result = this.listeners_[i].apply(null, args); | 227 var result = this.listeners_[i].apply(null, args); |
| 204 if (result !== undefined) | 228 if (result !== undefined) |
| 205 results.push(result); | 229 results.push(result); |
| 206 } catch (e) { | 230 } catch (e) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 240 if (!attachedNamedEvents[this.eventName_]) { | 264 if (!attachedNamedEvents[this.eventName_]) { |
| 241 throw new Error("chrome.Event '" + this.eventName_ + | 265 throw new Error("chrome.Event '" + this.eventName_ + |
| 242 "' is not attached."); | 266 "' is not attached."); |
| 243 } | 267 } |
| 244 | 268 |
| 245 delete attachedNamedEvents[this.eventName_]; | 269 delete attachedNamedEvents[this.eventName_]; |
| 246 }; | 270 }; |
| 247 | 271 |
| 248 chrome.Event.prototype.destroy_ = function() { | 272 chrome.Event.prototype.destroy_ = function() { |
| 249 this.listeners_ = []; | 273 this.listeners_ = []; |
| 250 this.validate_ = []; | 274 this.validateEventArgs_ = []; |
| 251 this.detach_(false); | 275 this.detach_(false); |
| 252 }; | 276 }; |
| 253 | 277 |
| 254 // Gets the declarative API object, or undefined if this extension doesn't | |
| 255 // have access to it. | |
| 256 // | |
| 257 // This is defined as a function (rather than a variable) because it isn't | |
| 258 // accessible until the schema bindings have been generated. | |
| 259 function getDeclarativeAPI() { | |
| 260 return chromeHidden.internalAPIs.declarative; | |
| 261 } | |
| 262 | |
| 263 chrome.Event.prototype.addRules = function(rules, opt_cb) { | 278 chrome.Event.prototype.addRules = function(rules, opt_cb) { |
| 264 if (!this.eventOptions_.supportsRules) | 279 if (!this.eventOptions_.supportsRules) |
| 265 throw new Error("This event does not support rules."); | 280 throw new Error("This event does not support rules."); |
| 266 if (!getDeclarativeAPI()) { | 281 |
| 267 throw new Error("You must have permission to use the declarative " + | 282 // Takes a list of JSON datatype identifiers and returns a schema fragment |
| 268 "API to support rules in events"); | 283 // that verifies that a JSON object corresponds to an array of only these |
| 284 // data types. | |
| 285 function buildArrayOfChoicesSchema(typesList) { | |
| 286 return { | |
| 287 'type': 'array', | |
| 288 'items': { | |
| 289 'choices': typesList.map(function(el) {return {'$ref': el};}) | |
| 290 } | |
| 291 }; | |
| 292 }; | |
| 293 | |
| 294 // Validate conditions and actions against specific schemas of this | |
| 295 // event object type. | |
| 296 // |rules| is an array of JSON objects that follow the Rule type of the | |
| 297 // declarative extension APIs. |conditions| is an array of JSON type | |
| 298 // identifiers that are allowed to occur in the conditions attribute of each | |
| 299 // rule. Likewise, |actions| is an array of JSON type identifiers that are | |
| 300 // allowed to occur in the actions attribute of each rule. | |
| 301 function validateRules(rules, conditions, actions) { | |
| 302 var conditionsSchema = buildArrayOfChoicesSchema(conditions); | |
| 303 var actionsSchema = buildArrayOfChoicesSchema(actions); | |
| 304 rules.forEach(function(rule) { | |
| 305 chromeHidden.validate([rule.conditions], [conditionsSchema]); | |
| 306 chromeHidden.validate([rule.actions], [actionsSchema]); | |
| 307 }) | |
| 308 }; | |
| 309 | |
| 310 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) { | |
| 311 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' + | |
| 312 'actions in the API specification.'); | |
| 269 } | 313 } |
| 270 getDeclarativeAPI().addRules(this.eventName_, rules, opt_cb); | 314 |
| 315 validateRules(rules, | |
| 316 this.eventOptions_.conditions, | |
| 317 this.eventOptions_.actions); | |
| 318 | |
| 319 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], | |
| 320 this.addRulesParams_); | |
|
not at google - send to devlin
2012/05/10 09:01:25
did we lose the ability to do chromeHidden.interna
battre
2012/05/10 16:40:29
Yes, because there is no such function anymore. "e
| |
| 271 } | 321 } |
| 272 | 322 |
| 273 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { | 323 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { |
| 274 if (!this.eventOptions_.supportsRules) | 324 if (!this.eventOptions_.supportsRules) |
| 275 throw new Error("This event does not support rules."); | 325 throw new Error("This event does not support rules."); |
| 276 if (!getDeclarativeAPI()) { | 326 sendRequest("events.removeRules", |
| 277 throw new Error("You must have permission to use the declarative " + | 327 [this.eventName_, ruleIdentifiers, opt_cb], |
| 278 "API to support rules in events"); | 328 this.removeRulesParams_); |
| 279 } | |
| 280 getDeclarativeAPI().removeRules( | |
| 281 this.eventName_, ruleIdentifiers, opt_cb); | |
| 282 } | 329 } |
| 283 | 330 |
| 284 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { | 331 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { |
| 285 if (!this.eventOptions_.supportsRules) | 332 if (!this.eventOptions_.supportsRules) |
| 286 throw new Error("This event does not support rules."); | 333 throw new Error("This event does not support rules."); |
| 287 if (!getDeclarativeAPI()) { | 334 sendRequest("events.getRules", |
| 288 throw new Error("You must have permission to use the declarative " + | 335 [this.eventName_, ruleIdentifiers, cb], |
| 289 "API to support rules in events"); | 336 this.getRulesParams_); |
| 290 } | |
| 291 getDeclarativeAPI().getRules( | |
| 292 this.eventName_, ruleIdentifiers, cb); | |
| 293 } | 337 } |
| 294 | 338 |
| 295 // Special load events: we don't use the DOM unload because that slows | 339 // Special load events: we don't use the DOM unload because that slows |
| 296 // down tab shutdown. On the other hand, onUnload might not always fire, | 340 // down tab shutdown. On the other hand, onUnload might not always fire, |
| 297 // since Chrome will terminate renderers on shutdown (SuddenTermination). | 341 // since Chrome will terminate renderers on shutdown (SuddenTermination). |
| 298 chromeHidden.onLoad = new chrome.Event(); | 342 chromeHidden.onLoad = new chrome.Event(); |
| 299 chromeHidden.onUnload = new chrome.Event(); | 343 chromeHidden.onUnload = new chrome.Event(); |
| 300 | 344 |
| 301 chromeHidden.dispatchOnLoad = | 345 chromeHidden.dispatchOnLoad = |
| 302 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); | 346 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); |
| 303 | 347 |
| 304 chromeHidden.dispatchOnUnload = function() { | 348 chromeHidden.dispatchOnUnload = function() { |
| 305 chromeHidden.onUnload.dispatch(); | 349 chromeHidden.onUnload.dispatch(); |
| 306 for (var i = 0; i < allAttachedEvents.length; ++i) { | 350 for (var i = 0; i < allAttachedEvents.length; ++i) { |
| 307 var event = allAttachedEvents[i]; | 351 var event = allAttachedEvents[i]; |
| 308 if (event) | 352 if (event) |
| 309 event.detach_(false); | 353 event.detach_(false); |
| 310 } | 354 } |
| 311 }; | 355 }; |
| 312 | 356 |
| 313 chromeHidden.dispatchError = function(msg) { | 357 chromeHidden.dispatchError = function(msg) { |
| 314 console.error(msg); | 358 console.error(msg); |
| 315 }; | 359 }; |
| OLD | NEW |