ferrum 0.10.1 → 0.12

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +261 -28
  4. data/lib/ferrum/browser/binary.rb +46 -0
  5. data/lib/ferrum/browser/client.rb +15 -12
  6. data/lib/ferrum/browser/command.rb +7 -8
  7. data/lib/ferrum/browser/options/base.rb +1 -7
  8. data/lib/ferrum/browser/options/chrome.rb +17 -10
  9. data/lib/ferrum/browser/options/firefox.rb +11 -4
  10. data/lib/ferrum/browser/process.rb +41 -35
  11. data/lib/ferrum/browser/subscriber.rb +1 -3
  12. data/lib/ferrum/browser/web_socket.rb +9 -12
  13. data/lib/ferrum/browser/xvfb.rb +4 -8
  14. data/lib/ferrum/browser.rb +49 -12
  15. data/lib/ferrum/context.rb +12 -4
  16. data/lib/ferrum/contexts.rb +13 -9
  17. data/lib/ferrum/cookies.rb +10 -9
  18. data/lib/ferrum/errors.rb +115 -0
  19. data/lib/ferrum/frame/runtime.rb +20 -17
  20. data/lib/ferrum/frame.rb +32 -24
  21. data/lib/ferrum/headers.rb +2 -2
  22. data/lib/ferrum/keyboard.rb +11 -11
  23. data/lib/ferrum/mouse.rb +8 -7
  24. data/lib/ferrum/network/auth_request.rb +7 -2
  25. data/lib/ferrum/network/exchange.rb +14 -10
  26. data/lib/ferrum/network/intercepted_request.rb +10 -8
  27. data/lib/ferrum/network/request.rb +5 -0
  28. data/lib/ferrum/network/response.rb +4 -4
  29. data/lib/ferrum/network.rb +124 -35
  30. data/lib/ferrum/node.rb +98 -40
  31. data/lib/ferrum/page/animation.rb +15 -0
  32. data/lib/ferrum/page/frames.rb +46 -15
  33. data/lib/ferrum/page/screenshot.rb +53 -67
  34. data/lib/ferrum/page/stream.rb +38 -0
  35. data/lib/ferrum/page/tracing.rb +71 -0
  36. data/lib/ferrum/page.rb +88 -34
  37. data/lib/ferrum/proxy.rb +58 -0
  38. data/lib/ferrum/{rbga.rb → rgba.rb} +4 -2
  39. data/lib/ferrum/target.rb +1 -0
  40. data/lib/ferrum/utils/attempt.rb +20 -0
  41. data/lib/ferrum/utils/elapsed_time.rb +27 -0
  42. data/lib/ferrum/utils/platform.rb +28 -0
  43. data/lib/ferrum/version.rb +1 -1
  44. data/lib/ferrum.rb +4 -140
  45. metadata +65 -50
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ferrum
4
+ class Error < StandardError; end
5
+ class NoSuchPageError < Error; end
6
+ class NoSuchTargetError < Error; end
7
+ class NotImplementedError < Error; end
8
+ class BinaryNotFoundError < Error; end
9
+ class EmptyPathError < Error; end
10
+
11
+ class StatusError < Error
12
+ def initialize(url, message = nil)
13
+ super(message || "Request to #{url} failed to reach server, check DNS and server status")
14
+ end
15
+ end
16
+
17
+ class PendingConnectionsError < StatusError
18
+ attr_reader :pendings
19
+
20
+ def initialize(url, pendings = [])
21
+ @pendings = pendings
22
+
23
+ message = "Request to #{url} reached server, but there are still pending connections: #{pendings.join(', ')}"
24
+
25
+ super(url, message)
26
+ end
27
+ end
28
+
29
+ class TimeoutError < Error
30
+ def message
31
+ "Timed out waiting for response. It's possible that this happened " \
32
+ "because something took a very long time (for example a page load " \
33
+ "was slow). If so, setting the :timeout option to a higher value might " \
34
+ "help."
35
+ end
36
+ end
37
+
38
+ class ScriptTimeoutError < Error
39
+ def message
40
+ "Timed out waiting for evaluated script to return a value"
41
+ end
42
+ end
43
+
44
+ class ProcessTimeoutError < Error
45
+ attr_reader :output
46
+
47
+ def initialize(timeout, output)
48
+ @output = output
49
+ super("Browser did not produce websocket url within #{timeout} seconds, try to increase `:process_timeout`. See https://github.com/rubycdp/ferrum#customization")
50
+ end
51
+ end
52
+
53
+ class DeadBrowserError < Error
54
+ def initialize(message = "Browser is dead or given window is closed")
55
+ super
56
+ end
57
+ end
58
+
59
+ class NodeMovingError < Error
60
+ def initialize(node, prev, current)
61
+ @node = node
62
+ @prev = prev
63
+ @current = current
64
+ super(message)
65
+ end
66
+
67
+ def message
68
+ "#{@node.inspect} that you're trying to click is moving, hence " \
69
+ "we cannot. Previously it was at #{@prev.inspect} but now at " \
70
+ "#{@current.inspect}."
71
+ end
72
+ end
73
+
74
+ class CoordinatesNotFoundError < Error
75
+ def initialize(message = "Could not compute content quads")
76
+ super
77
+ end
78
+ end
79
+
80
+ class BrowserError < Error
81
+ attr_reader :response
82
+
83
+ def initialize(response)
84
+ @response = response
85
+ super(response["message"])
86
+ end
87
+
88
+ def code
89
+ response["code"]
90
+ end
91
+
92
+ def data
93
+ response["data"]
94
+ end
95
+ end
96
+
97
+ class NodeNotFoundError < BrowserError; end
98
+
99
+ class NoExecutionContextError < BrowserError
100
+ def initialize(response = nil)
101
+ response ||= { "message" => "There's no context available" }
102
+ super(response)
103
+ end
104
+ end
105
+
106
+ class JavaScriptError < BrowserError
107
+ attr_reader :class_name, :message, :stack_trace
108
+
109
+ def initialize(response, stack_trace = nil)
110
+ @class_name, @message = response.values_at("className", "description")
111
+ @stack_trace = stack_trace
112
+ super(response.merge("message" => @message))
113
+ end
114
+ end
115
+ end
@@ -46,7 +46,7 @@ module Ferrum
46
46
  JS
