-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cherry-pick 184aa40. rdar://120887424
[iOS] [scroll-anchoring] Software keyboard overlaps input field on webauthn.io https://bugs.webkit.org/show_bug.cgi?id=267440 rdar://120753568 Reviewed by Simon Fraser. It's possible for scroll anchoring to cause the scroll position to jump to the previously anchored element, when scrolling to reveal the focused element on top of the software keyboard on iOS. This scrolling (driven by `-_zoomToCenter:atScale:animated:` in the UI process) is bookended in Safari by calls to `-_(begin|end)InteractiveObscuredInsetsChange` while the keyboard is showing, which puts us in unstable state and also passes `ViewportRectStability::ChangingObscuredInsetsInteractively` into `AsyncScrollingCoordinator::reconcileScrollingState`. This, in turn, means we'll skip setting the layout viewport override rect when syncing the scroll position. This isn't normally an issue, because we'll eventually update the layout viewport once we end interactive obscured inset changes. However, in the context of scroll anchoring, the fact that the scroll position is updated but the layout viewport rect isn't (when reconciling scrolling state) means that it's possible for the scroll anchoring controller to adjust the layout viewport rect in the following codepath during layout: ``` void LocalFrameView::updateLayoutViewport() { … if (m_layoutViewportOverrideRect) { if (currentScrollType() == ScrollType::Programmatic) { LOG_WITH_STREAM(Scrolling, stream << "computing new override layout viewport because of programmatic scrolling"); LayoutPoint newOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, StickToDocumentBounds); setLayoutViewportOverrideRect(LayoutRect(newOrigin, m_layoutViewportOverrideRect.value().size())); } layoutOrVisualViewportChanged(); return; } ``` ..._without_ having properly invalidated and updated the current scroll anchor. In turn, this makes it possible for us to compare the layout viewport after accounting for the keyboard scrolling amount against a stale `m_lastOffsetForAnchorElement`, causing the adjustment to overcompensate and jump back up to the top of the page in the middle of the keyboard scrolling animation. To fix this, we simply invalidate the anchor element on `ScrollAnchoringController` (along with the stale `m_lastOffsetForAnchorElement`) when the layout viewport changes. * LayoutTests/fast/scrolling/ios/scroll-anchoring-momentum-scroll.html: Drive-by fix: indentation. * LayoutTests/fast/scrolling/ios/scroll-anchoring-when-revealing-focused-element-expected.txt: Added. * LayoutTests/fast/scrolling/ios/scroll-anchoring-when-revealing-focused-element.html: Added. Add a new layout test that fails without this fix. While it doesn't precisely simulate the bug as it appears on webauthn.io, it exercises the same root cause by repeatedly triggering scroll anchoring adjustment while scrolling to reveal the focused element with the software keyboard, all in the scope of interactive obscured inset changes. * LayoutTests/resources/ui-helper.js: (window.UIHelper.beginInteractiveObscuredInsetsChange): (window.UIHelper.endInteractiveObscuredInsetsChange): (window.UIHelper.setObscuredInsets): Add new `UIHelper` methods to allow layout tests to adjust the obscured insets in the same way that Safari does when showing the software keyboard. * Source/WebCore/page/LocalFrameView.cpp: (WebCore::LocalFrameView::setLayoutViewportOverrideRect): See comments above for more detail. * Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h: * Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm: (-[WKWebView _resetObscuredInsetsForTesting]): Add a testing hook to reset the web view's obscured insets; used when resetting state in between layout tests. * Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl: * Tools/TestRunnerShared/UIScriptContext/UIScriptController.h: (WTR::UIScriptController::beginInteractiveObscuredInsetsChange): (WTR::UIScriptController::endInteractiveObscuredInsetsChange): (WTR::UIScriptController::setObscuredInsets): Add boilerplate code to implement the new script controller helper methods. * Tools/WebKitTestRunner/ios/TestControllerIOS.mm: (WTR::TestController::platformResetStateToConsistentValues): * Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h: * Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm: (WTR::UIScriptControllerIOS::beginInteractiveObscuredInsetsChange): (WTR::UIScriptControllerIOS::setObscuredInsets): (WTR::UIScriptControllerIOS::endInteractiveObscuredInsetsChange): Canonical link: https://commits.webkit.org/272957@main Identifier: 270272.1644@safari-7619.0.1-branch
- Loading branch information
Showing
12 changed files
with
176 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
LayoutTests/fast/scrolling/ios/scroll-anchoring-when-revealing-focused-element-expected.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
Verifies that we scroll to avoid obscuring the focused element with the software keyboard, when scroll anchoring is enabled. To run the test manually, focus the text field without a hardware keyboard attached and check that the text field is visible | ||
|
||
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". | ||
|
||
PASS distanceToTopOfKeyboard is > distanceToBottomOfTextField | ||
PASS successfullyParsed is true | ||
|
||
TEST COMPLETE | ||
Scroll Anchor Element | ||
|
95 changes: 95 additions & 0 deletions
95
LayoutTests/fast/scrolling/ios/scroll-anchoring-when-revealing-focused-element.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true focusStartsInputSessionPolicy=allow ] --> | ||
<html> | ||
<head> | ||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> | ||
<style> | ||
body, html { | ||
width: 100%; | ||
margin: 0; | ||
} | ||
|
||
body { | ||
height: 5000px; | ||
} | ||
|
||
input { | ||
font-size: 18px; | ||
position: absolute; | ||
width: 100%; | ||
top: 85vh; | ||
overflow-anchor: none; | ||
} | ||
|
||
#content-above-anchor { | ||
width: 100%; | ||
} | ||
|
||
#scroll-anchor { | ||
width: 100%; | ||
height: 100px; | ||
font-weight: bold; | ||
line-height: 100px; | ||
color: white; | ||
background: tomato; | ||
text-align: center; | ||
} | ||
|
||
#description, #console { | ||
overflow-anchor: none; | ||
position: absolute; | ||
top: 0; | ||
} | ||
</style> | ||
<script src="../../../resources/js-test.js"></script> | ||
<script src="../../../resources/ui-helper.js"></script> | ||
</head> | ||
<body> | ||
<p id="description"></p> | ||
<p id="console"></p> | ||
<div id="content-above-anchor"></div> | ||
<div id="scroll-anchor">Scroll Anchor Element</div> | ||
<input placeholder="Focus me" /> | ||
<script> | ||
jsTestIsAsync = true; | ||
finalContentAboveAnchorHeight = 25; | ||
const contentAboveAnchor = document.getElementById("content-above-anchor"); | ||
|
||
addEventListener("load", async () => { | ||
description("Verifies that we scroll to avoid obscuring the focused element with the software keyboard, when scroll anchoring is enabled. To run the test manually, focus the text field without a hardware keyboard attached and check that the text field is visible"); | ||
|
||
let textField = document.querySelector("input"); | ||
textField.addEventListener("focus", async function() { | ||
while (true) { | ||
const currentHeight = parseInt(getComputedStyle(contentAboveAnchor).height); | ||
contentAboveAnchor.style.height = `${currentHeight + 1}px`; | ||
if (currentHeight >= finalContentAboveAnchorHeight) | ||
return; | ||
await UIHelper.renderingUpdate(); | ||
} | ||
}, { once: true }); | ||
|
||
document.scrollingElement.scrollTo(0, 10); | ||
if (!window.testRunner) | ||
return; | ||
|
||
await UIHelper.setObscuredInsets(59, 0, 134, 0); | ||
await UIHelper.setHardwareKeyboardAttached(false); | ||
await UIHelper.beginInteractiveObscuredInsetsChange(); | ||
|
||
textField.focus(); | ||
|
||
await UIHelper.waitForKeyboardToShow(); | ||
await UIHelper.endInteractiveObscuredInsetsChange(); | ||
|
||
distanceToTopOfKeyboard = innerHeight - (await UIHelper.inputViewBounds()).height; | ||
distanceToBottomOfTextField = textField.offsetTop + textField.offsetHeight - pageYOffset; | ||
shouldBeGreaterThan("distanceToTopOfKeyboard", "distanceToBottomOfTextField"); | ||
|
||
textField.blur(); | ||
await UIHelper.waitForKeyboardToHide(); | ||
|
||
finishJSTest(); | ||
}); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters