ferrum 0.13 → 0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +288 -154
- data/lib/ferrum/browser/command.rb +8 -0
- data/lib/ferrum/browser/options/chrome.rb +17 -5
- data/lib/ferrum/browser/options.rb +38 -25
- data/lib/ferrum/browser/process.rb +44 -17
- data/lib/ferrum/browser.rb +34 -52
- data/lib/ferrum/client/subscriber.rb +76 -0
- data/lib/ferrum/{browser → client}/web_socket.rb +36 -22
- 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/cookie.rb +57 -0
- data/lib/ferrum/cookies.rb +40 -4
- data/lib/ferrum/downloads.rb +60 -0
- data/lib/ferrum/errors.rb +2 -1
- data/lib/ferrum/frame.rb +1 -0
- data/lib/ferrum/headers.rb +1 -1
- data/lib/ferrum/network/exchange.rb +29 -2
- data/lib/ferrum/network/intercepted_request.rb +8 -17
- data/lib/ferrum/network/request.rb +23 -39
- data/lib/ferrum/network/request_params.rb +57 -0
- data/lib/ferrum/network/response.rb +25 -5
- data/lib/ferrum/network.rb +43 -16
- data/lib/ferrum/node.rb +21 -1
- data/lib/ferrum/page/frames.rb +5 -5
- data/lib/ferrum/page/screenshot.rb +42 -24
- data/lib/ferrum/page.rb +183 -131
- 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 -114
- data/lib/ferrum/browser/client.rb +0 -102
- data/lib/ferrum/browser/subscriber.rb +0 -36
data/lib/ferrum.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ferrum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.15'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Vorotilin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -55,121 +55,17 @@ dependencies:
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: websocket-driver
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0.6'
|
62
|
-
- - "<"
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: '0.8'
|
65
|
-
type: :runtime
|
66
|
-
prerelease: false
|
67
|
-
version_requirements: !ruby/object:Gem::Requirement
|
68
|
-
requirements:
|
69
|
-
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version: '0.6'
|
72
|
-
- - "<"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '0.8'
|
75
|
-
- !ruby/object:Gem::Dependency
|
76
|
-
name: chunky_png
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - "~>"
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: '1.3'
|
82
|
-
type: :development
|
83
|
-
prerelease: false
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - "~>"
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '1.3'
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: image_size
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - "~>"
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '2.0'
|
96
|
-
type: :development
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '2.0'
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: pdf-reader
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '2.2'
|
110
|
-
type: :development
|
111
|
-
prerelease: false
|
112
|
-
version_requirements: !ruby/object:Gem::Requirement
|
113
58
|
requirements:
|
114
59
|
- - "~>"
|
115
60
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
117
|
-
|
118
|
-
name: puma
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
120
|
-
requirements:
|
121
|
-
- - "~>"
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
version: '4.1'
|
124
|
-
type: :development
|
125
|
-
prerelease: false
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
127
|
-
requirements:
|
128
|
-
- - "~>"
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
version: '4.1'
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: rake
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
134
|
-
requirements:
|
135
|
-
- - "~>"
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
version: '13.0'
|
138
|
-
type: :development
|
139
|
-
prerelease: false
|
140
|
-
version_requirements: !ruby/object:Gem::Requirement
|
141
|
-
requirements:
|
142
|
-
- - "~>"
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '13.0'
|
145
|
-
- !ruby/object:Gem::Dependency
|
146
|
-
name: rspec
|
147
|
-
requirement: !ruby/object:Gem::Requirement
|
148
|
-
requirements:
|
149
|
-
- - "~>"
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
version: '3.8'
|
152
|
-
type: :development
|
153
|
-
prerelease: false
|
154
|
-
version_requirements: !ruby/object:Gem::Requirement
|
155
|
-
requirements:
|
156
|
-
- - "~>"
|
157
|
-
- !ruby/object:Gem::Version
|
158
|
-
version: '3.8'
|
159
|
-
- !ruby/object:Gem::Dependency
|
160
|
-
name: sinatra
|
161
|
-
requirement: !ruby/object:Gem::Requirement
|
162
|
-
requirements:
|
163
|
-
- - "~>"
|
164
|
-
- !ruby/object:Gem::Version
|
165
|
-
version: '2.0'
|
166
|
-
type: :development
|
61
|
+
version: '0.7'
|
62
|
+
type: :runtime
|
167
63
|
prerelease: false
|
168
64
|
version_requirements: !ruby/object:Gem::Requirement
|
169
65
|
requirements:
|
170
66
|
- - "~>"
|
171
67
|
- !ruby/object:Gem::Version
|
172
|
-
version: '
|
68
|
+
version: '0.7'
|
173
69
|
description: Ferrum allows you to control headless Chrome browser
|
174
70
|
email:
|
175
71
|
- d.vorotilin@gmail.com
|
@@ -182,22 +78,23 @@ files:
|
|
182
78
|
- lib/ferrum.rb
|
183
79
|
- lib/ferrum/browser.rb
|
184
80
|
- lib/ferrum/browser/binary.rb
|
185
|
-
- lib/ferrum/browser/client.rb
|
186
81
|
- lib/ferrum/browser/command.rb
|
187
82
|
- lib/ferrum/browser/options.rb
|
188
83
|
- lib/ferrum/browser/options/base.rb
|
189
84
|
- lib/ferrum/browser/options/chrome.rb
|
190
85
|
- lib/ferrum/browser/options/firefox.rb
|
191
86
|
- lib/ferrum/browser/process.rb
|
192
|
-
- lib/ferrum/browser/subscriber.rb
|
193
87
|
- lib/ferrum/browser/version_info.rb
|
194
|
-
- lib/ferrum/browser/web_socket.rb
|
195
88
|
- lib/ferrum/browser/xvfb.rb
|
89
|
+
- lib/ferrum/client.rb
|
90
|
+
- lib/ferrum/client/subscriber.rb
|
91
|
+
- lib/ferrum/client/web_socket.rb
|
196
92
|
- lib/ferrum/context.rb
|
197
93
|
- lib/ferrum/contexts.rb
|
198
94
|
- lib/ferrum/cookies.rb
|
199
95
|
- lib/ferrum/cookies/cookie.rb
|
200
96
|
- lib/ferrum/dialog.rb
|
97
|
+
- lib/ferrum/downloads.rb
|
201
98
|
- lib/ferrum/errors.rb
|
202
99
|
- lib/ferrum/frame.rb
|
203
100
|
- lib/ferrum/frame/dom.rb
|
@@ -212,6 +109,7 @@ files:
|
|
212
109
|
- lib/ferrum/network/exchange.rb
|
213
110
|
- lib/ferrum/network/intercepted_request.rb
|
214
111
|
- lib/ferrum/network/request.rb
|
112
|
+
- lib/ferrum/network/request_params.rb
|
215
113
|
- lib/ferrum/network/response.rb
|
216
114
|
- lib/ferrum/node.rb
|
217
115
|
- lib/ferrum/page.rb
|
@@ -225,7 +123,9 @@ files:
|
|
225
123
|
- lib/ferrum/target.rb
|
226
124
|
- lib/ferrum/utils/attempt.rb
|
227
125
|
- lib/ferrum/utils/elapsed_time.rb
|
126
|
+
- lib/ferrum/utils/event.rb
|
228
127
|
- lib/ferrum/utils/platform.rb
|
128
|
+
- lib/ferrum/utils/thread.rb
|
229
129
|
- lib/ferrum/version.rb
|
230
130
|
homepage: https://github.com/rubycdp/ferrum
|
231
131
|
licenses:
|
@@ -245,14 +145,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
245
145
|
requirements:
|
246
146
|
- - ">="
|
247
147
|
- !ruby/object:Gem::Version
|
248
|
-
version: 2.
|
148
|
+
version: 2.7.0
|
249
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
250
150
|
requirements:
|
251
151
|
- - ">="
|
252
152
|
- !ruby/object:Gem::Version
|
253
153
|
version: '0'
|
254
154
|
requirements: []
|
255
|
-
rubygems_version: 3.
|
155
|
+
rubygems_version: 3.5.6
|
256
156
|
signing_key:
|
257
157
|
specification_version: 4
|
258
158
|
summary: Ruby headless Chrome driver
|
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "ferrum/browser/subscriber"
|
4
|
-
require "ferrum/browser/web_socket"
|
5
|
-
|
6
|
-
module Ferrum
|
7
|
-
class Browser
|
8
|
-
class Client
|
9
|
-
INTERRUPTIONS = %w[Fetch.requestPaused Fetch.authRequired].freeze
|
10
|
-
|
11
|
-
def initialize(ws_url, connectable, logger: nil, ws_max_receive_size: nil, id_starts_with: 0)
|
12
|
-
@connectable = connectable
|
13
|
-
@command_id = id_starts_with
|
14
|
-
@pendings = Concurrent::Hash.new
|
15
|
-
@ws = WebSocket.new(ws_url, ws_max_receive_size, logger)
|
16
|
-
@subscriber, @interrupter = Subscriber.build(2)
|
17
|
-
|
18
|
-
@thread = Thread.new do
|
19
|
-
Thread.current.abort_on_exception = true
|
20
|
-
Thread.current.report_on_exception = true if Thread.current.respond_to?(:report_on_exception=)
|
21
|
-
|
22
|
-
loop do
|
23
|
-
message = @ws.messages.pop
|
24
|
-
break unless message
|
25
|
-
|
26
|
-
if INTERRUPTIONS.include?(message["method"])
|
27
|
-
@interrupter.async.call(message)
|
28
|
-
elsif message.key?("method")
|
29
|
-
@subscriber.async.call(message)
|
30
|
-
else
|
31
|
-
@pendings[message["id"]]&.set(message)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def command(method, params = {})
|
38
|
-
pending = Concurrent::IVar.new
|
39
|
-
message = build_message(method, params)
|
40
|
-
@pendings[message[:id]] = pending
|
41
|
-
@ws.send_message(message)
|
42
|
-
data = pending.value!(@connectable.timeout)
|
43
|
-
@pendings.delete(message[:id])
|
44
|
-
|
45
|
-
raise DeadBrowserError if data.nil? && @ws.messages.closed?
|
46
|
-
raise TimeoutError unless data
|
47
|
-
|
48
|
-
error, response = data.values_at("error", "result")
|
49
|
-
raise_browser_error(error) if error
|
50
|
-
response
|
51
|
-
end
|
52
|
-
|
53
|
-
def on(event, &block)
|
54
|
-
case event
|
55
|
-
when *INTERRUPTIONS
|
56
|
-
@interrupter.on(event, &block)
|
57
|
-
else
|
58
|
-
@subscriber.on(event, &block)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def subscribed?(event)
|
63
|
-
[@interrupter, @subscriber].any? { |s| s.subscribed?(event) }
|
64
|
-
end
|
65
|
-
|
66
|
-
def close
|
67
|
-
@ws.close
|
68
|
-
# Give a thread some time to handle a tail of messages
|
69
|
-
@pendings.clear
|
70
|
-
@thread.kill unless @thread.join(1)
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def build_message(method, params)
|
76
|
-
{ method: method, params: params }.merge(id: next_command_id)
|
77
|
-
end
|
78
|
-
|
79
|
-
def next_command_id
|
80
|
-
@command_id += 1
|
81
|
-
end
|
82
|
-
|
83
|
-
def raise_browser_error(error)
|
84
|
-
case error["message"]
|
85
|
-
# Node has disappeared while we were trying to get it
|
86
|
-
when "No node with given id found",
|
87
|
-
"Could not find node with given id"
|
88
|
-
raise NodeNotFoundError, error
|
89
|
-
# Context is lost, page is reloading
|
90
|
-
when "Cannot find context with specified id"
|
91
|
-
raise NoExecutionContextError, error
|
92
|
-
when "No target with given id found"
|
93
|
-
raise NoSuchPageError
|
94
|
-
when /Could not compute content quads/
|
95
|
-
raise CoordinatesNotFoundError
|
96
|
-
else
|
97
|
-
raise BrowserError, error
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Ferrum
|
4
|
-
class Browser
|
5
|
-
class Subscriber
|
6
|
-
include Concurrent::Async
|
7
|
-
|
8
|
-
def self.build(size)
|
9
|
-
(0..size).map { new }
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
super
|
14
|
-
@on = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
|
15
|
-
end
|
16
|
-
|
17
|
-
def on(event, &block)
|
18
|
-
@on[event] << block
|
19
|
-
true
|
20
|
-
end
|
21
|
-
|
22
|
-
def subscribed?(event)
|
23
|
-
@on.key?(event)
|
24
|
-
end
|
25
|
-
|
26
|
-
def call(message)
|
27
|
-
method, params = message.values_at("method", "params")
|
28
|
-
total = @on[method].size
|
29
|
-
@on[method].each_with_index do |block, index|
|
30
|
-
# In case of multiple callbacks we provide current index and total
|
31
|
-
block.call(params, index, total)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|