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

#include <stddef.h>

#include <string_view>
#include <vector>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/predictors/autocomplete_action_predictor.h"
#include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/task_manager/common/task_manager_features.h"
#include "chrome/browser/task_manager/task_manager_browsertest_util.h"
#include "chrome/browser/task_manager/task_manager_interface.h"
#include "chrome/browser/task_manager/task_manager_tester.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/dialogs/browser_dialogs.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/task_manager/task_manager_table_model.h"
#include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/chrome_test_path_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "components/url_formatter/url_formatter.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/no_renderer_crashes_assertion.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/render_frame_host_test_support.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/strings/grit/services_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
#include "url/url_constants.h"

using content::WebContents;
using task_manager::DisplayCategory;
using task_manager::browsertest_util::ColumnSpecifier;
using task_manager::browsertest_util::MatchAboutBlankTab;
using task_manager::browsertest_util::MatchAnyApp;
using task_manager::browsertest_util::MatchAnyBFCache;
using task_manager::browsertest_util::MatchAnyExtension;
using task_manager::browsertest_util::MatchAnyFencedFrame;
using task_manager::browsertest_util::MatchAnyIncognitoFencedFrame;
using task_manager::browsertest_util::MatchAnyIncognitoTab;
using task_manager::browsertest_util::MatchAnyPrerender;
using task_manager::browsertest_util::MatchAnySubframe;
using task_manager::browsertest_util::MatchAnyTab;
using task_manager::browsertest_util::MatchAnyUtility;
using task_manager::browsertest_util::MatchApp;
using task_manager::browsertest_util::MatchBFCache;
using task_manager::browsertest_util::MatchExtension;
using task_manager::browsertest_util::MatchFencedFrame;
using task_manager::browsertest_util::MatchIncognitoFencedFrame;
using task_manager::browsertest_util::MatchIncognitoTab;
using task_manager::browsertest_util::MatchPrerender;
using task_manager::browsertest_util::MatchSubframe;
using task_manager::browsertest_util::MatchTab;
using task_manager::browsertest_util::MatchUtility;
using task_manager::browsertest_util::WaitForTaskManagerRows;
using task_manager::browsertest_util::WaitForTaskManagerStatToExceed;

namespace {

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

}  // namespace

class TaskManagerBrowserTest : public extensions::ExtensionBrowserTest {
 public:
  TaskManagerBrowserTest() = default;
  TaskManagerBrowserTest(const TaskManagerBrowserTest&) = delete;
  TaskManagerBrowserTest& operator=(const TaskManagerBrowserTest&) = delete;
  ~TaskManagerBrowserTest() override = default;

  task_manager::TaskManagerTester* model() { return model_.get(); }

  void ShowTaskManager() {
    // Show the task manager. This populates the model, and helps with debugging
    // (you see the task manager).
    chrome::ShowTaskManager(browser());
    model_ = task_manager::TaskManagerTester::Create(base::BindRepeating(
        &TaskManagerBrowserTest::TaskManagerTableModelSanityCheck,
        base::Unretained(this)));
  }

  void HideTaskManager() {
    model_.reset();

    // Hide the task manager, and wait for it to go.
    chrome::HideTaskManager();
    base::RunLoop().RunUntilIdle();  // OnWindowClosed happens asynchronously.
  }

  GURL GetTestURL() {
    return chrome_test_utils::GetTestUrl(
        base::FilePath(base::FilePath::kCurrentDirectory),
        base::FilePath(kTitle1File));
  }

  std::optional<size_t> FindResourceIndex(const std::u16string& title) {
    for (size_t i = 0; i < model_->GetRowCount(); ++i) {
      if (title == model_->GetRowTitle(i))
        return i;
    }
    return std::nullopt;
  }

 protected:
  void TearDownOnMainThread() override {
    model_.reset();
    extensions::ExtensionBrowserTest::TearDownOnMainThread();
  }

  void SetUpOnMainThread() override {
    extensions::ExtensionBrowserTest::SetUpOnMainThread();
    host_resolver()->AddRule("*", "127.0.0.1");

    // Add content/test/data so we can use cross_site_iframe_factory.html
    base::FilePath test_data_dir;
    ASSERT_TRUE(
        base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_data_dir));
    embedded_test_server()->ServeFilesFromDirectory(
        test_data_dir.AppendASCII("content/test/data/"));
    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
    content::SetupCrossSiteRedirector(embedded_test_server());
    embedded_test_server()->StartAcceptingConnections();
  }

 private:
  void TaskManagerTableModelSanityCheck() {
    // Ensure the groups are self-consistent.
    for (size_t i = 0; i < model()->GetRowCount(); ++i) {
      size_t start, length;
      model()->GetRowsGroupRange(i, &start, &length);
      for (size_t j = 0; j < length; ++j) {
        size_t start2, length2;
        model()->GetRowsGroupRange(start + j, &start2, &length2);
        EXPECT_EQ(start, start2);
        EXPECT_EQ(length, length2);
      }
    }
  }

  std::unique_ptr<task_manager::TaskManagerTester> model_;
};

class TaskManagerUtilityProcessBrowserTest : public TaskManagerBrowserTest {
 public:
  TaskManagerUtilityProcessBrowserTest() = default;
  TaskManagerUtilityProcessBrowserTest(
      const TaskManagerUtilityProcessBrowserTest&) = delete;
  TaskManagerUtilityProcessBrowserTest& operator=(
      const TaskManagerUtilityProcessBrowserTest&) = delete;

 protected:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    TaskManagerBrowserTest::SetUpCommandLine(command_line);

    // Use a trivial PAC script to ensure that some javascript is being
    // executed.
    command_line->AppendSwitchASCII(
        switches::kProxyPacUrl,
        "data:,function FindProxyForURL(url, host){return \"DIRECT;\";}");
  }
};

// Parameterized variant of TaskManagerBrowserTest which runs with/without
// --site-per-process, which enables out of process iframes (OOPIFs).
class TaskManagerOOPIFBrowserTest : public TaskManagerBrowserTest,
                                    public testing::WithParamInterface<bool> {
 public:
  TaskManagerOOPIFBrowserTest() = default;
  TaskManagerOOPIFBrowserTest(const TaskManagerOOPIFBrowserTest&) = delete;
  TaskManagerOOPIFBrowserTest& operator=(const TaskManagerOOPIFBrowserTest&) =
      delete;

 protected:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    TaskManagerBrowserTest::SetUpCommandLine(command_line);
    if (GetParam())
      content::IsolateAllSitesForTesting(command_line);
  }

  bool ShouldExpectSubframes() {
    return content::AreAllSitesIsolatedForTesting();
  }
};

INSTANTIATE_TEST_SUITE_P(DefaultIsolation,
                         TaskManagerOOPIFBrowserTest,
                         ::testing::Values(false));
INSTANTIATE_TEST_SUITE_P(SitePerProcess,
                         TaskManagerOOPIFBrowserTest,
                         ::testing::Values(true));

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_ShutdownWhileOpen DISABLED_ShutdownWhileOpen
#else
#define MAYBE_ShutdownWhileOpen ShutdownWhileOpen
#endif

// Regression test for http://crbug.com/40847504
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_ShutdownWhileOpen) {
  ShowTaskManager();
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeTabContentsChanges) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));

  // Open a new tab and make sure the task manager notices it.
  ASSERT_TRUE(AddTabAtIndex(0, GetTestURL(), ui::PAGE_TRANSITION_TYPED));

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));

  // Close the tab and verify that we notice.
  browser()->tab_strip_model()->CloseWebContentsAt(0,
                                                   TabCloseTypes::CLOSE_NONE);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, KillTab) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));

  // Open a new tab and make sure the task manager notices it.
  ASSERT_TRUE(AddTabAtIndex(0, GetTestURL(), ui::PAGE_TRANSITION_TYPED));

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));

  // Killing the tab via task manager should remove the row.
  std::optional<size_t> tab = FindResourceIndex(MatchTab("title1.html"));
  ASSERT_TRUE(tab.has_value());
  ASSERT_TRUE(model()->GetTabId(tab.value()).is_valid());
  {
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    model()->Kill(tab.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  }

  // Tab should reappear in task manager upon reload.
  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
}

