Chrome · Extensions › Extensions
CVE-2026-113307
Memory Corruption in Extensions (Extensions)
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ExtensionPopupchrome/browser/ui/views/extensions/extension_popup.cc |
modified | |
ifchrome/browser/ui/views/extensions/extension_popup.cc |
modified | |
BrowserContextchrome/browser/ui/views/extensions/extension_popup.h |
modified | |
DevToolsAgentHostchrome/browser/ui/views/extensions/extension_popup.h |
modified | |
ScopedDevToolsAgentHostObservationchrome/browser/ui/views/extensions/extension_popup.h |
modified | |
forchrome/browser/ui/views/extensions/security_dialog_tracker.cc |
modified | |
ifchrome/browser/ui/views/extensions/security_dialog_tracker.cc |
modified |
Files Changed
chrome/browser/ui/views/extensions/BUILD.gnchrome/browser/ui/views/extensions/security_dialog_tracker_browsertest.ccchrome/browser/ui/views/extensions/extension_popup_interactive_uitest.ccchrome/browser/ui/views/extensions/security_dialog_tracker.ccchrome/browser/ui/views/extensions/extension_popup.ccchrome/browser/devtools/devtools_window.ccchrome/browser/ui/views/extensions/security_dialog_tracker.hchrome/browser/ui/views/extensions/extension_popup.h
Patch
From 4b8e494ea39fbac80a26968fa0189502a263bec6 Mon Sep 17 00:00:00 2001
From: Eva Su <evasu@chromium.org>
Date: Mon, 21 Sep 2026 14:07:25 -0700
Subject: [PATCH] [Extensions] Dismiss extension popup when security dialog is shown
Previously, an extension with the `debugger` permission could call
`chrome.debugger.attach()` to attach a Chrome DevTools Protocol client
to its own popup target. This triggered `DevToolsAgentHostAttached()`,
setting the popup inspection state and causing
`ExtensionPopup::CloseUnlessBlockedByInspectionOrJSDialog()` to return
early. As a result, the extension popup remained pinned open over
security-sensitive dialogs (such as WebAuthn, FedCM, or Payment
Request).
This CL addresses the issue by prioritizing security-sensitive dialogs
over DevTools inspection state. Extension popups are now force-closed
under all circumstances when a security dialog is present.
Specifically, this CL introduces the following changes:
- Updates `SecurityDialogTracker` to implement the Observer pattern. `ExtensionPopup` now observes the tracker and proactively dismisses itself via a deferred task when a new security dialog is added.
- Checks `SecurityDialogTracker::BrowserHasVisibleSecurityDialogs()` before checking DevTools inspection state in the close logic.
- Checks `DevToolsWindow::GetInstanceForInspectedWebContents()` instead of relying on generic CDP attachment events. This ensures that only an active user-facing DevTools window prevents popup auto-dismissal, rather than any programmatic `chrome.debugger` client.
- Updates `DevToolsWindow::GetInstanceForInspectedWebContents()` to ignore windows in the `kClosing` state, preventing race conditions
where closing DevTools triggers auto-close logic before destruction.
- Updates `ExtensionPopup` activation and dialog-tracking logic to safely ignore child or descendant widgets belonging to the popup itself, preventing the popup from erroneously dismissing itself when interacting with its own sub-elements.
Fixed: 514066180
Change-Id: I6b8f76900581929cf92e7382d1b026042306bc3f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8372287
Commit-Queue: Eva Su <evasu@chromium.org>
Reviewed-by: Tim <tjudkins@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1702043}
---
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 53c1cda3..7613579 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -640,7 +640,8 @@
return nullptr;
}
for (auto& instance : GetDevToolsWindowInstances()) {
- if (instance->GetInspectedWebContents() == inspected_web_contents) {
+ if (instance->GetInspectedWebContents() == inspected_web_contents &&
+ instance->life_stage_ != kClosing) {
return instance;
}
}
diff --git a/chrome/browser/ui/views/extensions/extension_popup.cc b/chrome/browser/ui/views/extensions/extension_popup.cc
index 6cbc396..7dbab95 100644
--- a/chrome/browser/ui/views/extensions/extension_popup.cc
+++ b/chrome/browser/ui/views/extensions/extension_popup.cc
@@ -7,6 +7,7 @@
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
+#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/extensions/extension_view_host.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -15,7 +16,6 @@
#include "components/javascript_dialogs/app_modal_dialog_queue.h"
#include "components/web_modal/web_modal_utils.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
@@ -49,31 +49,6 @@
// The most recently constructed popup; used for testing purposes.
ExtensionPopup* g_last_popup_for_testing = nullptr;
-// A helper class to scope the observation of DevToolsAgentHosts. We can't just
-// use base::ScopedObservation here because that requires a specific source
-// object, where as DevToolsAgentHostObservers are added to a singleton list.
-// The `observer_` passed into this object will be registered as an observer
-// for this object's lifetime.
-class ExtensionPopup::ScopedDevToolsAgentHostObservation {
- public:
- explicit ScopedDevToolsAgentHostObservation(
- content::DevToolsAgentHostObserver* observer)
- : observer_(observer) {
- content::DevToolsAgentHost::AddObserver(observer_);
- }
-
- ScopedDevToolsAgentHostObservation(
- const ScopedDevToolsAgentHostObservation&) = delete;
- ScopedDevToolsAgentHostObservation& operator=(
- const ScopedDevToolsAgentHostObservation&) = delete;
-
- ~ScopedDevToolsAgentHostObservation() {
- content::DevToolsAgentHost::RemoveObserver(observer_);
- }
-
- private:
- raw_ptr<content::DevToolsAgentHostObserver> observer_;
-};
// static
ExtensionPopup* ExtensionPopup::last_popup_for_testing() {
@@ -154,9 +129,9 @@
void ExtensionPopup::OnWidgetDestroying(views::Widget* widget) {
BubbleDialogDelegateView::OnWidgetDestroying(widget);
- scoped_devtools_observation_.reset();
anchor_widget_observation_.Reset();
extension_registry_observation_.Reset();
+ security_dialog_tracker_observation_.Reset();
extension_view_ = nullptr;
host_.reset();
browser_ = nullptr;
@@ -178,6 +153,23 @@
// TODO(crbug.com/326681253): don't show the popup if it might cover
// security-sensitive UIs.
if (active_widget != GetWidget()) {
+ // Ignore activation of child/descendant widgets belonging to this popup.
+ if (extensions::SecurityDialogTracker::IsWidgetOrDescendantOf(
+ active_widget, GetWidget())) {
+ return;
+ }
+
+ if (extensions::SecurityDialogTracker::GetInstance()
+ ->BrowserHasVisibleSecurityDialogs(browser_, GetWidget())) {
+ // Defer to avoid reentrant focus and window activation changes in
+ // FocusController while the security dialog is in the middle of being
+ // shown.
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ExtensionPopup::CloseIfSecurityDialogPresent,
+ deferred_close_weak_ptr_factory_.GetWeakPtr()));
+ return;
+ }
CloseUnlessBlockedByInspectionOrJSDialog();
}
}
@@ -219,12 +211,6 @@
// try to access the host during Widget closure, destroy it immediately.
RemoveChildViewT(extension_view_.ExtractAsDangling());
- // Note: it's important that we unregister the devtools observation *before*
- // we destroy `host_`. Otherwise, destroying `host_` can synchronously cause
- // the associated WebContents to be destroyed, which will cause devtools to
- // detach, which will notify our observer, where we rely on `host_` - all
- // synchronously.
- scoped_devtools_observation_.reset();
host_.reset();
// Stop observing the registry immediately to prevent any subsequent
// notifications, since Widget::Close is asynchronous.
@@ -250,20 +236,18 @@
}
}
-void ExtensionPopup::DevToolsAgentHostAttached(
- content::DevToolsAgentHost* agent_host) {
- DCHECK(host_);
- if (host_->host_contents() == agent_host->GetWebContents()) {
- inspected_ = true;
+void ExtensionPopup::OnSecurityDialogAdded(views::Widget* widget) {
+ // Don't close if the security dialog belongs to this popup.
+ if (extensions::SecurityDialogTracker::IsWidgetOrDescendantOf(widget,
+ GetWidget())) {
+ return;
}
-}
-void ExtensionPopup::DevToolsAgentHostDetached(
- content::DevToolsAgentHost* agent_host) {
- DCHECK(host_);
- if (host_->host_contents() == agent_host->GetWebContents()) {
- inspected_ = false;
- }
+ // Defer to avoid reentrant focus and window activation changes in
+ // FocusController while the security dialog is in the middle of being shown.
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE, base::BindOnce(&ExtensionPopup::CloseIfSecurityDialogPresent,
+ deferred_close_weak_ptr_factory_.GetWeakPtr()));
}
ExtensionPopup::ExtensionPopup(
@@ -301,13 +285,14 @@
// See comments in OnWidgetActivationChanged().
set_close_on_deactivate(false);
- scoped_devtools_observation_ =
- std::make_unique<ScopedDevToolsAgentHostObservation>(this);
browser_->GetTabStripModel()->AddObserver(this);
CHECK(anchor_widget());
anchor_widget_observation_.Observe(anchor_widget()->GetPrimaryWindowWidget());
+ security_dialog_tracker_observation_.Observe(
+ extensions::SecurityDialogTracker::GetInstance());
+
// Handle the containing view calling window.close();
// The base::Unretained() below is safe because this object owns `host_`, so
// the callback will never fire if `this` is deleted.
@@ -331,7 +316,7 @@
// Don't show the popup if there are visible security dialogs. This protects
// the security dialogs from spoofing.
if (extensions::SecurityDialogTracker::GetInstance()
- ->BrowserHasVisibleSecurityDialogs(browser_)) {
+ ->BrowserHasVisibleSecurityDialogs(browser_, GetWidget())) {
CloseDeferredIfNecessary();
return;
}
@@ -352,9 +337,19 @@
}
}
+void ExtensionPopup::CloseIfSecurityDialogPresent() {
+ if (!browser_ || !host_) {
+ return;
+ }
+
+ if (extensions::SecurityDialogTracker::GetInstance()
+ ->BrowserHasVisibleSecurityDialogs(browser_, GetWidget())) {
+ CloseDeferredIfNecessary(views::Widget::ClosedReason::kLostFocus);
+ }
+}
+
void ExtensionPopup::CloseUnlessBlockedByInspectionOrJSDialog() {
- // Don't close if the extension page is under inspection.
- if (inspected_) {
+ if (!browser_ || !host_) {
return;
}
@@ -372,6 +367,12 @@
return;
}
+ // Don't close if the extension page is under inspection in a DevTools window.
+ if (DevToolsWindow::GetInstanceForInspectedWebContents(
+ host_->host_contents())) {
+ return;
+ }
+
CloseDeferredIfNecessary(views::Widget::ClosedReason::kLostFocus);
}
diff --git a/chrome/browser/ui/views/extensions/extension_popup.h b/chrome/browser/ui/views/extensions/extension_popup.h
index cbc211a..ec99063 100644
--- a/chrome/browser/ui/views/extensions/extension_popup.h
+++ b/chrome/browser/ui/views/extensions/extension_popup.h
@@ -12,7 +12,7 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/views/extensions/extension_view_views.h"
-#include "content/public/browser/devtools_agent_host_observer.h"
+#include "chrome/browser/ui/views/extensions/security_dialog_tracker.h"
#include "content/public/browser/web_contents_observer.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_registry.h"
@@ -26,7 +26,6 @@
namespace content {
class BrowserContext;
-class DevToolsAgentHost;
} // namespace content
namespace extensions {
@@ -42,7 +41,7 @@
public extensions::ExtensionRegistryObserver,
public content::WebContentsObserver,
public TabStripModelObserver,
- public content::DevToolsAgentHostObserver {
+ public extensions::SecurityDialogTracker::Observer {
METADATA_HEADER(ExtensionPopup, views::BubbleDialogDelegateView)
public:
@@ -102,11 +101,8 @@
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
- // content::DevToolsAgentHostObserver:
- void DevToolsAgentHostAttached(
- content::DevToolsAgentHost* agent_host) override;
- void DevToolsAgentHostDetached(
- content::DevToolsAgentHost* agent_host) override;
+ // extensions::SecurityDialogTracker::Observer:
+ void OnSecurityDialogAdded(views::Widget* widget) override;
// Closes the popup immediately, if possible. On Mac, if a nested
// run loop is running, schedule a deferred close for after the
@@ -118,8 +114,6 @@
static ExtensionPopup* last_popup_for_testing();
private:
- class ScopedDevToolsAgentHostObservation;
-
ExtensionPopup(BrowserWindowInterface* browser,
std::unique_ptr<extensions::ExtensionViewHost> host,
views::BubbleAnchor anchor,
@@ -135,6 +129,10 @@
// 2. an open web dialog, e.g. JS alert.
void CloseUnlessBlockedByInspectionOrJSDialog();
+ // Closes the bubble if there is a security dialog present in the browser
+ // window.
+ void CloseIfSecurityDialogPresent();
+
// Handles a signal from the extension host to close.
void HandleCloseExtensionHost(extensions::ExtensionHost* host);
@@ -149,22 +147,18 @@
extensions::ExtensionRegistryObserver>
extension_registry_observation_{this};
+ base::ScopedObservation<extensions::SecurityDialogTracker,
+ extensions::SecurityDialogTracker::Observer>
+ security_dialog_tracker_observation_{this};
+
// Action (as requested by the caller) to be done when the popup gets shown.
const PopupShowAction show_action_;
- // Whether DevTools is currently inspecting the popup contents.
- bool inspected_ = false;
-
ShowPopupCallback shown_callback_;
base::ScopedObservation<views::Widget, views::WidgetObserver>
anchor_widget_observation_{this};
- // Note: This must be reset *before* `host_`. See note in
- // OnExtensionUnloaded().
- std::unique_ptr<ScopedDevToolsAgentHostObservation>
- scoped_devtools_observation_;
-
base::WeakPtrFactory<ExtensionPopup> deferred_close_weak_ptr_factory_;
};
diff --git a/chrome/browser/ui/views/extensions/security_dialog_tracker.cc b/chrome/browser/ui/views/extensions/security_dialog_tracker.cc
index 438742c1..f10819e3 100644
--- a/chrome/browser/ui/views/extensions/security_dialog_tracker.cc
+++ b/chrome/browser/ui/views/extensions/security_dialog_tracker.cc
@@ -41,6 +41,14 @@
return &*s_instance;
}
+void SecurityDialogTracker::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void SecurityDialogTracker::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
void SecurityDialogTracker::AddSecurityDialog(views::Widget* widget) {
views::View* root_view = widget->GetRootView();
if (root_view->GetProperty(kIdentifierViewKey)) {
@@ -50,6 +58,10 @@
// Add an invisible identifier view to the root view of the widget.
root_view->SetProperty(kIdentifierViewKey,
root_view->AddChildView(MakeIdentifierView()));
+
+ for (Observer& observer : observers_) {
+ observer.OnSecurityDialogAdded(widget);
+ }
}
void SecurityDialogTracker::RemoveSecurityDialog(views::Widget* widget) {
@@ -63,15 +75,46 @@
root_view->ClearProperty(kIdentifierViewKey);
}
+// static
+bool SecurityDialogTracker::IsWidgetOrDescendantOf(
+ const views::Widget* widget,
+ const views::Widget* ancestor) {
+ if (!widget || !ancestor) {
+ return false;
+ }
+ for (const views::Widget* w = widget; w; w = w->parent()) {
+ if (w == ancestor) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool SecurityDialogTracker::BrowserHasVisibleSecurityDialogs(
- BrowserWindowInterface* browser) const {
- const auto views = BrowserElementsViews::From(browser)->GetAllViews(
- kSecuritySensitiveDialogIdentifier);
+ BrowserWindowInterface* browser,
+ const views::Widget* ignore_widget) const {
+ if (!browser) {
+ return false;
+ }
+ auto* browser_elements = BrowserElementsViews::From(browser);
+ if (!browser_elements) {
+ return false;
+ }
+ const auto views =
+ browser_elements->GetAllViews(kSecuritySensitiveDialogIdentifier);
return std::any_of(views.begin(), views.end(),
- [](views::View* identifier_view) {
+ [ignore_widget](views::View* identifier_view) {
views::Widget* dialog_widget =
identifier_view->GetWidget();
- return dialog_widget && dialog_widget->IsVisible();
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/views/extensions/extension_popup_interactive_uitest.cc b/chrome/browser/ui/views/extensions/extension_popup_interactive_uitest.cc
index 1b56f9b3..2f0ad286 100644
--- a/chrome/browser/ui/views/extensions/extension_popup_interactive_uitest.cc
+++ b/chrome/browser/ui/views/extensions/extension_popup_interactive_uitest.cc
@@ -79,6 +79,12 @@
raw_ptr<content::WebContents> web_contents_;
};
+class CloseableTestDevToolsProtocolClient
+ : public content::TestDevToolsProtocolClient {
+ public:
+ CloseableTestDevToolsProtocolClient() { set_agent_host_can_close(); }
+};
+
views::UniqueWidgetPtr CreateTestTopLevelWidget() {
views::UniqueWidgetPtr widget = std::make_unique<views::Widget>();
views::Widget::InitParams params(
@@ -773,6 +779,158 @@
popup_waiter.WaitForHostDestroyed();
}
+// Tests that an extension popup under active DevTools inspection is dismissed
+// when a security dialog is shown.
+IN_PROC_BROWSER_TEST_F(ExtensionPopupInteractiveUiTest,
+ InspectedPopupClosesWhenSecurityDialogShown) {
+ static constexpr char kManifest[] =
+ R"({
+ "name": "Test Extension",
+ "manifest_version": 3,
+ "action": { "default_popup": "popup.html" },
+ "version": "0.1"
+ })";
+
+ extensions::TestExtensionDir test_dir;
+ test_dir.WriteManifest(kManifest);
+ test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"),
+ "<html>Hello, world!</html>");
+ const extensions::Extension* extension =
+ LoadExtension(test_dir.UnpackedPath());
+ ASSERT_TRUE(extension);
+
+ // Open the popup and inspect it.
+ extensions::ExtensionHostTestHelper popup_waiter(profile(), extension->id());
+ popup_waiter.RestrictToType(extensions::mojom::ViewType::kExtensionPopup);
+ ExtensionActionTestHelper::Create(browser())->InspectPopup(extension->id());
+ popup_waiter.WaitForHostCompletedFirstLoad();
+ content::WebContents* extension_contents =
+ ExtensionPopup::last_popup_for_testing()->host()->host_contents();
+ ASSERT_NE(extension_contents, nullptr);
+ DevToolsAttachWaiter(extension_contents).Wait();
+
+ base::WeakPtr<views::Widget> extension_popup_widget =
+ ExtensionPopup::last_popup_for_testing()->GetWidget()->GetWeakPtr();
+ ASSERT_TRUE(extension_popup_widget);
+ views::test::WidgetVisibleWaiter(extension_popup_widget.get()).Wait();
+ EXPECT_TRUE(extension_popup_widget->IsVisible());
+
+ // Show a security dialog.
+ BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+ views::UniqueWidgetPtr security_widget =
+ CreateTestDialogWidget(browser_view->GetWidget());
+ extensions::SecurityDialogTracker::GetInstance()->AddSecurityDialog(
+ security_widget.get());
+ security_widget->Show();
+ views::test::WidgetVisibleWaiter(security_widget.get()).Wait();
+
+ // The popup should be dismissed even though it was under inspection.
+ ExpectWidgetDestroy(extension_popup_widget);
+}
+
+// Tests that an extension popup attached to a DevTools protocol client
+// (emulating chrome.debugger.attach()) is dismissed when a security dialog is
+// shown.
+IN_PROC_BROWSER_TEST_F(
+ ExtensionPopupInteractiveUiTest,
+ DevToolsProtocolClientAttachedPopupClosesWhenSecurityDialogShown) {
+ static constexpr char kManifest[] =
+ R"({
+ "name": "Test Extension",
+ "manifest_version": 3,
+ "action": { "default_popup": "popup.html" },
+ "version": "0.1"
+ })";
+
+ extensions::TestExtensionDir test_dir;
+ test_dir.WriteManifest(kManifest);
+ test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"),
+ "<html>Hello, world!</html>");
+ const extensions::Extension* extension =
+ LoadExtension(test_dir.UnpackedPath());
+ ASSERT_TRUE(extension);
+
+ // Open the popup.
+ base::WeakPtr<views::Widget> extension_popup_widget =
+ OpenExtensionPopup(browser(), extension);
+ ASSERT_TRUE(extension_popup_widget);
+ EXPECT_TRUE(extension_popup_widget->IsVisible());
+
+ content::WebContents* extension_contents =
+ ExtensionPopup::last_popup_for_testing()->host()->host_contents();
+ ASSERT_NE(extension_contents, nullptr);
+
+ // Attach a DevTools protocol client (emulating chrome.debugger.attach()).
+ CloseableTestDevToolsProtocolClient devtools_client;
+ devtools_client.AttachToWebContents(extension_contents);
+
+ // Show a security dialog.
+ BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+ views::UniqueWidgetPtr security_widget =
+ CreateTestDialogWidget(browser_view->GetWidget());
+ extensions::SecurityDialogTracker::GetInstance()->AddSecurityDialog(
+ security_widget.get());
+ security_widget->Show();
+ views::test::WidgetVisibleWaiter(security_widget.get()).Wait();
+
+ // The popup should be dismissed.
+ ExpectWidgetDestroy(extension_popup_widget);
+}
+
+// Tests that an extension popup in one browser window does not close when a
+// security dialog is shown in a different browser window.
+IN_PROC_BROWSER_TEST_F(
+ ExtensionPopupInteractiveUiTest,
+ PopupDoesNotCloseWhenSecurityDialogShownInDifferentBrowser) {
+ static constexpr char kManifest[] =
+ R"({
+ "name": "Test Extension",
+ "manifest_version": 3,
+ "action": { "default_popup": "popup.html" },
+ "version": "0.1"
+ })";
+
+ extensions::TestExtensionDir test_dir;
+ test_dir.WriteManifest(kManifest);
+ test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"),
+ "<html>Hello, world!</html>");
+ const extensions::Extension* extension =
+ LoadExtension(test_dir.UnpackedPath());
+ ASSERT_TRUE(extension);
+
+ // Open the popup in the first browser.
+ base::WeakPtr<views::Widget> extension_popup_widget =
+ OpenExtensionPopup(browser(), extension);
+ ASSERT_TRUE(extension_popup_widget);
+ EXPECT_TRUE(extension_popup_widget->IsVisible());
+
+ // Create a second browser window and show a security dialog in it.
+ BrowserWindowInterface* second_browser = CreateBrowser(profile());
+ BrowserView* second_browser_view =
+ BrowserView::GetBrowserViewForBrowser(second_browser);
+ views::UniqueWidgetPtr security_widget =
+ CreateTestDialogWidget(second_browser_view->GetWidget());
+ extensions::SecurityDialogTracker::GetInstance()->AddSecurityDialog(
+ security_widget.get());
+ security_widget->Show();
+ views::test::WidgetVisibleWaiter(security_widget.get()).Wait();
+
+ // Flush the deferred CloseIfSecurityDialogPresent task.
+ base::RunLoop run_loop;
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE, run_loop.QuitClosure());
+ run_loop.Run();
+
+ // The popup in the first browser should remain open.
+ EXPECT_TRUE(extension_popup_widget);
+ EXPECT_TRUE(extension_popup_widget->IsVisible());
+
+ // Clean up.
+ security_widget->Close();
+ views::test::WidgetDestroyedWaiter(security_widget.get()).Wait();
+ CloseBrowserSynchronously(second_browser);
+}
+
// Tests that pressing Escape in an extension popup does not close the popup
// if the keydown event is default-prevented by JavaScript.
IN_PROC_BROWSER_TEST_F(ExtensionPopupInteractiveUiTest,
diff --git a/chrome/browser/ui/views/extensions/security_dialog_tracker_browsertest.cc b/chrome/browser/ui/views/extensions/security_dialog_tracker_browsertest.cc
index 5618d0a..327060d 100644
--- a/chrome/browser/ui/views/extensions/security_dialog_tracker_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/security_dialog_tracker_browsertest.cc
@@ -47,6 +47,20 @@
views::test::WidgetVisibleWaiter(security_widget.get()).Wait();
// Security dialog is now visible.
EXPECT_TRUE(tracker->BrowserHasVisibleSecurityDialogs(browser()));
+ EXPECT_FALSE(tracker->BrowserHasVisibleSecurityDialogs(
+ browser(), security_widget.get()));
+ EXPECT_FALSE(tracker->BrowserHasVisibleSecurityDialogs(nullptr));
+
+ EXPECT_TRUE(SecurityDialogTracker::IsWidgetOrDescendantOf(
+ security_widget.get(), security_widget.get()));
+ EXPECT_TRUE(SecurityDialogTracker::IsWidgetOrDescendantOf(
+ security_widget.get(), browser_view->GetWidget()));
+ EXPECT_FALSE(SecurityDialogTracker::IsWidgetOrDescendantOf(
+ browser_view->GetWidget(), security_widget.get()));
+ EXPECT_FALSE(SecurityDialogTracker::IsWidgetOrDescendantOf(
+ nullptr, security_widget.get()));
+ EXPECT_FALSE(SecurityDialogTracker::IsWidgetOrDescendantOf(
+ security_widget.get(), nullptr));
BrowserWindowInterface* new_browser = CreateBrowser(browser()->GetProfile());
// No security dialogs under a different browser.
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.
References
On This Page