From 0505b0d0b51e26b6d646dfdeadd17c2cac9ebc84 Mon Sep 17 00:00:00 2001 From: hensm Date: Sat, 10 Sep 2022 21:34:42 +0100 Subject: [PATCH] Implement chrome.cast.requestSessionById --- ext/src/background/castManager.ts | 164 ++++++++++++++++++------------ ext/src/cast/sdk/index.ts | 7 +- ext/src/messaging.ts | 2 + 3 files changed, 104 insertions(+), 69 deletions(-) diff --git a/ext/src/background/castManager.ts b/ext/src/background/castManager.ts index c5f79f6..67f7225 100644 --- a/ext/src/background/castManager.ts +++ b/ext/src/background/castManager.ts @@ -48,7 +48,7 @@ interface CastSession { deviceId: string; appId: string; sessionId?: string; - initialContentContext?: ContentContext; + autoJoinContexts: ContentContext[]; } /** Creates a cast session object and sets up messaging. */ @@ -71,9 +71,13 @@ async function createCastSession(opts: { bridgePort: await bridge.connect(), deviceId: opts.deviceId, appId: opts.appId, - initialContentContext: opts.instance.contentContext + autoJoinContexts: [] }; + if (opts.instance.contentContext) { + session.autoJoinContexts.push(opts.instance.contentContext); + } + opts.instance.session = session; opts.instance.bridgeMessageListener = message => { handleBridgeMessage(opts.instance, message); @@ -96,6 +100,56 @@ async function createCastSession(opts: { return session; } +function joinSession(instance: CastInstance, session: CastSession) { + if (!session.sessionId) return; + + instance.session = session; + instance.bridgeMessageListener = message => + handleBridgeMessage(instance, message); + + session.bridgePort.onMessage.addListener(instance.bridgeMessageListener); + session.bridgePort.onDisconnect.addListener(() => + destroyCastInstance(instance) + ); + + const device = deviceManager.getDeviceById(session.deviceId); + if (!device?.status?.applications?.length) { + throw logger.error("Invalid device state!"); + } + + /** + * Re-create sessionCreated message. Since the + * sender app hasn't requested a session, this + * will be handled by calling the session + * listener. + */ + const application = device?.status?.applications[0]; + instance.contentPort.postMessage({ + subject: "cast:sessionCreated", + data: { + appId: application.appId, + appImages: [], + displayName: application.displayName, + namespaces: application.namespaces, + receiver: createReceiver(device), + receiverFriendlyName: device.friendlyName, + receiverId: device.id, + senderApps: [], + sessionId: session.sessionId, + statusText: application.statusText, + transportId: session.sessionId, + volume: device.status.volume + } + }); + + if (instance.contentContext?.tabId) { + updateActionState( + ActionState.Connected, + instance.contentContext?.tabId + ); + } +} + export interface CastInstance { contentPort: AnyPort; contentContext?: ContentContext; @@ -180,6 +234,7 @@ function destroyCastInstance(instance: CastInstance) { const allowedContentMessages: Array = [ "main:initializeCastSdk", "main:requestSession", + "main:requestSessionById", "bridge:sendCastReceiverMessage", "bridge:sendCastSessionMessage" ]; @@ -493,75 +548,29 @@ async function handleContentMessage(instance: CastInstance, message: Message) { continue; } - switch (instance.apiConfig.autoJoinPolicy) { - case AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED: - // Ensure matching content tontext + // Check for valid auto join sessions + const { autoJoinPolicy } = instance.apiConfig; + if ( + autoJoinPolicy === AutoJoinPolicy.ORIGIN_SCOPED || + autoJoinPolicy === AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED + ) { + for (const context of session.autoJoinContexts) { + // Check same origin if ( - !isSameContext( - instance.contentContext, - session.initialContentContext - ) - ) - break; - // eslint-disable-next-line no-fallthrough - case AutoJoinPolicy.ORIGIN_SCOPED: { - // Ensure matching origin + context.origin !== instance.contentContext?.origin + ) { + continue; + } + // Check same context for tab scoped if ( - instance.contentContext?.origin !== - session.initialContentContext?.origin - ) - break; - - instance.session = session; - instance.bridgeMessageListener = message => - handleBridgeMessage(instance, message); - - session.bridgePort.onMessage.addListener( - instance.bridgeMessageListener - ); - session.bridgePort.onDisconnect.addListener(() => - destroyCastInstance(instance) - ); - - const device = deviceManager.getDeviceById( - session.deviceId - ); - if (!device?.status?.applications?.length) { - throw logger.error("Invalid device state"); - } - - /** - * Re-create sessionCreated message. Since the - * sender app hasn't requested a session, this - * will be handled by calling the session - * listener. - */ - const application = device?.status?.applications[0]; - instance.contentPort.postMessage({ - subject: "cast:sessionCreated", - data: { - appId: application.appId, - appImages: [], - displayName: application.displayName, - namespaces: application.namespaces, - receiver: createReceiver(device), - receiverFriendlyName: device.friendlyName, - receiverId: device.id, - senderApps: [], - sessionId: session.sessionId, - statusText: application.statusText, - transportId: session.sessionId, - volume: device.status.volume - } - }); - - if (instance.contentContext?.tabId) { - updateActionState( - ActionState.Connected, - instance.contentContext?.tabId - ); + autoJoinPolicy === + AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED && + !isSameContext(context, instance.contentContext) + ) { + continue; } + joinSession(instance, session); break sessionLoop; } } @@ -666,6 +675,27 @@ async function handleContentMessage(instance: CastInstance, message: Message) { break; } + + case "main:requestSessionById": { + const session = activeSessions.get(message.data.sessionId); + if (!session) { + logger.log( + `Session not found! (id: ${message.data.sessionId})` + ); + break; + } + + if (instance.apiConfig?.sessionRequest.appId === session.appId) { + joinSession(instance, session); + + // If requesting by ID, add to the list of auto join contexts + if (instance.contentContext) { + session.autoJoinContexts.push(instance.contentContext); + } + } + + break; + } } } diff --git a/ext/src/cast/sdk/index.ts b/ext/src/cast/sdk/index.ts index 89fb637..eca4db2 100644 --- a/ext/src/cast/sdk/index.ts +++ b/ext/src/cast/sdk/index.ts @@ -349,8 +349,11 @@ export default class { }); } - requestSessionById(_sessionId: string) { - logger.info("STUB :: cast.requestSessionById"); + requestSessionById(sessionId: string) { + pageMessaging.page.sendMessage({ + subject: "main:requestSessionById", + data: { sessionId } + }); } setCustomReceivers( diff --git a/ext/src/messaging.ts b/ext/src/messaging.ts index f357aa4..eb71d80 100644 --- a/ext/src/messaging.ts +++ b/ext/src/messaging.ts @@ -92,6 +92,8 @@ type ExtMessageDefinitions = { /** Return message to the cast API when a selection is cancelled. */ "cast:sessionRequestCancelled": undefined; + "main:requestSessionById": { sessionId: string }; + "cast:instanceCreated": { isAvailable: boolean }; "cast:receiverAvailabilityUpdated": { isAvailable: boolean };