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
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
require "ferrum/client/subscriber"
|
|
5
|
+
require "ferrum/client/web_socket"
|
|
6
|
+
|
|
7
|
+
module Ferrum
|
|
8
|
+
class SessionClient
|
|
9
|
+
attr_reader :client, :session_id
|
|
10
|
+
|
|
11
|
+
def self.event_name(event, session_id)
|
|
12
|
+
[event, session_id].compact.join("_")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(client, session_id)
|
|
16
|
+
@client = client
|
|
17
|
+
@session_id = session_id
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def command(method, async: false, **params)
|
|
21
|
+
message = build_message(method, params)
|
|
22
|
+
@client.send_message(message, async: async)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def on(event, &block)
|
|
26
|
+
@client.on(event_name(event), &block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def subscribed?(event)
|
|
30
|
+
@client.subscribed?(event_name(event))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def respond_to_missing?(name, include_private)
|
|
34
|
+
@client.respond_to?(name, include_private)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def method_missing(name, ...)
|
|
38
|
+
@client.send(name, ...)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def close
|
|
42
|
+
@client.subscriber.clear(session_id: session_id)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def build_message(method, params)
|
|
48
|
+
@client.build_message(method, params).merge(sessionId: session_id)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def event_name(event)
|
|
52
|
+
self.class.event_name(event, session_id)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class Client
|
|
57
|
+
extend Forwardable
|
|
58
|
+
delegate %i[timeout timeout=] => :options
|
|
59
|
+
|
|
60
|
+
attr_reader :ws_url, :options, :subscriber
|
|
61
|
+
|
|
62
|
+
def initialize(ws_url, options)
|
|
63
|
+
@command_id = 0
|
|
64
|
+
@ws_url = ws_url
|
|
65
|
+
@options = options
|
|
66
|
+
@pendings = Concurrent::Hash.new
|
|
67
|
+
@ws = WebSocket.new(ws_url, options.ws_max_receive_size, options.logger)
|
|
68
|
+
@subscriber = Subscriber.new
|
|
69
|
+
|
|
70
|
+
start
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def command(method, async: false, **params)
|
|
74
|
+
message = build_message(method, params)
|
|
75
|
+
send_message(message, async: async)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def send_message(message, async:)
|
|
79
|
+
if async
|
|
80
|
+
@ws.send_message(message)
|
|
81
|
+
true
|
|
82
|
+
else
|
|
83
|
+
pending = Concurrent::IVar.new
|
|
84
|
+
@pendings[message[:id]] = pending
|
|
85
|
+
@ws.send_message(message)
|
|
86
|
+
data = pending.value!(timeout)
|
|
87
|
+
@pendings.delete(message[:id])
|
|
88
|
+
|
|
89
|
+
raise DeadBrowserError if data.nil? && @ws.messages.closed?
|
|
90
|
+
raise TimeoutError unless data
|
|
91
|
+
|
|
92
|
+
error, response = data.values_at("error", "result")
|
|
93
|
+
raise_browser_error(error) if error
|
|
94
|
+
response
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def on(event, &block)
|
|
99
|
+
@subscriber.on(event, &block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def subscribed?(event)
|
|
103
|
+
@subscriber.subscribed?(event)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def session(session_id)
|
|
107
|
+
SessionClient.new(self, session_id)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def close
|
|
111
|
+
@ws.close
|
|
112
|
+
# Give a thread some time to handle a tail of messages
|
|
113
|
+
@pendings.clear
|
|
114
|
+
@thread.kill unless @thread.join(1)
|
|
115
|
+
@subscriber.close
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def inspect
|
|
119
|
+
"#<#{self.class} " \
|
|
120
|
+
"@command_id=#{@command_id.inspect} " \
|
|
121
|
+
"@pendings=#{@pendings.inspect} " \
|
|
122
|
+
"@ws=#{@ws.inspect}>"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def build_message(method, params)
|
|
126
|
+
{ method: method, params: params }.merge(id: next_command_id)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def start
|
|
132
|
+
@thread = Utils::Thread.spawn do
|
|
133
|
+
loop do
|
|
134
|
+
message = @ws.messages.pop
|
|
135
|
+
break unless message
|
|
136
|
+
|
|
137
|
+
if message.key?("method")
|
|
138
|
+
@subscriber << message
|
|
139
|
+
else
|
|
140
|
+
@pendings[message["id"]]&.set(message)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def next_command_id
|
|
147
|
+
@command_id += 1
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def raise_browser_error(error)
|
|
151
|
+
case error["message"]
|
|
152
|
+
# Node has disappeared while we were trying to get it
|
|
153
|
+
when "No node with given id found",
|
|
154
|
+
"Could not find node with given id",
|
|
155
|
+
"Inspected target navigated or closed"
|
|
156
|
+
raise NodeNotFoundError, error
|
|
157
|
+
# Context is lost, page is reloading
|
|
158
|
+
when "Cannot find context with specified id"
|
|
159
|
+
raise NoExecutionContextError, error
|
|
160
|
+
when "No target with given id found"
|
|
161
|
+
raise NoSuchPageError
|
|
162
|
+
when /Could not compute content quads/
|
|
163
|
+
raise CoordinatesNotFoundError
|
|
164
|
+
else
|
|
165
|
+
raise BrowserError, error
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
data/lib/ferrum/context.rb
CHANGED
|
@@ -8,9 +8,9 @@ module Ferrum
|
|
|
8
8
|
|
|
9
9
|
attr_reader :id, :targets
|
|
10
10
|
|
|
11
|
-
def initialize(
|
|
11
|
+
def initialize(client, contexts, id)
|
|
12
12
|
@id = id
|
|
13
|
-
@
|
|
13
|
+
@client = client
|
|
14
14
|
@contexts = contexts
|
|
15
15
|
@targets = Concurrent::Map.new
|
|
16
16
|
@pendings = Concurrent::MVar.new
|
|
@@ -46,33 +46,37 @@ module Ferrum
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def create_target
|
|
49
|
-
@
|
|
50
|
-
|
|
51
|
-
url: "about:blank")
|
|
52
|
-
target = @pendings.take(@browser.timeout)
|
|
49
|
+
@client.command("Target.createTarget", browserContextId: @id, url: "about:blank")
|
|
50
|
+
target = @pendings.take(@client.timeout)
|
|
53
51
|
raise NoSuchTargetError unless target.is_a?(Target)
|
|
54
52
|
|
|
55
|
-
@targets.put_if_absent(target.id, target)
|
|
56
53
|
target
|
|
57
54
|
end
|
|
58
55
|
|
|
59
|
-
def add_target(params)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
end
|
|
56
|
+
def add_target(params:, session_id: nil)
|
|
57
|
+
new_target = Target.new(@client, session_id, params)
|
|
58
|
+
target = @targets.put_if_absent(new_target.id, new_target)
|
|
59
|
+
target ||= new_target # `put_if_absent` returns nil if added a new value or existing if there was one already
|
|
60
|
+
@pendings.put(target, @client.timeout) if @pendings.empty?
|
|
61
|
+
target
|
|
66
62
|
end
|
|
67
63
|
|
|
68
64
|
def update_target(target_id, params)
|
|
69
|
-
@targets[target_id]
|
|
65
|
+
@targets[target_id]&.update(params)
|
|
70
66
|
end
|
|
71
67
|
|
|
72
68
|
def delete_target(target_id)
|
|
73
69
|
@targets.delete(target_id)
|
|
74
70
|
end
|
|
75
71
|
|
|
72
|
+
def close_targets_connection
|
|
73
|
+
@targets.each_value do |target|
|
|
74
|
+
next unless target.connected?
|
|
75
|
+
|
|
76
|
+
target.page.close_connection
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
76
80
|
def dispose
|
|
77
81
|
@contexts.dispose(@id)
|
|
78
82
|
end
|
data/lib/ferrum/contexts.rb
CHANGED
|
@@ -4,12 +4,15 @@ require "ferrum/context"
|
|
|
4
4
|
|
|
5
5
|
module Ferrum
|
|
6
6
|
class Contexts
|
|
7
|
+
include Enumerable
|
|
8
|
+
|
|
7
9
|
attr_reader :contexts
|
|
8
10
|
|
|
9
|
-
def initialize(
|
|
11
|
+
def initialize(client)
|
|
10
12
|
@contexts = Concurrent::Map.new
|
|
11
|
-
@
|
|
13
|
+
@client = client
|
|
12
14
|
subscribe
|
|
15
|
+
auto_attach
|
|
13
16
|
discover
|
|
14
17
|
end
|
|
15
18
|
|
|
@@ -17,6 +20,16 @@ module Ferrum
|
|
|
17
20
|
@default_context ||= create
|
|
18
21
|
end
|
|
19
22
|
|
|
23
|
+
def each(&block)
|
|
24
|
+
return enum_for(__method__) unless block_given?
|
|
25
|
+
|
|
26
|
+
@contexts.each(&block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def [](id)
|
|
30
|
+
@contexts[id]
|
|
31
|
+
end
|
|
32
|
+
|
|
20
33
|
def find_by(target_id:)
|
|
21
34
|
context = nil
|
|
22
35
|
@contexts.each_value { |c| context = c if c.target?(target_id) }
|
|
@@ -24,21 +37,25 @@ module Ferrum
|
|
|
24
37
|
end
|
|
25
38
|
|
|
26
39
|
def create(**options)
|
|
27
|
-
response = @
|
|
40
|
+
response = @client.command("Target.createBrowserContext", **options)
|
|
28
41
|
context_id = response["browserContextId"]
|
|
29
|
-
context = Context.new(@
|
|
42
|
+
context = Context.new(@client, self, context_id)
|
|
30
43
|
@contexts[context_id] = context
|
|
31
44
|
context
|
|
32
45
|
end
|
|
33
46
|
|
|
34
47
|
def dispose(context_id)
|
|
35
48
|
context = @contexts[context_id]
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
context.close_targets_connection
|
|
50
|
+
@client.command("Target.disposeBrowserContext", browserContextId: context.id)
|
|
38
51
|
@contexts.delete(context_id)
|
|
39
52
|
true
|
|
40
53
|
end
|
|
41
54
|
|
|
55
|
+
def close_connections
|
|
56
|
+
@contexts.each_value(&:close_targets_connection)
|
|
57
|
+
end
|
|
58
|
+
|
|
42
59
|
def reset
|
|
43
60
|
@default_context = nil
|
|
44
61
|
@contexts.each_key { |id| dispose(id) }
|
|
@@ -51,15 +68,26 @@ module Ferrum
|
|
|
51
68
|
private
|
|
52
69
|
|
|
53
70
|
def subscribe
|
|
54
|
-
@
|
|
71
|
+
@client.on("Target.attachedToTarget") do |params|
|
|
72
|
+
info, session_id = params.values_at("targetInfo", "sessionId")
|
|
73
|
+
next unless info["type"] == "page"
|
|
74
|
+
|
|
75
|
+
context_id = info["browserContextId"]
|
|
76
|
+
@contexts[context_id]&.add_target(session_id: session_id, params: info)
|
|
77
|
+
if params["waitingForDebugger"]
|
|
78
|
+
@client.session(session_id).command("Runtime.runIfWaitingForDebugger", async: true)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@client.on("Target.targetCreated") do |params|
|
|
55
83
|
info = params["targetInfo"]
|
|
56
84
|
next unless info["type"] == "page"
|
|
57
85
|
|
|
58
86
|
context_id = info["browserContextId"]
|
|
59
|
-
@contexts[context_id]&.add_target(info)
|
|
87
|
+
@contexts[context_id]&.add_target(params: info)
|
|
60
88
|
end
|
|
61
89
|
|
|
62
|
-
@
|
|
90
|
+
@client.on("Target.targetInfoChanged") do |params|
|
|
63
91
|
info = params["targetInfo"]
|
|
64
92
|
next unless info["type"] == "page"
|
|
65
93
|
|
|
@@ -67,19 +95,25 @@ module Ferrum
|
|
|
67
95
|
@contexts[context_id]&.update_target(target_id, info)
|
|
68
96
|
end
|
|
69
97
|
|
|
70
|
-
@
|
|
98
|
+
@client.on("Target.targetDestroyed") do |params|
|
|
71
99
|
context = find_by(target_id: params["targetId"])
|
|
72
100
|
context&.delete_target(params["targetId"])
|
|
73
101
|
end
|
|
74
102
|
|
|
75
|
-
@
|
|
103
|
+
@client.on("Target.targetCrashed") do |params|
|
|
76
104
|
context = find_by(target_id: params["targetId"])
|
|
77
105
|
context&.delete_target(params["targetId"])
|
|
78
106
|
end
|
|
79
107
|
end
|
|
80
108
|
|
|
81
109
|
def discover
|
|
82
|
-
@
|
|
110
|
+
@client.command("Target.setDiscoverTargets", discover: true)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def auto_attach
|
|
114
|
+
return unless @client.options.flatten
|
|
115
|
+
|
|
116
|
+
@client.command("Target.setAutoAttach", autoAttach: true, waitForDebuggerOnStart: true, flatten: true)
|
|
83
117
|
end
|
|
84
118
|
end
|
|
85
119
|
end
|
data/lib/ferrum/cookies.rb
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ferrum
|
|
4
|
+
class Downloads
|
|
5
|
+
VALID_BEHAVIOR = %i[deny allow allowAndName default].freeze
|
|
6
|
+
|
|
7
|
+
def initialize(page)
|
|
8
|
+
@page = page
|
|
9
|
+
@event = Utils::Event.new.tap(&:set)
|
|
10
|
+
@files = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def files
|
|
14
|
+
@files.values
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def wait(timeout = 5)
|
|
18
|
+
@event.reset
|
|
19
|
+
yield if block_given?
|
|
20
|
+
@event.wait(timeout)
|
|
21
|
+
@event.set
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def set_behavior(save_path:, behavior: :allow)
|
|
25
|
+
raise ArgumentError unless VALID_BEHAVIOR.include?(behavior.to_sym)
|
|
26
|
+
raise Error, "supply absolute path for `:save_path` option" unless Pathname.new(save_path.to_s).absolute?
|
|
27
|
+
|
|
28
|
+
@page.command("Browser.setDownloadBehavior",
|
|
29
|
+
browserContextId: @page.context_id,
|
|
30
|
+
downloadPath: save_path,
|
|
31
|
+
behavior: behavior,
|
|
32
|
+
eventsEnabled: true)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def subscribe
|
|
36
|
+
subscribe_download_will_begin
|
|
37
|
+
subscribe_download_progress
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def subscribe_download_will_begin
|
|
41
|
+
@page.on("Browser.downloadWillBegin") do |params|
|
|
42
|
+
@event.reset
|
|
43
|
+
@files[params["guid"]] = params
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def subscribe_download_progress
|
|
48
|
+
@page.on("Browser.downloadProgress") do |params|
|
|
49
|
+
@files[params["guid"]].merge!(params)
|
|
50
|
+
|
|
51
|
+
case params["state"]
|
|
52
|
+
when "completed", "canceled"
|
|
53
|
+
@event.set
|
|
54
|
+
else
|
|
55
|
+
@event.reset
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/ferrum/errors.rb
CHANGED
|
@@ -6,7 +6,8 @@ module Ferrum
|
|
|
6
6
|
class NoSuchTargetError < Error; end
|
|
7
7
|
class NotImplementedError < Error; end
|
|
8
8
|
class BinaryNotFoundError < Error; end
|
|
9
|
-
class EmptyPathError
|
|
9
|
+
class EmptyPathError < Error; end
|
|
10
|
+
class ServerError < Error; end
|
|
10
11
|
|
|
11
12
|
class StatusError < Error
|
|
12
13
|
def initialize(url, message = nil)
|
data/lib/ferrum/headers.rb
CHANGED
|
@@ -68,7 +68,7 @@ module Ferrum
|
|
|
68
68
|
|
|
69
69
|
def set_overrides(user_agent: nil, accept_language: nil, platform: nil)
|
|
70
70
|
options = {}
|
|
71
|
-
options[:userAgent] = user_agent || @page.
|
|
71
|
+
options[:userAgent] = user_agent || @page.default_user_agent
|
|
72
72
|
options[:acceptLanguage] = accept_language if accept_language
|
|
73
73
|
options[:platform] if platform
|
|
74
74
|
|
|
@@ -79,7 +79,7 @@ module Ferrum
|
|
|
79
79
|
# @return [Boolean]
|
|
80
80
|
#
|
|
81
81
|
def finished?
|
|
82
|
-
blocked? || response&.loaded? || !error.nil?
|
|
82
|
+
blocked? || response&.loaded? || !error.nil? || ping?
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
#
|
|
@@ -118,6 +118,15 @@ module Ferrum
|
|
|
118
118
|
response&.redirect?
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
+
#
|
|
122
|
+
# Determines if the exchange is ping.
|
|
123
|
+
#
|
|
124
|
+
# @return [Boolean]
|
|
125
|
+
#
|
|
126
|
+
def ping?
|
|
127
|
+
!!request&.ping?
|
|
128
|
+
end
|
|
129
|
+
|
|
121
130
|
#
|
|
122
131
|
# Returns request's URL.
|
|
123
132
|
#
|
|
@@ -10,9 +10,9 @@ module Ferrum
|
|
|
10
10
|
|
|
11
11
|
attr_accessor :request_id, :frame_id, :resource_type, :network_id, :status
|
|
12
12
|
|
|
13
|
-
def initialize(
|
|
13
|
+
def initialize(client, params)
|
|
14
14
|
@status = nil
|
|
15
|
-
@
|
|
15
|
+
@client = client
|
|
16
16
|
@params = params
|
|
17
17
|
@request_id = params["requestId"]
|
|
18
18
|
@frame_id = params["frameId"]
|
|
@@ -43,18 +43,18 @@ module Ferrum
|
|
|
43
43
|
options = options.merge(body: Base64.strict_encode64(options.fetch(:body, ""))) if has_body
|
|
44
44
|
|
|
45
45
|
@status = :responded
|
|
46
|
-
@
|
|
46
|
+
@client.command("Fetch.fulfillRequest", async: true, **options)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def continue(**options)
|
|
50
50
|
options = options.merge(requestId: request_id)
|
|
51
51
|
@status = :continued
|
|
52
|
-
@
|
|
52
|
+
@client.command("Fetch.continueRequest", async: true, **options)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def abort
|
|
56
56
|
@status = :aborted
|
|
57
|
-
@
|
|
57
|
+
@client.command("Fetch.failRequest", async: true, requestId: request_id, errorReason: "BlockedByClient")
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def initial_priority
|
|
@@ -80,6 +80,15 @@ module Ferrum
|
|
|
80
80
|
@time ||= Time.strptime(@params["wallTime"].to_s, "%s")
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
#
|
|
84
|
+
# Determines if a request is of type ping.
|
|
85
|
+
#
|
|
86
|
+
# @return [Boolean]
|
|
87
|
+
#
|
|
88
|
+
def ping?
|
|
89
|
+
type?("ping")
|
|
90
|
+
end
|
|
91
|
+
|
|
83
92
|
#
|
|
84
93
|
# Converts the request to a Hash.
|
|
85
94
|
#
|
data/lib/ferrum/network.rb
CHANGED
|
@@ -59,7 +59,7 @@ module Ferrum
|
|
|
59
59
|
# browser.at_xpath("//a[text() = 'No UI changes button']").click
|
|
60
60
|
# browser.network.wait_for_idle
|
|
61
61
|
#
|
|
62
|
-
def wait_for_idle(connections: 0, duration: 0.05, timeout: @page.
|
|
62
|
+
def wait_for_idle(connections: 0, duration: 0.05, timeout: @page.timeout)
|
|
63
63
|
start = Utils::ElapsedTime.monotonic_time
|
|
64
64
|
|
|
65
65
|
until idle?(connections)
|
|
@@ -385,19 +385,19 @@ module Ferrum
|
|
|
385
385
|
def subscribe_response_received
|
|
386
386
|
@page.on("Network.responseReceived") do |params|
|
|
387
387
|
exchange = select(params["requestId"]).last
|
|
388
|
+
next unless exchange
|
|
388
389
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
exchange.response = response
|
|
392
|
-
end
|
|
390
|
+
response = Network::Response.new(@page, params)
|
|
391
|
+
exchange.response = response
|
|
393
392
|
end
|
|
394
393
|
end
|
|
395
394
|
|
|
396
395
|
def subscribe_loading_finished
|
|
397
396
|
@page.on("Network.loadingFinished") do |params|
|
|
398
|
-
|
|
397
|
+
exchange = select(params["requestId"]).last
|
|
398
|
+
next unless exchange
|
|
399
399
|
|
|
400
|
-
if response
|
|
400
|
+
if (response = exchange.response)
|
|
401
401
|
response.loaded = true
|
|
402
402
|
response.body_size = params["encodedDataLength"]
|
|
403
403
|
end
|
|
@@ -407,8 +407,9 @@ module Ferrum
|
|
|
407
407
|
def subscribe_loading_failed
|
|
408
408
|
@page.on("Network.loadingFailed") do |params|
|
|
409
409
|
exchange = select(params["requestId"]).last
|
|
410
|
-
|
|
410
|
+
next unless exchange
|
|
411
411
|
|
|
412
|
+
exchange.error ||= Network::Error.new
|
|
412
413
|
exchange.error.id = params["requestId"]
|
|
413
414
|
exchange.error.type = params["type"]
|
|
414
415
|
exchange.error.error_text = params["errorText"]
|
|
@@ -422,8 +423,9 @@ module Ferrum
|
|
|
422
423
|
entry = params["entry"] || {}
|
|
423
424
|
if entry["source"] == "network" && entry["level"] == "error"
|
|
424
425
|
exchange = select(entry["networkRequestId"]).last
|
|
425
|
-
|
|
426
|
+
next unless exchange
|
|
426
427
|
|
|
428
|
+
exchange.error ||= Network::Error.new
|
|
427
429
|
exchange.error.id = entry["networkRequestId"]
|
|
428
430
|
exchange.error.url = entry["url"]
|
|
429
431
|
exchange.error.description = entry["text"]
|
data/lib/ferrum/page/frames.rb
CHANGED
|
@@ -16,8 +16,8 @@ module Ferrum
|
|
|
16
16
|
# @return [Array<Frame>]
|
|
17
17
|
#
|
|
18
18
|
# @example
|
|
19
|
-
#
|
|
20
|
-
#
|
|
19
|
+
# page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
20
|
+
# page.frames # =>
|
|
21
21
|
# # [
|
|
22
22
|
# # #<Ferrum::Frame
|
|
23
23
|
# # @id="C6D104CE454A025FBCF22B98DE612B12"
|
|
@@ -39,7 +39,7 @@ module Ferrum
|
|
|
39
39
|
# Find frame by given options.
|
|
40
40
|
#
|
|
41
41
|
# @param [String] id
|
|
42
|
-
# Unique frame's id that
|
|
42
|
+
# Unique frame's id that page provides.
|
|
43
43
|
#
|
|
44
44
|
# @param [String] name
|
|
45
45
|
# Frame's name if there's one.
|
|
@@ -51,7 +51,7 @@ module Ferrum
|
|
|
51
51
|
# The matching frame.
|
|
52
52
|
#
|
|
53
53
|
# @example
|
|
54
|
-
#
|
|
54
|
+
# page.frame_by(id: "C6D104CE454A025FBCF22B98DE612B12")
|
|
55
55
|
#
|
|
56
56
|
def frame_by(id: nil, name: nil, execution_id: nil)
|
|
57
57
|
if id
|
|
@@ -134,7 +134,7 @@ module Ferrum
|
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
frame = @frames[params["frameId"]]
|
|
137
|
-
frame
|
|
137
|
+
frame&.state = :stopped_loading
|
|
138
138
|
|
|
139
139
|
@event.set if idling?
|
|
140
140
|
end
|