Skip to content

Commit

Permalink
feat: Classify exchanges when loader changes
Browse files Browse the repository at this point in the history
  • Loading branch information
route committed Jan 27, 2025
1 parent a610938 commit 2338302
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 24 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
- `Ferrum::Network::Request#headers` are enhanced and supplemented with `Network.requestWillBeSentExtraInfo` [#506]
- `Ferrum::Page#off` to unsubscribe from CDP events [#455]
- `Ferrum::Mouse#scroll_by` to be able to scroll by, as alternative to `scroll_to` [#514]
- `Ferrum::Network::Exchange#unknown` determines if the exchange is in an unknown state, meaning that browser might not return info about it [#426]
- `Ferrum::Network::Exchange#loader_id` returns loader id [#426]

### Changed

- `Ferrum::Network::Exchange#finished?` takes into account that exchange might be in an unknown state after loader changed(next `go_to` call) and considers
such as finished [#426]

### Fixed

- Correctly set mouse events buttons property [#509]
Expand Down
36 changes: 13 additions & 23 deletions lib/ferrum/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ def idle?(connections = 0)
end

def total_connections
exchange_connections.count
@traffic.size
end

def finished_connections
exchange_connections.count(&:finished?)
@traffic.count(&:finished?)
end

def pending_connections
Expand All @@ -100,7 +100,7 @@ def pending_connections
#
# Page request of the main frame.
#
# @return [Request]
# @return [Request, nil]
#
# @example
# browser.go_to("https://github.com/")
Expand Down Expand Up @@ -391,7 +391,7 @@ def subscribe_request_will_be_sent

if exchange.navigation_request?(@page.main_frame.id)
@exchange = exchange
mark_pending_exchanges_as_unknown(exchange)
classify_pending_exchanges(exchange.loader_id)
end
end

Expand All @@ -403,17 +403,6 @@ def subscribe_request_will_be_sent
end
end

# When the main frame navigates Chrome doesn't send `Network.loadingFailed`
# for pending async requests. Therefore, we mark pending connections as unknown since
# they are not relevant to the current navigation.
def mark_pending_exchanges_as_unknown(navigation_exchange)
@traffic.each do |exchange|
break if exchange.id == navigation_exchange.id

exchange.unknown = true if exchange.pending?
end
end

def subscribe_response_received
@page.on("Network.responseReceived") do |params|
exchange = select(params["requestId"]).last
Expand Down Expand Up @@ -514,15 +503,16 @@ def whitelist?
Array(@whitelist).any?
end

def exchange_connections
@traffic.select { |e| exchange_connection?(e) }
end

def exchange_connection?(exchange)
return false if !@page.frames.any? { |f| f.id == exchange.request&.frame_id }
return false if exchange.request&.frame_id != @exchange.request&.frame_id
# When the main frame navigates Chrome doesn't send `Network.loadingFailed`
# for pending async requests. Therefore, we mark pending connections as unknown since
# they are not relevant to the current navigation.
def classify_pending_exchanges(new_loader_id)
@traffic.each do |exchange|
break if exchange.loader_id == new_loader_id
next unless exchange.pending?

exchange.request&.loader_id == @exchange.request&.loader_id
exchange.unknown = true
end
end
end
end
9 changes: 9 additions & 0 deletions lib/ferrum/network/exchange.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ def navigation_request?(frame_id)
request&.type?(:document) && request&.frame_id == frame_id
end

#
# The loader ID of the request.
#
# @return [String, nil]
#
def loader_id
request&.loader_id
end

#
# Determines if the network exchange has a request.
#
Expand Down
2 changes: 1 addition & 1 deletion lib/ferrum/page/frames.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def subscribe_frame_stopped_loading
on("Page.frameStoppedLoading") do |params|
# `DOM.performSearch` doesn't work without getting #document node first.
# It returns node with nodeId 1 and nodeType 9 from which descend the
# tree and we save it in a variable because if we call that again root
# tree, and we save it in a variable because if we call that again root
# node will change the id and all subsequent nodes have to change id too.
if @main_frame.id == params["frameId"]
@event.set if idling?
Expand Down
16 changes: 16 additions & 0 deletions spec/network/exchange_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,22 @@
)
expect(last_exchange.pending?).to be true
end

it "classifies request if loader changes" do
page.go_to("/ferrum/with_slow_ajax_connection")
xhr = page.network.traffic.find(&:pending?)

expect(page.body).to include("Slow AJAX")
expect(xhr.url).to include("really_slow")
expect(xhr.unknown).to be_falsey
expect(xhr).to be_pending

page.go_to("/")

expect(page.body).to include("Hello world!")
expect(xhr.unknown).to be_truthy
expect(xhr).not_to be_pending
end
end

describe "#intercepted?" do
Expand Down

0 comments on commit 2338302

Please sign in to comment.