UNPKG

penpal

Version:

A promise-based library for communicating with iframes via postMessage.

114 lines (91 loc) 3.24 kB
"use strict"; 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;