Chrome · Extensions › Extensions
CVE-2026-113137
Memory Corruption in Extensions (Extensions)
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/tabs/organizer/organizer_panel_controller.cc |
modified | |
TEST_Fchrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc |
modified | |
ifchrome/browser/ui/tabs/organizer/BUILD.gn |
modified | |
source_setchrome/browser/ui/tabs/organizer/BUILD.gn |
modified |
Files Changed
chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.ccchrome/browser/ui/tabs/organizer/BUILD.gnchrome/browser/ui/extensions/extension_side_panel_manager.ccchrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.ccchrome/browser/extensions/extension_view_host_factory_browsertest.ccchrome/browser/ui/tabs/organizer/organizer_panel_controller.ccchrome/browser/ui/views/tabs/organizer/organizer_panel_extension_view.ccchrome/browser/extensions/extension_view_host_factory.cc
Patch
From a8a06c4fcadf7991beaa9e4087a0c49997faa0e4 Mon Sep 17 00:00:00 2001
From: Kelvin Jiang <kelvinjiang@chromium.org>
Date: Wed, 16 Sep 2026 14:19:46 -0700
Subject: [PATCH] [Extensions] Fix side panel incognito bypass by spanning mode extensions
ExtensionRegistry redirects to the original (regular) profile when
queried in incognito. Consequently,
ExtensionRegistry::enabled_extensions() returns all regular-profile
extensions regardless of whether they have been granted incognito
access.
This led to an incognito isolation bypass across two layers: 1.
ExtensionSidePanelManager: When associated with an incognito window
or tab, it registered SidePanelEntry items and action items without
verifying if the extension was enabled in incognito.
2. ExtensionViewHostFactory::CreateViewHostForIncognito(): Spanning mode
(!IsSplitMode) extensions were redirected to the original profile's
process without checking incognito enablement. Even though the host
belongs to the original profile, its delegate is bound to the incognito
window/tab, allowing an unauthorized extension to access incognito state.
This CL enforces defense-in-depth:
- ExtensionSidePanelManager checks util::IsIncognitoEnabled() before
creating coordinators in off-the-record contexts.
- ExtensionViewHostFactory::CreateViewHostForIncognito() rejects host
creation if util::IsIncognitoEnabled() returns false.
- Adds browser tests verifying that unauthorized extensions cannot
register entries, show side panels, or instantiate view hosts in incognito.
Fixed: 513758047
Change-Id: I4583d66bb68e5508bd0b5e14553a260ca442e75a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8392718
Auto-Submit: Kelvin Jiang <kelvinjiang@chromium.org>
Reviewed-by: Dana Fried <dfried@chromium.org>
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Commit-Queue: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1699265}
---
diff --git a/chrome/browser/extensions/extension_view_host_factory.cc b/chrome/browser/extensions/extension_view_host_factory.cc
index e8e5e53..10be173 100644
--- a/chrome/browser/extensions/extension_view_host_factory.cc
+++ b/chrome/browser/extensions/extension_view_host_factory.cc
@@ -205,8 +205,9 @@
std::move(delegate));
}
-// Creates a view host for an extension in an incognito window. Returns NULL
-// if the extension is not allowed to run in incognito.
+// Creates a view host for an extension in an incognito window. This must only
+// be called for an incognito profile and for an extension that has been granted
+// incognito access.
std::unique_ptr<ExtensionViewHost> CreateViewHostForIncognito(
const Extension* extension,
const GURL& url,
@@ -216,6 +217,15 @@
DCHECK(extension);
DCHECK(profile->IsOffTheRecord());
+ // Callers must ensure the extension is allowed to run in incognito before
+ // attempting to create a host. Note that even in spanning mode (!IsSplitMode)
+ // where the host process belongs to the original profile, `delegate` is bound
+ // to the incognito browser window or tab. Creating a host would allow the
+ // regular-profile extension to access and manipulate incognito state (e.g.
+ // via APIs that resolve kCurrentWindowId through the host's delegate) without
+ // incognito permission.
+ CHECK(util::IsIncognitoEnabled(extension->id(), profile));
+
if (!IncognitoInfo::IsSplitMode(extension)) {
// If it's not split-mode the host is associated with the original profile.
Profile* original_profile = profile->GetOriginalProfile();
@@ -223,13 +233,8 @@
view_type, std::move(delegate));
}
- // Create the host if the extension can run in incognito.
- if (util::IsIncognitoEnabled(extension->id(), profile)) {
- return CreateViewHostForExtension(extension, url, profile, view_type,
- std::move(delegate));
- }
- NOTREACHED() << "We shouldn't be trying to create an incognito extension "
- "view unless it has been enabled for incognito.";
+ return CreateViewHostForExtension(extension, url, profile, view_type,
+ std::move(delegate));
}
std::unique_ptr<ExtensionViewHost> CreateExtensionViewHost(
diff --git a/chrome/browser/ui/extensions/extension_side_panel_manager.cc b/chrome/browser/ui/extensions/extension_side_panel_manager.cc
index 716ff76..dcc0cb46 100644
--- a/chrome/browser/ui/extensions/extension_side_panel_manager.cc
+++ b/chrome/browser/ui/extensions/extension_side_panel_manager.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/ui/extensions/extension_side_panel_coordinator.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_util.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
@@ -88,13 +89,22 @@
void ExtensionSidePanelManager::MaybeCreateExtensionSidePanelCoordinator(
const Extension* extension) {
- if (extension->permissions_data()->HasAPIPermission(
+ if (!extension->permissions_data()->HasAPIPermission(
mojom::APIPermissionID::kSidePanel)) {
- coordinators_.emplace(extension->id(),
- std::make_unique<ExtensionSidePanelCoordinator>(
- profile_, browser_, tab_interface_, extension,
- registry_, for_tab_));
+ return;
}
+
+ // In incognito, only create a coordinator for extensions that are allowed to
+ // run in incognito.
+ if (profile_->IsOffTheRecord() &&
+ !util::IsIncognitoEnabled(extension->id(), profile_)) {
+ return;
+ }
+
+ coordinators_.emplace(
+ extension->id(),
+ std::make_unique<ExtensionSidePanelCoordinator>(
+ profile_, browser_, tab_interface_, extension, registry_, for_tab_));
}
} // namespace extensions
diff --git a/chrome/browser/ui/tabs/organizer/organizer_panel_controller.cc b/chrome/browser/ui/tabs/organizer/organizer_panel_controller.cc
index 0ae414e0..a2f8702 100644
--- a/chrome/browser/ui/tabs/organizer/organizer_panel_controller.cc
+++ b/chrome/browser/ui/tabs/organizer/organizer_panel_controller.cc
@@ -30,6 +30,11 @@
#include "ui/views/view_tracker.h"
#include "ui/views/view_utils.h"
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/profiles/profile.h"
+#include "extensions/browser/extension_util.h"
+#endif
+
DEFINE_USER_DATA(OrganizerPanelController);
// Handles the work of moving the panel view around to the correct host based on
@@ -167,6 +172,12 @@
#if BUILDFLAG(ENABLE_EXTENSIONS)
void OrganizerPanelController::OpenForExtension(
const extensions::ExtensionId& extension_id) {
+ Profile* const profile = browser_window_->GetProfile();
+ if (profile->IsOffTheRecord() &&
+ !extensions::util::IsIncognitoEnabled(extension_id, profile)) {
+ return;
+ }
+
if (is_visible_ && active_extension_id_ == extension_id) {
return;
}
diff --git a/chrome/browser/ui/views/tabs/organizer/organizer_panel_extension_view.cc b/chrome/browser/ui/views/tabs/organizer/organizer_panel_extension_view.cc
index 97a78dac..4382e55 100644
--- a/chrome/browser/ui/views/tabs/organizer/organizer_panel_extension_view.cc
+++ b/chrome/browser/ui/views/tabs/organizer/organizer_panel_extension_view.cc
@@ -17,6 +17,7 @@
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/organizer/organizer_panel_controller.h"
#include "chrome/browser/ui/views/tabs/organizer/organizer_panel_utils.h"
+#include "extensions/browser/extension_util.h"
#include "ui/views/view_class_properties.h"
OrganizerPanelExtensionView::OrganizerPanelExtensionView(
@@ -150,6 +151,12 @@
}
for (const auto& extension : registry->enabled_extensions()) {
+ if (browser_->GetProfile()->IsOffTheRecord() &&
+ !extensions::util::IsIncognitoEnabled(extension->id(),
+ browser_->GetProfile())) {
+ continue;
+ }
+
auto options = service->GetOptions(*extension, std::nullopt);
if (options.enabled.value_or(false) && options.path.has_value()) {
UpdateExtensionContent(extension->id());
diff --git a/chrome/browser/extensions/extension_view_host_factory_browsertest.cc b/chrome/browser/extensions/extension_view_host_factory_browsertest.cc
index 9a4ba32..58595412 100644
--- a/chrome/browser/extensions/extension_view_host_factory_browsertest.cc
+++ b/chrome/browser/extensions/extension_view_host_factory_browsertest.cc
@@ -29,7 +29,7 @@
LoadExtension(test_data_dir_.AppendASCII("api_test")
.AppendASCII("browser_action")
.AppendASCII("none"));
- ASSERT_TRUE(extension.get());
+ ASSERT_TRUE(extension);
content::BrowserContext* browser_context = profile();
@@ -54,7 +54,7 @@
LoadExtension(test_data_dir_.AppendASCII("api_test")
.AppendASCII("side_panel")
.AppendASCII("simple_default"));
- ASSERT_TRUE(extension.get());
+ ASSERT_TRUE(extension);
content::BrowserContext* browser_context = profile();
diff --git a/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc b/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc
index e5a6a05..0c8f5a5 100644
--- a/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc
+++ b/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc
@@ -137,6 +137,25 @@
EXPECT_FALSE(controller()->IsOrganizerPanelVisible());
EXPECT_FALSE(controller()->active_extension_id().has_value());
}
+
+TEST_F(OrganizerPanelControllerTest, IncognitoRejectsUnauthorizedExtension) {
+ Profile* const incognito_profile =
+ profile_.GetPrimaryOTRProfile(/*create_if_needed=*/true);
+ EXPECT_CALL(mock_browser_window_interface_, GetProfile())
+ .WillRepeatedly(testing::Return(incognito_profile));
+
+ const extensions::ExtensionId kExt = "abcdefghijklmnopabcdefghijklmnop";
+
+ // Opening for an unauthorized extension in incognito should do nothing.
+ controller()->OpenForExtension(kExt);
+ EXPECT_FALSE(controller()->IsOrganizerPanelVisible());
+ EXPECT_FALSE(controller()->active_extension_id().has_value());
+
+ // Toggling while closed also should do nothing.
+ controller()->ToggleForExtension(kExt);
+ EXPECT_FALSE(controller()->IsOrganizerPanelVisible());
+ EXPECT_FALSE(controller()->active_extension_id().has_value());
+}
#endif
// Test suite that ensures that the panel gets parented to the tray and the
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
index 452bb5f..088b64e3 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
@@ -21,6 +21,8 @@
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/extension_view_host_factory.h"
#include "chrome/browser/metrics/critical_user_journeys/critical_user_journey_session.h"
#include "chrome/browser/metrics/critical_user_journeys/features.h"
#include "chrome/browser/ui/browser_actions.h"
@@ -65,7 +67,9 @@
#include "content/public/test/test_utils.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/extension_host_test_helper.h"
+#include "extensions/browser/extension_util.h"
#include "extensions/browser/test_event_router_observer.h"
+#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/browser/test_image_loader.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_builder.h"
@@ -505,6 +509,89 @@
EXPECT_FALSE(GetActionItemForExtension(extension.get(), browser_actions));
}
+// Test that an extension's SidePanelEntry, coordinator, and action item are not
+// registered for an incognito window or tab when the extension is not allowed
+// to run in incognito.
+// Regression test for crbug.com/513758047.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
+ NoEntryInIncognitoBrowserWithoutIncognitoAccess) {
+ // Load an extension that is not allowed to run in incognito.
+ scoped_refptr<const extensions::Extension> extension = LoadExtension(
+ test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
+ ASSERT_TRUE(extension);
+ ASSERT_FALSE(util::IsIncognitoEnabled(extension->id(), profile()));
+ SidePanelEntry::Key extension_key = GetKey(extension->id());
+
+ // The extension's entry is registered for the on-the-record browser.
+ EXPECT_TRUE(
+ SidePanelRegistry::From(browser())->GetEntryForKey(extension_key));
+
+ // Open an incognito browser window.
+ BrowserWindowInterface* incognito_browser =
+ OpenURLOffTheRecord(browser()->GetProfile(), GURL("about:blank"));
+ ASSERT_TRUE(incognito_browser);
+
+ // The extension's entry, coordinator, and action item should not be
+ // registered for the incognito window or its active tab.
+ EXPECT_FALSE(SidePanelRegistry::From(incognito_browser)
+ ->GetEntryForKey(extension_key));
+ EXPECT_FALSE(
+ SidePanelRegistry::From(incognito_browser->GetActiveTabInterface())
+ ->GetEntryForKey(extension_key));
+ EXPECT_FALSE(ExtensionSidePanelManager::From(incognito_browser)
+ ->GetExtensionCoordinatorForTesting(extension->id()));
+ EXPECT_FALSE(GetActionItemForExtension(
+ extension.get(), BrowserActions::From(incognito_browser)));
+
+ // Enable the extension in incognito while the incognito window is already
+ // open.
+ {
+ TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()),
+ extension->id());
+ util::SetIsIncognitoEnabled(extension->id(), profile(), true);
+ extension = observer.WaitForExtensionLoaded();
+ }
+ ASSERT_TRUE(util::IsIncognitoEnabled(extension->id(), profile()));
+
+ // The extension's entry, coordinator, and action item should now be
+ // registered for the already-open incognito window (without creating a new
+ // side panel registry).
+ EXPECT_TRUE(SidePanelRegistry::From(incognito_browser)
+ ->GetEntryForKey(extension_key));
+ EXPECT_TRUE(ExtensionSidePanelManager::From(incognito_browser)
+ ->GetExtensionCoordinatorForTesting(extension->id()));
+ EXPECT_TRUE(GetActionItemForExtension(
+ extension.get(), BrowserActions::From(incognito_browser)));
+}
+
+// Test that an extension's SidePanelEntry, coordinator, and action item are
+// registered for an incognito window when the extension is allowed to run in
+// incognito.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
+ EntryInIncognitoBrowserWithIncognitoAccess) {
+ // Load an extension that is allowed to run in incognito.
+ scoped_refptr<const extensions::Extension> extension = LoadExtension(
+ test_data_dir_.AppendASCII("api_test/side_panel/simple_default"),
+ {.allow_in_incognito = true});
+ ASSERT_TRUE(extension);
+ ASSERT_TRUE(util::IsIncognitoEnabled(extension->id(), profile()));
+ SidePanelEntry::Key extension_key = GetKey(extension->id());
+
+ // Open an incognito browser window.
+ BrowserWindowInterface* incognito_browser =
+ OpenURLOffTheRecord(browser()->GetProfile(), GURL("about:blank"));
+ ASSERT_TRUE(incognito_browser);
+
+ // The extension's entry, coordinator, and action item should be registered
+ // for the incognito window.
+ EXPECT_TRUE(SidePanelRegistry::From(incognito_browser)
+ ->GetEntryForKey(extension_key));
+ EXPECT_TRUE(ExtensionSidePanelManager::From(incognito_browser)
+ ->GetExtensionCoordinatorForTesting(extension->id()));
+ EXPECT_TRUE(GetActionItemForExtension(
+ extension.get(), BrowserActions::From(incognito_browser)));
+}
+
// Test that the shared action item is created when the first SidePanelEntry
// referencing it is registered and removed when the last one is deregistered.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
diff --git a/chrome/browser/ui/tabs/organizer/BUILD.gn b/chrome/browser/ui/tabs/organizer/BUILD.gn
index c8b507c..5672ea5 100644
--- a/chrome/browser/ui/tabs/organizer/BUILD.gn
+++ b/chrome/browser/ui/tabs/organizer/BUILD.gn
@@ -48,6 +48,10 @@
"//components/prefs",
"//ui/views",
]
+
+ if (enable_extensions) {
+ deps += [ "//extensions/browser" ]
+ }
}
source_set("unit_tests") {
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/extensions/extension_view_host_factory_browsertest.cc b/chrome/browser/extensions/extension_view_host_factory_browsertest.cc
index 9a4ba32..58595412 100644
--- a/chrome/browser/extensions/extension_view_host_factory_browsertest.cc
+++ b/chrome/browser/extensions/extension_view_host_factory_browsertest.cc
@@ -29,7 +29,7 @@
LoadExtension(test_data_dir_.AppendASCII("api_test")
.AppendASCII("browser_action")
.AppendASCII("none"));
- ASSERT_TRUE(extension.get());
+ ASSERT_TRUE(extension);
content::BrowserContext* browser_context = profile();
@@ -54,7 +54,7 @@
LoadExtension(test_data_dir_.AppendASCII("api_test")
.AppendASCII("side_panel")
.AppendASCII("simple_default"));
- ASSERT_TRUE(extension.get());
+ ASSERT_TRUE(extension);
content::BrowserContext* browser_context = profile();
diff --git a/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc b/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc
index e5a6a05..0c8f5a5 100644
--- a/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc
+++ b/chrome/browser/ui/tabs/organizer/organizer_panel_controller_unittest.cc
@@ -137,6 +137,25 @@
EXPECT_FALSE(controller()->IsOrganizerPanelVisible());
EXPECT_FALSE(controller()->active_extension_id().has_value());
}
+
+TEST_F(OrganizerPanelControllerTest, IncognitoRejectsUnauthorizedExtension) {
+ Profile* const incognito_profile =
+ profile_.GetPrimaryOTRProfile(/*create_if_needed=*/true);
+ EXPECT_CALL(mock_browser_window_interface_, GetProfile())
+ .WillRepeatedly(testing::Return(incognito_profile));
+
+ const extensions::ExtensionId kExt = "abcdefghijklmnopabcdefghijklmnop";
+
+ // Opening for an unauthorized extension in incognito should do nothing.
+ controller()->OpenForExtension(kExt);
+ EXPECT_FALSE(controller()->IsOrganizerPanelVisible());
+ EXPECT_FALSE(controller()->active_extension_id().has_value());
+
+ // Toggling while closed also should do nothing.
+ controller()->ToggleForExtension(kExt);
+ EXPECT_FALSE(controller()->IsOrganizerPanelVisible());
+ EXPECT_FALSE(controller()->active_extension_id().has_value());
+}
#endif
// Test suite that ensures that the panel gets parented to the tray and the
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
index 452bb5f..088b64e3 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
@@ -21,6 +21,8 @@
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/extension_view_host_factory.h"
#include "chrome/browser/metrics/critical_user_journeys/critical_user_journey_session.h"
#include "chrome/browser/metrics/critical_user_journeys/features.h"
#include "chrome/browser/ui/browser_actions.h"
@@ -65,7 +67,9 @@
#include "content/public/test/test_utils.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/extension_host_test_helper.h"
+#include "extensions/browser/extension_util.h"
#include "extensions/browser/test_event_router_observer.h"
+#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/browser/test_image_loader.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_builder.h"
@@ -505,6 +509,89 @@
EXPECT_FALSE(GetActionItemForExtension(extension.get(), browser_actions));
}
+// Test that an extension's SidePanelEntry, coordinator, and action item are not
+// registered for an incognito window or tab when the extension is not allowed
+// to run in incognito.
+// Regression test for crbug.com/513758047.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
+ NoEntryInIncognitoBrowserWithoutIncognitoAccess) {
+ // Load an extension that is not allowed to run in incognito.
+ scoped_refptr<const extensions::Extension> extension = LoadExtension(
+ test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
+ ASSERT_TRUE(extension);
+ ASSERT_FALSE(util::IsIncognitoEnabled(extension->id(), profile()));
+ SidePanelEntry::Key extension_key = GetKey(extension->id());
+
+ // The extension's entry is registered for the on-the-record browser.
+ EXPECT_TRUE(
+ SidePanelRegistry::From(browser())->GetEntryForKey(extension_key));
+
+ // Open an incognito browser window.
+ BrowserWindowInterface* incognito_browser =
+ OpenURLOffTheRecord(browser()->GetProfile(), GURL("about:blank"));
+ ASSERT_TRUE(incognito_browser);
+
+ // The extension's entry, coordinator, and action item should not be
+ // registered for the incognito window or its active tab.
+ EXPECT_FALSE(SidePanelRegistry::From(incognito_browser)
+ ->GetEntryForKey(extension_key));
+ EXPECT_FALSE(
+ SidePanelRegistry::From(incognito_browser->GetActiveTabInterface())
+ ->GetEntryForKey(extension_key));
+ EXPECT_FALSE(ExtensionSidePanelManager::From(incognito_browser)
+ ->GetExtensionCoordinatorForTesting(extension->id()));
+ EXPECT_FALSE(GetActionItemForExtension(
+ extension.get(), BrowserActions::From(incognito_browser)));
+
+ // Enable the extension in incognito while the incognito window is already
+ // open.
+ {
+ TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()),
+ extension->id());
+ util::SetIsIncognitoEnabled(extension->id(), profile(), true);
+ extension = observer.WaitForExtensionLoaded();
+ }
+ ASSERT_TRUE(util::IsIncognitoEnabled(extension->id(), profile()));
+
+ // The extension's entry, coordinator, and action item should now be
+ // registered for the already-open incognito window (without creating a new
+ // side panel registry).
+ EXPECT_TRUE(SidePanelRegistry::From(incognito_browser)
+ ->GetEntryForKey(extension_key));
+ EXPECT_TRUE(ExtensionSidePanelManager::From(incognito_browser)
+ ->GetExtensionCoordinatorForTesting(extension->id()));
+ EXPECT_TRUE(GetActionItemForExtension(
+ extension.get(), BrowserActions::From(incognito_browser)));
+}
+
+// Test that an extension's SidePanelEntry, coordinator, and action item are
+// registered for an incognito window when the extension is allowed to run in
+// incognito.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
+ EntryInIncognitoBrowserWithIncognitoAccess) {
+ // Load an extension that is allowed to run in incognito.
+ scoped_refptr<const extensions::Extension> extension = LoadExtension(
+ test_data_dir_.AppendASCII("api_test/side_panel/simple_default"),
+ {.allow_in_incognito = true});
+ ASSERT_TRUE(extension);
+ ASSERT_TRUE(util::IsIncognitoEnabled(extension->id(), profile()));
+ SidePanelEntry::Key extension_key = GetKey(extension->id());
+
+ // Open an incognito browser window.
+ BrowserWindowInterface* incognito_browser =
+ OpenURLOffTheRecord(browser()->GetProfile(), GURL("about:blank"));
+ ASSERT_TRUE(incognito_browser);
+
+ // The extension's entry, coordinator, and action item should be registered
+ // for the incognito window.
+ EXPECT_TRUE(SidePanelRegistry::From(incognito_browser)
+ ->GetEntryForKey(extension_key));
+ EXPECT_TRUE(ExtensionSidePanelManager::From(incognito_browser)
+ ->GetExtensionCoordinatorForTesting(extension->id()));
+ EXPECT_TRUE(GetActionItemForExtension(
+ extension.get(), BrowserActions::From(incognito_browser)));
+}
+
// Test that the shared action item is created when the first SidePanelEntry
// referencing it is registered and removed when the last one is deregistered.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
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