chromium/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.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 <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "ash/public/cpp/app_menu_constants.h"
#include "ash/public/cpp/shelf_item_delegate.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.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/ash/app_list/app_service/app_service_app_item.h"
#include "chrome/browser/ash/crosapi/url_handler_ash.h"
#include "chrome/browser/ash/login/login_manager_test.h"
#include "chrome/browser/ash/login/test/login_manager_mixin.h"
#include "chrome/browser/ash/login/ui/user_adding_screen.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/system_web_apps/apps/os_url_handler_system_web_app_info.h"
#include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
#include "chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h"
#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/delete_profile_helper.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.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/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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/webui/chrome_web_ui_controller_factory.h"
#include "chrome/browser/web_applications/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_tab_helper.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/web_contents_user_data.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 "ui/base/models/menu_model.h"
#include "ui/display/screen.h"
#include "ui/wm/public/activation_change_observer.h"
#include "ui/wm/public/activation_client.h"

namespace web_app {

class SystemWebAppLinkCaptureBrowserTest
    : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> {
 public:
  SystemWebAppLinkCaptureBrowserTest() {
    SetSystemWebAppInstallation(
        ash::TestSystemWebAppInstallation::SetUpAppThatCapturesNavigation());
  }
  ~SystemWebAppLinkCaptureBrowserTest() override = default;

  bool IsLacrosOnly() {
    return GetParam().crosapi_state == TestProfileParam::CrosapiParam::kEnabled;
  }

  content::WebContents* CreateInitiatingWebContents() {
    if (IsLacrosOnly()) {
      // Ash can only have app windows, launch the helper app.
      return LaunchApp(kInitiatingAppType);
    } else {
      // Ash can have ordinary tabbed browser windows.
      GURL kInitiatingChromeUrl = GURL(chrome::kChromeUIAboutURL);
      NavigateViaLinkClickToURLAndWait(browser(), kInitiatingChromeUrl);
      EXPECT_EQ(kInitiatingChromeUrl, browser()
                                          ->tab_strip_model()
                                          ->GetActiveWebContents()
                                          ->GetLastCommittedURL());
      return browser()->tab_strip_model()->GetActiveWebContents();
    }
  }

 protected:
  Browser* CreateIncognitoBrowser() {
    Browser* incognito = Browser::Create(Browser::CreateParams(
        browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true),
        true));

    auto* contents =
        chrome::AddSelectedTabWithURL(incognito, GURL(url::kAboutBlankURL),
                                      ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
    EXPECT_TRUE(content::WaitForLoadStop(contents));

    incognito->window()->Show();
    return incognito;
  }
  const GURL kInitiatingAppUrl = GURL("chrome://initiating-app/pwa.html");
  const ash::SystemWebAppType kInitiatingAppType =
      ash::SystemWebAppType::SETTINGS;
};

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest,
                       OmniboxTypeURLAndNavigate) {
  if (IsLacrosOnly()) {
    GTEST_SKIP() << "In LacrosOnly mode, Ash can't create browser windows with "
                    "Omnibox. Because users can't interact with Omnibox, "
                    "there's no need to test this.";
  }

  WaitForTestSystemAppInstall();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  ui_test_utils::SendToOmniboxAndSubmit(browser(), GetStartUrl().spec());
  observer.Wait();

  Browser* app_browser =
      FindSystemWebAppBrowser(browser()->profile(), GetAppType());
  EXPECT_TRUE(app_browser);
  ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
  EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
  EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest, OmniboxPasteAndGo) {
  if (IsLacrosOnly()) {
    GTEST_SKIP() << "In LacrosOnly mode, Ash can't create browser windows "
                    "with Omnibox. Because users can't interact with "
                    "Omnibox, there's no need to test this.";
  }

  WaitForTestSystemAppInstall();
  OmniboxEditModel* model =
      browser()->window()->GetLocationBar()->GetOmniboxView()->model();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  model->PasteAndGo(base::UTF8ToUTF16(GetStartUrl().spec()));
  observer.Wait();

  Browser* app_browser =
      FindSystemWebAppBrowser(browser()->profile(), GetAppType());
  EXPECT_TRUE(app_browser);
  ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
  EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
  EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest, AnchorLinkClick) {
  WaitForTestSystemAppInstall();

  content::WebContents* initiating_web_contents = CreateInitiatingWebContents();
  const GURL& initiating_url = initiating_web_contents->GetLastCommittedURL();
  size_t starting_browser_count = chrome::GetTotalBrowserCount();

  const std::string kAnchorTargets[] = {"", "_blank", "_self"};
  const std::string kAnchorRelValues[] = {"", "noreferrer", "noopener",
                                          "noreferrer noopener"};

  for (const auto& target : kAnchorTargets) {
    for (const auto& rel : kAnchorRelValues) {
      SCOPED_TRACE(testing::Message() << "anchor link: target='" << target
                                      << "', rel='" << rel << "'");
      content::TestNavigationObserver observer(GetStartUrl());
      observer.StartWatchingNewWebContents();
      EXPECT_TRUE(content::ExecJs(
          initiating_web_contents,
          content::JsReplace("{"
                             "  let el = document.createElement('a');"
                             "  el.href = $1;"
                             "  el.target = $2;"
                             "  el.rel = $3;"
                             "  el.textContent = 'target = ' + $2;"
                             "  document.body.appendChild(el);"
                             "  el.click();"
                             "}",
                             GetStartUrl(), target, rel)));
      observer.Wait();

      Browser* app_browser =
          FindSystemWebAppBrowser(browser()->profile(), GetAppType());
      EXPECT_TRUE(app_browser);
      ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
      EXPECT_EQ(1 + starting_browser_count, chrome::GetTotalBrowserCount());
      EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
      EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
      app_browser->window()->Close();
      ui_test_utils::WaitForBrowserToClose(app_browser);

      // Check the initiating page is intact.
      EXPECT_EQ(initiating_url, initiating_web_contents->GetLastCommittedURL());
    }
  }
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest,
                       AnchorLinkContextMenuNewTab) {
  if (IsLacrosOnly()) {
    GTEST_SKIP() << "In LacrosOnly mode, Ash can't create browser windows "
                    "with Omnibox, and we don't show new tab option for links "
                    "to a different SWA in SWA browser windows. So it makes no "
                    "sense to test this.";
  }

  WaitForTestSystemAppInstall();

  GURL kInitiatingChromeUrl = GURL(chrome::kChromeUIAboutURL);
  NavigateViaLinkClickToURLAndWait(browser(), kInitiatingChromeUrl);
  EXPECT_EQ(kInitiatingChromeUrl, browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetLastCommittedURL());

  content::ContextMenuParams context_menu_params;
  context_menu_params.page_url = kInitiatingChromeUrl;
  context_menu_params.link_url = GetStartUrl();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();

  TestRenderViewContextMenu menu(*browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetPrimaryMainFrame(),
                                 context_menu_params);
  menu.Init();
  menu.ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, 0);

  observer.Wait();

  Browser* app_browser =
      FindSystemWebAppBrowser(browser()->profile(), GetAppType());
  EXPECT_TRUE(app_browser);
  ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
  EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
  EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
  app_browser->window()->Close();
  ui_test_utils::WaitForBrowserToClose(app_browser);

  // Check the initiating browser window is intact.
  EXPECT_EQ(kInitiatingChromeUrl, browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetLastCommittedURL());
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest,
                       AnchorLinkContextMenuNewWindow) {
  if (IsLacrosOnly()) {
    GTEST_SKIP() << "In LacrosOnly mode, Ash can't create browser windows "
                    "with Omnibox, and we don't show new window option for "
                    "links to SWA in SWA browser windows. So it makes no sense "
                    "to test this.";
  }

  WaitForTestSystemAppInstall();

  GURL kInitiatingChromeUrl = GURL(chrome::kChromeUIAboutURL);
  NavigateViaLinkClickToURLAndWait(browser(), kInitiatingChromeUrl);
  EXPECT_EQ(kInitiatingChromeUrl, browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetLastCommittedURL());

  content::ContextMenuParams context_menu_params;
  context_menu_params.page_url = kInitiatingChromeUrl;
  context_menu_params.link_url = GetStartUrl();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();

  TestRenderViewContextMenu menu(*browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetPrimaryMainFrame(),
                                 context_menu_params);
  menu.Init();
  menu.ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW, 0);

  observer.Wait();

  Browser* app_browser =
      FindSystemWebAppBrowser(browser()->profile(), GetAppType());
  EXPECT_TRUE(app_browser);
  ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
  EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
  EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
  app_browser->window()->Close();
  ui_test_utils::WaitForBrowserToClose(app_browser);

  // Check the initiating browser window is intact.
  EXPECT_EQ(kInitiatingChromeUrl, browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetLastCommittedURL());
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest, ChangeLocationHref) {
  WaitForTestSystemAppInstall();

  content::WebContents* initiating_web_contents = CreateInitiatingWebContents();
  const GURL& initiating_url = initiating_web_contents->GetLastCommittedURL();
  size_t starting_browser_count = chrome::GetTotalBrowserCount();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  EXPECT_TRUE(
      content::ExecJs(initiating_web_contents,
                      content::JsReplace("location.href=$1;", GetStartUrl())));
  observer.Wait();

  Browser* app_browser =
      FindSystemWebAppBrowser(browser()->profile(), GetAppType());
  EXPECT_TRUE(app_browser);
  ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
  EXPECT_EQ(1 + starting_browser_count, chrome::GetTotalBrowserCount());
  EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
  EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());

  // Check the initiating browser window is intact.
  EXPECT_EQ(initiating_url, initiating_web_contents->GetLastCommittedURL());
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest, WindowOpen) {
  WaitForTestSystemAppInstall();

  content::WebContents* initiating_web_contents = CreateInitiatingWebContents();
  const GURL& initiating_url = initiating_web_contents->GetLastCommittedURL();
  size_t starting_browser_count = chrome::GetTotalBrowserCount();

  const std::string kWindowOpenTargets[] = {"", "_blank"};
  const std::string kWindowOpenFeatures[] = {"", "noreferrer", "noopener",
                                             "noreferrer noopener"};

  for (const auto& target : kWindowOpenTargets) {
    for (const auto& features : kWindowOpenFeatures) {
      SCOPED_TRACE(testing::Message() << "window.open: target='" << target
                                      << "', features='" << features << "'");
      content::TestNavigationObserver observer(GetStartUrl());
      observer.StartWatchingNewWebContents();
      EXPECT_TRUE(
          content::ExecJs(initiating_web_contents,
                          content::JsReplace("window.open($1, $2, $3);",
                                             GetStartUrl(), target, features)));
      observer.Wait();

      Browser* app_browser =
          FindSystemWebAppBrowser(browser()->profile(), GetAppType());
      EXPECT_TRUE(app_browser);
      ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
      EXPECT_EQ(1 + starting_browser_count, chrome::GetTotalBrowserCount());
      EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
      EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
      app_browser->window()->Close();
      ui_test_utils::WaitForBrowserToClose(app_browser);

      // Check the initiating browser window is intact.
      EXPECT_EQ(initiating_url, initiating_web_contents->GetLastCommittedURL());
    }
  }
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest,
                       WindowOpenFromOtherSWA) {
  WaitForTestSystemAppInstall();

  content::WebContents* initiating_web_contents = LaunchApp(kInitiatingAppType);

  const std::string kWindowOpenTargets[] = {"", "_blank"};
  const std::string kWindowOpenFeatures[] = {"", "noreferrer", "noopener",
                                             "noreferrer noopener"};

  for (const auto& target : kWindowOpenTargets) {
    for (const auto& features : kWindowOpenFeatures) {
      SCOPED_TRACE(testing::Message() << "window.open: target='" << target
                                      << "', features='" << features << "'");
      content::TestNavigationObserver observer(GetStartUrl());
      observer.StartWatchingNewWebContents();
      EXPECT_TRUE(
          content::ExecJs(initiating_web_contents,
                          content::JsReplace("window.open($1, $2, $3);",
                                             GetStartUrl(), target, features)));
      observer.Wait();

      Browser* app_browser =
          FindSystemWebAppBrowser(browser()->profile(), GetAppType());
      EXPECT_TRUE(app_browser);
      ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();

      // There should be three browsers: the default one (new tab page), the
      // initiating system app, the link capturing system app.
      EXPECT_EQ(3U, chrome::GetTotalBrowserCount());
      EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
      EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
      app_browser->window()->Close();
      ui_test_utils::WaitForBrowserToClose(app_browser);

      // Check the initiating browser window is intact.
      EXPECT_EQ(kInitiatingAppUrl,
                initiating_web_contents->GetLastCommittedURL());
    }
  }
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest,
                       CaptureToOpenedWindowAndNavigateURL) {
  WaitForTestSystemAppInstall();

  Browser* app_browser;
  content::WebContents* web_contents = LaunchApp(GetAppType(), &app_browser);

  GURL kInitiatingChromeUrl = GURL(chrome::kChromeUIAboutURL);
  NavigateViaLinkClickToURLAndWait(browser(), kInitiatingChromeUrl);
  EXPECT_EQ(kInitiatingChromeUrl, browser()
                                      ->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetLastCommittedURL());

  const GURL kPageURL = GetStartUrl().Resolve("/page2.html");
  content::TestNavigationObserver observer(web_contents);
  EXPECT_TRUE(content::ExecJs(
      browser()->tab_strip_model()->GetActiveWebContents(),
      content::JsReplace("let el = document.createElement('a');"
                         "el.href = $1;"
                         "el.textContent = 'Link to SWA Page 2';"
                         "document.body.appendChild(el);"
                         "el.click();",
                         kPageURL)));
  observer.Wait();

  EXPECT_EQ(kPageURL, app_browser->tab_strip_model()
                          ->GetActiveWebContents()
                          ->GetLastCommittedURL());
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLinkCaptureBrowserTest,
                       IncognitoBrowserOmniboxLinkCapture) {
  if (IsLacrosOnly()) {
    GTEST_SKIP() << "In LacrosOnly mode, Ash can't create browser windows with "
                    "Omnibox. Because users can't interact with Omnibox, "
                    "there's no need to test this.";
  }

  WaitForTestSystemAppInstall();
  GURL start_url = GetStartUrl();

  Browser* incognito_browser = CreateIncognitoBrowser();
  browser()->window()->Close();
  ui_test_utils::WaitForBrowserToClose(browser());

  content::TestNavigationObserver observer(start_url);
  observer.StartWatchingNewWebContents();
  incognito_browser->window()->GetLocationBar()->FocusLocation(true);
  ui_test_utils::SendToOmniboxAndSubmit(incognito_browser, start_url.spec());
  observer.Wait();

  // We launch SWAs into the incognito profile's original profile.
  Browser* app_browser = FindSystemWebAppBrowser(
      incognito_browser->profile()->GetOriginalProfile(), GetAppType());
  EXPECT_TRUE(app_browser);
  ui_test_utils::BrowserActivationWaiter(app_browser).WaitForActivation();
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
  EXPECT_EQ(Browser::TYPE_APP, app_browser->type());
  EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
}

