puppeteer-ruby 0.37.3 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84ede7b4fc083303903e960a08d2a21b44866b2b47662489005e09e6e955df01
4
- data.tar.gz: aba2c71b916d6779560a200fff6685cb9f4ba83dc2b04e092926fb121add3155
3
+ metadata.gz: af966d033f8cff7c99c121805ffe7dde6f4d73cfe0fa9f34937b9eedbeafd9b8
4
+ data.tar.gz: 3578653749391fd953144e21160beb6baee5eb6463b43b8cecafcf3f3433bda7
5
5
  SHA512:
6
- metadata.gz: 3b6a5cc66476c8954ab1ae942df3be6ad161ffe3aeb9b1ea51bde681f6cd9cf32d61d251980ab5d7f0907923b5ac42871cd7e99fbb55f0c00205ee231190f595
7
- data.tar.gz: 7329fc6a4f8d1933efee34180c6d051e73613da2189b2af5226629d681c9f4b52abadf5eb548f45cb3af37d3ab1f093067b5e67e10e8c3cde09815a4fa3a0e4c
6
+ metadata.gz: 9ae375c2763cd2fd597f72b0e82ccae84eba414cb4e1b7b3e0067c250c869020118f58fc1d74e0bfe0bb512318501fcedd5c1a25f3725de44a29e01393227765
7
+ data.tar.gz: 4dec50af05864ec0410e4d99bbec2cdccfbf06412eb21b3f923be4de892244510ba4a5d553dafc04b62d6de6381caf852238a9e971d2ba2d30c3009e78c9e048
data/.rubocop.yml CHANGED
@@ -149,6 +149,8 @@ Style/MethodCallWithArgsParentheses:
149
149
  # utils
150
150
  - debug_print
151
151
  - debug_puts
152
+ - exit
153
+ - puts
152
154
 
153
155
  Style/MethodCallWithoutArgsParentheses:
154
156
  Enabled: true
data/CHANGELOG.md CHANGED
@@ -1,7 +1,31 @@
1
- ### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.3...master)]
1
+ ### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.39.0...main)]
2
2
 
3
3
  - xxx
4
4
 
5
+ ### 0.39.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.38.0...0.39.0)]
6
+
7
+ New features:
8
+
9
+ - Puppeteer 12.0 functionalities
10
+
11
+ ### 0.38.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.4...0.38.0)]
12
+
13
+ New features:
14
+
15
+ - Puppeteer 11.0 functionalities
16
+ * OOP iframe support
17
+ * Customize debugging port
18
+ * HTTPRequest#initiator
19
+ * Customize temp directory for user data
20
+ * Add webp to screenshot quality option allow list
21
+
22
+
23
+ ### 0.37.4 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.3...0.37.4)]
24
+
25
+ Bugfix:
26
+
27
+ - Fix ElementHandle#screenshot to work.
28
+
5
29
  ### 0.37.3 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.2...0.37.3)]
6
30
 
7
31
  Improvement:
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # API coverages
2
- - Puppeteer version: v12.0.0
3
- - puppeteer-ruby version: 0.37.3
2
+ - Puppeteer version: v13.0.1
3
+ - puppeteer-ruby version: 0.40.0
4
4
 
5
5
  ## Puppeteer
6
6
 
@@ -134,7 +134,7 @@
134
134
  * viewport
135
135
  * ~~waitFor~~
136
136
  * waitForFileChooser => `#wait_for_file_chooser`
137
- * ~~waitForFrame~~
137
+ * waitForFrame => `#wait_for_frame`
138
138
  * waitForFunction => `#wait_for_function`
139
139
  * waitForNavigation => `#wait_for_navigation`
140
140
  * ~~waitForNetworkIdle~~
@@ -227,7 +227,7 @@
227
227
  * goto
228
228
  * hover
229
229
  * isDetached => `#detached?`
230
- * ~~isOOPFrame~~
230
+ * isOOPFrame => `#oop_frame?`
231
231
  * name
232
232
  * parentFrame => `#parent_frame`
233
233
  * select
@@ -308,7 +308,7 @@
308
308
  * finalizeInterceptions => `#finalize_interceptions`
309
309
  * frame
310
310
  * headers
311
- * ~~initiator~~
311
+ * initiator
312
312
  * isNavigationRequest => `#navigation_request?`
313
313
  * method
314
314
  * postData => `#post_data`
@@ -360,7 +360,7 @@
360
360
 
361
361
  * connection
362
362
  * detach
