diff --git a/ext/src/background/ShimManager.ts b/ext/src/background/ShimManager.ts index b6f6743..597e1fa 100644 --- a/ext/src/background/ShimManager.ts +++ b/ext/src/background/ShimManager.ts @@ -8,7 +8,8 @@ import options from "../lib/options"; import { Message } from "../types"; import { getMediaTypesForPageUrl } from "../lib/utils"; -import { ReceiverSelectorMediaType } from "./receiverSelector"; +import { ReceiverSelectionActionType + , ReceiverSelectorMediaType } from "./receiverSelector"; import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager"; @@ -182,31 +183,46 @@ export default new class ShimManager { break; } - /** - * If the media type returned from the selector has been - * changed, we need to cancel the current sender and switch - * it out for the right one. - */ - if (selection.mediaType !== ReceiverSelectorMediaType.App) { - shim.contentPort.postMessage({ - subject: "shim:/selectReceiverCancelled" - }); + switch (selection.actionType) { + case ReceiverSelectionActionType.Cast: { + /** + * If the media type returned from the selector has + * been changed, we need to cancel the current + * sender and switch it out for the right one. + */ + if (selection.mediaType !== + ReceiverSelectorMediaType.App) { - loadSender({ - tabId: shim.contentTabId - , frameId: shim.contentFrameId - , selection - }); + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverCancelled" + }); - break; + loadSender({ + tabId: shim.contentTabId + , frameId: shim.contentFrameId + , selection + }); + + break; + } + + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverEnd" + , data: selection + }); + + break; + } + + case ReceiverSelectionActionType.Stop: { + shim.contentPort.postMessage({ + subject: "shim:/selectReceiverStop" + , data: selection + }); + + break; + } } - - // Pass selection back to shim - shim.contentPort.postMessage({ - subject: "shim:/selectReceiverEnd" - , data: selection - }); - } catch (err) { // TODO: Report errors properly shim.contentPort.postMessage({ diff --git a/ext/src/background/background.ts b/ext/src/background/background.ts index 1e7a60a..8bd6387 100755 --- a/ext/src/background/background.ts +++ b/ext/src/background/background.ts @@ -11,7 +11,8 @@ import { getMediaTypesForPageUrl, stringify } from "../lib/utils"; import { CAST_FRAMEWORK_LOADER_SCRIPT_URL , CAST_LOADER_SCRIPT_URL } from "../lib/endpoints"; -import { ReceiverSelectorMediaType } from "./receiverSelector"; +import { ReceiverSelectionActionType + , ReceiverSelectorMediaType } from "./receiverSelector"; import ReceiverSelectorManager from "./receiverSelector/ReceiverSelectorManager"; @@ -200,32 +201,41 @@ async function initMenus () { break; } - /** - * If the selected media type is App, that refers to the - * media sender in this context, so load media sender. - */ - if (selection.mediaType === ReceiverSelectorMediaType.App) { - await browser.tabs.executeScript(tab.id, { - code: stringify` - window.receiver = ${selection.receiver}; - window.mediaUrl = ${info.srcUrl}; - window.targetElementId = ${info.targetElementId}; - ` - , frameId: info.frameId - }); + switch (selection.actionType) { + case ReceiverSelectionActionType.Cast: { + /** + * If the selected media type is App, that refers to the + * media sender in this context, so load media sender. + */ + if (selection.mediaType === + ReceiverSelectorMediaType.App) { - await browser.tabs.executeScript(tab.id, { - file: "senders/media/bundle.js" - , frameId: info.frameId - }); - } else { + await browser.tabs.executeScript(tab.id, { + code: stringify` + window.receiver = ${selection.receiver}; + window.mediaUrl = ${info.srcUrl}; + window.targetElementId = ${ + info.targetElementId}; + ` + , frameId: info.frameId + }); - // Handle other responses - loadSender({ - tabId: tab.id - , frameId: info.frameId - , selection - }); + await browser.tabs.executeScript(tab.id, { + file: "senders/media/bundle.js" + , frameId: info.frameId + }); + } else { + + // Handle other responses + loadSender({ + tabId: tab.id + , frameId: info.frameId + , selection + }); + } + + break; + } } break; diff --git a/ext/src/background/receiverSelector/ReceiverSelector.ts b/ext/src/background/receiverSelector/ReceiverSelector.ts index 464a5e7..8f38839 100644 --- a/ext/src/background/receiverSelector/ReceiverSelector.ts +++ b/ext/src/background/receiverSelector/ReceiverSelector.ts @@ -11,18 +11,30 @@ export enum ReceiverSelectorMediaType { , File = 8 } -export interface ReceiverSelection { +export enum ReceiverSelectionActionType { + Cast = 1 + , Stop = 2 +} + +export interface ReceiverSelectionCast { + actionType: ReceiverSelectionActionType.Cast; receiver: Receiver; mediaType: ReceiverSelectorMediaType; filePath?: string; } +export interface ReceiverSelectionStop { + actionType: ReceiverSelectionActionType.Stop; + receiver: Receiver; +} + +export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop; export interface ReceiverSelectorEvents { - "selected": ReceiverSelection; + "selected": ReceiverSelectionCast; "error": string; "cancelled": void; - "stop": { receiver: Receiver }; + "stop": ReceiverSelectionStop; } export default interface ReceiverSelector diff --git a/ext/src/background/receiverSelector/ReceiverSelectorManager.ts b/ext/src/background/receiverSelector/ReceiverSelectorManager.ts index da7008c..e10cd20 100644 --- a/ext/src/background/receiverSelector/ReceiverSelectorManager.ts +++ b/ext/src/background/receiverSelector/ReceiverSelectorManager.ts @@ -13,6 +13,7 @@ import { DEFAULT_MEDIA_RECEIVER_APP_ID } from "../../shim/cast/media/"; import { ReceiverSelector , ReceiverSelectorType } from "./"; import { ReceiverSelection + , ReceiverSelectionActionType , ReceiverSelectorMediaType } from "./ReceiverSelector"; import NativeReceiverSelector from "./NativeReceiverSelector"; @@ -122,10 +123,28 @@ async function getSelection ( // Get a new selector for each selection sharedSelector = await createSelector(); - sharedSelector.addEventListener("selected", onSelected); - sharedSelector.addEventListener("cancelled", onCancelled); - sharedSelector.addEventListener("error", onError); - sharedSelector.addEventListener("stop", onStop); + + let onSelected: any; + let onCancelled: any; + let onError: any; + let onStop: any; + + type EvParamsType = + Parameters[0]; + + function storeListener (type: EvParamsType, fn: T) { + if (type === "selected") { + onSelected = fn; + } else if (type === "cancelled") { + onCancelled = fn; + } else if (type === "error") { + onError = fn; + } else if (type === "stop") { + onStop = fn; + } + + return fn; + } function removeListeners () { sharedSelector.removeEventListener("selected", onSelected); @@ -134,31 +153,48 @@ async function getSelection ( sharedSelector.removeEventListener("stop", onStop); } - function onSelected (ev: any) { - logger.info("Selected receiver", ev.detail); - resolve(ev.detail); - removeListeners(); - } + sharedSelector.addEventListener("selected" + , storeListener("selected", ev => { + + logger.info("Selected receiver", ev.detail); + resolve({ + actionType: ReceiverSelectionActionType.Cast + , receiver: ev.detail.receiver + , mediaType: ev.detail.mediaType + , filePath: ev.detail.filePath + }); + removeListeners(); + })); + + sharedSelector.addEventListener("cancelled" + , storeListener("cancelled", () => { - function onCancelled () { logger.info("Cancelled receiver selection"); resolve(null); removeListeners(); - } + })); - function onError () { - logger.error("Failed to select receiver"); + sharedSelector.addEventListener("error" + , storeListener("error", () => { reject(); removeListeners(); - } + })); + + sharedSelector.addEventListener("stop" + , storeListener("stop", ev => { - function onStop (ev: any) { logger.info("Stopped receiver app", ev.detail); - StatusManager.init().then(() => { - StatusManager.stopReceiverApp(ev.detail.receiver); - }); - } + StatusManager.init() + .then(() => StatusManager.stopReceiverApp(ev.detail.receiver)) + .then(() => { + resolve({ + actionType: ReceiverSelectionActionType.Stop + , receiver: ev.detail.receiver + }); + removeListeners(); + }); + })); // Ensure status manager is initialized diff --git a/ext/src/background/receiverSelector/index.ts b/ext/src/background/receiverSelector/index.ts index 1789fc5..4d6f912 100644 --- a/ext/src/background/receiverSelector/index.ts +++ b/ext/src/background/receiverSelector/index.ts @@ -14,4 +14,5 @@ export enum ReceiverSelectorType { } export { ReceiverSelection + , ReceiverSelectionActionType , ReceiverSelectorMediaType } from "./ReceiverSelector"; diff --git a/ext/src/lib/loadSender.ts b/ext/src/lib/loadSender.ts index fa15d9c..033672b 100644 --- a/ext/src/lib/loadSender.ts +++ b/ext/src/lib/loadSender.ts @@ -4,6 +4,7 @@ import logger from "./logger"; import { stringify } from "./utils"; import { ReceiverSelection + , ReceiverSelectionActionType , ReceiverSelectorMediaType } from "../background/receiverSelector"; import ShimManager from "../background/ShimManager"; @@ -25,6 +26,10 @@ export default async function loadSender (opts: LoadSenderOptions) { return; } + if (opts.selection.actionType !== ReceiverSelectionActionType.Cast) { + return; + } + switch (opts.selection.mediaType) { case ReceiverSelectorMediaType.App: { const shim = ShimManager.getShim(opts.tabId, opts.frameId); diff --git a/ext/src/shim/cast/index.ts b/ext/src/shim/cast/index.ts index 82862d5..0a99776 100755 --- a/ext/src/shim/cast/index.ts +++ b/ext/src/shim/cast/index.ts @@ -41,12 +41,13 @@ type ErrorCallback = (err: Error_) => void; let apiConfig: ApiConfig; -let receiverList: any[] = []; + +const receiverList: Receiver[] = []; const sessionList: Session[] = []; + +const receiverActionListeners = new Set(); + let sessionRequestInProgress = false; - -const receiverListeners = new Set(); - let sessionSuccessCallback: RequestSessionSuccessCallback; let sessionErrorCallback: ErrorCallback; @@ -76,8 +77,7 @@ export const VERSION = [1, 2]; export function addReceiverActionListener ( listener: ReceiverActionListener): void { - console.info("fx_cast (Debug): cast.addReceiverActionListener"); - receiverListeners.add(listener); + receiverActionListeners.add(listener); } export function initialize ( @@ -125,7 +125,7 @@ export function precache (_data: string): void { export function removeReceiverActionListener ( listener: ReceiverActionListener): void { - receiverListeners.delete(listener); + receiverActionListeners.delete(listener); } export function requestSession ( @@ -306,8 +306,10 @@ onMessage(async message => { * and update availability state. */ case "shim:/serviceDown": { - receiverList = receiverList.filter( - receiver => receiver.id !== message.data.id); + const receiverIndex = receiverList.findIndex( + receiver => receiver.id === message.data.id); + + receiverList.splice(receiverIndex, 1); if (receiverList.length === 0) { if (apiConfig) { @@ -322,10 +324,19 @@ onMessage(async message => { case "shim:/selectReceiverEnd": { console.info("fx_cast (Debug): Selected receiver"); + if (!sessionRequestInProgress) { + break; + } + const selectedReceiver = new Receiver_( message.data.receiver.id , message.data.receiver.friendlyName); + for (const listener of receiverActionListeners) { + console.info("fx_cast (Debug): Calling receiver action listener (CAST)", message.data.receiver); + listener(selectedReceiver, ReceiverAction.CAST); + } + (selectedReceiver as any)._address = message.data.receiver.host; (selectedReceiver as any)._port = message.data.receiver.port; @@ -363,6 +374,23 @@ onMessage(async message => { break; } + case "shim:/selectReceiverStop": { + console.info("fx_cast (Debug): Stopped receiver"); + + if (sessionRequestInProgress) { + for (const listener of receiverActionListeners) { + const castReceiver = new Receiver_( + message.data.receiver.id + , message.data.receiver.friendlyName); + + console.info("fx_cast (Debug): Calling receiver action listener (STOP)", message.data.receiver); + listener(castReceiver, ReceiverAction.STOP); + } + } + + break; + } + /** * Popup closed before session established. */