puppeteer-ruby 0.0.26 → 0.31.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.
@@ -314,33 +314,23 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
314
314
  end
315
315
  end
316
316
 
317
- # `$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
317
+ private def query_handler_manager
318
+ Puppeteer::QueryHandlerManager.instance
319
+ end
320
+
321
+ # `$()` in JavaScript.
318
322
  # @param selector [String]
319
- def S(selector)
320
- handle = evaluate_handle(
321
- '(element, selector) => element.querySelector(selector)',
322
- selector,
323
- )
324
- element = handle.as_element
325
-
326
- if element
327
- return element
328
- end
329
- handle.dispose
330
- nil
323
+ def query_selector(selector)
324
+ query_handler_manager.detect_query_handler(selector).query_one(self)
331
325
  end
326
+ alias_method :S, :query_selector
332
327
 
333
- # `$$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
328
+ # `$$()` in JavaScript.
334
329
  # @param selector [String]
335
- def SS(selector)
336
- handles = evaluate_handle(
337
- '(element, selector) => element.querySelectorAll(selector)',
338
- selector,
339
- )
340
- properties = handles.properties
341
- handles.dispose
342
- properties.values.map(&:as_element).compact
330
+ def query_selector_all(selector)
331
+ query_handler_manager.detect_query_handler(selector).query_all(self)
343
332
  end
333
+ alias_method :SS, :query_selector_all
344
334
 
345
335
  class ElementNotFoundError < StandardError
346
336
  def initialize(selector)
@@ -348,12 +338,12 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
348
338
  end
349
339
  end
350
340
 
351
- # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
341
+ # `$eval()` in JavaScript.
352
342
  # @param selector [String]
353
343
  # @param page_function [String]
354
344
  # @return [Object]
355
- def Seval(selector, page_function, *args)
356
- element_handle = S(selector)
345
+ def eval_on_selector(selector, page_function, *args)
346
+ element_handle = query_selector(selector)
357
347
  unless element_handle
358
348
  raise ElementNotFoundError.new(selector)
359
349
  end
@@ -362,25 +352,24 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
362
352
 
363
353
  result
364
354
  end
355
+ alias_method :Seval, :eval_on_selector
365
356
 
366
- define_async_method :async_Seval
357
+ define_async_method :async_eval_on_selector
367
358
 
368
- # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
359
+ # `$$eval()` in JavaScript.
369
360
  # @param selector [String]
370
361
  # @param page_function [String]
371
362
  # @return [Object]
372
- def SSeval(selector, page_function, *args)
373
- handles = evaluate_handle(
374
- '(element, selector) => Array.from(element.querySelectorAll(selector))',
375
- selector,
376
- )
363
+ def eval_on_selector_all(selector, page_function, *args)
364
+ handles = query_handler_manager.detect_query_handler(selector).query_all_array(self)
377
365
  result = handles.evaluate(page_function, *args)
378
366
  handles.dispose
379
367
 
380
368
  result
381
369
  end
370
+ alias_method :SSeval, :eval_on_selector_all
382
371
 
383
- define_async_method :async_SSeval
372
+ define_async_method :async_eval_on_selector_all
384
373
 
385
374
  # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
386
375
  # @param expression [String]
@@ -430,4 +419,10 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
430
419
  # https://en.wikipedia.org/wiki/Polygon#Simple_polygons
431
420
  quad.zip(quad.rotate).map { |p1, p2| (p1.x * p2.y - p2.x * p1.y) / 2 }.reduce(:+).abs
432
421
  end
422
+
423
+ # used in AriaQueryHandler
424
+ def query_ax_tree(accessible_name: nil, role: nil)
425
+ @remote_object.query_ax_tree(@client,
426
+ accessible_name: accessible_name, role: role)
427
+ end
433
428
  end
data/lib/puppeteer/env.rb CHANGED
@@ -14,6 +14,10 @@ class Puppeteer::Env
14
14
  def darwin?
15
15
  RUBY_PLATFORM.include?('darwin')
16
16
  end
17
+
18
+ def windows?
19
+ RUBY_PLATFORM =~ /mswin|mingw|cygwin/
20
+ end
17
21
  end
18
22
 
19
23
  class Puppeteer
@@ -12,10 +12,21 @@ class Puppeteer::ExecutionContext
12
12
  @client = client
13
13
  @world = world
14
14
  @context_id = context_payload['id']
15
+ @context_name = context_payload['name']
15
16
  end
16
17
 
17
18
  attr_reader :client, :world
18
19
 
