puppeteer-ruby 0.45.6 → 0.50.0.alpha6
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/.rubocop.yml +1 -3
- data/AGENTS.md +170 -0
- data/CLAUDE/README.md +41 -0
- data/CLAUDE/architecture.md +253 -0
- data/CLAUDE/cdp_protocol.md +230 -0
- data/CLAUDE/concurrency.md +216 -0
- data/CLAUDE/porting_puppeteer.md +605 -0
- data/CLAUDE/rbs_type_checking.md +101 -0
- data/CLAUDE/spec_migration_plans.md +1039 -0
- data/CLAUDE/testing.md +278 -0
- data/CLAUDE.md +242 -0
- data/README.md +9 -0
- data/Rakefile +7 -0
- data/Steepfile +28 -0
- data/docs/api_coverage.md +106 -57
- data/lib/puppeteer/aria_query_handler.rb +3 -2
- data/lib/puppeteer/async_utils.rb +214 -0
- data/lib/puppeteer/browser.rb +98 -56
- data/lib/puppeteer/browser_connector.rb +18 -3
- data/lib/puppeteer/browser_context.rb +196 -3
- data/lib/puppeteer/browser_runner.rb +18 -10
- data/lib/puppeteer/cdp_session.rb +67 -23
- data/lib/puppeteer/chrome_target_manager.rb +65 -40
- data/lib/puppeteer/connection.rb +55 -36
- data/lib/puppeteer/console_message.rb +9 -1
- data/lib/puppeteer/console_patch.rb +47 -0
- data/lib/puppeteer/css_coverage.rb +5 -3
- data/lib/puppeteer/custom_query_handler.rb +80 -33
- data/lib/puppeteer/define_async_method.rb +31 -37
- data/lib/puppeteer/dialog.rb +47 -14
- data/lib/puppeteer/element_handle.rb +236 -62
- data/lib/puppeteer/emulation_manager.rb +1 -1
- data/lib/puppeteer/env.rb +1 -1
- data/lib/puppeteer/errors.rb +25 -2
- data/lib/puppeteer/event_callbackable.rb +15 -0
- data/lib/puppeteer/events.rb +4 -0
- data/lib/puppeteer/execution_context.rb +148 -3
- data/lib/puppeteer/file_chooser.rb +6 -0
- data/lib/puppeteer/frame.rb +177 -91
- data/lib/puppeteer/frame_manager.rb +69 -48
- data/lib/puppeteer/http_request.rb +114 -38
- data/lib/puppeteer/http_response.rb +24 -7
- data/lib/puppeteer/isolated_world.rb +64 -41
- data/lib/puppeteer/js_coverage.rb +5 -3
- data/lib/puppeteer/js_handle.rb +77 -16
- data/lib/puppeteer/keyboard.rb +30 -17
- data/lib/puppeteer/launcher/browser_options.rb +3 -1
- data/lib/puppeteer/launcher/chrome.rb +8 -5
- data/lib/puppeteer/launcher/launch_options.rb +7 -2
- data/lib/puppeteer/launcher.rb +4 -8
- data/lib/puppeteer/lifecycle_watcher.rb +38 -22
- data/lib/puppeteer/locators.rb +733 -0
- data/lib/puppeteer/mouse.rb +273 -64
- data/lib/puppeteer/network_event_manager.rb +7 -0
- data/lib/puppeteer/network_manager.rb +393 -112
- data/lib/puppeteer/p_query_handler.rb +367 -0
- data/lib/puppeteer/p_selector_parser.rb +241 -0
- data/lib/puppeteer/page/screenshot_task_queue.rb +14 -4
- data/lib/puppeteer/page.rb +583 -226
- data/lib/puppeteer/puppeteer.rb +171 -64
- data/lib/puppeteer/query_handler_manager.rb +66 -16
- data/lib/puppeteer/reactor_runner.rb +247 -0
- data/lib/puppeteer/remote_object.rb +127 -47
- data/lib/puppeteer/target.rb +74 -27
- data/lib/puppeteer/task_manager.rb +3 -1
- data/lib/puppeteer/timeout_helper.rb +6 -10
- data/lib/puppeteer/touch_handle.rb +39 -0
- data/lib/puppeteer/touch_screen.rb +72 -22
- data/lib/puppeteer/tracing.rb +3 -3
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer/wait_task.rb +264 -101
- data/lib/puppeteer/web_socket.rb +2 -2
- data/lib/puppeteer/web_socket_transport.rb +91 -27
- data/lib/puppeteer/web_worker.rb +175 -0
- data/lib/puppeteer.rb +23 -4
- data/puppeteer-ruby.gemspec +15 -11
- data/sig/_external.rbs +8 -0
- data/sig/_supplementary.rbs +314 -0
- data/sig/puppeteer/browser.rbs +166 -0
- data/sig/puppeteer/cdp_session.rbs +64 -0
- data/sig/puppeteer/dialog.rbs +41 -0
- data/sig/puppeteer/element_handle.rbs +308 -0
- data/sig/puppeteer/execution_context.rbs +87 -0
- data/sig/puppeteer/frame.rbs +233 -0
- data/sig/puppeteer/http_request.rbs +214 -0
- data/sig/puppeteer/http_response.rbs +89 -0
- data/sig/puppeteer/js_handle.rbs +64 -0
- data/sig/puppeteer/keyboard.rbs +40 -0
- data/sig/puppeteer/locators.rbs +222 -0
- data/sig/puppeteer/mouse.rbs +113 -0
- data/sig/puppeteer/p_query_handler.rbs +73 -0
- data/sig/puppeteer/p_selector_parser.rbs +31 -0
- data/sig/puppeteer/page.rbs +522 -0
- data/sig/puppeteer/puppeteer.rbs +98 -0
- data/sig/puppeteer/remote_object.rbs +78 -0
- data/sig/puppeteer/touch_handle.rbs +21 -0
- data/sig/puppeteer/touch_screen.rbs +35 -0
- data/sig/puppeteer/web_worker.rbs +83 -0
- metadata +122 -45
- data/CHANGELOG.md +0 -397
- data/lib/puppeteer/concurrent_ruby_utils.rb +0 -81
- data/lib/puppeteer/firefox_target_manager.rb +0 -157
- data/lib/puppeteer/launcher/firefox.rb +0 -453
|
@@ -31,6 +31,7 @@ module Puppeteer::Launcher
|
|
|
31
31
|
# @property {boolean=} dumpio
|
|
32
32
|
# @property {!Object<string, string | undefined>=} env
|
|
33
33
|
# @property {boolean=} pipe
|
|
34
|
+
# @property {boolean=} wait_for_initial_page
|
|
34
35
|
def initialize(options)
|
|
35
36
|
@channel = options[:channel]
|
|
36
37
|
@executable_path = options[:executable_path]
|
|
@@ -42,10 +43,10 @@ module Puppeteer::Launcher
|
|
|
42
43
|
@dumpio = options[:dumpio] || false
|
|
43
44
|
@env = options[:env] || ENV
|
|
44
45
|
@pipe = options[:pipe] || false
|
|
45
|
-
@
|
|
46
|
+
@wait_for_initial_page = options.fetch(:wait_for_initial_page, true)
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
attr_reader :channel, :executable_path, :ignore_default_args, :timeout, :env
|
|
49
|
+
attr_reader :channel, :executable_path, :ignore_default_args, :timeout, :env
|
|
49
50
|
|
|
50
51
|
def handle_SIGINT?
|
|
51
52
|
@handle_SIGINT
|
|
@@ -66,5 +67,9 @@ module Puppeteer::Launcher
|
|
|
66
67
|
def pipe?
|
|
67
68
|
@pipe
|
|
68
69
|
end
|
|
70
|
+
|
|
71
|
+
def wait_for_initial_page?
|
|
72
|
+
@wait_for_initial_page
|
|
73
|
+
end
|
|
69
74
|
end
|
|
70
75
|
end
|
data/lib/puppeteer/launcher.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require_relative './launcher/browser_options'
|
|
2
2
|
require_relative './launcher/chrome'
|
|
3
3
|
require_relative './launcher/chrome_arg_options'
|
|
4
|
-
require_relative './launcher/firefox'
|
|
5
4
|
require_relative './launcher/launch_options'
|
|
6
5
|
|
|
7
6
|
# https://github.com/puppeteer/puppeteer/blob/main/src/node/Launcher.ts
|
|
@@ -9,19 +8,16 @@ module Puppeteer::Launcher
|
|
|
9
8
|
# @param project_root [String]
|
|
10
9
|
# @param prefereed_revision [String]
|
|
11
10
|
# @param is_puppeteer_core [String]
|
|
12
|
-
# @param product [String] 'chrome'
|
|
11
|
+
# @param product [String] 'chrome'
|
|
13
12
|
# @return [Puppeteer::Launcher::Chrome]
|
|
14
13
|
module_function def new(project_root:, preferred_revision:, is_puppeteer_core:, product:)
|
|
15
14
|
unless is_puppeteer_core
|
|
16
15
|
product ||= ENV['PUPPETEER_PRODUCT']
|
|
17
16
|
end
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
preferred_revision: preferred_revision,
|
|
23
|
-
is_puppeteer_core: is_puppeteer_core,
|
|
24
|
-
)
|
|
18
|
+
product = product.to_s if product
|
|
19
|
+
if product && product != 'chrome'
|
|
20
|
+
raise ArgumentError.new("Unsupported product: #{product}. Only 'chrome' is supported.")
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
Chrome.new(
|
|
@@ -50,12 +50,12 @@ class Puppeteer::LifecycleWatcher
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
class FrameDetachedError <
|
|
53
|
+
class FrameDetachedError < Puppeteer::Error
|
|
54
54
|
def initialize
|
|
55
55
|
super('Navigating frame was detached')
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
|
-
class TerminatedError <
|
|
58
|
+
class TerminatedError < Puppeteer::Error; end
|
|
59
59
|
|
|
60
60
|
# * @param {!Puppeteer.FrameManager} frameManager
|
|
61
61
|
# * @param {!Puppeteer.Frame} frame
|
|
@@ -72,6 +72,12 @@ class Puppeteer::LifecycleWatcher
|
|
|
72
72
|
@listener_ids['client'] = @frame_manager.client.add_event_listener(CDPSessionEmittedEvents::Disconnected) do
|
|
73
73
|
terminate(TerminatedError.new('Navigation failed because browser has disconnected!'))
|
|
74
74
|
end
|
|
75
|
+
connection = @frame_manager.client.respond_to?(:connection) ? @frame_manager.client.connection : nil
|
|
76
|
+
if connection
|
|
77
|
+
@listener_ids['connection'] = connection.add_event_listener(ConnectionEmittedEvents::Disconnected) do
|
|
78
|
+
terminate(TerminatedError.new('Navigation failed because browser has disconnected!'))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
75
81
|
@listener_ids['frame_manager'] = [
|
|
76
82
|
@frame_manager.add_event_listener(FrameManagerEmittedEvents::LifecycleEvent) do |_|
|
|
77
83
|
check_lifecycle_complete
|
|
@@ -87,24 +93,28 @@ class Puppeteer::LifecycleWatcher
|
|
|
87
93
|
@frame_manager.network_manager.add_event_listener(NetworkManagerEmittedEvents::RequestFailed, &method(:handle_request_failed)),
|
|
88
94
|
]
|
|
89
95
|
|
|
90
|
-
@same_document_navigation_promise =
|
|
91
|
-
@lifecycle_promise =
|
|
92
|
-
@new_document_navigation_promise =
|
|
93
|
-
@termination_promise =
|
|
96
|
+
@same_document_navigation_promise = Async::Promise.new
|
|
97
|
+
@lifecycle_promise = Async::Promise.new
|
|
98
|
+
@new_document_navigation_promise = Async::Promise.new
|
|
99
|
+
@termination_promise = Async::Promise.new
|
|
100
|
+
@navigation_response_received = Async::Promise.new.tap { |promise| promise.resolve(nil) }
|
|
94
101
|
check_lifecycle_complete
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
# @param [Puppeteer::HTTPRequest] request
|
|
98
105
|
def handle_request(request)
|
|
99
106
|
return if request.frame != @frame || !request.navigation_request?
|
|
107
|
+
if @navigation_request && request.redirect_chain.empty?
|
|
108
|
+
return
|
|
109
|
+
end
|
|
100
110
|
@navigation_request = request
|
|
101
111
|
# Resolve previous navigation response in case there are multiple
|
|
102
112
|
# navigation requests reported by the backend. This generally should not
|
|
103
113
|
# happen by it looks like it's possible.
|
|
104
|
-
@navigation_response_received.
|
|
105
|
-
@navigation_response_received =
|
|
114
|
+
@navigation_response_received.resolve(nil) if @navigation_response_received && !@navigation_response_received.resolved?
|
|
115
|
+
@navigation_response_received = Async::Promise.new
|
|
106
116
|
if request.response && !@navigation_response_received.resolved?
|
|
107
|
-
@navigation_response_received.
|
|
117
|
+
@navigation_response_received.resolve(nil)
|
|
108
118
|
end
|
|
109
119
|
end
|
|
110
120
|
|
|
@@ -112,14 +122,14 @@ class Puppeteer::LifecycleWatcher
|
|
|
112
122
|
def handle_request_failed(request)
|
|
113
123
|
return if @navigation_request&.internal&.request_id != request.internal.request_id
|
|
114
124
|
|
|
115
|
-
@navigation_response_received.
|
|
125
|
+
@navigation_response_received.resolve(nil) unless @navigation_response_received.resolved?
|
|
116
126
|
end
|
|
117
127
|
|
|
118
128
|
# @param [Puppeteer::HTTPResponse] response
|
|
119
129
|
def handle_response(response)
|
|
120
130
|
return if @navigation_request&.internal&.request_id != response.request.internal.request_id
|
|
121
131
|
|
|
122
|
-
@navigation_response_received.
|
|
132
|
+
@navigation_response_received.resolve(nil) unless @navigation_response_received.resolved?
|
|
123
133
|
end
|
|
124
134
|
|
|
125
135
|
# @param frame [Puppeteer::Frame]
|
|
@@ -134,7 +144,7 @@ class Puppeteer::LifecycleWatcher
|
|
|
134
144
|
# @return [Puppeteer::HTTPResponse]
|
|
135
145
|
def navigation_response
|
|
136
146
|
# Continue with a possibly null response.
|
|
137
|
-
@navigation_response_received.
|
|
147
|
+
@navigation_response_received.wait rescue nil
|
|
138
148
|
if_present(@navigation_request) do |request|
|
|
139
149
|
request.response
|
|
140
150
|
end
|
|
@@ -142,6 +152,8 @@ class Puppeteer::LifecycleWatcher
|
|
|
142
152
|
|
|
143
153
|
# @param error [TerminatedError]
|
|
144
154
|
private def terminate(error)
|
|
155
|
+
return if @termination_promise.resolved?
|
|
156
|
+
|
|
145
157
|
@termination_promise.reject(error)
|
|
146
158
|
end
|
|
147
159
|
|
|
@@ -153,12 +165,12 @@ class Puppeteer::LifecycleWatcher
|
|
|
153
165
|
|
|
154
166
|
def timeout_or_termination_promise
|
|
155
167
|
if @timeout > 0
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
@termination_promise.
|
|
168
|
+
-> do
|
|
169
|
+
begin
|
|
170
|
+
Puppeteer::AsyncUtils.async_timeout(@timeout, @termination_promise).wait
|
|
171
|
+
rescue Async::TimeoutError
|
|
172
|
+
raise Puppeteer::TimeoutError.new("Navigation timeout of #{@timeout} ms exceeded")
|
|
159
173
|
end
|
|
160
|
-
rescue Timeout::Error
|
|
161
|
-
raise Puppeteer::TimeoutError.new("Navigation timeout of #{@timeout}ms exceeded")
|
|
162
174
|
end
|
|
163
175
|
else
|
|
164
176
|
@termination_promise
|
|
@@ -187,12 +199,12 @@ class Puppeteer::LifecycleWatcher
|
|
|
187
199
|
private def check_lifecycle_complete
|
|
188
200
|
# We expect navigation to commit.
|
|
189
201
|
return unless @expected_lifecycle.completed?(@frame)
|
|
190
|
-
@lifecycle_promise.
|
|
191
|
-
if @has_same_document_navigation &&
|
|
192
|
-
@same_document_navigation_promise.
|
|
202
|
+
@lifecycle_promise.resolve(true) unless @lifecycle_promise.resolved?
|
|
203
|
+
if @has_same_document_navigation && !@same_document_navigation_promise.resolved?
|
|
204
|
+
@same_document_navigation_promise.resolve(true)
|
|
193
205
|
end
|
|
194
|
-
if (@swapped || @frame.loader_id != @initial_loader_id) &&
|
|
195
|
-
@new_document_navigation_promise.
|
|
206
|
+
if (@swapped || @frame.loader_id != @initial_loader_id) && !@new_document_navigation_promise.resolved?
|
|
207
|
+
@new_document_navigation_promise.resolve(true)
|
|
196
208
|
end
|
|
197
209
|
end
|
|
198
210
|
|
|
@@ -203,6 +215,10 @@ class Puppeteer::LifecycleWatcher
|
|
|
203
215
|
if_present(@listener_ids['frame_manager']) do |ids|
|
|
204
216
|
@frame_manager.remove_event_listener(*ids)
|
|
205
217
|
end
|
|
218
|
+
if_present(@listener_ids['connection']) do |id|
|
|
219
|
+
connection = @frame_manager.client.respond_to?(:connection) ? @frame_manager.client.connection : nil
|
|
220
|
+
connection&.remove_event_listener(id)
|
|
221
|
+
end
|
|
206
222
|
if_present(@listener_ids['network_manager']) do |ids|
|
|
207
223
|
@frame_manager.network_manager.remove_event_listener(*ids)
|
|
208
224
|
end
|