CVE-2026-14030
Overview
Files Changed
chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.ccchrome/browser/ui/views/frame/contents_web_view.ccchrome/browser/ui/views/frame/contents_web_view.hchrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
Patch
From ed05567436ea5cb1afaf32ee255fae6a58360709 Mon Sep 17 00:00:00 2001 From: behery <[email protected]> Date: Mon, 01 Jun 2026 07:48:06 -0700 Subject: [PATCH] [SxS/Linux] Fix UI origin confusion during split view permission prompts On Linux and Linux-chromeos, when Chrome is in split view and a permission prompt is visible in one tab, the native window focus is kept by the prompt. If the user clicks on the other side's web contents, the OS may deliver the interaction to the background tab without transferring window focus. Since the browser UI (Omnibox) relies on native focus to determine the active tab, it remains associated with the tab showing the permission prompt, leading to UI origin confusion. This CL fixes the issue by overriding `DidGetUserInteraction` in `ContentsWebView`. When physical input (mouse down or touch start) is received by the unfocused WebContents, we explicitly call `RequestFocus()`. This forces the browser to synchronize the active tab state with the user's interaction, ensuring the Omnibox and privileged UI correctly reflect the newly interacted-with origin. An interactive_ui_test case was also added to verify the UI state transition. Additionally, this CL fixes a crash in `TwoClientSendTabToSelfSyncTest` that was exposed by the focus changes. The altered focus behavior caused an activation event to be triggered during browser teardown. Bug: 488762971 Test: Manual testing done to verify that the WebContents view is correctly activated on click when switching between split view tabs Change-Id: I2b9830c7a5c1926c99e7a63f297666de3843424d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7860845 Reviewed-by: Jood Hajeer <[email protected]> Reviewed-by: Caroline Rising <[email protected]> Commit-Queue: Hamzah Behery <[email protected]> Reviewed-by: Foromo Daniel Soromou <[email protected]> Cr-Commit-Position: refs/heads/main@{#1639383} --- diff --git a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc index c37926bf..1b5c33a 100644 --- a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc @@ -16,6 +16,7 @@ #include "chrome/browser/sync/test/integration/sync_test.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "components/history/core/browser/history_service.h" #include "components/send_tab_to_self/page_context.h" @@ -322,6 +323,10 @@ ->SendEntry(kUrl, "example", target_guid, context, send_tab_to_self::NavigationHistory(), base::DoNothing()); + // Ensure receiver browser is active so notification is handled immediately, + // as opposed to getting queued and executing during teardown. + GetBrowser(1)->window()->Activate(); + // Client 1: Wait for entry and fill. send_tab_to_self::SendTabToSelfSyncService* service1 = SendTabToSelfSyncServiceFactory::GetForProfile(GetProfile(1)); diff --git a/chrome/browser/ui/views/frame/contents_web_view.cc b/chrome/browser/ui/views/frame/contents_web_view.cc index b2cbadc..a831bfd 100644 --- a/chrome/browser/ui/views/frame/contents_web_view.cc +++ b/chrome/browser/ui/views/frame/contents_web_view.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/frame/contents_web_view.h" #include "base/debug/dump_without_crashing.h" +#include "build/build_config.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/views/frame/web_contents_close_handler.h" #include "chrome/browser/ui/views/status_bubble_views.h" @@ -13,6 +14,7 @@ #include "components/web_modal/web_contents_modal_dialog_manager.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" +#include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/color/color_provider.h" @@ -169,6 +171,22 @@ } } +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +void ContentsWebView::DidGetUserInteraction(const blink::WebInputEvent& event) { + // If the user interacts with the web contents, ensure it is activated. + // This handles cases where the native window does not receive a focus + // event, such as when a permission prompt is open in another split view. + if (event.GetType() == blink::WebInputEvent::Type::kMouseDown || + event.GetType() == blink::WebInputEvent::Type::kTouchStart) { + // RequestFocus() ensures the container view receives focus, + // which is sufficient to update the browser UI. + if (!HasFocus()) { + RequestFocus(); + } + } +} +#endif + void ContentsWebView::UpdateBackgroundColor() { const SkColor color = GetColorProvider()->GetColor( is_letterboxing() ? kColorWebContentsBackgroundLetterboxing diff --git a/chrome/browser/ui/views/frame/contents_web_view.h b/chrome/browser/ui/views/frame/contents_web_view.h index 902b0fc..dba4f32 100644 --- a/chrome/browser/ui/views/frame/contents_web_view.h +++ b/chrome/browser/ui/views/frame/contents_web_view.h @@ -8,6 +8,7 @@ #include <memory> #include "base/memory/raw_ptr.h" +#include "build/build_config.h" #include "chrome/browser/ui/views/frame/web_contents_close_handler_delegate.h" #include "chrome/common/buildflags.h" #include "ui/base/interaction/element_identifier.h" @@ -62,6 +63,15 @@ void OnLetterboxingChanged() override; void SetWebContents(content::WebContents* web_contents) override; + // content::WebContentsObserver overrides: + // Overridden to track physical interactions (mouse/touch) on the WebContents. + // This allows the browser to force focus synchronization in split view even + // when native OS focus gets stuck on a different window (like a permission + // prompt). +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + void DidGetUserInteraction(const blink::WebInputEvent& event) override; +#endif + // ui::View overrides: std::unique_ptr<ui::Layer> RecreateLayer() override; diff --git a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc index 55ea2c7c..77189d5f 100644 --- a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc @@ -28,6 +28,7 @@ #include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h" #include "chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h" #include "chrome/browser/ui/views/frame/scrim_view.h" +#include "chrome/browser/ui/views/page_info/page_info_main_view.h" #include "chrome/browser/ui/views/side_panel/side_panel.h" #include "chrome/browser/ui/views/test/split_view_interactive_test_mixin.h" #include "chrome/browser/ui/views/test/tab_strip_interactive_test_mixin.h" @@ -390,6 +391,20 @@ } // Check that MultiContentsView changes its active view when inactive view is +// focused using mouse click while a PageInfo bubble is open in the active view. +// This prevents UI origin confusion issues (e.g. b/488762971). +IN_PROC_BROWSER_TEST_P(MultiContentsViewUiTest, + ActivatesInactiveViewUsingMouseClickWithPageInfoOpen) { + RunTestSequence(CreateTabsAndEnterSplitView(), WaitForActiveTabChange(0), + // Open PageInfo bubble on the active tab (0). + PressButton(kLocationIconElementId), + // Click the inactive tab (1). + FocusInactiveTabInSplit(), + // Active tab should change to 1. + WaitForActiveTabChange(1), CheckActiveContentsHasFocus()); +} + +// Check that MultiContentsView changes its active view when inactive view is // focused using keyboard. IN_PROC_BROWSER_TEST_P(MultiContentsViewUiTest, ActivatesInactiveViewUsingKeyboard) {
Regression Test / PoC
diff --git a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
index c37926bf..1b5c33a 100644
--- a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
@@ -16,6 +16,7 @@
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/history/core/browser/history_service.h"
#include "components/send_tab_to_self/page_context.h"
@@ -322,6 +323,10 @@
->SendEntry(kUrl, "example", target_guid, context,
send_tab_to_self::NavigationHistory(), base::DoNothing());
+ // Ensure receiver browser is active so notification is handled immediately,
+ // as opposed to getting queued and executing during teardown.
+ GetBrowser(1)->window()->Activate();
+
// Client 1: Wait for entry and fill.
send_tab_to_self::SendTabToSelfSyncService* service1 =
SendTabToSelfSyncServiceFactory::GetForProfile(GetProfile(1));
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
index 55ea2c7c..77189d5f 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
@@ -28,6 +28,7 @@
#include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h"
#include "chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h"
#include "chrome/browser/ui/views/frame/scrim_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_main_view.h"
#include "chrome/browser/ui/views/side_panel/side_panel.h"
#include "chrome/browser/ui/views/test/split_view_interactive_test_mixin.h"
#include "chrome/browser/ui/views/test/tab_strip_interactive_test_mixin.h"
@@ -390,6 +391,20 @@
}
// Check that MultiContentsView changes its active view when inactive view is
+// focused using mouse click while a PageInfo bubble is open in the active view.
+// This prevents UI origin confusion issues (e.g. b/488762971).
+IN_PROC_BROWSER_TEST_P(MultiContentsViewUiTest,
+ ActivatesInactiveViewUsingMouseClickWithPageInfoOpen) {
+ RunTestSequence(CreateTabsAndEnterSplitView(), WaitForActiveTabChange(0),
+ // Open PageInfo bubble on the active tab (0).
+ PressButton(kLocationIconElementId),
+ // Click the inactive tab (1).
+ FocusInactiveTabInSplit(),
+ // Active tab should change to 1.
+ WaitForActiveTabChange(1), CheckActiveContentsHasFocus());
+}
+
+// Check that MultiContentsView changes its active view when inactive view is
// focused using keyboard.
IN_PROC_BROWSER_TEST_P(MultiContentsViewUiTest,
ActivatesInactiveViewUsingKeyboard) {
Original Bug Report
Omnibox shows wrong origin when clicking another page during permission prompt (Linux, split view)
Steps to reproduce the problem
When Chrome is in split view with two pages open side-by-side on Linux, the browser can lose track of the active origin while a permission prompt is visible. If the right-side page (permission.site) triggers a location permission prompt, and while that permission UI is visible the user clicks inside the left-side page (localhost:8000) which triggers the Payment Request API, Chrome does not correctly update the active origin. Even after interacting with localhost:8000, the omnibox continues to display permission.site as the active origin. As a result, user interaction occurs on localhost:8000 while the browser UI (address bar and permission context) remains associated with permission.site. This causes a mismatch between user interaction, omnibox origin, and privileged UI state, leading to UI origin confusion involving security-sensitive browser dialogs.
This issue reproduces on Linux, but does not reproduce on Windows.
Steps:
- Open http://localhost:8000/poc.html in Chrome.
- Right-click the “Go to permission.site” link.
- Select Location (this triggers a location permission prompt from permission.site).
- While the location permission prompt is visible, click anywhere inside localhost:8000/poc.html.
Problem Description
When a location permission prompt from permission.site is visible in split view on Linux, clicking inside localhost:8000 does not update the omnibox, causing the browser to show the wrong active origin and resulting in UI origin confusion.
Summary
Omnibox shows wrong origin when clicking another page during permission prompt (Linux, split view)
Additional Data
Category: Security
Chrome Channel: Not sure
Regression: N/A \