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

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: Extend apiDefinitions.GetExtensionAPIDefinition to return individual apis 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 utils = require('utils');
8 10
9 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 11 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
12 var GetExtensionAPIDefinition =
13 requireNative('apiDefinitions').GetExtensionAPIDefinition;
14
15 // Schema definitions for the parameters of functions of Event.
16 var addRulesParams;
17 var getRulesParams;
18 var removeRulesParams;
not at google - send to devlin 2012/05/14 07:29:38 I find this a bit funny to look at out of context.
battre 2012/05/15 16:50:09 Done.
19
20 // This function needs to be called once (only in case Events with rules are
21 // instantiated) to provide the schema definitions for function calls.
22 // |eventsSchema| is the schema of the "events" namespace.
23 function storeFunctionSchemes(eventsSchema) {
24 // Schema definition of Events object.
not at google - send to devlin 2012/05/14 07:29:38 Schemes -> Schemas?
battre 2012/05/15 16:50:09 Done.
25 var eventType = utils.lookup(eventsSchema.types, 'id', 'Event');
26
27 addRulesParams =
28 utils.lookup(eventType.functions, 'name', 'addRules').parameters;
29 getRulesParams =
30 utils.lookup(eventType.functions, 'name', 'getRules').parameters;
31 removeRulesParams =
32 utils.lookup(eventType.functions, 'name', 'removeRules').parameters;
33 }
10 34
11 // Local implementation of JSON.parse & JSON.stringify that protect us 35 // Local implementation of JSON.parse & JSON.stringify that protect us
12 // from being clobbered by an extension. 36 // from being clobbered by an extension.
13 // 37 //
14 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass 38 // 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 39 // Values directly over IPC without serializing to strings and use
16 // JSONValueConverter. 40 // JSONValueConverter.
17 chromeHidden.JSON = new (function() { 41 chromeHidden.JSON = new (function() {
18 var $Object = Object; 42 var $Object = Object;
19 var $Array = Array; 43 var $Array = Array;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 // entries "supportsListeners" and "supportsRules". 85 // entries "supportsListeners" and "supportsRules".
62 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { 86 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) {
63 this.eventName_ = opt_eventName; 87 this.eventName_ = opt_eventName;
64 this.listeners_ = []; 88 this.listeners_ = [];
65 this.eventOptions_ = opt_eventOptions || 89 this.eventOptions_ = opt_eventOptions ||
66 {"supportsListeners": true, "supportsRules": false}; 90 {"supportsListeners": true, "supportsRules": false};
67 91
68 if (this.eventOptions_.supportsRules && !opt_eventName) 92 if (this.eventOptions_.supportsRules && !opt_eventName)
69 throw new Error("Events that support rules require an event name."); 93 throw new Error("Events that support rules require an event name.");
70 94
71 // Validate event parameters if we are in debug. 95 // Validate event arguments (the data that is passed to the callbacks)
96 // if we are in debug.
72 if (opt_argSchemas && 97 if (opt_argSchemas &&
73 chromeHidden.validateCallbacks && 98 chromeHidden.validateCallbacks &&
74 chromeHidden.validate) { 99 chromeHidden.validate) {
75 100
76 this.validate_ = function(args) { 101 this.validateEventArgs_ = function(args) {
77 try { 102 try {
78 chromeHidden.validate(args, opt_argSchemas); 103 chromeHidden.validate(args, opt_argSchemas);
79 } catch (exception) { 104 } catch (exception) {
80 return "Event validation error during " + opt_eventName + " -- " + 105 return "Event validation error during " + opt_eventName + " -- " +
81 exception; 106 exception;
82 } 107 }
83 }; 108 };
84 } else { 109 } else {
85 this.validate_ = function() {} 110 this.validateEventArgs_ = function() {}
111 }
112
113 // Initialize schema for functions lazily.
114 if (this.eventOptions_.supportsRules && !addRulesParams) {
115 storeFunctionSchemes(GetExtensionAPIDefinition("events")[0]);
not at google - send to devlin 2012/05/14 07:29:38 You really only need to load these right before ca
battre 2012/05/15 16:50:09 Done.
86 } 116 }
87 }; 117 };
88 118
89 // A map of event names to the event object that is registered to that name. 119 // A map of event names to the event object that is registered to that name.
90 var attachedNamedEvents = {}; 120 var attachedNamedEvents = {};
91 121
92 // An array of all attached event objects, used for detaching on unload. 122 // An array of all attached event objects, used for detaching on unload.
93 var allAttachedEvents = []; 123 var allAttachedEvents = [];
94 124
95 // A map of functions that massage event arguments before they are dispatched. 125 // 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 215
186 return -1; 216 return -1;
187 }; 217 };
188 218
189 // Dispatches this event object to all listeners, passing all supplied 219 // Dispatches this event object to all listeners, passing all supplied
190 // arguments to this function each listener. 220 // arguments to this function each listener.
191 chrome.Event.prototype.dispatch = function(varargs) { 221 chrome.Event.prototype.dispatch = function(varargs) {
192 if (!this.eventOptions_.supportsListeners) 222 if (!this.eventOptions_.supportsListeners)
193 throw new Error("This event does not support listeners."); 223 throw new Error("This event does not support listeners.");
194 var args = Array.prototype.slice.call(arguments); 224 var args = Array.prototype.slice.call(arguments);
195 var validationErrors = this.validate_(args); 225 var validationErrors = this.validateEventArgs_(args);
196 if (validationErrors) { 226 if (validationErrors) {
197 return {validationErrors: validationErrors}; 227 return {validationErrors: validationErrors};
198 } 228 }
199 var results = []; 229 var results = [];
200 for (var i = 0; i < this.listeners_.length; i++) { 230 for (var i = 0; i < this.listeners_.length; i++) {
201 try { 231 try {
202 var result = this.listeners_[i].apply(null, args); 232 var result = this.listeners_[i].apply(null, args);
203 if (result !== undefined) 233 if (result !== undefined)
204 results.push(result); 234 results.push(result);
205 } catch (e) { 235 } catch (e) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 if (!attachedNamedEvents[this.eventName_]) { 269 if (!attachedNamedEvents[this.eventName_]) {
240 throw new Error("chrome.Event '" + this.eventName_ + 270 throw new Error("chrome.Event '" + this.eventName_ +
241 "' is not attached."); 271 "' is not attached.");
242 } 272 }
243 273
244 delete attachedNamedEvents[this.eventName_]; 274 delete attachedNamedEvents[this.eventName_];
245 }; 275 };
246 276
247 chrome.Event.prototype.destroy_ = function() { 277 chrome.Event.prototype.destroy_ = function() {
248 this.listeners_ = []; 278 this.listeners_ = [];
249 this.validate_ = []; 279 this.validateEventArgs_ = [];
250 this.detach_(false); 280 this.detach_(false);
251 }; 281 };
252 282
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) { 283 chrome.Event.prototype.addRules = function(rules, opt_cb) {
263 if (!this.eventOptions_.supportsRules) 284 if (!this.eventOptions_.supportsRules)
264 throw new Error("This event does not support rules."); 285 throw new Error("This event does not support rules.");
265 if (!getDeclarativeAPI()) { 286
266 throw new Error("You must have permission to use the declarative " + 287 // Takes a list of JSON datatype identifiers and returns a schema fragment
267 "API to support rules in events"); 288 // that verifies that a JSON object corresponds to an array of only these
289 // data types.
290 function buildArrayOfChoicesSchema(typesList) {
291 return {
292 'type': 'array',
293 'items': {
294 'choices': typesList.map(function(el) {return {'$ref': el};})
295 }
296 };
297 };
298
299 // Validate conditions and actions against specific schemas of this
300 // event object type.
301 // |rules| is an array of JSON objects that follow the Rule type of the
302 // declarative extension APIs. |conditions| is an array of JSON type
303 // identifiers that are allowed to occur in the conditions attribute of each
304 // rule. Likewise, |actions| is an array of JSON type identifiers that are
305 // allowed to occur in the actions attribute of each rule.
306 function validateRules(rules, conditions, actions) {
307 var conditionsSchema = buildArrayOfChoicesSchema(conditions);
308 var actionsSchema = buildArrayOfChoicesSchema(actions);
309 rules.forEach(function(rule) {
310 chromeHidden.validate([rule.conditions], [conditionsSchema]);
311 chromeHidden.validate([rule.actions], [actionsSchema]);
312 })
313 };
314
315 if (!this.eventOptions_.conditions || !this.eventOptions_.actions) {
316 throw new Error('Event ' + this.eventName_ + ' misses conditions or ' +
317 'actions in the API specification.');
268 } 318 }
269 getDeclarativeAPI().addRules(this.eventName_, rules, opt_cb); 319
320 validateRules(rules,
321 this.eventOptions_.conditions,
322 this.eventOptions_.actions);
323
324 sendRequest("events.addRules", [this.eventName_, rules, opt_cb],
325 addRulesParams);
270 } 326 }
271 327
272 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { 328 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
273 if (!this.eventOptions_.supportsRules) 329 if (!this.eventOptions_.supportsRules)
274 throw new Error("This event does not support rules."); 330 throw new Error("This event does not support rules.");
275 if (!getDeclarativeAPI()) { 331 sendRequest("events.removeRules",
276 throw new Error("You must have permission to use the declarative " + 332 [this.eventName_, ruleIdentifiers, opt_cb],
277 "API to support rules in events"); 333 removeRulesParams);
278 }
279 getDeclarativeAPI().removeRules(
280 this.eventName_, ruleIdentifiers, opt_cb);
281 } 334 }
282 335
283 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { 336 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
284 if (!this.eventOptions_.supportsRules) 337 if (!this.eventOptions_.supportsRules)
285 throw new Error("This event does not support rules."); 338 throw new Error("This event does not support rules.");
286 if (!getDeclarativeAPI()) { 339 sendRequest("events.getRules",
287 throw new Error("You must have permission to use the declarative " + 340 [this.eventName_, ruleIdentifiers, cb],
288 "API to support rules in events"); 341 getRulesParams);
289 }
290 getDeclarativeAPI().getRules(
291 this.eventName_, ruleIdentifiers, cb);
292 } 342 }
293 343
294 // Special load events: we don't use the DOM unload because that slows 344 // 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, 345 // down tab shutdown. On the other hand, onUnload might not always fire,
296 // since Chrome will terminate renderers on shutdown (SuddenTermination). 346 // since Chrome will terminate renderers on shutdown (SuddenTermination).
297 chromeHidden.onLoad = new chrome.Event(); 347 chromeHidden.onLoad = new chrome.Event();
298 chromeHidden.onUnload = new chrome.Event(); 348 chromeHidden.onUnload = new chrome.Event();
299 349
300 chromeHidden.dispatchOnLoad = 350 chromeHidden.dispatchOnLoad =
301 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); 351 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad);
302 352
303 chromeHidden.dispatchOnUnload = function() { 353 chromeHidden.dispatchOnUnload = function() {
304 chromeHidden.onUnload.dispatch(); 354 chromeHidden.onUnload.dispatch();
305 for (var i = 0; i < allAttachedEvents.length; ++i) { 355 for (var i = 0; i < allAttachedEvents.length; ++i) {
306 var event = allAttachedEvents[i]; 356 var event = allAttachedEvents[i];
307 if (event) 357 if (event)
308 event.detach_(false); 358 event.detach_(false);
309 } 359 }
310 }; 360 };
311 361
312 chromeHidden.dispatchError = function(msg) { 362 chromeHidden.dispatchError = function(msg) {
313 console.error(msg); 363 console.error(msg);
314 }; 364 };
315 365
316 exports.Event = chrome.Event; 366 exports.Event = chrome.Event;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698