Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / extensions / renderer / resources / guest_view / guest_view.js
blob27368a488f792040d6cfcfd90d270f48dc8ffd46
1 // Copyright 2014 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 // This module implements a wrapper for a guestview that manages its
6 // creation, attaching, and destruction.
8 var CreateEvent = require('guestViewEvents').CreateEvent;
9 var EventBindings = require('event_bindings');
10 var GuestViewInternal =
11 require('binding').Binding.create('guestViewInternal').generate();
12 var GuestViewInternalNatives = requireNative('guest_view_internal');
14 // Events.
15 var ResizeEvent = CreateEvent('guestViewInternal.onResize');
17 // Possible states.
18 var GUEST_STATE_ATTACHED = 2;
19 var GUEST_STATE_CREATED = 1;
20 var GUEST_STATE_START = 0;
22 // Error messages.
23 var ERROR_MSG_ALREADY_ATTACHED = 'The guest has already been attached.';
24 var ERROR_MSG_ALREADY_CREATED = 'The guest has already been created.';
25 var ERROR_MSG_INVALID_STATE = 'The guest is in an invalid state.';
26 var ERROR_MSG_NOT_ATTACHED = 'The guest is not attached.';
27 var ERROR_MSG_NOT_CREATED = 'The guest has not been created.';
29 // Properties.
30 var PROPERTY_ON_RESIZE = 'onresize';
32 // Contains and hides the internal implementation details of |GuestView|,
33 // including maintaining its state and enforcing the proper usage of its API
34 // fucntions.
35 function GuestViewImpl(guestView, viewType, guestInstanceId) {
36 if (guestInstanceId) {
37 this.id = guestInstanceId;
38 this.state = GUEST_STATE_CREATED;
39 } else {
40 this.id = 0;
41 this.state = GUEST_STATE_START;
43 this.actionQueue = [];
44 this.contentWindow = null;
45 this.guestView = guestView;
46 this.pendingAction = null;
47 this.viewType = viewType;
48 this.internalInstanceId = 0;
50 this.setupOnResize();
53 // Sets up the onResize property on the GuestView.
54 GuestViewImpl.prototype.setupOnResize = function() {
55 $Object.defineProperty(this.guestView, PROPERTY_ON_RESIZE, {
56 get: function() {
57 return this[PROPERTY_ON_RESIZE];
58 }.bind(this),
59 set: function(value) {
60 this[PROPERTY_ON_RESIZE] = value;
61 }.bind(this),
62 enumerable: true
63 });
65 this.callOnResize = function(e) {
66 if (!this[PROPERTY_ON_RESIZE]) {
67 return;
69 this[PROPERTY_ON_RESIZE](e);
70 }.bind(this);
73 // Callback wrapper that is used to call the callback of the pending action (if
74 // one exists), and then performs the next action in the queue.
75 GuestViewImpl.prototype.handleCallback = function(callback) {
76 if (callback) {
77 callback();
79 this.pendingAction = null;
80 this.performNextAction();
83 // Perform the next action in the queue, if one exists.
84 GuestViewImpl.prototype.performNextAction = function() {
85 // Make sure that there is not already an action in progress, and that there
86 // exists a queued action to perform.
87 if (!this.pendingAction && this.actionQueue.length) {
88 this.pendingAction = this.actionQueue.shift();
89 this.pendingAction();
93 // Check the current state to see if the proposed action is valid. Returns false
94 // if invalid.
95 GuestViewImpl.prototype.checkState = function(action) {
96 // Create an error prefix based on the proposed action.
97 var errorPrefix = 'Error calling ' + action + ': ';
99 // Check that the current state is valid.
100 if (!(this.state >= 0 && this.state <= 2)) {
101 window.console.error(errorPrefix + ERROR_MSG_INVALID_STATE);
102 return false;
105 // Map of possible errors for each action. For each action, the errors are
106 // listed for states in the order: GUEST_STATE_START, GUEST_STATE_CREATED,
107 // GUEST_STATE_ATTACHED.
108 var errors = {
109 'attach': [ERROR_MSG_NOT_CREATED, null, ERROR_MSG_ALREADY_ATTACHED],
110 'create': [null, ERROR_MSG_ALREADY_CREATED, ERROR_MSG_ALREADY_CREATED],
111 'destroy': [null, null, null],
112 'detach': [ERROR_MSG_NOT_ATTACHED, ERROR_MSG_NOT_ATTACHED, null],
113 'setSize': [ERROR_MSG_NOT_CREATED, null, null]
116 // Check that the proposed action is a real action.
117 if (errors[action] == undefined) {
118 window.console.error(errorPrefix + ERROR_MSG_INVALID_ACTION);
119 return false;
122 // Report the error if the proposed action is found to be invalid for the
123 // current state.
124 var error;
125 if (error = errors[action][this.state]) {
126 window.console.error(errorPrefix + error);
127 return false;
130 return true;
133 // Internal implementation of attach().
134 GuestViewImpl.prototype.attachImpl = function(
135 internalInstanceId, viewInstanceId, attachParams, callback) {
136 // Check the current state.
137 if (!this.checkState('attach')) {
138 this.handleCallback(callback);
139 return;
142 // Callback wrapper function to store the contentWindow from the attachGuest()
143 // callback, handle potential attaching failure, register an automatic detach,
144 // and advance the queue.
145 var callbackWrapper = function(callback, contentWindow) {
146 // Check if attaching failed.
147 if (!contentWindow) {
148 this.state = GUEST_STATE_CREATED;
149 this.internalInstanceId = 0;
150 } else {
151 // Only update the contentWindow if attaching is successful.
152 this.contentWindow = contentWindow;
155 this.handleCallback(callback);
158 attachParams['instanceId'] = viewInstanceId;
159 GuestViewInternalNatives.AttachGuest(internalInstanceId,
160 this.id,
161 attachParams,
162 callbackWrapper.bind(this, callback));
164 this.internalInstanceId = internalInstanceId;
165 this.state = GUEST_STATE_ATTACHED;
167 // Detach automatically when the container is destroyed.
168 GuestViewInternalNatives.RegisterDestructionCallback(internalInstanceId,
169 function() {
170 if (this.state != GUEST_STATE_ATTACHED ||
171 this.internalInstanceId != internalInstanceId) {
172 return;
175 this.internalInstanceId = 0;
176 this.state = GUEST_STATE_CREATED;
177 }.bind(this));
180 // Internal implementation of create().
181 GuestViewImpl.prototype.createImpl = function(createParams, callback) {
182 // Check the current state.
183 if (!this.checkState('create')) {
184 this.handleCallback(callback);
185 return;
188 // Callback wrapper function to store the guestInstanceId from the
189 // createGuest() callback, handle potential creation failure, and advance the
190 // queue.
191 var callbackWrapper = function(callback, guestInfo) {
192 this.id = guestInfo.id;
193 this.contentWindow =
194 GuestViewInternalNatives.GetContentWindow(guestInfo.contentWindowId);
196 // Check if creation failed.
197 if (this.id === 0) {
198 this.state = GUEST_STATE_START;
199 this.contentWindow = null;
202 ResizeEvent.addListener(this.callOnResize, {instanceId: this.id});
203 this.handleCallback(callback);
206 GuestViewInternal.createGuest(this.viewType,
207 createParams,
208 callbackWrapper.bind(this, callback));
210 this.state = GUEST_STATE_CREATED;
213 // Internal implementation of destroy().
214 GuestViewImpl.prototype.destroyImpl = function(callback) {
215 // Check the current state.
216 if (!this.checkState('destroy')) {
217 this.handleCallback(callback);
218 return;
221 if (this.state == GUEST_STATE_START) {
222 // destroy() does nothing in this case.
223 this.handleCallback(callback);
224 return;
227 // If this guest is attached, then detach it first.
228 if (!!this.internalInstanceId) {
229 GuestViewInternalNatives.DetachGuest(this.internalInstanceId);
232 GuestViewInternal.destroyGuest(this.id,
233 this.handleCallback.bind(this, callback));
235 // Reset the state of the destroyed guest;
236 this.contentWindow = null;
237 this.id = 0;
238 this.internalInstanceId = 0;
239 this.state = GUEST_STATE_START;
240 if (ResizeEvent.hasListener(this.callOnResize)) {
241 ResizeEvent.removeListener(this.callOnResize);
245 // Internal implementation of detach().
246 GuestViewImpl.prototype.detachImpl = function(callback) {
247 // Check the current state.
248 if (!this.checkState('detach')) {
249 this.handleCallback(callback);
250 return;
253 GuestViewInternalNatives.DetachGuest(
254 this.internalInstanceId,
255 this.handleCallback.bind(this, callback));
257 this.internalInstanceId = 0;
258 this.state = GUEST_STATE_CREATED;
261 // Internal implementation of setSize().
262 GuestViewImpl.prototype.setSizeImpl = function(sizeParams, callback) {
263 // Check the current state.
264 if (!this.checkState('setSize')) {
265 this.handleCallback(callback);
266 return;
269 GuestViewInternal.setSize(this.id, sizeParams,
270 this.handleCallback.bind(this, callback));
273 // The exposed interface to a guestview. Exposes in its API the functions
274 // attach(), create(), destroy(), and getId(). All other implementation details
275 // are hidden.
276 function GuestView(viewType, guestInstanceId) {
277 privates(this).internal = new GuestViewImpl(this, viewType, guestInstanceId);
280 // Attaches the guestview to the container with ID |internalInstanceId|.
281 GuestView.prototype.attach = function(
282 internalInstanceId, viewInstanceId, attachParams, callback) {
283 var internal = privates(this).internal;
284 internal.actionQueue.push(internal.attachImpl.bind(
285 internal, internalInstanceId, viewInstanceId, attachParams, callback));
286 internal.performNextAction();
289 // Creates the guestview.
290 GuestView.prototype.create = function(createParams, callback) {
291 var internal = privates(this).internal;
292 internal.actionQueue.push(internal.createImpl.bind(
293 internal, createParams, callback));
294 internal.performNextAction();
297 // Destroys the guestview. Nothing can be done with the guestview after it has
298 // been destroyed.
299 GuestView.prototype.destroy = function(callback) {
300 var internal = privates(this).internal;
301 internal.actionQueue.push(internal.destroyImpl.bind(internal, callback));
302 internal.performNextAction();
305 // Detaches the guestview from its container.
306 // Note: This is not currently used.
307 GuestView.prototype.detach = function(callback) {
308 var internal = privates(this).internal;
309 internal.actionQueue.push(internal.detachImpl.bind(internal, callback));
310 internal.performNextAction();
313 // Adjusts the guestview's sizing parameters.
314 GuestView.prototype.setSize = function(sizeParams, callback) {
315 var internal = privates(this).internal;
316 internal.actionQueue.push(internal.setSizeImpl.bind(
317 internal, sizeParams, callback));
318 internal.performNextAction();
321 // Returns the contentWindow for this guestview.
322 GuestView.prototype.getContentWindow = function() {
323 var internal = privates(this).internal;
324 return internal.contentWindow;
327 // Returns the ID for this guestview.
328 GuestView.prototype.getId = function() {
329 var internal = privates(this).internal;
330 return internal.id;
333 // Exports
334 exports.GuestView = GuestView;