puppeteer-ruby 0.45.6 → 0.50.0.alpha5

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -3
  3. data/AGENTS.md +169 -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 +575 -0
  9. data/CLAUDE/rbs_type_checking.md +101 -0
  10. data/CLAUDE/spec_migration_plans.md +1041 -0
  11. data/CLAUDE/testing.md +278 -0
  12. data/CLAUDE.md +242 -0
  13. data/README.md +8 -0
  14. data/Rakefile +7 -0
  15. data/Steepfile +28 -0
  16. data/docs/api_coverage.md +105 -56
  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 +231 -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 +162 -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 +58 -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/mouse.rb +273 -64
  54. data/lib/puppeteer/network_event_manager.rb +7 -0
  55. data/lib/puppeteer/network_manager.rb +393 -112
  56. data/lib/puppeteer/page/screenshot_task_queue.rb +14 -4
  57. data/lib/puppeteer/page.rb +568 -226
  58. data/lib/puppeteer/puppeteer.rb +171 -64
  59. data/lib/puppeteer/query_handler_manager.rb +112 -16
  60. data/lib/puppeteer/reactor_runner.rb +247 -0
  61. data/lib/puppeteer/remote_object.rb +127 -47
  62. data/lib/puppeteer/target.rb +74 -27
  63. data/lib/puppeteer/task_manager.rb +3 -1
  64. data/lib/puppeteer/timeout_helper.rb +6 -10
  65. data/lib/puppeteer/touch_handle.rb +39 -0
  66. data/lib/puppeteer/touch_screen.rb +72 -22
  67. data/lib/puppeteer/tracing.rb +3 -3
  68. data/lib/puppeteer/version.rb +1 -1
  69. data/lib/puppeteer/wait_task.rb +264 -101
  70. data/lib/puppeteer/web_socket.rb +2 -2
  71. data/lib/puppeteer/web_socket_transport.rb +91 -27
  72. data/lib/puppeteer/web_worker.rb +175 -0
  73. data/lib/puppeteer.rb +20 -4
  74. data/puppeteer-ruby.gemspec +15 -11
  75. data/sig/_external.rbs +8 -0
  76. data/sig/_supplementary.rbs +314 -0
  77. data/sig/puppeteer/browser.rbs +166 -0
  78. data/sig/puppeteer/cdp_session.rbs +64 -0
  79. data/sig/puppeteer/dialog.rbs +41 -0
  80. data/sig/puppeteer/element_handle.rbs +305 -0
  81. data/sig/puppeteer/execution_context.rbs +87 -0
  82. data/sig/puppeteer/frame.rbs +226 -0
  83. data/sig/puppeteer/http_request.rbs +214 -0
  84. data/sig/puppeteer/http_response.rbs +89 -0
  85. data/sig/puppeteer/js_handle.rbs +64 -0
  86. data/sig/puppeteer/keyboard.rbs +40 -0
  87. data/sig/puppeteer/mouse.rbs +113 -0
  88. data/sig/puppeteer/page.rbs +515 -0
  89. data/sig/puppeteer/puppeteer.rbs +98 -0
  90. data/sig/puppeteer/remote_object.rbs +78 -0
  91. data/sig/puppeteer/touch_handle.rbs +21 -0
  92. data/sig/puppeteer/touch_screen.rbs +35 -0
  93. data/sig/puppeteer/web_worker.rbs +83 -0
  94. metadata +116 -45
  95. data/CHANGELOG.md +0 -397
  96. data/lib/puppeteer/concurrent_ruby_utils.rb +0 -81
  97. data/lib/puppeteer/firefox_target_manager.rb +0 -157
  98. data/lib/puppeteer/launcher/firefox.rb +0 -453
@@ -1,7 +1,10 @@
1
+ # rbs_inline: enabled
2
+
1
3
  class Puppeteer::Puppeteer
2
- # @param project_root [String]
3
- # @param prefereed_revision [String]
4
- # @param is_puppeteer_core [String]
4
+ # @rbs project_root: String -- Project root directory
5
+ # @rbs preferred_revision: String -- Preferred Chromium revision
6
+ # @rbs is_puppeteer_core: bool -- Whether puppeteer-core mode is enabled
7
+ # @rbs return: void -- No return value
5
8
  def initialize(project_root:, preferred_revision:, is_puppeteer_core:)
