Skip to content

fix(node): click the center of a transformed node's bounding rect when content quads fail - #696

Merged
route merged 1 commit into
rubycdp:mainfrom
thomasklemm:fix-transformed-bounding-rect-fallback
Nov 7, 2026
Merged

route merged 1 commit into
rubycdp:mainfrom
thomasklemm:fix-transformed-bounding-rect-fallback

Conversation

@thomasklemm

Copy link
Copy Markdown
Contributor

Fixes #695.

When DOM.getContentQuads fails, Node#find_position (and so Node#click) falls back to bounding_rect_coordinates. That added half of offsetWidth/offsetHeight to the bounding rect's left/top. The rect includes transforms, the offset size doesn't, so for a scaled or rotated node the point could land outside it. This uses the rect's own width/height, which gives the node's visual center for any 2D transform and the same point as before for an untransformed node.

The fallback now looks like this:

def bounding_rect_coordinates
  evaluate(<<~JS)
    function() {
      const { left, top, width, height } = this.getBoundingClientRect();
      return [left + (width / 2), top + (height / 2)];
    }
  JS
end

Test

The new example in the existing when content quads can't be computed context rotates a 200×20 link by 45° and clicks it. A rotated element covers the center of its bounding rect but neither its corners nor the old left + offsetWidth / 2 point. So the spec fails with the old formula (the URL stays at /simple) and also with a top-left-corner variant, and passes with the fix. It passed 20 of 20 repeated runs.

  • spec/node_spec.rb: 166 examples, 0 failures
  • Full suite: 1235 examples, 2 failures, 4 pending. The two failures (Options::Chrome.version and colors protocol lines written to a terminal) fail the same way on main in my environment: there is no chrome on PATH, and dbus messages end up in the log.
  • RuboCop is clean.

Side effect

An SVG element has no offsetWidth, so the old fallback returned [NaN, NaN] and mouse.click raised JSON::GeneratorError. It now gets the center of its rect. The CHANGELOG mentions this.

Out of scope (unchanged)

  • The rect is relative to the node's frame while the mouse uses page coordinates, so the fallback ignores iframe offsets, as before.
  • The raise if x.zero? && y.zero? guard stays as it is. With the corrected point it can also fire for a visible transformed node whose rect center lies exactly at the viewport origin. It's an existing sentinel for "no box", and changing it would be a separate behaviour change.
  • For an inline element wrapped across lines, the rect center can fall between the fragments, as before.

@thomasklemm

Copy link
Copy Markdown
Contributor Author

The one failing test on every Ruby version, spec/contexts_spec.rb:9 ("detaches from attached targets of a type it doesn't track"), isn't caused by this change. This run got Chrome for Testing 157.0.8103.38 (chrome-version: stable), while the last green main run (944be71) used 156.0.8078.80. Locally, the spec fails on unchanged main with 157.0.8103.38 and passes with 156. It waits for at least two untracked browser_ui targets per context, and Chrome 157 seems to open fewer. spec/node_spec.rb passes with both versions.

@thomasklemm

Copy link
Copy Markdown
Contributor Author

The contexts_spec.rb failure is fixed separately in #697. I'll rebase this PR once that's merged.

…n content quads fail

When `DOM.getContentQuads` fails, `find_position` falls back to the
node's bounding rect. It added half of `offsetWidth`/`offsetHeight` to
the rect's left/top, but those are the untransformed layout size while
the rect includes transforms, so a scaled node could be missed. Use the
rect's own width and height.

Fixes rubycdp#695
@thomasklemm
thomasklemm force-pushed the fix-transformed-bounding-rect-fallback branch from d905ec1 to 4ee94b4 Compare November 5, 2026 10:53
@route
route merged commit 1e14702 into rubycdp:main Nov 7, 2026
7 checks passed
@route

route commented Nov 7, 2026

Copy link
Copy Markdown
Member

Looking good thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node#find_position fallback ignores CSS transforms (offsetWidth/Height instead of rect size)

2 participants