penpal
Version:
A promise-based library for communicating with iframes via postMessage.
114 lines (91 loc) • 3.24 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _createDestructor = _interopRequireDefault(require("../createDestructor"));
var _createLogger = _interopRequireDefault(require("../createLogger"));
var _enums = require("../enums");
var _validateWindowIsIframe = _interopRequireDefault(require("./validateWindowIsIframe"));
var _handleSynAckMessageFactory = _interopRequireDefault(require("./handleSynAckMessageFactory"));
var _startConnectionTimeout = _interopRequireDefault(require("../startConnectionTimeout"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const areGlobalsAccessible = () => {
try {
clearTimeout();
} catch (e) {
return false;
}
return true;
};
/**
* Attempts to establish communication with the parent window.
*/
var _default = (options = {}) => {
const {
parentOrigin = '*',
methods = {},
timeout,
debug = false
} = options;
const log = (0, _createLogger.default)(debug);
const destructor = (0, _createDestructor.default)();
const {
destroy,
onDestroy
} = destructor;
(0, _validateWindowIsIframe.default)();
const handleSynAckMessage = (0, _handleSynAckMessageFactory.default)(parentOrigin, methods, destructor, log);
const sendSynMessage = () => {
log('Child: Handshake - Sending SYN');
const synMessage = {
penpal: _enums.MessageType.Syn
};
const parentOriginForSyn = parentOrigin instanceof RegExp ? '*' : parentOrigin;
window.parent.postMessage(synMessage, parentOriginForSyn);
};
const promise = new Promise((resolve, reject) => {
const stopConnectionTimeout = (0, _startConnectionTimeout.default)(timeout, destroy);
const handleMessage = event => {
// Under niche scenarios, we get into this function after
// the iframe has been removed from the DOM. In Edge, this
// results in "Object expected" errors being thrown when we
// try to access properties on window (global properties).
// For this reason, we try to access a global up front (clearTimeout)
// and if it fails we can assume the iframe has been removed
// and we ignore the message event.
if (!areGlobalsAccessible()) {
return;
}
if (event.source !== parent || !event.data) {
return;
}
if (event.data.penpal === _enums.MessageType.SynAck) {
const callSender = handleSynAckMessage(event);
if (callSender) {
window.removeEventListener(_enums.NativeEventType.Message, handleMessage);
stopConnectionTimeout();
resolve(callSender);
}
}
};
window.addEventListener(_enums.NativeEventType.Message, handleMessage);
sendSynMessage();
onDestroy(error => {
window.removeEventListener(_enums.NativeEventType.Message, handleMessage);
if (!error) {
error = new Error('Connection destroyed');
error.code = _enums.ErrorCode.ConnectionDestroyed;
}
reject(error);
});
});
return {
promise,
destroy() {
// Don't allow consumer to pass an error into destroy.
destroy();
}
};
};
exports.default = _default;