363
- * ~~id~~
363
+ * id
364
364
  * send
365
365
 
366
366
  ## Coverage
@@ -7,10 +7,10 @@ class Puppeteer::AriaQueryHandler
7
7
  private def parse_aria_selector(selector)
8
8
  known_attributes = %w(name role)
9
9
  query_options = {}
10
- attribute_regexp = /\[\s*(?<attribute>\w+)\s*=\s*"(?<value>\\.|[^"\\]*)"\s*\]/
10
+ attribute_regexp = /\[\s*(?<attribute>\w+)\s*=\s*(?<quote>"|')(?<value>\\.|.*?(?=\k<quote>))\k<quote>\s*\]/
11
11
  default_name = selector.gsub(attribute_regexp) do
12
12
  attribute = $1.strip
13
- value = $2
13
+ value = $3
14
14
  unless known_attributes.include?(attribute)
15
15
  raise ArgumentError.new("Unkown aria attribute \"#{attribute}\" in selector")
16
16
  end
@@ -36,10 +36,11 @@ class Puppeteer::AriaQueryHandler
36
36
  end
37
37
  end
38
38
 
39
- def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil)
39
+ def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil, root: nil)
40
+ # addHandlerToWorld
40
41
  binding_function = Puppeteer::DOMWorld::BindingFunction.new(
41
42
  name: 'ariaQuerySelector',
42
- proc: -> (selector) { query_one(dom_world.send(:document), selector) },
43
+ proc: -> (sel) { query_one(root || dom_world.send(:document), sel) },
43
44
  )
44
45
  dom_world.send(:wait_for_selector_in_page,
45
46
  '(_, selector) => globalThis.ariaQuerySelector(selector)',
@@ -47,7 +48,9 @@ class Puppeteer::AriaQueryHandler
47
48
  visible: visible,
48
49
  hidden: hidden,
49
50
  timeout: timeout,
50
- binding_function: binding_function)
51
+ binding_function: binding_function,
52
+ root: root,
53
+ )
51
54
  end
52
55
 
53
56
  def query_all(element, selector)
@@ -4,13 +4,17 @@ require 'timeout'
4
4
 
5
5
  # https://github.com/puppeteer/puppeteer/blob/master/lib/Launcher.js
6
6
  class Puppeteer::BrowserRunner
7
+ include Puppeteer::DebugPrint
8
+
7
9
  # @param {string} executablePath
8
10
  # @param {!Array<string>} processArguments
9
11
  # @param {string=} tempDirectory
10
- def initialize(executable_path, process_arguments, temp_directory)
12
+ def initialize(for_firefox, executable_path, process_arguments, user_data_dir, using_temp_user_data_dir)
13
+ @for_firefox = for_firefox
11
14
  @executable_path = executable_path
12
15
  @process_arguments = process_arguments
13
- @temp_directory = temp_directory
16
+ @user_data_dir = user_data_dir
17
+ @using_temp_user_data_dir = using_temp_user_data_dir
14
18
  @proc = nil
15
19
  @connection = nil
16
20
  @closed = true
@@ -90,8 +94,8 @@ class Puppeteer::BrowserRunner
90
94
  @process_closing = -> {
91
95
  @proc.dispose
92
96
  @closed = true
93
- if @temp_directory
94
- FileUtils.rm_rf(@temp_directory)
97
+ if @using_temp_user_data_dir
98
+ FileUtils.rm_rf(@user_data_dir)
95
99
  end
96
100
  }
97
101
  at_exit do
@@ -122,7 +126,7 @@ class Puppeteer::BrowserRunner
122
126
  def close
123
127
  return if @closed
124
128
 
125
- if @temp_directory
129
+ if @using_temp_user_data_dir && !@for_firefox
126
130
  kill
127
131
  elsif @connection
128
132
  begin
@@ -137,11 +141,18 @@ class Puppeteer::BrowserRunner
137
141
 
138
142
  # @return {Promise}
139
143
  def kill
140
- if @temp_directory
141
- FileUtils.rm_rf(@temp_directory)
142
- end
143
- unless @closed
144
- @proc.kill
144
+ # If the process failed to launch (for example if the browser executable path
145
+ # is invalid), then the process does not get a pid assigned. A call to
146
+ # `proc.kill` would error, as the `pid` to-be-killed can not be found.
147
+ @proc&.kill
148
+
149
+ # Attempt to remove temporary profile directory to avoid littering.
150
+ begin
151
+ if @using_temp_user_data_dir
152
+ FileUtils.rm_rf(@user_data_dir)
153
+ end
154
+ rescue => err
155
+ debug_puts(err)
145
156
  end
