chromium/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc

// 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 "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/extensions/api/side_panel/side_panel_api.h"
#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/ui/browser.h"
#include "chrome/browser/ui/browser_actions.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/extensions/extension_action_test_helper.h"
#include "chrome/browser/ui/extensions/extensions_container.h"
#include "chrome/browser/ui/tabs/tab_model.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.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/side_panel/extensions/extension_side_panel_coordinator.h"
#include "chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.h"
#include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
#include "chrome/browser/ui/views/side_panel/side_panel_entry.h"
#include "chrome/browser/ui/views/side_panel/side_panel_entry_observer.h"
#include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
#include "chrome/browser/ui/views/side_panel/side_panel_registry_observer.h"
#include "chrome/browser/ui/views/side_panel/side_panel_test_utils.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/crx_file/id_util.h"
#include "components/sessions/content/session_tab_helper.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/test_image_loader.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_features.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "ui/actions/actions.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/image/image_unittest_util.h"

namespace extensions {
namespace {
enum class CommandState {};

CommandState GetCommandState(const ExtensionContextMenuModel& menu,
                             int command_id) {}

SidePanelEntry::Key GetKey(const ExtensionId& id) {}

// A class which waits on various SidePanelEntryObserver events.
class TestSidePanelEntryWaiter : public SidePanelEntryObserver {};

// A class which waits for an extension's SidePanelEntry to be registered and/or
// deregistered.
class ExtensionSidePanelRegistryWaiter : public SidePanelRegistryObserver {};

class ExtensionSidePanelBrowserTest : public ExtensionBrowserTest {};

// Test that only extensions with side panel content will have a SidePanelEntry
// registered.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       ExtensionEntryVisibleInSidePanel) {}

// Test that an extension's view is shown/behaves correctly in the side panel.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       ExtensionViewVisibleInsideSidePanel) {}

// Test that an extension's SidePanelEntry is registered for new browser
// windows.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, MultipleBrowsers) {}

// Test that if the side panel is closed while the extension's side panel view
// is still loading, there will not be a crash. Regression for
// crbug.com/1403168.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, SidePanelQuicklyClosed) {}

// Test that the extension's side panel entry shows the extension's icon.
// TODO(crbug.com/40915500): Re-enable this test
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#define MAYBE_EntryShowsExtensionIcon
#else
#define MAYBE_EntryShowsExtensionIcon
#endif
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       MAYBE_EntryShowsExtensionIcon) {}

// Test that sidePanel.setOptions() will register and deregister the extension's
// SidePanelEntry when called with enabled: true/false.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, SetOptions_Enabled) {}

// Test that sidePanel.setOptions() will change what is shown in the extension's
// SidePanelEntry's view when called with different paths.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, SetOptions_Path) {}

// Test that calling window.close() from an extension side panel deletes the
// panel's web contents and closes the extension's side panel if it's also
// shown.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, WindowCloseCalled) {}

// Test that calling window.close() from an extension's side panel deletes the
// panel's web contents and closes the extension's side panel if it's also
// shown.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       WindowCloseCalledFromTabSpecificPanel) {}

// Test that calling window.close() from an extension side panel when it is
// shown closes the side panel even if another entry is loading and will be
// shown.
// TODO(crbug.com/347643170) Test is flaky.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       DISABLED_WindowCloseCalledWhenLoading) {}

// Test that calling sidePanel.setOptions({enabled: false}) for a specific tab
// will hide the extension's global side panel for that tab.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, HideGlobalPanelForTab) {}

// Test that the saved view state for the hidden global extension side panel is
// invalidated if setOptions({enabled: false}) is called without a tab ID.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       DisableGlobalPanelWhileHidden) {}

// Test that when the extension's side panel is shown, switching from a tab
// where the panel is enabled to one where it's disabled then back to the first
// tab will re-register the entry but not show it. This behavior is a little
// weird, but trying to have it reopen causes far more complexity than is
// worthwhile.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, ReEnabledPanelNotShown) {}

// Test that calling setOptions on the current tab while the global entry is
// showing should show the new entry for the current tab.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       TabSpecificPanelShownOnOptionsUpdate) {}

// Test that when switching tabs, the new tab shows the extension's contextual
// entry if one exists, or the global entry if there is no tab-specific entry
// specified for that tab.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       ShowTabSpecificPaneOnTabSwitch) {}

// Test that the view state between the extension's global side panel entry and
// all of its tab-specific side panel entries are independent of each other.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       TabSpecificPanelsOwnViewState) {}

// Test that unloading an extension after its tab-specific side panel is moved
// to another browser does not crash. This tests a rare use case where the
// extension's contextual SidePanelEntry is deregistered before its global one,
// all while the extension itself is being unloaded. See
// ExtensionSidePanelCoordinator::CreateVIew for more details.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       UnloadExtensionAfterMovingTab) {}

// Test that when the openSidePanelOnClick pref is true, clicking the extension
// icon will show the extension's entry if it's not shown, or close
// the side panel if the extension's entry is shown.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       ToggleExtensionEntryOnUserAction) {}

// Test that extension action behavior falls back to defaults if the extension
// has no side panel panel for the current tab (global or contextual) or if the
// openSidePanelOnClick pref is false.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       FallbackActionWithoutSidePanel) {}

IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
                       CloseSidePanelButtonVisibleWhenExtensionsSidePanelOpen) {}

