// 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/browser.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <array>
#include <memory>
#include <string>

#include "ash/constants/web_app_id_constants.h"
#include "base/byte_size.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/run_until.h"
#include "base/test/test_future.h"
#include "base/version_info/version_info.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/apps/app_service/app_registry_cache_waiter.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/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/app_tab_helper.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit.h"
#include "chrome/browser/resource_coordinator/tab_manager.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/sessions/session_service_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/ui/bookmarks/bookmark_bar_controller.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_manager_service.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_ui_prefs.h"
#include "chrome/browser/ui/browser_web_contents_delegate/browser_web_contents_delegate.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/create_browser_window.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/fullscreen/browser_window_fullscreen_controller.h"
#include "chrome/browser/ui/interaction/browser_elements.h"
#include "chrome/browser/ui/search/search_tab_helper.h"
#include "chrome/browser/ui/startup/launch_mode_recorder.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
#include "chrome/browser/ui/startup/startup_types.h"
#include "chrome/browser/ui/startup/web_app_startup_utils.h"
#include "chrome/browser/ui/tabs/pinned_tab_codec.h"
#include "chrome/browser/ui/tabs/split_tab_metrics.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/unload_controller.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/window_feature_controller/window_feature_controller.h"
#include "chrome/browser/ui/window_metadata/window_metadata_controller.h"
#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/chrome_test_path_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/search_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/embedder_support/switches.h"
#include "components/javascript_dialogs/app_modal_dialog_controller.h"
#include "components/javascript_dialogs/app_modal_dialog_queue.h"
#include "components/javascript_dialogs/app_modal_dialog_view.h"
#include "components/javascript_dialogs/tab_modal_dialog_manager.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/omnibox/common/omnibox_focus_state.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url_service.h"
#include "components/services/app_service/public/cpp/app_launch_params.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/sessions/core/command_storage_manager_test_helper.h"
#include "components/strings/grit/components_strings.h"
#include "components/translate/core/browser/language_state.h"
#include "components/translate/core/common/language_detection_details.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/webapps/common/web_app_id.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/reload_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/url_constants.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/no_renderer_crashes_assertion.h"
#include "content/public/test/slow_http_response.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/extension_registrar.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/switches.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/mojom/window_show_state.mojom.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/ui_base_features.h"
#include "ui/display/types/display_constants.h"
#include "ui/views/focus/focus_manager.h"

#if BUILDFLAG(IS_MAC)
#include "base/apple/scoped_nsautorelease_pool.h"
#include "chrome/browser/ui/cocoa/test/run_loop_testing.h"
#include "ui/accelerated_widget_mac/ca_transaction_observer.h"
#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "base/i18n/rtl.h"
#include "base/test/file_path_reparse_point_win.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "ash/constants/ash_switches.h"
#include "chrome/test/base/testing_profile.h"
#include "components/user_manager/user_names.h"

#endif

using base::ASCIIToUTF16;
using content::HostZoomMap;
using content::NavigationController;
using content::NavigationEntry;
using content::OpenURLParams;
using content::Referrer;
using content::WebContents;
using content::WebContentsObserver;
using extensions::Extension;
using javascript_dialogs::AppModalDialogController;
using javascript_dialogs::AppModalDialogQueue;

namespace {

const char* kBeforeUnloadHTML =
    "<html><head><title>beforeunload</title></head><body>"
    "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
    "</body></html>";

const char16_t* kOpenNewBeforeUnloadPage =
    u"w=window.open(); w.onbeforeunload=function(e){return 'foo'};";

const base::FilePath::CharType* kTitle1File = FILE_PATH_LITERAL("title1.html");
const base::FilePath::CharType* kTitle2File = FILE_PATH_LITERAL("title2.html");

// Returns true if any browser window uses an off-the-record profile related
// to `profile`. Inlined from the previous chrome::IsOffTheRecordBrowserInUse
// helper in chrome/browser/ui/browser_finder.h.
bool IsOffTheRecordBrowserInUse(Profile* profile) {
  if (!profile) {
    return false;
  }

  bool off_the_record_in_use = false;
  GlobalBrowserCollection::GetInstance()->ForEach(
      [&](BrowserWindowInterface* browser) {
        Profile* window_profile = browser->GetProfile();
        if (window_profile && window_profile->IsSameOrParent(profile) &&
            window_profile->IsOffTheRecord()) {
          off_the_record_in_use = true;
        }
        return !off_the_record_in_use;
      });
  return off_the_record_in_use;
}

// Given a page title, returns the expected window caption string.
std::u16string WindowCaptionFromPageTitle(const std::u16string& page_title) {
#if BUILDFLAG(IS_MAC)
  // On Mac, we don't want to suffix the page title with the application name.
  if (page_title.empty()) {
    return l10n_util::GetStringUTF16(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED);
  }
  return page_title;
#else
  if (page_title.empty()) {
    return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
  }

  return l10n_util::GetStringFUTF16(IDS_BROWSER_WINDOW_TITLE_FORMAT,
                                    page_title);
#endif
}

// Returns the number of active RenderProcessHosts.
int CountRenderProcessHosts() {
  int result = 0;
  for (content::RenderProcessHost::iterator i(
           content::RenderProcessHost::AllHostsIterator());
       !i.IsAtEnd(); i.Advance()) {
    ++result;
  }
  return result;
}

class TabClosingObserver : public TabStripModelObserver {
 public:
  TabClosingObserver() = default;
  TabClosingObserver(const TabClosingObserver&) = delete;
  TabClosingObserver& operator=(const TabClosingObserver&) = delete;

  void OnTabStripModelChanged(
      TabStripModel* tab_strip_model,
      const TabStripModelChange& change,
      const TabStripSelectionChange& selection) override {
    if (change.type() != TabStripModelChange::kRemoved) {
      return;
    }

    auto* remove = change.GetRemove();
    for (const auto& contents : remove->contents) {
      if (contents.remove_reason == TabRemovedReason::kDeleted) {
        closing_count_ += 1;
      }
    }
  }

  int closing_count() const { return closing_count_; }

 private:
  int closing_count_ = 0;
};

// Used by CloseWithAppMenuOpen. Posts a CloseWindowCallback and shows the app
// menu.
void RunCloseWithAppMenuCallback(Browser* browser) {
  // ShowAppMenu is modal under views. Schedule a task that closes the window.
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(&chrome::CloseWindow, browser));
  chrome::ShowAppMenu(browser);
}

class RenderViewSizeObserver : public content::WebContentsObserver {
 public:
  RenderViewSizeObserver(content::WebContents* web_contents,
                         BrowserWindow* browser_window)
      : WebContentsObserver(web_contents), browser_window_(browser_window) {}
  RenderViewSizeObserver(const RenderViewSizeObserver&) = delete;
  RenderViewSizeObserver& operator=(const RenderViewSizeObserver&) = delete;

  void GetSizeForRenderViewHost(content::RenderViewHost* render_view_host,
                                gfx::Size* rwhv_create_size,
                                gfx::Size* rwhv_commit_size,
                                gfx::Size* wcv_commit_size) {
    RenderViewSizes::const_iterator result = render_view_sizes_.end();
    result = render_view_sizes_.find(render_view_host);
    if (result != render_view_sizes_.end()) {
      *rwhv_create_size = result->second.rwhv_create_size;
      *rwhv_commit_size = result->second.rwhv_commit_size;
      *wcv_commit_size = result->second.wcv_commit_size;
    }
  }

  void set_wcv_resize_insets(const gfx::Size& wcv_resize_insets) {
    wcv_resize_insets_ = wcv_resize_insets;
  }

  // Cache the size when RenderViewHost's main frame is first created.
  void RenderFrameCreated(
      content::RenderFrameHost* render_frame_host) override {
    if (!render_frame_host->GetParent()) {
      content::RenderViewHost* render_view_host =
          render_frame_host->GetRenderViewHost();
      render_view_sizes_[render_view_host].rwhv_create_size =
          render_view_host->GetWidget()->GetView()->GetViewBounds().size();
    }
  }

  // Enlarge WebContentsView by |wcv_resize_insets_| while the navigation entry
  // is pending. Since the new render process may not be created when the
  // navigation starts if the feature DeferSpeculativeRFHCreation is enabled,
  // resize the window when the navigation is ready to commit. Otherwise we will
  // change the size of the original window.
  void ReadyToCommitNavigation(
      content::NavigationHandle* navigation_handle) override {
    Resize();
  }

  void Resize() {
    if (wcv_resize_insets_.IsEmpty()) {
      return;
    }
    // Resizing the main browser window by |wcv_resize_insets_| will
    // automatically resize the WebContentsView by the same amount.
    // Just resizing WebContentsView directly doesn't work on Linux, because the
    // next automatic layout of the browser window will resize WebContentsView
    // back to the previous size.  To make it consistent, resize main browser
    // window on all platforms.
    gfx::Rect bounds(browser_window_->GetBounds());
    gfx::Size size(bounds.size());
    size.Enlarge(wcv_resize_insets_.width(), wcv_resize_insets_.height());
    bounds.set_size(size);
    browser_window_->SetBounds(bounds);
    // Let the message loop run so that resize actually takes effect.
    content::RunAllPendingInMessageLoop();
  }

  // Cache the sizes of RenderWidgetHostView and WebContentsView when the
  // navigation entry is committed, which is before
  // WebContentsObserver::DidFinishNavigation is called.
  void NavigationEntryCommitted(
      const content::LoadCommittedDetails& details) override {
    content::RenderViewHost* rvh =
        web_contents()->GetPrimaryMainFrame()->GetRenderViewHost();
    render_view_sizes_[rvh].rwhv_commit_size =
        web_contents()->GetRenderWidgetHostView()->GetViewBounds().size();
    render_view_sizes_[rvh].wcv_commit_size =
        web_contents()->GetContainerBounds().size();
  }

 private:
  struct Sizes {
    gfx::Size rwhv_create_size;  // Size of RenderWidgetHostView when created.
    gfx::Size rwhv_commit_size;  // Size of RenderWidgetHostView when committed.
    gfx::Size wcv_commit_size;   // Size of WebContentsView when committed.
  };

  typedef std::map<content::RenderViewHost*, Sizes> RenderViewSizes;
  RenderViewSizes render_view_sizes_;
  // Enlarge WebContentsView by this size insets in
  // DidStartNavigation.
  gfx::Size wcv_resize_insets_;
  raw_ptr<BrowserWindow> browser_window_;  // Weak ptr.
};

}  // namespace

class BrowserTest : public extensions::ExtensionBrowserTest {
 protected:
  void SetUpOnMainThread() override {
    extensions::ExtensionBrowserTest::SetUpOnMainThread();
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  // In RTL locales wrap the page title with RTL embedding characters so that it
  // matches the value returned by GetWindowTitle().
  std::u16string LocaleWindowCaptionFromPageTitle(
      const std::u16string& expected_title) {
    std::u16string page_title = WindowCaptionFromPageTitle(expected_title);
#if BUILDFLAG(IS_WIN)
    std::string locale = g_browser_process->GetApplicationLocale();
    if (base::i18n::GetTextDirectionForLocale(locale.c_str()) ==
        base::i18n::RIGHT_TO_LEFT) {
      base::i18n::WrapStringWithLTRFormatting(&page_title);
    }

    return page_title;
#else
    // Do we need to use the above code on POSIX as well?
    return page_title;
#endif
  }

  void OpenURLFromTab(WebContents* source, OpenURLParams params) {
    BrowserWebContentsDelegate::From(browser())->OpenURLFromTab(
        source, params,
        /*navigation_handle_callback=*/{});
  }

  // Returns the app extension aptly named "App Test".
  const Extension* GetExtension() {
    extensions::ExtensionRegistry* registry =
        extensions::ExtensionRegistry::Get(browser()->GetProfile());
    for (const scoped_refptr<const extensions::Extension>& extension :
         registry->enabled_extensions()) {
      if (extension->name() == "App Test") {
        return extension.get();
      }
    }
    NOTREACHED();
  }

 private:
  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
};

// Launch the app on a page with no title, check that the app title was set
// correctly.
IN_PROC_BROWSER_TEST_F(BrowserTest, NoTitle) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), chrome_test_utils::GetTestUrl(
                     base::FilePath(base::FilePath::kCurrentDirectory),
                     base::FilePath(kTitle1File))));
  EXPECT_EQ(
      LocaleWindowCaptionFromPageTitle(u"title1.html"),
      WindowMetadataController::From(browser())->GetWindowTitleForCurrentTab(
          true /* include_app_name */));
  std::u16string tab_title;
  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
  EXPECT_EQ(u"title1.html", tab_title);
}

// Check that a file:// URL displays the filename, but no path, with any ref or
// query parameters following it if the content does not have a <title> tag.
// Specifically verify the cases where the ref or query parameters have a '/'
// character in them. This is a regression test for
// https://crbug.com/40423811.
IN_PROC_BROWSER_TEST_F(BrowserTest, NoTitleFileUrl) {
  // Note that the host names used and the order of these cases are by design.
  // There must be unique query parameters and references per case (i.e. the
  // indexed foo*.com hosts) because if the same query parameter is repeated in
  // a row, then the navigation may not actually happen, as it will only appear
  // as a reference change.  Additionally, cases with references first must
  // appear after a query parameter case since otherwise it will not be a
  // navigation.
  struct {
    std::string suffix;
    std::string message;
  } cases[]{
      {"#https://foo1.com", "file:/// URL with slash in ref"},
      {"?x=https://foo2.com", "file:/// URL with slash in query parameter"},
      {"?x=https://foo3.com#https://foo3.com",
       "file:/// URL with slashes in query parameter and ref"},
      {"#https://foo4.com?x=https://foo4.com",
       "file:/// URL with slashes in ref and query parameter"},
      {"?x=https://foo6.com?x=https://foo6.com",
       "file:/// URL with slashes in multiple query parameter"},
      {"#https://foo5.com#https://foo5.com",
       "file:/// URL with slashes in multiple refs"}};

  GURL prefix_url = chrome_test_utils::GetTestUrl(
      base::FilePath(base::FilePath::kCurrentDirectory),
      base::FilePath(kTitle1File));
  std::u16string tab_title;
  std::u16string test_title;
  for (const auto& c : cases) {
    SCOPED_TRACE(c.message);
    GURL url(prefix_url.spec() + c.suffix);
    test_title = u"title1.html" + ASCIIToUTF16(c.suffix);
    content::TitleWatcher title_watcher(
        browser()->tab_strip_model()->GetActiveWebContents(), test_title);
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
    EXPECT_EQ(test_title, title_watcher.WaitAndGetTitle());
  }
}

// Launch the app, navigate to a page with a title, check that the app title
// was set correctly.
IN_PROC_BROWSER_TEST_F(BrowserTest, Title) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), chrome_test_utils::GetTestUrl(
                     base::FilePath(base::FilePath::kCurrentDirectory),
                     base::FilePath(kTitle2File))));
  const std::u16string test_title(u"Title Of Awesomeness");
  EXPECT_EQ(
      LocaleWindowCaptionFromPageTitle(test_title),
      WindowMetadataController::From(browser())->GetWindowTitleForCurrentTab(
          true /* include_app_name */));
  std::u16string tab_title;
  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
  EXPECT_EQ(test_title, tab_title);
}

#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// Check that the title is different when a page is opened in a captive portal
// window.
IN_PROC_BROWSER_TEST_F(BrowserTest, CaptivePortalWindowTitle) {
  const GURL url = chrome_test_utils::GetTestUrl(
      base::FilePath(base::FilePath::kCurrentDirectory),
      base::FilePath(kTitle2File));
  NavigateParams captive_portal_params(browser(), url,
                                       ui::PAGE_TRANSITION_TYPED);
  captive_portal_params.disposition = WindowOpenDisposition::NEW_POPUP;
  captive_portal_params.captive_portal_window_type =
      captive_portal::CaptivePortalWindowType::kPopup;
  ui_test_utils::NavigateToURL(&captive_portal_params);
  std::u16string captive_portal_window_title =
      WindowMetadataController::From(
          GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(
              captive_portal_params.navigated_or_inserted_contents))
          ->GetWindowTitleForCurrentTab(true /* include_app_name */);

  NavigateParams normal_params(browser(), url, ui::PAGE_TRANSITION_TYPED);
  normal_params.disposition = WindowOpenDisposition::NEW_POPUP;
  ui_test_utils::NavigateToURL(&normal_params);
  std::u16string normal_window_title =
      WindowMetadataController::From(
          GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(
              normal_params.navigated_or_inserted_contents))
          ->GetWindowTitleForCurrentTab(true /* include_app_name */);

  ASSERT_NE(captive_portal_window_title, normal_window_title);
}
#endif

