/
client.rb
86 lines (74 loc) · 2.49 KB
/
client.rb
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
# frozen_string_literal: true
require "concurrent-ruby"
require "ferrum/browser/subscriber"
require "ferrum/browser/web_socket"
module Ferrum
class Browser
class Client
def initialize(browser, ws_url, start_id = 0, allow_slowmo = true)
@command_id = start_id
@pendings = Concurrent::Hash.new
@browser = browser
@slowmo = @browser.slowmo if allow_slowmo && @browser.slowmo > 0
@ws = WebSocket.new(ws_url, @browser.logger)
@subscriber = Subscriber.new
@thread = Thread.new do
Thread.current.abort_on_exception = true
Thread.current.report_on_exception = true if Thread.current.respond_to?(:report_on_exception=)
while message = @ws.messages.pop
if message.key?("method")
@subscriber.async.call(message)
else
@pendings[message["id"]]&.set(message)
end
end
end
end
def command(method, params = {})
pending = Concurrent::IVar.new
message = build_message(method, params)
@pendings[message[:id]] = pending
sleep(@slowmo) if @slowmo
@ws.send_message(message)
data = pending.value!(@browser.timeout)
@pendings.delete(message[:id])
raise DeadBrowserError if data.nil? && @ws.messages.closed?
raise TimeoutError unless data
error, response = data.values_at("error", "result")
raise_browser_error(error) if error
response
end
def on(event, &block)
@subscriber.on(event, &block)
end
def close
@ws.close
# Give a thread some time to handle a tail of messages
@pendings.clear
@thread.kill unless @thread.join(1)
end
private
def build_message(method, params)
{ method: method, params: params }.merge(id: next_command_id)
end
def next_command_id
@command_id += 1
end
def raise_browser_error(error)
case error["message"]
# Node has disappeared while we were trying to get it
when "No node with given id found",
"Could not find node with given id"
raise NodeNotFoundError.new(error)
# Context is lost, page is reloading
when "Cannot find context with specified id"
raise NoExecutionContextError.new(error)
when "No target with given id found"
raise NoSuchPageError
else
raise BrowserError.new(error)
end
end
end
end
end