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 <[email protected]> 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 <[email protected]> Reviewed-by: Tim <[email protected]> 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