chromium/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc

// Copyright 2012 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/bookmarks/bookmark_bar_view.h"

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/actions/chrome_actions.h"
#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
#include "chrome/browser/ui/bookmarks/bookmark_utils_desktop.h"
#include "chrome/browser/ui/bookmarks/test_bookmark_navigation_wrapper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h"
#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_observer.h"
#include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
#include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
#include "chrome/browser/ui/views/chrome_constrained_window_views_client.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/test/view_event_test_base.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/test_browser_window.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/common/bookmark_pref_names.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/gcm_driver/fake_gcm_profile_service.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/page_navigator.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/base/test/ui_controls.h"
#include "ui/base/ui_base_features.h"
#include "ui/display/display_switches.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_scroll_view_container.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/drop_helper.h"
#include "ui/views/widget/widget.h"

#if !BUILDFLAG(IS_MAC)
#include "ui/aura/env.h"
#include "ui/aura/env_observer.h"
#include "ui/aura/window.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "ui/aura/window_tree_host.h"
#endif

ASCIIToUTF16;
BookmarkModel;
BookmarkNode;
BrowserThread;
OpenURLParams;
PageNavigator;
WebContents;

namespace {

#if !BUILDFLAG(IS_MAC)

// Waits for a views::Widget dialog to show up.
class DialogWaiter : public aura::EnvObserver, public views::WidgetObserver {};

// Waits for a dialog to terminate.
class DialogCloseWaiter : public views::WidgetObserver {};

// Waits for a views::Widget to receive a Tab key.
class TabKeyWaiter : public ui::EventHandler {};

void MoveMouseAndPress(const gfx::Point& screen_pos,
                       ui_controls::MouseButton button,
                       int button_state,
                       base::OnceClosure closure) {}

#endif  // !BUILDFLAG(IS_MAC)

// PageNavigator implementation that records the URL.
class TestingPageNavigator : public PageNavigator {};

}  // namespace

// Base class for event generating bookmark view tests. These test are intended
// to exercise View's menus, but that's easier done with BookmarkBarView rather
// than View's menu itself.
//
// SetUp creates a bookmark model with the following structure.
// All folders are in upper case, all URLs in lower case.
// F1
//   f1a
//   F11
//     f11a
//   f1b
//   *
// a
// b
// c
// d
// F2
// e
// OTHER
//   oa
//   OF
//     ofa
//     ofb
//   OF2
//     of2a
//     of2b
//
// * if CreateBigMenu returns return true, 100 menu items are created here with
//   the names f1-f100.
//
// Subclasses should be sure and invoke super's implementation of SetUp and
// TearDown.
class BookmarkBarViewEventTestBase : public ViewEventTestBase {};

class BookmarkBarViewDragTestBase : public BookmarkBarViewEventTestBase,
                                    public BookmarkBarViewObserver,
                                    public views::WidgetObserver {};

#if !BUILDFLAG(IS_MAC)
// The following tests were not enabled on Mac before. Consider enabling those
// that are able to run on Mac (https://crbug.com/845342).

// Clicks on first menu, makes sure button is depressed. Moves mouse to first
// child, clicks it and makes sure a navigation occurs.
class BookmarkBarViewTest1 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_Basic
#else
#define MAYBE_Basic
#endif
VIEW_TEST(BookmarkBarViewTest1, MAYBE_Basic)

// Brings up menu, clicks on empty space and make sure menu hides.
class BookmarkBarViewTest2 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_HideOnDesktopClick
#else
#define MAYBE_HideOnDesktopClick
#endif
VIEW_TEST(BookmarkBarViewTest2, MAYBE_HideOnDesktopClick)

// Brings up menu. Moves over child to make sure submenu appears, moves over
// another child and make sure next menu appears.
class BookmarkBarViewTest3 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_Submenus
#else
#define MAYBE_Submenus
#endif
VIEW_TEST(BookmarkBarViewTest3, MAYBE_Submenus)

// Observer that posts a task upon the context menu creation.
// This is necessary for Linux as the context menu has to check the clipboard,
// which invokes the event loop.
// Because |task| is a OnceClosure, callers should use a separate observer
// instance for each successive context menu creation they wish to observe.
class BookmarkContextMenuNotificationObserver {};

