mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 01:29:58 +00:00
Add typed messaging
This commit is contained in:
@@ -3,11 +3,9 @@
|
||||
import bridge from "../lib/bridge";
|
||||
import loadSender from "../lib/loadSender";
|
||||
import logger from "../lib/logger";
|
||||
import { Message, Port } from "../lib/messaging";
|
||||
import options from "../lib/options";
|
||||
|
||||
import { Message } from "../types";
|
||||
|
||||
import { getMediaTypesForPageUrl } from "../lib/utils";
|
||||
import { ReceiverSelectionActionType
|
||||
, ReceiverSelectorMediaType } from "./receiverSelector";
|
||||
|
||||
@@ -17,11 +15,11 @@ import ReceiverSelectorManager
|
||||
import StatusManager from "./StatusManager";
|
||||
|
||||
|
||||
type Port = browser.runtime.Port | MessagePort;
|
||||
type AnyPort = Port | MessagePort;
|
||||
|
||||
export interface Shim {
|
||||
bridgePort: browser.runtime.Port;
|
||||
contentPort: Port;
|
||||
bridgePort: Port;
|
||||
contentPort: AnyPort;
|
||||
contentTabId?: number;
|
||||
contentFrameId?: number;
|
||||
requestedAppId?: string;
|
||||
@@ -48,7 +46,7 @@ export default new class ShimManager {
|
||||
}
|
||||
}
|
||||
|
||||
public async createShim (port: Port) {
|
||||
public async createShim (port: AnyPort) {
|
||||
const shim = await (port instanceof MessagePort
|
||||
? this.createShimFromBackground(port)
|
||||
: this.createShimFromContent(port));
|
||||
@@ -74,20 +72,19 @@ export default new class ShimManager {
|
||||
this.activeShims.delete(shim);
|
||||
});
|
||||
|
||||
shim.bridgePort.onMessage.addListener((message: Message) => {
|
||||
shim.bridgePort.onMessage.addListener(message => {
|
||||
contentPort.postMessage(message);
|
||||
});
|
||||
|
||||
contentPort.onmessage = ev => {
|
||||
const message = ev.data as Message;
|
||||
this.handleContentMessage(shim, message);
|
||||
};
|
||||
contentPort.addEventListener("message", ev => {
|
||||
this.handleContentMessage(shim, ev.data);
|
||||
});
|
||||
|
||||
return shim;
|
||||
}
|
||||
|
||||
private async createShimFromContent (
|
||||
contentPort: browser.runtime.Port): Promise<Shim> {
|
||||
contentPort: Port): Promise<Shim> {
|
||||
|
||||
if (contentPort.sender?.tab?.id === undefined
|
||||
|| contentPort.sender?.frameId === undefined) {
|
||||
|
||||
@@ -2,44 +2,23 @@
|
||||
|
||||
import bridge from "../lib/bridge";
|
||||
import logger from "../lib/logger";
|
||||
import { Message, Port } from "../lib/messaging";
|
||||
|
||||
import { TypedEventTarget } from "../lib/typedEvents";
|
||||
import { Message, Receiver, ReceiverStatus } from "../types";
|
||||
|
||||
|
||||
interface ReceiverStatusMessage extends Message {
|
||||
subject: "receiverStatus";
|
||||
data: {
|
||||
id: string;
|
||||
status: ReceiverStatus;
|
||||
};
|
||||
}
|
||||
|
||||
interface ServiceDownMessage extends Message {
|
||||
subject: "shim:/serviceDown";
|
||||
data: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ServiceUpMessage extends Message {
|
||||
subject: "shim:/serviceUp";
|
||||
data: Receiver;
|
||||
}
|
||||
|
||||
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||
import { Receiver, ReceiverStatus } from "../types";
|
||||
|
||||
|
||||
interface EventMap {
|
||||
"serviceUp": ServiceUpMessage["data"];
|
||||
"serviceDown": ServiceDownMessage["data"];
|
||||
"statusUpdate": ReceiverStatusMessage["data"];
|
||||
"serviceUp": Receiver;
|
||||
"serviceDown": { id: string };
|
||||
"statusUpdate": { id: string, status: ReceiverStatus };
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:new-parens
|
||||
export default new class StatusManager
|
||||
extends TypedEventTarget<EventMap> {
|
||||
|
||||
private bridgePort: (browser.runtime.Port | null) = null;
|
||||
private bridgePort: (Port | null) = null;
|
||||
private receivers = new Map<string, Receiver>();
|
||||
|
||||
constructor () {
|
||||
@@ -97,8 +76,8 @@ export default new class StatusManager
|
||||
*/
|
||||
private onBridgePortMessage (message: Message) {
|
||||
switch (message.subject) {
|
||||
case "shim:/serviceUp": {
|
||||
const { data: receiver } = (message as ServiceUpMessage);
|
||||
case "main:/serviceUp": {
|
||||
const { data: receiver } = message;
|
||||
this.receivers.set(receiver.id, receiver);
|
||||
|
||||
const serviceUpEvent = new CustomEvent("serviceUp", {
|
||||
@@ -110,8 +89,8 @@ export default new class StatusManager
|
||||
break;
|
||||
}
|
||||
|
||||
case "shim:/serviceDown": {
|
||||
const { data: { id }} = (message as ServiceDownMessage);
|
||||
case "main:/serviceDown": {
|
||||
const { data: { id }} = message;
|
||||
|
||||
if (this.receivers.has(id)) {
|
||||
this.receivers.delete(id);
|
||||
@@ -126,10 +105,8 @@ export default new class StatusManager
|
||||
break;
|
||||
}
|
||||
|
||||
case "receiverStatus": {
|
||||
const { data: { id, status }}
|
||||
= (message as ReceiverStatusMessage);
|
||||
|
||||
case "main:/receiverStatus": {
|
||||
const { data: { id, status }} = message;
|
||||
const receiver = this.receivers.get(id);
|
||||
|
||||
if (!receiver) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import defaultOptions from "../defaultOptions";
|
||||
import loadSender from "../lib/loadSender";
|
||||
import logger from "../lib/logger";
|
||||
import messaging from "../lib/messaging";
|
||||
import options from "../lib/options";
|
||||
|
||||
import { getChromeUserAgent } from "../lib/userAgents";
|
||||
@@ -617,9 +618,9 @@ async function init () {
|
||||
* established, pass it to createShim to handle the setup
|
||||
* and maintenance.
|
||||
*/
|
||||
browser.runtime.onConnect.addListener(async port => {
|
||||
messaging.onConnect.addListener(async port => {
|
||||
if (port.name === "shim") {
|
||||
ShimManager.createShim(port);
|
||||
ShimManager.createShim(port as any);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
import bridge from "../../lib/bridge";
|
||||
import knownApps from "../../lib/knownApps";
|
||||
import logger from "../../lib/logger";
|
||||
import { Message, Port } from "../../lib/messaging";
|
||||
import options from "../../lib/options";
|
||||
|
||||
import { TypedEventTarget } from "../../lib/typedEvents";
|
||||
import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { getWindowCenteredProps } from "../../lib/utils";
|
||||
import { Message, Receiver } from "../../types";
|
||||
import { Receiver } from "../../types";
|
||||
|
||||
import ReceiverSelector, {
|
||||
ReceiverSelection
|
||||
@@ -17,27 +18,20 @@ import ReceiverSelector, {
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
|
||||
interface NativeReceiverSelectorSelectedMessage extends Message {
|
||||
subject: "main:/receiverSelector/selected";
|
||||
data: ReceiverSelection;
|
||||
}
|
||||
|
||||
interface NativeReceiverSelectorErrorMessage extends Message {
|
||||
subject: "main:/receiverSelector/error";
|
||||
data: string;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Figure out lifetime properly
|
||||
export default class NativeReceiverSelector
|
||||
extends TypedEventTarget<ReceiverSelectorEvents>
|
||||
implements ReceiverSelector {
|
||||
|
||||
private bridgePort: (browser.runtime.Port | null) = null;
|
||||
private bridgePort: (Port | null) = null;
|
||||
private wasReceiverSelected: boolean = false;
|
||||
private _isOpen: boolean = false;
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
this.onBridgePortMessage = this.onBridgePortMessage.bind(this);
|
||||
}
|
||||
|
||||
get isOpen () {
|
||||
return this._isOpen;
|
||||
}
|
||||
@@ -50,31 +44,7 @@ export default class NativeReceiverSelector
|
||||
|
||||
this.bridgePort = await bridge.connect();
|
||||
|
||||
this.bridgePort.onMessage.addListener((message: Message) => {
|
||||
switch (message.subject) {
|
||||
case "main:/receiverSelector/selected": {
|
||||
this.onBridgePortMessageSelected(
|
||||
message as NativeReceiverSelectorSelectedMessage);
|
||||
break;
|
||||
}
|
||||
case "main:/receiverSelector/error": {
|
||||
this.onBridgePortMessageError(
|
||||
message as NativeReceiverSelectorErrorMessage);
|
||||
break;
|
||||
}
|
||||
case "main:/receiverSelector/close": {
|
||||
this.onBridgePortMessageClose();
|
||||
break;
|
||||
}
|
||||
case "main:/receiverSelector/stop": {
|
||||
this.dispatchEvent(new CustomEvent("stop", {
|
||||
detail: message.data
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.bridgePort.onMessage.addListener(this.onBridgePortMessage);
|
||||
this.bridgePort.onDisconnect.addListener(() => {
|
||||
this.bridgePort = null;
|
||||
this.wasReceiverSelected = false;
|
||||
@@ -128,40 +98,46 @@ export default class NativeReceiverSelector
|
||||
this._isOpen = false;
|
||||
}
|
||||
|
||||
private async onBridgePortMessage (message: Message) {
|
||||
switch (message.subject) {
|
||||
case "main:/receiverSelector/selected": {
|
||||
this.wasReceiverSelected = true;
|
||||
this.dispatchEvent(new CustomEvent("selected", {
|
||||
detail: message.data
|
||||
}));
|
||||
|
||||
private async onBridgePortMessageSelected (
|
||||
message: NativeReceiverSelectorSelectedMessage) {
|
||||
if (!(await options.get("receiverSelectorWaitForConnection"))) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.wasReceiverSelected = true;
|
||||
break;
|
||||
}
|
||||
case "main:/receiverSelector/error": {
|
||||
logger.error("Native receiver selector error", message.data);
|
||||
this.dispatchEvent(new CustomEvent("error"));
|
||||
break;
|
||||
}
|
||||
case "main:/receiverSelector/close": {
|
||||
if (!this.wasReceiverSelected) {
|
||||
this.dispatchEvent(new CustomEvent("cancelled"));
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent("selected", {
|
||||
detail: message.data
|
||||
}));
|
||||
if (this.bridgePort) {
|
||||
this.bridgePort.disconnect();
|
||||
}
|
||||
|
||||
if (!(await options.get("receiverSelectorWaitForConnection"))) {
|
||||
this.close();
|
||||
this.bridgePort = null;
|
||||
this.wasReceiverSelected = false;
|
||||
this._isOpen = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case "main:/receiverSelector/stop": {
|
||||
this.dispatchEvent(new CustomEvent("stop", {
|
||||
detail: message.data
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async onBridgePortMessageError (
|
||||
message: NativeReceiverSelectorErrorMessage) {
|
||||
|
||||
logger.error("Native receiver selector error", message.data);
|
||||
|
||||
this.dispatchEvent(new CustomEvent("error"));
|
||||
}
|
||||
|
||||
private async onBridgePortMessageClose () {
|
||||
if (!this.wasReceiverSelected) {
|
||||
this.dispatchEvent(new CustomEvent("cancelled"));
|
||||
}
|
||||
|
||||
if (this.bridgePort) {
|
||||
this.bridgePort.disconnect();
|
||||
}
|
||||
|
||||
this.bridgePort = null;
|
||||
this.wasReceiverSelected = false;
|
||||
this._isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ import ReceiverSelector, {
|
||||
, ReceiverSelectorMediaType } from "./ReceiverSelector";
|
||||
|
||||
import logger from "../../lib/logger";
|
||||
import messaging, { Port, Message } from "../../lib/messaging";
|
||||
import options from "../../lib/options";
|
||||
|
||||
import { TypedEventTarget } from "../../lib/typedEvents";
|
||||
import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
|
||||
import { Message, Receiver } from "../../types";
|
||||
import { Receiver } from "../../types";
|
||||
|
||||
|
||||
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
|
||||
@@ -20,7 +21,7 @@ export default class PopupReceiverSelector
|
||||
|
||||
private windowId?: number;
|
||||
|
||||
private messagePort?: browser.runtime.Port;
|
||||
private messagePort?: Port;
|
||||
private messagePortDisconnected?: boolean;
|
||||
|
||||
private receivers?: Receiver[];
|
||||
@@ -37,6 +38,7 @@ export default class PopupReceiverSelector
|
||||
super();
|
||||
|
||||
// Bind methods to pass to addListener
|
||||
this.onConnect = this.onConnect.bind(this);
|
||||
this.onPopupMessage = this.onPopupMessage.bind(this);
|
||||
this.onWindowsRemoved = this.onWindowsRemoved.bind(this);
|
||||
this.onWindowsFocusChanged = this.onWindowsFocusChanged.bind(this);
|
||||
@@ -47,39 +49,7 @@ export default class PopupReceiverSelector
|
||||
* Handle incoming message channel connection from popup
|
||||
* window script.
|
||||
*/
|
||||
browser.runtime.onConnect.addListener(port => {
|
||||
// Don't pollute history
|
||||
browser.history.deleteUrl({ url: POPUP_URL });
|
||||
|
||||
if (port.name !== "popup") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnect existing port
|
||||
if (this.messagePort) {
|
||||
this.messagePort.disconnect();
|
||||
}
|
||||
|
||||
this.messagePort = port;
|
||||
this.messagePort.onMessage.addListener(this.onPopupMessage);
|
||||
this.messagePort.onDisconnect.addListener(() => {
|
||||
this.messagePortDisconnected = true;
|
||||
});
|
||||
|
||||
this.messagePort.postMessage({
|
||||
subject: "popup:/sendRequestedAppId"
|
||||
, data: { requestedAppId: this.requestedAppId }
|
||||
});
|
||||
|
||||
this.messagePort.postMessage({
|
||||
subject: "popup:/populateReceiverList"
|
||||
, data: {
|
||||
receivers: this.receivers
|
||||
, defaultMediaType: this.defaultMediaType
|
||||
, availableMediaTypes: this.availableMediaTypes
|
||||
}
|
||||
});
|
||||
});
|
||||
messaging.onConnect.addListener(this.onConnect);
|
||||
}
|
||||
|
||||
get isOpen () {
|
||||
@@ -162,6 +132,46 @@ export default class PopupReceiverSelector
|
||||
}
|
||||
}
|
||||
|
||||
private onConnect (port: Port) {
|
||||
browser.history.deleteUrl({ url: POPUP_URL });
|
||||
|
||||
if (port.name !== "popup") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.messagePort) {
|
||||
this.messagePort.disconnect();
|
||||
}
|
||||
|
||||
this.messagePort = port;
|
||||
this.messagePort.onMessage.addListener(this.onPopupMessage);
|
||||
this.messagePort.onDisconnect.addListener(() => {
|
||||
this.messagePortDisconnected = true;
|
||||
});
|
||||
|
||||
if (!this.requestedAppId
|
||||
|| !this.receivers
|
||||
|| !this.defaultMediaType
|
||||
|| !this.availableMediaTypes) {
|
||||
throw logger.error("Popup receiver data not found.");
|
||||
}
|
||||
|
||||
this.messagePort.postMessage({
|
||||
subject: "popup:/sendRequestedAppId"
|
||||
, data: { requestedAppId: this.requestedAppId }
|
||||
});
|
||||
|
||||
this.messagePort.postMessage({
|
||||
subject: "popup:/populateReceiverList"
|
||||
, data: {
|
||||
receivers: this.receivers
|
||||
, defaultMediaType: this.defaultMediaType
|
||||
, availableMediaTypes: this.availableMediaTypes
|
||||
}
|
||||
});
|
||||
|
||||
messaging.onConnect.removeListener(this.onConnect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles popup messages.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
import { TypedEventTarget } from "../../lib/typedEvents";
|
||||
import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { Receiver } from "../../types";
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user