// Regression test for http://crbug.com/41149820.
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NavigateAwayFromHungRenderer) {
  ShowTaskManager();

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  GURL url1(embedded_test_server()->GetURL("/title2.html"));
  GURL url3(embedded_test_server()->GetURL("a.com", "/iframe.html"));

  // Open a new tab and make sure the task manager notices it.
  ASSERT_TRUE(AddTabAtIndex(0, url1, ui::PAGE_TRANSITION_TYPED));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  WebContents* tab1 = browser()->tab_strip_model()->GetActiveWebContents();

  // Initiate a navigation that will create a new WebContents in the same
  // SiteInstance. Then immediately hang the renderer so that title3.html can't
  // load in this process.
  content::WebContentsAddedObserver web_contents_added_observer;
  content::DOMMessageQueue message_queue;
  content::ExecuteScriptAsync(tab1->GetPrimaryMainFrame(),
                              "window.open('title3.html', '_blank');\n"
                              "window.domAutomationController.send(false);\n"
                              "while(1);");
  std::string message;
  EXPECT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("false", message);

  // Blocks until a new WebContents appears as a result of window.open().
  WebContents* tab2 = web_contents_added_observer.GetWebContents();

  // Make sure the new WebContents is in tab1's hung renderer process.
  ASSERT_NE(nullptr, tab2);
  ASSERT_NE(tab1, tab2);
  ASSERT_EQ(tab1->GetPrimaryMainFrame()->GetProcess(),
            tab2->GetPrimaryMainFrame()->GetProcess())
      << "New WebContents must be in the same process as the old WebContents, "
      << "so that the new tab doesn't finish loading (what this test is all "
      << "about)";
  ASSERT_EQ(tab1->GetSiteInstance(), tab2->GetSiteInstance())
      << "New WebContents must initially be in the same site instance as the "
      << "old WebContents";

  // Now navigate this tab to a different site. This should wind up in a
  // different renderer process, so it should complete and show up in the task
  // manager.
  tab2->OpenURL(content::OpenURLParams(url3, content::Referrer(),
                                       WindowOpenDisposition::CURRENT_TAB,
                                       ui::PAGE_TRANSITION_TYPED, false),
                /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("iframe test")));
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeExtensionTabChanges) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  ASSERT_TRUE(LoadExtension(
      test_data_dir_.AppendASCII("good").AppendASCII("Extensions")
                    .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
                    .AppendASCII("1.0.0.0")));

  // Browser, Extension background page, and the New Tab Page.
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchExtension("My extension 1")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  // Open a new tab to an extension URL. Afterwards, the third entry (background
  // page) should be an extension resource whose title starts with "Extension:".
  // The fourth entry (page.html) is also of type extension and has both a
  // WebContents and an extension. The title should start with "Extension:".
  GURL url("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html");
  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchExtension("Foobar")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchExtension("My extension 1")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  std::optional<size_t> extension_tab =
      FindResourceIndex(MatchExtension("Foobar"));
  ASSERT_TRUE(extension_tab.has_value());
  ASSERT_TRUE(model()->GetTabId(extension_tab.value()).is_valid());

  std::optional<size_t> background_page =
      FindResourceIndex(MatchExtension("My extension 1"));
  ASSERT_TRUE(background_page.has_value());
  ASSERT_FALSE(model()->GetTabId(background_page.value()).is_valid());
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeExtensionTab) {
  // With the task manager closed, open a new tab to an extension URL.
  // Afterwards, when we open the task manager, the third entry (background
  // page) should be an extension resource whose title starts with "Extension:".
  // The fourth entry (page.html) is also of type extension and has both a
  // WebContents and an extension. The title should start with "Extension:".
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("good")
                                .AppendASCII("Extensions")
                                .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
                                .AppendASCII("1.0.0.0")));
  GURL url("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html");
  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));

  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchExtension("Foobar")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchExtension("My extension 1")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  std::optional<size_t> extension_tab =
      FindResourceIndex(MatchExtension("Foobar"));
  ASSERT_TRUE(extension_tab.has_value());
  ASSERT_TRUE(model()->GetTabId(extension_tab.value()).is_valid());

  std::optional<size_t> background_page =
      FindResourceIndex(MatchExtension("My extension 1"));
  ASSERT_TRUE(background_page.has_value());
  ASSERT_FALSE(model()->GetTabId(background_page.value()).is_valid());
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeAppTabChanges) {
  ShowTaskManager();

  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("packaged_app")));
  const extensions::Extension* extension =
      extension_registry()->GetExtensionById(
          last_loaded_extension_id(), extensions::ExtensionRegistry::ENABLED);

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyApp()));

  // Open a new tab to the app's launch URL and make sure we notice that.
  GURL url(extension->GetResourceURL("main.html"));
  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));

  // There should be 1 "App: " tab and the original new tab page.
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchApp("Packaged App Test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyApp()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));

  // Check that the third entry (main.html) is of type extension and has both
  // a tab contents and an extension.
  std::optional<size_t> app_tab =
      FindResourceIndex(MatchApp("Packaged App Test"));
  ASSERT_TRUE(app_tab.has_value());
  ASSERT_TRUE(model()->GetTabId(app_tab.value()).is_valid());
  ASSERT_EQ(2, browser()->tab_strip_model()->count());

  // Unload extension to make sure the tab goes away.
  UnloadExtension(last_loaded_extension_id());

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyApp()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_EQ(1, browser()->tab_strip_model()->count());
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeAppTab) {
  ASSERT_TRUE(LoadExtension(
      test_data_dir_.AppendASCII("packaged_app")));
  const extensions::Extension* extension =
      extension_registry()->GetExtensionById(
          last_loaded_extension_id(), extensions::ExtensionRegistry::ENABLED);

  // Open a new tab to the app's launch URL and make sure we notice that.
  GURL url(extension->GetResourceURL("main.html"));
  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));

  ShowTaskManager();

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchApp("Packaged App Test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyApp()));

  // Check that the third entry (main.html) is of type extension and has both
  // a tab contents and an extension.
  std::optional<size_t> app_tab =
      FindResourceIndex(MatchApp("Packaged App Test"));
  ASSERT_TRUE(app_tab.has_value());
  ASSERT_TRUE(model()->GetTabId(app_tab.value()).is_valid());
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeHostedAppTabChanges) {
  ShowTaskManager();

  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL::Replacements replace_host;
  replace_host.SetHostStr("localhost");
  GURL base_url = embedded_test_server()->GetURL(
      "/extensions/api_test/app_process/");
  base_url = base_url.ReplaceComponents(replace_host);

  // Open a new tab to an app URL before the app is loaded.
  GURL url(base_url.Resolve("path1/empty.html"));
  NavigateToURLWithDisposition(browser(), url,
                               WindowOpenDisposition::NEW_FOREGROUND_TAB,
                               ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Check that the new entry's title starts with "Tab:".
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));

  // Load the hosted app and make sure it still starts with "Tab:",
  // since it hasn't changed to an app process yet.
  ASSERT_TRUE(LoadExtension(
      test_data_dir_.AppendASCII("api_test").AppendASCII("app_process")));
  // Force the TaskManager to query the title.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("Unmodified")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));

  // Now reload and check that the last entry's title now starts with "App:".
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  // Force the TaskManager to query the title.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyApp()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchApp("Unmodified")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));

  // Disable extension and reload.
  DisableExtension(last_loaded_extension_id());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  // The hosted app should now show up as a normal "Tab: ".
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("Unmodified")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyApp()));
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeHostedAppTabAfterReload) {
  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL base_url = embedded_test_server()->GetURL(
      "localhost", "/extensions/api_test/app_process/");

  // Open a new tab to an app URL before the app is loaded.
  GURL url(base_url.Resolve("path1/empty.html"));
  NavigateToURLWithDisposition(browser(), url,
                               WindowOpenDisposition::NEW_FOREGROUND_TAB,
                               ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Load the hosted app and make sure it still starts with "Tab:",
  // since it hasn't changed to an app process yet.
  ASSERT_TRUE(LoadExtension(
      test_data_dir_.AppendASCII("api_test").AppendASCII("app_process")));

  // Now reload, which should transition this tab to being an App.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  ShowTaskManager();

  // The TaskManager should show this as an "App: "
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyApp()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeHostedAppTabBeforeReload) {
  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL base_url = embedded_test_server()->GetURL(
      "localhost", "/extensions/api_test/app_process/");

  // Open a new tab to an app URL before the app is loaded.
  GURL url(base_url.Resolve("path1/empty.html"));
  NavigateToURLWithDisposition(browser(), url,
                               WindowOpenDisposition::NEW_FOREGROUND_TAB,
                               ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Load the hosted app and make sure it still starts with "Tab:",
  // since it hasn't changed to an app process yet.
  ASSERT_TRUE(LoadExtension(
      test_data_dir_.AppendASCII("api_test").AppendASCII("app_process")));

  ShowTaskManager();

  // The TaskManager should show this as a "Tab: " because the page hasn't been
  // reloaded since the hosted app was installed.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyApp()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
}

// Regression test for http://crbug.com/40970634.
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, ReloadExtension) {
  ShowTaskManager();
  ASSERT_TRUE(LoadExtension(
      test_data_dir_.AppendASCII("common").AppendASCII("background_page")));

  // Wait until we see the loaded extension in the task manager (the three
  // resources are: the browser process, New Tab Page, and the extension).
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchExtension("background_page")));

  // Reload the extension a few times and make sure our resource count doesn't
  // increase.
  std::string extension_id = last_loaded_extension_id();
  for (int i = 1; i <= 5; i++) {
    SCOPED_TRACE(testing::Message() << "Reloading extension for the " << i
                                    << "th time.");
    ReloadExtension(extension_id);
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchExtension("background_page")));
  }
}