class SystemWebAppManagerWindowSizeControlsTest
    : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> {
 public:
  SystemWebAppManagerWindowSizeControlsTest() {
    SetSystemWebAppInstallation(ash::TestSystemWebAppInstallation::
                                    SetUpNonResizeableAndNonMaximizableApp());
  }
  ~SystemWebAppManagerWindowSizeControlsTest() override = default;
};

IN_PROC_BROWSER_TEST_P(SystemWebAppManagerWindowSizeControlsTest,
                       NonResizeableWindow) {
  WaitForTestSystemAppInstall();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  Browser* app_browser;
  LaunchApp(GetAppType(), &app_browser);

  EXPECT_FALSE(app_browser->create_params().can_resize);
}

IN_PROC_BROWSER_TEST_P(SystemWebAppManagerWindowSizeControlsTest,
                       NonMaximizableWindow) {
  WaitForTestSystemAppInstall();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  Browser* app_browser;
  LaunchApp(GetAppType(), &app_browser);

  EXPECT_FALSE(app_browser->create_params().can_maximize);
}

// Use LoginManagerTest here instead of SystemWebAppManagerBrowserTest, because
// it's less complicated to add SWA to LoginManagerTest than adding multi-logins
// to SWA browsertest.
class SystemWebAppManagerMultiDesktopLaunchBrowserTest
    : public ash::LoginManagerTest {
 public:
  SystemWebAppManagerMultiDesktopLaunchBrowserTest() {
    login_mixin_.AppendRegularUsers(2);
    account_id1_ = login_mixin_.users()[0].account_id;
    account_id2_ = login_mixin_.users()[1].account_id;
    installation_ =
        ash::TestSystemWebAppInstallation::SetUpAppThatCapturesNavigation();
  }

  ~SystemWebAppManagerMultiDesktopLaunchBrowserTest() override = default;

  void WaitForSystemWebAppInstall(Profile* profile) {
    base::RunLoop run_loop;

    ash::SystemWebAppManager::Get(profile)->on_apps_synchronized().Post(
        FROM_HERE, base::BindLambdaForTesting([&]() {
          // Wait one execution loop for
          // on_apps_synchronized() to be called on all
          // listeners.
          base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
              FROM_HERE, run_loop.QuitClosure());
        }));
    run_loop.Run();
  }

  webapps::AppId GetAppId(Profile* profile) {
    std::optional<webapps::AppId> app_id =
        ash::SystemWebAppManager::Get(profile)->GetAppIdForSystemApp(
            installation_->GetType());
    CHECK(app_id.has_value());
    return *app_id;
  }

  Browser* LaunchAppOnProfile(Profile* profile) {
    webapps::AppId app_id = GetAppId(profile);

    auto launch_params = apps::AppLaunchParams(
        app_id, apps::LaunchContainer::kLaunchContainerWindow,
        WindowOpenDisposition::CURRENT_TAB,
        apps::LaunchSource::kFromAppListGrid);

    content::TestNavigationObserver navigation_observer(
        installation_->GetAppUrl());

    // Watch new WebContents to wait for launches that open an app for the first
    // time.
    navigation_observer.StartWatchingNewWebContents();

    // Watch existing WebContents to wait for launches that re-use the
    // WebContents e.g. launching an already opened SWA.
    navigation_observer.WatchExistingWebContents();

    LaunchSystemWebAppAsync(profile, installation_->GetType());

    navigation_observer.Wait();

    Browser* swa_browser =
        FindSystemWebAppBrowser(profile, installation_->GetType());
    EXPECT_TRUE(swa_browser);
    ui_test_utils::BrowserActivationWaiter(swa_browser).WaitForActivation();

    return swa_browser;
  }

  void AwaitWebAppCommandsCompleteForTesting(Profile* profile) {
    ash::SystemWebAppManager::Get(profile)
        ->GetWebAppProvider(profile)
        ->command_manager()
        .AwaitAllCommandsCompleteForTesting();
  }

 protected:
  std::unique_ptr<ash::TestSystemWebAppInstallation> installation_;
  ash::LoginManagerMixin login_mixin_{&mixin_host_};
  AccountId account_id1_;
  AccountId account_id2_;
};

