UNPKG

@aeternity/aepp-sdk

Version:
144 lines (134 loc) 5.87 kB
/* * ISC License (ISC) * Copyright (c) 2018 aeternity developers * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /** * Browser window Post Message connector module * * This is the complement to {@link module:@aeternity/aepp-sdk/es/utils/aepp-wallet-communication/connection}. * @module @aeternity/aepp-sdk/es/utils/aepp-wallet-communication/connection/browser-window-message * @export BrowserWindowMessageConnection * @example import BrowserWindowMessageConnection from '@aeternity/aepp-sdk/es/utils/aepp-wallet-communication/connection/browser-window-message' */ import stampit from '@stamp/it' import WalletConnection from '.' import { v4 as uuid } from 'uuid' import { MESSAGE_DIRECTION } from '../schema' import { getBrowserAPI, isInIframe } from '../helpers' /** * Check if connected * @function isConnected * @instance * @rtype () => Boolean * @return {Boolean} Is connected */ function isConnected () { return this.listener } /** * Connect * @function connect * @instance * @rtype (onMessage: Function) => void * @param {Function} onMessage - Message handler * @return {void} */ function connect (onMessage) { const origin = this.origin const receiveDirection = this.receiveDirection const debug = this.debug const forceOrigin = this.forceOrigin if (this.listener) throw new Error('You already connected') this.listener = (msg, source) => { if (!msg || typeof msg.data !== 'object') return if (!forceOrigin && origin && origin !== msg.origin) return if (debug) console.log('Receive message: ', msg) if (msg.data.type) { if (msg.data.type !== receiveDirection) return onMessage(msg.data.data, msg.origin, msg.source) } else { onMessage(msg.data, msg.origin, msg.source) } } this.subscribeFn(this.listener) } /** * Disconnect * @function disconnect * @instance * @rtype () => void * @return {void} */ function disconnect () { if (!this.listener) throw new Error('You dont have connection. Please connect before') this.unsubscribeFn(this.listener) this.listener = null } /** * Send message * @function sendMessage * @instance * @rtype (msg: Object) => void * @param {Object} msg - Message * @return {void} */ function sendMessage (msg) { const message = this.sendDirection ? { type: this.sendDirection, data: msg } : msg if (this.debug) console.log('Send message: ', message) this.postFn(message) } const getTarget = () => { const isExtensionContext = typeof getBrowserAPI(true).extension === 'object' const isWeb = window && window.location && window.location.protocol.startsWith('http') const isContentScript = isExtensionContext && isWeb if (isContentScript) return window // When we is the main page we need to decide the target by our self // Probably can be implemented some algo for checking DOM for Iframes and somehow decide which Iframe to talk return isInIframe() ? window.parent : undefined } /** * BrowserWindowMessageConnection * @function * @alias module:@aeternity/aepp-sdk/es/utils/aepp-wallet-communication/connection/browser-window-message * @rtype Stamp * @param {Object} [params={}] - Initializer object * @param {Object} [params.target=window.parent] - Target window for message * @param {Object} [params.self=window] - Host window for message * @param {Object} [params.origin] - Origin of receiver * @param {Object} [params.sendDirection] - Optional field for wrapping messages in additional structure({ type: 'to_aepp' || 'to_waellet', data }).Used for handling messages netween content script and page * @param {Object} [params.receiveDirection='to_aepp'] - Optional(default: 'to_aepp') field for unwrapping messages from additional structure({ type: 'to_aepp' || 'to_waellet', data }).Used for handling messages netween content script and page * @param {Object} [params.connectionInfo={}] - Connection info object * @param {Boolean} [params.debug=false] - Debug flag * @return {Object} */ export default stampit({ init ({ connectionInfo = {}, target = getTarget(), self = window, origin, sendDirection, receiveDirection = MESSAGE_DIRECTION.to_aepp, debug = false, forceOrigin = false } = {}) { if (sendDirection && !Object.keys(MESSAGE_DIRECTION).includes(sendDirection)) throw new Error(`sendDirection must be one of [${Object.keys(MESSAGE_DIRECTION)}]`) if (!Object.keys(MESSAGE_DIRECTION).includes(receiveDirection)) throw new Error(`receiveDirection must be one of [${Object.keys(MESSAGE_DIRECTION)}]`) this.connectionInfo = { id: uuid(), ...connectionInfo } const selfP = self const targetP = target this.origin = origin this.debug = debug this.forceOrigin = forceOrigin this.sendDirection = sendDirection this.receiveDirection = receiveDirection this.subscribeFn = (listener) => selfP.addEventListener('message', listener, false) this.unsubscribeFn = (listener) => selfP.removeEventListener('message', listener, false) this.postFn = (msg) => targetP.postMessage(msg, this.origin || '*') if (!this.connectionInfo.id) throw new Error('ID required.') }, methods: { connect, sendMessage, disconnect, isConnected } }, WalletConnection)