// Checks that task manager counts a worker thread JS heap size.
// http://crbug.com/41012512
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, WebWorkerJSHeapMemory) {
  // Workers require a trustworthy (e.g. https) context.
  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server.Start());
  GURL test_url = https_server.GetURL("/title1.html");

  ShowTaskManager();
  model()->ToggleColumnVisibility(ColumnSpecifier::V8_MEMORY);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));
  size_t minimal_heap_size = 4 * 1024 * 1024 * sizeof(void*);
  std::string test_js = base::StringPrintf(
      "var blob = new Blob([\n"
      "    'mem = new Array(%lu);',\n"
      "    'for (var i = 0; i < mem.length; i += 16)',"
      "    '  mem[i] = i;',\n"
      "    'postMessage(\"okay\");']);\n"
      "blobURL = window.URL.createObjectURL(blob);\n"
      "var worker = new Worker(blobURL);\n"
      "new Promise(resolve => {\n"
      "  worker.addEventListener('message', function(e) {\n"
      "    resolve(e.data);\n"  // e.data == "okay"
      "  });\n"
      "  worker.postMessage('go');\n"
      "});\n",
      static_cast<unsigned long>(minimal_heap_size));
  ASSERT_EQ("okay",
            content::EvalJs(
                browser()->tab_strip_model()->GetActiveWebContents(), test_js));

  // The worker has allocated objects of at least |minimal_heap_size| bytes.
  // Wait for the heap stats to reflect this.
  const char kTabWildcard[] = "127.0.0.1:*/title1.html";
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab(kTabWildcard), ColumnSpecifier::V8_MEMORY, minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab(kTabWildcard), ColumnSpecifier::V8_MEMORY_USED,
      minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab(kTabWildcard), ColumnSpecifier::MEMORY_FOOTPRINT,
      minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab(kTabWildcard)));
}

// Checks that task manager counts renderer JS heap size.
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, JSHeapMemory) {
  ShowTaskManager();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestURL()));
  size_t minimal_heap_size = 4 * 1024 * 1024 * sizeof(void*);
  std::string test_js = base::StringPrintf(
      "mem = new Array(%lu);\n"
      "for (var i = 0; i < mem.length; i += 16)\n"
      "  mem[i] = i;\n"
      "\"okay\";\n",
      static_cast<unsigned long>(minimal_heap_size));
  std::string ok;
  ASSERT_EQ("okay",
            content::EvalJs(
                browser()->tab_strip_model()->GetActiveWebContents(), test_js));

  model()->ToggleColumnVisibility(ColumnSpecifier::V8_MEMORY);

  // The page's js has allocated objects of at least |minimal_heap_size| bytes.
  // Wait for the heap stats to reflect this.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("title1.html"), ColumnSpecifier::V8_MEMORY, minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("title1.html"), ColumnSpecifier::V8_MEMORY_USED,
      minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
}

#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// This tests times out when MSan is enabled. See https://crbug.com/41417851.
// Failing on Linux CFI. See https://crbug.com/41476833.
#define MAYBE_SentDataObserved DISABLED_SentDataObserved
#else
#define MAYBE_SentDataObserved SentDataObserved
#endif
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_SentDataObserved) {
  // TODO(crbug.com/397484647): Migrate TaskManagerDesktopRefreshBrowserTest
  // version of this test into this one.
  ShowTaskManager();
  GURL test_gurl = embedded_test_server()->GetURL("/title1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_gurl));
  std::string test_js = R"(
      document.title = 'network use';
      var mem = new Uint8Array(16 << 20);
      for (var i = 0; i < mem.length; i += 16) {
        mem[i] = i;
      }
      var formData = new FormData();
      formData.append('StringKey1', new Blob([mem]));
      var request =
          new Request(location.href, {method: 'POST', body: formData});
      fetch(request).then(response => response.text());
      )";

  browser()
      ->tab_strip_model()
      ->GetActiveWebContents()
      ->GetPrimaryMainFrame()
      ->ExecuteJavaScriptForTests(base::UTF8ToUTF16(test_js),
                                  base::NullCallback(),
                                  content::ISOLATED_WORLD_ID_GLOBAL);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("network use"), ColumnSpecifier::TOTAL_NETWORK_USE, 16000000));
  // There shouldn't be too much usage on the browser process. Note that it
  // should be the first row since tasks are sorted by process ID then by task
  // ID.
  model()->UpdateModel(DisplayCategory::kSystem, u"");
  EXPECT_GE(20000,
            model()->GetColumnValue(ColumnSpecifier::TOTAL_NETWORK_USE, 0));
}

#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// This tests times out when MSan is enabled. See https://crbug.com/41417851.
// Failing on Linux CFI. See https://crbug.com/41476833.
#define MAYBE_TotalSentDataObserved DISABLED_TotalSentDataObserved
#else
#define MAYBE_TotalSentDataObserved TotalSentDataObserved
#endif
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_TotalSentDataObserved) {
  // TODO(crbug.com/397484647): Migrate TaskManagerDesktopRefreshBrowserTest
  // version of this test into this one.
  ShowTaskManager();
  GURL test_gurl = embedded_test_server()->GetURL("/title1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_gurl));
  std::string test_js = R"(
      document.title = 'network use';
      var mem = new Uint8Array(16 << 20);
      for (var i = 0; i < mem.length; i += 16) {
        mem[i] = i;
      }
      var formData = new FormData();
      formData.append('StringKey1', new Blob([mem]));
      var request =
          new Request(location.href, {method: 'POST', body: formData});
      fetch(request).then(response => response.text());
      )";

  browser()
      ->tab_strip_model()
      ->GetActiveWebContents()
      ->GetPrimaryMainFrame()
      ->ExecuteJavaScriptForTests(base::UTF8ToUTF16(test_js),
                                  base::NullCallback(),
                                  content::ISOLATED_WORLD_ID_GLOBAL);

  // This test uses |setTimeout| to exceed the Nyquist ratio to ensure that at
  // least 1 refresh has happened of no traffic.
  test_js = R"(
      var request =
          new Request(location.href, {method: 'POST', body: formData});
      setTimeout(
          () => {fetch(request).then(response => response.text())}, 2000);
      )";

  browser()
      ->tab_strip_model()
      ->GetActiveWebContents()
      ->GetPrimaryMainFrame()
      ->ExecuteJavaScriptForTests(base::UTF8ToUTF16(test_js),
                                  base::NullCallback(),
                                  content::ISOLATED_WORLD_ID_GLOBAL);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("network use"), ColumnSpecifier::TOTAL_NETWORK_USE,
      16000000 * 2));
  // There shouldn't be too much usage on the browser process. Note that it
  // should be the first row since tasks are sorted by process ID then by task
  // ID.
  model()->UpdateModel(DisplayCategory::kSystem, u"");
  EXPECT_GE(20000,
            model()->GetColumnValue(ColumnSpecifier::TOTAL_NETWORK_USE, 0));
}

// Checks that task manager counts idle wakeups. Since this test relies on
// forcing actual system-level idle wakeups to happen, it is inherently
// dependent on the load of the rest of the system, details of the OS scheduler,
// and so on, which makes it very prone to flakes.
#if BUILDFLAG(IS_MAC)
// This test is too flaky to be useable on Mac, because of the reasons given
// above.
#define MAYBE_IdleWakeups DISABLED_IdleWakeups
#else
#define MAYBE_IdleWakeups IdleWakeups
#endif
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, MAYBE_IdleWakeups) {
  ShowTaskManager();
  model()->ToggleColumnVisibility(ColumnSpecifier::IDLE_WAKEUPS);

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

  std::string test_js =
      "function myWait() {\n"
      "  setTimeout(function() { myWait(); }, 1)\n"
      "}\n"
      "myWait();\n"
      "\"okay\";\n";

  ASSERT_EQ("okay",
            content::EvalJs(
                browser()->tab_strip_model()->GetActiveWebContents(), test_js));

  // The script above should trigger a lot of idle wakeups - up to 1000 per
  // second. Let's make sure we get at least 100 (in case the test runs slow).
  const int kMinExpectedWakeCount = 100;
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("title1.html"), ColumnSpecifier::IDLE_WAKEUPS,
      kMinExpectedWakeCount));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
}