IN_PROC_BROWSER_TEST_F(BrowserTest, NoJavaScriptDialogsActivateTab) {
  // Set up two tabs, with the tab at index 0 active.
  GURL url(chrome_test_utils::GetTestUrl(
      base::FilePath(base::FilePath::kCurrentDirectory),
      base::FilePath(kTitle1File)));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
  EXPECT_EQ(2, browser()->tab_strip_model()->count());
  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());

  WebContents* second_tab = browser()->tab_strip_model()->GetWebContentsAt(1);
  ASSERT_TRUE(second_tab);

  // Show a confirm() dialog from the tab at index 1. The active index shouldn't
  // budge.
  {
    content::WebContentsConsoleObserver confirm_observer(second_tab);
    confirm_observer.SetPattern("*confirm*suppressed*");
    second_tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
        u"confirm('Activate!');", base::NullCallback(),
        content::ISOLATED_WORLD_ID_GLOBAL);
    ASSERT_TRUE(confirm_observer.Wait());
  }
  EXPECT_EQ(2, browser()->tab_strip_model()->count());
  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());

  // Show a prompt() dialog from the tab at index 1. The active index shouldn't
  // budge.
  {
    content::WebContentsConsoleObserver prompt_observer(second_tab);
    prompt_observer.SetPattern("*prompt*suppressed*");
    second_tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
        u"prompt('Activate!');", base::NullCallback(),
        content::ISOLATED_WORLD_ID_GLOBAL);
    ASSERT_TRUE(prompt_observer.Wait());
  }
  EXPECT_EQ(2, browser()->tab_strip_model()->count());
  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());

  // Show an alert() dialog from the tab at index 1. The active index shouldn't
  // budge.
  auto* js_dialog_manager =
      javascript_dialogs::TabModalDialogManager::FromWebContents(second_tab);
  base::RunLoop alert_wait;
  js_dialog_manager->SetDialogShownCallbackForTesting(alert_wait.QuitClosure());
  second_tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      u"alert('Activate!');", base::NullCallback(),
      content::ISOLATED_WORLD_ID_GLOBAL);
  alert_wait.Run();
  EXPECT_EQ(2, browser()->tab_strip_model()->count());
  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
}

// Regression test for crbug.com/40624231.
IN_PROC_BROWSER_TEST_F(
    BrowserTest,
    ClosingBackgroundSameProcessTabRestoresFocusToActiveWebContents) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/empty.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  TabStripModel* tab_strip_model = browser()->tab_strip_model();
  content::WebContents* active_contents =
      tab_strip_model->GetActiveWebContents();
  ASSERT_TRUE(active_contents);
  ASSERT_TRUE(content::ExecJs(
      active_contents,
      "document.body.innerHTML = '<input id=\"q\" autofocus>';"));
  content::ExecuteScriptAsync(active_contents,
                              R"JS(
        let popupWin = window.open('', '_blank');
        popupWin.document.open();
        popupWin.document.write(
            '<html><body onload="window.print()">HTML</body></html>');
        popupWin.document.close();
      )JS");
  ASSERT_TRUE(
      base::test::RunUntil([&]() { return tab_strip_model->count() == 2; }));

  content::WebContents* closing_contents = tab_strip_model->GetWebContentsAt(1);
  ASSERT_TRUE(closing_contents);
  ASSERT_TRUE(content::WaitForLoadStop(closing_contents));
  auto* dialog_manager =
      web_modal::WebContentsModalDialogManager::FromWebContents(
          closing_contents);
  ASSERT_TRUE(dialog_manager);
  ASSERT_TRUE(
      base::test::RunUntil([&]() { return dialog_manager->IsDialogActive(); }));

  // Put focus back on the opener and close the popup tab in the background.
  tab_strip_model->ActivateTabAt(0);
  ASSERT_EQ(active_contents, tab_strip_model->GetActiveWebContents());
  ASSERT_EQ(active_contents->GetPrimaryMainFrame()->GetProcess(),
            closing_contents->GetPrimaryMainFrame()->GetProcess())
      << "Test relies on active and closing tabs sharing a renderer process.";

  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
  ASSERT_TRUE(browser_view);
  views::View* active_webview = browser_view->GetActiveContentsWebView();
  ASSERT_TRUE(active_webview);
  views::FocusManager* focus_manager = browser_view->GetFocusManager();
  ASSERT_TRUE(focus_manager);

  tab_strip_model->CloseWebContentsAt(1, CLOSE_USER_GESTURE);
  ASSERT_TRUE(
      base::test::RunUntil([&]() { return tab_strip_model->count() == 1; }));
  ASSERT_TRUE(base::test::RunUntil([&]() {
    content::RenderWidgetHostView* rwhv =
        active_contents->GetRenderWidgetHostView();
    return rwhv && rwhv->HasFocus() &&
           focus_manager->GetFocusedView() == active_webview;
  }));

  content::SimulateMouseClickOrTapElementWithId(active_contents, "q");
  EXPECT_EQ("q", content::EvalJs(active_contents, "document.activeElement.id"));
}

// Create 34 tabs and verify that a lot of processes have been created. The
// exact number of processes depends on the amount of memory. Previously we
// had a hard limit of 31 processes and this test is mainly directed at
// verifying that we don't crash when we pass this limit.
// Warning: this test can take >30 seconds when running on a slow (low
// memory?) Mac builder.
// Test is flaky on Win, Linux, Mac: https://crbug.com/40702340.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
#define MAYBE_ThirtyFourTabs DISABLED_ThirtyFourTabs
#else
#define MAYBE_ThirtyFourTabs ThirtyFourTabs
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_ThirtyFourTabs) {
  GURL url(chrome_test_utils::GetTestUrl(
      base::FilePath(base::FilePath::kCurrentDirectory),
      base::FilePath(kTitle2File)));

  // There is one initial tab.
  const int kTabCount = 34;
  for (int ix = 0; ix != (kTabCount - 1); ++ix) {
    chrome::AddSelectedTabWithURL(browser(), url, ui::PAGE_TRANSITION_TYPED);
  }
  EXPECT_EQ(kTabCount, browser()->tab_strip_model()->count());

  // See GetMaxRendererProcessCount() in
  // content/browser/renderer_host/render_process_host_impl.cc
  // for the algorithm to decide how many processes to create.
  const int kExpectedProcessCount =
#if defined(ARCH_CPU_64_BITS)
      12;
#else
      17;
#endif
  if (base::SysInfo::AmountOfTotalPhysicalMemory().InGiB() >= 2) {
    EXPECT_GE(CountRenderProcessHosts(), kExpectedProcessCount);
  } else {
    EXPECT_LT(CountRenderProcessHosts(), kExpectedProcessCount);
  }
}

// Test that a browser-initiated navigation to an aborted URL load leaves around
// a pending entry if we start from the NTP but not from a normal page.
// See http://crbug.com/40079181.
IN_PROC_BROWSER_TEST_F(BrowserTest, ClearPendingOnFailUnlessNTP) {
  ASSERT_TRUE(embedded_test_server()->Start());
  WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
                                           chrome::ChromeUINewTabURLAsGURL()));

  // Navigate to a 204 URL (aborts with no content) on the NTP and make sure it
  // sticks around so that the user can edit it.
  GURL abort_url(embedded_test_server()->GetURL("/nocontent"));
  {
    content::LoadStopObserver stop_observer(web_contents);
    browser()->OpenURL(
        OpenURLParams(abort_url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
                      ui::PAGE_TRANSITION_TYPED, false),
        /*navigation_handle_callback=*/{});
    stop_observer.Wait();
    EXPECT_TRUE(web_contents->GetController().GetPendingEntry());
    EXPECT_EQ(abort_url, web_contents->GetVisibleURL());
  }

  // Navigate to a real URL.
  GURL real_url(embedded_test_server()->GetURL("/title1.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), real_url));
  EXPECT_EQ(real_url, web_contents->GetVisibleURL());

  // Now navigating to a 204 URL should clear the pending entry.
  {
    content::LoadStopObserver stop_observer(web_contents);
    browser()->OpenURL(
        OpenURLParams(abort_url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
                      ui::PAGE_TRANSITION_TYPED, false),
        /*navigation_handle_callback=*/{});
    stop_observer.Wait();
    EXPECT_FALSE(web_contents->GetController().GetPendingEntry());
    EXPECT_EQ(real_url, web_contents->GetVisibleURL());
  }
}

// Test for crbug.com/40780261. Ensure that a non-user-initiated navigation
// doesn't commit while a JS dialog is showing.
IN_PROC_BROWSER_TEST_F(BrowserTest, DialogDefersNavigationCommit) {
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL kEmptyUrl(embedded_test_server()->GetURL("/empty.html"));
  const GURL kSecondUrl(embedded_test_server()->GetURL("/title1.html"));

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kEmptyUrl));

  content::TestNavigationManager manager(contents, kSecondUrl);
  auto* js_dialog_manager =
      javascript_dialogs::TabModalDialogManager::FromWebContents(contents);

  // Start a non-user-gesture navigation to the second page but block after the
  // request is started.
  {
    auto script = content::JsReplace("window.location = $1;", kSecondUrl);
    ASSERT_TRUE(content::ExecJs(contents->GetPrimaryMainFrame(), script,
                                content::EXECUTE_SCRIPT_NO_USER_GESTURE));
    ASSERT_TRUE(manager.WaitForRequestStart());
  }

  // Show a modal JavaScript dialog.
  {
    base::RunLoop run_loop;

    js_dialog_manager->SetDialogShownCallbackForTesting(run_loop.QuitClosure());
    contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
        u"alert('one'); ", base::NullCallback(),
        content::ISOLATED_WORLD_ID_GLOBAL);
    run_loop.Run();

    ASSERT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
  }

  // Continue the navigation through the response and on to commit. Since a
  // dialog is showing, this should cause the navigation to be deferred before
  // commit and the dialog should remain showing.
  {
    ASSERT_TRUE(manager.WaitForResponse());
    manager.ResumeNavigation();

    content::NavigationHandle* handle = manager.GetNavigationHandle();
    EXPECT_FALSE(handle->IsWaitingToCommit());
    EXPECT_TRUE(handle->IsCommitDeferringConditionDeferredForTesting());
    EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
  }

  // Dismiss the dialog. This should resume the navigation.
  {
    js_dialog_manager->ClickDialogButtonForTesting(true, std::u16string());
    ASSERT_FALSE(js_dialog_manager->IsShowingDialogForTesting());

    content::NavigationHandle* handle = manager.GetNavigationHandle();
    EXPECT_FALSE(handle->IsCommitDeferringConditionDeferredForTesting());
    EXPECT_TRUE(handle->IsWaitingToCommit());
  }

  ASSERT_TRUE(manager.WaitForNavigationFinished());
}

// Test for crbug.com/40334128.  Ensure that modal dialogs are closed when a
// cross-process navigation is ready to commit.
IN_PROC_BROWSER_TEST_F(BrowserTest, CrossProcessNavCancelsDialogs) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/empty.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  // Test this with multiple alert dialogs to ensure that we can navigate away
  // even if the renderer tries to synchronously create more.
  // See http://crbug.com/40078304.
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  auto* js_dialog_manager =
      javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
  base::RunLoop dialog_wait;
  js_dialog_manager->SetDialogShownCallbackForTesting(
      dialog_wait.QuitClosure());
  contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      u"alert('one'); alert('two');", base::NullCallback(),
      content::ISOLATED_WORLD_ID_GLOBAL);
  dialog_wait.Run();
  EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());

  // A cross-site navigation should force the dialog to close.
  GURL url2("http://www.example.com/empty.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
  EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());

  // Make sure input events still work in the renderer process.
  EXPECT_FALSE(contents->GetPrimaryMainFrame()->GetProcess()->IsBlocked());
}

// Similar to CrossProcessNavCancelsDialogs, with a renderer-initiated main
// frame navigation with user gesture.
IN_PROC_BROWSER_TEST_F(BrowserTest, RendererCrossProcessNavCancelsDialogs) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/empty.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();

  // A cross-site renderer-initiated navigation with user gesture (started
  // before the dialog is shown) should force the dialog to close. (ExecJS sends
  // a user gesture by default.)
  GURL url2("http://www.example.com/empty.html");
  content::TestNavigationManager manager(contents, url2);
  EXPECT_TRUE(content::ExecJs(contents, "location = '" + url2.spec() + "';"));
  EXPECT_TRUE(manager.WaitForRequestStart());

  auto* js_dialog_manager =
      javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
  base::RunLoop dialog_wait;
  js_dialog_manager->SetDialogShownCallbackForTesting(
      dialog_wait.QuitClosure());
  content::ExecuteScriptAsync(contents, "alert('dialog')");
  dialog_wait.Run();
  EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());

  // Let the navigation to url2 finish and dismiss the dialog.
  ASSERT_TRUE(manager.WaitForNavigationFinished());
  EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());

  // Make sure input events still work in the renderer process.
  EXPECT_FALSE(contents->GetPrimaryMainFrame()->GetProcess()->IsBlocked());
}

// Ensures that a download can complete while a dialog is showing, because it
// poses no risk of dismissing the dialog.
IN_PROC_BROWSER_TEST_F(BrowserTest, DownloadDoesntDismissDialog) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/empty.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();

  // A renderer-initiated navigation without a user gesture would normally be
  // deferred until the dialog is dismissed.  If the navigation turns out to be
  // a download at response time (e.g., because download-test3.gif has a
  // Content-Disposition: attachment response header), then the download should
  // not be deferred or dismiss the dialog.
  std::unique_ptr<content::DownloadTestObserver> download_waiter(
      new content::DownloadTestObserverTerminal(
          browser()->GetProfile()->GetDownloadManager(), 1,
          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
  GURL url2(embedded_test_server()->GetURL("/download-test3.gif"));
  content::TestNavigationManager manager(contents, url2);
  EXPECT_TRUE(content::ExecJs(contents, "location = '" + url2.spec() + "';",
                              content::EXECUTE_SCRIPT_NO_USER_GESTURE));
  EXPECT_TRUE(manager.WaitForRequestStart());

  // Show a dialog while we're waiting for the url2 response.
  auto* js_dialog_manager =
      javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
  base::RunLoop dialog_wait;
  js_dialog_manager->SetDialogShownCallbackForTesting(
      dialog_wait.QuitClosure());
  content::ExecuteScriptAsync(contents, "alert('dialog')");
  dialog_wait.Run();
  EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());

  // Let the url2 response finish and become a download, without dismissing the
  // dialog.
  ASSERT_TRUE(manager.WaitForNavigationFinished());
  EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
  download_waiter->WaitForFinished();

  // Close the dialog after the download finishes, to clean up.
  js_dialog_manager->ClickDialogButtonForTesting(true, std::u16string());
  EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());

  // Make sure input events still work in the renderer process.
  EXPECT_FALSE(contents->GetPrimaryMainFrame()->GetProcess()->IsBlocked());
}

#if BUILDFLAG(IS_MAC)
// Flaky on Mac 10.11 CI builder. See https://crbug.com/40792686.
#define MAYBE_SadTabCancelsDialogs DISABLED_SadTabCancelsDialogs
#else
#define MAYBE_SadTabCancelsDialogs SadTabCancelsDialogs
#endif

// Make sure that dialogs are closed after a renderer process dies, and that
// subsequent navigations work.  See http://crbug/com/343265.
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_SadTabCancelsDialogs) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL beforeunload_url(embedded_test_server()->GetURL("/beforeunload.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), beforeunload_url));
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  content::PrepContentsForBeforeUnloadTest(contents);

  // Start a navigation to trigger the beforeunload dialog.
  contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      u"window.location.href = 'about:blank'", base::NullCallback(),
      content::ISOLATED_WORLD_ID_GLOBAL);
  AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
  EXPECT_TRUE(alert->IsValid());
  AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
  EXPECT_TRUE(dialog_queue->HasActiveDialog());

  // Crash the renderer process and ensure the dialog is gone.
  content::RenderProcessHost* child_process =
      contents->GetPrimaryMainFrame()->GetProcess();
  content::RenderProcessHostWatcher crash_observer(
      child_process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
  child_process->Shutdown(0);
  crash_observer.Wait();
  EXPECT_FALSE(dialog_queue->HasActiveDialog());

  // Make sure subsequent navigations work.
  GURL url2("http://www.example.com/empty.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
}

// Make sure that dialogs opened by subframes are closed when the process dies.
// See http://crbug.com/40079401.
IN_PROC_BROWSER_TEST_F(BrowserTest, SadTabCancelsSubframeDialogs) {
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL("data:text/html, <html><body></body></html>")));

  // Create an iframe that opens an alert dialog.
  auto* js_dialog_manager =
      javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
  base::RunLoop dialog_wait;
  js_dialog_manager->SetDialogShownCallbackForTesting(
      dialog_wait.QuitClosure());
  contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      u"f = document.createElement('iframe');"
      u"f.srcdoc = '<script>alert(1)</script>';"
      u"document.body.appendChild(f);",
      base::NullCallback(), content::ISOLATED_WORLD_ID_GLOBAL);
  dialog_wait.Run();
  EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());

  // Crash the renderer process and ensure the dialog is gone.
  content::RenderProcessHost* child_process =
      contents->GetPrimaryMainFrame()->GetProcess();
  content::RenderProcessHostWatcher crash_observer(
      child_process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
  child_process->Shutdown(0);
  crash_observer.Wait();
  EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());

  // Make sure subsequent navigations work.
  GURL url2("data:text/html,foo");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
}

