| 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..032533e0c530473deb98f4ebdbecd27a77692efe 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) {
|
| 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.
|
| @@ -127,6 +137,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 +147,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 +162,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 +189,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_) {
|
| var validationErrors = this.validate_(args);
|
| @@ -227,6 +247,105 @@ var chrome = chrome || {};
|
| this.detach_();
|
| };
|
|
|
| + chrome.Event.prototype.getFunctionDefinition_ =
|
| + function(namespace, functionName) {
|
| + var filterNamespace = function(val) {return val.namespace === namespace;};
|
| + var apiSchema =
|
| + chromeHidden.GetExtensionAPIDefinition().filter(filterNamespace)[0];
|
| + var filterFunctionName = function (val) {return val.name === functionName;};
|
| + return apiSchema.functions.filter(filterFunctionName)[0];
|
| + }
|
| +
|
| + // 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};})
|
| + }
|
| + };
|
| + }
|
| +
|
| + // 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) {
|
| + for (var i = 0; i < rules.lengh; ++i) {
|
| + // TODO(battre): check for "".
|
| + if (!("id" in rule[i])) {
|
| + // Generate a unique ID.
|
| + var newRuleId = "";
|
| + do {
|
| + newRuleId = "_" + (this.lastGeneratedRuleId_++) + "_"
|
| + } while (newRuleId in this.ruleIds_);
|
| + // And store it.
|
| + rule[i]["id"] = newRuleId;
|
| + }
|
| + this.ruleIds_[rule[i]["id"]] = 1;
|
| + }
|
| + }
|
| +
|
| + chrome.Event.prototype.addRules = function(rules, opt_cb) {
|
| + if (!this.eventOptions_.supportsRules)
|
| + throw new Error("This event does not support rules.");
|
| +
|
| + this.validateRules_(rules,
|
| + this.eventOptions_.conditions,
|
| + this.eventOptions_.actions);
|
| + this.addMissingIds_(rules);
|
| + var callback = opt_cb ? opt_cb.bind(undefined, rules) : undefined;
|
| +
|
| + var functionDef =
|
| + this.getFunctionDefinition_("experimental.declarative", "addRules");
|
| + return chromeHidden.sendRequest.call(
|
| + this,
|
| + "experimental.declarative.addRules",
|
| + [this.eventName_, rules, callback],
|
| + functionDef.parameters);
|
| + }
|
| +
|
| + chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
|
| + if (!this.eventOptions_.supportsRules)
|
| + throw new Error("This event does not support rules.");
|
| + var functionDef =
|
| + this.getFunctionDefinition_("experimental.declarative", "removeRules");
|
| + return chromeHidden.sendRequest.call(
|
| + this,
|
| + "experimental.declarative.removeRules",
|
| + [this.eventName_, ruleIdentifiers, opt_cb],
|
| + functionDef.parameters);
|
| + }
|
| +
|
| + chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
|
| + if (!this.eventOptions_.supportsRules)
|
| + throw new Error("This event does not support rules.");
|
| + var functionDef =
|
| + this.getFunctionDefinition_("experimental.declarative", "getRules");
|
| + return chromeHidden.sendRequest.call(
|
| + this,
|
| + "experimental.declarative.getRules",
|
| + [this.eventName_, ruleIdentifiers, cb],
|
| + functionDef.parameters);
|
| + }
|
| +
|
| // 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).
|
| @@ -245,6 +364,8 @@ var chrome = chrome || {};
|
| var event = allAttachedEvents[i];
|
| if (event)
|
| event.detach_();
|
| + if (event && event.eventOptions_.supportsRules)
|
| + event.removeRules([]);
|
| }
|
| };
|
|
|
|
|