This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
/** 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user