146
157
  end
147
158
 
@@ -15,6 +15,11 @@ class Puppeteer::CDPSession
15
15
  @session_id = session_id
16
16
  end
17
17
 
18
+ # @internal
19
+ def id
20
+ @session_id
21
+ end
22
+
18
23
  attr_reader :connection
19
24
 
20
25
  # @param method [String]
@@ -21,12 +21,12 @@ class Puppeteer::CustomQueryHandler
21
21
  nil
22
22
  end
23
23
 
24
- def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil)
24
+ def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil, root: nil)
25
25
  unless @query_one
26
26
  raise NotImplementedError.new("#{self.class}##{__method__} is not implemented.")
27
27
  end
28
28
 
29
- dom_world.send(:wait_for_selector_in_page, @query_one, selector, visible: visible, hidden: hidden, timeout: timeout)
29
+ dom_world.send(:wait_for_selector_in_page, @query_one, selector, visible: visible, hidden: hidden, timeout: timeout, root: root)
30
30
  end
31
31
 
32
32
  def query_all(element, selector)
@@ -45,10 +45,14 @@ class Puppeteer::DOMWorld
45
45
  end
46
46
  end
47
47
 
48
- # @param {!Puppeteer.FrameManager} frameManager
49
- # @param {!Puppeteer.Frame} frame
50
- # @param {!Puppeteer.TimeoutSettings} timeoutSettings
51
- def initialize(frame_manager, frame, timeout_settings)
48
+ # @param client [Puppeteer::CDPSession]
49
+ # @param frame_manager [Puppeteer::FrameManager]
50
+ # @param frame [Puppeteer::Frame]
51
+ # @param timeout_settings [Puppeteer::TimeoutSettings]
52
+ def initialize(client, frame_manager, frame, timeout_settings)
53
+ # Keep own reference to client because it might differ from the FrameManager's
54
+ # client for OOP iframes.
55
+ @client = client
52
56
  @frame_manager = frame_manager
53
57
  @frame = frame
54
58
  @timeout_settings = timeout_settings
@@ -58,7 +62,7 @@ class Puppeteer::DOMWorld
58
62
  @ctx_bindings = Set.new
59
63
  @detached = false
60
64
 
61
- frame_manager.client.on_event('Runtime.bindingCalled', &method(:handle_binding_called))
65
+ @client.on_event('Runtime.bindingCalled', &method(:handle_binding_called))
62
66
  end
63
67
 
64
68
  attr_reader :frame
@@ -416,10 +420,10 @@ class Puppeteer::DOMWorld
416
420
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
417
421
  # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
418
422
  # @param timeout [Integer]
419
- def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
423
+ def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil, root: nil)
420
424
  # call wait_for_selector_in_page with custom query selector.
421
425
  query_selector_manager = Puppeteer::QueryHandlerManager.instance
422
- query_selector_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout)
426
+ query_selector_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout, root: root)
423
427
  end
424
428
 
425
429
  private def binding_identifier(name, context)
@@ -493,10 +497,11 @@ class Puppeteer::DOMWorld
493
497
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
494
498
  # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
495
499
  # @param timeout [Integer]
496
- private def wait_for_selector_in_page(query_one, selector, visible: nil, hidden: nil, timeout: nil, binding_function: nil)
500
+ private def wait_for_selector_in_page(query_one, selector, visible: nil, hidden: nil, timeout: nil, root: nil, binding_function: nil)
497
501
  option_wait_for_visible = visible || false
498
502
  option_wait_for_hidden = hidden || false
499
503
  option_timeout = timeout || @timeout_settings.timeout
504
+ option_root = root
500
505
 
501
506
  polling =
502
507
  if option_wait_for_visible || option_wait_for_hidden
@@ -507,11 +512,11 @@ class Puppeteer::DOMWorld
507
512
  title = "selector #{selector}#{option_wait_for_hidden ? 'to be hidden' : ''}"
508
513
 
509
514
  selector_predicate = make_predicate_string(
510
- predicate_arg_def: '(selector, waitForVisible, waitForHidden)',
515
+ predicate_arg_def: '(root, selector, waitForVisible, waitForHidden)',
511
516
  predicate_query_handler: query_one,
512
517
  async: true,
513
518
  predicate_body: <<~JAVASCRIPT
514
- const node = await predicateQueryHandler(document, selector)
519
+ const node = await predicateQueryHandler(root, selector)
515
520
  return checkWaitForOptions(node, waitForVisible, waitForHidden);
516
521
  JAVASCRIPT
517
522
  )