// Crashes on multiple builders.  http://crbug.com/40107830
// Checks that task manager counts utility process JS heap size.
IN_PROC_BROWSER_TEST_F(TaskManagerUtilityProcessBrowserTest,
                       DISABLED_UtilityJSHeapMemory) {
  ShowTaskManager();
  model()->ToggleColumnVisibility(ColumnSpecifier::V8_MEMORY);

  auto proxy_resolver_name =
      l10n_util::GetStringUTF16(IDS_PROXY_RESOLVER_DISPLAY_NAME);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestURL()));
  // The PAC script is trivial, so don't expect a large heap.
  size_t minimal_heap_size = 1024;
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchUtility(proxy_resolver_name), ColumnSpecifier::V8_MEMORY,
      minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchUtility(proxy_resolver_name), ColumnSpecifier::V8_MEMORY_USED,
      minimal_heap_size));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchUtility(proxy_resolver_name)));
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DevToolsNewDockedWindow) {
  ShowTaskManager();  // Task manager shown BEFORE dev tools window.

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  DevToolsWindow* devtools =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DevToolsNewUndockedWindow) {
  ShowTaskManager();  // Task manager shown BEFORE dev tools window.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  DevToolsWindow* devtools =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnyTab()));
  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DevToolsOldDockedWindow) {
  DevToolsWindow* devtools =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
  ShowTaskManager();  // Task manager shown AFTER dev tools window.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DevToolsOldUndockedWindow) {
  DevToolsWindow* devtools =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
  ShowTaskManager();  // Task manager shown AFTER dev tools window.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnyTab()));
  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
}

IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, HistoryNavigationInNewTab) {
  ShowTaskManager();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestURL()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL("chrome://version/")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("About Version")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  chrome::GoBack(browser(), WindowOpenDisposition::NEW_BACKGROUND_TAB);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("About Version")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));

  // In http://crbug.com/40088230, the task_manager::Task for the background tab
  // was created with process id 0, resulting in zero values for all process
  // metrics. Ensure that this is not the case.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("title1.html"), ColumnSpecifier::PROCESS_ID,
      base::kNullProcessId));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("title1.html"), ColumnSpecifier::MEMORY_FOOTPRINT, 1000));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchTab("About Version"), ColumnSpecifier::MEMORY_FOOTPRINT, 1000));
}

IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest, SubframeHistoryNavigation) {
  if (!ShouldExpectSubframes())
    return;  // This test is lame without OOPIFs.

  ShowTaskManager();

  // This URL will have two out-of-process iframe processes (for b.com and
  // c.com) under --site-per-process: it's an a.com page containing a b.com
  // <iframe> containing a b.com <iframe> containing a c.com <iframe>.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL(
                     "a.com", "/cross_site_iframe_factory.html?a(b(b(c)))")));

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Cross-site iframe factory")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));

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

  // Simulate a user gesture on the frame about to be navigated so that the
  // corresponding navigation entry is not marked as skippable.
  content::RenderFrameHost* child_frame =
      ChildFrameAt(tab->GetPrimaryMainFrame(), 0);
  content::RenderFrameHost* grandchild_frame = ChildFrameAt(child_frame, 0);
  grandchild_frame->ExecuteJavaScriptWithUserGestureForTests(
      u"a=5", base::NullCallback(), content::ISOLATED_WORLD_ID_GLOBAL);

  GURL d_url = embedded_test_server()->GetURL(
      "d.com", "/cross_site_iframe_factory.html?d(e)");
  ASSERT_TRUE(
      content::ExecJs(tab->GetPrimaryMainFrame(),
                      "frames[0][0].location.href = '" + d_url.spec() + "';"));

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(0, MatchSubframe("http://c.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://d.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://e.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnySubframe()));

  ASSERT_TRUE(chrome::CanGoBack(browser()));
  chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(0, MatchSubframe("http://d.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(0, MatchSubframe("http://e.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));

  ASSERT_TRUE(chrome::CanGoForward(browser()));
  chrome::GoForward(browser(), WindowOpenDisposition::CURRENT_TAB);

  // When the subframe appears in the cloned process, it must have a valid
  // process ID.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchSubframe("http://d.com/"), ColumnSpecifier::PROCESS_ID,
      base::kNullProcessId));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchSubframe("http://e.com/"), ColumnSpecifier::PROCESS_ID,
      base::kNullProcessId));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(0, MatchSubframe("http://c.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://d.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://e.com/")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnySubframe()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  // Subframe processes should report some amount of physical memory usage.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchSubframe("http://d.com/"), ColumnSpecifier::MEMORY_FOOTPRINT, 1000));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerStatToExceed(
      MatchSubframe("http://e.com/"), ColumnSpecifier::MEMORY_FOOTPRINT, 1000));
}

IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest, KillSubframe) {
  ShowTaskManager();

  // Navigate to a page A(B,C).
  content::TestNavigationObserver navigation_observer(
      browser()->tab_strip_model()->GetActiveWebContents());
  GURL main_url(embedded_test_server()->GetURL(
      "/cross-site/a.com/iframe_cross_site.html"));
  int expected_c_subframes = 1;
  if (!content::AreAllSitesIsolatedForTesting()) {
    // Isolate b.com so that it will be forced into a separate process. This
    // will prevent the main frame and c.com subframe from being placed in the
    // the process that gets killed by this test.
    content::IsolateOriginsForTesting(
        embedded_test_server(),
        browser()->tab_strip_model()->GetActiveWebContents(), {"b.com"});

    // Do not expect to see subframe information for c.com. This is because
    // c.com will not require a dedicated process and will be placed in the same
    // process as the main frame (a.com).
    expected_c_subframes = 0;
  }

  auto check_num_subframes = [](int expected_b_subframes,
                                int expected_c_subframes) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
        expected_b_subframes, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
        expected_c_subframes, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
        expected_b_subframes + expected_c_subframes, MatchAnySubframe()));
  };
  browser()->OpenURL(content::OpenURLParams(main_url, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  // Verify the expected number of b.com and c.com subframes.
  ASSERT_NO_FATAL_FAILURE(check_num_subframes(1, expected_c_subframes));

  // Remember |b_url| to be able to later renavigate to the same URL without
  // doing any process swaps (we want to avoid redirects that would happen
  // when going through /cross-site/foo.com/..., because
  // https://crbug.com/40483075 wouldn't repro in presence of process swaps).
  navigation_observer.Wait();
  auto* b_frame =
      ChildFrameAt(browser()->tab_strip_model()->GetActiveWebContents(), 0);
  GURL b_url = b_frame->GetLastCommittedURL();
  ASSERT_EQ(b_url.GetHost(), "b.com");  // Sanity check of test code / setup.
  ASSERT_TRUE(b_frame->GetSiteInstance()->RequiresDedicatedProcess());
  {
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> subframe_b =
        FindResourceIndex(MatchSubframe("http://b.com/"));
    ASSERT_TRUE(subframe_b.has_value());
    ASSERT_TRUE(model()->GetTabId(subframe_b.value()).is_valid());
    model()->Kill(subframe_b.value());

    // Verify the expected number of b.com and c.com subframes.
    ASSERT_NO_FATAL_FAILURE(check_num_subframes(0, expected_c_subframes));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
  }

  HideTaskManager();
  ShowTaskManager();

  // Verify the expected number of b.com and c.com subframes.
  ASSERT_NO_FATAL_FAILURE(check_num_subframes(0, expected_c_subframes));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));

  // Reload the subframe and verify it has re-appeared in the task manager.
  // This is a regression test for https://crbug.com/40483075.
  ASSERT_TRUE(content::ExecJs(
      browser()
          ->tab_strip_model()
          ->GetActiveWebContents()
          ->GetPrimaryMainFrame(),
      "document.getElementById('frame1').src = '" + b_url.spec() + "';"));

  // Verify the expected number of b.com and c.com subframes.
  ASSERT_NO_FATAL_FAILURE(check_num_subframes(1, expected_c_subframes));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
}

// Tests what happens when a tab navigates to a site (a.com) that it previously
// has a cross-process subframe into (b.com).
IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest, NavigateToSubframeProcess) {
  ShowTaskManager();

  // Navigate the tab to a page on a.com with cross-process subframes to
  // b.com and c.com.
  GURL a_dotcom(embedded_test_server()->GetURL(
      "/cross-site/a.com/iframe_cross_site.html"));
  browser()->OpenURL(content::OpenURLParams(a_dotcom, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));
  }

  // Now navigate to a page on b.com with a simple (same-site) iframe.
  // This should not show any subframe resources in the task manager.
  GURL b_dotcom(
      embedded_test_server()->GetURL("/cross-site/b.com/iframe.html"));

  browser()->OpenURL(content::OpenURLParams(b_dotcom, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  HideTaskManager();
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
}

IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest,
                       NavigateToSiteWithSubframeToOriginalSite) {
  ShowTaskManager();

  // Navigate to a page on b.com with a simple (same-site) iframe.
  // This should not show any subframe resources in the task manager.
  GURL b_dotcom(
      embedded_test_server()->GetURL("/cross-site/b.com/iframe.html"));

  browser()->OpenURL(content::OpenURLParams(b_dotcom, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));

  // Now navigate the tab to a page on a.com with cross-process subframes to
  // b.com and c.com.
  GURL a_dotcom(embedded_test_server()->GetURL(
      "/cross-site/a.com/iframe_cross_site.html"));
  browser()->OpenURL(content::OpenURLParams(a_dotcom, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));
  }

  HideTaskManager();
  ShowTaskManager();

  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));
  }
}

