File: client/auth/lightbox/iframe_channel.js
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Communicate with an iframed content server.
*
* @class IFrameChannel
*/
define([
'p-promise',
'client/lib/object'
], function (p, ObjectHelpers) {
'use strict';
function IFrameChannel(options) {
options = options || {};
this._window = options.window;
this._contentWindow = options.contentWindow;
this._iframeHost = options.iframeHost;
}
IFrameChannel.prototype = {
/**
* Protocol version number. When the protocol to communicate with the
* content server changes, this should be bumped.
* @property version
* @type {String}
*/
version: '0.0.0',
/**
* Start listening for messages from the iframe.
* @method attach
*/
attach: function () {
this._boundOnMessage = onMessage.bind(this);
this._window.addEventListener('message', this._boundOnMessage, false);
this._deferred = p.defer();
return this._deferred.promise;
},
/**
* Stop listening for messages from the iframe.
* @method detach
*/
detach: function () {
this._window.removeEventListener('message', this._boundOnMessage, false);
},
/**
* Send a message to the iframe.
*
* @method send
* @param {String} command
* Message to send.
* @param {Object} [data]
* Data to send.
*/
send: function (command, data) {
var dataToSend = ObjectHelpers.extend({ version: this.version }, data);
var msg = stringifyFxAEvent(command, dataToSend);
this._contentWindow.postMessage(msg, this._iframeHost);
}
};
// commands that come from the iframe. They are called
// in the Lightbox object context.
var COMMANDS = {
error: function (command, data) {
this.detach();
this._deferred.reject(data);
},
/*jshint camelcase:false*/
ping: function (command, data) {
// ping is used to get the RP's origin. If the RP's origin is not
// whitelisted, it cannot be iframed.
this.send(command, data);
},
ignore: function (command, data) {
console.log('ignoring command: %s', command);
},
oauth_cancel: function (command, data) {
this.detach();
return this._deferred.reject({ reason: 'cancel' });
},
oauth_complete: function (command, data) {
this.detach();
this._deferred.resolve(data);
}
};
function onMessage(event) {
/*jshint validthis: true*/
if (event.origin !== this._iframeHost) {
return;
}
var parsed = parseFxAEvent(event.data);
var command = parsed.command;
var data = parsed.data;
var handler = COMMANDS[command] || COMMANDS.ignore;
handler.call(this, command, data);
}
function parseFxAEvent(msg) {
return JSON.parse(msg);
}
function stringifyFxAEvent(command, data) {
return JSON.stringify({
command: command,
data: data || {}
});
}
IFrameChannel.stringifyFxAEvent = stringifyFxAEvent;
IFrameChannel.parseFxAEvent = parseFxAEvent;
return IFrameChannel;
});