puppeteer-ruby 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|