// Tests what happens when a tab navigates a cross-frame iframe (to b.com)
// back to the site of the parent document (a.com).
IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest,
                       CrossSiteIframeBecomesSameSite) {
  ShowTaskManager();

  // Navigate the tab to a page on a.com with cross-process subframes to
  // b.com and c.com.
  content::TestNavigationObserver navigation_observer(
      browser()->tab_strip_model()->GetActiveWebContents());
  GURL a_dotcom(embedded_test_server()->GetURL(
      "/cross-site/a.com/iframe_cross_site.html"));
  browser()->OpenURL(content::OpenURLParams(a_dotcom, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));
  }

  // Navigate the b.com frame back to a.com. It is no longer a cross-site iframe
  navigation_observer.Wait();
  const std::string r_script =
      R"( document.getElementById('frame1').src='/title1.html';
          document.title='aac'; )";
  ASSERT_TRUE(content::ExecJs(browser()
                                  ->tab_strip_model()
                                  ->GetActiveWebContents()
                                  ->GetPrimaryMainFrame(),
                              r_script));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("aac")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(0, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnySubframe()));
  }
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("aac")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  HideTaskManager();
  ShowTaskManager();

  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(0, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnySubframe()));
  }
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("aac")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
}

IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest,
                       LeavePageWithCrossSiteIframes) {
  ShowTaskManager();

  // Navigate the tab to a page on a.com with cross-process subframes.
  GURL a_dotcom_with_iframes(embedded_test_server()->GetURL(
      "/cross-site/a.com/iframe_cross_site.html"));
  browser()->OpenURL(
      content::OpenURLParams(a_dotcom_with_iframes, content::Referrer(),
                             WindowOpenDisposition::CURRENT_TAB,
                             ui::PAGE_TRANSITION_TYPED, false),
      /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("cross-site iframe test")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  if (!ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnySubframe()));
  }

  // Navigate the tab to a page on a.com without cross-process subframes, and
  // the subframe processes should disappear.
  GURL a_dotcom_simple(
      embedded_test_server()->GetURL("/cross-site/a.com/title2.html"));
  browser()->OpenURL(
      content::OpenURLParams(a_dotcom_simple, content::Referrer(),
                             WindowOpenDisposition::CURRENT_TAB,
                             ui::PAGE_TRANSITION_TYPED, false),
      /*navigation_handle_callback=*/{});
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));

  HideTaskManager();
  ShowTaskManager();

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
}

// TODO(crbug.com/40710551): disabled as test is flaky.
IN_PROC_BROWSER_TEST_P(TaskManagerOOPIFBrowserTest,
                       DISABLED_OrderingOfDependentRows) {
  ShowTaskManager();

  GURL a_with_frames(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b,b,c(d,a,b,c))"));
  browser()->OpenURL(content::OpenURLParams(a_with_frames, content::Referrer(),
                                            WindowOpenDisposition::CURRENT_TAB,
                                            ui::PAGE_TRANSITION_TYPED, false),
                     /*navigation_handle_callback=*/{});

  if (ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://d.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(3, MatchAnySubframe()));
  }
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Cross-site iframe factory")));

  std::optional<size_t> index =
      FindResourceIndex(MatchTab("Cross-site iframe factory"));
  ASSERT_TRUE(index.has_value());
  std::vector<size_t> subframe_offsets;
  if (ShouldExpectSubframes()) {
    std::optional<size_t> index_b =
        FindResourceIndex(MatchSubframe("http://b.com/"));
    ASSERT_TRUE(index_b.has_value());
    std::optional<size_t> index_c =
        FindResourceIndex(MatchSubframe("http://c.com/"));
    ASSERT_TRUE(index_c.has_value());
    std::optional<size_t> index_d =
        FindResourceIndex(MatchSubframe("http://d.com/"));
    ASSERT_TRUE(index_d.has_value());
    subframe_offsets = {index_b.value() - index.value(),
                        index_c.value() - index.value(),
                        index_d.value() - index.value()};
    EXPECT_THAT(subframe_offsets, testing::UnorderedElementsAre(1u, 2u, 3u));
  }

  // Opening a new tab should appear below the existing tab.
  GURL other_tab_url(embedded_test_server()->GetURL(
      "d.com", "/cross_site_iframe_factory.html?d(a(c(b)))"));
  browser()->OpenURL(
      content::OpenURLParams(other_tab_url, content::Referrer(),
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
                             ui::PAGE_TRANSITION_TYPED, false),
      /*navigation_handle_callback=*/{});

  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(2, MatchTab("Cross-site iframe factory")));
  if (ShouldExpectSubframes()) {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(6, MatchAnySubframe()));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(2, MatchSubframe("http://b.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(2, MatchSubframe("http://c.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://d.com/")));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchSubframe("http://a.com/")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(6, MatchAnySubframe()));
  } else {
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
  }

  // The first tab may have moved in absolute position in the list (due to
  // random e.g. zygote or gpu activity).
  index = FindResourceIndex(MatchTab("Cross-site iframe factory"));
  ASSERT_TRUE(index.has_value());

  // All of Tab 2's subframes will reuse Tab 1's existing processes for
  // corresponding sites.  Tab 2's d.com main frame row should then appear
  // after all the subframe processes.
  size_t tab2_subframe_count = ShouldExpectSubframes() ? 3 : 0;
  size_t tab2_main_frame_index =
      index.value() + subframe_offsets.size() + tab2_subframe_count + 1;
  EXPECT_EQ("Tab: Cross-site iframe factory",
            base::UTF16ToUTF8(model()->GetRowTitle(tab2_main_frame_index)));

  if (ShouldExpectSubframes()) {
    // Tab 2's a.com subframe should share Tab 1's main frame process and go
    // directly below it.
    EXPECT_EQ(index.value() + 1,
              FindResourceIndex(MatchSubframe("http://a.com/")));

    // The other tab 2 subframes (b.com and c.com) should join existing
    // subframe processes from tab 1.  Check that the b.com and c.com subframe
    // processes now have two rows each.
    std::optional<size_t> subframe_b_index =
        FindResourceIndex(MatchSubframe("http://b.com/"));
    ASSERT_TRUE(subframe_b_index.has_value());
    std::optional<size_t> subframe_c_index =
        FindResourceIndex(MatchSubframe("http://c.com/"));
    ASSERT_TRUE(subframe_c_index.has_value());
    std::optional<size_t> subframe_d_index =
        FindResourceIndex(MatchSubframe("http://d.com/"));
    ASSERT_TRUE(subframe_d_index.has_value());
    EXPECT_EQ(
        "Subframe: http://b.com/",
        base::UTF16ToUTF8(model()->GetRowTitle(subframe_b_index.value() + 1)));
    EXPECT_EQ(
        "Subframe: http://c.com/",
        base::UTF16ToUTF8(model()->GetRowTitle(subframe_c_index.value() + 1)));

    // The subframe processes should preserve their relative ordering.
    EXPECT_EQ(subframe_offsets[0] < subframe_offsets[1],
              subframe_b_index < subframe_c_index);
    EXPECT_EQ(subframe_offsets[1] < subframe_offsets[2],
              subframe_c_index < subframe_d_index);
    EXPECT_EQ(subframe_offsets[0] < subframe_offsets[2],
              subframe_b_index < subframe_d_index);
  }
}

//==============================================================================
// Prerender tasks test.
namespace {
// Prerender trigger page URL.
const char kMainPageUrl[] = "/title2.html";
// The prerendered URL.
const char kPrerenderURL[] = "/title1.html";

class AutocompleteActionPredictorObserverImpl
    : public predictors::AutocompleteActionPredictor::Observer {
 public:
  explicit AutocompleteActionPredictorObserverImpl(
      predictors::AutocompleteActionPredictor* predictor) {
    observation_.Observe(predictor);
  }

  ~AutocompleteActionPredictorObserverImpl() override = default;

  void WaitForInitialization() {
    base::RunLoop loop;
    waiting_ = loop.QuitClosure();
    loop.Run();
  }

  // predictors::AutocompleteActionPredictor::Observer:
  void OnInitialized() override {
    DCHECK(waiting_);
    std::move(waiting_).Run();
  }

  base::ScopedObservation<predictors::AutocompleteActionPredictor,
                          predictors::AutocompleteActionPredictor::Observer>
      observation_{this};
  base::OnceClosure waiting_;
};

class PrerenderTaskBrowserTest : public TaskManagerBrowserTest {
 public:
  PrerenderTaskBrowserTest() {
    // `blink::features::kPrerender2` and
    // `blink::features::kPrerender2MemoryControls` are enabled in
    // |prerender_helper_|.
    prerender_helper_ = std::make_unique<content::test::PrerenderTestHelper>(
        base::BindRepeating(&PrerenderTaskBrowserTest::GetActiveWebContents,
                            base::Unretained(this)));
    feature_list_.InitWithFeaturesAndParameters(
        content::GetDefaultEnabledBackForwardCacheFeaturesForTesting(
            /*ignore_outstanding_network_request=*/false),
        /*disabled_features=*/{});
    EXPECT_TRUE(content::BackForwardCache::IsBackForwardCacheFeatureEnabled());
  }
  PrerenderTaskBrowserTest(const PrerenderTaskBrowserTest&) = delete;
  PrerenderTaskBrowserTest& operator=(const PrerenderTaskBrowserTest&) = delete;
  ~PrerenderTaskBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    content::IsolateAllSitesForTesting(command_line);
    ASSERT_TRUE(content::AreAllSitesIsolatedForTesting());
    TaskManagerBrowserTest::SetUpCommandLine(command_line);
  }

  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    embedded_test_server()->ServeFilesFromDirectory(
        base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
    embedded_test_server()->StartAcceptingConnections();
  }