// Opens a bookmark folder, right clicks on the first bookmark to get a context
// menu, and selects the first menu item (open).
class BookmarkBarViewTest4 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ContextMenus
#else
#define MAYBE_ContextMenus
#endif
VIEW_TEST(BookmarkBarViewTest4, MAYBE_ContextMenus)

// Tests drag and drop within the same menu.
class BookmarkBarViewTest5 : public BookmarkBarViewDragTestBase {};

VIEW_TEST(BookmarkBarViewTest5, DND)

// Tests holding mouse down on overflow button, dragging such that menu pops up
// then selecting an item.
class BookmarkBarViewTest6 : public BookmarkBarViewEventTestBase {};

#if BUILDFLAG(IS_OZONE_WAYLAND) || BUILDFLAG(IS_WIN)
// TODO (crbug/1523247): This test is failing under wayland and Windows. This
// skips it until it can be fixed.
#define MAYBE_OpenMenuOnClickAndHold
#else
#define MAYBE_OpenMenuOnClickAndHold
#endif  // BUILDFLAG(IS_OZONE_WAYLAND) || BUILDFLAG(IS_WIN)
// If this flakes, disable and log details in http://crbug.com/523255.
VIEW_TEST(BookmarkBarViewTest6, MAYBE_OpenMenuOnClickAndHold)

// Tests drag and drop to different menu.
class BookmarkBarViewTest7 : public BookmarkBarViewDragTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_DNDToDifferentMenu
#else
#define MAYBE_DNDToDifferentMenu
#endif
VIEW_TEST(BookmarkBarViewTest7, MAYBE_DNDToDifferentMenu)

// Drags from one menu to next so that original menu closes, then back to
// original menu.
class BookmarkBarViewTest8 : public BookmarkBarViewDragTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_DNDBackToOriginatingMenu
#else
#define MAYBE_DNDBackToOriginatingMenu
#endif
VIEW_TEST(BookmarkBarViewTest8, MAYBE_DNDBackToOriginatingMenu)

// Moves the mouse over the scroll button and makes sure we get scrolling.
class BookmarkBarViewTest9 : public BookmarkBarViewEventTestBase {};

// Something about coordinate transforms is wrong on Wayland -- attempting to
// hover the scroll buttons sends the mouse to the wrong location, so it never
// winds up over the button, so the test times out.
// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_OZONE_WAYLAND) || BUILDFLAG(IS_WIN)
#define MAYBE_ScrollButtonScrolls
#else
#define MAYBE_ScrollButtonScrolls
#endif
VIEW_TEST(BookmarkBarViewTest9, MAYBE_ScrollButtonScrolls)

// Tests up/down/left/enter key messages.
class BookmarkBarViewTest10 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_KeyEvents
#else
#define MAYBE_KeyEvents
#endif
VIEW_TEST(BookmarkBarViewTest10, MAYBE_KeyEvents)

// Make sure the menu closes with the following sequence: show menu, show
// context menu, close context menu (via escape), then click else where. This
// effectively verifies we maintain mouse capture after the context menu is
// hidden.
class BookmarkBarViewTest11 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40282036): Fails on latest versions of ChromeOS.
// TODO(crbug.com/337055374): Flaky on Windows.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
#define MAYBE_CloseMenuAfterClosingContextMenu
#else
#define MAYBE_CloseMenuAfterClosingContextMenu
#endif
VIEW_TEST(BookmarkBarViewTest11, MAYBE_CloseMenuAfterClosingContextMenu)

// Tests showing a modal dialog from a context menu.
class BookmarkBarViewTest12 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_CloseWithModalDialog
#else
#define MAYBE_CloseWithModalDialog
#endif
VIEW_TEST(BookmarkBarViewTest12, MAYBE_CloseWithModalDialog)

// Tests clicking on the separator of a context menu (this is for coverage of
// bug 17862).
class BookmarkBarViewTest13 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ClickOnContextMenuSeparator
#else
#define MAYBE_ClickOnContextMenuSeparator
#endif
VIEW_TEST(BookmarkBarViewTest13, MAYBE_ClickOnContextMenuSeparator)

// Makes sure right clicking on a folder on the bookmark bar doesn't result in
// both a context menu and showing the menu.
class BookmarkBarViewTest14 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ContextMenus2
#else
#define MAYBE_ContextMenus2
#endif
VIEW_TEST(BookmarkBarViewTest14, MAYBE_ContextMenus2)