@@ -523,6 +528,7 @@ class Puppeteer::DOMWorld
523
528
  polling: polling,
524
529
  timeout: option_timeout,
525
530
  args: [selector, option_wait_for_visible, option_wait_for_hidden],
531
+ root: option_root,
526
532
  binding_function: binding_function,
527
533
  )
528
534
  handle = wait_task.await_promise
@@ -537,10 +543,11 @@ class Puppeteer::DOMWorld
537
543
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
538
544
  # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
539
545
  # @param timeout [Integer]
540
- def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
546
+ def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil, root: nil)
541
547
  option_wait_for_visible = visible || false
542
548
  option_wait_for_hidden = hidden || false
543
549
  option_timeout = timeout || @timeout_settings.timeout
550
+ option_root = root
544
551
 
545
552
  polling =
546
553
  if option_wait_for_visible || option_wait_for_hidden
@@ -551,9 +558,9 @@ class Puppeteer::DOMWorld
551
558
  title = "XPath #{xpath}#{option_wait_for_hidden ? 'to be hidden' : ''}"
552
559
 
553
560
  xpath_predicate = make_predicate_string(
554
- predicate_arg_def: '(selector, waitForVisible, waitForHidden)',
561
+ predicate_arg_def: '(root, selector, waitForVisible, waitForHidden)',
555
562
  predicate_body: <<~JAVASCRIPT
556
- const node = document.evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
563
+ const node = document.evaluate(selector, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
557
564
  return checkWaitForOptions(node, waitForVisible, waitForHidden);
558
565
  JAVASCRIPT
559
566
  )
@@ -565,6 +572,7 @@ class Puppeteer::DOMWorld
565
572
  polling: polling,
566
573
  timeout: option_timeout,
567
574
  args: [xpath, option_wait_for_visible, option_wait_for_hidden],
575
+ root: option_root,
568
576
  )
569
577
  handle = wait_task.await_promise
570
578
  unless handle.as_element
@@ -20,6 +20,60 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
20
20
  @disposed = false
21
21
  end
22
22
 
23
+ def inspect
24
+ values = %i[context remote_object page disposed].map do |sym|
25
+ value = instance_variable_get(:"@#{sym}")
26
+ "@#{sym}=#{value}"
27
+ end
28
+ "#<Puppeteer::ElementHandle #{values.join(' ')}>"
29
+ end
30
+
31
+ #
32
+ # Wait for the `selector` to appear within the element. If at the moment of calling the
33
+ # method the `selector` already exists, the method will return immediately. If
34
+ # the `selector` doesn't appear after the `timeout` milliseconds of waiting, the
35
+ # function will throw.
36
+ #
37
+ # This method does not work across navigations or if the element is detached from DOM.
38
+ #
39
+ # @param selector - A
40
+ # {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
41
+ # of an element to wait for
42
+ # @param options - Optional waiting parameters
43
+ # @returns Promise which resolves when element specified by selector string
44
+ # is added to DOM. Resolves to `null` if waiting for hidden: `true` and
45
+ # selector is not found in DOM.
46
+ # @remarks
47
+ # The optional parameters in `options` are:
48
+ #
49
+ # - `visible`: wait for the selected element to be present in DOM and to be
50
+ # visible, i.e. to not have `display: none` or `visibility: hidden` CSS
51
+ # properties. Defaults to `false`.
52
+ #
53
+ # - `hidden`: wait for the selected element to not be found in the DOM or to be hidden,
54
+ # i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to
55
+ # `false`.
56
+ #
57
+ # - `timeout`: maximum time to wait in milliseconds. Defaults to `30000`
58
+ # (30 seconds). Pass `0` to disable timeout. The default value can be changed
59
+ # by using the {@link Page.setDefaultTimeout} method.
60
+ def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
61
+ frame = @context.frame
62
+
63
+ secondary_world = frame.secondary_world
64
+ adopted_root = secondary_world.execution_context.adopt_element_handle(self)
65
+ handle = secondary_world.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout, root: adopted_root)
66
+ adopted_root.dispose
67
+ return nil unless handle
68
+
69
+ main_world = frame.main_world
70
+ result = main_world.execution_context.adopt_element_handle(handle)
71
+ handle.dispose
72
+ result
73
+ end
74
+
75
+ define_async_method :async_wait_for_selector
76
+
23
77
  def as_element