IN_PROC_BROWSER_TEST_F(SystemWebAppManagerMultiDesktopLaunchBrowserTest,
                       LaunchToActiveDesktop) {
  // Login two users.
  LoginUser(account_id1_);
  base::RunLoop().RunUntilIdle();

  // Wait for System Apps to be installed on both user profiles.
  auto* user_manager = user_manager::UserManager::Get();
  Profile* profile1 = ash::ProfileHelper::Get()->GetProfileByUser(
      user_manager->FindUser(account_id1_));
  WaitForSystemWebAppInstall(profile1);

  installation_ =
      ash::TestSystemWebAppInstallation::SetUpAppThatCapturesNavigation();
  ash::UserAddingScreen::Get()->Start();
  AddUser(account_id2_);
  base::RunLoop().RunUntilIdle();
  Profile* profile2 = ash::ProfileHelper::Get()->GetProfileByUser(
      user_manager->FindUser(account_id2_));
  WaitForSystemWebAppInstall(profile2);
  // Set user 1 to be active.
  user_manager->SwitchActiveUser(account_id1_);
  EXPECT_TRUE(multi_user_util::IsProfileFromActiveUser(profile1));
  EXPECT_FALSE(multi_user_util::IsProfileFromActiveUser(profile2));

  // Launch the app from user 2 profile. The window should be on user 1
  // (the active) desktop.
  Browser* browser2 = LaunchAppOnProfile(profile2);
  EXPECT_TRUE(
      MultiUserWindowManagerHelper::GetInstance()->IsWindowOnDesktopOfUser(
          browser2->window()->GetNativeWindow(), account_id1_));

  // Launch the app from user 1 profile. The window should be on user 1 (the
  // active) desktop. And there should be two different browser windows
  // (for each profile).
  Browser* browser1 = LaunchAppOnProfile(profile1);
  EXPECT_TRUE(
      MultiUserWindowManagerHelper::GetInstance()->IsWindowOnDesktopOfUser(
          browser1->window()->GetNativeWindow(), account_id1_));

  EXPECT_NE(browser1, browser2);
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());

  // Switch to user 2, then launch the app. SWAs reuse their window, so it
  // should bring `browser2` to user 2 (the active) desktop.
  user_manager->SwitchActiveUser(account_id2_);
  Browser* browser2_relaunch = LaunchAppOnProfile(profile2);

  EXPECT_EQ(browser2, browser2_relaunch);
  EXPECT_TRUE(
      MultiUserWindowManagerHelper::GetInstance()->IsWindowOnDesktopOfUser(
          browser2->window()->GetNativeWindow(), account_id2_));
}