47
47
 
48
48
  def evaluate(expression, *args)
49
- expression = "function() { return %s }" % expression
49
+ expression = format("function() { return %s }", expression)
50
50
  call(expression: expression, arguments: args)
51
51
  end
52
52
 
@@ -66,12 +66,12 @@ module Ferrum
66
66
  }
67
67
  JS
68
68
 
69
- expression = template % [wait * 1000, expression]
69
+ expression = format(template, wait * 1000, expression)
70
70
  call(expression: expression, arguments: args, awaitPromise: true)
71
71
  end
72
72
 
73
73
  def execute(expression, *args)
74
- expression = "function() { %s }" % expression
74
+ expression = format("function() { %s }", expression)
75
75
  call(expression: expression, arguments: args, handle: false, returnByValue: true)
76
76
  true
77
77
  end
@@ -82,7 +82,7 @@ module Ferrum
82
82
 
83
83
  def evaluate_on(node:, expression:, by_value: true, wait: 0)
84
84
  options = { handle: true }
85
- expression = "function() { return %s }" % expression
85
+ expression = format("function() { return %s }", expression)
86
86
  options = { handle: false, returnByValue: true } if by_value
87
87
  call(expression: expression, on: node, wait: wait, **options)
88
88
  end
@@ -119,9 +119,10 @@ module Ferrum
119
119
 
120
120
  def call(expression:, arguments: [], on: nil, wait: 0, handle: true, **options)
121
121
  errors = [NodeNotFoundError, NoExecutionContextError]
122
- attempts, sleep = INTERMITTENT_ATTEMPTS, INTERMITTENT_SLEEP
122
+ sleep = INTERMITTENT_SLEEP
123
+ attempts = INTERMITTENT_ATTEMPTS
123
124
 
124
- Ferrum.with_attempts(errors: errors, max: attempts, wait: sleep) do
125
+ Utils::Attempt.with_retry(errors: errors, max: attempts, wait: sleep) do
125
126
  params = options.dup
126
127
 
127
128
  if on
@@ -141,7 +142,7 @@ module Ferrum
141
142
  handle_error(response)
142
143
  response = response["result"]
143
144
 
144
- handle ? handle_response(response) : response.dig("value")
145
+ handle ? handle_response(response) : response["value"]
145
146
  end
146
147
  end
147
148
 
@@ -154,7 +155,7 @@ module Ferrum
154
155
  when /\AError: timed out promise/