24
78
  self
25
79
  end
@@ -323,7 +377,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
323
377
  end
324
378
  end
325
379
 
326
- def screenshot(options = {})
380
+ def screenshot(type: nil, path: nil, full_page: nil, clip: nil, quality: nil, omit_background: nil, encoding: nil)
327
381
  needs_viewport_reset = false
328
382
 
329
383
  box = bounding_box
@@ -358,14 +412,16 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
358
412
  page_x = layout_metrics["layoutViewport"]["pageX"]
359
413
  page_y = layout_metrics["layoutViewport"]["pageY"]
360
414
 
361
- clip = {
362
- x: page_x + box.x,
363
- y: page_y + box.y,
364
- width: box.width,
365
- height: box.height,
366
- }
415
+ if clip.nil?
416
+ clip = {
417
+ x: page_x + box.x,
418
+ y: page_y + box.y,
419
+ width: box.width,
420
+ height: box.height,
421
+ }
422
+ end
367
423
 
368
- @page.screenshot({ clip: clip }.merge(options))
424
+ @page.screenshot(type: type, path: path, full_page: full_page, clip: clip, quality: quality, omit_background: omit_background, encoding: encoding)
369
425
  ensure
370
426
  if needs_viewport_reset
371
427
  @page.viewport = viewport
@@ -81,6 +81,7 @@ module NetworkManagerEmittedEvents ; end
81
81
 
82
82
  {
83
83
  Request: EventsDefinitionUtils.symbol('NetworkManager.Request'),
84
+ RequestServedFromCache: EventsDefinitionUtils.symbol('NetworkManager.RequestServedFromCache'),
84
85
  Response: EventsDefinitionUtils.symbol('NetworkManager.Response'),
85
86
  RequestFailed: EventsDefinitionUtils.symbol('NetworkManager.RequestFailed'),
86
87
  RequestFinished: EventsDefinitionUtils.symbol('NetworkManager.RequestFinished'),
@@ -1,25 +1,47 @@
1
1
  class Puppeteer::Frame
2
2
  using Puppeteer::DefineAsyncMethod
3
3
 
4
- # @param {!FrameManager} frameManager
5
- # @param {!Puppeteer.CDPSession} client
6
- # @param {?Frame} parentFrame
7
- # @param {string} frameId
8
- def initialize(frame_manager, client, parent_frame, frame_id)
4
+ # @param frame_manager [Puppeteer::FrameManager]
5
+ # @param parent_frame [Puppeteer::Frame|nil]
6
+ # @param frame_id [String]
7
+ # @param client [Puppeteer::CDPSession]
8
+ def initialize(frame_manager, parent_frame, frame_id, client)
9
9
  @frame_manager = frame_manager
10
- @client = client
11
10
  @parent_frame = parent_frame
12
11
  @id = frame_id
13
12
  @detached = false
14
13
 
15
14
  @loader_id = ''
16
15
  @lifecycle_events = Set.new
17
- @main_world = Puppeteer::DOMWorld.new(frame_manager, self, frame_manager.timeout_settings)
18
- @secondary_world = Puppeteer::DOMWorld.new(frame_manager, self, frame_manager.timeout_settings)
19
16
  @child_frames = Set.new
20
17
  if parent_frame
21
18
  parent_frame._child_frames << self
22
19
  end
20
+
21
+ update_client(client)
22
+ end
23
+
24
+ def inspect
25
+ values = %i[id parent_frame detached loader_id lifecycle_events child_frames].map do |sym|
26
+ value = instance_variable_get(:"@#{sym}")
27
+ "@#{sym}=#{value}"
28
+ end
29
+ "#<Puppeteer::Frame #{values.join(' ')}>"
30
+ end
31
+
32
+ def _client
33
+ @client
34
+ end
35
+
36
+ # @param client [Puppeteer::CDPSession]
37
+ private def update_client(client)
38
+ @client = client
39
+ @main_world = Puppeteer::DOMWorld.new(@client, @frame_manager, self, @frame_manager.timeout_settings)
40
+ @secondary_world = Puppeteer::DOMWorld.new(@client, @frame_manager, self, @frame_manager.timeout_settings)
41
+ end
42
+
43
+ def oop_frame?
44
+ @client != @frame_manager.client
23
45
  end
24
46
 
25
47
  attr_accessor :frame_manager, :id, :loader_id, :lifecycle_events, :main_world, :secondary_world