class Ferrum::Client
def build_message(method, params)
def build_message(method, params) { method: method, params: params }.merge(id: next_command_id) end
def close
def close @ws.close # Give a thread some time to handle a tail of messages @pendings.clear @thread.kill unless @thread.join(1) @subscriber.close end
def command(method, async: false, **params)
def command(method, async: false, **params) message = build_message(method, params) send_message(message, async: async) end
def initialize(ws_url, options)
def initialize(ws_url, options) @command_id = 0 @ws_url = ws_url @options = options @pendings = Concurrent::Hash.new @ws = WebSocket.new(ws_url, options.ws_max_receive_size, options.logger) @subscriber = Subscriber.new start end
def inspect
def inspect "#<#{self.class} " \ "@command_id=#{@command_id.inspect} " \ "@pendings=#{@pendings.inspect} " \ "@ws=#{@ws.inspect}>" end
def next_command_id
def next_command_id @command_id += 1 end
def on(event, &block)
def on(event, &block) @subscriber.on(event, &block) end
def raise_browser_error(error)
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", "Inspected target navigated or closed" raise NodeNotFoundError, error # Context is lost, page is reloading when "Cannot find context with specified id" raise NoExecutionContextError, error when "No target with given id found" raise NoSuchPageError when /Could not compute content quads/ raise CoordinatesNotFoundError else raise BrowserError, error end end
def send_message(message, async:)
def send_message(message, async:) if async @ws.send_message(message) true else pending = Concurrent::IVar.new @pendings[message[:id]] = pending @ws.send_message(message) data = pending.value!(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 end
def session(session_id)
def session(session_id) SessionClient.new(self, session_id) end
def start
def start @thread = Utils::Thread.spawn do loop do message = @ws.messages.pop break unless message if message.key?("method") @subscriber << message else @pendings[message["id"]]&.set(message) end end end end
def subscribed?(event)
def subscribed?(event) @subscriber.subscribed?(event) end