155
156
  raise ScriptTimeoutError
156
157
  else
157
- raise JavaScriptError.new(result)
158
+ raise JavaScriptError.new(result, response.dig("exceptionDetails", "stackTrace"))
158
159
  end
159
160
  end
160
161
 
@@ -171,16 +172,17 @@ module Ferrum
171
172
 
172
173
  case response["subtype"]
173
174
  when "node"
174
- # We cannot store object_id in the node because page can be reloaded
175
- # and node destroyed so we need to retrieve it each time for given id.
176
- # Though we can try to subscribe to `DOM.childNodeRemoved` and
177
- # `DOM.childNodeInserted` in the future.
178
- node_id = @page.command("DOM.requestNode", objectId: object_id)["nodeId"]
179
- description = @page.command("DOM.describeNode", nodeId: node_id)["node"]
180
- Node.new(self, @page.target_id, node_id, description)
175
+ # We cannot store object_id in the node because page can be reloaded
176
+ # and node destroyed so we need to retrieve it each time for given id.
177
+ # Though we can try to subscribe to `DOM.childNodeRemoved` and
178
+ # `DOM.childNodeInserted` in the future.
179
+ node_id = @page.command("DOM.requestNode", objectId: object_id)["nodeId"]
180
+ description = @page.command("DOM.describeNode", nodeId: node_id)["node"]
181
+ Node.new(self, @page.target_id, node_id, description)
181
182
  when "array"
182
183
  reduce_props(object_id, []) do |memo, key, value|
183
- next(memo) unless (Integer(key) rescue nil)
184
+ next(memo) unless Integer(key, exception: false)
185
+
184
186
  value = value["objectId"] ? handle_response(value) : value["value"]
185
187
  memo.insert(key.to_i, value)
186
188
  end.compact
@@ -212,11 +214,12 @@ module Ferrum
212
214
 
213
215
  def reduce_props(object_id, to)
214
216
  if cyclic?(object_id).dig("result", "value")
215
- return to.is_a?(Array) ? [cyclic_object] : cyclic_object
217
+ to.is_a?(Array) ? [cyclic_object] : cyclic_object
216
218
  else
217
219
  props = @page.command("Runtime.getProperties", ownProperties: true, objectId: object_id)
218
220
  props["result"].reduce(to) do |memo, prop|
219
221
  next(memo) unless prop["enumerable"]
222
+
220
223
  yield(memo, prop["name"], prop["value"])
221
224
  end
222
225
  end
data/lib/ferrum/frame.rb CHANGED
@@ -5,21 +5,28 @@ require "ferrum/frame/runtime"
5
5
 
6
6
  module Ferrum
7
7
  class Frame
8
- include DOM, Runtime
8
+ include DOM
9
+ include Runtime
10
+
11
+ STATE_VALUES = %i[
12
+ started_loading
13
+ navigated
14
+ stopped_loading
15
+ ].freeze
9
16
 
10
- attr_reader :page, :parent_id, :state
11
17
  attr_accessor :id, :name
18
+ attr_reader :page, :parent_id, :state
12
19
 
13
20
  def initialize(id, page, parent_id = nil)
14
- @execution_id = nil
15
- @id, @page, @parent_id = id, page, parent_id
21
+ @id = id
22
+ @page = page
23
+ @parent_id = parent_id
24
+ @execution_id = Concurrent::MVar.new
16
25
  end
17
26
 
18
- # Can be one of:
19
- # * started_loading
20
- # * navigated
21
- # * stopped_loading
22
27
  def state=(value)
28
+ raise ArgumentError unless STATE_VALUES.include?(value)
29
+
23
30
  @state = value
24
31
  end
25
32
 
@@ -35,7 +42,7 @@ module Ferrum
35
42
  @parent_id.nil?
36
43
  end
37
44
 
38
- def set_content(html)
45
+ def content=(html)
39
46
  evaluate_async(%(
40
47
  document.open();
41
48
  document.write(arguments[0]);
@@ -43,29 +50,30 @@ module Ferrum
43
50
  arguments[1](true);
44
51
  ), @page.timeout, html)
45
52
  end
46
-
47
- def execution_id?(execution_id)
48
- @execution_id == execution_id
49
- end
53
+ alias set_content content=
50
54
 
51
55
  def execution_id
