diff --git a/app/src/main.js b/app/src/main.js index 0664af4..5bef0f8 100755 --- a/app/src/main.js +++ b/app/src/main.js @@ -168,7 +168,7 @@ async function handleMessage (message) { browser.on("serviceUp", service => { transforms.encode.write({ - subject: "shim:serviceUp" + subject: "serviceUp" , data: { address: service.addresses[0] , port: service.port @@ -181,7 +181,7 @@ browser.on("serviceUp", service => { browser.on("serviceDown", service => { transforms.encode.write({ - subject:"shim:serviceDown" + subject:"serviceDown" , data: { id: service.txt.id } diff --git a/ext/src/content.js b/ext/src/content.js index 3a35c94..2b15233 100644 --- a/ext/src/content.js +++ b/ext/src/content.js @@ -1,12 +1,37 @@ "use strict"; -document.addEventListener("__castMessageResponse", ev => { - browser.runtime.sendMessage(ev.detail); -}) - -browser.runtime.onMessage.addListener(message => { - const event = new CustomEvent("__castMessage", { - detail: JSON.stringify(message) - }); - document.dispatchEvent(event); +const backgroundPort = browser.runtime.connect({ + name: "shim" +}); + +backgroundPort.onMessage.addListener(message => { + const event = new CustomEvent("__castMessage", { + detail: JSON.stringify(message) + }); + document.dispatchEvent(event); +}); + +let popupPort; +browser.runtime.onConnect.addListener(port => { + if (port.name === "popup") { + popupPort = port; + } + + port.onMessage.addListener(message => { + const event = new CustomEvent("__castMessage", { + detail: JSON.stringify(message) + }); + document.dispatchEvent(event); + }) +}); + +document.addEventListener("__castMessageResponse", ev => { + if (ev.detail.destination === "popup") { + if (popupPort) { + popupPort.postMessage(ev.detail); + } + return; + } + + backgroundPort.postMessage(ev.detail); }); diff --git a/ext/src/main.js b/ext/src/main.js index 0c418fa..c0293bd 100755 --- a/ext/src/main.js +++ b/ext/src/main.js @@ -319,60 +319,15 @@ browser.menus.onClicked.addListener(async (info, tab) => { }); -const bridgeMap = new Map(); - -/** - * Initializes native application and handles message - * forwarding. - */ -function initBridge (tabId, frameId) { - const existingPort = bridgeMap.get(tabId); - - if (existingPort) { - existingPort.disconnect(); - bridgeMap.delete(tabId); - } - - const port = browser.runtime.connectNative(APPLICATION_NAME); - - if (port.error) { - console.error(`Failed connect to ${APPLICATION_NAME}:`, port.error.message); - } else { - bridgeMap.set(tabId, port); - } - - port.onDisconnect.addListener(p => { - if (p.error) { - console.error(`${APPLICATION_NAME} disconnected:`, p.error.message); - } else { - console.log(`${APPLICATION_NAME} disconnected`); - } - - bridgeMap.delete(tabId); - }); - - port.onMessage.addListener(message => { - // Forward shim: messages - // TODO: Integrate into messageRouter - if (message.subject.startsWith("shim:")) { - browser.tabs.sendMessage(tabId, message, { frameId }); - } else { - messageRouter.handleMessage(message); - } - }); -} - - let popupWinId; -let popupOpenerTabId; -let popupOpenerFrameId; +let popupShimId; /** * Creates popup window for cast destination selection. * Refocusing other browser windows causes the popup window * to close and returns an API error (TODO). */ -async function openPopup (tabId, frameId) { +async function openPopup (shimId) { const width = 350; const height = 200; @@ -397,8 +352,7 @@ async function openPopup (tabId, frameId) { // Store popup details for message forwarding popupWinId = popup.id; - popupOpenerTabId = tabId; - popupOpenerFrameId = frameId; + popupShimId = shimId; // Size/position not set correctly on creation (bug?) await browser.windows.update(popup.id, { @@ -422,80 +376,138 @@ async function openPopup (tabId, frameId) { browser.windows.onRemoved.addListener(id => { if (id === popupWinId) { messageRouter.handleMessage({ - subject: "shim:popupClosed" + subject: "popupClosed" }); popupWinId = null; - popupOpenerTabId = null; - + popupShimId = null; } }); -/** - * Extension scripts make a connection to the background script - * with a destination name to be registered as message route. - */ -browser.runtime.onConnect.addListener(port => { - messageRouter.register(port.name, message => { +const shimMap = new Map(); + +browser.runtime.onMessage.addListener(message => { + if (message.subject === "getPopupShimInfo") { + return new Promise(resolve => { + const { tabId, frameId } = shimMap.get(popupShimId); + + resolve({ + tabId + , frameId + }); + }); + } +}); + +async function onConnectShim (port) { + const bridgeInfo = await getBridgeInfo(); + if (bridgeInfo && !bridgeInfo.isVersionCompatible) { + return; + } + + + const tabId = port.sender.tab.id; + const frameId = port.sender.frameId; + const shimId = `${tabId}:${frameId}`; + + // Disconnect existing shim + if (shimMap.has(shimId)) { + shimMap.get(shimId).port.disconnect(); + shimMap.delete(shimId); + } + + // Spawn bridge app instance + const bridgePort = browser.runtime.connectNative(APPLICATION_NAME); + + if (bridgePort.error) { + console.error(`Failed connect to ${APPLICATION_NAME}:` + , bridgePort.error.message); + } + + shimMap.set(shimId, { + port + , bridgePort + , tabId + , frameId + }); + + bridgePort.onDisconnect.addListener(() => { + if (bridgePort.error) { + console.error(`${APPLICATION_NAME} disconnected:` + , bridgePort.error.message); + } else { + console.log(`${APPLICATION_NAME} disconnected`); + } + }); + + // Handle disconnect + port.onDisconnect.addListener(() => { + bridgePort.disconnect(); + shimMap.delete(shimId); + }); + + + bridgePort.onMessage.addListener(message => { port.postMessage(message); }); - port.onMessage.addListener(message => { - messageRouter.handleMessage(message); - }) -}); -messageRouter.register("main", async (message, sender) => { - const tabId = sender && sender.tab.id; + port.onMessage.addListener(async message => { + if (message.subject.startsWith("bridge")) { + bridgePort.postMessage(message); + } - switch (message.subject) { - case "main:initialize": { - const bridgeInfo = await getBridgeInfo(); - if (bridgeInfo && bridgeInfo.isVersionCompatible) { - initBridge(tabId, sender.frameId); - } + switch (message.subject) { + case "openPopup": { + /** + * If popup already open, reassign to new shim, + * otherwise create a new popup. + */ + if (popupWinId) { + /** + * Notify shim that existing popup has closed and + * to re-populate receiver list for new popup. + */ + port.postMessage({ subject: "popupClosed" }); + port.postMessage({ subject: "popupReady" }); + } else { + await openPopup(shimId); + } - browser.tabs.sendMessage(sender.tab.id, { - subject: "shim:initialized" - , data: bridgeInfo - }, { frameId: sender.frameId }); + break; + }; - break; - }; - - case "main:openPopup": { - // If popup already open, reassign opener tab to new shim - if (popupWinId) { - - // Notify shim that existing popup is gone - messageRouter.handleMessage({ - subject: "shim:popupClosed" + case "discover": { + bridgePort.postMessage({ + subject: "bridge:discover" }); - popupOpenerTabId = tabId; - popupOpenerFrameId = sender.frameId; + break; + }; - // Notify shim to re-populate receiver list - messageRouter.handleMessage({ - subject: "shim:popupReady" - }); - } else { - await openPopup(tabId, sender.frameId); + default: { + // TODO: Remove need for this + messageRouter.handleMessage(message); + break; } + } + }); + + port.postMessage({ + subject: "shimInitialized" + , data: bridgeInfo + }); +} + +browser.runtime.onConnect.addListener(port => { + switch (port.name) { + case "shim": { + onConnectShim(port); break; }; } }); -messageRouter.register("bridge", (message, sender) => { - console.log(message); - bridgeMap.get(sender.tab.id).postMessage(message); -}); - -messageRouter.register("shim", (message, sender) => { - browser.tabs.sendMessage(popupOpenerTabId, message - , { frameId: popupOpenerFrameId }) -}); messageRouter.register("mirrorCast", message => { browser.tabs.sendMessage(mirrorCastTabId, message @@ -507,8 +519,12 @@ messageRouter.register("mediaCast", message => { }); +// Forward messages into messageRouter browser.runtime.onMessage.addListener((message, sender) => { - messageRouter.handleMessage(message, sender); + messageRouter.handleMessage(message, { + tabId: sender.tab.id + , frameId: sender.frameId + }); }); diff --git a/ext/src/messageRouter.js b/ext/src/messageRouter.js index e745f15..69de90d 100644 --- a/ext/src/messageRouter.js +++ b/ext/src/messageRouter.js @@ -11,10 +11,10 @@ function deregister (routeName) { routeMap.delete(routeName); } -function handleMessage (message, sender) { +function handleMessage (message, details) { const destination = message.subject.split(":")[0]; if (routeMap.has(destination)) { - routeMap.get(destination)(message, sender); + routeMap.get(destination)(message, details); } } diff --git a/ext/src/popup/index.jsx b/ext/src/popup/index.jsx index efc0cf3..671a53e 100755 --- a/ext/src/popup/index.jsx +++ b/ext/src/popup/index.jsx @@ -42,18 +42,23 @@ class App extends Component { }); } - componentDidMount () { - this.port = browser.runtime.connect({ + async componentDidMount () { + const { tabId, frameId } = await browser.runtime.sendMessage({ + subject: "getPopupShimInfo" + }); + + this.port = browser.tabs.connect(tabId, { name: "popup" + , frameId }); this.port.postMessage({ - subject: "shim:popupReady" + subject: "popupReady" }); this.port.onMessage.addListener(message => { switch (message.subject) { - case "popup:populate": { + case "populateReceiverList": { this.setState({ receivers: message.data.receivers , selectedMedia: message.data.selectedMedia @@ -70,8 +75,9 @@ class App extends Component { break; } - case "popup:close": { + case "close": { window.close(); + break; } } @@ -84,7 +90,7 @@ class App extends Component { }); this.port.postMessage({ - subject: "shim:selectReceiver" + subject: "selectReceiver" , data: { receiver , selectedMedia: this.state.selectedMedia diff --git a/ext/src/shim/cast/classes/Session.js b/ext/src/shim/cast/classes/Session.js index f1bbc15..c0a1ea7 100755 --- a/ext/src/shim/cast/classes/Session.js +++ b/ext/src/shim/cast/classes/Session.js @@ -44,7 +44,7 @@ export default class Session { this.status = SessionStatus.CONNECTED; this.statusText = null; - if (receiver) { + if (receiver) { this._sendMessage("bridge:bridgesession/initialize", { address: receiver._address , port: receiver._port diff --git a/ext/src/shim/cast/index.js b/ext/src/shim/cast/index.js index a554477..cb419ef 100755 --- a/ext/src/shim/cast/index.js +++ b/ext/src/shim/cast/index.js @@ -91,7 +91,7 @@ cast.initialize = ( state.apiConfig = apiConfig; sendMessage({ - subject: "bridge:discover" + subject: "discover" }); apiConfig.receiverListener(state.receiverList.length @@ -146,7 +146,7 @@ cast.requestSession = ( // Open destination chooser sendMessage({ - subject: "main:openPopup" + subject: "openPopup" }); }; @@ -175,11 +175,9 @@ onMessage(message => { * Cast destination found (serviceUp). Set the API availability * property and call the page event function (__onGCastApiAvailable). */ - case "shim:serviceUp": { + case "serviceUp": { const receiver = message.data; - console.log(receiver); - if (state.receiverList.find(r => r.id === receiver.id)) { break; } @@ -196,7 +194,7 @@ onMessage(message => { * Cast destination lost (serviceDown). Remove from the receiver list * and update availability state. */ - case "shim:serviceDown": { + case "serviceDown": { state.receiverList = state.receiverList.filter( receiver => receiver.id !== message.data.id); @@ -208,7 +206,7 @@ onMessage(message => { break; }; - case "shim:selectReceiver": { + case "selectReceiver": { console.info("Caster (Debug): Selected receiver"); const selectedReceiver = new Receiver( @@ -226,7 +224,8 @@ onMessage(message => { , selectedReceiver // receiver , (session) => { sendMessage({ - subject: "popup:close" + subject: "close" + , destination: "popup" }); state.apiConfig.sessionListener(session); @@ -258,9 +257,10 @@ onMessage(message => { * Popup is ready to receive data to populate the cast destination * chooser. */ - case "shim:popupReady": { + case "popupReady": { sendMessage({ - subject: "popup:populate" + subject: "populateReceiverList" + , destination: "popup" , data: { receivers: state.receiverList , selectedMedia: state.apiConfig._selectedMedia @@ -273,7 +273,7 @@ onMessage(message => { /** * Popup closed before session established. */ - case "shim:popupClosed": { + case "popupClosed": { if (state.sessionRequestInProgress) { state.sessionRequestInProgress = false; sessionErrorCallback(new Error_(ErrorCode.CANCEL)); diff --git a/ext/src/shim/index.js b/ext/src/shim/index.js index cd3a35a..d485752 100755 --- a/ext/src/shim/index.js +++ b/ext/src/shim/index.js @@ -16,7 +16,7 @@ window.chrome.cast.media = media; onMessage(message => { switch (message.subject) { - case "shim:initialized": { + case "shimInitialized": { const bridgeInfo = message.data; // Call page's API loaded function if defined @@ -28,8 +28,3 @@ onMessage(message => { }; } }); - -// Trigger bridge mDNS discovery -sendMessage({ - subject: "main:initialize" -});