IN_PROC_BROWSER_TEST_F(SystemWebAppManagerMultiDesktopLaunchBrowserTest,
                       ProfileScheduledForDeletion) {
  // Login two users.
  LoginUser(account_id1_);
  base::RunLoop().RunUntilIdle();

  // Wait for System Apps to be installed on both user profiles.
  auto* user_manager = user_manager::UserManager::Get();
  Profile* profile1 = ash::ProfileHelper::Get()->GetProfileByUser(
      user_manager->FindUser(account_id1_));
  WaitForSystemWebAppInstall(profile1);

  installation_ =
      ash::TestSystemWebAppInstallation::SetUpAppThatCapturesNavigation();
  ash::UserAddingScreen::Get()->Start();
  AddUser(account_id2_);
  base::RunLoop().RunUntilIdle();
  AwaitWebAppCommandsCompleteForTesting(profile1);
  Profile* profile2 = ash::ProfileHelper::Get()->GetProfileByUser(
      user_manager->FindUser(account_id2_));
  WaitForSystemWebAppInstall(profile2);
  AwaitWebAppCommandsCompleteForTesting(profile2);
  const webapps::AppId& app_id1 = GetAppId(profile1);
  const webapps::AppId& app_id2 = GetAppId(profile2);

  g_browser_process->profile_manager()
      ->GetDeleteProfileHelper()
      .MaybeScheduleProfileForDeletion(
          profile2->GetPath(), base::DoNothing(),
          ProfileMetrics::DELETE_PROFILE_USER_MANAGER);

  {
    auto launch_params = apps::AppLaunchParams(
        app_id2, apps::LaunchContainer::kLaunchContainerWindow,
        WindowOpenDisposition::CURRENT_TAB,
        apps::LaunchSource::kFromAppListGrid);
    content::WebContents* web_contents =
        apps::AppServiceProxyFactory::GetForProfile(profile2)
            ->BrowserAppLauncher()
            ->LaunchAppWithParamsForTesting(std::move(launch_params));
    EXPECT_EQ(web_contents, nullptr);
  }

  {
    auto launch_params = apps::AppLaunchParams(
        app_id1, apps::LaunchContainer::kLaunchContainerWindow,
        WindowOpenDisposition::CURRENT_TAB,
        apps::LaunchSource::kFromAppListGrid);
    content::WebContents* web_contents =
        apps::AppServiceProxyFactory::GetForProfile(profile1)
            ->BrowserAppLauncher()
            ->LaunchAppWithParamsForTesting(std::move(launch_params));
    EXPECT_NE(web_contents, nullptr);
  }
}

