# 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
# 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
# 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
# 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
# File debug-1.4.0/lib/debug/server_cdp.rb, line 475
def cleanup_reader
Process.kill :KILL, @chrome_pid if @chrome_pid
end
# File debug-1.4.0/lib/debug/server_cdp.rb, line 470
def deactivate_bp
@q_msg << 'del'
@q_ans << 'y'
end
# 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
# 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
# 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
# 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
# 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
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
# File debug-1.4.0/lib/debug/server_cdp.rb, line 487
def respond req, **result
send_response req, **result
end
# File debug-1.4.0/lib/debug/server_cdp.rb, line 491
def respond_fail req, **result
send_fail_response req, **result
end
# 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
# 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