mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-11 10:09:59 +00:00
Replace eventMessageChannel with clearer implementation
This commit is contained in:
@@ -1,21 +1,25 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { onMessageResponse, sendMessage } from "./eventMessageChannel";
|
import eventMessaging from "./eventMessaging";
|
||||||
|
|
||||||
import messaging, { Message } from "../messaging";
|
import messaging, { Message } from "../messaging";
|
||||||
|
|
||||||
// Message port to background script
|
// Message port to background script
|
||||||
export const backgroundPort = messaging.connect({ name: "cast" });
|
export const backgroundPort = messaging.connect({ name: "cast" });
|
||||||
|
|
||||||
const forwardToCast = (message: Message) => sendMessage(message);
|
const forwardToPage = (message: Message) => {
|
||||||
const forwardToMain = (message: Message) => backgroundPort.postMessage(message);
|
eventMessaging.extension.sendMessage(message);
|
||||||
|
};
|
||||||
|
const forwardToMain = (message: Message) => {
|
||||||
|
backgroundPort.postMessage(message);
|
||||||
|
};
|
||||||
|
|
||||||
// Add message listeners
|
// Add message listeners
|
||||||
backgroundPort.onMessage.addListener(forwardToCast);
|
backgroundPort.onMessage.addListener(forwardToPage);
|
||||||
const listener = onMessageResponse(forwardToMain);
|
eventMessaging.extension.addListener(forwardToMain);
|
||||||
|
|
||||||
// Remove listeners
|
// Remove listeners
|
||||||
backgroundPort.onDisconnect.addListener(() => {
|
backgroundPort.onDisconnect.addListener(() => {
|
||||||
backgroundPort.onMessage.removeListener(forwardToCast);
|
backgroundPort.onMessage.removeListener(forwardToPage);
|
||||||
listener.disconnect();
|
eventMessaging.extension.addListener(forwardToMain);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
import { Message } from "../messaging";
|
|
||||||
|
|
||||||
type ListenerFunc = (message: Message) => void;
|
|
||||||
|
|
||||||
export interface ListenerObject {
|
|
||||||
disconnect(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function onMessage(listener: ListenerFunc): ListenerObject {
|
|
||||||
function on__castMessage(ev: CustomEvent) {
|
|
||||||
listener(JSON.parse(ev.detail));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO:
|
|
||||||
* Figure out a way to handle and stop propagation of this
|
|
||||||
* event to hide it from page scripts.
|
|
||||||
* Currently the event handler is set after the page loads the
|
|
||||||
* cast API, allowing pages set handlers before this script,
|
|
||||||
* intercept the event, and cancel it.
|
|
||||||
*/
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
document.addEventListener("__castMessage", on__castMessage, true);
|
|
||||||
|
|
||||||
return {
|
|
||||||
disconnect() {
|
|
||||||
// @ts-ignore
|
|
||||||
document.removeEventListener(
|
|
||||||
"__castMessage",
|
|
||||||
on__castMessage,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendMessageResponse(message: Message) {
|
|
||||||
const event = new CustomEvent("__castMessageResponse", {
|
|
||||||
detail: JSON.stringify(message)
|
|
||||||
});
|
|
||||||
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function onMessageResponse(listener: ListenerFunc): ListenerObject {
|
|
||||||
function on__castMessageResponse(ev: CustomEvent) {
|
|
||||||
listener(JSON.parse(ev.detail));
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
document.addEventListener(
|
|
||||||
"__castMessageResponse",
|
|
||||||
on__castMessageResponse,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
disconnect() {
|
|
||||||
// @ts-ignore
|
|
||||||
document.removeEventListener(
|
|
||||||
"__castMessageResponse",
|
|
||||||
on__castMessageResponse,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendMessage(message: Message) {
|
|
||||||
const event = new CustomEvent("__castMessage", {
|
|
||||||
detail: JSON.stringify(message)
|
|
||||||
});
|
|
||||||
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
93
ext/src/cast/eventMessaging.ts
Normal file
93
ext/src/cast/eventMessaging.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import logger from "../lib/logger";
|
||||||
|
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||||
|
import { Message } from "../messaging";
|
||||||
|
|
||||||
|
type EventMessengerListener = (message: Message) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messenger class for cross-context messages via CustomEvent.
|
||||||
|
*
|
||||||
|
* Supplied with an incoming and outgoing event name, it provides a
|
||||||
|
* message channel from content scripts to page scripts provided that
|
||||||
|
* the opposite event names are used with instances on either side.
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* Extending EventTarget seems to cause issues with dispatching custom
|
||||||
|
* events in WebExtension content scripts (sandbox issue?), so custom
|
||||||
|
* addListener/removeListener methods are used instead.
|
||||||
|
*/
|
||||||
|
abstract class EventMessenger {
|
||||||
|
private listeners = new Set<EventMessengerListener>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private incomingMessageEventName: string,
|
||||||
|
private outgoingMessageEventName: string
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
document.addEventListener(
|
||||||
|
this.incomingMessageEventName,
|
||||||
|
(ev: CustomEvent<string>) => {
|
||||||
|
for (const listener of this.listeners) {
|
||||||
|
listener(JSON.parse(ev.detail));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addListener(listener: EventMessengerListener) {
|
||||||
|
this.listeners.add(listener);
|
||||||
|
}
|
||||||
|
removeListener(listener: EventMessengerListener) {
|
||||||
|
this.listeners.delete(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(message: Message) {
|
||||||
|
document.dispatchEvent(
|
||||||
|
new CustomEvent<string>(this.outgoingMessageEventName, {
|
||||||
|
detail: JSON.stringify(message)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EV_TO_PAGE = "__castMessage";
|
||||||
|
const EV_FROM_PAGE = "__castMessageResponse";
|
||||||
|
|
||||||
|
export class PageEventMessenger extends EventMessenger {
|
||||||
|
constructor() {
|
||||||
|
super(EV_TO_PAGE, EV_FROM_PAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class ExtensionEventMessenger extends EventMessenger {
|
||||||
|
constructor() {
|
||||||
|
super(EV_FROM_PAGE, EV_TO_PAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure only one instance of the type initially created is used
|
||||||
|
let messenger: EventMessenger;
|
||||||
|
function getMessenger(messengerType: { new (): EventMessenger }) {
|
||||||
|
if (!messenger) {
|
||||||
|
messenger = new messengerType();
|
||||||
|
} else if (!(messenger instanceof messengerType)) {
|
||||||
|
throw logger.error(
|
||||||
|
"Requested messenger does not match type of instantiated messenger!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messenger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/** Event messenger for page scripts. */
|
||||||
|
get page() {
|
||||||
|
return getMessenger(PageEventMessenger);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Event messenger for extension content scripts. */
|
||||||
|
get extension() {
|
||||||
|
return getMessenger(ExtensionEventMessenger);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { Message } from "../messaging";
|
import messaging, { Message } from "../messaging";
|
||||||
|
|
||||||
import { BridgeInfo } from "../lib/bridge";
|
import { BridgeInfo } from "../lib/bridge";
|
||||||
import { TypedMessagePort } from "../lib/TypedMessagePort";
|
import { TypedMessagePort } from "../lib/TypedMessagePort";
|
||||||
|
|
||||||
import {
|
|
||||||
onMessage,
|
|
||||||
onMessageResponse,
|
|
||||||
sendMessage
|
|
||||||
} from "./eventMessageChannel";
|
|
||||||
|
|
||||||
import CastSDK from "./sdk";
|
import CastSDK from "./sdk";
|
||||||
|
|
||||||
|
import { PageEventMessenger, ExtensionEventMessenger } from "./eventMessaging";
|
||||||
|
|
||||||
|
// Create messengers manually instead of relying on getters
|
||||||
|
const eventMessaging = {
|
||||||
|
page: new PageEventMessenger(),
|
||||||
|
extension: new ExtensionEventMessenger()
|
||||||
|
};
|
||||||
|
|
||||||
let initializedBridgeInfo: BridgeInfo;
|
let initializedBridgeInfo: BridgeInfo;
|
||||||
let initializedBackgroundPort: MessagePort;
|
let initializedBackgroundPort: MessagePort;
|
||||||
|
|
||||||
@@ -57,14 +59,14 @@ export function ensureInit(): Promise<TypedMessagePort<Message>> {
|
|||||||
const message = ev.data as Message;
|
const message = ev.data as Message;
|
||||||
|
|
||||||
// Send message to cast instance
|
// Send message to cast instance
|
||||||
sendMessage(message);
|
eventMessaging.extension.sendMessage(message);
|
||||||
handleIncomingMessageToCast(message);
|
handleIncomingMessageToCast(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
// cast instance -> bridge
|
// cast instance -> bridge
|
||||||
onMessageResponse(message => {
|
eventMessaging.extension.addListener(message =>
|
||||||
channel.port1.postMessage(message);
|
channel.port1.postMessage(message)
|
||||||
});
|
);
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* Import reference to message port created by contentBridge.
|
* Import reference to message port created by contentBridge.
|
||||||
@@ -85,7 +87,9 @@ export function ensureInit(): Promise<TypedMessagePort<Message>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle cast messages
|
// Handle cast messages
|
||||||
onMessage(handleIncomingMessageToCast);
|
eventMessaging.page.addListener(message =>
|
||||||
|
handleIncomingMessageToCast(message)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleIncomingMessageToCast(message: Message) {
|
function handleIncomingMessageToCast(message: Message) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import logger from "../lib/logger";
|
|||||||
|
|
||||||
import { loadScript } from "../lib/utils";
|
import { loadScript } from "../lib/utils";
|
||||||
import { CAST_FRAMEWORK_SCRIPT_URL } from "./endpoints";
|
import { CAST_FRAMEWORK_SCRIPT_URL } from "./endpoints";
|
||||||
import { onMessage } from "./eventMessageChannel";
|
import eventMessaging from "./eventMessaging";
|
||||||
|
|
||||||
import CastSDK from "./sdk";
|
import CastSDK from "./sdk";
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ if (document.currentScript) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(async message => {
|
eventMessaging.page.addListener(async message => {
|
||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "cast:initialized": {
|
case "cast:initialized": {
|
||||||
bridgeInfo = message.data;
|
bridgeInfo = message.data;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { v4 as uuid } from "uuid";
|
|||||||
|
|
||||||
import logger from "../../lib/logger";
|
import logger from "../../lib/logger";
|
||||||
|
|
||||||
import { sendMessageResponse } from "../eventMessageChannel";
|
import eventMessaging from "../eventMessaging";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ErrorCallback,
|
ErrorCallback,
|
||||||
@@ -201,7 +201,7 @@ export default class Session {
|
|||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const messageId = uuid();
|
const messageId = uuid();
|
||||||
|
|
||||||
sendMessageResponse({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "bridge:sendCastReceiverMessage",
|
subject: "bridge:sendCastReceiverMessage",
|
||||||
data: {
|
data: {
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -271,7 +271,7 @@ export default class Session {
|
|||||||
) {
|
) {
|
||||||
const messageId = uuid();
|
const messageId = uuid();
|
||||||
|
|
||||||
sendMessageResponse({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "bridge:sendCastSessionMessage",
|
subject: "bridge:sendCastSessionMessage",
|
||||||
data: {
|
data: {
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from "../../types";
|
} from "../../types";
|
||||||
import { ErrorCallback, SuccessCallback } from "../types";
|
import { ErrorCallback, SuccessCallback } from "../types";
|
||||||
|
|
||||||
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
import eventMessaging from "../eventMessaging";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoJoinPolicy,
|
AutoJoinPolicy,
|
||||||
@@ -125,7 +125,7 @@ export default class {
|
|||||||
timeout = new Timeout();
|
timeout = new Timeout();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
onMessage(this.#onMessage.bind(this));
|
eventMessaging.page.addListener(this.#onMessage.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
#sendSessionRequest(
|
#sendSessionRequest(
|
||||||
@@ -136,7 +136,7 @@ export default class {
|
|||||||
listener(createReceiver(receiverDevice), ReceiverAction.CAST);
|
listener(createReceiver(receiverDevice), ReceiverAction.CAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageResponse({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "bridge:createCastSession",
|
subject: "bridge:createCastSession",
|
||||||
data: {
|
data: {
|
||||||
appId: sessionRequest.appId,
|
appId: sessionRequest.appId,
|
||||||
@@ -158,7 +158,7 @@ export default class {
|
|||||||
*/
|
*/
|
||||||
case "cast:sessionCreated": {
|
case "cast:sessionCreated": {
|
||||||
// Notify background to close UI
|
// Notify background to close UI
|
||||||
sendMessageResponse({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "main:closeReceiverSelector"
|
subject: "main:closeReceiverSelector"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -397,7 +397,7 @@ export default class {
|
|||||||
|
|
||||||
this.#apiConfig = apiConfig;
|
this.#apiConfig = apiConfig;
|
||||||
|
|
||||||
sendMessageResponse({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "main:initializeCast",
|
subject: "main:initializeCast",
|
||||||
data: { appId: this.#apiConfig.sessionRequest.appId }
|
data: { appId: this.#apiConfig.sessionRequest.appId }
|
||||||
});
|
});
|
||||||
@@ -465,7 +465,7 @@ export default class {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Open receiver selector UI
|
// Open receiver selector UI
|
||||||
sendMessageResponse({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "main:selectReceiver",
|
subject: "main:selectReceiver",
|
||||||
data: { sessionRequest: this.#sessionRequest }
|
data: { sessionRequest: this.#sessionRequest }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,5 +5,5 @@
|
|||||||
*/
|
*/
|
||||||
export interface TypedMessagePort<T> extends MessagePort {
|
export interface TypedMessagePort<T> extends MessagePort {
|
||||||
postMessage(message: T, transfer: Transferable[]): void;
|
postMessage(message: T, transfer: Transferable[]): void;
|
||||||
postMessage(message: T, options?: PostMessageOptions): void;
|
postMessage(message: T, options?: StructuredSerializeOptions): void;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user