6
9
  @project_root = project_root
7
10
  @preferred_revision = preferred_revision
@@ -10,25 +13,30 @@ class Puppeteer::Puppeteer
10
13
 
11
14
  class NoViewport ; end
12
15
 
13
- # @param product [String]
14
- # @param channel [String|Symbol]
15
- # @param executable_path [String]
16
- # @param ignore_default_args [Array<String>|nil]
17
- # @param handle_SIGINT [Boolean]
18
- # @param handle_SIGTERM [Boolean]
19
- # @param handle_SIGHUP [Boolean]
20
- # @param timeout [Integer]
21
- # @param dumpio [Boolean]
22
- # @param env [Hash]
23
- # @param pipe [Boolean]
24
- # @param args [Array<String>]
25
- # @param user_data_dir [String]
26
- # @param devtools [Boolean]
27
- # @param headless [Boolean]
28
- # @param ignore_https_errors [Boolean]
29
- # @param default_viewport [Puppeteer::Viewport|nil]
30
- # @param slow_mo [Integer]
31
- # @return [Puppeteer::Browser]
16
+ # @rbs product: String? -- Browser product (chrome only)
17
+ # @rbs channel: (String | Symbol)? -- Browser channel
18
+ # @rbs executable_path: String? -- Path to browser executable
19
+ # @rbs ignore_default_args: Array[String]? -- Arguments to exclude from defaults
20
+ # @rbs handle_SIGINT: bool? -- Handle SIGINT in browser process
21
+ # @rbs handle_SIGTERM: bool? -- Handle SIGTERM in browser process
22
+ # @rbs handle_SIGHUP: bool? -- Handle SIGHUP in browser process
23
+ # @rbs timeout: Integer? -- Launch timeout in milliseconds
24
+ # @rbs dumpio: bool? -- Pipe browser stdout/stderr to current process
25
+ # @rbs env: Hash[String, String]? -- Environment variables for browser
26
+ # @rbs pipe: bool? -- Use pipe instead of WebSocket
27
+ # @rbs args: Array[String]? -- Additional browser arguments
28
+ # @rbs user_data_dir: String? -- Path to user data directory
29
+ # @rbs devtools: bool? -- Auto-open DevTools
30
+ # @rbs debugging_port: Integer? -- Remote debugging port
31
+ # @rbs headless: bool? -- Run browser in headless mode
32
+ # @rbs ignore_https_errors: bool? -- Ignore HTTPS errors
33
+ # @rbs network_enabled: bool? -- Enable network domain
34
+ # @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
35
+ # @rbs slow_mo: Integer? -- Delay between operations (ms)
36
+ # @rbs protocol_timeout: Integer? -- CDP protocol timeout in milliseconds
37
+ # @rbs wait_for_initial_page: bool? -- Wait for initial page to load
38
+ # @rbs block: Proc? -- Optional block receiving the browser
39
+ # @rbs return: Puppeteer::Browser -- Browser instance
32
40
  def launch(
33
41
  product: nil,
34
42
  channel: nil,
@@ -47,9 +55,18 @@ class Puppeteer::Puppeteer
47
55
  debugging_port: nil,
48
56
  headless: nil,
49
57
  ignore_https_errors: nil,
58
+ network_enabled: true,
50
59
  default_viewport: NoViewport.new,
51
- slow_mo: nil
60
+ slow_mo: nil,
61
+ protocol_timeout: nil,
62
+ wait_for_initial_page: nil,
63
+ &block
52
64
  )
65
+ product = product.to_s if product
66
+ if product && product != 'chrome'
67
+ raise ArgumentError.new("Unsupported product: #{product}. Only 'chrome' is supported.")
68
+ end
69
+
53
70
  options = {
54
71
  channel: channel&.to_s,
55
72
  executable_path: executable_path,
@@ -67,62 +84,115 @@ class Puppeteer::Puppeteer
67
84
  debugging_port: debugging_port,
68
85
  headless: headless,
69
86
  ignore_https_errors: ignore_https_errors,
87
+ network_enabled: network_enabled,
70
88
  default_viewport: default_viewport,
71
89
  slow_mo: slow_mo,
90
+ protocol_timeout: protocol_timeout,
91
+ wait_for_initial_page: wait_for_initial_page,
72
92
  }