  void NavigateTo(std::string_view page_url) const {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), embedded_test_server()->GetURL(page_url)));
  }

  WebContents* GetActiveWebContents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }

  predictors::AutocompleteActionPredictor* GetAutocompleteActionPredictor() {
    return predictors::AutocompleteActionPredictorFactory::GetForProfile(
        browser()->GetProfile());
  }

  void WaitForAutocompleteActionPredictorInitialization() {
    if (GetAutocompleteActionPredictor()->initialized()) {
      return;
    }
    AutocompleteActionPredictorObserverImpl predictor_observer(
        GetAutocompleteActionPredictor());
    predictor_observer.WaitForInitialization();
  }

  WebContents* NavigateToURLWithDispositionAndTransition(
      const GURL& url,
      WindowOpenDisposition disposition,
      ui::PageTransition transition) {
    return GetActiveWebContents()->OpenURL(
        content::OpenURLParams(url, content::Referrer(), disposition,
                               transition,
                               /*is_renderer_initiated=*/false),
        /*navigation_handle_callback=*/{});
  }

  content::test::PrerenderTestHelper* prerender_helper() {
    return prerender_helper_.get();
  }

  // Prerender's task title is constructed from |RFH->GetLastCommittedURL|,
  // which contains the port of the testing webserver.
  std::string port() const {
    return base::NumberToString(embedded_test_server()->port());
  }

 private:
  std::unique_ptr<content::test::PrerenderTestHelper> prerender_helper_;
  base::test::ScopedFeatureList feature_list_;
};

}  // namespace

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ProperlyShowsTasks DISABLED_ProperlyShowsTasks
#else
#define MAYBE_ProperlyShowsTasks ProperlyShowsTasks
#endif
// Tests that the task manager properly:
// 1. shows the Prerender entry when the speculation rule is injected;
// 2. shows the Prerender entry when the manager is closed and reopened.
// 3. deletes the Prerender entry when the prerendered page is activated.
IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest, MAYBE_ProperlyShowsTasks) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(kMainPageUrl);

  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
  std::string server_port;
  if (prerender_gurl.has_port()) {
    server_port = prerender_gurl.GetPort();
  }

  // Inject the speculation rule and wait for prerender to complete.
  prerender_helper()->AddPrerender(prerender_gurl);

  // Must have one tab task, one prerender task.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));

  // "Close" the task manager and "reopen" it. We should see the same tasks.
  HideTaskManager();
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));

  // Activate the prerender page. The triggering page is placed in BFCache,
  // and the prerendered page is activated.
  content::test::PrerenderHostObserver obs(*GetActiveWebContents(),
                                           prerender_gurl);
  content::test::PrerenderTestHelper::NavigatePrimaryPage(
      *GetActiveWebContents(), prerender_gurl);
  ASSERT_TRUE(obs.was_activated());

  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyBFCache()));
  // Take out the "http://".
  const auto tab_title =
      url_formatter::FormatUrl(embedded_test_server()->GetURL(kPrerenderURL));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab(base::UTF16ToUTF8(tab_title))));
  if (content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault(
          browser()->GetProfile()) &&
      !server_port.empty()) {
    // When kOriginKeyedProcessesByDefault is enabled, we need to include the
    // port number as the SiteInstance's site_url will include it.
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
        1, MatchBFCache("http://127.0.0.1:" + server_port + "/")));
  } else {
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchBFCache("http://127.0.0.1/")));
  }
}

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_DeletesTaskAfterPrerenderKilled \
  DISABLED_DeletesTaskAfterPrerenderKilled
#else
#define MAYBE_DeletesTaskAfterPrerenderKilled DeletesTaskAfterPrerenderKilled
#endif
// Tests that the task manager properly deletes the prerender task once the
// prerender is cancelled.
IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
                       MAYBE_DeletesTaskAfterPrerenderKilled) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(kMainPageUrl);

  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
  prerender_helper()->AddPrerender(prerender_gurl);

  // Must have one tab task, one prerender task.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));

  // Terminate the prerender task, which should signal the task manager to
  // remove the prerender task entry.
  {
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> prerender_row =
        FindResourceIndex(MatchPrerender(prerender_gurl.spec()));
    ASSERT_TRUE(prerender_row.has_value());
    ASSERT_TRUE(model()->GetTabId(prerender_row.value()).is_valid());
    model()->Kill(prerender_row.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  }
}

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_DeletesTaskAfterTriggerPageKilled \
  DISABLED_DeletesTaskAfterTriggerPageKilled
#else
#define MAYBE_DeletesTaskAfterTriggerPageKilled \
  DeletesTaskAfterTriggerPageKilled
#endif
// Tests that the task manager properly deletes the task of the trigger tab and
// prerender when the trigger is terminated.
IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
                       MAYBE_DeletesTaskAfterTriggerPageKilled) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(kMainPageUrl);

  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
  prerender_helper()->AddPrerender(prerender_gurl);

  // Must have one tab task, one prerender task.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));

  // Terminate the prerender task, which should signal the task manager to
  // remove the prerender task entry.
  {
    base::HistogramTester histogram_tester;
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> trigger_row =
        FindResourceIndex(MatchTab("Title Of Awesomeness"));
    ASSERT_TRUE(trigger_row.has_value());
    ASSERT_TRUE(model()->GetTabId(trigger_row.value()).is_valid());
    model()->Kill(trigger_row.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
    histogram_tester.ExpectUniqueSample(
        "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule",
        /*PrerenderFinalStatus::kPrimaryMainFrameRendererProcessKilled=*/57, 1);
  }
}

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ProperlyShowsPrerenderTaskByAutocompletePredictor \
  DISABLED_ProperlyShowsPrerenderTaskByAutocompletePredictor
#else
#define MAYBE_ProperlyShowsPrerenderTaskByAutocompletePredictor \
  ProperlyShowsPrerenderTaskByAutocompletePredictor
#endif
// Test that the autocomplete action predictor trigger Prerender tasks are
// properly displayed. Such predictor is used to trigger Omnibox Prerender.
IN_PROC_BROWSER_TEST_F(
    PrerenderTaskBrowserTest,
    MAYBE_ProperlyShowsPrerenderTaskByAutocompletePredictor) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(kMainPageUrl);

  ASSERT_TRUE(GetAutocompleteActionPredictor());
  WaitForAutocompleteActionPredictorInitialization();
  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
  GetAutocompleteActionPredictor()->StartPrerendering(
      prerender_gurl, *(browser()->tab_strip_model()->GetActiveWebContents()));

  // One task for main page and one for the prerendered page.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
  // Main task stays after prerendered task is terminated.
  {
    base::HistogramTester histogram_tester;
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> prerender_row =
        FindResourceIndex(MatchPrerender(prerender_gurl.spec()));
    ASSERT_TRUE(prerender_row.has_value());
    ASSERT_TRUE(model()->GetTabId(prerender_row.value()).is_valid());
    model()->Kill(prerender_row.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
    histogram_tester.ExpectUniqueSample(
        "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_"
        "DirectURLInput",
        /*PrerenderFinalStatus::kRendererProcessKilled=*/14, 1);
  }
  // Both tasks are deleted after main task is terminated.
  {
    // Use a different URL because re-using the same URL does not trigger new
    // prerendering:
    // https://crsrc.org/c/chrome/browser/predictors/autocomplete_action_predictor.cc;l=208;drc=a08a4e1c3f6862b3b1385b8a040a4fdb524e509d
    base::HistogramTester histogram_tester;
    const char kNewPrerenderURL[] = "/title3.html";
    const auto new_prerender_gurl =
        embedded_test_server()->GetURL(kNewPrerenderURL);
    GetAutocompleteActionPredictor()->StartPrerendering(
        embedded_test_server()->GetURL(kNewPrerenderURL),
        *GetActiveWebContents());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchPrerender(new_prerender_gurl.spec())));

    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> trigger_row =
        FindResourceIndex(MatchTab("Title Of Awesomeness"));
    ASSERT_TRUE(trigger_row.has_value());
    ASSERT_TRUE(model()->GetTabId(trigger_row.value()).is_valid());
    model()->Kill(trigger_row.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
    histogram_tester.ExpectUniqueSample(
        "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_"
        "DirectURLInput",
        /*PrerenderFinalStatus::kPrimaryMainFrameRendererProcessKilled=*/57, 1);
  }
}

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_OmniboxPrerenderActivationClearsTask \
  DISABLED_OmniboxPrerenderActivationClearsTask
