module DEBUGGER__::UI_CDP

Constants

INVALID_REQUEST
SHOW_PROTOCOL

Public Class Methods

get_chrome_path() click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 55
def get_chrome_path
  return CONFIG[:chrome_path] if CONFIG[:chrome_path]

  # The process to check OS is based on `selenium` project.
  case RbConfig::CONFIG['host_os']
  when /mswin|msys|mingw|cygwin|emc/
    'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
  when /darwin|mac os/
    '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
  when /linux/
    'google-chrome'
  else
    raise "Unsupported OS"
  end
end
run_new_chrome() click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 71
def run_new_chrome
  dir = Dir.mktmpdir
  # The command line flags are based on: https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Chrome_Desktop#connecting
  stdin, stdout, stderr, wait_thr = *Open3.popen3("#{get_chrome_path} --remote-debugging-port=0 --no-first-run --no-default-browser-check --user-data-dir=#{dir}")
  stdin.close
  stdout.close

  data = stderr.readpartial 4096
  if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
    port = $1
    path = $2
  end
  stderr.close

  at_exit{
    CONFIG[:skip_path] = [//] # skip all
    FileUtils.rm_rf dir
  }

  [port, path, wait_thr.pid]
end
setup_chrome(addr) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 16
def setup_chrome addr
  return if CONFIG[:chrome_path] == ''

  port, path, pid = run_new_chrome
  begin
    s = Socket.tcp '127.0.0.1', port
  rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL
    return
  end

  ws_client = WebSocketClient.new(s)
  ws_client.handshake port, path
  ws_client.send id: 1, method: 'Target.getTargets'

  4.times do
    res = ws_client.extract_data
    case
    when res['id'] == 1 && target_info = res.dig('result', 'targetInfos')
      page = target_info.find{|t| t['type'] == 'page'}
      ws_client.send id: 2, method: 'Target.attachToTarget',
                    params: {
                      targetId: page['targetId'],
                      flatten: true
                    }
    when res['id'] == 2
      s_id = res.dig('result', 'sessionId')
      sleep 0.1
      ws_client.send sessionId: s_id, id: 1,
                    method: 'Page.navigate',
                    params: {
                      url: "devtools://devtools/bundled/inspector.html?ws=#{addr}"
                    }
    end
  end
  pid
rescue Errno::ENOENT
  nil
end

Public Instance Methods

activate_bp(bps) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 458
def activate_bp bps
  bps.each_key{|k|
    if k.match /^\d+:(\d+):(.*)/
      line = $1
      path = $2
      SESSION.add_line_breakpoint(path, line.to_i + 1)
    else
      SESSION.add_catch_breakpoint 'Exception'
    end
  }
end
cleanup_reader() click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 475
def cleanup_reader
  Process.kill :KILL, @chrome_pid if @chrome_pid
end
deactivate_bp() click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 470
def deactivate_bp
  @q_msg << 'del'
  @q_ans << 'y'
end
del_bp(bps, k) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 441
def del_bp bps, k
  return bps unless idx = bps[k]

  bps.delete k
  bps.each_key{|i| bps[i] -= 1 if bps[i] > idx}
  @q_msg << "del #{idx}"
  bps
end
fire_event(event, **result) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 495
def fire_event event, **result
  if result.empty?
    send_event event
  else
    send_event event, **result
  end
end
get_source_code(path) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 450
def get_source_code path
  return @src_map[path] if @src_map[path]

  src = File.read(path)
  @src_map[path] = src
  src
end
process() click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 263
def process
  bps = {}
  @src_map = {}
  loop do
    req = @ws_server.extract_data
    $stderr.puts '[>]' + req.inspect if SHOW_PROTOCOL

    case req['method']

    ## boot/configuration
    when 'Page.getResourceTree'
      path = File.absolute_path($0)
      src = File.read(path)
      @src_map[path] = src
      send_response req,
                    frameTree: {
                      frame: {
                        id: SecureRandom.hex(16),
                        loaderId: SecureRandom.hex(16),
                        url: 'http://debuggee/',
                        securityOrigin: 'http://debuggee',
                        mimeType: 'text/plain' },
                      resources: [
                      ]
                    }
      send_event 'Debugger.scriptParsed',
                  scriptId: path,
                  url: "http://debuggee#{path}",
                  startLine: 0,
                  startColumn: 0,
                  endLine: src.count("\n"),
                  endColumn: 0,
                  executionContextId: 1,
                  hash: src.hash
      send_event 'Runtime.executionContextCreated',
                  context: {
                    id: SecureRandom.hex(16),
                    origin: "http://#{@addr}",
                    name: ''
                  }
    when 'Debugger.getScriptSource'
      s_id = req.dig('params', 'scriptId')
      src = get_source_code s_id
      send_response req, scriptSource: src
      @q_msg << req
    when 'Page.startScreencast', 'Emulation.setTouchEmulationEnabled', 'Emulation.setEmitTouchEventsForMouse',
      'Runtime.compileScript', 'Page.getResourceContent', 'Overlay.setPausedInDebuggerMessage',
      'Runtime.releaseObjectGroup', 'Runtime.discardConsoleEntries', 'Log.clear'
      send_response req

    ## control
    when 'Debugger.resume'
      @q_msg << 'c'
      @q_msg << req
      send_response req
      send_event 'Debugger.resumed'
    when 'Debugger.stepOver'
      begin
        @session.check_postmortem
        @q_msg << 'n'
        send_response req
        send_event 'Debugger.resumed'
      rescue PostmortemError
        send_fail_response req,
                          code: INVALID_REQUEST,
                          message: "'stepOver' is not supported while postmortem mode"
      ensure
        @q_msg << req
      end
    when 'Debugger.stepInto'
      begin
        @session.check_postmortem
        @q_msg << 's'
        send_response req
        send_event 'Debugger.resumed'
      rescue PostmortemError
        send_fail_response req,
                          code: INVALID_REQUEST,
                          message: "'stepInto' is not supported while postmortem mode"
      ensure
        @q_msg << req
      end
    when 'Debugger.stepOut'
      begin
        @session.check_postmortem
        @q_msg << 'fin'
        send_response req
        send_event 'Debugger.resumed'
      rescue PostmortemError
        send_fail_response req,
                          code: INVALID_REQUEST,
                          message: "'stepOut' is not supported while postmortem mode"
      ensure
        @q_msg << req
      end
    when 'Debugger.setSkipAllPauses'
      skip = req.dig('params', 'skip')
      if skip
        deactivate_bp
      else
        activate_bp bps
      end
      send_response req

    # breakpoint
    when 'Debugger.getPossibleBreakpoints'
      s_id = req.dig('params', 'start', 'scriptId')
      line = req.dig('params', 'start', 'lineNumber')
      src = get_source_code s_id
      end_line = src.count("\n")
      line = end_line  if line > end_line
      send_response req,
                    locations: [
                      { scriptId: s_id,
                        lineNumber: line,
                      }
                    ]
    when 'Debugger.setBreakpointByUrl'
      line = req.dig('params', 'lineNumber')
      url = req.dig('params', 'url')
      locations = []
      if url.match /http:\/\/debuggee(.*)/
        path = $1
        cond = req.dig('params', 'condition')
        src = get_source_code path
        end_line = src.count("\n")
        line = end_line  if line > end_line
        b_id = "1:#{line}:#{path}"
        if cond != ''
          SESSION.add_line_breakpoint(path, line + 1, cond: cond)
        else
          SESSION.add_line_breakpoint(path, line + 1)
        end
        bps[b_id] = bps.size
        locations << {scriptId: path, lineNumber: line}
      else
        b_id = "1:#{line}:#{url}"
      end
      send_response req,
                    breakpointId: b_id,
                    locations: locations
    when 'Debugger.removeBreakpoint'
      b_id = req.dig('params', 'breakpointId')
      bps = del_bp bps, b_id
      send_response req
    when 'Debugger.setBreakpointsActive'
      active = req.dig('params', 'active')
      if active
        activate_bp bps
      else
        deactivate_bp # TODO: Change this part because catch breakpoints should not be deactivated.
      end
      send_response req
    when 'Debugger.setPauseOnExceptions'
      state = req.dig('params', 'state')
      ex = 'Exception'
      case state
      when 'none'
        @q_msg << 'config postmortem = false'
        bps = del_bp bps, ex
      when 'uncaught'
        @q_msg << 'config postmortem = true'
        bps = del_bp bps, ex
      when 'all'
        @q_msg << 'config postmortem = false'
        SESSION.add_catch_breakpoint ex
        bps[ex] = bps.size
      end
      send_response req

    when 'Debugger.evaluateOnCallFrame', 'Runtime.getProperties'
      @q_msg << req
    end
  end
rescue Detach
  @q_msg << 'continue'
end
puts(result) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 507
def puts result
  # STDERR.puts "puts: #{result}"
  # send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s
end
readline(prompt) click to toggle source

Called by the SESSION thread

# File debug-1.4.0/lib/debug/server_cdp.rb, line 481
def readline prompt
  return 'c' unless @q_msg

  @q_msg.pop || 'kill!'
end
respond(req, **result) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 487
def respond req, **result
  send_response req, **result
end
respond_fail(req, **result) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 491
def respond_fail req, **result
  send_fail_response req, **result
end
send_event(method, **params) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 253
def send_event method, **params
  if params.empty?
    @ws_server.send method: method, params: {}
  else
    @ws_server.send method: method, params: params
  end
end
send_fail_response(req, **res) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 249
def send_fail_response req, **res
  @ws_server.send id: req['id'], error: res
end
send_response(req, **res) click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 241
def send_response req, **res
  if res.empty?
    @ws_server.send id: req['id'], result: {}
  else
    @ws_server.send id: req['id'], result: res
  end
end
sock(skip: false) { |$stderr| ... } click to toggle source
# File debug-1.4.0/lib/debug/server_cdp.rb, line 503
def sock skip: false
  yield $stderr
end