ferrum 0.11 → 0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +178 -29
- data/lib/ferrum/browser/binary.rb +46 -0
- data/lib/ferrum/browser/client.rb +13 -12
- data/lib/ferrum/browser/command.rb +7 -8
- data/lib/ferrum/browser/options/base.rb +1 -7
- data/lib/ferrum/browser/options/chrome.rb +17 -11
- data/lib/ferrum/browser/options/firefox.rb +11 -4
- data/lib/ferrum/browser/process.rb +41 -35
- data/lib/ferrum/browser/subscriber.rb +1 -3
- data/lib/ferrum/browser/web_socket.rb +9 -12
- data/lib/ferrum/browser/xvfb.rb +4 -8
- data/lib/ferrum/browser.rb +44 -12
- data/lib/ferrum/context.rb +6 -2
- data/lib/ferrum/contexts.rb +10 -8
- data/lib/ferrum/cookies.rb +10 -9
- data/lib/ferrum/errors.rb +115 -0
- data/lib/ferrum/frame/runtime.rb +20 -17
- data/lib/ferrum/frame.rb +32 -24
- data/lib/ferrum/headers.rb +2 -2
- data/lib/ferrum/keyboard.rb +11 -11
- data/lib/ferrum/mouse.rb +8 -7
- data/lib/ferrum/network/auth_request.rb +7 -2
- data/lib/ferrum/network/exchange.rb +14 -10
- data/lib/ferrum/network/intercepted_request.rb +10 -8
- data/lib/ferrum/network/request.rb +5 -0
- data/lib/ferrum/network/response.rb +4 -4
- data/lib/ferrum/network.rb +124 -35
- data/lib/ferrum/node.rb +69 -23
- data/lib/ferrum/page/animation.rb +0 -1
- data/lib/ferrum/page/frames.rb +46 -20
- data/lib/ferrum/page/screenshot.rb +51 -65
- data/lib/ferrum/page/stream.rb +38 -0
- data/lib/ferrum/page/tracing.rb +71 -0
- data/lib/ferrum/page.rb +81 -36
- data/lib/ferrum/proxy.rb +58 -0
- data/lib/ferrum/{rbga.rb → rgba.rb} +4 -2
- data/lib/ferrum/target.rb +1 -0
- data/lib/ferrum/utils/attempt.rb +20 -0
- data/lib/ferrum/utils/elapsed_time.rb +27 -0
- data/lib/ferrum/utils/platform.rb +28 -0
- data/lib/ferrum/version.rb +1 -1
- data/lib/ferrum.rb +4 -146
- metadata +60 -51
data/lib/ferrum/network.rb
CHANGED
@@ -14,6 +14,10 @@ module Ferrum
|
|
14
14
|
RESOURCE_TYPES = %w[Document Stylesheet Image Media Font Script TextTrack
|
15
15
|
XHR Fetch EventSource WebSocket Manifest
|
16
16
|
SignedExchange Ping CSPViolationReport Other].freeze
|
17
|
+
AUTHORIZE_BLOCK_MISSING = "Block is missing, call `authorize(...) { |r| r.continue } " \
|
18
|
+
"or subscribe to `on(:request)` events before calling it"
|
19
|
+
AUTHORIZE_TYPE_WRONG = ":type should be in #{AUTHORIZE_TYPE}"
|
20
|
+
ALLOWED_CONNECTION_TYPE = %w[none cellular2g cellular3g cellular4g bluetooth ethernet wifi wimax other].freeze
|
17
21
|
|
18
22
|
attr_reader :traffic
|
19
23
|
|
@@ -21,13 +25,16 @@ module Ferrum
|
|
21
25
|
@page = page
|
22
26
|
@traffic = []
|
23
27
|
@exchange = nil
|
28
|
+
@blacklist = nil
|
29
|
+
@whitelist = nil
|
24
30
|
end
|
25
31
|
|
26
32
|
def wait_for_idle(connections: 0, duration: 0.05, timeout: @page.browser.timeout)
|
27
|
-
start =
|
33
|
+
start = Utils::ElapsedTime.monotonic_time
|
28
34
|
|
29
35
|
until idle?(connections)
|
30
|
-
raise TimeoutError if
|
36
|
+
raise TimeoutError if Utils::ElapsedTime.timeout?(start, timeout)
|
37
|
+
|
31
38
|
sleep(duration)
|
32
39
|
end
|
33
40
|
end
|
@@ -61,9 +68,7 @@ module Ferrum
|
|
61
68
|
end
|
62
69
|
|
63
70
|
def clear(type)
|
64
|
-
unless CLEAR_TYPE.include?(type)
|
65
|
-
raise ArgumentError, ":type should be in #{CLEAR_TYPE}"
|
66
|
-
end
|
71
|
+
raise ArgumentError, ":type should be in #{CLEAR_TYPE}" unless CLEAR_TYPE.include?(type)
|
67
72
|
|
68
73
|
if type == :traffic
|
69
74
|
@traffic.clear
|
@@ -74,23 +79,28 @@ module Ferrum
|
|
74
79
|
true
|
75
80
|
end
|
76
81
|
|
82
|
+
def blacklist=(patterns)
|
83
|
+
@blacklist = Array(patterns)
|
84
|
+
blacklist_subscribe
|
85
|
+
end
|
86
|
+
alias blocklist= blacklist=
|
87
|
+
|
88
|
+
def whitelist=(patterns)
|
89
|
+
@whitelist = Array(patterns)
|
90
|
+
whitelist_subscribe
|
91
|
+
end
|
92
|
+
alias allowlist= whitelist=
|
93
|
+
|
77
94
|
def intercept(pattern: "*", resource_type: nil)
|
78
95
|
pattern = { urlPattern: pattern }
|
79
|
-
if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)
|
80
|
-
pattern[:resourceType] = resource_type
|
81
|
-
end
|
96
|
+
pattern[:resourceType] = resource_type if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)
|
82
97
|
|
83
98
|
@page.command("Fetch.enable", handleAuthRequests: true, patterns: [pattern])
|
84
99
|
end
|
85
100
|
|
86
101
|
def authorize(user:, password:, type: :server, &block)
|
87
|
-
unless AUTHORIZE_TYPE.include?(type)
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
if !block_given? && !@page.subscribed?("Fetch.requestPaused")
|
92
|
-
raise ArgumentError, "Block is missing, call `authorize(...) { |r| r.continue } or subscribe to `on(:request)` events before calling it"
|
93
|
-
end
|
102
|
+
raise ArgumentError, AUTHORIZE_TYPE_WRONG unless AUTHORIZE_TYPE.include?(type)
|
103
|
+
raise ArgumentError, AUTHORIZE_BLOCK_MISSING if !block_given? && !@page.subscribed?("Fetch.requestPaused")
|
94
104
|
|
95
105
|
@authorized_ids ||= {}
|
96
106
|
@authorized_ids[type] ||= []
|
@@ -116,6 +126,53 @@ module Ferrum
|
|
116
126
|
end
|
117
127
|
|
118
128
|
def subscribe
|
129
|
+
subscribe_request_will_be_sent
|
130
|
+
subscribe_response_received
|
131
|
+
subscribe_loading_finished
|
132
|
+
subscribe_loading_failed
|
133
|
+
subscribe_log_entry_added
|
134
|
+
end
|
135
|
+
|
136
|
+
def authorized_response(ids, request_id, username, password)
|
137
|
+
if ids.include?(request_id)
|
138
|
+
{ response: "CancelAuth" }
|
139
|
+
elsif username && password
|
140
|
+
{ response: "ProvideCredentials",
|
141
|
+
username: username,
|
142
|
+
password: password }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def select(request_id)
|
147
|
+
@traffic.select { |e| e.id == request_id }
|
148
|
+
end
|
149
|
+
|
150
|
+
def build_exchange(id)
|
151
|
+
Network::Exchange.new(@page, id).tap { |e| @traffic << e }
|
152
|
+
end
|
153
|
+
|
154
|
+
def emulate_network_conditions(offline: false, latency: 0,
|
155
|
+
download_throughput: -1, upload_throughput: -1,
|
156
|
+
connection_type: nil)
|
157
|
+
params = {
|
158
|
+
offline: offline, latency: latency,
|
159
|
+
downloadThroughput: download_throughput,
|
160
|
+
uploadThroughput: upload_throughput
|
161
|
+
}
|
162
|
+
|
163
|
+
params[:connectionType] = connection_type if connection_type && ALLOWED_CONNECTION_TYPE.include?(connection_type)
|
164
|
+
|
165
|
+
@page.command("Network.emulateNetworkConditions", **params)
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
def offline_mode
|
170
|
+
emulate_network_conditions(offline: true, latency: 0, download_throughput: 0, upload_throughput: 0)
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def subscribe_request_will_be_sent
|
119
176
|
@page.on("Network.requestWillBeSent") do |params|
|
120
177
|
request = Network::Request.new(params)
|
121
178
|
|
@@ -140,25 +197,29 @@ module Ferrum
|
|
140
197
|
|
141
198
|
exchange.request = request
|
142
199
|
|
143
|
-
if exchange.navigation_request?(@page.main_frame.id)
|
144
|
-
@exchange = exchange
|
145
|
-
end
|
200
|
+
@exchange = exchange if exchange.navigation_request?(@page.main_frame.id)
|
146
201
|
end
|
202
|
+
end
|
147
203
|
|
204
|
+
def subscribe_response_received
|
148
205
|
@page.on("Network.responseReceived") do |params|
|
149
|
-
|
206
|
+
exchange = select(params["requestId"]).last
|
207
|
+
|
208
|
+
if exchange
|
150
209
|
response = Network::Response.new(@page, params)
|
151
210
|
exchange.response = response
|
152
211
|
end
|
153
212
|
end
|
213
|
+
end
|
154
214
|
|
215
|
+
def subscribe_loading_finished
|
155
216
|
@page.on("Network.loadingFinished") do |params|
|
156
217
|
exchange = select(params["requestId"]).last
|
157
|
-
|
158
|
-
exchange.response.body_size = params["encodedDataLength"]
|
159
|
-
end
|
218
|
+
exchange.response.body_size = params["encodedDataLength"] if exchange&.response
|
160
219
|
end
|
220
|
+
end
|
161
221
|
|
222
|
+
def subscribe_loading_failed
|
162
223
|
@page.on("Network.loadingFailed") do |params|
|
163
224
|
exchange = select(params["requestId"]).last
|
164
225
|
exchange.error ||= Network::Error.new
|
@@ -169,7 +230,9 @@ module Ferrum
|
|
169
230
|
exchange.error.monotonic_time = params["timestamp"]
|
170
231
|
exchange.error.canceled = params["canceled"]
|
171
232
|
end
|
233
|
+
end
|
172
234
|
|
235
|
+
def subscribe_log_entry_added
|
173
236
|
@page.on("Log.entryAdded") do |params|
|
174
237
|
entry = params["entry"] || {}
|
175
238
|
if entry["source"] == "network" && entry["level"] == "error"
|
@@ -184,24 +247,50 @@ module Ferrum
|
|
184
247
|
end
|
185
248
|
end
|
186
249
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
250
|
+
def blacklist_subscribe
|
251
|
+
return unless blacklist?
|
252
|
+
raise ArgumentError, "You can't use blacklist along with whitelist" if whitelist?
|
253
|
+
|
254
|
+
@blacklist_subscribe ||= begin
|
255
|
+
intercept
|
256
|
+
|
257
|
+
@page.on(:request) do |request|
|
258
|
+
if @blacklist.any? { |p| request.match?(p) }
|
259
|
+
request.abort
|
260
|
+
else
|
261
|
+
request.continue
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
true
|
196
266
|
end
|
197
267
|
end
|
198
268
|
|
199
|
-
def
|
200
|
-
|
269
|
+
def whitelist_subscribe
|
270
|
+
return unless whitelist?
|
271
|
+
raise ArgumentError, "You can't use whitelist along with blacklist" if blacklist?
|
272
|
+
|
273
|
+
@whitelist_subscribe ||= begin
|
274
|
+
intercept
|
275
|
+
|
276
|
+
@page.on(:request) do |request|
|
277
|
+
if @whitelist.any? { |p| request.match?(p) }
|
278
|
+
request.continue
|
279
|
+
else
|
280
|
+
request.abort
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
true
|
285
|
+
end
|
201
286
|
end
|
202
287
|
|
203
|
-
def
|
204
|
-
|
288
|
+
def blacklist?
|
289
|
+
Array(@blacklist).any?
|
290
|
+
end
|
291
|
+
|
292
|
+
def whitelist?
|
293
|
+
Array(@whitelist).any?
|
205
294
|
end
|
206
295
|
end
|
207
296
|
end
|
data/lib/ferrum/node.rb
CHANGED
@@ -10,7 +10,8 @@ module Ferrum
|
|
10
10
|
def initialize(frame, target_id, node_id, description)
|
11
11
|
@page = frame.page
|
12
12
|
@target_id = target_id
|
13
|
-
@node_id
|
13
|
+
@node_id = node_id
|
14
|
+
@description = description
|
14
15
|
@tag_name = description["nodeName"].downcase
|
15
16
|
end
|
16
17
|
|
@@ -38,15 +39,16 @@ module Ferrum
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def wait_for_stop_moving(delay: MOVING_WAIT_DELAY, attempts: MOVING_WAIT_ATTEMPTS)
|
41
|
-
|
42
|
-
previous, current =
|
42
|
+
Utils::Attempt.with_retry(errors: NodeMovingError, max: attempts, wait: 0) do
|
43
|
+
previous, current = content_quads_with(delay: delay)
|
43
44
|
raise NodeMovingError.new(self, previous, current) if previous != current
|
45
|
+
|
44
46
|
current
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
50
|
def moving?(delay: MOVING_WAIT_DELAY)
|
49
|
-
previous, current =
|
51
|
+
previous, current = content_quads_with(delay: delay)
|
50
52
|
previous == current
|
51
53
|
end
|
52
54
|
|
@@ -122,17 +124,52 @@ module Ferrum
|
|
122
124
|
def property(name)
|
123
125
|
evaluate("this['#{name}']")
|
124
126
|
end
|
127
|
+
alias [] property
|
125
128
|
|
126
129
|
def attribute(name)
|
127
130
|
evaluate("this.getAttribute('#{name}')")
|
128
131
|
end
|
129
132
|
|
133
|
+
def selected
|
134
|
+
function = <<~JS
|
135
|
+
function(element) {
|
136
|
+
if (element.nodeName.toLowerCase() !== 'select') {
|
137
|
+
throw new Error('Element is not a <select> element.');
|
138
|
+
}
|
139
|
+
return Array.from(element).filter(option => option.selected);
|
140
|
+
}
|
141
|
+
JS
|
142
|
+
page.evaluate_func(function, self, on: self)
|
143
|
+
end
|
144
|
+
|
145
|
+
def select(*values, by: :value)
|
146
|
+
tap do
|
147
|
+
function = <<~JS
|
148
|
+
function(element, values, by) {
|
149
|
+
if (element.nodeName.toLowerCase() !== 'select') {
|
150
|
+
throw new Error('Element is not a <select> element.');
|
151
|
+
}
|
152
|
+
const options = Array.from(element.options);
|
153
|
+
element.value = undefined;
|
154
|
+
for (const option of options) {
|
155
|
+
option.selected = values.some((value) => option[by] === value);
|
156
|
+
if (option.selected && !element.multiple) break;
|
157
|
+
}
|
158
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
159
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
160
|
+
}
|
161
|
+
JS
|
162
|
+
page.evaluate_func(function, self, values.flatten, by, on: self)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
130
166
|
def evaluate(expression)
|
131
167
|
page.evaluate_on(node: self, expression: expression)
|
132
168
|
end
|
133
169
|
|
134
170
|
def ==(other)
|
135
171
|
return false unless other.is_a?(Node)
|
172
|
+
|
136
173
|
# We compare backendNodeId because once nodeId is sent to frontend backend
|
137
174
|
# never returns same nodeId sending 0. In other words frontend is
|
138
175
|
# responsible for keeping track of node ids.
|
@@ -147,30 +184,39 @@ module Ferrum
|
|
147
184
|
points = wait_for_stop_moving.map { |q| to_points(q) }.first
|
148
185
|
get_position(points, x, y, position)
|
149
186
|
rescue CoordinatesNotFoundError
|
150
|
-
x, y =
|
151
|
-
raise if x
|
187
|
+
x, y = bounding_rect_coordinates
|
188
|
+
raise if x.zero? && y.zero?
|
189
|
+
|
152
190
|
[x, y]
|
153
191
|
end
|
154
192
|
|
193
|
+
# Returns a hash of the computed styles for the node
|
194
|
+
def computed_style
|
195
|
+
page
|
196
|
+
.command("CSS.getComputedStyleForNode", nodeId: node_id)["computedStyle"]
|
197
|
+
.each_with_object({}) { |style, memo| memo.merge!(style["name"] => style["value"]) }
|
198
|
+
end
|
199
|
+
|
155
200
|
private
|
156
201
|
|
157
|
-
def
|
202
|
+
def bounding_rect_coordinates
|
158
203
|
evaluate <<~JS
|
159
204
|
[this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2),
|
160
205
|
this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)]
|
161
206
|
JS
|
162
207
|
end
|
163
208
|
|
164
|
-
def
|
209
|
+
def content_quads
|
165
210
|
quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
|
166
|
-
raise CoordinatesNotFoundError, "Node is either not visible or not an HTMLElement" if quads.size
|
211
|
+
raise CoordinatesNotFoundError, "Node is either not visible or not an HTMLElement" if quads.size.zero?
|
212
|
+
|
167
213
|
quads
|
168
214
|
end
|
169
215
|
|
170
|
-
def
|
171
|
-
previous =
|
216
|
+
def content_quads_with(delay: MOVING_WAIT_DELAY)
|
217
|
+
previous = content_quads
|
172
218
|
sleep(delay)
|
173
|
-
current =
|
219
|
+
current = content_quads
|
174
220
|
[previous, current]
|
175
221
|
end
|
176
222
|
|
@@ -182,28 +228,28 @@ module Ferrum
|
|
182
228
|
x = point[:x] + offset_x.to_i
|
183
229
|
y = point[:y] + offset_y.to_i
|
184
230
|
else
|
185
|
-
x, y = points.inject([0, 0]) do |memo,
|
186
|
-
[memo[0] +
|
187
|
-
memo[1] +
|
231
|
+
x, y = points.inject([0, 0]) do |memo, coordinate|
|
232
|
+
[memo[0] + coordinate[:x],
|
233
|
+
memo[1] + coordinate[:y]]
|
188
234
|
end
|
189
235
|
|
190
|
-
x
|
191
|
-
y
|
236
|
+
x /= 4
|
237
|
+
y /= 4
|
192
238
|
end
|
193
239
|
|
194
240
|
if offset_x && offset_y && position == :center
|
195
|
-
x
|
196
|
-
y
|
241
|
+
x += offset_x.to_i
|
242
|
+
y += offset_y.to_i
|
197
243
|
end
|
198
244
|
|
199
245
|
[x, y]
|
200
246
|
end
|
201
247
|
|
202
248
|
def to_points(quad)
|
203
|
-
[{x: quad[0], y: quad[1]},
|
204
|
-
{x: quad[2], y: quad[3]},
|
205
|
-
{x: quad[4], y: quad[5]},
|
206
|
-
{x: quad[6], y: quad[7]}]
|
249
|
+
[{ x: quad[0], y: quad[1] },
|
250
|
+
{ x: quad[2], y: quad[3] },
|
251
|
+
{ x: quad[4], y: quad[5] },
|
252
|
+
{ x: quad[6], y: quad[7] }]
|
207
253
|
end
|
208
254
|
end
|
209
255
|
end
|
data/lib/ferrum/page/frames.rb
CHANGED
@@ -11,35 +11,60 @@ module Ferrum
|
|
11
11
|
@frames.values
|
12
12
|
end
|
13
13
|
|
14
|
-
def frame_by(id: nil, name: nil)
|
14
|
+
def frame_by(id: nil, name: nil, execution_id: nil)
|
15
15
|
if id
|
16
16
|
@frames[id]
|
17
17
|
elsif name
|
18
18
|
frames.find { |f| f.name == name }
|
19
|
+
elsif execution_id
|
20
|
+
frames.find { |f| f.execution_id == execution_id }
|
19
21
|
else
|
20
22
|
raise ArgumentError
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
def frames_subscribe
|
27
|
+
subscribe_frame_attached
|
28
|
+
subscribe_frame_started_loading
|
29
|
+
subscribe_frame_navigated
|
30
|
+
subscribe_frame_stopped_loading
|
31
|
+
|
32
|
+
subscribe_navigated_within_document
|
33
|
+
|
34
|
+
subscribe_request_will_be_sent
|
35
|
+
|
36
|
+
subscribe_execution_context_created
|
37
|
+
subscribe_execution_context_destroyed
|
38
|
+
subscribe_execution_contexts_cleared
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def subscribe_frame_attached
|
25
44
|
on("Page.frameAttached") do |params|
|
26
45
|
parent_frame_id, frame_id = params.values_at("parentFrameId", "frameId")
|
27
46
|
@frames[frame_id] = Frame.new(frame_id, self, parent_frame_id)
|
28
47
|
end
|
48
|
+
end
|
29
49
|
|
50
|
+
def subscribe_frame_started_loading
|
30
51
|
on("Page.frameStartedLoading") do |params|
|
31
52
|
frame = @frames[params["frameId"]]
|
32
53
|
frame.state = :started_loading
|
33
54
|
@event.reset
|
34
55
|
end
|
56
|
+
end
|
35
57
|
|
58
|
+
def subscribe_frame_navigated
|
36
59
|
on("Page.frameNavigated") do |params|
|
37
60
|
frame_id, name = params["frame"]&.values_at("id", "name")
|
38
61
|
frame = @frames[frame_id]
|
39
62
|
frame.state = :navigated
|
40
63
|
frame.name = name unless name.to_s.empty?
|
41
64
|
end
|
65
|
+
end
|
42
66
|
|
67
|
+
def subscribe_frame_stopped_loading
|
43
68
|
on("Page.frameStoppedLoading") do |params|
|
44
69
|
# `DOM.performSearch` doesn't work without getting #document node first.
|
45
70
|
# It returns node with nodeId 1 and nodeType 9 from which descend the
|
@@ -47,7 +72,7 @@ module Ferrum
|
|
47
72
|
# node will change the id and all subsequent nodes have to change id too.
|
48
73
|
if @main_frame.id == params["frameId"]
|
49
74
|
@event.set if idling?
|
50
|
-
|
75
|
+
document_node_id
|
51
76
|
end
|
52
77
|
|
53
78
|
frame = @frames[params["frameId"]]
|
@@ -55,58 +80,59 @@ module Ferrum
|
|
55
80
|
|
56
81
|
@event.set if idling?
|
57
82
|
end
|
83
|
+
end
|
58
84
|
|
85
|
+
def subscribe_navigated_within_document
|
59
86
|
on("Page.navigatedWithinDocument") do
|
60
87
|
@event.set if idling?
|
61
88
|
end
|
89
|
+
end
|
62
90
|
|
91
|
+
def subscribe_request_will_be_sent
|
63
92
|
on("Network.requestWillBeSent") do |params|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
@event.reset if params["type"] == "Document"
|
70
|
-
end
|
93
|
+
# Possible types:
|
94
|
+
# Document, Stylesheet, Image, Media, Font, Script, TextTrack, XHR,
|
95
|
+
# Fetch, EventSource, WebSocket, Manifest, SignedExchange, Ping,
|
96
|
+
# CSPViolationReport, Other
|
97
|
+
@event.reset if params["frameId"] == @main_frame.id && params["type"] == "Document"
|
71
98
|
end
|
99
|
+
end
|
72
100
|
|
101
|
+
def subscribe_execution_context_created
|
73
102
|
on("Runtime.executionContextCreated") do |params|
|
74
|
-
setting_up_main_frame = false
|
75
103
|
context_id = params.dig("context", "id")
|
76
104
|
frame_id = params.dig("context", "auxData", "frameId")
|
77
105
|
|
78
106
|
unless @main_frame.id
|
79
107
|
root_frame = command("Page.getFrameTree").dig("frameTree", "frame", "id")
|
80
108
|
if frame_id == root_frame
|
81
|
-
setting_up_main_frame = true
|
82
109
|
@main_frame.id = frame_id
|
83
110
|
@frames[frame_id] = @main_frame
|
84
111
|
end
|
85
112
|
end
|
86
113
|
|
87
114
|
frame = @frames[frame_id] || Frame.new(frame_id, self)
|
88
|
-
frame.
|
89
|
-
|
90
|
-
# Set event because `execution_id` might raise NoExecutionContextError
|
91
|
-
@event.set if setting_up_main_frame
|
115
|
+
frame.execution_id = context_id
|
92
116
|
|
93
117
|
@frames[frame_id] ||= frame
|
94
118
|
end
|
119
|
+
end
|
95
120
|
|
121
|
+
def subscribe_execution_context_destroyed
|
96
122
|
on("Runtime.executionContextDestroyed") do |params|
|
97
123
|
execution_id = params["executionContextId"]
|
98
|
-
frame =
|
99
|
-
frame
|
124
|
+
frame = frame_by(execution_id: execution_id)
|
125
|
+
frame&.execution_id = nil
|
100
126
|
end
|
127
|
+
end
|
101
128
|
|
129
|
+
def subscribe_execution_contexts_cleared
|
102
130
|
on("Runtime.executionContextsCleared") do
|
103
131
|
@frames.delete_if { |_, f| !f.main? }
|
104
|
-
@main_frame.
|
132
|
+
@main_frame.execution_id = nil
|
105
133
|
end
|
106
134
|
end
|
107
135
|
|
108
|
-
private
|
109
|
-
|
110
136
|
def idling?
|
111
137
|
@frames.all? { |_, f| f.state == :stopped_loading }
|
112
138
|
end
|