// Makes sure deleting from the context menu keeps the bookmark menu showing.
class BookmarkBarViewTest15 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_MenuStaysVisibleAfterDelete
#else
#define MAYBE_MenuStaysVisibleAfterDelete
#endif
VIEW_TEST(BookmarkBarViewTest15, MAYBE_MenuStaysVisibleAfterDelete)

// Tests that we don't crash or get stuck if the parent of a menu is closed.
class BookmarkBarViewTest16 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_DeleteMenu
#else
#define MAYBE_DeleteMenu
#endif
VIEW_TEST(BookmarkBarViewTest16, MAYBE_DeleteMenu)

// Makes sure right clicking on an item while a context menu is already showing
// doesn't crash and works.
class BookmarkBarViewTest17 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40282036): Fails on latest versions of ChromeOS.
// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
#define MAYBE_ContextMenus3
#else
#define MAYBE_ContextMenus3
#endif
VIEW_TEST(BookmarkBarViewTest17, MAYBE_ContextMenus3)

// Verifies sibling menus works. Clicks on the 'all bookmarks' folder, then
// moves the mouse over the first item on the bookmark bar and makes sure the
// menu appears.
class BookmarkBarViewTest18 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_BookmarkBarViewTest18_SiblingMenu
#else
#define MAYBE_BookmarkBarViewTest18_SiblingMenu
#endif
VIEW_TEST(BookmarkBarViewTest18, MAYBE_BookmarkBarViewTest18_SiblingMenu)

// Verifies mousing over an already open sibling menu doesn't prematurely cancel
// the menu.
class BookmarkBarViewTest19 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_BookmarkBarViewTest19_SiblingMenu
#else
#define MAYBE_BookmarkBarViewTest19_SiblingMenu
#endif
VIEW_TEST(BookmarkBarViewTest19, MAYBE_BookmarkBarViewTest19_SiblingMenu)

// Verify that when clicking a mouse button outside a context menu,
// the context menu is dismissed *and* the underlying view receives
// the the mouse event (due to event reposting).
class BookmarkBarViewTest20 : public BookmarkBarViewEventTestBase {};

BEGIN_METADATA()
END_METADATA

// TODO(crbug.com/40947483): Flaky on Windows.
// TODO (crbug/1523247): This test is failing under wayland and Windows. This
// skips it until it can be fixed.
#if BUILDFLAG(IS_OZONE_WAYLAND) || BUILDFLAG(IS_WIN)
#define MAYBE_ContextMenuExitTest
#else
#define MAYBE_ContextMenuExitTest
#endif  // BUILDFLAG(IS_OZONE_WAYLAND) || BUILDFLAG(IS_WIN)
VIEW_TEST(BookmarkBarViewTest20, MAYBE_ContextMenuExitTest)

// Tests context menu by way of opening a context menu for a empty folder menu.
// The opened context menu should behave as it is from the folder button.
class BookmarkBarViewTest21 : public BookmarkBarViewEventTestBase {};

// If this flakes, disable and log details in http://crbug.com/523255.
// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ContextMenusForEmptyFolder
#else
#define MAYBE_ContextMenusForEmptyFolder
#endif
VIEW_TEST(BookmarkBarViewTest21, MAYBE_ContextMenusForEmptyFolder)

// Test that closing the source browser window while dragging a bookmark does
// not cause a crash.
class BookmarkBarViewTest22 : public BookmarkBarViewDragTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_CloseSourceBrowserDuringDrag
#else
#define MAYBE_CloseSourceBrowserDuringDrag
#endif
VIEW_TEST(BookmarkBarViewTest22, MAYBE_CloseSourceBrowserDuringDrag)

// Tests opening a context menu for a bookmark node from the keyboard.
class BookmarkBarViewTest23 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ContextMenusKeyboard
#else
#define MAYBE_ContextMenusKeyboard
#endif
VIEW_TEST(BookmarkBarViewTest23, MAYBE_ContextMenusKeyboard)

// Test that pressing escape on a menu opened via the keyboard dismisses the
// context menu but not the parent menu.
class BookmarkBarViewTest24 : public BookmarkBarViewEventTestBase {};

// Fails on latest versions of Windows. (https://crbug.com/1108551).
// Flaky on Linux (https://crbug.com/1193137).
VIEW_TEST(BookmarkBarViewTest24, DISABLED_ContextMenusKeyboardEscape)

