class Capybara::Apparition::ChromeClient
Constants
- DEFAULT_OPTIONS
Attributes
timeout[RW]
Public Class Methods
client(ws_url)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 15 def client(ws_url) new(ws_url) end
new(ws_url)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 32 def initialize(ws_url) @ws = WebSocketClient.new(ws_url) @handlers = Hash.new { |hash, key| hash[key] = [] } @responses = {} @events = Queue.new @send_mutex = Mutex.new @msg_mutex = Mutex.new @message_available = ConditionVariable.new @session_handlers = Hash.new { |hash, key| hash[key] = Hash.new { |h, k| h[k] = [] } } @timeout = nil @async_ids = [] start_threads end
Private Class Methods
get_ws_url(options)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 21 def get_ws_url(options) response = Net::HTTP.get(options[:host], '/json', options[:port]) # TODO: handle unsuccesful request response = JSON.parse(response) first_page = response.find { |e| e['type'] == 'page' } # TODO: handle no entry found first_page['webSocketDebuggerUrl'] end
Public Instance Methods
add_async_id(msg_id)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 75 def add_async_id(msg_id) @msg_mutex.synchronize do @async_ids.push(msg_id) end end
on(event_name, session_id = nil, &block)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 56 def on(event_name, session_id = nil, &block) return @handlers[event_name] << block unless session_id @session_handlers[session_id][event_name] << block end
send_cmd(command, params)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 62 def send_cmd(command, params) time = Time.now msg_id = send_msg(command, params) Response.new(self, msg_id, send_time: time) end
send_cmd_to_session(session_id, command, params)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 68 def send_cmd_to_session(session_id, command, params) time = Time.now msg_id, msg = generate_msg(command, params) wrapper_msg_id = send_msg('Target.sendMessageToTarget', sessionId: session_id, message: msg) Response.new(self, wrapper_msg_id, msg_id, send_time: time) end
stop()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 52 def stop @ws.close end
Private Instance Methods
cleanup_async_responses()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 160 def cleanup_async_responses loop do @msg_mutex.synchronize do @message_available.wait(@msg_mutex, 0.1) (@responses.keys & @async_ids).each do |msg_id| puts "Cleaning up response for #{msg_id}" if ENV['DEBUG'] == 'V' @responses.delete(msg_id) @async_ids.delete(msg_id) end end end end
generate_msg(command, params)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 101 def generate_msg(command, params) @send_mutex.synchronize do msg_id = generate_unique_id [msg_id, { method: command, params: params, id: msg_id }.to_json] end end
generate_unique_id()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 122 def generate_unique_id @last_id ||= 0 @last_id += 1 end
handle_error(error)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 83 def handle_error(error) case error['code'] when -32_000 raise WrongWorld.new(nil, error) else raise CDPError.new(error) end end
listen()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 138 def listen read_until { false } end
listen_until() { || ... }
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 134 def listen_until read_until { yield } end
process_handlers(handlers, event)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 205 def process_handlers(handlers, event) event_name = event['method'] handlers[event_name].each do |handler| puts "Calling handler for #{event_name}" if ENV['DEBUG'] == 'V' handler.call(**event['params'].transform_keys(&method(:snake_sym))) end end
process_messages()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 173 def process_messages # run handlers in own thread so as not to hang message processing loop do event = @events.pop next unless event event_name = event['method'] puts "Popped event #{event_name}" if ENV['DEBUG'] == 'V' if event_name == 'Target.receivedMessageFromTarget' session_id = event.dig('params', 'sessionId') event = JSON.parse(event.dig('params', 'message')) process_handlers(@session_handlers[session_id], event) end process_handlers(@handlers, event) end rescue CDPError => e if e.code == -32_602 puts "Attempt to contact session that's gone away" else puts "Unexpected CDPError: #{e.message}" end retry rescue StandardError => e puts "Unexpected inner loop exception: #{e}: #{e.message}: #{e.backtrace}" retry rescue Exception => e # rubocop:disable Lint/RescueException puts "Unexpected Outer Loop exception: #{e}" retry end
read_msg()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 142 def read_msg msg = JSON.parse(@ws.read_msg) puts "#{Time.now.to_i}: got msg: #{msg}" if ENV['DEBUG'] == 'V' # Check if it's an event and push on event queue @events.push msg.dup if msg['method'] msg = JSON.parse(msg['params']['message']) if msg['method'] == 'Target.receivedMessageFromTarget' if msg['id'] @msg_mutex.synchronize do puts "broadcasting response to #{msg['id']}" if ENV['DEBUG'] == 'V' @responses[msg['id']] = msg @message_available.broadcast end end msg end
read_until() { |msg| ... }
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 127 def read_until loop do msg = read_msg return msg if yield(msg) end end
send_msg(command, params)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 92 def send_msg(command, params) msg_id, msg = generate_msg(command, params) @send_mutex.synchronize do puts "#{Time.now.to_i}: sending msg: #{msg}" if ENV['DEBUG'] == 'V' @ws.send_msg(msg) end msg_id end
snake_sym(str)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 231 def snake_sym(str) str.gsub(/([a-z\d])([A-Z])/, '\1_\2') .tr('-', '_') .downcase .to_sym end
start_threads()
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 213 def start_threads @processor = Thread.new do process_messages end @processor.abort_on_exception = true @async_response_handler = Thread.new do cleanup_async_responses end @async_response_handler.abort_on_exception = true @listener = Thread.new do listen rescue EOFError # rubocop:disable Lint/SuppressedException end # @listener.abort_on_exception = true end
wait_for_msg_response(msg_id)
click to toggle source
# File lib/capybara/apparition/driver/chrome_client.rb, line 108 def wait_for_msg_response(msg_id) @msg_mutex.synchronize do timer = Capybara::Helpers.timer(expire_in: @timeout) while (response = @responses.delete(msg_id)).nil? if @timeout && timer.expired? puts "Timedout waiting for response for msg: #{msg_id}" raise TimeoutError.new(msg_id) end @message_available.wait(@msg_mutex, 0.1) end response end end