52
- raise NoExecutionContextError unless @execution_id
53
- @execution_id
54
- rescue NoExecutionContextError
55
- @page.event.reset
56
- @page.event.wait(@page.timeout) ? retry : raise
57
- end
56
+ value = @execution_id.borrow(@page.timeout, &:itself)
57
+ raise NoExecutionContextError if value.instance_of?(Object)
58
58
 
59
- def set_execution_id(value)
60
- @execution_id ||= value
59
+ value
61
60
  end
62
61
 
63
- def reset_execution_id
64
- @execution_id = nil
62
+ def execution_id=(value)
63
+ if value.nil?
64
+ @execution_id.try_take!
65
+ else
66
+ @execution_id.try_put!(value)
67
+ end
65
68
  end
66
69
 
67
70
  def inspect
68
- %(#<#{self.class} @id=#{@id.inspect} @parent_id=#{@parent_id.inspect} @name=#{@name.inspect} @state=#{@state.inspect} @execution_id=#{@execution_id.inspect}>)
71
+ "#<#{self.class} " \
72
+ "@id=#{@id.inspect} " \
73
+ "@parent_id=#{@parent_id.inspect} " \
74
+ "@name=#{@name.inspect} " \
75
+ "@state=#{@state.inspect} " \
76
+ "@execution_id=#{@execution_id.inspect}>"
69
77
  end
70
78
  end
71
79
  end
@@ -39,12 +39,12 @@ module Ferrum
39
39
  private
40
40
 
41
41
  def set_overrides(user_agent: nil, accept_language: nil, platform: nil)
42
- options = Hash.new
42
+ options = {}
43
43
  options[:userAgent] = user_agent || @page.browser.default_user_agent
44
44
  options[:acceptLanguage] = accept_language if accept_language
45
45
  options[:platform] if platform
46
46
 
47
- @page.command("Network.setUserAgentOverride", **options) if !options.empty?
47
+ @page.command("Network.setUserAgentOverride", **options) unless options.empty?
48
48
  end
49
49
  end
50
50
  end
@@ -4,14 +4,14 @@ require "json"
4
4
 
5
5
  module Ferrum
6
6
  class Keyboard
7
- KEYS = JSON.parse(File.read(File.expand_path("../keyboard.json", __FILE__)))
7
+ KEYS = JSON.parse(File.read(File.expand_path("keyboard.json", __dir__)))
8
8
  MODIFIERS = { "alt" => 1, "ctrl" => 2, "control" => 2,
9
- "meta" => 4, "command" => 4, "shift" => 8 }
9
+ "meta" => 4, "command" => 4, "shift" => 8 }.freeze
10
10
  KEYS_MAPPING = {
11
11
  cancel: "Cancel", help: "Help", backspace: "Backspace", tab: "Tab",
12
12
  clear: "Clear", return: "Enter", enter: "Enter", shift: "Shift",
13
13
  ctrl: "Control", control: "Control", alt: "Alt", pause: "Pause",
14
- escape: "Escape", space: "Space", pageup: "PageUp", page_up: "PageUp",
14
+ escape: "Escape", space: "Space", pageup: "PageUp", page_up: "PageUp",
15
15
  pagedown: "PageDown", page_down: "PageDown", end: "End", home: "Home",
16
16
  left: "ArrowLeft", up: "ArrowUp", right: "ArrowRight",
17
17
  down: "ArrowDown", insert: "Insert", delete: "Delete",
@@ -23,8 +23,8 @@ module Ferrum
23
23
  separator: "NumpadDecimal", subtract: "NumpadSubtract",
24
24
  decimal: "NumpadDecimal", divide: "NumpadDivide", f1: "F1", f2: "F2",
25
25
  f3: "F3", f4: "F4", f5: "F5", f6: "F6", f7: "F7", f8: "F8", f9: "F9",
26
- f10: "F10", f11: "F11", f12: "F12", meta: "Meta", command: "Meta",
27
- }
26
+ f10: "F10", f11: "F11", f12: "F12", meta: "Meta", command: "Meta"
27
+ }.freeze
28
28
 
29
29
  def initialize(page)
30
30
  @page = page
@@ -77,25 +77,25 @@ module Ferrum
77
77
  pressed_keys.last.push(key)
78
78
  nil
79
79
  else
