1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Event management for GuestViewContainers.
7 var EventBindings = require('event_bindings');
8 var GuestViewInternalNatives = requireNative('guest_view_internal');
9 var MessagingNatives = requireNative('messaging_natives');
11 var CreateEvent = function(name) {
12 var eventOpts = {supportsListeners: true, supportsFilters: true};
13 return new EventBindings.Event(name, undefined, eventOpts);
16 function GuestViewEvents(view) {
22 // |setupEventProperty| is normally called automatically, but these events are
23 // are registered here because they are dispatched from GuestViewContainer
24 // instead of in response to extension events.
25 this.setupEventProperty('contentresize');
26 this.setupEventProperty('resize');
30 // |GuestViewEvents.EVENTS| is a dictionary of extension events to be listened
31 // for, which specifies how each event should be handled. The events are
32 // organized by name, and by default will be dispatched as DOM events with
34 // |cancelable| (default: false) specifies whether the DOM event's default
35 // behavior can be canceled. If the default action associated with the event
36 // is prevented, then its dispatch function will return false in its event
37 // handler. The event must have a specified |handler| for this to be
39 // |evt| specifies a descriptor object for the extension event. An event
40 // listener will be attached to this descriptor.
41 // |fields| (default: none) specifies the public-facing fields in the DOM event
42 // that are accessible to developers.
43 // |handler| specifies the name of a handler function to be called each time
44 // that extension event is caught by its event listener. The DOM event
45 // should be dispatched within this handler function (if desired). With no
46 // handler function, the DOM event will be dispatched by default each time
47 // the extension event is caught.
48 // |internal| (default: false) specifies that the event will not be dispatched
49 // as a DOM event, and will also not appear as an on* property on the view’s
50 // element. A |handler| should be specified for all internal events, and
51 // |fields| and |cancelable| should be left unspecified (as they are only
52 // meaningful for DOM events).
53 GuestViewEvents.EVENTS = {};
55 // Attaches |listener| onto the event descriptor object |evt|, and registers it
56 // to be removed once this GuestViewEvents object is garbage collected.
57 GuestViewEvents.prototype.addScopedListener = function(
58 evt, listener, listenerOpts) {
59 this.listenersToBeRemoved.push({ 'evt': evt, 'listener': listener });
60 evt.addListener(listener, listenerOpts);
63 // Sets up the handling of events.
64 GuestViewEvents.prototype.setupEvents = function() {
65 // An array of registerd event listeners that should be removed when this
66 // GuestViewEvents is garbage collected.
67 this.listenersToBeRemoved = [];
68 MessagingNatives.BindToGC(this, function(listenersToBeRemoved) {
69 for (var i = 0; i != listenersToBeRemoved.length; ++i) {
70 listenersToBeRemoved[i].evt.removeListener(
71 listenersToBeRemoved[i].listener);
72 listenersToBeRemoved[i] = null;
74 }.bind(undefined, this.listenersToBeRemoved), -1 /* portId */);
76 // Set up the GuestView events.
77 for (var eventName in GuestViewEvents.EVENTS) {
78 this.setupEvent(eventName, GuestViewEvents.EVENTS[eventName]);
81 // Set up the derived view's events.
82 var events = this.getEvents();
83 for (var eventName in events) {
84 this.setupEvent(eventName, events[eventName]);
88 // Sets up the handling of the |eventName| event.
89 GuestViewEvents.prototype.setupEvent = function(eventName, eventInfo) {
90 if (!eventInfo.internal) {
91 this.setupEventProperty(eventName);
94 var listenerOpts = { instanceId: this.view.viewInstanceId };
95 if (eventInfo.handler) {
96 this.addScopedListener(eventInfo.evt, this.weakWrapper(function(e) {
97 this[eventInfo.handler](e, eventName);
102 // Internal events are not dispatched as DOM events.
103 if (eventInfo.internal) {
107 this.addScopedListener(eventInfo.evt, this.weakWrapper(function(e) {
108 var domEvent = this.makeDomEvent(e, eventName);
109 this.view.dispatchEvent(domEvent);
113 // Constructs a DOM event based on the info for the |eventName| event provided
114 // in either |GuestViewEvents.EVENTS| or getEvents().
115 GuestViewEvents.prototype.makeDomEvent = function(event, eventName) {
117 GuestViewEvents.EVENTS[eventName] || this.getEvents()[eventName];
119 // Internal events are not dispatched as DOM events.
120 if (eventInfo.internal) {
124 var details = { bubbles: true };
125 if (eventInfo.cancelable) {
126 details.cancelable = true;
128 var domEvent = new Event(eventName, details);
129 if (eventInfo.fields) {
130 $Array.forEach(eventInfo.fields, function(field) {
131 if (event[field] !== undefined) {
132 domEvent[field] = event[field];
140 // Adds an 'on<event>' property on the view, which can be used to set/unset
142 GuestViewEvents.prototype.setupEventProperty = function(eventName) {
143 var propertyName = 'on' + eventName.toLowerCase();
144 $Object.defineProperty(this.view.element, propertyName, {
146 return this.on[propertyName];
148 set: function(value) {
149 if (this.on[propertyName]) {
150 this.view.element.removeEventListener(eventName, this.on[propertyName]);
152 this.on[propertyName] = value;
154 this.view.element.addEventListener(eventName, value);
161 // returns a wrapper for |func| with a weak reference to |this|.
162 GuestViewEvents.prototype.weakWrapper = function(func) {
163 var viewInstanceId = this.view.viewInstanceId;
165 var view = GuestViewInternalNatives.GetViewFromID(viewInstanceId);
169 return $Function.apply(func, view.events, $Array.slice(arguments));
173 // Implemented by the derived event manager, if one exists.
174 GuestViewEvents.prototype.getEvents = function() { return {}; };
177 exports.GuestViewEvents = GuestViewEvents;
178 exports.CreateEvent = CreateEvent;