// Test for crbug.com/40999745.  Reloading a page with a before unload handler
// and then canceling the dialog should not leave the throbber spinning.
// https://crbug.com/40599465: Test is flakily timing out
IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_ReloadThenCancelBeforeUnload) {
  GURL url(std::string("data:text/html,") + kBeforeUnloadHTML);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  content::PrepContentsForBeforeUnloadTest(contents);

  // Navigate to another page, but click cancel in the dialog.  Make sure that
  // the throbber stops spinning.
  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();

  alert->CloseModalDialog();
  EXPECT_FALSE(contents->IsLoading());

  // Clear the beforeunload handler so the test can easily exit.
  contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      u"onbeforeunload=null;", base::NullCallback(),
      content::ISOLATED_WORLD_ID_GLOBAL);
}

// Test for crbug.com/40163641.  A page closed with window.close() should not
// have two beforeunload dialogs shown. http://crbug.com/40382821
IN_PROC_BROWSER_TEST_F(BrowserTest,
                       DISABLED_SingleBeforeUnloadAfterWindowClose) {
  browser()
      ->tab_strip_model()
      ->GetActiveWebContents()
      ->GetPrimaryMainFrame()
      ->ExecuteJavaScriptWithUserGestureForTests(
          kOpenNewBeforeUnloadPage, base::NullCallback(),
          content::ISOLATED_WORLD_ID_GLOBAL);

  // Close the new window with JavaScript, which should show a single
  // beforeunload dialog.  Then show another alert, to make it easy to verify
  // that a second beforeunload dialog isn't shown.
  browser()
      ->tab_strip_model()
      ->GetWebContentsAt(0)
      ->GetPrimaryMainFrame()
      ->ExecuteJavaScriptWithUserGestureForTests(
          u"w.close(); alert('bar');", base::NullCallback(),
          content::ISOLATED_WORLD_ID_GLOBAL);
  AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
  alert->view()->AcceptAppModalDialog();

  alert = ui_test_utils::WaitForAppModalDialog();
  EXPECT_FALSE(alert->is_before_unload_dialog());
  alert->view()->AcceptAppModalDialog();
}

// Test that when a page has an onbeforeunload handler, reloading a page shows a
// different dialog than navigating to a different page.
IN_PROC_BROWSER_TEST_F(BrowserTest, BeforeUnloadVsBeforeReload) {
  GURL url(std::string("data:text/html,") + kBeforeUnloadHTML);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  content::PrepContentsForBeforeUnloadTest(contents);

  // Reload the page, and check that we get a "before reload" dialog.
  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
  EXPECT_TRUE(alert->is_reload());

  // Proceed with the reload.
  alert->view()->AcceptAppModalDialog();
  EXPECT_TRUE(content::WaitForLoadStop(contents));

  content::PrepContentsForBeforeUnloadTest(contents);

  // Navigate to another url, and check that we get a "before unload" dialog.
  GURL url2(url::kAboutBlankURL);
  browser()->OpenURL(
      OpenURLParams(url2, Referrer(), WindowOpenDisposition::CURRENT_TAB,
                    ui::PAGE_TRANSITION_TYPED, false),
      /*navigation_handle_callback=*/{});

  alert = ui_test_utils::WaitForAppModalDialog();
  EXPECT_FALSE(alert->is_reload());

  // Accept the navigation so we end up on a page without a beforeunload hook.
  alert->view()->AcceptAppModalDialog();
  EXPECT_TRUE(content::WaitForLoadStop(contents));
}

IN_PROC_BROWSER_TEST_F(BrowserTest, NotifiesBrowserDidClose) {
  const GURL url(base::StrCat({"data:text/html,", kBeforeUnloadHTML}));
  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  content::PrepContentsForBeforeUnloadTest(contents);

  // Assert the did close notification is not fired if unload handlers prevent
  // the browser from closing.
  base::MockCallback<BrowserWindowInterface::BrowserDidCloseCallback>
      browser_did_close_callback;
  EXPECT_CALL(browser_did_close_callback, Run).Times(0);
  base::CallbackListSubscription subscription =
      browser()->RegisterBrowserDidClose(browser_did_close_callback.Get());
  browser()->GetWindow()->Close();
  EXPECT_FALSE(browser()->IsDeleteScheduled());
  testing::Mock::VerifyAndClearExpectations(&browser_did_close_callback);

  // Close the browser skipping unload handlers, ensure the did close
  // notification is propagated.
  EXPECT_CALL(browser_did_close_callback, Run).Times(1);
  UnloadController::From(browser())->set_force_skip_warning_user_on_close(true);
  browser()->GetWindow()->Close();
  EXPECT_TRUE(browser()->IsDeleteScheduled());
}

// TODO(crbug.com/40641945): Test this with implicitly-created links.
IN_PROC_BROWSER_TEST_F(BrowserTest, TargetBlankLinkOpensInGroup) {
  ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
  ASSERT_TRUE(embedded_test_server()->Start());

  // Add a grouped tab.
  TabStripModel* const model = browser()->tab_strip_model();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL(
                     "/frame_tree/anchor_to_same_site_location.html")));
  const tab_groups::TabGroupId group_id = model->AddToNewGroup({0});

  // Click a target=_blank link.
  WebContents* const contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(
      ExecJs(contents, "simulateClick(\"test-anchor-with-blank-target\", {})"));

  // The new tab should have inherited the tab group from the first tab.
  EXPECT_EQ(group_id, browser()->tab_strip_model()->GetTabGroupForTab(1));
}

IN_PROC_BROWSER_TEST_F(BrowserTest, NewTabFromLinkInGroupedTabOpensInGroup) {
  ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
  ASSERT_TRUE(embedded_test_server()->Start());

  // Add a grouped tab.
  TabStripModel* const model = browser()->tab_strip_model();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/empty.html")));
  const tab_groups::TabGroupId group_id = model->AddToNewGroup({0});

  // Open a new background tab.
  WebContents* const contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  OpenURLFromTab(
      contents,
      OpenURLParams(embedded_test_server()->GetURL("/empty.html"), Referrer(),
                    WindowOpenDisposition::NEW_BACKGROUND_TAB,
                    ui::PAGE_TRANSITION_TYPED, false));

  // It should have inherited the tab group from the first tab.
  EXPECT_EQ(group_id, model->GetTabGroupForTab(1));
}

// Tests that other popup navigations that do not follow the steps at
// http://www.google.com/chrome/intl/en/webmasters-faq.html#newtab will not
// fork a new renderer process.
IN_PROC_BROWSER_TEST_F(BrowserTest, OtherRedirectsDontForkProcess) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      embedder_support::kDisablePopupBlocking);

  // Create http and https servers for a cross-site transition.
  ASSERT_TRUE(embedded_test_server()->Start());
  net::EmbeddedTestServer https_test_server(
      net::EmbeddedTestServer::TYPE_HTTPS);
  https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
  ASSERT_TRUE(https_test_server.Start());
  GURL http_url(embedded_test_server()->GetURL("/title1.html"));
  GURL https_url(https_test_server.GetURL("/title2.html"));

  // Start with an http URL.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));
  WebContents* oldtab = browser()->tab_strip_model()->GetActiveWebContents();
  content::RenderProcessHost* process =
      oldtab->GetPrimaryMainFrame()->GetProcess();

  // Now open a tab to a blank page and redirect it cross-site.
  std::string dont_fork_popup = "w=window.open();";
  dont_fork_popup += "w.document.location=\"";
  dont_fork_popup += https_url.spec();
  dont_fork_popup += "\";";

  ui_test_utils::TabAddedWaiter tab_add(browser());
  EXPECT_TRUE(content::ExecJs(oldtab->GetPrimaryMainFrame(), dont_fork_popup));

  // The tab should be created by the time the script finished running.
  ASSERT_EQ(2, browser()->tab_strip_model()->count());
  WebContents* newtab = browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(newtab);
  EXPECT_NE(oldtab, newtab);

  // New tab should be in the middle of document.location navigation.
  EXPECT_TRUE(newtab->IsLoading());
  content::WaitForLoadStop(newtab);

  ASSERT_TRUE(newtab->GetController().GetLastCommittedEntry());
  EXPECT_EQ(https_url.spec(),
            newtab->GetController().GetLastCommittedEntry()->GetURL().spec());

  // Process of the (cross-site) popup window depends on whether
  // site-per-process mode is enabled or not.
  content::RenderProcessHost* popup_process =
      newtab->GetPrimaryMainFrame()->GetProcess();
  if (content::AreAllSitesIsolatedForTesting()) {
    EXPECT_NE(process, popup_process);
  } else {
    EXPECT_EQ(process, popup_process);
  }

  // Same thing if the current tab tries to navigate itself.
  std::string navigate_str = "document.location=\"";
  navigate_str += https_url.spec();
  navigate_str += "\";";
  EXPECT_TRUE(content::ExecJs(oldtab->GetPrimaryMainFrame(), navigate_str));

  // The old tab should be in the middle of document.location navigation.
  EXPECT_TRUE(oldtab->IsLoading());
  content::WaitForLoadStop(oldtab);

  ASSERT_TRUE(oldtab->GetController().GetLastCommittedEntry());
  EXPECT_EQ(https_url.spec(),
            oldtab->GetController().GetLastCommittedEntry()->GetURL().spec());

  // Whether original stays in the original process (when navigating to a
  // cross-site url) depends on whether site-per-process mode is enabled or not.
  content::RenderProcessHost* new_process =
      newtab->GetPrimaryMainFrame()->GetProcess();
  if (content::AreAllSitesIsolatedForTesting()) {
    EXPECT_NE(process, new_process);

    // site-per-process should reuse the process for the https site.
    EXPECT_EQ(popup_process, new_process);
  } else {
    EXPECT_EQ(process, new_process);
  }
}

// Test RenderView correctly send back favicon url for web page that redirects
// to an anchor in javascript body.onload handler.
IN_PROC_BROWSER_TEST_F(BrowserTest,
                       DISABLED_FaviconOfOnloadRedirectToAnchorPage) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/onload_redirect_to_anchor.html"));
  GURL expected_favicon_url(embedded_test_server()->GetURL("/test.png"));

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  NavigationEntry* entry = browser()
                               ->tab_strip_model()
                               ->GetActiveWebContents()
                               ->GetController()
                               .GetLastCommittedEntry();
  EXPECT_EQ(expected_favicon_url.spec(), entry->GetFavicon().url.spec());
}

// Makes sure TabClosing is sent when uninstalling an extension that is an app
// tab.
IN_PROC_BROWSER_TEST_F(BrowserTest, TabClosingWhenRemovingExtension) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/empty.html"));
  TabStripModel* model = browser()->tab_strip_model();

  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));

  const Extension* extension_app = GetExtension();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  std::unique_ptr<WebContents> app_contents =
      WebContents::Create(WebContents::CreateParams(browser()->GetProfile()));
  extensions::AppTabHelper::CreateForWebContents(app_contents.get());
  extensions::AppTabHelper* extensions_tab_helper =
      extensions::AppTabHelper::FromWebContents(app_contents.get());
  ASSERT_TRUE(extensions_tab_helper);
  extensions_tab_helper->SetExtensionApp(extension_app);

  model->AddWebContents(std::move(app_contents), 0,
                        ui::PageTransitionFromInt(0), AddTabTypes::ADD_NONE);
  model->SetTabPinned(0, true);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  TabClosingObserver observer;
  model->AddObserver(&observer);

  // Uninstall the extension and make sure TabClosing is sent.
  extensions::ExtensionRegistrar::Get(browser()->GetProfile())
      ->UninstallExtension(GetExtension()->id(),
                           extensions::UNINSTALL_REASON_FOR_TESTING, nullptr);
  EXPECT_EQ(1, observer.closing_count());

  model->RemoveObserver(&observer);

  // There should only be one tab now.
  ASSERT_EQ(1, browser()->tab_strip_model()->count());
}

// Open with --app-id=<id>, and see that an application window opens by default.
#if !BUILDFLAG(IS_CHROMEOS)
// TODO(https://crbug.com/399807131): Re-enable on chrome win builders.
#if BUILDFLAG(IS_WIN)
#define MAYBE_AppIdSwitch DISABLED_AppIdSwitch
#else
#define MAYBE_AppIdSwitch AppIdSwitch
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_AppIdSwitch) {
  base::HistogramTester tester;
  ASSERT_TRUE(embedded_test_server()->Start());

  // There should be one browser and one tab to start with.
  EXPECT_EQ(1u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());
  ASSERT_EQ(1, browser()->tab_strip_model()->count());

  // Load an app.
  webapps::AppId app_id = web_app::test::InstallDummyWebApp(
      browser()->GetProfile(), "testapp", GURL("https://testapp.com"));
  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  command_line.AppendSwitchASCII(switches::kAppId, app_id);

  ui_test_utils::BrowserCreatedObserver browser_created_observer;
  base::test::TestFuture<void> launch_done;
  web_app::startup::SetStartupDoneCallbackForTesting(launch_done.GetCallback());
  EXPECT_TRUE(StartupBrowserCreator().ProcessCmdLineImpl(
      command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
      {browser()->GetProfile(), StartupProfileMode::kBrowserWindow}, {}));

  ASSERT_TRUE(launch_done.Wait());
  Browser* app_browser = browser_created_observer.Wait();
  EXPECT_EQ(app_browser->GetType(), BrowserWindowInterface::Type::TYPE_APP);

#if BUILDFLAG(IS_WIN)
  {  // From launch_mode_recorder.cc:
    constexpr char kLaunchModesHistogram[] = "Launch.Mode2";
    const base::HistogramBase::Sample32 kWebAppOther = 22;

    tester.ExpectUniqueSample(kLaunchModesHistogram, kWebAppOther, 1);
  }
#endif  // BUILDFLAG(IS_WIN)

  // Check that the number of browsers and tabs is correct.
  EXPECT_EQ(2u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());
  EXPECT_EQ(1, browser()->tab_strip_model()->count());
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

// Overscroll is only enabled on Aura platforms currently, and even then only
// when a specific feature (OverscrollHistoryNavigation) is enabled.
#if defined(USE_AURA)
IN_PROC_BROWSER_TEST_F(BrowserTest, OverscrollEnabledInRegularWindows) {
  ASSERT_EQ(browser()->GetType(), BrowserWindowInterface::Type::TYPE_NORMAL);
  EXPECT_TRUE(
      BrowserWebContentsDelegate::From(browser())->CanOverscrollContent());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, OverscrollEnabledInPopups) {
  Browser* popup_browser =
      CreateBrowserWindow(
          BrowserWindowCreateParams(BrowserWindowInterface::TYPE_POPUP,
                                    browser()->GetProfile(),
                                    /*from_user_gesture=*/true))
          ->GetBrowserForMigrationOnly();
  ASSERT_EQ(popup_browser->GetType(), BrowserWindowInterface::Type::TYPE_POPUP);
  EXPECT_TRUE(
      BrowserWebContentsDelegate::From(popup_browser)->CanOverscrollContent());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, OverscrollDisabledInDevToolsWindows) {
  DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
  BrowserWindowInterface* dev_tools_browser =
      GlobalBrowserCollection::GetInstance()->GetLastActiveBrowser();
  ASSERT_EQ(dev_tools_browser->GetType(),
            BrowserWindowInterface::Type::TYPE_DEVTOOLS);
  EXPECT_FALSE(BrowserWebContentsDelegate::From(dev_tools_browser)
                   ->CanOverscrollContent());
}
#endif

// Open an app window and the dev tools window and ensure that the location
// bar settings are correct.
IN_PROC_BROWSER_TEST_F(BrowserTest, ShouldShowLocationBar) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Load an app.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
  const Extension* extension_app = GetExtension();

  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
  auto browser_created_observer =
      std::make_optional<ui_test_utils::BrowserCreatedObserver>();
  WebContents* app_window =
      apps::AppServiceProxyFactory::GetForProfile(browser()->GetProfile())
          ->BrowserAppLauncher()
          ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
              extension_app->id(),
              apps::LaunchContainer::kLaunchContainerWindow,
              WindowOpenDisposition::NEW_WINDOW,
              apps::LaunchSource::kFromTest));
  ASSERT_TRUE(app_window);
  Browser* const app_browser = browser_created_observer->Wait();

  browser_created_observer.emplace();
  DevToolsWindow* devtools_window =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
  Browser* const dev_tools_browser = browser_created_observer->Wait();

  // The launch should have created a new app browser and a dev tools browser.
  ASSERT_EQ(3u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());

  ASSERT_TRUE(dev_tools_browser);
  ASSERT_TRUE(app_browser);
  ASSERT_TRUE(app_browser != browser());

  EXPECT_FALSE(
      WindowFeatureController::From(dev_tools_browser)
          ->SupportsWindowFeature(
              WindowFeatureController::WindowFeature::kFeatureLocationBar));

  // App windows can show location bars, for example when they navigate away
  // from their starting origin.
  EXPECT_TRUE(
      WindowFeatureController::From(app_browser)
          ->SupportsWindowFeature(
              WindowFeatureController::WindowFeature::kFeatureLocationBar));

  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
}

