puppeteer-ruby 0.0.26 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)