# frozen_string_literal: truerequire'shellwords'require'socket'require'timeout'require'tmpdir'require'rbconfig'require'opal/os'moduleOpalmoduleCliRunnersclassChromeSCRIPT_PATH=File.expand_path('chrome_cdp_interface.rb',__dir__).freezeDEFAULT_CHROME_HOST='localhost'DEFAULT_CHROME_PORT=9222defself.call(data)runner=new(data)runner.runenddefinitialize(data)argv=data[:argv]ifargv&&argv.any?warn"warning: ARGV is not supported by the Chrome runner #{argv.inspect}"endoptions=data[:options]@output=options.fetch(:output,$stdout)@builder=data[:builder].callendattr_reader:output,:exit_status,:builderdefrunmktmpdirdo|dir|with_chrome_serverdoprepare_files_in(dir)env={'CHROME_HOST'=>chrome_host,'CHROME_PORT'=>chrome_port.to_s,'NODE_PATH'=>File.join(__dir__,'node_modules'),'OPAL_CDP_EXT'=>builder.output_extension}cmd=[RbConfig.ruby,"#{__dir__}/../../../exe/opal",'--no-exit','-I',__dir__,'-r','source-map-support-node',SCRIPT_PATH,dir]Kernel.exec(env,*cmd)endendendprivatedefprepare_files_in(dir)js=builder.to_smap=builder.source_map.to_jsonstack=File.binread("#{__dir__}/source-map-support-browser.js")ext=builder.output_extensionmodule_type=' type="module"'ifbuilder.esm?# Some maps may contain `</script>` fragment (eg. in strings) which would close our# `<script>` tag prematurely. For this case, we need to escape the `</script>` tag.map_json=map.to_json.gsub(/(<\/scr)(ipt>)/i,'\1"+"\2')# Chrome can't handle huge data passed to `addScriptToEvaluateOnLoad`# https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/U5qyeX_ydBo# The only way is to create temporary files and pass them to chrome.File.binwrite("#{dir}/index.#{ext}",js)File.binwrite("#{dir}/index.map",map)File.binwrite("#{dir}/source-map-support.js",stack)File.binwrite("#{dir}/index.html",<<~HTML)
<html><head>
<meta charset='utf-8'>
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<script src='./source-map-support.js'></script>
<script>
window.opalheadlesschrome = true;
sourceMapSupport.install({
retrieveSourceMap: function(path) {
return path.endsWith('/index.#{ext}') ? {
url: './index.map', map: #{map_json}
} : null;
}
});
</script>
</head><body>
<script src='./index.#{ext}'#{module_type}></script>
</body></html>
HTMLenddefchrome_hostENV['CHROME_HOST']||DEFAULT_CHROME_HOSTenddefchrome_portENV['CHROME_PORT']||DEFAULT_CHROME_PORTenddefwith_chrome_serverifchrome_server_running?yieldelserun_chrome_server{yield}endenddefrun_chrome_serverraise'Chrome server can be started only on localhost'ifchrome_host!=DEFAULT_CHROME_HOSTprofile=mktmpprofile# Disable web security with "--disable-web-security" flag to be able to do XMLHttpRequest (see test_openuri.rb)# For other options see https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/node/ChromeLauncher.tschrome_server_cmd=%{#{OS.shellescape(chrome_executable)}\
--allow-pre-commit-input \
--disable-background-networking \
--enable-features=NetworkServiceInProcess2 \
--disable-background-timer-throttling \
--disable-backgrounding-occluded-windows \
--disable-breakpad \
--disable-client-side-phishing-detection \
--disable-component-extensions-with-background-pages \
--disable-default-apps \
--disable-dev-shm-usage \
--disable-extensions \
--disable-features=Translate,BackForwardCache,AcceptCHFrame,AvoidUnnecessaryBeforeUnloadCheckSync \
--disable-hang-monitor \
--disable-ipc-flooding-protection \
--disable-popup-blocking \
--disable-prompt-on-repost \
--disable-renderer-backgrounding \
--disable-sync \
--force-color-profile=srgb \
--metrics-recording-only \
--no-first-run \
--enable-automation \
--password-store=basic \
--use-mock-keychain \
--enable-blink-features=IdleDetection \
--export-tagged-pdf \
--headless \
--user-data-dir=#{profile}\
--hide-scrollbars \
--mute-audio \
--disable-web-security \
--remote-debugging-port=#{chrome_port}\#{ENV['CHROME_OPTS']}}chrome_pid=Process.spawn(chrome_server_cmd,in: OS.dev_null,out: OS.dev_null,err: OS.dev_null)Timeout.timeout(30)doloopdobreakifchrome_server_running?sleep0.5endendyieldrescueTimeout::Errorputs'Failed to start chrome server'puts'Make sure that you have it installed and that its version is > 59'exit(1)ensureifOS.windows?&&chrome_pidProcess.kill('KILL',chrome_pid)unlesssystem("taskkill /f /t /pid #{chrome_pid} >NUL 2>NUL")elsifchrome_pidProcess.kill('HUP',chrome_pid)endFileUtils.rm_rf(profile)ifprofileenddefchrome_server_running?puts"Connecting to #{chrome_host}:#{chrome_port}..."TCPSocket.new(chrome_host,chrome_port).closetruerescueErrno::ECONNREFUSED,Errno::EADDRNOTAVAILfalseenddefchrome_executableENV['GOOGLE_CHROME_BINARY']||ifOS.windows?['C:/Program Files/Google/Chrome Dev/Application/chrome.exe','C:/Program Files/Google/Chrome/Application/chrome.exe'].eachdo|path|nextunlessFile.exist?pathreturnpathendelsifOS.macos?'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'else%w[
google-chrome-stable
chromium
chromium-freeworld
chromium-browser
].eachdo|name|nextunlesssystem('sh','-c',"command -v #{name.shellescape}",out: '/dev/null')returnnameendraise'Cannot find chrome executable'endenddefmktmpdir(&block)Dir.mktmpdir('chrome-opal-',&block)enddefmktmpprofileDir.mktmpdir('chrome-opal-profile-')endendendend