Class: Ferrum::Page

Inherits:
Object
  • Object
show all
Includes:
DOM, Frame, Input, Net, Runtime, Screenshot
Defined in:
lib/ferrum/page.rb,
lib/ferrum/page/dom.rb,
lib/ferrum/page/net.rb,
lib/ferrum/page/frame.rb,
lib/ferrum/page/input.rb,
lib/ferrum/page/runtime.rb,
lib/ferrum/page/screenshot.rb

Defined Under Namespace

Modules: DOM, Frame, Input, Net, Runtime, Screenshot

Constant Summary collapse

NEW_WINDOW_BUG_SLEEP =
0.3

Constants included from Runtime

Runtime::DEFAULT_OPTIONS, Runtime::EVALUATE_ASYNC_OPTIONS, Runtime::EXECUTE_OPTIONS

Constants included from Net

Net::RESOURCE_TYPES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Input

#find_position, #scroll_to

Methods included from DOM

#at_css, #at_xpath, #body, #css, #current_url, #title, #xpath

Methods included from Runtime

#evaluate, #evaluate_async, #evaluate_on, #execute

Methods included from Frame

#execution_context_id, #frame_name, #frame_title, #frame_url, #within_frame

Methods included from Net

#abort_request, #authorize, #continue_request, #intercept_request, #on_request_intercepted, #proxy_authorize

Methods included from Screenshot

#pdf, #screenshot

Constructor Details

#initialize(target_id, browser, new_window = false) ⇒ Page

Returns a new instance of Page.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/ferrum/page.rb', line 48

def initialize(target_id, browser, new_window = false)
  @target_id, @browser = target_id, browser
  @network_traffic = []
  @event = Concurrent::Event.new.tap(&:set)

  @frames = {}
  @waiting_frames ||= Set.new
  @frame_stack = []
  @accept_modal = []
  @modal_messages = []

  # Dirty hack because new window doesn't have events at all
  sleep(NEW_WINDOW_BUG_SLEEP) if new_window

  @session_id = @browser.command("Target.attachToTarget", targetId: @target_id)["sessionId"]

  host = @browser.process.host
  port = @browser.process.port
  ws_url = "ws://#{host}:#{port}/devtools/page/#{@target_id}"
  @client = Browser::Client.new(browser, ws_url, 1000)

  @mouse, @keyboard = Mouse.new(self), Keyboard.new(self)
  @headers, @cookies = Headers.new(self), Cookies.new(self)

  subscribe
  prepare_page
end

Instance Attribute Details

#browserObject (readonly)

Returns the value of attribute browser.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def browser
  @browser
end

#cookiesObject (readonly)

Returns the value of attribute cookies.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def cookies
  @cookies
end

#headersObject (readonly)

Returns the value of attribute headers.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def headers
  @headers
end

#keyboardObject (readonly)

Returns the value of attribute keyboard.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def keyboard
  @keyboard
end

#mouseObject (readonly)

Returns the value of attribute mouse.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def mouse
  @mouse
end

#referrerObject

Returns the value of attribute referrer.



42
43
44
# File 'lib/ferrum/page.rb', line 42

def referrer
  @referrer
end

#response_headersObject (readonly)

Returns the value of attribute response_headers.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def response_headers
  @response_headers
end

#statusObject (readonly)

Returns the value of attribute status.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def status
  @status
end

#target_idObject (readonly)

Returns the value of attribute target_id.



43
44
45
# File 'lib/ferrum/page.rb', line 43

def target_id
  @target_id
end

Instance Method Details

#accept_confirmObject



145
146
147
# File 'lib/ferrum/page.rb', line 145

def accept_confirm
  @accept_modal << true
end

#accept_prompt(modal_response) ⇒ Object



153
154
155
156
# File 'lib/ferrum/page.rb', line 153

def accept_prompt(modal_response)
  @accept_modal << true
  @modal_response = modal_response
end

#backObject



137
138
139
# File 'lib/ferrum/page.rb', line 137

def back
  history_navigate(delta: -1)
end

#clear_network_trafficObject



133
134
135
# File 'lib/ferrum/page.rb', line 133

def clear_network_traffic
  @network_traffic = []
