Class: Ferrum::Page

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Animation, Frames, Screenshot, Stream
Defined in:
lib/ferrum/page.rb,
lib/ferrum/page/frames.rb,
lib/ferrum/page/stream.rb,
lib/ferrum/page/tracing.rb,
lib/ferrum/page/animation.rb,
lib/ferrum/page/screenshot.rb

Defined Under Namespace

Modules: Animation, Frames, Screenshot, Stream Classes: Tracing

Constant Summary collapse

GOTO_WAIT =
ENV.fetch("FERRUM_GOTO_WAIT", 0.1).to_f

Constants included from Stream

Stream::STREAM_CHUNK

Constants included from Screenshot

Screenshot::AREA_WARNING, Screenshot::DEFAULT_PDF_OPTIONS, Screenshot::FULL_WARNING, Screenshot::PAPER_FORMATS

Instance Attribute Summary collapse

Attributes included from Frames

#main_frame

Instance Method Summary collapse

Methods included from Stream

#stream, #stream_to, #stream_to_file, #stream_to_memory

Methods included from Frames

#frame_by, #frames, #frames_subscribe

Methods included from Screenshot

#device_pixel_ratio, #document_size, #mhtml, #pdf, #screenshot, #viewport_size

Methods included from Animation

#playback_rate, #playback_rate=

Constructor Details

#initialize(client, context_id:, target_id:, proxy: nil) ⇒ Page

Returns a new instance of Page.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/ferrum/page.rb', line 72

def initialize(client, context_id:, target_id:, proxy: nil)
  @client = client
  @context_id = context_id
  @target_id = target_id
  @options = client.options

  @frames = Concurrent::Map.new
  @main_frame = Frame.new(nil, self)
  @event = Utils::Event.new.tap(&:set)
  self.proxy = proxy

  @mouse = Mouse.new(self)
  @keyboard = Keyboard.new(self)
  @headers = Headers.new(self)
  @cookies = Cookies.new(self)
  @network = Network.new(self)
  @tracing = Tracing.new(self)
  @downloads = Downloads.new(self)

  subscribe
  prepare_page
end

Instance Attribute Details

#clientClient (readonly)

Client connection.

Returns:



40
41
42
# File 'lib/ferrum/page.rb', line 40

def client
  @client
end

#context_idObject (readonly)

Returns the value of attribute context_id.



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

def context_id
  @context_id
end

#cookiesCookies (readonly)

Cookie store.

Returns:



65
66
67
# File 'lib/ferrum/page.rb', line 65

def cookies
  @cookies
end

#downloadsDownloads (readonly)

Downloads object.

Returns:



70
71
72
# File 'lib/ferrum/page.rb', line 70

def downloads
  @downloads
end

#eventObject (readonly)

Returns the value of attribute event.



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

def event
  @event
end

#headersHeaders (readonly)

Headers object.

Returns:



60
61
62
# File 'lib/ferrum/page.rb', line 60

def headers
  @headers
end

#keyboardKeyboard (readonly)

Keyboard object.

Returns:



50
51
52
# File 'lib/ferrum/page.rb', line 50

def keyboard
  @keyboard
end

#mouseMouse (readonly)

Mouse object.

Returns:



45
46
47
# File 'lib/ferrum/page.rb', line 45

def mouse
  @mouse
end

#networkNetwork (readonly)

Network object.

Returns:



55
56
57
# File 'lib/ferrum/page.rb', line 55

def network
  @network
end

#referrerObject

Returns the value of attribute referrer.



34
35
36
# File 'lib/ferrum/page.rb', line 34

def referrer
  @referrer
end

#target_idObject (readonly)

Returns the value of attribute target_id.



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

def target_id
  @target_id
end

#tracingObject (readonly)

Returns the value of attribute tracing.



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

def tracing
  @tracing
end

Instance Method Details

#backObject

Navigates to the previous URL in the history.

Examples:

page.go_to("https://github.com/")
page.at_xpath("//a").click
page.back


297
298
299
# File 'lib/ferrum/page.rb', line 297

def back
  history_navigate(delta: -1)
end

#bypass_csp(enabled: true) ⇒ Boolean

Enables/disables CSP bypass.

Examples:

page.bypass_csp # => true
page.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
page.refresh
page.add_script_tag(content: "window.__injected = 42")
page.evaluate("window.__injected") # => 42

