Skip to content

Commit b2d153e

Browse files
committed
fix(ios): report a scroll's clipped band in its response
1 parent b6ea4e6 commit b2d153e

2 files changed

Lines changed: 38 additions & 12 deletions

File tree

apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerScrollViewportPolicy.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,25 @@ enum ScrollGestureOutcome {
5959
case occluded(keyboardMinY: Double, visibleHeight: Double)
6060
}
6161

62+
extension ScrollGestureDispatch {
63+
/// Reports the gesture against the band its plan ran inside, beside the keyboard evidence. The
64+
/// synthesis frame stays the full viewport so the coordinates rotate correctly, which leaves the
65+
/// payload measured against an axis the caller never planned on: `pixels` are a fraction of the
66+
/// band, so the band is what `referenceWidth` and `referenceHeight` have to name.
67+
func attachingEvidence(to response: Response) -> Response {
68+
guard response.ok else { return response }
69+
var payload = response.data ?? DataPayload()
70+
payload.referenceWidth = Double(planFrame.width)
71+
payload.referenceHeight = Double(planFrame.height)
72+
guard let keyboardMinY else {
73+
return Response(ok: response.ok, data: payload, error: response.error)
74+
}
75+
payload.keyboardAvoided = true
76+
payload.keyboardMinY = keyboardMinY
77+
return Response(ok: response.ok, data: payload, error: response.error)
78+
}
79+
}
80+
6281
extension RunnerScrollViewport {
6382
/// Plans the swipe inside the band the keyboard left and keeps the viewport as the coordinate basis,
6483
/// so a clip shortens the travel without moving the gesture's lane.
@@ -338,6 +357,22 @@ extension RunnerTests {
338357
"a landscape swipe must stay clear of the keys"
339358
)
340359

360+
let reported = gesture.attachingEvidence(
361+
to: Response(
362+
ok: true,
363+
data: DataPayload(referenceWidth: viewport.width, referenceHeight: viewport.height),
364+
error: nil
365+
)
366+
)
367+
XCTAssertEqual(
368+
reported.data?.referenceHeight,
369+
band.height,
370+
"the payload names the band the plan ran inside, not the synthesis frame"
371+
)
372+
XCTAssertEqual(reported.data?.referenceWidth, viewport.width)
373+
XCTAssertEqual(reported.data?.keyboardMinY, keyboardMinY)
374+
XCTAssertEqual(reported.data?.keyboardAvoided, true)
375+
341376
let orientedStartY = gesture.planFrame.minY + gesture.plan.y1
342377
let dispatched = nativeSynthesizedPoint(
343378
orientedX: gesture.planFrame.minX + gesture.plan.x1,

apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,8 +1983,8 @@ extension RunnerTests {
19831983
guard scrollDurationIsValid(command.durationMs) else {
19841984
return invalidScrollDurationResponse(commandName: "scroll")
19851985
}
1986-
return attachingScrollViewportEvidence(
1987-
executeScrollDragGesture(
1986+
return gesture.attachingEvidence(
1987+
to: executeScrollDragGesture(
19881988
activeApp: activeApp,
19891989
x: gesture.planFrame.minX + gesture.plan.x1,
19901990
y: gesture.planFrame.minY + gesture.plan.y1,
@@ -1994,8 +1994,7 @@ extension RunnerTests {
19941994
message: "scrolled",
19951995
context: scrollContext.withReferenceFrame(gesture.coordinateFrame),
19961996
releaseBehavior: command.scrollReleaseBehavior
1997-
),
1998-
keyboardMinY: gesture.keyboardMinY
1997+
)
19991998
)
20001999
}
20012000
case .desktopScroll:
@@ -2575,14 +2574,6 @@ extension RunnerTests {
25752574
/// Adds the #2500 avoidance evidence to a scroll response. Only the frame resolver knows whether
25762575
/// it trimmed the swipe for a keyboard, and only `scroll` has this evidence to carry, so it is
25772576
/// attached where the frame was resolved rather than threaded through every gesture response.
2578-
private func attachingScrollViewportEvidence(_ response: Response, keyboardMinY: Double?) -> Response {
2579-
guard response.ok, let keyboardMinY else { return response }
2580-
var payload = response.data ?? DataPayload()
2581-
payload.keyboardAvoided = true
2582-
payload.keyboardMinY = keyboardMinY
2583-
return Response(ok: response.ok, data: payload, error: response.error)
2584-
}
2585-
25862577
/// The refusal a keyboard forces. It performs no gesture: swiping into the keys would leave the
25872578
/// surface where it was, which the daemon's no-progress fingerprint reads as a stuck container
25882579
/// (#2499) and an agent reads as a broken scroll. The TS owner maps the code to the

0 commit comments

Comments
 (0)