end

#closeObject



94
95
96
97
98
99
# File 'lib/ferrum/page.rb', line 94

def close
  @headers.clear
  @browser.command("Target.detachFromTarget", sessionId: @session_id)
  @browser.command("Target.closeTarget", targetId: @target_id)
  close_connection
end

#close_connectionObject



101
102
103
# File 'lib/ferrum/page.rb', line 101

def close_connection
  @client.close
end

#command(method, timeout: 0, **params) ⇒ Object



188
189
190
191
192
193
194
195
196
197
# File 'lib/ferrum/page.rb', line 188

def command(method, timeout: 0, **params)
  result = @client.command(method, params)

  if timeout > 0
    @event.reset
    @event.wait(timeout)
  end

  result
end

#dismiss_confirmObject



149
150
151
# File 'lib/ferrum/page.rb', line 149

def dismiss_confirm
  @accept_modal << false
end

#dismiss_promptObject



158
159
160
# File 'lib/ferrum/page.rb', line 158

def dismiss_prompt
  @accept_modal << false
end

#find_modal(options) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/ferrum/page.rb', line 162

def find_modal(options)
  start_time    = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
  timeout_sec   = options.fetch(:wait) { session_wait_time }
  expect_text   = options[:text]
  expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
  not_found_msg = "Unable to find modal dialog"
  not_found_msg += " with #{expect_text}" if expect_text

  begin
    modal_text = @modal_messages.shift
    raise ModalNotFoundError if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
  rescue ModalNotFoundError => e
    raise e, not_found_msg if (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start_time) >= timeout_sec
    sleep(0.05)
    retry
  end

  modal_text
end

#forwardObject



141
142
143
# File 'lib/ferrum/page.rb', line 141

def forward
  history_navigate(delta: 1)
end

#goto(url = nil) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ferrum/page.rb', line 80

def goto(url = nil)
  options = { url: combine_url!(url) }
  options.merge!(referrer: referrer) if referrer
  response = command("Page.navigate", timeout: timeout, **options)
  # https://cs.chromium.org/chromium/src/net/base/net_error_list.h
  if %w[net::ERR_NAME_NOT_RESOLVED
        net::ERR_NAME_RESOLUTION_FAILED
        net::ERR_INTERNET_DISCONNECTED
        net::ERR_CONNECTION_TIMED_OUT].include?(response["errorText"])
    raise StatusError, options[:url]
  end
  response["frameId"]
end

#network_traffic(type = nil) ⇒ Object



122
123
124
125
126
127
128
129
130
131
# File 'lib/ferrum/page.rb', line 122

def network_traffic(type = nil)
  case type.to_s
  when "all"
    @network_traffic
  when "blocked"
    @network_traffic.select { |r| r.response.nil? } # when request blocked
  else
    @network_traffic.select { |r| r.response } # when request isn't blocked
  end
end

#refreshObject



118
119
120
# File 'lib/ferrum/page.rb', line 118

def refresh
  command("Page.reload", timeout: timeout)
end

#reset_modalsObject



182
183
184
185
186
# File 'lib/ferrum/page.rb', line 182

def reset_modals
  @accept_modal = []
  @modal_response = nil
  @modal_messages = []
end

#resize(width: nil, height: nil, fullscreen: false) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/ferrum/page.rb', line 105

def resize(width: nil, height: nil, fullscreen: false)
  result = @browser.command("Browser.getWindowForTarget", targetId: @target_id)
  @window_id, @bounds = result.values_at("windowId", "bounds")

  if fullscreen
    @browser.command("Browser.setWindowBounds", windowId: @window_id, bounds: { windowState: "fullscreen" })
  else
    @browser.command("Browser.setWindowBounds", windowId: @window_id, bounds: { windowState: "normal" })
    @browser.command("Browser.setWindowBounds", windowId: @window_id, bounds: { width: width, height: height, windowState: "normal" })
    command("Emulation.setDeviceMetricsOverride", width: width, height: height, deviceScaleFactor: 1, mobile: false)
  end
end

#timeoutObject



76
77
78
# File 'lib/ferrum/page.rb', line 76

def timeout
  @browser.timeout
end