gluon: 4.0!

Gluon 4.0, now with a mostly stable IPC API, and more:
- Rewrote IPC API to be more friendly and easy to use
- Added more versions to Versions API
- Fixed refreshing breaking Gluon API
- Reduced debug logging
This commit is contained in:
CanadaHonk 2022-12-10 15:40:51 +00:00
parent cf69ee38b3
commit c441cd360e

View File

@ -1,7 +1,7 @@
const rgb = (r, g, b, msg) => `\x1b[38;2;${r};${g};${b}m${msg}\x1b[0m`;
const log = (...args) => console.log(`[${rgb(88, 101, 242, 'Gluon')}]`, ...args);
process.versions.gluon = '3.2';
process.versions.gluon = '4.0';
const presets = { // Presets from OpenAsar
'base': '--autoplay-policy=no-user-gesture-required --disable-features=WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService', // Base Discord
@ -47,14 +47,24 @@ const findChromiumPath = async () => {
if (!whichChromium) return null;
return chromiumPathsWin[whichChromium];
return [ chromiumPathsWin[whichChromium], whichChromium ];
};
const getFriendlyName = whichChromium => {
switch (whichChromium) {
case 'stable': return 'Chrome';
case 'canary': return 'Chrome Canary';
case 'edge': return 'Edge';
}
};
const getDataPath = () => join(__dirname, '..', 'chrome_data');
const startChromium = async (url, { windowSize }) => {
const dataPath = getDataPath();
const chromiumPath = await findChromiumPath();
const [ chromiumPath, chromiumName ] = await findChromiumPath();
const friendlyProductName = getFriendlyName(chromiumName);
log('chromium path:', chromiumPath);
log('data path:', dataPath);
@ -82,7 +92,7 @@ const startChromium = async (url, { windowSize }) => {
const onMessage = msg => {
msg = JSON.parse(msg);
log('received', msg);
// log('received', msg);
if (onReply[msg.id]) {
onReply[msg.id](msg);
delete onReply[msg.id];
@ -106,7 +116,7 @@ const startChromium = async (url, { windowSize }) => {
pipeWrite.write(JSON.stringify(msg));
pipeWrite.write('\0');
log('sent', msg);
// log('sent', msg);
const reply = await new Promise(res => {
onReply[id] = msg => res(msg);
@ -141,6 +151,12 @@ const startChromium = async (url, { windowSize }) => {
// await new Promise(res => setTimeout(res, 1000));
let browserInfo;
sendMessage('Browser.getVersion').then(x => { // get browser info async as not important
browserInfo = x;
log('browser:', x.product);
});
const target = (await sendMessage('Target.getTargets')).targetInfos[0];
const { sessionId } = await sendMessage('Target.attachToTarget', {
@ -148,11 +164,17 @@ const startChromium = async (url, { windowSize }) => {
flatten: true
});
(await sendMessage('Runtime.enable', {}, sessionId)); // enable runtime API
// sendMessage('Page.enable', {}, sessionId); // pause page execution until we inject
// sendMessage('Page.waitForDebugger', {}, sessionId);
(await sendMessage('Runtime.addBinding', { // setup sending from window to Node via Binding
// (await sendMessage('Page.enable', {}, sessionId));
// (await sendMessage('Page.stopLoading', {}, sessionId));
sendMessage('Runtime.enable', {}, sessionId); // enable runtime API
sendMessage('Runtime.addBinding', { // setup sending from window to Node via Binding
name: '_gluonSend'
}, sessionId));
}, sessionId);
const evalInWindow = async func => {
return await sendMessage(`Runtime.evaluate`, {
@ -160,24 +182,27 @@ const startChromium = async (url, { windowSize }) => {
}, sessionId);
};
evalInWindow(`(() => {
let onIPCReply = {};
const windowInjectionSource = `(() => {
let onIPCReply = {}, ipcListeners = {};
window.Gluon = {
versions: {
gluon: '${process.versions.gluon}',
builder: '${'GLUGUN_VERSION' === 'G\LUGUN_VERSION' ? 'nothing' : 'Glugun GLUGUN_VERSION'}',
node: '${process.versions.node}',
chromium: navigator.userAgentData.brands.find(x => x.brand === "Chromium").version,
chromium: '${browserInfo.product.split('/')[1]}' ?? navigator.userAgentData.brands.find(x => x.brand === "Chromium").version,
product: '${friendlyProductName}',
v8: {
node: '${process.versions.v8}'
node: '${process.versions.v8}',
chromium: '${browserInfo.jsVersion}'
}
},
ipc: {
send: async (type, data, id = undefined) => {
id = id ?? Math.random().toString().split('.')[1];
window._gluonSend(JSON.stringify({
window.Gluon.ipc._send(JSON.stringify({
id,
type,
data
@ -192,6 +217,16 @@ window.Gluon = {
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);
},
_recieve: msg => {
const { id, type, data } = msg;
@ -201,22 +236,41 @@ window.Gluon = {
return;
}
if (window.Gluon.recieve?.(type, data) !== true) {
window.Gluon.send('pong', {}, id);
if (ipcListeners[type]) {
let reply;
for (const cb of ipcListeners[type]) {
const ret = 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', {}, id);
},
_send: window._gluonSend
},
};
delete window._gluonSend;
})();`); // inject nice reciever and sender wrappers
})();`;
evalInWindow(windowInjectionSource); // inject nice reciever and sender wrappers
let onIPCReply = {};
// sendMessage('Runtime.runIfWaitingForDebugger', {}, sessionId);
sendMessage(`Page.enable`, {}, sessionId);
sendMessage(`Page.addScriptToEvaluateOnNewDocument`, {
source: windowInjectionSource
}, sessionId);
let onIPCReply = {}, ipcListeners = {};
const sendToWindow = async (type, data, id = undefined) => {
id = id ?? Math.random().toString().split('.')[1];
evalInWindow(`window.Gluon._recieve(${JSON.stringify({
evalInWindow(`window.Gluon.ipc._recieve(${JSON.stringify({
id,
type,
data
@ -238,24 +292,40 @@ delete window._gluonSend;
return;
}
if (windowMessageCallback(type, data) !== true) { // true = replied itself, otherwise simply pong
sendToWindow('pong', {}, id);
}
};
if (ipcListeners[type]) {
let reply;
let windowMessageCallback = () => {};
for (const cb of ipcListeners[type]) {
const ret = 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', {}, id); // send simple pong to confirm
};
return {
window: {
onMessage: cb => {
windowMessageCallback = cb;
},
send: sendToWindow,
eval: evalInWindow,
},
CDP: {
ipc: {
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,
},
cdp: {
send: (method, params) => sendMessage(method, params, sessionId)
}
};
@ -275,12 +345,11 @@ export const open = async (url, { windowSize, onLoad } = {}) => {
Chromium.window.eval(toRun);
await Chromium.CDP.send(`Page.enable`);
await Chromium.CDP.send(`Page.addScriptToEvaluateOnNewDocument`, {
await Chromium.cdp.send(`Page.enable`);
await Chromium.cdp.send(`Page.addScriptToEvaluateOnNewDocument`, {
source: toRun
});
}
return Chromium;
};