#if BUILDFLAG(IS_WIN)
// Tests that pressing the key KEYCODE closes the menu.
template <ui::KeyboardCode KEYCODE>
class BookmarkBarViewTest25 : public BookmarkBarViewEventTestBase {
 protected:
  void DoTestOnMessageLoop() override {
    // Move the mouse to the first folder on the bookmark bar and press the
    // mouse.
    OpenMenuByClick(GetBookmarkButton(0),
                    CreateEventTask(this, &BookmarkBarViewTest25::Step2));
  }

 private:
  void Step2() {
    // Send KEYCODE key event, which should close the menu.
    ASSERT_TRUE(ui_controls::SendKeyPressNotifyWhenDone(
        window()->GetNativeWindow(), KEYCODE, false, false, false, false,
        CreateEventTask(this, &BookmarkBarViewTest25::Step3)));
  }

  void Step3() {
    ASSERT_FALSE(MenuIsShowing());
    Done();
  }
};

// Tests that pressing F10 system key closes the menu.
using BookmarkBarViewTest25F10 = BookmarkBarViewTest25<ui::VKEY_F10>;
// TODO(crbug.com/41493431) flaky on windows
#if BUILDFLAG(IS_WIN)
#define MAYBE_F10ClosesMenu
#else
#define MAYBE_F10ClosesMenu
#endif
VIEW_TEST(BookmarkBarViewTest25F10, MAYBE_F10ClosesMenu)

// Tests that pressing Alt system key closes the menu.
using BookmarkBarViewTest25Alt = BookmarkBarViewTest25<ui::VKEY_MENU>;
// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_AltClosesMenu
#else
#define MAYBE_AltClosesMenu
#endif
VIEW_TEST(BookmarkBarViewTest25Alt, MAYBE_AltClosesMenu)

// Tests that WM_CANCELMODE closes the menu.
class BookmarkBarViewTest26 : public BookmarkBarViewEventTestBase {
 protected:
  void DoTestOnMessageLoop() override {
    // Move the mouse to the first folder on the bookmark bar and press the
    // mouse.
    OpenMenuByClick(GetBookmarkButton(0),
                    CreateEventTask(this, &BookmarkBarViewTest26::Step2));
  }

 private:
  void Step2() {
    // Send WM_CANCELMODE, which should close the menu. The message is sent
    // synchronously, however, we post a task to make sure that the message is
    // processed completely before finishing the test.
    ::SendMessage(window()->GetNativeView()->GetHost()->GetAcceleratedWidget(),
                  WM_CANCELMODE, 0, 0);

    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&BookmarkBarViewTest26::Step3, base::Unretained(this)));
  }

  void Step3() {
    ASSERT_FALSE(MenuIsShowing());
    Done();
  }
};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_CancelModeClosesMenu
#else
#define MAYBE_CancelModeClosesMenu
#endif
VIEW_TEST(BookmarkBarViewTest26, MAYBE_CancelModeClosesMenu)
#endif

class BookmarkBarViewTest27 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_MiddleClickOnFolderOpensAllBookmarks
#else
#define MAYBE_MiddleClickOnFolderOpensAllBookmarks
#endif
VIEW_TEST(BookmarkBarViewTest27, MAYBE_MiddleClickOnFolderOpensAllBookmarks)

#endif  // BUILDFLAG(IS_MAC)

class BookmarkBarViewTest28 : public BookmarkBarViewEventTestBase {};

// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ClickWithModifierOnFolderOpensAllBookmarks
#else
#define MAYBE_ClickWithModifierOnFolderOpensAllBookmarks
#endif

VIEW_TEST(BookmarkBarViewTest28,
          MAYBE_ClickWithModifierOnFolderOpensAllBookmarks)

// Tests drag and drop to an empty menu.
class BookmarkBarViewTest29 : public BookmarkBarViewDragTestBase {};

// TODO(crbug.com/40943907): Flaky on Mac.
// TODO(crbug.com/40947483): Flaky on Windows.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#define MAYBE_DNDToEmptyMenu
#else
#define MAYBE_DNDToEmptyMenu
#endif
VIEW_TEST(BookmarkBarViewTest29, MAYBE_DNDToEmptyMenu)