// Regression test for crbug.com/41307933.
IN_PROC_BROWSER_TEST_F(BrowserTest, ReattachDevToolsWindow) {
  ASSERT_TRUE(embedded_test_server()->Start());
  net::EmbeddedTestServer https_test_server(
      net::EmbeddedTestServer::TYPE_HTTPS);
  https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
  ASSERT_TRUE(https_test_server.Start());
  GURL http_url(embedded_test_server()->GetURL("/title1.html"));
  WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));

  // Open a devtools window.
  DevToolsWindow* devtools_window =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
                                                    /*is_docked=*/true);
  ASSERT_EQ(1u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());

  // Grab its main web contents.
  content::WebContents* devtools_main_web_contents =
      DevToolsWindow::GetInTabWebContents(
          devtools_window->GetInspectedWebContents(), nullptr);
  ASSERT_NE(web_contents, devtools_main_web_contents);

  // Detach the devtools window.
  DevToolsUIBindings::Delegate* devtools_delegate =
      static_cast<DevToolsUIBindings::Delegate*>(devtools_window);
  devtools_delegate->SetIsDocked(false);
  // This should have created a new dev tools browser.
  ASSERT_EQ(2u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());

  // Re-attach the dev tools window. This resets its Browser*.
  ui_test_utils::BrowserDestroyedObserver observer(
      GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(
          devtools_main_web_contents));
  devtools_delegate->SetIsDocked(true);
  // Wait until the browser actually gets closed.
  observer.Wait();
  ASSERT_EQ(1u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());

  // Do something that will make SearchTabHelper access its OmniboxView. This
  // should not crash, even though the Browser association and thus the
  // OmniboxView* has changed, and the old OmniboxView has been deleted.
  SearchTabHelper* search_tab_helper =
      SearchTabHelper::FromWebContents(devtools_main_web_contents);
  SearchIPCRouter::Delegate* search_ipc_router_delegate =
      static_cast<SearchIPCRouter::Delegate*>(search_tab_helper);
  search_ipc_router_delegate->FocusOmnibox(OMNIBOX_FOCUS_INVISIBLE);

  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
}

// Chromeos defaults to restoring the last session, so this test isn't
// applicable.
#if !BUILDFLAG(IS_CHROMEOS)
// Makes sure pinned tabs are restored correctly on start.
IN_PROC_BROWSER_TEST_F(BrowserTest, RestorePinnedTabs) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Add a pinned tab.
  GURL url(embedded_test_server()->GetURL("/empty.html"));
  TabStripModel* model = browser()->tab_strip_model();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  model->SetTabPinned(0, true);

  // Add a non pinned tab.
  chrome::NewTab(browser(), NewTabTypes::kNoUserAction);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  // Add another pinned tab.
  chrome::NewTab(browser(), NewTabTypes::kNoUserAction);
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
  model->SetTabPinned(2, true);

  // Write out the pinned tabs.
  PinnedTabCodec::WritePinnedTabs(browser()->GetProfile());

  // Set last What's New version to the current version so there is no What's
  // New tab shown on launch (for the non-first-run case).
  g_browser_process->local_state()->SetInteger(
      prefs::kLastWhatsNewVersion, version_info::GetMajorVersionNumberAsInt());

  auto keep_alive = std::make_unique<ScopedKeepAlive>(
      KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED);
  auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>(
      browser()->GetProfile(), ProfileKeepAliveOrigin::kBrowserWindow);

  // Close the browser window.
  CloseBrowserSynchronously(browser());

  // Launch again with the same profile.
  ui_test_utils::BrowserCreatedObserver browser_created_observer;
  base::CommandLine dummy(base::CommandLine::NO_PROGRAM);
  chrome::startup::IsFirstRun first_run =
      first_run::IsChromeFirstRun() ? chrome::startup::IsFirstRun::kYes
                                    : chrome::startup::IsFirstRun::kNo;
  StartupBrowserCreatorImpl launch(base::FilePath(), dummy, first_run);
  launch.Launch(profile(), chrome::startup::IsProcessStartup::kNo,
                /*restore_tabbed_browser=*/true);
  Browser* const new_browser = browser_created_observer.Wait();

  // The launch should have created a new browser.
  ASSERT_EQ(1u, ProfileBrowserCollection::GetForProfile(profile())->GetSize());

  // We should get back an additional tab for the app, and another for the
  // default home page.
  ASSERT_EQ(3, new_browser->tab_strip_model()->count());

  // Make sure the state matches.
  TabStripModel* new_model = new_browser->tab_strip_model();
  EXPECT_TRUE(new_model->IsTabPinned(0));
  EXPECT_TRUE(new_model->IsTabPinned(1));
  EXPECT_FALSE(new_model->IsTabPinned(2));
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

// This test verifies we don't crash when closing the last window and the app
// menu is showing.
IN_PROC_BROWSER_TEST_F(BrowserTest, CloseWithAppMenuOpen) {
  if (browser_defaults::kBrowserAliveWithNoWindows) {
    return;
  }

  // We need a message loop running for menus on windows.
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(&RunCloseWithAppMenuCallback, browser()));
}

#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(BrowserTest, OpenAppWindowLikeNtp) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Load an app
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
  const Extension* extension_app = GetExtension();
  ASSERT_TRUE(extension_app);

  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
  ui_test_utils::BrowserCreatedObserver browser_created_observer;
  WebContents* app_window =
      apps::AppServiceProxyFactory::GetForProfile(browser()->GetProfile())
          ->BrowserAppLauncher()
          ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
              extension_app->id(),
              apps::LaunchContainer::kLaunchContainerWindow,
              WindowOpenDisposition::NEW_WINDOW,
              apps::LaunchSource::kFromTest));
  ASSERT_TRUE(app_window);
  Browser* const new_browser = browser_created_observer.Wait();

  // Apps launched in a window from the NTP have an extensions tab helper with
  // extension_app set.
  ASSERT_TRUE(extensions::AppTabHelper::FromWebContents(app_window));
  EXPECT_TRUE(extensions::AppTabHelper::FromWebContents(app_window)->is_app());
  EXPECT_EQ(extensions::AppLaunchInfo::GetFullLaunchURL(extension_app),
            app_window->GetURL());

  // The launch should have created a new browser.
  ASSERT_EQ(2u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());

  ASSERT_TRUE(new_browser);
  ASSERT_TRUE(new_browser != browser());

  EXPECT_EQ(new_browser->GetType(), BrowserWindowInterface::Type::TYPE_APP);

  // The browser's app name should include the extension's id.
  std::string app_name =
      BrowserInitState::From(new_browser)->create_params().app_name;
  EXPECT_NE(app_name.find(extension_app->id()), std::string::npos)
      << "Name " << app_name << " should contain id " << extension_app->id();
}
#endif  // !BUILDFLAG(IS_MAC)

// Makes sure the browser doesn't crash when
// set_show_state(ui::mojom::WindowShowState::kMaximized) has been invoked.
IN_PROC_BROWSER_TEST_F(BrowserTest, StartMaximized) {
  std::vector<BrowserWindowCreateParams> params;
  params.push_back(BrowserWindowCreateParams(
      BrowserWindowInterface::TYPE_NORMAL, browser()->GetProfile(),
      /*from_user_gesture=*/true));
  params.push_back(BrowserWindowCreateParams(BrowserWindowInterface::TYPE_POPUP,
                                             browser()->GetProfile(),
                                             /*from_user_gesture=*/true));
  params.push_back(BrowserWindowCreateParams::CreateForApp(
      "app_name", /*trusted_source=*/true, gfx::Rect(), browser()->GetProfile(),
      /*user_gesture=*/true));
  params.push_back(
      BrowserWindowCreateParams::CreateForDevTools(browser()->GetProfile()));
  params.push_back(BrowserWindowCreateParams::CreateForAppPopup(
      "app_name", /*trusted_source=*/true, gfx::Rect(), browser()->GetProfile(),
      /*user_gesture=*/true));
  params.push_back(BrowserWindowCreateParams(
      BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE, browser()->GetProfile(),
      /*from_user_gesture=*/true));
  for (auto& param : params) {
    param.initial_show_state = ui::mojom::WindowShowState::kMaximized;
    AddBlankTabAndShow(
        CreateBrowserWindow(std::move(param))->GetBrowserForMigrationOnly());
  }
}

// Makes sure the browser doesn't crash when
// set_show_state(ui::mojom::WindowShowState::kMinimized) has been invoked.
IN_PROC_BROWSER_TEST_F(BrowserTest, StartMinimized) {
  std::vector<BrowserWindowCreateParams> params;
  params.push_back(BrowserWindowCreateParams(
      BrowserWindowInterface::TYPE_NORMAL, browser()->GetProfile(),
      /*from_user_gesture=*/true));
  params.push_back(BrowserWindowCreateParams(BrowserWindowInterface::TYPE_POPUP,
                                             browser()->GetProfile(),
                                             /*from_user_gesture=*/true));
  params.push_back(BrowserWindowCreateParams::CreateForApp(
      "app_name", /*trusted_source=*/true, gfx::Rect(), browser()->GetProfile(),
      /*user_gesture=*/true));
  params.push_back(
      BrowserWindowCreateParams::CreateForDevTools(browser()->GetProfile()));
  params.push_back(BrowserWindowCreateParams::CreateForAppPopup(
      "app_name", /*trusted_source=*/true, gfx::Rect(), browser()->GetProfile(),
      /*user_gesture=*/true));
  params.push_back(BrowserWindowCreateParams(
      BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE, browser()->GetProfile(),
      /*from_user_gesture=*/true));
  for (auto& param : params) {
    param.initial_show_state = ui::mojom::WindowShowState::kMinimized;
    AddBlankTabAndShow(
        CreateBrowserWindow(std::move(param))->GetBrowserForMigrationOnly(),
        /*wait_for_activation=*/false);
  }
}

// Makes sure the forward button is disabled immediately when navigating
// forward to a slow-to-commit page.
IN_PROC_BROWSER_TEST_F(BrowserTest, ForwardDisabledOnForward) {
  GURL blank_url(url::kAboutBlankURL);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), blank_url));

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), chrome_test_utils::GetTestUrl(
                     base::FilePath(base::FilePath::kCurrentDirectory),
                     base::FilePath(kTitle1File))));

  content::LoadStopObserver back_nav_load_observer(
      browser()->tab_strip_model()->GetActiveWebContents());
  chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
  back_nav_load_observer.Wait();
  CommandUpdater* command_updater =
      chrome::BrowserCommandController::From(browser());
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_FORWARD));

  content::LoadStopObserver forward_nav_load_observer(
      browser()->tab_strip_model()->GetActiveWebContents());
  chrome::GoForward(browser(), WindowOpenDisposition::CURRENT_TAB);
  // This check will happen before the navigation completes, since the browser
  // won't process the renderer's response until the Wait() call below.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_FORWARD));
  forward_nav_load_observer.Wait();
}

// Makes sure certain commands are disabled when Incognito mode is forced.
IN_PROC_BROWSER_TEST_F(BrowserTest, DisableMenuItemsWhenIncognitoIsForced) {
  CommandUpdater* command_updater =
      chrome::BrowserCommandController::From(browser());
  // At the beginning, all commands are enabled.
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_OPTIONS));

  // Set Incognito to FORCED.
  IncognitoModePrefs::SetAvailability(
      browser()->GetProfile()->GetPrefs(),
      policy::IncognitoModeAvailability::kForced);
  // Bookmarks & Settings commands should get disabled.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
  // New Incognito Window command, however, should be enabled.
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));

  // Create a new browser.
  Browser* new_browser =
      CreateBrowserWindow(BrowserWindowCreateParams(
                              browser()->GetProfile()->GetPrimaryOTRProfile(
                                  /*create_if_needed=*/true),
                              /*from_user_gesture=*/true))
          ->GetBrowserForMigrationOnly();
  CommandUpdater* new_command_updater =
      chrome::BrowserCommandController::From(new_browser);
  // It should have Bookmarks & Settings commands disabled by default.
  EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
  EXPECT_FALSE(
      new_command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_OPTIONS));
  EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
}

// Makes sure New Incognito Window command is disabled when Incognito mode is
// not available.
IN_PROC_BROWSER_TEST_F(BrowserTest,
                       NoNewIncognitoWindowWhenIncognitoIsDisabled) {
  CommandUpdater* command_updater =
      chrome::BrowserCommandController::From(browser());
  // Set Incognito to DISABLED.
  IncognitoModePrefs::SetAvailability(
      browser()->GetProfile()->GetPrefs(),
      policy::IncognitoModeAvailability::kDisabled);
  // Make sure New Incognito Window command is disabled. All remaining commands
  // should be enabled.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_OPTIONS));

  // Create a new browser.
  Browser* new_browser =
      CreateBrowserWindow(BrowserWindowCreateParams(browser()->GetProfile(),
                                                    /*from_user_gesture=*/true))
          ->GetBrowserForMigrationOnly();
  CommandUpdater* new_command_updater =
      chrome::BrowserCommandController::From(new_browser);
  EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
  EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
  EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_OPTIONS));
}

class BrowserTestWithExtensionsDisabled : public BrowserTest {
 protected:
  BrowserTestWithExtensionsDisabled() = default;

 public:
  BrowserTestWithExtensionsDisabled(const BrowserTestWithExtensionsDisabled&) =
      delete;
  BrowserTestWithExtensionsDisabled& operator=(
      const BrowserTestWithExtensionsDisabled&) = delete;

 protected:
  ~BrowserTestWithExtensionsDisabled() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    BrowserTest::SetUpCommandLine(command_line);
    command_line->AppendSwitch(extensions::switches::kDisableExtensions);
  }
};

