From e250ea4853384978f4f5058e6ea96bf13bd9dfbf Mon Sep 17 00:00:00 2001 From: CanadaHonk Date: Sun, 9 Apr 2023 00:27:32 +0100 Subject: [PATCH] api/resources: initial add --- src/api/resources.js | 81 ++++++++++++++++++++++++++++++++++++++++++ src/launcher/inject.js | 2 ++ 2 files changed, 83 insertions(+) create mode 100644 src/api/resources.js diff --git a/src/api/resources.js b/src/api/resources.js new file mode 100644 index 0000000..036d4ed --- /dev/null +++ b/src/api/resources.js @@ -0,0 +1,81 @@ +export default async CDP => { + const run = async js => { + await CDP.send('Runtime.evaluate', { + expression: typeof js === 'string' ? js : `(${js.toString()})()` + }); + }; + + const injectJS = async code => { + const toRun = `(async () => { +if (window.self !== window.top) return; +await new Promise(res => { + const check = () => { + if (!window.Gluon) return setTimeout(check, 20); + res(); + }; + + check(); +}); + +await new Promise(res => { + if (document.readyState !== 'loading') { + res(); + } else { + document.addEventListener('DOMContentLoaded', res); + } +}); + +${code} +})();`; + + await run(toRun); + + const { identifier } = await CDP.send('Page.addScriptToEvaluateOnNewDocument', { + source: toRun + }); + + return async () => { + await CDP.send('Page.removeScriptToEvaluateOnNewDocument', { + identifier + }); + }; + }; + + const escapeCSS = code => code.replaceAll('`', '\\`').replaceAll('\\', '\\\\'); + + const injectCSS = async (code, id = 'gluon-resource-' + Math.random().toString().split('.')[1]) => { + const js = `const el = document.querySelector('style#${id}') ?? document.createElement('style'); +el.id = '${id}'; +el.textContent = \`${escapeCSS(code)}\`; +if (!el.isConnected) document.head.appendChild(el);`; + + return [ id, await injectJS(js) ]; + }; + + return { + js: async inp => { + const code = typeof inp === 'function' ? `(${inp.toString()})()` : inp; + const remove = await injectJS(code); + + return { + remove + }; + }, + + css: async css => { + let [ id, removeJS ] = await injectCSS(css); + + return { + remove: async () => { + await removeJS(); // do not add in future + await run(`document.querySelector('style#${id}').remove()`); // remove current element + }, + + modify: async newCss => { + await removeJS(); // do not add old in future + [ id, removeJS ] = await injectCSS(newCss, id); // inject new css with same id + }, + } + } + }; +}; \ No newline at end of file diff --git a/src/launcher/inject.js b/src/launcher/inject.js index c51fa70..e551367 100644 --- a/src/launcher/inject.js +++ b/src/launcher/inject.js @@ -7,6 +7,7 @@ import LocalCDP from '../lib/local/cdp.js'; import PageApi from '../api/page.js'; import IdleApi from '../api/idle.js'; import ControlsApi from '../api/controls.js'; +import ResourcesApi from '../api/resources.js'; import V8CacheApi from '../api/v8Cache.js'; const acquireTarget = async (CDP, filter = () => true) => { @@ -193,6 +194,7 @@ export default async (CDP, proc, injectionType = 'browser', { dataPath, browserN Window.page = await PageApi(Window.cdp, evalInWindow, { pageLoadPromise }); Window.idle = await IdleApi(Window.cdp, { browserType, closeHandlers }); Window.controls = await ControlsApi(Window.cdp); + Window.resources = await ResourcesApi(Window.cdp); Window.v8Cache = await V8CacheApi(Window.cdp, evalInWindow, { browserType, dataPath }); return Window;