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

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

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

Powered by Google App Engine
This is Rietveld 408576698