// Makes sure Extensions and Settings commands are disabled in certain
// circumstances even though normally they should stay enabled.
IN_PROC_BROWSER_TEST_F(BrowserTestWithExtensionsDisabled,
                       DisableExtensionsAndSettingsWhenIncognitoIsDisabled) {
  CommandUpdater* command_updater =
      chrome::BrowserCommandController::From(browser());
  // Set Incognito to DISABLED.
  IncognitoModePrefs::SetAvailability(
      browser()->GetProfile()->GetPrefs(),
      policy::IncognitoModeAvailability::kDisabled);
  // Make sure Manage Extensions command is disabled.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_OPTIONS));

  // Create a popup (non-main-UI-type) browser. Settings command as well
  // as Extensions should be disabled.
  Browser* popup_browser =
      CreateBrowserWindow(
          BrowserWindowCreateParams(BrowserWindowInterface::TYPE_POPUP,
                                    browser()->GetProfile(),
                                    /*from_user_gesture=*/true))
          ->GetBrowserForMigrationOnly();
  CommandUpdater* popup_command_updater =
      chrome::BrowserCommandController::From(popup_browser);
  EXPECT_FALSE(popup_command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
  EXPECT_FALSE(popup_command_updater->IsCommandEnabled(IDC_OPTIONS));
  EXPECT_TRUE(
      popup_command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
  EXPECT_FALSE(popup_command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
}

// Makes sure Extensions and Settings commands are disabled in certain
// circumstances even though normally they should stay enabled.
IN_PROC_BROWSER_TEST_F(BrowserTest,
                       DisableOptionsAndImportMenuItemsConsistently) {
  // Create a popup browser.
  Browser* popup_browser =
      CreateBrowserWindow(
          BrowserWindowCreateParams(BrowserWindowInterface::TYPE_POPUP,
                                    browser()->GetProfile(),
                                    /*from_user_gesture=*/true))
          ->GetBrowserForMigrationOnly();
  CommandUpdater* command_updater =
      chrome::BrowserCommandController::From(popup_browser);
  // OPTIONS and IMPORT_SETTINGS are disabled for a non-normal UI.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));

  // Set Incognito to FORCED.
  IncognitoModePrefs::SetAvailability(
      popup_browser->GetProfile()->GetPrefs(),
      policy::IncognitoModeAvailability::kForced);
  // OPTIONS and IMPORT_SETTINGS are disabled when Incognito is forced.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
  // Set Incognito to AVAILABLE.
  IncognitoModePrefs::SetAvailability(
      popup_browser->GetProfile()->GetPrefs(),
      policy::IncognitoModeAvailability::kEnabled);
  // OPTIONS and IMPORT_SETTINGS are still disabled since it is a non-normal UI.
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
}

namespace {

void OnZoomLevelChanged(base::OnceClosure* callback,
                        const HostZoomMap::ZoomLevelChange& host) {
  std::move(*callback).Run();
}

int GetZoomPercent(content::WebContents* contents,
                   bool* enable_plus,
                   bool* enable_minus) {
  int percent =
      zoom::ZoomController::FromWebContents(contents)->GetZoomPercent();
  *enable_plus = percent < contents->GetMaximumZoomPercent();
  *enable_minus = percent > contents->GetMinimumZoomPercent();
  return percent;
}

}  // namespace

IN_PROC_BROWSER_TEST_F(BrowserTest, PageZoom) {
  WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
  bool enable_plus, enable_minus;

  {
    scoped_refptr<content::MessageLoopRunner> loop_runner(
        new content::MessageLoopRunner);
    base::OnceClosure quit_closure = loop_runner->QuitClosure();
    content::HostZoomMap::ZoomLevelChangedCallback callback =
        base::BindRepeating(&OnZoomLevelChanged, &quit_closure);
    base::CallbackListSubscription subscription =
        content::HostZoomMap::GetDefaultForBrowserContext(
            browser()->GetProfile())
            ->AddZoomLevelChangedCallback(std::move(callback));
    chrome::Zoom(browser(), content::PAGE_ZOOM_IN);
    loop_runner->Run();
    subscription = {};
    EXPECT_EQ(GetZoomPercent(contents, &enable_plus, &enable_minus), 110);
    EXPECT_TRUE(enable_plus);
    EXPECT_TRUE(enable_minus);
  }

  {
    scoped_refptr<content::MessageLoopRunner> loop_runner(
        new content::MessageLoopRunner);
    base::OnceClosure quit_closure = loop_runner->QuitClosure();
    content::HostZoomMap::ZoomLevelChangedCallback callback =
        base::BindRepeating(&OnZoomLevelChanged, &quit_closure);
    base::CallbackListSubscription subscription =
        content::HostZoomMap::GetDefaultForBrowserContext(
            browser()->GetProfile())
            ->AddZoomLevelChangedCallback(std::move(callback));
    chrome::Zoom(browser(), content::PAGE_ZOOM_RESET);
    loop_runner->Run();
    subscription = {};
    EXPECT_EQ(GetZoomPercent(contents, &enable_plus, &enable_minus), 100);
    EXPECT_TRUE(enable_plus);
    EXPECT_TRUE(enable_minus);
  }

  {
    scoped_refptr<content::MessageLoopRunner> loop_runner(
        new content::MessageLoopRunner);
    base::OnceClosure quit_closure = loop_runner->QuitClosure();
    content::HostZoomMap::ZoomLevelChangedCallback callback =
        base::BindRepeating(&OnZoomLevelChanged, &quit_closure);
    base::CallbackListSubscription subscription =
        content::HostZoomMap::GetDefaultForBrowserContext(
            browser()->GetProfile())
            ->AddZoomLevelChangedCallback(std::move(callback));
    chrome::Zoom(browser(), content::PAGE_ZOOM_OUT);
    loop_runner->Run();
    subscription = {};
    EXPECT_EQ(GetZoomPercent(contents, &enable_plus, &enable_minus), 90);
    EXPECT_TRUE(enable_plus);
    EXPECT_TRUE(enable_minus);
  }

  chrome::Zoom(browser(), content::PAGE_ZOOM_RESET);
}

// TODO(ben): this test was never enabled. It has bit-rotted since being added.
// It originally lived in browser_unittest.cc, but has been moved here to make
// room for real browser unit tests.
#if 0
class BrowserTest2 : public InProcessBrowserTest {
 public:
  BrowserTest2() {
    host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
    // Avoid making external DNS lookups. In this test we don't need this
    // to succeed.
    host_resolver_proc_->AddSimulatedFailure("*.google.com");
    scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
  }

 private:
  scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_;
  net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_;
};

IN_PROC_BROWSER_TEST_F(BrowserTest2, NoTabsInPopups) {
  chrome::RegisterAppPrefs(L"Test");

  // We start with a normal browser with one tab.
  EXPECT_EQ(1, browser()->tab_strip_model()->count());

  // Open a popup browser with a single blank foreground tab.
  Browser* popup_browser =
      CreateBrowserWindow(BrowserWindowCreateParams(
                              BrowserWindowInterface::TYPE_POPUP,
                              browser()->GetProfile()))
          ->GetBrowserForMigrationOnly();
  chrome::AddTabAt(popup_browser, GURL(), -1, true);
  EXPECT_EQ(1, popup_browser->tab_strip_model()->count());

  // Now try opening another tab in the popup browser.
  AddTabWithURLParams params1(url, ui::PAGE_TRANSITION_TYPED);
  popup_browser->AddTabWithURL(&params1);
  EXPECT_EQ(popup_browser, params1.target);

  // The popup should still only have one tab.
  EXPECT_EQ(1, popup_browser->tab_strip_model()->count());

  // The normal browser should now have two.
  EXPECT_EQ(2, browser()->tab_strip_model()->count());

  // Open an app frame browser with a single blank foreground tab.
  Browser* app_browser =
      CreateBrowserWindow(BrowserWindowCreateParams::CreateForApp(
                              "Test", /*trusted_source=*/false, gfx::Rect(),
                              browser()->GetProfile(), /*user_gesture=*/false))
          ->GetBrowserForMigrationOnly();
  chrome::AddTabAt(app_browser, GURL(), -1, true);
  EXPECT_EQ(1, app_browser->tab_strip_model()->count());

  // Now try opening another tab in the app browser.
  AddTabWithURLParams params2(GURL(url::kAboutBlankURL),
                              ui::PAGE_TRANSITION_TYPED);
  app_browser->AddTabWithURL(&params2);
  EXPECT_EQ(app_browser, params2.target);

  // The popup should still only have one tab.
  EXPECT_EQ(1, app_browser->tab_strip_model()->count());

  // The normal browser should now have three.
  EXPECT_EQ(3, browser()->tab_strip_model()->count());

  // Open an app frame popup browser with a single blank foreground tab.
  Browser* app_popup_browser =
      CreateBrowserWindow(BrowserWindowCreateParams::CreateForApp(
                              "Test", /*trusted_source=*/false, gfx::Rect(),
                              browser()->GetProfile(), /*user_gesture=*/false))
          ->GetBrowserForMigrationOnly();
  chrome::AddTabAt(app_popup_browser, GURL(), -1, true);
  EXPECT_EQ(1, app_popup_browser->tab_strip_model()->count());

  // Now try opening another tab in the app popup browser.
  AddTabWithURLParams params3(GURL(url::kAboutBlankURL),
                              ui::PAGE_TRANSITION_TYPED);
  app_popup_browser->AddTabWithURL(&params3);
  EXPECT_EQ(app_popup_browser, params3.target);

  // The popup should still only have one tab.
  EXPECT_EQ(1, app_popup_browser->tab_strip_model()->count());

  // The normal browser should now have four.
  EXPECT_EQ(4, browser()->tab_strip_model()->count());

  // Close the additional browsers.
  popup_browser->tab_strip_model()->CloseAllTabs();
  app_browser->tab_strip_model()->CloseAllTabs();
  app_popup_browser->tab_strip_model()->CloseAllTabs();
}
#endif

IN_PROC_BROWSER_TEST_F(BrowserTest, WindowOpenClose1) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      embedder_support::kDisablePopupBlocking);
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/window.close.html");
  GURL::Replacements add_query;
  std::string query("test1");
  add_query.SetQueryStr(query);
  url = url.ReplaceComponents(add_query);

  std::u16string title = u"Title Of Awesomeness";
  content::TitleWatcher title_watcher(
      browser()->tab_strip_model()->GetActiveWebContents(), title);
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, WindowOpenClose2) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      embedder_support::kDisablePopupBlocking);
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/window.close.html");
  GURL::Replacements add_query;
  std::string query("test2");
  add_query.SetQueryStr(query);
  url = url.ReplaceComponents(add_query);

  std::u16string title = u"Title Of Awesomeness";
  content::TitleWatcher title_watcher(
      browser()->tab_strip_model()->GetActiveWebContents(), title);
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}

// Disabled because of timeouts in several builders.
// https://crbug.com/40720134
IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_WindowOpenClose3) {
#if BUILDFLAG(IS_MAC)
  // Ensure that tests don't wait for frames that will never come.
  ui::CATransactionCoordinator::Get().DisableForTesting();
#endif
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      embedder_support::kDisablePopupBlocking);
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/window.close.html");
  GURL::Replacements add_query;
  std::string query("test3");
  add_query.SetQueryStr(query);
  url = url.ReplaceComponents(add_query);

  std::u16string title = u"Title Of Awesomeness";
  content::TitleWatcher title_watcher(
      browser()->tab_strip_model()->GetActiveWebContents(), title);
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}

// TODO(linux_aura) http://crbug.com/40295645
#if !BUILDFLAG(IS_LINUX)
IN_PROC_BROWSER_TEST_F(BrowserTest, FullscreenBookmarkBar) {
#if BUILDFLAG(IS_MAC)
  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
#endif

  chrome::ToggleBookmarkBar(browser());
  EXPECT_EQ(BookmarkBar::SHOW,
            BookmarkBarController::From(browser())->bookmark_bar_state());
  chrome::ToggleFullscreenMode(browser());
  EXPECT_TRUE(browser()->GetWindow()->IsFullscreen());
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
  // Mac and Chrome OS both have an "immersive style" fullscreen where the
  // bookmark bar is visible when the top views slide down.
  EXPECT_EQ(BookmarkBar::SHOW,
            BookmarkBarController::From(browser())->bookmark_bar_state());
#else
  EXPECT_EQ(BookmarkBar::HIDDEN,
            BookmarkBarController::From(browser())->bookmark_bar_state());
#endif
}
#endif

// TODO(crbug.com/424490718): Fullscreen tests work differently on Mac and
// require some additional flags to work.
// See chrome/browser/ui/views/fullscreen_mac_browsertest.cc
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(BrowserTest, BrowserFullscreenShowBookmarkBarSplitView) {
  chrome::AddTabAt(browser(), GURL("chrome://new-tab-page"), -1, true);
  chrome::AddTabAt(browser(), GURL(), -1, true);

  browser()->tab_strip_model()->ActivateTabAt(0);
  browser()->tab_strip_model()->AddToNewSplit(
      {1}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kToolbarButton);

  // Create a bookmark so we have something to show when bookmark bar is enabled
  bookmarks::BookmarkModel* const model =
      BookmarkModelFactory::GetForBrowserContext(browser()->GetProfile());
  bookmarks::test::WaitForBookmarkModelToLoad(model);
  bookmarks::AddIfNotBookmarked(model, GURL("https://www.foo.goog"), u"Foo");

  chrome::ToggleBookmarkBar(browser());
  EXPECT_EQ(BookmarkBar::SHOW,
            BookmarkBarController::From(browser())->bookmark_bar_state());
  chrome::ToggleFullscreenMode(browser());
  EXPECT_TRUE(browser()->GetWindow()->IsFullscreen());
  EXPECT_EQ(BookmarkBar::SHOW,
            BookmarkBarController::From(browser())->bookmark_bar_state());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, TabFullscreenHiddenBookmarkBarSplitView) {
  chrome::AddTabAt(browser(), GURL("chrome://new-tab-page"), -1, true);
  chrome::AddTabAt(browser(), GURL(), -1, true);

  browser()->tab_strip_model()->ActivateTabAt(0);
  browser()->tab_strip_model()->AddToNewSplit(
      {1}, split_tabs::SplitTabVisualData(),
      split_tabs::SplitTabCreatedSource::kToolbarButton);

  // Create a bookmark so we have something to show when bookmark bar is enabled
  bookmarks::BookmarkModel* const model =
      BookmarkModelFactory::GetForBrowserContext(browser()->GetProfile());
  bookmarks::test::WaitForBookmarkModelToLoad(model);
  bookmarks::AddIfNotBookmarked(model, GURL("https://www.foo.goog"), u"Foo");

  chrome::ToggleBookmarkBar(browser());
  EXPECT_EQ(BookmarkBar::SHOW,
            BookmarkBarController::From(browser())->bookmark_bar_state());

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  browser()
      ->GetFeatures()
      .exclusive_access_manager()
      ->fullscreen_controller()
      ->EnterFullscreenModeForTab(web_contents->GetPrimaryMainFrame());

  EXPECT_TRUE(browser()->GetWindow()->IsFullscreen());
  EXPECT_EQ(BookmarkBar::HIDDEN,
            BookmarkBarController::From(browser())->bookmark_bar_state());
}
#endif

IN_PROC_BROWSER_TEST_F(BrowserTest, DisallowFileUrlUniversalAccessTest) {
  GURL url = chrome_test_utils::GetTestUrl(
      base::FilePath(),
      base::FilePath().AppendASCII("fileurl_universalaccess.html"));

  std::u16string expected_title(u"Disallowed");
  content::TitleWatcher title_watcher(
      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
  title_watcher.AlsoWaitForTitle(u"Allowed");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}

class KioskModeTest : public BrowserTest {
 public:
  KioskModeTest() = default;

  void SetUpOnMainThread() override {
    BrowserTest::SetUpOnMainThread();
    BrowserWindowFullscreenController::From(browser())->SetForceFullscreen(
        true);
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kKioskMode);
  }
};

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Mac: http://crbug.com/40113467
// Linux: http://crbug.com/40295645
#define MAYBE_EnableKioskModeTest DISABLED_EnableKioskModeTest
#else
#define MAYBE_EnableKioskModeTest EnableKioskModeTest
#endif
IN_PROC_BROWSER_TEST_F(KioskModeTest, MAYBE_EnableKioskModeTest) {
  // Check if browser is in fullscreen mode.
  ASSERT_TRUE(browser()->GetWindow()->IsFullscreen());
  const auto* fullscreen_bubble_element =
      BrowserElements::From(browser())->GetElement(
          kExclusiveAccessBubbleViewElementId);
  ASSERT_FALSE(fullscreen_bubble_element);
}

#if BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(KioskModeTest, DoNotExitFullscreen) {
  BrowserWindow::FromBrowser(browser())
      ->GetExclusiveAccessContext()
      ->ExitFullscreen();
  ASSERT_TRUE(browser()->GetWindow()->IsFullscreen());
}

IN_PROC_BROWSER_TEST_F(KioskModeTest, DoNotChangeBounds) {
  gfx::Rect old_bounds = browser()->GetWindow()->GetBounds();

  browser()->GetWindow()->SetBounds(gfx::Rect(10, 10, 10, 10));
  gfx::Rect new_bounds = browser()->GetWindow()->GetBounds();

  ASSERT_TRUE(browser()->GetWindow()->IsFullscreen());
  ASSERT_EQ(old_bounds, new_bounds);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
// This test verifies that Chrome can be launched with a user-data-dir path
// which contains non ASCII characters.
class LaunchBrowserWithNonAsciiUserDatadir : public BrowserTest {
 public:
  LaunchBrowserWithNonAsciiUserDatadir() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    base::FilePath tmp_profile = temp_dir_.GetPath().AppendASCII("tmp_profile");
    tmp_profile = tmp_profile.Append(L"Test Chrome G\u00E9raldine");

    ASSERT_TRUE(base::CreateDirectory(tmp_profile));
    command_line->AppendSwitchPath(switches::kUserDataDir, tmp_profile);
  }

  base::ScopedTempDir temp_dir_;
};

IN_PROC_BROWSER_TEST_F(LaunchBrowserWithNonAsciiUserDatadir,
                       TestNonAsciiUserDataDir) {
  // Verify that the window is present.
  ASSERT_TRUE(browser());
  ASSERT_TRUE(browser()->GetProfile());
  // Verify that the profile has been added correctly to the
  // ProfileAttributesStorage.
  ASSERT_EQ(1u, g_browser_process->profile_manager()
                    ->GetProfileAttributesStorage()
                    .GetNumberOfProfiles());
}

// This test verifies that Chrome can be launched with a user-data-dir path
// which contains a reparse point. This is important because sandbox
// policy validates that paths passed to policy rules do not contain
// reparse points. New code in Chrome that adjusts the sandbox can
// accidentally pass paths with reparse points to the sandbox and cause
// Chrome not to start anymore.
class LaunchBrowserWithReparsePointUserDatadir : public BrowserTest {
 public:
  LaunchBrowserWithReparsePointUserDatadir() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    base::FilePath tmp_profile = temp_dir_.GetPath().AppendASCII("profile");
    ASSERT_TRUE(base::CreateDirectory(tmp_profile));
    base::FilePath reparse_profile =
        temp_dir_.GetPath().AppendASCII("profile_reparse");
    ASSERT_TRUE(base::CreateDirectory(reparse_profile));
    auto reparse_point =
        base::test::FilePathReparsePoint::Create(reparse_profile, tmp_profile);
    ASSERT_TRUE(reparse_point.has_value());
    reparse_point_.emplace(std::move(reparse_point.value()));
    command_line->AppendSwitchPath(switches::kUserDataDir, reparse_profile);
  }

  base::ScopedTempDir temp_dir_;
  std::optional<base::test::FilePathReparsePoint> reparse_point_;
};

IN_PROC_BROWSER_TEST_F(LaunchBrowserWithReparsePointUserDatadir,
                       TestReparsePointUserDataDir) {
  // Verify that the window is present.
  ASSERT_TRUE(browser());
  ASSERT_TRUE(browser()->GetProfile());
  // Verify that the profile has been added correctly to the
  // ProfileAttributesStorage.
  ASSERT_EQ(1u, g_browser_process->profile_manager()
                    ->GetProfileAttributesStorage()
                    .GetNumberOfProfiles());
}
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_WIN)
// This test verifies that Chrome can be launched with a user-data-dir path
// which trailing slashes.
class LaunchBrowserWithTrailingSlashDatadir : public BrowserTest {
 public:
  LaunchBrowserWithTrailingSlashDatadir() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    base::FilePath tmp_profile = temp_dir_.GetPath().AppendASCII("tmp_profile");
    tmp_profile = tmp_profile.Append(L"Test Chrome\\");

