Skip to content

Commit

Permalink
feat: flatten
Browse files Browse the repository at this point in the history
  • Loading branch information
route committed Jan 5, 2024
1 parent af88f87 commit 8e3e929
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 33 deletions.
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,9 +533,8 @@ to `Ferrum::Browser#default_context`
### Fixed

### Removed
- `Ferrum::EmptyTargetsError`
- the `hack` to handle `new window` which doesn't have events at all by `Ferrum::Page#session_id` with
`Target.attachToTarget` and `Target.detachFromTarget` usage
- `Ferrum::EmptyTargetsError` the hack to handle `new window` which doesn't have events at all by
`Ferrum::Page#session_id` with `Target.attachToTarget` and `Target.detachFromTarget` usage
- `Ferrum::Page#close_connection` - the logic is moved to `Ferrum::Page#close` directly
- the third argument (`new_window = false`) for `Ferrum::Page` initializer
- `Ferrum::Targets` class with the delegations to `Ferrum::Targets` instance in `Ferrum::Browser` instance:
Expand Down
3 changes: 3 additions & 0 deletions lib/ferrum/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class Browser
# @option options [Boolean] :xvfb (false)
# Run browser in a virtual framebuffer.
#
# @option options [Boolean] :flatten (true)
# Use one websocket connection to the browser and all the pages in flatten mode.
#
# @option options [(Integer, Integer)] :window_size ([1024, 768])
# The dimensions of the browser window in which to test, expressed as a
# 2-element array, e.g. `[1024, 768]`.
Expand Down
3 changes: 2 additions & 1 deletion lib/ferrum/browser/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Options
:js_errors, :base_url, :slowmo, :pending_connection_errors,
:url, :env, :process_timeout, :browser_name, :browser_path,
:save_path, :proxy, :port, :host, :headless, :browser_options,
:ignore_default_browser_options, :xvfb
:ignore_default_browser_options, :xvfb, :flatten
attr_accessor :timeout, :ws_url, :default_user_agent

def initialize(options = nil)
Expand All @@ -27,6 +27,7 @@ def initialize(options = nil)
@window_size = @options.fetch(:window_size, WINDOW_SIZE)
@js_errors = @options.fetch(:js_errors, false)
@headless = @options.fetch(:headless, true)
@flatten = @options.fetch(:flatten, true)
@pending_connection_errors = @options.fetch(:pending_connection_errors, true)
@process_timeout = @options.fetch(:process_timeout, PROCESS_TIMEOUT)
@slowmo = @options[:slowmo].to_f
Expand Down
42 changes: 34 additions & 8 deletions lib/ferrum/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@
require "ferrum/client/web_socket"

module Ferrum
class SessionClient
attr_reader :client, :session_id

def initialize(client, session_id)
@client = client
@session_id = session_id
end

def command(method, async: false, **params)
@client.command(method, async: async, session_id: session_id, **params)
end

def on(event, &block)
@client.on([event, session_id].compact.join("_"), &block)
end

def respond_to_missing?(name, include_private)
@client.respond_to?(name, include_private)
end

def method_missing(name, ...)
@client.send(name, ...)
end
end

class Client
extend Forwardable
delegate %i[timeout timeout=] => :options
Expand All @@ -21,8 +46,8 @@ def initialize(ws_url, options)
start
end

def command(method, async: false, **params)
message = build_message(method, params)
def command(method, async: false, session_id: nil, **params)
message = build_message(method, session_id, params)

if async
@ws.send_message(message)
Expand All @@ -43,12 +68,12 @@ def command(method, async: false, **params)
end
end

def on(event, &block)
@subscriber.on(event, &block)
def on(event, session_id = nil, &block)
@subscriber.on([event, session_id].compact.join("_"), &block)
end

def subscribed?(event)
@subscriber.subscribed?(event)
def subscribed?(event, session_id = nil)
@subscriber.subscribed?([event, session_id].compact.join("_"))
end

def close
Expand Down Expand Up @@ -83,8 +108,9 @@ def start
end
end

def build_message(method, params)
{ method: method, params: params }.merge(id: next_command_id)
def build_message(method, session_id, params)
message = { method: method, params: params }.merge(id: next_command_id)
session_id ? message.merge(sessionId: session_id) : message
end

def next_command_id
Expand Down
8 changes: 5 additions & 3 deletions lib/ferrum/client/subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ def start
end

