puppeteer-ruby 0.0.3 → 0.0.8
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/.circleci/config.yml +30 -0
- data/.github/stale.yml +16 -0
- data/.rubocop.yml +4 -5
- data/README.md +4 -1
- data/docs/Puppeteer.html +2020 -0
- data/docs/Puppeteer/AsyncAwaitBehavior.html +105 -0
- data/docs/Puppeteer/Browser.html +2150 -0
- data/docs/Puppeteer/BrowserContext.html +809 -0
- data/docs/Puppeteer/BrowserFetcher.html +214 -0
- data/docs/Puppeteer/BrowserRunner.html +914 -0
- data/docs/Puppeteer/BrowserRunner/BrowserProcess.html +477 -0
- data/docs/Puppeteer/CDPSession.html +813 -0
- data/docs/Puppeteer/CDPSession/Error.html +124 -0
- data/docs/Puppeteer/ConcurrentRubyUtils.html +430 -0
- data/docs/Puppeteer/Connection.html +960 -0
- data/docs/Puppeteer/Connection/MessageCallback.html +434 -0
- data/docs/Puppeteer/Connection/ProtocolError.html +216 -0
- data/docs/Puppeteer/Connection/RequestDebugPrinter.html +217 -0
- data/docs/Puppeteer/Connection/ResponseDebugPrinter.html +244 -0
- data/docs/Puppeteer/ConsoleMessage.html +565 -0
- data/docs/Puppeteer/ConsoleMessage/Location.html +433 -0
- data/docs/Puppeteer/DOMWorld.html +2219 -0
- data/docs/Puppeteer/DOMWorld/DetachedError.html +124 -0
- data/docs/Puppeteer/DOMWorld/DocumentEvaluationError.html +124 -0
- data/docs/Puppeteer/DebugPrint.html +233 -0
- data/docs/Puppeteer/Device.html +470 -0
- data/docs/Puppeteer/Devices.html +139 -0
- data/docs/Puppeteer/ElementHandle.html +2224 -0
- data/docs/Puppeteer/ElementHandle/ElementNotFoundError.html +206 -0
- data/docs/Puppeteer/ElementHandle/ElementNotVisibleError.html +206 -0
- data/docs/Puppeteer/ElementHandle/Point.html +481 -0
- data/docs/Puppeteer/ElementHandle/ScrollIntoViewError.html +124 -0
- data/docs/Puppeteer/EmulationManager.html +454 -0
- data/docs/Puppeteer/EventCallbackable.html +433 -0
- data/docs/Puppeteer/EventCallbackable/EventListeners.html +435 -0
- data/docs/Puppeteer/ExecutionContext.html +998 -0
- data/docs/Puppeteer/ExecutionContext/EvaluationError.html +124 -0
- data/docs/Puppeteer/ExecutionContext/JavaScriptExpression.html +357 -0
- data/docs/Puppeteer/ExecutionContext/JavaScriptFunction.html +389 -0
- data/docs/Puppeteer/FileChooser.html +455 -0
- data/docs/Puppeteer/Frame.html +3677 -0
- data/docs/Puppeteer/FrameManager.html +2414 -0
- data/docs/Puppeteer/FrameManager/NavigationError.html +124 -0
- data/docs/Puppeteer/IfPresent.html +222 -0
- data/docs/Puppeteer/JSHandle.html +1352 -0
- data/docs/Puppeteer/Keyboard.html +1557 -0
- data/docs/Puppeteer/Keyboard/KeyDefinition.html +831 -0
- data/docs/Puppeteer/Keyboard/KeyDescription.html +603 -0
- data/docs/Puppeteer/Launcher.html +237 -0
- data/docs/Puppeteer/Launcher/Base.html +385 -0
- data/docs/Puppeteer/Launcher/Base/ExecutablePathNotFound.html +124 -0
- data/docs/Puppeteer/Launcher/BrowserOptions.html +441 -0
- data/docs/Puppeteer/Launcher/Chrome.html +669 -0
- data/docs/Puppeteer/Launcher/Chrome/DefaultArgs.html +382 -0
- data/docs/Puppeteer/Launcher/ChromeArgOptions.html +531 -0
- data/docs/Puppeteer/Launcher/LaunchOptions.html +893 -0
- data/docs/Puppeteer/LifecycleWatcher.html +834 -0
- data/docs/Puppeteer/LifecycleWatcher/ExpectedLifecycle.html +363 -0
- data/docs/Puppeteer/LifecycleWatcher/FrameDetachedError.html +206 -0
- data/docs/Puppeteer/LifecycleWatcher/TerminatedError.html +124 -0
- data/docs/Puppeteer/Mouse.html +1105 -0
- data/docs/Puppeteer/Mouse/Button.html +136 -0
- data/docs/Puppeteer/NetworkManager.html +901 -0
- data/docs/Puppeteer/NetworkManager/Credentials.html +385 -0
- data/docs/Puppeteer/Page.html +5970 -0
- data/docs/Puppeteer/Page/FileChooserTimeoutError.html +206 -0
- data/docs/Puppeteer/Page/ScreenshotOptions.html +845 -0
- data/docs/Puppeteer/Page/ScriptTag.html +555 -0
- data/docs/Puppeteer/Page/StyleTag.html +448 -0
- data/docs/Puppeteer/Page/TargetCrashedError.html +124 -0
- data/docs/Puppeteer/RemoteObject.html +1016 -0
- data/docs/Puppeteer/Target.html +1384 -0
- data/docs/Puppeteer/Target/InitializeFailure.html +124 -0
- data/docs/Puppeteer/Target/TargetInfo.html +729 -0
- data/docs/Puppeteer/TimeoutError.html +135 -0
- data/docs/Puppeteer/TimeoutSettings.html +496 -0
- data/docs/Puppeteer/TouchScreen.html +464 -0
- data/docs/Puppeteer/Viewport.html +757 -0
- data/docs/Puppeteer/WaitTask.html +637 -0
- data/docs/Puppeteer/WaitTask/TerminatedError.html +124 -0
- data/docs/Puppeteer/WaitTask/TimeoutError.html +206 -0
- data/docs/Puppeteer/WebSocket.html +673 -0
- data/docs/Puppeteer/WebSocket/DriverImpl.html +412 -0
- data/docs/Puppeteer/WebSocketTransport.html +600 -0
- data/docs/Puppeteer/WebSocktTransportError.html +124 -0
- data/docs/_index.html +809 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +496 -0
- data/docs/file.README.html +123 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +123 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +3979 -0
- data/docs/top-level-namespace.html +126 -0
- data/lib/puppeteer.rb +16 -8
- data/lib/puppeteer/async_await_behavior.rb +6 -0
- data/lib/puppeteer/browser.rb +21 -1
- data/lib/puppeteer/browser_runner.rb +1 -1
- data/lib/puppeteer/cdp_session.rb +33 -11
- data/lib/puppeteer/connection.rb +1 -1
- data/lib/puppeteer/dom_world.rb +142 -121
- data/lib/puppeteer/element_handle.rb +223 -181
- data/lib/puppeteer/execution_context.rb +41 -17
- data/lib/puppeteer/file_chooser.rb +29 -0
- data/lib/puppeteer/frame.rb +23 -15
- data/lib/puppeteer/frame_manager.rb +7 -9
- data/lib/puppeteer/js_handle.rb +3 -3
- data/lib/puppeteer/keyboard.rb +1 -1
- data/lib/puppeteer/keyboard/us_keyboard_layout.rb +4 -4
- data/lib/puppeteer/launcher.rb +0 -1
- data/lib/puppeteer/launcher/chrome.rb +48 -2
- data/lib/puppeteer/lifecycle_watcher.rb +9 -4
- data/lib/puppeteer/mouse.rb +10 -7
- data/lib/puppeteer/page.rb +134 -70
- data/lib/puppeteer/remote_object.rb +11 -1
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer/wait_task.rb +183 -1
- data/puppeteer-ruby.gemspec +4 -1
- metadata +143 -4
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>
|
|
7
|
+
Top Level Namespace
|
|
8
|
+
|
|
9
|
+
— Documentation by YARD 0.9.24
|
|
10
|
+
|
|
11
|
+
</title>
|
|
12
|
+
|
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
|
14
|
+
|
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
|
16
|
+
|
|
17
|
+
<script type="text/javascript">
|
|
18
|
+
pathId = "";
|
|
19
|
+
relpath = '';
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
|
24
|
+
|
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div class="nav_wrap">
|
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
|
32
|
+
<div id="resizer"></div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div id="main" tabindex="-1">
|
|
36
|
+
<div id="header">
|
|
37
|
+
<div id="menu">
|
|
38
|
+
|
|
39
|
+
<a href="_index.html">Index</a> »
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<span class="title">Top Level Namespace</span>
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div id="search">
|
|
47
|
+
|
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
|
49
|
+
href="class_list.html">
|
|
50
|
+
|
|
51
|
+
<svg width="24" height="24">
|
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
|
55
|
+
</svg>
|
|
56
|
+
</a>
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
<div class="clear"></div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
</h1>
|
|
67
|
+
<div class="box_info">
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
<dl>
|
|
75
|
+
<dt>Includes:</dt>
|
|
76
|
+
<dd><span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html" title="Puppeteer::ConcurrentRubyUtils (module)">Puppeteer::ConcurrentRubyUtils</a></span></dd>
|
|
77
|
+
</dl>
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<h2>Defined Under Namespace</h2>
|
|
87
|
+
<p class="children">
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
<strong class="classes">Classes:</strong> <span class='object_link'><a href="Puppeteer.html" title="Puppeteer (class)">Puppeteer</a></span>
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
</p>
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
<h2>Method Summary</h2>
|
|
111
|
+
|
|
112
|
+
<h3 class="inherited">Methods included from <span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html" title="Puppeteer::ConcurrentRubyUtils (module)">Puppeteer::ConcurrentRubyUtils</a></span></h3>
|
|
113
|
+
<p class="inherited"><span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html#await-instance_method" title="Puppeteer::ConcurrentRubyUtils#await (method)">#await</a></span>, <span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html#await_all-instance_method" title="Puppeteer::ConcurrentRubyUtils#await_all (method)">#await_all</a></span>, <span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html#await_any-instance_method" title="Puppeteer::ConcurrentRubyUtils#await_any (method)">#await_any</a></span>, <span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html#future-instance_method" title="Puppeteer::ConcurrentRubyUtils#future (method)">#future</a></span>, <span class='object_link'><a href="Puppeteer/ConcurrentRubyUtils.html#resolvable_future-instance_method" title="Puppeteer::ConcurrentRubyUtils#resolvable_future (method)">#resolvable_future</a></span></p>
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div id="footer">
|
|
119
|
+
Generated on Thu Jun 4 23:54:40 2020 by
|
|
120
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
121
|
+
0.9.24 (ruby-2.6.3).
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
</div>
|
|
125
|
+
</body>
|
|
126
|
+
</html>
|
data/lib/puppeteer.rb
CHANGED
|
@@ -25,6 +25,7 @@ require 'puppeteer/devices'
|
|
|
25
25
|
require 'puppeteer/dom_world'
|
|
26
26
|
require 'puppeteer/emulation_manager'
|
|
27
27
|
require 'puppeteer/execution_context'
|
|
28
|
+
require 'puppeteer/file_chooser'
|
|
28
29
|
require 'puppeteer/frame'
|
|
29
30
|
require 'puppeteer/frame_manager'
|
|
30
31
|
require 'puppeteer/js_handle'
|
|
@@ -55,9 +56,10 @@ class Puppeteer
|
|
|
55
56
|
|
|
56
57
|
def instance
|
|
57
58
|
@instance ||= Puppeteer.new(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
project_root: __dir__,
|
|
60
|
+
preferred_revision: '706915',
|
|
61
|
+
is_puppeteer_core: true,
|
|
62
|
+
)
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
|
|
@@ -141,7 +143,12 @@ class Puppeteer
|
|
|
141
143
|
default_viewport: default_viewport,
|
|
142
144
|
slow_mo: slow_mo,
|
|
143
145
|
}.compact
|
|
144
|
-
launcher.connect(options)
|
|
146
|
+
browser = launcher.connect(options)
|
|
147
|
+
if block_given?
|
|
148
|
+
yield(browser)
|
|
149
|
+
else
|
|
150
|
+
browser
|
|
151
|
+
end
|
|
145
152
|
end
|
|
146
153
|
|
|
147
154
|
# @return {string}
|
|
@@ -151,10 +158,11 @@ class Puppeteer
|
|
|
151
158
|
|
|
152
159
|
private def launcher
|
|
153
160
|
@launcher ||= Puppeteer::Launcher.new(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
project_root: @project_root,
|
|
162
|
+
preferred_revision: @preferred_revision,
|
|
163
|
+
is_puppeteer_core: @is_puppeteer_core,
|
|
164
|
+
product: @product_name,
|
|
165
|
+
)
|
|
158
166
|
end
|
|
159
167
|
|
|
160
168
|
# @return {string}
|
|
@@ -11,6 +11,9 @@ module Puppeteer::AsyncAwaitBehavior
|
|
|
11
11
|
define_method(method_name) do |*args|
|
|
12
12
|
Concurrent::Promises.future do
|
|
13
13
|
original_method.bind(self).call(*args)
|
|
14
|
+
rescue => err
|
|
15
|
+
Logger.new(STDERR).warn(err)
|
|
16
|
+
raise err
|
|
14
17
|
end
|
|
15
18
|
end
|
|
16
19
|
rescue NameError
|
|
@@ -24,6 +27,9 @@ module Puppeteer::AsyncAwaitBehavior
|
|
|
24
27
|
define_singleton_method(method_name) do |*args|
|
|
25
28
|
Concurrent::Promises.future do
|
|
26
29
|
original_method.call(*args)
|
|
30
|
+
rescue => err
|
|
31
|
+
Logger.new(STDERR).warn(err)
|
|
32
|
+
raise err
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
end
|
data/lib/puppeteer/browser.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'timeout'
|
|
|
4
4
|
class Puppeteer::Browser
|
|
5
5
|
include Puppeteer::DebugPrint
|
|
6
6
|
include Puppeteer::EventCallbackable
|
|
7
|
+
include Puppeteer::IfPresent
|
|
7
8
|
using Puppeteer::AsyncAwaitBehavior
|
|
8
9
|
|
|
9
10
|
# @param {!Puppeteer.Connection} connection
|
|
@@ -106,6 +107,10 @@ class Puppeteer::Browser
|
|
|
106
107
|
emit_event 'Events.Browser.TargetCreated', target
|
|
107
108
|
context.emit_event 'Events.BrowserContext.TargetCreated', target
|
|
108
109
|
end
|
|
110
|
+
|
|
111
|
+
if_present(pending_target_info_changed_event.delete(target_info.target_id)) do |pending_event|
|
|
112
|
+
handle_target_info_changed(pending_event)
|
|
113
|
+
end
|
|
109
114
|
end
|
|
110
115
|
|
|
111
116
|
|
|
@@ -127,7 +132,18 @@ class Puppeteer::Browser
|
|
|
127
132
|
target_info = Puppeteer::Target::TargetInfo.new(event['targetInfo'])
|
|
128
133
|
target = @targets[target_info.target_id]
|
|
129
134
|
if !target
|
|
130
|
-
|
|
135
|
+
# targetCreated is sometimes notified after targetInfoChanged.
|
|
136
|
+
# We don't raise error. Instead, keep the event as a pending change,
|
|
137
|
+
# and handle it on handle_target_created.
|
|
138
|
+
#
|
|
139
|
+
# D, [2020-04-22T00:22:26.630328 #79646] DEBUG -- : RECV << {"method"=>"Target.targetInfoChanged", "params"=>{"targetInfo"=>{"targetId"=>"8068CED48357B9557EEC85AA62165A8E", "type"=>"iframe", "title"=>"", "url"=>"", "attached"=>true, "browserContextId"=>"7895BFB24BF22CE40584808713D96E8D"}}}
|
|
140
|
+
# E, [2020-04-22T00:22:26.630448 #79646] ERROR -- : target should exist before targetInfoChanged (StandardError)
|
|
141
|
+
# D, [2020-04-22T00:22:26.630648 #79646] DEBUG -- : RECV << {"method"=>"Target.targetCreated", "params"=>{"targetInfo"=>{"targetId"=>"8068CED48357B9557EEC85AA62165A8E", "type"=>"iframe", "title"=>"", "url"=>"", "attached"=>false, "browserContextId"=>"7895BFB24BF22CE40584808713D96E8D"}}}
|
|
142
|
+
pending_target_info_changed_event[target_info.target_id] = event
|
|
143
|
+
return
|
|
144
|
+
# original implementation is:
|
|
145
|
+
#
|
|
146
|
+
# raise StandardError.new('target should exist before targetInfoChanged')
|
|
131
147
|
end
|
|
132
148
|
previous_url = target.url
|
|
133
149
|
was_initialized = target.initialized?
|
|
@@ -138,6 +154,10 @@ class Puppeteer::Browser
|
|
|
138
154
|
end
|
|
139
155
|
end
|
|
140
156
|
|
|
157
|
+
private def pending_target_info_changed_event
|
|
158
|
+
@pending_target_info_changed_event ||= {}
|
|
159
|
+
end
|
|
160
|
+
|
|
141
161
|
# @return [String]
|
|
142
162
|
def websocket_endpoint
|
|
143
163
|
@connection.url
|
|
@@ -147,7 +147,7 @@ class Puppeteer::BrowserRunner
|
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
private def wait_for_ws_endpoint(browser_process, timeout, preferred_revision)
|
|
150
|
-
Timeout.timeout(timeout / 1000) do
|
|
150
|
+
Timeout.timeout(timeout / 1000.0) do
|
|
151
151
|
loop do
|
|
152
152
|
line = browser_process.stderr.readline
|
|
153
153
|
/^DevTools listening on (ws:\/\/.*)$/.match(line) do |m|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
class Puppeteer::CDPSession
|
|
2
|
+
include Puppeteer::DebugPrint
|
|
2
3
|
include Puppeteer::EventCallbackable
|
|
3
4
|
using Puppeteer::AsyncAwaitBehavior
|
|
4
5
|
|
|
@@ -12,6 +13,7 @@ class Puppeteer::CDPSession
|
|
|
12
13
|
@connection = connection
|
|
13
14
|
@target_type = target_type
|
|
14
15
|
@session_id = session_id
|
|
16
|
+
@pending_messages = {}
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
attr_reader :connection
|
|
@@ -32,7 +34,13 @@ class Puppeteer::CDPSession
|
|
|
32
34
|
end
|
|
33
35
|
id = @connection.raw_send(message: { sessionId: @session_id, method: method, params: params })
|
|
34
36
|
promise = resolvable_future
|
|
35
|
-
|
|
37
|
+
callback = Puppeteer::Connection::MessageCallback.new(method: method, promise: promise)
|
|
38
|
+
if pending_message = @pending_messages.delete(id)
|
|
39
|
+
debug_puts "Pending message (id: #{id}) is handled"
|
|
40
|
+
callback_with_message(callback, pending_message)
|
|
41
|
+
else
|
|
42
|
+
@callbacks[id] = callback
|
|
43
|
+
end
|
|
36
44
|
promise
|
|
37
45
|
end
|
|
38
46
|
|
|
@@ -40,23 +48,37 @@ class Puppeteer::CDPSession
|
|
|
40
48
|
def handle_message(message)
|
|
41
49
|
if message['id']
|
|
42
50
|
if callback = @callbacks.delete(message['id'])
|
|
43
|
-
|
|
44
|
-
callback.reject(
|
|
45
|
-
Puppeteer::Connection::ProtocolError.new(
|
|
46
|
-
method: callback.method,
|
|
47
|
-
error_message: response['error']['message'],
|
|
48
|
-
error_data: response['error']['data']))
|
|
49
|
-
else
|
|
50
|
-
callback.resolve(message['result'])
|
|
51
|
-
end
|
|
51
|
+
callback_with_message(callback, message)
|
|
52
52
|
else
|
|
53
|
-
|
|
53
|
+
debug_puts "unknown id: #{id}. Store it into pending message"
|
|
54
|
+
|
|
55
|
+
# RECV is often notified before SEND.
|
|
56
|
+
# Wait about 10 frames before throwing an error.
|
|
57
|
+
message_id = message['id']
|
|
58
|
+
@pending_messages[message_id] = message
|
|
59
|
+
Concurrent::Promises.schedule(0.16, message_id) do |id|
|
|
60
|
+
if @pending_messages.delete(id)
|
|
61
|
+
raise Error.new("unknown id: #{id}")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
54
64
|
end
|
|
55
65
|
else
|
|
56
66
|
emit_event message['method'], message['params']
|
|
57
67
|
end
|
|
58
68
|
end
|
|
59
69
|
|
|
70
|
+
private def callback_with_message(callback, message)
|
|
71
|
+
if message['error']
|
|
72
|
+
callback.reject(
|
|
73
|
+
Puppeteer::Connection::ProtocolError.new(
|
|
74
|
+
method: callback.method,
|
|
75
|
+
error_message: message['error']['message'],
|
|
76
|
+
error_data: message['error']['data']))
|
|
77
|
+
else
|
|
78
|
+
callback.resolve(message['result'])
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
60
82
|
def detach
|
|
61
83
|
if !@connection
|
|
62
84
|
raise Error.new("Session already detarched. Most likely the #{@target_type} has been closed.")
|
data/lib/puppeteer/connection.rb
CHANGED
data/lib/puppeteer/dom_world.rb
CHANGED
|
@@ -12,12 +12,18 @@ class Puppeteer::DOMWorld
|
|
|
12
12
|
@frame = frame
|
|
13
13
|
@timeout_settings = timeout_settings
|
|
14
14
|
@context_promise = resolvable_future
|
|
15
|
+
@pending_destroy = []
|
|
15
16
|
@wait_tasks = Set.new
|
|
16
17
|
@detached = false
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
attr_reader :frame
|
|
20
21
|
|
|
22
|
+
# only used in Puppeteer::WaitTask#initialize
|
|
23
|
+
def _wait_tasks
|
|
24
|
+
@wait_tasks
|
|
25
|
+
end
|
|
26
|
+
|
|
21
27
|
# @param {?Puppeteer.ExecutionContext} context
|
|
22
28
|
def context=(context)
|
|
23
29
|
# D, [2020-04-12T22:45:03.938754 #46154] DEBUG -- : RECV << {"method"=>"Runtime.executionContextCreated", "params"=>{"context"=>{"id"=>3, "origin"=>"https://github.com", "name"=>"", "auxData"=>{"isDefault"=>true, "type"=>"default", "frameId"=>"3AD7F1E82BCBA88BFE31D03BC49FF6CB"}}}, "sessionId"=>"636CEF0C4FEAFC4FE815E9E7B5F7BA68"}
|
|
@@ -29,20 +35,23 @@ class Puppeteer::DOMWorld
|
|
|
29
35
|
|
|
30
36
|
if context
|
|
31
37
|
if @context_promise.fulfilled?
|
|
38
|
+
@pending_destroy << context._context_id
|
|
32
39
|
@document = nil
|
|
33
40
|
@context_promise = resolvable_future
|
|
34
|
-
@pending_destroy_exists = true
|
|
35
41
|
end
|
|
36
42
|
@context_promise.fulfill(context)
|
|
37
|
-
|
|
38
|
-
# waitTask.rerun();
|
|
43
|
+
@wait_tasks.each(&:async_rerun)
|
|
39
44
|
else
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
raise ArgumentError.new("context should now be nil. Use #delete_context for clearing document.")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def delete_context(execution_context_id)
|
|
50
|
+
if @pending_destroy.include?(execution_context_id)
|
|
51
|
+
@pending_destroy.delete(execution_context_id)
|
|
52
|
+
else
|
|
53
|
+
@document = nil
|
|
54
|
+
@context_promise = resolvable_future
|
|
46
55
|
end
|
|
47
56
|
end
|
|
48
57
|
|
|
@@ -50,7 +59,7 @@ class Puppeteer::DOMWorld
|
|
|
50
59
|
@context_promise.resolved?
|
|
51
60
|
end
|
|
52
61
|
|
|
53
|
-
|
|
62
|
+
def detach
|
|
54
63
|
@detached = true
|
|
55
64
|
@wait_tasks.each do |wait_task|
|
|
56
65
|
wait_task.terminate(Puppeteer::WaitTask::TerminatedError.new('waitForFunction failed: frame got detached.'))
|
|
@@ -88,8 +97,21 @@ class Puppeteer::DOMWorld
|
|
|
88
97
|
document.S(selector)
|
|
89
98
|
end
|
|
90
99
|
|
|
100
|
+
class DocumentEvaluationError < StandardError; end
|
|
101
|
+
|
|
102
|
+
private def evaluate_document
|
|
103
|
+
# sometimes execution_context.evaluate_handle('document') returns null object.
|
|
104
|
+
# D, [2020-04-24T02:17:51.023631 #220] DEBUG -- : RECV << {"id"=>20, "result"=>{"result"=>{"type"=>"object", "subtype"=>"null", "value"=>nil}}, "sessionId"=>"78E9CF1E14D81294E320E7C20E5CDE06"}
|
|
105
|
+
# retry if so.
|
|
106
|
+
5.times do
|
|
107
|
+
handle = execution_context.evaluate_handle('document')
|
|
108
|
+
return handle if handle.is_a?(Puppeteer::ElementHandle)
|
|
109
|
+
end
|
|
110
|
+
raise DocumentEvaluationError.new("'document' object cannot be evaluated as an Element")
|
|
111
|
+
end
|
|
112
|
+
|
|
91
113
|
private def document
|
|
92
|
-
@document ||=
|
|
114
|
+
@document ||= evaluate_document.as_element
|
|
93
115
|
end
|
|
94
116
|
|
|
95
117
|
# `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
|
|
@@ -303,16 +325,15 @@ class Puppeteer::DOMWorld
|
|
|
303
325
|
# }
|
|
304
326
|
# }
|
|
305
327
|
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
#
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
# }
|
|
328
|
+
# @param selector [String]
|
|
329
|
+
# @param delay [Number]
|
|
330
|
+
# @param button [String] "left"|"right"|"middle"
|
|
331
|
+
# @param click_count [Number]
|
|
332
|
+
def click(selector, delay: nil, button: nil, click_count: nil)
|
|
333
|
+
handle = S(selector)
|
|
334
|
+
handle.click(delay: delay, button: button, click_count: click_count)
|
|
335
|
+
handle.dispose
|
|
336
|
+
end
|
|
316
337
|
|
|
317
338
|
# /**
|
|
318
339
|
# * @param {string} selector
|
|
@@ -334,58 +355,47 @@ class Puppeteer::DOMWorld
|
|
|
334
355
|
# await handle.dispose();
|
|
335
356
|
# }
|
|
336
357
|
|
|
337
|
-
#
|
|
338
|
-
#
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
# const handle = await this.$(selector);
|
|
344
|
-
# assert(handle, 'No node found for selector: ' + selector);
|
|
345
|
-
# const result = await handle.select(...values);
|
|
346
|
-
# await handle.dispose();
|
|
347
|
-
# return result;
|
|
348
|
-
# }
|
|
358
|
+
# @param selector [String]
|
|
359
|
+
# @return [Array<String>]
|
|
360
|
+
def select(selector, *values)
|
|
361
|
+
handle = S(selector)
|
|
362
|
+
result = handle.select(*values)
|
|
363
|
+
handle.dispose
|
|
349
364
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
# */
|
|
353
|
-
# async tap(selector) {
|
|
354
|
-
# const handle = await this.$(selector);
|
|
355
|
-
# assert(handle, 'No node found for selector: ' + selector);
|
|
356
|
-
# await handle.tap();
|
|
357
|
-
# await handle.dispose();
|
|
358
|
-
# }
|
|
365
|
+
result
|
|
366
|
+
end
|
|
359
367
|
|
|
360
|
-
#
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
# const handle = await this.$(selector);
|
|
367
|
-
# assert(handle, 'No node found for selector: ' + selector);
|
|
368
|
-
# await handle.type(text, options);
|
|
369
|
-
# await handle.dispose();
|
|
370
|
-
# }
|
|
368
|
+
# @param selector [String]
|
|
369
|
+
def tap(selector)
|
|
370
|
+
handle = S(selector)
|
|
371
|
+
handle.tap
|
|
372
|
+
handle.dispose
|
|
373
|
+
end
|
|
371
374
|
|
|
372
|
-
#
|
|
373
|
-
#
|
|
374
|
-
#
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
375
|
+
# @param selector [String]
|
|
376
|
+
# @param text [String]
|
|
377
|
+
# @param delay [Number]
|
|
378
|
+
def type_text(selector, text, delay: nil)
|
|
379
|
+
handle = S(selector)
|
|
380
|
+
handle.type_text(text, delay: delay)
|
|
381
|
+
handle.dispose
|
|
382
|
+
end
|
|
380
383
|
|
|
381
|
-
#
|
|
382
|
-
#
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
384
|
+
# @param selector [String]
|
|
385
|
+
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
|
386
|
+
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
|
387
|
+
# @param timeout [Integer]
|
|
388
|
+
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
|
|
389
|
+
wait_for_selector_or_xpath(selector, false, visible: visible, hidden: hidden, timeout: timeout)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# @param xpath [String]
|
|
393
|
+
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
|
394
|
+
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
|
395
|
+
# @param timeout [Integer]
|
|
396
|
+
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
|
|
397
|
+
wait_for_selector_or_xpath(xpath, true, visible: visible, hidden: hidden, timeout: timeout)
|
|
398
|
+
end
|
|
389
399
|
|
|
390
400
|
# /**
|
|
391
401
|
# * @param {Function|string} pageFunction
|
|
@@ -407,57 +417,68 @@ class Puppeteer::DOMWorld
|
|
|
407
417
|
# return this.evaluate(() => document.title);
|
|
408
418
|
# }
|
|
409
419
|
|
|
410
|
-
#
|
|
411
|
-
#
|
|
412
|
-
#
|
|
413
|
-
#
|
|
414
|
-
#
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
420
|
+
# @param selector_or_xpath [String]
|
|
421
|
+
# @param is_xpath [Boolean]
|
|
422
|
+
# @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
|
|
423
|
+
# @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
|
|
424
|
+
# @param timeout [Integer]
|
|
425
|
+
private def wait_for_selector_or_xpath(selector_or_xpath, is_xpath, visible: nil, hidden: nil, timeout: nil)
|
|
426
|
+
option_wait_for_visible = visible || false
|
|
427
|
+
option_wait_for_hidden = hidden || false
|
|
428
|
+
option_timeout = timeout || @timeout_settings.timeout
|
|
429
|
+
|
|
430
|
+
polling =
|
|
431
|
+
if option_wait_for_visible || option_wait_for_hidden
|
|
432
|
+
'raf'
|
|
433
|
+
else
|
|
434
|
+
'mutation'
|
|
435
|
+
end
|
|
436
|
+
title = "#{is_xpath ? :XPath : :selector} #{selector_or_xpath}#{option_wait_for_hidden ? 'to be hidden' : ''}"
|
|
437
|
+
|
|
438
|
+
wait_task = Puppeteer::WaitTask.new(
|
|
439
|
+
dom_world: self,
|
|
440
|
+
predicate_body: "return (#{PREDICATE})(...args)",
|
|
441
|
+
title: title,
|
|
442
|
+
polling: polling,
|
|
443
|
+
timeout: option_timeout,
|
|
444
|
+
args: [selector_or_xpath, is_xpath, option_wait_for_visible, option_wait_for_hidden],
|
|
445
|
+
)
|
|
446
|
+
handle = wait_task.await_promise
|
|
447
|
+
unless handle.as_element
|
|
448
|
+
handle.dispose
|
|
449
|
+
return nil
|
|
450
|
+
end
|
|
451
|
+
handle.as_element
|
|
452
|
+
end
|
|
431
453
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
# }
|
|
454
|
+
PREDICATE = <<~JAVASCRIPT
|
|
455
|
+
/**
|
|
456
|
+
* @param {string} selectorOrXPath
|
|
457
|
+
* @param {boolean} isXPath
|
|
458
|
+
* @param {boolean} waitForVisible
|
|
459
|
+
* @param {boolean} waitForHidden
|
|
460
|
+
* @return {?Node|boolean}
|
|
461
|
+
*/
|
|
462
|
+
function _(selectorOrXPath, isXPath, waitForVisible, waitForHidden) {
|
|
463
|
+
const node = isXPath
|
|
464
|
+
? document.evaluate(selectorOrXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
|
|
465
|
+
: document.querySelector(selectorOrXPath);
|
|
466
|
+
if (!node)
|
|
467
|
+
return waitForHidden;
|
|
468
|
+
if (!waitForVisible && !waitForHidden)
|
|
469
|
+
return node;
|
|
470
|
+
const element = /** @type {Element} */ (node.nodeType === Node.TEXT_NODE ? node.parentElement : node);
|
|
471
|
+
const style = window.getComputedStyle(element);
|
|
472
|
+
const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
|
|
473
|
+
const success = (waitForVisible === isVisible || waitForHidden === !isVisible);
|
|
474
|
+
return success ? node : null;
|
|
475
|
+
/**
|
|
476
|
+
* @return {boolean}
|
|
477
|
+
*/
|
|
478
|
+
function hasVisibleBoundingBox() {
|
|
479
|
+
const rect = element.getBoundingClientRect();
|
|
480
|
+
return !!(rect.top || rect.bottom || rect.width || rect.height);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
JAVASCRIPT
|
|
463
484
|
end
|