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

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

Issue 12632004: Revert 186643 - Caused a 10% regression on SunSpider benchmark (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 9 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 dispatchArgs(args); 220 dispatchArgs(args);
223 }; 221 };
224 222
225 // Test if a named event has any listeners. 223 // Test if a named event has any listeners.
226 chromeHidden.Event.hasListener = function(name) { 224 chromeHidden.Event.hasListener = function(name) {
227 return (attachedNamedEvents[name] && 225 return (attachedNamedEvents[name] &&
228 attachedNamedEvents[name].listeners_.length > 0); 226 attachedNamedEvents[name].listeners_.length > 0);
229 }; 227 };
230 228
231 // Registers a callback to be called when this event is dispatched. 229 // Registers a callback to be called when this event is dispatched.
232 Event.prototype.addListener = function(cb, filters) { 230 chrome.Event.prototype.addListener = function(cb, filters) {
233 if (!this.eventOptions_.supportsListeners) 231 if (!this.eventOptions_.supportsListeners)
234 throw new Error("This event does not support listeners."); 232 throw new Error("This event does not support listeners.");
235 if (this.eventOptions_.maxListeners && 233 if (this.eventOptions_.maxListeners &&
236 this.getListenerCount() >= this.eventOptions_.maxListeners) 234 this.getListenerCount() >= this.eventOptions_.maxListeners)
237 throw new Error("Too many listeners for " + this.eventName_); 235 throw new Error("Too many listeners for " + this.eventName_);
238 if (filters) { 236 if (filters) {
239 if (!this.eventOptions_.supportsFilters) 237 if (!this.eventOptions_.supportsFilters)
240 throw new Error("This event does not support filters."); 238 throw new Error("This event does not support filters.");
241 if (filters.url && !(filters.url instanceof Array)) 239 if (filters.url && !(filters.url instanceof Array))
242 throw new Error("filters.url should be an array"); 240 throw new Error("filters.url should be an array");
243 } 241 }
244 var listener = {callback: cb, filters: filters}; 242 var listener = {callback: cb, filters: filters};
245 this.attach_(listener); 243 this.attach_(listener);
246 this.listeners_.push(listener); 244 this.listeners_.push(listener);
247 }; 245 };
248 246
249 Event.prototype.attach_ = function(listener) { 247 chrome.Event.prototype.attach_ = function(listener) {
250 this.attachmentStrategy_.onAddedListener(listener); 248 this.attachmentStrategy_.onAddedListener(listener);
251 if (this.listeners_.length == 0) { 249 if (this.listeners_.length == 0) {
252 allAttachedEvents[allAttachedEvents.length] = this; 250 allAttachedEvents[allAttachedEvents.length] = this;
253 if (!this.eventName_) 251 if (!this.eventName_)
254 return; 252 return;
255 253
256 if (attachedNamedEvents[this.eventName_]) { 254 if (attachedNamedEvents[this.eventName_]) {
257 throw new Error("chrome.Event '" + this.eventName_ + 255 throw new Error("chrome.Event '" + this.eventName_ +
258 "' is already attached."); 256 "' is already attached.");
259 } 257 }
260 258
261 attachedNamedEvents[this.eventName_] = this; 259 attachedNamedEvents[this.eventName_] = this;
262 } 260 }
263 }; 261 };
264 262
265 // Unregisters a callback. 263 // Unregisters a callback.
266 Event.prototype.removeListener = function(cb) { 264 chrome.Event.prototype.removeListener = function(cb) {
267 if (!this.eventOptions_.supportsListeners) 265 if (!this.eventOptions_.supportsListeners)
268 throw new Error("This event does not support listeners."); 266 throw new Error("This event does not support listeners.");
269 var idx = this.findListener_(cb); 267 var idx = this.findListener_(cb);
270 if (idx == -1) { 268 if (idx == -1) {
271 return; 269 return;
272 } 270 }
273 271
274 var removedListener = this.listeners_.splice(idx, 1)[0]; 272 var removedListener = this.listeners_.splice(idx, 1)[0];
275 this.attachmentStrategy_.onRemovedListener(removedListener); 273 this.attachmentStrategy_.onRemovedListener(removedListener);
276 274
277 if (this.listeners_.length == 0) { 275 if (this.listeners_.length == 0) {
278 var i = allAttachedEvents.indexOf(this); 276 var i = allAttachedEvents.indexOf(this);
279 if (i >= 0) 277 if (i >= 0)
280 delete allAttachedEvents[i]; 278 delete allAttachedEvents[i];
281 if (!this.eventName_) 279 if (!this.eventName_)
282 return; 280 return;
283 281
284 if (!attachedNamedEvents[this.eventName_]) { 282 if (!attachedNamedEvents[this.eventName_]) {
285 throw new Error("chrome.Event '" + this.eventName_ + 283 throw new Error("chrome.Event '" + this.eventName_ +
286 "' is not attached."); 284 "' is not attached.");
287 } 285 }
288 286
289 delete attachedNamedEvents[this.eventName_]; 287 delete attachedNamedEvents[this.eventName_];
290 } 288 }
291 }; 289 };
292 290
293 // Test if the given callback is registered for this event. 291 // Test if the given callback is registered for this event.
294 Event.prototype.hasListener = function(cb) { 292 chrome.Event.prototype.hasListener = function(cb) {
295 if (!this.eventOptions_.supportsListeners) 293 if (!this.eventOptions_.supportsListeners)
296 throw new Error("This event does not support listeners."); 294 throw new Error("This event does not support listeners.");
297 return this.findListener_(cb) > -1; 295 return this.findListener_(cb) > -1;
298 }; 296 };
299 297
300 // Test if any callbacks are registered for this event. 298 // Test if any callbacks are registered for this event.
301 Event.prototype.hasListeners = function() { 299 chrome.Event.prototype.hasListeners = function() {
302 return this.getListenerCount() > 0; 300 return this.getListenerCount() > 0;
303 }; 301 };
304 302
305 // Return the number of listeners on this event. 303 // Return the number of listeners on this event.
306 Event.prototype.getListenerCount = function() { 304 chrome.Event.prototype.getListenerCount = function() {
307 if (!this.eventOptions_.supportsListeners) 305 if (!this.eventOptions_.supportsListeners)
308 throw new Error("This event does not support listeners."); 306 throw new Error("This event does not support listeners.");
309 return this.listeners_.length; 307 return this.listeners_.length;
310 }; 308 };
311 309
312 // Returns the index of the given callback if registered, or -1 if not 310 // Returns the index of the given callback if registered, or -1 if not
313 // found. 311 // found.
314 Event.prototype.findListener_ = function(cb) { 312 chrome.Event.prototype.findListener_ = function(cb) {
315 for (var i = 0; i < this.listeners_.length; i++) { 313 for (var i = 0; i < this.listeners_.length; i++) {
316 if (this.listeners_[i].callback == cb) { 314 if (this.listeners_[i].callback == cb) {
317 return i; 315 return i;
318 } 316 }
319 } 317 }
320 318
321 return -1; 319 return -1;
322 }; 320 };
323 321
324 Event.prototype.dispatch_ = function(args, listenerIDs) { 322 chrome.Event.prototype.dispatch_ = function(args, listenerIDs) {
325 if (!this.eventOptions_.supportsListeners) 323 if (!this.eventOptions_.supportsListeners)
326 throw new Error("This event does not support listeners."); 324 throw new Error("This event does not support listeners.");
327 var validationErrors = this.validateEventArgs_(args); 325 var validationErrors = this.validateEventArgs_(args);
328 if (validationErrors) { 326 if (validationErrors) {
329 console.error(validationErrors); 327 console.error(validationErrors);
330 return {validationErrors: validationErrors}; 328 return {validationErrors: validationErrors};
331 } 329 }
332 330
333 // Make a copy of the listeners in case the listener list is modified 331 // Make a copy of the listeners in case the listener list is modified
334 // while dispatching the event. 332 // while dispatching the event.
335 var listeners = 333 var listeners =
336 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice(); 334 this.attachmentStrategy_.getListenersByIDs(listenerIDs).slice();
337 335
338 var results = []; 336 var results = [];
339 for (var i = 0; i < listeners.length; i++) { 337 for (var i = 0; i < listeners.length; i++) {
340 try { 338 try {
341 var result = this.dispatchToListener(listeners[i].callback, args); 339 var result = this.dispatchToListener(listeners[i].callback, args);
342 if (result !== undefined) 340 if (result !== undefined)
343 results.push(result); 341 results.push(result);
344 } catch (e) { 342 } catch (e) {
345 console.error("Error in event handler for '" + this.eventName_ + 343 console.error("Error in event handler for '" + this.eventName_ +
346 "': " + e.message + ' ' + e.stack); 344 "': " + e.message + ' ' + e.stack);
347 } 345 }
348 } 346 }
349 if (results.length) 347 if (results.length)
350 return {results: results}; 348 return {results: results};
351 } 349 }
352 350
353 // Can be overridden to support custom dispatching. 351 // Can be overridden to support custom dispatching.
354 Event.prototype.dispatchToListener = function(callback, args) { 352 chrome.Event.prototype.dispatchToListener = function(callback, args) {
355 return callback.apply(null, args); 353 return callback.apply(null, args);
356 } 354 }
357 355
358 // Dispatches this event object to all listeners, passing all supplied 356 // Dispatches this event object to all listeners, passing all supplied
359 // arguments to this function each listener. 357 // arguments to this function each listener.
360 Event.prototype.dispatch = function(varargs) { 358 chrome.Event.prototype.dispatch = function(varargs) {
361 return this.dispatch_(Array.prototype.slice.call(arguments), undefined); 359 return this.dispatch_(Array.prototype.slice.call(arguments), undefined);
362 }; 360 };
363 361
364 // Detaches this event object from its name. 362 // Detaches this event object from its name.
365 Event.prototype.detach_ = function() { 363 chrome.Event.prototype.detach_ = function() {
366 this.attachmentStrategy_.detach(false); 364 this.attachmentStrategy_.detach(false);
367 }; 365 };
368 366
369 Event.prototype.destroy_ = function() { 367 chrome.Event.prototype.destroy_ = function() {
370 this.listeners_ = []; 368 this.listeners_ = [];
371 this.validateEventArgs_ = []; 369 this.validateEventArgs_ = [];
372 this.detach_(false); 370 this.detach_(false);
373 }; 371 };
374 372
375 Event.prototype.addRules = function(rules, opt_cb) { 373 chrome.Event.prototype.addRules = function(rules, opt_cb) {
376 if (!this.eventOptions_.supportsRules) 374 if (!this.eventOptions_.supportsRules)
377 throw new Error("This event does not support rules."); 375 throw new Error("This event does not support rules.");
378 376
379 // Takes a list of JSON datatype identifiers and returns a schema fragment 377 // Takes a list of JSON datatype identifiers and returns a schema fragment
380 // that verifies that a JSON object corresponds to an array of only these 378 // that verifies that a JSON object corresponds to an array of only these
381 // data types. 379 // data types.
382 function buildArrayOfChoicesSchema(typesList) { 380 function buildArrayOfChoicesSchema(typesList) {
383 return { 381 return {
384 'type': 'array', 382 'type': 'array',
385 'items': { 383 'items': {
(...skipping 29 matching lines...) Expand all
415 413
416 ensureRuleSchemasLoaded(); 414 ensureRuleSchemasLoaded();
417 // We remove the first parameter from the validation to give the user more 415 // We remove the first parameter from the validation to give the user more
418 // meaningful error messages. 416 // meaningful error messages.
419 validate([rules, opt_cb], 417 validate([rules, opt_cb],
420 ruleFunctionSchemas.addRules.parameters.slice().splice(1)); 418 ruleFunctionSchemas.addRules.parameters.slice().splice(1));
421 sendRequest("events.addRules", [this.eventName_, rules, opt_cb], 419 sendRequest("events.addRules", [this.eventName_, rules, opt_cb],
422 ruleFunctionSchemas.addRules.parameters); 420 ruleFunctionSchemas.addRules.parameters);
423 } 421 }
424 422
425 Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { 423 chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
426 if (!this.eventOptions_.supportsRules) 424 if (!this.eventOptions_.supportsRules)
427 throw new Error("This event does not support rules."); 425 throw new Error("This event does not support rules.");
428 ensureRuleSchemasLoaded(); 426 ensureRuleSchemasLoaded();
429 // We remove the first parameter from the validation to give the user more 427 // We remove the first parameter from the validation to give the user more
430 // meaningful error messages. 428 // meaningful error messages.
431 validate([ruleIdentifiers, opt_cb], 429 validate([ruleIdentifiers, opt_cb],
432 ruleFunctionSchemas.removeRules.parameters.slice().splice(1)); 430 ruleFunctionSchemas.removeRules.parameters.slice().splice(1));
433 sendRequest("events.removeRules", 431 sendRequest("events.removeRules",
434 [this.eventName_, ruleIdentifiers, opt_cb], 432 [this.eventName_, ruleIdentifiers, opt_cb],
435 ruleFunctionSchemas.removeRules.parameters); 433 ruleFunctionSchemas.removeRules.parameters);
436 } 434 }
437 435
438 Event.prototype.getRules = function(ruleIdentifiers, cb) { 436 chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
439 if (!this.eventOptions_.supportsRules) 437 if (!this.eventOptions_.supportsRules)
440 throw new Error("This event does not support rules."); 438 throw new Error("This event does not support rules.");
441 ensureRuleSchemasLoaded(); 439 ensureRuleSchemasLoaded();
442 // We remove the first parameter from the validation to give the user more 440 // We remove the first parameter from the validation to give the user more
443 // meaningful error messages. 441 // meaningful error messages.
444 validate([ruleIdentifiers, cb], 442 validate([ruleIdentifiers, cb],
445 ruleFunctionSchemas.getRules.parameters.slice().splice(1)); 443 ruleFunctionSchemas.getRules.parameters.slice().splice(1));
446 444
447 sendRequest("events.getRules", 445 sendRequest("events.getRules",
448 [this.eventName_, ruleIdentifiers, cb], 446 [this.eventName_, ruleIdentifiers, cb],
449 ruleFunctionSchemas.getRules.parameters); 447 ruleFunctionSchemas.getRules.parameters);
450 } 448 }
451 449
452 // Special load events: we don't use the DOM unload because that slows 450 // Special load events: we don't use the DOM unload because that slows
453 // down tab shutdown. On the other hand, onUnload might not always fire, 451 // down tab shutdown. On the other hand, onUnload might not always fire,
454 // since Chrome will terminate renderers on shutdown (SuddenTermination). 452 // since Chrome will terminate renderers on shutdown (SuddenTermination).
455 chromeHidden.onLoad = new Event(); 453 chromeHidden.onLoad = new chrome.Event();
456 chromeHidden.onUnload = new Event(); 454 chromeHidden.onUnload = new chrome.Event();
457 455
458 chromeHidden.dispatchOnLoad = 456 chromeHidden.dispatchOnLoad =
459 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); 457 chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad);
460 458
461 chromeHidden.dispatchOnUnload = function() { 459 chromeHidden.dispatchOnUnload = function() {
462 chromeHidden.onUnload.dispatch(); 460 chromeHidden.onUnload.dispatch();
463 chromeHidden.wasUnloaded = true; 461 chromeHidden.wasUnloaded = true;
464 462
465 for (var i = 0; i < allAttachedEvents.length; ++i) { 463 for (var i = 0; i < allAttachedEvents.length; ++i) {
466 var event = allAttachedEvents[i]; 464 var event = allAttachedEvents[i];
467 if (event) 465 if (event)
468 event.detach_(); 466 event.detach_();
469 } 467 }
470 }; 468 };
471 469
472 chromeHidden.dispatchError = function(msg) { 470 chromeHidden.dispatchError = function(msg) {
473 console.error(msg); 471 console.error(msg);
474 }; 472 };
475 473
476 chrome.Event = Event; 474 exports.Event = chrome.Event;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698