15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  var eventNatives = requireNative('event_natives');
6868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  var logging = requireNative('logging');
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  var schemaRegistry = requireNative('schema_registry');
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var sendRequest = require('sendRequest').sendRequest;
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var utils = require('utils');
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var validate = require('schemaUtils').validate;
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  var unloadEvent = require('unload_event');
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Schemas for the rule-style functions on the events API that
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // only need to be generated occasionally, so populate them lazily.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ruleFunctionSchemas = {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // These values are set lazily:
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // addRules: {},
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // getRules: {},
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // removeRules: {}
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This function ensures that |ruleFunctionSchemas| is populated.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function ensureRuleSchemasLoaded() {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ruleFunctionSchemas.addRules)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var eventsSchema = schemaRegistry.GetSchema("events");
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event');
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ruleFunctionSchemas.addRules =
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        utils.lookup(eventType.functions, 'name', 'addRules');
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ruleFunctionSchemas.getRules =
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        utils.lookup(eventType.functions, 'name', 'getRules');
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ruleFunctionSchemas.removeRules =
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        utils.lookup(eventType.functions, 'name', 'removeRules');
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // A map of event names to the event object that is registered to that name.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var attachedNamedEvents = {};
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // An array of all attached event objects, used for detaching on unload.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var allAttachedEvents = [];
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // A map of functions that massage event arguments before they are dispatched.
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Key is event name, value is function.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var eventArgumentMassagers = {};
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // An attachment strategy for events that aren't attached to the browser.
48ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // This applies to events with the "unmanaged" option and events without
49ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // names.
50ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  var NullAttachmentStrategy = function(event) {
51ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    this.event_ = event;
52ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
53ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  NullAttachmentStrategy.prototype.onAddedListener =
54ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      function(listener) {
55ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
56ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  NullAttachmentStrategy.prototype.onRemovedListener =
57ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      function(listener) {
58ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
59ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  NullAttachmentStrategy.prototype.detach = function(manual) {
60ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
61ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  NullAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
62ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // |ids| is for filtered events only.
63ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return this.event_.listeners_;
64ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
65ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Handles adding/removing/dispatching listeners for unfiltered events.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var UnfilteredAttachmentStrategy = function(event) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.event_ = event;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnfilteredAttachmentStrategy.prototype.onAddedListener =
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function(listener) {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only attach / detach on the first / last listener removed.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.event_.listeners_.length == 0)
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      eventNatives.AttachEvent(privates(this.event_).eventName);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnfilteredAttachmentStrategy.prototype.onRemovedListener =
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function(listener) {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.event_.listeners_.length == 0)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.detach(true);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnfilteredAttachmentStrategy.prototype.detach = function(manual) {
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    eventNatives.DetachEvent(privates(this.event_).eventName, manual);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
89ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // |ids| is for filtered events only.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return this.event_.listeners_;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var FilteredAttachmentStrategy = function(event) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.event_ = event;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.listenerMap_ = {};
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilteredAttachmentStrategy.idToEventMap = {};
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) {
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var id = eventNatives.AttachFilteredEvent(privates(this.event_).eventName,
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                              listener.filters || {});
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (id == -1)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("Can't add listener");
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener.id = id;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.listenerMap_[id] = listener;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FilteredAttachmentStrategy.idToEventMap[id] = this.event_;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilteredAttachmentStrategy.prototype.onRemovedListener = function(listener) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.detachListener(listener, true);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilteredAttachmentStrategy.prototype.detachListener =
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function(listener, manual) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (listener.id == undefined)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("listener.id undefined - '" + listener + "'");
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var id = listener.id;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete this.listenerMap_[id];
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete FilteredAttachmentStrategy.idToEventMap[id];
121868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    eventNatives.DetachFilteredEvent(id, manual);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilteredAttachmentStrategy.prototype.detach = function(manual) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i in this.listenerMap_)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.detachListener(this.listenerMap_[i], manual);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var result = [];
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < ids.length; i++)
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      $Array.push(result, this.listenerMap_[ids[i]]);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return result;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  function parseEventOptions(opt_eventOptions) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function merge(dest, src) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var k in src) {
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (!$Object.hasOwnProperty(dest, k)) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          dest[k] = src[k];
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var options = opt_eventOptions || {};
146ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    merge(options, {
147ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // Event supports adding listeners with filters ("filtered events"), for
148ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // example as used in the webNavigation API.
149ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      //
150ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // event.addListener(listener, [filter1, filter2]);
151ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      supportsFilters: false,
152ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
153ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // Events supports vanilla events. Most APIs use these.
154ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      //
155ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // event.addListener(listener);
156ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      supportsListeners: true,
157ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
158ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // Event supports adding rules ("declarative events") rather than
159ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // listeners, for example as used in the declarativeWebRequest API.
160ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      //
161ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // event.addRules([rule1, rule2]);
162ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      supportsRules: false,
163ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
164ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // Event is unmanaged in that the browser has no knowledge of its
165ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // existence; it's never invoked, doesn't keep the renderer alive, and
166ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // the bindings system has no knowledge of it.
167ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      //
168ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // Both events created by user code (new chrome.Event()) and messaging
169ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // events are unmanaged, though in the latter case the browser *does*
170ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // interact indirectly with them via IPCs written by hand.
171ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      unmanaged: false,
172ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    });
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return options;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Event object.  If opt_eventName is provided, this object represents
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the unique instance of that named event, and dispatching an event
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // with that name will route through this object's listeners. Note that
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // opt_eventName is required for events that support rules.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Example:
182868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //   var Event = require('event_bindings').Event;
183868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //   chrome.tabs.onChanged = new Event("tab-changed");
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   chrome.tabs.onChanged.addListener(function(data) { alert(data); });
185868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //   Event.dispatch("tab-changed", "hi");
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will result in an alert dialog that says 'hi'.
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If opt_eventOptions exists, it is a dictionary that contains the boolean
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // entries "supportsListeners" and "supportsRules".
190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // If opt_webViewInstanceId exists, it is an integer uniquely identifying a
191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // <webview> tag within the embedder. If it does not exist, then this is an
192f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // extension event rather than a <webview> event.
193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions,
194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                       opt_webViewInstanceId) {
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    privates(this).eventName = opt_eventName;
196ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    this.argSchemas_ = opt_argSchemas;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.listeners_ = [];
198868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    this.eventOptions_ = parseEventOptions(opt_eventOptions);
199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    this.webViewInstanceId_ = opt_webViewInstanceId || 0;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!privates(this).eventName) {
202ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      if (this.eventOptions_.supportsRules)
203ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        throw new Error("Events that support rules require an event name.");
204ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // Events without names cannot be managed by the browser by definition
205ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // (the browser has no way of identifying them).
206ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      this.eventOptions_.unmanaged = true;
207ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
208ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
2097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // Track whether the event has been destroyed to help track down the cause
2107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // of http://crbug.com/258526.
211ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // This variable will eventually hold the stack trace of the destroy call.
2127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // TODO(kalman): Delete this and replace with more sound logic that catches
2137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // when events are used without being *attached*.
214ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    this.destroyed_ = null;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
216ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if (this.eventOptions_.unmanaged)
217ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      this.attachmentStrategy_ = new NullAttachmentStrategy(this);
218ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    else if (this.eventOptions_.supportsFilters)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.attachmentStrategy_ = new FilteredAttachmentStrategy(this);
220ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    else
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.attachmentStrategy_ = new UnfilteredAttachmentStrategy(this);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // callback is a function(args, dispatch). args are the args we receive from
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // dispatchEvent(), and dispatch is a function(args) that dispatches args to
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // its listeners.
227868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  function registerArgumentMassager(name, callback) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (eventArgumentMassagers[name])
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("Massager already registered for event: " + name);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    eventArgumentMassagers[name] = callback;
231868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Dispatches a named event with the given argument array. The args array is
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the list of arguments that will be sent to the event callback.
235868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  function dispatchEvent(name, args, filteringInfo) {
23658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    var listenerIDs = [];
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (filteringInfo)
239868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      listenerIDs = eventNatives.MatchAgainstEventFilter(name, filteringInfo);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var event = attachedNamedEvents[name];
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!event)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var dispatchArgs = function(args) {
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var result = event.dispatch_(args, listenerIDs);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (result)
248868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        logging.DCHECK(!result.validationErrors, result.validationErrors);
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return result;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (eventArgumentMassagers[name])
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      eventArgumentMassagers[name](args, dispatchArgs);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dispatchArgs(args);
256868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Registers a callback to be called when this event is dispatched.
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.addListener = function(cb, filters) {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsListeners)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support listeners.");
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.eventOptions_.maxListeners &&
263ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        this.getListenerCount() >= this.eventOptions_.maxListeners) {
264a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      throw new Error("Too many listeners for " + privates(this).eventName);
265ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (filters) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.eventOptions_.supportsFilters)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw new Error("This event does not support filters.");
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (filters.url && !(filters.url instanceof Array))
27058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        throw new Error("filters.url should be an array.");
27158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      if (filters.serviceType &&
27258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          !(typeof filters.serviceType === 'string')) {
27358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        throw new Error("filters.serviceType should be a string.")
27458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var listener = {callback: cb, filters: filters};
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.attach_(listener);
278eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    $Array.push(this.listeners_, listener);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.attach_ = function(listener) {
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.attachmentStrategy_.onAddedListener(listener);
283ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.listeners_.length == 0) {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      allAttachedEvents[allAttachedEvents.length] = this;
286a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (privates(this).eventName) {
287a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (attachedNamedEvents[privates(this).eventName]) {
288a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          throw new Error("Event '" + privates(this).eventName +
289ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                          "' is already attached.");
290ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        }
291a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        attachedNamedEvents[privates(this).eventName] = this;
292ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      }
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Unregisters a callback.
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.removeListener = function(cb) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsListeners)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support listeners.");
300ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var idx = this.findListener_(cb);
302868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (idx == -1)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    var removedListener = $Array.splice(this.listeners_, idx, 1)[0];
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.attachmentStrategy_.onRemovedListener(removedListener);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.listeners_.length == 0) {
309f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      var i = $Array.indexOf(allAttachedEvents, this);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (i >= 0)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delete allAttachedEvents[i];
312a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (privates(this).eventName) {
313a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (!attachedNamedEvents[privates(this).eventName]) {
314a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          throw new Error(
315a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)              "Event '" + privates(this).eventName + "' is not attached.");
316a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
317a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        delete attachedNamedEvents[privates(this).eventName];
318ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      }
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Test if the given callback is registered for this event.
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.hasListener = function(cb) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsListeners)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support listeners.");
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return this.findListener_(cb) > -1;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Test if any callbacks are registered for this event.
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.hasListeners = function() {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return this.getListenerCount() > 0;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return the number of listeners on this event.
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.getListenerCount = function() {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsListeners)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support listeners.");
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return this.listeners_.length;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns the index of the given callback if registered, or -1 if not
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // found.
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.findListener_ = function(cb) {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < this.listeners_.length; i++) {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.listeners_[i].callback == cb) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return i;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.dispatch_ = function(args, listenerIDs) {
3547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (this.destroyed_) {
355a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      throw new Error(privates(this).eventName + ' was already destroyed at: ' +
356ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                      this.destroyed_);
3577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    }
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsListeners)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support listeners.");
360ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
361ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if (this.argSchemas_ && logging.DCHECK_IS_ON()) {
362ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      try {
363ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        validate(args, this.argSchemas_);
364ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      } catch (e) {
365a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        e.message += ' in ' + privates(this).eventName;
366ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        throw e;
367ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      }
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Make a copy of the listeners in case the listener list is modified
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // while dispatching the event.
372eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    var listeners = $Array.slice(
373eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        this.attachmentStrategy_.getListenersByIDs(listenerIDs));
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var results = [];
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < listeners.length; i++) {
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var result = this.dispatchToListener(listeners[i].callback, args);
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (result !== undefined)
380eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          $Array.push(results, result);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } catch (e) {
382a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        console.error(
383a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          'Error in event handler for ' +
384a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          (privates(this).eventName ? privates(this).eventName : '(unknown)') +
385a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          ': ' + e.stack);
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (results.length)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {results: results};
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Can be overridden to support custom dispatching.
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.dispatchToListener = function(callback, args) {
394eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return $Function.apply(callback, null, args);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Dispatches this event object to all listeners, passing all supplied
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // arguments to this function each listener.
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.dispatch = function(varargs) {
400eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return this.dispatch_($Array.slice(arguments), undefined);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Detaches this event object from its name.
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.detach_ = function() {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.attachmentStrategy_.detach(false);
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.destroy_ = function() {
4097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    this.listeners_.length = 0;
4107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    this.detach_();
411ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    this.destroyed_ = new Error().stack;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.addRules = function(rules, opt_cb) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsRules)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support rules.");
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Takes a list of JSON datatype identifiers and returns a schema fragment
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // that verifies that a JSON object corresponds to an array of only these
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // data types.
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function buildArrayOfChoicesSchema(typesList) {
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'type': 'array',
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'items': {
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'choices': typesList.map(function(el) {return {'$ref': el};})
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Validate conditions and actions against specific schemas of this
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // event object type.
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // |rules| is an array of JSON objects that follow the Rule type of the
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // declarative extension APIs. |conditions| is an array of JSON type
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // identifiers that are allowed to occur in the conditions attribute of each
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // rule. Likewise, |actions| is an array of JSON type identifiers that are
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // allowed to occur in the actions attribute of each rule.
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function validateRules(rules, conditions, actions) {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var conditionsSchema = buildArrayOfChoicesSchema(conditions);
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var actionsSchema = buildArrayOfChoicesSchema(actions);
4407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      $Array.forEach(rules, function(rule) {
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        validate([rule.conditions], [conditionsSchema]);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        validate([rule.actions], [actionsSchema]);
443868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      });
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.conditions || !this.eventOptions_.actions) {
447a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      throw new Error('Event ' + privates(this).eventName + ' misses ' +
448a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                      'conditions or actions in the API specification.');
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    validateRules(rules,
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  this.eventOptions_.conditions,
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  this.eventOptions_.actions);
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureRuleSchemasLoaded();
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We remove the first parameter from the validation to give the user more
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // meaningful error messages.
458f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    validate([this.webViewInstanceId_, rules, opt_cb],
459eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch             $Array.splice(
460eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 $Array.slice(ruleFunctionSchemas.addRules.parameters), 1));
461a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    sendRequest(
462a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      "events.addRules",
463a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      [privates(this).eventName, this.webViewInstanceId_, rules,  opt_cb],
464a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ruleFunctionSchemas.addRules.parameters);
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsRules)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support rules.");
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureRuleSchemasLoaded();
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We remove the first parameter from the validation to give the user more
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // meaningful error messages.
473f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    validate([this.webViewInstanceId_, ruleIdentifiers, opt_cb],
474eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch             $Array.splice(
475eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 $Array.slice(ruleFunctionSchemas.removeRules.parameters), 1));
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sendRequest("events.removeRules",
477a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                [privates(this).eventName,
478f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 this.webViewInstanceId_,
479f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 ruleIdentifiers,
480f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 opt_cb],
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ruleFunctionSchemas.removeRules.parameters);
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Event.prototype.getRules = function(ruleIdentifiers, cb) {
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.eventOptions_.supportsRules)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error("This event does not support rules.");
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureRuleSchemasLoaded();
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We remove the first parameter from the validation to give the user more
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // meaningful error messages.
490f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    validate([this.webViewInstanceId_, ruleIdentifiers, cb],
491eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch             $Array.splice(
492eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 $Array.slice(ruleFunctionSchemas.getRules.parameters), 1));
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
494a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    sendRequest(
495a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      "events.getRules",
496a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      [privates(this).eventName, this.webViewInstanceId_, ruleIdentifiers, cb],
497a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ruleFunctionSchemas.getRules.parameters);
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
500868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  unloadEvent.addListener(function() {
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < allAttachedEvents.length; ++i) {
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var event = allAttachedEvents[i];
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (event)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.detach_();
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
506868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  });
507868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
508868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // NOTE: Event is (lazily) exposed as chrome.Event from dispatcher.cc.
509868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  exports.Event = Event;
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
511868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  exports.dispatchEvent = dispatchEvent;
512868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  exports.parseEventOptions = parseEventOptions;
513868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  exports.registerArgumentMassager = registerArgumentMassager;
514