Adjust CastManager naming and add comments

This commit is contained in:
hensm
2022-03-15 20:19:57 +00:00
parent 5120a3a7ab
commit 936a92a5a5
3 changed files with 60 additions and 21 deletions

View File

@@ -25,39 +25,48 @@ export interface CastInstance {
appId?: string; appId?: string;
} }
/**
* Keeps track of cast API instances and provides bridge
* messaging.
*/
export default new (class CastManager { export default new (class CastManager {
private activeInstances = new Set<CastInstance>(); private activeInstances = new Set<CastInstance>();
public async init() { public async init() {
// Wait for "cast" ports // Handle incoming instance connections
messaging.onConnect.addListener(async port => { messaging.onConnect.addListener(async port => {
if (port.name === "cast") { if (port.name === "cast") {
this.createInstance(port); this.createInstance(port);
} }
}); });
// Forward receiver eventes to cast instances
receiverDevices.addEventListener("receiverDeviceUp", ev => { receiverDevices.addEventListener("receiverDeviceUp", ev => {
for (const instance of this.activeInstances) { for (const instance of this.activeInstances) {
instance.contentPort.postMessage({ instance.contentPort.postMessage({
subject: "cast:serviceUp", subject: "cast:receiverDeviceUp",
data: { receiverDevice: ev.detail.deviceInfo } data: { receiverDevice: ev.detail.deviceInfo }
}); });
} }
}); });
receiverDevices.addEventListener("receiverDeviceDown", ev => { receiverDevices.addEventListener("receiverDeviceDown", ev => {
for (const instance of this.activeInstances) { for (const instance of this.activeInstances) {
instance.contentPort.postMessage({ instance.contentPort.postMessage({
subject: "cast:serviceDown", subject: "cast:receiverDeviceDown",
data: { receiverDeviceId: ev.detail.deviceId } data: { receiverDeviceId: ev.detail.deviceId }
}); });
} }
}); });
} }
/**
* Finds a cast instance at the given tab (and optionally
* frame) ID.
*/
public getInstance(tabId: number, frameId?: number) { public getInstance(tabId: number, frameId?: number) {
for (const instance of this.activeInstances) { for (const instance of this.activeInstances) {
if (instance.contentTabId === tabId) { if (instance.contentTabId === tabId) {
// If frame ID doesn't match go to next instance
if (frameId && instance.contentFrameId !== frameId) { if (frameId && instance.contentFrameId !== frameId) {
continue; continue;
} }
@@ -67,19 +76,28 @@ export default new (class CastManager {
} }
} }
/**
* Creates a cast instance with a given port and connects
* messaging correctly depending on the type of port.
*/
public async createInstance(port: AnyPort) { public async createInstance(port: AnyPort) {
const instance = await (port instanceof MessagePort const instance = await (port instanceof MessagePort
? this.createInstanceFromBackground(port) ? this.createInstanceFromBackground(port)
: this.createInstanceFromContent(port)); : this.createInstanceFromContent(port));
this.activeInstances.add(instance);
instance.contentPort.postMessage({ instance.contentPort.postMessage({
subject: "cast:initialized", subject: "cast:initialized",
data: await bridge.getInfo() data: await bridge.getInfo()
}); });
this.activeInstances.add(instance); return instance;
} }
/**
* Creates a cast instance with a `MessagePort` content port.
*/
private async createInstanceFromBackground( private async createInstanceFromBackground(
contentPort: MessagePort contentPort: MessagePort
): Promise<CastInstance> { ): Promise<CastInstance> {
@@ -93,10 +111,12 @@ export default new (class CastManager {
this.activeInstances.delete(instance); this.activeInstances.delete(instance);
}); });
// bridge -> content
instance.bridgePort.onMessage.addListener(message => { instance.bridgePort.onMessage.addListener(message => {
contentPort.postMessage(message); contentPort.postMessage(message);
}); });
// content -> (any)
contentPort.addEventListener("message", ev => { contentPort.addEventListener("message", ev => {
this.handleContentMessage(instance, ev.data); this.handleContentMessage(instance, ev.data);
}); });
@@ -104,7 +124,13 @@ export default new (class CastManager {
return instance; return instance;
} }
private async createInstanceFromContent(contentPort: Port): Promise<CastInstance> { /**
* Creates a cast instance with a WebExtension `Port` content
* port.
*/
private async createInstanceFromContent(
contentPort: Port
): Promise<CastInstance> {
if ( if (
contentPort.sender?.tab?.id === undefined || contentPort.sender?.tab?.id === undefined ||
contentPort.sender?.frameId === undefined contentPort.sender?.frameId === undefined
@@ -117,6 +143,8 @@ export default new (class CastManager {
/** /**
* If there's already an active instance for the sender * If there's already an active instance for the sender
* tab/frame ID, disconnect it. * tab/frame ID, disconnect it.
*
* TODO: Fix this behaviour!
*/ */
for (const instance of this.activeInstances) { for (const instance of this.activeInstances) {
if ( if (
@@ -134,10 +162,11 @@ export default new (class CastManager {
contentFrameId: contentPort.sender.frameId contentFrameId: contentPort.sender.frameId
}; };
// content -> (any)
const onContentPortMessage = (message: Message) => { const onContentPortMessage = (message: Message) => {
this.handleContentMessage(instance, message); this.handleContentMessage(instance, message);
}; };
// bridge -> content
const onBridgePortMessage = (message: Message) => { const onBridgePortMessage = (message: Message) => {
contentPort.postMessage(message); contentPort.postMessage(message);
}; };
@@ -161,19 +190,28 @@ export default new (class CastManager {
return instance; return instance;
} }
private async handleContentMessage(instance: CastInstance, message: Message) { /**
* Handle content messages from the cast instance. These will
* either be handled here in the background script or forwarded
* to the bridge associated with the cast instance.
*/
private async handleContentMessage(
instance: CastInstance,
message: Message
) {
const [destination] = message.subject.split(":"); const [destination] = message.subject.split(":");
if (destination === "bridge") { if (destination === "bridge") {
instance.bridgePort.postMessage(message); instance.bridgePort.postMessage(message);
} }
switch (message.subject) { switch (message.subject) {
case "main:castReady": { // Cast API has been initialized
case "main:initializeCast": {
instance.appId = message.data.appId; instance.appId = message.data.appId;
for (const receiverDevice of receiverDevices.getDevices()) { for (const receiverDevice of receiverDevices.getDevices()) {
instance.contentPort.postMessage({ instance.contentPort.postMessage({
subject: "cast:serviceUp", subject: "cast:receiverDeviceUp",
data: { receiverDevice } data: { receiverDevice }
}); });
} }
@@ -181,6 +219,7 @@ export default new (class CastManager {
break; break;
} }
// User has triggered receiver selection via the cast API
case "main:selectReceiver": { case "main:selectReceiver": {
if ( if (
instance.contentTabId === undefined || instance.contentTabId === undefined ||
@@ -262,7 +301,7 @@ export default new (class CastManager {
* TODO: If we're closing a selector, make sure it's the same * TODO: If we're closing a selector, make sure it's the same
* one that caused the session creation. * one that caused the session creation.
*/ */
case "main:sessionCreated": { case "main:closeReceiverSelector": {
const selector = await ReceiverSelectorManager.getSelector(); const selector = await ReceiverSelectorManager.getSelector();
const shouldClose = await options.get( const shouldClose = await options.get(
"receiverSelectorWaitForConnection" "receiverSelectorWaitForConnection"

View File

@@ -133,7 +133,7 @@ export function initialize(
apiConfig = newApiConfig; apiConfig = newApiConfig;
sendMessageResponse({ sendMessageResponse({
subject: "main:castReady", subject: "main:initializeCast",
data: { appId: apiConfig.sessionRequest.appId } data: { appId: apiConfig.sessionRequest.appId }
}); });
@@ -255,7 +255,7 @@ onMessage(message => {
case "cast:sessionCreated": { case "cast:sessionCreated": {
// Notify background to close UI // Notify background to close UI
sendMessageResponse({ sendMessageResponse({
subject: "main:sessionCreated" subject: "main:closeReceiverSelector"
}); });
const status = message.data; const status = message.data;
@@ -360,7 +360,7 @@ onMessage(message => {
break; break;
} }
case "cast:serviceUp": { case "cast:receiverDeviceUp": {
const { receiverDevice } = message.data; const { receiverDevice } = message.data;
if (receiverDevices.has(receiverDevice.id)) { if (receiverDevices.has(receiverDevice.id)) {
break; break;
@@ -376,7 +376,7 @@ onMessage(message => {
break; break;
} }
case "cast:serviceDown": { case "cast:receiverDeviceDown": {
const { receiverDeviceId } = message.data; const { receiverDeviceId } = message.data;
receiverDevices.delete(receiverDeviceId); receiverDevices.delete(receiverDeviceId);

View File

@@ -51,18 +51,18 @@ type ExtMessageDefinitions = {
"receiverSelector:selected": ReceiverSelection; "receiverSelector:selected": ReceiverSelection;
"receiverSelector:stop": ReceiverSelection; "receiverSelector:stop": ReceiverSelection;
"main:castReady": { appId: string };
"main:selectReceiver": {}; "main:selectReceiver": {};
"cast:selectReceiver/selected": ReceiverSelectionCast; "cast:selectReceiver/selected": ReceiverSelectionCast;
"cast:selectReceiver/stopped": ReceiverSelectionStop; "cast:selectReceiver/stopped": ReceiverSelectionStop;
"cast:selectReceiver/cancelled": {}; "cast:selectReceiver/cancelled": {};
"main:sessionCreated": {}; "main:closeReceiverSelector": {};
"main:initializeCast": { appId: string };
"cast:initialized": BridgeInfo; "cast:initialized": BridgeInfo;
"cast:serviceUp": { receiverDevice: ReceiverDevice };
"cast:serviceDown": { receiverDeviceId: ReceiverDevice["id"] }; "cast:receiverDeviceUp": { receiverDevice: ReceiverDevice };
"cast:receiverDeviceDown": { receiverDeviceId: ReceiverDevice["id"] };
"cast:launchApp": { receiver: ReceiverDevice }; "cast:launchApp": { receiver: ReceiverDevice };
}; };