poltergeist-cj 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.md +434 -0
- data/lib/capybara/poltergeist.rb +27 -0
- data/lib/capybara/poltergeist/browser.rb +339 -0
- data/lib/capybara/poltergeist/client.rb +151 -0
- data/lib/capybara/poltergeist/client/agent.coffee +374 -0
- data/lib/capybara/poltergeist/client/browser.coffee +393 -0
- data/lib/capybara/poltergeist/client/compiled/agent.js +535 -0
- data/lib/capybara/poltergeist/client/compiled/browser.js +526 -0
- data/lib/capybara/poltergeist/client/compiled/connection.js +25 -0
- data/lib/capybara/poltergeist/client/compiled/main.js +204 -0
- data/lib/capybara/poltergeist/client/compiled/node.js +77 -0
- data/lib/capybara/poltergeist/client/compiled/web_page.js +421 -0
- data/lib/capybara/poltergeist/client/connection.coffee +11 -0
- data/lib/capybara/poltergeist/client/main.coffee +89 -0
- data/lib/capybara/poltergeist/client/node.coffee +57 -0
- data/lib/capybara/poltergeist/client/web_page.coffee +297 -0
- data/lib/capybara/poltergeist/cookie.rb +35 -0
- data/lib/capybara/poltergeist/driver.rb +278 -0
- data/lib/capybara/poltergeist/errors.rb +178 -0
- data/lib/capybara/poltergeist/inspector.rb +46 -0
- data/lib/capybara/poltergeist/json.rb +25 -0
- data/lib/capybara/poltergeist/network_traffic.rb +6 -0
- data/lib/capybara/poltergeist/network_traffic/request.rb +26 -0
- data/lib/capybara/poltergeist/network_traffic/response.rb +40 -0
- data/lib/capybara/poltergeist/node.rb +154 -0
- data/lib/capybara/poltergeist/server.rb +36 -0
- data/lib/capybara/poltergeist/utility.rb +9 -0
- data/lib/capybara/poltergeist/version.rb +5 -0
- data/lib/capybara/poltergeist/web_socket_server.rb +96 -0
- metadata +285 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class Poltergeist.Connection
|
|
2
|
+
constructor: (@owner, @port) ->
|
|
3
|
+
@socket = new WebSocket "ws://127.0.0.1:#{@port}/"
|
|
4
|
+
@socket.onmessage = this.commandReceived
|
|
5
|
+
@socket.onclose = -> phantom.exit()
|
|
6
|
+
|
|
7
|
+
commandReceived: (message) =>
|
|
8
|
+
@owner.runCommand(JSON.parse(message.data))
|
|
9
|
+
|
|
10
|
+
send: (message) ->
|
|
11
|
+
@socket.send(JSON.stringify(message))
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
class Poltergeist
|
|
2
|
+
constructor: (port, width, height) ->
|
|
3
|
+
@browser = new Poltergeist.Browser(this, width, height)
|
|
4
|
+
@connection = new Poltergeist.Connection(this, port)
|
|
5
|
+
|
|
6
|
+
# The QtWebKit bridge doesn't seem to like Function.prototype.bind
|
|
7
|
+
that = this
|
|
8
|
+
phantom.onError = (message, stack) -> that.onError(message, stack)
|
|
9
|
+
|
|
10
|
+
@running = false
|
|
11
|
+
|
|
12
|
+
runCommand: (command) ->
|
|
13
|
+
@running = true
|
|
14
|
+
|
|
15
|
+
try
|
|
16
|
+
@browser.runCommand(command.name, command.args)
|
|
17
|
+
catch error
|
|
18
|
+
if error instanceof Poltergeist.Error
|
|
19
|
+
this.sendError(error)
|
|
20
|
+
else
|
|
21
|
+
this.sendError(new Poltergeist.BrowserError(error.toString(), error.stack))
|
|
22
|
+
|
|
23
|
+
sendResponse: (response) ->
|
|
24
|
+
this.send(response: response)
|
|
25
|
+
|
|
26
|
+
sendError: (error) ->
|
|
27
|
+
this.send(
|
|
28
|
+
error:
|
|
29
|
+
name: error.name || 'Generic',
|
|
30
|
+
args: error.args && error.args() || [error.toString()]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
send: (data) ->
|
|
34
|
+
# Prevents more than one response being sent for a single
|
|
35
|
+
# command. This can happen in some scenarios where an error
|
|
36
|
+
# is raised but the script can still continue.
|
|
37
|
+
if @running
|
|
38
|
+
@connection.send(data)
|
|
39
|
+
@running = false
|
|
40
|
+
|
|
41
|
+
# This is necessary because the remote debugger will wrap the
|
|
42
|
+
# script in a function, causing the Poltergeist variable to
|
|
43
|
+
# become local.
|
|
44
|
+
window.Poltergeist = Poltergeist
|
|
45
|
+
|
|
46
|
+
class Poltergeist.Error
|
|
47
|
+
|
|
48
|
+
class Poltergeist.ObsoleteNode extends Poltergeist.Error
|
|
49
|
+
name: "Poltergeist.ObsoleteNode"
|
|
50
|
+
args: -> []
|
|
51
|
+
toString: -> this.name
|
|
52
|
+
|
|
53
|
+
class Poltergeist.InvalidSelector extends Poltergeist.Error
|
|
54
|
+
constructor: (@method, @selector) ->
|
|
55
|
+
name: "Poltergeist.InvalidSelector"
|
|
56
|
+
args: -> [@method, @selector]
|
|
57
|
+
|
|
58
|
+
class Poltergeist.FrameNotFound extends Poltergeist.Error
|
|
59
|
+
constructor: (@frameName) ->
|
|
60
|
+
name: "Poltergeist.FrameNotFound"
|
|
61
|
+
args: -> [@frameName]
|
|
62
|
+
|
|
63
|
+
class Poltergeist.MouseEventFailed extends Poltergeist.Error
|
|
64
|
+
constructor: (@eventName, @selector, @position) ->
|
|
65
|
+
name: "Poltergeist.MouseEventFailed"
|
|
66
|
+
args: -> [@eventName, @selector, @position]
|
|
67
|
+
|
|
68
|
+
class Poltergeist.JavascriptError extends Poltergeist.Error
|
|
69
|
+
constructor: (@errors) ->
|
|
70
|
+
name: "Poltergeist.JavascriptError"
|
|
71
|
+
args: -> [@errors]
|
|
72
|
+
|
|
73
|
+
class Poltergeist.BrowserError extends Poltergeist.Error
|
|
74
|
+
constructor: (@message, @stack) ->
|
|
75
|
+
name: "Poltergeist.BrowserError"
|
|
76
|
+
args: -> [@message, @stack]
|
|
77
|
+
|
|
78
|
+
class Poltergeist.StatusFailError extends Poltergeist.Error
|
|
79
|
+
name: "Poltergeist.StatusFailError"
|
|
80
|
+
args: -> []
|
|
81
|
+
|
|
82
|
+
# We're using phantom.libraryPath so that any stack traces
|
|
83
|
+
# report the full path.
|
|
84
|
+
phantom.injectJs("#{phantom.libraryPath}/web_page.js")
|
|
85
|
+
phantom.injectJs("#{phantom.libraryPath}/node.js")
|
|
86
|
+
phantom.injectJs("#{phantom.libraryPath}/connection.js")
|
|
87
|
+
phantom.injectJs("#{phantom.libraryPath}/browser.js")
|
|
88
|
+
|
|
89
|
+
new Poltergeist(phantom.args[0], phantom.args[1], phantom.args[2])
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Proxy object for forwarding method calls to the node object inside the page.
|
|
2
|
+
|
|
3
|
+
class Poltergeist.Node
|
|
4
|
+
@DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete',
|
|
5
|
+
'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'getAttributes',
|
|
6
|
+
'isVisible', 'position', 'trigger', 'parentId', 'parentIds', 'mouseEventTest',
|
|
7
|
+
'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText', 'containsSelection']
|
|
8
|
+
|
|
9
|
+
constructor: (@page, @id) ->
|
|
10
|
+
|
|
11
|
+
parent: ->
|
|
12
|
+
new Poltergeist.Node(@page, this.parentId())
|
|
13
|
+
|
|
14
|
+
for name in @DELEGATES
|
|
15
|
+
do (name) =>
|
|
16
|
+
this.prototype[name] = (args...) ->
|
|
17
|
+
@page.nodeCall(@id, name, args)
|
|
18
|
+
|
|
19
|
+
mouseEventPosition: ->
|
|
20
|
+
viewport = @page.viewportSize()
|
|
21
|
+
pos = this.position()
|
|
22
|
+
|
|
23
|
+
middle = (start, end, size) ->
|
|
24
|
+
start + ((Math.min(end, size) - start) / 2)
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
x: middle(pos.left, pos.right, viewport.width),
|
|
28
|
+
y: middle(pos.top, pos.bottom, viewport.height)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
mouseEvent: (name) ->
|
|
32
|
+
this.scrollIntoView()
|
|
33
|
+
|
|
34
|
+
pos = this.mouseEventPosition()
|
|
35
|
+
test = this.mouseEventTest(pos.x, pos.y)
|
|
36
|
+
|
|
37
|
+
if test.status == 'success'
|
|
38
|
+
if name == 'rightclick'
|
|
39
|
+
@page.mouseEvent('click', pos.x, pos.y, 'right')
|
|
40
|
+
this.trigger('contextmenu')
|
|
41
|
+
else
|
|
42
|
+
@page.mouseEvent(name, pos.x, pos.y)
|
|
43
|
+
pos
|
|
44
|
+
else
|
|
45
|
+
throw new Poltergeist.MouseEventFailed(name, test.selector, pos)
|
|
46
|
+
|
|
47
|
+
dragTo: (other) ->
|
|
48
|
+
this.scrollIntoView()
|
|
49
|
+
|
|
50
|
+
position = this.mouseEventPosition()
|
|
51
|
+
otherPosition = other.mouseEventPosition()
|
|
52
|
+
|
|
53
|
+
@page.mouseEvent('mousedown', position.x, position.y)
|
|
54
|
+
@page.mouseEvent('mouseup', otherPosition.x, otherPosition.y)
|
|
55
|
+
|
|
56
|
+
isEqual: (other) ->
|
|
57
|
+
@page == other.page && this.isDOMEqual(other.id)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
class Poltergeist.WebPage
|
|
2
|
+
@CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized',
|
|
3
|
+
'onLoadStarted', 'onResourceRequested', 'onResourceReceived',
|
|
4
|
+
'onError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated']
|
|
5
|
+
|
|
6
|
+
@DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render', 'renderBase64', 'goBack', 'goForward']
|
|
7
|
+
|
|
8
|
+
@COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize', 'beforeUpload', 'afterUpload']
|
|
9
|
+
|
|
10
|
+
@EXTENSIONS = []
|
|
11
|
+
|
|
12
|
+
constructor: (@native) ->
|
|
13
|
+
@native or= require('webpage').create()
|
|
14
|
+
|
|
15
|
+
@_source = null
|
|
16
|
+
@_errors = []
|
|
17
|
+
@_networkTraffic = {}
|
|
18
|
+
@_temp_headers = {}
|
|
19
|
+
@frames = []
|
|
20
|
+
|
|
21
|
+
for callback in WebPage.CALLBACKS
|
|
22
|
+
this.bindCallback(callback)
|
|
23
|
+
|
|
24
|
+
for command in @COMMANDS
|
|
25
|
+
do (command) =>
|
|
26
|
+
this.prototype[command] =
|
|
27
|
+
(args...) -> this.runCommand(command, args)
|
|
28
|
+
|
|
29
|
+
for delegate in @DELEGATES
|
|
30
|
+
do (delegate) =>
|
|
31
|
+
this.prototype[delegate] =
|
|
32
|
+
-> @native[delegate].apply(@native, arguments)
|
|
33
|
+
|
|
34
|
+
onInitializedNative: ->
|
|
35
|
+
@_source = null
|
|
36
|
+
@injectAgent()
|
|
37
|
+
this.removeTempHeaders()
|
|
38
|
+
this.setScrollPosition(left: 0, top: 0)
|
|
39
|
+
|
|
40
|
+
injectAgent: ->
|
|
41
|
+
if @native.evaluate(-> typeof __poltergeist) == "undefined"
|
|
42
|
+
@native.injectJs "#{phantom.libraryPath}/agent.js"
|
|
43
|
+
for extension in WebPage.EXTENSIONS
|
|
44
|
+
@native.injectJs extension
|
|
45
|
+
|
|
46
|
+
injectExtension: (file) ->
|
|
47
|
+
WebPage.EXTENSIONS.push file
|
|
48
|
+
@native.injectJs file
|
|
49
|
+
|
|
50
|
+
onConsoleMessageNative: (message) ->
|
|
51
|
+
if message == '__DOMContentLoaded'
|
|
52
|
+
@_source = @native.content
|
|
53
|
+
false
|
|
54
|
+
|
|
55
|
+
onLoadStartedNative: ->
|
|
56
|
+
@requestId = @lastRequestId
|
|
57
|
+
|
|
58
|
+
onLoadFinishedNative: ->
|
|
59
|
+
@_source or= @native.content
|
|
60
|
+
|
|
61
|
+
onConsoleMessage: (message) ->
|
|
62
|
+
console.log(message)
|
|
63
|
+
|
|
64
|
+
onErrorNative: (message, stack) ->
|
|
65
|
+
stackString = message
|
|
66
|
+
|
|
67
|
+
stack.forEach (frame) ->
|
|
68
|
+
stackString += "\n"
|
|
69
|
+
stackString += " at #{frame.file}:#{frame.line}"
|
|
70
|
+
stackString += " in #{frame.function}" if frame.function && frame.function != ''
|
|
71
|
+
|
|
72
|
+
@_errors.push(message: message, stack: stackString)
|
|
73
|
+
|
|
74
|
+
onResourceRequestedNative: (request) ->
|
|
75
|
+
@lastRequestId = request.id
|
|
76
|
+
|
|
77
|
+
if request.url == @redirectURL
|
|
78
|
+
@redirectURL = null
|
|
79
|
+
@requestId = request.id
|
|
80
|
+
|
|
81
|
+
@_networkTraffic[request.id] = {
|
|
82
|
+
request: request,
|
|
83
|
+
responseParts: []
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onResourceReceivedNative: (response) ->
|
|
87
|
+
@_networkTraffic[response.id]?.responseParts.push(response)
|
|
88
|
+
|
|
89
|
+
if @requestId == response.id
|
|
90
|
+
if response.redirectURL
|
|
91
|
+
@redirectURL = response.redirectURL
|
|
92
|
+
else
|
|
93
|
+
@_statusCode = response.status
|
|
94
|
+
@_responseHeaders = response.headers
|
|
95
|
+
|
|
96
|
+
setHttpAuth: (user, password) ->
|
|
97
|
+
@native.settings.userName = user
|
|
98
|
+
@native.settings.password = password
|
|
99
|
+
|
|
100
|
+
networkTraffic: ->
|
|
101
|
+
@_networkTraffic
|
|
102
|
+
|
|
103
|
+
clearNetworkTraffic: ->
|
|
104
|
+
@_networkTraffic = {}
|
|
105
|
+
|
|
106
|
+
content: ->
|
|
107
|
+
@native.frameContent
|
|
108
|
+
|
|
109
|
+
source: ->
|
|
110
|
+
@_source
|
|
111
|
+
|
|
112
|
+
title: ->
|
|
113
|
+
@native.frameTitle
|
|
114
|
+
|
|
115
|
+
errors: ->
|
|
116
|
+
@_errors
|
|
117
|
+
|
|
118
|
+
clearErrors: ->
|
|
119
|
+
@_errors = []
|
|
120
|
+
|
|
121
|
+
statusCode: ->
|
|
122
|
+
@_statusCode
|
|
123
|
+
|
|
124
|
+
responseHeaders: ->
|
|
125
|
+
headers = {}
|
|
126
|
+
@_responseHeaders.forEach (item) ->
|
|
127
|
+
headers[item.name] = item.value
|
|
128
|
+
headers
|
|
129
|
+
|
|
130
|
+
cookies: ->
|
|
131
|
+
@native.cookies
|
|
132
|
+
|
|
133
|
+
deleteCookie: (name) ->
|
|
134
|
+
@native.deleteCookie(name)
|
|
135
|
+
|
|
136
|
+
viewportSize: ->
|
|
137
|
+
@native.viewportSize
|
|
138
|
+
|
|
139
|
+
setViewportSize: (size) ->
|
|
140
|
+
@native.viewportSize = size
|
|
141
|
+
|
|
142
|
+
setZoomFactor: (zoom_factor) ->
|
|
143
|
+
@native.zoomFactor = zoom_factor
|
|
144
|
+
|
|
145
|
+
setPaperSize: (size) ->
|
|
146
|
+
@native.paperSize = size
|
|
147
|
+
|
|
148
|
+
scrollPosition: ->
|
|
149
|
+
@native.scrollPosition
|
|
150
|
+
|
|
151
|
+
setScrollPosition: (pos) ->
|
|
152
|
+
@native.scrollPosition = pos
|
|
153
|
+
|
|
154
|
+
clipRect: ->
|
|
155
|
+
@native.clipRect
|
|
156
|
+
|
|
157
|
+
setClipRect: (rect) ->
|
|
158
|
+
@native.clipRect = rect
|
|
159
|
+
|
|
160
|
+
elementBounds: (selector) ->
|
|
161
|
+
@native.evaluate(
|
|
162
|
+
(selector) -> document.querySelector(selector).getBoundingClientRect(),
|
|
163
|
+
selector
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
setUserAgent: (userAgent) ->
|
|
167
|
+
@native.settings.userAgent = userAgent
|
|
168
|
+
|
|
169
|
+
getCustomHeaders: ->
|
|
170
|
+
@native.customHeaders
|
|
171
|
+
|
|
172
|
+
setCustomHeaders: (headers) ->
|
|
173
|
+
@native.customHeaders = headers
|
|
174
|
+
|
|
175
|
+
addTempHeader: (header) ->
|
|
176
|
+
for name, value of header
|
|
177
|
+
@_temp_headers[name] = value
|
|
178
|
+
|
|
179
|
+
removeTempHeaders: ->
|
|
180
|
+
allHeaders = this.getCustomHeaders()
|
|
181
|
+
for name, value of @_temp_headers
|
|
182
|
+
delete allHeaders[name]
|
|
183
|
+
this.setCustomHeaders(allHeaders)
|
|
184
|
+
|
|
185
|
+
pushFrame: (name) ->
|
|
186
|
+
if @native.switchToFrame(name)
|
|
187
|
+
@frames.push(name)
|
|
188
|
+
true
|
|
189
|
+
else
|
|
190
|
+
false
|
|
191
|
+
|
|
192
|
+
pages: ->
|
|
193
|
+
@native.pagesWindowName
|
|
194
|
+
|
|
195
|
+
popFrame: ->
|
|
196
|
+
@frames.pop()
|
|
197
|
+
@native.switchToParentFrame()
|
|
198
|
+
|
|
199
|
+
getPage: (name) ->
|
|
200
|
+
page = @native.getPage(name)
|
|
201
|
+
new Poltergeist.WebPage(page) if page
|
|
202
|
+
|
|
203
|
+
dimensions: ->
|
|
204
|
+
scroll = this.scrollPosition()
|
|
205
|
+
viewport = this.viewportSize()
|
|
206
|
+
|
|
207
|
+
top: scroll.top, bottom: scroll.top + viewport.height,
|
|
208
|
+
left: scroll.left, right: scroll.left + viewport.width,
|
|
209
|
+
viewport: viewport
|
|
210
|
+
document: this.documentSize()
|
|
211
|
+
|
|
212
|
+
# A work around for http://code.google.com/p/phantomjs/issues/detail?id=277
|
|
213
|
+
validatedDimensions: ->
|
|
214
|
+
dimensions = this.dimensions()
|
|
215
|
+
document = dimensions.document
|
|
216
|
+
|
|
217
|
+
if dimensions.right > document.width
|
|
218
|
+
dimensions.left = Math.max(0, dimensions.left - (dimensions.right - document.width))
|
|
219
|
+
dimensions.right = document.width
|
|
220
|
+
|
|
221
|
+
if dimensions.bottom > document.height
|
|
222
|
+
dimensions.top = Math.max(0, dimensions.top - (dimensions.bottom - document.height))
|
|
223
|
+
dimensions.bottom = document.height
|
|
224
|
+
|
|
225
|
+
this.setScrollPosition(left: dimensions.left, top: dimensions.top)
|
|
226
|
+
|
|
227
|
+
dimensions
|
|
228
|
+
|
|
229
|
+
get: (id) ->
|
|
230
|
+
new Poltergeist.Node(this, id)
|
|
231
|
+
|
|
232
|
+
# Before each mouse event we make sure that the mouse is moved to where the
|
|
233
|
+
# event will take place. This deals with e.g. :hover changes.
|
|
234
|
+
mouseEvent: (name, x, y, button = 'left') ->
|
|
235
|
+
this.sendEvent('mousemove', x, y)
|
|
236
|
+
this.sendEvent(name, x, y, button)
|
|
237
|
+
|
|
238
|
+
evaluate: (fn, args...) ->
|
|
239
|
+
this.injectAgent()
|
|
240
|
+
JSON.parse this.sanitize(@native.evaluate("function() { return PoltergeistAgent.stringify(#{this.stringifyCall(fn, args)}) }"))
|
|
241
|
+
|
|
242
|
+
sanitize: (potential_string) ->
|
|
243
|
+
if typeof(potential_string) == "string"
|
|
244
|
+
# JSON doesn't like \r or \n in strings unless escaped
|
|
245
|
+
potential_string.replace("\n","\\n").replace("\r","\\r")
|
|
246
|
+
else
|
|
247
|
+
potential_string
|
|
248
|
+
|
|
249
|
+
execute: (fn, args...) ->
|
|
250
|
+
@native.evaluate("function() { #{this.stringifyCall(fn, args)} }")
|
|
251
|
+
|
|
252
|
+
stringifyCall: (fn, args) ->
|
|
253
|
+
if args.length == 0
|
|
254
|
+
"(#{fn.toString()})()"
|
|
255
|
+
else
|
|
256
|
+
# The JSON.stringify happens twice because the second time we are essentially
|
|
257
|
+
# escaping the string.
|
|
258
|
+
"(#{fn.toString()}).apply(this, JSON.parse(#{JSON.stringify(JSON.stringify(args))}))"
|
|
259
|
+
|
|
260
|
+
# For some reason phantomjs seems to have trouble with doing 'fat arrow' binding here,
|
|
261
|
+
# hence the 'that' closure.
|
|
262
|
+
bindCallback: (name) ->
|
|
263
|
+
that = this
|
|
264
|
+
@native[name] = ->
|
|
265
|
+
if that[name + 'Native']? # For internal callbacks
|
|
266
|
+
result = that[name + 'Native'].apply(that, arguments)
|
|
267
|
+
|
|
268
|
+
if result != false && that[name]? # For externally set callbacks
|
|
269
|
+
that[name].apply(that, arguments)
|
|
270
|
+
|
|
271
|
+
# Any error raised here or inside the evaluate will get reported to
|
|
272
|
+
# phantom.onError. If result is null, that means there was an error
|
|
273
|
+
# inside the agent.
|
|
274
|
+
runCommand: (name, args) ->
|
|
275
|
+
result = this.evaluate(
|
|
276
|
+
(name, args) -> __poltergeist.externalCall(name, args),
|
|
277
|
+
name, args
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if result != null
|
|
281
|
+
if result.error?
|
|
282
|
+
switch result.error.message
|
|
283
|
+
when 'PoltergeistAgent.ObsoleteNode'
|
|
284
|
+
throw new Poltergeist.ObsoleteNode
|
|
285
|
+
when 'PoltergeistAgent.InvalidSelector'
|
|
286
|
+
[method, selector] = args
|
|
287
|
+
throw new Poltergeist.InvalidSelector(method, selector)
|
|
288
|
+
else
|
|
289
|
+
throw new Poltergeist.BrowserError(result.error.message, result.error.stack)
|
|
290
|
+
else
|
|
291
|
+
result.value
|
|
292
|
+
|
|
293
|
+
canGoBack: ->
|
|
294
|
+
@native.canGoBack
|
|
295
|
+
|
|
296
|
+
canGoForward: ->
|
|
297
|
+
@native.canGoForward
|