This repository has been archived by the owner on Dec 4, 2019. It is now read-only.
/
phantom.coffee
140 lines (116 loc) · 4.01 KB
/
phantom.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
dnode = require 'dnode'
http = require 'http'
shoe = require 'shoe'
spawn = require 'win-spawn'
# the list of phantomjs RPC wrapper
phanta = []
# @Description: starts and returns a child process running phantomjs
# @param: port:int
# @args: args:object
# @return: ps:object
startPhantomProcess = (binary, port, hostname, args) ->
spawn binary, args.concat([
__dirname + '/shim.js'
port
hostname
])
# @Description: kills off all phantom processes within spawned by this parent
# process when it is exits
cleanUp = ->
phantom.exit() for phantom in phanta
onSignalClean = (signal) ->
return ->
if process.listeners(signal).length is 1
process.exit(0)
process.on('exit', cleanUp)
process.on(signal, onSignalClean(signal)) for signal in ['SIGINT', 'SIGTERM']
# @Description: We need this because dnode does magic clever stuff with
# functions, but we want the function to make it intact to phantom
wrap = (ph) ->
ph.callback = (fn) ->
return '__phantomCallback__'+fn.toString()
ph._createPage = ph.createPage
ph.createPage = (cb) ->
ph._createPage (page) ->
page._evaluate = page.evaluate
page.evaluate = (fn, cb, args...) ->
page._evaluate.apply(page, [fn.toString(), cb].concat(args))
page._onResourceRequested = page.onResourceRequested
# can apply extra args which will be passed to phantomjs
# onResourceRequested scope
page.onResourceRequested = (fn, cb, args...) ->
page._onResourceRequested.apply(page, [fn.toString(), cb].concat(args))
cb page
module.exports =
create: ->
args = ['--web-security=no']
options = {}
for arg in arguments
switch typeof arg
when 'function' then cb = arg
when 'string' then args.push arg
when 'object' then options = arg
if typeof options.parameters is 'object'
for key, value of options.parameters
args.push '--'+key+'='+value
options.path ?= ''
options.binary ?= options.path+'phantomjs'
options.port ?= 0
options.hostname ?= 'localhost'
options.dnodeOpts ?= {}
ps = null
phantom = null
httpServer = http.createServer()
httpServer.listen options.port, options.hostname
httpServer.on "error", (err) ->
if cb?
cb null, err
else
throw err
httpServer.on 'listening', ->
port = httpServer.address().port
hostname = httpServer.address().address
ps = startPhantomProcess options.binary, port, hostname, args
ps.stdout.on 'data', options.onStdout || (data) ->
console.log "phantom stdout: #{data}"
ps.stderr.on 'data', options.onStderr || (data) ->
module.exports.stderrHandler(data.toString('utf8'))
ps.on 'error', (err) ->
httpServer.close()
if err?.code is 'ENOENT'
console.error "phantomjs-node: You don't have 'phantomjs' installed"
if cb?
cb null, err
else
throw err
# @Description: when the background phantomjs child process exits or
# crashes removes the current dNode phantomjs RPC wrapper from the list of
# phantomjs RPC wrapper
ps.on 'exit', (code, signal) ->
httpServer.close()
if phantom
phantom.onExit?()
phanta = (p for p in phanta when p isnt phantom)
if options.onExit
options.onExit code, signal
else
console.assert not signal?, "signal killed phantomjs: #{signal}"
if code isnt 0
process.exit code
sock = shoe (stream) ->
d = dnode({}, options.dnodeOpts)
d.on 'remote', (_phantom) ->
phantom = _phantom
wrap phantom
phantom.process = ps
phanta.push phantom
cb? phantom, null
d.pipe stream
stream.pipe d
sock.install httpServer, '/dnode'
stderrHandler: (message) ->
NON_ERROR_MESSAGE =
/No such method.*socketSentData|CoreText performance note/
if NON_ERROR_MESSAGE.test message
return # Stupid, stupid QTWebKit
console.warn "phantom stderr: #{message}"