puppeteer-ruby 0.28.1 → 0.31.3

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,10 +14,14 @@ 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
- class Puppeteer
20
- def self.env
21
- Puppeteer::Env.new
23
+ module Puppeteer
24
+ module_function def env
25
+ ::Puppeteer::Env.new
22
26
  end
23
27
  end
@@ -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
@@ -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)
@@ -199,6 +202,7 @@ class Puppeteer::Page
199
202
  end
200
203
 
201
204
  attr_reader :javascript_enabled, :target
205
+ alias_method :javascript_enabled?, :javascript_enabled
202
206
 
203
207
  def browser
204
208
  @target.browser
@@ -276,23 +280,25 @@ class Puppeteer::Page
276
280
  @timeout_settings.default_timeout = timeout
277
281
  end
278
282
 
279
- # `$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
283
+ # `$()` in JavaScript.
280
284
  # @param {string} selector
281
285
  # @return {!Promise<?Puppeteer.ElementHandle>}
282
- def S(selector)
283
- main_frame.S(selector)
286
+ def query_selector(selector)
287
+ main_frame.query_selector(selector)
284
288
  end
289
+ alias_method :S, :query_selector
285
290
 
286
- define_async_method :async_S
291
+ define_async_method :async_query_selector
287
292
 
288
- # `$$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
293
+ # `$$()` in JavaScript.
289
294
  # @param {string} selector
290
295
  # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
291
- def SS(selector)
292
- main_frame.SS(selector)
296
+ def query_selector_all(selector)
297
+ main_frame.query_selector_all(selector)
293
298
  end
299
+ alias_method :SS, :query_selector_all
294
300
 
295
- define_async_method :async_SS
301
+ define_async_method :async_query_selector_all
296
302
 
297
303
  # @param {Function|string} pageFunction
298
304
  # @param {!Array<*>} args
@@ -311,25 +317,27 @@ class Puppeteer::Page
311
317
  context.query_objects(prototype_handle)
312
318
  end
313
319
 
314
- # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
320
+ # `$eval()` in JavaScript.
315
321
  # @param selector [String]
316
322
  # @param page_function [String]
317
323
  # @return [Object]
318
- def Seval(selector, page_function, *args)
319
- main_frame.Seval(selector, page_function, *args)
324
+ def eval_on_selector(selector, page_function, *args)
325
+ main_frame.eval_on_selector(selector, page_function, *args)
320
326
  end
327
+ alias_method :Seval, :eval_on_selector
321
328
 
322
- define_async_method :async_Seval
329
+ define_async_method :async_eval_on_selector
323
330
 
324
- # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
331
+ # `$$eval()` in JavaScript.
325
332
  # @param selector [String]
326
333
  # @param page_function [String]
327
334
  # @return [Object]
328
- def SSeval(selector, page_function, *args)
329
- main_frame.SSeval(selector, page_function, *args)
335
+ def eval_on_selector_all(selector, page_function, *args)
336
+ main_frame.eval_on_selector_all(selector, page_function, *args)
330
337
  end
338
+ alias_method :SSeval, :eval_on_selector_all
331
339
 
332
- define_async_method :async_SSeval
340
+ define_async_method :async_eval_on_selector_all
333
341
 
334
342
  # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
335
343
  # @param {string} expression
@@ -369,37 +377,19 @@ class Puppeteer::Page
369
377
  end
370
378
  end
371
379
 
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
380
+ # @param url [String?]
381
+ # @param path [String?]
382
+ # @param content [String?]
383
+ # @param type [String?]
384
+ def add_script_tag(url: nil, path: nil, content: nil, type: nil)
385
+ main_frame.add_script_tag(url: url, path: path, content: content, type: type)
397
386
  end
398
387
 
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)
388
+ # @param url [String?]
389
+ # @param path [String?]
390
+ # @param content [String?]
391
+ def add_style_tag(url: nil, path: nil, content: nil)
392
+ main_frame.add_style_tag(url: url, path: path, content: content)
403
393
  end
404
394
 
405
395
  # /**
@@ -490,30 +480,31 @@ class Puppeteer::Page
490
480
  emit_event(PageEmittedEvents::PageError, err)
