"in-view center point" is broken when an inline element contains block-level elements
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 718
- Forks
- 228
- PR merge metrics
- No merged PRs in 30d
Description
In chapter 12. Elements, section 12.1 Interactability:
The first step of the algorithm to calculate in-view center point says:
1. Let rectangle be the first object of the DOMRect collection returned by calling getClientRects() on element.
That rectangle is then used to find the center of the element.
The problem is when that first object has zero width and height, when another of the returned rects in the collection contain the main part of the element. This happens when an inline element such as a <a> contains block-level elements.
When coordinates are not integer, the final floor operation will yield coordinates that are outside the element. The floor operation doesn't make a lot of sense to me (the user can click on non-integer CSS pixels when the dpi isn't 1), but just removing this floor operation wouldn't be good enough: clicks would work, but they wouldn't be at the center of the element.
So I think the step 1. above should be changed to:
1. Let rectangle be the first object of the DOMRect collection returned by calling getClientRects() on element, that has non-null width and height. If all have a null width and height, let rectangle be the first object of the returned collection.
(maybe this could be extracted to another algorithm)
Here is a test that demonstrates the problem in Firefox:
def test_inline_element_with_zero_size_rects_at_fractional_position(
session, inline
):
# An inline <a> wrapping a block-level child produces zero-size line-box
# rects at the start and end of its getClientRects() list: [0x0, 32x32,
# 0x0]. Placing the element at a fractional y-coordinate (via
# margin-top: 0.5px) means Math.floor on the zero-size rect's y lands
# outside the element and elementsFromPoint misses it.
# This test verifies that clicking such an element succeeds despite the
# zero-size first rect, i.e. that the non-zero rect is used to determine
# the element's position.
session.url = inline("""
<style>
body { margin: 0; }
</style>
<div style="margin-top: 0.5px">
<a id="link" href="#"
onclick="document.getElementById('log').textContent = 'clicked'">
<div style="width: 32px; height: 32px;"></div>
</a>
</div>
<div id="log"></div>
""")
element = session.find.css("#link", all=False)
# Verify the layout actually produces zero-size first rect; if this fails
# the test premise has changed and the test should be revisited.
first_rect = session.execute_script(
"""
const r = arguments[0].getClientRects()[0];
return { w: r.width, h: r.height };
""",
args=[element],
)
assert first_rect["w"] == 0 and first_rect["h"] == 0, (
"expected zero-size first rect, got {}x{}".format(
first_rect["w"], first_rect["h"]
)
)
response = element_click(session, element)
assert_success(response)
log = session.find.css("#log", all=False)
assert log.property("textContent") == "clicked"
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read chapter 12, section 12.1 Interactability, starting with the in-view center point algorithm and its getClientRects() step. Run the named test, test_inline_element_with_zero_size_rects_at_fractional_position, and trace element_click with the zero-size first rect. Done means the algorithm handles the demonstrated inline case and the test succeeds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- html, python
- Domain
- testing-qa, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 57/100