def call(message)
method, params = message.values_at("method", "params")
total = @on[method].size
@on[method].each_with_index do |block, index|
method, session_id, params = message.values_at("method", "sessionId", "params")
event = [method, session_id].compact.join("_")

total = @on[event].size
@on[event].each_with_index do |block, index|
# In case of multiple callbacks we provide current index and total
block.call(params, index, total)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/ferrum/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def create_target
target
end

def add_target(params)
new_target = Target.new(@client, params)
def add_target(params:, session_id: nil)
new_target = Target.new(@client, session_id, params)
target = @targets.put_if_absent(new_target.id, new_target)
target ||= new_target # `put_if_absent` returns nil if added a new value or existing if there was one already
@pendings.put(target, @client.timeout) if @pendings.empty?
Expand Down
18 changes: 17 additions & 1 deletion lib/ferrum/contexts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def initialize(client)
@contexts = Concurrent::Map.new
@client = client
subscribe
auto_attach
discover
end

Expand Down Expand Up @@ -67,12 +68,21 @@ def size
private

def subscribe
@client.on("Target.attachedToTarget") do |params|
info, session_id = params.values_at("targetInfo", "sessionId")
next unless info["type"] == "page"

context_id = info["browserContextId"]
@contexts[context_id]&.add_target(session_id: session_id, params: info)
@client.command("Runtime.runIfWaitingForDebugger", async: true, session_id: session_id) if params["waitingForDebugger"]
end

@client.on("Target.targetCreated") do |params|
info = params["targetInfo"]
next unless info["type"] == "page"

context_id = info["browserContextId"]
@contexts[context_id]&.add_target(info)
@contexts[context_id]&.add_target(params: info)
end

@client.on("Target.targetInfoChanged") do |params|
Expand All @@ -97,5 +107,11 @@ def subscribe
def discover
@client.command("Target.setDiscoverTargets", discover: true)
end

def auto_attach
return unless @client.options.flatten

@client.command("Target.setAutoAttach", autoAttach: true, waitForDebuggerOnStart: true, flatten: true)
end
end
end
17 changes: 9 additions & 8 deletions lib/ferrum/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Page
include Stream

attr_accessor :referrer
attr_reader :context_id, :target_id, :event, :tracing
attr_reader :context_id, :target_id, :session_id, :event, :tracing

# Client connection.
#
Expand Down Expand Up @@ -69,10 +69,11 @@ class Page
# @return [Downloads]
attr_reader :downloads

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

@frames = Concurrent::Map.new
Expand Down Expand Up @@ -339,7 +340,7 @@ def bypass_csp(enabled: true)
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)
result = client.command(method, session_id: session_id, **params)

if wait.positive?
# Wait a bit after command and check if iteration has
Expand All @@ -357,30 +358,30 @@ def command(method, wait: 0, slowmoable: false, **params)
def on(name, &block)
case name
when :dialog
client.on("Page.javascriptDialogOpening") do |params, index, total|
client.on("Page.javascriptDialogOpening", session_id) 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|
client.on("Fetch.requestPaused", session_id) 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|
client.on("Fetch.authRequired", session_id) do |params, index, total|
request = Network::AuthRequest.new(self, params)
block.call(request, index, total)
end
else
client.on(name, &block)
client.on(name, session_id, &block)
end
end

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

def use_proxy?
Expand Down
19 changes: 12 additions & 7 deletions lib/ferrum/target.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ class Target
# where we enhance page class and build page ourselves.
attr_writer :page

def initialize(client, params = nil)
attr_reader :session_id

def initialize(client, session_id = nil, params = nil)
@page = nil
@client = client
@session_id = session_id
@params = params
end

def update(params)
@params = params
@params.merge!(params)
end

def attached?
Expand All @@ -28,7 +31,11 @@ def page

def build_page(**options)
maybe_sleep_if_new_window
Page.new(build_client, context_id: context_id, target_id: id, **options)
Page.new(build_client, context_id: context_id, target_id: id, session_id: session_id, **options)
end

def context_id
@params["browserContextId"]
end

def id
Expand All @@ -51,10 +58,6 @@ def opener_id
@params["openerId"]
end

def context_id
@params["browserContextId"]
end

def window?
!!opener_id
end
Expand All @@ -68,6 +71,8 @@ def maybe_sleep_if_new_window

def build_client
options = @client.options
return SessionClient.new(@client, session_id) if options.flatten

ws_url = options.ws_url.merge(path: "/devtools/page/#{id}").to_s
Client.new(ws_url, options)
end
Expand Down

0 comments on commit 8e3e929

Please sign in to comment.