ferrum 0.14 → 0.15
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/LICENSE +1 -1
- data/README.md +277 -154
- data/lib/ferrum/browser/command.rb +4 -0
- data/lib/ferrum/browser/options/chrome.rb +8 -3
- data/lib/ferrum/browser/options.rb +38 -25
- data/lib/ferrum/browser/process.rb +43 -16
- data/lib/ferrum/browser.rb +26 -50
- data/lib/ferrum/client/subscriber.rb +76 -0
- data/lib/ferrum/{browser → client}/web_socket.rb +35 -21
- data/lib/ferrum/client.rb +169 -0
- data/lib/ferrum/context.rb +19 -15
- data/lib/ferrum/contexts.rb +46 -12
- data/lib/ferrum/cookies.rb +1 -1
- data/lib/ferrum/downloads.rb +60 -0
- data/lib/ferrum/errors.rb +2 -1
- data/lib/ferrum/headers.rb +1 -1
- data/lib/ferrum/network/exchange.rb +10 -1
- data/lib/ferrum/network/intercepted_request.rb +5 -5
- data/lib/ferrum/network/request.rb +9 -0
- data/lib/ferrum/network.rb +11 -9
- data/lib/ferrum/page/frames.rb +5 -5
- data/lib/ferrum/page/screenshot.rb +36 -24
- data/lib/ferrum/page.rb +177 -119
- data/lib/ferrum/proxy.rb +1 -1
- data/lib/ferrum/target.rb +25 -5
- data/lib/ferrum/utils/elapsed_time.rb +0 -2
- data/lib/ferrum/utils/event.rb +19 -0
- data/lib/ferrum/utils/platform.rb +4 -0
- data/lib/ferrum/utils/thread.rb +18 -0
- data/lib/ferrum/version.rb +1 -1
- data/lib/ferrum.rb +3 -0
- metadata +14 -17
- data/lib/ferrum/browser/client.rb +0 -103
- data/lib/ferrum/browser/subscriber.rb +0 -36
|
@@ -5,6 +5,9 @@ require "ferrum/rgba"
|
|
|
5
5
|
module Ferrum
|
|
6
6
|
class Page
|
|
7
7
|
module Screenshot
|
|
8
|
+
FULL_WARNING = "Ignoring :selector or :area in #screenshot since full: true was given at %s"
|
|
9
|
+
AREA_WARNING = "Ignoring :area in #screenshot since selector: was given at %s"
|
|
10
|
+
|
|
8
11
|
DEFAULT_PDF_OPTIONS = {
|
|
9
12
|
landscape: false,
|
|
10
13
|
paper_width: 8.5,
|
|
@@ -50,6 +53,9 @@ module Ferrum
|
|
|
50
53
|
# @option opts [String] :selector
|
|
51
54
|
# CSS selector for the given element.
|
|
52
55
|
#
|
|
56
|
+
# @option opts [Hash] :area
|
|
57
|
+
# x, y, width, height to screenshot an area.
|
|
58
|
+
#
|
|
53
59
|
# @option opts [Float] :scale
|
|
54
60
|
# Zoom in/out.
|
|
55
61
|
#
|
|
@@ -57,19 +63,19 @@ module Ferrum
|
|
|
57
63
|
# Sets the background color.
|
|
58
64
|
#
|
|
59
65
|
# @example
|
|
60
|
-
#
|
|
66
|
+
# page.go_to("https://google.com/")
|
|
61
67
|
#
|
|
62
68
|
# @example Save on the disk in PNG:
|
|
63
|
-
#
|
|
69
|
+
# page.screenshot(path: "google.png") # => 134660
|
|
64
70
|
#
|
|
65
71
|
# @example Save on the disk in JPG:
|
|
66
|
-
#
|
|
72
|
+
# page.screenshot(path: "google.jpg") # => 30902
|
|
67
73
|
#
|
|
68
74
|
# @example Save to Base64 the whole page not only viewport and reduce quality:
|
|
69
|
-
#
|
|
75
|
+
# page.screenshot(full: true, quality: 60) # "iVBORw0KGgoAAAANS...
|
|
70
76
|
#
|
|
71
77
|
# @example Save with specific background color:
|
|
72
|
-
#
|
|
78
|
+
# page.screenshot(background_color: Ferrum::RGBA.new(0, 0, 0, 0.0))
|
|
73
79
|
#
|
|
74
80
|
def screenshot(**opts)
|
|
75
81
|
path, encoding = common_options(**opts)
|
|
@@ -113,9 +119,9 @@ module Ferrum
|
|
|
113
119
|
# can pass.
|
|
114
120
|
#
|
|
115
121
|
# @example
|
|
116
|
-
#
|
|
122
|
+
# page.go_to("https://google.com/")
|
|
117
123
|
# # Save to disk as a PDF
|
|
118
|
-
#
|
|
124
|
+
# page.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # => true
|
|
119
125
|
#
|
|
120
126
|
def pdf(**opts)
|
|
121
127
|
path, encoding = common_options(**opts)
|
|
@@ -131,8 +137,8 @@ module Ferrum
|
|
|
131
137
|
# The path to save a file on the disk.
|
|
132
138
|
#
|
|
133
139
|
# @example
|
|
134
|
-
#
|
|
135
|
-
#
|
|
140
|
+
# page.go_to("https://google.com/")
|
|
141
|
+
# page.mhtml(path: "google.mhtml") # => 87742
|
|
136
142
|
#
|
|
137
143
|
def mhtml(path: nil)
|
|
138
144
|
data = command("Page.captureSnapshot", format: :mhtml).fetch("data")
|
|
@@ -198,7 +204,7 @@ module Ferrum
|
|
|
198
204
|
screenshot_options.merge!(quality: quality) if quality
|
|
199
205
|
screenshot_options.merge!(format: format)
|
|
200
206
|
|
|
201
|
-
clip = area_options(options[:full], options[:selector], scale)
|
|
207
|
+
clip = area_options(options[:full], options[:selector], scale, options[:area])
|
|
202
208
|
screenshot_options.merge!(clip: clip) if clip
|
|
203
209
|
|
|
204
210
|
screenshot_options
|
|
@@ -214,29 +220,35 @@ module Ferrum
|
|
|
214
220
|
[format, quality]
|
|
215
221
|
end
|
|
216
222
|
|
|
217
|
-
def area_options(full, selector, scale)
|
|
218
|
-
|
|
219
|
-
warn(
|
|
223
|
+
def area_options(full, selector, scale, area = nil)
|
|
224
|
+
warn(FULL_WARNING % caller(1..1).first) if full && (selector || area)
|
|
225
|
+
warn(AREA_WARNING % caller(1..1).first) if selector && area
|
|
220
226
|
|
|
221
227
|
clip = if full
|
|
222
|
-
|
|
223
|
-
{ x: 0, y: 0, width: width, height: height, scale: scale } if width.positive? && height.positive?
|
|
228
|
+
full_window_area || viewport_area
|
|
224
229
|
elsif selector
|
|
225
|
-
bounding_rect(selector)
|
|
230
|
+
bounding_rect(selector)
|
|
231
|
+
elsif area
|
|
232
|
+
area
|
|
233
|
+
else
|
|
234
|
+
viewport_area
|
|
226
235
|
end
|
|
227
236
|
|
|
228
|
-
|
|
229
|
-
unless clip
|
|
230
|
-
width, height = viewport_size
|
|
231
|
-
clip = { x: 0, y: 0, width: width, height: height }
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
clip.merge!(scale: scale)
|
|
235
|
-
end
|
|
237
|
+
clip.merge!(scale: scale)
|
|
236
238
|
|
|
237
239
|
clip
|
|
238
240
|
end
|
|
239
241
|
|
|
242
|
+
def full_window_area
|
|
243
|
+
width, height = document_size
|
|
244
|
+
{ x: 0, y: 0, width: width, height: height } if width.positive? && height.positive?
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def viewport_area
|
|
248
|
+
width, height = viewport_size
|
|
249
|
+
{ x: 0, y: 0, width: width, height: height }
|
|
250
|
+
end
|
|
251
|
+
|
|
240
252
|
def bounding_rect(selector)
|
|
241
253
|
rect = evaluate_async(%(
|
|
242
254
|
const rect = document
|
data/lib/ferrum/page.rb
CHANGED
|
@@ -1,50 +1,43 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "forwardable"
|
|
4
|
+
require "pathname"
|
|
4
5
|
require "ferrum/mouse"
|
|
5
6
|
require "ferrum/keyboard"
|
|
6
7
|
require "ferrum/headers"
|
|
7
8
|
require "ferrum/cookies"
|
|
8
9
|
require "ferrum/dialog"
|
|
9
10
|
require "ferrum/network"
|
|
11
|
+
require "ferrum/downloads"
|
|
10
12
|
require "ferrum/page/frames"
|
|
11
13
|
require "ferrum/page/screenshot"
|
|
12
14
|
require "ferrum/page/animation"
|
|
13
15
|
require "ferrum/page/tracing"
|
|
14
16
|
require "ferrum/page/stream"
|
|
15
|
-
require "ferrum/browser/client"
|
|
16
17
|
|
|
17
18
|
module Ferrum
|
|
18
19
|
class Page
|
|
19
20
|
GOTO_WAIT = ENV.fetch("FERRUM_GOTO_WAIT", 0.1).to_f
|
|
20
21
|
|
|
21
|
-
class Event < Concurrent::Event
|
|
22
|
-
def iteration
|
|
23
|
-
synchronize { @iteration }
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def reset
|
|
27
|
-
synchronize do
|
|
28
|
-
@iteration += 1
|
|
29
|
-
@set = false if @set
|
|
30
|
-
@iteration
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
22
|
extend Forwardable
|
|
36
23
|
delegate %i[at_css at_xpath css xpath
|
|
37
24
|
current_url current_title url title body doctype content=
|
|
38
25
|
execution_id execution_id! evaluate evaluate_on evaluate_async execute evaluate_func
|
|
39
26
|
add_script_tag add_style_tag] => :main_frame
|
|
27
|
+
delegate %i[base_url default_user_agent timeout timeout=] => :@options
|
|
40
28
|
|
|
41
29
|
include Animation
|
|
42
30
|
include Screenshot
|
|
43
31
|
include Frames
|
|
44
32
|
include Stream
|
|
45
33
|
|
|
46
|
-
attr_accessor :referrer
|
|
47
|
-
attr_reader :
|
|
34
|
+
attr_accessor :referrer
|
|
35
|
+
attr_reader :context_id, :target_id, :event, :tracing
|
|
36
|
+
|
|
37
|
+
# Client connection.
|
|
38
|
+
#
|
|
39
|
+
# @return [Client]
|
|
40
|
+
attr_reader :client
|
|
48
41
|
|
|
49
42
|
# Mouse object.
|
|
50
43
|
#
|
|
@@ -71,55 +64,56 @@ module Ferrum
|
|
|
71
64
|
# @return [Cookies]
|
|
72
65
|
attr_reader :cookies
|
|
73
66
|
|
|
74
|
-
|
|
67
|
+
# Downloads object.
|
|
68
|
+
#
|
|
69
|
+
# @return [Downloads]
|
|
70
|
+
attr_reader :downloads
|
|
71
|
+
|
|
72
|
+
def initialize(client, context_id:, target_id:, proxy: nil)
|
|
73
|
+
@client = client
|
|
74
|
+
@context_id = context_id
|
|
75
|
+
@target_id = target_id
|
|
76
|
+
@options = client.options
|
|
77
|
+
|
|
75
78
|
@frames = Concurrent::Map.new
|
|
76
79
|
@main_frame = Frame.new(nil, self)
|
|
77
|
-
@
|
|
78
|
-
@target_id = target_id
|
|
79
|
-
@timeout = @browser.timeout
|
|
80
|
-
@event = Event.new.tap(&:set)
|
|
80
|
+
@event = Utils::Event.new.tap(&:set)
|
|
81
81
|
self.proxy = proxy
|
|
82
82
|
|
|
83
|
-
@client = Browser::Client.new(ws_url, self,
|
|
84
|
-
logger: @browser.options.logger,
|
|
85
|
-
ws_max_receive_size: @browser.options.ws_max_receive_size,
|
|
86
|
-
id_starts_with: 1000)
|
|
87
|
-
|
|
88
83
|
@mouse = Mouse.new(self)
|
|
89
84
|
@keyboard = Keyboard.new(self)
|
|
90
85
|
@headers = Headers.new(self)
|
|
91
86
|
@cookies = Cookies.new(self)
|
|
92
87
|
@network = Network.new(self)
|
|
93
88
|
@tracing = Tracing.new(self)
|
|
89
|
+
@downloads = Downloads.new(self)
|
|
94
90
|
|
|
95
91
|
subscribe
|
|
96
92
|
prepare_page
|
|
97
93
|
end
|
|
98
94
|
|
|
99
|
-
def context
|
|
100
|
-
@browser.contexts.find_by(target_id: target_id)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
95
|
#
|
|
104
96
|
# Navigates the page to a URL.
|
|
105
97
|
#
|
|
106
98
|
# @param [String, nil] url
|
|
107
99
|
# The URL to navigate to. The url should include scheme unless you set
|
|
108
|
-
# `{Browser#base_url = url}` when configuring
|
|
100
|
+
# `{Browser#base_url = url}` when configuring.
|
|
109
101
|
#
|
|
110
102
|
# @example
|
|
111
|
-
#
|
|
103
|
+
# page.go_to("https://github.com/")
|
|
112
104
|
#
|
|
113
105
|
def go_to(url = nil)
|
|
114
106
|
options = { url: combine_url!(url) }
|
|
115
107
|
options.merge!(referrer: referrer) if referrer
|
|
116
108
|
response = command("Page.navigate", wait: GOTO_WAIT, **options)
|
|
117
|
-
error_text = response["errorText"]
|
|
118
|
-
|
|
109
|
+
error_text = response["errorText"] # https://cs.chromium.org/chromium/src/net/base/net_error_list.h
|
|
110
|
+
if error_text && error_text != "net::ERR_ABORTED" # Request aborted due to user action or download
|
|
111
|
+
raise StatusError.new(options[:url], "Request to #{options[:url]} failed (#{error_text})")
|
|
112
|
+
end
|
|
119
113
|
|
|
120
114
|
response["frameId"]
|
|
121
115
|
rescue TimeoutError
|
|
122
|
-
if @
|
|
116
|
+
if @options.pending_connection_errors
|
|
123
117
|
pendings = network.traffic.select(&:pending?).map(&:url).compact
|
|
124
118
|
raise PendingConnectionsError.new(options[:url], pendings) unless pendings.empty?
|
|
125
119
|
end
|
|
@@ -129,41 +123,76 @@ module Ferrum
|
|
|
129
123
|
|
|
130
124
|
def close
|
|
131
125
|
@headers.clear
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
client.command("Target.closeTarget", async: true, targetId: @target_id)
|
|
127
|
+
close_connection
|
|
128
|
+
|
|
129
|
+
true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def close_connection
|
|
133
|
+
client&.close
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
#
|
|
137
|
+
# Overrides device screen dimensions and emulates viewport according to parameters
|
|
138
|
+
#
|
|
139
|
+
# Read more [here](https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride).
|
|
140
|
+
#
|
|
141
|
+
# @param [Integer] width width value in pixels. 0 disables the override
|
|
142
|
+
#
|
|
143
|
+
# @param [Integer] height height value in pixels. 0 disables the override
|
|
144
|
+
#
|
|
145
|
+
# @param [Float] scale_factor device scale factor value. 0 disables the override
|
|
146
|
+
#
|
|
147
|
+
# @param [Boolean] mobile whether to emulate mobile device
|
|
148
|
+
#
|
|
149
|
+
def set_viewport(width:, height:, scale_factor: 0, mobile: false)
|
|
150
|
+
command(
|
|
151
|
+
"Emulation.setDeviceMetricsOverride",
|
|
152
|
+
slowmoable: true,
|
|
153
|
+
width: width,
|
|
154
|
+
height: height,
|
|
155
|
+
deviceScaleFactor: scale_factor,
|
|
156
|
+
mobile: mobile
|
|
157
|
+
)
|
|
134
158
|
end
|
|
135
159
|
|
|
136
160
|
def resize(width: nil, height: nil, fullscreen: false)
|
|
137
161
|
if fullscreen
|
|
138
162
|
width, height = document_size
|
|
139
|
-
|
|
163
|
+
self.window_bounds = { window_state: "fullscreen" }
|
|
140
164
|
else
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
self.window_bounds = { window_state: "normal" }
|
|
166
|
+
self.window_bounds = { width: width, height: height }
|
|
143
167
|
end
|
|
144
168
|
|
|
145
|
-
|
|
146
|
-
width: width,
|
|
147
|
-
height: height,
|
|
148
|
-
deviceScaleFactor: 0,
|
|
149
|
-
mobile: false)
|
|
169
|
+
set_viewport(width: width, height: height)
|
|
150
170
|
end
|
|
151
171
|
|
|
152
172
|
#
|
|
153
|
-
#
|
|
173
|
+
# Disables JavaScript execution from the HTML source for the page.
|
|
174
|
+
#
|
|
175
|
+
# This doesn't prevent users evaluate JavaScript with Ferrum.
|
|
176
|
+
#
|
|
177
|
+
def disable_javascript
|
|
178
|
+
command("Emulation.setScriptExecutionDisabled", value: true)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
#
|
|
182
|
+
# The current position of the window.
|
|
154
183
|
#
|
|
155
184
|
# @return [(Integer, Integer)]
|
|
156
|
-
# The left, top coordinates of the
|
|
185
|
+
# The left, top coordinates of the window.
|
|
157
186
|
#
|
|
158
187
|
# @example
|
|
159
|
-
#
|
|
188
|
+
# page.position # => [10, 20]
|
|
160
189
|
#
|
|
161
190
|
def position
|
|
162
|
-
|
|
191
|
+
window_bounds.values_at("left", "top")
|
|
163
192
|
end
|
|
164
193
|
|
|
165
194
|
#
|
|
166
|
-
# Sets the position of the
|
|
195
|
+
# Sets the position of the window.
|
|
167
196
|
#
|
|
168
197
|
# @param [Hash{Symbol => Object}] options
|
|
169
198
|
#
|
|
@@ -174,20 +203,72 @@ module Ferrum
|
|
|
174
203
|
# The number of pixels from the top of the screen.
|
|
175
204
|
#
|
|
176
205
|
# @example
|
|
177
|
-
#
|
|
206
|
+
# page.position = { left: 10, top: 20 }
|
|
178
207
|
#
|
|
179
208
|
def position=(options)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
self.window_bounds = { left: options[:left], top: options[:top] }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Sets the position of the window.
|
|
213
|
+
#
|
|
214
|
+
# @param [Hash{Symbol => Object}] bounds
|
|
215
|
+
#
|
|
216
|
+
# @option options [Integer] :left
|
|
217
|
+
# The number of pixels from the left-hand side of the screen.
|
|
218
|
+
#
|
|
219
|
+
# @option options [Integer] :top
|
|
220
|
+
# The number of pixels from the top of the screen.
|
|
221
|
+
#
|
|
222
|
+
# @option options [Integer] :width
|
|
223
|
+
# The window width in pixels.
|
|
224
|
+
#
|
|
225
|
+
# @option options [Integer] :height
|
|
226
|
+
# The window height in pixels.
|
|
227
|
+
#
|
|
228
|
+
# @option options [String] :window_state
|
|
229
|
+
# The window state. Default to normal. Allowed Values: normal, minimized, maximized, fullscreen
|
|
230
|
+
#
|
|
231
|
+
# @example
|
|
232
|
+
# page.window_bounds = { left: 10, top: 20, width: 1024, height: 768, window_state: "normal" }
|
|
233
|
+
#
|
|
234
|
+
def window_bounds=(bounds)
|
|
235
|
+
options = bounds.dup
|
|
236
|
+
window_state = options.delete(:window_state)
|
|
237
|
+
bounds = { windowState: window_state, **options }.compact
|
|
238
|
+
|
|
239
|
+
client.command("Browser.setWindowBounds", windowId: window_id, bounds: bounds)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
#
|
|
243
|
+
# Current window bounds.
|
|
244
|
+
#
|
|
245
|
+
# @return [Hash{String => (Integer, String)}]
|
|
246
|
+
#
|
|
247
|
+
# @example
|
|
248
|
+
# page.window_bounds # => { "left": 0, "top": 1286, "width": 10, "height": 10, "windowState": "normal" }
|
|
249
|
+
#
|
|
250
|
+
def window_bounds
|
|
251
|
+
client.command("Browser.getWindowBounds", windowId: window_id).fetch("bounds")
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
#
|
|
255
|
+
# Current window id.
|
|
256
|
+
#
|
|
257
|
+
# @return [Integer]
|
|
258
|
+
#
|
|
259
|
+
# @example
|
|
260
|
+
# page.window_id # => 1
|
|
261
|
+
#
|
|
262
|
+
def window_id
|
|
263
|
+
client.command("Browser.getWindowForTarget", targetId: target_id)["windowId"]
|
|
183
264
|
end
|
|
184
265
|
|
|
185
266
|
#
|
|
186
267
|
# Reloads the current page.
|
|
187
268
|
#
|
|
188
269
|
# @example
|
|
189
|
-
#
|
|
190
|
-
#
|
|
270
|
+
# page.go_to("https://github.com/")
|
|
271
|
+
# page.refresh
|
|
191
272
|
#
|
|
192
273
|
def refresh
|
|
193
274
|
command("Page.reload", wait: timeout, slowmoable: true)
|
|
@@ -198,41 +279,41 @@ module Ferrum
|
|
|
198
279
|
# Stop all navigations and loading pending resources on the page.
|
|
199
280
|
#
|
|
200
281
|
# @example
|
|
201
|
-
#
|
|
202
|
-
#
|
|
282
|
+
# page.go_to("https://github.com/")
|
|
283
|
+
# page.stop
|
|
203
284
|
#
|
|
204
285
|
def stop
|
|
205
286
|
command("Page.stopLoading", slowmoable: true)
|
|
206
287
|
end
|
|
207
288
|
|
|
208
289
|
#
|
|
209
|
-
# Navigates to the previous URL in the
|
|
290
|
+
# Navigates to the previous URL in the history.
|
|
210
291
|
#
|
|
211
292
|
# @example
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
#
|
|
293
|
+
# page.go_to("https://github.com/")
|
|
294
|
+
# page.at_xpath("//a").click
|
|
295
|
+
# page.back
|
|
215
296
|
#
|
|
216
297
|
def back
|
|
217
298
|
history_navigate(delta: -1)
|
|
218
299
|
end
|
|
219
300
|
|
|
220
301
|
#
|
|
221
|
-
# Navigates to the next URL in the
|
|
302
|
+
# Navigates to the next URL in the history.
|
|
222
303
|
#
|
|
223
304
|
# @example
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
#
|
|
305
|
+
# page.go_to("https://github.com/")
|
|
306
|
+
# page.at_xpath("//a").click
|
|
307
|
+
# page.back
|
|
308
|
+
# page.forward
|
|
228
309
|
#
|
|
229
310
|
def forward
|
|
230
311
|
history_navigate(delta: 1)
|
|
231
312
|
end
|
|
232
313
|
|
|
233
|
-
def wait_for_reload(
|
|
314
|
+
def wait_for_reload(timeout = 1)
|
|
234
315
|
@event.reset if @event.set?
|
|
235
|
-
@event.wait(
|
|
316
|
+
@event.wait(timeout)
|
|
236
317
|
@event.set
|
|
237
318
|
end
|
|
238
319
|
|
|
@@ -244,29 +325,21 @@ module Ferrum
|
|
|
244
325
|
# @return [Boolean]
|
|
245
326
|
#
|
|
246
327
|
# @example
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
#
|
|
328
|
+
# page.bypass_csp # => true
|
|
329
|
+
# page.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
|
|
330
|
+
# page.refresh
|
|
331
|
+
# page.add_script_tag(content: "window.__injected = 42")
|
|
332
|
+
# page.evaluate("window.__injected") # => 42
|
|
252
333
|
#
|
|
253
334
|
def bypass_csp(enabled: true)
|
|
254
335
|
command("Page.setBypassCSP", enabled: enabled)
|
|
255
336
|
enabled
|
|
256
337
|
end
|
|
257
338
|
|
|
258
|
-
def window_id
|
|
259
|
-
@browser.command("Browser.getWindowForTarget", targetId: @target_id)["windowId"]
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def set_window_bounds(bounds = {})
|
|
263
|
-
@browser.command("Browser.setWindowBounds", windowId: window_id, bounds: bounds)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
339
|
def command(method, wait: 0, slowmoable: false, **params)
|
|
267
340
|
iteration = @event.reset if wait.positive?
|
|
268
|
-
sleep(@
|
|
269
|
-
result =
|
|
341
|
+
sleep(@options.slowmo) if slowmoable && @options.slowmo.positive?
|
|
342
|
+
result = client.command(method, **params)
|
|
270
343
|
|
|
271
344
|
if wait.positive?
|
|
272
345
|
# Wait a bit after command and check if iteration has
|
|
@@ -284,30 +357,30 @@ module Ferrum
|
|
|
284
357
|
def on(name, &block)
|
|
285
358
|
case name
|
|
286
359
|
when :dialog
|
|
287
|
-
|
|
360
|
+
client.on("Page.javascriptDialogOpening") do |params, index, total|
|
|
288
361
|
dialog = Dialog.new(self, params)
|
|
289
362
|
block.call(dialog, index, total)
|
|
290
363
|
end
|
|
291
364
|
when :request
|
|
292
|
-
|
|
293
|
-
request = Network::InterceptedRequest.new(
|
|
365
|
+
client.on("Fetch.requestPaused") do |params, index, total|
|
|
366
|
+
request = Network::InterceptedRequest.new(client, params)
|
|
294
367
|
exchange = network.select(request.network_id).last
|
|
295
368
|
exchange ||= network.build_exchange(request.network_id)
|
|
296
369
|
exchange.intercepted_request = request
|
|
297
370
|
block.call(request, index, total)
|
|
298
371
|
end
|
|
299
372
|
when :auth
|
|
300
|
-
|
|
373
|
+
client.on("Fetch.authRequired") do |params, index, total|
|
|
301
374
|
request = Network::AuthRequest.new(self, params)
|
|
302
375
|
block.call(request, index, total)
|
|
303
376
|
end
|
|
304
377
|
else
|
|
305
|
-
|
|
378
|
+
client.on(name, &block)
|
|
306
379
|
end
|
|
307
380
|
end
|
|
308
381
|
|
|
309
382
|
def subscribed?(event)
|
|
310
|
-
|
|
383
|
+
client.subscribed?(event)
|
|
311
384
|
end
|
|
312
385
|
|
|
313
386
|
def use_proxy?
|
|
@@ -327,14 +400,15 @@ module Ferrum
|
|
|
327
400
|
def subscribe
|
|
328
401
|
frames_subscribe
|
|
329
402
|
network.subscribe
|
|
403
|
+
downloads.subscribe
|
|
330
404
|
|
|
331
|
-
if @
|
|
405
|
+
if @options.logger
|
|
332
406
|
on("Runtime.consoleAPICalled") do |params|
|
|
333
|
-
params["args"].each { |r| @
|
|
407
|
+
params["args"].each { |r| @options.logger.puts(r["value"]) }
|
|
334
408
|
end
|
|
335
409
|
end
|
|
336
410
|
|
|
337
|
-
if @
|
|
411
|
+
if @options.js_errors
|
|
338
412
|
on("Runtime.exceptionThrown") do |params|
|
|
339
413
|
# FIXME: https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/
|
|
340
414
|
Thread.main.raise JavaScriptError.new(
|
|
@@ -369,26 +443,14 @@ module Ferrum
|
|
|
369
443
|
end
|
|
370
444
|
end
|
|
371
445
|
|
|
372
|
-
if @
|
|
373
|
-
unless Pathname.new(@browser.options.save_path).absolute?
|
|
374
|
-
raise Error, "supply absolute path for `:save_path` option"
|
|
375
|
-
end
|
|
446
|
+
downloads.set_behavior(save_path: @options.save_path) if @options.save_path
|
|
376
447
|
|
|
377
|
-
|
|
378
|
-
browserContextId: context.id,
|
|
379
|
-
downloadPath: @browser.options.save_path,
|
|
380
|
-
behavior: "allow", eventsEnabled: true)
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
@browser.extensions.each do |extension|
|
|
448
|
+
@options.extensions.each do |extension|
|
|
384
449
|
command("Page.addScriptToEvaluateOnNewDocument", source: extension)
|
|
385
450
|
end
|
|
386
451
|
|
|
387
452
|
inject_extensions
|
|
388
453
|
|
|
389
|
-
width, height = @browser.window_size
|
|
390
|
-
resize(width: width, height: height)
|
|
391
|
-
|
|
392
454
|
response = command("Page.getNavigationHistory")
|
|
393
455
|
transition_type = response.dig("entries", 0, "transitionType")
|
|
394
456
|
return if transition_type == "auto_toplevel"
|
|
@@ -402,7 +464,7 @@ module Ferrum
|
|
|
402
464
|
end
|
|
403
465
|
|
|
404
466
|
def inject_extensions
|
|
405
|
-
@
|
|
467
|
+
@options.extensions.each do |extension|
|
|
406
468
|
# https://github.com/GoogleChrome/puppeteer/issues/1443
|
|
407
469
|
# https://github.com/ChromeDevTools/devtools-protocol/issues/77
|
|
408
470
|
# https://github.com/cyrus-and/chrome-remote-interface/issues/319
|
|
@@ -432,22 +494,18 @@ module Ferrum
|
|
|
432
494
|
url = Addressable::URI.parse(url_or_path)
|
|
433
495
|
nil_or_relative = url.nil? || url.relative?
|
|
434
496
|
|
|
435
|
-
if nil_or_relative && !@
|
|
497
|
+
if nil_or_relative && !@options.base_url
|
|
436
498
|
raise "Set :base_url browser's option or use absolute url in `go_to`, you passed: #{url_or_path}"
|
|
437
499
|
end
|
|
438
500
|
|
|
439
|
-
(nil_or_relative ? @
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
def ws_url
|
|
443
|
-
"ws://#{@browser.process.host}:#{@browser.process.port}/devtools/page/#{@target_id}"
|
|
501
|
+
(nil_or_relative ? @options.base_url.join(url.to_s) : url).to_s
|
|
444
502
|
end
|
|
445
503
|
|
|
446
504
|
def proxy=(options)
|
|
447
|
-
@proxy_host = options&.[](:host) || @
|
|
448
|
-
@proxy_port = options&.[](:port) || @
|
|
449
|
-
@proxy_user = options&.[](:user) || @
|
|
450
|
-
@proxy_password = options&.[](:password) || @
|
|
505
|
+
@proxy_host = options&.[](:host) || @options.proxy&.[](:host)
|
|
506
|
+
@proxy_port = options&.[](:port) || @options.proxy&.[](:port)
|
|
507
|
+
@proxy_user = options&.[](:user) || @options.proxy&.[](:user)
|
|
508
|
+
@proxy_password = options&.[](:password) || @options.proxy&.[](:password)
|
|
451
509
|
end
|
|
452
510
|
end
|
|
453
511
|
end
|