    ASSERT_TRUE(base::CreateDirectory(tmp_profile));
    command_line->AppendSwitchPath(switches::kUserDataDir, tmp_profile);
  }

  base::ScopedTempDir temp_dir_;
};

IN_PROC_BROWSER_TEST_F(LaunchBrowserWithTrailingSlashDatadir,
                       TestTrailingSlashUserDataDir) {
  // Verify that the window is present.
  ASSERT_TRUE(browser());
  ASSERT_TRUE(browser()->GetProfile());
  // Verify that the profile has been added correctly to the
  // ProfileAttributesStorage.
  ASSERT_EQ(1u, g_browser_process->profile_manager()
                    ->GetProfileAttributesStorage()
                    .GetNumberOfProfiles());
}
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
// Tests to ensure that the browser continues running in the background after
// the last window closes.
class RunInBackgroundTest : public BrowserTest {
 public:
  RunInBackgroundTest() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kKeepAliveForTest);
  }
};

IN_PROC_BROWSER_TEST_F(RunInBackgroundTest, RunInBackgroundBasicTest) {
  // Close the browser window, then open a new one - the browser should keep
  // running.
  Profile* profile = browser()->GetProfile();
  EXPECT_EQ(1u, GlobalBrowserCollection::GetInstance()->GetSize());
  CloseBrowserSynchronously(browser());
  EXPECT_EQ(0u, GlobalBrowserCollection::GetInstance()->GetSize());

  chrome::NewEmptyWindow(profile);

  EXPECT_EQ(1u, GlobalBrowserCollection::GetInstance()->GetSize());
}
#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)

// Tests to ensure that the browser continues running in the background after
// the last window closes.
class NoStartupWindowTest : public BrowserTest {
 public:
  NoStartupWindowTest() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kNoStartupWindow);
    command_line->AppendSwitch(switches::kKeepAliveForTest);
  }

  // Returns true if any commands were processed.
  bool ProcessedAnyCommands(
      sessions::CommandStorageManager* command_storage_manager) {
    sessions::CommandStorageManagerTestHelper test_helper(
        command_storage_manager);
    return test_helper.ProcessedAnyCommands();
  }
};

IN_PROC_BROWSER_TEST_F(NoStartupWindowTest, NoStartupWindowBasicTest) {
  // No browser window should be started by default.
  EXPECT_EQ(0u, GlobalBrowserCollection::GetInstance()->GetSize());

  // Starting a browser window should work just fine.
  CreateBrowser(ProfileManager::GetLastUsedProfile());

  EXPECT_EQ(1u, GlobalBrowserCollection::GetInstance()->GetSize());
}

// Chromeos needs to track app windows because it considers them to be part of
// session state.
#if !BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(NoStartupWindowTest, DontInitSessionServiceForApps) {
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();

  SessionService* session_service =
      SessionServiceFactory::GetForProfile(profile);
  sessions::CommandStorageManager* command_storage_manager =
      session_service->GetCommandStorageManagerForTest();
  ASSERT_FALSE(ProcessedAnyCommands(command_storage_manager));

  CreateBrowserForApp("blah", profile);

  ASSERT_FALSE(ProcessedAnyCommands(command_storage_manager));
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

// This test needs to be placed outside the anonymous namespace because we
// need to access private type of Browser.
class AppModeTest : public BrowserTest {
 public:
  AppModeTest() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    GURL url = chrome_test_utils::GetTestUrl(
        base::FilePath(), base::FilePath().AppendASCII("title1.html"));
    command_line->AppendSwitchASCII(switches::kApp, url.spec());
  }
};

IN_PROC_BROWSER_TEST_F(AppModeTest, EnableAppModeTest) {
  // Test that an application browser window loads correctly.

  // Verify the browser is in application mode.
  EXPECT_EQ(browser()->GetType(), BrowserWindowInterface::Type::TYPE_APP);
}

// Confirm chrome://version contains some expected content.
IN_PROC_BROWSER_TEST_F(BrowserTest, AboutVersion) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
                                           GURL(chrome::kChromeUIVersionURL)));
  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_GT(
      ui_test_utils::FindInPage(tab, u"WebKit", true, true, nullptr, nullptr),
      0);
  ASSERT_GT(ui_test_utils::FindInPage(tab, u"OS", true, true, nullptr, nullptr),
            0);
  ASSERT_GT(ui_test_utils::FindInPage(tab, u"JavaScript", true, true, nullptr,
                                      nullptr),
            0);
}

static const base::FilePath::CharType* kTestDir =
    FILE_PATH_LITERAL("click_modifier");
static const char16_t kFirstPageTitle[] = u"First window";
static const char16_t kSecondPageTitle[] = u"New window!";

class ClickModifierTest : public InProcessBrowserTest {
 public:
  ClickModifierTest() = default;
  ClickModifierTest(const ClickModifierTest&) = delete;
  ClickModifierTest& operator=(const ClickModifierTest&) = delete;

  // Returns a url that opens a new window or tab when clicked, via javascript.
  GURL GetWindowOpenURL() const {
    return chrome_test_utils::GetTestUrl(
        base::FilePath(kTestDir),
        base::FilePath(FILE_PATH_LITERAL("window_open.html")));
  }

  // Returns a url that follows a simple link when clicked, unless affected by
  // modifiers.
  GURL GetHrefURL() const {
    return chrome_test_utils::GetTestUrl(
        base::FilePath(kTestDir),
        base::FilePath(FILE_PATH_LITERAL("href.html")));
  }

  std::u16string GetFirstPageTitle() const { return kFirstPageTitle; }

  std::u16string GetSecondPageTitle() const { return kSecondPageTitle; }

  // Loads our test page and simulates a single click using the supplied button
  // and modifiers.  The click will cause either a navigation or the creation of
  // a new window or foreground or background tab.  We verify that the expected
  // disposition occurs.
  void RunTest(Browser* browser,
               const GURL& url,
               int modifiers,
               blink::WebMouseEvent::Button button,
               WindowOpenDisposition disposition) {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
    EXPECT_EQ(1u, ProfileBrowserCollection::GetForProfile(browser->GetProfile())
                      ->GetSize());
    EXPECT_EQ(1, browser->tab_strip_model()->count());
    content::WebContents* web_contents =
        browser->tab_strip_model()->GetActiveWebContents();
    EXPECT_EQ(url, web_contents->GetURL());

    if (disposition == WindowOpenDisposition::CURRENT_TAB) {
      content::TestNavigationObserver same_tab_observer(web_contents);
      SimulateMouseClick(web_contents, modifiers, button);
      same_tab_observer.Wait();
      EXPECT_EQ(1u,
                ProfileBrowserCollection::GetForProfile(browser->GetProfile())
                    ->GetSize());
      EXPECT_EQ(1, browser->tab_strip_model()->count());
      EXPECT_EQ(GetSecondPageTitle(), web_contents->GetTitle());
      return;
    }

    content::TestNavigationObserver new_tab_observer(nullptr);
    new_tab_observer.StartWatchingNewWebContents();
    SimulateMouseClick(web_contents, modifiers, button);
    new_tab_observer.Wait();

    if (disposition == WindowOpenDisposition::NEW_WINDOW) {
      EXPECT_EQ(2u,
                ProfileBrowserCollection::GetForProfile(browser->GetProfile())
                    ->GetSize());
      return;
    }

    EXPECT_EQ(1u, ProfileBrowserCollection::GetForProfile(browser->GetProfile())
                      ->GetSize());
    EXPECT_EQ(2, browser->tab_strip_model()->count());
    web_contents = browser->tab_strip_model()->GetActiveWebContents();
    if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
      EXPECT_EQ(GetSecondPageTitle(), web_contents->GetTitle());
    } else {
      ASSERT_EQ(WindowOpenDisposition::NEW_BACKGROUND_TAB, disposition);
      EXPECT_EQ(GetFirstPageTitle(), web_contents->GetTitle());
    }
  }
};

// Tests for clicking on elements with handlers that run window.open.

IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenBasicClickTest) {
  int modifiers = 0;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
  RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}

// TODO(ericu): Alt-click behavior on window.open is platform-dependent and not
// well defined.  Should we add tests so we know if it changes?

// Shift-clicks open in a new window.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenShiftClickTest) {
  int modifiers = blink::WebInputEvent::kShiftKey;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_WINDOW;
  RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}

// Control-clicks open in a background tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenControlClickTest) {
#if BUILDFLAG(IS_MAC)
  int modifiers = blink::WebInputEvent::kMetaKey;
#else
  int modifiers = blink::WebInputEvent::kControlKey;
#endif
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
  RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}

// Control-shift-clicks open in a foreground tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenControlShiftClickTest) {
#if BUILDFLAG(IS_MAC)
  int modifiers = blink::WebInputEvent::kMetaKey;
#else
  int modifiers = blink::WebInputEvent::kControlKey;
#endif
  modifiers |= blink::WebInputEvent::kShiftKey;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
  RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}

// Tests for clicking on normal links.

IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefBasicClickTest) {
  int modifiers = 0;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB;
  RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}

// TODO(ericu): Alt-click behavior on links is platform-dependent and not well
// defined.  Should we add tests so we know if it changes?

// Shift-clicks open in a new window.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefShiftClickTest) {
  int modifiers = blink::WebInputEvent::kShiftKey;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_WINDOW;
  RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}

// Control-clicks open in a background tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefControlClickTest) {
#if BUILDFLAG(IS_MAC)
  int modifiers = blink::WebInputEvent::kMetaKey;
#else
  int modifiers = blink::WebInputEvent::kControlKey;
#endif
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
  RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}

// Control-shift-clicks open in a foreground tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefControlShiftClickTest) {
#if BUILDFLAG(IS_MAC)
  int modifiers = blink::WebInputEvent::kMetaKey;
#else
  int modifiers = blink::WebInputEvent::kControlKey;
#endif
  modifiers |= blink::WebInputEvent::kShiftKey;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
  RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}

// Middle-clicks open in a background tab.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefMiddleClickTest) {
  int modifiers = 0;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kMiddle;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
  RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}

// Shift-middle-clicks open in a foreground tab.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefShiftMiddleClickTest) {
  int modifiers = blink::WebInputEvent::kShiftKey;
  blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kMiddle;
  WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
  RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}

IN_PROC_BROWSER_TEST_F(BrowserTest, GetSizeForNewRenderView) {
  // Force an initial resize. This works around a test-only problem on Chrome OS
  // where the shelf may not be created before the initial test browser window
  // opens, which leads to sizing issues in WebContents resize.
  browser()->GetWindow()->SetBounds(gfx::Rect(10, 20, 600, 400));
  // Let the message loop run so that resize actually takes effect.
  content::RunAllPendingInMessageLoop();

  // The instant extended NTP has javascript that does not work with
  // ui_test_utils::NavigateToURL.  The NTP rvh reloads when the browser tries
  // to navigate away from the page, which causes the WebContents to end up in
  // an inconsistent state. (is_loaded = true, last_commited_url=ntp,
  // visible_url=title1.html)
  browser()->GetProfile()->GetPrefs()->SetBoolean(
      prefs::kWebKitJavascriptEnabled, false);
  ASSERT_TRUE(embedded_test_server()->Start());
  // Create an HTTPS server for cross-site transition.
  net::EmbeddedTestServer https_test_server(
      net::EmbeddedTestServer::TYPE_HTTPS);
  https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
  ASSERT_TRUE(https_test_server.Start());

  // Start with NTP.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")));
  ASSERT_EQ(BookmarkBar::HIDDEN,
            BookmarkBarController::From(browser())->bookmark_bar_state());
  WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::RenderViewHost* prev_rvh =
      web_contents->GetPrimaryMainFrame()->GetRenderViewHost();
  const gfx::Size initial_wcv_size = web_contents->GetContainerBounds().size();
  RenderViewSizeObserver observer(web_contents,
                                  BrowserWindow::FromBrowser(browser()));

  // Navigate to a non-NTP page, without resizing WebContentsView.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));
  ASSERT_EQ(BookmarkBar::HIDDEN,
            BookmarkBarController::From(browser())->bookmark_bar_state());
  // A new RenderViewHost should be created.
  EXPECT_NE(prev_rvh, web_contents->GetPrimaryMainFrame()->GetRenderViewHost());
  prev_rvh = web_contents->GetPrimaryMainFrame()->GetRenderViewHost();
  gfx::Size rwhv_create_size0, rwhv_commit_size0, wcv_commit_size0;
  observer.GetSizeForRenderViewHost(
      web_contents->GetPrimaryMainFrame()->GetRenderViewHost(),
      &rwhv_create_size0, &rwhv_commit_size0, &wcv_commit_size0);
  EXPECT_EQ(gfx::Size(initial_wcv_size.width(), initial_wcv_size.height()),
            rwhv_create_size0);
  // When a navigation entry is committed, the size of RenderWidgetHostView
  // should be the same as when it was first created.
  EXPECT_EQ(rwhv_create_size0, rwhv_commit_size0);
  // Sizes of the current RenderWidgetHostView and WebContentsView should not
  // change before and after WebContentsObserver::DidFinishNavigation
  // (implemented by Browser); we obtain the sizes before PostCommit via
  // WebContentsObserver::NavigationEntryCommitted (implemented by
  // RenderViewSizeObserver).
  EXPECT_EQ(rwhv_commit_size0,
            web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
// The behavior differs between OSX and views.
// In OSX, the wcv does not change size until after the commit, when the
// bookmark bar disappears (correct).
// In views, the wcv changes size at commit time.
#if BUILDFLAG(IS_MAC)
  EXPECT_EQ(gfx::Size(wcv_commit_size0.width(), wcv_commit_size0.height()),
            web_contents->GetContainerBounds().size());
#else
  EXPECT_EQ(wcv_commit_size0, web_contents->GetContainerBounds().size());
#endif

  // Navigate to another non-NTP page, without resizing WebContentsView.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_test_server.GetURL("/title2.html")));
  ASSERT_EQ(BookmarkBar::HIDDEN,
            BookmarkBarController::From(browser())->bookmark_bar_state());
  // A new RenderVieHost should be created.
  EXPECT_NE(prev_rvh, web_contents->GetPrimaryMainFrame()->GetRenderViewHost());
  gfx::Size rwhv_create_size1, rwhv_commit_size1, wcv_commit_size1;
  observer.GetSizeForRenderViewHost(
      web_contents->GetPrimaryMainFrame()->GetRenderViewHost(),
      &rwhv_create_size1, &rwhv_commit_size1, &wcv_commit_size1);
  EXPECT_EQ(rwhv_create_size1, rwhv_commit_size1);
  EXPECT_EQ(rwhv_commit_size1,
            web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
  EXPECT_EQ(wcv_commit_size1, web_contents->GetContainerBounds().size());

  // Navigate from NTP to a non-NTP page, resizing WebContentsView while
  // navigation entry is pending.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")));
  gfx::Size wcv_resize_insets(1, 1);
  observer.set_wcv_resize_insets(wcv_resize_insets);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title2.html")));
  ASSERT_EQ(BookmarkBar::HIDDEN,
            BookmarkBarController::From(browser())->bookmark_bar_state());
  gfx::Size rwhv_create_size2, rwhv_commit_size2, wcv_commit_size2;
  observer.GetSizeForRenderViewHost(
      web_contents->GetPrimaryMainFrame()->GetRenderViewHost(),
      &rwhv_create_size2, &rwhv_commit_size2, &wcv_commit_size2);

  // The behavior on OSX and Views is incorrect in this edge case, but they are
  // differently incorrect.
  // The behavior should be:
  // initial wcv size: (100,100)  (to choose random numbers)
  // initial rwhv size: (100,140)
  // commit wcv size: (101, 101)
  // commit rwhv size: (101, 141)
  // final wcv size: (101, 141)
  // final rwhv size: (101, 141)
  //
  // On OSX, the commit rwhv size is (101, 101)
  // On views, the commit wcv size is (101, 141)
  // All other sizes are correct.

  EXPECT_EQ(gfx::Size(initial_wcv_size.width(), initial_wcv_size.height()),
            rwhv_create_size2);
  gfx::Size exp_commit_size(initial_wcv_size);

#if BUILDFLAG(IS_MAC)
  exp_commit_size.Enlarge(wcv_resize_insets.width(),
                          wcv_resize_insets.height());
#else
  exp_commit_size.Enlarge(wcv_resize_insets.width(),
                          wcv_resize_insets.height());
#endif
  EXPECT_EQ(exp_commit_size, rwhv_commit_size2);
  EXPECT_EQ(exp_commit_size, wcv_commit_size2);

  gfx::Size exp_final_size(initial_wcv_size);
  exp_final_size.Enlarge(wcv_resize_insets.width(), wcv_resize_insets.height());
  EXPECT_EQ(exp_final_size,
            web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
  EXPECT_EQ(exp_final_size, web_contents->GetContainerBounds().size());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, CanDuplicateTab) {
  GURL url(chrome_test_utils::GetTestUrl(
      base::FilePath(base::FilePath::kCurrentDirectory),
      base::FilePath(kTitle1File)));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));

  int active_index = browser()->tab_strip_model()->active_index();
  EXPECT_EQ(0, active_index);

  EXPECT_TRUE(chrome::CanDuplicateTab(browser()));
  EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 0));
  EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 1));
}