73
93
  if default_viewport.is_a?(NoViewport)
74
94
  options.delete(:default_viewport)
75
95
  end
96
+ options.delete(:wait_for_initial_page) if wait_for_initial_page.nil?
76
97
 
77
98
  @product_name = product
78
- browser = launcher.launch(options)
79
- if block_given?
80
- begin
81
- yield(browser)
82
- ensure
83
- browser.close
99
+ if async_context?
100
+ browser = launcher.launch(options)
101
+ if block
102
+ begin
103
+ block.call(browser)
104
+ ensure
105
+ browser.close
106
+ end
107
+ else
108
+ browser
84
109
  end
85
110
  else
86
- browser
111
+ runner = Puppeteer::ReactorRunner.new
112
+ begin
113
+ browser = runner.sync { launcher.launch(options) }
114
+ rescue StandardError
115
+ runner.close
116
+ raise
117
+ end
118
+ proxy = Puppeteer::ReactorRunner::Proxy.new(runner, browser, owns_runner: true)
119
+ if block
120
+ begin
121
+ block.call(proxy)
122
+ ensure
123
+ proxy.close
124
+ end
125
+ else
126
+ proxy
127
+ end
87
128
  end
88
129
  end
89
130
 
90
- # @param browser_ws_endpoint [String]
91
- # @param browser_url [String]
92
- # @param transport [Puppeteer::WebSocketTransport]
93
- # @param ignore_https_errors [Boolean]
94
- # @param default_viewport [Puppeteer::Viewport|nil]
95
- # @param slow_mo [Integer]
96
- # @return [Puppeteer::Browser]
131
+ # @rbs browser_ws_endpoint: String? -- Browser WebSocket endpoint
132
+ # @rbs browser_url: String? -- Browser HTTP URL for WebSocket discovery
133
+ # @rbs transport: Puppeteer::WebSocketTransport? -- Pre-connected transport
134
+ # @rbs ignore_https_errors: bool? -- Ignore HTTPS errors
135
+ # @rbs network_enabled: bool? -- Enable network domain
136
+ # @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
137
+ # @rbs slow_mo: Integer? -- Delay between operations (ms)
138
+ # @rbs protocol_timeout: Integer? -- CDP protocol timeout in milliseconds
139
+ # @rbs block: Proc? -- Optional block receiving the browser
140
+ # @rbs return: Puppeteer::Browser -- Browser instance
97
141
  def connect(
98
142
  browser_ws_endpoint: nil,
99
143
  browser_url: nil,
100
144
  transport: nil,
101
145
  ignore_https_errors: nil,
146
+ network_enabled: true,
102
147
  default_viewport: nil,
103
- slow_mo: nil
148
+ slow_mo: nil,
149
+ protocol_timeout: nil,
150
+ &block
104
151
  )
105
152
  options = {
106
153
  browser_ws_endpoint: browser_ws_endpoint,
107
154
  browser_url: browser_url,
108
155
  transport: transport,
109
156
  ignore_https_errors: ignore_https_errors,
157
+ network_enabled: network_enabled,
110
158
  default_viewport: default_viewport,
111
159
  slow_mo: slow_mo,
160
+ protocol_timeout: protocol_timeout,
112
161
  }.compact
113
- browser = Puppeteer::BrowserConnector.new(options).connect_to_browser
114
- if block_given?
115
- begin
116
- yield(browser)
117
- ensure
118
- browser.disconnect
162
+ if async_context?
163
+ browser = Puppeteer::BrowserConnector.new(options).connect_to_browser
164
+ if block
165
+ begin
166
+ block.call(browser)
167
+ ensure
168
+ browser.disconnect
169
+ end
170
+ else
171
+ browser
119
172
  end
120
173
  else