80
- _key = KEYS.fetch(KEYS_MAPPING[key.to_sym] || key.to_sym)
81
- _key[:modifiers] = pressed_keys.flatten.map { |k| MODIFIERS[k] }.reduce(0, :|)
82
- to_options(_key)
80
+ key = KEYS.fetch(KEYS_MAPPING[key.to_sym] || key.to_sym)
81
+ key[:modifiers] = pressed_keys.flatten.map { |k| MODIFIERS[k] }.reduce(0, :|)
82
+ to_options(key)
83
83
  end
84
84
  when String
85
85
  pressed = pressed_keys.flatten
86
86
  keys.each_char.map do |char|
87
+ key = KEYS[char] || {}
88
+
87
89
  if pressed.empty?
88
- key = KEYS[char] || {}
89
90
  key = key.merge(text: char, unmodifiedText: char)
90
91
  [to_options(key)]
91
92
  else
92
- key = KEYS[char] || {}
93
93
  text = pressed == ["shift"] ? char.upcase : char
94
94
  key = key.merge(
95
95
  text: text,
96
96
  unmodifiedText: text,
97
97
  isKeypad: key["location"] == 3,
98
- modifiers: pressed.map { |k| MODIFIERS[k] }.reduce(0, :|),
98
+ modifiers: pressed.map { |k| MODIFIERS[k] }.reduce(0, :|)
99
99
  )
100
100
 
101
101
  modifiers = pressed.map { |k| to_options(KEYS.fetch(KEYS_MAPPING[k.to_sym])) }
data/lib/ferrum/mouse.rb CHANGED
@@ -33,12 +33,14 @@ module Ferrum
33
33
  end
34
34
 
35
35
  def move(x:, y:, steps: 1)
36
- from_x, from_y = @x, @y
37
- @x, @y = x, y
36
+ from_x = @x
37
+ from_y = @y
38
+ @x = x
39
+ @y = y
38
40
 
39
41
  steps.times do |i|
40
- new_x = from_x + (@x - from_x) * ((i + 1) / steps.to_f)
41
- new_y = from_y + (@y - from_y) * ((i + 1) / steps.to_f)
42
+ new_x = from_x + ((@x - from_x) * ((i + 1) / steps.to_f))
43
+ new_y = from_y + ((@y - from_y) * ((i + 1) / steps.to_f))
42
44
 