Parameters:

  • enabled (Boolean) (defaults to: true)

Returns:

  • (Boolean)


334
335
336
337
# File 'lib/ferrum/page.rb', line 334

def bypass_csp(enabled: true)
  command("Page.setBypassCSP", enabled: enabled)
  enabled
end

#closeObject



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

def close
  @headers.clear
  client.command("Target.closeTarget", async: true, targetId: @target_id)
  close_connection

  true
end

#close_connectionObject



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

def close_connection
  client&.close
end

#command(method, wait: 0, slowmoable: false, **params) ⇒ Object



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/ferrum/page.rb', line 339

def command(method, wait: 0, slowmoable: false, **params)
  iteration = @event.reset if wait.positive?
  sleep(@options.slowmo) if slowmoable && @options.slowmo.positive?
  result = client.command(method, **params)

  if wait.positive?
    # Wait a bit after command and check if iteration has
    # changed which means there was some network event for
    # the main frame and it started to load new content.
    @event.wait(wait)
    if iteration != @event.iteration
      set = @event.wait(timeout)
      raise TimeoutError unless set
    end
  end
  result
end

#disable_javascriptObject

Disables JavaScript execution from the HTML source for the page.

This doesn’t prevent users evaluate JavaScript with Ferrum.



177
178
179
# File 'lib/ferrum/page.rb', line 177

def disable_javascript
  command("Emulation.setScriptExecutionDisabled", value: true)
end

#document_node_idObject



394
395
396
# File 'lib/ferrum/page.rb', line 394

def document_node_id
  command("DOM.getDocument", depth: 0).dig("root", "nodeId")
end

#forwardObject

Navigates to the next URL in the history.

Examples:

page.go_to("https://github.com/")
page.at_xpath("//a").click
page.back
page.forward


310
311
312
# File 'lib/ferrum/page.rb', line 310

def forward
  history_navigate(delta: 1)
end

#go_to(url = nil) ⇒ Object Also known as: goto, go

Navigates the page to a URL.

Examples:

page.go_to("https://github.com/")