121
- browser
174
+ runner = Puppeteer::ReactorRunner.new
175
+ begin
176
+ browser = runner.sync { Puppeteer::BrowserConnector.new(options).connect_to_browser }
177
+ rescue StandardError
178
+ runner.close
179
+ raise
180
+ end
181
+ proxy = Puppeteer::ReactorRunner::Proxy.new(runner, browser, owns_runner: true)
182
+ if block
183
+ begin
184
+ block.call(proxy)
185
+ ensure
186
+ proxy.disconnect
187
+ end
188
+ else
189
+ proxy
190
+ end
122
191
  end
123
192
  end
124
193
 
125
- # @return [String]
194
+ # @rbs channel: String? -- Browser channel
195
+ # @rbs return: String -- Executable path
126
196
  def executable_path(channel: nil)
127
197
  launcher.executable_path(channel: channel)
128
198
  end
@@ -136,34 +206,56 @@ class Puppeteer::Puppeteer
136
206
  )
137
207
  end
138
208
 
139
- # @return [String]
209
+ # @rbs return: String -- Product name
140
210
  def product
141
211
  launcher.product
142
212
  end
143
213
 
144
- def register_custom_query_handler(name:, query_one:, query_all:)
214
+ private def async_context?
215
+ task = Async::Task.current
216
+ !task.nil?
217
+ rescue RuntimeError, NoMethodError
218
+ false
219
+ end
220
+
221
+ # @rbs name: String -- Custom query handler name
222
+ # @rbs query_one: String? -- Query-one handler
223
+ # @rbs query_all: String? -- Query-all handler
224
+ # @rbs return: void -- No return value
225
+ def register_custom_query_handler(name:, query_one: nil, query_all: nil)
145
226
  unless name =~ /\A[a-zA-Z]+\z/
146
227
  raise ArgumentError.new("Custom query handler names may only contain [a-zA-Z]")
147
228
  end
148
229
 
230
+ if query_one.nil? && query_all.nil?
231
+ raise ArgumentError.new('At least one query method must be implemented.')
232
+ end
233
+
234
+ handler = Puppeteer::CustomQueryHandler.new(query_one: query_one, query_all: query_all)
149
235
  handler_name = name.to_sym
150
236
  if query_handler_manager.query_handlers.key?(handler_name)
151
237
  raise ArgumentError.new("A query handler named #{name} already exists")
152
238
  end
153
239
 
154
- handler = Puppeteer::CustomQueryHandler.new(query_one: query_one, query_all: query_all)
155
- Puppeteer::QueryHandlerManager.instance.query_handlers[handler_name] = handler
240
+ query_handler_manager.query_handlers[handler_name] = handler
156
241
  end
157
242
 
158
- def with_custom_query_handler(name:, query_one:, query_all:, &block)
243
+ # @rbs name: String -- Custom query handler name
244
+ # @rbs query_one: String? -- Query-one handler
245
+ # @rbs query_all: String? -- Query-all handler
246
+ # @rbs return: untyped -- Block result
247
+ def with_custom_query_handler(name:, query_one: nil, query_all: nil, &block)
159
248
  unless name =~ /\A[a-zA-Z]+\z/
160
249
  raise ArgumentError.new("Custom query handler names may only contain [a-zA-Z]")
161
250
  end
162
251
 
252
+ if query_one.nil? && query_all.nil?
253
+ raise ArgumentError.new('At least one query method must be implemented.')
254
+ end
255
+
163
256
  handler_name = name.to_sym
164
257
 
165
258
  handler = Puppeteer::CustomQueryHandler.new(query_one: query_one, query_all: query_all)
166
- query_handler_manager = Puppeteer::QueryHandlerManager.instance
167
259
  original = query_handler_manager.query_handlers.delete(handler_name)
168
260
  query_handler_manager.query_handlers[handler_name] = handler
169
261
  begin
@@ -177,26 +269,41 @@ class Puppeteer::Puppeteer
177
269
  end
178
270
  end
179
271
 