43
45
  @page.command("Input.dispatchMouseEvent",
44
46
  slowmoable: true,
@@ -61,9 +63,8 @@ module Ferrum
61
63
 
62
64
  def validate_button(button)
63
65
  button = button.to_s
64
- unless VALID_BUTTONS.include?(button)
65
- raise "Invalid button: #{button}"
66
- end
66
+ raise "Invalid button: #{button}" unless VALID_BUTTONS.include?(button)
67
+
67
68
  button
68
69
  end
69
70
  end
@@ -6,7 +6,8 @@ module Ferrum
6
6
  attr_accessor :request_id, :frame_id, :resource_type
7
7
 
8
8
  def initialize(page, params)
9
- @page, @params = page, params
9
+ @page = page
10
+ @params = params
10
11
  @request_id = params["requestId"]
11
12
  @frame_id = params["frameId"]
12
13
  @resource_type = params["resourceType"]
@@ -55,7 +56,11 @@ module Ferrum
55
56
  end
56
57
 
57
58
  def inspect
58
- %(#<#{self.class} @request_id=#{@request_id.inspect} @frame_id=#{@frame_id.inspect} @resource_type=#{@resource_type.inspect} @request=#{@request.inspect}>)
59
+ "#<#{self.class} " \
60
+ "@request_id=#{@request_id.inspect} " \
61
+ "@frame_id=#{@frame_id.inspect} " \
62
+ "@resource_type=#{@resource_type.inspect} " \
63
+ "@request=#{@request.inspect}>"
59
64
  end
60
65
  end
61
66
  end
@@ -4,11 +4,11 @@ module Ferrum
4
4
  class Network
5
5
  class Exchange
6
6
  attr_reader :id
7
- attr_accessor :intercepted_request
8
- attr_accessor :request, :response, :error
7
+ attr_accessor :intercepted_request, :request, :response, :error
9
8
 
10
9
  def initialize(page, id)
11
- @page, @id = page, id
10
+ @id = id
11
+ @page = page
12
12
  @intercepted_request = nil
13
13
  @request = @response = @error = nil
14
14
  end
@@ -23,7 +23,7 @@ module Ferrum
23
23
  end
24
24
 
25
25
  def blocked?
26
- intercepted_request && intercepted_request.status?(:aborted)
26
+ intercepted? && intercepted_request.status?(:aborted)
27
27
  end
28
28
 
29
29
  def finished?
@@ -34,17 +34,21 @@ module Ferrum
34
34
  !finished?
35
35
  end
36
36
 
37
+ def intercepted?
38
+ intercepted_request
39
+ end
40
+
37
41
  def to_a
38
42
  [request, response, error]
39
43
  end
40
44
 
41
45
  def inspect
42
- "#<#{self.class} "\
43
- "@id=#{@id.inspect} "\
44
- "@intercepted_request=#{@intercepted_request.inspect} "\
45
- "@request=#{@request.inspect} "\
46
- "@response=#{@response.inspect} "\
47
- "@error=#{@error.inspect}>"
46
+ "#<#{self.class} " \
47
+ "@id=#{@id.inspect} " \
48
+ "@intercepted_request=#{@intercepted_request.inspect} " \
49
+ "@request=#{@request.inspect} " \
50
+ "@response=#{@response.inspect} " \
51
+ "@error=#{@error.inspect}>"
48
52
  end
49
53
  end
50
54
  end
@@ -9,7 +9,8 @@ module Ferrum
9
9
 
10
10
  def initialize(page, params)
11
11
  @status = nil
12
- @page, @params = page, params
12
+ @page = page
13
+ @params = params
13
14
  @request_id = params["requestId"]
14
15
  @frame_id = params["frameId"]
15
16
  @resource_type = params["resourceType"]
@@ -30,15 +31,12 @@ module Ferrum
30
31
  end
31
32
 
32
33
  def respond(**options)
33
- has_body = options.has_key?(:body)
34
+ has_body = options.key?(:body)
34
35
  headers = has_body ? { "content-length" => options.fetch(:body, "").length } : {}
35
36
  headers = headers.merge(options.fetch(:responseHeaders, {}))
36
37
 
37
- options = {responseCode: 200}.merge(options)
38
- options = options.merge({
39
- requestId: request_id,
40
- responseHeaders: header_array(headers),
41
- })
38
+ options = { responseCode: 200 }.merge(options)
39
+ options = options.merge(requestId: request_id, responseHeaders: header_array(headers))
42
40
  options = options.merge(body: Base64.strict_encode64(options.fetch(:body, ""))) if has_body
43
41
 
44
42
  @status = :responded
@@ -77,7 +75,11 @@ module Ferrum
77
75
  end
78
76
 
79
77
  def inspect
80
- %(#<#{self.class} @request_id=#{@request_id.inspect} @frame_id=#{@frame_id.inspect} @resource_type=#{@resource_type.inspect} @request=#{@request.inspect}>)
78
+ "#<#{self.class} " \
79
+ "@request_id=#{@request_id.inspect} " \
80
+ "@frame_id=#{@frame_id.inspect} " \
81
+ "@resource_type=#{@resource_type.inspect} " \
82
+ "@request=#{@request.inspect}>"
81
83
  end
82
84
 
83
85
  private
@@ -45,6 +45,11 @@ module Ferrum
45
45
  def time
46
46
  @time ||= Time.strptime(@params["wallTime"].to_s, "%s")
47
47
  end
48
+
49
+ def post_data
50
+ @request["postData"]
51
+ end
52
+ alias body post_data
48
53
  end
49
54
  end
50
55
  end
@@ -3,7 +3,7 @@
3
3
  module Ferrum
4
4
  class Network
5
5
  class Response
6
- attr_reader :body_size
6
+ attr_reader :body_size, :params
7
7
 
8
8
  def initialize(page, params)
9
9
  @page = page
@@ -34,7 +34,7 @@ module Ferrum
34
34
  def headers_size
35
35
  @response["encodedDataLength"]
36
36
  end
37
-
37
+
38
38
  def type
39
39
  @params["type"]
40
40
  end
@@ -55,8 +55,8 @@ module Ferrum
55
55
  def body
56
56
  @body ||= begin
57
57
  body, encoded = @page
58
- .command("Network.getResponseBody", requestId: id)
59
- .values_at("body", "base64Encoded")
58
+ .command("Network.getResponseBody", requestId: id)
59
+ .values_at("body", "base64Encoded")
60
60
  encoded ? Base64.decode64(body) : body
61
61
  end
62
62
  end