ferrum 0.14 → 0.16

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.
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.14'
4
+ version: '0.16'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-14 00:00:00.000000000 Z
11
+ date: 2024-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: base64
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: concurrent-ruby
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,22 +70,16 @@ dependencies:
56
70
  name: websocket-driver
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0.6'
62
- - - "<"
73
+ - - "~>"
63
74
  - !ruby/object:Gem::Version
64
- version: '0.8'
75
+ version: '0.7'
65
76
  type: :runtime
66
77
  prerelease: false
67
78
  version_requirements: !ruby/object:Gem::Requirement
68
79
  requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- version: '0.6'
72
- - - "<"
80
+ - - "~>"
73
81
  - !ruby/object:Gem::Version
74
- version: '0.8'
82
+ version: '0.7'
75
83
  description: Ferrum allows you to control headless Chrome browser
76
84
  email:
77
85
  - d.vorotilin@gmail.com
@@ -84,22 +92,23 @@ files:
84
92
  - lib/ferrum.rb
85
93
  - lib/ferrum/browser.rb
86
94
  - lib/ferrum/browser/binary.rb
87
- - lib/ferrum/browser/client.rb
88
95
  - lib/ferrum/browser/command.rb
89
96
  - lib/ferrum/browser/options.rb
90
97
  - lib/ferrum/browser/options/base.rb
91
98
  - lib/ferrum/browser/options/chrome.rb
92
99
  - lib/ferrum/browser/options/firefox.rb
93
100
  - lib/ferrum/browser/process.rb
94
- - lib/ferrum/browser/subscriber.rb
95
101
  - lib/ferrum/browser/version_info.rb
96
- - lib/ferrum/browser/web_socket.rb
97
102
  - lib/ferrum/browser/xvfb.rb
103
+ - lib/ferrum/client.rb
104
+ - lib/ferrum/client/subscriber.rb
105
+ - lib/ferrum/client/web_socket.rb
98
106
  - lib/ferrum/context.rb
99
107
  - lib/ferrum/contexts.rb
100
108
  - lib/ferrum/cookies.rb
101
109
  - lib/ferrum/cookies/cookie.rb
102
110
  - lib/ferrum/dialog.rb
111
+ - lib/ferrum/downloads.rb
103
112
  - lib/ferrum/errors.rb
104
113
  - lib/ferrum/frame.rb
105
114
  - lib/ferrum/frame/dom.rb
@@ -128,7 +137,9 @@ files:
128
137
  - lib/ferrum/target.rb
129
138
  - lib/ferrum/utils/attempt.rb
130
139
  - lib/ferrum/utils/elapsed_time.rb
140
+ - lib/ferrum/utils/event.rb
131
141
  - lib/ferrum/utils/platform.rb
142
+ - lib/ferrum/utils/thread.rb
132
143
  - lib/ferrum/version.rb
133
144
  homepage: https://github.com/rubycdp/ferrum
134
145
  licenses:
@@ -148,14 +159,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
159
  requirements:
149
160
  - - ">="
150
161
  - !ruby/object:Gem::Version
151
- version: 2.6.0
162
+ version: 2.7.0
152
163
  required_rubygems_version: !ruby/object:Gem::Requirement
153
164
  requirements:
154
165
  - - ">="
155
166
  - !ruby/object:Gem::Version
156
167
  version: '0'
157
168
  requirements: []
158
- rubygems_version: 3.4.13
169
+ rubygems_version: 3.5.22
159
170
  signing_key:
160
171
  specification_version: 4
161
172
  summary: Ruby headless Chrome driver
@@ -1,103 +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
- "Inspected target navigated or closed"
89
- raise NodeNotFoundError, error
90
- # Context is lost, page is reloading
91
- when "Cannot find context with specified id"
92
- raise NoExecutionContextError, error
93
- when "No target with given id found"
94
- raise NoSuchPageError
95
- when /Could not compute content quads/
96
- raise CoordinatesNotFoundError
97
- else
98
- raise BrowserError, error
99
- end
100
- end
101
- end
102
- end
103
- 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
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "socket"
5
- require "websocket/driver"
6
-
7
- module Ferrum
8
- class Browser
9
- class WebSocket
10
- WEBSOCKET_BUG_SLEEP = 0.05
11
- SKIP_LOGGING_SCREENSHOTS = !ENV["FERRUM_LOGGING_SCREENSHOTS"]
12
-
13
- attr_reader :url, :messages
14
-
15
- def initialize(url, max_receive_size, logger)
16
- @url = url
17
- @logger = logger
18
- uri = URI.parse(@url)
19
- @sock = TCPSocket.new(uri.host, uri.port)
20
- max_receive_size ||= ::WebSocket::Driver::MAX_LENGTH
21
- @driver = ::WebSocket::Driver.client(self, max_length: max_receive_size)
22
- @messages = Queue.new
23
-
24
- @screenshot_commands = Concurrent::Hash.new if SKIP_LOGGING_SCREENSHOTS
25
-
26
- @driver.on(:open, &method(:on_open))
27
- @driver.on(:message, &method(:on_message))
28
- @driver.on(:close, &method(:on_close))
29
-
30
- @thread = Thread.new do
31
- Thread.current.abort_on_exception = true
32
- Thread.current.report_on_exception = true if Thread.current.respond_to?(:report_on_exception=)
33
-
34
- begin
35
- loop do
36
- data = @sock.readpartial(512)
37
- break unless data
38
-
39
- @driver.parse(data)
40
- end
41
- rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
42
- @messages.close
43
- end
44
- end
45
-
46
- @driver.start
47
- end
48
-
49
- def on_open(_event)
50
- # https://github.com/faye/websocket-driver-ruby/issues/46
51
- sleep(WEBSOCKET_BUG_SLEEP)
52
- end
53
-
54
- def on_message(event)
55
- data = JSON.parse(event.data)
56
- @messages.push(data)
57
-
58
- output = event.data
59
- if SKIP_LOGGING_SCREENSHOTS && @screenshot_commands[data["id"]]
60
- @screenshot_commands.delete(data["id"])
61
- output.sub!(/{"data":"(.*)"}/, %("Set FERRUM_LOGGING_SCREENSHOTS=true to see screenshots in Base64"))
62
- end
63
-
64
- @logger&.puts(" ◀ #{Utils::ElapsedTime.elapsed_time} #{output}\n")
65
- end
66
-
67
- def on_close(_event)
68
- @messages.close
69
- @thread.kill
70
- end
71
-
72
- def send_message(data)
73
- @screenshot_commands[data[:id]] = true if SKIP_LOGGING_SCREENSHOTS
74
-
75
- json = data.to_json
76
- @driver.text(json)
77
- @logger&.puts("\n\n▶ #{Utils::ElapsedTime.elapsed_time} #{json}")
78
- end
79
-
80
- def write(data)
81
- @sock.write(data)
82
- rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
83
- @messages.close
84
- end
85
-
86
- def close
87
- @driver.close
88
- end
89
- end
90
- end
91
- end