blob: 97b3e42c0baa510b10af9095d43dc8ea92c9c4ad [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/extensions/extension_popup.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/ui/extensions/extension_action_test_helper.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/permissions/permission_request_manager_test_api.h"
#include "components/permissions/request_type.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "extensions/test/test_extension_dir.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/test/widget_test.h"
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif
using ExtensionPopupInteractiveUiTest = extensions::ExtensionApiTest;
// Tests unloading an extension while its popup is actively under inspection.
// Regression test for https://crbug.com/1304499.
IN_PROC_BROWSER_TEST_F(ExtensionPopupInteractiveUiTest,
UnloadExtensionWhileInspectingPopup) {
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);
// Inspect the popup and wait for it to open.
{
extensions::ExtensionHostTestHelper popup_waiter(profile(),
extension->id());
popup_waiter.RestrictToType(extensions::mojom::ViewType::kExtensionPopup);
ExtensionActionTestHelper::Create(browser())->InspectPopup(extension->id());
popup_waiter.WaitForHostCompletedFirstLoad();
}
// Unload the extension. This causes the popup's ExtensionHost to be
// destroyed. This should be safe.
{
extensions::ExtensionHostTestHelper popup_waiter(profile(),
extension->id());
popup_waiter.RestrictToType(extensions::mojom::ViewType::kExtensionPopup);
extension_service()->DisableExtension(
extension->id(), extensions::disable_reason::DISABLE_USER_ACTION);
popup_waiter.WaitForHostDestroyed();
}
}
// Tests that the extension popup does not render over an anchored permissions
// bubble. Regression test for https://crbug.com/1300006.
IN_PROC_BROWSER_TEST_F(ExtensionPopupInteractiveUiTest,
ExtensionPopupOverPermissions) {
// Geolocation requires HTTPS. Since we programmatically show the geolocation
// prompt from C++ (rather than triggering the web API), this might not be
// strictly necessary, but is nice to have.
UseHttpsTestServer();
ASSERT_TRUE(StartEmbeddedTestServer());
// Load an extension with a popup.
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);
// Navigate to an https site.
const GURL secure_url =
embedded_test_server()->GetURL("example.com", "/simple.html");
EXPECT_EQ("https", secure_url.scheme());
content::RenderFrameHost* frame =
ui_test_utils::NavigateToURL(browser(), secure_url);
ASSERT_TRUE(frame);
// Show the geolocation permissions prompt.
test::PermissionRequestManagerTestApi permissions_api(browser());
ASSERT_TRUE(permissions_api.manager());
permissions_api.AddSimpleRequest(frame,
permissions::RequestType::kGeolocation);
// Sadly, there's no notification for these. All tests seem to rely on
// RunUntilIdle() being sufficient.
// TODO(https://crbug.com/1317865): Change this to be more deterministic.
base::RunLoop().RunUntilIdle();
// The permission may be shown using a chip UI instead of a popped-up bubble.
// If so, click on the chip to open the bubble.
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
LocationBarView* lbv = browser_view->toolbar()->location_bar();
if (lbv->chip_controller()->IsPermissionPromptChipVisible() &&
!lbv->chip_controller()->IsBubbleShowing()) {
views::test::ButtonTestApi(lbv->chip_controller()->chip())
.NotifyClick(ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, 0));
base::RunLoop().RunUntilIdle();
}
// The permissions bubble should now be showing.
ASSERT_TRUE(permissions_api.GetPromptWindow());
{
// Open the extension popup.
extensions::ExtensionHostTestHelper popup_waiter(profile(),
extension->id());
popup_waiter.RestrictToType(extensions::mojom::ViewType::kExtensionPopup);
ExtensionActionTestHelper::Create(browser())->Press(extension->id());
popup_waiter.WaitForHostCompletedFirstLoad();
}
ExtensionPopup* extension_popup = ExtensionPopup::last_popup_for_testing();
ASSERT_TRUE(extension_popup);
// Ensure the popup is visible (it shows asynchronously from resource load).
// This is safe to call even if the widget is already visible.
views::test::WidgetVisibleWaiter(extension_popup->GetWidget()).Wait();
EXPECT_TRUE(extension_popup->GetWidget()->IsVisible());
// Finally, verify that the extension popup is not on top of the permissions
// bubble.
const bool is_stacked_above = views::test::WidgetTest::IsWindowStackedAbove(
extension_popup->GetWidget(), permissions_api.GetPromptWindow());
#if BUILDFLAG(IS_MAC)
// Child window re-ordering is not reliable on macOS <= 10.13.
if (base::mac::IsAtMostOS10_13())
return;
#endif
EXPECT_FALSE(is_stacked_above);
}