opal 1.6.1 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +17 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +1 -0
- data/HACKING.md +47 -26
- data/benchmark/benchmarks +415 -103
- data/benchmark/bm_call_overhead.yml +28 -0
- data/benchmark/run.rb +61 -40
- data/docs/cdp_common.json +3364 -0
- data/docs/cdp_common.md +18 -0
- data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
- data/lib/opal/ast/builder.rb +1 -1
- data/lib/opal/builder.rb +6 -1
- data/lib/opal/builder_processors.rb +5 -3
- data/lib/opal/cache.rb +1 -7
- data/lib/opal/cli_options.rb +72 -58
- data/lib/opal/cli_runners/chrome.rb +47 -9
- data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
- data/lib/opal/cli_runners/compiler.rb +146 -13
- data/lib/opal/cli_runners/deno.rb +32 -0
- data/lib/opal/cli_runners/firefox.rb +350 -0
- data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
- data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
- data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
- data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
- data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
- data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
- data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
- data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
- data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
- data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
- data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
- data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
- data/lib/opal/cli_runners/package-lock.json +62 -0
- data/lib/opal/cli_runners/package.json +1 -1
- data/lib/opal/cli_runners.rb +26 -4
- data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
- data/lib/opal/nodes/def.rb +8 -8
- data/lib/opal/nodes/iter.rb +12 -12
- data/lib/opal/nodes/logic.rb +1 -1
- data/lib/opal/nodes/masgn.rb +2 -2
- data/lib/opal/parser/with_ruby_lexer.rb +1 -1
- data/lib/opal/paths.rb +14 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/forward_args.rb +52 -4
- data/lib/opal/rewriters/targeted_patches.rb +94 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/basic_object.rb +1 -1
- data/opal/corelib/boolean.rb +2 -2
- data/opal/corelib/class.rb +11 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/enumerable.rb +4 -0
- data/opal/corelib/enumerator.rb +1 -1
- data/opal/corelib/hash.rb +2 -2
- data/opal/corelib/helpers.rb +1 -1
- data/opal/corelib/kernel.rb +3 -3
- data/opal/corelib/method.rb +1 -1
- data/opal/corelib/module.rb +29 -8
- data/opal/corelib/proc.rb +7 -5
- data/opal/corelib/runtime.js +141 -78
- data/opal/corelib/set.rb +252 -0
- data/opal/corelib/string.rb +2 -1
- data/opal/corelib/time.rb +2 -2
- data/opal/opal.rb +1 -0
- data/opal.gemspec +1 -0
- data/spec/filters/bugs/array.rb +22 -13
- data/spec/filters/bugs/base64.rb +5 -5
- data/spec/filters/bugs/basicobject.rb +16 -8
- data/spec/filters/bugs/bigdecimal.rb +161 -160
- data/spec/filters/bugs/binding.rb +10 -10
- data/spec/filters/bugs/class.rb +8 -8
- data/spec/filters/bugs/complex.rb +2 -1
- data/spec/filters/bugs/date.rb +79 -81
- data/spec/filters/bugs/datetime.rb +29 -29
- data/spec/filters/bugs/delegate.rb +1 -3
- data/spec/filters/bugs/encoding.rb +69 -69
- data/spec/filters/bugs/enumerable.rb +22 -20
- data/spec/filters/bugs/enumerator.rb +88 -85
- data/spec/filters/bugs/exception.rb +46 -40
- data/spec/filters/bugs/file.rb +32 -32
- data/spec/filters/bugs/float.rb +26 -21
- data/spec/filters/bugs/freeze.rb +88 -0
- data/spec/filters/bugs/hash.rb +39 -38
- data/spec/filters/bugs/integer.rb +57 -44
- data/spec/filters/bugs/io.rb +1 -1
- data/spec/filters/bugs/kernel.rb +349 -269
- data/spec/filters/bugs/language.rb +220 -188
- data/spec/filters/bugs/main.rb +5 -3
- data/spec/filters/bugs/marshal.rb +38 -38
- data/spec/filters/bugs/math.rb +2 -1
- data/spec/filters/bugs/method.rb +73 -62
- data/spec/filters/bugs/module.rb +163 -143
- data/spec/filters/bugs/numeric.rb +6 -6
- data/spec/filters/bugs/objectspace.rb +16 -16
- data/spec/filters/bugs/openstruct.rb +1 -1
- data/spec/filters/bugs/pack_unpack.rb +51 -51
- data/spec/filters/bugs/pathname.rb +7 -7
- data/spec/filters/bugs/proc.rb +63 -63
- data/spec/filters/bugs/random.rb +7 -6
- data/spec/filters/bugs/range.rb +12 -9
- data/spec/filters/bugs/rational.rb +8 -7
- data/spec/filters/bugs/regexp.rb +49 -48
- data/spec/filters/bugs/ruby-32.rb +56 -0
- data/spec/filters/bugs/set.rb +30 -30
- data/spec/filters/bugs/singleton.rb +4 -4
- data/spec/filters/bugs/string.rb +187 -99
- data/spec/filters/bugs/stringio.rb +7 -0
- data/spec/filters/bugs/stringscanner.rb +68 -68
- data/spec/filters/bugs/struct.rb +11 -9
- data/spec/filters/bugs/symbol.rb +1 -1
- data/spec/filters/bugs/time.rb +78 -63
- data/spec/filters/bugs/trace_point.rb +4 -4
- data/spec/filters/bugs/unboundmethod.rb +32 -17
- data/spec/filters/bugs/warnings.rb +8 -12
- data/spec/filters/unsupported/array.rb +24 -107
- data/spec/filters/unsupported/basicobject.rb +12 -12
- data/spec/filters/unsupported/bignum.rb +27 -52
- data/spec/filters/unsupported/class.rb +1 -2
- data/spec/filters/unsupported/delegator.rb +3 -3
- data/spec/filters/unsupported/enumerable.rb +2 -9
- data/spec/filters/unsupported/enumerator.rb +2 -11
- data/spec/filters/unsupported/file.rb +1 -1
- data/spec/filters/unsupported/float.rb +28 -47
- data/spec/filters/unsupported/hash.rb +8 -14
- data/spec/filters/unsupported/integer.rb +75 -91
- data/spec/filters/unsupported/kernel.rb +17 -35
- data/spec/filters/unsupported/language.rb +11 -19
- data/spec/filters/unsupported/marshal.rb +22 -41
- data/spec/filters/unsupported/matchdata.rb +28 -52
- data/spec/filters/unsupported/math.rb +1 -1
- data/spec/filters/unsupported/privacy.rb +229 -285
- data/spec/filters/unsupported/range.rb +1 -5
- data/spec/filters/unsupported/regexp.rb +40 -66
- data/spec/filters/unsupported/set.rb +2 -2
- data/spec/filters/unsupported/singleton.rb +4 -4
- data/spec/filters/unsupported/string.rb +305 -508
- data/spec/filters/unsupported/struct.rb +3 -4
- data/spec/filters/unsupported/symbol.rb +15 -18
- data/spec/filters/unsupported/thread.rb +1 -7
- data/spec/filters/unsupported/time.rb +159 -202
- data/spec/filters/unsupported/usage_of_files.rb +170 -259
- data/spec/lib/builder_spec.rb +4 -4
- data/spec/lib/rewriters/forward_args_spec.rb +32 -12
- data/spec/mspec-opal/runner.rb +2 -0
- data/spec/ruby_specs +4 -0
- data/stdlib/deno/base.rb +28 -0
- data/stdlib/deno/file.rb +340 -0
- data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
- data/stdlib/headless_browser/file.rb +15 -0
- data/stdlib/headless_browser.rb +4 -0
- data/stdlib/native.rb +1 -1
- data/stdlib/nodejs/file.rb +5 -0
- data/stdlib/opal/platform.rb +8 -6
- data/stdlib/opal-platform.rb +14 -8
- data/stdlib/set.rb +1 -258
- data/tasks/benchmarking.rake +62 -19
- data/tasks/performance.rake +1 -1
- data/tasks/testing.rake +5 -3
- data/test/nodejs/test_file.rb +29 -10
- data/test/opal/http_server.rb +28 -11
- data/test/opal/unsupported_and_bugs.rb +2 -1
- metadata +89 -50
- data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
- data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
- data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
- data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
- data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
- data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
- data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
- data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
- data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
- data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
- data/spec/filters/bugs/boolean.rb +0 -3
- data/spec/filters/bugs/matrix.rb +0 -3
- data/spec/filters/unsupported/fixnum.rb +0 -15
- data/spec/filters/unsupported/freeze.rb +0 -102
- data/spec/filters/unsupported/pathname.rb +0 -4
- data/spec/filters/unsupported/proc.rb +0 -4
- data/spec/filters/unsupported/random.rb +0 -5
- data/spec/filters/unsupported/taint.rb +0 -162
@@ -0,0 +1,350 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'rbconfig'
|
8
|
+
require 'opal/os'
|
9
|
+
require 'json'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'net/http'
|
12
|
+
|
13
|
+
module Opal
|
14
|
+
module CliRunners
|
15
|
+
class Firefox
|
16
|
+
SCRIPT_PATH = File.expand_path('firefox_cdp_interface.rb', __dir__).freeze
|
17
|
+
|
18
|
+
DEFAULT_CHROME_HOST = 'localhost'
|
19
|
+
DEFAULT_CHROME_PORT = 9333 # makes sure it doesn't accidentally connect to a lingering chrome
|
20
|
+
|
21
|
+
def self.call(data)
|
22
|
+
runner = new(data)
|
23
|
+
runner.run
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(data)
|
27
|
+
builder = data[:builder].call
|
28
|
+
options = data[:options]
|
29
|
+
argv = data[:argv]
|
30
|
+
|
31
|
+
if argv && argv.any?
|
32
|
+
warn "warning: ARGV is not supported by the Firefox runner #{argv.inspect}"
|
33
|
+
end
|
34
|
+
|
35
|
+
@output = options.fetch(:output, $stdout)
|
36
|
+
@builder = builder
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :output, :exit_status, :builder
|
40
|
+
|
41
|
+
def run
|
42
|
+
mktmpdir do |dir|
|
43
|
+
with_firefox_server do
|
44
|
+
prepare_files_in(dir)
|
45
|
+
|
46
|
+
env = {
|
47
|
+
'CHROME_HOST' => chrome_host,
|
48
|
+
'CHROME_PORT' => chrome_port.to_s,
|
49
|
+
'NODE_PATH' => File.join(__dir__, 'node_modules')
|
50
|
+
}
|
51
|
+
env['OPAL_CDP_EXT'] = builder.output_extension
|
52
|
+
|
53
|
+
cmd = [
|
54
|
+
RbConfig.ruby,
|
55
|
+
"#{__dir__}/../../../exe/opal",
|
56
|
+
'--no-exit',
|
57
|
+
'-I', __dir__,
|
58
|
+
'-r', 'source-map-support-node',
|
59
|
+
SCRIPT_PATH,
|
60
|
+
dir
|
61
|
+
]
|
62
|
+
|
63
|
+
Kernel.exec(env, *cmd)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def prepare_files_in(dir)
|
71
|
+
js = builder.to_s
|
72
|
+
map = builder.source_map.to_json
|
73
|
+
ext = builder.output_extension
|
74
|
+
module_type = ' type="module"' if builder.esm?
|
75
|
+
|
76
|
+
# CDP can't handle huge data passed to `addScriptToEvaluateOnLoad`
|
77
|
+
# https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/U5qyeX_ydBo
|
78
|
+
# The only way is to create temporary files and pass them to the browser.
|
79
|
+
File.binwrite("#{dir}/index.#{ext}", js)
|
80
|
+
File.binwrite("#{dir}/index.#{ext}.map", map)
|
81
|
+
File.binwrite("#{dir}/index.html", <<~HTML)
|
82
|
+
<!DOCTYPE html>
|
83
|
+
<html><head>
|
84
|
+
<meta charset='utf-8'>
|
85
|
+
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
86
|
+
<script>
|
87
|
+
window.opalheadlessfirefox = true;
|
88
|
+
</script>
|
89
|
+
</head><body>
|
90
|
+
<script src='/index.#{ext}'#{module_type}></script>
|
91
|
+
</body></html>
|
92
|
+
HTML
|
93
|
+
end
|
94
|
+
|
95
|
+
def chrome_host
|
96
|
+
ENV['CHROME_HOST'] || DEFAULT_CHROME_HOST
|
97
|
+
end
|
98
|
+
|
99
|
+
def chrome_port
|
100
|
+
ENV['CHROME_PORT'] || DEFAULT_CHROME_PORT
|
101
|
+
end
|
102
|
+
|
103
|
+
def with_firefox_server
|
104
|
+
if firefox_server_running?
|
105
|
+
yield
|
106
|
+
else
|
107
|
+
run_firefox_server { yield }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def run_firefox_server
|
112
|
+
raise 'Firefox server can be started only on localhost' if chrome_host != DEFAULT_CHROME_HOST
|
113
|
+
|
114
|
+
profile = mktmpprofile
|
115
|
+
|
116
|
+
# For options see https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/node/FirefoxLauncher.ts
|
117
|
+
firefox_server_cmd = %{#{OS.shellescape(firefox_executable)} \
|
118
|
+
--no-remote \
|
119
|
+
--profile #{profile} \
|
120
|
+
--headless \
|
121
|
+
--remote-debugging-port #{chrome_port} \
|
122
|
+
#{ENV['FIREFOX_OPTS']}}
|
123
|
+
|
124
|
+
firefox_pid = Process.spawn(firefox_server_cmd, in: OS.dev_null, out: OS.dev_null, err: OS.dev_null)
|
125
|
+
|
126
|
+
Timeout.timeout(30) do
|
127
|
+
loop do
|
128
|
+
break if firefox_server_running?
|
129
|
+
sleep 0.5
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
yield
|
134
|
+
rescue Timeout::Error
|
135
|
+
puts 'Failed to start firefox server'
|
136
|
+
puts 'Make sure that you have it installed and that its version is > 100'
|
137
|
+
exit(1)
|
138
|
+
ensure
|
139
|
+
if OS.windows? && firefox_pid
|
140
|
+
Process.kill('KILL', firefox_pid) unless system("taskkill /f /t /pid #{firefox_pid} >NUL 2>NUL")
|
141
|
+
elsif firefox_pid
|
142
|
+
Process.kill('HUP', firefox_pid)
|
143
|
+
end
|
144
|
+
FileUtils.rm_rf(profile) if profile
|
145
|
+
end
|
146
|
+
|
147
|
+
def firefox_server_running?
|
148
|
+
puts "Connecting to #{chrome_host}:#{chrome_port}..."
|
149
|
+
TCPSocket.new(chrome_host, chrome_port).close
|
150
|
+
# Firefox CDP endpoints are initialized after the CDP port is ready
|
151
|
+
# this causes first requests to fail
|
152
|
+
# wait until the CDP endpoints are ready
|
153
|
+
response = Net::HTTP.get_response('localhost', '/json/list', chrome_port)
|
154
|
+
raise Errno::EADDRNOTAVAIL if response.code != '200'
|
155
|
+
true
|
156
|
+
rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL
|
157
|
+
false
|
158
|
+
end
|
159
|
+
|
160
|
+
def firefox_executable
|
161
|
+
ENV['MOZILLA_FIREFOX_BINARY'] ||
|
162
|
+
if OS.windows?
|
163
|
+
[
|
164
|
+
'C:/Program Files/Mozilla Firefox/firefox.exe'
|
165
|
+
].each do |path|
|
166
|
+
next unless File.exist? path
|
167
|
+
return path
|
168
|
+
end
|
169
|
+
elsif OS.macos?
|
170
|
+
'/Applications/Firefox.app/Contents/MacOS/Firefox'
|
171
|
+
else
|
172
|
+
%w[
|
173
|
+
firefox
|
174
|
+
firefox-esr
|
175
|
+
].each do |name|
|
176
|
+
next unless system('sh', '-c', "command -v #{name.shellescape}", out: '/dev/null')
|
177
|
+
return name
|
178
|
+
end
|
179
|
+
raise 'Cannot find firefox executable'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def mktmpdir(&block)
|
184
|
+
Dir.mktmpdir('firefox-opal-', &block)
|
185
|
+
end
|
186
|
+
|
187
|
+
def mktmpprofile
|
188
|
+
profile = Dir.mktmpdir('firefox-opal-profile-')
|
189
|
+
default_prefs = {
|
190
|
+
# Make sure Shield doesn't hit the network.
|
191
|
+
'app.normandy.api_url': '',
|
192
|
+
# Disable Firefox old build background check
|
193
|
+
'app.update.checkInstallTime': false,
|
194
|
+
# Disable automatically upgrading Firefox
|
195
|
+
'app.update.disabledForTesting': true,
|
196
|
+
# Increase the APZ content response timeout to 1 minute
|
197
|
+
'apz.content_response_timeout': 60_000,
|
198
|
+
# Prevent various error message on the console
|
199
|
+
'browser.contentblocking.features.standard':
|
200
|
+
'-tp,tpPrivate,cookieBehavior0,-cm,-fp',
|
201
|
+
# Enable the dump function: which sends messages to the system console
|
202
|
+
# https://bugzilla.mozilla.org/show_bug.cgi?id=1543115
|
203
|
+
'browser.dom.window.dump.enabled': true,
|
204
|
+
# Disable topstories
|
205
|
+
'browser.newtabpage.activity-stream.feeds.system.topstories': false,
|
206
|
+
# Always display a blank page
|
207
|
+
'browser.newtabpage.enabled': false,
|
208
|
+
# Background thumbnails in particular cause grief: and disabling
|
209
|
+
# thumbnails in general cannot hurt
|
210
|
+
'browser.pagethumbnails.capturing_disabled': true,
|
211
|
+
# Disable safebrowsing components.
|
212
|
+
'browser.safebrowsing.blockedURIs.enabled': false,
|
213
|
+
'browser.safebrowsing.downloads.enabled': false,
|
214
|
+
'browser.safebrowsing.malware.enabled': false,
|
215
|
+
'browser.safebrowsing.passwords.enabled': false,
|
216
|
+
'browser.safebrowsing.phishing.enabled': false,
|
217
|
+
# Disable updates to search engines.
|
218
|
+
'browser.search.update': false,
|
219
|
+
# Do not restore the last open set of tabs if the browser has crashed
|
220
|
+
'browser.sessionstore.resume_from_crash': false,
|
221
|
+
# Skip check for default browser on startup
|
222
|
+
'browser.shell.checkDefaultBrowser': false,
|
223
|
+
# Disable newtabpage
|
224
|
+
'browser.startup.homepage': 'about:blank',
|
225
|
+
# Do not redirect user when a milstone upgrade of Firefox is detected
|
226
|
+
'browser.startup.homepage_override.mstone': 'ignore',
|
227
|
+
# Start with a blank page about:blank
|
228
|
+
'browser.startup.page': 0,
|
229
|
+
# Do not allow background tabs to be zombified on Android: otherwise for
|
230
|
+
# tests that open additional tabs: the test harness tab itself might get unloaded
|
231
|
+
'browser.tabs.disableBackgroundZombification': false,
|
232
|
+
# Do not warn when closing all other open tabs
|
233
|
+
'browser.tabs.warnOnCloseOtherTabs': false,
|
234
|
+
# Do not warn when multiple tabs will be opened
|
235
|
+
'browser.tabs.warnOnOpen': false,
|
236
|
+
# Disable the UI tour.
|
237
|
+
'browser.uitour.enabled': false,
|
238
|
+
# Turn off search suggestions in the location bar so as not to trigger
|
239
|
+
# network connections.
|
240
|
+
'browser.urlbar.suggest.searches': false,
|
241
|
+
# Disable first run splash page on Windows 10
|
242
|
+
'browser.usedOnWindows10.introURL': '',
|
243
|
+
# Do not warn on quitting Firefox
|
244
|
+
'browser.warnOnQuit': false,
|
245
|
+
# Defensively disable data reporting systems
|
246
|
+
'datareporting.healthreport.documentServerURI': 'http://localhost/dummy/healthreport/',
|
247
|
+
'datareporting.healthreport.logging.consoleEnabled': false,
|
248
|
+
'datareporting.healthreport.service.enabled': false,
|
249
|
+
'datareporting.healthreport.service.firstRun': false,
|
250
|
+
'datareporting.healthreport.uploadEnabled': false,
|
251
|
+
# Do not show datareporting policy notifications which can interfere with tests
|
252
|
+
'datareporting.policy.dataSubmissionEnabled': false,
|
253
|
+
'datareporting.policy.dataSubmissionPolicyBypassNotification': true,
|
254
|
+
# DevTools JSONViewer sometimes fails to load dependencies with its require.js.
|
255
|
+
# This doesn't affect Puppeteer but spams console (Bug 1424372)
|
256
|
+
'devtools.jsonview.enabled': false,
|
257
|
+
# Disable popup-blocker
|
258
|
+
'dom.disable_open_during_load': false,
|
259
|
+
# Enable the support for File object creation in the content process
|
260
|
+
# Required for |Page.setFileInputFiles| protocol method.
|
261
|
+
'dom.file.createInChild': true,
|
262
|
+
# Disable the ProcessHangMonitor
|
263
|
+
'dom.ipc.reportProcessHangs': false,
|
264
|
+
# Disable slow script dialogues
|
265
|
+
'dom.max_chrome_script_run_time': 0,
|
266
|
+
'dom.max_script_run_time': 0,
|
267
|
+
# Only load extensions from the application and user profile
|
268
|
+
# AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
|
269
|
+
'extensions.autoDisableScopes': 0,
|
270
|
+
'extensions.enabledScopes': 5,
|
271
|
+
# Disable metadata caching for installed add-ons by default
|
272
|
+
'extensions.getAddons.cache.enabled': false,
|
273
|
+
# Disable installing any distribution extensions or add-ons.
|
274
|
+
'extensions.installDistroAddons': false,
|
275
|
+
# Disabled screenshots extension
|
276
|
+
'extensions.screenshots.disabled': true,
|
277
|
+
# Turn off extension updates so they do not bother tests
|
278
|
+
'extensions.update.enabled': false,
|
279
|
+
# Turn off extension updates so they do not bother tests
|
280
|
+
'extensions.update.notifyUser': false,
|
281
|
+
# Make sure opening about:addons will not hit the network
|
282
|
+
'extensions.webservice.discoverURL': 'http://localhost/dummy/discoveryURL',
|
283
|
+
# Temporarily force disable BFCache in parent (https://bit.ly/bug-1732263)
|
284
|
+
'fission.bfcacheInParent': false,
|
285
|
+
# Force all web content to use a single content process
|
286
|
+
'fission.webContentIsolationStrategy': 0,
|
287
|
+
# Allow the application to have focus even it runs in the background
|
288
|
+
'focusmanager.testmode': true,
|
289
|
+
# Disable useragent updates
|
290
|
+
'general.useragent.updates.enabled': false,
|
291
|
+
# Always use network provider for geolocation tests so we bypass the
|
292
|
+
# macOS dialog raised by the corelocation provider
|
293
|
+
'geo.provider.testing': true,
|
294
|
+
# Do not scan Wifi
|
295
|
+
'geo.wifi.scan': false,
|
296
|
+
# No hang monitor
|
297
|
+
'hangmonitor.timeout': 0,
|
298
|
+
# Show chrome errors and warnings in the error console
|
299
|
+
'javascript.options.showInConsole': true,
|
300
|
+
# Disable download and usage of OpenH264: and Widevine plugins
|
301
|
+
'media.gmp-manager.updateEnabled': false,
|
302
|
+
# Prevent various error message on the console
|
303
|
+
'network.cookie.cookieBehavior': 0,
|
304
|
+
# Disable experimental feature that is only available in Nightly
|
305
|
+
'network.cookie.sameSite.laxByDefault': false,
|
306
|
+
# Do not prompt for temporary redirects
|
307
|
+
'network.http.prompt-temp-redirect': false,
|
308
|
+
# Disable speculative connections so they are not reported as leaking
|
309
|
+
# when they are hanging around
|
310
|
+
'network.http.speculative-parallel-limit': 0,
|
311
|
+
# Do not automatically switch between offline and online
|
312
|
+
'network.manage-offline-status': false,
|
313
|
+
# Make sure SNTP requests do not hit the network
|
314
|
+
'network.sntp.pools': 'localhost',
|
315
|
+
# Disable Flash.
|
316
|
+
'plugin.state.flash': 0,
|
317
|
+
'privacy.trackingprotection.enabled': false,
|
318
|
+
# Can be removed once Firefox 89 is no longer supported
|
319
|
+
# https://bugzilla.mozilla.org/show_bug.cgi?id=1710839
|
320
|
+
'remote.enabled': true,
|
321
|
+
# Don't do network connections for mitm priming
|
322
|
+
'security.certerrors.mitm.priming.enabled': false,
|
323
|
+
# Local documents have access to all other local documents,
|
324
|
+
# including directory listings
|
325
|
+
'security.fileuri.strict_origin_policy': false,
|
326
|
+
# Do not wait for the notification button security delay
|
327
|
+
'security.notification_enable_delay': 0,
|
328
|
+
# Ensure blocklist updates do not hit the network
|
329
|
+
'services.settings.server': 'http://localhost/dummy/blocklist/',
|
330
|
+
# Do not automatically fill sign-in forms with known usernames and passwords
|
331
|
+
'signon.autofillForms': false,
|
332
|
+
# Disable password capture, so that tests that include forms are not
|
333
|
+
# influenced by the presence of the persistent doorhanger notification
|
334
|
+
'signon.rememberSignons': false,
|
335
|
+
# Disable first-run welcome page
|
336
|
+
'startup.homepage_welcome_url': 'about:blank',
|
337
|
+
# Disable first-run welcome page
|
338
|
+
'startup.homepage_welcome_url.additional': '',
|
339
|
+
# Disable browser animations (tabs, fullscreen, sliding alerts)
|
340
|
+
'toolkit.cosmeticAnimations.enabled': false,
|
341
|
+
# Prevent starting into safe mode after application crashes
|
342
|
+
'toolkit.startup.max_resumed_crashes': -1,
|
343
|
+
}
|
344
|
+
prefs = default_prefs.map { |key, value| "user_pref(\"#{key}\", #{JSON.dump(value)});" }
|
345
|
+
File.binwrite(profile + '/prefs.js', prefs.join("\n"))
|
346
|
+
profile
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This script I converted into Opal, so that I don't have to write
|
4
|
+
# buffer handling again. We have gets, Node has nothing close to it,
|
5
|
+
# even async.
|
6
|
+
# For CDP see docs/cdp_common.(md|json)
|
7
|
+
|
8
|
+
require 'opal/platform'
|
9
|
+
require 'nodejs/env'
|
10
|
+
|
11
|
+
%x{
|
12
|
+
var CDP = require("chrome-remote-interface");
|
13
|
+
var fs = require("fs");
|
14
|
+
var http = require("http");
|
15
|
+
|
16
|
+
var dir = #{ARGV.last};
|
17
|
+
// var ext = #{ENV['OPAL_CDP_EXT']}; // not used at the moment
|
18
|
+
var offset; // port offset for http server, depending on number of targets
|
19
|
+
|
20
|
+
// even though its Firefox, "chrome-remote-interface" expects CHROME_* vars
|
21
|
+
var options = {
|
22
|
+
host: #{ENV['CHROME_HOST'] || 'localhost'},
|
23
|
+
port: parseInt(#{ENV['CHROME_PORT'] || '9333'}) // makes sure it doesn't accidentally connect to a lingering chrome
|
24
|
+
};
|
25
|
+
|
26
|
+
// support functions
|
27
|
+
|
28
|
+
function perror(error) { console.error(error); }
|
29
|
+
|
30
|
+
var exiting = false;
|
31
|
+
|
32
|
+
function shutdown(exit_code) {
|
33
|
+
if (exiting) { return Promise.resolve(); }
|
34
|
+
exiting = true;
|
35
|
+
cdp_client.Target.closeTarget(target_id); // Promise doesn't get resolved
|
36
|
+
server.close();
|
37
|
+
process.exit(exit_code);
|
38
|
+
};
|
39
|
+
|
40
|
+
// simple HTTP server to deliver page, scripts to, and trigger commands from browser
|
41
|
+
|
42
|
+
function not_found(res) {
|
43
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
44
|
+
res.end("NOT FOUND");
|
45
|
+
}
|
46
|
+
|
47
|
+
function response_ok(res) {
|
48
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
49
|
+
res.end("OK");
|
50
|
+
}
|
51
|
+
|
52
|
+
function handle_post(req, res, fun) {
|
53
|
+
var data = "";
|
54
|
+
req.on('data', function(chunk) {
|
55
|
+
data += chunk;
|
56
|
+
})
|
57
|
+
req.on('end', function() {
|
58
|
+
var obj = JSON.parse(data);
|
59
|
+
fun.call(this, obj);
|
60
|
+
});
|
61
|
+
}
|
62
|
+
|
63
|
+
var server = http.createServer(function(req, res) {
|
64
|
+
if (req.method === "GET") {
|
65
|
+
var path = dir + '/' + req.url.slice(1);
|
66
|
+
if (path.includes('..') || !fs.existsSync(path)) {
|
67
|
+
not_found(res);
|
68
|
+
} else {
|
69
|
+
var content_type;
|
70
|
+
if (path.endsWith(".html")) {
|
71
|
+
content_type = "text/html"
|
72
|
+
} else if (path.endsWith(".map")) {
|
73
|
+
content_type = "application/json"
|
74
|
+
} else {
|
75
|
+
content_type = "application/javascript"
|
76
|
+
}
|
77
|
+
res.writeHead(200, { "Content-Type": content_type });
|
78
|
+
res.end(fs.readFileSync(path));
|
79
|
+
}
|
80
|
+
} else if (req.method === "POST") {
|
81
|
+
if (req.url === "/File.write") {
|
82
|
+
// totally insecure on purpose
|
83
|
+
handle_post(req, res, function(obj) {
|
84
|
+
fs.writeFileSync(obj.filename, obj.data);
|
85
|
+
response_ok(res);
|
86
|
+
});
|
87
|
+
} else {
|
88
|
+
not_found(res);
|
89
|
+
}
|
90
|
+
} else {
|
91
|
+
not_found(res);
|
92
|
+
}
|
93
|
+
});
|
94
|
+
|
95
|
+
// actual CDP code
|
96
|
+
|
97
|
+
CDP.List(options, function(err, targets) {
|
98
|
+
offset = targets ? targets.length + 1 : 1;
|
99
|
+
|
100
|
+
return CDP(options, function(browser_client) {
|
101
|
+
|
102
|
+
server.listen({port: offset + options.port, host: options.host });
|
103
|
+
|
104
|
+
browser_client.Target.createTarget({url: "about:blank"}).then(function(target) {
|
105
|
+
target_id = target;
|
106
|
+
options.target = target_id.targetId;
|
107
|
+
|
108
|
+
CDP(options, function(client) {
|
109
|
+
cdp_client = client;
|
110
|
+
|
111
|
+
var Log = client.Log,
|
112
|
+
Page = client.Page,
|
113
|
+
Runtime = client.Runtime;
|
114
|
+
|
115
|
+
// enable used CDP domains
|
116
|
+
Promise.all([
|
117
|
+
Log.enable(),
|
118
|
+
Page.enable(),
|
119
|
+
Runtime.enable()
|
120
|
+
]).then(function() {
|
121
|
+
|
122
|
+
// receive and handle all kinds of log and console messages
|
123
|
+
Log.entryAdded(function(entry) {
|
124
|
+
process.stdout.write(entry.entry.level + ': ' + entry.entry.text + "\n");
|
125
|
+
});
|
126
|
+
|
127
|
+
Runtime.consoleAPICalled(function(entry) {
|
128
|
+
var args = entry.args;
|
129
|
+
var stack = null;
|
130
|
+
var i, arg, frame, value;
|
131
|
+
|
132
|
+
// output actual message
|
133
|
+
for(i = 0; i < args.length; i++) {
|
134
|
+
arg = args[i];
|
135
|
+
if (arg.type === "string") { value = arg.value; }
|
136
|
+
else { value = JSON.stringify(arg); }
|
137
|
+
process.stdout.write(value);
|
138
|
+
}
|
139
|
+
|
140
|
+
if (entry.stackTrace && entry.stackTrace.callFrames) { stack = entry.stackTrace.callFrames; }
|
141
|
+
if (entry.type === "error" && stack) {
|
142
|
+
// print full stack for errors
|
143
|
+
process.stdout.write("\n");
|
144
|
+
for(i = 0; i < stack.length; i++) {
|
145
|
+
frame = stack[i];
|
146
|
+
if (frame) {
|
147
|
+
value = frame.url + ':' + frame.lineNumer + ':' + frame.columnNumber + '\n';
|
148
|
+
process.stdout.write(value);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
});
|
153
|
+
|
154
|
+
Runtime.exceptionThrown(function(exception) {
|
155
|
+
var ex = exception.exceptionDetails;
|
156
|
+
var stack = ex.stackTrace.callFrames;
|
157
|
+
var fr;
|
158
|
+
perror(ex.url + ':' + ex.lineNumber + ':' + ex.columnNumber + ': ' + ex.text);
|
159
|
+
for (var i = 0; i < stack.length; i++) {
|
160
|
+
fr = stack[i];
|
161
|
+
perror(fr.url + ':' + fr.lineNumber + ':' + fr.columnNumber + ': in ' + fr.functionName);
|
162
|
+
}
|
163
|
+
return shutdown(1);
|
164
|
+
});
|
165
|
+
|
166
|
+
Page.javascriptDialogOpening((dialog) => {
|
167
|
+
#{
|
168
|
+
if `dialog.type` == 'prompt'
|
169
|
+
message = gets&.chomp
|
170
|
+
if message
|
171
|
+
`Page.handleJavaScriptDialog({accept: true, promptText: #{message}})`
|
172
|
+
else
|
173
|
+
`Page.handleJavaScriptDialog({accept: false})`
|
174
|
+
end
|
175
|
+
elsif `dialog.type` == 'alert' && `dialog.message` == 'opalheadlessbrowserexit'
|
176
|
+
# A special case of an alert with a magic string "opalheadlessbrowserexit".
|
177
|
+
# This denotes that `Kernel#exit` has been called. We would have rather used
|
178
|
+
# an exception here, but they don't bubble sometimes.
|
179
|
+
%x{
|
180
|
+
Page.handleJavaScriptDialog({accept: true});
|
181
|
+
Runtime.evaluate({ expression: "window.OPAL_EXIT_CODE" }).then(function(output) {
|
182
|
+
var exit_code = 0;
|
183
|
+
if (typeof(output.result) !== "undefined" && output.result.type === "number") {
|
184
|
+
exit_code = output.result.value;
|
185
|
+
}
|
186
|
+
return shutdown(exit_code);
|
187
|
+
});
|
188
|
+
}
|
189
|
+
end
|
190
|
+
}
|
191
|
+
});
|
192
|
+
|
193
|
+
Page.loadEventFired(() => {
|
194
|
+
Runtime.evaluate({ expression: "window.OPAL_EXIT_CODE" }).then(function(output) {
|
195
|
+
if (typeof(output.result) !== "undefined" && output.result.type === "number") {
|
196
|
+
return shutdown(output.result.value);
|
197
|
+
} else if (typeof(output.result) !== "undefined" && output.result.type === "string" && output.result.value === "noexit") {
|
198
|
+
// do nothing, we have headless chrome support enabled and there are most probably async events awaiting
|
199
|
+
} else {
|
200
|
+
return shutdown(0);
|
201
|
+
}
|
202
|
+
})
|
203
|
+
});
|
204
|
+
|
205
|
+
Page.navigate({ url: "http://localhost:" + (offset + options.port).toString() + "/index.html" })
|
206
|
+
});
|
207
|
+
});
|
208
|
+
});
|
209
|
+
});
|
210
|
+
});
|
211
|
+
}
|
212
|
+
# end of code (marker to help see if brackets match above)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
@ECHO off
|
2
|
+
GOTO start
|
3
|
+
:find_dp0
|
4
|
+
SET dp0=%~dp0
|
5
|
+
EXIT /b
|
6
|
+
:start
|
7
|
+
SETLOCAL
|
8
|
+
CALL :find_dp0
|
9
|
+
|
10
|
+
IF EXIST "%dp0%\node.exe" (
|
11
|
+
SET "_prog=%dp0%\node.exe"
|
12
|
+
) ELSE (
|
13
|
+
SET "_prog=node"
|
14
|
+
SET PATHEXT=%PATHEXT:;.JS;=;%
|
15
|
+
)
|
16
|
+
|
17
|
+
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\chrome-remote-interface\bin\client.js" %*
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env pwsh
|
2
|
+
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
3
|
+
|
4
|
+
$exe=""
|
5
|
+
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
6
|
+
# Fix case when both the Windows and Linux builds of Node
|
7
|
+
# are installed in the same directory
|
8
|
+
$exe=".exe"
|
9
|
+
}
|
10
|
+
$ret=0
|
11
|
+
if (Test-Path "$basedir/node$exe") {
|
12
|
+
# Support pipeline input
|
13
|
+
if ($MyInvocation.ExpectingInput) {
|
14
|
+
$input | & "$basedir/node$exe" "$basedir/../chrome-remote-interface/bin/client.js" $args
|
15
|
+
} else {
|
16
|
+
& "$basedir/node$exe" "$basedir/../chrome-remote-interface/bin/client.js" $args
|
17
|
+
}
|
18
|
+
$ret=$LASTEXITCODE
|
19
|
+
} else {
|
20
|
+
# Support pipeline input
|
21
|
+
if ($MyInvocation.ExpectingInput) {
|
22
|
+
$input | & "node$exe" "$basedir/../chrome-remote-interface/bin/client.js" $args
|
23
|
+
} else {
|
24
|
+
& "node$exe" "$basedir/../chrome-remote-interface/bin/client.js" $args
|
25
|
+
}
|
26
|
+
$ret=$LASTEXITCODE
|
27
|
+
}
|
28
|
+
exit $ret
|
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"name": "cli_runners",
|
3
|
+
"lockfileVersion": 2,
|
4
|
+
"requires": true,
|
5
|
+
"packages": {
|
6
|
+
"node_modules/chrome-remote-interface": {
|
7
|
+
"version": "0.31.3",
|
8
|
+
"license": "MIT",
|
9
|
+
"dependencies": {
|
10
|
+
"commander": "2.11.x",
|
11
|
+
"ws": "^7.2.0"
|
12
|
+
},
|
13
|
+
"bin": {
|
14
|
+
"chrome-remote-interface": "bin/client.js"
|
15
|
+
}
|
16
|
+
},
|
17
|
+
"node_modules/commander": {
|
18
|
+
"version": "2.11.0",
|
19
|
+
"license": "MIT"
|
20
|
+
},
|
21
|
+
"node_modules/ws": {
|
22
|
+
"version": "7.5.9",
|
23
|
+
"license": "MIT",
|
24
|
+
"engines": {
|
25
|
+
"node": ">=8.3.0"
|
26
|
+
},
|
27
|
+
"peerDependencies": {
|
28
|
+
"bufferutil": "^4.0.1",
|
29
|
+
"utf-8-validate": "^5.0.2"
|
30
|
+
},
|
31
|
+
"peerDependenciesMeta": {
|
32
|
+
"bufferutil": {
|
33
|
+
"optional": true
|
34
|
+
},
|
35
|
+
"utf-8-validate": {
|
36
|
+
"optional": true
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2021 Andrea Cardaci <cyrus.and@gmail.com>
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
4
|
this software and associated documentation files (the "Software"), to deal in
|