180
- # @return [Puppeteer::Devices]
272
+ # @rbs name: String -- Custom query handler name
273
+ # @rbs return: void -- No return value
274
+ def unregister_custom_query_handler(name:)
275
+ query_handler_manager.unregister_custom_query_handler(name)
276
+ end
277
+
278
+ # @rbs return: void -- No return value
279
+ def clear_custom_query_handlers
280
+ query_handler_manager.clear_custom_query_handlers
281
+ end
282
+
283
+ # @rbs return: Array[String] -- Registered custom query handler names
284
+ def custom_query_handler_names
285
+ query_handler_manager.custom_query_handler_names
286
+ end
287
+
288
+ private def query_handler_manager
289
+ Puppeteer::QueryHandlerManager.instance
290
+ end
291
+
292
+ # @rbs return: Puppeteer::Devices -- Devices registry
181
293
  def devices
182
294
  Puppeteer::Devices
183
295
  end
184
296
 
185
- # # @return {Object}
186
- # def errors
187
- # # ???
188
- # end
189
-
190
- # @return [Puppeteer::NetworkConditions]
297
+ # @rbs return: Puppeteer::NetworkConditions -- Network conditions registry
191
298
  def network_conditions
192
299
  Puppeteer::NetworkConditions
193
300
  end
194
301
 
195
- # @param args [Array<String>]
196
- # @param user_data_dir [String]
197
- # @param devtools [Boolean]
198
- # @param headless [Boolean]
199
- # @return [Array<String>]
302
+ # @rbs args: Array[String]? -- Additional arguments
303
+ # @rbs user_data_dir: String? -- Path to user data directory
304
+ # @rbs devtools: bool? -- Enable DevTools
305
+ # @rbs headless: bool? -- Run browser in headless mode
306
+ # @rbs return: Array[String] -- Default launch arguments
200
307
  def default_args(args: nil, user_data_dir: nil, devtools: nil, headless: nil)