class ExtensionOpenSidePanelBrowserTest : public ExtensionSidePanelBrowserTest {};

// Tests that calling `sidePanel.open()` for an extension with a global panel
// registered opens the panel on the specified tab.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_OpenGlobalPanelOnActiveTab) {}

// Tests that calling `sidePanel.open()` for an extension with a global panel
// registered opens the panel on the specified tab when using an incognito
// window. Regression test for https://crbug.com/329211590.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_OpenGlobalPanelOnActiveTab_Incognito) {}

// Tests that calling `sidePanel.open()` for an extension with a global panel
// registered opens the panel on all tabs (since the registration is global,
// rather than contextual).
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_OpenGlobalPanelOnInactiveTab) {}

// Tests that calling `sidePanel.open()` will override a different, active
// global side panel in the tab when the active tab's tab ID is provided.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_OverridesGlobalPanelWithActiveTabIdProvided) {}

// Tests that calling `sidePanel.open()` will override a different, active
// global side panel in the tab when an inactive tab ID is provided.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_OverridesGlobalPanelWithInactiveTabIdProvided) {}

// Tests that calling `sidePanel.open()` with a contextual panel on the active
// tab will open that contextual panel and will not override a global panel
// that's open in a different tab.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_OpenContextualPanelInActiveTab) {}

// Tests that calling `sidePanel.open()` for a different tab will not override
// an active contextual panel.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_DoesNotOverrideActiveContextualPanelIfOtherTabIdProvided) {}

// Tests that calling `sidePanel.open()` will override an open contextual panel
// in an inactive tab if the tab ID provided matches.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_OverridesContextualEntryInInactiveTabIfTabIdMatches) {}

// Tests that calling `sidePanel.open()` can override an active contextual
// panel if the `tabId` of that tab is specified.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_OverridesActiveContextualPanelOnSameTab) {}

// Tests that calling `sidePanel.open()` on an inactive tab with a contextual
// side panel sets that panel as the active entry for that tab, but does not
// open the side panel in the active tab.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_OpenContextualPanelInInactiveTab) {}

// Tests calling `sidePanel.open()` with a given window ID will open the
// side panel in that window when there is no active side panel.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_WindowId_OpenWithNoActivePanel) {}

// Tests calling `sidePanel.open()` with a given window ID for an incognito
// window will open the side panel in that window when there is no active side
// panel.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_WindowId_OpenWithNoActivePanel_Incognito) {}

// Tests calling `sidePanel.open()` with a given window ID will override an
// active global side panel in that window.
IN_PROC_BROWSER_TEST_F(ExtensionOpenSidePanelBrowserTest,
                       OpenSidePanel_WindowId_OverridesActiveGlobalPanel) {}

// Tests calling `sidePanel.open()` with a given window ID will not override an
// active contextual panel.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_WindowId_DoesNotOverrideActiveContextualPanel) {}

// Tests calling `sidePanel.open()` with a given window ID will not override an
// inactive contextual panel.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_WindowId_DoesNotOverrideInactiveContextualPanel) {}

// Tests that extension context menus show the "(Open / Close) side panel" menu
// item when appropriate, and that the menu item toggles the global side panel.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_ContextMenu_GlobalPanel_ToggleSidePanelVisibility) {}

// Tests that extension context menus show the "(Open / Close) side panel" menu
// item when appropriate, and that the menu item toggles the contextual side
// panel.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_ContextMenu_ContextualPanel_ToggleSidePanelVisibility) {}

// Tests that the extension context menus "(Open / Close) side panel" menu item
// does nothing if the page navigated while the menu is open.
IN_PROC_BROWSER_TEST_F(
    ExtensionOpenSidePanelBrowserTest,
    OpenSidePanel_ContextMenu_ContextualPanel_PageNavigations) {}

// TODO(crbug.com/40243760): Add a test here which requires a browser in
// ExtensionViewHost for both global and contextual extension entries. One
// example of this is having a link in the page that the user can open in a new
// tab.

class ExtensionSidePanelDisabledBrowserTest : public ExtensionBrowserTest {};

// Tests that an extension's SidePanelEntry is not registered if the
// `kExtensionSidePanelIntegration` feature flag is not enabled.
IN_PROC_BROWSER_TEST_F(ExtensionSidePanelDisabledBrowserTest,
                       NoSidePanelEntry) {}

}  // namespace
}  // namespace extensions