Restructure shim/bridge initialization steps

This commit is contained in:
hensm
2019-02-10 06:31:31 +00:00
parent a1101d6cf6
commit 1c55b67922
8 changed files with 181 additions and 139 deletions

View File

@@ -168,7 +168,7 @@ async function handleMessage (message) {
browser.on("serviceUp", service => { browser.on("serviceUp", service => {
transforms.encode.write({ transforms.encode.write({
subject: "shim:serviceUp" subject: "serviceUp"
, data: { , data: {
address: service.addresses[0] address: service.addresses[0]
, port: service.port , port: service.port
@@ -181,7 +181,7 @@ browser.on("serviceUp", service => {
browser.on("serviceDown", service => { browser.on("serviceDown", service => {
transforms.encode.write({ transforms.encode.write({
subject:"shim:serviceDown" subject:"serviceDown"
, data: { , data: {
id: service.txt.id id: service.txt.id
} }

View File

@@ -1,12 +1,37 @@
"use strict"; "use strict";
document.addEventListener("__castMessageResponse", ev => { const backgroundPort = browser.runtime.connect({
browser.runtime.sendMessage(ev.detail); name: "shim"
}) });
browser.runtime.onMessage.addListener(message => { backgroundPort.onMessage.addListener(message => {
const event = new CustomEvent("__castMessage", { const event = new CustomEvent("__castMessage", {
detail: JSON.stringify(message) detail: JSON.stringify(message)
}); });
document.dispatchEvent(event); 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);
}); });

View File

@@ -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 popupWinId;
let popupOpenerTabId; let popupShimId;
let popupOpenerFrameId;
/** /**
* Creates popup window for cast destination selection. * Creates popup window for cast destination selection.
* Refocusing other browser windows causes the popup window * Refocusing other browser windows causes the popup window
* to close and returns an API error (TODO). * to close and returns an API error (TODO).
*/ */
async function openPopup (tabId, frameId) { async function openPopup (shimId) {
const width = 350; const width = 350;
const height = 200; const height = 200;
@@ -397,8 +352,7 @@ async function openPopup (tabId, frameId) {
// Store popup details for message forwarding // Store popup details for message forwarding
popupWinId = popup.id; popupWinId = popup.id;
popupOpenerTabId = tabId; popupShimId = shimId;
popupOpenerFrameId = frameId;
// Size/position not set correctly on creation (bug?) // Size/position not set correctly on creation (bug?)
await browser.windows.update(popup.id, { await browser.windows.update(popup.id, {
@@ -422,80 +376,138 @@ async function openPopup (tabId, frameId) {
browser.windows.onRemoved.addListener(id => { browser.windows.onRemoved.addListener(id => {
if (id === popupWinId) { if (id === popupWinId) {
messageRouter.handleMessage({ messageRouter.handleMessage({
subject: "shim:popupClosed" subject: "popupClosed"
}); });
popupWinId = null; popupWinId = null;
popupOpenerTabId = null; popupShimId = null;
} }
}); });
/** const shimMap = new Map();
* Extension scripts make a connection to the background script
* with a destination name to be registered as message route. browser.runtime.onMessage.addListener(message => {
*/ if (message.subject === "getPopupShimInfo") {
browser.runtime.onConnect.addListener(port => { return new Promise(resolve => {
messageRouter.register(port.name, message => { 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.postMessage(message);
}); });
port.onMessage.addListener(message => {
messageRouter.handleMessage(message);
})
});
messageRouter.register("main", async (message, sender) => { port.onMessage.addListener(async message => {
const tabId = sender && sender.tab.id; if (message.subject.startsWith("bridge")) {
bridgePort.postMessage(message);
}
switch (message.subject) { switch (message.subject) {
case "main:initialize": { case "openPopup": {
const bridgeInfo = await getBridgeInfo(); /**
if (bridgeInfo && bridgeInfo.isVersionCompatible) { * If popup already open, reassign to new shim,
initBridge(tabId, sender.frameId); * 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, { break;
subject: "shim:initialized" };
, data: bridgeInfo
}, { frameId: sender.frameId });
break; case "discover": {
}; bridgePort.postMessage({
subject: "bridge:discover"
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"
}); });
popupOpenerTabId = tabId; break;
popupOpenerFrameId = sender.frameId; };
// Notify shim to re-populate receiver list default: {
messageRouter.handleMessage({ // TODO: Remove need for this
subject: "shim:popupReady" messageRouter.handleMessage(message);
}); break;
} else {
await openPopup(tabId, sender.frameId);
} }
}
});
port.postMessage({
subject: "shimInitialized"
, data: bridgeInfo
});
}
browser.runtime.onConnect.addListener(port => {
switch (port.name) {
case "shim": {
onConnectShim(port);
break; 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 => { messageRouter.register("mirrorCast", message => {
browser.tabs.sendMessage(mirrorCastTabId, message browser.tabs.sendMessage(mirrorCastTabId, message
@@ -507,8 +519,12 @@ messageRouter.register("mediaCast", message => {
}); });
// Forward messages into messageRouter
browser.runtime.onMessage.addListener((message, sender) => { browser.runtime.onMessage.addListener((message, sender) => {
messageRouter.handleMessage(message, sender); messageRouter.handleMessage(message, {
tabId: sender.tab.id
, frameId: sender.frameId
});
}); });

View File

@@ -11,10 +11,10 @@ function deregister (routeName) {
routeMap.delete(routeName); routeMap.delete(routeName);
} }
function handleMessage (message, sender) { function handleMessage (message, details) {
const destination = message.subject.split(":")[0]; const destination = message.subject.split(":")[0];
if (routeMap.has(destination)) { if (routeMap.has(destination)) {
routeMap.get(destination)(message, sender); routeMap.get(destination)(message, details);
} }
} }

View File

@@ -42,18 +42,23 @@ class App extends Component {
}); });
} }
componentDidMount () { async componentDidMount () {
this.port = browser.runtime.connect({ const { tabId, frameId } = await browser.runtime.sendMessage({
subject: "getPopupShimInfo"
});
this.port = browser.tabs.connect(tabId, {
name: "popup" name: "popup"
, frameId
}); });
this.port.postMessage({ this.port.postMessage({
subject: "shim:popupReady" subject: "popupReady"
}); });
this.port.onMessage.addListener(message => { this.port.onMessage.addListener(message => {
switch (message.subject) { switch (message.subject) {
case "popup:populate": { case "populateReceiverList": {
this.setState({ this.setState({
receivers: message.data.receivers receivers: message.data.receivers
, selectedMedia: message.data.selectedMedia , selectedMedia: message.data.selectedMedia
@@ -70,8 +75,9 @@ class App extends Component {
break; break;
} }
case "popup:close": { case "close": {
window.close(); window.close();
break; break;
} }
} }
@@ -84,7 +90,7 @@ class App extends Component {
}); });
this.port.postMessage({ this.port.postMessage({
subject: "shim:selectReceiver" subject: "selectReceiver"
, data: { , data: {
receiver receiver
, selectedMedia: this.state.selectedMedia , selectedMedia: this.state.selectedMedia

View File

@@ -91,7 +91,7 @@ cast.initialize = (
state.apiConfig = apiConfig; state.apiConfig = apiConfig;
sendMessage({ sendMessage({
subject: "bridge:discover" subject: "discover"
}); });
apiConfig.receiverListener(state.receiverList.length apiConfig.receiverListener(state.receiverList.length
@@ -146,7 +146,7 @@ cast.requestSession = (
// Open destination chooser // Open destination chooser
sendMessage({ sendMessage({
subject: "main:openPopup" subject: "openPopup"
}); });
}; };
@@ -175,11 +175,9 @@ onMessage(message => {
* Cast destination found (serviceUp). Set the API availability * Cast destination found (serviceUp). Set the API availability
* property and call the page event function (__onGCastApiAvailable). * property and call the page event function (__onGCastApiAvailable).
*/ */
case "shim:serviceUp": { case "serviceUp": {
const receiver = message.data; const receiver = message.data;
console.log(receiver);
if (state.receiverList.find(r => r.id === receiver.id)) { if (state.receiverList.find(r => r.id === receiver.id)) {
break; break;
} }
@@ -196,7 +194,7 @@ onMessage(message => {
* Cast destination lost (serviceDown). Remove from the receiver list * Cast destination lost (serviceDown). Remove from the receiver list
* and update availability state. * and update availability state.
*/ */
case "shim:serviceDown": { case "serviceDown": {
state.receiverList = state.receiverList.filter( state.receiverList = state.receiverList.filter(
receiver => receiver.id !== message.data.id); receiver => receiver.id !== message.data.id);
@@ -208,7 +206,7 @@ onMessage(message => {
break; break;
}; };
case "shim:selectReceiver": { case "selectReceiver": {
console.info("Caster (Debug): Selected receiver"); console.info("Caster (Debug): Selected receiver");
const selectedReceiver = new Receiver( const selectedReceiver = new Receiver(
@@ -226,7 +224,8 @@ onMessage(message => {
, selectedReceiver // receiver , selectedReceiver // receiver
, (session) => { , (session) => {
sendMessage({ sendMessage({
subject: "popup:close" subject: "close"
, destination: "popup"
}); });
state.apiConfig.sessionListener(session); state.apiConfig.sessionListener(session);
@@ -258,9 +257,10 @@ onMessage(message => {
* Popup is ready to receive data to populate the cast destination * Popup is ready to receive data to populate the cast destination
* chooser. * chooser.
*/ */
case "shim:popupReady": { case "popupReady": {
sendMessage({ sendMessage({
subject: "popup:populate" subject: "populateReceiverList"
, destination: "popup"
, data: { , data: {
receivers: state.receiverList receivers: state.receiverList
, selectedMedia: state.apiConfig._selectedMedia , selectedMedia: state.apiConfig._selectedMedia
@@ -273,7 +273,7 @@ onMessage(message => {
/** /**
* Popup closed before session established. * Popup closed before session established.
*/ */
case "shim:popupClosed": { case "popupClosed": {
if (state.sessionRequestInProgress) { if (state.sessionRequestInProgress) {
state.sessionRequestInProgress = false; state.sessionRequestInProgress = false;
sessionErrorCallback(new Error_(ErrorCode.CANCEL)); sessionErrorCallback(new Error_(ErrorCode.CANCEL));

View File

@@ -16,7 +16,7 @@ window.chrome.cast.media = media;
onMessage(message => { onMessage(message => {
switch (message.subject) { switch (message.subject) {
case "shim:initialized": { case "shimInitialized": {
const bridgeInfo = message.data; const bridgeInfo = message.data;
// Call page's API loaded function if defined // Call page's API loaded function if defined
@@ -28,8 +28,3 @@ onMessage(message => {
}; };
} }
}); });
// Trigger bridge mDNS discovery
sendMessage({
subject: "main:initialize"
});