Chromium Code Reviews| Index: chrome/renderer/resources/extensions/event.js |
| diff --git a/chrome/renderer/resources/extensions/event.js b/chrome/renderer/resources/extensions/event.js |
| index 45d7080a6c478b2fbc9002e6ccba527ea80c57a2..b1ab1e13ecbdd7fb45d5ce012cabddce2bfcd9e4 100644 |
| --- a/chrome/renderer/resources/extensions/event.js |
| +++ b/chrome/renderer/resources/extensions/event.js |
| @@ -58,9 +58,14 @@ var chrome = chrome || {}; |
| // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); |
| // chromeHidden.Event.dispatch("tab-changed", "hi"); |
| // will result in an alert dialog that says 'hi'. |
| - chrome.Event = function(opt_eventName, opt_argSchemas) { |
| + // |
| + // If opt_eventOptions exists, it is a dictionary that contains the boolean |
| + // entries "supportsListeners" and "supportsRules". |
| + chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { |
|
not at google - send to devlin
2012/02/01 07:23:12
eventCapabilities might be a better name?
battre
2012/02/01 17:35:39
Even if we continue adding stuff, such in this exa
not at google - send to devlin
2012/02/01 23:07:23
Yep, never mind. They're also defined as "options"
|
| this.eventName_ = opt_eventName; |
| this.listeners_ = []; |
| + this.eventOptions_ = opt_eventOptions || |
| + {"supportsListeners": true, "supportsRules": false}; |
| // Validate event parameters if we are in debug. |
| if (opt_argSchemas && |
| @@ -75,7 +80,12 @@ var chrome = chrome || {}; |
| exception; |
| } |
| }; |
| + } else { |
| + this.validate_ = function() {} |
| } |
| + |
| + this.ruleIds_ = {}; |
| + this.lastGeneratedRuleId_ = 0; |
| }; |
| // A map of event names to the event object that is registered to that name. |
| @@ -84,6 +94,9 @@ var chrome = chrome || {}; |
| // An array of all attached event objects, used for detaching on unload. |
| var allAttachedEvents = []; |
| + // An array of all events that have had rules added the them at any time. |
| + var allEventsWithRules = []; |
| + |
| // A map of functions that massage event arguments before they are dispatched. |
| // Key is event name, value is function. |
| var eventArgumentMassagers = {}; |
| @@ -127,6 +140,8 @@ var chrome = chrome || {}; |
| // Registers a callback to be called when this event is dispatched. |
| chrome.Event.prototype.addListener = function(cb) { |
| + if (!this.eventOptions_.supportsListeners) |
| + throw new Error("This event does not support listeners."); |
| if (this.listeners_.length == 0) { |
| this.attach_(); |
| } |
| @@ -135,6 +150,8 @@ var chrome = chrome || {}; |
| // Unregisters a callback. |
| chrome.Event.prototype.removeListener = function(cb) { |
| + if (!this.eventOptions_.supportsListeners) |
| + throw new Error("This event does not support listeners."); |
| var idx = this.findListener_(cb); |
| if (idx == -1) { |
| return; |
| @@ -148,11 +165,15 @@ var chrome = chrome || {}; |
| // Test if the given callback is registered for this event. |
| chrome.Event.prototype.hasListener = function(cb) { |
| + if (!this.eventOptions_.supportsListeners) |
| + throw new Error("This event does not support listeners."); |
| return this.findListener_(cb) > -1; |
| }; |
| // Test if any callbacks are registered for this event. |
| - chrome.Event.prototype.hasListeners = function(cb) { |
| + chrome.Event.prototype.hasListeners = function() { |
| + if (!this.eventOptions_.supportsListeners) |
| + throw new Error("This event does not support listeners."); |
| return this.listeners_.length > 0; |
| }; |
| @@ -171,6 +192,8 @@ var chrome = chrome || {}; |
| // Dispatches this event object to all listeners, passing all supplied |
| // arguments to this function each listener. |
| chrome.Event.prototype.dispatch = function(varargs) { |
| + if (!this.eventOptions_.supportsListeners) |
| + throw new Error("This event does not support listeners."); |
| var args = Array.prototype.slice.call(arguments); |
| if (this.validate_) { |
|
not at google - send to devlin
2012/02/01 07:23:12
now that there's always set a validate_ method, th
|
| var validationErrors = this.validate_(args); |
| @@ -227,6 +250,105 @@ var chrome = chrome || {}; |
| this.detach_(); |
| }; |
| + // Takes a list of JSON datatype identifiers and returns a schema fragment |
| + // that verifies that a JSON object corresponds to an array of only these |
| + // data types. |
| + chrome.Event.prototype.buildArrayOfChoicesSchema_ = function(typesList) { |
| + return { |
| + "type": "array", |
| + "items": { |
| + "choices": typesList.forEach(function(el) {return {"$ref": el};}) |
|
not at google - send to devlin
2012/02/01 08:15:57
What does this do? I thought forEach has no return
battre
2012/02/01 17:35:39
This should have been "map" instead of "forEach".
|
| + } |
| + }; |
| + } |
| + |
| + // Validate conditions and actions against specific schemas of this |
| + // event object type. |
| + // |rules| is an array of JSON objects that follow the Rule type of the |
| + // declarative extension APIs. |conditions| is an array of JSON type |
| + // identifiers that are allowed to occur in the conditions attribute of each |
| + // rule. Likewise, |actions| is an array of JSON type identifiers that are |
| + // allowed to occur in the actions attribute of each rule. |
| + chrome.Event.prototype.validateRules_ = function(rules, conditions, actions) { |
| + if (!conditions || !actions) { |
| + throw new Error("Error in API specification."); |
| + } |
| + var conditionsSchema = this.buildArrayOfChoicesSchema_(conditions); |
| + var actionsSchema = this.buildArrayOfChoicesSchema_(actions); |
| + rules.forEach(function(rule) { |
| + chromeHidden.validate([rule.conditions], [conditionsSchema]); |
| + chromeHidden.validate([rule.actions], [actionsSchema]); |
| + }) |
| + } |
| + |
| + chrome.Event.prototype.addMissingIds_ = function(rules) { |
|
not at google - send to devlin
2012/02/01 07:23:12
This, and addMissingPriorities, should be done in
battre
2012/02/01 17:35:39
This does not work:
We want to tell the extension
|
| + for (var i = 0; i < rules.length; ++i) { |
| + // TODO(battre): check for "". |
| + if (!("id" in rules[i])) { |
| + // Generate a unique ID. |
| + var newRuleId = ""; |
| + do { |
| + newRuleId = "_" + (this.lastGeneratedRuleId_++) + "_" |
| + } while (newRuleId in this.ruleIds_); |
| + // And store it. |
| + rules[i]["id"] = newRuleId; |
| + } |
| + this.ruleIds_[rules[i]["id"]] = 1; |
| + } |
| + } |
| + |
| + chrome.Event.prototype.addMissingPriorities_ = function(rules) { |
| + for (var i = 0; i < rules.length; ++i) { |
| + if (!("priority" in rules[i])) |
| + rules[i]["priority"] = 100; |
| + } |
| + } |
| + |
| + chrome.Event.prototype.addRules = function(rules, opt_cb) { |
|
not at google - send to devlin
2012/02/01 07:23:12
I think that this should just be a thin wrapper ar
battre
2012/02/01 17:35:39
Isn't it a pretty thin wrapper? Which part would y
not at google - send to devlin
2012/02/01 23:07:23
Yep, it is now. By "thin wrapper" I meant... what
|
| + if (!this.eventOptions_.supportsRules) |
| + throw new Error("This event does not support rules."); |
| + |
| + var found = false; |
| + for (var i = 0; i < allEventsWithRules.length; ++i) { |
| + if (allEventsWithRules === this) { |
| + found = true; |
| + break; |
| + } |
| + } |
| + if (!found) |
| + allEventsWithRules.push(this); |
|
not at google - send to devlin
2012/02/01 07:23:12
This implies an O(n) iteration over all events eac
battre
2012/02/01 17:35:39
I have removed this logic, see below.
|
| + |
| + this.validateRules_(rules, |
|
not at google - send to devlin
2012/02/01 07:23:12
I would have thought that the schema-based validat
not at google - send to devlin
2012/02/01 08:15:57
I may have gotten my wires a little bit crossed wh
battre
2012/02/01 17:35:39
I am not sure how how we will implement these cons
|
| + this.eventOptions_.conditions, |
| + this.eventOptions_.actions); |
| + this.addMissingIds_(rules); |
| + this.addMissingPriorities_(rules); |
| + var callback = opt_cb ? opt_cb.bind(undefined, rules) : undefined; |
| + |
| + return chromeHidden.validatingSendRequest.call( |
|
not at google - send to devlin
2012/02/01 07:23:12
This is interesting. All you really need to do, at
battre
2012/02/01 17:35:39
Wow, this became significantly simpler.
Done.
|
| + this, |
| + "experimental.declarative.addRules", |
| + [this.eventName_, rules, callback]); |
| + } |
| + |
| + chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { |
| + if (!this.eventOptions_.supportsRules) |
| + throw new Error("This event does not support rules."); |
| + return chromeHidden.validatingSendRequest.call( |
| + this, |
| + "experimental.declarative.removeRules", |
| + [this.eventName_, ruleIdentifiers, opt_cb]); |
| + } |
| + |
| + chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { |
| + if (!this.eventOptions_.supportsRules) |
| + throw new Error("This event does not support rules."); |
| + return chromeHidden.validatingSendRequest.call( |
| + this, |
| + "experimental.declarative.getRules", |
| + [this.eventName_, ruleIdentifiers, cb]); |
| + } |
| + |
| // Special load events: we don't use the DOM unload because that slows |
| // down tab shutdown. On the other hand, onUnload might not always fire, |
| // since Chrome will terminate renderers on shutdown (SuddenTermination). |
| @@ -246,6 +368,11 @@ var chrome = chrome || {}; |
| if (event) |
| event.detach_(); |
| } |
| + for (var i = 0; i < allEventsWithRules.length; ++i) { |
| + var event = allEventsWithRules[i]; |
| + if (event && event.eventOptions_.supportsRules) |
| + event.removeRules([]); |
|
not at google - send to devlin
2012/02/01 07:23:12
Why [] ? Is this to signify removing all rules?
T
battre
2012/02/01 17:35:39
I have removed this logic because
a) it did not wo
|
| + } |
| }; |
| chromeHidden.dispatchError = function(msg) { |