namespace {

void CheckDisplayModeMQ(const std::u16string& display_mode,
                        content::WebContents* web_contents) {
  std::u16string function =
      u"(function() {return window.matchMedia('(display-mode: " + display_mode +
      u")').matches;})();";
  bool js_result = false;
  base::RunLoop run_loop;
  web_contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
      function, base::BindLambdaForTesting([&](base::Value value) {
        DCHECK(value.is_bool());
        js_result = value.GetBool();
        run_loop.Quit();
      }),
      content::ISOLATED_WORLD_ID_GLOBAL);
  run_loop.Run();
  EXPECT_TRUE(js_result);
}

}  // namespace

// flaky new test: http://crbug.com/41165267
IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_ChangeDisplayMode) {
  CheckDisplayModeMQ(u"browser",
                     browser()->tab_strip_model()->GetActiveWebContents());

  Profile* profile = browser()->GetProfile();
  Browser* app_browser = CreateBrowserForApp("blah", profile);
  auto* app_contents = app_browser->tab_strip_model()->GetActiveWebContents();
  CheckDisplayModeMQ(u"standalone", app_contents);

  app_browser->GetFeatures()
      .exclusive_access_manager()
      ->context()
      ->EnterFullscreen(
          url::Origin(),
          EXCLUSIVE_ACCESS_BUBBLE_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION,
          FullscreenTabParams());

  // Sync navigation just to make sure IPC has passed (updated
  // display mode is delivered to RP).
  content::TestNavigationObserver observer(app_contents, 1);
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(app_browser, GURL(url::kAboutBlankURL)));
  observer.Wait();

  CheckDisplayModeMQ(u"fullscreen", app_contents);
}

// Test to ensure the bounds of popup, devtool, and app windows are properly
// restored.
IN_PROC_BROWSER_TEST_F(BrowserTest, TestPopupBounds) {
  // TODO(tdanderson|pkasting): Change this to verify that the contents bounds
  // set by params.initial_bounds are the same as the contents bounds in the
  // initialized window. See crbug.com/41238992.
  {
    // Minimum height a popup window should have added to the supplied content
    // bounds when drawn. This accommodates the browser toolbar.
    const int minimum_popup_padding = 26;

    // Creates an untrusted popup window and asserts that the eventual height is
    // padded with the toolbar and title bar height (initial height is content
    // height).
    BrowserWindowCreateParams params(BrowserWindowInterface::TYPE_POPUP,
                                     browser()->GetProfile(),
                                     /*from_user_gesture=*/true);
    params.initial_bounds = gfx::Rect(0, 0, 100, 122);
    Browser* browser =
        CreateBrowserWindow(std::move(params))->GetBrowserForMigrationOnly();
    gfx::Rect bounds = browser->GetWindow()->GetBounds();

    // Should be EXPECT_EQ, but this width is inconsistent across platforms.
    // See https://crbug.com/41227805.
    EXPECT_GE(bounds.width(), 100);

    // EXPECT_GE as Mac will have a larger height with the additional title bar.
    EXPECT_GE(bounds.height(), 122 + minimum_popup_padding);
    browser->GetWindow()->Close();
  }

  {
    // Creates a trusted popup window and asserts that the eventual height
    // doesn't change (initial height is window height).
    BrowserWindowCreateParams params(BrowserWindowInterface::TYPE_POPUP,
                                     browser()->GetProfile(),
                                     /*from_user_gesture=*/true);
    params.initial_bounds = gfx::Rect(0, 0, 100, 122);
    params.is_trusted_source = true;
    Browser* browser =
        CreateBrowserWindow(std::move(params))->GetBrowserForMigrationOnly();
    gfx::Rect bounds = browser->GetWindow()->GetBounds();

    // Should be EXPECT_EQ, but this width is inconsistent across platforms.
    // See https://crbug.com/41227805.
    EXPECT_GE(bounds.width(), 100);

    // EXPECT_GE as Mac will have a larger height with the additional title bar.
    EXPECT_GE(bounds.height(), 122);
    browser->GetWindow()->Close();
  }

  {
    // Creates an untrusted app window and asserts that the eventual height
    // doesn't change.
    BrowserWindowCreateParams params = BrowserWindowCreateParams::CreateForApp(
        "app-name", /*trusted_source=*/false, gfx::Rect(0, 0, 100, 122),
        browser()->GetProfile(), /*user_gesture=*/true);
    Browser* browser =
        CreateBrowserWindow(std::move(params))->GetBrowserForMigrationOnly();
    gfx::Rect bounds = browser->GetWindow()->GetBounds();

    // Should be EXPECT_EQ, but this width is inconsistent across platforms.
    // See https://crbug.com/41227805.
    EXPECT_GE(bounds.width(), 100);
    EXPECT_EQ(122, bounds.height());
    browser->GetWindow()->Close();
  }

  {
    // Creates a trusted app window and asserts that the eventual height
    // doesn't change.
    BrowserWindowCreateParams params = BrowserWindowCreateParams::CreateForApp(
        "app-name", /*trusted_source=*/true, gfx::Rect(0, 0, 100, 122),
        browser()->GetProfile(), /*user_gesture=*/true);
    Browser* browser =
        CreateBrowserWindow(std::move(params))->GetBrowserForMigrationOnly();
    gfx::Rect bounds = browser->GetWindow()->GetBounds();

    // Should be EXPECT_EQ, but this width is inconsistent across platforms.
    // See https://crbug.com/41227805.
    EXPECT_GE(bounds.width(), 100);
    EXPECT_EQ(122, bounds.height());
    browser->GetWindow()->Close();
  }

  {
    // Creates a devtools window and asserts that the eventual height
    // doesn't change.
    BrowserWindowCreateParams params =
        BrowserWindowCreateParams::CreateForDevTools(browser()->GetProfile());
    params.initial_bounds = gfx::Rect(0, 0, 100, 122);
    Browser* browser =
        CreateBrowserWindow(std::move(params))->GetBrowserForMigrationOnly();
    gfx::Rect bounds = browser->GetWindow()->GetBounds();

    // Should be EXPECT_EQ, but this width is inconsistent across platforms.
    // See https://crbug.com/41227805.
    EXPECT_GE(bounds.width(), 100);

    // EXPECT_GE as Mac will have a larger height with the additional title bar.
    EXPECT_GE(bounds.height(), 122);
    browser->GetWindow()->Close();
  }
}

IN_PROC_BROWSER_TEST_F(BrowserTest, IsOffTheRecordBrowserInUse) {
  EXPECT_FALSE(IsOffTheRecordBrowserInUse(browser()->GetProfile()));

  Browser* incognito_browser = CreateIncognitoBrowser(browser()->GetProfile());
  EXPECT_TRUE(IsOffTheRecordBrowserInUse(browser()->GetProfile()));

  CloseBrowserSynchronously(incognito_browser);
  EXPECT_FALSE(IsOffTheRecordBrowserInUse(browser()->GetProfile()));
}

IN_PROC_BROWSER_TEST_F(BrowserTest, TestActiveTabChangedUserAction) {
  base::UserActionTester user_action_tester;
  chrome::NewTab(browser(), NewTabTypes::kNoUserAction);
  EXPECT_EQ(user_action_tester.GetActionCount("ActiveTabChanged"), 1);
}

IN_PROC_BROWSER_TEST_F(BrowserTest, TestNavEntryCommittedUserAction) {
  base::UserActionTester user_action_tester;
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")));
  EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted"), 1);
}

namespace {

void SetTestDefaultSearchProvider(TemplateURLService* service,
                                  const GURL& search_template) {
  ASSERT_TRUE(service);
  search_test_utils::WaitForTemplateURLServiceToLoad(service);
  ASSERT_TRUE(service->loaded());

  TemplateURLData data;
  data.SetShortName(u"test");
  data.SetKeyword(data.short_name());
  data.SetURL(search_template.spec());

  TemplateURL* template_url = service->Add(std::make_unique<TemplateURL>(data));
  ASSERT_TRUE(template_url);
  service->SetUserSelectedDefaultSearchProvider(template_url);
}

}  // namespace

IN_PROC_BROWSER_TEST_F(BrowserTest,
                       TestNavEntryCommittedUserActionOnlyRecordedForTabs) {
  ASSERT_TRUE(embedded_test_server()->Start());
  TemplateURLService* service =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile());
  SetTestDefaultSearchProvider(
      service,
      embedded_test_server()->GetURL("a.test", "/title1.html?q={searchTerms}"));

  const GURL srp_url =
      service->GenerateSearchURLForDefaultSearchProvider(u"testing");

  base::UserActionTester user_action_tester;

  // Create and navigate a WebContents that is not a tab. The NavEntryCommitted
  // actions should not be recorded for this WebContents.
  std::unique_ptr<content::WebContents> non_tab_web_contents =
      content::WebContents::Create(
          content::WebContents::CreateParams(browser()->GetProfile()));
  content::TestNavigationObserver observer(non_tab_web_contents.get());
  non_tab_web_contents->GetController().LoadURL(
      srp_url, content::Referrer(), ::ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
      std::string());
  observer.Wait();

  EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted"), 0);
}

IN_PROC_BROWSER_TEST_F(BrowserTest, TestTabCountMetrics) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // This test assumes there's only one browser with one tab at the start of the
  // test.
  EXPECT_EQ(1u, GlobalBrowserCollection::GetInstance()->GetSize());
  ASSERT_EQ(1, browser()->tab_strip_model()->count());

  // Create an additional browser with two tabs.
  Browser* browser2 = CreateBrowser(browser()->GetProfile());
  chrome::NewTab(browser2, NewTabTypes::kNoUserAction);
  ASSERT_TRUE(content::WaitForLoadStop(
      browser2->tab_strip_model()->GetActiveWebContents()));
  EXPECT_EQ(2u, GlobalBrowserCollection::GetInstance()->GetSize());
  ASSERT_EQ(1, browser()->tab_strip_model()->count());
  ASSERT_EQ(2, browser2->tab_strip_model()->count());

  base::HistogramTester histogram_tester;
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser2, embedded_test_server()->GetURL("/title1.html")));

  histogram_tester.ExpectBucketCount("Tabs.TabCountPerWindow", 1, 1);
  histogram_tester.ExpectBucketCount("Tabs.TabCountPerWindow", 2, 1);
  histogram_tester.ExpectUniqueSample("Tabs.TabCountPerLoad", 3, 1);
  // Skipping "Tabs.TabCountActiveWindow" due to flakiness risk w.r.t. focus.

  // Also check that tab group count metrics are recorded. In this case, there
  // aren't any groups.
  histogram_tester.ExpectUniqueSample("TabGroups.UserGroupCountPerLoad", 0, 1);
  histogram_tester.ExpectUniqueSample("TabGroups.UserPinnedTabCountPerLoad", 0,
                                      1);
  histogram_tester.ExpectUniqueSample("Tabs.TabCountInGroupPerLoad", 0, 1);
  histogram_tester.ExpectUniqueSample(
      "TabGroups.UserCustomizedGroupCountPerLoad", 0, 1);
  histogram_tester.ExpectUniqueSample("TabGroups.CollapsedGroupCountPerLoad", 0,
                                      1);
}

IN_PROC_BROWSER_TEST_F(BrowserTest,
                       TestTabCountMetricsOnlyRecordedWhenTabLoads) {
  ASSERT_TRUE(embedded_test_server()->Start());

  base::HistogramTester histogram_tester;

  // Create and navigate a WebContents that is not a tab. Tab count metrics
  // should not be recorded when this WebContents loads.
  std::unique_ptr<content::WebContents> non_tab_web_contents =
      content::WebContents::Create(
          content::WebContents::CreateParams(browser()->GetProfile()));
  content::TestNavigationObserver observer(non_tab_web_contents.get());
  non_tab_web_contents->GetController().LoadURL(
      embedded_test_server()->GetURL("/title1.html"), content::Referrer(),
      ::ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
  observer.Wait();

  histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 0);
}

IN_PROC_BROWSER_TEST_F(BrowserTest,
                       TestTabCountMetricsNotRecordedWhenIframeLoads) {
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/iframe.html")));

  base::HistogramTester histogram_tester;

  // Tab count metrics should not be recorded when the iframe navigates.
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  const GURL iframe_url = embedded_test_server()->GetURL("/title2.html");
  ASSERT_TRUE(content::NavigateIframeToURL(web_contents, "test", iframe_url));

  histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 0);
}

IN_PROC_BROWSER_TEST_F(
    BrowserTest,
    TestTabCountMetricsNotRecordedForSameDocumentNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));

  base::HistogramTester histogram_tester;

  // Tab count metrics should not be recorded for same document navigations.
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::TestNavigationObserver nav_observer(web_contents);
  ASSERT_TRUE(content::ExecJs(web_contents, "location.href = '#test'"));
  nav_observer.Wait();

  histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 0);
}

IN_PROC_BROWSER_TEST_F(BrowserTest, TestTabCountMetricsRecordedOnReload) {
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));

  base::HistogramTester histogram_tester;

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::TestNavigationObserver nav_observer(web_contents);
  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  nav_observer.Wait();

  histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 1);
}

IN_PROC_BROWSER_TEST_F(BrowserTest,
                       TestTabCountMetricsRecordedOnBackNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title2.html")));

  base::HistogramTester histogram_tester;

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::TestNavigationObserver nav_observer(web_contents);
  chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
  nav_observer.Wait();

  histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 1);
}

IN_PROC_BROWSER_TEST_F(BrowserTest, TestActiveBrowserChangedUserAction) {
  base::UserActionTester user_action_tester;
  ui_test_utils::DeprecatedFakeActivateBrowser(browser());
  EXPECT_EQ(user_action_tester.GetActionCount("ActiveBrowserChanged"), 1);
}

// DISABLED for flakiness. See https://crbug.com/40752437
IN_PROC_BROWSER_TEST_F(
    BrowserTest,
    DISABLED_SameDocumentNavigationWithNothingCommittedAfterCrash) {
  // The test sets this closure before each navigation to /sometimes-slow in
  // order to control the response for that navigation.
  content::SlowHttpResponse::GotRequestCallback got_slow_request;

  embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
      [&](const net::test_server::HttpRequest& request)
          -> std::unique_ptr<net::test_server::HttpResponse> {
        if (request.relative_url != "/sometimes-slow") {
          return nullptr;
        }
        DCHECK(got_slow_request)
            << "Set `got_slow_request` before each navigation request.";
        return std::make_unique<content::SlowHttpResponse>(
            std::move(got_slow_request));
      }));
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL url1 = embedded_test_server()->GetURL("/sometimes-slow");
  GURL url2 = embedded_test_server()->GetURL("/sometimes-slow#foo");

  WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();

  // Successfully navigate to `url1`.
  got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
  EXPECT_TRUE(content::NavigateToURL(wc, url1));

  // Kill the renderer for the tab.
  {
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;

    content::RenderFrameDeletedObserver crash_observer(
        wc->GetPrimaryMainFrame());
    wc->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
    crash_observer.WaitUntilDeleted();
  }

  // Bring the process back to life for the current RenderFrameHost, though with
  // a speculative RenderFrameHost navigating back to `url1`.
  {
    content::NavigationController::LoadURLParams params(url1);
    params.transition_type = ui::PageTransitionFromInt(
        ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);

    base::RunLoop loop;
    got_slow_request =
        base::BindLambdaForTesting([&](base::OnceClosure start_response,
                                       base::OnceClosure finish_response) {
          // Never starts the response, but informs the test the request has
          // been received.
          loop.Quit();
        });
    wc->GetController().LoadURLWithParams(params);
    loop.Run();
  }
  // The navigation has not completed, but the renderer has come alive.
  EXPECT_TRUE(wc->GetPrimaryMainFrame()->IsRenderFrameLive());
  EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL().spec(), "");

  // Now try to navigate to `url2`. We're currently trying to load `url1` since
  // the above navigation will be delayed. Going to `url2` should be a
  // same-document navigation according to the urls alone. But it can't be since
  // the current frame host does not actually have a document loaded.
  content::NavigationHandleCommitObserver nav_observer(wc, url2);
  {
    content::NavigationController::LoadURLParams params(url2);
    params.transition_type = ui::PageTransitionFromInt(
        ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);

    got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
    wc->GetController().LoadURLWithParams(params);
  }
  EXPECT_TRUE(WaitForLoadStop(wc));
  EXPECT_TRUE(nav_observer.has_committed());
  EXPECT_FALSE(nav_observer.was_same_document());
}

