text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <stddef.h>
  
  #include <memory>
  #include <vector>
  
  #include "base/bind.h"
  #include "base/cancelable_callback.h"
  #include "base/command_line.h"
  #include "base/compiler_specific.h"
  #include "base/cxx17_backports.h"
  #include "base/files/file_path.h"
  #include "base/location.h"
  #include "base/memory/ptr_util.h"
  #include "base/memory/ref_counted.h"
  #include "base/path_service.h"
  #include "base/run_loop.h"
  #include "base/single_thread_task_runner.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/string_util.h"
  #include "base/strings/stringprintf.h"
  #include "base/strings/utf_string_conversions.h"
  #include "base/test/test_timeouts.h"
  #include "base/threading/thread_restrictions.h"
  #include "base/threading/thread_task_runner_handle.h"
  #include "build/build_config.h"
  #include "build/chromeos_buildflags.h"
  #include "chrome/browser/browser_process.h"
  #include "chrome/browser/chrome_notification_types.h"
  #include "chrome/browser/devtools/device/tcp_device_provider.h"
  #include "chrome/browser/devtools/devtools_window_testing.h"
  #include "chrome/browser/devtools/protocol/browser_handler.h"
  #include "chrome/browser/extensions/extension_apitest.h"
  #include "chrome/browser/extensions/extension_browsertest.h"
  #include "chrome/browser/extensions/extension_service.h"
  #include "chrome/browser/extensions/extension_util.h"
  #include "chrome/browser/extensions/unpacked_installer.h"
  #include "chrome/browser/lifetime/application_lifetime.h"
  #include "chrome/browser/lifetime/browser_shutdown.h"
  #include "chrome/browser/policy/chrome_browser_policy_connector.h"
  #include "chrome/browser/policy/developer_tools_policy_handler.h"
  #include "chrome/browser/profiles/profile.h"
  #include "chrome/browser/profiles/profile_manager.h"
  #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
  #include "chrome/browser/ui/browser.h"
  #include "chrome/browser/ui/browser_commands.h"
  #include "chrome/browser/ui/browser_list.h"
  #include "chrome/browser/ui/tabs/tab_strip_model.h"
  #include "chrome/common/chrome_paths.h"
  #include "chrome/common/chrome_switches.h"
  #include "chrome/common/pref_names.h"
  #include "chrome/common/url_constants.h"
  #include "chrome/test/base/in_process_browser_test.h"
  #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
  #include "chrome/test/base/ui_test_utils.h"
  #include "components/autofill/content/browser/content_autofill_driver.h"
  #include "components/autofill/content/browser/content_autofill_driver_factory.h"
  #include "components/autofill/core/browser/autofill_experiments.h"
  #include "components/autofill/core/browser/browser_autofill_manager.h"
  #include "components/autofill/core/browser/browser_autofill_manager_test_delegate.h"
  #include "components/autofill/core/common/autofill_features.h"
  #include "components/javascript_dialogs/app_modal_dialog_controller.h"
  #include "components/javascript_dialogs/app_modal_dialog_view.h"
  #include "components/keep_alive_registry/keep_alive_registry.h"
  #include "components/keep_alive_registry/keep_alive_types.h"
  #include "components/language/core/browser/pref_names.h"
  #include "components/policy/core/common/mock_configuration_policy_provider.h"
  #include "components/policy/core/common/policy_map.h"
  #include "components/policy/policy_constants.h"
  #include "components/prefs/pref_service.h"
  #include "content/public/browser/browser_task_traits.h"
  #include "content/public/browser/browser_thread.h"
  #include "content/public/browser/child_process_data.h"
  #include "content/public/browser/content_browser_client.h"
  #include "content/public/browser/devtools_agent_host.h"
  #include "content/public/browser/notification_registrar.h"
  #include "content/public/browser/notification_service.h"
  #include "content/public/browser/render_frame_host.h"
  #include "content/public/browser/render_widget_host.h"
  #include "content/public/browser/render_widget_host_view.h"
  #include "content/public/browser/storage_partition.h"
  #include "content/public/browser/url_data_source.h"
  #include "content/public/browser/web_contents.h"
  #include "content/public/browser/web_ui_controller.h"
  #include "content/public/common/content_switches.h"
  #include "content/public/common/url_constants.h"
  #include "content/public/test/browser_test.h"
  #include "content/public/test/browser_test_utils.h"
  #include "content/public/test/hit_test_region_observer.h"
  #include "content/public/test/scoped_web_ui_controller_factory_registration.h"
  #include "content/public/test/test_navigation_observer.h"
  #include "content/public/test/test_utils.h"
  #include "content/public/test/url_loader_interceptor.h"
  #include "extensions/browser/extension_registry.h"
  #include "extensions/browser/extension_registry_observer.h"
  #include "extensions/browser/extension_system.h"
  #include "extensions/browser/notification_types.h"
  #include "extensions/browser/test_extension_registry_observer.h"
  #include "extensions/common/manifest.h"
  #include "extensions/common/switches.h"
  #include "extensions/common/value_builder.h"
  #include "extensions/test/test_extension_dir.h"
  #include "net/dns/mock_host_resolver.h"
  #include "net/test/embedded_test_server/embedded_test_server.h"
  #include "net/test/embedded_test_server/http_request.h"
  #include "net/test/embedded_test_server/http_response.h"
  #include "services/network/public/cpp/features.h"
  #include "services/network/public/mojom/url_response_head.mojom.h"
  #include "third_party/abseil-cpp/absl/types/optional.h"
  #include "third_party/blink/public/common/chrome_debug_urls.h"
  #include "third_party/blink/public/common/input/web_input_event.h"
  #include "ui/base/ui_base_switches.h"
  #include "ui/compositor/compositor_switches.h"
  #include "ui/gl/gl_switches.h"
  #include "url/gurl.h"
  
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  #include "ash/constants/ash_switches.h"
  #endif
  
  using content::DevToolsAgentHost;
  using content::DevToolsAgentHostObserver;
  using content::NavigationController;
  using content::RenderFrameHost;
  using content::WebContents;
  using extensions::Extension;
  using javascript_dialogs::AppModalDialogView;
  
  namespace {
  
  const char kDebuggerTestPage[] = "/devtools/debugger_test_page.html";
  const char kPauseWhenLoadingDevTools[] =
      "/devtools/pause_when_loading_devtools.html";
  const char kPauseWhenScriptIsRunning[] =
      "/devtools/pause_when_script_is_running.html";
  const char kPageWithContentScript[] = "/devtools/page_with_content_script.html";
  const char kNavigateBackTestPage[] = "/devtools/navigate_back.html";
  const char kWindowOpenTestPage[] = "/devtools/window_open.html";
  const char kLatencyInfoTestPage[] = "/devtools/latency_info.html";
  const char kChunkedTestPage[] = "/chunked";
  const char kPushTestPage[] = "/devtools/push_test_page.html";
  // The resource is not really pushed, but mock url request job pretends it is.
  const char kPushTestResource[] = "/devtools/image.png";
  const char kPushUseNullEndTime[] = "pushUseNullEndTime";
  const char kSlowTestPage[] =
      "/chunked?waitBeforeHeaders=100&waitBetweenChunks=100&chunksNumber=2";
  const char kSharedWorkerTestPage[] = "/workers/workers_ui_shared_worker.html";
  const char kSharedWorkerTestWorker[] = "/workers/workers_ui_shared_worker.js";
  const char kReloadSharedWorkerTestPage[] =
      "/workers/debug_shared_worker_initialization.html";
  const char kReloadSharedWorkerTestWorker[] =
      "/workers/debug_shared_worker_initialization.js";
  const char kEmulateNetworkConditionsPage[] =
      "/devtools/emulate_network_conditions.html";
  const char kDispatchKeyEventShowsAutoFill[] =
      "/devtools/dispatch_key_event_shows_auto_fill.html";
  const char kDOMWarningsTestPage[] = "/devtools/dom_warnings_page.html";
  const char kEmptyTestPage[] = "/devtools/empty.html";
  // Arbitrary page that returns a 200 response, for tests that don't care about
  // more than that.
  const char kArbitraryPage[] = "/title1.html";
  
  template <typename... T>
  void DispatchOnTestSuiteSkipCheck(DevToolsWindow* window,
                                    const char* method,
                                    T... args) {
    WebContents* wc = DevToolsWindowTesting::Get(window)->main_web_contents();
    std::string result;
    const char* args_array[] = {method, args...};
    std::ostringstream script;
    script << "uiTests.dispatchOnTestSuite([";
    for (size_t i = 0; i < base::size(args_array); ++i)
      script << (i ? "," : "") << '\"' << args_array[i] << '\"';
    script << "])";
    ASSERT_TRUE(
        content::ExecuteScriptAndExtractString(wc, script.str(), &result));
    EXPECT_EQ("[OK]", result);
  }
  
  template <typename... T>
  void DispatchOnTestSuite(DevToolsWindow* window,
                           const char* method,
                           T... args) {
    std::string result;
    WebContents* wc = DevToolsWindowTesting::Get(window)->main_web_contents();
    // At first check that JavaScript part of the front-end is loaded by
    // checking that global variable uiTests exists(it's created after all js
    // files have been loaded) and has runTest method.
    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
        wc,
        "window.domAutomationController.send("
        "    '' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite)));",
        &result));
    ASSERT_EQ("function", result) << "DevTools front-end is broken.";
    DispatchOnTestSuiteSkipCheck(window, method, args...);
  }
  
  void RunTestFunction(DevToolsWindow* window, const char* test_name) {
    DispatchOnTestSuite(window, test_name);
  }
  
  void SwitchToPanel(DevToolsWindow* window, const char* panel) {
    DispatchOnTestSuite(window, "switchToPanel", panel);
  }
  
  // Version of SwitchToPanel that works with extension-created panels.
  void SwitchToExtensionPanel(DevToolsWindow* window,
                              const Extension* devtools_extension,
                              const char* panel_name) {
    // The full name is the concatenation of the extension URL (stripped of its
    // trailing '/') and the |panel_name| that was passed to panels.create().
    std::string prefix(base::TrimString(devtools_extension->url().spec(), "/",
                                        base::TRIM_TRAILING));
    SwitchToPanel(window, (prefix + panel_name).c_str());
  }
  
  }  // namespace
  
  class DevToolsTest : public InProcessBrowserTest {
   public:
    DevToolsTest() : window_(nullptr) {}
  
    void SetUpOnMainThread() override {
      // A number of tests expect favicon requests to succeed - otherwise, they'll
      // generate console errors.
      embedded_test_server()->RegisterRequestHandler(
          base::BindRepeating(&DevToolsTest::HandleFaviconRequest));
      // LoadNetworkResourceForFrontend depends on "hello.html" from content's
      // test directory.
      embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
      ASSERT_TRUE(embedded_test_server()->Start());
      host_resolver()->AddRule("*", "127.0.0.1");
    }
  
   protected:
    static std::unique_ptr<net::test_server::HttpResponse> HandleFaviconRequest(
        const net::test_server::HttpRequest& request) {
      if (request.relative_url != "/favicon.ico")
        return nullptr;
      // The response doesn't have to be a valid favicon to avoid logging a
      // console error. Any 200 response will do.
      return std::make_unique<net::test_server::BasicHttpResponse>();
    }
  
    void RunTest(const std::string& test_name, const std::string& test_page) {
      OpenDevToolsWindow(test_page, false);
      RunTestFunction(window_, test_name.c_str());
      CloseDevToolsWindow();
    }
  
    template <typename... T>
    void RunTestMethod(const char* method, T... args) {
      DispatchOnTestSuiteSkipCheck(window_, method, args...);
    }
  
    template <typename... T>
    void DispatchAndWait(const char* method, T... args) {
      DispatchOnTestSuiteSkipCheck(window_, "waitForAsync", method, args...);
    }
  
    template <typename... T>
    void DispatchInPageAndWait(const char* method, T... args) {
      DispatchAndWait("invokePageFunctionAsync", method, args...);
    }
  
    void LoadTestPage(const std::string& test_page) {
      GURL url;
      if (base::StartsWith(test_page, "/")) {
        url = embedded_test_server()->GetURL(test_page);
      } else {
        url = GURL(test_page);
      }
      ui_test_utils::NavigateToURL(browser(), url);
    }
  
    void OpenDevToolsWindow(const std::string& test_page, bool is_docked) {
      LoadTestPage(test_page);
  
      window_ = DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(),
                                                              is_docked);
    }
  
    void OpenDevToolsWindowOnOffTheRecordTab(const std::string& test_page) {
      GURL url;
      if (base::StartsWith(test_page, "/")) {
        url = embedded_test_server()->GetURL(test_page);
      } else {
        url = GURL(test_page);
      }
      auto* otr_browser = OpenURLOffTheRecord(browser()->profile(), url);
  
      window_ = DevToolsWindowTesting::OpenDevToolsWindowSync(
          otr_browser->tab_strip_model()->GetWebContentsAt(0), false);
    }
  
    WebContents* GetInspectedTab() {
      return browser()->tab_strip_model()->GetWebContentsAt(0);
    }
  
    void CloseDevToolsWindow() {
      DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
    }
  
    WebContents* main_web_contents() {
      return DevToolsWindowTesting::Get(window_)->main_web_contents();
    }
  
    WebContents* toolbox_web_contents() {
      return DevToolsWindowTesting::Get(window_)->toolbox_web_contents();
    }
  
    DevToolsWindow* window_;
  };
  
  class SitePerProcessDevToolsTest : public DevToolsTest {
   public:
    void SetUpCommandLine(base::CommandLine* command_line) override {
      DevToolsTest::SetUpCommandLine(command_line);
      content::IsolateAllSitesForTesting(command_line);
    }
  
    void SetUpOnMainThread() override {
      content::SetupCrossSiteRedirector(embedded_test_server());
      DevToolsTest::SetUpOnMainThread();
    }
  };
  
  // Used to block until a dev tools window gets beforeunload event.
  class DevToolsWindowBeforeUnloadObserver
      : public content::WebContentsObserver {
   public:
    explicit DevToolsWindowBeforeUnloadObserver(DevToolsWindow*);
    void Wait();
   private:
    // Invoked when the beforeunload handler fires.
    void BeforeUnloadFired(bool proceed,
                           const base::TimeTicks& proceed_time) override;
  
    bool m_fired;
    scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
    DISALLOW_COPY_AND_ASSIGN(DevToolsWindowBeforeUnloadObserver);
  };
  
  DevToolsWindowBeforeUnloadObserver::DevToolsWindowBeforeUnloadObserver(
      DevToolsWindow* devtools_window)
      : WebContentsObserver(
            DevToolsWindowTesting::Get(devtools_window)->main_web_contents()),
        m_fired(false) {
  }
  
  void DevToolsWindowBeforeUnloadObserver::Wait() {
    if (m_fired)
      return;
    message_loop_runner_ = base::MakeRefCounted<content::MessageLoopRunner>();
    message_loop_runner_->Run();
  }
  
  void DevToolsWindowBeforeUnloadObserver::BeforeUnloadFired(
      bool proceed,
      const base::TimeTicks& proceed_time) {
    m_fired = true;
    if (message_loop_runner_.get())
      message_loop_runner_->Quit();
  }
  
  class DevToolsBeforeUnloadTest: public DevToolsTest {
   public:
    void CloseInspectedTab() {
      browser()->tab_strip_model()->CloseWebContentsAt(0,
          TabStripModel::CLOSE_NONE);
    }
  
    void CloseDevToolsWindowAsync() {
      DevToolsWindowTesting::CloseDevToolsWindow(window_);
    }
  
    void CloseInspectedBrowser() {
      chrome::CloseWindow(browser());
    }
  
   protected:
    void InjectBeforeUnloadListener(content::WebContents* web_contents) {
      ASSERT_TRUE(content::ExecuteScript(
          web_contents,
          "window.addEventListener('beforeunload',"
          "function(event) { event.returnValue = 'Foo'; });"));
      content::PrepContentsForBeforeUnloadTest(web_contents);
    }
  
    void RunBeforeUnloadTest(
        bool is_docked,
        base::RepeatingCallback<void(void)> close_method,
        bool wait_for_browser_close = true) {
      OpenDevToolsWindow(kDebuggerTestPage, is_docked);
      auto runner = base::MakeRefCounted<content::MessageLoopRunner>();
      DevToolsWindowTesting::Get(window_)->
          SetCloseCallback(runner->QuitClosure());
      InjectBeforeUnloadListener(main_web_contents());
      {
        DevToolsWindowBeforeUnloadObserver before_unload_observer(window_);
        close_method.Run();
        CancelModalDialog();
        before_unload_observer.Wait();
      }
      {
        close_method.Run();
        AcceptModalDialog();
        if (wait_for_browser_close)
          ui_test_utils::WaitForBrowserToClose(browser());
      }
      runner->Run();
    }
  
    DevToolsWindow* OpenDevToolWindowOnWebContents(
        content::WebContents* contents, bool is_docked) {
      DevToolsWindow* window =
          DevToolsWindowTesting::OpenDevToolsWindowSync(contents, is_docked);
      return window;
    }
  
    void OpenDevToolsPopupWindow(DevToolsWindow* devtools_window) {
      content::WindowedNotificationObserver observer(
          content::NOTIFICATION_LOAD_STOP,
          content::NotificationService::AllSources());
      ASSERT_TRUE(content::ExecuteScript(
          DevToolsWindowTesting::Get(devtools_window)->main_web_contents(),
          "window.open(\"\", \"\", \"location=0\");"));
      observer.Wait();
    }
  
    void CloseDevToolsPopupWindow(DevToolsWindow* devtools_window) {
      DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
    }
  
    void AcceptModalDialog() {
      AppModalDialogView* view = GetDialog();
      view->AcceptAppModalDialog();
    }
  
    void CancelModalDialog() {
      AppModalDialogView* view = GetDialog();
      view->CancelAppModalDialog();
    }
  
    AppModalDialogView* GetDialog() {
      javascript_dialogs::AppModalDialogController* dialog =
          ui_test_utils::WaitForAppModalDialog();
      AppModalDialogView* view = dialog->view();
      EXPECT_TRUE(view);
      return view;
    }
  };
  
  void TimeoutCallback(const std::string& timeout_message) {
    ADD_FAILURE() << timeout_message;
    base::RunLoop::QuitCurrentWhenIdleDeprecated();
  }
  
  // Base class for DevTools tests that test devtools functionality for
  // extensions and content scripts.
  class DevToolsExtensionTest : public DevToolsTest,
                                public content::NotificationObserver {
   public:
    DevToolsExtensionTest()
        : test_extensions_dir_(
              base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
                  .AppendASCII("devtools")
                  .AppendASCII("extensions")) {}
  
   protected:
    // Load an extension from test\data\devtools\extensions\<extension_name>
    void LoadExtension(const char* extension_name) {
      base::FilePath path = test_extensions_dir_.AppendASCII(extension_name);
      ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension.";
    }
  
    const Extension* LoadExtensionFromPath(const base::FilePath& path) {
      extensions::ExtensionService* service =
          extensions::ExtensionSystem::Get(browser()->profile())
              ->extension_service();
      extensions::ExtensionRegistry* registry =
          extensions::ExtensionRegistry::Get(browser()->profile());
      extensions::TestExtensionRegistryObserver observer(registry);
      extensions::UnpackedInstaller::Create(service)->Load(path);
      observer.WaitForExtensionLoaded();
  
      if (!WaitForExtensionViewsToLoad())
        return nullptr;
  
      return GetExtensionByPath(registry->enabled_extensions(), path);
    }
  
    // Loads a dynamically generated extension populated with a bunch of test
    // pages. |name| is the extension name to use in the manifest.
    // |devtools_page|, if non-empty, indicates which test page should be be
    // listed as a devtools_page in the manifest.  If |devtools_page| is empty, a
    // non-devtools extension is created instead. |panel_iframe_src| controls the
    // src= attribute of the <iframe> element in the 'panel.html' test page.
    const Extension* LoadExtensionForTest(const std::string& name,
                                          const std::string& devtools_page,
                                          const std::string& panel_iframe_src) {
      test_extension_dirs_.push_back(
          std::make_unique<extensions::TestExtensionDir>());
      extensions::TestExtensionDir* dir = test_extension_dirs_.back().get();
  
      extensions::DictionaryBuilder manifest;
      manifest.Set("name", name)
          .Set("version", "1")
          .Set("manifest_version", 2)
          // simple_test_page.html is currently the only page referenced outside
          // of its own extension in the tests
          .Set("web_accessible_resources",
               extensions::ListBuilder().Append("simple_test_page.html").Build());
  
      // If |devtools_page| isn't empty, make it a devtools extension in the
      // manifest.
      if (!devtools_page.empty())
        manifest.Set("devtools_page", devtools_page);
  
      dir->WriteManifest(manifest.ToJSON());
  
      GURL http_frame_url =
          embedded_test_server()->GetURL("a.com", "/popup_iframe.html");
  
      // If this is a devtools extension, |devtools_page| will indicate which of
      // these devtools_pages will end up being used.  Different tests use
      // different devtools_pages.
      dir->WriteFile(FILE_PATH_LITERAL("web_devtools_page.html"),
                     "<html><body><iframe src='" + http_frame_url.spec() +
                         "'></iframe></body></html>");
  
      dir->WriteFile(FILE_PATH_LITERAL("simple_devtools_page.html"),
                     "<html><body></body></html>");
  
      dir->WriteFile(
          FILE_PATH_LITERAL("panel_devtools_page.html"),
          "<html><head><script "
          "src='panel_devtools_page.js'></script></head><body></body></html>");
  
      dir->WriteFile(FILE_PATH_LITERAL("panel_devtools_page.js"),
                     "chrome.devtools.panels.create('iframe_panel',\n"
                     "    null,\n"
                     "    'panel.html',\n"
                     "    function(panel) {\n"
                     "      chrome.devtools.inspectedWindow.eval(\n"
                     "        'console.log(\"PASS\")');\n"
                     "    }\n"
                     ");\n");
  
      dir->WriteFile(FILE_PATH_LITERAL("sidebarpane_devtools_page.html"),
                     "<html><head><script src='sidebarpane_devtools_page.js'>"
                     "</script></head><body></body></html>");
  
      dir->WriteFile(
          FILE_PATH_LITERAL("sidebarpane_devtools_page.js"),
          "chrome.devtools.panels.elements.createSidebarPane('iframe_pane',\n"
          "    function(sidebar) {\n"
          "      chrome.devtools.inspectedWindow.eval(\n"
          "        'console.log(\"PASS\")');\n"
          "      sidebar.setPage('panel.html');\n"
          "    }\n"
          ");\n");
  
      dir->WriteFile(FILE_PATH_LITERAL("panel.html"),
                     "<html><body><iframe src='" + panel_iframe_src +
                         "'></iframe></body></html>");
  
      dir->WriteFile(FILE_PATH_LITERAL("simple_test_page.html"),
                     "<html><body>This is a test</body></html>");
  
      GURL web_url = embedded_test_server()->GetURL("a.com", "/title3.html");
  
      dir->WriteFile(FILE_PATH_LITERAL("multi_frame_page.html"),
                     "<html><body><iframe src='about:blank'>"
                     "</iframe><iframe src='data:text/html,foo'>"
                     "</iframe><iframe src='" +
                         web_url.spec() + "'></iframe></body></html>");
  
      // Install the extension.
      return LoadExtensionFromPath(dir->UnpackedPath());
    }
  
   private:
    const Extension* GetExtensionByPath(
        const extensions::ExtensionSet& extensions,
        const base::FilePath& path) {
      base::ScopedAllowBlockingForTesting allow_blocking;
      base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
      EXPECT_TRUE(!extension_path.empty());
      for (const scoped_refptr<const Extension>& extension : extensions) {
        if (extension->path() == extension_path) {
          return extension.get();
        }
      }
      return nullptr;
    }
  
    bool WaitForExtensionViewsToLoad() {
      // Wait for all the extension render views that exist to finish loading.
      // NOTE: This assumes that the extension views list is not changing while
      // this method is running.
  
      content::NotificationRegistrar registrar;
      registrar.Add(this,
                    extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD,
                    content::NotificationService::AllSources());
      base::CancelableOnceClosure timeout(
          base::BindOnce(&TimeoutCallback, "Extension host load timed out."));
      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
          FROM_HERE, timeout.callback(), TestTimeouts::action_timeout());
  
      extensions::ProcessManager* manager =
          extensions::ProcessManager::Get(browser()->profile());
      extensions::ProcessManager::FrameSet all_frames = manager->GetAllFrames();
      for (auto iter = all_frames.begin(); iter != all_frames.end();) {
        if (!content::WebContents::FromRenderFrameHost(*iter)->IsLoading())
          ++iter;
        else
          content::RunMessageLoop();
      }
  
      timeout.Cancel();
      return true;
    }
  
    void Observe(int type,
                 const content::NotificationSource& source,
                 const content::NotificationDetails& details) override {
      DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD,
                type);
      base::RunLoop::QuitCurrentWhenIdleDeprecated();
    }
  
    std::vector<std::unique_ptr<extensions::TestExtensionDir>>
        test_extension_dirs_;
    const base::FilePath test_extensions_dir_;
  };
  
  class DevToolsExperimentalExtensionTest : public DevToolsExtensionTest {
   public:
    void SetUpCommandLine(base::CommandLine* command_line) override {
      command_line->AppendSwitch(
          extensions::switches::kEnableExperimentalExtensionApis);
    }
  };
  
  class WorkerDevToolsTest : public InProcessBrowserTest {
   public:
    WorkerDevToolsTest() : window_(nullptr) {}
  
    void SetUpOnMainThread() override {
      ASSERT_TRUE(embedded_test_server()->Start());
    }
  
   protected:
    class WorkerCreationObserver : public DevToolsAgentHostObserver {
     public:
      WorkerCreationObserver(const std::string& path,
                             scoped_refptr<DevToolsAgentHost>* out_host,
                             base::OnceClosure quit)
          : path_(path), out_host_(out_host), quit_(std::move(quit)) {
        DevToolsAgentHost::AddObserver(this);
      }
  
     private:
      ~WorkerCreationObserver() override {
        DevToolsAgentHost::RemoveObserver(this);
      }
  
      void DevToolsAgentHostCreated(DevToolsAgentHost* host) override {
        if (host->GetType() == DevToolsAgentHost::kTypeSharedWorker &&
            host->GetURL().path().rfind(path_) != std::string::npos) {
          *out_host_ = host;
          content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                                       std::move(quit_));
          delete this;
        }
      }
  
      std::string path_;
      scoped_refptr<DevToolsAgentHost>* out_host_;
      base::OnceClosure quit_;
    };
  
    static scoped_refptr<DevToolsAgentHost> WaitForFirstSharedWorker(
        const char* path) {
      for (auto& host : DevToolsAgentHost::GetOrCreateAll()) {
        if (host->GetType() == DevToolsAgentHost::kTypeSharedWorker &&
            host->GetURL().path().rfind(path) != std::string::npos) {
          return host;
        }
      }
      scoped_refptr<DevToolsAgentHost> host;
      base::RunLoop run_loop;
      new WorkerCreationObserver(path, &host, run_loop.QuitWhenIdleClosure());
      content::RunThisRunLoop(&run_loop);
      return host;
    }
  
    void OpenDevToolsWindow(scoped_refptr<DevToolsAgentHost> agent_host) {
      Profile* profile = browser()->profile();
      window_ =
          DevToolsWindowTesting::OpenDevToolsWindowSync(profile, agent_host);
    }
  
    void CloseDevToolsWindow() {
      DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
    }
  
    DevToolsWindow* window_;
  };
  
  // Tests that BeforeUnload event gets called on docked devtools if
  // we try to close them.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDockedDevToolsClose) {
    RunBeforeUnloadTest(
        true,
        base::BindRepeating(&DevToolsBeforeUnloadTest::CloseDevToolsWindowAsync,
                            base::Unretained(this)),
        false);
  }
  
  // Tests that BeforeUnload event gets called on docked devtools if
  // we try to close the inspected page.
  //
  // TODO(https://crbug.com/1061052): Flaky on Windows.
  #if defined(OS_WIN)
  #define MAYBE_TestDockedDevToolsInspectedTabClose \
    DISABLED_TestDockedDevToolsInspectedTabClose
  #else
  #define MAYBE_TestDockedDevToolsInspectedTabClose \
    TestDockedDevToolsInspectedTabClose
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         MAYBE_TestDockedDevToolsInspectedTabClose) {
    RunBeforeUnloadTest(
        true, base::BindRepeating(&DevToolsBeforeUnloadTest::CloseInspectedTab,
                                  base::Unretained(this)));
  }
  
  // Tests that BeforeUnload event gets called on docked devtools if
  // we try to close the inspected browser.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         TestDockedDevToolsInspectedBrowserClose) {
    RunBeforeUnloadTest(
        true,
        base::BindRepeating(&DevToolsBeforeUnloadTest::CloseInspectedBrowser,
                            base::Unretained(this)));
  }
  
  // Tests that BeforeUnload event gets called on undocked devtools if
  // we try to close them.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsClose) {
    RunBeforeUnloadTest(
        false,
        base::BindRepeating(&DevToolsBeforeUnloadTest::CloseDevToolsWindowAsync,
                            base::Unretained(this)),
        false);
  }
  
  // Tests that BeforeUnload event gets called on undocked devtools if
  // we try to close the inspected page.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         TestUndockedDevToolsInspectedTabClose) {
    RunBeforeUnloadTest(
        false, base::BindRepeating(&DevToolsBeforeUnloadTest::CloseInspectedTab,
                                   base::Unretained(this)));
  }
  
  // Tests that BeforeUnload event gets called on undocked devtools if
  // we try to close the inspected browser.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         TestUndockedDevToolsInspectedBrowserClose) {
    RunBeforeUnloadTest(
        false,
        base::BindRepeating(&DevToolsBeforeUnloadTest::CloseInspectedBrowser,
                            base::Unretained(this)));
  }
  
  // Tests that BeforeUnload event gets called on undocked devtools if
  // we try to exit application.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         TestUndockedDevToolsApplicationClose) {
    RunBeforeUnloadTest(false,
                              base::BindRepeating(&chrome::CloseAllBrowsers));
  }
  
  // Tests that inspected tab gets closed if devtools renderer
  // becomes unresponsive during beforeunload event interception.
  // @see http://crbug.com/322380
  // Disabled because of http://crbug.com/410327
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         DISABLED_TestUndockedDevToolsUnresponsive) {
    LoadTestPage(kDebuggerTestPage);
    DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
        GetInspectedTab(), false);
  
    auto runner = base::MakeRefCounted<content::MessageLoopRunner>();
    DevToolsWindowTesting::Get(devtools_window)->SetCloseCallback(
        runner->QuitClosure());
  
    ASSERT_TRUE(content::ExecuteScript(
        DevToolsWindowTesting::Get(devtools_window)->main_web_contents(),
        "window.addEventListener('beforeunload',"
        "function(event) { while (true); });"));
    CloseInspectedTab();
    runner->Run();
  }
  
  // Tests that closing worker inspector window does not cause browser crash
  // @see http://crbug.com/323031
  // TODO(https://crbug.com/1100888): Disabled due to flakiness.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         DISABLED_TestWorkerWindowClosing) {
    LoadTestPage(kDebuggerTestPage);
    DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
        GetInspectedTab(), false);
  
    OpenDevToolsPopupWindow(devtools_window);
    CloseDevToolsPopupWindow(devtools_window);
  }
  
  // Tests that BeforeUnload event gets called on devtools that are opened
  // on another devtools.
  // TODO(https://crbug.com/1000654): Re-enable this test.
  IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
                         DISABLED_TestDevToolsOnDevTools) {
    LoadTestPage(kDebuggerTestPage);
  
    std::vector<DevToolsWindow*> windows;
    std::vector<std::unique_ptr<content::WebContentsDestroyedWatcher>>
        close_observers;
    content::WebContents* inspected_web_contents = GetInspectedTab();
    for (int i = 0; i < 3; ++i) {
      DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
        inspected_web_contents, i == 0);
      windows.push_back(devtools_window);
      close_observers.push_back(
          std::make_unique<content::WebContentsDestroyedWatcher>(
              DevToolsWindowTesting::Get(devtools_window)->main_web_contents()));
      inspected_web_contents =
          DevToolsWindowTesting::Get(devtools_window)->main_web_contents();
    }
  
    InjectBeforeUnloadListener(
        DevToolsWindowTesting::Get(windows[0])->main_web_contents());
    InjectBeforeUnloadListener(
        DevToolsWindowTesting::Get(windows[2])->main_web_contents());
    // Try to close second devtools.
    {
      chrome::CloseWindow(DevToolsWindowTesting::Get(windows[1])->browser());
      CancelModalDialog();
      base::RunLoop().RunUntilIdle();
      // The second devtools hasn't closed.
      EXPECT_EQ(windows[1],
                DevToolsWindow::GetInstanceForInspectedWebContents(
                    DevToolsWindowTesting::Get(windows[0])->main_web_contents()));
    }
    // Try to close browser window.
    {
      chrome::CloseWindow(browser());
      AcceptModalDialog();
      CancelModalDialog();
      base::RunLoop().RunUntilIdle();
      EXPECT_EQ(browser(), BrowserList::GetInstance()->get(0));
    }
    // Try to exit application.
    {
      chrome::CloseAllBrowsers();
      AcceptModalDialog();
      AcceptModalDialog();
      ui_test_utils::WaitForBrowserToClose(browser());
    }
    for (auto& close_observer : close_observers)
      close_observer->Wait();
  }
  
  // Tests scripts panel showing.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestShowScriptsTab) {
    RunTest("testShowScriptsTab", kDebuggerTestPage);
  }
  
  // Tests that scripts tab is populated with inspected scripts even if it
  // hadn't been shown by the moment inspected paged refreshed.
  // @see http://crbug.com/26312
  // This test is flaky on windows and linux asan. See https://crbug.com/1013003
  #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
      defined(OS_CHROMEOS)
  #define MAYBE_TestScriptsTabIsPopulatedOnInspectedPageRefresh \
    DISABLED_TestScriptsTabIsPopulatedOnInspectedPageRefresh
  #else
  #define MAYBE_TestScriptsTabIsPopulatedOnInspectedPageRefresh \
    TestScriptsTabIsPopulatedOnInspectedPageRefresh
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest,
                         MAYBE_TestScriptsTabIsPopulatedOnInspectedPageRefresh) {
    RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh",
            kDebuggerTestPage);
  }
  
  // Tests that chrome.devtools extension is correctly exposed.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         TestDevToolsExtensionAPI) {
    LoadExtension("devtools_extension");
    RunTest("waitForTestResultsInConsole", kArbitraryPage);
  }
  
  // Tests that chrome.devtools extension is correctly exposed.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, TestExtensionOnNewTab) {
    // Install the dynamically-generated devtools extension.
    const Extension* devtools_extension = LoadExtensionForTest(
        "Devtools Extension", "panel_devtools_page.html", "");
    ASSERT_TRUE(devtools_extension);
    extensions::util::SetIsIncognitoEnabled(devtools_extension->id(),
                                            browser()->profile(), true);
  
    OpenDevToolsWindowOnOffTheRecordTab(chrome::kChromeUINewTabURL);
  
    // Wait for the extension's panel to finish loading -- it'll output 'PASS'
    // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  }
  
  // Tests that http Iframes within the visible devtools panel for the devtools
  // extension are rendered in their own processes and not in the devtools process
  // or the extension's process.  This is tested because this is one of the
  // extension pages with devtools access
  // (https://developer.chrome.com/extensions/devtools).  Also tests that frames
  // with data URLs and about:blank URLs are rendered in the devtools process,
  // unless a web OOPIF navigates itself to about:blank, in which case it does not
  // end up back in the devtools process.  Also tests that when a web IFrame is
  // navigated back to a devtools extension page, it gets put back in the devtools
  // process.
  // http://crbug.com/570483
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         HttpIframeInDevToolsExtensionPanel) {
    // Install the dynamically-generated extension.
    const Extension* extension =
        LoadExtensionForTest("Devtools Extension", "panel_devtools_page.html",
                             "/multi_frame_page.html");
    ASSERT_TRUE(extension);
  
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    // Wait for the extension's panel to finish loading -- it'll output 'PASS'
    // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  
    // Now that we know the panel is loaded, switch to it.
    SwitchToExtensionPanel(window_, extension, "iframe_panel");
    EXPECT_TRUE(content::WaitForLoadStop(main_web_contents()));
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(7U, rfhs.size());
  
    // This test creates a page with the following frame tree:
    // - DevTools
    //   - devtools_page from DevTools extension
    //   - Panel (DevTools extension)
    //     - iframe (DevTools extension)
    //       - about:blank
    //       - data:
    //       - web URL
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
    RenderFrameHost* devtools_extension_devtools_page_rfh =
        ChildFrameAt(main_devtools_rfh, 0);
    RenderFrameHost* devtools_extension_panel_rfh =
        ChildFrameAt(main_devtools_rfh, 1);
    RenderFrameHost* panel_frame_rfh =
        ChildFrameAt(devtools_extension_panel_rfh, 0);
    RenderFrameHost* about_blank_frame_rfh = ChildFrameAt(panel_frame_rfh, 0);
    RenderFrameHost* data_frame_rfh = ChildFrameAt(panel_frame_rfh, 1);
    RenderFrameHost* web_frame_rfh = ChildFrameAt(panel_frame_rfh, 2);
  
    GURL web_url = embedded_test_server()->GetURL("a.com", "/title3.html");
    GURL about_blank_url = GURL(url::kAboutBlankURL);
    GURL data_url = GURL("data:text/html,foo");
  
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(extension->GetResourceURL("/panel_devtools_page.html"),
              devtools_extension_devtools_page_rfh->GetLastCommittedURL());
    EXPECT_EQ(extension->GetResourceURL("/panel.html"),
              devtools_extension_panel_rfh->GetLastCommittedURL());
    EXPECT_EQ(extension->GetResourceURL("/multi_frame_page.html"),
              panel_frame_rfh->GetLastCommittedURL());
    EXPECT_EQ(about_blank_url, about_blank_frame_rfh->GetLastCommittedURL());
    EXPECT_EQ(data_url, data_frame_rfh->GetLastCommittedURL());
    EXPECT_EQ(web_url, web_frame_rfh->GetLastCommittedURL());
  
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extensions_instance =
        devtools_extension_devtools_page_rfh->GetSiteInstance();
  
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_TRUE(
        extensions_instance->GetSiteURL().SchemeIs(extensions::kExtensionScheme));
  
    EXPECT_NE(devtools_instance, extensions_instance);
    EXPECT_EQ(extensions_instance,
              devtools_extension_panel_rfh->GetSiteInstance());
    EXPECT_EQ(extensions_instance, panel_frame_rfh->GetSiteInstance());
    EXPECT_EQ(extensions_instance, about_blank_frame_rfh->GetSiteInstance());
    EXPECT_EQ(extensions_instance, data_frame_rfh->GetSiteInstance());
  
    EXPECT_EQ(web_url.host(),
              web_frame_rfh->GetSiteInstance()->GetSiteURL().host());
    EXPECT_NE(devtools_instance, web_frame_rfh->GetSiteInstance());
    EXPECT_NE(extensions_instance, web_frame_rfh->GetSiteInstance());
  
    // Check that if the web iframe navigates itself to about:blank, it stays in
    // the web SiteInstance.
    std::string about_blank_javascript = "location.href='about:blank';";
  
    content::TestNavigationManager web_about_blank_manager(main_web_contents(),
                                                           about_blank_url);
  
    ASSERT_TRUE(content::ExecuteScript(web_frame_rfh, about_blank_javascript));
  
    web_about_blank_manager.WaitForNavigationFinished();
    // After navigation, the frame may change.
    web_frame_rfh = ChildFrameAt(panel_frame_rfh, 2);
  
    EXPECT_EQ(about_blank_url, web_frame_rfh->GetLastCommittedURL());
    EXPECT_EQ(web_url.host(),
              web_frame_rfh->GetSiteInstance()->GetSiteURL().host());
    EXPECT_NE(devtools_instance, web_frame_rfh->GetSiteInstance());
    EXPECT_NE(extensions_instance, web_frame_rfh->GetSiteInstance());
  
    // Check that if the web IFrame is navigated back to a devtools extension
    // page, it gets put back in the devtools process.
    GURL extension_simple_url =
        extension->GetResourceURL("/simple_test_page.html");
    std::string renavigation_javascript =
        "location.href='" + extension_simple_url.spec() + "';";
  
    content::TestNavigationManager renavigation_manager(main_web_contents(),
                                                        extension_simple_url);
  
    ASSERT_TRUE(content::ExecuteScript(web_frame_rfh, renavigation_javascript));
  
    renavigation_manager.WaitForNavigationFinished();
  
    // The old RFH is no longer valid after the renavigation, so we must get the
    // new one.
    RenderFrameHost* extension_simple_frame_rfh =
        ChildFrameAt(panel_frame_rfh, 2);
  
    EXPECT_EQ(extension_simple_url,
              extension_simple_frame_rfh->GetLastCommittedURL());
    EXPECT_EQ(extensions_instance, extension_simple_frame_rfh->GetSiteInstance());
  }
  
  // Tests that http Iframes within the sidebar pane page for the devtools
  // extension that is visible in the elements panel are rendered in their own
  // processes and not in the devtools process or the extension's process.  This
  // is tested because this is one of the extension pages with devtools access
  // (https://developer.chrome.com/extensions/devtools).  http://crbug.com/570483
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         HttpIframeInDevToolsExtensionSideBarPane) {
    GURL web_url = embedded_test_server()->GetURL("a.com", "/title3.html");
  
    // Install the dynamically-generated extension.
    const Extension* extension = LoadExtensionForTest(
        "Devtools Extension", "sidebarpane_devtools_page.html", web_url.spec());
    ASSERT_TRUE(extension);
  
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    // Wait for the extension's sidebarpane to finish loading -- it'll output
    // 'PASS' when it's installed. waitForTestResultsInConsole waits until that
    // 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  
    // Now that we know the sidebarpane is loaded, switch to it.
    content::TestNavigationManager web_manager(main_web_contents(), web_url);
    SwitchToPanel(window_, "elements");
    // This is a bit of a hack to switch to the sidebar pane in the elements panel
    // that the Iframe has been added to.
    SwitchToPanel(window_, "iframe_pane");
    web_manager.WaitForNavigationFinished();
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(4U, rfhs.size());
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
    RenderFrameHost* devtools_extension_devtools_page_rfh =
        ChildFrameAt(main_devtools_rfh, 0);
    RenderFrameHost* devtools_sidebar_pane_extension_rfh =
        ChildFrameAt(main_devtools_rfh, 1);
    RenderFrameHost* http_iframe_rfh =
        ChildFrameAt(devtools_sidebar_pane_extension_rfh, 0);
  
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(extension->GetResourceURL("/sidebarpane_devtools_page.html"),
              devtools_extension_devtools_page_rfh->GetLastCommittedURL());
    EXPECT_EQ(extension->GetResourceURL("/panel.html"),
              devtools_sidebar_pane_extension_rfh->GetLastCommittedURL());
    EXPECT_EQ(web_url, http_iframe_rfh->GetLastCommittedURL());
  
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extensions_instance =
        devtools_extension_devtools_page_rfh->GetSiteInstance();
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_NE(devtools_instance, extensions_instance);
    EXPECT_EQ(extensions_instance,
              devtools_extension_devtools_page_rfh->GetSiteInstance());
    EXPECT_EQ(extensions_instance,
              devtools_sidebar_pane_extension_rfh->GetSiteInstance());
    EXPECT_EQ(web_url.host(),
              http_iframe_rfh->GetSiteInstance()->GetSiteURL().host());
    EXPECT_NE(devtools_instance, http_iframe_rfh->GetSiteInstance());
    EXPECT_NE(extensions_instance, http_iframe_rfh->GetSiteInstance());
  }
  
  // Tests that http Iframes within the devtools background page, which is
  // different from the extension's background page, are rendered in their own
  // processes and not in the devtools process or the extension's process.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         HttpIframeInDevToolsExtensionDevtools) {
    // Install the dynamically-generated extension.
    const Extension* extension =
        LoadExtensionForTest("Devtools Extension", "web_devtools_page.html",
                             "" /* panel_iframe_src */);
    ASSERT_TRUE(extension);
  
    // Wait for a 'DONE' message sent from popup_iframe.html, indicating that it
    // loaded successfully.
    content::DOMMessageQueue message_queue;
    std::string message;
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    while (true) {
      ASSERT_TRUE(message_queue.WaitForMessage(&message));
      if (message == "\"DONE\"")
        break;
    }
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(3U, rfhs.size());
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
    RenderFrameHost* devtools_extension_devtools_page_rfh =
        ChildFrameAt(main_devtools_rfh, 0);
    RenderFrameHost* http_iframe_rfh =
        ChildFrameAt(devtools_extension_devtools_page_rfh, 0);
  
    GURL web_url = embedded_test_server()->GetURL("a.com", "/popup_iframe.html");
  
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(extension->GetResourceURL("/web_devtools_page.html"),
              devtools_extension_devtools_page_rfh->GetLastCommittedURL());
    EXPECT_EQ(web_url, http_iframe_rfh->GetLastCommittedURL());
  
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extensions_instance =
        devtools_extension_devtools_page_rfh->GetSiteInstance();
  
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_NE(devtools_instance, extensions_instance);
    EXPECT_EQ(web_url.host(),
              http_iframe_rfh->GetSiteInstance()->GetSiteURL().host());
    EXPECT_NE(devtools_instance, http_iframe_rfh->GetSiteInstance());
    EXPECT_NE(extensions_instance, http_iframe_rfh->GetSiteInstance());
  }
  
  // Tests that iframes to a non-devtools extension embedded in a devtools
  // extension will be isolated from devtools and the devtools extension.
  // http://crbug.com/570483
  // Disabled due to flakiness https://crbug.com/1062802
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         DISABLED_NonDevToolsExtensionInDevToolsExtension) {
    // Install the dynamically-generated non-devtools extension.
    const Extension* non_devtools_extension =
        LoadExtensionForTest("Non-DevTools Extension", "" /* devtools_page */,
                             "" /* panel_iframe_src */);
    ASSERT_TRUE(non_devtools_extension);
  
    GURL non_dt_extension_test_url =
        non_devtools_extension->GetResourceURL("/simple_test_page.html");
  
    // Install the dynamically-generated devtools extension.
    const Extension* devtools_extension =
        LoadExtensionForTest("Devtools Extension", "panel_devtools_page.html",
                             non_dt_extension_test_url.spec());
    ASSERT_TRUE(devtools_extension);
  
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    // Wait for the extension's panel to finish loading -- it'll output 'PASS'
    // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  
    // Now that we know the panel is loaded, switch to it.
    content::TestNavigationManager non_devtools_manager(
        main_web_contents(), non_dt_extension_test_url);
    SwitchToExtensionPanel(window_, devtools_extension, "iframe_panel");
    non_devtools_manager.WaitForNavigationFinished();
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(4U, rfhs.size());
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
    RenderFrameHost* devtools_extension_devtools_page_rfh =
        ChildFrameAt(main_devtools_rfh, 0);
    RenderFrameHost* devtools_extension_panel_rfh =
        ChildFrameAt(main_devtools_rfh, 1);
    RenderFrameHost* non_devtools_extension_rfh =
        ChildFrameAt(devtools_extension_panel_rfh, 0);
  
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(devtools_extension->GetResourceURL("/panel_devtools_page.html"),
              devtools_extension_devtools_page_rfh->GetLastCommittedURL());
    EXPECT_EQ(devtools_extension->GetResourceURL("/panel.html"),
              devtools_extension_panel_rfh->GetLastCommittedURL());
    EXPECT_EQ(non_dt_extension_test_url,
              non_devtools_extension_rfh->GetLastCommittedURL());
  
    // simple_test_page.html's frame should be in |non_devtools_extension|'s
    // process, not in devtools or |devtools_extension|'s process.
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extensions_instance =
        devtools_extension_devtools_page_rfh->GetSiteInstance();
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_NE(devtools_instance, extensions_instance);
    EXPECT_EQ(extensions_instance,
              devtools_extension_panel_rfh->GetSiteInstance());
    EXPECT_EQ(non_dt_extension_test_url.GetOrigin(),
              non_devtools_extension_rfh->GetSiteInstance()->GetSiteURL());
    EXPECT_NE(devtools_instance, non_devtools_extension_rfh->GetSiteInstance());
    EXPECT_NE(extensions_instance, non_devtools_extension_rfh->GetSiteInstance());
  }
  
  // Tests that if a devtools extension's devtools panel page has a subframe to a
  // page for another devtools extension, the subframe is rendered in the devtools
  // process as well.  http://crbug.com/570483
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         DevToolsExtensionInDevToolsExtension) {
    // Install the dynamically-generated extension.
    const Extension* devtools_b_extension =
        LoadExtensionForTest("Devtools Extension B", "simple_devtools_page.html",
                             "" /* panel_iframe_src */);
    ASSERT_TRUE(devtools_b_extension);
  
    GURL extension_b_page_url =
        devtools_b_extension->GetResourceURL("/simple_test_page.html");
  
    // Install another dynamically-generated extension.  This extension's
    // panel.html's iframe will point to an extension b URL.
    const Extension* devtools_a_extension =
        LoadExtensionForTest("Devtools Extension A", "panel_devtools_page.html",
                             extension_b_page_url.spec());
    ASSERT_TRUE(devtools_a_extension);
  
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    // Wait for the extension's panel to finish loading -- it'll output 'PASS'
    // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  
    // Now that we know the panel is loaded, switch to it.
    content::TestNavigationManager extension_b_manager(main_web_contents(),
                                                       extension_b_page_url);
    SwitchToExtensionPanel(window_, devtools_a_extension, "iframe_panel");
    extension_b_manager.WaitForNavigationFinished();
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(5U, rfhs.size());
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
  
    RenderFrameHost* devtools_extension_a_devtools_rfh =
        content::FrameMatchingPredicate(
            main_web_contents(),
            base::BindRepeating(&content::FrameHasSourceUrl,
                                devtools_a_extension->GetResourceURL(
                                    "/panel_devtools_page.html")));
    EXPECT_TRUE(devtools_extension_a_devtools_rfh);
    RenderFrameHost* devtools_extension_b_devtools_rfh =
        content::FrameMatchingPredicate(
            main_web_contents(),
            base::BindRepeating(&content::FrameHasSourceUrl,
                                devtools_b_extension->GetResourceURL(
                                    "/simple_devtools_page.html")));
    EXPECT_TRUE(devtools_extension_b_devtools_rfh);
  
    RenderFrameHost* devtools_extension_a_panel_rfh =
        ChildFrameAt(main_devtools_rfh, 2);
    RenderFrameHost* devtools_extension_b_frame_rfh =
        ChildFrameAt(devtools_extension_a_panel_rfh, 0);
  
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(devtools_a_extension->GetResourceURL("/panel_devtools_page.html"),
              devtools_extension_a_devtools_rfh->GetLastCommittedURL());
    EXPECT_EQ(devtools_b_extension->GetResourceURL("/simple_devtools_page.html"),
              devtools_extension_b_devtools_rfh->GetLastCommittedURL());
    EXPECT_EQ(devtools_a_extension->GetResourceURL("/panel.html"),
              devtools_extension_a_panel_rfh->GetLastCommittedURL());
    EXPECT_EQ(extension_b_page_url,
              devtools_extension_b_frame_rfh->GetLastCommittedURL());
  
    // Main extension frame should be loaded in the extensions process. Nested
    // iframes should be loaded consistently with any other extensions iframes
    // (in or out of process).
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extension_a_instance =
        devtools_extension_a_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extension_b_instance =
        devtools_extension_b_devtools_rfh->GetSiteInstance();
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_NE(devtools_instance, extension_a_instance);
    EXPECT_NE(devtools_instance, extension_b_instance);
    EXPECT_NE(extension_a_instance, extension_b_instance);
    EXPECT_EQ(extension_a_instance,
              devtools_extension_a_panel_rfh->GetSiteInstance());
    EXPECT_EQ(extension_b_instance,
              devtools_extension_b_frame_rfh->GetSiteInstance());
  }
  
  // Tests that a devtools extension can still have subframes to itself in a
  // "devtools page" and that they will be rendered within the extension process
  // as well, not in some other process.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, DevToolsExtensionInItself) {
    // Install the dynamically-generated extension.
    const Extension* extension =
        LoadExtensionForTest("Devtools Extension", "panel_devtools_page.html",
                             "/simple_test_page.html");
    ASSERT_TRUE(extension);
  
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    // Wait for the extension's panel to finish loading -- it'll output 'PASS'
    // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  
    // Now that we know the panel is loaded, switch to it.
    GURL extension_test_url = extension->GetResourceURL("/simple_test_page.html");
    content::TestNavigationManager test_page_manager(main_web_contents(),
                                                     extension_test_url);
    SwitchToExtensionPanel(window_, extension, "iframe_panel");
    test_page_manager.WaitForNavigationFinished();
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(4U, rfhs.size());
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
    RenderFrameHost* devtools_extension_devtools_page_rfh =
        ChildFrameAt(main_devtools_rfh, 0);
    RenderFrameHost* devtools_extension_panel_rfh =
        ChildFrameAt(main_devtools_rfh, 1);
    RenderFrameHost* devtools_extension_panel_frame_rfh =
        ChildFrameAt(devtools_extension_panel_rfh, 0);
  
    // Extension frames should be in the extensions process, including
    // simple_test_page.html
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(extension->GetResourceURL("/panel_devtools_page.html"),
              devtools_extension_devtools_page_rfh->GetLastCommittedURL());
    EXPECT_EQ(extension->GetResourceURL("/panel.html"),
              devtools_extension_panel_rfh->GetLastCommittedURL());
    EXPECT_EQ(extension_test_url,
              devtools_extension_panel_frame_rfh->GetLastCommittedURL());
  
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    content::SiteInstance* extensions_instance =
        devtools_extension_devtools_page_rfh->GetSiteInstance();
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_EQ(extensions_instance,
              devtools_extension_panel_rfh->GetSiteInstance());
    EXPECT_EQ(extensions_instance,
              devtools_extension_panel_frame_rfh->GetSiteInstance());
  }
  
  // Tests that a devtools (not a devtools extension) Iframe can be injected into
  // devtools.  http://crbug.com/570483
  // crbug.com/1124981: flaky on win
  #if defined(OS_WIN)
  #define MAYBE_DevtoolsInDevTools DISABLED_DevtoolsInDevTools
  #else
  #define MAYBE_DevtoolsInDevTools DevtoolsInDevTools
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_DevtoolsInDevTools) {
    GURL devtools_url = GURL(chrome::kChromeUIDevToolsURL);
  
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    std::string javascript =
        "var devtoolsFrame = document.createElement('iframe');"
        "document.body.appendChild(devtoolsFrame);"
        "devtoolsFrame.src = '" +
        devtools_url.spec() + "';";
  
    RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
  
    content::TestNavigationManager manager(main_web_contents(), devtools_url);
    ASSERT_TRUE(content::ExecuteScript(main_devtools_rfh, javascript));
    manager.WaitForNavigationFinished();
  
    std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
    EXPECT_EQ(2U, rfhs.size());
    RenderFrameHost* devtools_iframe_rfh = ChildFrameAt(main_devtools_rfh, 0);
    EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
        content::kChromeDevToolsScheme));
    EXPECT_EQ(devtools_url, devtools_iframe_rfh->GetLastCommittedURL());
    content::SiteInstance* devtools_instance =
        main_devtools_rfh->GetSiteInstance();
    EXPECT_TRUE(
        devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
    EXPECT_EQ(devtools_instance, devtools_iframe_rfh->GetSiteInstance());
  
    std::string message;
    EXPECT_TRUE(ExecuteScriptAndExtractString(
        devtools_iframe_rfh, "domAutomationController.send(self.origin)",
        &message));
    EXPECT_EQ(devtools_url.GetOrigin().spec(), message + "/");
  }
  
  // Some web features, when used from an extension, are subject to browser-side
  // security policy enforcement. Make sure they work properly from inside a
  // devtools extension.
  // ToDo(993982): The test is flaky (timeout, crash, and fail) on several builds:
  // Debug, Windows, Mac, MSan, and ASan.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         DISABLED_DevToolsExtensionSecurityPolicyGrants) {
    auto dir = std::make_unique<extensions::TestExtensionDir>();
  
    extensions::DictionaryBuilder manifest;
    dir->WriteManifest(extensions::DictionaryBuilder()
                           .Set("name", "Devtools Panel")
                           .Set("version", "1")
                           // Whitelist the script we stuff into the 'blob:' URL:
                           .Set("content_security_policy",
                                "script-src 'self' "
                                "'sha256-uv9gxBEOFchPzak3TK6O39RdKxJeZvfha9zOHGam"
                                "TB4='; "
                                "object-src 'none'")
                           .Set("manifest_version", 2)
                           .Set("devtools_page", "devtools.html")
                           .ToJSON());
  
    dir->WriteFile(
        FILE_PATH_LITERAL("devtools.html"),
        "<html><head><script src='devtools.js'></script></head></html>");
  
    dir->WriteFile(
        FILE_PATH_LITERAL("devtools.js"),
        "chrome.devtools.panels.create('the_panel_name',\n"
        "    null,\n"
        "    'panel.html',\n"
        "    function(panel) {\n"
        "      chrome.devtools.inspectedWindow.eval('console.log(\"PASS\")');\n"
        "    }\n"
        ");\n");
  
    dir->WriteFile(FILE_PATH_LITERAL("panel.html"),
                   "<html><body>A panel."
                   "<script src='blob_xhr.js'></script>"
                   "<script src='blob_iframe.js'></script>"
                   "</body></html>");
    // Creating blobs from chrome-extension:// origins is only permitted if the
    // process has been granted permission to commit 'chrome-extension' schemes.
    dir->WriteFile(
        FILE_PATH_LITERAL("blob_xhr.js"),
        "var blob_url = URL.createObjectURL(new Blob(['xhr blob contents']));\n"
        "var xhr = new XMLHttpRequest();\n"
        "xhr.open('GET', blob_url, true);\n"
        "xhr.onload = function (e) {\n"
        "    domAutomationController.send(xhr.response);\n"
        "};\n"
        "xhr.send(null);\n");
    dir->WriteFile(
        FILE_PATH_LITERAL("blob_iframe.js"),
        "var payload = `"
        "<html><body>iframe blob contents"
        "<script>"
        "    domAutomationController.send(document.body.innerText);\n"
        "</script></body></html>"
        "`;"
        "document.body.appendChild(document.createElement('iframe')).src ="
        "    URL.createObjectURL(new Blob([payload], {type: 'text/html'}));");
    // Install the extension.
    const Extension* extension = LoadExtensionFromPath(dir->UnpackedPath());
    ASSERT_TRUE(extension);
  
    // Open a devtools window.
    OpenDevToolsWindow(kDebuggerTestPage, false);
  
    // Wait for the panel extension to finish loading -- it'll output 'PASS'
    // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
    RunTestFunction(window_, "waitForTestResultsInConsole");
  
    // Now that we know the panel is loaded, switch to it. We'll wait until we
    // see a 'DONE' message sent from popup_iframe.html, indicating that it
    // loaded successfully.
    content::DOMMessageQueue message_queue;
    SwitchToExtensionPanel(window_, extension, "the_panel_name");
    std::string message;
    while (true) {
      ASSERT_TRUE(message_queue.WaitForMessage(&message));
      if (message == "\"xhr blob contents\"")
        break;
    }
    while (true) {
      ASSERT_TRUE(message_queue.WaitForMessage(&message));
      if (message == "\"iframe blob contents\"")
        break;
    }
  }
  
  // Disabled on Windows due to flakiness. http://crbug.com/183649
  #if defined(OS_WIN)
  #define MAYBE_TestDevToolsExtensionMessaging DISABLED_TestDevToolsExtensionMessaging
  #else
  #define MAYBE_TestDevToolsExtensionMessaging TestDevToolsExtensionMessaging
  #endif
  
  // Tests that chrome.devtools extension can communicate with background page
  // using extension messaging.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         MAYBE_TestDevToolsExtensionMessaging) {
    LoadExtension("devtools_messaging");
    RunTest("waitForTestResultsInConsole", kArbitraryPage);
  }
  
  // Tests that chrome.experimental.devtools extension is correctly exposed
  // when the extension has experimental permission.
  IN_PROC_BROWSER_TEST_F(DevToolsExperimentalExtensionTest,
                         TestDevToolsExperimentalExtensionAPI) {
    LoadExtension("devtools_experimental");
    RunTest("waitForTestResultsInConsole", kArbitraryPage);
  }
  
  // Tests that a content script is in the scripts list.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         TestContentScriptIsPresent) {
    LoadExtension("simple_content_script");
    RunTest("testContentScriptIsPresent", kPageWithContentScript);
  }
  
  // Tests that console selector shows correct context names.
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         TestConsoleContextNames) {
    LoadExtension("simple_content_script");
    RunTest("testConsoleContextNames", kPageWithContentScript);
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, TestEvaluateOnChromeScheme) {
    LoadExtension("chrome_scheme");
    RunTest("waitForTestResultsAsMessage", kArbitraryPage);
  }
  
  // Tests that scripts are not duplicated after Scripts Panel switch.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestNoScriptDuplicatesOnPanelSwitch) {
    RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage);
  }
  
  // Tests that debugger works correctly if pause event occurs when DevTools
  // frontend is being loaded.
  // Flaky on win and linux: crbug.com/1092924.
  #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
  #define MAYBE_TestPauseWhenLoadingDevTools DISABLED_TestPauseWhenLoadingDevTools
  #else
  #define MAYBE_TestPauseWhenLoadingDevTools TestPauseWhenLoadingDevTools
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestPauseWhenLoadingDevTools) {
    RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
  }
  
  // Tests that pressing 'Pause' will pause script execution if the script
  // is already running.
  #if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
      defined(ARCH_CPU_ARM_FAMILY)
  // Timing out on linux ARM bot: https://crbug/238453
  #define MAYBE_TestPauseWhenScriptIsRunning DISABLED_TestPauseWhenScriptIsRunning
  #elif ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(MEMORY_SANITIZER))
  // Timing out on Linux and ChromeOS MSan: https://crbug.com/1181692
  #define MAYBE_TestPauseWhenScriptIsRunning DISABLED_TestPauseWhenScriptIsRunning
  #else
  #define MAYBE_TestPauseWhenScriptIsRunning TestPauseWhenScriptIsRunning
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest,
                         MAYBE_TestPauseWhenScriptIsRunning) {
    RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
  }
  
  // Tests network timing.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestNetworkTiming) {
    RunTest("testNetworkTiming", kSlowTestPage);
  }
  
  // Tests network size.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestNetworkSize) {
    RunTest("testNetworkSize", kChunkedTestPage);
  }
  
  // Tests raw headers text.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestNetworkSyncSize) {
    RunTest("testNetworkSyncSize", kChunkedTestPage);
  }
  
  // Tests raw headers text.
  // TODO(https://crbug.com/1199825): Flaky on Mac.
  #if defined(OS_MAC)
  #define MAYBE_TestNetworkRawHeadersText DISABLED_TestNetworkRawHeadersText
  #else
  #define MAYBE_TestNetworkRawHeadersText TestNetworkRawHeadersText
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestNetworkRawHeadersText) {
    // This test expects headers to be exactly 112 bytes in length, so add an
    // extra header to reach that length.
    RunTest("testNetworkRawHeadersText",
            "/set-header?Extra-Header: 1234567890123");
  }
  
  namespace {
  
  bool InterceptURLLoad(content::URLLoaderInterceptor::RequestParams* params) {
    const GURL& url = params->url_request.url;
    if (!base::EndsWith(url.path(), kPushTestResource,
                        base::CompareCase::SENSITIVE)) {
      return false;
    }
  
    auto response = network::mojom::URLResponseHead::New();
  
    response->headers = new net::HttpResponseHeaders("200 OK\r\n\r\n");
  
    auto start_time =
        base::TimeTicks::Now() - base::TimeDelta::FromMilliseconds(10);
    response->request_start = start_time;
    response->response_start = base::TimeTicks::Now();
    response->request_time =
        base::Time::Now() - base::TimeDelta::FromMilliseconds(10);
    response->response_time = base::Time::Now();
  
    auto& load_timing = response->load_timing;
    load_timing.request_start = start_time;
    load_timing.request_start_time = response->request_time;
    load_timing.send_start = start_time;
    load_timing.send_end = base::TimeTicks::Now();
    load_timing.receive_headers_end = base::TimeTicks::Now();
    load_timing.push_start = start_time - base::TimeDelta::FromMilliseconds(100);
    if (url.query() != kPushUseNullEndTime)
      load_timing.push_end = base::TimeTicks::Now();
  
    params->client->OnReceiveResponse(std::move(response));
  
    // The response's body is empty. The pipe is not filled.
    mojo::ScopedDataPipeProducerHandle producer_handle;
    mojo::ScopedDataPipeConsumerHandle consumer_handle;
    EXPECT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
              MOJO_RESULT_OK);
    params->client->OnStartLoadingResponseBody(std::move(consumer_handle));
    params->client->OnComplete(network::URLLoaderCompletionStatus());
    return true;
  }
  
  }  // namespace
  
  // TODO(crbug.com/1046784) Flaky
  IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_TestNetworkPushTime) {
    content::URLLoaderInterceptor interceptor(
        base::BindRepeating(InterceptURLLoad));
  
    OpenDevToolsWindow(kPushTestPage, false);
    GURL push_url = embedded_test_server()->GetURL(kPushTestResource);
  
    DispatchOnTestSuite(window_, "testPushTimes", push_url.spec().c_str());
  
    CloseDevToolsWindow();
  }
  
  #if defined(OS_WIN)
  // Flaky on Windows: https://crbug.com/1087320
  #define MAYBE_TestDOMWarnings DISABLED_TestDOMWarnings
  #else
  #define MAYBE_TestDOMWarnings TestDOMWarnings
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestDOMWarnings) {
    RunTest("testDOMWarnings", kDOMWarningsTestPage);
  }
  
  // Tests that console messages are not duplicated on navigation back.
  #if defined(OS_WIN) || defined(MEMORY_SANITIZER)
  // Flaking on windows swarm try runs: crbug.com/409285.
  // Also flaking on MSan runs: crbug.com/1182861
  #define MAYBE_TestConsoleOnNavigateBack DISABLED_TestConsoleOnNavigateBack
  #else
  #define MAYBE_TestConsoleOnNavigateBack TestConsoleOnNavigateBack
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestConsoleOnNavigateBack) {
    RunTest("testConsoleOnNavigateBack", kNavigateBackTestPage);
  }
  
  #if defined(OS_LINUX) || defined(OS_CHROMEOS)
  // Flaking on linux runs, see crbug.com/990692.
  #define MAYBE_TestDeviceEmulation DISABLED_TestDeviceEmulation
  #else
  #define MAYBE_TestDeviceEmulation TestDeviceEmulation
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestDeviceEmulation) {
    RunTest("testDeviceMetricsOverrides", "about:blank");
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestDispatchKeyEventDoesNotCrash) {
    RunTest("testDispatchKeyEventDoesNotCrash", "about:blank");
  }
  
  class BrowserAutofillManagerTestDelegateDevtoolsImpl
      : public autofill::BrowserAutofillManagerTestDelegate {
   public:
    explicit BrowserAutofillManagerTestDelegateDevtoolsImpl(
        WebContents* inspectedContents)
        : inspected_contents_(inspectedContents) {}
    ~BrowserAutofillManagerTestDelegateDevtoolsImpl() override {}
  
    void DidPreviewFormData() override {}
  
    void DidFillFormData() override {}
  
    void DidShowSuggestions() override {
      ASSERT_TRUE(content::ExecuteScript(inspected_contents_,
                                         "console.log('didShowSuggestions');"));
    }
  
    void OnTextFieldChanged() override {}
  
   private:
    WebContents* inspected_contents_;
  
    DISALLOW_COPY_AND_ASSIGN(BrowserAutofillManagerTestDelegateDevtoolsImpl);
  };
  
  // Disabled. Failing on MacOS MSAN. See https://crbug.com/849129.
  // Also failing on Linux. See https://crbug.com/1187693.
  #if defined(OS_MAC) || defined(OS_LINUX)
  #define MAYBE_TestDispatchKeyEventShowsAutoFill \
    DISABLED_TestDispatchKeyEventShowsAutoFill
  #else
  #define MAYBE_TestDispatchKeyEventShowsAutoFill \
    TestDispatchKeyEventShowsAutoFill
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest,
                         MAYBE_TestDispatchKeyEventShowsAutoFill) {
    OpenDevToolsWindow(kDispatchKeyEventShowsAutoFill, false);
  
    autofill::ContentAutofillDriver* autofill_driver =
        autofill::ContentAutofillDriverFactory::FromWebContents(GetInspectedTab())
            ->DriverForFrame(GetInspectedTab()->GetMainFrame());
    autofill::BrowserAutofillManager* autofill_manager =
        autofill_driver->browser_autofill_manager();
    BrowserAutofillManagerTestDelegateDevtoolsImpl autoFillTestDelegate(
        GetInspectedTab());
    autofill_manager->SetTestDelegate(&autoFillTestDelegate);
  
    RunTestFunction(window_, "testDispatchKeyEventShowsAutoFill");
    CloseDevToolsWindow();
  }
  
  // Tests that whitelisted unhandled shortcuts are forwarded from inspected page
  // into devtools frontend
  IN_PROC_BROWSER_TEST_F(DevToolsTest, testKeyEventUnhandled) {
    OpenDevToolsWindow("about:blank", true);
    RunTestFunction(window_, "testKeyEventUnhandled");
    CloseDevToolsWindow();
  }
  
  // Tests that the keys that are forwarded from the browser update
  // when their shortcuts change
  IN_PROC_BROWSER_TEST_F(DevToolsTest, testForwardedKeysChanged) {
    OpenDevToolsWindow("about:blank", true);
    RunTestFunction(window_, "testForwardedKeysChanged");
    CloseDevToolsWindow();
  }
  
  // Test that showing a certificate in devtools does not crash the process.
  // Disabled on windows as this opens a modal in its own thread, which leads to a
  // test timeout.
  #if defined(OS_WIN)
  #define MAYBE_testShowCertificate DISABLED_testShowCertificate
  #else
  #define MAYBE_testShowCertificate testShowCertificate
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_testShowCertificate) {
    OpenDevToolsWindow("about:blank", true);
    RunTestFunction(window_, "testShowCertificate");
    CloseDevToolsWindow();
  }
  
  // Tests that settings are stored in profile correctly.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestSettings) {
    OpenDevToolsWindow("about:blank", true);
    RunTestFunction(window_, "testSettings");
    CloseDevToolsWindow();
  }
  
  // Tests that external navigation from inspector page is always handled by
  // DevToolsWindow and results in inspected page navigation.  See also
  // https://crbug.com/180555.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestDevToolsExternalNavigation) {
    OpenDevToolsWindow(kDebuggerTestPage, true);
    GURL url = embedded_test_server()->GetURL(kNavigateBackTestPage);
    ui_test_utils::UrlLoadObserver observer(url,
        content::NotificationService::AllSources());
    ASSERT_TRUE(content::ExecuteScript(
        main_web_contents(),
        std::string("window.location = \"") + url.spec() + "\""));
    observer.Wait();
  
    ASSERT_TRUE(main_web_contents()->GetURL().
                    SchemeIs(content::kChromeDevToolsScheme));
    ASSERT_EQ(url, GetInspectedTab()->GetURL());
    CloseDevToolsWindow();
  }
  
  // Tests that toolbox window is loaded when DevTools window is undocked.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestToolboxLoadedUndocked) {
    OpenDevToolsWindow(kDebuggerTestPage, false);
    ASSERT_TRUE(toolbox_web_contents());
    DevToolsWindow* on_self =
        DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
    ASSERT_FALSE(DevToolsWindowTesting::Get(on_self)->toolbox_web_contents());
    DevToolsWindowTesting::CloseDevToolsWindowSync(on_self);
    CloseDevToolsWindow();
  }
  
  // Tests that toolbox window is not loaded when DevTools window is docked.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestToolboxNotLoadedDocked) {
    OpenDevToolsWindow(kDebuggerTestPage, true);
    ASSERT_FALSE(toolbox_web_contents());
    DevToolsWindow* on_self =
        DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
    ASSERT_FALSE(DevToolsWindowTesting::Get(on_self)->toolbox_web_contents());
    DevToolsWindowTesting::CloseDevToolsWindowSync(on_self);
    CloseDevToolsWindow();
  }
  
  // Tests that inspector will reattach to inspected page when it is reloaded
  // after a crash. See http://crbug.com/101952
  // Disabled. it doesn't check anything right now: http://crbug.com/461790
  IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_TestReattachAfterCrash) {
    RunTest("testReattachAfterCrash", kArbitraryPage);
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestPageWithNoJavaScript) {
    OpenDevToolsWindow("about:blank", false);
    std::string result;
    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
        main_web_contents(),
        "window.domAutomationController.send("
        "    '' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite)));",
        &result));
    ASSERT_EQ("function", result) << "DevTools front-end is broken.";
    CloseDevToolsWindow();
  }
  
  class DevToolsAutoOpenerTest : public DevToolsTest {
   public:
    void SetUpCommandLine(base::CommandLine* command_line) override {
      command_line->AppendSwitch(switches::kAutoOpenDevToolsForTabs);
      observer_ = std::make_unique<DevToolsWindowCreationObserver>();
    }
   protected:
    std::unique_ptr<DevToolsWindowCreationObserver> observer_;
  };
  
  // TODO(https://crbug.com/1167158): Flaky on debug builds.
  #if !defined(NDEBUG)
  #define MAYBE_TestAutoOpenForTabs DISABLED_TestAutoOpenForTabs
  #else
  #define MAYBE_TestAutoOpenForTabs TestAutoOpenForTabs
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsAutoOpenerTest, MAYBE_TestAutoOpenForTabs) {
    {
      DevToolsWindowCreationObserver observer;
      AddTabAtIndexToBrowser(browser(), 0, GURL("about:blank"),
          ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false);
      observer.WaitForLoad();
    }
    Browser* new_browser = nullptr;
    {
      DevToolsWindowCreationObserver observer;
      new_browser = CreateBrowser(browser()->profile());
      observer.WaitForLoad();
    }
    {
      DevToolsWindowCreationObserver observer;
      AddTabAtIndexToBrowser(new_browser, 0, GURL("about:blank"),
          ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false);
      observer.WaitForLoad();
    }
    observer_->CloseAllSync();
  }
  
  class DevToolsReattachAfterCrashTest : public DevToolsTest {
   protected:
    void RunTestWithPanel(const char* panel_name) {
      OpenDevToolsWindow("about:blank", false);
      SwitchToPanel(window_, panel_name);
      ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
  
      content::RenderProcessHostWatcher crash_observer(
          GetInspectedTab(),
          content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
      ui_test_utils::NavigateToURL(browser(), GURL(blink::kChromeUICrashURL));
      crash_observer.Wait();
      content::TestNavigationObserver navigation_observer(GetInspectedTab(), 1);
      chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
      navigation_observer.Wait();
    }
  };
  
  IN_PROC_BROWSER_TEST_F(DevToolsReattachAfterCrashTest,
                         TestReattachAfterCrashOnTimeline) {
    RunTestWithPanel("timeline");
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsReattachAfterCrashTest,
                         TestReattachAfterCrashOnNetwork) {
    RunTestWithPanel("network");
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, AutoAttachToWindowOpen) {
    OpenDevToolsWindow(kWindowOpenTestPage, false);
    DevToolsWindowTesting::Get(window_)->SetOpenNewWindowForPopups(true);
    DevToolsWindowCreationObserver observer;
    ASSERT_TRUE(content::ExecuteScript(
        GetInspectedTab(), "window.open('window_open.html', '_blank');"));
    observer.WaitForLoad();
    DispatchOnTestSuite(observer.devtools_window(), "waitForDebuggerPaused");
    DevToolsWindowTesting::CloseDevToolsWindowSync(observer.devtools_window());
    CloseDevToolsWindow();
  }
  
  // TODO(crbug.com/1102964) Flaky
  IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_SecondTabAfterDevTools) {
    OpenDevToolsWindow(kDebuggerTestPage, true);
  
    ui_test_utils::NavigateToURLWithDisposition(
        browser(), embedded_test_server()->GetURL(kDebuggerTestPage),
        WindowOpenDisposition::NEW_FOREGROUND_TAB,
        ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
            ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
    WebContents* second = browser()->tab_strip_model()->GetActiveWebContents();
  
    scoped_refptr<content::DevToolsAgentHost> agent(
        content::DevToolsAgentHost::GetOrCreateFor(second));
    EXPECT_EQ("page", agent->GetType());
  
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(WorkerDevToolsTest, InspectSharedWorker) {
    GURL url = embedded_test_server()->GetURL(kSharedWorkerTestPage);
    ui_test_utils::NavigateToURL(browser(), url);
  
    scoped_refptr<DevToolsAgentHost> host =
        WaitForFirstSharedWorker(kSharedWorkerTestWorker);
    OpenDevToolsWindow(host);
    RunTestFunction(window_, "testSharedWorker");
    CloseDevToolsWindow();
  }
  
  // Flaky on multiple platforms. See http://crbug.com/432444
  IN_PROC_BROWSER_TEST_F(WorkerDevToolsTest,
                         PauseInSharedWorkerInitialization) {
    GURL url = embedded_test_server()->GetURL(kReloadSharedWorkerTestPage);
    ui_test_utils::NavigateToURL(browser(), url);
  
    scoped_refptr<DevToolsAgentHost> host =
        WaitForFirstSharedWorker(kReloadSharedWorkerTestWorker);
    OpenDevToolsWindow(host);
  
    // We should make sure that the worker inspector has loaded before
    // terminating worker.
    RunTestFunction(window_, "testPauseInSharedWorkerInitialization1");
  
    host->Close();
  
    // Reload page to restart the worker.
    ui_test_utils::NavigateToURL(browser(), url);
  
    // Wait until worker script is paused on the debugger statement.
    RunTestFunction(window_, "testPauseInSharedWorkerInitialization2");
    CloseDevToolsWindow();
  }
  
  class DevToolsAgentHostTest : public InProcessBrowserTest {};
  
  // Tests DevToolsAgentHost retention by its target.
  IN_PROC_BROWSER_TEST_F(DevToolsAgentHostTest, TestAgentHostReleased) {
    ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
    WebContents* web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    DevToolsAgentHost* agent_raw =
        DevToolsAgentHost::GetOrCreateFor(web_contents).get();
    const std::string agent_id = agent_raw->GetId();
    ASSERT_EQ(agent_raw, DevToolsAgentHost::GetForId(agent_id).get())
        << "DevToolsAgentHost cannot be found by id";
    browser()->tab_strip_model()->
        CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
    ASSERT_FALSE(DevToolsAgentHost::GetForId(agent_id).get())
        << "DevToolsAgentHost is not released when the tab is closed";
  }
  
  class RemoteDebuggingTest : public extensions::ExtensionApiTest {
    void SetUpCommandLine(base::CommandLine* command_line) override {
      extensions::ExtensionApiTest::SetUpCommandLine(command_line);
      command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort, "9222");
  
      // Override the extension root path.
      base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
      test_data_dir_ = test_data_dir_.AppendASCII("devtools");
    }
  };
  
  // Fails on CrOS. crbug.com/431399
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  #define MAYBE_RemoteDebugger DISABLED_RemoteDebugger
  #else
  // TODO(crbug.com/997911): Flaky on all platforms.
  #define MAYBE_RemoteDebugger DISABLED_RemoteDebugger
  #endif
  IN_PROC_BROWSER_TEST_F(RemoteDebuggingTest, MAYBE_RemoteDebugger) {
    ASSERT_TRUE(RunExtensionTest("target_list")) << message_;
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, PolicyDisallowed) {
    browser()->profile()->GetPrefs()->SetInteger(
        prefs::kDevToolsAvailability,
        static_cast<int>(
            policy::DeveloperToolsPolicyHandler::Availability::kDisallowed));
    ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
    content::WebContents* web_contents =
        browser()->tab_strip_model()->GetWebContentsAt(0);
    DevToolsWindow::OpenDevToolsWindow(web_contents);
    auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
    ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
  }
  
  class DevToolsExtensionForceInstallTest : public extensions::ExtensionBrowserTest {
   public:
    // Installs an extensions, emulating that it has been force-installed by
    // policy.
    // Contains assertions - callers should wrap calls of this method in
    // |ASSERT_NO_FATAL_FAILURE|.
    void ForceInstallExtension(std::string* extension_id) {
      base::FilePath crx_path;
      base::PathService::Get(chrome::DIR_TEST_DATA, &crx_path);
      crx_path = crx_path.AppendASCII("devtools")
                     .AppendASCII("extensions")
                     .AppendASCII("options.crx");
      const Extension* extension = InstallExtension(
          crx_path, 1,
          extensions::mojom::ManifestLocation::kExternalPolicyDownload);
      ASSERT_TRUE(extension);
      *extension_id = extension->id();
    }
  
    // Same as above, but also fills |*out_web_contents| with a |WebContents|
    // that has been navigated to the force-installed extension.
    void ForceInstallExtensionAndOpen(content::WebContents** out_web_contents) {
      std::string extension_id;
      ForceInstallExtension(&extension_id);
      GURL url("chrome-extension://" + extension_id + "/options.html");
      ui_test_utils::NavigateToURL(browser(), url);
      content::WebContents* web_contents =
          browser()->tab_strip_model()->GetWebContentsAt(0);
      *out_web_contents = web_contents;
    }
  };
  
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionForceInstallTest,
                         PolicyDisallowedForForceInstalledExtensions) {
    browser()->profile()->GetPrefs()->SetInteger(
        prefs::kDevToolsAvailability,
        static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability::
                             kDisallowedForForceInstalledExtensions));
  
    content::WebContents* web_contents = nullptr;
    ASSERT_NO_FATAL_FAILURE(ForceInstallExtensionAndOpen(&web_contents));
  
    DevToolsWindow::OpenDevToolsWindow(web_contents);
    auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
    ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
  }
  
  IN_PROC_BROWSER_TEST_F(
      DevToolsExtensionForceInstallTest,
      PolicyDisallowedForForceInstalledExtensionsAfterNavigation) {
    browser()->profile()->GetPrefs()->SetInteger(
        prefs::kDevToolsAvailability,
        static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability::
                             kDisallowedForForceInstalledExtensions));
  
    std::string extension_id;
    ASSERT_NO_FATAL_FAILURE(ForceInstallExtension(&extension_id));
    content::WebContents* web_contents =
        browser()->tab_strip_model()->GetWebContentsAt(0);
  
    // It's possible to open DevTools for about:blank.
    ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
    DevToolsWindow::OpenDevToolsWindow(web_contents);
    auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
    ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
  
    // Navigating to extension page should close DevTools.
    ui_test_utils::NavigateToURL(
        browser(), GURL("chrome-extension://" + extension_id + "/options.html"));
    ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
  }
  
  class DevToolsAllowedByCommandLineSwitch : public DevToolsExtensionForceInstallTest {
   public:
    void SetUpCommandLine(base::CommandLine* command_line) override {
      extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
      // Same as |chromeos::switches::kForceDevToolsAvailable|, but used as a
      // literal here so it's possible to verify that the switch does not apply on
      // non-ChromeOS platforms.
      const std::string kForceDevToolsAvailableBase = "force-devtools-available";
  #if BUILDFLAG(IS_CHROMEOS_ASH)
      ASSERT_EQ(kForceDevToolsAvailableBase,
                chromeos::switches::kForceDevToolsAvailable);
  #endif
      command_line->AppendSwitch("--" + kForceDevToolsAvailableBase);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(DevToolsAllowedByCommandLineSwitch,
                         SwitchOverridesPolicyOnChromeOS) {
    browser()->profile()->GetPrefs()->SetInteger(
        prefs::kDevToolsAvailability,
        static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability::
                             kDisallowedForForceInstalledExtensions));
  
    content::WebContents* web_contents = nullptr;
    ASSERT_NO_FATAL_FAILURE(ForceInstallExtensionAndOpen(&web_contents));
  
    DevToolsWindow::OpenDevToolsWindow(web_contents);
    auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
  #if BUILDFLAG(IS_CHROMEOS_ASH)
    ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
  #else
    ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
  #endif
  }
  
  class DevToolsPixelOutputTests : public DevToolsTest {
   public:
    void SetUp() override {
      EnablePixelOutput();
      DevToolsTest::SetUp();
    }
    void SetUpCommandLine(base::CommandLine* command_line) override {
      command_line->AppendSwitch(switches::kUseGpuInTests);
    }
  };
  
  // This test enables switches::kUseGpuInTests which causes false positives
  // with MemorySanitizer. This is also flakey on many configurations.
  // See https://crbug.com/510291
  IN_PROC_BROWSER_TEST_F(DevToolsPixelOutputTests,
                         DISABLED_TestScreenshotRecording) {
    RunTest("testScreenshotRecording", kArbitraryPage);
  }
  
  // This test enables switches::kUseGpuInTests which causes false positives
  // with MemorySanitizer.
  // Flaky on multiple platforms https://crbug.com/624215
  IN_PROC_BROWSER_TEST_F(DevToolsPixelOutputTests,
                         DISABLED_TestLatencyInfoInstrumentation) {
    WebContents* web_contents = GetInspectedTab();
    OpenDevToolsWindow(kLatencyInfoTestPage, false);
    DispatchAndWait("startTimeline");
  
    for (int i = 0; i < 3; ++i) {
      SimulateMouseEvent(web_contents, blink::WebInputEvent::Type::kMouseMove,
                         gfx::Point(30, 60));
      DispatchInPageAndWait("waitForEvent", "mousemove");
    }
  
    SimulateMouseClickAt(web_contents, 0,
                         blink::WebPointerProperties::Button::kLeft,
                         gfx::Point(30, 60));
    DispatchInPageAndWait("waitForEvent", "click");
  
    SimulateMouseWheelEvent(web_contents, gfx::Point(300, 100),
                            gfx::Vector2d(0, 120),
                            blink::WebMouseWheelEvent::kPhaseBegan);
    DispatchInPageAndWait("waitForEvent", "wheel");
  
    SimulateTapAt(web_contents, gfx::Point(30, 60));
    DispatchInPageAndWait("waitForEvent", "gesturetap");
  
    DispatchAndWait("stopTimeline");
    RunTestMethod("checkInputEventsPresent", "MouseMove", "MouseDown",
                  "MouseWheel", "GestureTap");
  
    CloseDevToolsWindow();
  }
  
  class DevToolsNetInfoTest : public DevToolsTest {
   protected:
    void SetUpCommandLine(base::CommandLine* command_line) override {
      command_line->AppendSwitch(switches::kEnableNetworkInformationDownlinkMax);
      command_line->AppendSwitch(
          switches::kEnableExperimentalWebPlatformFeatures);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(DevToolsNetInfoTest, EmulateNetworkConditions) {
    RunTest("testEmulateNetworkConditions", kEmulateNetworkConditionsPage);
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsNetInfoTest, OfflineNetworkConditions) {
    RunTest("testOfflineNetworkConditions", kEmulateNetworkConditionsPage);
  }
  
  class StaticURLDataSource : public content::URLDataSource {
   public:
    StaticURLDataSource(const std::string& source, const std::string& content)
        : source_(source), content_(content) {}
    ~StaticURLDataSource() override = default;
  
    // content::URLDataSource:
    std::string GetSource() override { return source_; }
    void StartDataRequest(const GURL& url,
                          const content::WebContents::Getter& wc_getter,
                          GotDataCallback callback) override {
      std::string data(content_);
      std::move(callback).Run(base::RefCountedString::TakeString(&data));
    }
    std::string GetMimeType(const std::string& path) override {
      return "text/html";
    }
    bool ShouldAddContentSecurityPolicy() override { return false; }
  
   private:
    const std::string source_;
    const std::string content_;
  
    DISALLOW_COPY_AND_ASSIGN(StaticURLDataSource);
  };
  
  class MockWebUIProvider
      : public TestChromeWebUIControllerFactory::WebUIProvider {
   public:
    MockWebUIProvider(const std::string& source, const std::string& content)
        : source_(source), content_(content) {}
    ~MockWebUIProvider() override = default;
  
    std::unique_ptr<content::WebUIController> NewWebUI(content::WebUI* web_ui,
                                                       const GURL& url) override {
      content::URLDataSource::Add(
          Profile::FromWebUI(web_ui),
          std::make_unique<StaticURLDataSource>(source_, content_));
      return std::make_unique<content::WebUIController>(web_ui);
    }
  
   private:
    std::string source_;
    std::string content_;
    DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider);
  };
  
  // This tests checks that window is correctly initialized when DevTools is
  // opened while navigation through history with forward and back actions.
  // (crbug.com/627407)
  IN_PROC_BROWSER_TEST_F(DevToolsTest,
                         TestWindowInitializedOnNavigateBack) {
    TestChromeWebUIControllerFactory test_factory;
    content::ScopedWebUIControllerFactoryRegistration factory_registration(
        &test_factory);
    MockWebUIProvider mock_provider("dummyurl",
                                    "<script>\n"
                                    "  window.abc = 239;\n"
                                    "  console.log(abc);\n"
                                    "</script>");
    test_factory.AddFactoryOverride(GURL("chrome://dummyurl").host(),
                                    &mock_provider);
  
    ui_test_utils::NavigateToURL(browser(), GURL("chrome://dummyurl"));
    DevToolsWindow* window =
        DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), true);
    chrome::DuplicateTab(browser());
    chrome::SelectPreviousTab(browser());
    ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
    chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
    RunTestFunction(window, "testWindowInitializedOnNavigateBack");
  
    DevToolsWindowTesting::CloseDevToolsWindowSync(window);
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, TestRawHeadersWithRedirectAndHSTS) {
    net::EmbeddedTestServer https_test_server(
        net::EmbeddedTestServer::TYPE_HTTPS);
    https_test_server.SetSSLConfig(
        net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
    https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
    ASSERT_TRUE(https_test_server.Start());
    GURL https_url = https_test_server.GetURL("localhost", "/devtools/image.png");
    base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
    bool include_subdomains = false;
    mojo::ScopedAllowSyncCallForTesting allow_sync_call;
    content::StoragePartition* partition =
        browser()->profile()->GetDefaultStoragePartition();
    base::RunLoop run_loop;
    partition->GetNetworkContext()->AddHSTS(
        https_url.host(), expiry, include_subdomains, run_loop.QuitClosure());
    run_loop.Run();
  
    OpenDevToolsWindow(kArbitraryPage, false);
  
    net::EmbeddedTestServer test_server2;
    test_server2.AddDefaultHandlers();
    ASSERT_TRUE(test_server2.Start());
    GURL::Replacements replace_scheme;
    replace_scheme.SetSchemeStr("http");
    GURL http_url = https_url.ReplaceComponents(replace_scheme);
    GURL redirect_url =
        test_server2.GetURL("/server-redirect?" + http_url.spec());
  
    DispatchOnTestSuite(window_, "testRawHeadersWithHSTS",
                        redirect_url.spec().c_str());
    CloseDevToolsWindow();
  }
  
  // Tests that OpenInNewTab filters URLs.
  // TODO(https://crbug.com/1127051): Flaky on Windows.
  #if defined(OS_WIN)
  #define MAYBE_TestOpenInNewTabFilter DISABLED_TestOpenInNewTabFilter
  #else
  #define MAYBE_TestOpenInNewTabFilter TestOpenInNewTabFilter
  #endif
  IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestOpenInNewTabFilter) {
    OpenDevToolsWindow(kDebuggerTestPage, false);
    DevToolsUIBindings::Delegate* bindings_delegate_ =
        static_cast<DevToolsUIBindings::Delegate*>(window_);
    std::string test_url =
        embedded_test_server()->GetURL(kDebuggerTestPage).spec();
    const std::string self_blob_url =
        base::StringPrintf("blob:%s", test_url.c_str());
    const std::string self_filesystem_url =
        base::StringPrintf("filesystem:%s", test_url.c_str());
  
    // Pairs include a URL string and boolean whether it should be allowed.
    std::vector<std::pair<const std::string, const std::string>> tests = {
        {test_url, test_url},
        {"data:,foo", "data:,foo"},
        {"about://inspect", "about:blank"},
        {"chrome://inspect", "about:blank"},
        {"chrome://inspect/#devices", "about:blank"},
        {self_blob_url, self_blob_url},
        {"blob:chrome://inspect", "about:blank"},
        {self_filesystem_url, self_filesystem_url},
        {"filesystem:chrome://inspect", "about:blank"},
        {"view-source:http://chromium.org", "about:blank"},
        {"file:///", "about:blank"},
        {"about://gpu", "about:blank"},
        {"chrome://gpu", "about:blank"},
        {"chrome://crash", "about:blank"},
        {"", "about:blank"},
    };
  
    TabStripModel* tabs = browser()->tab_strip_model();
    int i = 0;
    for (const std::pair<const std::string, const std::string>& pair : tests) {
      bindings_delegate_->OpenInNewTab(pair.first);
      i++;
  
      std::string opened_url = tabs->GetWebContentsAt(i)->GetVisibleURL().spec();
      SCOPED_TRACE(
          base::StringPrintf("while testing URL: %s", pair.first.c_str()));
      EXPECT_EQ(opened_url, pair.second);
    }
  
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, LoadNetworkResourceForFrontend) {
    std::string file_url =
        "file://" + base::PathService::CheckedGet(base::DIR_SOURCE_ROOT)
                        .AppendASCII("content/test/data/devtools/navigation.html")
                        .NormalizePathSeparatorsTo('/')
                        .AsUTF8Unsafe();
  
    GURL url(embedded_test_server()->GetURL("/"));
    ui_test_utils::NavigateToURL(browser(),
                                 embedded_test_server()->GetURL("/hello.html"));
    window_ =
        DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), false);
    RunTestMethod("testLoadResourceForFrontend", url.spec().c_str(),
                  file_url.c_str());
    DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
  }
  
  // TODO(crbug.com/921608) Disabled for flakiness.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_CreateBrowserContext) {
    GURL url(embedded_test_server()->GetURL("/devtools/empty.html"));
    window_ = DevToolsWindowTesting::OpenDiscoveryDevToolsWindowSync(
        browser()->profile());
    RunTestMethod("testCreateBrowserContext", url.spec().c_str());
    DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
  }
  
  // TODO(crbug.com/1110417): Flaky.
  IN_PROC_BROWSER_TEST_F(DevToolsTest,
                         DISABLED_DisposeEmptyBrowserContext) {
    window_ = DevToolsWindowTesting::OpenDiscoveryDevToolsWindowSync(
        browser()->profile());
    RunTestMethod("testDisposeEmptyBrowserContext");
    DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
  }
  
  // TODO(1078348): Find a better strategy for testing protocol methods against
  // non-headless Chrome.
  IN_PROC_BROWSER_TEST_F(DevToolsTest, NewWindowFromBrowserContext) {
    window_ = DevToolsWindowTesting::OpenDiscoveryDevToolsWindowSync(
        browser()->profile());
    RunTestMethod("testNewWindowFromBrowserContext");
    DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
  }
  
  IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsTest, InspectElement) {
    GURL url(embedded_test_server()->GetURL("a.com", "/devtools/oopif.html"));
    GURL iframe_url(
        embedded_test_server()->GetURL("b.com", "/devtools/oopif_frame.html"));
  
    WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
  
    content::TestNavigationManager navigation_manager(tab, url);
    content::TestNavigationManager navigation_manager_iframe(tab, iframe_url);
  
    tab->GetController().LoadURL(url, content::Referrer(),
                                 ui::PAGE_TRANSITION_LINK, std::string());
  
    navigation_manager.WaitForNavigationFinished();
    navigation_manager_iframe.WaitForNavigationFinished();
    EXPECT_TRUE(content::WaitForLoadStop(tab));
  
    std::vector<RenderFrameHost*> frames = GetInspectedTab()->GetAllFrames();
    ASSERT_EQ(2u, frames.size());
    ASSERT_NE(frames[0]->GetProcess(), frames[1]->GetProcess());
    RenderFrameHost* frame_host = frames[0]->GetParent() ? frames[0] : frames[1];
  
    DevToolsWindowCreationObserver observer;
    DevToolsWindow::InspectElement(frame_host, 100, 100);
    observer.WaitForLoad();
    DevToolsWindow* window = observer.devtools_window();
  
    DispatchOnTestSuite(window, "testInspectedElementIs", "INSPECTED-DIV");
    DevToolsWindowTesting::CloseDevToolsWindowSync(window);
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsTest, ExistsForWebContentsAfterClosing) {
    ASSERT_FALSE(content::DevToolsAgentHost::HasFor(GetInspectedTab()));
  
    // Simulate opening devtools for the current tab.
    OpenDevToolsWindow(kDebuggerTestPage, true);
    ASSERT_TRUE(content::DevToolsAgentHost::HasFor(GetInspectedTab()));
  
    // Closes devtools window for the current tab i.e. exit the devtools
    // inspector.
    CloseDevToolsWindow();
  
    // The devtools window instance still exists for the current tab even though
    // it is now closed.
    ASSERT_TRUE(content::DevToolsAgentHost::HasFor(GetInspectedTab()));
  }
  
  IN_PROC_BROWSER_TEST_F(InProcessBrowserTest, BrowserCloseWithBeforeUnload) {
    EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsOriginRegistered(
        KeepAliveOrigin::REMOTE_DEBUGGING));
    ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
    WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
    ASSERT_TRUE(content::ExecuteScript(
        tab,
        "window.addEventListener('beforeunload',"
        "function(event) { event.returnValue = 'Foo'; });"));
    content::PrepContentsForBeforeUnloadTest(tab);
    BrowserHandler handler(nullptr, std::string());
    handler.Close();
    ui_test_utils::WaitForBrowserToClose(browser());
  }
  
  // Flaky.
  // TODO(https://crbug.com/1132296): Re-enable.
  IN_PROC_BROWSER_TEST_F(InProcessBrowserTest,
                         DISABLED_BrowserCloseWithContextMenuOpened) {
    EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsOriginRegistered(
        KeepAliveOrigin::REMOTE_DEBUGGING));
    ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
    auto callback = [](RenderViewContextMenu* context_menu) {
      BrowserHandler handler(nullptr, std::string());
      handler.Close();
    };
    WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
    RenderViewContextMenu::RegisterMenuShownCallbackForTesting(
        base::BindOnce(callback));
    content::SimulateMouseClickAt(tab, 0, blink::WebMouseEvent::Button::kRight,
                                  gfx::Point(15, 15));
    ui_test_utils::WaitForBrowserToClose(browser());
  }
  
  #if !BUILDFLAG(IS_CHROMEOS_ASH)
  // Skip for ChromeOS because the keep alive is not created for ChromeOS.
  // See https://crbug.com/1174627.
  class KeepAliveDevToolsTest : public InProcessBrowserTest {
   protected:
    void SetUpCommandLine(base::CommandLine* command_line) override {
      command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort, "0");
      command_line->AppendSwitch(switches::kNoStartupWindow);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(KeepAliveDevToolsTest, KeepsAliveUntilBrowserClose) {
    EXPECT_FALSE(browser_shutdown::IsTryingToQuit());
    EXPECT_TRUE(BrowserList::GetInstance()->empty());
    EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
    EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsOriginRegistered(
        KeepAliveOrigin::REMOTE_DEBUGGING));
    chrome::NewEmptyWindow(ProfileManager::GetLastUsedProfile());
    EXPECT_FALSE(BrowserList::GetInstance()->empty());
    BrowserHandler handler(nullptr, std::string());
    handler.Close();
    ui_test_utils::WaitForBrowserToClose();
    EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
    EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsOriginRegistered(
        KeepAliveOrigin::REMOTE_DEBUGGING));
  }
  #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
  
  class DevToolsPolicyTest : public InProcessBrowserTest {
   protected:
    DevToolsPolicyTest() {
      ON_CALL(provider_, IsInitializationComplete(testing::_))
          .WillByDefault(testing::Return(true));
      ON_CALL(provider_, IsFirstPolicyLoadComplete(testing::_))
          .WillByDefault(testing::Return(true));
    }
  
    void SetUpInProcessBrowserTestFixture() override {
      policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
    }
    testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
  };
  
  IN_PROC_BROWSER_TEST_F(DevToolsPolicyTest, OpenBlackListedDevTools) {
    base::ListValue blacklist;
    blacklist.AppendString("devtools://*");
    policy::PolicyMap policies;
    policies.Set(policy::key::kURLBlacklist, policy::POLICY_LEVEL_MANDATORY,
                 policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
                 blacklist.Clone(), nullptr);
    provider_.UpdateChromePolicy(policies);
  
    WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
    scoped_refptr<content::DevToolsAgentHost> agent(
        content::DevToolsAgentHost::GetOrCreateFor(wc));
    DevToolsWindow::OpenDevToolsWindow(wc);
    DevToolsWindow* window = DevToolsWindow::FindDevToolsWindow(agent.get());
    if (window) {
      base::RunLoop run_loop;
      DevToolsWindowTesting::Get(window)->SetCloseCallback(
          run_loop.QuitClosure());
      run_loop.Run();
    }
    window = DevToolsWindow::FindDevToolsWindow(agent.get());
    ASSERT_EQ(nullptr, window);
  }
  
  // Flaky on Mus. See https://crbug.com/819285.
  IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsTest,
                         DISABLED_InputDispatchEventsToOOPIF) {
    GURL url(
        embedded_test_server()->GetURL("a.com", "/devtools/oopif-input.html"));
    GURL iframe_url(embedded_test_server()->GetURL(
        "b.com", "/devtools/oopif-input-frame.html"));
  
    WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
  
    content::TestNavigationManager navigation_manager(tab, url);
    content::TestNavigationManager navigation_manager_iframe(tab, iframe_url);
  
    tab->GetController().LoadURL(url, content::Referrer(),
                                 ui::PAGE_TRANSITION_LINK, std::string());
  
    navigation_manager.WaitForNavigationFinished();
    navigation_manager_iframe.WaitForNavigationFinished();
    EXPECT_TRUE(content::WaitForLoadStop(tab));
  
    for (auto* frame : GetInspectedTab()->GetAllFrames()) {
      content::WaitForHitTestData(frame);
    }
    DevToolsWindow* window =
        DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), false);
    RunTestFunction(window, "testInputDispatchEventsToOOPIF");
    DevToolsWindowTesting::CloseDevToolsWindowSync(window);
  }
  
  // See https://crbug.com/971241
  IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
                         DISABLED_ExtensionWebSocketUserAgentOverride) {
    net::SpawnedTestServer websocket_server(
        net::SpawnedTestServer::TYPE_WS,
        base::FilePath(FILE_PATH_LITERAL("net/data/websocket")));
    websocket_server.set_websocket_basic_auth(false);
    ASSERT_TRUE(websocket_server.Start());
    uint16_t websocket_port = websocket_server.host_port_pair().port();
  
    LoadExtension("web_request");
    OpenDevToolsWindow(kEmptyTestPage, /* is_docked */ false);
    DispatchOnTestSuite(window_, "testExtensionWebSocketUserAgentOverride",
                        std::to_string(websocket_port).c_str());
    CloseDevToolsWindow();
  }
  
  namespace {
  
  class DevToolsLocalizationTest : public DevToolsTest {
   public:
    bool NavigatorLanguageMatches(const std::string& expected_locale) {
      bool result = false;
      const bool execute_result = content::ExecuteScriptAndExtractBool(
          main_web_contents(),
          "window.domAutomationController.send(window.navigator.language === "
          "'" +
              expected_locale + "')",
          &result);
      return execute_result && result;
    }
  };
  
  }  // namespace
  
  IN_PROC_BROWSER_TEST_F(DevToolsLocalizationTest,
                         NavigatorLanguageMatchesApplicationLocaleDocked) {
    g_browser_process->SetApplicationLocale("es");
  
    OpenDevToolsWindow("about:blank", /* is_docked */ true);
    EXPECT_TRUE(NavigatorLanguageMatches("es"));
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsLocalizationTest,
                         NavigatorLanguageMatchesApplicationLocaleUndocked) {
    g_browser_process->SetApplicationLocale("es");
  
    OpenDevToolsWindow("about:blank", /* is_docked */ false);
    EXPECT_TRUE(NavigatorLanguageMatches("es"));
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsLocalizationTest,
                         AcceptedLanguageChangesWhileDevToolsIsOpen) {
    g_browser_process->SetApplicationLocale("es");
  
    OpenDevToolsWindow("about:blank", true);
    EXPECT_TRUE(NavigatorLanguageMatches("es"));
  
    PrefService* prefs = browser()->profile()->GetPrefs();
    prefs->SetString(language::prefs::kAcceptLanguages, "de-DE");
  
    EXPECT_TRUE(NavigatorLanguageMatches("es"));
  
    CloseDevToolsWindow();
  }
  
  namespace {
  
  class DevToolsFetchTest : public DevToolsTest {
   protected:
    content::EvalJsResult Fetch(
        const content::ToRenderFrameHost& execution_target,
        const std::string& url) {
      return content::EvalJs(execution_target, content::JsReplace(R"(
        (async function() {
          const response = await fetch($1);
          return response.status;
        })();
      )",
                                                                  url));
    }
  
    content::EvalJsResult FetchFromDevToolsWindow(const std::string& url) {
      WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
      return Fetch(wc, url);
    }
  };
  
  }  // namespace
  
  IN_PROC_BROWSER_TEST_F(DevToolsFetchTest,
                         DevToolsFetchFromDevToolsSchemeUndocked) {
    OpenDevToolsWindow("about:blank", false);
  
    EXPECT_EQ(200, FetchFromDevToolsWindow(
                       "devtools://devtools/bundled/Images/whatsnew.avif"));
  
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsFetchTest,
                         DevToolsFetchFromDevToolsSchemeDocked) {
    OpenDevToolsWindow("about:blank", true);
  
    EXPECT_EQ(200, FetchFromDevToolsWindow(
                       "devtools://devtools/bundled/Images/whatsnew.avif"));
  
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsFetchTest, DevToolsFetchFromHttpDisallowed) {
    OpenDevToolsWindow("about:blank", true);
  
    const auto result = FetchFromDevToolsWindow("http://www.google.com");
    EXPECT_EQ("a JavaScript error:\nTypeError: Failed to fetch\n", result.error);
  
    CloseDevToolsWindow();
  }
  
  IN_PROC_BROWSER_TEST_F(DevToolsFetchTest, FetchFromDevToolsSchemeIsProhibited) {
    ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
  
    const auto result = Fetch(GetInspectedTab(),
                              "devtools://devtools/bundled/Images/whatsnew.avif");
    EXPECT_EQ("a JavaScript error:\nTypeError: Failed to fetch\n", result.error);
  }
  