gluon/src/lib/ipc.js
2022-12-18 12:37:13 +00:00

148 lines
3.6 KiB
JavaScript

export default ({ browserName, browserInfo }, { evaluate, addScriptToEvaluateOnNewDocument, pageLoadPromise }) => {
const injection = `(() => {
if (window.Gluon) return;
let onIPCReply = {}, ipcListeners = {};
window.Gluon = {
versions: {
gluon: '${process.versions.gluon}',
builder: '${'GLUGUN_VERSION' === 'G\LUGUN_VERSION' ? 'nothing' : 'Glugun GLUGUN_VERSION'}',
node: '${process.versions.node}',
browser: '${browserInfo.product.split('/')[1]}',
browserType: '${browserName.startsWith('Firefox') ? 'firefox' : 'chromium'}',
product: '${browserName}',
js: {
node: '${process.versions.v8}',
browser: '${browserInfo.jsVersion}'
},
embedded: {
node: ${'EMBEDDED_NODE' === 'true' ? 'true' : 'false'},
browser: false
}
},
ipc: {
send: async (type, data, id = undefined) => {
id = id ?? Math.random().toString().split('.')[1];
window.Gluon.ipc._send(JSON.stringify({
id,
type,
data
}));
if (id) return;
const reply = await new Promise(res => {
onIPCReply[id] = msg => res(msg);
});
return reply;
},
on: (type, cb) => {
if (!ipcListeners[type]) ipcListeners[type] = [];
ipcListeners[type].push(cb);
},
removeListener: (type, cb) => {
if (!ipcListeners[type]) return false;
ipcListeners[type].splice(ipcListeners[type].indexOf(cb), 1);
},
_receive: async msg => {
const { id, type, data } = msg;
if (onIPCReply[id]) {
onIPCReply[id]({ type, data });
delete onIPCReply[id];
return;
}
if (ipcListeners[type]) {
let reply;
for (const cb of ipcListeners[type]) {
const ret = await cb(data);
if (!reply) reply = ret; // use first returned value as reply
}
if (reply) return Gluon.ipc.send('reply', reply, id); // reply with wanted reply
}
Gluon.ipc.send('pong', null, id);
},
_send: window._gluonSend
},
};
delete window._gluonSend;
})();`;
evaluate({
expression: injection
});
addScriptToEvaluateOnNewDocument({
source: injection
});
let onIPCReply = {}, ipcListeners = {};
const sendToWindow = async (type, data, id = undefined) => {
id = id ?? Math.random().toString().split('.')[1];
await pageLoadPromise; // wait for page to load before sending, otherwise messages won't be heard
evalInWindow(`window.Gluon.ipc._receive(${JSON.stringify({
id,
type,
data
})})`);
if (id) return; // we are replying, don't expect reply back
const reply = await new Promise(res => {
onIPCReply[id] = msg => res(msg);
});
return reply;
};
const onWindowMessage = async ({ id, type, data }) => {
if (onIPCReply[id]) {
onIPCReply[id]({ type, data });
delete onIPCReply[id];
return;
}
if (ipcListeners[type]) {
let reply;
for (const cb of ipcListeners[type]) {
const ret = await cb(data);
if (!reply) reply = ret; // use first returned value as reply
}
if (reply) return sendToWindow('reply', reply, id); // reply with wanted reply
}
sendToWindow('pong', null, id); // send simple pong to confirm
};
return [ onWindowMessage, () => evaluate({
expression: injection
}), {
on: (type, cb) => {
if (!ipcListeners[type]) ipcListeners[type] = [];
ipcListeners[type].push(cb);
},
removeListener: (type, cb) => {
if (!ipcListeners[type]) return false;
ipcListeners[type].splice(ipcListeners[type].indexOf(cb), 1);
},
send: sendToWindow,
} ];
};