Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Input
DescriptionInsufficient validation of untrusted input in Input
ComponentInput
Bug ClassLogic Error
Tracker514072607
Fix commitc03d94e51186 (chromium/src) +60/-29
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
modified

Files Changed

  • content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
From c03d94e511860de8f90c092b28d7253f72a35dac Mon Sep 17 00:00:00 2001
From: Aaron Moss <[email protected]>
Date: Thu, 28 May 2026 15:30:27 -0700
Subject: [PATCH] [a11y] Clamp cursor-following to viewport

Ensure that cursor-following cannot be used to scroll outside the
WebView.

Bug: 514072607
Change-Id: I82729ae0837f12f91620adc599c3eff86ff9ef3d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7859114
Reviewed-by: Bo Liu <[email protected]>
Commit-Queue: Aaron Moss <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1638003}
---

diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
index 17ade2a..4adbbbe6 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
@@ -1593,8 +1593,10 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA
                 && Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1
                 && ContentFeatureList.sAccessibilityMagnificationFollowsFocus.isEnabled()) {
-            Rect nodePix = fromCssToDevicePix(nodeLeftDip, nodeTopDip, nodeRightDip, nodeBottomDip);
-            if (!nodePix.isEmpty()) {
+            Rect nodePix =
+                    fromViewportDipToViewContentPix(
+                            nodeLeftDip, nodeTopDip, nodeRightDip, nodeBottomDip, containerView);
+            if (nodePix != null && !nodePix.isEmpty()) {
                 containerView.requestRectangleOnScreen(
                         nodePix,
                         /* immediate= */ false,
@@ -1768,23 +1770,49 @@
     }
 
     /**
-     * Converts bounds from CSS pixels to device pixels, accounting for page scale, device scale,
-     * and content Y offset.
+     * Converts bounds from device-independent pixels in viewport space to device pixels in
+     * view-content space, clamped to the visible rectangle of the container view.
      *
-     * @param left left X coordinate in CSS pixels
-     * @param top top Y coordinate in CSS pixels
-     * @param right right X coordinate in CSS pixels
-     * @param bottom bottom Y coordinate in CSS pixels
-     * @return {@link Rect} with the device pixel equivalents of the provided coordinates.
+     * @param left left X coordinate in Blink viewport-relative DIPs
+     * @param top top Y coordinate in Blink viewport-relative DIPs
+     * @param right right X coordinate in Blink viewport-relative DIPs
+     * @param bottom bottom Y coordinate in Blink viewport-relative DIPs
+     * @param containerView view to clamp coordinates to
+     * @return {@link Rect} with the device pixel equivalents of the provided coordinates in
+     *     view-content space, clamped to the visible portion of the container view, or null if the
+     *     bounds do not intersect the visible portion of the container view.
      */
-    private Rect fromCssToDevicePix(float left, float top, float right, float bottom) {
+    private @Nullable Rect fromViewportDipToViewContentPix(
+            float left, float top, float right, float bottom, View containerView) {
+        // Get container view's visible area in view-content coordinates.
+        // Chrome team trial-and-error has determined that (inconsistently with other View
+        // documentation), "top left" in the View#getLocalVisibleRect is in content space rather
+        // than viewport space for the view.
+        Rect visibleRect = new Rect();
+        if (!containerView.getLocalVisibleRect(visibleRect)) {
+            return null; // The container is entirely offscreen.
+        }
+
+        // Scale bounds to device pixels in Android view coordinates.
         RenderCoordinatesImpl coords = mWebContents.getRenderCoordinates();
-        final int topOffset = coords.getContentOffsetYPixInt();
-        return new Rect(
-                (int) coords.fromLocalCssToPix(left),
-                ((int) coords.fromLocalCssToPix(top)) + topOffset,
-                (int) coords.fromLocalCssToPix(right),
-                ((int) coords.fromLocalCssToPix(bottom)) + topOffset);
+        final int topOffset = coords.getContentOffsetYPixInt(); // Offset for Chrome address bar.
+        final float scale = coords.getDeviceScaleFactor();
+        Rect bounds =
+                new Rect(
+                        (int) (left * scale),
+                        (int) (top * scale) + topOffset,
+                        (int) (right * scale),
+                        (int) (bottom * scale) + topOffset);
+
+        // Translate bounds to view-content coordinates by adding Android view scroll. This is a
+        // no-op for Chrome (which always reports zero scroll), but will adjust for WebView scroll.
+        bounds.offset(containerView.getScrollX(), containerView.getScrollY());
+
+        // Clamp to visible area and return clamped bounds in view-content coordinates.
+        if (!bounds.intersect(visibleRect)) {
+            return null; // The bounds are entirely offscreen.
+        }
+        return bounds;
     }
 
     /**
@@ -1810,23 +1838,26 @@
             // Convert caret bounds from CSS pixels to device pixels relative to root view.
             var caretCss = cursorAnchorInfo.insertionMarker;
             Rect caretPix =
-                    fromCssToDevicePix(
+                    fromViewportDipToViewContentPix(
                             caretCss.x,
                             caretCss.y,
                             caretCss.x + caretCss.width,
-                            caretCss.y + caretCss.height);
+                            caretCss.y + caretCss.height,
+                            containerView);
 
-            // Note: `SDK_INT_FULL` added in `BAKLAVA`, hence two checks.
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA
-                    && Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1) {
-                containerView.requestRectangleOnScreen(
-                        caretPix,
-                        /* immediate= */ false,
-                        View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR);
-            } else {
-                // Fallback to previous API (where `requestRectangleOnScreen()` calls are assumed
-                // to come from text cursor moves).
-                containerView.requestRectangleOnScreen(caretPix);
+            if (caretPix != null) {
+                // Note: `SDK_INT_FULL` added in `BAKLAVA`, hence two checks.
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA
+                        && Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1) {
+                    containerView.requestRectangleOnScreen(
+                            caretPix,
+                            /* immediate= */ false,
+                            View.RECTANGLE_ON_SCREEN_REQUEST_SOURCE_TEXT_CURSOR);
+                } else {
+                    // Fallback to previous API (where `requestRectangleOnScreen()` calls are
+                    // assumed to come from text cursor moves).
+                    containerView.requestRectangleOnScreen(caretPix);
+                }
             }
         }
     }
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.