Class: Ferrum::Page

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Animation, Frames, Screencast, 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/screencast.rb,
lib/ferrum/page/screenshot.rb

Defined Under Namespace

Modules: Animation, Frames, Screencast, 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::DEFAULT_SCREENSHOT_FORMAT, Screenshot::FULL_WARNING, Screenshot::PAPER_FORMATS, Screenshot::SUPPORTED_SCREENSHOT_FORMAT

Constants included from Screencast

Screencast::START_SCREENCAST_KEY_CONV

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 Screencast

#start_screencast, #stop_screencast

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.



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

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:



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

def client
  @client
end

#context_idObject (readonly)

Returns the value of attribute context_id.



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

def context_id
  @context_id
end

#cookiesCookies (readonly)

Cookie store.

Returns:



67
68
69
# File 'lib/ferrum/page.rb', line 67

def cookies
  @cookies
end

#downloadsDownloads (readonly)

Downloads object.

Returns:



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

def downloads
  @downloads
end

#eventObject (readonly)

Returns the value of attribute event.



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

def event
  @event
end

#headersHeaders (readonly)

Headers object.

Returns:



62
63
64
# File 'lib/ferrum/page.rb', line 62

def headers
  @headers
end

#keyboardKeyboard (readonly)

Keyboard object.

Returns:



52
53
54
# File 'lib/ferrum/page.rb', line 52

def keyboard
  @keyboard
end

#mouseMouse (readonly)

Mouse object.

Returns:



47
48
49
# File 'lib/ferrum/page.rb', line 47

def mouse
  @mouse
end

#networkNetwork (readonly)

Network object.

Returns:



57
58
59
# File 'lib/ferrum/page.rb', line 57

def network
  @network
end

#referrerObject

Returns the value of attribute referrer.



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

def referrer
  @referrer
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

#tracingObject (readonly)

Returns the value of attribute tracing.



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

def tracing
  @tracing
end

Instance Method Details

#activateBoolean

Activates (focuses) the target for the given page. When you have multiple tabs you work with, and you need to switch a given one.

Examples:

page.activate # => true

Returns:

  • (Boolean)


350
351
352
353
# File 'lib/ferrum/page.rb', line 350

def activate
  command("Target.activateTarget", targetId: target_id)
  true
end

#backObject

Navigates to the previous URL in the history.

Examples:

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


299
300
301
# File 'lib/ferrum/page.rb', line 299

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)


336
337
338
339
# File 'lib/ferrum/page.rb', line 336

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

#closeObject



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

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

  true
end

#close_connectionObject



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

def close_connection
  client&.close
end

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



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/ferrum/page.rb', line 355

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.



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

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

#document_node_id(async: false) ⇒ Object



423
424
425
426
427
# File 'lib/ferrum/page.rb', line 423

def document_node_id(async: false)
  return client.command("DOM.getDocument", async: true, depth: 0) if async

  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


312
313
314
# File 'lib/ferrum/page.rb', line 312

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.



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

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

#off(name, id) ⇒ Object



398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/ferrum/page.rb', line 398

def off(name, id)
  case name
  when :dialog
    client.off("Page.javascriptDialogOpening", id)
  when :request
    client.off("Fetch.requestPaused", id)
  when :auth
    client.off("Fetch.authRequired", id)
  else
    client.off(name, id)
  end
end

#on(name, &block) ⇒ Object



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/ferrum/page.rb', line 373

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.



192
193
194
# File 'lib/ferrum/page.rb', line 192

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.



210
211
212
# File 'lib/ferrum/page.rb', line 210

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


275
276
277
# File 'lib/ferrum/page.rb', line 275

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

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



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

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



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

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


287
288
289
# File 'lib/ferrum/page.rb', line 287

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

#subscribed?(event) ⇒ Boolean

Returns:

  • (Boolean)


411
412
413
# File 'lib/ferrum/page.rb', line 411

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

#use_authorized_proxy?Boolean

Returns:

  • (Boolean)


419
420
421
# File 'lib/ferrum/page.rb', line 419

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

#use_proxy?Boolean

Returns:

  • (Boolean)


415
416
417
# File 'lib/ferrum/page.rb', line 415

def use_proxy?
  @proxy_host && @proxy_port
end

#wait_for_reload(timeout = 1) ⇒ Object



316
317
318
319
320
# File 'lib/ferrum/page.rb', line 316

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)})


252
253
254
# File 'lib/ferrum/page.rb', line 252

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



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

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)


264
265
266
# File 'lib/ferrum/page.rb', line 264

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