phantom-page-eval
Version:
Evaluate a script function on a page with PhantomJS
271 lines (216 loc) • 7.66 kB
JavaScript
/* start logScript part */
var MAX_LOG_ENTRY_SIZE = 1000
var messages = []
function trimMessage (pars) {
var message = Array.prototype.join.call(pars, ' ')
// this is special case, because phantom logs base64 images content completely into the output
if (message.indexOf('Request data:image') === 0 && message.length > 100) {
return message.substring(0, 100) + '...'
}
if (message.length > MAX_LOG_ENTRY_SIZE) {
return message.substring(0, MAX_LOG_ENTRY_SIZE) + '...'
}
return message
}
console.log = function (m) {
var lastArg = arguments[arguments.length - 1]
var log = { timestamp: new Date().getTime(), message: trimMessage(arguments), level: 'debug' }
if (
lastArg != null &&
typeof lastArg === 'object' &&
!Array.isArray(lastArg) &&
Object.prototype.hasOwnProperty.call(lastArg, 'userLevel')
) {
log.userLevel = lastArg.userLevel === true
}
messages.push(log)
}
console.error = function (m) {
var lastArg = arguments[arguments.length - 1]
var log = { timestamp: new Date().getTime(), message: trimMessage(arguments), level: 'error' }
if (
lastArg != null &&
typeof lastArg === 'object' &&
!Array.isArray(lastArg) &&
Object.prototype.hasOwnProperty.call(lastArg, 'userLevel')
) {
log.userLevel = lastArg.userLevel === true
}
messages.push(log)
}
console.warn = function (m) {
var lastArg = arguments[arguments.length - 1]
var log = { timestamp: new Date().getTime(), message: trimMessage(arguments), level: 'warn' }
if (
lastArg != null &&
typeof lastArg === 'object' &&
!Array.isArray(lastArg) &&
Object.prototype.hasOwnProperty.call(lastArg, 'userLevel')
) {
log.userLevel = lastArg.userLevel === true
}
messages.push(log)
}
/* end logScript part */
try {
/* eslint-disable-next-line no-undef */
console.log('evaluating in phantomjs version: ' + phantom.version.major + '.' + phantom.version.minor + '.' + phantom.version.patch)
var webpage = require('webpage')
var system = require('system')
var fs = require('fs')
var page = webpage.create()
var settingsFile = system.args[system.args.length - 1]
var stream = fs.open(settingsFile, 'r')
var body = JSON.parse(stream.read())
stream.close()
// eslint-disable-next-line no-inner-declarations
function respond (page, response) {
system.stdout.write(JSON.stringify(response))
// Work-around to avoid "Unsafe JavaScript attempt to access frame" warning in PhantomJS 1.9.8.
// See: https://github.com/ariya/phantomjs/issues/12697
// since we rely on stdout for the dedicated-process strategy this work-around
// ensures the phantom process don't log anything we don't want
page.close()
setTimeout(function () {
/* eslint-disable-next-line no-undef */
phantom.exit(0)
}, 0)
}
// eslint-disable-next-line no-unneeded-ternary
var pageJSisDone = body.waitForJS ? false : true
/* start conversion script part */
page.viewportSize = {
width: body.viewport.width,
height: body.viewport.height
}
page.settings.javascriptEnabled = true
page.onResourceRequested = function (request, networkRequest) {
console.log('Request ' + request.url)
if (request.url.lastIndexOf(body.url, 0) === 0) {
return
}
// potentially dangerous request
if (request.url.lastIndexOf('file:///', 0) === 0) {
networkRequest.abort()
return
}
// to support cdn like format //cdn.jquery...
if (request.url.lastIndexOf('file://', 0) === 0 && request.url.lastIndexOf('file:///', 0) !== 0) {
networkRequest.changeUrl(request.url.replace('file://', 'http://'))
}
if (body.waitForJS === true && request.url.lastIndexOf('http://intruct-javascript-ending', 0) === 0) {
pageJSisDone = true
}
}
page.onConsoleMessage = function (msg, line, source) {
console.log(msg, line, source, { userLevel: true })
}
page.onResourceError = function (resourceError) {
console.warn('Unable to load resource (#' + resourceError.id + 'URL:' + resourceError.url + ')')
console.warn('Error code: ' + resourceError.errorCode + '. Description: ' + resourceError.errorString)
}
page.onError = function (msg, trace) {
console.warn(msg)
trace.forEach(function (item) {
console.warn(' ', item.file, ':', item.line)
})
}
page.onInitialized = function () {
if (body.waitForJS === true) {
page.evaluate(function (varName) {
if (typeof Object.defineProperty === 'function') {
// eslint-disable-next-line accessor-pairs
Object.defineProperty(window, varName, {
set: function (val) {
if (!val) {
return
}
if (val === true) {
var scriptNode = document.createElement('script')
scriptNode.src = 'http://intruct-javascript-ending'
document.body.appendChild(scriptNode)
}
}
})
}
}, body.waitForJSVarName)
}
}
page.open(body.url, function () {
page.zoomFactor = 1
if (body.styles.length > 0) {
console.log('adding custom styles at the beginning of page..')
page.evaluate(function (styles) {
var fragment = document.createDocumentFragment()
styles.forEach(function (css) {
var styleNode = document.createElement('style')
styleNode.appendChild(document.createTextNode(css))
fragment.appendChild(styleNode)
})
document.head.insertBefore(fragment, document.head.firstChild)
}, body.styles)
}
setTimeout(function () {
resolvePage()
}, 0)
// eslint-disable-next-line no-inner-declarations
function resolvePage () {
try {
if (body.waitForJS === true && !pageJSisDone) {
setTimeout(function () {
resolvePage()
}, 100)
return
}
var resultData = page.evaluate(function (rawFnSource, customArgs) {
var fnSource = rawFnSource.trim()
fnSource = fnSource.slice(-1) === ';' ? fnSource.slice(0, -1) : fnSource
var fn = '(function result() { '
fn += 'return (' + fnSource + ') '
fn += '})()'
try {
// eslint-disable-next-line
fn = window.eval(fn)
} catch (e) {
// eslint-disable-next-line no-template-curly-in-string
return {
error: 'script code passed as `scriptFn` option has syntax error: ' + e.message
}
}
if (typeof fn !== 'function') {
return {
error: 'script code passed as `scriptFn` option is not a function'
}
}
return {
result: fn.apply(null, customArgs)
}
}, body.scriptFn, body.args)
// resultData can still be null if evaluated script returned
// non-serializable value
if (resultData && resultData.error) {
throw new Error(resultData.error)
}
fs.write(body.outputFilePath, JSON.stringify({
result: resultData != null ? resultData.result : undefined
}), 'w')
respond(page, {
empty: resultData == null,
logs: messages
})
} catch (e) {
onMainError(e)
}
}
})
/* end conversion script part */
} catch (e) {
onMainError(e)
}
function onMainError (e) {
console.error(e.message)
e.message += '; log: ' + JSON.stringify(messages)
system.stderr.write(JSON.stringify(e))
/* eslint-disable-next-line no-undef */
phantom.exit(1)
}