chromium/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc

// Copyright 2020 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/memory/stack_allocated.h"
#include "base/strings/string_util.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/browser_app_launcher.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser_command_controller.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_tabstrip.h"
#include "chrome/browser/ui/tab_ui_helper.h"
#include "chrome/browser/ui/tabs/existing_window_sub_menu_model.h"
#include "chrome/browser/ui/tabs/tab_menu_model.h"
#include "chrome/browser/ui/unload_controller.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/custom_tab_bar_view.h"
#include "chrome/browser/ui/views/tabs/tab_icon.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/web_applications/web_app_browser_controller.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/web_applications/manifest_update_manager.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_params.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/embedder_support/switches.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "components/webapps/browser/install_result_code.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/test/background_color_change_waiter.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "third_party/blink/public/common/features.h"

OpenURLParams;
Referrer;

namespace {

constexpr SkColor kAppBackgroundColor =;

// The page shouldn't have a manifest so no updating will occur to override
// our settings.
constexpr char kAppPath[] =;

}  // namespace
namespace web_app {

class WebAppTabStripBrowserTest : public WebAppBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       CustomTabBarUpdateOnTabSwitch) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, PopOutTabOnInstall) {}

// TODO(crbug.com/40598974) Enabled tab strip for web apps on non-Chrome OS.
#if BUILDFLAG(IS_CHROMEOS)

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       ActiveTabColorIsBackgroundColor) {
  // Ensure we're not using the system theme on Linux.
  ThemeService* theme_service =
      ThemeServiceFactory::GetForProfile(browser()->profile());
  theme_service->UseDefaultTheme();

  webapps::AppId app_id = Install();

  // Trigger the launch but do not wait for the web contents to load.
  content::WebContents* web_contents =
      apps::AppServiceProxyFactory::GetForProfile(profile())
          ->BrowserAppLauncher()
          ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
              app_id, apps::LaunchContainer::kLaunchContainerWindow,
              WindowOpenDisposition::CURRENT_TAB,
              apps::LaunchSource::kFromTest));
  ASSERT_TRUE(web_contents);
  Browser* app_browser = chrome::FindBrowserWithTab(web_contents);
  App app{app_id, app_browser,
          BrowserView::GetBrowserViewForBrowser(app_browser), web_contents};

  EXPECT_EQ(registrar().GetAppBackgroundColor(app.id), kAppBackgroundColor);

  // Expect manifest background color prior to page loading.
  {
    ASSERT_FALSE(
        app.web_contents->IsDocumentOnLoadCompletedInPrimaryMainFrame());
    EXPECT_EQ(app.browser->app_controller()->GetBackgroundColor().value(),
              kAppBackgroundColor);
    EXPECT_EQ(GetTabColor(app.browser_view), kAppBackgroundColor);
  }

  // Expect initial page background color to be white.
  {
    content::BackgroundColorChangeWaiter(app.web_contents).Wait();
    EXPECT_EQ(app.browser->app_controller()->GetBackgroundColor().value(),
              SK_ColorWHITE);
    EXPECT_EQ(GetTabColor(app.browser_view), SK_ColorWHITE);
  }

  // Ensure HTML document has loaded before we execute JS in it.
  content::AwaitDocumentOnLoadCompleted(app.web_contents);

  // Set document color to black and read tab background color.
  {
    content::BackgroundColorChangeWaiter waiter(app.web_contents);
    EXPECT_TRUE(content::ExecJs(
        app.web_contents, "document.body.style.backgroundColor = 'black';"));
    waiter.Wait();
    EXPECT_EQ(app.browser->app_controller()->GetBackgroundColor().value(),
              SK_ColorBLACK);
    EXPECT_EQ(GetTabColor(app.browser_view), SK_ColorBLACK);
  }

  // Update document color to cyan and check that the tab color matches.
  {
    content::BackgroundColorChangeWaiter waiter(app.web_contents);
    EXPECT_TRUE(content::ExecJs(
        app.web_contents, "document.body.style.backgroundColor = 'cyan';"));
    waiter.Wait();
    EXPECT_EQ(app.browser->app_controller()->GetBackgroundColor().value(),
              SK_ColorCYAN);
    EXPECT_EQ(GetTabColor(app.browser_view), SK_ColorCYAN);
  }
}

#endif  // BUILDFLAG(IS_CHROMEOS)

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, AutoNewTabUrl) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, NewTabUrl) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, NonTabbedWebApp) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, InstallingPinsHomeTab) {}

// Tests that the monochrome app icon is used on the home tab and it is
// correctly colored.
IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, MonochromeAppIconOnHomeTab) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, InstallFromNonHomeTabUrl) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, OpeningPinsHomeTab) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, ReparentingPinsHomeTab) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, NavigationThrottle) {}

// TODO(crbug.com/40257354): Enable this test.
IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, DISABLED_TargetBlankLink) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, OpenInChrome) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, WebAppMenuModelTabbedApp) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, WebAppMenuModelNonTabbedApp) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, MoveTabsToNewWindow) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, MoveTabsToExistingWindow) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       OnlyNavigateHomeTabIfDifferentUrl) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, NoFavicons) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       OnlyThrottlePrimaryMainFrame) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, DontCreateThrottleForReload) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, QueryParamsInStartUrl) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       OutOfScopeNavigationFromHomeTab) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, TabbedModeMediaCSS) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       CloseTabCommandDisabledForHomeTab) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       MiddleClickDoesntCloseHomeTab) {}

