Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 98c8b69

Browse files
committed
Bug 1937785 - Exclude the dynamic toolbar from the composition bounds in the case of overlays-content with software keyboard. r=botond,geckoview-reviewers,owlish
Otherwise the composition bounds will be larger than the layout viewport. Differential Revision: https://phabricator.services.mozilla.com/D232895
1 parent 271ff0b commit 98c8b69

File tree

4 files changed

+96
-4
lines changed

4 files changed

+96
-4
lines changed

layout/base/nsLayoutUtils.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8167,7 +8167,19 @@ bool nsLayoutUtils::UpdateCompositionBoundsForRCDRSF(
81678167
? SubtractDynamicToolbar::Yes
81688168
: SubtractDynamicToolbar::No;
81698169

8170-
if (shouldSubtractDynamicToolbar == SubtractDynamicToolbar::Yes) {
8170+
const bool isKeyboardVisibleOnOverlaysContent =
8171+
aPresContext->GetKeyboardHeight() &&
8172+
aPresContext->Document()->InteractiveWidget() ==
8173+
InteractiveWidget::OverlaysContent;
8174+
if (shouldSubtractDynamicToolbar == SubtractDynamicToolbar::Yes &&
8175+
// In `overlays-content` mode with the software keyboard visible, avoid
8176+
// flipping `shouldSubtractDynamicToolbar` below. We want to exclude
8177+
// the dynamic toolbar height from the visual viewport (composition bounds)
8178+
// height in this case to be consistent with the handling of the layout
8179+
// viewport height in ExpandHeightForDynamicToolbar(). Otherwise,
8180+
// the visual viewport will be taller than the layout viewport which can
8181+
// lead to rendering problems.
8182+
!isKeyboardVisibleOnOverlaysContent) {
81718183
if (RefPtr<MobileViewportManager> MVM =
81728184
aPresContext->PresShell()->GetMobileViewportManager()) {
81738185
// Convert the intrinsic composition size to app units here since
@@ -8200,9 +8212,7 @@ bool nsLayoutUtils::UpdateCompositionBoundsForRCDRSF(
82008212
// Add the keyboard height in the case of
82018213
// `interactive-widget=overlays-content` so that contents being overlaid by
82028214
// the keyboard can NOT be reachable by scrolling.
8203-
if (aPresContext->GetKeyboardHeight() &&
8204-
aPresContext->Document()->InteractiveWidget() ==
8205-
InteractiveWidget::OverlaysContent) {
8215+
if (isKeyboardVisibleOnOverlaysContent) {
82068216
contentSize.height += ViewAs<LayoutDevicePixel>(
82078217
aPresContext->GetKeyboardHeight(),
82088218
PixelCastJustification::LayoutDeviceIsScreenForBounds);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!doctype html>
2+
<meta charset="utf-8" />
3+
<meta
4+
name="viewport"
5+
content="width=device-width,initial-scale=1,interactive-widget=overlays-content"
6+
/>
7+
<style>
8+
html {
9+
scrollbar-width: none;
10+
}
11+
body {
12+
margin: 0px;
13+
}
14+
</style>
15+
<div style="width: 100vw; height: 101lvh; background: rgb(0, 128, 0)"></div>

mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ open class BaseSessionTest(
151151
const val POSITION_STICKY_HTML_PATH = "/assets/www/position-sticky.html"
152152
const val POSITION_STICKY_ON_MAIN_THREAD_HTML_PATH = "/assets/www/position-sticky-on-main-thread.html"
153153
const val INTERACTIVE_WIDGET_HTML_PATH = "/assets/www/interactive-widget.html"
154+
const val INTERACTIVE_WIDGET_OVERLAYS_CONTENT_HTML_PATH = "/assets/www/interactive-widget-overlays-content.html"
154155

155156
const val TEST_ENDPOINT = GeckoSessionTestRule.TEST_ENDPOINT
156157
const val TEST_HOST = GeckoSessionTestRule.TEST_HOST

mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/InteractiveWidgetTest.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,70 @@ class InteractiveWidgetTest : BaseSessionTest() {
140140
// Close the software keyboard.
141141
imm.hideSoftInputFromWindow(view.getWindowToken(), 0)
142142
}
143+
144+
@GeckoSessionTestRule.NullDelegate(Autofill.Delegate::class)
145+
@Test
146+
fun overlaysContent() {
147+
mainSession.setActive(true)
148+
149+
mainSession.loadTestPath(BaseSessionTest.INTERACTIVE_WIDGET_OVERLAYS_CONTENT_HTML_PATH)
150+
mainSession.waitForPageStop()
151+
mainSession.promiseAllPaintsDone()
152+
mainSession.flushApzRepaints()
153+
154+
view.requestFocus()
155+
156+
// Open the software keyboard.
157+
imm.showSoftInput(view, 0)
158+
159+
// Hide the dynamic toolbar.
160+
view.setVerticalClipping(-dynamicToolbarMaxHeight)
161+
162+
// To make sure the dynamic toolbar height has been reflected into APZ.
163+
mainSession.flushApzRepaints()
164+
// Also to make sure the dynamic toolbar height has been reflected on the main-thread.
165+
mainSession.promiseAllPaintsDone()
166+
167+
// Scroll the visual viewport to the bottom once.
168+
mainSession.panZoomController.scrollTo(
169+
ScreenLength.zero(),
170+
ScreenLength.bottom(),
171+
PanZoomController.SCROLL_BEHAVIOR_AUTO,
172+
)
173+
mainSession.flushApzRepaints()
174+
mainSession.promiseAllPaintsDone()
175+
176+
// Then scroll up a bit.
177+
mainSession.panZoomController.scrollBy(
178+
ScreenLength.zero(),
179+
ScreenLength.fromPixels(-10.0),
180+
PanZoomController.SCROLL_BEHAVIOR_AUTO,
181+
)
182+
mainSession.flushApzRepaints()
183+
mainSession.promiseAllPaintsDone()
184+
185+
fun createReferenceImage(height: Double): Bitmap {
186+
val rect = Rect()
187+
mainSession.getSurfaceBounds(rect)
188+
189+
val bitmap = Bitmap.createBitmap(rect.width(), height.toInt(), Bitmap.Config.ARGB_8888)
190+
val canvas = Canvas(bitmap)
191+
val paint = Paint()
192+
paint.color = Color.rgb(0, 128, 0)
193+
canvas.drawRect(0f, 0f, rect.width().toFloat(), height.toFloat(), paint)
194+
195+
return bitmap
196+
}
197+
198+
val result = sessionRule.waitForResult(view.capturePixels())
199+
200+
// On overlays-content mode neither the visual viewport nor the layout viewport is changed,
201+
// thus there's no way to tell the visible area while the software keyboard is shown.
202+
val reference = createReferenceImage(result.height.toDouble())
203+
204+
AssertUtils.assertScreenshotResult(result, reference)
205+
206+
// Close the software keyboard.
207+
imm.hideSoftInputFromWindow(view.getWindowToken(), 0)
208+
}
143209
}

0 commit comments

Comments
 (0)