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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -3
  3. data/AGENTS.md +170 -0
  4. data/CLAUDE/README.md +41 -0
  5. data/CLAUDE/architecture.md +253 -0
  6. data/CLAUDE/cdp_protocol.md +230 -0
  7. data/CLAUDE/concurrency.md +216 -0
  8. data/CLAUDE/porting_puppeteer.md +605 -0
  9. data/CLAUDE/rbs_type_checking.md +101 -0
  10. data/CLAUDE/spec_migration_plans.md +1039 -0
  11. data/CLAUDE/testing.md +278 -0
  12. data/CLAUDE.md +242 -0
  13. data/README.md +9 -0
  14. data/Rakefile +7 -0
  15. data/Steepfile +28 -0
  16. data/docs/api_coverage.md +106 -57
  17. data/lib/puppeteer/aria_query_handler.rb +3 -2
  18. data/lib/puppeteer/async_utils.rb +214 -0
  19. data/lib/puppeteer/browser.rb +98 -56
  20. data/lib/puppeteer/browser_connector.rb +18 -3
  21. data/lib/puppeteer/browser_context.rb +196 -3
  22. data/lib/puppeteer/browser_runner.rb +18 -10
  23. data/lib/puppeteer/cdp_session.rb +67 -23
  24. data/lib/puppeteer/chrome_target_manager.rb +65 -40
  25. data/lib/puppeteer/connection.rb +55 -36
  26. data/lib/puppeteer/console_message.rb +9 -1
  27. data/lib/puppeteer/console_patch.rb +47 -0
  28. data/lib/puppeteer/css_coverage.rb +5 -3
  29. data/lib/puppeteer/custom_query_handler.rb +80 -33
  30. data/lib/puppeteer/define_async_method.rb +31 -37
  31. data/lib/puppeteer/dialog.rb +47 -14
  32. data/lib/puppeteer/element_handle.rb +236 -62
  33. data/lib/puppeteer/emulation_manager.rb +1 -1
  34. data/lib/puppeteer/env.rb +1 -1
  35. data/lib/puppeteer/errors.rb +25 -2
  36. data/lib/puppeteer/event_callbackable.rb +15 -0
  37. data/lib/puppeteer/events.rb +4 -0
  38. data/lib/puppeteer/execution_context.rb +148 -3
  39. data/lib/puppeteer/file_chooser.rb +6 -0
  40. data/lib/puppeteer/frame.rb +177 -91
  41. data/lib/puppeteer/frame_manager.rb +69 -48
  42. data/lib/puppeteer/http_request.rb +114 -38
  43. data/lib/puppeteer/http_response.rb +24 -7
  44. data/lib/puppeteer/isolated_world.rb +64 -41
  45. data/lib/puppeteer/js_coverage.rb +5 -3
  46. data/lib/puppeteer/js_handle.rb +77 -16
  47. data/lib/puppeteer/keyboard.rb +30 -17
  48. data/lib/puppeteer/launcher/browser_options.rb +3 -1
  49. data/lib/puppeteer/launcher/chrome.rb +8 -5
  50. data/lib/puppeteer/launcher/launch_options.rb +7 -2
  51. data/lib/puppeteer/launcher.rb +4 -8
  52. data/lib/puppeteer/lifecycle_watcher.rb +38 -22
  53. data/lib/puppeteer/locators.rb +733 -0
  54. data/lib/puppeteer/mouse.rb +273 -64
  55. data/lib/puppeteer/network_event_manager.rb +7 -0
  56. data/lib/puppeteer/network_manager.rb +393 -112
  57. data/lib/puppeteer/p_query_handler.rb +367 -0
  58. data/lib/puppeteer/p_selector_parser.rb +241 -0
  59. data/lib/puppeteer/page/screenshot_task_queue.rb +14 -4
  60. data/lib/puppeteer/page.rb +583 -226
  61. data/lib/puppeteer/puppeteer.rb +171 -64
  62. data/lib/puppeteer/query_handler_manager.rb +66 -16
  63. data/lib/puppeteer/reactor_runner.rb +247 -0
  64. data/lib/puppeteer/remote_object.rb +127 -47
  65. data/lib/puppeteer/target.rb +74 -27
  66. data/lib/puppeteer/task_manager.rb +3 -1
  67. data/lib/puppeteer/timeout_helper.rb +6 -10
  68. data/lib/puppeteer/touch_handle.rb +39 -0
  69. data/lib/puppeteer/touch_screen.rb +72 -22
  70. data/lib/puppeteer/tracing.rb +3 -3
  71. data/lib/puppeteer/version.rb +1 -1
  72. data/lib/puppeteer/wait_task.rb +264 -101
  73. data/lib/puppeteer/web_socket.rb +2 -2
  74. data/lib/puppeteer/web_socket_transport.rb +91 -27
  75. data/lib/puppeteer/web_worker.rb +175 -0
  76. data/lib/puppeteer.rb +23 -4
  77. data/puppeteer-ruby.gemspec +15 -11
  78. data/sig/_external.rbs +8 -0
  79. data/sig/_supplementary.rbs +314 -0
  80. data/sig/puppeteer/browser.rbs +166 -0
  81. data/sig/puppeteer/cdp_session.rbs +64 -0
  82. data/sig/puppeteer/dialog.rbs +41 -0
  83. data/sig/puppeteer/element_handle.rbs +308 -0
  84. data/sig/puppeteer/execution_context.rbs +87 -0
  85. data/sig/puppeteer/frame.rbs +233 -0
  86. data/sig/puppeteer/http_request.rbs +214 -0
  87. data/sig/puppeteer/http_response.rbs +89 -0
  88. data/sig/puppeteer/js_handle.rbs +64 -0
  89. data/sig/puppeteer/keyboard.rbs +40 -0
  90. data/sig/puppeteer/locators.rbs +222 -0
  91. data/sig/puppeteer/mouse.rbs +113 -0
  92. data/sig/puppeteer/p_query_handler.rbs +73 -0
  93. data/sig/puppeteer/p_selector_parser.rbs +31 -0
  94. data/sig/puppeteer/page.rbs +522 -0
  95. data/sig/puppeteer/puppeteer.rbs +98 -0
  96. data/sig/puppeteer/remote_object.rbs +78 -0
  97. data/sig/puppeteer/touch_handle.rbs +21 -0
  98. data/sig/puppeteer/touch_screen.rbs +35 -0
  99. data/sig/puppeteer/web_worker.rbs +83 -0
  100. metadata +122 -45
  101. data/CHANGELOG.md +0 -397
  102. data/lib/puppeteer/concurrent_ruby_utils.rb +0 -81
  103. data/lib/puppeteer/firefox_target_manager.rb +0 -157
  104. 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
- @extra_prefs_firefox = options[:extra_prefs_firefox] || {}
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, :extra_prefs_firefox
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
@@ -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' or 'firefox'
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
- if product == 'firefox'
20
- return Firefox.new(
21
- project_root: project_root,
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 < StandardError
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 < StandardError; end
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 = resolvable_future
91
- @lifecycle_promise = resolvable_future
92
- @new_document_navigation_promise = resolvable_future
93
- @termination_promise = resolvable_future
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.fulfill(nil) if @navigation_response_received && !@navigation_response_received.resolved?
105
- @navigation_response_received = resolvable_future
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.fulfill(nil)
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.fulfill(nil) unless @navigation_response_received.resolved?
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.fulfill(nil) unless @navigation_response_received.resolved?
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.value! rescue nil
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
- future do
157
- Timeout.timeout(@timeout / 1000.0) do
158
- @termination_promise.value!
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.fulfill(true) if @lifecycle_promise.pending?
191
- if @has_same_document_navigation && @same_document_navigation_promise.pending?
192
- @same_document_navigation_promise.fulfill(true)
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) && @new_document_navigation_promise.pending?
195
- @new_document_navigation_promise.fulfill(true)
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