blob: 1a14ab655923511b0f6adb6b279bcd1da83f98c3 [file] [log] [blame]
// Copyright 2021 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/extensions_toolbar_unittest.h"
#include <algorithm>
#include "base/command_line.h"
#include "base/containers/to_vector.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/permissions/scripting_permissions_modifier.h"
#include "chrome/browser/extensions/permissions/site_permissions_helper.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "components/crx_file/id_util.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_registrar.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/mojom/manifest.mojom-shared.h"
#include "extensions/test/permissions_manager_waiter.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/layout/animating_layout_manager_test_util.h"
#include "ui/views/view_utils.h"
using PermissionsManager = extensions::PermissionsManager;
using SitePermissionsHelper = extensions::SitePermissionsHelper;
ExtensionsToolbarUnitTest::ExtensionsToolbarUnitTest() {
// Allow unpacked extensions without developer mode for testing.
scoped_feature_list_.InitAndDisableFeature(
extensions_features::kExtensionDisableUnsupportedDeveloper);
}
ExtensionsToolbarUnitTest::ExtensionsToolbarUnitTest(
base::test::TaskEnvironment::TimeSource time_source)
: TestWithBrowserView(time_source) {
// Allow unpacked extensions without developer mode for testing.
scoped_feature_list_.InitAndDisableFeature(
extensions_features::kExtensionDisableUnsupportedDeveloper);
}
ExtensionsToolbarUnitTest::~ExtensionsToolbarUnitTest() = default;
void ExtensionsToolbarUnitTest::SetUp() {
TestWithBrowserView::SetUp();
extensions::TestExtensionSystem* extension_system =
static_cast<extensions::TestExtensionSystem*>(
extensions::ExtensionSystem::Get(profile()));
extension_system->CreateExtensionService(
base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
permissions_manager_ = PermissionsManager::Get(profile());
permissions_helper_ = std::make_unique<SitePermissionsHelper>(profile());
// Shorten delay on animations so tests run faster.
views::test::ReduceAnimationDuration(extensions_container());
}
void ExtensionsToolbarUnitTest::TearDown() {
// Avoid dangling pointer to profile.
permissions_helper_.reset(nullptr);
TestWithBrowserView::TearDown();
}
scoped_refptr<const extensions::Extension>
ExtensionsToolbarUnitTest::InstallExtension(const std::string& name) {
return InstallExtension(name, {}, {});
}
scoped_refptr<const extensions::Extension>
ExtensionsToolbarUnitTest::InstallExtensionWithHostPermissions(
const std::string& name,
const std::vector<std::string>& host_permissions) {
return InstallExtension(name, {}, host_permissions);
}
scoped_refptr<const extensions::Extension>
ExtensionsToolbarUnitTest::InstallExtensionWithPermissions(
const std::string& name,
const std::vector<std::string>& permissions) {
return InstallExtension(name, permissions, {});
}
scoped_refptr<const extensions::Extension>
ExtensionsToolbarUnitTest::InstallEnterpriseExtension(
const std::string& name,
const std::vector<std::string>& host_permissions) {
return InstallExtension(name, {}, host_permissions,
extensions::mojom::ManifestLocation::kExternalPolicy);
}
scoped_refptr<const extensions::Extension>
ExtensionsToolbarUnitTest::InstallExtension(
const std::string& name,
const std::vector<std::string>& permissions,
const std::vector<std::string>& host_permissions,
extensions::mojom::ManifestLocation location) {
scoped_refptr<const extensions::Extension> extension =
extensions::ExtensionBuilder(name)
.SetLocation(location)
.AddAPIPermissions(permissions)
.AddHostPermissions(host_permissions)
.SetID(crx_file::id_util::GenerateId(name))
.Build();
extension_registrar()->AddExtension(extension);
// Force the container to re-layout, since a new extension was added.
LayoutContainerIfNecessary();
return extension;
}
void ExtensionsToolbarUnitTest::ReloadExtension(
const extensions::ExtensionId& extension_id) {
extension_registrar()->ReloadExtension(extension_id);
}
void ExtensionsToolbarUnitTest::UninstallExtension(
const extensions::ExtensionId& extension_id) {
// In some cases, exiting the test too early could cause it to fail,
// because a worker thread is holding a lock to files it's trying to delete.
// This prevents the test's temp dir from cleaning up properly.
//
// This is also a known bug for Ephemeral Profiles. NukeProfileFromDisk() can
// race with a bunch of things, and extension uninstall is just one of them.
// See crbug.com/1191455.
base::RunLoop run_loop;
extension_registrar()->UninstallExtension(
extension_id, extensions::UninstallReason::UNINSTALL_REASON_FOR_TESTING,
nullptr, run_loop.QuitClosure());
run_loop.Run();
}
void ExtensionsToolbarUnitTest::EnableExtension(
const extensions::ExtensionId& extension_id) {
extension_registrar()->EnableExtension(extension_id);
}
void ExtensionsToolbarUnitTest::DisableExtension(
const extensions::ExtensionId& extension_id) {
extension_registrar()->DisableExtension(
extension_id, {extensions::disable_reason::DISABLE_USER_ACTION});
}
void ExtensionsToolbarUnitTest::WithholdHostPermissions(
const extensions::Extension* extension) {
extensions::PermissionsManagerWaiter waiter(permissions_manager_);
extensions::ScriptingPermissionsModifier(profile(), extension)
.RemoveAllGrantedHostPermissions();
waiter.WaitForExtensionPermissionsUpdate();
}
void ExtensionsToolbarUnitTest::ClickButton(views::Button* button) const {
ui::MouseEvent press_event(ui::EventType::kMousePressed, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, 0);
button->OnMousePressed(press_event);
ui::MouseEvent release_event(ui::EventType::kMouseReleased, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, 0);
button->OnMouseReleased(release_event);
}
void ExtensionsToolbarUnitTest::UpdateUserSiteAccess(
const extensions::Extension& extension,
content::WebContents* web_contents,
PermissionsManager::UserSiteAccess site_access) {
extensions::PermissionsManagerWaiter waiter(
PermissionsManager::Get(browser()->profile()));
permissions_helper_->UpdateSiteAccess(extension, web_contents, site_access);
waiter.WaitForExtensionPermissionsUpdate();
}
void ExtensionsToolbarUnitTest::UpdateUserSiteSetting(
extensions::PermissionsManager::UserSiteSetting site_setting,
const GURL& url) {
extensions::PermissionsManagerWaiter waiter(permissions_manager_);
permissions_manager_->UpdateUserSiteSetting(url::Origin::Create(url),
site_setting);
waiter.WaitForUserPermissionsSettingsChange();
}
void ExtensionsToolbarUnitTest::AddHostAccessRequest(
const extensions::Extension& extension,
content::WebContents* web_contents,
const std::optional<URLPattern>& filter) {
int tab_id = extensions::ExtensionTabUtil::GetTabId(web_contents);
permissions_manager_->AddHostAccessRequest(web_contents, tab_id, extension,
filter);
}
void ExtensionsToolbarUnitTest::RemoveHostAccessRequest(
const extensions::Extension& extension,
content::WebContents* web_contents) {
int tab_id = extensions::ExtensionTabUtil::GetTabId(web_contents);
permissions_manager_->RemoveHostAccessRequest(tab_id, extension.id());
}
PermissionsManager::UserSiteSetting
ExtensionsToolbarUnitTest::GetUserSiteSetting(const GURL& url) {
return permissions_manager_->GetUserSiteSetting(url::Origin::Create(url));
}
PermissionsManager::UserSiteAccess ExtensionsToolbarUnitTest::GetUserSiteAccess(
const extensions::Extension& extension,
const GURL& url) const {
return permissions_manager_->GetUserSiteAccess(extension, url);
}
SitePermissionsHelper::SiteInteraction
ExtensionsToolbarUnitTest::GetSiteInteraction(
const extensions::Extension& extension,
content::WebContents* web_contents) const {
return permissions_helper_->GetSiteInteraction(extension, web_contents);
}
std::vector<ToolbarActionView*>
ExtensionsToolbarUnitTest::GetPinnedExtensionViews() {
std::vector<ToolbarActionView*> result;
for (views::View* child : extensions_container()->children()) {
// Ensure we don't downcast the ExtensionsToolbarButton.
if (views::IsViewClass<ToolbarActionView>(child)) {
ToolbarActionView* const action = static_cast<ToolbarActionView*>(child);
#if BUILDFLAG(IS_MAC)
// TODO(crbug.com/40670141): Use IsActionVisibleOnToolbar() because it
// queries the underlying model and not GetVisible(), as that relies on an
// animation running, which is not reliable in unit tests on Mac.
const bool is_visible = extensions_container()->IsActionVisibleOnToolbar(
action->view_controller()->GetId());
#else
const bool is_visible = action->GetVisible();
#endif
if (is_visible) {
result.push_back(action);
}
}
}
return result;
}
std::vector<std::string> ExtensionsToolbarUnitTest::GetPinnedExtensionNames() {
return base::ToVector(GetPinnedExtensionViews(), [](ToolbarActionView* view) {
return base::UTF16ToUTF8(view->view_controller()->GetActionName());
});
}
void ExtensionsToolbarUnitTest::WaitForAnimation() {
#if BUILDFLAG(IS_MAC)
// TODO(crbug.com/40670141): we avoid using animations on Mac due to the lack
// of support in unit tests. Therefore this is a no-op.
#else
views::test::WaitForAnimatingLayoutManager(extensions_container());
#endif
}
void ExtensionsToolbarUnitTest::LayoutContainerIfNecessary() {
extensions_container()->GetWidget()->LayoutRootViewIfNecessary();
}
content::WebContentsTester*
ExtensionsToolbarUnitTest::AddWebContentsAndGetTester() {
std::unique_ptr<content::WebContents> contents(
content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
content::WebContents* raw_contents = contents.get();
browser()->tab_strip_model()->AppendWebContents(std::move(contents), true);
EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), raw_contents);
return content::WebContentsTester::For(raw_contents);
}