// DISABLED for flakiness. See https://crbug.com/40752437
IN_PROC_BROWSER_TEST_F(
    BrowserTest,
    DISABLED_SameDocumentHistoryNavigationWithNothingCommittedAfterCrash) {
  content::SlowHttpResponse::GotRequestCallback got_slow_request;

  embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
      [&](const net::test_server::HttpRequest& request)
          -> std::unique_ptr<net::test_server::HttpResponse> {
        if (request.relative_url != "/sometimes-slow") {
          return nullptr;
        }
        DCHECK(got_slow_request)
            << "Set `got_slow_request` before each navigation request.";
        return std::make_unique<content::SlowHttpResponse>(
            std::move(got_slow_request));
      }));
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL url1 = embedded_test_server()->GetURL("/sometimes-slow");
  GURL url2 = embedded_test_server()->GetURL("/sometimes-slow#foo");

  content::WebContents* wc =
      browser()->tab_strip_model()->GetActiveWebContents();

  // Successfully navigate to `url1`, then do a same-document navigation to
  // `url2`.
  got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
  EXPECT_TRUE(content::NavigateToURL(wc, url1));
  EXPECT_TRUE(content::NavigateToURL(wc, url2));

  // Kill the renderer for the tab.
  {
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;

    content::RenderFrameDeletedObserver crash_observer(
        wc->GetPrimaryMainFrame());
    wc->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
    crash_observer.WaitUntilDeleted();
  }

  // Bring the process back to life for the current RenderFrameHost, though with
  // a speculative RenderFrameHost navigating back to `url1`.
  {
    content::NavigationController::LoadURLParams params(url1);
    params.transition_type = ui::PageTransitionFromInt(
        ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);

    base::RunLoop loop;
    got_slow_request =
        base::BindLambdaForTesting([&](base::OnceClosure start_response,
                                       base::OnceClosure finish_response) {
          // Never starts the response, but informs the test the request has
          // been received.
          loop.Quit();
        });
    wc->GetController().LoadURLWithParams(params);
    loop.Run();
  }
  // The navigation has not completed, but the renderer has come alive.
  EXPECT_TRUE(wc->GetPrimaryMainFrame()->IsRenderFrameLive());
  EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL().spec(), "");

  content::NavigationHandleCommitObserver back_observer(wc, url1);
  // Now try to go back. We're currently at `url2` since the above navigation
  // will be blocked. Going back to `url1` should be a same-document history
  // navigation according to the NavigationEntry. But it can't be since the
  // current frame host does not actually have a document loaded.
  got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
  wc->GetController().GoBack();
  EXPECT_TRUE(WaitForLoadStop(wc));
  EXPECT_TRUE(back_observer.has_committed());
  EXPECT_FALSE(back_observer.was_same_document());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, CreatePictureInPicture) {
  Browser* popup_browser =
      CreateBrowserWindow(BrowserWindowCreateParams(
                              BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE,
                              browser()->GetProfile(),
                              /*from_user_gesture=*/true))
          ->GetBrowserForMigrationOnly();
  ASSERT_EQ(popup_browser->GetType(),
            BrowserWindowInterface::Type::TYPE_PICTURE_IN_PICTURE);
}

#if BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(BrowserTest, PreventCloseYieldsCancelledEvent) {
  base::test::TestFuture<void> policy_refresh_sync_future;
  web_app::WebAppProvider::GetForWebApps(profile())
      ->policy_manager()
      .SetRefreshPolicySettingsCompletedCallbackForTesting(
          policy_refresh_sync_future.GetCallback());

  const absl::Cleanup policy_cleanup = [this]() {
    // Clear policy values, otherwise we won't be able to gracefully close the
    // browser test.
    profile()->GetPrefs()->SetList(prefs::kWebAppSettings, base::ListValue());
  };

  // Set up policy values.
  static constexpr char kCalculatorAppUrl[] = "https://calculator.apps.chrome/";
  profile()->GetPrefs()->SetList(
      prefs::kWebAppSettings,
      base::ListValue().Append(
          base::DictValue()
              .Set(web_app::kManifestId, kCalculatorAppUrl)
              .Set(web_app::kRunOnOsLogin, web_app::kRunWindowed)
              .Set(web_app::kPreventClose, true)));
  profile()->GetPrefs()->SetList(
      prefs::kWebAppInstallForceList,
      base::ListValue().Append(
          base::DictValue()
              .Set(web_app::kUrlKey, kCalculatorAppUrl)
              .Set(web_app::kDefaultLaunchContainerKey,
                   web_app::kDefaultLaunchContainerWindowValue)));
  ASSERT_TRUE(policy_refresh_sync_future.Wait());

  apps::AppUpdateWaiter waiter(
      profile(), ash::kCalculatorAppId,
      base::BindRepeating([](const apps::AppUpdate& update) {
        return update.AllowClose().has_value() && !update.AllowClose().value();
      }));
  waiter.Await();

  BrowserWindowInterface* const browser =
      web_app::LaunchWebAppBrowser(profile(), ash::kCalculatorAppId);
  ASSERT_TRUE(browser);

  int times_called = 0;
  base::CallbackListSubscription browser_close_canelled_subscription =
      browser->RegisterBrowserCloseCancelled(base::BindLambdaForTesting(
          [&](BrowserWindowInterface* bwi,
              BrowserWindowInterface::ClosingStatus status) {
            EXPECT_EQ(BrowserWindowInterface::ClosingStatus::kDeniedByPolicy,
                      status);
            times_called++;
          }));
  UnloadController::From(browser)->OnWindowClosing();
  EXPECT_EQ(1, times_called);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

// Asserts that browser close operations only propagate browser closed
// notifications once.
IN_PROC_BROWSER_TEST_F(BrowserTest, BrowserCloseEmitsClosedNotificationsOnce) {
  base::HistogramTester tester;
  ASSERT_TRUE(embedded_test_server()->Start());

  // There should be one browser and one tab to start with.
  EXPECT_EQ(1u, ProfileBrowserCollection::GetForProfile(browser()->GetProfile())
                    ->GetSize());
  ASSERT_EQ(1, browser()->tab_strip_model()->count());

  // Assert only a single closed operation is propagated to registered clients.
  base::MockCallback<BrowserWindowInterface::BrowserDidCloseCallback>
      browser_did_close_callback;
  EXPECT_CALL(browser_did_close_callback, Run).Times(1);
  base::CallbackListSubscription subscription =
      browser()->RegisterBrowserDidClose(browser_did_close_callback.Get());

  EXPECT_EQ(1u, GlobalBrowserCollection::GetInstance()->GetSize());
  CloseBrowserSynchronously(browser());
  EXPECT_EQ(0u, GlobalBrowserCollection::GetInstance()->GetSize());
}

// Asserts that browser propagates browser closed notifications in the case the
// object is synchronously destroyed via
// `BrowserManagerService::SynchronouslyDestroyBrowser()`.
IN_PROC_BROWSER_TEST_F(BrowserTest,
                       BrowserCloseEmitsClosedNotificationsWhenDestroyed) {
  Browser* const new_browser = CreateBrowser(GetProfile());

  // Assert a closed event is delivered in the case the browser is synchronously
  // destroyed.
  base::MockCallback<BrowserWindowInterface::BrowserDidCloseCallback>
      browser_did_close_callback;
  EXPECT_CALL(browser_did_close_callback, Run).Times(1);
  base::CallbackListSubscription subscription =
      new_browser->RegisterBrowserDidClose(browser_did_close_callback.Get());

  EXPECT_EQ(2u, GlobalBrowserCollection::GetInstance()->GetSize());
  BrowserManagerService::SynchronouslyDestroyBrowser(new_browser);
  EXPECT_EQ(1u, GlobalBrowserCollection::GetInstance()->GetSize());
}

IN_PROC_BROWSER_TEST_F(BrowserTest, ClosedBrowsersShouldNotShow) {
  // Create a new browser but do not show it yet.
  BrowserWindowInterface* const new_browser = CreateBrowserWindow(
      BrowserWindowCreateParams(GetProfile(), /*from_user_gesture=*/true));
  ui::BaseWindow* const new_window = new_browser->GetWindow();
  EXPECT_FALSE(new_window->IsVisible());

  // Close the browser before Show() is called.
  new_window->Close();

  // Attempts to show a closed browser should no-op.
  new_window->Show();
  EXPECT_FALSE(new_window->IsVisible());
}

class GuestSessionBrowserTest : public BrowserTest {
 public:
#if BUILDFLAG(IS_CHROMEOS)
  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(ash::switches::kGuestSession);
    command_line->AppendSwitchASCII(ash::switches::kLoginUser,
                                    user_manager::kGuestUserName);
    command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                    TestingProfile::kTestUserProfileDir);
  }
#endif  // BUILDFLAG(IS_CHROMEOS)

  void SetUpOnMainThread() override {
    BrowserTest::SetUpOnMainThread();
#if BUILDFLAG(IS_CHROMEOS)
    guest_browser_ = browser();
#else
    guest_browser_ = CreateGuestBrowser();
#endif  // BUILDFLAG(IS_CHROMEOS)
  }

  void TearDownOnMainThread() override {
    guest_browser_ = nullptr;
    BrowserTest::TearDownOnMainThread();
  }

  Browser* guest_browser() { return guest_browser_; }

 private:
  raw_ptr<Browser> guest_browser_ = nullptr;
};

// Tests that Browser::Create creates a guest session browser.
IN_PROC_BROWSER_TEST_F(GuestSessionBrowserTest, CreateGuestSessionBrowser) {
  // Creating a guest session browser should succeed and the instantiated
  // browser should be using an OTR profile.
  Profile* guest_profile = guest_browser()->GetProfile();
  EXPECT_TRUE(guest_browser());
  EXPECT_TRUE(guest_profile->IsGuestSession());
  EXPECT_TRUE(guest_profile->IsOffTheRecord());

  // Try creating a browser in original non-OTR guest profile - it should fail.
  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
            GetBrowserWindowCreationStatusForProfile(
                *guest_profile->GetOriginalProfile()));
}

class MockBookmarkBarController : public BookmarkBarController {
 public:
  MockBookmarkBarController(BrowserWindowInterface& browser,
                            TabStripModel& tab_strip_model)
      : BookmarkBarController(browser, tab_strip_model), browser_(browser) {}
  ~MockBookmarkBarController() override {
    // Trigger a theme change notification via ThemeService by changing a
    // preference it observes.
    browser_->GetProfile()->GetPrefs()->SetInteger(
        prefs::kBrowserColorScheme,
        static_cast<int>(ThemeService::BrowserColorScheme::kDark));
  }

 private:
  const raw_ref<BrowserWindowInterface> browser_;
};

class BrowserDestructorBrowserTest : public InProcessBrowserTest {
 public:
  BrowserDestructorBrowserTest() {
    // Inject our mock BookmarkBarController.
    bookmark_bar_override_ =
        BrowserWindowFeatures::GetUserDataFactoryForTesting()
            .AddOverrideForTesting<MockBookmarkBarController>(
                base::BindRepeating([](BrowserWindowInterface& browser) {
                  return std::make_unique<MockBookmarkBarController>(
                      browser, *browser.GetTabStripModel());
                }));
  }

 private:
  ui::UserDataFactory::ScopedOverride bookmark_bar_override_;
};

// This test verifies that a reentrant call to OnThemeChanged during Browser
// destruction does not cause a crash. Before the fix, this would crash because
// RemoveObserver(this) was called after window_.reset() and features_.reset().
// See https://www.crbug.com/458008770.
IN_PROC_BROWSER_TEST_F(BrowserDestructorBrowserTest,
                       NoCrashOnReentrantThemeChangeDuringDestruction) {
  // Create a new browser.
  Browser* second_browser = CreateBrowser(browser()->GetProfile());
  ASSERT_TRUE(second_browser);
  // Close the browser. This will trigger the destructor.
  // The MockBookmarkBarController will be destroyed during features_.reset(),
  // which will call second_browser->OnThemeChanged().
  CloseBrowserSynchronously(second_browser);
}

// Ensure crashed tabs are not reloaded when selected. crbug.com/41007585
IN_PROC_BROWSER_TEST_F(BrowserTest, ReloadCrashedTab) {
  TabStripModel* tab_strip_model = browser()->tab_strip_model();

  // Start with a single foreground tab.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
  content::WebContents* contents1 = tab_strip_model->GetActiveWebContents();

  EXPECT_TRUE(tab_strip_model->IsTabSelected(0));
  EXPECT_FALSE(contents1->IsLoading());

  // Add a second tab in the background.
  ASSERT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
  content::WebContents* contents2 = tab_strip_model->GetWebContentsAt(1);

  // Switch active tab back to index 0.
  tab_strip_model->ActivateTabAt(
      0, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));

  EXPECT_EQ(2, tab_strip_model->count());
  EXPECT_EQ(0, tab_strip_model->active_index());
  EXPECT_FALSE(contents2->IsLoading());

  // Simulate the second tab crashing.
  content::CrashTab(contents2);
  EXPECT_TRUE(contents2->IsCrashed());

  // Selecting the second tab does not cause a load or clear the crash.
  tab_strip_model->ActivateTabAt(
      1, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));
  EXPECT_TRUE(tab_strip_model->IsTabSelected(1));
  EXPECT_FALSE(contents2->IsLoading());
  EXPECT_TRUE(contents2->IsCrashed());
}

// This tests a workaround which is not necessary on Mac.
// https://crbug.com/41317454
#if BUILDFLAG(IS_MAC)
#define MAYBE_SetBackgroundColorForNewTab DISABLED_SetBackgroundColorForNewTab
#else
#define MAYBE_SetBackgroundColorForNewTab SetBackgroundColorForNewTab
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_SetBackgroundColorForNewTab) {
  TabStripModel* tab_strip_model = browser()->tab_strip_model();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL("data:text/html,<body style='background-color:red;'>")));

  // Add a second tab in the background.
  chrome::AddTabAt(browser(), GURL(), -1, /*foreground=*/false);
  content::WebContents* contents2 = tab_strip_model->GetWebContentsAt(1);

  tab_strip_model->ActivateTabAt(
      1, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));
  ASSERT_TRUE(
      contents2->GetPrimaryMainFrame()->GetView()->GetBackgroundColor());
  EXPECT_EQ(SK_ColorRED,
            *contents2->GetPrimaryMainFrame()->GetView()->GetBackgroundColor());
}

#if BUILDFLAG(ENABLE_PRINTING)
// Ensure the print command gets disabled when a tab crashes.
IN_PROC_BROWSER_TEST_F(BrowserTest, DisablePrintOnCrashedTab) {
  TabStripModel* tab_strip_model = browser()->tab_strip_model();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
  content::WebContents* contents = tab_strip_model->GetActiveWebContents();

  CommandUpdater* command_updater =
      browser()->GetFeatures().browser_command_controller();

  EXPECT_FALSE(contents->IsCrashed());
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_PRINT));
  EXPECT_TRUE(chrome::CanPrint(browser()));

  content::CrashTab(contents);

  EXPECT_TRUE(contents->IsCrashed());
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_PRINT));
  EXPECT_FALSE(chrome::CanPrint(browser()));
}
#endif  // BUILDFLAG(ENABLE_PRINTING)

// Ensure the zoom-in and zoom-out commands get disabled when a tab crashes.
IN_PROC_BROWSER_TEST_F(BrowserTest, DisableZoomOnCrashedTab) {
  TabStripModel* tab_strip_model = browser()->tab_strip_model();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
  content::WebContents* contents = tab_strip_model->GetActiveWebContents();

  zoom::ZoomController* zoom_controller =
      zoom::ZoomController::FromWebContents(contents);
  EXPECT_TRUE(
      zoom_controller->SetZoomLevel(zoom_controller->GetDefaultZoomLevel()));

  CommandUpdater* command_updater =
      browser()->GetFeatures().browser_command_controller();

  EXPECT_TRUE(zoom_controller->IsAtDefaultZoom());
  EXPECT_FALSE(contents->IsCrashed());
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_ZOOM_PLUS));
  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_ZOOM_MINUS));
  EXPECT_TRUE(chrome::CanZoomIn(contents));
  EXPECT_TRUE(chrome::CanZoomOut(contents));

  content::CrashTab(contents);

  EXPECT_TRUE(contents->IsCrashed());
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_ZOOM_PLUS));
  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_ZOOM_MINUS));
  EXPECT_FALSE(chrome::CanZoomIn(contents));
  EXPECT_FALSE(chrome::CanZoomOut(contents));
}

using BrowserBookmarkBarTest = InProcessBrowserTest;

IN_PROC_BROWSER_TEST_F(BrowserBookmarkBarTest, StateOnActiveTabChanged) {
  const BookmarkBarController* controller =
      BookmarkBarController::From(browser());
  ASSERT_TRUE(controller);

  EXPECT_EQ(BookmarkBar::HIDDEN, controller->bookmark_bar_state());

  // Navigate to normal page.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
  EXPECT_EQ(BookmarkBar::HIDDEN, controller->bookmark_bar_state());

  // Toggle bookmark bar on.
  chrome::ToggleBookmarkBar(browser());
  EXPECT_EQ(BookmarkBar::SHOW, controller->bookmark_bar_state());

  // Open a new tab to NTP.
  ASSERT_TRUE(
      AddTabAtIndex(1, GURL("chrome://newtab"), ui::PAGE_TRANSITION_TYPED));
  EXPECT_EQ(BookmarkBar::SHOW, controller->bookmark_bar_state());

  // Switch back to 1st tab.
  browser()->tab_strip_model()->ActivateTabAt(
      0, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));
  EXPECT_EQ(BookmarkBar::SHOW, controller->bookmark_bar_state());

  // Toggle bookmark bar off.
  chrome::ToggleBookmarkBar(browser());
  EXPECT_EQ(BookmarkBar::HIDDEN, controller->bookmark_bar_state());
}