#else
#define MAYBE_OmniboxPrerenderActivationClearsTask \
  OmniboxPrerenderActivationClearsTask
#endif
// Test that the Omnibox-triggered prerender activation clears the prerender
// entry in the task manager.
IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
                       MAYBE_OmniboxPrerenderActivationClearsTask) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(kMainPageUrl);

  ASSERT_TRUE(GetAutocompleteActionPredictor());
  WaitForAutocompleteActionPredictorInitialization();
  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
  GetAutocompleteActionPredictor()->StartPrerendering(prerender_gurl,
                                                      *GetActiveWebContents());

  // One task for main page and one for the prerendered page.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));

  // Activate the Omnibox prerender, after which the prerender task should
  // disappear.
  content::test::PrerenderHostObserver obs(*GetActiveWebContents(),
                                           prerender_gurl);
  // |ui::PAGE_TRANSITION_FROM_ADDRESS_BAR| augmentation is required for omnibox
  // activation.
  auto* web_contents = NavigateToURLWithDispositionAndTransition(
      prerender_gurl, WindowOpenDisposition::CURRENT_TAB,
      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
  obs.WaitForActivation();
  ASSERT_TRUE(obs.was_activated());
  ASSERT_EQ(web_contents, GetActiveWebContents());  // Current tab.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
  // Take out the "http://".
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1,
      MatchTab(base::UTF16ToUTF8(url_formatter::FormatUrl(prerender_gurl)))));
}

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_NewTabPrerenderShowsTask DISABLED_NewTabPrerenderShowsTask
#else
#define MAYBE_NewTabPrerenderShowsTask NewTabPrerenderShowsTask
#endif
// Tests that prerendering a new tab (target_hint=_blank) shows a "Prerender:"
// entry in the task manager, and that the entry disappears after activation and
// is replaced by a regular tab entry.
IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
                       MAYBE_NewTabPrerenderShowsTask) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  // Navigate to a page with target=_blank links.
  const GURL initial_url =
      embedded_test_server()->GetURL("/prerender/simple_links.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  // Start prerendering with target_hint=_blank.
  const GURL prerender_url =
      embedded_test_server()->GetURL("/prerender/empty.html");
  content::PrerenderHostId host_id = prerender_helper()->AddPrerender(
      prerender_url, /*eagerness=*/std::nullopt, "_blank");
  ASSERT_TRUE(host_id);

  auto* prerender_web_contents =
      content::test::PrerenderTestHelper::GetPrerenderWebContents(host_id);
  ASSERT_NE(prerender_web_contents, GetActiveWebContents());

  // The prerender should show up in the task manager.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_url.spec())));

  // Click the target=_blank link to activate the prerendered page.
  content::TestNavigationObserver activation_observer(prerender_url);
  activation_observer.WatchExistingWebContents();
  content::test::PrerenderHostObserver prerender_observer(
      *prerender_web_contents, host_id);
  EXPECT_TRUE(ExecJs(GetActiveWebContents(),
                     "clickSameSiteNewWindowWithNoopenerLink();"));
  activation_observer.WaitForNavigationFinished();
  EXPECT_TRUE(prerender_observer.was_activated());

  // After activation, the prerender entry should disappear and a new tab entry
  // should appear instead.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
}

// TODO(crbug.com/40232771): Flaky on Windows7.
#if BUILDFLAG(IS_WIN)
#define MAYBE_NewTabPrerenderDeletesTaskOnCancel \
  DISABLED_NewTabPrerenderDeletesTaskOnCancel
#else
#define MAYBE_NewTabPrerenderDeletesTaskOnCancel \
  NewTabPrerenderDeletesTaskOnCancel
#endif
// Tests that cancelling a new tab prerender removes its entry from the task
// manager.
IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
                       MAYBE_NewTabPrerenderDeletesTaskOnCancel) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  // Navigate to a page with target=_blank links.
  const GURL initial_url =
      embedded_test_server()->GetURL("/prerender/simple_links.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));

  // Start prerendering with target_hint=_blank.
  const GURL prerender_url =
      embedded_test_server()->GetURL("/prerender/empty.html");
  content::PrerenderHostId host_id = prerender_helper()->AddPrerender(
      prerender_url, /*eagerness=*/std::nullopt, "_blank");
  ASSERT_TRUE(host_id);

  // The prerender should show up in the task manager.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchPrerender(prerender_url.spec())));

  // Navigate the initiator page away, which cancels the prerender.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));

  // The prerender task should be removed.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
}

//==============================================================================
// FencedFrame tasks test.
namespace {

class FencedFrameTaskBrowserTest : public TaskManagerBrowserTest {
 public:
  FencedFrameTaskBrowserTest() {
    EXPECT_TRUE(blink::features::IsFencedFramesEnabled());
  }
  FencedFrameTaskBrowserTest(const FencedFrameTaskBrowserTest&) = delete;
  FencedFrameTaskBrowserTest& operator=(const FencedFrameTaskBrowserTest&) =
      delete;
  ~FencedFrameTaskBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    content::IsolateAllSitesForTesting(command_line);
    ASSERT_TRUE(content::AreAllSitesIsolatedForTesting());
    TaskManagerBrowserTest::SetUpCommandLine(command_line);
  }

  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    https_server()->ServeFilesFromDirectory(
        base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
    https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
    content::SetupCrossSiteRedirector(https_server());
    ASSERT_TRUE(https_server()->InitializeAndListen());
    https_server()->StartAcceptingConnections();
  }

  void NavigateTo(BrowserWindowInterface* browser,
                  std::string_view host,
                  std::string_view rel_url) {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser, https_server()->GetURL(host, rel_url)));
  }

  std::string GetFencedFrameTitle(const GURL& url) const {
    GURL::Replacements replacements;
    replacements.ClearPath();
    replacements.ClearRef();
    if (!content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault(
            browser()->GetProfile())) {
      // Only include the port for origin-isolated urls.
      replacements.ClearPort();
    }
    return url.ReplaceComponents(replacements).spec();
  }

  net::EmbeddedTestServer* https_server() { return &https_server_; }

  content::test::FencedFrameTestHelper* helper() { return helper_.get(); }

 private:
  base::test::ScopedFeatureList feature_list_;
  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
  std::unique_ptr<content::test::FencedFrameTestHelper> helper_ =
      std::make_unique<content::test::FencedFrameTestHelper>();
};

// TODO(crbug.com/40285326): This fails with the field trial testing config.
class FencedFrameTaskBrowserTestNoTestingConfig
    : public FencedFrameTaskBrowserTest {
 public:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    FencedFrameTaskBrowserTest::SetUpCommandLine(command_line);
    command_line->AppendSwitch("disable-field-trial-config");
  }
};

}  // namespace

// Testing that the task manager properly displays fenced frame tasks with
// re-opening task manager, and with fenced frame navigations.
IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTestNoTestingConfig,
                       ProperlyShowsTasks) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(browser(), "a.test", "/title2.html");
  // Create two fenced frames.
  auto* main_frame = browser()
                         ->tab_strip_model()
                         ->GetActiveWebContents()
                         ->GetPrimaryMainFrame();
  const auto initial_gurl =
      https_server()->GetURL("a.test", "/fenced_frames/title1.html");
  content::RenderFrameHostWrapper fenced_frame_rfh(
      helper()->CreateFencedFrame(main_frame, initial_gurl));
  ASSERT_TRUE(fenced_frame_rfh);

  // One task for the embedder. Same origin fenced frame does not show up in the
  // task manager.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));

  // Navigate the same-site FF to a cross-site url. The changes should be
  // reflected in the task manager.
  const auto cross_site_gurl =
      https_server()->GetURL("b.test", "/fenced_frames/title2.html");
  helper()->NavigateFrameInFencedFrameTree(fenced_frame_rfh.get(),
                                           cross_site_gurl);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchFencedFrame(GetFencedFrameTitle(cross_site_gurl))));

  // Close the task manager and re-open it, all tasks should be re-created.
  HideTaskManager();
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchFencedFrame(GetFencedFrameTitle(cross_site_gurl))));

  // Terminate the fenced frame. The embedder frame remains intact.
  {
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> fenced_frame_row = FindResourceIndex(
        MatchFencedFrame(GetFencedFrameTitle(cross_site_gurl)));
    ASSERT_TRUE(fenced_frame_row.has_value());
    ASSERT_TRUE(model()->GetTabId(fenced_frame_row.value()).is_valid());
    model()->Kill(fenced_frame_row.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
    ASSERT_NO_FATAL_FAILURE(
        WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  }
  // Re-create the fenced frame and terminate the embedding frame. The
  // embedder's task and the remaining fenced frame tasks are destroyed.
  {
    helper()->CreateFencedFrame(main_frame, initial_gurl);
    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
    std::optional<size_t> embedder_row =
        FindResourceIndex(MatchTab("Title Of Awesomeness"));
    ASSERT_TRUE(embedder_row.has_value());
    ASSERT_TRUE(model()->GetTabId(embedder_row.value()).is_valid());
    model()->Kill(embedder_row.value());
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
  }
}

// Test that the empty fenced frame (one without a `src`) is not shown in the
// task manager. Not shown because we cannot observe any navigation events for
// fenced frame creation (only |RenderFrameCreated| is triggered).
IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, EmptyFencedFrameNotShown) {
  const std::string kEmptyFencedFrameSnippet = R"(
    const ff = document.createElement("fencedframe");
    document.body.appendChild(ff);
  )";

  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(browser(), "a.test", "/title2.html");

  auto* main_frame = browser()
                         ->tab_strip_model()
                         ->GetActiveWebContents()
                         ->GetPrimaryMainFrame();
  ASSERT_TRUE(content::ExecJs(main_frame, kEmptyFencedFrameSnippet));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));

  // Navigation on the empty fenced frame should create an entry.
  auto* fenced_frame_rfh =
      content::test::FencedFrameTestHelper::GetMostRecentlyAddedFencedFrame(
          main_frame);
  ASSERT_NE(fenced_frame_rfh, nullptr);
  const auto fenced_frame_gurl =
      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
  helper()->NavigateFrameInFencedFrameTree(fenced_frame_rfh, fenced_frame_gurl);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchFencedFrame(GetFencedFrameTitle(fenced_frame_gurl))));
}

