chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include <memory>

#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/extension_action_test_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/sessions/content/session_tab_helper.h"
#include "components/zoom/test/zoom_test_utils.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/extension_action.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_host_registry.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "ui/base/buildflags.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/scrollbar_size.h"
#include "ui/views/widget/widget.h"

#if !BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/download/bubble/download_bubble_ui_controller.h"
#include "chrome/browser/download/bubble/download_display_controller.h"
#include "chrome/browser/ui/download/download_display.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "ui/views/win/hwnd_util.h"
#endif

namespace extensions {
namespace {

#if !BUILDFLAG(IS_CHROMEOS)
bool IsDownloadSurfaceVisible(BrowserWindow* window) {}
#endif

// Helper to ensure all extension hosts are destroyed during the test. If a host
// is still alive, the Profile can not be destroyed in
// BrowserProcessImpl::StartTearDown().
// TODO(tapted): The existence of this helper is probably a bug. Extension
// hosts do not currently block shutdown the way a browser tab does. Maybe they
// should. See http://crbug.com/729476.
class PopupHostWatcher : public ExtensionHostRegistry::Observer {};

// chrome.browserAction API tests that interact with the UI in such a way that
// they cannot be run concurrently (i.e. openPopup API tests that require the
// window be focused/active).
class BrowserActionInteractiveTest : public ExtensionApiTest {};

// Tests opening a popup using the chrome.browserAction.openPopup API. This test
// opens a popup in the starting window, closes the popup, creates a new window
// and opens a popup in the new window. Both popups should succeed in opening.
// TODO(crbug.com/40781224): Test flaking frequently on Lacros.
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_TestOpenPopup
#else
#define MAYBE_TestOpenPopup
#endif
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, MAYBE_TestOpenPopup) {}

// Tests opening a popup in an incognito window.
// TODO(crbug.com/345091943): Extremely flaky on Mac release builds.
#if BUILDFLAG(IS_MAC) && defined(NDEBUG)
#define MAYBE_TestOpenPopupIncognito
#else
#define MAYBE_TestOpenPopupIncognito
#endif
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       MAYBE_TestOpenPopupIncognito) {}

// Tests that an extension can open a popup in the last active incognito window
// even from a background page with a non-incognito profile.
// (crbug.com/448853)
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       TestOpenPopupIncognitoFromBackground) {}

// Tests if there is already a popup open (by a user click or otherwise), that
// the openPopup API does not override it.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       TestOpenPopupDoesNotCloseOtherPopups) {}

// Test that openPopup does not grant tab permissions like for browser action
// clicks if the activeTab permission is set.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       TestOpenPopupDoesNotGrantTabPermissions) {}

// Test that the extension popup is closed when the browser window is focused.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, FocusLossClosesPopup1) {}

// Test that the extension popup is closed when the browser window is focused.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, FocusLossClosesPopup2) {}

// TODO(crbug.com/330684964): Test flaking frequently on Linux MSan builder
// and Mac release builders.
#if (BUILDFLAG(IS_MAC) && defined(NDEBUG)) || \
    (BUILDFLAG(IS_LINUX) && defined(MEMORY_SANITIZER))
#define MAYBE_TabSwitchClosesPopup
#else
#define MAYBE_TabSwitchClosesPopup
#endif
// Test that the extension popup is closed on browser tab switches.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       MAYBE_TabSwitchClosesPopup) {}

IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       DeleteBrowserActionWithPopupOpen) {}

IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, PopupZoomsIndependently) {}

class BrowserActionInteractiveViewsTest : public BrowserActionInteractiveTest {};

// Test closing the browser while inspecting an extension popup with dev tools.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveViewsTest,
                       CloseBrowserWithDevTools) {}

