minibidi 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +8 -0
- data/lib/minibidi/browser.rb +5 -6
- data/lib/minibidi/browsing_context.rb +61 -4
- data/lib/minibidi/firefox_launcher.rb +55 -24
- data/lib/minibidi/input.rb +298 -0
- data/lib/minibidi/keyboard_action_queue.rb +23 -0
- data/lib/minibidi/mouse_action_queue.rb +29 -0
- data/lib/minibidi/realm.rb +76 -0
- data/lib/minibidi/version.rb +1 -1
- data/lib/minibidi.rb +4 -1
- data/minibidi.gemspec +0 -4
- metadata +7 -60
- data/lib/minibidi/firefox.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74c2b07cd5b5a34c85ca23e6127e5c09b23af0814ab91339e8ffc3c18f2aaec4
|
4
|
+
data.tar.gz: 1de813a159df9930ffa27dc69b3c68426faebbe3604dc631b03fb899ea39a7ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2be5e98fd4c177b4b087a3ca8af9efa5110386df709de76c61b477463b44fb6a797c13c002f270fba542a8b548a226dbae8dfa86000d26cc4b93fbd4bd486025
|
7
|
+
data.tar.gz: 185322a8c0ee29af4e7fd1a0663dc89746ab7203163c5a9f27bd17158b17a009d7f7dde2b910163f950c678a4f66e1a18133fdd69f7d40353d69df32c21f2e30
|
data/Gemfile
CHANGED
@@ -4,3 +4,11 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in minibidi.gemspec
|
6
6
|
gemspec
|
7
|
+
|
8
|
+
gem "bundler"
|
9
|
+
gem "minitest"
|
10
|
+
gem "pry"
|
11
|
+
gem "rack-test_server"
|
12
|
+
gem "rake"
|
13
|
+
gem "sinatra", require: false
|
14
|
+
gem 'webrick'
|
data/lib/minibidi/browser.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'async'
|
2
2
|
require 'async/variable'
|
3
|
+
require 'json'
|
3
4
|
|
4
5
|
module Minibidi
|
5
6
|
class Browser
|
@@ -8,10 +9,8 @@ module Minibidi
|
|
8
9
|
@debug_protocol = %w[1 true].include?(ENV['DEBUG'])
|
9
10
|
|
10
11
|
Async do
|
11
|
-
while
|
12
|
-
|
13
|
-
handle_received_message_from_websocket(message.to_h)
|
14
|
-
end
|
12
|
+
while message = async_websocket_connection.read
|
13
|
+
handle_received_message_from_websocket(message.to_h)
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
@@ -79,7 +78,7 @@ module Minibidi
|
|
79
78
|
|
80
79
|
def send_message_to_websocket(payload)
|
81
80
|
debug_print_send(payload)
|
82
|
-
message = Protocol::WebSocket::
|
81
|
+
message = Protocol::WebSocket::TextMessage.new(JSON.generate(payload))
|
83
82
|
message.send(@websocket)
|
84
83
|
@websocket.flush
|
85
84
|
end
|
@@ -105,7 +104,7 @@ module Minibidi
|
|
105
104
|
new(
|
106
105
|
type: payload[:error],
|
107
106
|
message: payload[:message],
|
108
|
-
stacktrace: payload[:stacktrace].split("\n"),
|
107
|
+
stacktrace: (payload[:stacktrace] || '').split("\n"),
|
109
108
|
)
|
110
109
|
end
|
111
110
|
|
@@ -7,19 +7,76 @@ module Minibidi
|
|
7
7
|
@context_id = context_id
|
8
8
|
end
|
9
9
|
|
10
|
-
def navigate(url)
|
11
|
-
bidi_call_async('browsingContext.navigate', {
|
10
|
+
def navigate(url, wait: :interactive)
|
11
|
+
bidi_call_async('browsingContext.navigate', {
|
12
|
+
url: url,
|
13
|
+
wait: wait,
|
14
|
+
}.compact).wait
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
17
|
+
def realms
|
18
|
+
result = bidi_call_async('script.getRealms').wait
|
19
|
+
result[:realms].map do |realm|
|
20
|
+
Realm.new(
|
21
|
+
browsing_context: self,
|
22
|
+
id: realm[:realm],
|
23
|
+
origin: realm[:origin],
|
24
|
+
type: realm[:type],
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_realm
|
30
|
+
realms.find { |realm| realm.type == 'window' }
|
31
|
+
end
|
32
|
+
|
33
|
+
def input
|
34
|
+
Input.new(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def activate
|
38
|
+
bidi_call_async('browsingContext.activate').wait
|
39
|
+
end
|
40
|
+
|
41
|
+
def capture_screenshot(origin: nil, format: nil, clip: nil)
|
15
42
|
result = bidi_call_async('browsingContext.captureScreenshot', {
|
16
43
|
origin: origin,
|
17
44
|
format: format,
|
18
|
-
|
45
|
+
clip: clip,
|
46
|
+
}.compact).wait
|
19
47
|
|
20
48
|
Base64.strict_decode64(result[:data])
|
21
49
|
end
|
22
50
|
|
51
|
+
def close(prompt_unload: nil)
|
52
|
+
bidi_call_async('browsingContext.close', {
|
53
|
+
promptUnload: prompt_unload,
|
54
|
+
}.compact).wait
|
55
|
+
end
|
56
|
+
|
57
|
+
def reload(ignore_cache: nil, wait: nil)
|
58
|
+
bidi_call_async('browsingContext.reload', {
|
59
|
+
ignoreCache: ignore_cache,
|
60
|
+
wait: wait,
|
61
|
+
}.compact).wait
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_viewport(width:, height:, device_pixel_ratio: nil)
|
65
|
+
bidi_call_async('browsingContext.setViewport', {
|
66
|
+
viewport: {
|
67
|
+
width: width,
|
68
|
+
height: height,
|
69
|
+
},
|
70
|
+
devicePixelRatio: device_pixel_ratio,
|
71
|
+
}.compact).wait
|
72
|
+
end
|
73
|
+
|
74
|
+
def traverse_history(delta)
|
75
|
+
bidi_call_async('browsingContext.traverseHistory', {
|
76
|
+
delta: delta,
|
77
|
+
}).wait
|
78
|
+
end
|
79
|
+
|
23
80
|
private
|
24
81
|
|
25
82
|
def bidi_call_async(method_, params = {})
|
@@ -2,41 +2,72 @@ require 'async'
|
|
2
2
|
require 'async/http/endpoint'
|
3
3
|
require 'async/websocket'
|
4
4
|
require 'open3'
|
5
|
-
require 'protocol/websocket/json_message'
|
6
5
|
require 'timeout'
|
7
6
|
require 'tmpdir'
|
8
7
|
|
9
8
|
module Minibidi
|
9
|
+
class Firefox
|
10
|
+
def self.launch(&block)
|
11
|
+
FirefoxLauncher.new.launch(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.discover_binary
|
15
|
+
# check if macOS
|
16
|
+
if RUBY_PLATFORM =~ /darwin/
|
17
|
+
if File.exist?("/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox")
|
18
|
+
return "/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox"
|
19
|
+
end
|
20
|
+
elsif RUBY_PLATFORM =~ /linux/
|
21
|
+
if File.exist?("/usr/bin/firefox-devedition")
|
22
|
+
return "/usr/bin/firefox-devedition"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
raise LaunchError, 'Firefox Developer Edition is not found. Please install it from https://www.mozilla.org/firefox/developer/'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
10
30
|
class FirefoxLauncher
|
11
31
|
def launch(&block)
|
12
32
|
raise ArgumentError, "block is required" unless block
|
13
33
|
|
14
|
-
Dir.mktmpdir('minibidi')
|
15
|
-
|
34
|
+
tmp = Dir.mktmpdir('minibidi')
|
35
|
+
create_user_profile(tmp)
|
36
|
+
args = [
|
37
|
+
"--remote-debugging-port=0",
|
38
|
+
"--profile",
|
39
|
+
tmp,
|
40
|
+
"--no-remote",
|
41
|
+
]
|
42
|
+
if RUBY_PLATFORM =~ /darwin/
|
43
|
+
args << "--foreground"
|
44
|
+
end
|
45
|
+
if %w[true 1].include?(ENV['HEADLESS'])
|
46
|
+
args << "--headless"
|
47
|
+
end
|
16
48
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
49
|
+
proc = BrowserProcess.new(
|
50
|
+
Firefox.discover_binary,
|
51
|
+
*args,
|
52
|
+
"about:blank",
|
53
|
+
)
|
54
|
+
at_exit do
|
55
|
+
proc.kill
|
56
|
+
FileUtils.remove_entry(tmp)
|
57
|
+
end
|
58
|
+
trap(:INT) { proc.kill ; exit 130 }
|
59
|
+
trap(:TERM) { proc.kill ; proc.dispose }
|
60
|
+
trap(:HUP) { proc.kill ; proc.dispose }
|
29
61
|
|
30
|
-
|
62
|
+
endpoint = wait_for_ws_endpoint(proc)
|
31
63
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
64
|
+
Async::Reactor.run do
|
65
|
+
Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse("#{endpoint}/session")) do |async_websocket_connection|
|
66
|
+
browser = Browser.new(async_websocket_connection)
|
67
|
+
begin
|
68
|
+
block.call(browser)
|
69
|
+
ensure
|
70
|
+
browser.close
|
40
71
|
end
|
41
72
|
end
|
42
73
|
end
|
@@ -0,0 +1,298 @@
|
|
1
|
+
module Minibidi
|
2
|
+
class Input
|
3
|
+
def initialize(browser_context)
|
4
|
+
@browser_context = browser_context
|
5
|
+
end
|
6
|
+
|
7
|
+
def click(x:, y:, delay: 50)
|
8
|
+
perform_mouse_actions do |q|
|
9
|
+
q.pointer_move(x: x.to_i, y: y.to_i)
|
10
|
+
q.pointer_down(button: 0)
|
11
|
+
q.pause(delay)
|
12
|
+
q.pointer_up(button: 0)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def while_pressing_key(key, &block)
|
17
|
+
value = convert_key(key)
|
18
|
+
perform_keyboard_actions do |q|
|
19
|
+
q.key_down(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
block.call
|
24
|
+
ensure
|
25
|
+
perform_keyboard_actions do |q|
|
26
|
+
q.key_up(value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def press_key(key, delay: 50)
|
32
|
+
value = convert_key(key)
|
33
|
+
perform_keyboard_actions do |q|
|
34
|
+
q.key_down(value)
|
35
|
+
q.pause(delay)
|
36
|
+
q.key_up(value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_text(text, delay: 50)
|
41
|
+
text.each_char do |char|
|
42
|
+
perform_keyboard_actions do |q|
|
43
|
+
q.key_down(char)
|
44
|
+
q.pause(delay / 2)
|
45
|
+
q.key_up(char)
|
46
|
+
q.pause(delay / 2)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def perform_actions(actions)
|
54
|
+
@browser_context.send(:bidi_call_async, 'input.performActions', {
|
55
|
+
actions: actions,
|
56
|
+
}).wait
|
57
|
+
end
|
58
|
+
|
59
|
+
def perform_keyboard_actions(&block)
|
60
|
+
q = KeyboardActionQueue.new
|
61
|
+
block.call(q)
|
62
|
+
perform_actions([{
|
63
|
+
type: 'key',
|
64
|
+
id: '__minibidi_keyboard',
|
65
|
+
actions: q.to_a,
|
66
|
+
}])
|
67
|
+
end
|
68
|
+
|
69
|
+
def perform_mouse_actions(&block)
|
70
|
+
q = MouseActionQueue.new
|
71
|
+
block.call(q)
|
72
|
+
perform_actions([{
|
73
|
+
type: 'pointer',
|
74
|
+
id: '__minibidi_mouse',
|
75
|
+
actions: q.to_a,
|
76
|
+
}])
|
77
|
+
end
|
78
|
+
|
79
|
+
def macos?
|
80
|
+
RUBY_PLATFORM =~ /darwin/
|
81
|
+
end
|
82
|
+
|
83
|
+
# ref: https://github.com/puppeteer/puppeteer/blob/puppeteer-v23.5.3/packages/puppeteer-core/src/bidi/Input.ts#L52
|
84
|
+
# Converted using ChatGPT 4o
|
85
|
+
def convert_key(key)
|
86
|
+
return key if key.length == 1
|
87
|
+
|
88
|
+
case key
|
89
|
+
when 'Cancel'
|
90
|
+
"\uE001"
|
91
|
+
when 'Help'
|
92
|
+
"\uE002"
|
93
|
+
when 'Backspace'
|
94
|
+
"\uE003"
|
95
|
+
when 'Tab'
|
96
|
+
"\uE004"
|
97
|
+
when 'Clear'
|
98
|
+
"\uE005"
|
99
|
+
when 'Enter'
|
100
|
+
"\uE007"
|
101
|
+
when 'Shift', 'ShiftLeft'
|
102
|
+
"\uE008"
|
103
|
+
when 'Control', 'ControlLeft', 'Ctrl'
|
104
|
+
"\uE009"
|
105
|
+
when 'ControlOrMeta', 'CtrlOrMeta'
|
106
|
+
macos? ? "\uE03D" : "\uE009"
|
107
|
+
when 'Alt', 'AltLeft'
|
108
|
+
"\uE00A"
|
109
|
+
when 'Pause'
|
110
|
+
"\uE00B"
|
111
|
+
when 'Escape'
|
112
|
+
"\uE00C"
|
113
|
+
when 'PageUp'
|
114
|
+
"\uE00E"
|
115
|
+
when 'PageDown'
|
116
|
+
"\uE00F"
|
117
|
+
when 'End'
|
118
|
+
"\uE010"
|
119
|
+
when 'Home'
|
120
|
+
"\uE011"
|
121
|
+
when 'ArrowLeft'
|
122
|
+
"\uE012"
|
123
|
+
when 'ArrowUp'
|
124
|
+
"\uE013"
|
125
|
+
when 'ArrowRight'
|
126
|
+
"\uE014"
|
127
|
+
when 'ArrowDown'
|
128
|
+
"\uE015"
|
129
|
+
when 'Insert'
|
130
|
+
"\uE016"
|
131
|
+
when 'Delete'
|
132
|
+
"\uE017"
|
133
|
+
when 'NumpadEqual'
|
134
|
+
"\uE019"
|
135
|
+
when 'Numpad0'
|
136
|
+
"\uE01A"
|
137
|
+
when 'Numpad1'
|
138
|
+
"\uE01B"
|
139
|
+
when 'Numpad2'
|
140
|
+
"\uE01C"
|
141
|
+
when 'Numpad3'
|
142
|
+
"\uE01D"
|
143
|
+
when 'Numpad4'
|
144
|
+
"\uE01E"
|
145
|
+
when 'Numpad5'
|
146
|
+
"\uE01F"
|
147
|
+
when 'Numpad6'
|
148
|
+
"\uE020"
|
149
|
+
when 'Numpad7'
|
150
|
+
"\uE021"
|
151
|
+
when 'Numpad8'
|
152
|
+
"\uE022"
|
153
|
+
when 'Numpad9'
|
154
|
+
"\uE023"
|
155
|
+
when 'NumpadMultiply'
|
156
|
+
"\uE024"
|
157
|
+
when 'NumpadAdd'
|
158
|
+
"\uE025"
|
159
|
+
when 'NumpadSubtract'
|
160
|
+
"\uE027"
|
161
|
+
when 'NumpadDecimal'
|
162
|
+
"\uE028"
|
163
|
+
when 'NumpadDivide'
|
164
|
+
"\uE029"
|
165
|
+
when 'F1'
|
166
|
+
"\uE031"
|
167
|
+
when 'F2'
|
168
|
+
"\uE032"
|
169
|
+
when 'F3'
|
170
|
+
"\uE033"
|
171
|
+
when 'F4'
|
172
|
+
"\uE034"
|
173
|
+
when 'F5'
|
174
|
+
"\uE035"
|
175
|
+
when 'F6'
|
176
|
+
"\uE036"
|
177
|
+
when 'F7'
|
178
|
+
"\uE037"
|
179
|
+
when 'F8'
|
180
|
+
"\uE038"
|
181
|
+
when 'F9'
|
182
|
+
"\uE039"
|
183
|
+
when 'F10'
|
184
|
+
"\uE03A"
|
185
|
+
when 'F11'
|
186
|
+
"\uE03B"
|
187
|
+
when 'F12'
|
188
|
+
"\uE03C"
|
189
|
+
when 'Meta', 'MetaLeft'
|
190
|
+
"\uE03D"
|
191
|
+
when 'ShiftRight'
|
192
|
+
"\uE050"
|
193
|
+
when 'ControlRight'
|
194
|
+
"\uE051"
|
195
|
+
when 'AltRight'
|
196
|
+
"\uE052"
|
197
|
+
when 'MetaRight'
|
198
|
+
"\uE053"
|
199
|
+
when 'Digit0'
|
200
|
+
'0'
|
201
|
+
when 'Digit1'
|
202
|
+
'1'
|
203
|
+
when 'Digit2'
|
204
|
+
'2'
|
205
|
+
when 'Digit3'
|
206
|
+
'3'
|
207
|
+
when 'Digit4'
|
208
|
+
'4'
|
209
|
+
when 'Digit5'
|
210
|
+
'5'
|
211
|
+
when 'Digit6'
|
212
|
+
'6'
|
213
|
+
when 'Digit7'
|
214
|
+
'7'
|
215
|
+
when 'Digit8'
|
216
|
+
'8'
|
217
|
+
when 'Digit9'
|
218
|
+
'9'
|
219
|
+
when 'KeyA'
|
220
|
+
'a'
|
221
|
+
when 'KeyB'
|
222
|
+
'b'
|
223
|
+
when 'KeyC'
|
224
|
+
'c'
|
225
|
+
when 'KeyD'
|
226
|
+
'd'
|
227
|
+
when 'KeyE'
|
228
|
+
'e'
|
229
|
+
when 'KeyF'
|
230
|
+
'f'
|
231
|
+
when 'KeyG'
|
232
|
+
'g'
|
233
|
+
when 'KeyH'
|
234
|
+
'h'
|
235
|
+
when 'KeyI'
|
236
|
+
'i'
|
237
|
+
when 'KeyJ'
|
238
|
+
'j'
|
239
|
+
when 'KeyK'
|
240
|
+
'k'
|
241
|
+
when 'KeyL'
|
242
|
+
'l'
|
243
|
+
when 'KeyM'
|
244
|
+
'm'
|
245
|
+
when 'KeyN'
|
246
|
+
'n'
|
247
|
+
when 'KeyO'
|
248
|
+
'o'
|
249
|
+
when 'KeyP'
|
250
|
+
'p'
|
251
|
+
when 'KeyQ'
|
252
|
+
'q'
|
253
|
+
when 'KeyR'
|
254
|
+
'r'
|
255
|
+
when 'KeyS'
|
256
|
+
's'
|
257
|
+
when 'KeyT'
|
258
|
+
't'
|
259
|
+
when 'KeyU'
|
260
|
+
'u'
|
261
|
+
when 'KeyV'
|
262
|
+
'v'
|
263
|
+
when 'KeyW'
|
264
|
+
'w'
|
265
|
+
when 'KeyX'
|
266
|
+
'x'
|
267
|
+
when 'KeyY'
|
268
|
+
'y'
|
269
|
+
when 'KeyZ'
|
270
|
+
'z'
|
271
|
+
when 'Semicolon'
|
272
|
+
';'
|
273
|
+
when 'Equal'
|
274
|
+
'='
|
275
|
+
when 'Comma'
|
276
|
+
','
|
277
|
+
when 'Minus'
|
278
|
+
'-'
|
279
|
+
when 'Period'
|
280
|
+
'.'
|
281
|
+
when 'Slash'
|
282
|
+
'/'
|
283
|
+
when 'Backquote'
|
284
|
+
'`'
|
285
|
+
when 'BracketLeft'
|
286
|
+
'['
|
287
|
+
when 'Backslash'
|
288
|
+
'\\'
|
289
|
+
when 'BracketRight'
|
290
|
+
']'
|
291
|
+
when 'Quote'
|
292
|
+
'"'
|
293
|
+
else
|
294
|
+
raise ArgumentError, "Unknown key: \"#{key}\""
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Minibidi
|
2
|
+
class KeyboardActionQueue
|
3
|
+
def initialize
|
4
|
+
@actions = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_a
|
8
|
+
@actions
|
9
|
+
end
|
10
|
+
|
11
|
+
def pause(duration)
|
12
|
+
@actions << { type: 'pause', duration: duration }
|
13
|
+
end
|
14
|
+
|
15
|
+
def key_down(value)
|
16
|
+
@actions << { type: 'keyDown', value: value }
|
17
|
+
end
|
18
|
+
|
19
|
+
def key_up(value)
|
20
|
+
@actions << { type: 'keyUp', value: value }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Minibidi
|
2
|
+
class MouseActionQueue
|
3
|
+
def initialize
|
4
|
+
@actions = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_a
|
8
|
+
@actions
|
9
|
+
end
|
10
|
+
|
11
|
+
def pause(duration)
|
12
|
+
@actions << { type: 'pause', duration: duration }
|
13
|
+
end
|
14
|
+
|
15
|
+
# button: 0 for left, 1 for middle, 2 for right
|
16
|
+
def pointer_down(button:)
|
17
|
+
@actions << { type: 'pointerDown', button: button }
|
18
|
+
end
|
19
|
+
|
20
|
+
def pointer_move(x:, y:, duration: nil)
|
21
|
+
@actions << { type: 'pointerMove', x: x, y: y, duration: duration }.compact
|
22
|
+
end
|
23
|
+
|
24
|
+
# button: 0 for left, 1 for middle, 2 for right
|
25
|
+
def pointer_up(button:)
|
26
|
+
@actions << { type: 'pointerUp', button: button }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Minibidi
|
2
|
+
class Realm
|
3
|
+
def initialize(browsing_context:, id:, origin:, type: nil)
|
4
|
+
@browsing_context = browsing_context
|
5
|
+
@id = id
|
6
|
+
@origin = origin
|
7
|
+
@type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
class ScriptEvaluationError < StandardError ; end
|
11
|
+
|
12
|
+
def script_evaluate(expression)
|
13
|
+
result = @browsing_context.send(:bidi_call_async, 'script.evaluate', {
|
14
|
+
expression: expression,
|
15
|
+
target: { realm: @id },
|
16
|
+
awaitPromise: true,
|
17
|
+
}).wait
|
18
|
+
|
19
|
+
if result[:type] == 'exception'
|
20
|
+
raise ScriptEvaluationError.new(result[:exceptionDetails][:text])
|
21
|
+
end
|
22
|
+
|
23
|
+
deserialize(result[:result])
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :type
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def deserialize(result)
|
31
|
+
# ref: https://github.com/puppeteer/puppeteer/blob/puppeteer-v23.5.3/packages/puppeteer-core/src/bidi/Deserializer.ts#L21
|
32
|
+
# Converted using ChatGPT 4o
|
33
|
+
case result[:type]
|
34
|
+
when 'array'
|
35
|
+
result[:value]&.map { |value| deserialize(value) }
|
36
|
+
when 'set'
|
37
|
+
result[:value]&.each_with_object(Set.new) do |value, acc|
|
38
|
+
acc.add(deserialize(value))
|
39
|
+
end
|
40
|
+
when 'object'
|
41
|
+
result[:value]&.each_with_object({}) do |tuple, acc|
|
42
|
+
key, value = tuple
|
43
|
+
acc[key] = deserialize(value)
|
44
|
+
end
|
45
|
+
when 'map'
|
46
|
+
result[:value]&.each_with_object({}) do |tuple, acc|
|
47
|
+
key, value = tuple
|
48
|
+
acc[key] = deserialize(value)
|
49
|
+
end
|
50
|
+
when 'promise'
|
51
|
+
{}
|
52
|
+
when 'regexp'
|
53
|
+
flags = 0
|
54
|
+
result[:value][:flags]&.each_char do |flag|
|
55
|
+
case flag
|
56
|
+
when 'm'
|
57
|
+
flags |= Regexp::MULTILINE
|
58
|
+
when 'i'
|
59
|
+
flags |= Regexp::IGNORECASE
|
60
|
+
end
|
61
|
+
end
|
62
|
+
Regexp.new(result[:value][:pattern], flags)
|
63
|
+
when 'date'
|
64
|
+
Date.parse(result[:value])
|
65
|
+
when 'undefined'
|
66
|
+
nil
|
67
|
+
when 'null'
|
68
|
+
nil
|
69
|
+
when 'number', 'bigint', 'boolean', 'string'
|
70
|
+
result[:value]
|
71
|
+
else
|
72
|
+
raise ArgumentError, "Unknown type: #{result[:type]}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/minibidi/version.rb
CHANGED
data/lib/minibidi.rb
CHANGED
@@ -3,8 +3,11 @@ require "minibidi/version"
|
|
3
3
|
require 'minibidi/browser'
|
4
4
|
require 'minibidi/browsing_context'
|
5
5
|
require 'minibidi/browser_process'
|
6
|
-
require 'minibidi/firefox'
|
7
6
|
require 'minibidi/firefox_launcher'
|
7
|
+
require 'minibidi/input'
|
8
|
+
require 'minibidi/keyboard_action_queue'
|
9
|
+
require 'minibidi/mouse_action_queue'
|
10
|
+
require 'minibidi/realm'
|
8
11
|
|
9
12
|
module Minibidi
|
10
13
|
class LaunchError < StandardError ; end
|
data/minibidi.gemspec
CHANGED
@@ -25,8 +25,4 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.required_ruby_version = ">= 3.2" # Dependency for socketry/async and Data.define
|
26
26
|
spec.add_dependency "async"
|
27
27
|
spec.add_dependency "async-websocket"
|
28
|
-
spec.add_development_dependency "bundler"
|
29
|
-
spec.add_development_dependency "minitest"
|
30
|
-
spec.add_development_dependency "pry"
|
31
|
-
spec.add_development_dependency "rake"
|
32
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minibidi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YusukeIwaki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -38,62 +38,6 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bundler
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: minitest
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rake
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
41
|
description:
|
98
42
|
email:
|
99
43
|
- q7w8e9w8q7w8e9@yahoo.co.jp
|
@@ -110,8 +54,11 @@ files:
|
|
110
54
|
- lib/minibidi/browser.rb
|
111
55
|
- lib/minibidi/browser_process.rb
|
112
56
|
- lib/minibidi/browsing_context.rb
|
113
|
-
- lib/minibidi/firefox.rb
|
114
57
|
- lib/minibidi/firefox_launcher.rb
|
58
|
+
- lib/minibidi/input.rb
|
59
|
+
- lib/minibidi/keyboard_action_queue.rb
|
60
|
+
- lib/minibidi/mouse_action_queue.rb
|
61
|
+
- lib/minibidi/realm.rb
|
115
62
|
- lib/minibidi/version.rb
|
116
63
|
- minibidi.gemspec
|
117
64
|
homepage: https://github.com/YusukeIwaki/minibidi
|
@@ -133,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
80
|
- !ruby/object:Gem::Version
|
134
81
|
version: '0'
|
135
82
|
requirements: []
|
136
|
-
rubygems_version: 3.5.
|
83
|
+
rubygems_version: 3.5.22
|
137
84
|
signing_key:
|
138
85
|
specification_version: 4
|
139
86
|
summary: Mini WebDriver BiDi binding for Ruby
|