20
+ # only used in DOMWorld
21
+ private def _context_id
22
+ @context_id
23
+ end
24
+
25
+ # only used in DOMWorld::BindingFunction#add_binding_to_context
26
+ private def _context_name
27
+ @context_name
28
+ end
29
+
19
30
  # @return [Puppeteer::Frame]
20
31
  def frame
21
32
  if_present(@world) do |world|
@@ -223,6 +234,7 @@ class Puppeteer::ExecutionContext
223
234
  remote_object: Puppeteer::RemoteObject.new(response["object"]),
224
235
  )
225
236
  end
237
+ private define_async_method :async_adopt_backend_node_id
226
238
 
227
239
  # @param element_handle [Puppeteer::ElementHandle]
228
240
  # @return [Puppeteer::ElementHandle]
@@ -61,14 +61,15 @@ class Puppeteer::Frame
61
61
 
62
62
  define_async_method :async_evaluate
63
63
 
64
- # `$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
64
+ # `$()` in JavaScript.
65
65
  # @param {string} selector
66
66
  # @return {!Promise<?Puppeteer.ElementHandle>}
67
- def S(selector)
68
- @main_world.S(selector)
67
+ def query_selector(selector)
68
+ @main_world.query_selector(selector)
69
69
  end
70
+ alias_method :S, :query_selector
70
71
 
71
- define_async_method :async_S
72
+ define_async_method :async_query_selector
72
73
 
73
74
  # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
74
75
  # @param {string} expression
@@ -79,36 +80,39 @@ class Puppeteer::Frame
79
80
 
80
81
  define_async_method :async_Sx
81
82
 
82
- # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
83
+ # `$eval()` in JavaScript.
83
84
  # @param {string} selector
84
85
  # @param {Function|string} pageFunction
85
86
  # @param {!Array<*>} args
86
87
  # @return {!Promise<(!Object|undefined)>}
87
- def Seval(selector, page_function, *args)
88
- @main_world.Seval(selector, page_function, *args)
88
+ def eval_on_selector(selector, page_function, *args)
89
+ @main_world.eval_on_selector(selector, page_function, *args)
89
90
  end
91
+ alias_method :Seval, :eval_on_selector
90
92
 
91
- define_async_method :async_Seval
93
+ define_async_method :async_eval_on_selector
92
94
 
93
- # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
95
+ # `$$eval()` in JavaScript.
94
96
  # @param {string} selector
95
97
  # @param {Function|string} pageFunction
96
98
  # @param {!Array<*>} args
97
99
  # @return {!Promise<(!Object|undefined)>}
98
- def SSeval(selector, page_function, *args)
99
- @main_world.SSeval(selector, page_function, *args)
100
+ def eval_on_selector_all(selector, page_function, *args)
101
+ @main_world.eval_on_selector_all(selector, page_function, *args)
100
102
  end
103
+ alias_method :SSeval, :eval_on_selector_all
101
104
 
102
- define_async_method :async_SSeval
105
+ define_async_method :async_eval_on_selector_all
103
106
 
104
- # `$$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
107
+ # `$$()` in JavaScript.
105
108
  # @param {string} selector
106
109
  # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
107
- def SS(selector)
108
- @main_world.SS(selector)
110
+ def query_selector_all(selector)
111
+ @main_world.query_selector_all(selector)
109
112
  end
113
+ alias_method :SS, :query_selector_all
110
114
 
111
- define_async_method :async_SS
115
+ define_async_method :async_query_selector_all
112
116
 
113
117
  # @return [String]
114
118
  def content
@@ -149,16 +153,19 @@ class Puppeteer::Frame
149
153
  @detached
150
154
  end
151
155
 
152
- # @param style_tag [Puppeteer::Page::ScriptTag]
153
- # @return {!Promise<!ElementHandle>}
154
- def add_script_tag(script_tag)
155
- @main_world.add_script_tag(script_tag)
156
+ # @param url [String?]
157
+ # @param path [String?]
158
+ # @param content [String?]
159
+ # @param type [String?]
160
+ def add_script_tag(url: nil, path: nil, content: nil, type: nil)
161
+ @main_world.add_script_tag(url: url, path: path, content: content, type: type)
156
162
  end
157
163
 
158
- # @param style_tag [Puppeteer::Page::StyleTag]
159
- # @return {!Promise<!ElementHandle>}
160
- def add_style_tag(style_tag)
161
- @main_world.add_style_tag(style_tag)
164
+ # @param url [String?]
165
+ # @param path [String?]
166
+ # @param content [String?]
167
+ def add_style_tag(url: nil, path: nil, content: nil)
168
+ @main_world.add_style_tag(url: url, path: path, content: content)
162
169
  end