// Tests that the task manager properly shows tasks in Incognito mode.
IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, ShowsIncognitoTask) {
  auto* incognito_browser = CreateIncognitoBrowser();
  ASSERT_NE(incognito_browser, nullptr);
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(incognito_browser, "a.test", "/title2.html");
  auto* main_frame = incognito_browser->tab_strip_model()
                         ->GetActiveWebContents()
                         ->GetPrimaryMainFrame();
  const auto fenced_frame_gurl =
      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
  content::RenderFrameHostWrapper ff_rfh(
      helper()->CreateFencedFrame(main_frame, fenced_frame_gurl));
  ASSERT_TRUE(ff_rfh);
  // Two tasks: one for the incognito main frame and another for the incognito
  // fenced frames.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyIncognitoTab()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchAnyIncognitoFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchIncognitoTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchIncognitoFencedFrame(GetFencedFrameTitle(fenced_frame_gurl))));
}

// Test that clicking on the task manager fenced frame task row brings the focus
// to the embedder page.
IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, TaskActivationChangesFocus) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(browser(), "a.test", "/title2.html");
  // Create one fenced frame.
  auto* main_frame = browser()
                         ->tab_strip_model()
                         ->GetActiveWebContents()
                         ->GetPrimaryMainFrame();
  const auto fenced_frame_gurl =
      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
  content::RenderFrameHostWrapper ff_rfh(
      helper()->CreateFencedFrame(main_frame, fenced_frame_gurl));
  ASSERT_TRUE(ff_rfh);

  // One main tab task, one fenced frame task.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchFencedFrame(GetFencedFrameTitle(fenced_frame_gurl))));

  // Open a new tab of "about:blank". This appends an active WebContents at
  // index 1.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), GURL(url::kAboutBlankURL),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // The WebContents of "about:blank" is active.
  ASSERT_EQ(browser()->tab_strip_model()->active_index(), 1);

  const std::optional<size_t> fenced_frame_task_row = FindResourceIndex(
      MatchFencedFrame(GetFencedFrameTitle(fenced_frame_gurl)));
  ASSERT_TRUE(fenced_frame_task_row.has_value());
  model()->Activate(fenced_frame_task_row.value());

  // The WebContents of the embedder page is active.
  ASSERT_EQ(browser()->tab_strip_model()->active_index(), 0);
}

// Test that same-document navigation does not change the task's title.
IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest,
                       NoTitleChangeForSameDocNavigation) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  NavigateTo(browser(), "a.test", "/title2.html");
  // Create one fenced frame.
  auto* main_frame = browser()
                         ->tab_strip_model()
                         ->GetActiveWebContents()
                         ->GetPrimaryMainFrame();
  const auto fenced_frame_gurl =
      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
  content::RenderFrameHostWrapper ff_rfh(
      helper()->CreateFencedFrame(main_frame, fenced_frame_gurl));
  ASSERT_TRUE(ff_rfh);

  // One main tab task, one fenced frame task.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchFencedFrame(GetFencedFrameTitle(fenced_frame_gurl))));

  // Same-doc navigation of the fenced frame.
  const auto same_doc_navi_gurl = https_server()->GetURL(
      "b.test", base::StrCat({"/fenced_frames/title1.html", "#same_doc_navi"}));
  helper()->NavigateFrameInFencedFrameTree(ff_rfh.get(), same_doc_navi_gurl);
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
      1, MatchFencedFrame(GetFencedFrameTitle(fenced_frame_gurl))));
}

// Asserts that the task manager does not attempt to create any task for a RFH
// in `kPendingCommit` or `kPendingDeletion` state. Creating tasks during these
// two states will trigger a `NOTREACHED()` in
// `WebContentsTaskProvider::WebContentsEntry::CreateTaskForFrame`.
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest,
                       NoCrashOnPendingCommitPendingDeletaionRFH) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("a.test", "/title2.html")));
  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
  auto* main_frame = web_contents->GetPrimaryMainFrame();

  const std::string kCreateAndNavigateIFrame = R"(
    const iframe = document.createElement("iframe");
    iframe.src = $1;
    document.body.appendChild(iframe);
  )";

  // Create a cross-origin iframe, because we don't show tasks for iframes of
  // the same origin.
  const GURL cross_origin_subframe_url =
      embedded_test_server()->GetURL("b.test", "/title3.html");
  content::TestNavigationManager nav_obs(web_contents,
                                         cross_origin_subframe_url);
  ASSERT_TRUE(ExecJs(
      main_frame,
      content::JsReplace(kCreateAndNavigateIFrame, cross_origin_subframe_url)));
  ASSERT_TRUE(nav_obs.WaitForRequestStart());

  ShowTaskManager();
  // Main frame. The task manager does not create tasks for speculative RFHs.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));

  nav_obs.ResumeNavigation();
  ASSERT_TRUE(nav_obs.WaitForNavigationFinished());

  // Main frame + subframe after the navigation is resumed.
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnySubframe()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchSubframe("http://b.test/")));

  HideTaskManager();
  // Get hold of the subframe RFH, and stop it from being deleted.
  content::RenderFrameHostWrapper subframe_rfh(
      content::ChildFrameAt(main_frame, 0));
  content::LeaveInPendingDeletionState(subframe_rfh.get());

  const std::string kRemoveIFrame = R"(
    const iframe = document.querySelector('iframe');
    document.body.removeChild(iframe);
  )";
  ASSERT_TRUE(ExecJs(main_frame, kRemoveIFrame));

  // The `kPendingDeletion` subframe RFH is not destroyed, and reachable from
  // the `WebContents`, so it's possible for
  // `WebContentsTaskProvider::WebContentsEntry::CreateAllTasks()` to create a
  // task for it.
  ASSERT_FALSE(subframe_rfh.IsDestroyed());
  bool reached = false;
  web_contents->ForEachRenderFrameHost([&](content::RenderFrameHost* rfh) {
    if (rfh == subframe_rfh.get()) {
      reached = true;
    }
  });
  ASSERT_TRUE(reached);

  // However we shouldn't create any tasks for a RFH to be deleted.
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnySubframe()));
}

// Testing that the refreshed task manager properly displays tasks on different
// tabs.
IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest,
                       FilterTasksByCategoryAndSearchTerm) {
  ShowTaskManager();
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));

  // New tab should be shown on the default `Tabs` tab of the task manager.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("a.test", "/title2.html")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyUtility()));

  // Load an extension which should be shown in the default `Tabs & Extensions`
  // tab of the task manager. Current the task list is like below. The utility
  // processes might be different for different systems.
  // Utility: Network Service
  // Utility: Video Capture
  // Utility: Storage Service
  // Tab: Title Of Awesomeness
  // Extension: My extension 1
  // Extension: Foobar
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("good")
                                .AppendASCII("Extensions")
                                .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
                                .AppendASCII("1.0.0.0")));
  GURL url("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html");
  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyUtility()));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchExtension("Foobar")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchExtension("My extension 1")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyUtility()));

  // Switch to `System` tab, the extension and tabs should not be shown.
  model()->UpdateModel(DisplayCategory::kSystem, u"");
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));

  // Input search terms, all matched tasks would be shown no matter which tab
  // they lie in.
  model()->UpdateModel(DisplayCategory::kAll, u"title");
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyUtility()));

  model()->UpdateModel(DisplayCategory::kAll, u"EN");
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchExtension("Foobar")));
  ASSERT_NO_FATAL_FAILURE(
      WaitForTaskManagerRows(1, MatchExtension("My extension 1")));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyExtension()));
  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
}
