puppeteer-ruby 0.43.0 → 0.44.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/docs/api_coverage.md +5 -12
- data/lib/puppeteer/aria_query_handler.rb +51 -11
- data/lib/puppeteer/browser.rb +1 -1
- data/lib/puppeteer/chrome_target_manager.rb +28 -10
- data/lib/puppeteer/connection.rb +6 -3
- data/lib/puppeteer/coverage.rb +1 -1
- data/lib/puppeteer/custom_query_handler.rb +29 -2
- data/lib/puppeteer/element_handle.rb +16 -48
- data/lib/puppeteer/execution_context.rb +3 -60
- data/lib/puppeteer/frame.rb +30 -29
- data/lib/puppeteer/frame_manager.rb +6 -6
- data/lib/puppeteer/{dom_world.rb → isolated_world.rb} +36 -63
- data/lib/puppeteer/js_handle.rb +2 -6
- data/lib/puppeteer/lifecycle_watcher.rb +23 -3
- data/lib/puppeteer/page.rb +3 -3
- data/lib/puppeteer/query_handler_manager.rb +37 -2
- data/lib/puppeteer/remote_object.rb +2 -2
- data/lib/puppeteer/target.rb +2 -2
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ade25ba8f8e99240ed29fdad9300af7b65cc12ddf09e45c43f7b672c6324ed4
|
4
|
+
data.tar.gz: 97d3bfc48b3a2e3729dd8857c2bb6deca7babd4d0b055195f2dbe7c01c941e97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 891d8c29ac63d25cdb34178024d7a9202675d77b121a903e24a51e9465e46a9b7a0a9b70a2d893d9b274724b5547d5bb90e133aa2c217052f3f523f952e1a0a3
|
7
|
+
data.tar.gz: eea1fb6ff2f179208932a3bb84ca96841b1b08ff588d914e88ace6ad9e31f71c567c21321e3376b019a265122e51a4352b3f0039dd6bea770dba2a383efe82e5
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,17 @@
|
|
1
|
-
### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.
|
1
|
+
### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.44.0...main)]
|
2
2
|
|
3
3
|
- xxx
|
4
4
|
|
5
|
+
### 0.44.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.43.1...0.44.0)]
|
6
|
+
|
7
|
+
- Port Puppeteer v17.0-v17.1 features.
|
8
|
+
- `wait_for_selector` no longer accept `root` parameter.
|
9
|
+
|
10
|
+
|
11
|
+
### 0.43.1 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.43.0...0.43.1)]
|
12
|
+
|
13
|
+
- Port Puppeteer v16.1 features, including bugfix and XPath query handler.
|
14
|
+
|
5
15
|
### 0.43.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.42.0...0.43.0)]
|
6
16
|
|
7
17
|
- Port Puppeteer v16.0 features. Increasing stability.
|
data/docs/api_coverage.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# API coverages
|
2
|
-
- Puppeteer version:
|
3
|
-
- puppeteer-ruby version: 0.
|
2
|
+
- Puppeteer version: v17.1.3
|
3
|
+
- puppeteer-ruby version: 0.44.0
|
4
4
|
|
5
5
|
## Puppeteer
|
6
6
|
|
@@ -150,13 +150,6 @@
|
|
150
150
|
* ~~removeAllListeners~~
|
151
151
|
* ~~removeListener~~
|
152
152
|
|
153
|
-
## ExecutionContext
|
154
|
-
|
155
|
-
* evaluate
|
156
|
-
* evaluateHandle => `#evaluate_handle`
|
157
|
-
* frame
|
158
|
-
* ~~queryObjects~~
|
159
|
-
|
160
153
|
## FileChooser
|
161
154
|
|
162
155
|
* accept
|
@@ -172,12 +165,12 @@
|
|
172
165
|
* $x => `#Sx`
|
173
166
|
* addScriptTag => `#add_script_tag`
|
174
167
|
* addStyleTag => `#add_style_tag`
|
168
|
+
* addStyleTag => `#add_style_tag`
|
175
169
|
* childFrames => `#child_frames`
|
176
170
|
* click
|
177
171
|
* content
|
178
172
|
* evaluate
|
179
173
|
* evaluateHandle => `#evaluate_handle`
|
180
|
-
* executionContext => `#execution_context`
|
181
174
|
* focus
|
182
175
|
* goto
|
183
176
|
* hover
|
@@ -251,7 +244,6 @@
|
|
251
244
|
* dispose
|
252
245
|
* evaluate
|
253
246
|
* evaluateHandle => `#evaluate_handle`
|
254
|
-
* executionContext => `#execution_context`
|
255
247
|
* getProperties => `#properties`
|
256
248
|
* getProperty => `#[]`
|
257
249
|
* getProperty => `#[]`
|
@@ -289,6 +281,7 @@
|
|
289
281
|
* $x => `#Sx`
|
290
282
|
* addScriptTag => `#add_script_tag`
|
291
283
|
* addStyleTag => `#add_style_tag`
|
284
|
+
* addStyleTag => `#add_style_tag`
|
292
285
|
* authenticate
|
293
286
|
* bringToFront => `#bring_to_front`
|
294
287
|
* browser
|
@@ -313,6 +306,7 @@
|
|
313
306
|
* exposeFunction => `#expose_function`
|
314
307
|
* focus
|
315
308
|
* frames
|
309
|
+
* ~~getDefaultTimeout~~
|
316
310
|
* goBack => `#go_back`
|
317
311
|
* goForward => `#go_forward`
|
318
312
|
* goto
|
@@ -401,5 +395,4 @@
|
|
401
395
|
|
402
396
|
* ~~evaluate~~
|
403
397
|
* ~~evaluateHandle~~
|
404
|
-
* ~~executionContext~~
|
405
398
|
* ~~url~~
|
@@ -25,43 +25,83 @@ class Puppeteer::AriaQueryHandler
|
|
25
25
|
query_options
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
# @param element [Puppeteer::ElementHandle]
|
29
|
+
# @param selector [String]
|
30
|
+
private def query_one_id(element, selector)
|
30
31
|
parse_result = parse_aria_selector(selector)
|
31
32
|
res = element.query_ax_tree(accessible_name: parse_result[:name], role: parse_result[:role])
|
32
|
-
|
33
|
+
|
34
|
+
if res.first.is_a?(Hash)
|
35
|
+
res.first['backendDOMNodeId']
|
36
|
+
else
|
33
37
|
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def query_one(element, selector)
|
42
|
+
id = query_one_id(element, selector)
|
43
|
+
|
44
|
+
if id
|
45
|
+
element.frame.main_world.adopt_backend_node(id)
|
34
46
|
else
|
35
|
-
|
47
|
+
nil
|
36
48
|
end
|
37
49
|
end
|
38
50
|
|
39
|
-
def wait_for(
|
51
|
+
def wait_for(element_or_frame, selector, visible: nil, hidden: nil, timeout: nil)
|
52
|
+
case element_or_frame
|
53
|
+
when Puppeteer::Frame
|
54
|
+
frame = element_or_frame
|
55
|
+
element = nil
|
56
|
+
when Puppeteer::ElementHandle
|
57
|
+
frame = element_or_frame.frame
|
58
|
+
element = frame.puppeteer_world.adopt_handle(element_or_frame)
|
59
|
+
else
|
60
|
+
raise ArgumentError.new("element_or_frame must be a Frame or ElementHandle. #{element_or_frame.inspect}")
|
61
|
+
end
|
62
|
+
|
40
63
|
# addHandlerToWorld
|
41
|
-
binding_function = Puppeteer::
|
64
|
+
binding_function = Puppeteer::IsolaatedWorld::BindingFunction.new(
|
42
65
|
name: 'ariaQuerySelector',
|
43
|
-
proc: -> (sel) {
|
66
|
+
proc: -> (sel) {
|
67
|
+
id = query_one_id(element || frame.puppeteer_world.document, sel)
|
68
|
+
|
69
|
+
if id
|
70
|
+
frame.puppeteer_world.adopt_backend_node(id)
|
71
|
+
else
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
},
|
44
75
|
)
|
45
|
-
|
76
|
+
result = frame.puppeteer_world.send(:wait_for_selector_in_page,
|
46
77
|
'(_, selector) => globalThis.ariaQuerySelector(selector)',
|
78
|
+
element,
|
47
79
|
selector,
|
48
80
|
visible: visible,
|
49
81
|
hidden: hidden,
|
50
82
|
timeout: timeout,
|
51
83
|
binding_function: binding_function,
|
52
|
-
root: root,
|
53
84
|
)
|
85
|
+
|
86
|
+
element&.dispose
|
87
|
+
|
88
|
+
if result.is_a?(Puppeteer::ElementHandle)
|
89
|
+
result.frame.main_world.transfer_handle(result)
|
90
|
+
else
|
91
|
+
result&.dispose
|
92
|
+
nil
|
93
|
+
end
|
54
94
|
end
|
55
95
|
|
56
96
|
def query_all(element, selector)
|
57
|
-
|
97
|
+
world = element.frame.main_world
|
58
98
|
parse_result = parse_aria_selector(selector)
|
59
99
|
res = element.query_ax_tree(accessible_name: parse_result[:name], role: parse_result[:role])
|
60
100
|
if res.empty?
|
61
101
|
nil
|
62
102
|
else
|
63
103
|
promises = res.map do |ax_node|
|
64
|
-
|
104
|
+
world.send(:async_adopt_backend_node, ax_node['backendDOMNodeId'])
|
65
105
|
end
|
66
106
|
await_all(*promises)
|
67
107
|
end
|
data/lib/puppeteer/browser.rb
CHANGED
@@ -196,7 +196,7 @@ class Puppeteer::Browser
|
|
196
196
|
session: session,
|
197
197
|
browser_context: context,
|
198
198
|
target_manager: @target_manager,
|
199
|
-
session_factory: -> { @connection.create_session(target_info) },
|
199
|
+
session_factory: -> (auto_attach_emulated) { @connection.create_session(target_info, auto_attach_emulated: auto_attach_emulated) },
|
200
200
|
ignore_https_errors: @ignore_https_errors,
|
201
201
|
default_viewport: @default_viewport,
|
202
202
|
is_page_target_callback: @is_page_target_callback,
|
@@ -1,4 +1,5 @@
|
|
1
1
|
class Puppeteer::ChromeTargetManager
|
2
|
+
include Puppeteer::DebugPrint
|
2
3
|
include Puppeteer::EventCallbackable
|
3
4
|
|
4
5
|
def initialize(connection:, target_factory:, target_filter_callback:)
|
@@ -33,20 +34,34 @@ class Puppeteer::ChromeTargetManager
|
|
33
34
|
)
|
34
35
|
|
35
36
|
setup_attachment_listeners(@connection)
|
36
|
-
@connection.async_send_message('Target.setDiscoverTargets',
|
37
|
+
@connection.async_send_message('Target.setDiscoverTargets', {
|
38
|
+
discover: true,
|
39
|
+
filter: [
|
40
|
+
{ type: 'tab', exclude: true },
|
41
|
+
{},
|
42
|
+
],
|
43
|
+
}).then do
|
44
|
+
store_existing_targets_for_init
|
45
|
+
end.rescue do |err|
|
46
|
+
debug_puts(err)
|
47
|
+
end
|
37
48
|
end
|
38
49
|
|
39
|
-
def
|
50
|
+
private def store_existing_targets_for_init
|
40
51
|
@discovered_targets_by_target_id.each do |target_id, target_info|
|
41
|
-
if @target_filter_callback.call(target_info)
|
52
|
+
if @target_filter_callback.call(target_info) && target_info.type != 'browser'
|
42
53
|
@target_ids_for_init << target_id
|
43
54
|
end
|
44
55
|
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def init
|
45
59
|
@connection.send_message('Target.setAutoAttach', {
|
46
60
|
waitForDebuggerOnStart: true,
|
47
61
|
flatten: true,
|
48
62
|
autoAttach: true,
|
49
63
|
})
|
64
|
+
finish_initialization_if_ready
|
50
65
|
@initialize_promise.value!
|
51
66
|
end
|
52
67
|
|
@@ -114,7 +129,7 @@ class Puppeteer::ChromeTargetManager
|
|
114
129
|
# Special case (https://crbug.com/1338156): currently, shared_workers
|
115
130
|
# don't get auto-attached. This should be removed once the auto-attach
|
116
131
|
# works.
|
117
|
-
@connection.create_session(target_info)
|
132
|
+
@connection.create_session(target_info, auto_attach_emulated: true)
|
118
133
|
end
|
119
134
|
end
|
120
135
|
|
@@ -170,6 +185,8 @@ class Puppeteer::ChromeTargetManager
|
|
170
185
|
end
|
171
186
|
}
|
172
187
|
|
188
|
+
return unless @connection.auto_attached?(target_info.target_id)
|
189
|
+
|
173
190
|
# Special case for service workers: being attached to service workers will
|
174
191
|
# prevent them from ever being destroyed. Therefore, we silently detach
|
175
192
|
# from service workers unless the connection was manually created via
|
@@ -197,6 +214,8 @@ class Puppeteer::ChromeTargetManager
|
|
197
214
|
return
|
198
215
|
end
|
199
216
|
|
217
|
+
is_existing_target = @attached_targets_by_target_id.has_key?(target_info.target_id)
|
218
|
+
|
200
219
|
target = @attached_targets_by_target_id[target_info.target_id] || @target_factory.call(target_info, session)
|
201
220
|
setup_attachment_listeners(session)
|
202
221
|
|
@@ -218,11 +237,10 @@ class Puppeteer::ChromeTargetManager
|
|
218
237
|
end
|
219
238
|
|
220
239
|
@target_ids_for_init.delete(target.target_id)
|
221
|
-
|
222
|
-
|
223
|
-
if @target_ids_for_init.empty?
|
224
|
-
@initialize_promise.fulfill(nil) unless @initialize_promise.resolved?
|
240
|
+
unless is_existing_target
|
241
|
+
future { emit_event(TargetManagerEmittedEvents::TargetAvailable, target) }
|
225
242
|
end
|
243
|
+
finish_initialization_if_ready
|
226
244
|
|
227
245
|
future do
|
228
246
|
# TODO: the browser might be shutting down here. What do we do with the error?
|
@@ -239,8 +257,8 @@ class Puppeteer::ChromeTargetManager
|
|
239
257
|
end
|
240
258
|
end
|
241
259
|
|
242
|
-
private def finish_initialization_if_ready(target_id)
|
243
|
-
@target_ids_for_init.delete(target_id)
|
260
|
+
private def finish_initialization_if_ready(target_id = nil)
|
261
|
+
@target_ids_for_init.delete(target_id) if target_id
|
244
262
|
if @target_ids_for_init.empty?
|
245
263
|
@initialize_promise.fulfill(nil) unless @initialize_promise.resolved?
|
246
264
|
end
|
data/lib/puppeteer/connection.rb
CHANGED
@@ -319,15 +319,18 @@ class Puppeteer::Connection
|
|
319
319
|
end
|
320
320
|
|
321
321
|
def auto_attached?(target_id)
|
322
|
-
|
322
|
+
!@manually_attached.include?(target_id)
|
323
323
|
end
|
324
324
|
|
325
325
|
# @param {Protocol.Target.TargetInfo} targetInfo
|
326
326
|
# @return [CDPSession]
|
327
|
-
def create_session(target_info)
|
328
|
-
|
327
|
+
def create_session(target_info, auto_attach_emulated: false)
|
328
|
+
unless auto_attach_emulated
|
329
|
+
@manually_attached << target_info.target_id
|
330
|
+
end
|
329
331
|
result = send_message('Target.attachToTarget', targetId: target_info.target_id, flatten: true)
|
330
332
|
session_id = result['sessionId']
|
333
|
+
@manually_attached.delete(target_info.target_id)
|
331
334
|
@sessions[session_id]
|
332
335
|
end
|
333
336
|
end
|
data/lib/puppeteer/coverage.rb
CHANGED
@@ -21,12 +21,39 @@ class Puppeteer::CustomQueryHandler
|
|
21
21
|
nil
|
22
22
|
end
|
23
23
|
|
24
|
-
def wait_for(
|
24
|
+
def wait_for(element_or_frame, selector, visible: nil, hidden: nil, timeout: nil)
|
25
|
+
case element_or_frame
|
26
|
+
when Puppeteer::Frame
|
27
|
+
frame = element_or_frame
|
28
|
+
element = nil
|
29
|
+
when Puppeteer::ElementHandle
|
30
|
+
frame = element_or_frame.frame
|
31
|
+
element = frame.puppeteer_world.adopt_handle(element_or_frame)
|
32
|
+
else
|
33
|
+
raise ArgumentError.new("element_or_frame must be a Frame or ElementHandle. #{element_or_frame.inspect}")
|
34
|
+
end
|
35
|
+
|
25
36
|
unless @query_one
|
26
37
|
raise NotImplementedError.new("#{self.class}##{__method__} is not implemented.")
|
27
38
|
end
|
28
39
|
|
29
|
-
|
40
|
+
result = frame.puppeteer_world.send(:wait_for_selector_in_page,
|
41
|
+
@query_one,
|
42
|
+
element,
|
43
|
+
selector,
|
44
|
+
visible: visible,
|
45
|
+
hidden: hidden,
|
46
|
+
timeout: timeout,
|
47
|
+
)
|
48
|
+
|
49
|
+
element&.dispose
|
50
|
+
|
51
|
+
if result.is_a?(Puppeteer::ElementHandle)
|
52
|
+
result.frame.main_world.transfer_handle(result)
|
53
|
+
else
|
54
|
+
result&.dispose
|
55
|
+
nil
|
56
|
+
end
|
30
57
|
end
|
31
58
|
|
32
59
|
def query_all(element, selector)
|
@@ -12,16 +12,16 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
12
12
|
# @param client [Puppeteer::CDPSession]
|
13
13
|
# @param remote_object [Puppeteer::RemoteObject]
|
14
14
|
# @param frame [Puppeteer::Frame]
|
15
|
-
|
16
|
-
# @param frame_manager [Puppeteer::FrameManager]
|
17
|
-
def initialize(context:, client:, remote_object:, frame:, page:, frame_manager:)
|
15
|
+
def initialize(context:, client:, remote_object:, frame:)
|
18
16
|
super(context: context, client: client, remote_object: remote_object)
|
19
17
|
@frame = frame
|
20
|
-
@page = page
|
21
|
-
@frame_manager = frame_manager
|
18
|
+
@page = frame.page
|
19
|
+
@frame_manager = frame.frame_manager
|
22
20
|
@disposed = false
|
23
21
|
end
|
24
22
|
|
23
|
+
attr_reader :page, :frame, :frame_manager
|
24
|
+
|
25
25
|
def inspect
|
26
26
|
values = %i[context remote_object page disposed].map do |sym|
|
27
27
|
value = instance_variable_get(:"@#{sym}")
|
@@ -60,18 +60,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
60
60
|
# (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
61
61
|
# by using the {@link Page.setDefaultTimeout} method.
|
62
62
|
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
|
63
|
-
|
64
|
-
|
65
|
-
secondary_world = frame.secondary_world
|
66
|
-
adopted_root = secondary_world.execution_context.adopt_element_handle(self)
|
67
|
-
handle = secondary_world.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout, root: adopted_root)
|
68
|
-
adopted_root.dispose
|
69
|
-
return nil unless handle
|
70
|
-
|
71
|
-
main_world = frame.main_world
|
72
|
-
result = main_world.execution_context.adopt_element_handle(handle)
|
73
|
-
handle.dispose
|
74
|
-
result
|
63
|
+
query_handler_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout)
|
75
64
|
end
|
76
65
|
|
77
66
|
define_async_method :async_wait_for_selector
|
@@ -125,28 +114,14 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
125
114
|
# Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default
|
126
115
|
# value can be changed by using the {@link Page.setDefaultTimeout} method.
|
127
116
|
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
|
128
|
-
frame = @context.frame
|
129
|
-
|
130
|
-
secondary_world = frame.secondary_world
|
131
|
-
adopted_root = secondary_world.execution_context.adopt_element_handle(self)
|
132
117
|
param_xpath =
|
133
118
|
if xpath.start_with?('//')
|
134
119
|
".#{xpath}"
|
135
120
|
else
|
136
121
|
xpath
|
137
122
|
end
|
138
|
-
unless param_xpath.start_with?('.//')
|
139
|
-
adopted_root.dispose
|
140
|
-
raise ArgumentError.new("Unsupported xpath expression: #{xpath}")
|
141
|
-
end
|
142
|
-
handle = secondary_world.wait_for_xpath(param_xpath, visible: visible, hidden: hidden, timeout: timeout, root: adopted_root)
|
143
|
-
adopted_root.dispose
|
144
|
-
return nil unless handle
|
145
123
|
|
146
|
-
|
147
|
-
result = main_world.execution_context.adopt_element_handle(handle)
|
148
|
-
handle.dispose
|
149
|
-
result
|
124
|
+
wait_for_selector("xpath/#{param_xpath}", visible: visible, hidden: hidden, timeout: timeout)
|
150
125
|
end
|
151
126
|
|
152
127
|
define_async_method :async_wait_for_xpath
|
@@ -623,21 +598,14 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
623
598
|
# @param expression [String]
|
624
599
|
# @return [Array<ElementHandle>]
|
625
600
|
def Sx(expression)
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
return array;
|
635
|
-
}
|
636
|
-
JAVASCRIPT
|
637
|
-
handles = evaluate_handle(fn, expression)
|
638
|
-
properties = handles.properties
|
639
|
-
handles.dispose
|
640
|
-
properties.values.map(&:as_element).compact
|
601
|
+
param_xpath =
|
602
|
+
if expression.start_with?('//')
|
603
|
+
".#{expression}"
|
604
|
+
else
|
605
|
+
expression
|
606
|
+
end
|
607
|
+
|
608
|
+
query_selector_all("xpath/#{param_xpath}")
|
641
609
|
end
|
642
610
|
|
643
611
|
define_async_method :async_Sx
|
@@ -674,6 +642,6 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
674
642
|
# used in AriaQueryHandler
|
675
643
|
def query_ax_tree(accessible_name: nil, role: nil)
|
676
644
|
@remote_object.query_ax_tree(@client,
|
677
|
-
|
645
|
+
accessible_name: accessible_name, role: role)
|
678
646
|
end
|
679
647
|
end
|
@@ -7,7 +7,7 @@ class Puppeteer::ExecutionContext
|
|
7
7
|
|
8
8
|
# @param client [Puppeteer::CDPSession]
|
9
9
|
# @param context_payload [Hash]
|
10
|
-
# @param world [Puppeteer::
|
10
|
+
# @param world [Puppeteer::IsolaatedWorld?]
|
11
11
|
def initialize(client, context_payload, world)
|
12
12
|
@client = client
|
13
13
|
@world = world
|
@@ -17,23 +17,16 @@ class Puppeteer::ExecutionContext
|
|
17
17
|
|
18
18
|
attr_reader :client, :world
|
19
19
|
|
20
|
-
# only used in
|
20
|
+
# only used in IsolaatedWorld
|
21
21
|
private def _context_id
|
22
22
|
@context_id
|
23
23
|
end
|
24
24
|
|
25
|
-
# only used in
|
25
|
+
# only used in IsolaatedWorld::BindingFunction#add_binding_to_context
|
26
26
|
private def _context_name
|
27
27
|
@context_name
|
28
28
|
end
|
29
29
|
|
30
|
-
# @return [Puppeteer::Frame]
|
31
|
-
def frame
|
32
|
-
if_present(@world) do |world|
|
33
|
-
world.frame
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
30
|
# @param page_function [String]
|
38
31
|
# @return [Object]
|
39
32
|
def evaluate(page_function, *args)
|
@@ -208,54 +201,4 @@ class Puppeteer::ExecutionContext
|
|
208
201
|
context_id: @context_id,
|
209
202
|
)
|
210
203
|
end
|
211
|
-
|
212
|
-
# /**
|
213
|
-
# * @param {!JSHandle} prototypeHandle
|
214
|
-
# * @return {!Promise<!JSHandle>}
|
215
|
-
# */
|
216
|
-
# async queryObjects(prototypeHandle) {
|
217
|
-
# assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
|
218
|
-
# assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value');
|
219
|
-
# const response = await this._client.send('Runtime.queryObjects', {
|
220
|
-
# prototypeObjectId: prototypeHandle._remoteObject.objectId
|
221
|
-
# });
|
222
|
-
# return createJSHandle(this, response.objects);
|
223
|
-
# }
|
224
|
-
|
225
|
-
# @param backend_node_id [Integer]
|
226
|
-
# @return [Puppeteer::ElementHandle]
|
227
|
-
def adopt_backend_node_id(backend_node_id)
|
228
|
-
response = @client.send_message('DOM.resolveNode',
|
229
|
-
backendNodeId: backend_node_id,
|
230
|
-
executionContextId: @context_id,
|
231
|
-
)
|
232
|
-
Puppeteer::JSHandle.create(
|
233
|
-
context: self,
|
234
|
-
remote_object: Puppeteer::RemoteObject.new(response["object"]),
|
235
|
-
)
|
236
|
-
end
|
237
|
-
private define_async_method :async_adopt_backend_node_id
|
238
|
-
|
239
|
-
# @param element_handle [Puppeteer::ElementHandle]
|
240
|
-
# @return [Puppeteer::ElementHandle]
|
241
|
-
def adopt_element_handle(element_handle)
|
242
|
-
if element_handle.execution_context == self
|
243
|
-
raise ArgumentError.new('Cannot adopt handle that already belongs to this execution context')
|
244
|
-
end
|
245
|
-
|
246
|
-
unless @world
|
247
|
-
raise 'Cannot adopt handle without DOMWorld'
|
248
|
-
end
|
249
|
-
|
250
|
-
node_info = element_handle.remote_object.node_info(@client)
|
251
|
-
response = @client.send_message('DOM.resolveNode',
|
252
|
-
backendNodeId: node_info["node"]["backendNodeId"],
|
253
|
-
executionContextId: @context_id,
|
254
|
-
)
|
255
|
-
|
256
|
-
Puppeteer::JSHandle.create(
|
257
|
-
context: self,
|
258
|
-
remote_object: Puppeteer::RemoteObject.new(response["object"]),
|
259
|
-
)
|
260
|
-
end
|
261
204
|
end
|
data/lib/puppeteer/frame.rb
CHANGED
@@ -37,8 +37,8 @@ class Puppeteer::Frame
|
|
37
37
|
# @param client [Puppeteer::CDPSession]
|
38
38
|
private def update_client(client)
|
39
39
|
@client = client
|
40
|
-
@main_world = Puppeteer::
|
41
|
-
@
|
40
|
+
@main_world = Puppeteer::IsolaatedWorld.new(@client, @frame_manager, self, @frame_manager.timeout_settings)
|
41
|
+
@puppeteer_world = Puppeteer::IsolaatedWorld.new(@client, @frame_manager, self, @frame_manager.timeout_settings)
|
42
42
|
end
|
43
43
|
|
44
44
|
def page
|
@@ -49,7 +49,7 @@ class Puppeteer::Frame
|
|
49
49
|
@client != @frame_manager.client
|
50
50
|
end
|
51
51
|
|
52
|
-
attr_accessor :frame_manager, :id, :loader_id, :lifecycle_events, :main_world, :
|
52
|
+
attr_accessor :frame_manager, :id, :loader_id, :lifecycle_events, :main_world, :puppeteer_world
|
53
53
|
|
54
54
|
def has_started_loading?
|
55
55
|
@has_started_loading
|
@@ -106,7 +106,14 @@ class Puppeteer::Frame
|
|
106
106
|
# @param {string} expression
|
107
107
|
# @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
|
108
108
|
def Sx(expression)
|
109
|
-
|
109
|
+
param_xpath =
|
110
|
+
if expression.start_with?('//')
|
111
|
+
".#{expression}"
|
112
|
+
else
|
113
|
+
expression
|
114
|
+
end
|
115
|
+
|
116
|
+
query_selector_all("xpath/#{param_xpath}")
|
110
117
|
end
|
111
118
|
|
112
119
|
define_async_method :async_Sx
|
@@ -147,14 +154,14 @@ class Puppeteer::Frame
|
|
147
154
|
|
148
155
|
# @return [String]
|
149
156
|
def content
|
150
|
-
@
|
157
|
+
@puppeteer_world.content
|
151
158
|
end
|
152
159
|
|
153
160
|
# @param html [String]
|
154
161
|
# @param timeout [Integer]
|
155
162
|
# @param wait_until [String|Array<String>]
|
156
163
|
def set_content(html, timeout: nil, wait_until: nil)
|
157
|
-
@
|
164
|
+
@puppeteer_world.set_content(html, timeout: timeout, wait_until: wait_until)
|
158
165
|
end
|
159
166
|
|
160
167
|
# @return [String]
|
@@ -205,35 +212,35 @@ class Puppeteer::Frame
|
|
205
212
|
# @param button [String] "left"|"right"|"middle"
|
206
213
|
# @param click_count [Number]
|
207
214
|
def click(selector, delay: nil, button: nil, click_count: nil)
|
208
|
-
@
|
215
|
+
@puppeteer_world.click(selector, delay: delay, button: button, click_count: click_count)
|
209
216
|
end
|
210
217
|
|
211
218
|
define_async_method :async_click
|
212
219
|
|
213
220
|
# @param {string} selector
|
214
221
|
def focus(selector)
|
215
|
-
@
|
222
|
+
@puppeteer_world.focus(selector)
|
216
223
|
end
|
217
224
|
|
218
225
|
define_async_method :async_focus
|
219
226
|
|
220
227
|
# @param {string} selector
|
221
228
|
def hover(selector)
|
222
|
-
@
|
229
|
+
@puppeteer_world.hover(selector)
|
223
230
|
end
|
224
231
|
|
225
232
|
# @param {string} selector
|
226
233
|
# @param {!Array<string>} values
|
227
234
|
# @return {!Promise<!Array<string>>}
|
228
235
|
def select(selector, *values)
|
229
|
-
@
|
236
|
+
@puppeteer_world.select(selector, *values)
|
230
237
|
end
|
231
238
|
|
232
239
|
define_async_method :async_select
|
233
240
|
|
234
241
|
# @param {string} selector
|
235
242
|
def tap(selector)
|
236
|
-
@
|
243
|
+
@puppeteer_world.tap(selector)
|
237
244
|
end
|
238
245
|
|
239
246
|
define_async_method :async_tap
|
@@ -252,14 +259,8 @@ class Puppeteer::Frame
|
|
252
259
|
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
253
260
|
# @param timeout [Integer]
|
254
261
|
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
|
255
|
-
|
256
|
-
|
257
|
-
return nil
|
258
|
-
end
|
259
|
-
main_execution_context = @main_world.execution_context
|
260
|
-
result = main_execution_context.adopt_element_handle(handle)
|
261
|
-
handle.dispose
|
262
|
-
result
|
262
|
+
query_handler_manager = Puppeteer::QueryHandlerManager.instance
|
263
|
+
query_handler_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout)
|
263
264
|
end
|
264
265
|
|
265
266
|
define_async_method :async_wait_for_selector
|
@@ -274,14 +275,14 @@ class Puppeteer::Frame
|
|
274
275
|
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
275
276
|
# @param timeout [Integer]
|
276
277
|
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
278
|
+
param_xpath =
|
279
|
+
if xpath.start_with?('//')
|
280
|
+
".#{xpath}"
|
281
|
+
else
|
282
|
+
xpath
|
283
|
+
end
|
284
|
+
|
285
|
+
wait_for_selector("xpath/#{param_xpath}", visible: visible, hidden: hidden, timeout: timeout)
|
285
286
|
end
|
286
287
|
|
287
288
|
define_async_method :async_wait_for_xpath
|
@@ -299,7 +300,7 @@ class Puppeteer::Frame
|
|
299
300
|
|
300
301
|
# @return [String]
|
301
302
|
def title
|
302
|
-
@
|
303
|
+
@puppeteer_world.title
|
303
304
|
end
|
304
305
|
|
305
306
|
# @param frame_payload [Hash]
|
@@ -337,7 +338,7 @@ class Puppeteer::Frame
|
|
337
338
|
def detach
|
338
339
|
@detached = true
|
339
340
|
@main_world.detach
|
340
|
-
@
|
341
|
+
@puppeteer_world.detach
|
341
342
|
if @parent_frame
|
342
343
|
@parent_frame._child_frames.delete(self)
|
343
344
|
end
|
@@ -144,13 +144,13 @@ class Puppeteer::FrameManager
|
|
144
144
|
watcher.same_document_navigation_promise
|
145
145
|
end,
|
146
146
|
)
|
147
|
+
|
148
|
+
watcher.navigation_response
|
147
149
|
rescue Puppeteer::TimeoutError => err
|
148
150
|
raise NavigationError.new(err)
|
149
151
|
ensure
|
150
152
|
watcher.dispose
|
151
153
|
end
|
152
|
-
|
153
|
-
watcher.navigation_response
|
154
154
|
end
|
155
155
|
|
156
156
|
# @param timeout [number|nil]
|
@@ -168,13 +168,13 @@ class Puppeteer::FrameManager
|
|
168
168
|
watcher.same_document_navigation_promise,
|
169
169
|
watcher.new_document_navigation_promise,
|
170
170
|
)
|
171
|
+
|
172
|
+
watcher.navigation_response
|
171
173
|
rescue Puppeteer::TimeoutError => err
|
172
174
|
raise NavigationError.new(err)
|
173
175
|
ensure
|
174
176
|
watcher.dispose
|
175
177
|
end
|
176
|
-
|
177
|
-
watcher.navigation_response
|
178
178
|
end
|
179
179
|
|
180
180
|
# @param event [Hash]
|
@@ -413,11 +413,11 @@ class Puppeteer::FrameManager
|
|
413
413
|
|
414
414
|
if context_payload.dig('auxData', 'isDefault')
|
415
415
|
world = frame.main_world
|
416
|
-
elsif context_payload['name'] == UTILITY_WORLD_NAME && !frame.
|
416
|
+
elsif context_payload['name'] == UTILITY_WORLD_NAME && !frame.puppeteer_world.has_context?
|
417
417
|
# In case of multiple sessions to the same target, there's a race between
|
418
418
|
# connections so we might end up creating multiple isolated worlds.
|
419
419
|
# We can use either.
|
420
|
-
world = frame.
|
420
|
+
world = frame.puppeteer_world
|
421
421
|
end
|
422
422
|
end
|
423
423
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
-
# https://github.com/puppeteer/puppeteer/blob/master/src/
|
4
|
-
class Puppeteer::
|
3
|
+
# https://github.com/puppeteer/puppeteer/blob/master/src/IsolaatedWorld.js
|
4
|
+
class Puppeteer::IsolaatedWorld
|
5
5
|
using Puppeteer::DefineAsyncMethod
|
6
6
|
|
7
7
|
class BindingFunction
|
@@ -152,7 +152,7 @@ class Puppeteer::DOMWorld
|
|
152
152
|
raise 'Bug of puppeteer-ruby...'
|
153
153
|
end
|
154
154
|
|
155
|
-
|
155
|
+
def document
|
156
156
|
@document ||= evaluate_document.as_element
|
157
157
|
end
|
158
158
|
|
@@ -416,16 +416,6 @@ class Puppeteer::DOMWorld
|
|
416
416
|
handle.dispose
|
417
417
|
end
|
418
418
|
|
419
|
-
# @param selector [String]
|
420
|
-
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
421
|
-
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
422
|
-
# @param timeout [Integer]
|
423
|
-
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil, root: nil)
|
424
|
-
# call wait_for_selector_in_page with custom query selector.
|
425
|
-
query_selector_manager = Puppeteer::QueryHandlerManager.instance
|
426
|
-
query_selector_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout, root: root)
|
427
|
-
end
|
428
|
-
|
429
419
|
private def binding_identifier(name, context)
|
430
420
|
"#{name}_#{context.send(:_context_id)}"
|
431
421
|
end
|
@@ -497,7 +487,7 @@ class Puppeteer::DOMWorld
|
|
497
487
|
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
498
488
|
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
499
489
|
# @param timeout [Integer]
|
500
|
-
private def wait_for_selector_in_page(query_one, selector, visible: nil, hidden: nil, timeout: nil,
|
490
|
+
private def wait_for_selector_in_page(query_one, root, selector, visible: nil, hidden: nil, timeout: nil, binding_function: nil)
|
501
491
|
option_wait_for_visible = visible || false
|
502
492
|
option_wait_for_hidden = hidden || false
|
503
493
|
option_timeout = timeout || @timeout_settings.timeout
|
@@ -531,55 +521,7 @@ class Puppeteer::DOMWorld
|
|
531
521
|
root: option_root,
|
532
522
|
binding_function: binding_function,
|
533
523
|
)
|
534
|
-
|
535
|
-
unless handle.as_element
|
536
|
-
handle.dispose
|
537
|
-
return nil
|
538
|
-
end
|
539
|
-
handle.as_element
|
540
|
-
end
|
541
|
-
|
542
|
-
# @param xpath [String]
|
543
|
-
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
544
|
-
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
545
|
-
# @param timeout [Integer]
|
546
|
-
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil, root: nil)
|
547
|
-
option_wait_for_visible = visible || false
|
548
|
-
option_wait_for_hidden = hidden || false
|
549
|
-
option_timeout = timeout || @timeout_settings.timeout
|
550
|
-
option_root = root
|
551
|
-
|
552
|
-
polling =
|
553
|
-
if option_wait_for_visible || option_wait_for_hidden
|
554
|
-
'raf'
|
555
|
-
else
|
556
|
-
'mutation'
|
557
|
-
end
|
558
|
-
title = "XPath #{xpath}#{option_wait_for_hidden ? 'to be hidden' : ''}"
|
559
|
-
|
560
|
-
xpath_predicate = make_predicate_string(
|
561
|
-
predicate_arg_def: '(root, selector, waitForVisible, waitForHidden)',
|
562
|
-
predicate_body: <<~JAVASCRIPT
|
563
|
-
const node = document.evaluate(selector, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
564
|
-
return checkWaitForOptions(node, waitForVisible, waitForHidden);
|
565
|
-
JAVASCRIPT
|
566
|
-
)
|
567
|
-
|
568
|
-
wait_task = Puppeteer::WaitTask.new(
|
569
|
-
dom_world: self,
|
570
|
-
predicate_body: xpath_predicate,
|
571
|
-
title: title,
|
572
|
-
polling: polling,
|
573
|
-
timeout: option_timeout,
|
574
|
-
args: [xpath, option_wait_for_visible, option_wait_for_hidden],
|
575
|
-
root: option_root,
|
576
|
-
)
|
577
|
-
handle = wait_task.await_promise
|
578
|
-
unless handle.as_element
|
579
|
-
handle.dispose
|
580
|
-
return nil
|
581
|
-
end
|
582
|
-
handle.as_element
|
524
|
+
wait_task.await_promise
|
583
525
|
end
|
584
526
|
|
585
527
|
# @param page_function [String]
|
@@ -644,4 +586,35 @@ class Puppeteer::DOMWorld
|
|
644
586
|
}
|
645
587
|
JAVASCRIPT
|
646
588
|
end
|
589
|
+
|
590
|
+
# @param backend_node_id [Integer]
|
591
|
+
# @return [Puppeteer::ElementHandle]
|
592
|
+
def adopt_backend_node(backend_node_id)
|
593
|
+
response = @client.send_message('DOM.resolveNode',
|
594
|
+
backendNodeId: backend_node_id,
|
595
|
+
executionContextId: execution_context.send(:_context_id),
|
596
|
+
)
|
597
|
+
Puppeteer::JSHandle.create(
|
598
|
+
context: execution_context,
|
599
|
+
remote_object: Puppeteer::RemoteObject.new(response["object"]),
|
600
|
+
)
|
601
|
+
end
|
602
|
+
private define_async_method :async_adopt_backend_node
|
603
|
+
|
604
|
+
# @param element_handle [Puppeteer::ElementHandle]
|
605
|
+
# @return [Puppeteer::ElementHandle]
|
606
|
+
def adopt_handle(element_handle)
|
607
|
+
if element_handle.execution_context == execution_context
|
608
|
+
raise ArgumentError.new('Cannot adopt handle that already belongs to this execution context')
|
609
|
+
end
|
610
|
+
|
611
|
+
node_info = element_handle.remote_object.node_info(@client)
|
612
|
+
adopt_backend_node(node_info["node"]["backendNodeId"])
|
613
|
+
end
|
614
|
+
|
615
|
+
def transfer_handle(element_handle)
|
616
|
+
result = adopt_handle(element_handle)
|
617
|
+
element_handle.dispose
|
618
|
+
result
|
619
|
+
end
|
647
620
|
end
|
data/lib/puppeteer/js_handle.rb
CHANGED
@@ -5,16 +5,12 @@ class Puppeteer::JSHandle
|
|
5
5
|
# @param context [Puppeteer::ExecutionContext]
|
6
6
|
# @param remote_object [Puppeteer::RemoteObject]
|
7
7
|
def self.create(context:, remote_object:)
|
8
|
-
|
9
|
-
if remote_object.sub_type == 'node' && frame
|
10
|
-
frame_manager = frame.frame_manager
|
8
|
+
if remote_object.sub_type == 'node' && context.world
|
11
9
|
Puppeteer::ElementHandle.new(
|
12
10
|
context: context,
|
13
11
|
client: context.client,
|
14
12
|
remote_object: remote_object,
|
15
|
-
frame: frame,
|
16
|
-
page: frame_manager.page,
|
17
|
-
frame_manager: frame_manager,
|
13
|
+
frame: context.world.frame,
|
18
14
|
)
|
19
15
|
else
|
20
16
|
Puppeteer::JSHandle.new(
|
@@ -81,7 +81,10 @@ class Puppeteer::LifecycleWatcher
|
|
81
81
|
@frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameSwapped, &method(:handle_frame_swapped)),
|
82
82
|
@frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameDetached, &method(:handle_frame_detached)),
|
83
83
|
]
|
84
|
-
@listener_ids['network_manager'] =
|
84
|
+
@listener_ids['network_manager'] = [
|
85
|
+
@frame_manager.network_manager.add_event_listener(NetworkManagerEmittedEvents::Request, &method(:handle_request)),
|
86
|
+
@frame_manager.network_manager.add_event_listener(NetworkManagerEmittedEvents::Response, &method(:handle_response)),
|
87
|
+
]
|
85
88
|
|
86
89
|
@same_document_navigation_promise = resolvable_future
|
87
90
|
@lifecycle_promise = resolvable_future
|
@@ -94,6 +97,21 @@ class Puppeteer::LifecycleWatcher
|
|
94
97
|
def handle_request(request)
|
95
98
|
return if request.frame != @frame || !request.navigation_request?
|
96
99
|
@navigation_request = request
|
100
|
+
# Resolve previous navigation response in case there are multiple
|
101
|
+
# navigation requests reported by the backend. This generally should not
|
102
|
+
# happen by it looks like it's possible.
|
103
|
+
@navigation_response_received&.fulfill(nil)
|
104
|
+
@navigation_response_received = resolvable_future
|
105
|
+
if request.response && !@navigation_response_received.resolved?
|
106
|
+
@navigation_response_received.fulfill(nil)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param [Puppeteer::HTTPResponse] response
|
111
|
+
def handle_response(response)
|
112
|
+
return if @navigation_request&.internal&.request_id != response.request.internal.request_id
|
113
|
+
|
114
|
+
@navigation_response_received.fulfill(nil) unless @navigation_response_received.resolved?
|
97
115
|
end
|
98
116
|
|
99
117
|
# @param frame [Puppeteer::Frame]
|
@@ -107,6 +125,8 @@ class Puppeteer::LifecycleWatcher
|
|
107
125
|
|
108
126
|
# @return [Puppeteer::HTTPResponse]
|
109
127
|
def navigation_response
|
128
|
+
# Continue with a possibly null response.
|
129
|
+
@navigation_response_received.value! rescue nil
|
110
130
|
if_present(@navigation_request) do |request|
|
111
131
|
request.response
|
112
132
|
end
|
@@ -175,8 +195,8 @@ class Puppeteer::LifecycleWatcher
|
|
175
195
|
if_present(@listener_ids['frame_manager']) do |ids|
|
176
196
|
@frame_manager.remove_event_listener(*ids)
|
177
197
|
end
|
178
|
-
if_present(@listener_ids['network_manager']) do |
|
179
|
-
@frame_manager.network_manager.remove_event_listener(
|
198
|
+
if_present(@listener_ids['network_manager']) do |ids|
|
199
|
+
@frame_manager.network_manager.remove_event_listener(*ids)
|
180
200
|
end
|
181
201
|
end
|
182
202
|
end
|
data/lib/puppeteer/page.rb
CHANGED
@@ -191,8 +191,7 @@ class Puppeteer::Page
|
|
191
191
|
return if @file_chooser_interceptors.empty?
|
192
192
|
|
193
193
|
frame = @frame_manager.frame(event['frameId'])
|
194
|
-
|
195
|
-
element = context.adopt_backend_node_id(event['backendNodeId'])
|
194
|
+
element = frame.main_world.adopt_backend_node(event['backendNodeId'])
|
196
195
|
interceptors = @file_chooser_interceptors.to_a
|
197
196
|
@file_chooser_interceptors.clear
|
198
197
|
file_chooser = Puppeteer::FileChooser.new(element, event)
|
@@ -1070,7 +1069,8 @@ class Puppeteer::Page
|
|
1070
1069
|
clip = if_present(screenshot_options.clip) do |rect|
|
1071
1070
|
x = rect[:x].round
|
1072
1071
|
y = rect[:y].round
|
1073
|
-
|
1072
|
+
scale = rect[:scale] || 1
|
1073
|
+
{ x: x, y: y, width: rect[:width] + rect[:x] - x, height: rect[:height] + rect[:y] - y, scale: scale }
|
1074
1074
|
end
|
1075
1075
|
|
1076
1076
|
if screenshot_options.full_page?
|
@@ -6,6 +6,7 @@ class Puppeteer::QueryHandlerManager
|
|
6
6
|
def query_handlers
|
7
7
|
@query_handlers ||= {
|
8
8
|
aria: Puppeteer::AriaQueryHandler.new,
|
9
|
+
xpath: xpath_handler,
|
9
10
|
}
|
10
11
|
end
|
11
12
|
|
@@ -16,6 +17,40 @@ class Puppeteer::QueryHandlerManager
|
|
16
17
|
)
|
17
18
|
end
|
18
19
|
|
20
|
+
private def xpath_handler
|
21
|
+
@xpath_handler ||= Puppeteer::CustomQueryHandler.new(
|
22
|
+
query_one: <<~JAVASCRIPT,
|
23
|
+
(element, selector) => {
|
24
|
+
const doc = element.ownerDocument || document;
|
25
|
+
const result = doc.evaluate(
|
26
|
+
selector,
|
27
|
+
element,
|
28
|
+
null,
|
29
|
+
XPathResult.FIRST_ORDERED_NODE_TYPE
|
30
|
+
);
|
31
|
+
return result.singleNodeValue;
|
32
|
+
}
|
33
|
+
JAVASCRIPT
|
34
|
+
query_all: <<~JAVASCRIPT,
|
35
|
+
(element, selector) => {
|
36
|
+
const doc = element.ownerDocument || document;
|
37
|
+
const iterator = doc.evaluate(
|
38
|
+
selector,
|
39
|
+
element,
|
40
|
+
null,
|
41
|
+
XPathResult.ORDERED_NODE_ITERATOR_TYPE
|
42
|
+
);
|
43
|
+
const array = [];
|
44
|
+
let item;
|
45
|
+
while ((item = iterator.iterateNext())) {
|
46
|
+
array.push(item);
|
47
|
+
}
|
48
|
+
return array;
|
49
|
+
}
|
50
|
+
JAVASCRIPT
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
19
54
|
class Result
|
20
55
|
def initialize(query_handler:, selector:)
|
21
56
|
@query_handler = query_handler
|
@@ -26,8 +61,8 @@ class Puppeteer::QueryHandlerManager
|
|
26
61
|
@query_handler.query_one(element_handle, @selector)
|
27
62
|
end
|
28
63
|
|
29
|
-
def wait_for(
|
30
|
-
@query_handler.wait_for(
|
64
|
+
def wait_for(element_or_frame, visible:, hidden:, timeout:)
|
65
|
+
@query_handler.wait_for(element_or_frame, @selector, visible: visible, hidden: hidden, timeout: timeout)
|
31
66
|
end
|
32
67
|
|
33
68
|
def query_all(element_handle)
|
@@ -105,8 +105,8 @@ class Puppeteer::RemoteObject
|
|
105
105
|
role: role,
|
106
106
|
}.compact)
|
107
107
|
|
108
|
-
result['nodes'].
|
109
|
-
node['role']['value']
|
108
|
+
result['nodes'].select do |node|
|
109
|
+
node['role'] && node['role']['value'] != 'StaticText'
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
data/lib/puppeteer/target.rb
CHANGED
@@ -93,7 +93,7 @@ class Puppeteer::Target
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def create_cdp_session
|
96
|
-
@session_factory.call
|
96
|
+
@session_factory.call(false)
|
97
97
|
end
|
98
98
|
|
99
99
|
def target_manager
|
@@ -102,7 +102,7 @@ class Puppeteer::Target
|
|
102
102
|
|
103
103
|
def page
|
104
104
|
if @is_page_target_callback.call(@target_info) && @page.nil?
|
105
|
-
client = @session || @session_factory.call
|
105
|
+
client = @session || @session_factory.call(true)
|
106
106
|
@page = Puppeteer::Page.create(client, self, @ignore_https_errors, @default_viewport)
|
107
107
|
end
|
108
108
|
@page
|
data/lib/puppeteer/version.rb
CHANGED
data/lib/puppeteer.rb
CHANGED
@@ -32,7 +32,6 @@ require 'puppeteer/css_coverage'
|
|
32
32
|
require 'puppeteer/custom_query_handler'
|
33
33
|
require 'puppeteer/devices'
|
34
34
|
require 'puppeteer/dialog'
|
35
|
-
require 'puppeteer/dom_world'
|
36
35
|
require 'puppeteer/emulation_manager'
|
37
36
|
require 'puppeteer/exception_details'
|
38
37
|
require 'puppeteer/executable_path_finder'
|
@@ -43,6 +42,7 @@ require 'puppeteer/frame'
|
|
43
42
|
require 'puppeteer/frame_manager'
|
44
43
|
require 'puppeteer/http_request'
|
45
44
|
require 'puppeteer/http_response'
|
45
|
+
require 'puppeteer/isolated_world'
|
46
46
|
require 'puppeteer/js_coverage'
|
47
47
|
require 'puppeteer/js_handle'
|
48
48
|
require 'puppeteer/keyboard'
|
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.44.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YusukeIwaki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -272,7 +272,6 @@ files:
|
|
272
272
|
- lib/puppeteer/device.rb
|
273
273
|
- lib/puppeteer/devices.rb
|
274
274
|
- lib/puppeteer/dialog.rb
|
275
|
-
- lib/puppeteer/dom_world.rb
|
276
275
|
- lib/puppeteer/element_handle.rb
|
277
276
|
- lib/puppeteer/element_handle/bounding_box.rb
|
278
277
|
- lib/puppeteer/element_handle/box_model.rb
|
@@ -294,6 +293,7 @@ files:
|
|
294
293
|
- lib/puppeteer/http_request.rb
|
295
294
|
- lib/puppeteer/http_response.rb
|
296
295
|
- lib/puppeteer/if_present.rb
|
296
|
+
- lib/puppeteer/isolated_world.rb
|
297
297
|
- lib/puppeteer/js_coverage.rb
|
298
298
|
- lib/puppeteer/js_handle.rb
|
299
299
|
- lib/puppeteer/keyboard.rb
|