163
170
 
164
171
  # @param selector [String]
@@ -32,6 +32,14 @@ module Puppeteer::Launcher
32
32
  when Firefox
33
33
  '/Applications/Firefox Nightly.app/Contents/MacOS/firefox'
34
34
  end
35
+ elsif Puppeteer.env.windows?
36
+ case self
37
+ when Chrome
38
+ 'C:\Program Files\Google\Chrome\Application\chrome.exe'
39
+ # 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
40
+ when Firefox
41
+ 'C:\Program Files\Firefox Nightly\firefox.exe'
42
+ end
35
43
  else
36
44
  case self
37
45
  when Chrome
@@ -92,7 +92,7 @@ module Puppeteer::Launcher
92
92
  '--disable-default-apps',
93
93
  '--disable-dev-shm-usage',
94
94
  '--disable-extensions',
95
- '--disable-features=TranslateUI',
95
+ '--disable-features=Translate',
96
96
  '--disable-hang-monitor',
97
97
  '--disable-ipc-flooding-protection',
98
98
  '--disable-popup-blocking',
@@ -105,6 +105,9 @@ module Puppeteer::Launcher
105
105
  '--enable-automation',
106
106
  '--password-store=basic',
107
107
  '--use-mock-keychain',
108
+ # TODO(sadym): remove '--enable-blink-features=IdleDetection'
109
+ # once IdleDetection is turned on by default.
110
+ '--enable-blink-features=IdleDetection',
108
111
  ]
109
112
 
110
113
  if chrome_arg_options.user_data_dir
@@ -1,4 +1,5 @@
1
1
  require 'base64'
2
+ require 'json'
2
3
  require "stringio"
3
4
 
4
5
  require_relative './page/pdf_options'
@@ -98,7 +99,9 @@ class Puppeteer::Page
98
99
  @client.on_event('Page.loadEventFired') do |event|
99
100
  emit_event(PageEmittedEvents::Load)
100
101
  end
101
- # client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
102
+ @client.on('Runtime.consoleAPICalled') do |event|
103
+ handle_console_api(event)
104
+ end
102
105
  # client.on('Runtime.bindingCalled', event => this._onBindingCalled(event));
103
106
  @client.on_event('Page.javascriptDialogOpening') do |event|
104
107
  handle_dialog_opening(event)
@@ -276,23 +279,25 @@ class Puppeteer::Page
276
279
  @timeout_settings.default_timeout = timeout
277
280
  end
278
281
 
279
- # `$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
282
+ # `$()` in JavaScript.
280
283
  # @param {string} selector
281
284
  # @return {!Promise<?Puppeteer.ElementHandle>}
282
- def S(selector)
283
- main_frame.S(selector)
285
+ def query_selector(selector)
286
+ main_frame.query_selector(selector)
284
287
  end
288
+ alias_method :S, :query_selector
285
289
 
286
- define_async_method :async_S
290
+ define_async_method :async_query_selector
287
291
 
288
- # `$$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
292
+ # `$$()` in JavaScript.
289
293
  # @param {string} selector
290
294
  # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
291
- def SS(selector)
292
- main_frame.SS(selector)
295
+ def query_selector_all(selector)
296
+ main_frame.query_selector_all(selector)
293
297
  end
298
+ alias_method :SS, :query_selector_all
294
299
 
295
- define_async_method :async_SS
300
+ define_async_method :async_query_selector_all
296
301
 
297
302
  # @param {Function|string} pageFunction
298
303
  # @param {!Array<*>} args
@@ -311,25 +316,27 @@ class Puppeteer::Page
311
316
  context.query_objects(prototype_handle)
312
317
  end
313
318
 
314
- # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
319
+ # `$eval()` in JavaScript.
315
320
  # @param selector [String]
316
321
  # @param page_function [String]
317
322
  # @return [Object]
318
- def Seval(selector, page_function, *args)
319
- main_frame.Seval(selector, page_function, *args)
323
+ def eval_on_selector(selector, page_function, *args)
324
+ main_frame.eval_on_selector(selector, page_function, *args)
320
325
  end
326
+ alias_method :Seval, :eval_on_selector
321
327
 
322
- define_async_method :async_Seval
328
+ define_async_method :async_eval_on_selector
323
329
 
324
- # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
330
+ # `$$eval()` in JavaScript.
325
331
  # @param selector [String]
326
332
  # @param page_function [String]
327
333
  # @return [Object]
328
- def SSeval(selector, page_function, *args)
329
- main_frame.SSeval(selector, page_function, *args)
334
+ def eval_on_selector_all(selector, page_function, *args)
335
+ main_frame.eval_on_selector_all(selector, page_function, *args)
330
336
  end