using SystemWebAppLaunchProfileBrowserTest =
    ash::SystemWebAppManagerBrowserTest;

IN_PROC_BROWSER_TEST_P(SystemWebAppLaunchProfileBrowserTest,
                       LaunchFromNormalSessionIncognitoProfile) {
  Profile* startup_profile = browser()->profile();
  ASSERT_TRUE(!startup_profile->IsOffTheRecord());

  WaitForTestSystemAppInstall();
  Profile* incognito_profile =
      startup_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  LaunchSystemWebAppAsync(incognito_profile, GetAppType());
  observer.Wait();

  EXPECT_FALSE(FindSystemWebAppBrowser(incognito_profile, GetAppType()));
  EXPECT_TRUE(FindSystemWebAppBrowser(startup_profile, GetAppType()));
}

#if !DCHECK_IS_ON()
// The following tests are disabled in DCHECK builds. LaunchSystemWebAppAsync
// DCHECKs if it can't find a suitable profile. EXPECT_DCHECK_DEATH (or its
// variants) aren't reliable in browsertests, so we don't test this. Here we
// to verify LaunchSystemWebAppAsync doesn't crash in release builds
IN_PROC_BROWSER_TEST_P(SystemWebAppLaunchProfileBrowserTest,
                       LaunchFromSignInProfile) {
  WaitForTestSystemAppInstall();

  Profile* signin_profile = ash::ProfileHelper::GetSigninProfile();

  EXPECT_EQ(1U, chrome::GetTotalBrowserCount());

  LaunchSystemWebAppAsync(signin_profile, GetAppType());

  // Use RunUntilIdle() here, because this catches the scenario where
  // LaunchSystemWebAppAsync mistakenly picks a profile to launch the app.
  //
  // RunUntilIdle() serves a catch-all solution, so we don't have to flush mojo
  // calls on all existing profiles (and those potentially created during
  // launch).
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
}
#endif  // !DCHECK_IS_ON()

using SystemWebAppLaunchProfileGuestSessionBrowserTest =
    SystemWebAppLaunchProfileBrowserTest;

IN_PROC_BROWSER_TEST_P(SystemWebAppLaunchProfileGuestSessionBrowserTest,
                       LaunchFromGuestSessionOriginalProfile) {
  // We should start into the guest session browsing profile.
  Profile* startup_profile = browser()->profile();
  ASSERT_TRUE(startup_profile->IsGuestSession());
  ASSERT_TRUE(startup_profile->IsPrimaryOTRProfile());

  WaitForTestSystemAppInstall();

  // We typically don't get the original profile as an argument, but it is a
  // valid input to LaunchSystemWebAppAsync.
  Profile* original_profile = browser()->profile()->GetOriginalProfile();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  LaunchSystemWebAppAsync(original_profile, GetAppType());
  observer.Wait();

  EXPECT_FALSE(FindSystemWebAppBrowser(original_profile, GetAppType()));
  EXPECT_TRUE(FindSystemWebAppBrowser(startup_profile, GetAppType()));
}

IN_PROC_BROWSER_TEST_P(SystemWebAppLaunchProfileGuestSessionBrowserTest,
                       LaunchFromGuestSessionPrimaryOTRProfile) {
  // We should start into the guest session browsing profile.
  Profile* startup_profile = browser()->profile();
  ASSERT_TRUE(startup_profile->IsGuestSession());
  ASSERT_TRUE(startup_profile->IsPrimaryOTRProfile());

  WaitForTestSystemAppInstall();

  content::TestNavigationObserver observer(GetStartUrl());
  observer.StartWatchingNewWebContents();
  LaunchSystemWebAppAsync(startup_profile, GetAppType());
  observer.Wait();

  EXPECT_TRUE(FindSystemWebAppBrowser(startup_profile, GetAppType()));
}

using SystemWebAppLaunchOmniboxNavigateBrowsertest =
    ash::SystemWebAppManagerBrowserTest;

