Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / extensions / renderer / resources / guest_view / guest_view_events.js
blobfda0db1adc60a6486b537a39c25590db91a31d08
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) {
17 view.events = this;
19 this.view = view;
20 this.on = {};
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');
27 this.setupEvents();
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
33 // the same name.
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
38 // meaningful.
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);
98 }), listenerOpts);
99 return;
102 // Internal events are not dispatched as DOM events.
103 if (eventInfo.internal) {
104 return;
107 this.addScopedListener(eventInfo.evt, this.weakWrapper(function(e) {
108 var domEvent = this.makeDomEvent(e, eventName);
109 this.view.dispatchEvent(domEvent);
110 }), listenerOpts);
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) {
116 var eventInfo =
117 GuestViewEvents.EVENTS[eventName] || this.getEvents()[eventName];
119 // Internal events are not dispatched as DOM events.
120 if (eventInfo.internal) {
121 return null;
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];
134 }.bind(this));
137 return domEvent;
140 // Adds an 'on<event>' property on the view, which can be used to set/unset
141 // an event handler.
142 GuestViewEvents.prototype.setupEventProperty = function(eventName) {
143 var propertyName = 'on' + eventName.toLowerCase();
144 $Object.defineProperty(this.view.element, propertyName, {
145 get: function() {
146 return this.on[propertyName];
147 }.bind(this),
148 set: function(value) {
149 if (this.on[propertyName]) {
150 this.view.element.removeEventListener(eventName, this.on[propertyName]);
152 this.on[propertyName] = value;
153 if (value) {
154 this.view.element.addEventListener(eventName, value);
156 }.bind(this),
157 enumerable: true
161 // returns a wrapper for |func| with a weak reference to |this|.
162 GuestViewEvents.prototype.weakWrapper = function(func) {
163 var viewInstanceId = this.view.viewInstanceId;
164 return function() {
165 var view = GuestViewInternalNatives.GetViewFromID(viewInstanceId);
166 if (!view) {
167 return;
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 {}; };
176 // Exports.
177 exports.GuestViewEvents = GuestViewEvents;
178 exports.CreateEvent = CreateEvent;