Parameters:

  • url (String, nil) (defaults to: nil)

    The URL to navigate to. The url should include scheme unless you set ‘= url` when configuring.



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

def go_to(url = nil)
  options = { url: combine_url!(url) }
  options.merge!(referrer: referrer) if referrer
  response = command("Page.navigate", wait: GOTO_WAIT, **options)
  error_text = response["errorText"] # https://cs.chromium.org/chromium/src/net/base/net_error_list.h
  if error_text && error_text != "net::ERR_ABORTED" # Request aborted due to user action or download
    raise StatusError.new(options[:url], "Request to #{options[:url]} failed (#{error_text})")
  end

  response["frameId"]
rescue TimeoutError
  if @options.pending_connection_errors
    pendings = network.traffic.select(&:pending?).map(&:url).compact
    raise PendingConnectionsError.new(options[:url], pendings) unless pendings.empty?
  end
end

#on(name, &block) ⇒ Object



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/ferrum/page.rb', line 357

def on(name, &block)
  case name
  when :dialog
    client.on("Page.javascriptDialogOpening") do |params, index, total|
      dialog = Dialog.new(self, params)
      block.call(dialog, index, total)
    end
  when :request
    client.on("Fetch.requestPaused") do |params, index, total|
      request = Network::InterceptedRequest.new(client, params)
      exchange = network.select(request.network_id).last
      exchange ||= network.build_exchange(request.network_id)
      exchange.intercepted_request = request
      block.call(request, index, total)
    end
  when :auth
    client.on("Fetch.authRequired") do |params, index, total|
      request = Network::AuthRequest.new(self, params)
      block.call(request, index, total)
    end
  else
    client.on(name, &block)
  end
end

#position(Integer, Integer)

The current position of the window.

Examples:

page.position # => [10, 20]

Returns:

  • ((Integer, Integer))

    The left, top coordinates of the window.



190
191
192
# File 'lib/ferrum/page.rb', line 190

def position
  window_bounds.values_at("left", "top")
end

#position=(options) ⇒ Object

Sets the position of the window.

Examples:

page.position = { left: 10, top: 20 }

Parameters:

  • options (Hash{Symbol => Object})

Options Hash (options):

  • :left (Integer)

    The number of pixels from the left-hand side of the screen.

  • :top (Integer)

    The number of pixels from the top of the screen.



208
209
210
# File 'lib/ferrum/page.rb', line 208

def position=(options)
  self.window_bounds = { left: options[:left], top: options[:top] }
end

#refreshObject Also known as: reload

Reloads the current page.

Examples:

page.go_to("https://github.com/")
page.refresh


273
274
275
# File 'lib/ferrum/page.rb', line 273

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

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



160
161
162
163
164
165
166
167
168
169
170
# File 'lib/ferrum/page.rb', line 160

def resize(width: nil, height: nil, fullscreen: false)
  if fullscreen
    width, height = document_size
    self.window_bounds = { window_state: "fullscreen" }
  else
    self.window_bounds = { window_state: "normal" }
    self.window_bounds = { width: width, height: height }
  end

  set_viewport(width: width, height: height)
end

#set_viewport(width:, height:, scale_factor: 0, mobile: false) ⇒ Object

Overrides device screen dimensions and emulates viewport according to parameters

Read more [here](chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride).

Parameters:

  • width (Integer)

    width value in pixels. 0 disables the override

  • height (Integer)

    height value in pixels. 0 disables the override

  • scale_factor (Float) (defaults to: 0)

    device scale factor value. 0 disables the override

  • mobile (Boolean) (defaults to: false)

    whether to emulate mobile device



149
150
151
152
153
154
155
156
157
158
# File 'lib/ferrum/page.rb', line 149

def set_viewport(width:, height:, scale_factor: 0, mobile: false)
  command(
    "Emulation.setDeviceMetricsOverride",
    slowmoable: true,
    width: width,
    height: height,
    deviceScaleFactor: scale_factor,
    mobile: mobile
  )
end

#stopObject

Stop all navigations and loading pending resources on the page.

Examples:

page.go_to("https://github.com/")
page.stop


285
286
287
# File 'lib/ferrum/page.rb', line 285

def stop
  command("Page.stopLoading", slowmoable: true)
end

#subscribed?(event) ⇒ Boolean

Returns:

  • (Boolean)


382
383
384
# File 'lib/ferrum/page.rb', line 382

def subscribed?(event)
  client.subscribed?(event)
end

#use_authorized_proxy?Boolean

Returns:

  • (Boolean)


390
391
392
# File 'lib/ferrum/page.rb', line 390

def use_authorized_proxy?
  use_proxy? && @proxy_user && @proxy_password
end

#use_proxy?Boolean

Returns:

  • (Boolean)


386
387
388
# File 'lib/ferrum/page.rb', line 386

def use_proxy?
  @proxy_host && @proxy_port
end

#wait_for_reload(timeout = 1) ⇒ Object



314
315
316
317
318
# File 'lib/ferrum/page.rb', line 314

def wait_for_reload(timeout = 1)
  @event.reset if @event.set?
  @event.wait(timeout)
  @event.set
end

#window_boundsHash{String => (Integer, String)}

Current window bounds.

Examples:

page.window_bounds # => { "left": 0, "top": 1286, "width": 10, "height": 10, "windowState": "normal" }

Returns:

  • (Hash{String => (Integer, String)})


250
251
252
# File 'lib/ferrum/page.rb', line 250

def window_bounds
  client.command("Browser.getWindowBounds", windowId: window_id).fetch("bounds")
end

#window_bounds=(bounds) ⇒ Object

Sets the position of the window.

Examples:

page.window_bounds = { left: 10, top: 20, width: 1024, height: 768, window_state: "normal" }

Parameters:

  • bounds (Hash{Symbol => Object})
  • options (Hash)

    a customizable set of options



234
235
236
237
238
239
240
# File 'lib/ferrum/page.rb', line 234

def window_bounds=(bounds)
  options = bounds.dup
  window_state = options.delete(:window_state)
  bounds = { windowState: window_state, **options }.compact

  client.command("Browser.setWindowBounds", windowId: window_id, bounds: bounds)
end

#window_idInteger

Current window id.

Examples:

page.window_id # => 1

Returns:

  • (Integer)


262
263
264
# File 'lib/ferrum/page.rb', line 262

def window_id
  client.command("Browser.getWindowForTarget", targetId: target_id)["windowId"]
end