Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(213)

Side by Side Diff: chrome/renderer/resources/extensions/event.js

Issue 10392008: Move declarative API into events API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 sendRequest = require('sendRequest').sendRequest;
9 var lookup = require('utils').lookup;
8 10
9 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 11 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
10 12
11 // Local implementation of JSON.parse & JSON.stringify that protect us 13 // Local implementation of JSON.parse & JSON.stringify that protect us
12 // from being clobbered by an extension. 14 // from being clobbered by an extension.
13 // 15 //
14 // 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
15 // Values directly over IPC without serializing to strings and use 17 // Values directly over IPC without serializing to strings and use
16 // JSONValueConverter. 18 // JSONValueConverter.
17 chromeHidden.JSON = new (function() { 19 chromeHidden.JSON = new (function() {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 // opt_eventName is required for events that support rules. 54 // opt_eventName is required for events that support rules.
53 // 55 //
54 // Example: 56 // Example:
55 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); 57 // chrome.tabs.onChanged = new chrome.Event("tab-changed");
56 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); 58 // chrome.tabs.onChanged.addListener(function(data) { alert(data); });
57 // chromeHidden.Event.dispatch("tab-changed", "hi"); 59 // chromeHidden.Event.dispatch("tab-changed", "hi");
58 // will result in an alert dialog that says 'hi'. 60 // will result in an alert dialog that says 'hi'.
59 // 61 //
60 // 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
61 // entries "supportsListeners" and "supportsRules". 63 // entries "supportsListeners" and "supportsRules".
62 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { 64 chrome.Event = function(
65 opt_eventName, opt_argSchemas, opt_eventOptions, opt_eventsSchema) {
Aaron Boodman 2012/05/10 17:14:49 The need for the 'event' type's schema is an imple
battre 2012/05/10 18:05:22 I am just concerned that we might end up with mult
Aaron Boodman 2012/05/10 18:27:50 V8SchemaRegistry caches the js objects across the
63 this.eventName_ = opt_eventName; 66 this.eventName_ = opt_eventName;
64 this.listeners_ = []; 67 this.listeners_ = [];
65 this.eventOptions_ = opt_eventOptions || 68 this.eventOptions_ = opt_eventOptions ||
66 {"supportsListeners": true, "supportsRules": false}; 69 {"supportsListeners": true, "supportsRules": false};
70 this.eventsSchema_ = opt_eventsSchema;
67 71
68 if (this.eventOptions_.supportsRules && !opt_eventName) 72 if (this.eventOptions_.supportsRules && !opt_eventName)
69 throw new Error("Events that support rules require an event name."); 73 throw new Error("Events that support rules require an event name.");
70 74
71 // 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.
72 if (opt_argSchemas && 77 if (opt_argSchemas &&
73 chromeHidden.validateCallbacks && 78 chromeHidden.validateCallbacks &&
74 chromeHidden.validate) { 79 chromeHidden.validate) {
75 80
76 this.validate_ = function(args) { 81 this.validateEventArgs_ = function(args) {
77 try { 82 try {
78 chromeHidden.validate(args, opt_argSchemas); 83 chromeHidden.validate(args, opt_argSchemas);
79 } catch (exception) { 84 } catch (exception) {
80 return "Event validation error during " + opt_eventName + " -- " + 85 return "Event validation error during " + opt_eventName + " -- " +
81 exception; 86 exception;
82 } 87 }
83 }; 88 };
84 } else { 89 } else {
85 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 = lookup(this.eventsSchema_.types, 'id', 'Event');
Aaron Boodman 2012/05/10 17:14:49 This lookup is done for every event instance. It w
battre 2012/05/10 18:05:22 Done.
100 this.addRulesParams_ =
101 lookup(eventType.functions, 'name', 'addRules').parameters;
102 this.getRulesParams_ =
103 lookup(eventType.functions, 'name', 'getRules').parameters;
104 this.removeRulesParams_ =
105 lookup(eventType.functions, 'name', 'removeRules').parameters;
106 } else {
107 this.addRulesParams_ = [];
108 this.getRulesParams_ = [];
109 this.removeRulesParams_ = [];
86 } 110 }
87 }; 111 };
88 112
89 // A map of event names to the event object that is registered to that name. 113 // A map of event names to the event object that is registered to that name.
90 var attachedNamedEvents = {}; 114 var attachedNamedEvents = {};
91 115
92 // An array of all attached event objects, used for detaching on unload. 116 // An array of all attached event objects, used for detaching on unload.
93 var allAttachedEvents = []; 117 var allAttachedEvents = [];
94 118
95 // A map of functions that massage event arguments before they are dispatched. 119 // A map of functions that massage event arguments before they are dispatched.
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 209
186 return -1; 210 return -1;
187 }; 211 };
188 212
189 // Dispatches this event object to all listeners, passing all supplied 213 // Dispatches this event object to all listeners, passing all supplied
190 // arguments to this function each listener. 214 // arguments to this function each listener.
191 chrome.Event.prototype.dispatch = function(varargs) { 215 chrome.Event.prototype.dispatch = function(varargs) {
192 if (!this.eventOptions_.supportsListeners) 216 if (!this.eventOptions_.supportsListeners)
193 throw new Error("This event does not support listeners."); 217 throw new Error("This event does not support listeners.");
194 var args = Array.prototype.slice.call(arguments); 218 var args = Array.prototype.slice.call(arguments);
195 var validationErrors = this.validate_(args); 219 var validationErrors = this.validateEventArgs_(args);
196 if (validationErrors) { 220 if (validationErrors) {
197 return {validationErrors: validationErrors}; 221 return {validationErrors: validationErrors};
198 } 222 }
199 var results = []; 223 var results = [];
200 for (var i = 0; i < this.listeners_.length; i++) { 224 for (var i = 0; i < this.listeners_.length; i++) {
201 try { 225 try {
202 var result = this.listeners_[i].apply(null, args); 226 var result = this.listeners_[i].apply(null, args);
203 if (result !== undefined) 227 if (result !== undefined)
204 results.push(result); 228 results.push(result);
205 } catch (e) { 229 } catch (e) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 if (!attachedNamedEvents[this.eventName_]) { 263 if (!attachedNamedEvents[this.eventName_]) {
240 throw new Error("chrome.Event '" + this.eventName_ + 264 throw new Error("chrome.Event '" + this.eventName_ +
241 "' is not attached."); 265 "' is not attached.");
242 } 266 }
243 267
244 delete attachedNamedEvents[this.eventName_]; 268 delete attachedNamedEvents[this.eventName_];
245 }; 269 };
246 270
247 chrome.Event.prototype.destroy_ = function() { 271 chrome.Event.prototype.destroy_ = function() {
248 this.listeners_ = []; 272 this.listeners_ = [];
249 this.validate_ = []; 273 this.validateEventArgs_ = [];
250 this.detach_(false); 274 this.detach_(false);
251 }; 275 };
252 276
253 // Gets the declarative API object, or undefined if this extension doesn't
254 // have access to it.
255 //
256 // This is defined as a function (rather than a variable) because it isn't
257 // accessible until the schema bindings have been generated.
258 function getDeclarativeAPI() {
259 return chromeHidden.internalAPIs.declarative;
260 }
261
262 chrome.Event.prototype.addRules = function(rules, opt_cb) { 277 chrome.Event.prototype.addRules = function(rules, opt_cb) {
263 if (!this.eventOptions_.supportsRules) 278 if (!this.eventOptions_.supportsRules)
264 throw new Error("This event does not support rules."); 279 throw new Error("This event does not support rules.");
265 if (!getDeclarativeAPI()) { 280
266 throw new Error("You must have permission to use the declarative " + 281 // Takes a list of JSON datatype identifiers and returns a schema fragment
267 "API to support rules in events"); 282 // that verifies that a JSON object corresponds to an array of only these
283 // data types.
284 function buildArrayOfChoicesSchema(typesList) {
285 return {
286 'type': 'array',
287 'items': {
288 'choices': typesList.map(function(el) {return {'$ref': el};})
289 }
290 };
291 };
292
293 // Validate conditions and actions against specific schemas of this
294 // event object type.
295 // |rules| is an array of JSON objects that follow the Rule type of the
296 // declarative extension APIs. |conditions| is an array of JSON type
297 // identifiers that are allowed to occur in the conditions attribute of each
298 // rule. Likewise, |actions| is an array of JSON type identifiers that are
299 // allowed to occur in the actions attribute of each rule.
300 function validateRules(rules, conditions, actions) {
301 var conditionsSchema = buildArrayOfChoicesSchema(conditions);
302 var actionsSchema = buildArrayOfChoicesSchema(actions);
303 rules.forEach(function(rule) {
304 chromeHidden.validate([rule.conditions], [conditionsSchema]);
305 chromeHidden.validate([rule.actions], [actionsSchema]);
306 })
307 };
308
309 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) {
310 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' +
311 'actions in the API specification.');
268 } 312 }
269 getDeclarativeAPI().addRules(this.eventName_, rules, opt_cb); 313
314 validateRules(rules,
315 this.eventOptions_.conditions,
316 this.eventOptions_.actions);
317
318 sendRequest("events.addRules", [this.eventName_, rules, opt_cb],
319 this.addRulesParams_);
270 } 320 }
271 321
272 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { 322 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
273 if (!this.eventOptions_.supportsRules) 323 if (!this.eventOptions_.supportsRules)
274 throw new Error("This event does not support rules."); 324 throw new Error("This event does not support rules.");
275 if (!getDeclarativeAPI()) { 325 sendRequest("events.removeRules",
276 throw new Error("You must have permission to use the declarative " + 326 [this.eventName_, ruleIdentifiers, opt_cb],
277 "API to support rules in events"); 327 this.removeRulesParams_);
278 }
279 getDeclarativeAPI().removeRules(
280 this.eventName_, ruleIdentifiers, opt_cb);
281 } 328 }
282 329
283 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { 330 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
284 if (!this.eventOptions_.supportsRules) 331 if (!this.eventOptions_.supportsRules)
285 throw new Error("This event does not support rules."); 332 throw new Error("This event does not support rules.");
286 if (!getDeclarativeAPI()) { 333 sendRequest("events.getRules",
287 throw new Error("You must have permission to use the declarative " + 334 [this.eventName_, ruleIdentifiers, cb],
288 "API to support rules in events"); 335 this.getRulesParams_);
289 }
290 getDeclarativeAPI().getRules(
291 this.eventName_, ruleIdentifiers, cb);
292 } 336 }
293 337
294 // Special load events: we don't use the DOM unload because that slows 338 // Special load events: we don't use the DOM unload because that slows
295 // down tab shutdown. On the other hand, onUnload might not always fire, 339 // down tab shutdown. On the other hand, onUnload might not always fire,
296 // since Chrome will terminate renderers on shutdown (SuddenTermination). 340 // since Chrome will terminate renderers on shutdown (SuddenTermination).
297 chromeHidden.onLoad = new chrome.Event(); 341 chromeHidden.onLoad = new chrome.Event();
298 chromeHidden.onUnload = new chrome.Event(); 342 chromeHidden.onUnload = new chrome.Event();
299 343
300 chromeHidden.dispatchOnLoad = 344 chromeHidden.dispatchOnLoad =
301 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); 345 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad);
302 346
303 chromeHidden.dispatchOnUnload = function() { 347 chromeHidden.dispatchOnUnload = function() {
304 chromeHidden.onUnload.dispatch(); 348 chromeHidden.onUnload.dispatch();
305 for (var i = 0; i < allAttachedEvents.length; ++i) { 349 for (var i = 0; i < allAttachedEvents.length; ++i) {
306 var event = allAttachedEvents[i]; 350 var event = allAttachedEvents[i];
307 if (event) 351 if (event)
308 event.detach_(false); 352 event.detach_(false);
309 } 353 }
310 }; 354 };
311 355
312 chromeHidden.dispatchError = function(msg) { 356 chromeHidden.dispatchError = function(msg) {
313 console.error(msg); 357 console.error(msg);
314 }; 358 };
315 359
316 exports.Event = chrome.Event; 360 exports.Event = chrome.Event;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698