491
481
  end
492
482
 
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
- # }
483
+ private def handle_console_api(event)
484
+ if event['executionContextId'] == 0
485
+ # DevTools protocol stores the last 1000 console messages. These
486
+ # messages are always reported even for removed execution contexts. In
487
+ # this case, they are marked with executionContextId = 0 and are
488
+ # reported upon enabling Runtime agent.
489
+ #
490
+ # Ignore these messages since:
491
+ # - there's no execution context we can use to operate with message
492
+ # arguments
493
+ # - these messages are reported before Puppeteer clients can subscribe
494
+ # to the 'console'
495
+ # page event.
496
+ #
497
+ # @see https://github.com/puppeteer/puppeteer/issues/3865
498
+ return
499
+ end
500
+
501
+ context = @frame_manager.execution_context_by_id(event['executionContextId'])
502
+ values = event['args'].map do |arg|
503
+ remote_object = Puppeteer::RemoteObject.new(arg)
504
+ Puppeteer::JSHandle.create(context: context, remote_object: remote_object)
505
+ end
506
+ add_console_message(event['type'], values, event['stackTrace'])
507
+ end
517
508
 
518
509
  # /**
519
510
  # * @param {!Protocol.Runtime.bindingCalledPayload} event
@@ -566,32 +557,23 @@ class Puppeteer::Page
566
557
  # }
567
558
  # }
568
559
 
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
- # }
560
+ private def add_console_message(type, args, stack_trace)
561
+ text_tokens = args.map { |arg| arg.remote_object.value }
562
+
563
+ call_frame = stack_trace['callFrames']&.first
564
+ location =
565
+ if call_frame
566
+ Puppeteer::ConsoleMessage::Location.new(
567
+ url: call_frame['url'],
568
+ line_number: call_frame['lineNumber'],
569
+ column_number: call_frame['columnNumber'],
570
+ )
571
+ else
572
+ nil
573
+ end
574
+ console_message = Puppeteer::ConsoleMessage.new(type, text_tokens.join(' '), args, location)
575
+ emit_event(PageEmittedEvents::Console, console_message)
576
+ end
595
577
 
596
578
  private def handle_dialog_opening(event)
597
579
  dialog_type = event['type']
@@ -862,14 +844,41 @@ class Puppeteer::Page
862
844
 
863
845
  define_async_method :async_evaluate
864
846
 
865
- # /**
866
- # * @param {Function|string} pageFunction
867
- # * @param {!Array<*>} args
868
- # */
869
- # async evaluateOnNewDocument(pageFunction, ...args) {
870
- # const source = helper.evaluationString(pageFunction, ...args);
871
- # await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source });
872
- # }
847
+ class JavaScriptFunction
848
+ def initialize(expression, args)
849
+ @expression = expression
850
+ @args = args
851
+ end
852
+
853
+ def source
854
+ "(#{@expression})(#{arguments})"
855
+ end
856
+
857
+ private def arguments
858
+ @args.map { |arg| arg.nil? ? nil : JSON.dump(arg) }.join(", ")
859
+ end
860
+ end
861
+
862
+ class JavaScriptExpression
863
+ def initialize(expression)
864
+ @expression = expression
865
+ end
866
+
867
+ def source
868
+ @expression
869
+ end
870
+ end
871
+
872
+ def evaluate_on_new_document(page_function, *args)
873
+ source =
874
+ if ['=>', 'async', 'function'].any? { |keyword| page_function.include?(keyword) }
875
+ JavaScriptFunction.new(page_function, args).source
876
+ else
877
+ JavaScriptExpression.new(page_function).source
878
+ end
879
+
880
+ @client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
881
+ end
873
882
 
874
883
  # @param {boolean} enabled
875
884
  def cache_enabled=(enabled)
@@ -980,7 +989,7 @@ class Puppeteer::Page
980
989
 
981
990
  def read
982
991
  out = StringIO.new
983
- File.open(@path, 'w') do |file|
992
+ File.open(@path, 'wb') do |file|
984
993
  eof = false
985
994
  until eof
986
995
  response = @client.send_message('IO.read', handle: @handle)