puppeteer-ruby 0.39.0 → 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docs/api_coverage.md +2 -2
- data/lib/puppeteer/aria_query_handler.rb +6 -3
- data/lib/puppeteer/browser_runner.rb +1 -1
- data/lib/puppeteer/custom_query_handler.rb +2 -2
- data/lib/puppeteer/dom_world.rb +12 -8
- data/lib/puppeteer/element_handle.rb +54 -0
- data/lib/puppeteer/events.rb +1 -0
- data/lib/puppeteer/http_response.rb +25 -4
- data/lib/puppeteer/js_handle.rb +8 -0
- data/lib/puppeteer/network_event_manager.rb +122 -0
- data/lib/puppeteer/network_manager.rb +180 -40
- data/lib/puppeteer/query_handler_manager.rb +2 -2
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer/wait_task.rb +10 -7
- data/lib/puppeteer.rb +1 -0
- data/puppeteer-ruby.gemspec +2 -2
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af966d033f8cff7c99c121805ffe7dde6f4d73cfe0fa9f34937b9eedbeafd9b8
|
4
|
+
data.tar.gz: 3578653749391fd953144e21160beb6baee5eb6463b43b8cecafcf3f3433bda7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ae375c2763cd2fd597f72b0e82ccae84eba414cb4e1b7b3e0067c250c869020118f58fc1d74e0bfe0bb512318501fcedd5c1a25f3725de44a29e01393227765
|
7
|
+
data.tar.gz: 4dec50af05864ec0410e4d99bbec2cdccfbf06412eb21b3f923be4de892244510ba4a5d553dafc04b62d6de6381caf852238a9e971d2ba2d30c3009e78c9e048
|
data/docs/api_coverage.md
CHANGED
@@ -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: -> (sel) { query_one(dom_world.send(:document), sel) },
|
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)
|
@@ -149,7 +149,7 @@ class Puppeteer::BrowserRunner
|
|
149
149
|
# Attempt to remove temporary profile directory to avoid littering.
|
150
150
|
begin
|
151
151
|
if @using_temp_user_data_dir
|
152
|
-
FileUtils.rm_rf(@
|
152
|
+
FileUtils.rm_rf(@user_data_dir)
|
153
153
|
end
|
154
154
|
rescue => err
|
155
155
|
debug_puts(err)
|
@@ -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)
|
data/lib/puppeteer/dom_world.rb
CHANGED
@@ -420,10 +420,10 @@ class Puppeteer::DOMWorld
|
|
420
420
|
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
421
421
|
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
422
422
|
# @param timeout [Integer]
|
423
|
-
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)
|
424
424
|
# call wait_for_selector_in_page with custom query selector.
|
425
425
|
query_selector_manager = Puppeteer::QueryHandlerManager.instance
|
426
|
-
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)
|
427
427
|
end
|
428
428
|
|
429
429
|
private def binding_identifier(name, context)
|
@@ -497,10 +497,11 @@ class Puppeteer::DOMWorld
|
|
497
497
|
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
498
498
|
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
499
499
|
# @param timeout [Integer]
|
500
|
-
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)
|
501
501
|
option_wait_for_visible = visible || false
|
502
502
|
option_wait_for_hidden = hidden || false
|
503
503
|
option_timeout = timeout || @timeout_settings.timeout
|
504
|
+
option_root = root
|
504
505
|
|
505
506
|
polling =
|
506
507
|
if option_wait_for_visible || option_wait_for_hidden
|
@@ -511,11 +512,11 @@ class Puppeteer::DOMWorld
|
|
511
512
|
title = "selector #{selector}#{option_wait_for_hidden ? 'to be hidden' : ''}"
|
512
513
|
|
513
514
|
selector_predicate = make_predicate_string(
|
514
|
-
predicate_arg_def: '(selector, waitForVisible, waitForHidden)',
|
515
|
+
predicate_arg_def: '(root, selector, waitForVisible, waitForHidden)',
|
515
516
|
predicate_query_handler: query_one,
|
516
517
|
async: true,
|
517
518
|
predicate_body: <<~JAVASCRIPT
|
518
|
-
const node = await predicateQueryHandler(
|
519
|
+
const node = await predicateQueryHandler(root, selector)
|
519
520
|
return checkWaitForOptions(node, waitForVisible, waitForHidden);
|
520
521
|
JAVASCRIPT
|
521
522
|
)
|
@@ -527,6 +528,7 @@ class Puppeteer::DOMWorld
|
|
527
528
|
polling: polling,
|
528
529
|
timeout: option_timeout,
|
529
530
|
args: [selector, option_wait_for_visible, option_wait_for_hidden],
|
531
|
+
root: option_root,
|
530
532
|
binding_function: binding_function,
|
531
533
|
)
|
532
534
|
handle = wait_task.await_promise
|
@@ -541,10 +543,11 @@ class Puppeteer::DOMWorld
|
|
541
543
|
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
542
544
|
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
543
545
|
# @param timeout [Integer]
|
544
|
-
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)
|
545
547
|
option_wait_for_visible = visible || false
|
546
548
|
option_wait_for_hidden = hidden || false
|
547
549
|
option_timeout = timeout || @timeout_settings.timeout
|
550
|
+
option_root = root
|
548
551
|
|
549
552
|
polling =
|
550
553
|
if option_wait_for_visible || option_wait_for_hidden
|
@@ -555,9 +558,9 @@ class Puppeteer::DOMWorld
|
|
555
558
|
title = "XPath #{xpath}#{option_wait_for_hidden ? 'to be hidden' : ''}"
|
556
559
|
|
557
560
|
xpath_predicate = make_predicate_string(
|
558
|
-
predicate_arg_def: '(selector, waitForVisible, waitForHidden)',
|
561
|
+
predicate_arg_def: '(root, selector, waitForVisible, waitForHidden)',
|
559
562
|
predicate_body: <<~JAVASCRIPT
|
560
|
-
const node = document.evaluate(selector,
|
563
|
+
const node = document.evaluate(selector, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
561
564
|
return checkWaitForOptions(node, waitForVisible, waitForHidden);
|
562
565
|
JAVASCRIPT
|
563
566
|
)
|
@@ -569,6 +572,7 @@ class Puppeteer::DOMWorld
|
|
569
572
|
polling: polling,
|
570
573
|
timeout: option_timeout,
|
571
574
|
args: [xpath, option_wait_for_visible, option_wait_for_hidden],
|
575
|
+
root: option_root,
|
572
576
|
)
|
573
577
|
handle = wait_task.await_promise
|
574
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
|
data/lib/puppeteer/events.rb
CHANGED
@@ -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'),
|
@@ -31,7 +31,8 @@ class Puppeteer::HTTPResponse
|
|
31
31
|
# @param client [Puppeteer::CDPSession]
|
32
32
|
# @param request [Puppeteer::HTTPRequest]
|
33
33
|
# @param response_payload [Hash]
|
34
|
-
|
34
|
+
# @param extra_info [Hash|nil]
|
35
|
+
def initialize(client, request, response_payload, extra_info)
|
35
36
|
@client = client
|
36
37
|
@request = request
|
37
38
|
|
@@ -41,14 +42,15 @@ class Puppeteer::HTTPResponse
|
|
41
42
|
port: response_payload['remotePort'],
|
42
43
|
)
|
43
44
|
|
44
|
-
@
|
45
|
-
@status_text = response_payload['statusText']
|
45
|
+
@status_text = parse_štatus_text_from_extra_info(extra_info) || response_payload['statusText']
|
46
46
|
@url = request.url
|
47
47
|
@from_disk_cache = !!response_payload['fromDiskCache']
|
48
48
|
@from_service_worker = !!response_payload['fromServiceWorker']
|
49
49
|
|
50
|
+
@status = extra_info ? extra_info['statusCode'] : response_payload['status']
|
50
51
|
@headers = {}
|
51
|
-
|
52
|
+
headers = extra_info ? extra_info['headers'] : response_payload['headers']
|
53
|
+
headers.each do |key, value|
|
52
54
|
@headers[key.downcase] = value
|
53
55
|
end
|
54
56
|
@security_details = if_present(response_payload['securityDetails']) do |security_payload|
|
@@ -62,6 +64,25 @@ class Puppeteer::HTTPResponse
|
|
62
64
|
|
63
65
|
attr_reader :remote_address, :url, :status, :status_text, :headers, :security_details, :request
|
64
66
|
|
67
|
+
def inspect
|
68
|
+
values = %i[remote_address url status status_text headers security_details request].map do |sym|
|
69
|
+
value = instance_variable_get(:"@#{sym}")
|
70
|
+
"@#{sym}=#{value}"
|
71
|
+
end
|
72
|
+
"#<Puppeteer::HTTPRequest #{values.join(' ')}>"
|
73
|
+
end
|
74
|
+
|
75
|
+
private def parse_štatus_text_from_extra_info(extra_info)
|
76
|
+
return nil if !extra_info || !extra_info['headersText']
|
77
|
+
first_line = extra_info['headersText'].split("\r").first
|
78
|
+
return nil unless first_line
|
79
|
+
/[^ ]* [^ ]* (.*)/.match(first_line) do |m|
|
80
|
+
return m[1]
|
81
|
+
end
|
82
|
+
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
65
86
|
# @return [Boolean]
|
66
87
|
def ok?
|
67
88
|
@status == 0 || (@status >= 200 && @status <= 299)
|
data/lib/puppeteer/js_handle.rb
CHANGED
@@ -36,6 +36,14 @@ class Puppeteer::JSHandle
|
|
36
36
|
|
37
37
|
attr_reader :context, :remote_object
|
38
38
|
|
39
|
+
def inspect
|
40
|
+
values = %i[context remote_object disposed].map do |sym|
|
41
|
+
value = instance_variable_get(:"@#{sym}")
|
42
|
+
"@#{sym}=#{value}"
|
43
|
+
end
|
44
|
+
"#<Puppeteer::JSHandle #{values.join(' ')}>"
|
45
|
+
end
|
46
|
+
|
39
47
|
# @return [Puppeteer::ExecutionContext]
|
40
48
|
def execution_context
|
41
49
|
@context
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Helper class to track network events by request ID
|
2
|
+
class Puppeteer::NetworkEventManager
|
3
|
+
def initialize
|
4
|
+
#
|
5
|
+
# There are four possible orders of events:
|
6
|
+
# A. `_onRequestWillBeSent`
|
7
|
+
# B. `_onRequestWillBeSent`, `_onRequestPaused`
|
8
|
+
# C. `_onRequestPaused`, `_onRequestWillBeSent`
|
9
|
+
# D. `_onRequestPaused`, `_onRequestWillBeSent`, `_onRequestPaused`,
|
10
|
+
# `_onRequestWillBeSent`, `_onRequestPaused`, `_onRequestPaused`
|
11
|
+
# (see crbug.com/1196004)
|
12
|
+
#
|
13
|
+
# For `_onRequest` we need the event from `_onRequestWillBeSent` and
|
14
|
+
# optionally the `interceptionId` from `_onRequestPaused`.
|
15
|
+
#
|
16
|
+
# If request interception is disabled, call `_onRequest` once per call to
|
17
|
+
# `_onRequestWillBeSent`.
|
18
|
+
# If request interception is enabled, call `_onRequest` once per call to
|
19
|
+
# `_onRequestPaused` (once per `interceptionId`).
|
20
|
+
#
|
21
|
+
# Events are stored to allow for subsequent events to call `_onRequest`.
|
22
|
+
#
|
23
|
+
# Note that (chains of) redirect requests have the same `requestId` (!) as
|
24
|
+
# the original request. We have to anticipate series of events like these:
|
25
|
+
# A. `_onRequestWillBeSent`,
|
26
|
+
# `_onRequestWillBeSent`, ...
|
27
|
+
# B. `_onRequestWillBeSent`, `_onRequestPaused`,
|
28
|
+
# `_onRequestWillBeSent`, `_onRequestPaused`, ...
|
29
|
+
# C. `_onRequestWillBeSent`, `_onRequestPaused`,
|
30
|
+
# `_onRequestPaused`, `_onRequestWillBeSent`, ...
|
31
|
+
# D. `_onRequestPaused`, `_onRequestWillBeSent`,
|
32
|
+
# `_onRequestPaused`, `_onRequestWillBeSent`, `_onRequestPaused`,
|
33
|
+
# `_onRequestWillBeSent`, `_onRequestPaused`, `_onRequestPaused`, ...
|
34
|
+
# (see crbug.com/1196004)
|
35
|
+
@request_will_be_sent_map = {}
|
36
|
+
@request_paused_map = {}
|
37
|
+
@http_requests_map = {}
|
38
|
+
#
|
39
|
+
# The below maps are used to reconcile Network.responseReceivedExtraInfo
|
40
|
+
# events with their corresponding request. Each response and redirect
|
41
|
+
# response gets an ExtraInfo event, and we don't know which will come first.
|
42
|
+
# This means that we have to store a Response or an ExtraInfo for each
|
43
|
+
# response, and emit the event when we get both of them. In addition, to
|
44
|
+
# handle redirects, we have to make them Arrays to represent the chain of
|
45
|
+
# events.
|
46
|
+
@response_received_extra_info_map = {}
|
47
|
+
@queued_redirect_info_map = {}
|
48
|
+
@queued_event_group_map = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def forget(network_request_id)
|
52
|
+
@request_will_be_sent_map.delete(network_request_id)
|
53
|
+
@request_paused_map.delete(network_request_id)
|
54
|
+
@queued_event_group_map.delete(network_request_id)
|
55
|
+
@queued_redirect_info_map.delete(network_request_id)
|
56
|
+
@response_received_extra_info_map.delete(network_request_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def response_extra_info(network_request_id)
|
60
|
+
@response_received_extra_info_map[network_request_id] ||= []
|
61
|
+
end
|
62
|
+
|
63
|
+
private def queued_redirect_info(fetch_request_id)
|
64
|
+
@queued_redirect_info_map[fetch_request_id] ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
def enqueue_redirect_info(fetch_request_id, redirect_info)
|
68
|
+
queued_redirect_info(fetch_request_id) << redirect_info
|
69
|
+
end
|
70
|
+
|
71
|
+
def take_queued_redirect_info(fetch_request_id)
|
72
|
+
queued_redirect_info(fetch_request_id).shift
|
73
|
+
end
|
74
|
+
|
75
|
+
def num_requests_in_progress
|
76
|
+
@http_requests_map.count { |_, request| !request.response }
|
77
|
+
end
|
78
|
+
|
79
|
+
def store_request_will_be_sent(network_request_id, event)
|
80
|
+
@request_will_be_sent_map[network_request_id] = event
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_request_will_be_sent(network_request_id)
|
84
|
+
@request_will_be_sent_map[network_request_id]
|
85
|
+
end
|
86
|
+
|
87
|
+
def forget_request_will_be_sent(network_request_id)
|
88
|
+
@request_will_be_sent_map.delete(network_request_id)
|
89
|
+
end
|
90
|
+
|
91
|
+
def store_request_paused(network_request_id, event)
|
92
|
+
@request_paused_map[network_request_id] = event
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_request_paused(network_request_id)
|
96
|
+
@request_paused_map[network_request_id]
|
97
|
+
end
|
98
|
+
|
99
|
+
def fotget_request_paused(network_request_id)
|
100
|
+
@request_paused_map.delete(network_request_id)
|
101
|
+
end
|
102
|
+
|
103
|
+
def store_request(network_request_id, request)
|
104
|
+
@http_requests_map[network_request_id] = request
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_request(network_request_id)
|
108
|
+
@http_requests_map[network_request_id]
|
109
|
+
end
|
110
|
+
|
111
|
+
def forget_request(network_request_id)
|
112
|
+
@http_requests_map.delete(network_request_id)
|
113
|
+
end
|
114
|
+
|
115
|
+
def enqueue_event_group(network_request_id, queued_event_group)
|
116
|
+
@queued_event_group_map[network_request_id] = queued_event_group
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_queued_event_group(network_request_id)
|
120
|
+
@queued_event_group_map[network_request_id]
|
121
|
+
end
|
122
|
+
end
|
@@ -53,6 +53,22 @@ class Puppeteer::NetworkManager
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
class RedirectInfo
|
57
|
+
def initialize(event:, fetch_request_id:)
|
58
|
+
@event = event
|
59
|
+
@fetch_request_id = fetch_request_id
|
60
|
+
end
|
61
|
+
attr_reader :event, :fetch_request_id
|
62
|
+
end
|
63
|
+
|
64
|
+
class QueuedEventGroup
|
65
|
+
def initialize(response_received_event:)
|
66
|
+
@response_received_event = response_received_event
|
67
|
+
end
|
68
|
+
attr_reader :response_received_event
|
69
|
+
attr_accessor :loading_finished_event, :loading_failed_event
|
70
|
+
end
|
71
|
+
|
56
72
|
# @param {!Puppeteer.CDPSession} client
|
57
73
|
# @param {boolean} ignoreHTTPSErrors
|
58
74
|
# @param {!Puppeteer.FrameManager} frameManager
|
@@ -60,12 +76,7 @@ class Puppeteer::NetworkManager
|
|
60
76
|
@client = client
|
61
77
|
@ignore_https_errors = ignore_https_errors
|
62
78
|
@frame_manager = frame_manager
|
63
|
-
|
64
|
-
# @type {!Map<string, !Request>}
|
65
|
-
@request_id_to_request = {}
|
66
|
-
|
67
|
-
# @type {!Map<string, !Protocol.Network.requestWillBeSentPayload>}
|
68
|
-
@request_id_to_request_with_be_sent_event = {}
|
79
|
+
@network_event_manager = Puppeteer::NetworkEventManager.new
|
69
80
|
|
70
81
|
@extra_http_headers = {}
|
71
82
|
|
@@ -73,7 +84,6 @@ class Puppeteer::NetworkManager
|
|
73
84
|
@user_request_interception_enabled = false
|
74
85
|
@protocol_request_interception_enabled = false
|
75
86
|
@user_cache_disabled = false
|
76
|
-
@request_id_to_interception_id = {}
|
77
87
|
@internal_network_condition = InternalNetworkCondition.new(@client)
|
78
88
|
|
79
89
|
@client.on_event('Fetch.requestPaused') do |event|
|
@@ -97,6 +107,9 @@ class Puppeteer::NetworkManager
|
|
97
107
|
@client.on_event('Network.loadingFailed') do |event|
|
98
108
|
handle_loading_failed(event)
|
99
109
|
end
|
110
|
+
@client.on_event('Network.responseReceivedExtraInfo') do |event|
|
111
|
+
handle_response_received_extra_info(event)
|
112
|
+
end
|
100
113
|
end
|
101
114
|
|
102
115
|
def init
|
@@ -106,6 +119,14 @@ class Puppeteer::NetworkManager
|
|
106
119
|
end
|
107
120
|
end
|
108
121
|
|
122
|
+
def inspect
|
123
|
+
values = %i[network_event_manager].map do |sym|
|
124
|
+
value = instance_variable_get(:"@#{sym}")
|
125
|
+
"@#{sym}=#{value}"
|
126
|
+
end
|
127
|
+
"#<Puppeteer::HTTPRequest #{values.join(' ')}>"
|
128
|
+
end
|
129
|
+
|
109
130
|
# @param username [String|NilClass]
|
110
131
|
# @param password [String|NilClass]
|
111
132
|
def authenticate(username:, password:)
|
@@ -131,6 +152,10 @@ class Puppeteer::NetworkManager
|
|
131
152
|
@extra_http_headers.dup
|
132
153
|
end
|
133
154
|
|
155
|
+
def num_requests_in_progress
|
156
|
+
@network_event_manager.num_requests_in_progress
|
157
|
+
end
|
158
|
+
|
134
159
|
# @param value [TrueClass|FalseClass]
|
135
160
|
def offline_mode=(value)
|
136
161
|
@internal_network_condition.offline_mode=(value)
|
@@ -185,14 +210,17 @@ class Puppeteer::NetworkManager
|
|
185
210
|
|
186
211
|
private def handle_request_will_be_sent(event)
|
187
212
|
# Request interception doesn't happen for data URLs with Network Service.
|
188
|
-
if @
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
213
|
+
if @user_request_interception_enabled && !event['request']['url'].start_with?('data:')
|
214
|
+
network_request_id = event['requestId']
|
215
|
+
@network_event_manager.store_request_will_be_sent(network_request_id, event)
|
216
|
+
|
217
|
+
# CDP may have sent a Fetch.requestPaused event already. Check for it.
|
218
|
+
if_present(@network_event_manager.get_request_paused(network_request_id)) do |request_paused_event|
|
219
|
+
fetch_request_id = request_paused_event['requestId']
|
220
|
+
handle_request(event, fetch_request_id)
|
221
|
+
@network_event_manager.forget_request_paused(network_request_id)
|
195
222
|
end
|
223
|
+
|
196
224
|
return
|
197
225
|
end
|
198
226
|
handle_request(event, nil)
|
@@ -233,26 +261,61 @@ class Puppeteer::NetworkManager
|
|
233
261
|
end
|
234
262
|
end
|
235
263
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
264
|
+
network_request_id = event['networkId']
|
265
|
+
fetch_request_id = event['requestId']
|
266
|
+
return unless network_request_id
|
267
|
+
|
268
|
+
request_will_be_sent_event = @network_event_manager.get_request_will_be_sent(network_request_id)
|
269
|
+
|
270
|
+
# redirect requests have the same `requestId`
|
271
|
+
if request_will_be_sent_event &&
|
272
|
+
(request_will_be_sent_event['request']['url'] != event['request']['url'] ||
|
273
|
+
request_will_be_sent_event['request']['method'] != event['request']['method'])
|
274
|
+
|
275
|
+
@network_event_manager.forget_request_will_be_sent(network_request_id)
|
276
|
+
request_will_be_sent_event = nil
|
277
|
+
end
|
278
|
+
|
279
|
+
if request_will_be_sent_event
|
280
|
+
handle_request(request_will_be_sent_event, fetch_request_id)
|
240
281
|
else
|
241
|
-
@
|
282
|
+
@network_event_manager.store_request_paused(network_request_id, event)
|
242
283
|
end
|
243
284
|
end
|
244
285
|
|
245
|
-
private def handle_request(event,
|
286
|
+
private def handle_request(event, fetch_request_id)
|
246
287
|
redirect_chain = []
|
247
288
|
if event['redirectResponse']
|
248
|
-
|
249
|
-
|
289
|
+
# We want to emit a response and requestfinished for the
|
290
|
+
# redirectResponse, but we can't do so unless we have a
|
291
|
+
# responseExtraInfo ready to pair it up with. If we don't have any
|
292
|
+
# responseExtraInfos saved in our queue, they we have to wait until
|
293
|
+
# the next one to emit response and requestfinished, *and* we should
|
294
|
+
# also wait to emit this Request too because it should come after the
|
295
|
+
# response/requestfinished.
|
296
|
+
redirect_response_extra_info = nil
|
297
|
+
if event['redirectHasExtraInfo']
|
298
|
+
redirect_response_extra_info = @network_event_manager.response_extra_info(event['requestId']).shift
|
299
|
+
unless redirect_response_extra_info
|
300
|
+
redirect_info = RedirectInfo.new(
|
301
|
+
event: event,
|
302
|
+
fetch_request_id: fetch_request_id,
|
303
|
+
)
|
304
|
+
@network_event_manager.enqueue_redirect_info(event['requestId'], redirect_info)
|
305
|
+
return
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# If we connect late to the target, we could have missed the
|
310
|
+
# requestWillBeSent event.
|
311
|
+
if_present(@network_event_manager.get_request(event['requestId'])) do |request|
|
312
|
+
handle_request_redirect(request, event['redirectResponse'], redirect_response_extra_info)
|
250
313
|
redirect_chain = request.internal.redirect_chain
|
251
314
|
end
|
252
315
|
end
|
253
316
|
frame = if_present(event['frameId']) { |frame_id| @frame_manager.frame(frame_id) }
|
254
|
-
request = Puppeteer::HTTPRequest.new(@client, frame,
|
255
|
-
@
|
317
|
+
request = Puppeteer::HTTPRequest.new(@client, frame, fetch_request_id, @user_request_interception_enabled, event, redirect_chain)
|
318
|
+
@network_event_manager.store_request(event['requestId'], request)
|
256
319
|
emit_event(NetworkManagerEmittedEvents::Request, request)
|
257
320
|
begin
|
258
321
|
request.finalize_interceptions
|
@@ -262,55 +325,133 @@ class Puppeteer::NetworkManager
|
|
262
325
|
end
|
263
326
|
|
264
327
|
private def handle_request_served_from_cache(event)
|
265
|
-
|
328
|
+
request = @network_event_manager.get_request(event['requestId'])
|
329
|
+
if request
|
266
330
|
request.internal.from_memory_cache = true
|
267
331
|
end
|
332
|
+
emit_event(NetworkManagerEmittedEvents::RequestServedFromCache, request)
|
268
333
|
end
|
269
334
|
|
270
335
|
# @param request [Puppeteer::HTTPRequest]
|
271
336
|
# @param response_payload [Hash]
|
272
|
-
private def handle_request_redirect(request, response_payload)
|
273
|
-
response = Puppeteer::HTTPResponse.new(@client, request, response_payload)
|
337
|
+
private def handle_request_redirect(request, response_payload, extra_info)
|
338
|
+
response = Puppeteer::HTTPResponse.new(@client, request, response_payload, extra_info)
|
274
339
|
request.internal.response = response
|
275
340
|
request.internal.redirect_chain << request
|
276
341
|
response.internal.body_loaded_promise.reject(Puppeteer::HTTPResponse::Redirected.new)
|
277
|
-
|
278
|
-
@attempted_authentications.delete(request.internal.interception_id)
|
342
|
+
forget_request(request, false)
|
279
343
|
emit_event(NetworkManagerEmittedEvents::Response, response)
|
280
344
|
emit_event(NetworkManagerEmittedEvents::RequestFinished, request)
|
281
345
|
end
|
282
346
|
|
283
|
-
|
284
|
-
|
285
|
-
request = @request_id_to_request[event['requestId']]
|
347
|
+
private def emit_response_event(response_received_event, extra_info)
|
348
|
+
request = @network_event_manager.get_request(response_received_event['requestId'])
|
286
349
|
# FileUpload sends a response without a matching request.
|
287
350
|
return unless request
|
288
351
|
|
289
|
-
|
352
|
+
unless @network_event_manager.response_extra_info(response_received_event['requestId']).empty?
|
353
|
+
debug_puts("Unexpected extraInfo events for request #{response_received_event['requestId']}")
|
354
|
+
end
|
355
|
+
|
356
|
+
response = Puppeteer::HTTPResponse.new(@client, request, response_received_event['response'], extra_info)
|
290
357
|
request.internal.response = response
|
291
358
|
emit_event(NetworkManagerEmittedEvents::Response, response)
|
292
359
|
end
|
293
360
|
|
361
|
+
# @param event [Hash]
|
362
|
+
private def handle_response_received(event)
|
363
|
+
request = @network_event_manager.get_request(event['requestId'])
|
364
|
+
extra_info = nil
|
365
|
+
if request && !request.internal.from_memory_cache? && event['hasExtraInfo']
|
366
|
+
extra_info = @network_event_manager.response_extra_info(event['requestId']).shift
|
367
|
+
|
368
|
+
unless extra_info
|
369
|
+
# Wait until we get the corresponding ExtraInfo event.
|
370
|
+
@network_event_manager.enqueue_event_group(event['requestId'], QueuedEventGroup.new(event))
|
371
|
+
return
|
372
|
+
end
|
373
|
+
end
|
374
|
+
emit_response_event(event, extra_info)
|
375
|
+
end
|
376
|
+
|
377
|
+
private def handle_response_received_extra_info(event)
|
378
|
+
# We may have skipped a redirect response/request pair due to waiting for
|
379
|
+
# this ExtraInfo event. If so, continue that work now that we have the
|
380
|
+
# request.
|
381
|
+
if_present(@network_event_manager.take_queued_redirect_info(event['requestId'])) do |redirect_info|
|
382
|
+
@network_event_manager.response_extra_info(event['requestId']) << event
|
383
|
+
handle_request(redirect_info.event, redirect_info)
|
384
|
+
return
|
385
|
+
end
|
386
|
+
|
387
|
+
# We may have skipped response and loading events because we didn't have
|
388
|
+
# this ExtraInfo event yet. If so, emit those events now.
|
389
|
+
if_present(@network_event_manager.get_queued_event_group(event['requestId'])) do |queued_events|
|
390
|
+
emit_response_event(queued_events.response_received_event, event)
|
391
|
+
if_present(queued_events.loading_finished_event) do |loading_finished_event|
|
392
|
+
emit_loading_finished(loading_finished_event)
|
393
|
+
end
|
394
|
+
if_present(queued_events.loading_failed_event) do |loading_failed_event|
|
395
|
+
emit_loading_failed(loading_failed_event)
|
396
|
+
end
|
397
|
+
return
|
398
|
+
end
|
399
|
+
|
400
|
+
# Wait until we get another event that can use this ExtraInfo event.
|
401
|
+
@network_event_manager.response_extra_info(event['requestId']) << event
|
402
|
+
end
|
403
|
+
|
404
|
+
private def forget_request(request, forget_events)
|
405
|
+
request_id = request.internal.request_id
|
406
|
+
interception_id = request.internal.interception_id
|
407
|
+
|
408
|
+
@network_event_manager.forget_request(request_id)
|
409
|
+
@attempted_authentications.delete(interception_id)
|
410
|
+
if forget_events
|
411
|
+
@network_event_manager.forget(request_id)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
294
415
|
private def handle_loading_finished(event)
|
295
|
-
request
|
416
|
+
# If the response event for this request is still waiting on a
|
417
|
+
# corresponding ExtraInfo event, then wait to emit this event too.
|
418
|
+
queued_events = @network_event_manager.get_queued_event_group(event['requestId'])
|
419
|
+
if queued_events
|
420
|
+
queued_events.loading_finished_event = event
|
421
|
+
else
|
422
|
+
emit_loading_finished(event)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
private def emit_loading_finished(event)
|
427
|
+
request = @network_event_manager.get_request(event['requestId'])
|
296
428
|
# For certain requestIds we never receive requestWillBeSent event.
|
297
429
|
# @see https://crbug.com/750469
|
298
430
|
return unless request
|
299
431
|
|
300
|
-
|
301
432
|
# Under certain conditions we never get the Network.responseReceived
|
302
433
|
# event from protocol. @see https://crbug.com/883475
|
303
434
|
if_present(request.response) do |response|
|
304
435
|
response.internal.body_loaded_promise.fulfill(nil)
|
305
436
|
end
|
306
437
|
|
307
|
-
|
308
|
-
@attempted_authentications.delete(request.internal.interception_id)
|
438
|
+
forget_request(request, true)
|
309
439
|
emit_event(NetworkManagerEmittedEvents::RequestFinished, request)
|
310
440
|
end
|
311
441
|
|
312
442
|
private def handle_loading_failed(event)
|
313
|
-
request
|
443
|
+
# If the response event for this request is still waiting on a
|
444
|
+
# corresponding ExtraInfo event, then wait to emit this event too.
|
445
|
+
queued_events = @network_event_manager.get_queued_event_group(event['requestId'])
|
446
|
+
if queued_events
|
447
|
+
queued_events.loading_failed_event = event
|
448
|
+
else
|
449
|
+
emit_loading_failed(event)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
private def emit_loading_failed(event)
|
454
|
+
request = @network_event_manager.get_request(event['requestId'])
|
314
455
|
# For certain requestIds we never receive requestWillBeSent event.
|
315
456
|
# @see https://crbug.com/750469
|
316
457
|
return unless request
|
@@ -319,8 +460,7 @@ class Puppeteer::NetworkManager
|
|
319
460
|
if_present(request.response) do |response|
|
320
461
|
response.internal.body_loaded_promise.fulfill(nil)
|
321
462
|
end
|
322
|
-
|
323
|
-
@attempted_authentications.delete(request.internal.interception_id)
|
463
|
+
forget_request(request, true)
|
324
464
|
emit_event(NetworkManagerEmittedEvents::RequestFailed, request)
|
325
465
|
end
|
326
466
|
end
|
@@ -26,8 +26,8 @@ class Puppeteer::QueryHandlerManager
|
|
26
26
|
@query_handler.query_one(element_handle, @selector)
|
27
27
|
end
|
28
28
|
|
29
|
-
def wait_for(dom_world, visible:, hidden:, timeout:)
|
30
|
-
@query_handler.wait_for(dom_world, @selector, visible: visible, hidden: hidden, timeout: timeout)
|
29
|
+
def wait_for(dom_world, visible:, hidden:, timeout:, root:)
|
30
|
+
@query_handler.wait_for(dom_world, @selector, visible: visible, hidden: hidden, timeout: timeout, root: root)
|
31
31
|
end
|
32
32
|
|
33
33
|
def query_all(element_handle)
|
data/lib/puppeteer/version.rb
CHANGED
data/lib/puppeteer/wait_task.rb
CHANGED
@@ -9,7 +9,7 @@ class Puppeteer::WaitTask
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [], binding_function: nil)
|
12
|
+
def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [], binding_function: nil, root: nil)
|
13
13
|
if polling.is_a?(String)
|
14
14
|
if polling != 'raf' && polling != 'mutation'
|
15
15
|
raise ArgumentError.new("Unknown polling option: #{polling}")
|
@@ -25,6 +25,7 @@ class Puppeteer::WaitTask
|
|
25
25
|
@dom_world = dom_world
|
26
26
|
@polling = polling
|
27
27
|
@timeout = timeout
|
28
|
+
@root = root
|
28
29
|
@predicate_body = "return (#{predicate_body})(...args);"
|
29
30
|
@args = args
|
30
31
|
@binding_function = binding_function
|
@@ -68,6 +69,7 @@ class Puppeteer::WaitTask
|
|
68
69
|
begin
|
69
70
|
success = context.evaluate_handle(
|
70
71
|
WAIT_FOR_PREDICATE_PAGE_FUNCTION,
|
72
|
+
@root,
|
71
73
|
@predicate_body,
|
72
74
|
@polling,
|
73
75
|
@timeout,
|
@@ -121,8 +123,9 @@ class Puppeteer::WaitTask
|
|
121
123
|
private define_async_method :async_rerun
|
122
124
|
|
123
125
|
WAIT_FOR_PREDICATE_PAGE_FUNCTION = <<~JAVASCRIPT
|
124
|
-
async function _(predicateBody, polling, timeout, ...args) {
|
126
|
+
async function _(root, predicateBody, polling, timeout, ...args) {
|
125
127
|
const predicate = new Function('...args', predicateBody);
|
128
|
+
root = root || document
|
126
129
|
let timedOut = false;
|
127
130
|
if (timeout)
|
128
131
|
setTimeout(() => (timedOut = true), timeout);
|
@@ -136,7 +139,7 @@ class Puppeteer::WaitTask
|
|
136
139
|
* @return {!Promise<*>}
|
137
140
|
*/
|
138
141
|
async function pollMutation() {
|
139
|
-
const success = await predicate(...args);
|
142
|
+
const success = await predicate(root, ...args);
|
140
143
|
if (success) return Promise.resolve(success);
|
141
144
|
let fulfill;
|
142
145
|
const result = new Promise((x) => (fulfill = x));
|
@@ -145,13 +148,13 @@ class Puppeteer::WaitTask
|
|
145
148
|
observer.disconnect();
|
146
149
|
fulfill();
|
147
150
|
}
|
148
|
-
const success = await predicate(...args);
|
151
|
+
const success = await predicate(root, ...args);
|
149
152
|
if (success) {
|
150
153
|
observer.disconnect();
|
151
154
|
fulfill(success);
|
152
155
|
}
|
153
156
|
});
|
154
|
-
observer.observe(
|
157
|
+
observer.observe(root, {
|
155
158
|
childList: true,
|
156
159
|
subtree: true,
|
157
160
|
attributes: true,
|
@@ -168,7 +171,7 @@ class Puppeteer::WaitTask
|
|
168
171
|
fulfill();
|
169
172
|
return;
|
170
173
|
}
|
171
|
-
const success = await predicate(...args);
|
174
|
+
const success = await predicate(root, ...args);
|
172
175
|
if (success) fulfill(success);
|
173
176
|
else requestAnimationFrame(onRaf);
|
174
177
|
}
|
@@ -183,7 +186,7 @@ class Puppeteer::WaitTask
|
|
183
186
|
fulfill();
|
184
187
|
return;
|
185
188
|
}
|
186
|
-
const success = await predicate(...args);
|
189
|
+
const success = await predicate(root, ...args);
|
187
190
|
if (success) fulfill(success);
|
188
191
|
else setTimeout(onTimeout, pollInterval);
|
189
192
|
}
|
data/lib/puppeteer.rb
CHANGED
@@ -47,6 +47,7 @@ require 'puppeteer/launcher'
|
|
47
47
|
require 'puppeteer/lifecycle_watcher'
|
48
48
|
require 'puppeteer/mouse'
|
49
49
|
require 'puppeteer/network_conditions'
|
50
|
+
require 'puppeteer/network_event_manager'
|
50
51
|
require 'puppeteer/network_manager'
|
51
52
|
require 'puppeteer/page'
|
52
53
|
require 'puppeteer/protocol_stream_reader'
|
data/puppeteer-ruby.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency 'concurrent-ruby', '~> 1.1.0'
|
25
25
|
spec.add_dependency 'websocket-driver', '>= 0.6.0'
|
26
26
|
spec.add_dependency 'mime-types', '>= 3.0'
|
27
|
-
spec.add_development_dependency 'bundler', '~> 2.
|
27
|
+
spec.add_development_dependency 'bundler', '~> 2.3.4'
|
28
28
|
spec.add_development_dependency 'chunky_png'
|
29
29
|
spec.add_development_dependency 'dry-inflector'
|
30
30
|
spec.add_development_dependency 'pry-byebug'
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency 'rollbar'
|
33
33
|
spec.add_development_dependency 'rspec', '~> 3.10.0 '
|
34
34
|
spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
|
35
|
-
spec.add_development_dependency 'rubocop', '~> 1.
|
35
|
+
spec.add_development_dependency 'rubocop', '~> 1.24.0'
|
36
36
|
spec.add_development_dependency 'rubocop-rspec'
|
37
37
|
spec.add_development_dependency 'sinatra'
|
38
38
|
spec.add_development_dependency 'webrick'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppeteer-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.40.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YusukeIwaki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 2.
|
61
|
+
version: 2.3.4
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 2.
|
68
|
+
version: 2.3.4
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: chunky_png
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,14 +170,14 @@ dependencies:
|
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: 1.
|
173
|
+
version: 1.24.0
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: 1.
|
180
|
+
version: 1.24.0
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: rubocop-rspec
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -306,6 +306,7 @@ files:
|
|
306
306
|
- lib/puppeteer/mouse.rb
|
307
307
|
- lib/puppeteer/network_condition.rb
|
308
308
|
- lib/puppeteer/network_conditions.rb
|
309
|
+
- lib/puppeteer/network_event_manager.rb
|
309
310
|
- lib/puppeteer/network_manager.rb
|
310
311
|
- lib/puppeteer/page.rb
|
311
312
|
- lib/puppeteer/page/metrics.rb
|
@@ -346,7 +347,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
346
347
|
- !ruby/object:Gem::Version
|
347
348
|
version: '0'
|
348
349
|
requirements: []
|
349
|
-
rubygems_version: 3.
|
350
|
+
rubygems_version: 3.3.3
|
350
351
|
signing_key:
|
351
352
|
specification_version: 4
|
352
353
|
summary: A ruby port of puppeteer
|