blob: 004ec68291fa26fde1592548a297cfe71a732d46 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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_menu_test_util.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/extensions/extension_action_view_controller.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "chrome/browser/ui/views/extensions/extension_popup.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/layout/animating_layout_manager_test_util.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"
class ExtensionsMenuTestUtil::MenuViewObserver : public views::ViewObserver {
public:
explicit MenuViewObserver(ExtensionsMenuView** menu_view_ptr)
: menu_view_ptr_(menu_view_ptr) {}
~MenuViewObserver() override = default;
private:
void OnViewIsDeleting(views::View* observed_view) override {
*menu_view_ptr_ = nullptr;
}
const raw_ptr<ExtensionsMenuView*> menu_view_ptr_;
};
// A view wrapper class that owns the ExtensionsToolbarContainer.
// This is used when we don't have a "real" browser window, because the
// TestBrowserWindow does not have a view instantiated for the container.
class ExtensionsMenuTestUtil::Wrapper {
public:
explicit Wrapper(Browser* browser)
: extensions_container_(new ExtensionsToolbarContainer(browser)) {
container_parent_.SetSize(gfx::Size(1000, 1000));
container_parent_.Layout();
container_parent_.AddChildView(extensions_container_.get());
}
~Wrapper() = default;
Wrapper(const Wrapper& other) = delete;
Wrapper& operator=(const Wrapper& other) = delete;
ExtensionsToolbarContainer* extensions_container() {
return extensions_container_;
}
private:
views::View container_parent_;
raw_ptr<ExtensionsToolbarContainer> extensions_container_ = nullptr;
};
ExtensionsMenuTestUtil::ExtensionsMenuTestUtil(Browser* browser,
bool is_real_window)
: scoped_allow_extensions_menu_instances_(
ExtensionsMenuView::AllowInstancesForTesting()),
browser_(browser),
menu_view_observer_(std::make_unique<MenuViewObserver>(&menu_view_)) {
if (is_real_window) {
extensions_container_ = BrowserView::GetBrowserViewForBrowser(browser_)
->toolbar()
->extensions_container();
} else {
wrapper_ = std::make_unique<Wrapper>(browser);
extensions_container_ = wrapper_->extensions_container();
}
owned_menu_view_ = std::make_unique<ExtensionsMenuView>(
extensions_container_->GetExtensionsButton(), browser_,
extensions_container_, true);
menu_view_ = owned_menu_view_.get();
// The static_cast is needed to disambiguate between View::AddObserver and
// DialogDelegate::AddObserver.
static_cast<views::View*>(menu_view_)->AddObserver(menu_view_observer_.get());
if (is_real_window)
views::BubbleDialogDelegateView::CreateBubble(std::move(owned_menu_view_));
}
ExtensionsMenuTestUtil::~ExtensionsMenuTestUtil() {
if (!owned_menu_view_ && menu_view_) {
// If we don't own menu_view_, a widget owns menu_view_.
menu_view_->GetWidget()->CloseNow();
DCHECK_EQ(menu_view_, nullptr);
}
}
int ExtensionsMenuTestUtil::NumberOfBrowserActions() {
return menu_view_->extensions_menu_items_for_testing().size();
}
int ExtensionsMenuTestUtil::VisibleBrowserActions() {
int visible_icons = 0;
for (const auto& id_and_view : extensions_container_->icons_for_testing()) {
if (id_and_view.second->GetVisible())
++visible_icons;
}
return visible_icons;
}
bool ExtensionsMenuTestUtil::HasAction(const extensions::ExtensionId& id) {
return GetMenuItemViewForId(id) != nullptr;
}
void ExtensionsMenuTestUtil::InspectPopup(const extensions::ExtensionId& id) {
InstalledExtensionMenuItemView* view = GetMenuItemViewForId(id);
DCHECK(view);
static_cast<ExtensionActionViewController*>(view->view_controller())
->InspectPopup();
}
bool ExtensionsMenuTestUtil::HasIcon(const extensions::ExtensionId& id) {
InstalledExtensionMenuItemView* view = GetMenuItemViewForId(id);
DCHECK(view);
return !view->primary_action_button_for_testing()
->GetImage(views::Button::STATE_NORMAL)
.isNull();
}
gfx::Image ExtensionsMenuTestUtil::GetIcon(const extensions::ExtensionId& id) {
InstalledExtensionMenuItemView* view = GetMenuItemViewForId(id);
DCHECK(view);
return gfx::Image(view->primary_action_button_for_testing()->GetImage(
views::Button::STATE_NORMAL));
}
void ExtensionsMenuTestUtil::Press(const extensions::ExtensionId& id) {
InstalledExtensionMenuItemView* view = GetMenuItemViewForId(id);
DCHECK(view);
ExtensionsMenuButton* primary_button =
view->primary_action_button_for_testing();
ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
views::test::ButtonTestApi(primary_button).NotifyClick(event);
}
std::string ExtensionsMenuTestUtil::GetTooltip(
const extensions::ExtensionId& id) {
InstalledExtensionMenuItemView* view = GetMenuItemViewForId(id);
DCHECK(view);
ExtensionsMenuButton* primary_button =
view->primary_action_button_for_testing();
return base::UTF16ToUTF8(primary_button->GetTooltipText(gfx::Point()));
}
gfx::NativeView ExtensionsMenuTestUtil::GetPopupNativeView() {
ToolbarActionViewController* popup_owner =
extensions_container_->popup_owner_for_testing();
return popup_owner ? popup_owner->GetPopupNativeView() : nullptr;
}
bool ExtensionsMenuTestUtil::HasPopup() {
return !!GetPopupNativeView();
}
bool ExtensionsMenuTestUtil::HidePopup() {
// ExtensionsToolbarContainer::HideActivePopup() is private. Get around it by
// casting to an ExtensionsContainer.
static_cast<ExtensionsContainer*>(extensions_container_)->HideActivePopup();
return !HasPopup();
}
void ExtensionsMenuTestUtil::SetWidth(int width) {
extensions_container_->SetSize(
gfx::Size(width, extensions_container_->height()));
}
ExtensionsContainer* ExtensionsMenuTestUtil::GetExtensionsContainer() {
return extensions_container_;
}
void ExtensionsMenuTestUtil::WaitForExtensionsContainerLayout() {
views::test::WaitForAnimatingLayoutManager(
static_cast<views::View*>(extensions_container_));
}
std::unique_ptr<ExtensionActionTestHelper>
ExtensionsMenuTestUtil::CreateOverflowBar(Browser* browser) {
// There is no overflow bar with the ExtensionsMenu implementation.
NOTREACHED();
return nullptr;
}
void ExtensionsMenuTestUtil::LayoutForOverflowBar() {
// There is no overflow bar with the ExtensionsMenu implementation.
NOTREACHED();
}
gfx::Size ExtensionsMenuTestUtil::GetMinPopupSize() {
return ExtensionPopup::kMinSize;
}
gfx::Size ExtensionsMenuTestUtil::GetMaxPopupSize() {
return ExtensionPopup::kMaxSize;
}
gfx::Size ExtensionsMenuTestUtil::GetToolbarActionSize() {
return extensions_container_->GetToolbarActionSize();
}
gfx::Size ExtensionsMenuTestUtil::GetMaxAvailableSizeToFitBubbleOnScreen(
const extensions::ExtensionId& id) {
auto* view_delegate = static_cast<ToolbarActionViewDelegateViews*>(
static_cast<ExtensionActionViewController*>(
extensions_container_->GetActionForId(id))
->view_delegate());
return views::BubbleDialogDelegate::GetMaxAvailableScreenSpaceToPlaceBubble(
view_delegate->GetReferenceButtonForPopup(),
views::BubbleBorder::TOP_RIGHT,
views::PlatformStyle::kAdjustBubbleIfOffscreen,
views::BubbleFrameView::PreferredArrowAdjustment::kMirror);
}
InstalledExtensionMenuItemView* ExtensionsMenuTestUtil::GetMenuItemViewForId(
const extensions::ExtensionId& id) {
auto menu_items = menu_view_->extensions_menu_items_for_testing();
auto iter = base::ranges::find_if(
menu_items, [id](InstalledExtensionMenuItemView* view) {
return view->view_controller()->GetId() == id;
});
if (iter == menu_items.end())
return nullptr;
return *iter;
}
// static
std::unique_ptr<ExtensionActionTestHelper> ExtensionActionTestHelper::Create(
Browser* browser,
bool is_real_window) {
return std::make_unique<ExtensionsMenuTestUtil>(browser, is_real_window);
}