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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -3
- data/AGENTS.md +169 -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 +575 -0
- data/CLAUDE/rbs_type_checking.md +101 -0
- data/CLAUDE/spec_migration_plans.md +1041 -0
- data/CLAUDE/testing.md +278 -0
- data/CLAUDE.md +242 -0
- data/README.md +8 -0
- data/Rakefile +7 -0
- data/Steepfile +28 -0
- data/docs/api_coverage.md +105 -56
- 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 +231 -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 +162 -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 +58 -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/mouse.rb +273 -64
- data/lib/puppeteer/network_event_manager.rb +7 -0
- data/lib/puppeteer/network_manager.rb +393 -112
- data/lib/puppeteer/page/screenshot_task_queue.rb +14 -4
- data/lib/puppeteer/page.rb +568 -226
- data/lib/puppeteer/puppeteer.rb +171 -64
- data/lib/puppeteer/query_handler_manager.rb +112 -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 +20 -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 +305 -0
- data/sig/puppeteer/execution_context.rbs +87 -0
- data/sig/puppeteer/frame.rbs +226 -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/mouse.rbs +113 -0
- data/sig/puppeteer/page.rbs +515 -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 +116 -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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
1
3
|
class Puppeteer::ExecutionContext
|
|
2
4
|
include Puppeteer::IfPresent
|
|
3
5
|
using Puppeteer::DefineAsyncMethod
|
|
@@ -109,7 +111,7 @@ class Puppeteer::ExecutionContext
|
|
|
109
111
|
# But we don't support the syntax here.
|
|
110
112
|
|
|
111
113
|
result = client.send_message('Runtime.callFunctionOn',
|
|
112
|
-
functionDeclaration:
|
|
114
|
+
functionDeclaration: function_declaration,
|
|
113
115
|
executionContextId: context_id,
|
|
114
116
|
arguments: converted_args,
|
|
115
117
|
returnByValue: @return_by_value,
|
|
@@ -146,6 +148,10 @@ class Puppeteer::ExecutionContext
|
|
|
146
148
|
# if (Object.is(arg, NaN))
|
|
147
149
|
# return { unserializableValue: 'NaN' };
|
|
148
150
|
@args.map do |arg|
|
|
151
|
+
if recursive_object?(arg)
|
|
152
|
+
raise EvaluationError.new('Recursive objects are not allowed.')
|
|
153
|
+
end
|
|
154
|
+
|
|
149
155
|
if arg && arg.is_a?(Puppeteer::JSHandle)
|
|
150
156
|
if arg.context != @execution_context
|
|
151
157
|
raise EvaluationError.new('JSHandles can be evaluated only in the context they were created!')
|
|
@@ -154,12 +160,141 @@ class Puppeteer::ExecutionContext
|
|
|
154
160
|
end
|
|
155
161
|
|
|
156
162
|
arg.remote_object.converted_arg
|
|
163
|
+
elsif arg.is_a?(Regexp)
|
|
164
|
+
{ value: arg.source }
|
|
157
165
|
else
|
|
158
166
|
{ value: arg }
|
|
159
167
|
end
|
|
160
168
|
end
|
|
161
169
|
end
|
|
162
170
|
|
|
171
|
+
# @rbs value: untyped -- Value to check for cycles
|
|
172
|
+
# @rbs stack: Hash[Integer, bool]? -- Current stack for cycle detection
|
|
173
|
+
# @rbs visited: Hash[Integer, bool]? -- Already visited objects
|
|
174
|
+
# @rbs return: bool -- Whether the value contains cycles
|
|
175
|
+
private def recursive_object?(value, stack = nil, visited = nil)
|
|
176
|
+
return false unless value.is_a?(Hash) || value.is_a?(Array)
|
|
177
|
+
|
|
178
|
+
stack ||= {}
|
|
179
|
+
visited ||= {}
|
|
180
|
+
object_id = value.object_id
|
|
181
|
+
return true if stack[object_id]
|
|
182
|
+
return false if visited[object_id]
|
|
183
|
+
|
|
184
|
+
visited[object_id] = true
|
|
185
|
+
stack[object_id] = true
|
|
186
|
+
|
|
187
|
+
has_cycle =
|
|
188
|
+
if value.is_a?(Array)
|
|
189
|
+
value.any? { |item| recursive_object?(item, stack, visited) }
|
|
190
|
+
else
|
|
191
|
+
value.any? do |key, item|
|
|
192
|
+
recursive_object?(key, stack, visited) || recursive_object?(item, stack, visited)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
stack.delete(object_id)
|
|
197
|
+
has_cycle
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# @rbs return: String -- JavaScript function declaration
|
|
201
|
+
private def function_declaration
|
|
202
|
+
expression = @return_by_value ? serialized_function_declaration : @expression
|
|
203
|
+
"#{expression}\n#{suffix}\n"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# @rbs return: String -- Wrapped function with serialization
|
|
207
|
+
private def serialized_function_declaration
|
|
208
|
+
<<~JAVASCRIPT
|
|
209
|
+
async function(...__args) {
|
|
210
|
+
const fn = #{@expression};
|
|
211
|
+
const result = await fn.apply(globalThis, __args);
|
|
212
|
+
#{serialized_value_source}
|
|
213
|
+
}
|
|
214
|
+
JAVASCRIPT
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# @rbs return: String -- JavaScript serialization helper source
|
|
218
|
+
private def serialized_value_source
|
|
219
|
+
<<~JAVASCRIPT
|
|
220
|
+
const OMIT = Symbol('omit');
|
|
221
|
+
const UNDEFINED_SENTINEL = { __puppeteer_unserializable__: true };
|
|
222
|
+
const NUMBER_SENTINEL = (value) => ({ __puppeteer_number__: value });
|
|
223
|
+
const REGEXP_SENTINEL = (source, flags) => ({ __puppeteer_regexp__: { source, flags } });
|
|
224
|
+
const seen = new WeakSet();
|
|
225
|
+
const isPlainObject = (value) => {
|
|
226
|
+
if (!value || typeof value !== 'object') {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
const proto = Object.getPrototypeOf(value);
|
|
230
|
+
return proto === Object.prototype || proto === null;
|
|
231
|
+
};
|
|
232
|
+
const serialize = (value, allowOmit) => {
|
|
233
|
+
if (value === undefined) {
|
|
234
|
+
return allowOmit ? OMIT : UNDEFINED_SENTINEL;
|
|
235
|
+
}
|
|
236
|
+
if (typeof value === 'symbol' || typeof value === 'function') {
|
|
237
|
+
return UNDEFINED_SENTINEL;
|
|
238
|
+
}
|
|
239
|
+
if (typeof value === 'number') {
|
|
240
|
+
if (Number.isNaN(value)) {
|
|
241
|
+
return NUMBER_SENTINEL('NaN');
|
|
242
|
+
}
|
|
243
|
+
if (Object.is(value, -0)) {
|
|
244
|
+
return NUMBER_SENTINEL('-0');
|
|
245
|
+
}
|
|
246
|
+
if (value === Infinity) {
|
|
247
|
+
return NUMBER_SENTINEL('Infinity');
|
|
248
|
+
}
|
|
249
|
+
if (value === -Infinity) {
|
|
250
|
+
return NUMBER_SENTINEL('-Infinity');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (typeof value === 'bigint') {
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
if (value === null || typeof value !== 'object') {
|
|
257
|
+
return value;
|
|
258
|
+
}
|
|
259
|
+
if (typeof Window !== 'undefined' && value === window) {
|
|
260
|
+
return UNDEFINED_SENTINEL;
|
|
261
|
+
}
|
|
262
|
+
if (typeof ImageBitmap !== 'undefined' && value instanceof ImageBitmap) {
|
|
263
|
+
return UNDEFINED_SENTINEL;
|
|
264
|
+
}
|
|
265
|
+
if (value instanceof RegExp) {
|
|
266
|
+
return REGEXP_SENTINEL(value.source, value.flags || '');
|
|
267
|
+
}
|
|
268
|
+
if (value instanceof Promise) {
|
|
269
|
+
return {};
|
|
270
|
+
}
|
|
271
|
+
if (seen.has(value)) {
|
|
272
|
+
return UNDEFINED_SENTINEL;
|
|
273
|
+
}
|
|
274
|
+
seen.add(value);
|
|
275
|
+
if (Array.isArray(value)) {
|
|
276
|
+
return value.map((item) => {
|
|
277
|
+
const serialized = serialize(item, false);
|
|
278
|
+
return serialized === OMIT ? UNDEFINED_SENTINEL : serialized;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (!isPlainObject(value)) {
|
|
282
|
+
return UNDEFINED_SENTINEL;
|
|
283
|
+
}
|
|
284
|
+
const result = {};
|
|
285
|
+
for (const [key, val] of Object.entries(value)) {
|
|
286
|
+
const serialized = serialize(val, true);
|
|
287
|
+
if (serialized === OMIT) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
result[key] = serialized;
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
};
|
|
294
|
+
return serialize(result, false);
|
|
295
|
+
JAVASCRIPT
|
|
296
|
+
end
|
|
297
|
+
|
|
163
298
|
# /**
|
|
164
299
|
# * @param {!Error} error
|
|
165
300
|
# * @return {!Protocol.Runtime.evaluateReturnValue}
|
|
@@ -180,7 +315,7 @@ class Puppeteer::ExecutionContext
|
|
|
180
315
|
end
|
|
181
316
|
end
|
|
182
317
|
|
|
183
|
-
class EvaluationError <
|
|
318
|
+
class EvaluationError < Puppeteer::Error; end
|
|
184
319
|
|
|
185
320
|
# @param return_by_value [Boolean]
|
|
186
321
|
# @param page_function [String]
|
|
@@ -190,7 +325,7 @@ class Puppeteer::ExecutionContext
|
|
|
190
325
|
# https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Object_initializer
|
|
191
326
|
# But we don't support the syntax here.
|
|
192
327
|
js_object =
|
|
193
|
-
if
|
|
328
|
+
if function_string?(page_function)
|
|
194
329
|
JavaScriptFunction.new(self, page_function, args, return_by_value)
|
|
195
330
|
else
|
|
196
331
|
JavaScriptExpression.new(self, page_function, return_by_value)
|
|
@@ -201,4 +336,14 @@ class Puppeteer::ExecutionContext
|
|
|
201
336
|
context_id: @context_id,
|
|
202
337
|
)
|
|
203
338
|
end
|
|
339
|
+
|
|
340
|
+
# @rbs page_function: String -- JavaScript code to check
|
|
341
|
+
# @rbs return: bool -- Whether the code represents a function
|
|
342
|
+
private def function_string?(page_function)
|
|
343
|
+
stripped = page_function.lstrip
|
|
344
|
+
return true if page_function.include?('=>')
|
|
345
|
+
return true if stripped.start_with?('async function')
|
|
346
|
+
|
|
347
|
+
stripped.start_with?('function')
|
|
348
|
+
end
|
|
204
349
|
end
|
|
@@ -25,5 +25,11 @@ class Puppeteer::FileChooser
|
|
|
25
25
|
raise 'Cannot cancel FileChooser which is already handled!'
|
|
26
26
|
end
|
|
27
27
|
@handled = true
|
|
28
|
+
js = <<~JAVASCRIPT
|
|
29
|
+
(element) => {
|
|
30
|
+
element.dispatchEvent(new Event('cancel', { bubbles: true }));
|
|
31
|
+
}
|
|
32
|
+
JAVASCRIPT
|
|
33
|
+
@element.evaluate(js)
|
|
28
34
|
end
|
|
29
35
|
end
|