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

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

Issue 12313142: Revert 184837 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 10 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 DCHECK = requireNative('logging').DCHECK; 5 var DCHECK = requireNative('logging').DCHECK;
6 // TODO(cduvall/kalman): json_schema shouldn't put things on chromeHidden.
7 require('json_schema');
8 var eventBindingsNatives = requireNative('event_bindings'); 6 var eventBindingsNatives = requireNative('event_bindings');
9 var AttachEvent = eventBindingsNatives.AttachEvent; 7 var AttachEvent = eventBindingsNatives.AttachEvent;
10 var DetachEvent = eventBindingsNatives.DetachEvent; 8 var DetachEvent = eventBindingsNatives.DetachEvent;
11 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent; 9 var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent;
12 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent; 10 var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent;
13 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter; 11 var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter;
14 var sendRequest = require('sendRequest').sendRequest; 12 var sendRequest = require('sendRequest').sendRequest;
15 var utils = require('utils'); 13 var utils = require('utils');
16 var validate = require('schemaUtils').validate; 14 var validate = require('schemaUtils').validate;
17 15
18 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 16 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
19 var chrome = requireNative('chrome').GetChrome(); 17 var GetExtensionAPIDefinition =
20 var schemaRegistry = requireNative('schema_registry'); 18 requireNative('apiDefinitions').GetExtensionAPIDefinition;
21 19
22 // Schemas for the rule-style functions on the events API that 20 // Schemas for the rule-style functions on the events API that
23 // only need to be generated occasionally, so populate them lazily. 21 // only need to be generated occasionally, so populate them lazily.
24 var ruleFunctionSchemas = { 22 var ruleFunctionSchemas = {
25 // These values are set lazily: 23 // These values are set lazily:
26 // addRules: {}, 24 // addRules: {},
27 // getRules: {}, 25 // getRules: {},
28 // removeRules: {} 26 // removeRules: {}
29 }; 27 };
30 28
31 // This function ensures that |ruleFunctionSchemas| is populated. 29 // This function ensures that |ruleFunctionSchemas| is populated.
32 function ensureRuleSchemasLoaded() { 30 function ensureRuleSchemasLoaded() {
33 if (ruleFunctionSchemas.addRules) 31 if (ruleFunctionSchemas.addRules)
34 return; 32 return;
35 var eventsSchema = schemaRegistry.GetSchema("events"); 33 var eventsSchema = GetExtensionAPIDefinition("events")[0];
36 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); 34 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event');
37 35
38 ruleFunctionSchemas.addRules = 36 ruleFunctionSchemas.addRules =
39 utils.lookup(eventType.functions, 'name', 'addRules'); 37 utils.lookup(eventType.functions, 'name', 'addRules');
40 ruleFunctionSchemas.getRules = 38 ruleFunctionSchemas.getRules =
41 utils.lookup(eventType.functions, 'name', 'getRules'); 39 utils.lookup(eventType.functions, 'name', 'getRules');
42 ruleFunctionSchemas.removeRules = 40 ruleFunctionSchemas.removeRules =
43 utils.lookup(eventType.functions, 'name', 'removeRules'); 41 utils.lookup(eventType.functions, 'name', 'removeRules');
44 } 42 }
45 43
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 // opt_eventName is required for events that support rules. 144 // opt_eventName is required for events that support rules.
147 // 145 //
148 // Example: 146 // Example:
149 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); 147 // chrome.tabs.onChanged = new chrome.Event("tab-changed");
150 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); 148 // chrome.tabs.onChanged.addListener(function(data) { alert(data); });
151 // chromeHidden.Event.dispatch("tab-changed", "hi"); 149 // chromeHidden.Event.dispatch("tab-changed", "hi");
152 // will result in an alert dialog that says 'hi'. 150 // will result in an alert dialog that says 'hi'.
153 // 151 //
154 // If opt_eventOptions exists, it is a dictionary that contains the boolean 152 // If opt_eventOptions exists, it is a dictionary that contains the boolean
155 // entries "supportsListeners" and "supportsRules". 153 // entries "supportsListeners" and "supportsRules".
156 var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { 154 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) {
157 this.eventName_ = opt_eventName; 155 this.eventName_ = opt_eventName;
158 this.listeners_ = []; 156 this.listeners_ = [];
159 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); 157 this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions);
160 158
161 if (this.eventOptions_.supportsRules && !opt_eventName) 159 if (this.eventOptions_.supportsRules && !opt_eventName)
162 throw new Error("Events that support rules require an event name."); 160 throw new Error("Events that support rules require an event name.");
163 161
164 if (this.eventOptions_.supportsFilters) { 162 if (this.eventOptions_.supportsFilters) {
165 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this); 163 this.attachmentStrategy_ = new FilteredAttachmentStrategy(this);
166 } else { 164 } else {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 dispatchArgs(args); 219 dispatchArgs(args);
222 }; 220 };
223 221
224 // Test if a named event has any listeners. 222 // Test if a named event has any listeners.
225 chromeHidden.Event.hasListener = function(name) { 223 chromeHidden.Event.hasListener = function(name) {
226 return (attachedNamedEvents[name] && 224 return (attachedNamedEvents[name] &&
227 attachedNamedEvents[name].listeners_.length > 0); 225 attachedNamedEvents[name].listeners_.length > 0);
228 }; 226 };
229 227
230 // Registers a callback to be called when this event is dispatched. 228 // Registers a callback to be called when this event is dispatched.
231 Event.prototype.addListener = function(cb, filters) { 229 chrome.Event.prototype.addListener = function(cb, filters) {
232 if (!this.eventOptions_.supportsListeners) 230 if (!this.eventOptions_.supportsListeners)
233 throw new Error("This event does not support listeners."); 231 throw new Error("This event does not support listeners.");
234 if (this.eventOptions_.maxListeners && 232 if (this.eventOptions_.maxListeners &&
235 this.getListenerCount() >= this.eventOptions_.maxListeners) 233 this.getListenerCount() >= this.eventOptions_.maxListeners)
236 throw new Error("Too many listeners for " + this.eventName_); 234 throw new Error("Too many listeners for " + this.eventName_);
237 if (filters) { 235 if (filters) {
238 if (!this.eventOptions_.supportsFilters) 236 if (!this.eventOptions_.supportsFilters)
239 throw new Error("This event does not support filters."); 237 throw new Error("This event does not support filters.");
240 if (filters.url && !(filters.url instanceof Array)) 238 if (filters.url && !(filters.url instanceof Array))
241 throw new Error("filters.url should be an array"); 239 throw new Error("filters.url should be an array");
242 } 240 }
243 var listener = {callback: cb, filters: filters}; 241 var listener = {callback: cb, filters: filters};
244 this.attach_(listener); 242 this.attach_(listener);
245 this.listeners_.push(listener); 243 this.listeners_.push(listener);
246 }; 244 };
247 245
248 Event.prototype.attach_ = function(listener) { 246 chrome.Event.prototype.attach_ = function(listener) {
249 this.attachmentStrategy_.onAddedListener(listener); 247 this.attachmentStrategy_.onAddedListener(listener);
250 if (this.listeners_.length == 0) { 248 if (this.listeners_.length == 0) {
251 allAttachedEvents[allAttachedEvents.length] = this; 249 allAttachedEvents[allAttachedEvents.length] = this;
252 if (!this.eventName_) 250 if (!this.eventName_)
253 return; 251 return;
254 252
255 if (attachedNamedEvents[this.eventName_]) { 253 if (attachedNamedEvents[this.eventName_]) {
256 throw new Error("chrome.Event '" + this.eventName_ + 254 throw new Error("chrome.Event '" + this.eventName_ +
257 "' is already attached."); 255 "' is already attached.");
258 } 256 }
259 257
260 attachedNamedEvents[this.eventName_] = this; 258 attachedNamedEvents[this.eventName_] = this;
261 } 259 }
262 }; 260 };
263 261
264 // Unregisters a callback. 262 // Unregisters a callback.
265 Event.prototype.removeListener = function(cb) { 263 chrome.Event.prototype.removeListener = function(cb) {
266 if (!this.eventOptions_.supportsListeners) 264 if (!this.eventOptions_.supportsListeners)
267 throw new Error("This event does not support listeners."); 265 throw new Error("This event does not support listeners.");
268 var idx = this.findListener_(cb); 266 var idx = this.findListener_(cb);
269 if (idx == -1) { 267 if (idx == -1) {
270 return; 268 return;
271 } 269 }
272 270
273 var removedListener = this.listeners_.splice(idx, 1)[0]; 271 var removedListener = this.listeners_.splice(idx, 1)[0];
274 this.attachmentStrategy_.onRemovedListener(removedListener); 272 this.attachmentStrategy_.onRemovedListener(removedListener);
275 273
276 if (this.listeners_.length == 0) { 274 if (this.listeners_.length == 0) {
277 var i = allAttachedEvents.indexOf(this); 275 var i = allAttachedEvents.indexOf(this);
278 if (i >= 0) 276 if (i >= 0)
279 delete allAttachedEvents[i]; 277 delete allAttachedEvents[i];
280 if (!this.eventName_) 278 if (!this.eventName_)
281 return; 279 return;
282 280
283 if (!attachedNamedEvents[this.eventName_]) { 281 if (!attachedNamedEvents[this.eventName_]) {
284 throw new Error("chrome.Event '" + this.eventName_ + 282 throw new Error("chrome.Event '" + this.eventName_ +
285 "' is not attached."); 283 "' is not attached.");
286 } 284 }
287 285
288 delete attachedNamedEvents[this.eventName_]; 286 delete attachedNamedEvents[this.eventName_];
289 } 287 }
290 }; 288 };
291 289
292 // Test if the given callback is registered for this event. 290 // Test if the given callback is registered for this event.
293 Event.prototype.hasListener = function(cb) { 291 chrome.Event.prototype.hasListener = function(cb) {
294 if (!this.eventOptions_.supportsListeners) 292 if (!this.eventOptions_.supportsListeners)
295 throw new Error("This event does not support listeners."); 293 throw new Error("This event does not support listeners.");
296 return this.findListener_(cb) > -1; 294 return this.findListener_(cb) > -1;
297 }; 295 };
298 296
299 // Test if any callbacks are registered for this event. 297 // Test if any callbacks are registered for this event.
300 Event.prototype.hasListeners = function() { 298 chrome.Event.prototype.hasListeners = function() {
301 return this.getListenerCount() > 0; 299 return this.getListenerCount() > 0;
302 }; 300 };
303 301
304 // Return the number of listeners on this event. 302 // Return the number of listeners on this event.
305 Event.prototype.getListenerCount = function() { 303 chrome.Event.prototype.getListenerCount = function() {
306 if (!this.eventOptions_.supportsListeners) 304 if (!this.eventOptions_.supportsListeners)
307 throw new Error("This event does not support listeners."); 305 throw new Error("This event does not support listeners.");
308 return this.listeners_.length; 306 return this.listeners_.length;
309 }; 307 };
310 308
311 // Returns the index of the given callback if registered, or -1 if not 309 // Returns the index of the given callback if registered, or -1 if not
312 // found. 310 // found.
313 Event.prototype.findListener_ = function(cb) { 311 chrome.Event.prototype.findListener_ = function(cb) {
314 for (var i = 0; i < this.listeners_.length; i++) { 312 for (var i = 0; i < this.listeners_.length; i++) {
315 if (this.listeners_[i].callback == cb) { 313 if (this.listeners_[i].callback == cb) {
316 return i; 314 return i;
317 } 315 }
318 } 316 }
319 317
320 return -1; 318 return -1;
321 }; 319 };
322 320
323 Event.prototype.dispatch_ = function(args, listenerIDs) { 321 chrome.Event.prototype.dispatch_ = function(args, listenerIDs) {
324 if (!this.eventOptions_.supportsListeners) 322 if (!this.eventOptions_.supportsListeners)
325 throw new Error("This event does not support listeners."); 323 throw new Error("This event does not support listeners.");
326 var validationErrors = this.validateEventArgs_(args); 324 var validationErrors = this.validateEventArgs_(args);
327 if (validationErrors) { 325 if (validationErrors) {
328 console.error(validationErrors); 326 console.error(validationErrors);
329 return {validationErrors: validationErrors}; 327 return {validationErrors: validationErrors};
330 } 328 }
331 329
332 // Make a copy of the listeners in case the listener list is modified 330 // Make a copy of the listeners in case the listener list is modified
333 // while dispatching the event. 331 // while dispatching the event.
334 var listeners = 332 var listeners =
335 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); 333 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice();
336 334
337 var results = []; 335 var results = [];
338 for (var i = 0; i < listeners.length; i++) { 336 for (var i = 0; i < listeners.length; i++) {
339 try { 337 try {
340 var result = this.dispatchToListener(listeners[i].callback, args); 338 var result = this.dispatchToListener(listeners[i].callback, args);
341 if (result !== undefined) 339 if (result !== undefined)
342 results.push(result); 340 results.push(result);
343 } catch (e) { 341 } catch (e) {
344 console.error("Error in event handler for '" + this.eventName_ + 342 console.error("Error in event handler for '" + this.eventName_ +
345 "': " + e.message + ' ' + e.stack); 343 "': " + e.message + ' ' + e.stack);
346 } 344 }
347 } 345 }
348 if (results.length) 346 if (results.length)
349 return {results: results}; 347 return {results: results};
350 } 348 }
351 349
352 // Can be overridden to support custom dispatching. 350 // Can be overridden to support custom dispatching.
353 Event.prototype.dispatchToListener = function(callback, args) { 351 chrome.Event.prototype.dispatchToListener = function(callback, args) {
354 return callback.apply(null, args); 352 return callback.apply(null, args);
355 } 353 }
356 354
357 // Dispatches this event object to all listeners, passing all supplied 355 // Dispatches this event object to all listeners, passing all supplied
358 // arguments to this function each listener. 356 // arguments to this function each listener.
359 Event.prototype.dispatch = function(varargs) { 357 chrome.Event.prototype.dispatch = function(varargs) {
360 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); 358 return this.dispatch_(Array.prototype.slice.call(arguments), undefined);
361 }; 359 };
362 360
363 // Detaches this event object from its name. 361 // Detaches this event object from its name.
364 Event.prototype.detach_ = function() { 362 chrome.Event.prototype.detach_ = function() {
365 this.attachmentStrategy_.detach(false); 363 this.attachmentStrategy_.detach(false);
366 }; 364 };
367 365
368 Event.prototype.destroy_ = function() { 366 chrome.Event.prototype.destroy_ = function() {
369 this.listeners_ = []; 367 this.listeners_ = [];
370 this.validateEventArgs_ = []; 368 this.validateEventArgs_ = [];
371 this.detach_(false); 369 this.detach_(false);
372 }; 370 };
373 371
374 Event.prototype.addRules = function(rules, opt_cb) { 372 chrome.Event.prototype.addRules = function(rules, opt_cb) {
375 if (!this.eventOptions_.supportsRules) 373 if (!this.eventOptions_.supportsRules)
376 throw new Error("This event does not support rules."); 374 throw new Error("This event does not support rules.");
377 375
378 // Takes a list of JSON datatype identifiers and returns a schema fragment 376 // Takes a list of JSON datatype identifiers and returns a schema fragment
379 // that verifies that a JSON object corresponds to an array of only these 377 // that verifies that a JSON object corresponds to an array of only these
380 // data types. 378 // data types.
381 function buildArrayOfChoicesSchema(typesList) { 379 function buildArrayOfChoicesSchema(typesList) {
382 return { 380 return {
383 'type': 'array', 381 'type': 'array',
384 'items': { 382 'items': {
(...skipping 29 matching lines...) Expand all
414 412
415 ensureRuleSchemasLoaded(); 413 ensureRuleSchemasLoaded();
416 // We remove the first parameter from the validation to give the user more 414 // We remove the first parameter from the validation to give the user more
417 // meaningful error messages. 415 // meaningful error messages.
418 validate([rules, opt_cb], 416 validate([rules, opt_cb],
419 ruleFunctionSchemas.addRules.parameters.slice().splice(1)); 417 ruleFunctionSchemas.addRules.parameters.slice().splice(1));
420 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], 418 sendRequest("events.addRules", [this.eventName_, rules, opt_cb],
421 ruleFunctionSchemas.addRules.parameters); 419 ruleFunctionSchemas.addRules.parameters);
422 } 420 }
423 421
424 Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { 422 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
425 if (!this.eventOptions_.supportsRules) 423 if (!this.eventOptions_.supportsRules)
426 throw new Error("This event does not support rules."); 424 throw new Error("This event does not support rules.");
427 ensureRuleSchemasLoaded(); 425 ensureRuleSchemasLoaded();
428 // We remove the first parameter from the validation to give the user more 426 // We remove the first parameter from the validation to give the user more
429 // meaningful error messages. 427 // meaningful error messages.
430 validate([ruleIdentifiers, opt_cb], 428 validate([ruleIdentifiers, opt_cb],
431 ruleFunctionSchemas.removeRules.parameters.slice().splice(1)); 429 ruleFunctionSchemas.removeRules.parameters.slice().splice(1));
432 sendRequest("events.removeRules", 430 sendRequest("events.removeRules",
433 [this.eventName_, ruleIdentifiers, opt_cb], 431 [this.eventName_, ruleIdentifiers, opt_cb],
434 ruleFunctionSchemas.removeRules.parameters); 432 ruleFunctionSchemas.removeRules.parameters);
435 } 433 }
436 434
437 Event.prototype.getRules = function(ruleIdentifiers, cb) { 435 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
438 if (!this.eventOptions_.supportsRules) 436 if (!this.eventOptions_.supportsRules)
439 throw new Error("This event does not support rules."); 437 throw new Error("This event does not support rules.");
440 ensureRuleSchemasLoaded(); 438 ensureRuleSchemasLoaded();
441 // We remove the first parameter from the validation to give the user more 439 // We remove the first parameter from the validation to give the user more
442 // meaningful error messages. 440 // meaningful error messages.
443 validate([ruleIdentifiers, cb], 441 validate([ruleIdentifiers, cb],
444 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); 442 ruleFunctionSchemas.getRules.parameters.slice().splice(1));
445 443
446 sendRequest("events.getRules", 444 sendRequest("events.getRules",
447 [this.eventName_, ruleIdentifiers, cb], 445 [this.eventName_, ruleIdentifiers, cb],
448 ruleFunctionSchemas.getRules.parameters); 446 ruleFunctionSchemas.getRules.parameters);
449 } 447 }
450 448
451 // Special load events: we don't use the DOM unload because that slows 449 // Special load events: we don't use the DOM unload because that slows
452 // down tab shutdown. On the other hand, onUnload might not always fire, 450 // down tab shutdown. On the other hand, onUnload might not always fire,
453 // since Chrome will terminate renderers on shutdown (SuddenTermination). 451 // since Chrome will terminate renderers on shutdown (SuddenTermination).
454 chromeHidden.onLoad = new Event(); 452 chromeHidden.onLoad = new chrome.Event();
455 chromeHidden.onUnload = new Event(); 453 chromeHidden.onUnload = new chrome.Event();
456 454
457 chromeHidden.dispatchOnLoad = 455 chromeHidden.dispatchOnLoad =
458 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); 456 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad);
459 457
460 chromeHidden.dispatchOnUnload = function() { 458 chromeHidden.dispatchOnUnload = function() {
461 chromeHidden.onUnload.dispatch(); 459 chromeHidden.onUnload.dispatch();
462 chromeHidden.wasUnloaded = true; 460 chromeHidden.wasUnloaded = true;
463 461
464 for (var i = 0; i < allAttachedEvents.length; ++i) { 462 for (var i = 0; i < allAttachedEvents.length; ++i) {
465 var event = allAttachedEvents[i]; 463 var event = allAttachedEvents[i];
466 if (event) 464 if (event)
467 event.detach_(); 465 event.detach_();
468 } 466 }
469 }; 467 };
470 468
471 chromeHidden.dispatchError = function(msg) { 469 chromeHidden.dispatchError = function(msg) {
472 console.error(msg); 470 console.error(msg);
473 }; 471 };
474 472
475 chrome.Event = Event; 473 exports.Event = chrome.Event;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698