337
+ alias_method :SSeval, :eval_on_selector_all
331
338
 
332
- define_async_method :async_SSeval
339
+ define_async_method :async_eval_on_selector_all
333
340
 
334
341
  # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
335
342
  # @param {string} expression
@@ -369,37 +376,19 @@ class Puppeteer::Page
369
376
  end
370
377
  end
371
378
 
372
- class ScriptTag
373
- # @param {!{content?: string, path?: string, type?: string, url?: string}} options
374
- def initialize(content: nil, path: nil, type: nil, url: nil)
375
- @content = content
376
- @path = path
377
- @type = type
378
- @url = url
379
- end
380
- attr_reader :content, :path, :type, :url
381
- end
382
-
383
- # @param style_tag [Puppeteer::Page::ScriptTag]
384
- # @return {!Promise<!ElementHandle>}
385
- def add_script_tag(script_tag)
386
- main_frame.add_script_tag(script_tag)
387
- end
388
-
389
- class StyleTag
390
- # @param {!{content?: string, path?: string, url?: string}} options
391
- def initialize(content: nil, path: nil, url: nil)
392
- @content = content
393
- @path = path
394
- @url = url
395
- end
396
- attr_reader :content, :path, :url
379
+ # @param url [String?]
380
+ # @param path [String?]
381
+ # @param content [String?]
382
+ # @param type [String?]
383
+ def add_script_tag(url: nil, path: nil, content: nil, type: nil)
384
+ main_frame.add_script_tag(url: url, path: path, content: content, type: type)
397
385
  end
398
386
 
399
- # @param style_tag [Puppeteer::Page::StyleTag]
400
- # @return {!Promise<!ElementHandle>}
401
- def add_style_tag(style_tag)
402
- main_frame.add_style_tag(style_tag)
387
+ # @param url [String?]
388
+ # @param path [String?]
389
+ # @param content [String?]
390
+ def add_style_tag(url: nil, path: nil, content: nil)
391
+ main_frame.add_style_tag(url: url, path: path, content: content)
403
392
  end
404
393
 
405
394
  # /**
@@ -490,30 +479,31 @@ class Puppeteer::Page
490
479
  emit_event(PageEmittedEvents::PageError, err)
491
480
  end
492
481
 
493
- # /**
494
- # * @param {!Protocol.Runtime.consoleAPICalledPayload} event
495
- # */
496
- # async _onConsoleAPI(event) {
497
- # if (event.executionContextId === 0) {
498
- # // DevTools protocol stores the last 1000 console messages. These
499
- # // messages are always reported even for removed execution contexts. In
500
- # // this case, they are marked with executionContextId = 0 and are
501
- # // reported upon enabling Runtime agent.
502
- # //
503
- # // Ignore these messages since:
504
- # // - there's no execution context we can use to operate with message
505
- # // arguments
506
- # // - these messages are reported before Puppeteer clients can subscribe
507
- # // to the 'console'
508
- # // page event.
509
- # //
510
- # // @see https://github.com/puppeteer/puppeteer/issues/3865
511
- # return;
512
- # }
513
- # const context = this._frameManager.executionContextById(event.executionContextId);
514
- # const values = event.args.map(arg => createJSHandle(context, arg));
515
- # this._addConsoleMessage(event.type, values, event.stackTrace);
516
- # }
482
+ private def handle_console_api(event)
483
+ if event['executionContextId'] == 0
484
+ # DevTools protocol stores the last 1000 console messages. These
485
+ # messages are always reported even for removed execution contexts. In
486
+ # this case, they are marked with executionContextId = 0 and are
487
+ # reported upon enabling Runtime agent.
488
+ #
489
+ # Ignore these messages since:
490
+ # - there's no execution context we can use to operate with message
491
+ # arguments
492
+ # - these messages are reported before Puppeteer clients can subscribe
493
+ # to the 'console'
494
+ # page event.
495
+ #
496
+ # @see https://github.com/puppeteer/puppeteer/issues/3865
497
+ return
498
+ end
499
+
500
+ context = @frame_manager.execution_context_by_id(event['executionContextId'])
501
+ values = event['args'].map do |arg|
502
+ remote_object = Puppeteer::RemoteObject.new(arg)
503
+ Puppeteer::JSHandle.create(context: context, remote_object: remote_object)
504
+ end
505
+ add_console_message(event['type'], values, event['stackTrace'])
506
+ end
517
507
 
518
508
  # /**
519
509
  # * @param {!Protocol.Runtime.bindingCalledPayload} event
