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');
15 var ResizeEvent
= CreateEvent('guestViewInternal.onResize');
18 var GUEST_STATE_ATTACHED
= 2;
19 var GUEST_STATE_CREATED
= 1;
20 var GUEST_STATE_START
= 0;
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.';
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
35 function GuestViewImpl(guestView
, viewType
, guestInstanceId
) {
36 if (guestInstanceId
) {
37 this.id
= guestInstanceId
;
38 this.state
= GUEST_STATE_CREATED
;
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;
53 // Sets up the onResize property on the GuestView.
54 GuestViewImpl
.prototype.setupOnResize = function() {
55 $Object
.defineProperty(this.guestView
, PROPERTY_ON_RESIZE
, {
57 return this[PROPERTY_ON_RESIZE
];
59 set: function(value
) {
60 this[PROPERTY_ON_RESIZE
] = value
;
65 this.callOnResize = function(e
) {
66 if (!this[PROPERTY_ON_RESIZE
]) {
69 this[PROPERTY_ON_RESIZE
](e
);
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
) {
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();
93 // Check the current state to see if the proposed action is valid. Returns false
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
);
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.
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
);
122 // Report the error if the proposed action is found to be invalid for the
125 if (error
= errors
[action
][this.state
]) {
126 window
.console
.error(errorPrefix
+ error
);
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
);
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;
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
,
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
,
170 if (this.state
!= GUEST_STATE_ATTACHED
||
171 this.internalInstanceId
!= internalInstanceId
) {
175 this.internalInstanceId
= 0;
176 this.state
= GUEST_STATE_CREATED
;
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
);
188 // Callback wrapper function to store the guestInstanceId from the
189 // createGuest() callback, handle potential creation failure, and advance the
191 var callbackWrapper = function(callback
, guestInfo
) {
192 this.id
= guestInfo
.id
;
194 GuestViewInternalNatives
.GetContentWindow(guestInfo
.contentWindowId
);
196 // Check if creation failed.
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
,
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
);
221 if (this.state
== GUEST_STATE_START
) {
222 // destroy() does nothing in this case.
223 this.handleCallback(callback
);
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;
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
);
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
);
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
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
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;
334 exports
.GuestView
= GuestView
;