Class: Ferrum::Page

Inherits:
Object
  • Object
show all
Includes:
DOM, Frame, Input, Net, Runtime
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

Defined Under Namespace

Modules: DOM, Frame, Input, Net, Runtime

Constant Summary collapse

NEW_WINDOW_BUG_SLEEP =
0.3

Constants included from Input

Input::KEYS, Input::KEYS_MAPPING, Input::MODIFIERS

Constants included from Runtime

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Input

#click, #click_coordinates, #combine_strings, #double_click, #focus, #hover, #normalize_keys, #right_click, #scroll_to, #select, #send_keys, #set, #trigger

Methods included from DOM

#at_css, #at_xpath, #body, #css, #current_url, #property, #select_file, #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, #switch_to_frame

Methods included from Net

#authorize, #continue_request, #intercept_request, #proxy_authorize

Constructor Details

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

Returns a new instance of Page.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/ferrum/page.rb', line 39

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

  begin
    @session_id = @browser.command("Target.attachToTarget", targetId: @target_id)["sessionId"]
  rescue BrowserError => e
    case e.message
    when "No target with given id found"
      raise NoSuchWindowError
    else
      raise
    end
  end

  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)

  subscribe
  prepare_page
end

Instance Attribute Details

#referrerObject

Returns the value of attribute referrer.



36
37
38
# File 'lib/ferrum/page.rb', line 36

def referrer
  @referrer
end

#response_headersObject (readonly)

Returns the value of attribute response_headers.



37
38
39
# File 'lib/ferrum/page.rb', line 37

def response_headers
  @response_headers
end

#statusObject (readonly)

Returns the value of attribute status.



37
38
39
# File 'lib/ferrum/page.rb', line 37

def status
  @status
end

#target_idObject (readonly)

Returns the value of attribute target_id.



37
38
39
# File 'lib/ferrum/page.rb', line 37

def target_id
  @target_id
end

Instance Method Details

#accept_confirmObject



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

def accept_confirm
  @accept_modal << true
end

#accept_prompt(modal_response) ⇒ Object



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

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

#clear_network_trafficObject



129
130
131
# File 'lib/ferrum/page.rb', line 129

def clear_network_traffic
  @network_traffic = []
end

#closeObject



91
92
93
94
95
# File 'lib/ferrum/page.rb', line 91

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

#close_connectionObject



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

def close_connection
  @client.close
end

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



184
185
186
187
188
189
190
191
192
193
# File 'lib/ferrum/page.rb', line 184

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

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

  result
end

#dismiss_confirmObject



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

def dismiss_confirm
  @accept_modal << false
end

#dismiss_promptObject



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

def dismiss_prompt
  @accept_modal << false
end

#find_modal(options) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/ferrum/page.rb', line 158

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 ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
  rescue ModalNotFound => 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

#go_backObject



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

def go_back
  go(-1)
end

#go_forwardObject



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

def go_forward
  go(1)
end

#goto(url = nil) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ferrum/page.rb', line 77

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 StatusFailError, "url" => options[:url]
  end
  response["frameId"]
end

#network_traffic(type = nil) ⇒ Object



118
119
120
121
122
123
124
125
126
127
# File 'lib/ferrum/page.rb', line 118

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



114
115
116
# File 'lib/ferrum/page.rb', line 114

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

#reset_modalsObject



178
179
180
181
182
# File 'lib/ferrum/page.rb', line 178

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

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



101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ferrum/page.rb', line 101

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



73
74
75
# File 'lib/ferrum/page.rb', line 73

def timeout
  @browser.timeout
end