@@ -566,32 +556,23 @@ class Puppeteer::Page
566
556
  # }
567
557
  # }
568
558
 
569
- # /**
570
- # * @param {string} type
571
- # * @param {!Array<!Puppeteer.JSHandle>} args
572
- # * @param {Protocol.Runtime.StackTrace=} stackTrace
573
- # */
574
- # _addConsoleMessage(type, args, stackTrace) {
575
- # if (!this.listenerCount(PageEmittedEvents::Console)) {
576
- # args.forEach(arg => arg.dispose());
577
- # return;
578
- # }
579
- # const textTokens = [];
580
- # for (const arg of args) {
581
- # const remoteObject = arg._remoteObject;
582
- # if (remoteObject.objectId)
583
- # textTokens.push(arg.toString());
584
- # else
585
- # textTokens.push(helper.valueFromRemoteObject(remoteObject));
586
- # }
587
- # const location = stackTrace && stackTrace.callFrames.length ? {
588
- # url: stackTrace.callFrames[0].url,
589
- # lineNumber: stackTrace.callFrames[0].lineNumber,
590
- # columnNumber: stackTrace.callFrames[0].columnNumber,
591
- # } : {};
592
- # const message = new ConsoleMessage(type, textTokens.join(' '), args, location);
593
- # this.emit(PageEmittedEvents::Console, message);
594
- # }
559
+ private def add_console_message(type, args, stack_trace)
560
+ text_tokens = args.map { |arg| arg.remote_object.value }
561
+
562
+ call_frame = stack_trace['callFrames']&.first
563
+ location =
564
+ if call_frame
565
+ Puppeteer::ConsoleMessage::Location.new(
566
+ url: call_frame['url'],
567
+ line_number: call_frame['lineNumber'],
568
+ column_number: call_frame['columnNumber'],
569
+ )
570
+ else
571
+ nil
572
+ end
573
+ console_message = Puppeteer::ConsoleMessage.new(type, text_tokens.join(' '), args, location)
574
+ emit_event(PageEmittedEvents::Console, console_message)
575
+ end
595
576
 
596
577
  private def handle_dialog_opening(event)
597
578
  dialog_type = event['type']
@@ -829,6 +810,21 @@ class Puppeteer::Page
829
810
  end
830
811
  end
831
812
 
813
+ # @param is_user_active [Boolean]
814
+ # @param is_screen_unlocked [Boolean]
815
+ def emulate_idle_state(is_user_active: nil, is_screen_unlocked: nil)
816
+ overrides = {
817
+ isUserActive: is_user_active,
818
+ isScreenUnlocked: is_screen_unlocked,
819
+ }.compact
820
+
821
+ if overrides.empty?
822
+ @client.send_message('Emulation.clearIdleOverride')
823
+ else
824
+ @client.send_message('Emulation.setIdleOverride', overrides)
825
+ end
826
+ end
827
+
832
828
  # @param viewport [Viewport]
833
829
  def viewport=(viewport)
834
830
  needs_reload = @emulation_manager.emulate_viewport(viewport)
@@ -847,14 +843,41 @@ class Puppeteer::Page
847
843
 
848
844
  define_async_method :async_evaluate
849
845
 
850
- # /**
851
- # * @param {Function|string} pageFunction
852
- # * @param {!Array<*>} args
853
- # */
854
- # async evaluateOnNewDocument(pageFunction, ...args) {
855
- # const source = helper.evaluationString(pageFunction, ...args);
856
- # await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source });
857
- # }
846
+ class JavaScriptFunction
847
+ def initialize(expression, args)
848
+ @expression = expression
849
+ @args = args
850
+ end
851
+
852
+ def source
853
+ "(#{@expression})(#{arguments})"
854
+ end
855
+
856
+ private def arguments
857
+ @args.map { |arg| arg.nil? ? nil : JSON.dump(arg) }.join(", ")
858
+ end
859
+ end
860
+
861
+ class JavaScriptExpression
862
+ def initialize(expression)
863
+ @expression = expression
864
+ end
865
+
866
+ def source
867
+ @expression
868
+ end
869
+ end
870
+
871
+ def evaluate_on_new_document(page_function, *args)
872
+ source =
873
+ if ['=>', 'async', 'function'].any? { |keyword| page_function.include?(keyword) }
874
+ JavaScriptFunction.new(page_function, args).source
875
+ else
876
+ JavaScriptExpression.new(page_function).source
877
+ end
878
+
879
+ @client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
880
+ end
858
881
 
859
882
  # @param {boolean} enabled
860
883
  def cache_enabled=(enabled)