IN_PROC_BROWSER_TEST_P(SystemWebAppLaunchOmniboxNavigateBrowsertest,
                       OpenInTab) {
  WaitForTestSystemAppInstall();

  content::TestNavigationObserver observer(GetStartUrl());
  // The app should load in the blank WebContents created when browser starts.
  observer.WatchExistingWebContents();
  ui_test_utils::SendToOmniboxAndSubmit(browser(), GetStartUrl().spec());
  observer.Wait();

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_EQ(web_contents->GetLastCommittedURL(), GetStartUrl());
  EXPECT_EQ(1, browser()->tab_strip_model()->count());

  // Verifies the tab has an associated tab helper for System App's
  // webapps::AppId.
  EXPECT_EQ(*web_app::WebAppTabHelper::GetAppId(web_contents),
            *ash::GetAppIdForSystemWebApp(browser()->profile(), GetAppType()));
}

// A one shot observer which waits for an activation of any window.
class TestActivationObserver : public wm::ActivationChangeObserver {
 public:
  TestActivationObserver(const TestActivationObserver&) = delete;
  TestActivationObserver& operator=(const TestActivationObserver&) = delete;

  TestActivationObserver() {
    activation_observer_.Observe(ash::Shell::Get()->activation_client());
  }

  ~TestActivationObserver() override = default;

  void Wait() { run_loop_.Run(); }

  // wm::ActivationChangeObserver:
  void OnWindowActivated(ActivationReason reason,
                         aura::Window* gained_active,
                         aura::Window* lost_active) override {
    Browser* browser = chrome::FindBrowserWithWindow(gained_active);
    // Check that the activated window is actually a browser.
    EXPECT_TRUE(browser);
    // Check also that the browser is actually an app.
    EXPECT_TRUE(browser->is_type_app());
    run_loop_.Quit();
  }

 private:
  // The MessageLoopRunner used to spin the message loop.
  base::RunLoop run_loop_;
  base::ScopedObservation<wm::ActivationClient, wm::ActivationChangeObserver>
      activation_observer_{this};
};

// Tests which are exercising OpenUrl called by Lacros in Ash.
class SystemWebAppOpenInAshFromLacrosTests
    : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> {
 public:
  SystemWebAppOpenInAshFromLacrosTests() {
    OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(true);
    url_handler_ = std::make_unique<crosapi::UrlHandlerAsh>();
  }

  ~SystemWebAppOpenInAshFromLacrosTests() override {
    OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(false);
  }

  // A function to wait until a window activation change was observed.
  void LaunchAndWaitForActivationChange(const GURL& url) {
    TestActivationObserver observer;
    EXPECT_TRUE(url_handler_->OpenUrlInternal(url));
    observer.Wait();
  }

  void CloseApp(ash::SystemWebAppType type) {
    Browser* app_browser = FindSystemWebAppBrowser(browser()->profile(), type);
    app_browser->window()->Close();
    ui_test_utils::WaitForBrowserToClose(app_browser);
  }

 protected:
  std::unique_ptr<crosapi::UrlHandlerAsh> url_handler_;
};

// This test will make sure that only accepted URLs will be allowed to create
// applications.
IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests,
                       LaunchOnlyAllowedUrls) {
  WaitForTestSystemAppInstall();

  // There might be an initial browser from the testing framework.
  size_t initial_browser_count = BrowserList::GetInstance()->size();

  // Test that a non descript URL gets rejected.
  GURL url1 = GURL("http://www.foo.bar");
  EXPECT_FALSE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url1));
  EXPECT_FALSE(url_handler_->OpenUrlInternal(url1));

  // Test that an unknown internal url gets rejected.
  GURL url2 = GURL("chrome://foo-bar");
  EXPECT_FALSE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url2));
  EXPECT_FALSE(url_handler_->OpenUrlInternal(url2));

  // Test that an unknown internal chrome url gets rejected.
  GURL url3 = GURL("chrome://foo-bar");
  EXPECT_FALSE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url3));
  EXPECT_FALSE(url_handler_->OpenUrlInternal(url3));

  // Test that a known internal url gets accepted.
  GURL url4 = GURL("chrome://version");
  EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url4));
  LaunchAndWaitForActivationChange(url4);
  EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
  EXPECT_TRUE(
      ash::IsBrowserForSystemWebApp(BrowserList::GetInstance()->GetLastActive(),
                                    ash::SystemWebAppType::OS_URL_HANDLER));
}

IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests, TrailingSlashes) {
  WaitForTestSystemAppInstall();

  // There might be an initial browser from the testing framework.
  size_t initial_browser_count = BrowserList::GetInstance()->size();

  {
    GURL url = GURL("chrome://os-settings");
    DCHECK_EQ(url, "chrome://os-settings/");
    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url));
    LaunchAndWaitForActivationChange(url);
    EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
    EXPECT_TRUE(ash::IsBrowserForSystemWebApp(
        BrowserList::GetInstance()->GetLastActive(),
        ash::SystemWebAppType::SETTINGS));
    CloseApp(ash::SystemWebAppType::SETTINGS);
  }

  {
    GURL url = GURL("chrome://os-settings//");
    // Non-empty path. The app may not expect this but it mustn't crash.
    DCHECK_NE(url, "chrome://os-settings/");
    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url));
    LaunchAndWaitForActivationChange(url);
    EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
    EXPECT_TRUE(ash::IsBrowserForSystemWebApp(
        BrowserList::GetInstance()->GetLastActive(),
        ash::SystemWebAppType::SETTINGS));
    CloseApp(ash::SystemWebAppType::SETTINGS);
  }

  {
    GURL url = GURL("chrome://flags");
    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url));
    LaunchAndWaitForActivationChange(url);
    EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
    EXPECT_TRUE(ash::IsBrowserForSystemWebApp(
        BrowserList::GetInstance()->GetLastActive(),
        ash::SystemWebAppType::OS_FLAGS));
    CloseApp(ash::SystemWebAppType::OS_FLAGS);
  }

  {
    GURL url = GURL("chrome://flags//");
    // Non-empty path. The app may not expect this but it mustn't crash.
    DCHECK_NE(url, "chrome://flags/");
    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url));
    LaunchAndWaitForActivationChange(url);
    EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
    EXPECT_TRUE(ash::IsBrowserForSystemWebApp(
        BrowserList::GetInstance()->GetLastActive(),
        ash::SystemWebAppType::OS_FLAGS));
    CloseApp(ash::SystemWebAppType::OS_FLAGS);
  }

  {
    GURL url = GURL("chrome://scanning");
    DCHECK_EQ(url, "chrome://scanning/");
    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url));
    LaunchAndWaitForActivationChange(url);
    EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
    EXPECT_TRUE(ash::IsBrowserForSystemWebApp(
        BrowserList::GetInstance()->GetLastActive(),
        ash::SystemWebAppType::SCANNING));
    CloseApp(ash::SystemWebAppType::SCANNING);
  }

  {
    GURL url = GURL("chrome://scanning//");
    // Non-empty path. The app may not expect this but it mustn't crash.
    DCHECK_NE(url, "chrome://scanning/");
    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(url));
    LaunchAndWaitForActivationChange(url);
    EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
    EXPECT_TRUE(ash::IsBrowserForSystemWebApp(
        BrowserList::GetInstance()->GetLastActive(),
        ash::SystemWebAppType::SCANNING));
    CloseApp(ash::SystemWebAppType::SCANNING);
  }
}

