/** Run page JS on Bare (ctx.bare.bareVm / bare-realm) or a strict Function sandbox. */ var BARE_SUMMON_JS_MAX_SCRIPTS = 16 var BARE_SUMMON_JS_MAX_SOURCE = 256 * 1024 var BARE_SUMMON_JS_MAX_TIMERS = 32 function bareSummonResolveVm(ctx) { if (ctx && typeof ctx.bareOsSummonEval === 'function') { return { kind: 'syscall', eval: ctx.bareOsSummonEval } } var b = ctx && ctx.bare var vm = b && (b.bareVm || b.vm) if (vm && vm.default) vm = vm.default if ( vm && typeof vm.createContext === 'function' && typeof vm.runInContext === 'function' ) { return { kind: 'vm', vm: vm } } return { kind: 'function' } } function bareSummonMakeConsole(sink) { function push(level, args) { var msg = [] var i for (i = 0; i < args.length; i++) { try { msg.push(typeof args[i] === 'string' ? args[i] : String(args[i])) } catch (e) { msg.push('[unprintable]') } } sink.push({ level: level, text: msg.join(' ') }) } return { log: function () { push('log', arguments) }, info: function () { push('info', arguments) }, warn: function () { push('warn', arguments) }, error: function () { push('error', arguments) }, debug: function () { push('debug', arguments) } } } function bareSummonMakeLocation(href) { var u = bareSummonParseUrl(href) || { href: String(href || ''), protocol: '', host: '', hostname: '', port: '', pathname: '/', search: '', hash: '', origin: '' } return { href: u.href, protocol: u.protocol, host: u.host, hostname: u.hostname, port: u.port, pathname: u.pathname, search: u.search, hash: u.hash, origin: u.origin, toString: function () { return this.href } } } function bareSummonEvalSource(engine, source, sandbox) { if (engine.kind === 'syscall') { return engine.eval(source, sandbox) } if (engine.kind === 'vm') { var box = engine.vm.createContext() var k for (k in sandbox) { if (Object.prototype.hasOwnProperty.call(sandbox, k)) box[k] = sandbox[k] } return engine.vm.runInContext( 'var window = this; var self = this; var globalThis = this;\n' + source, box ) } var fn = new Function( 'window', '"use strict";' + 'var self = window;' + 'var document = window.document;' + 'var console = window.console;' + 'var location = window.location;' + 'var navigator = window.navigator;' + 'var setTimeout = window.setTimeout;' + 'var clearTimeout = window.clearTimeout;' + 'var fetch = window.fetch;' + source ) return fn(sandbox.window || sandbox) } async function bareSummonCollectScriptJobs(session, doc, pageUrl) { var jobs = [] var nodes = (doc && doc.scripts) || [] var i for ( i = 0; i < nodes.length && jobs.length < BARE_SUMMON_JS_MAX_SCRIPTS; i++ ) { var n = nodes[i] var src = n.attrs && n.attrs.src var type = ((n.attrs && n.attrs.type) || 'text/javascript').toLowerCase() if ( type && type !== 'text/javascript' && type !== 'application/javascript' && type !== 'module' && type !== '' ) { if (type.indexOf('javascript') < 0 && type !== 'module') continue } if (src) { var abs = bareSummonResolveUrl(src, pageUrl) if (!abs || !bareSummonIsHttp(abs)) continue jobs.push({ node: n, url: abs.href, source: '', external: true }) } else { var body = bareSummonScriptSource(n) if (body) jobs.push({ node: n, url: pageUrl, source: body, external: false }) } } for (i = 0; i < jobs.length; i++) { if (!jobs[i].external) continue var res = await bareSummonFetch(session.ctx, jobs[i].url, { jar: session.jar, maxBytes: BARE_SUMMON_JS_MAX_SOURCE }) jobs[i].source = (res && res.body) || '' if (jobs[i].source.length > BARE_SUMMON_JS_MAX_SOURCE) { jobs[i].source = jobs[i].source.slice(0, BARE_SUMMON_JS_MAX_SOURCE) } } return jobs } async function bareSummonRunDocumentJs(session, doc, pageUrl) { var logs = [] var errors = [] var ran = 0 var engine = bareSummonResolveVm(session && session.ctx) var timers = [] var tid = 0 var env = { console: bareSummonMakeConsole(logs) } var document = bareSummonCreateDocumentApi(doc, env) var location = bareSummonMakeLocation(pageUrl) var window = { document: document, console: env.console, location: location, navigator: { userAgent: 'Summon/0.2 (Bare OS; text; bare-vm)', platform: 'bare', language: 'en' }, innerWidth: session.cols || 80, innerHeight: 24, devicePixelRatio: 1, setTimeout: function (fn, ms) { if (timers.length >= BARE_SUMMON_JS_MAX_TIMERS) return 0 tid++ timers.push({ id: tid, fn: fn, ms: ms | 0 }) return tid }, clearTimeout: function (id) { timers = timers.filter(function (t) { return t.id !== id }) }, fetch: function () { return Promise.reject(new Error('summon: page fetch is not enabled')) } } window.window = window window.self = window env.document = document var jobs = await bareSummonCollectScriptJobs(session, doc, pageUrl) var i for (i = 0; i < jobs.length; i++) { var src = jobs[i].source if (!src || src.length > BARE_SUMMON_JS_MAX_SOURCE) continue try { bareSummonEvalSource(engine, src, { window: window, document: document, console: env.console, location: location, navigator: window.navigator }) ran++ } catch (err) { errors.push({ url: jobs[i].url, message: (err && err.message) || String(err) }) env.console.error((err && err.message) || String(err)) } } for (i = 0; i < timers.length; i++) { if (typeof timers[i].fn === 'function') { try { timers[i].fn.call(window) } catch (e2) { errors.push({ url: pageUrl, message: (e2 && e2.message) || String(e2) }) } } } bareSummonFlushInlineStyle(doc.body) bareSummonFlushInlineStyle(doc.head) return { engine: engine.kind, ran: ran, blocked: Math.max(0, ((doc.scripts && doc.scripts.length) || 0) - ran), logs: logs, errors: errors } }