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 => {
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
}

View File

@@ -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);
});

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 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
});
});

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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));

View File

@@ -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"
});