blob: 9e8a2e4c8ae492ce0dfa30e224dc8f0596f171f3 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <deque>
#include <memory>
#include <optional>
#include "base/cancelable_callback.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/devtools/device/tcp_device_provider.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/devtools/protocol/browser_handler.h"
#include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
#include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_management_constants.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_desktop.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/policy/profile_policy_connector.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/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/ui/autofill/autofill_suggestion_controller.h"
#include "chrome/browser/ui/autofill/chrome_autofill_client.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/extensions/extension_side_panel_test_utils.h"
#include "chrome/browser/ui/tabs/tab_enums.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/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/infobar.h"
#include "components/infobars/core/infobar_delegate.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 "components/search_engines/template_url_service.h"
#include "components/signin/public/identity_manager/account_capabilities_test_mutator.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/variations/service/variations_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_service.h"
#include "content/public/browser/notification_types.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_features.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/api_test_utils.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/offscreen_document_host.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/manifest.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "net/base/filename_util.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/network_context.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.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"
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 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();
const char* args_array[] = {method, args...};
std::ostringstream script;
script << "uiTests.dispatchOnTestSuite([";
for (size_t i = 0; i < std::size(args_array); ++i) {
script << (i ? "," : "") << '\"' << args_array[i] << '\"';
}
script << "])";
content::DOMMessageQueue message_queue;
EXPECT_TRUE(content::ExecJs(wc, script.str()));
std::string result;
EXPECT_TRUE(message_queue.WaitForMessage(&result));
EXPECT_EQ("\"[OK]\"", result);
}
void LoadLegacyFilesInFrontend(DevToolsWindow* window) {
WebContents* wc = DevToolsWindowTesting::Get(window)->main_web_contents();
content::DOMMessageQueue message_queue;
EXPECT_TRUE(content::ExecJs(wc, "uiTests.setupLegacyFilesForTest();",
content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
std::string result;
EXPECT_TRUE(message_queue.WaitForMessage(&result));
ASSERT_EQ("\"[OK]\"", result);
}
template <typename... T>
void DispatchOnTestSuite(DevToolsWindow* window,
const char* method,
T... args) {
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_EQ(
"function",
content::EvalJs(
wc, "'' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite))"))
<< "DevTools front-end is broken.";
LoadLegacyFilesInFrontend(window);
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());
}
void DisallowDevToolsForForceInstalledExtenions(Browser* browser) {
browser->profile()->GetPrefs()->SetInteger(
prefs::kDevToolsAvailability,
static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability::
kDisallowedForForceInstalledExtensions));
}
void DisallowDevTools(Browser* browser) {
browser->profile()->GetPrefs()->SetInteger(
prefs::kDevToolsAvailability,
static_cast<int>(
policy::DeveloperToolsPolicyHandler::Availability::kDisallowed));
}
void AllowDevTools(Browser* browser) {
browser->profile()->GetPrefs()->SetInteger(
prefs::kDevToolsAvailability,
static_cast<int>(
policy::DeveloperToolsPolicyHandler::Availability::kAllowed));
}
scoped_refptr<DevToolsAgentHost> GetOrCreateDevToolsHostForWebContents(
WebContents* wc) {
return base::FeatureList::IsEnabled(::features::kDevToolsTabTarget)
? content::DevToolsAgentHost::GetOrCreateForTab(wc)
: content::DevToolsAgentHost::GetOrCreateFor(wc);
}
} // 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);
}
ASSERT_TRUE(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();
}
raw_ptr<DevToolsWindow, DanglingUntriaged> 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*);
DevToolsWindowBeforeUnloadObserver(
const DevToolsWindowBeforeUnloadObserver&) = delete;
DevToolsWindowBeforeUnloadObserver& operator=(
const DevToolsWindowBeforeUnloadObserver&) = delete;
void Wait();
private:
// Invoked when the beforeunload handler fires.
void BeforeUnloadFired(bool proceed) override;
bool m_fired;
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
};
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) {
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,
TabCloseTypes::CLOSE_NONE);
}
void CloseDevToolsWindowAsync() {
DevToolsWindowTesting::CloseDevToolsWindow(window_);
}
void CloseInspectedBrowser() { chrome::CloseWindow(browser()); }
protected:
void InjectBeforeUnloadListener(content::WebContents* web_contents) {
ASSERT_TRUE(
content::ExecJs(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) {
ASSERT_TRUE(content::ExecJs(
DevToolsWindowTesting::Get(devtools_window)->main_web_contents(),
"window.open(\"\", \"\", \"location=0\");"));
Browser* popup_browser = BrowserList::GetInstance()->GetLastActive();
WebContents* popup_contents =
popup_browser->tab_strip_model()->GetActiveWebContents();
content::WaitForLoadStop(popup_contents);
}
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;
}
};
constexpr char kPublicKey[] =
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8c4fBSPZ6utYoZ8NiWF/"
"DSaimBhihjwgOsskyleFGaurhi3TDClTVSGPxNkgCzrz0wACML7M4aNjpd05qupdbR2d294j"
"kDuI7caxEGUucpP7GJRRHnm8Sx+"
"y0ury28n8jbN0PnInKKWcxpIXXmNQyC19HBuO3QIeUq9Dqc+7YFQIDAQAB";
// Base class for DevTools tests that test devtools functionality for
// extensions and content scripts.
class DevToolsExtensionTest : public DevToolsTest {
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,
bool allow_file_access = false) {
extensions::ExtensionService* service =
extensions::ExtensionSystem::Get(browser()->profile())
->extension_service();
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(browser()->profile());
extensions::TestExtensionRegistryObserver observer(registry);
auto installer = extensions::UnpackedInstaller::Create(service);
installer->set_allow_file_access(allow_file_access);
installer->Load(path);
observer.WaitForExtensionLoaded();
// Wait for any additional extension views to load.
extensions::ChromeExtensionTestNotificationObserver(browser())
.WaitForExtensionViewsToLoad();
return GetExtensionByPath(registry->enabled_extensions(), path);
}
base::Value::Dict BuildExtensionManifest(
const std::string& name,
const std::string& devtools_page = "",
const std::string& key = "") {
auto manifest = base::Value::Dict()
.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",
base::Value::List()
.Append("simple_test_page.html")
.Append("source.map"));
// If |devtools_page| isn't empty, make it a devtools extension in the
// manifest.
if (!devtools_page.empty()) {
manifest.Set("devtools_page", devtools_page);
}
if (!key.empty()) {
manifest.Set("key", key);
}
return manifest;
}
// Builds an 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.
extensions::TestExtensionDir& BuildExtensionForTest(
const std::string& name,
const std::string& devtools_page,
const std::string& panel_iframe_src) {
test_extension_dirs_.emplace_back();
extensions::TestExtensionDir& dir = test_extension_dirs_.back();
dir.WriteManifest(BuildExtensionManifest(name, devtools_page));
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("source.map"),
R"({"version":3,"sources":["foo.js"],"mappings":"AAyCAA"})");
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>");
return dir;
}
// Loads a dynamically generated extension populated with a bunch of test
// pages.
const Extension* LoadExtensionForTest(const std::string& name,
const std::string& devtools_page,
const std::string& panel_iframe_src) {
extensions::TestExtensionDir& dir =
BuildExtensionForTest(name, devtools_page, panel_iframe_src);
return LoadExtensionFromPath(dir.UnpackedPath());
}
std::string BuildComponentExtension() {
extensions::ExtensionService* extension_service =
extensions::ExtensionSystem::Get(browser()->profile())
->extension_service();
extensions::ComponentLoader* component_loader =
extension_service->component_loader();
extensions::ExtensionRegistry* extension_registry =
extensions::ExtensionRegistry::Get(browser()->profile());
extensions::TestExtensionDir& extension_dir =
BuildExtensionForTest("Component extension", "" /* devtools_page */,
"" /* panel_iframe_src */);
auto manifest = BuildExtensionManifest("Component extension",
"" /* devtools_page */, kPublicKey);
component_loader->set_ignore_allowlist_for_testing(true);
std::string extension_id = component_loader->Add(
std::move(manifest), extension_dir.UnpackedPath());
EXPECT_TRUE(extension_registry->enabled_extensions().GetByID(extension_id));
return extension_id;
}
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;
}
// Use std::deque to avoid dangling references to existing elements.
std::deque<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 DevToolsServiceWorkerExtensionTest : public InProcessBrowserTest {
protected:
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
Profile* profile = browser()->profile();
extension_service_ =
extensions::ExtensionSystem::Get(profile)->extension_service();
extension_registry_ = extensions::ExtensionRegistry::Get(profile);
}
const extensions::Extension* LoadExtension(base::FilePath extension_path) {
extensions::TestExtensionRegistryObserver observer(extension_registry_);
ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED");
extensions::UnpackedInstaller::Create(extension_service_)
->Load(extension_path);
observer.WaitForExtensionLoaded();
const extensions::Extension* extension = nullptr;
for (const auto& enabled_extension :
extension_registry_->enabled_extensions()) {
if (enabled_extension->path() == extension_path) {
extension = enabled_extension.get();
break;
}
}
CHECK(extension) << "Failed to find loaded extension " << extension_path;
EXPECT_TRUE(activated_listener.WaitUntilSatisfied());
return extension;
}
scoped_refptr<DevToolsAgentHost> FindExtensionHost(const std::string& id) {
for (auto& host : DevToolsAgentHost::GetOrCreateAll()) {
if (host->GetType() == DevToolsAgentHost::kTypeServiceWorker &&
host->GetURL().host() == id) {
return host;
}
}
return nullptr;
}
void OpenDevToolsWindow(scoped_refptr<DevToolsAgentHost> host) {
Profile* profile = browser()->profile();
window_ = DevToolsWindowTesting::OpenDevToolsWindowSync(profile, host);
}
void CloseDevToolsWindow() {
DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
}
raw_ptr<DevToolsWindow, DanglingUntriaged> window_ = nullptr;
raw_ptr<extensions::ExtensionService, DanglingUntriaged> extension_service_ =
nullptr;
raw_ptr<extensions::ExtensionRegistry, DanglingUntriaged>
extension_registry_ = nullptr;
};
// TODO(crbug.com/40943436): Fix the memory leak and enable the test.
#if defined(LEAK_SANITIZER) && BUILDFLAG(IS_LINUX)
#define MAYBE_AttachOnReload DISABLED_AttachOnReload
#else
#define MAYBE_AttachOnReload AttachOnReload
#endif
IN_PROC_BROWSER_TEST_F(DevToolsServiceWorkerExtensionTest,
MAYBE_AttachOnReload) {
base::FilePath extension_path =
base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
.AppendASCII("devtools")
.AppendASCII("extensions")
.AppendASCII("service_worker");
std::string extension_id;
{
const extensions::Extension* extension = LoadExtension(extension_path);
extension_id = extension->id();
}
scoped_refptr<DevToolsAgentHost> host = FindExtensionHost(extension_id);
ASSERT_TRUE(host);
OpenDevToolsWindow(host);
extension_service_->ReloadExtension(extension_id);
RunTestFunction(window_, "waitForTestResultsInConsole");
CloseDevToolsWindow();
}
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_;
raw_ptr<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());
run_loop.Run();
return host;
}
void OpenDevToolsWindow(scoped_refptr<DevToolsAgentHost> agent_host) {
Profile* profile = browser()->profile();
window_ =
DevToolsWindowTesting::OpenDevToolsWindowSync(profile, agent_host);
}
void CloseDevToolsWindow() {
DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
}
raw_ptr<DevToolsWindow, DanglingUntriaged> 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(crbug.com/40679397): Flaky on Windows.
#if BUILDFLAG(IS_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::ExecJs(
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(crbug.com/40703256): 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(crbug.com/40645764): 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 recorder panel showing.
IN_PROC_BROWSER_TEST_F(DevToolsTest, TestShowRecorderTab) {
RunTest("testShowRecorderTab", kDebuggerTestPage);
}
// Tests that chrome.devtools extension is correctly exposed.
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, TestDevToolsExtensionAPI) {
LoadExtension("devtools_extension");
RunTest("waitForTestResultsInConsole", kArbitraryPage);
}
// 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 =
CollectAllRenderFrameHosts(main_web_contents());
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()->GetPrimaryMainFrame();
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::ExecJs(web_frame_rfh, about_blank_javascript));
ASSERT_TRUE(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::ExecJs(web_frame_rfh, renavigation_javascript));
ASSERT_TRUE(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
// TODO(crbug.com/40944663): Enable once the test is fixed.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_HttpIframeInDevToolsExtensionSideBarPane \
DISABLED_HttpIframeInDevToolsExtensionSideBarPane
#else
#define MAYBE_HttpIframeInDevToolsExtensionSideBarPane \
HttpIframeInDevToolsExtensionSideBarPane
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
MAYBE_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");
ASSERT_TRUE(web_manager.WaitForNavigationFinished());
std::vector<RenderFrameHost*> rfhs =
CollectAllRenderFrameHosts(main_web_contents());
EXPECT_EQ(4U, rfhs.size());
RenderFrameHost* main_devtools_rfh =
main_web_contents()->GetPrimaryMainFrame();
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.
std::unique_ptr<content::DOMMessageQueue> message_queue;
std::string message;
// OpenDevToolsWindow() internally creates and initializes a WebContents,
// which we need to listen to messages from; to ensure that we don't miss
// the message, listen for that WebContents being created and set up a
// DOMMessageQueue for it.
{
auto subscription = content::RegisterWebContentsCreationCallback(
// Note that we only care about the first WebContents; for all
// subsequent WebContents, message_queue will already be non-null.
base::BindLambdaForTesting([&](content::WebContents* contents) {
if (!message_queue) {
message_queue =
std::make_unique<content::DOMMessageQueue>(contents);
}
}));
OpenDevToolsWindow(kDebuggerTestPage, false);
}
ASSERT_TRUE(message_queue)
<< "OpenDevToolsWindow must create at least one WebContents";
while (true) {
ASSERT_TRUE(message_queue->WaitForMessage(&message));
if (message == "\"DONE\"") {
break;
}
}
std::vector<RenderFrameHost*> rfhs =
CollectAllRenderFrameHosts(main_web_contents());
EXPECT_EQ(3U, rfhs.size());
RenderFrameHost* main_devtools_rfh =
main_web_contents()->GetPrimaryMainFrame();
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");
ASSERT_TRUE(non_devtools_manager.WaitForNavigationFinished());
std::vector<RenderFrameHost*> rfhs =
CollectAllRenderFrameHosts(main_web_contents());
EXPECT_EQ(4U, rfhs.size());
RenderFrameHost* main_devtools_rfh =
main_web_contents()->GetPrimaryMainFrame();
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.DeprecatedGetOriginAsURL(),
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");
ASSERT_TRUE(extension_b_manager.WaitForNavigationFinished());
std::vector<RenderFrameHost*> rfhs =
CollectAllRenderFrameHosts(main_web_contents());
EXPECT_EQ(5U, rfhs.size());
RenderFrameHost* main_devtools_rfh =
main_web_contents()->GetPrimaryMainFrame();
RenderFrameHost* devtools_extension_a_devtools_rfh =
content::FrameMatchingPredicate(
main_web_contents()->GetPrimaryPage(),
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()->GetPrimaryPage(),
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");
ASSERT_TRUE(test_page_manager.WaitForNavigationFinished());
std::vector<RenderFrameHost*> rfhs =
CollectAllRenderFrameHosts(main_web_contents());
EXPECT_EQ(4U, rfhs.size());
RenderFrameHost* main_devtools_rfh =
main_web_contents()->GetPrimaryMainFrame();
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 BUILDFLAG(IS_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()->GetPrimaryMainFrame();
content::TestNavigationManager manager(main_web_contents(), devtools_url);
ASSERT_TRUE(content::ExecJs(main_devtools_rfh, javascript));
ASSERT_TRUE(manager.WaitForNavigationFinished());
std::vector<RenderFrameHost*> rfhs =
CollectAllRenderFrameHosts(main_web_contents());
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 =
content::EvalJs(devtools_iframe_rfh, "self.origin").ExtractString();
EXPECT_EQ(devtools_url.DeprecatedGetOriginAsURL().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>();
dir->WriteManifest(base::Value::Dict()
.Set("name", "Devtools Panel")
.Set("version", "1")
// Allow 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"));
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(main_web_contents());
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 BUILDFLAG(IS_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.
//
// TODO(crbug.com/40933538): Flaky on "Linux Tests (dbg)(1)".
#if BUILDFLAG(IS_LINUX)
#define MAYBE_TestContentScriptIsPresent DISABLED_TestContentScriptIsPresent
#else
#define MAYBE_TestContentScriptIsPresent TestContentScriptIsPresent
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
MAYBE_TestContentScriptIsPresent) {
LoadExtension("simple_content_script");
RunTest("testContentScriptIsPresent", kPageWithContentScript);
}
// Tests that console selector shows correct context names.
// TODO(crbug.com/328131890): Test is flaky on multiple platforms. Tends to time
// out when trying to open the devtools window.
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
DISABLED_TestConsoleContextNames) {
LoadExtension("simple_content_script");
RunTest("testConsoleContextNames", kPageWithContentScript);
}
// TODO(crbug.com/40930033): Flaky on Linux and ChromeOS Tests.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_CantInspectNewTabPage DISABLED_CantInspectNewTabPage
#else
#define MAYBE_CantInspectNewTabPage CantInspectNewTabPage
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, MAYBE_CantInspectNewTabPage) {
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#chrome://newtab/"}));
}
// TODO(crbug.com/40943634): Re-enable the test once it is fixed.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_CantInspectChromeScheme DISABLED_CantInspectChromeScheme
#else
#define MAYBE_CantInspectChromeScheme CantInspectChromeScheme
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, MAYBE_CantInspectChromeScheme) {
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#chrome://version/"}));
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, CantInspectDevtoolsScheme) {
LoadExtension("can_inspect_url");
RunTest(
"waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage,
"#devtools://devtools/bundled/devtools_compatibility.js"}));
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
CantInspectViewSourceDevtoolsScheme) {
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage,
"#view-source:devtools://devtools/bundled/"
"devtools_compatibility.js"}));
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, CantInspectComponentExtension) {
std::string extension_id = BuildComponentExtension();
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#chrome-extension://", extension_id,
"/simple_test_page.html"}));
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, CantInspectRemoteNewTabPage) {
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());
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
TemplateURLData data;
data.SetShortName(u"example.com");
data.SetURL("https://example.com/url?bar={searchTerms}");
data.new_tab_url =
https_test_server.GetURL("localhost", "/devtools/empty.html").spec();
TemplateURL* template_url =
template_url_service->Add(std::make_unique<TemplateURL>(data));
template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#", data.new_tab_url}));
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
CantInspectViewSourceComponentExtension) {
std::string extension_id = BuildComponentExtension();
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#view-source:chrome-extension://",
extension_id, "/simple_test_page.html"}));
}
// Flaky on several platforms: https://crbug.com/1487065
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
DISABLED_CantInspectFileUrlWithoutFileAccess) {
LoadExtension("can_inspect_url");
std::string file_url =
net::FilePathToFileURL(
base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
.AppendASCII("content/test/data/devtools/navigation.html"))
.spec();
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#", file_url}));
}
class DevToolsExtensionSidePanelTest
: public DevToolsExtensionTest,
public ::testing::WithParamInterface<bool> {
public:
DevToolsExtensionSidePanelTest() {
if (GetParam()) {
feature_list_.InitWithFeatures(
/*enabled_features=*/{extensions_features::
kExtensionSidePanelIntegration,
::features::kDevToolsTabTarget},
/*disabled_features=*/{});
} else {
feature_list_.InitWithFeatures(
/*enabled_features=*/{extensions_features::
kExtensionSidePanelIntegration},
/*disabled_features=*/{::features::kDevToolsTabTarget});
}
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Test that an extension's side panel view is inspectable whether or not the
// `kDevToolsTabTarget` flag is enabled.
IN_PROC_BROWSER_TEST_P(DevToolsExtensionSidePanelTest,
CanInspectExtensionSidePanelView) {
base::FilePath side_panel_extension_dir =
base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
.AppendASCII("extensions/api_test/side_panel");
// Load an extension and wait for its side panel view to be shown.
scoped_refptr<const extensions::Extension> extension = LoadExtensionFromPath(
side_panel_extension_dir.AppendASCII("simple_default"));
ASSERT_TRUE(extension);
ExtensionTestMessageListener default_path_listener("default_path");
extensions::OpenExtensionSidePanel(*browser(), extension->id());
ASSERT_TRUE(default_path_listener.WaitUntilSatisfied());
content::WebContents* side_panel_contents =
extensions::GetExtensionSidePanelWebContents(*browser(), extension->id());
ASSERT_TRUE(side_panel_contents);
EXPECT_TRUE(content::WaitForLoadStop(side_panel_contents));
std::vector<RenderFrameHost*> frames =
CollectAllRenderFrameHosts(side_panel_contents);
ASSERT_EQ(1u, frames.size());
RenderFrameHost* side_panel_host = frames[0];
// Inspect the extension's side panel view and check that the top level html
// tag is inspected.
DevToolsWindowCreationObserver observer;
DevToolsWindow::InspectElement(side_panel_host, 0, 0);
observer.WaitForLoad();
DevToolsWindow* window = observer.devtools_window();
DispatchOnTestSuite(window, "testInspectedElementIs", "HTML");
DevToolsWindowTesting::CloseDevToolsWindowSync(window);
}
INSTANTIATE_TEST_SUITE_P(All,
DevToolsExtensionSidePanelTest,
::testing::Bool(),
[](const testing::TestParamInfo<
DevToolsExtensionSidePanelTest::ParamType>& info) {
return info.param ? "DevToolsTabTargetEnabled"
: "DevToolsTabTargetDisabled";
});
// TODO(crbug.com/41495883): Re-enable on linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_CanInspectExtensionOffscreenDoc \
DISABLED_CanInspectExtensionOffscreenDoc
#else
#define MAYBE_CanInspectExtensionOffscreenDoc CanInspectExtensionOffscreenDoc
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
MAYBE_CanInspectExtensionOffscreenDoc) {
static constexpr char kManifest[] =
R"({
"name": "Offscreen Document Test",
"manifest_version": 3,
"version": "0.1"
})";
extensions::TestExtensionDir test_dir;
test_dir.WriteManifest(kManifest);
test_dir.WriteFile(FILE_PATH_LITERAL("offscreen.html"),
"<html>offscreen</html>");
const extensions::Extension* extension =
LoadExtensionFromPath(test_dir.UnpackedPath());
// Create an offscreen document and wait for it to load.
GURL offscreen_url = extension->GetResourceURL("offscreen.html");
std::unique_ptr<extensions::OffscreenDocumentHost> offscreen_document =
std::make_unique<extensions::OffscreenDocumentHost>(
*extension,
extensions::ProcessManager::Get(browser()->profile())
->GetSiteInstanceForURL(offscreen_url)
.get(),
offscreen_url);
{
extensions::ExtensionHostTestHelper offscreen_waiter(browser()->profile(),
extension->id());
offscreen_waiter.RestrictToType(
extensions::mojom::ViewType::kOffscreenDocument);
offscreen_document->CreateRendererSoon();
offscreen_waiter.WaitForHostCompletedFirstLoad();
}
// Get the list of inspectable views for the extension.
auto get_info_function = base::MakeRefCounted<
extensions::api::DeveloperPrivateGetExtensionInfoFunction>();
std::optional<base::Value> result =
extensions::api_test_utils::RunFunctionAndReturnSingleResult(
get_info_function.get(),
content::JsReplace(R"([$1])", extension->id()), browser()->profile());
ASSERT_TRUE(result);
auto info =
extensions::api::developer_private::ExtensionInfo::FromValue(*result);
ASSERT_TRUE(info);
// The only inspectable view should be the offscreen document. Validate the
// metadata.
ASSERT_EQ(1u, info->views.size());
const extensions::api::developer_private::ExtensionView& view =
info->views[0];
EXPECT_EQ(extensions::api::developer_private::ViewType::kOffscreenDocument,
view.type);
content::WebContents* offscreen_contents =
offscreen_document->host_contents();
EXPECT_EQ(offscreen_url.spec(), view.url);
EXPECT_EQ(offscreen_document->render_process_host()->GetID(),
view.render_process_id);
EXPECT_EQ(offscreen_contents->GetPrimaryMainFrame()->GetRoutingID(),
view.render_view_id);
EXPECT_FALSE(view.incognito);
EXPECT_FALSE(view.is_iframe);
// The document shouldn't currently be under inspection.
EXPECT_FALSE(
DevToolsWindow::GetInstanceForInspectedWebContents(offscreen_contents));
DevToolsWindowCreationObserver observer;
// Call the API function to inspect the offscreen document.
auto dev_tools_function = base::MakeRefCounted<
extensions::api::DeveloperPrivateOpenDevToolsFunction>();
extensions::api_test_utils::RunFunction(
dev_tools_function.get(),
content::JsReplace(
R"([{"renderViewId": $1,
"renderProcessId": $2,
"extensionId": $3
}])",
view.render_view_id, view.render_process_id, extension->id()),
browser()->profile());
// Validate that the devtools window is now shown.
observer.WaitForLoad();
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
view.render_process_id, view.render_view_id);
ASSERT_TRUE(rfh);
DevToolsWindow::InspectElement(rfh, 0, 0);
DispatchOnTestSuite(observer.devtools_window(), "testInspectedElementIs",
"HTML");
}
class DevToolsExtensionFileAccessTest : public DevToolsExtensionTest {
protected:
void Run(bool allow_file_access, const std::string& url_scheme) {
extensions::TestExtensionDir dir;
dir.WriteManifest(BuildExtensionManifest("File Access", "devtools.html"));
dir.WriteFile(
FILE_PATH_LITERAL("devtools.html"),
"<html><head><script src='devtools.js'></script></head></html>");
dir.WriteFile(FILE_PATH_LITERAL("devtools.js"),
base::StringPrintf(R"(
chrome.devtools.inspectedWindow.getResources((resources) => {
const hasFile = !!resources.find(r => r.url.startsWith('file:'));
setInterval(() => {
top.postMessage(
{testOutput: (hasFile == %d) ? 'PASS' : 'FAIL'}, '*');
}, 10);
});)",
allow_file_access));
std::string file_url =
net::FilePathToFileURL(
base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
.AppendASCII("content/test/data/devtools/navigation.html"))
.spec();
const Extension* extension =
LoadExtensionFromPath(dir.UnpackedPath(), allow_file_access);
ASSERT_TRUE(extension);
std::string url = base::StringPrintf(
R"(data:text/html,<script>//%%23%%20sourceMappingURL=data:application/json,{"version":3,"sources":["%s:%s"]}</script>)",
url_scheme.c_str(), file_url.c_str() + strlen("file:///"));
OpenDevToolsWindow(url, false);
RunTestFunction(window_, "waitForTestResultsAsMessage");
}
};
IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
CanGetFileResourceWithFileAccess) {
Run(true, "file:///");
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
CantGetFileResourceWithoutFileAccess) {
Run(false, "file:///");
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
CantGetFileResourceWithoutFileAccessNoSlashes) {
Run(false, "file:");
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
CantGetFileResourceWithoutFileAccessMixedCase) {
Run(false, "fILe:");
}
// 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 BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_TestPauseWhenLoadingDevTools DISABLED_TestPauseWhenLoadingDevTools
#else
#define MAYBE_TestPauseWhenLoadingDevTools TestPauseWhenLoadingDevTools
#endif
IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestPauseWhenLoadingDevTools) {
RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
}
// Tests network timing.
IN_PROC_BROWSER_TEST_F(DevToolsTest, TestNetworkTiming) {
RunTest("testNetworkTiming", kSlowTestPage);
}
// Tests network size.
// TODO(crbug/40218872): Enable this flaky test. This is flaky on Linux debug
// build. See also: https://crrev.com/c/2772698
#if BUILDFLAG(IS_LINUX) && !defined(NDEBUG)
#define MAYBE_TestNetworkSize DISABLED_TestNetworkSize
#else
#define MAYBE_TestNetworkSize TestNetworkSize
#endif
IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestNetworkSize) {
RunTest("testNetworkSize", kChunkedTestPage);
}
// Tests raw headers text.
IN_PROC_BROWSER_TEST_F(DevToolsTest, TestNetworkSyncSize) {
RunTest("testNetworkSyncSize", kChunkedTestPage);
}
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::Milliseconds(10);
response->request_start = start_time;
response->response_start = base::TimeTicks::Now();
response->request_time = base::Time::Now() - base::Milliseconds(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::Milliseconds(100);
if (url.query() != kPushUseNullEndTime) {
load_timing.push_end = base::TimeTicks::Now();
}
// 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->OnReceiveResponse(std::move(response),
std::move(consumer_handle), std::nullopt);
params->client->OnComplete(network::URLLoaderCompletionStatus());
return true;
}
} // namespace
// TODO(crbug.com/40116595) 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 BUILDFLAG(IS_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 BUILDFLAG(IS_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 BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_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* inspected_contents)
: inspected_contents_(inspected_contents) {}
BrowserAutofillManagerTestDelegateDevtoolsImpl(
const BrowserAutofillManagerTestDelegateDevtoolsImpl&) = delete;
BrowserAutofillManagerTestDelegateDevtoolsImpl& operator=(
const BrowserAutofillManagerTestDelegateDevtoolsImpl&) = delete;
~BrowserAutofillManagerTestDelegateDevtoolsImpl() override {}
void DidPreviewFormData() override {}
void DidFillFormData() override {}
void DidShowSuggestions() override {
// Set an override for the minimum 500 ms threshold before enter key strokes
// are accepted.
if (base::WeakPtr<autofill::AutofillSuggestionController> controller =
autofill::ChromeAutofillClient::FromWebContentsForTesting(
inspected_contents_.get())
->suggestion_controller_for_testing()) {
controller->DisableThresholdForTesting(true);
}
ASSERT_TRUE(content::ExecJs(inspected_contents_,
"console.log('didShowSuggestions');"));
}
void DidHideSuggestions() override {}
private:
const raw_ptr<WebContents> inspected_contents_;
};
// Disabled. Failing on MacOS MSAN. See https://crbug.com/849129.
// Also failing on Linux. See https://crbug.com/1187693.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_TestDispatchKeyEventShowsAutoFill \
DISABLED_TestDispatchKeyEventShowsAutoFill
#else
#define MAYBE_TestDispatchKeyEventShowsAutoFill \
TestDispatchKeyEventShowsAutoFill
#endif
IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestDispatchKeyEventShowsAutoFill) {
OpenDevToolsWindow(kDispatchKeyEventShowsAutoFill, false);
GetInspectedTab()->Focus();
autofill::ContentAutofillDriver* autofill_driver =
autofill::ContentAutofillDriverFactory::FromWebContents(GetInspectedTab())
->DriverForFrame(GetInspectedTab()->GetPrimaryMainFrame());
auto& autofill_manager = static_cast<autofill::BrowserAutofillManager&>(
autofill_driver->GetAutofillManager());
BrowserAutofillManagerTestDelegateDevtoolsImpl autofill_test_delegate(
GetInspectedTab());
autofill_test_delegate.Observe(autofill_manager);
RunTestFunction(window_, "testDispatchKeyEventShowsAutoFill");
CloseDevToolsWindow();
}
// Tests that allowed 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();
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, testCloseActionRecorded) {
base::UserActionTester user_action_tester;
OpenDevToolsWindow("about:blank", true);
CloseDevToolsWindow();
EXPECT_EQ(1, user_action_tester.GetActionCount("DevTools_Close"));
}
// 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 BUILDFLAG(IS_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::ExecJs(main_web_contents(),
std::string("window.location = \"") + url.spec() + "\""));
observer.Wait();
ASSERT_TRUE(main_web_contents()->GetLastCommittedURL().SchemeIs(
content::kChromeDevToolsScheme));
ASSERT_EQ(url, GetInspectedTab()->GetLastCommittedURL());
CloseDevToolsWindow();
}
// Tests that toolbox window is loaded when DevTools window is undocked.
// TODO(crbug.com/40929457) - Fix this failing browser test.
IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_TestToolboxLoadedUndocked) {
OpenDevToolsWindow(kDebuggerTestPage, false);
ASSERT_TRUE(toolbox_web_contents());
DevToolsWindow* on_self =
DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
EXPECT_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.
// TODO(crbug.com/40836594): Re-enable this test
IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_TestToolboxNotLoadedDocked) {
OpenDevToolsWindow(kDebuggerTestPage, true);
EXPECT_FALSE(toolbox_web_contents());
DevToolsWindow* on_self =
DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
EXPECT_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_EQ(
"function",
content::EvalJs(
main_web_contents(),
"'' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite));"))
<< "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(crbug.com/40742539): Flaky on debug builds.
#if !defined(NDEBUG)
#define MAYBE_TestAutoOpenForTabs DISABLED_TestAutoOpenForTabs
#elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
// TODO(crbug.com/40817460): Flaky failures
#define MAYBE_TestAutoOpenForTabs DISABLED_TestAutoOpenForTabs
#else
#define MAYBE_TestAutoOpenForTabs TestAutoOpenForTabs
#endif
IN_PROC_BROWSER_TEST_F(DevToolsAutoOpenerTest, MAYBE_TestAutoOpenForTabs) {
{
DevToolsWindowCreationObserver observer;
ASSERT_TRUE(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;
ASSERT_TRUE(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);
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
content::RenderProcessHostWatcher crash_observer(
GetInspectedTab(),
content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
ASSERT_TRUE(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();
}
};
// TODO(crbug.com/40936829): Reenable after fixing consistent Windows failure.
IN_PROC_BROWSER_TEST_F(DevToolsReattachAfterCrashTest,
DISABLED_TestReattachAfterCrashOnTimeline) {
RunTestWithPanel("timeline");
}
// TODO(crbug.com/40938244): Gardener 2023-10-26: Flaky on bots.
IN_PROC_BROWSER_TEST_F(DevToolsReattachAfterCrashTest,
DISABLED_TestReattachAfterCrashOnNetwork) {
RunTestWithPanel("network");
}
// Very flaky on Linux only. http://crbug.com/1216219
#if BUILDFLAG(IS_LINUX)
#define MAYBE_AutoAttachToWindowOpen DISABLED_AutoAttachToWindowOpen
#else
#define MAYBE_AutoAttachToWindowOpen AutoAttachToWindowOpen
#endif
IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_AutoAttachToWindowOpen) {
OpenDevToolsWindow(kWindowOpenTestPage, false);
DevToolsWindowTesting::Get(window_)->SetOpenNewWindowForPopups(true);
DevToolsWindowCreationObserver observer;
ASSERT_TRUE(content::ExecJs(GetInspectedTab(),
"window.open('window_open.html', '_blank');"));
observer.WaitForLoad();
DispatchOnTestSuite(observer.devtools_window(), "waitForDebuggerPaused");
DevToolsWindowTesting::CloseDevToolsWindowSync(observer.devtools_window());
CloseDevToolsWindow();
}
// TODO(crbug.com/40704377) 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(
GetOrCreateDevToolsHostForWebContents(second));
EXPECT_EQ("page", agent->GetType());
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(WorkerDevToolsTest, InspectSharedWorker) {
GURL url = embedded_test_server()->GetURL(kSharedWorkerTestPage);
ASSERT_TRUE(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/1263230
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_MAC)
#define MAYBE_PauseInSharedWorkerInitialization \
DISABLED_PauseInSharedWorkerInitialization
#else
#define MAYBE_PauseInSharedWorkerInitialization \
PauseInSharedWorkerInitialization
#endif
IN_PROC_BROWSER_TEST_F(WorkerDevToolsTest,
MAYBE_PauseInSharedWorkerInitialization) {
GURL url = embedded_test_server()->GetURL(kReloadSharedWorkerTestPage);
ASSERT_TRUE(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.
ASSERT_TRUE(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) {
ASSERT_TRUE(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,
TabCloseTypes::CLOSE_NONE);
EXPECT_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");
command_line->AppendSwitchASCII(switches::kRemoteAllowOrigins, "*");
// 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/41478279): 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(RemoteDebuggingTest, DiscoveryPage) {
ASSERT_TRUE(RunExtensionTest("discovery_page")) << message_;
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, PolicyDisallowed) {
DisallowDevTools(browser());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetWebContentsAt(0);
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
EXPECT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, PolicyDisallowedCloseConnection) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetWebContentsAt(0);
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
// Policy change must close the connection
DisallowDevTools(browser());
EXPECT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
using ManifestLocation = extensions::mojom::ManifestLocation;
class DevToolsDisallowedForForceInstalledExtensionsPolicyTest
: public extensions::ExtensionBrowserTest {
public:
// Installs an extensions, using the specified manifest location.
// Contains assertions - callers should wrap calls of this method in
// |ASSERT_NO_FATAL_FAILURE|.
void InstallExtensionWithLocation(ManifestLocation location,
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, location);
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 installed extension.
void InstallExtensionAndOpen(ManifestLocation location,
content::WebContents** out_web_contents) {
std::string extension_id;
InstallExtensionWithLocation(location, &extension_id);
GURL url("chrome-extension://" + extension_id + "/options.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
*out_web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
}
void PolicyInstallExtensionAndOpen(content::WebContents** out_web_contents) {
InstallExtensionAndOpen(ManifestLocation::kExternalPolicyDownload,
out_web_contents);
}
void InstallComponentExtensionAndOpen(
content::WebContents** out_web_contents) {
InstallExtensionAndOpen(ManifestLocation::kExternalComponent,
out_web_contents);
}
};
IN_PROC_BROWSER_TEST_F(DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
DisallowedForExternalPolicyDownloadExtension) {
// DevTools are disallowed for policy-installed extensions by default.
content::WebContents* web_contents = nullptr;
ASSERT_NO_FATAL_FAILURE(PolicyInstallExtensionAndOpen(&web_contents));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
EXPECT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
IN_PROC_BROWSER_TEST_F(DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
DisallowedForComponentExtensionForManagedUsers) {
// DevTools are disallowed for component extensions by default for managed
// profiles.
profile()->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
content::WebContents* web_contents = nullptr;
ASSERT_NO_FATAL_FAILURE(InstallComponentExtensionAndOpen(&web_contents));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
EXPECT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
IN_PROC_BROWSER_TEST_F(DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
AllowedForComponentExtensionForNonManagedUsers) {
// DevTools are allowed for component extensions by default non-managed
// profiles.
content::WebContents* web_contents = nullptr;
ASSERT_NO_FATAL_FAILURE(InstallComponentExtensionAndOpen(&web_contents));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
IN_PROC_BROWSER_TEST_F(DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
ExtensionConnectionClosedOnPolicyChange) {
AllowDevTools(browser());
content::WebContents* web_contents = nullptr;
ASSERT_NO_FATAL_FAILURE(PolicyInstallExtensionAndOpen(&web_contents));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
// Policy change must close the connection with the policy installed
// extension.
DisallowDevToolsForForceInstalledExtenions(browser());
EXPECT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
IN_PROC_BROWSER_TEST_F(DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
ClosedAfterNavigationToExtension) {
// DevTools are disallowed for policy-installed extensions by default.
std::string extension_id;
ASSERT_NO_FATAL_FAILURE(InstallExtensionWithLocation(
ManifestLocation::kExternalPolicyDownload, &extension_id));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetWebContentsAt(0);
// It's possible to open DevTools for about:blank.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
// Navigating to extension page should close DevTools.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL("chrome-extension://" + extension_id + "/options.html")));
EXPECT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
IN_PROC_BROWSER_TEST_F(DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
AboutBlankConnectionKeptOnPolicyChange) {
AllowDevTools(browser());
std::string extension_id;
ASSERT_NO_FATAL_FAILURE(InstallExtensionWithLocation(
ManifestLocation::kExternalPolicyDownload, &extension_id));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetWebContentsAt(0);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
// Policy change to must not disrupt CDP coneciton unrelated to a force
// installed extension.
DisallowDevToolsForForceInstalledExtenions(browser());
ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
}
class DevToolsAllowedByCommandLineSwitch
: public DevToolsDisallowedForForceInstalledExtensionsPolicyTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
// Same as `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)
ASSERT_EQ(kForceDevToolsAvailableBase, switches::kForceDevToolsAvailable);
#endif
command_line->AppendSwitch("--" + kForceDevToolsAvailableBase);
}
};
IN_PROC_BROWSER_TEST_F(DevToolsAllowedByCommandLineSwitch,
SwitchOverridesPolicyOnChromeOS) {
// DevTools are disallowed for policy-installed extensions by default.
content::WebContents* web_contents = nullptr;
ASSERT_NO_FATAL_FAILURE(PolicyInstallExtensionAndOpen(&web_contents));
DevToolsWindow::OpenDevToolsWindow(web_contents,
DevToolsOpenedByAction::kUnknown);
auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
#if BUILDFLAG(IS_CHROMEOS)
EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
#else
EXPECT_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(const StaticURLDataSource&) = delete;
StaticURLDataSource& operator=(const StaticURLDataSource&) = delete;
~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::move(callback).Run(
base::MakeRefCounted<base::RefCountedString>(std::string(content_)));
}
std::string GetMimeType(const GURL& url) override { return "text/html"; }
bool ShouldAddContentSecurityPolicy() override { return false; }
private:
const std::string source_;
const std::string content_;
};
class MockWebUIProvider
: public TestChromeWebUIControllerFactory::WebUIProvider {
public:
MockWebUIProvider(const std::string& source, const std::string& content)
: source_(source), content_(content) {}
MockWebUIProvider(const MockWebUIProvider&) = delete;
MockWebUIProvider& operator=(const MockWebUIProvider&) = delete;
~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_;
};
// This tests checks that window is correctly initialized when DevTools is
// opened while navigation through history with forward and back actions.
// (crbug.com/627407)
// TODO(crbug.com/40267320): Deflake and re-enable this test.
IN_PROC_BROWSER_TEST_F(DevToolsTest,
DISABLED_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://foo/dummyurl").host(),
&mock_provider);
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL("chrome://foo/dummyurl")));
DevToolsWindow* window =
DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), true);
chrome::DuplicateTab(browser());
chrome::SelectPreviousTab(browser());
ASSERT_TRUE(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::Days(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(crbug.com/40847130): Flaky on Windows and Linux.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
#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, TestOpenSearchResultsInNewTab) {
OpenDevToolsWindow(kDebuggerTestPage, false);
DevToolsUIBindings::Delegate* bindings_delegate_ =
static_cast<DevToolsUIBindings::Delegate*>(window_);
TabStripModel* tabs = browser()->tab_strip_model();
bindings_delegate_->OpenSearchResultsInNewTab("test query");
std::string opened_url = tabs->GetWebContentsAt(1)->GetVisibleURL().spec();
EXPECT_EQ(
opened_url,
"https://www.google.com/search?q=test+query&sourceid=chrome&ie=UTF-8");
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, LoadNetworkResourceForFrontend) {
std::string file_url =
"file://" + base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
.AppendASCII("content/test/data/devtools/navigation.html")
.NormalizePathSeparatorsTo('/')
.AsUTF8Unsafe();
GURL url(embedded_test_server()->GetURL("/"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/hello.html")));
window_ =
DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), false);
LoadLegacyFilesInFrontend(window_);
RunTestMethod("testLoadResourceForFrontend", url.spec().c_str(),
file_url.c_str());
DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
}
// TODO(crbug.com/41435439) 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/40708597): Flaky.
IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_DisposeEmptyBrowserContext) {
window_ = DevToolsWindowTesting::OpenDiscoveryDevToolsWindowSync(
browser()->profile());
RunTestMethod("testDisposeEmptyBrowserContext");
DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
}
// TODO(crbug.com/40689291): Find a better strategy for testing protocol methods
// against non-headless Chrome.
IN_PROC_BROWSER_TEST_F(DevToolsTest, NewWindowFromBrowserContext) {
window_ = DevToolsWindowTesting::OpenDiscoveryDevToolsWindowSync(
browser()->profile());
LoadLegacyFilesInFrontend(window_);
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());
ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
ASSERT_TRUE(navigation_manager_iframe.WaitForNavigationFinished());
EXPECT_TRUE(content::WaitForLoadStop(tab));
std::vector<RenderFrameHost*> frames =
CollectAllRenderFrameHosts(GetInspectedTab());
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);
}
class DevToolsTabTargetTest : public DevToolsTest {
base::test::ScopedFeatureList scoped_feature_list_{
::features::kDevToolsTabTarget};
};
IN_PROC_BROWSER_TEST_F(DevToolsTabTargetTest, InspectElement) {
GURL url(
embedded_test_server()->GetURL("a.com", "/devtools/oopif_frame.html"));
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationManager navigation_manager(tab, url);
tab->GetController().LoadURL(url, content::Referrer(),
ui::PAGE_TRANSITION_LINK, std::string());
ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
EXPECT_TRUE(content::WaitForLoadStop(tab));
std::vector<RenderFrameHost*> frames =
CollectAllRenderFrameHosts(GetInspectedTab());
ASSERT_EQ(1u, frames.size());
RenderFrameHost* frame_host = frames[0];
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(DevToolsTabTargetTest, UKMTest) {
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
GURL url(
embedded_test_server()->GetURL("a.com", "/devtools/oopif_frame.html"));
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
tab->GetController().LoadURL(url, content::Referrer(),
ui::PAGE_TRANSITION_LINK, std::string());
EXPECT_TRUE(content::WaitForLoadStop(tab));
std::vector<RenderFrameHost*> frames =
CollectAllRenderFrameHosts(GetInspectedTab());
RenderFrameHost* frame_host = frames[0];
DevToolsWindow::InspectElement(frame_host, 100, 100);
// Make sure we are recording the UKM when DevTools are opened.
auto ukm_entries = test_ukm_recorder.GetEntriesByName("DevTools.Opened");
EXPECT_EQ(1u, ukm_entries.size());
test_ukm_recorder.ExpectEntrySourceHasUrl(ukm_entries[0], url);
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, ExistsForWebContentsAfterClosing) {
EXPECT_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));
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(
content::ExecJs(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(crbug.com/40721876): Re-enable.
IN_PROC_BROWSER_TEST_F(InProcessBrowserTest,
DISABLED_BrowserCloseWithContextMenuOpened) {
EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsOriginRegistered(
KeepAliveOrigin::REMOTE_DEBUGGING));
ASSERT_TRUE(
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)
// 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)
class DevToolsPolicyTest : public InProcessBrowserTest {
protected:
DevToolsPolicyTest() {
provider_.SetDefaultReturns(
/*is_initialization_complete_return=*/true,
/*is_first_policy_load_complete_return=*/true);
}
void SetUpInProcessBrowserTestFixture() override {
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
};
IN_PROC_BROWSER_TEST_F(DevToolsPolicyTest, OpenBlockedDevTools) {
base::Value::List blocklist;
blocklist.Append("devtools://*");
policy::PolicyMap policies;
policies.Set(policy::key::kURLBlocklist, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value(std::move(blocklist)), nullptr);
provider_.UpdateChromePolicy(policies);
WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
scoped_refptr<content::DevToolsAgentHost> agent(
GetOrCreateDevToolsHostForWebContents(wc));
DevToolsWindow::OpenDevToolsWindow(wc, DevToolsOpenedByAction::kUnknown);
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);
}
class DevToolsExtensionHostsPolicyTest : public DevToolsExtensionTest {
protected:
DevToolsExtensionHostsPolicyTest() {
provider_.SetDefaultReturns(
/*is_initialization_complete_return=*/true,
/*is_first_policy_load_complete_return=*/true);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
void SetUpInProcessBrowserTestFixture() override {
DevToolsExtensionTest::SetUpInProcessBrowserTestFixture();
base::Value::Dict settings;
settings.Set(
"*", base::Value::Dict()
.Set(extensions::schema_constants::kPolicyBlockedHosts,
base::Value::List().Append("*://*.example.com"))
.Set(extensions::schema_constants::kPolicyAllowedHosts,
base::Value::List().Append("*://public.example.com")));
policy::PolicyMap policies;
policies.Set(policy::key::kExtensionSettings,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD, base::Value(std::move(settings)),
nullptr);
provider_.UpdateChromePolicy(policies);
}
testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DevToolsExtensionHostsPolicyTest,
CantInspectBlockedHost) {
GURL url(embedded_test_server()->GetURL("example.com", kArbitraryPage));
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#", url.spec()}));
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionHostsPolicyTest,
CantInspectBlockedSubdomainHost) {
GURL url(embedded_test_server()->GetURL("foo.example.com", kArbitraryPage));
LoadExtension("can_inspect_url");
RunTest("waitForTestResultsAsMessage",
base::StrCat({kArbitraryPage, "#", url.spec()}));
}
// TODO(crbug.com/333791064): Flaky on multiple Mac & Linux builders.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#define MAYBE_CanInspectAllowedHttpHost DISABLED_CanInspectAllowedHttpHost
#else
#define MAYBE_CanInspectAllowedHttpHost CanInspectAllowedHttpHost
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionHostsPolicyTest,
MAYBE_CanInspectAllowedHttpHost) {
GURL url(
embedded_test_server()->GetURL("public.example.com", kArbitraryPage));
extensions::TestExtensionDir dir;
dir.WriteManifest(
BuildExtensionManifest("Runtime Hosts Policy", "devtools.html"));
dir.WriteFile(
FILE_PATH_LITERAL("devtools.html"),
"<html><head><script src='devtools.js'></script></head></html>");
dir.WriteFile(FILE_PATH_LITERAL("devtools.js"),
R"(
chrome.devtools.network.getHAR((result) => {
setInterval(() => {
top.postMessage(
{testOutput: ('entries' in result) ? 'PASS' : 'FAIL'},
'*'
);
}, 10);
});)");
const Extension* extension = LoadExtensionFromPath(dir.UnpackedPath());
ASSERT_TRUE(extension);
RunTest("waitForTestResultsAsMessage", url.spec());
}
// Times out. 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());
ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
ASSERT_TRUE(navigation_manager_iframe.WaitForNavigationFinished());
EXPECT_TRUE(content::WaitForLoadStop(tab));
for (auto* frame : CollectAllRenderFrameHosts(GetInspectedTab())) {
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",
base::NumberToString(websocket_port).c_str());
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, SourceMapsFromExtension) {
const Extension* extension =
LoadExtensionForTest("Non-DevTools Extension", "" /* devtools_page */,
"" /* panel_iframe_src */);
ASSERT_TRUE(extension);
OpenDevToolsWindow(kEmptyTestPage, /* is_docked */ false);
DispatchOnTestSuite(window_, "testSourceMapsFromExtension",
extension->id().c_str());
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, SourceMapsFromDevtools) {
OpenDevToolsWindow(kEmptyTestPage, /* is_docked */ false);
DispatchOnTestSuite(window_, "testSourceMapsFromDevtools");
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsTest,
DoesNotCrashOnSourceMapsFromUnknownScheme) {
OpenDevToolsWindow(kEmptyTestPage, /* is_docked */ false);
DispatchOnTestSuite(window_, "testDoesNotCrashOnSourceMapsFromUnknownScheme");
CloseDevToolsWindow();
}
// TODO(crbug.com/40937316): Test is flaky on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_ExtensionWebSocketOfflineNetworkConditions \
DISABLED_ExtensionWebSocketOfflineNetworkConditions
#else
#define MAYBE_ExtensionWebSocketOfflineNetworkConditions \
ExtensionWebSocketOfflineNetworkConditions
#endif
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
MAYBE_ExtensionWebSocketOfflineNetworkConditions) {
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_, "testExtensionWebSocketOfflineNetworkConditions",
base::NumberToString(websocket_port).c_str());
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, IsDeveloperModeTrueHistogram) {
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kExtensionsUIDeveloperMode, true);
base::HistogramTester histograms;
const char* histogram_name = "Extensions.DevTools.UserIsInDeveloperMode";
LoadExtension("devtools_extension");
RunTest("waitForTestResultsInConsole", kArbitraryPage);
histograms.ExpectBucketCount(histogram_name, true, 2);
}
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, IsDeveloperModeFalseHistogram) {
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kExtensionsUIDeveloperMode, false);
base::HistogramTester histograms;
const char* histogram_name = "Extensions.DevTools.UserIsInDeveloperMode";
LoadExtension("devtools_extension");
RunTest("waitForTestResultsInConsole", kArbitraryPage);
histograms.ExpectBucketCount(histogram_name, false, 2);
}
namespace {
class DevToolsLocalizationTest : public DevToolsTest {
public:
bool NavigatorLanguageMatches(const std::string& expected_locale) {
return content::EvalJs(main_web_contents(),
"window.navigator.language === "
"'" +
expected_locale + "'")
.ExtractBool();
}
};
} // 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/devtools_compatibility.js"));
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsFetchTest,
DevToolsFetchFromDevToolsSchemeDocked) {
OpenDevToolsWindow("about:blank", true);
EXPECT_EQ(200, FetchFromDevToolsWindow(
"devtools://devtools/bundled/devtools_compatibility.js"));
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsFetchTest, DevToolsFetchFromHttpDisallowed) {
OpenDevToolsWindow("about:blank", true);
const auto result = FetchFromDevToolsWindow("http://www.google.com");
EXPECT_THAT(result.error,
::testing::StartsWith(
"a JavaScript error: \"TypeError: Failed to fetch\n"));
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsFetchTest, FetchFromDevToolsSchemeIsProhibited) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
const auto result =
Fetch(GetInspectedTab(),
"devtools://devtools/bundled/devtools_compatibility.js");
EXPECT_THAT(result.error,
::testing::StartsWith(
"a JavaScript error: \"TypeError: Failed to fetch\n"));
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, HostBindingsSyncIntegration) {
// Smoke test to make sure that `registerPreference` works from JavaScript.
OpenDevToolsWindow("about:blank", true);
LoadLegacyFilesInFrontend(window_);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
ASSERT_TRUE(content::ExecJs(
wc, content::JsReplace(
R"(
Host.InspectorFrontendHost.setPreference($1, 'false'); // Disable sync.
Host.InspectorFrontendHost.registerPreference(
'synced_setting', {synced: true});
Host.InspectorFrontendHost.registerPreference(
'unsynced_setting', {synced: false});
Host.InspectorFrontendHost.setPreference('synced_setting', 'synced value');
Host.InspectorFrontendHost.setPreference(
'unsynced_setting', 'unsynced value');
)",
DevToolsSettings::kSyncDevToolsPreferencesFrontendName)));
const base::Value::Dict& synced_settings =
browser()->profile()->GetPrefs()->GetDict(
prefs::kDevToolsSyncedPreferencesSyncDisabled);
const base::Value::Dict& unsynced_settings =
browser()->profile()->GetPrefs()->GetDict(prefs::kDevToolsPreferences);
EXPECT_EQ(*synced_settings.FindString("synced_setting"), "synced value");
EXPECT_EQ(*unsynced_settings.FindString("unsynced_setting"),
"unsynced value");
}
IN_PROC_BROWSER_TEST_F(DevToolsTest, NoJavascriptUrlOnDevtools) {
// As per crbug/1115460 one could use javascript: url as a homepage URL and
// then trigger homepage navigation (e.g. via keyboard shortcut) to execute in
// the context of the privileged devtools frontend.
OpenDevToolsWindow("about:blank", true);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
wc->GetController().LoadURL(GURL("javascript:window.xss=true"),
content::Referrer(), ui::PAGE_TRANSITION_TYPED,
std::string());
EXPECT_EQ(false, content::EvalJs(wc, "!!window.xss"));
}
// According to DevToolsTest.AutoAttachToWindowOpen, using
// `waitForDebuggerPaused()` is flaky on Linux.
// TODO(crbug.com/40770357): Enable the test on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_PauseWhenSameOriginDebuggerAlreadyAttached \
DISABLED_PauseWhenSameOriginDebuggerAlreadyAttached
#else
#define MAYBE_PauseWhenSameOriginDebuggerAlreadyAttached \
PauseWhenSameOriginDebuggerAlreadyAttached
#endif
IN_PROC_BROWSER_TEST_F(DevToolsTest,
MAYBE_PauseWhenSameOriginDebuggerAlreadyAttached) {
base::HistogramTester histograms;
const GURL hello_url =
embedded_test_server()->GetURL("a.test", "/hello.html");
const GURL pause_url = embedded_test_server()->GetURL(
"a.test", "/devtools/pause_when_loading_devtools.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), hello_url));
DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetWebContentsAt(0), true);
Browser* another_browser = CreateBrowser(browser()->profile());
ASSERT_TRUE(ui_test_utils::NavigateToURL(another_browser, pause_url));
DevToolsWindow* another_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(
another_browser->tab_strip_model()->GetWebContentsAt(0), true);
DispatchOnTestSuite(another_window, "waitForDebuggerPaused");
histograms.ExpectBucketCount(
"DevTools.IsSameOriginDebuggerAttachedInAnotherRenderer", true, 1);
}
// According to DevToolsTest.AutoAttachToWindowOpen, using
// `waitForDebuggerPaused()` is flaky on Linux.
// TODO(crbug.com/40770357): Enable the test on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_PauseWhenSameOriginDebuggerAlreadyPaused \
DISABLED_PauseWhenSameOriginDebuggerAlreadyPaused
#else
#define MAYBE_PauseWhenSameOriginDebuggerAlreadyPaused \
PauseWhenSameOriginDebuggerAlreadyPaused
#endif
IN_PROC_BROWSER_TEST_F(DevToolsTest,
MAYBE_PauseWhenSameOriginDebuggerAlreadyPaused) {
base::HistogramTester histograms;
const GURL pause_url = embedded_test_server()->GetURL(
"a.test", "/devtools/pause_when_loading_devtools.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), pause_url));
DevToolsWindow* window = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetWebContentsAt(0), true);
DispatchOnTestSuite(window, "waitForDebuggerPaused");
Browser* another_browser = CreateBrowser(browser()->profile());
ASSERT_TRUE(ui_test_utils::NavigateToURL(another_browser, pause_url));
DevToolsWindow* another_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(
another_browser->tab_strip_model()->GetWebContentsAt(0), true);
DispatchOnTestSuite(another_window, "waitForDebuggerPaused");
histograms.ExpectBucketCount(
"DevTools.IsSameOriginDebuggerPausedInAnotherRenderer", true, 1);
}
class DevToolsSyncTest : public SyncTest {
public:
DevToolsSyncTest() : SyncTest(SyncTest::SINGLE_CLIENT) {}
};
IN_PROC_BROWSER_TEST_F(DevToolsSyncTest, GetSyncInformation) {
// Smoke test to make sure that `getSyncInformation` works from JavaScript.
ASSERT_TRUE(SetupSync());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
DevToolsWindow* window = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetActiveWebContents(), GetProfile(0),
true);
LoadLegacyFilesInFrontend(window);
WebContents* wc = DevToolsWindowTesting::Get(window)->main_web_contents();
const auto result = content::EvalJs(wc, content::JsReplace(R"(
(async function() {
return new Promise(resolve => {
Host.InspectorFrontendHost.getSyncInformation(resolve);
});
})();
)"));
ASSERT_TRUE(result.value.is_dict());
EXPECT_TRUE(*result.value.GetDict().FindBool("isSyncActive"));
EXPECT_TRUE(*result.value.GetDict().FindBool("arePreferencesSynced"));
EXPECT_EQ(*result.value.GetDict().FindString("accountEmail"),
"user@gmail.com");
}
// Regression test for https://crbug.com/1270184.
// TODO(crbug.com/40809266): Fix flakyness. Test is disabled for now.
IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_NoCrashFor1270184) {
OpenDevToolsWindow("/devtools/regress-crbug-1270184.html", true);
}
class DevToolsProcessPerSiteUpToMainFrameThresholdTest : public DevToolsTest {
public:
DevToolsProcessPerSiteUpToMainFrameThresholdTest() {
scoped_feature_list_.InitAndEnableFeature(
features::kProcessPerSiteUpToMainFrameThreshold);
}
~DevToolsProcessPerSiteUpToMainFrameThresholdTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DevToolsProcessPerSiteUpToMainFrameThresholdTest,
DevToolsWasAttachedBefore) {
const GURL url = embedded_test_server()->GetURL("foo.test", "/hello.html");
OpenDevToolsWindow(kDebuggerTestPage, false);
Browser* browser1 = CreateBrowser(browser()->profile());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser1, url));
Browser* browser2 = CreateBrowser(browser()->profile());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser2, url));
ASSERT_NE(browser1->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame()
->GetProcess(),
browser2->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame()
->GetProcess());
}
// TODO(crbug.com/40924806): The test is failing on multiple builders.
IN_PROC_BROWSER_TEST_F(DevToolsProcessPerSiteUpToMainFrameThresholdTest,
DISABLED_DontReuseProcess) {
OpenDevToolsWindow(kDebuggerTestPage, false);
DevToolsWindow* window =
DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), true);
WebContents* webcontents =
DevToolsWindowTesting::Get(window)->main_web_contents();
DevToolsWindow* window2 =
DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), false);
WebContents* webcontents2 =
DevToolsWindowTesting::Get(window2)->main_web_contents();
ASSERT_NE(webcontents->GetPrimaryMainFrame()->GetProcess(),
webcontents2->GetPrimaryMainFrame()->GetProcess());
}
class DevToolsProcessPerSiteTest : public DevToolsTest {
public:
DevToolsProcessPerSiteTest() = default;
~DevToolsProcessPerSiteTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kProcessPerSite);
}
private:
base::test::ScopedFeatureList scoped_feature_list_{
::features::kDevToolsSharedProcessInfobar};
};
// TODO(https://crbug.com/328693031): Flaky on Linux dbg.
#if BUILDFLAG(IS_LINUX) && !defined(NDEBUG)
#define MAYBE_DevToolsSharedProcessInfobar DISABLED_DevToolsSharedProcessInfobar
#else
#define MAYBE_DevToolsSharedProcessInfobar DevToolsSharedProcessInfobar
#endif
IN_PROC_BROWSER_TEST_F(DevToolsProcessPerSiteTest,
MAYBE_DevToolsSharedProcessInfobar) {
const GURL url = embedded_test_server()->GetURL("foo.test", "/hello.html");
Browser* browser1 = CreateBrowser(browser()->profile());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser1, url));
Browser* browser2 = CreateBrowser(browser()->profile());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser2, url));
ASSERT_EQ(browser1->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame()
->GetProcess(),
browser2->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame()
->GetProcess());
auto* window = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser1->tab_strip_model()->GetActiveWebContents(), true);
auto* infobar_manager = infobars::ContentInfoBarManager::FromWebContents(
browser1->tab_strip_model()->GetActiveWebContents());
ASSERT_EQ(infobar_manager->infobars().size(), 1u);
ASSERT_EQ(infobar_manager->infobars()[0]->GetIdentifier(),
infobars::InfoBarDelegate::DEV_TOOLS_SHARED_PROCESS_DELEGATE);
DevToolsWindowTesting::CloseDevToolsWindowSync(window);
ASSERT_EQ(infobar_manager->infobars().size(), 0u);
// Now try in the undocked case.
window = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser1->tab_strip_model()->GetActiveWebContents(), false);
// The infobar should appear in the undocked window.
ASSERT_EQ(infobar_manager->infobars().size(), 0u);
// Retrieve the infobar manager from the devtools window, this is different
// than `infobar_maanger` when undocked.
auto* undocked_infobar_manager =
static_cast<DevToolsUIBindings::Delegate*>(window)->GetInfoBarManager();
ASSERT_EQ(undocked_infobar_manager->infobars().size(), 1u);
ASSERT_EQ(undocked_infobar_manager->infobars()[0]->GetIdentifier(),
infobars::InfoBarDelegate::DEV_TOOLS_SHARED_PROCESS_DELEGATE);
}
// Observe that the active tab has changed.
class ActiveTabChangedObserver : public TabStripModelObserver {
public:
explicit ActiveTabChangedObserver(TabStripModel* tab_strip_model) {
tab_strip_model->AddObserver(this);
}
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
if (change.type() == TabStripModelChange::kSelectionOnly &&
tab_strip_model->active_index() == 0) {
loop_.Quit();
return;
}
}
void Wait() { loop_.Run(); }
private:
base::RunLoop loop_;
};
// TODO: crbug.com/337141755 - Flaky on Windows ASAN.
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_PausedDebuggerFocus DISABLED_PausedDebuggerFocus
#else
#define MAYBE_PausedDebuggerFocus PausedDebuggerFocus
#endif
IN_PROC_BROWSER_TEST_F(DevToolsProcessPerSiteTest, MAYBE_PausedDebuggerFocus) {
const GURL url = embedded_test_server()->GetURL("foo.test", "/hello.html");
auto* tab_strip_model = browser()->tab_strip_model();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
auto* devtools_window = DevToolsWindowTesting::OpenDevToolsWindowSync(
tab_strip_model->GetWebContentsAt(0), true);
ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 1, url,
ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
ASSERT_EQ(2, tab_strip_model->count());
ASSERT_EQ(
tab_strip_model->GetWebContentsAt(0)->GetPrimaryMainFrame()->GetProcess(),
tab_strip_model->GetWebContentsAt(1)
->GetPrimaryMainFrame()
->GetProcess());
ASSERT_EQ(1, tab_strip_model->active_index());
ASSERT_TRUE(content::ExecJs(tab_strip_model->GetWebContentsAt(0),
"setTimeout(() => {debugger;}, 0);"));
DispatchOnTestSuite(devtools_window, "waitForDebuggerPaused");
ActiveTabChangedObserver active_tab_observer(tab_strip_model);
content::SimulateMouseClick(tab_strip_model->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft);
active_tab_observer.Wait();
ASSERT_EQ(0, tab_strip_model->active_index());
}
class DevToolsConsoleInsightsTest : public DevToolsTest {
public:
DevToolsConsoleInsightsTest() {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{features::kDevToolsConsoleInsights,
features::kDevToolsConsoleInsightsSettingVisible},
/*disabled_features=*/{});
policy_provider_.SetDefaultReturns(
/*is_initialization_complete_return=*/true,
/*is_first_policy_load_complete_return=*/true);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
&policy_provider_);
}
void SetupAccountCapabilities(bool is_minor = false) {
auto* identity_manager =
IdentityManagerFactory::GetForProfile(browser()->profile());
auto account_info = signin::MakePrimaryAccountAvailable(
identity_manager, "test@example.com", signin::ConsentLevel::kSync);
AccountCapabilitiesTestMutator mutator(&account_info.capabilities);
mutator.set_can_use_devtools_generative_ai_features(!is_minor);
signin::UpdateAccountInfoForAccount(identity_manager, account_info);
}
~DevToolsConsoleInsightsTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
protected:
testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
};
bool hasQueryParam(WebContents* wc, std::string query_param) {
return std::string::npos !=
wc->GetLastCommittedURL().query().find(query_param);
}
IN_PROC_BROWSER_TEST_F(DevToolsConsoleInsightsTest,
EnterprisePolicyEnabledByDefault) {
g_browser_process->variations_service()->OverrideStoredPermanentCountry("us");
SetupAccountCapabilities();
OpenDevToolsWindow(kDebuggerTestPage, false);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_TRUE(hasQueryParam(wc, "&ci_disabledByDefault=true"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_disabledByDefault=true"));
#endif
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByAge=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByGeo=true"));
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsConsoleInsightsTest, IsNotEnabledForMinors) {
g_browser_process->variations_service()->OverrideStoredPermanentCountry("us");
SetupAccountCapabilities(true);
OpenDevToolsWindow(kDebuggerTestPage, false);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_TRUE(hasQueryParam(wc, "&ci_blockedByAge=true"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByAge=true"));
#endif
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByGeo=true"));
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsConsoleInsightsTest,
CanBeDisabledByEnterprisePolicy) {
g_browser_process->variations_service()->OverrideStoredPermanentCountry("us");
SetupAccountCapabilities();
// Disable via enterprise policy.
policy::PolicyMap policies;
policies.Set(policy::key::kDevToolsGenAiSettings,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD, base::Value(/* disable */ 2),
nullptr);
policy_provider_.UpdateChromePolicy(policies);
base::RunLoop().RunUntilIdle();
OpenDevToolsWindow(kDebuggerTestPage, false);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_TRUE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
#endif
CloseDevToolsWindow();
// Enable via enterprise policy.
policies.Set(policy::key::kDevToolsGenAiSettings,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD, base::Value(/* allow */ 0),
nullptr);
policy_provider_.UpdateChromePolicy(policies);
base::RunLoop().RunUntilIdle();
OpenDevToolsWindow(kDebuggerTestPage, false);
wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
#endif
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
CloseDevToolsWindow();
}
IN_PROC_BROWSER_TEST_F(DevToolsConsoleInsightsTest,
IsDisabledWhenPolicySetToOne) {
g_browser_process->variations_service()->OverrideStoredPermanentCountry("us");
policy::PolicyMap policies;
policies.Set(
policy::key::kDevToolsGenAiSettings, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value(/* enable and don't use data for training */ 1), nullptr);
policy_provider_.UpdateChromePolicy(policies);
base::RunLoop().RunUntilIdle();
OpenDevToolsWindow(kDebuggerTestPage, false);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_TRUE(hasQueryParam(wc, "&ci_disallowLogging"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
#endif
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
CloseDevToolsWindow();
}
class DevToolsConsoleInsightsBlockedByRegionTest : public DevToolsTest {
public:
DevToolsConsoleInsightsBlockedByRegionTest() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
features::kDevToolsConsoleInsightsSettingVisible,
{{"blocked_reason", "region"}});
}
~DevToolsConsoleInsightsBlockedByRegionTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DevToolsConsoleInsightsBlockedByRegionTest,
IsBlockedByGeo) {
OpenDevToolsWindow(kDebuggerTestPage, false);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_TRUE(hasQueryParam(wc, "&ci_blockedByGeo=true"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByGeo=true"));
#endif
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByAge=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByRollout=true"));
CloseDevToolsWindow();
}
class DevToolsConsoleInsightsBlockedByRolloutTest : public DevToolsTest {
public:
DevToolsConsoleInsightsBlockedByRolloutTest() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
features::kDevToolsConsoleInsightsSettingVisible,
{{"blocked_reason", "rollout"}});
}
~DevToolsConsoleInsightsBlockedByRolloutTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DevToolsConsoleInsightsBlockedByRolloutTest,
IsBlockedByRollout) {
OpenDevToolsWindow(kDebuggerTestPage, false);
WebContents* wc = DevToolsWindowTesting::Get(window_)->main_web_contents();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_TRUE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_TRUE(hasQueryParam(wc, "&ci_blockedByRollout=true"));
#else
EXPECT_FALSE(hasQueryParam(wc, "&enableAida=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByRollout=true"));
#endif
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByAge=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByEnterprisePolicy=true"));
EXPECT_FALSE(hasQueryParam(wc, "&ci_blockedByGeo=true"));
CloseDevToolsWindow();
}