Skip to content

Commit 4ff62fb

Browse files
authored
Merge pull request #91 from rubycdp/fix-moving-node
Fix click when node is moving
2 parents c88917c + bbab47c commit 4ff62fb

File tree

2 files changed

+51
-17
lines changed

2 files changed

+51
-17
lines changed

lib/ferrum.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ def initialize(message = "Browser is dead or given window is closed")
4848
end
4949
end
5050

51+
class NodeIsMovingError < Error
52+
def initialize(node, prev, current)
53+
@node, @prev, @current = node, prev, current
54+
super(message)
55+
end
56+
57+
def message
58+
"#{@node.inspect} that you're trying to click is moving, hence " \
59+
"we cannot. Previosuly it was at #{@prev.inspect} but now at " \
60+
"#{@current.inspect}."
61+
end
62+
end
63+
5164
class BrowserError < Error
5265
attr_reader :response
5366

lib/ferrum/node.rb

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
module Ferrum
44
class Node
5+
MOVING_WAIT = ENV.fetch("FERRUM_NODE_MOVING_WAIT", 0.01).to_f
6+
MOVING_ATTEMPTS = ENV.fetch("FERRUM_NODE_MOVING_ATTEMPTS", 50).to_i
7+
58
attr_reader :page, :target_id, :node_id, :description, :tag_name
69

710
def initialize(frame, target_id, node_id, description)
@@ -121,16 +124,42 @@ def inspect
121124
end
122125

123126
def find_position(x: nil, y: nil, position: :top)
124-
offset_x, offset_y = x, y
125-
quads = get_content_quads
127+
prev = get_content_quads
128+
129+
# FIXME: Case when a few quads returned
130+
points = Ferrum.with_attempts(errors: NodeIsMovingError, max: MOVING_ATTEMPTS, wait: 0) do
131+
sleep(MOVING_WAIT)
132+
current = get_content_quads
133+
134+
if current != prev
135+
error = NodeIsMovingError.new(self, prev, current)
136+
prev = current
137+
raise(error)
138+
end
139+
140+
current
141+
end.map { |q| to_points(q) }.first
142+
143+
get_position(points, x, y, position)
144+
end
145+
146+
private
147+
148+
def get_content_quads
149+
quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
150+
raise "Node is either not visible or not an HTMLElement" if quads.size == 0
151+
quads
152+
end
153+
154+
def get_position(points, offset_x, offset_y, position)
126155
x = y = nil
127156

128157
if offset_x && offset_y && position == :top
129-
point = quads.first
158+
point = points.first
130159
x = point[:x] + offset_x.to_i
131160
y = point[:y] + offset_y.to_i
132161
else
133-
x, y = quads.inject([0, 0]) do |memo, point|
162+
x, y = points.inject([0, 0]) do |memo, point|
134163
[memo[0] + point[:x],
135164
memo[1] + point[:y]]
136165
end
@@ -147,19 +176,11 @@ def find_position(x: nil, y: nil, position: :top)
147176
[x, y]
148177
end
149178

150-
private
151-
152-
def get_content_quads
153-
result = page.command("DOM.getContentQuads", nodeId: node_id)
154-
raise "Node is either not visible or not an HTMLElement" if result["quads"].size == 0
155-
156-
# FIXME: Case when a few quads returned
157-
result["quads"].map do |quad|
158-
[{x: quad[0], y: quad[1]},
159-
{x: quad[2], y: quad[3]},
160-
{x: quad[4], y: quad[5]},
161-
{x: quad[6], y: quad[7]}]
162-
end.first
179+
def to_points(quad)
180+
[{x: quad[0], y: quad[1]},
181+
{x: quad[2], y: quad[3]},
182+
{x: quad[4], y: quad[5]},
183+
{x: quad[6], y: quad[7]}]
163184
end
164185
end
165186
end

0 commit comments

Comments
 (0)