// This test will make sure that opening the same system URL multiple times will
// re-use the existing app.
IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests,
                       LaunchLacrosDeDuplicationtest) {
  WaitForTestSystemAppInstall();

  // There might be an initial browser from the testing framework.
  size_t initial_browser_count = BrowserList::GetInstance()->size();

  // Start an application which uses the OS url handler.
  LaunchAndWaitForActivationChange(GURL("chrome://credits"));
  EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
  EXPECT_TRUE(
      ash::IsBrowserForSystemWebApp(BrowserList::GetInstance()->GetLastActive(),
                                    ash::SystemWebAppType::OS_URL_HANDLER));

  // Start another application.
  LaunchAndWaitForActivationChange(GURL("chrome://flags"));
  EXPECT_EQ(initial_browser_count + 2, BrowserList::GetInstance()->size());
  EXPECT_TRUE(
      ash::IsBrowserForSystemWebApp(BrowserList::GetInstance()->GetLastActive(),
                                    ash::SystemWebAppType::OS_FLAGS));

  // Start an application of the first type and see that no new app got created.
  LaunchAndWaitForActivationChange(GURL("chrome://credits"));
  EXPECT_EQ(initial_browser_count + 2, BrowserList::GetInstance()->size());
  EXPECT_TRUE(
      ash::IsBrowserForSystemWebApp(BrowserList::GetInstance()->GetLastActive(),
                                    ash::SystemWebAppType::OS_URL_HANDLER));
}

// This test will make sure that opening a different system URL (other than
// flags) will open different windows.
IN_PROC_BROWSER_TEST_P(SystemWebAppOpenInAshFromLacrosTests,
                       LaunchLacrosCreateNewAppForNewSystemUrl) {
  WaitForTestSystemAppInstall();

  // There might be an initial browser from the testing framework.
  size_t initial_browser_count = BrowserList::GetInstance()->size();

  // Start an application using the OS Url handler.
  LaunchAndWaitForActivationChange(GURL("chrome://credits"));
  EXPECT_EQ(initial_browser_count + 1, BrowserList::GetInstance()->size());
  EXPECT_TRUE(
      ash::IsBrowserForSystemWebApp(BrowserList::GetInstance()->GetLastActive(),
                                    ash::SystemWebAppType::OS_URL_HANDLER));

  // Start another application using the OS Url handler.
  LaunchAndWaitForActivationChange(GURL("chrome://components"));
  EXPECT_EQ(initial_browser_count + 2, BrowserList::GetInstance()->size());
  EXPECT_TRUE(
      ash::IsBrowserForSystemWebApp(BrowserList::GetInstance()->GetLastActive(),
                                    ash::SystemWebAppType::OS_URL_HANDLER));
}

class SystemWebAppManagerCloseFromScriptsTest
    : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> {
 public:
  SystemWebAppManagerCloseFromScriptsTest() {
    SetSystemWebAppInstallation(
        ash::TestSystemWebAppInstallation::
            SetupAppWithAllowScriptsToCloseWindows(true));
  }
  ~SystemWebAppManagerCloseFromScriptsTest() override = default;
};