// Tests that the home tab is not closable unless it is the only tab left in the
// window. This is to ensure that window.close() does not close the home tab.
IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, HomeTabCantBeClosedUsingJS) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, HomeTabScopeSegmentWildcard) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, HomeTabScopeFixedString) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, HomeTabScopeWildcardString) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, CloseAllTabsCommand) {}

IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest,
                       NewTabButtonUrlInHomeTabScope) {}

// Tests that middle clicking links to the home tab, opens the link in the home
// tab.
IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, MiddleClickHomeTabLink) {}

// Tests the page title, which is used for accessibility.
IN_PROC_BROWSER_TEST_F(WebAppTabStripBrowserTest, PageTitle) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)

// Browser tests that verify web-app tab strip behavior when locked (and not
// locked) for OnTask. Only relevant for non-web browser scenarios.
using WebAppTabStripForOnTaskBrowserTest = WebAppTabStripBrowserTest;

IN_PROC_BROWSER_TEST_F(WebAppTabStripForOnTaskBrowserTest,
                       MiddleClickDoesNotCloseTabsWhenLockedForOnTask) {
  // Set up app and lock the app for OnTask.
  GURL start_url =
      embedded_test_server()->GetURL("/web_apps/tab_strip_customizations.html");
  const webapps::AppId app_id = InstallTestWebApp(start_url);
  Browser* const app_browser = FindWebAppBrowser(browser()->profile(), app_id);
  app_browser->SetLockedForOnTask(true);

  const TabStripModel* const tab_strip_model = app_browser->tab_strip_model();
  ASSERT_TRUE(registrar().IsTabbedWindowModeEnabled(app_id));
  ASSERT_EQ(tab_strip_model->count(), 1);
  ASSERT_TRUE(tab_strip_model->IsTabPinned(0));

  // Open another tab so we can test tab close behavior on both home and
  // non-home tabs.
  OpenUrlAndWait(app_browser,
                 embedded_test_server()->GetURL("/web_apps/get_manifest.html"));
  ASSERT_EQ(tab_strip_model->count(), 2);

  // Verify home tab cannot be closed.
  auto* const tab_strip =
      BrowserView::GetBrowserViewForBrowser(app_browser)->tabstrip();
  tab_strip->CloseTab(tab_strip->tab_at(0),
                      CloseTabSource::CLOSE_TAB_FROM_MOUSE);
  ASSERT_EQ(tab_strip_model->count(), 2);

  // Also verify the non-home tab cannot be closed.
  tab_strip->CloseTab(tab_strip->tab_at(1),
                      CloseTabSource::CLOSE_TAB_FROM_MOUSE);
  EXPECT_EQ(tab_strip_model->count(), 2);
}

IN_PROC_BROWSER_TEST_F(WebAppTabStripForOnTaskBrowserTest,
                       MiddleClickCanCloseTabsWhenNotLockedForOnTask) {
  // Set up app and do not lock the app for OnTask.
  GURL start_url =
      embedded_test_server()->GetURL("/web_apps/tab_strip_customizations.html");
  const webapps::AppId app_id = InstallTestWebApp(start_url);
  Browser* const app_browser = FindWebAppBrowser(browser()->profile(), app_id);
  app_browser->SetLockedForOnTask(false);

  const TabStripModel* const tab_strip_model = app_browser->tab_strip_model();
  ASSERT_TRUE(registrar().IsTabbedWindowModeEnabled(app_id));
  ASSERT_EQ(tab_strip_model->count(), 1);
  ASSERT_TRUE(tab_strip_model->IsTabPinned(0));

  // Open another tab so we can test tab close behavior on both home and
  // non-home tabs.
  OpenUrlAndWait(app_browser,
                 embedded_test_server()->GetURL("/web_apps/get_manifest.html"));
  ASSERT_EQ(tab_strip_model->count(), 2);

  // Verify home tab cannot be closed (default behavior on tabbed web apps).
  auto* const tab_strip =
      BrowserView::GetBrowserViewForBrowser(app_browser)->tabstrip();
  tab_strip->CloseTab(tab_strip->tab_at(0),
                      CloseTabSource::CLOSE_TAB_FROM_MOUSE);
  ASSERT_EQ(tab_strip_model->count(), 2);

  // Verify the non-home tab can be closed.
  tab_strip->CloseTab(tab_strip->tab_at(1),
                      CloseTabSource::CLOSE_TAB_FROM_MOUSE);
  ASSERT_EQ(tab_strip_model->count(), 1);

  // The home tab is the only tab open so it can be closed now.
  tab_strip->CloseTab(tab_strip->tab_at(0),
                      CloseTabSource::CLOSE_TAB_FROM_MOUSE);
  EXPECT_EQ(tab_strip_model->count(), 0);
}

#endif

}  // namespace web_app