201
308
  options = {
202
309
  args: args,
@@ -3,6 +3,8 @@ require 'singleton'
3
3
  class Puppeteer::QueryHandlerManager
4
4
  include Singleton
5
5
 
6
+ DEFAULT_QUERY_HANDLER_NAMES = %i[aria xpath text].freeze
7
+
6
8
  def query_handlers
7
9
  @query_handlers ||= {
8
10
  aria: Puppeteer::AriaQueryHandler.new,
@@ -11,6 +13,30 @@ class Puppeteer::QueryHandlerManager
11
13
  }
12
14
  end
13
15
 
16
+ def custom_query_handler_names
17
+ query_handlers.keys.reject { |name| DEFAULT_QUERY_HANDLER_NAMES.include?(name) }.map(&:to_s)
18
+ end
19
+
20
+ def unregister_custom_query_handler(name)
21
+ handler_name = name.to_sym
22
+ if DEFAULT_QUERY_HANDLER_NAMES.include?(handler_name)
23
+ raise ArgumentError.new("Cannot unregister built-in query handler: #{name}")
24
+ end
25
+ unless query_handlers.key?(handler_name)
26
+ raise ArgumentError.new("Cannot unregister unknown handler: #{name}")
27
+ end
28
+
29
+ query_handlers.delete(handler_name)
30
+ end
31
+
32
+ def clear_custom_query_handlers
33
+ query_handlers.each_key do |name|
34
+ next if DEFAULT_QUERY_HANDLER_NAMES.include?(name)
35
+
36
+ query_handlers.delete(name)
37
+ end
38
+ end
39
+
14
40
  private def default_handler
15
41
  @default_handler ||= Puppeteer::CustomQueryHandler.new(
16
42
  query_one: '(element, selector) => element.querySelector(selector)',
@@ -18,6 +44,65 @@ class Puppeteer::QueryHandlerManager
18
44
  )
19
45
  end
20
46
 
47
+ private def p_query_handler
48
+ @p_query_handler ||= Puppeteer::CustomQueryHandler.new(
49
+ query_one: <<~JAVASCRIPT,
50
+ (element, selector) => {
51
+ const parts = selector.split('>>>').map((part) => part.trim()).filter(Boolean);
52
+ let roots = [element];
53
+ if (parts.length === 0) return null;
54
+ for (let i = 0; i < parts.length; i++) {
55
+ const part = parts[i];
56
+ const next = [];
57
+ for (const root of roots) {
58
+ const scope = root;
59
+ const elements = scope.querySelectorAll(part);
60
+ for (const node of elements) {
61
+ if (i === parts.length - 1) {
62
+ next.push(node);
63
+ } else if (node.shadowRoot) {
64
+ next.push(node.shadowRoot);
65
+ }
66
+ }
67
+ }
68
+ if (i === parts.length - 1) {
69
+ return next[0] || null;
70
+ }
71
+ roots = next;
72
+ }
73
+ return null;
74
+ }
75
+ JAVASCRIPT
76
+ query_all: <<~JAVASCRIPT,
77
+ (element, selector) => {
78
+ const parts = selector.split('>>>').map((part) => part.trim()).filter(Boolean);
79
+ let roots = [element];
80
+ if (parts.length === 0) return [];
81
+ for (let i = 0; i < parts.length; i++) {
82
+ const part = parts[i];
83
+ const next = [];
84
+ for (const root of roots) {
85
+ const scope = root;
86
+ const elements = scope.querySelectorAll(part);
87
+ for (const node of elements) {
88
+ if (i === parts.length - 1) {
89
+ next.push(node);
90
+ } else if (node.shadowRoot) {
91
+ next.push(node.shadowRoot);
92
+ }
93
+ }
94
+ }
95
+ if (i === parts.length - 1) {
96
+ return next;
97
+ }
98
+ roots = next;
99
+ }
100
+ return [];
101
+ }
102
+ JAVASCRIPT
103
+ )
104
+ end
105
+
21
106
  private def xpath_handler
22
107
  @xpath_handler ||= Puppeteer::CustomQueryHandler.new(
23
108
  query_one: <<~JAVASCRIPT,
@@ -203,9 +288,10 @@ class Puppeteer::QueryHandlerManager
203
288
  end
204
289
 
205
290
  class Result
206
- def initialize(query_handler:, selector:)
291
+ def initialize(query_handler:, selector:, polling:)
207
292
  @query_handler = query_handler
208
293
  @selector = selector
294
+ @polling = polling
209
295
  end
210
296
 
211
297
  def query_one(element_handle)
@@ -213,7 +299,14 @@ class Puppeteer::QueryHandlerManager
213
299
  end
214
300
 
215
301
  def wait_for(element_or_frame, visible:, hidden:, timeout:)
216
- @query_handler.wait_for(element_or_frame, @selector, visible: visible, hidden: hidden, timeout: timeout)
302
+ @query_handler.wait_for(
303
+ element_or_frame,
304
+ @selector,
305
+ visible: visible,
306
+ hidden: hidden,
307
+ timeout: timeout,
308
+ polling: @polling,
309
+ )
217
310
  end
218
311
 
219
312
  def query_all(element_handle)
@@ -226,26 +319,29 @@ class Puppeteer::QueryHandlerManager
226
319
  end
227
320
 
228
321
  def detect_query_handler(selector)
229
- unless /^[a-zA-Z]+\// =~ selector
230
- return Result.new(
231
- query_handler: default_handler,
232
- selector: selector,
233
- )
234
- end
235
-
236
- chunk = selector.split("/")
237
- name = chunk.shift
238
- updated_selector = chunk.join("/")
239
-
240
- query_handler = query_handlers[name.to_sym]
322
+ query_handler = nil
323
+ updated_selector = selector
324
+ polling = selector.include?(':') ? 'raf' : 'mutation'
241
325
 
242
- unless query_handler
243
- raise ArgumentError.new("Query set to use \"#{name}\", but no query handler of that name was found")
326
+ if (match = selector.match(/^([a-zA-Z][\w-]*)([=\/])(.*)$/))
327
+ name = match[1]
328
+ updated_selector = match[3]
329
+ query_handler = query_handlers[name.to_sym]
330
+ unless query_handler
331
+ raise ArgumentError.new("Query set to use \"#{name}\", but no query handler of that name was found")
332
+ end
333
+ polling = name == 'aria' ? 'raf' : 'mutation'
334
+ elsif selector.include?('>>>')
335
+ query_handler = p_query_handler
336
+ polling = 'raf'
337
+ else
338
+ query_handler = default_handler
244
339
  end
245
340
 
246
341
  Result.new(
247
342
  query_handler: query_handler,
248
343
  selector: updated_selector,
344
+ polling: polling,
249
345
  )
250
346
  end
251
347
  end