Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
route committed May 9, 2022
1 parent 24b9143 commit bb8d3b0
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 302 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ a block with this page, after which the page is closed.
- `Ferrum::Cookies#set` ability to set cookie using `Ferrum::Cookies::Cookie` object
- `Ferrum::Network#emulate_network_conditions` activates emulation of network conditions
- `Ferrum::Network#offline_mode` puts browser into offline mode
- `Ferrum::Page#tracing` - instance of `Ferrum::Page::Tracing` for trace capabilities.
- `Ferrum::Page::Tracing#record(&block)` start/stop tracing for steps provided in passed block

### Changed

Expand Down
47 changes: 15 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1151,46 +1151,29 @@ browser.at_xpath("//*[select]").select(["1", "2"])

## Tracing

You can use `tracing.record` to create a trace file which can be opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/).
You can use `tracing.record` to create a trace file which can be opened in Chrome DevTools or
[timeline viewer](https://chromedevtools.github.io/timeline-viewer/).

```ruby
browser.page.tracing.record(path: "trace.json") do
browser.go_to("https://www.google.com")
page.tracing.record(path: "trace.json") do
page.go_to("https://www.google.com")
end
```

#### tracing.record(\*\*options) : `Hash`
#### tracing.record(\*\*options) : `String`

- By default: returns Trace data from `Tracing.tracingComplete` event.
- When `path` specified: return `true` and store Trace data from `Tracing.tracingComplete` event to file.
Accepts block, records trace and by default returns trace data from `Tracing.tracingComplete` event as output. When
`path` is specified returns `true` and stores trace data into file.

* options `Hash`
* `:path` (String) - `String` to save a Trace data output on the disk, not specified by default.
* `:encoding` (Symbol) - `:base64` | `:binary` setting only for memory Trace data output, `:binary` by default.
* `:timeout` (Integer) - [Timeout of promise](https://github.com/ruby-concurrency/concurrent-ruby/blob/52c08fca13cc3811673ea2f6fdb244a0e42e0ebe/lib/concurrent-ruby/concurrent/promises.rb#L986) to wait til event `Tracing.Complete` triggered that fills buffer/file, `nil` by default, means "wait forever".
* `:screenshots` (Boolean) - When true - Captures screenshots in the trace, `false` by default.
* `:included_categories` (Array[String]) - An array of categories that be included to tracing data, by default:
```ruby
["devtools.timeline",
"v8.execute",
"disabled-by-default-devtools.timeline",
"disabled-by-default-devtools.timeline.frame",
"toplevel",
"blink.console",
"blink.user_timing",
"latencyInfo",
"disabled-by-default-devtools.timeline.stack",
"disabled-by-default-v8.cpu_profiler",
"disabled-by-default-v8.cpu_profiler.hires"]
```
* `:excluded_categories` (Array[String]) - An array of categories that be excluded from tracing data, by default:
```ruby
["*"]
```

See all categories by `browser.client.command("Tracing.getCategories")`

Only one trace can be active at a time per browser.
* :path `String` save data on the disk, `nil` by default
* :encoding `Symbol` `:base64` | `:binary` encode output as Base64 or plain text. `:binary` by default
* :timeout `Float` wait until file streaming finishes in the specified time or raise error, defaults to `nil`
* :screenshots `Boolean` capture screenshots in the trace, `false` by default
* :trace_config `Hash<String, Object>` config for
[trace](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#type-TraceConfig), for categories
see [getCategories](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#method-getCategories),
only one trace config can be active at a time per browser.


## Thread safety ##
Expand Down
1 change: 0 additions & 1 deletion lib/ferrum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require "ferrum/utils/platform"
require "ferrum/utils/elapsed_time"
require "ferrum/utils/attempt"
require "ferrum/utils/stream"
require "ferrum/errors"
require "ferrum/browser"
require "ferrum/node"
Expand Down
10 changes: 5 additions & 5 deletions lib/ferrum/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require "ferrum/page/screenshot"
require "ferrum/page/animation"
require "ferrum/page/tracing"
require "ferrum/page/stream"
require "ferrum/browser/client"

module Ferrum
Expand Down Expand Up @@ -40,11 +41,13 @@ def reset
include Animation
include Screenshot
include Frames
include Stream

attr_accessor :referrer
attr_reader :target_id, :browser,
:headers, :cookies, :network,
:mouse, :keyboard, :event
:mouse, :keyboard, :event,
:tracing

def initialize(target_id, browser)
@frames = {}
Expand All @@ -63,6 +66,7 @@ def initialize(target_id, browser)
@headers = Headers.new(self)
@cookies = Cookies.new(self)
@network = Network.new(self)
@tracing = Tracing.new(self)

subscribe
prepare_page
Expand Down Expand Up @@ -216,10 +220,6 @@ def subscribed?(event)
@client.subscribed?(event)
end

def tracing
@tracing ||= Tracing.new(client: @client)
end

private

def subscribe
Expand Down
6 changes: 1 addition & 5 deletions lib/ferrum/page/screenshot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ module Screenshot
A6: { width: 4.13, height: 5.83 }
}.freeze

STREAM_CHUNK = 128 * 1024

def screenshot(**opts)
path, encoding = common_options(**opts)
options = screenshot_options(path, **opts)
Expand All @@ -42,9 +40,7 @@ def pdf(**opts)
path, encoding = common_options(**opts)
options = pdf_options(**opts).merge(transferMode: "ReturnAsStream")
handle = command("Page.printToPDF", **options).fetch("stream")
Utils::Stream.fetch(encoding: encoding, path: path) do |read_stream|
read_stream.call(client: self, handle: handle)
end
stream_to(path: path, encoding: encoding, handle: handle)
end

def mhtml(path: nil)
Expand Down
38 changes: 38 additions & 0 deletions lib/ferrum/page/stream.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Ferrum
class Page
module Stream
STREAM_CHUNK = 128 * 1024

def stream_to(path:, encoding:, handle:)
if path.nil?
stream_to_memory(encoding: encoding, handle: handle)
else
stream_to_file(path: path, handle: handle)
end
end

def stream_to_file(path:, handle:)
File.open(path, "wb") { |f| stream(output: f, handle: handle) }
true
end

def stream_to_memory(encoding:, handle:)
data = String.new # Mutable string has << and compatible to File
stream(output: data, handle: handle)
encoding == :base64 ? Base64.encode64(data) : data
end

def stream(output:, handle:)
loop do
result = command("IO.read", handle: handle, size: STREAM_CHUNK)
chunk = result.fetch("data")
chunk = Base64.decode64(chunk) if result["base64Encoded"]
output << chunk
break if result["eof"]
end
end
end
end
end
103 changes: 45 additions & 58 deletions lib/ferrum/page/tracing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,66 @@
module Ferrum
class Page
class Tracing
INCLUDED_CATEGORIES = %w[
devtools.timeline
v8.execute
disabled-by-default-devtools.timeline
disabled-by-default-devtools.timeline.frame
toplevel
blink.console
blink.user_timing
latencyInfo
disabled-by-default-devtools.timeline.stack
disabled-by-default-v8.cpu_profiler
disabled-by-default-v8.cpu_profiler.hires
].freeze
EXCLUDED_CATEGORIES = %w[
*
].freeze
EXCLUDED_CATEGORIES = %w[*].freeze
SCREENSHOT_CATEGORIES = %w[disabled-by-default-devtools.screenshot].freeze
INCLUDED_CATEGORIES = %w[devtools.timeline v8.execute disabled-by-default-devtools.timeline
disabled-by-default-devtools.timeline.frame toplevel blink.console
blink.user_timing latencyInfo disabled-by-default-devtools.timeline.stack
disabled-by-default-v8.cpu_profiler disabled-by-default-v8.cpu_profiler.hires].freeze
DEFAULT_TRACE_CONFIG = {
includedCategories: INCLUDED_CATEGORIES,
excludedCategories: EXCLUDED_CATEGORIES
}.freeze

def initialize(client:)
@client = client
def initialize(page)
@page = page
@subscribed_tracing_complete = false
end

def record(options = {}, &block)
@options = {
timeout: nil,
screenshots: false,
encoding: :binary,
included_categories: INCLUDED_CATEGORIES,
excluded_categories: EXCLUDED_CATEGORIES,
**options
}
@promise = Concurrent::Promises.resolvable_future
subscribe_on_tracing_event
start
block.call
@client.command("Tracing.end")
@promise.value!(@options[:timeout])
def record(path: nil, encoding: :binary, timeout: nil, trace_config: nil, screenshots: false)
@path, @encoding = path, encoding
@result = Concurrent::Promises.resolvable_future
trace_config ||= DEFAULT_TRACE_CONFIG.dup

if screenshots
included = trace_config.fetch(:includedCategories, [])
trace_config.merge!(includedCategories: included | SCREENSHOT_CATEGORIES)
end

subscribe_tracing_complete

start(trace_config)
yield
stop

@result.value!(timeout)
end

private

def start
@client.command(
"Tracing.start",
transferMode: "ReturnAsStream",
traceConfig: {
includedCategories: included_categories,
excludedCategories: @options[:excluded_categories]
}
)
def start(config)
@page.command("Tracing.start", transferMode: "ReturnAsStream", traceConfig: config)
end

def included_categories
included_categories = @options[:included_categories]
if @options[:screenshots] == true
included_categories = @options[:included_categories] | ["disabled-by-default-devtools.screenshot"]
end
included_categories
def stop
@page.command("Tracing.end")
end

def subscribe_on_tracing_event
@client.on("Tracing.tracingComplete") do |event, index|
next if index.to_i != 0
def subscribe_tracing_complete
return if @subscribed_tracing_complete

@promise.fulfill(stream(event.fetch("stream")))
rescue StandardError => e
@promise.reject(e)
@page.on("Tracing.tracingComplete") do |event, index|
next if index.to_i != 0
@result.fulfill(stream_handle(event["stream"]))
rescue => e
@result.reject(e)
end

@subscribed_tracing_complete = true
end

def stream(handle)
Utils::Stream.fetch(encoding: @options[:encoding], path: @options[:path]) do |read_stream|
read_stream.call(client: @client, handle: handle)
end
def stream_handle(handle)
@page.stream_to(path: @path, encoding: @encoding, handle: handle)
end
end
end
Expand Down
43 changes: 0 additions & 43 deletions lib/ferrum/utils/stream.rb

This file was deleted.

Loading

0 comments on commit bb8d3b0

Please sign in to comment.