"use strict"; // License: MIT import { EventEmitter } from "./events"; // eslint-disable-next-line no-unused-vars import {runtime, tabs, RawPort, MessageSender} from "./browser"; export class Port extends EventEmitter { private port: RawPort | null; constructor(port: RawPort) { super(); this.port = port; let disconnected = false; const disconnect = () => { if (disconnected) { return; } disconnected = true; this.port = null; // Break the cycle this.emit("disconnect", this, port); }; // Nasty firefox bug, thus listen for tab removal explicitly if (port.sender && port.sender.tab && port.sender.tab.id) { const otherTabId = port.sender.tab.id; const tabListener = function(tabId: number) { if (tabId !== otherTabId) { return; } disconnect(); }; tabs.onRemoved.addListener(tabListener); } port.onMessage.addListener(this.onMessage.bind(this)); port.onDisconnect.addListener(disconnect); } get name() { if (!this.port) { return null; } return this.port.name; } get id() { if (!this.port || !this.port.sender) { return null; } return this.port.sender.id; } get isSelf() { return this.id === runtime.id; } post(msg: string, ...data: any[]) { if (!this.port) { return; } if (!data) { this.port.postMessage({msg}); return; } if (data.length === 1) { [data] = data; } this.port.postMessage({msg, data}); } onMessage(message: any) { if (!this.port) { return; } if (Array.isArray(message)) { message.forEach(this.onMessage, this); return; } if (Object.keys(message).includes("msg")) { this.emit(message.msg, message); return; } if (typeof message === "string") { this.emit(message); return; } console.error(`Unhandled message in ${this.port.name}:`, message); } } export const Bus = new class extends EventEmitter { private readonly ports: EventEmitter; public readonly onPort: (event: string, port: (port: Port) => void) => void; public readonly offPort: Function; public readonly oncePort: (event: string, port: (port: Port) => void) => void; constructor() { super(); this.ports = new EventEmitter(); this.onPort = this.ports.on.bind(this.ports); this.offPort = this.ports.off.bind(this.ports); this.oncePort = this.ports.once.bind(this.ports); runtime.onMessage.addListener(this.onMessage.bind(this)); runtime.onConnect.addListener(this.onConnect.bind(this)); } onMessage(msg: any, sender: MessageSender, callback: any) { let {type = null} = msg; if (!type) { type = msg; } this.emit(type, msg, callback); } onConnect(port: RawPort) { if (!port.name) { port.disconnect(); return; } this.ports.emit(port.name, new Port(port)); } }();