#if BUILDFLAG(IS_WIN)
// Forcibly closing a browser HWND with a popup should not cause a crash.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, DestroyHWNDDoesNotCrash) {
  OpenPopupViaAPI(false);
  auto test_util = ExtensionActionTestHelper::Create(browser());
  const gfx::NativeView popup_view = test_util->GetPopupNativeView();
  EXPECT_NE(gfx::NativeView(), popup_view);
  const HWND popup_hwnd = views::HWNDForNativeView(popup_view);
  EXPECT_EQ(TRUE, ::IsWindow(popup_hwnd));
  const HWND browser_hwnd =
      views::HWNDForNativeView(browser()->window()->GetNativeWindow());
  EXPECT_EQ(TRUE, ::IsWindow(browser_hwnd));

  // Create a new browser window to prevent the message loop from terminating.
  browser()->OpenURL(
      content::OpenURLParams(GURL("chrome://version"), content::Referrer(),
                             WindowOpenDisposition::NEW_WINDOW,
                             ui::PAGE_TRANSITION_TYPED, false),
      /*navigation_handle_callback=*/{});

  // Forcibly closing the browser HWND should not cause a crash.
  EXPECT_EQ(TRUE, ::CloseWindow(browser_hwnd));
  EXPECT_EQ(TRUE, ::DestroyWindow(browser_hwnd));
  EXPECT_EQ(FALSE, ::IsWindow(browser_hwnd));
  EXPECT_EQ(FALSE, ::IsWindow(popup_hwnd));
}
#endif  // BUILDFLAG(IS_WIN)

class MainFrameSizeWaiter : public content::WebContentsObserver {};

// TODO(crbug.com/40791502): Test crashes on Windows
#if BUILDFLAG(IS_WIN)
#define MAYBE_BrowserActionPopup
#elif BUILDFLAG(IS_LINUX) && \
    (defined(THREAD_SANITIZER) || defined(ADDRESS_SANITIZER))
// TODO(crbug.com/40803969): Test is flaky for linux tsan and asan builds
#define MAYBE_BrowserActionPopup
#elif BUILDFLAG(IS_MAC)
// TODO(crbug.com/40803969): Test is flaky on Mac as well.
#define MAYBE_BrowserActionPopup
#else
#define MAYBE_BrowserActionPopup
#endif
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, MAYBE_BrowserActionPopup) {}

// Test that a browser action popup can download data URLs. See
// https://crbug.com/821219
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       BrowserActionPopupDownload) {}

// Test that we don't try and show a browser action popup with
// browserAction.openPopup if there is no toolbar (e.g., for web popup windows).
// Regression test for crbug.com/584747.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, OpenPopupOnPopup) {}

// Watches a frame is swapped with a new frame by e.g., navigation.
class RenderFrameChangedWatcher : public content::WebContentsObserver {};

// Test that a browser action popup with a web iframe works correctly. The
// iframe is expected to run in a separate process.
// See https://crbug.com/546267.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
                       BrowserActionPopupWithIframe) {}

class BrowserActionInteractiveFencedFrameTest
    : public BrowserActionInteractiveTest {};

IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveFencedFrameTest,
                       BrowserActionPopupWithFencedFrame) {}

class NavigatingExtensionPopupInteractiveTest
    : public BrowserActionInteractiveTest {};

// Tests that an extension pop-up cannot be navigated to a web page.
IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest, Webpage_Get) {}
IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest, Webpage_Post) {}

// Tests that an extension pop-up can be navigated to another page
// in the same extension.
IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest,
                       PageInSameExtension_Get) {}
IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest,
                       PageInSameExtension_Post) {}

// Tests that an extension pop-up cannot be navigated to a page
// in another extension.
IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest,
                       PageInOtherExtension_Get) {}

IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest,
                       PageInOtherExtension_Post) {}

// Tests that navigating an extension pop-up to a http URI that returns
// Content-Disposition: attachment; filename=...
// works: No navigation, but download shelf visible + download goes through.
IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest,
                       DownloadViaPost) {}

IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupInteractiveTest,
                       DownloadViaGet) {}

}  // namespace
}  // namespace extensions