IN_PROC_BROWSER_TEST_P(SystemWebAppManagerCloseFromScriptsTest, WindowClose) {
  WaitForTestSystemAppInstall();

  Browser* app_browser;
  LaunchApp(GetAppType(), &app_browser);

  const GURL kPageURL = GetStartUrl().Resolve("/page2.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(app_browser, kPageURL));
  EXPECT_EQ(kPageURL, app_browser->tab_strip_model()
                          ->GetActiveWebContents()
                          ->GetLastCommittedURL());

  EXPECT_TRUE(
      content::ExecJs(app_browser->tab_strip_model()->GetActiveWebContents(),
                      "window.close();"));

  ui_test_utils::WaitForBrowserToClose(app_browser);
  EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
}

class SystemWebAppManagerShouldNotCloseFromScriptsTest
    : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> {
 public:
  SystemWebAppManagerShouldNotCloseFromScriptsTest() {
    SetSystemWebAppInstallation(
        ash::TestSystemWebAppInstallation::
            SetupAppWithAllowScriptsToCloseWindows(false));
  }
  ~SystemWebAppManagerShouldNotCloseFromScriptsTest() override = default;
};

IN_PROC_BROWSER_TEST_P(SystemWebAppManagerShouldNotCloseFromScriptsTest,
                       ShouldNotCloseWindow) {
  WaitForTestSystemAppInstall();

  Browser* app_browser;
  LaunchApp(GetAppType(), &app_browser);

  const GURL kPageURL = GetStartUrl().Resolve("/page2.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(app_browser, kPageURL));
  EXPECT_EQ(kPageURL, app_browser->tab_strip_model()
                          ->GetActiveWebContents()
                          ->GetLastCommittedURL());

  content::WebContentsConsoleObserver console_observer(
      app_browser->tab_strip_model()->GetActiveWebContents());
  console_observer.SetPattern(
      "Scripts may close only the windows that were opened by them.");

  EXPECT_TRUE(
      content::ExecJs(app_browser->tab_strip_model()->GetActiveWebContents(),
                      "window.close();"));

  ASSERT_TRUE(console_observer.Wait());
  EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
}

class SystemWebAppNewWindowMenuItemTest
    : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> {
 public:
  SystemWebAppNewWindowMenuItemTest() {
    SetSystemWebAppInstallation(
        ash::TestSystemWebAppInstallation::SetUpAppWithNewWindowMenuItem());
  }
  ~SystemWebAppNewWindowMenuItemTest() override = default;

  ash::ShelfItemDelegate* GetAppShelfItemDelegate() {
    auto app_id = GetManager().GetAppIdForSystemApp(GetAppType()).value();
    return ash::ShelfModel::Get()->GetShelfItemDelegate(ash::ShelfID(app_id));
  }

  std::unique_ptr<AppServiceAppItem> GetAppServiceAppItem() {
    Profile* profile = browser()->profile();
    std::unique_ptr<AppServiceAppItem> item;
    auto app_id = GetManager().GetAppIdForSystemApp(GetAppType()).value();
    apps::AppServiceProxyFactory::GetForProfile(profile)
        ->AppRegistryCache()
        .ForOneApp(
            app_id, [profile, &item](const apps::AppUpdate& update) {
              item = std::make_unique<AppServiceAppItem>(
                  profile, /*model_updater=*/nullptr, /*sync_item=*/nullptr,
                  update);

              // Because model updater is null, set position manually.
              item->SetChromePosition(item->CalculateDefaultPositionForTest());
            });
    return item;
  }

  std::unique_ptr<ui::MenuModel> GetShelfContextMenu(
      ash::ShelfItemDelegate* item_delegate,
      int64_t display_id) {
    base::RunLoop run_loop;
    std::unique_ptr<ui::MenuModel> menu;
    item_delegate->GetContextMenu(
        display_id, base::BindLambdaForTesting(
                        [&](std::unique_ptr<ui::SimpleMenuModel> created_menu) {
                          menu = std::move(created_menu);
                          run_loop.Quit();
                        }));
    run_loop.Run();
    return menu;
  }

  std::unique_ptr<ui::SimpleMenuModel> GetAppListContextMenu(
      ChromeAppListItem* item) {
    base::RunLoop run_loop;
    std::unique_ptr<ui::SimpleMenuModel> menu;
    item->GetContextMenuModel(
        ash::AppListItemContext::kNone,
        base::BindLambdaForTesting(
            [&](std::unique_ptr<ui::SimpleMenuModel> created_menu) {
              menu = std::move(created_menu);
              run_loop.Quit();
            }));
    run_loop.Run();
    return menu;
  }

  void ExpectMenuCommandLaunchesSystemWebApp(
      std::unique_ptr<ui::MenuModel> menu,
      int command_id,
      const GURL& app_url) {
    ASSERT_TRUE(menu);
    ui::MenuModel* model = menu.get();

    size_t command_index;
    ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model,
                                                &command_index);
    EXPECT_TRUE(menu->IsEnabledAt(command_index));

    content::TestNavigationObserver observer(app_url);
    observer.StartWatchingNewWebContents();
    menu->ActivatedAt(command_index);
    observer.Wait();
  }

  int64_t GetDisplayId() {
    return display::Screen::GetScreen()->GetPrimaryDisplay().id();
  }
};

IN_PROC_BROWSER_TEST_P(SystemWebAppNewWindowMenuItemTest,
                       ShelfContextMenuOpensNewWindow) {
  WaitForTestSystemAppInstall();

  // Launch the app so it shows up in shelf.
  LaunchApp(GetAppType());

  auto* shelf_item_delegate = GetAppShelfItemDelegate();
  ASSERT_TRUE(shelf_item_delegate);

  ExpectMenuCommandLaunchesSystemWebApp(
      GetShelfContextMenu(shelf_item_delegate, GetDisplayId()), ash::LAUNCH_NEW,
      GetStartUrl());

  EXPECT_EQ(2U, GetSystemWebAppBrowserCount(GetAppType()));
}

IN_PROC_BROWSER_TEST_P(SystemWebAppNewWindowMenuItemTest,
                       AppListContextMenuLaunchNew) {
  WaitForTestSystemAppInstall();

  LaunchApp(GetAppType());

  auto item = GetAppServiceAppItem();
  ASSERT_TRUE(item);

  ExpectMenuCommandLaunchesSystemWebApp(GetAppListContextMenu(item.get()),
                                        ash::LAUNCH_NEW, GetStartUrl());

  EXPECT_EQ(2U, GetSystemWebAppBrowserCount(GetAppType()));
}

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppLinkCaptureBrowserTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppLaunchProfileBrowserTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_GUEST_SESSION_P(
    SystemWebAppLaunchProfileGuestSessionBrowserTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppManagerWindowSizeControlsTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_ALL_PROFILE_TYPES_P(
    SystemWebAppLaunchOmniboxNavigateBrowsertest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppManagerCloseFromScriptsTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppManagerShouldNotCloseFromScriptsTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppNewWindowMenuItemTest);

INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
    SystemWebAppOpenInAshFromLacrosTests);

}  // namespace web_app